From d55dfb4ebab3c10d6a81b71c259fb55e6c917b6f Mon Sep 17 00:00:00 2001 From: Matthew Adams Date: Thu, 15 Mar 2018 22:25:15 -0500 Subject: [PATCH] fix #80: add autoHelpFlags --- .gitignore | 1 + index.js | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ readme.md | 11 +++++++++- test.js | 22 +++++++++++++++++++- 4 files changed, 91 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 239ecff..fb45677 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules yarn.lock +.idea/ diff --git a/index.js b/index.js index 4673c43..dab214e 100644 --- a/index.js +++ b/index.js @@ -32,6 +32,7 @@ module.exports = (helpMessage, opts) => { input: 'string', help: helpMessage, autoHelp: true, + autoHelpFlags: true, autoVersion: true }, opts); @@ -62,6 +63,64 @@ module.exports = (helpMessage, opts) => { help = (description ? `\n ${description}\n` : '') + (help ? `\n${help}\n` : '\n'); + if (opts.autoHelpFlags && opts.flags) { + const options = Object.keys(opts.flags).reduce((accum, flag) => { + const o = opts.flags[flag]; + if (Object.prototype.hasOwnProperty.call(o, 'default')) { + o.default = o.default.toString(); + } + + const option = { + flag: '', + value: '', + desc: '' + }; + + if (flag.length === 1) { + o.alias = o.alias || flag; + } + + option.flag += o.alias ? `-${o.alias}, ` : ' '.repeat(4); + option.flag += `--${flag === '--' ? '' : flag}`; + accum.flagMaxLen = Math.max(accum.flagMaxLen, option.flag.length); + + if (flag === '--') { + option.value = 'arg(s)'; + } else if (o.type === 'boolean') { + option.value = '[true|false]'; + } else { + option.value = `${o.default ? '[' : ''}${o.default ? ']' : ''}`; + } + accum.valMaxLen = Math.max(accum.valMaxLen, option.value.length); + + if (flag === '--') { + option.desc = 'option/arg separator'; + } else { + if (o.description) { + option.desc += o.description; + } + if (o.default) { + option.desc += `${o.description ? '; ' : ''}default ${o.default}`; + } + } + + accum.entries.push(option); + return accum; + }, { + flagMaxLen: 0, + valMaxLen: 0, + entries: [] + }); + + if (options.entries.length > 0) { + help += redent(options.entries.map(it => + `${it.flag}${' '.repeat(options.flagMaxLen - it.flag.length + 2)}` + + `${it.value}${' '.repeat(options.valMaxLen - it.value.length + 2)}` + + it.desc + ).join('\n'), 2); + } + } + const showHelp = code => { console.log(help); process.exit(typeof code === 'number' ? code : 2); diff --git a/readme.md b/readme.md index 78f5a13..57790d1 100644 --- a/readme.md +++ b/readme.md @@ -94,6 +94,7 @@ The key is the flag name and the value is an object with any of: - `type`: Type of value. (Possible values: `string` `boolean`) - `alias`: Usually used to define a short flag alias. - `default`: Default value when the flag is not specified. +- `description`: Description to render when autogenerating flag help. Example: @@ -102,7 +103,8 @@ flags: { unicorn: { type: 'string', alias: 'u', - default: 'rainbow' + default: 'rainbow', + description: 'Unicorn name' } } ``` @@ -148,6 +150,13 @@ Default: `true` Automatically show the version text when the `--version` flag is present. Useful to set this value to `false` when a CLI manages child CLIs with their own version text. +##### autoHelpFlags + +Type: `boolean`
+Default: `true` + +Automatically show help on flags/switches. + ##### pkg Type: `Object`
diff --git a/test.js b/test.js index e6130ee..b280be9 100644 --- a/test.js +++ b/test.js @@ -11,6 +11,7 @@ test('return object', t => { Usage foo `, + autoHelpFlags: false, flags: { unicorn: {alias: 'u'}, meow: {default: 'dog'}, @@ -27,6 +28,25 @@ test('return object', t => { t.is(cli.help, indentString('\nCLI app helper\n\nUsage\n foo \n', 2)); }); +test('return object with arg help', t => { + const cli = m({ + argv: ['foo', '--foo-bar', '-u', 'cat', '--', 'unicorn', 'cake'], + flags: { + unicorn: {alias: 'u', description: 'Unicorn name'}, + meow: {default: 'dog', description: 'What to meow'}, + '--': true + } + }); + + t.is(cli.input[0], 'foo'); + t.true(cli.flags.fooBar); + t.is(cli.flags.meow, 'dog'); + t.is(cli.flags.unicorn, 'cat'); + t.deepEqual(cli.flags['--'], ['unicorn', 'cake']); + t.is(cli.pkg.name, 'meow'); + t.is(cli.help, indentString('\nCLI app helper\n\n-u, --unicorn Unicorn name\n --meow [] What to meow; default dog\n -- arg(s) option/arg separator', 2)); +}); + test('support help shortcut', t => { const cli = m(` unicorn @@ -47,7 +67,7 @@ test('spawn cli and not show version', async t => { test('spawn cli and show help screen', async t => { const {stdout} = await execa('./fixture.js', ['--help']); - t.is(stdout, indentString('\nCustom description\n\nUsage\n foo \n\n', 2)); + t.is(stdout, indentString('\nCustom description\n\nUsage\n foo \n\n-u, --unicorn \n --meow [] default dog\n --camelCaseOption [] default foo', 2)); }); test('spawn cli and not show help screen', async t => {