-
-
Notifications
You must be signed in to change notification settings - Fork 151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generate help by flag descriptions #140
Changes from all commits
bfdab34
8d788e3
5989825
a4cacb0
20ddb4c
854419d
8b2c2eb
6893bac
935911f
fd33347
daaecaf
8642dc1
3081560
dabb139
a00c07e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
'use strict'; | ||
const decamelizeKeys = require('decamelize-keys'); | ||
const trimNewlines = require('trim-newlines'); | ||
const redent = require('redent'); | ||
|
||
function flagName(name, alias, type) { | ||
let result = `--${name}`; | ||
if (alias) { | ||
result += `, -${alias}`; | ||
} | ||
|
||
if (type && type !== 'boolean') { | ||
result += ` <${type}>`; | ||
} | ||
|
||
return result; | ||
} | ||
|
||
function buildFlagLines(flags) { | ||
flags = {...flags, help: {type: 'boolean', description: 'Show help'}}; | ||
|
||
const entries = Object.entries(decamelizeKeys(flags, '-')).map(([name, def]) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't use abbreviations (regarding |
||
const type = def.type || def; | ||
|
||
const entry = { | ||
name: flagName(name, def.alias, type), | ||
description: def.description || '' | ||
}; | ||
if (typeof def === 'object' && def !== null && 'default' in def) { | ||
entry.description += ` [default: ${def.default}]`; | ||
} | ||
|
||
entry.description = entry.description.trim(); | ||
|
||
return entry; | ||
}); | ||
|
||
const maxNameLengh = Math.max(...entries.map(({name}) => name.length)); | ||
|
||
const lines = entries.map(({name, description}) => { | ||
if (!description) { | ||
return name; | ||
} | ||
|
||
const spaces = 4; | ||
const padding = ' '.repeat(maxNameLengh - name.length + spaces); | ||
|
||
let [firstLine, ...restLines] = description.split(/\r?\n/); | ||
if (restLines.length === 0) { | ||
return `${name}${padding}${firstLine}`; | ||
} | ||
|
||
const fullPadding = ' '.repeat(maxNameLengh + spaces); | ||
restLines = restLines.map(line => fullPadding + line).join('\n'); | ||
|
||
return `${name}${padding}${firstLine}\n${restLines}`; | ||
}); | ||
|
||
return lines; | ||
} | ||
|
||
module.exports = (options, defaultDescription) => { | ||
let lines = []; | ||
|
||
let {description} = options; | ||
if (!description && description !== false) { | ||
description = defaultDescription; | ||
} | ||
|
||
if (description) { | ||
lines.push(redent(description)); | ||
} | ||
|
||
let flagLines; | ||
|
||
const {help} = options; | ||
if (typeof help === 'string' && help.length > 0) { | ||
if (lines.length > 0) { | ||
lines.push(''); | ||
} | ||
|
||
lines.push(redent(help)); | ||
} else { | ||
if (lines.length > 0) { | ||
lines.push(''); | ||
} | ||
|
||
flagLines = buildFlagLines(options.flags); | ||
lines.push('Options:'); | ||
lines.push(flagLines.map(line => redent(line, 2)).join('\n')); | ||
} | ||
|
||
lines = lines.map(line => trimNewlines(line)); | ||
|
||
const content = lines.join('\n').trimEnd(); | ||
const wholeText = '\n' + trimNewlines(redent(content, 2)) + '\n'; | ||
|
||
if (typeof help === 'function') { | ||
return help({wholeText, description, flagLines, flagOptions: options}); | ||
} | ||
|
||
return wholeText; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ declare namespace meow { | |
readonly type?: Type; | ||
readonly alias?: string; | ||
readonly default?: Default; | ||
readonly description?: string; | ||
readonly isRequired?: boolean | IsRequiredPredicate; | ||
readonly isMultiple?: boolean; | ||
} | ||
|
@@ -28,6 +29,37 @@ declare namespace meow { | |
type AnyFlag = StringFlag | BooleanFlag | NumberFlag; | ||
type AnyFlags = {[key: string]: AnyFlag}; | ||
|
||
interface GenerateHelpOptions { | ||
/** | ||
A whole help text generated by default. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo |
||
*/ | ||
wholeText: string; | ||
|
||
/** | ||
A description of a command generated by default. | ||
*/ | ||
description: string; | ||
|
||
/** | ||
A list of a line including each flag's name and description. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo |
||
*/ | ||
flagLines: readonly string[]; | ||
|
||
/** | ||
An object including each flag information. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not clear to the user what "flag information" means. |
||
*/ | ||
flagOptions: Readonly<Options<AnyFlags>>; | ||
} | ||
|
||
/** | ||
Callback function to customize a help text you want. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typos |
||
|
||
@param options - Options to help you customize. | ||
|
||
@returns A help text to be shown in a console. | ||
*/ | ||
type GenerateHelp = (options: Readonly<GenerateHelpOptions>) => string; | ||
|
||
interface Options<Flags extends AnyFlags> { | ||
/** | ||
Define argument flags. | ||
|
@@ -48,6 +80,7 @@ declare namespace meow { | |
type: 'string', | ||
alias: 'u', | ||
default: ['rainbow', 'cat'], | ||
description: 'This is an unicorn option' | ||
isMultiple: true, | ||
isRequired: (flags, input) => { | ||
if (flags.otherFlag) { | ||
|
@@ -78,7 +111,7 @@ declare namespace meow { | |
|
||
Set it to `false` to disable it altogether. | ||
*/ | ||
readonly help?: string | false; | ||
readonly help?: string | false | GenerateHelp; | ||
|
||
/** | ||
Set a custom version output. Default: The package.json `"version"` property. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -112,7 +112,7 @@ Returns an `object` with: | |
- `flags` *(Object)* - Flags converted to camelCase excluding aliases | ||
- `unnormalizedFlags` *(Object)* - Flags converted to camelCase including aliases | ||
- `pkg` *(Object)* - The `package.json` object | ||
- `help` *(string)* - The help text used with `--help` | ||
- `help` *(string | boolean | Function)* - The help text used with `--help` | ||
- `showHelp([exitCode=2])` *(Function)* - Show the help text and exit with `exitCode` | ||
- `showVersion()` *(Function)* - Show the version text and exit | ||
|
||
|
@@ -137,6 +137,7 @@ The key is the flag name and the value is an object with any of: | |
- `type`: Type of value. (Possible values: `string` `boolean` `number`) | ||
- `alias`: Usually used to define a short flag alias. | ||
- `default`: Default value when the flag is not specified. | ||
- `description`: Description of the flag. | ||
- `isRequired`: Determine if the flag is required. (Default: false) | ||
- If it's only known at runtime whether the flag is required or not, you can pass a `Function` instead of a `boolean`, which based on the given flags and other non-flag arguments, should decide if the flag is required. Two arguments are passed to the function: | ||
- The first argument is the **flags** object, which contains the flags converted to camel-case excluding aliases. | ||
|
@@ -152,6 +153,7 @@ flags: { | |
type: 'string', | ||
alias: 'u', | ||
default: ['rainbow', 'cat'], | ||
description: 'This is an unicorn option' | ||
isMultiple: true, | ||
isRequired: (flags, input) => { | ||
if (flags.otherFlag) { | ||
|
@@ -175,14 +177,27 @@ Set it to `false` to disable it altogether. | |
|
||
##### help | ||
|
||
Type: `string | boolean` | ||
Type: `string | boolean | Function` | ||
|
||
The help text you want shown. | ||
The help text you want to show. | ||
|
||
The input is reindented and starting/ending newlines are trimmed which means you can use a [template literal](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/template_strings) without having to care about using the correct amount of indent. | ||
|
||
The description will be shown above your help text automatically. | ||
|
||
Also, you can customize the auto-generated help text by giving the function as follows: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The readme and index.d.ts should be in sync. |
||
|
||
```js | ||
meow({ | ||
help: ({wholeText, flagLines, description, options}) => { | ||
return 'A help text you want...'; | ||
}, | ||
flags: { | ||
rainbow: {type: 'boolean', alias: 'r'} | ||
} | ||
}); | ||
``` | ||
|
||
##### version | ||
|
||
Type: `string | boolean`\ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file needs to be added to
files
in package.json.