-
-
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 5 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,93 @@ | ||||||
'use strict'; | ||||||
const {EOL} = require('os'); | ||||||
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 flagsSection(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), | ||||||
desc: def.description || '' | ||||||
}; | ||||||
if (typeof def === 'object' && def !== null && 'default' in def) { | ||||||
entry.desc += ` [default: ${def.default}]`; | ||||||
} | ||||||
|
||||||
entry.desc = entry.desc.trim(); | ||||||
|
||||||
return entry; | ||||||
}); | ||||||
|
||||||
const maxNameLengh = Math.max(...entries.map(({name}) => name.length)); | ||||||
|
||||||
const lines = entries.map(({name, desc}) => { | ||||||
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 for variable/parameter names. |
||||||
if (!desc) { | ||||||
return name; | ||||||
} | ||||||
|
||||||
const spaces = 4; | ||||||
const padding = ' '.repeat(maxNameLengh - name.length + spaces); | ||||||
|
||||||
let [firstLine, ...restLines] = desc.split(/\r?\n/); | ||||||
if (restLines.length === 0) { | ||||||
return `${name}${padding}${firstLine}`; | ||||||
} | ||||||
|
||||||
const fullPadding = ' '.repeat(maxNameLengh + spaces); | ||||||
restLines = restLines.map(line => fullPadding + line).join(EOL); | ||||||
|
||||||
return `${name}${padding}${firstLine}${EOL}${restLines}`; | ||||||
}); | ||||||
|
||||||
return lines; | ||||||
} | ||||||
|
||||||
module.exports = function ({description, help, flags}, pkg) { | ||||||
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.
Suggested change
|
||||||
let lines = []; | ||||||
|
||||||
if (!description && description !== false) { | ||||||
description = pkg.description; | ||||||
} | ||||||
|
||||||
if (description) { | ||||||
lines.push(description); | ||||||
} | ||||||
|
||||||
if (help) { | ||||||
if (lines.length > 0) { | ||||||
lines.push(''); | ||||||
} | ||||||
|
||||||
lines.push(help); | ||||||
} else { | ||||||
if (lines.length > 0) { | ||||||
lines.push(''); | ||||||
} | ||||||
|
||||||
lines.push('Options:'); | ||||||
lines.push(...flagsSection(flags).map(line => redent(line, 2))); | ||||||
} | ||||||
|
||||||
lines = lines.map(line => trimNewlines(line)); | ||||||
|
||||||
const content = lines.join(EOL).replace(/^\t+/gm, '').replace(/[\t ]+[\r\n]*$/gm, ''); | ||||||
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. Instead of the regexes here, couldn't you use |
||||||
return EOL + trimNewlines(redent(content, 2)) + EOL; | ||||||
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. Use |
||||||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
import test from 'ava'; | ||
import meow from '..'; | ||
|
||
const inputHelpText = ` | ||
Usage: unicorn [options] <file> | ||
|
||
Example: unicorn path/to/file.js | ||
`; | ||
|
||
test('when no arguments and options', t => { | ||
const cli = meow(); | ||
t.is(cli.help, ` | ||
CLI app helper | ||
|
||
Options: | ||
--help Show help | ||
`); | ||
}); | ||
|
||
test('when shortcut and no description', t => { | ||
const cli = meow(inputHelpText); | ||
t.is(cli.help, ` | ||
CLI app helper | ||
|
||
Usage: unicorn [options] <file> | ||
|
||
Example: unicorn path/to/file.js | ||
`); | ||
}); | ||
|
||
test('when no shortcut and description:false', t => { | ||
const cli = meow({description: false}); | ||
t.is(cli.help, ` | ||
Options: | ||
--help Show help | ||
`); | ||
}); | ||
|
||
test('when shortcut and description', t => { | ||
const cli = meow(inputHelpText, {description: 'A command for unicorns'}); | ||
t.is(cli.help, ` | ||
A command for unicorns | ||
|
||
Usage: unicorn [options] <file> | ||
|
||
Example: unicorn path/to/file.js | ||
`); | ||
}); | ||
|
||
test('when shortcut and description:false', t => { | ||
const cli = meow(inputHelpText, {description: false}); | ||
t.is(cli.help, ` | ||
Usage: unicorn [options] <file> | ||
|
||
Example: unicorn path/to/file.js | ||
`); | ||
}); | ||
|
||
test('when help:<string> and no description', t => { | ||
const cli = meow({help: inputHelpText}); | ||
t.is(cli.help, ` | ||
CLI app helper | ||
|
||
Usage: unicorn [options] <file> | ||
|
||
Example: unicorn path/to/file.js | ||
`); | ||
}); | ||
|
||
test('when help:<string> and description:false', t => { | ||
const cli = meow({help: inputHelpText, description: false}); | ||
t.is(cli.help, ` | ||
Usage: unicorn [options] <file> | ||
|
||
Example: unicorn path/to/file.js | ||
`); | ||
}); | ||
|
||
test('when help:<string> and description:<string>', t => { | ||
const cli = meow({help: inputHelpText, description: 'A command for unicorns'}); | ||
t.is(cli.help, ` | ||
A command for unicorns | ||
|
||
Usage: unicorn [options] <file> | ||
|
||
Example: unicorn path/to/file.js | ||
`); | ||
}); | ||
|
||
test('when description and flags', t => { | ||
const cli = meow({ | ||
description: inputHelpText, | ||
flags: { | ||
format: 'string', | ||
output: { | ||
type: 'string', | ||
alias: 'o' | ||
}, | ||
input: { | ||
type: 'string', | ||
default: 'stdin', | ||
description: 'Input file path' | ||
}, | ||
indent: { | ||
type: 'number', | ||
alias: 'i', | ||
default: 2, | ||
description: 'Indent level' | ||
}, | ||
verbose: { | ||
type: 'boolean', | ||
default: false, | ||
description: 'Turn on verbose mode' | ||
}, | ||
longLongOption: { | ||
type: 'string', | ||
alias: 'llo', | ||
default: 'none', | ||
description: 'A long long option.\nThis is the second line.' | ||
} | ||
} | ||
}); | ||
t.is(cli.help, ` | ||
Usage: unicorn [options] <file> | ||
|
||
Example: unicorn path/to/file.js | ||
|
||
Options: | ||
--format <string> | ||
--output, -o <string> | ||
--input <string> Input file path [default: stdin] | ||
--indent, -i <number> Indent level [default: 2] | ||
--verbose Turn on verbose mode [default: false] | ||
--long-long-option, -llo <string> A long long option. | ||
This is the second line. [default: none] | ||
--help Show help | ||
`); | ||
}); | ||
|
||
test('when no description and flags', t => { | ||
const cli = meow({ | ||
flags: { | ||
input: { | ||
type: 'string', | ||
description: 'Input file path' | ||
} | ||
} | ||
}); | ||
t.is(cli.help, ` | ||
CLI app helper | ||
|
||
Options: | ||
--input <string> Input file path | ||
--help Show help | ||
`); | ||
}); |
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.