diff --git a/Readme.md b/Readme.md index 115ff995f..f89088a75 100644 --- a/Readme.md +++ b/Readme.md @@ -224,6 +224,12 @@ You may specify custom flags by passing an additional parameter to the `version` program.version('0.0.1', '-v, --version'); ``` +You can also override the help description for the version command. + +```js +program.version('0.0.1', '-v, --version', 'output the current version'); +``` + ## Command-specific options @@ -433,6 +439,15 @@ function make_red(txt) { } ``` +## .helpOption(flags, description) + + Override the default help flags and description. + +```js +program + .helpOption('-e, --HELP', 'read more information'); +``` + ## .help(cb) Output help information and exit immediately. diff --git a/examples/custom-help-description b/examples/custom-help-description new file mode 100644 index 000000000..9892a912a --- /dev/null +++ b/examples/custom-help-description @@ -0,0 +1,21 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('../'); + +program + .helpOption('-c, --HELP', 'custom help message') + .option('-s, --sessions', 'add session support') + .option('-t, --template ', 'specify template engine (jade|ejs) [jade]', 'jade'); + +program + .command('child') + .option('--gender', 'specific gender of child') + .action((cmd) => { + console.log('Childsubcommand...'); + }); + +program.parse(process.argv); diff --git a/examples/custom-version b/examples/custom-version new file mode 100644 index 000000000..11043a06f --- /dev/null +++ b/examples/custom-version @@ -0,0 +1,13 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('../'); + +program + .version('0.0.1', '-v, --VERSION', 'new version message') + .option('-s, --sessions', 'add session support') + .option('-t, --template ', 'specify template engine (jade|ejs) [jade]', 'jade') + .parse(process.argv); diff --git a/index.js b/index.js index da594e9cd..8fd53074b 100644 --- a/index.js +++ b/index.js @@ -103,6 +103,11 @@ function Command(name) { this._allowUnknownOption = false; this._args = []; this._name = name || ''; + + this._helpFlags = '-h, --help'; + this._helpDescription = 'output usage information'; + this._helpShortFlag = '-h'; + this._helpLongFlag = '--help'; } /** @@ -182,6 +187,10 @@ Command.prototype.command = function(name, desc, opts) { if (opts.isDefault) this.defaultExecutable = cmd._name; } cmd._noHelp = !!opts.noHelp; + cmd._helpFlags = this._helpFlags; + cmd._helpDescription = this._helpDescription; + cmd._helpShortFlag = this._helpShortFlag; + cmd._helpLongFlag = this._helpLongFlag; this.commands.push(cmd); cmd.parseExpectedArgs(args); cmd.parent = this; @@ -466,7 +475,7 @@ Command.prototype.parse = function(argv) { // github-style sub-commands with no sub-command if (this.executables && argv.length < 3 && !this.defaultExecutable) { // this user needs help - argv.push('--help'); + argv.push(this._helpLongFlag); } // process argv @@ -519,7 +528,7 @@ Command.prototype.executeSubCommand = function(argv, args, unknown) { // --help if (args[0] === 'help') { args[0] = args[1]; - args[1] = '--help'; + args[1] = this._helpLongFlag; } // executable @@ -846,17 +855,21 @@ Command.prototype.variadicArgNotLast = function(name) { * This method auto-registers the "-V, --version" flag * which will print the version number when passed. * + * You can optionally supply the flags and description to override the defaults. + * * @param {String} str * @param {String} [flags] + * @param {String} [description] * @return {Command} for chaining * @api public */ -Command.prototype.version = function(str, flags) { +Command.prototype.version = function(str, flags, description) { if (arguments.length === 0) return this._version; this._version = str; flags = flags || '-V, --version'; - var versionOption = new Option(flags, 'output the version number'); + description = description || 'output the version number'; + var versionOption = new Option(flags, description); this._versionOptionName = versionOption.long.substr(2) || 'version'; this.options.push(versionOption); this.on('option:' + this._versionOptionName, function() { @@ -990,8 +1003,9 @@ Command.prototype.largestCommandLength = function() { Command.prototype.largestOptionLength = function() { var options = [].slice.call(this.options); options.push({ - flags: '-h, --help' + flags: this._helpFlags }); + return options.reduce(function(max, option) { return Math.max(max, option.flags.length); }, 0); @@ -1048,7 +1062,7 @@ Command.prototype.optionHelp = function() { return this.options.map(function(option) { return pad(option.flags, width) + ' ' + option.description + ((option.bool && option.defaultValue !== undefined) ? ' (default: ' + JSON.stringify(option.defaultValue) + ')' : ''); - }).concat([pad('-h, --help', width) + ' ' + 'output usage information']) + }).concat([pad(this._helpFlags, width) + ' ' + this._helpDescription]) .join('\n'); }; @@ -1129,7 +1143,10 @@ Command.prototype.helpInformation = function() { }; /** - * Output help information for this command + * Output help information for this command. + * + * When listener(s) are available for the helpLongFlag + * those callbacks are invoked. * * @api public */ @@ -1145,12 +1162,36 @@ Command.prototype.outputHelp = function(cb) { throw new Error('outputHelp callback must return a string or a Buffer'); } process.stdout.write(cbOutput); - this.emit('--help'); + this.emit(this._helpLongFlag); +}; + +/** + * You can pass in flags and a description to override the help + * flags and help description for your command. + * + * @param {String} [flags] + * @param {String} [description] + * @return {Command} + * @api public + */ + +Command.prototype.helpOption = function(flags, description) { + this._helpFlags = flags || this._helpFlags; + this._helpDescription = description || this._helpDescription; + + var splitFlags = this._helpFlags.split(/[ ,|]+/); + + if (splitFlags.length > 1) this._helpShortFlag = splitFlags.shift(); + + this._helpLongFlag = splitFlags.shift(); + + return this; }; /** * Output help information and exit. * + * @param {Function} [cb] * @api public */ @@ -1197,8 +1238,9 @@ function pad(str, width) { function outputHelpIfNecessary(cmd, options) { options = options || []; + for (var i = 0; i < options.length; i++) { - if (options[i] === '--help' || options[i] === '-h') { + if (options[i] === cmd._helpLongFlag || options[i] === cmd._helpShortFlag) { cmd.outputHelp(); process.exit(0); } diff --git a/test/test.command.helpInformation.custom.js b/test/test.command.helpInformation.custom.js new file mode 100644 index 000000000..50ad9ab91 --- /dev/null +++ b/test/test.command.helpInformation.custom.js @@ -0,0 +1,46 @@ +var program = require('../') + , sinon = require('sinon').sandbox.create() + , should = require('should'); + +sinon.stub(process, 'exit'); +sinon.stub(process.stdout, 'write'); + +program.helpOption('-c, --HELP', 'custom help output'); +program.command('somecommand'); +program.command('anothercommand [options]'); + +var expectedHelpInformation = [ + 'Usage: [options] [command]', + '', + 'Options:', + ' -c, --HELP custom help output', + '', + 'Commands:', + ' somecommand', + ' anothercommand [options]', + '' +].join('\n'); + +program.helpInformation().should.equal(expectedHelpInformation); + +// Test arguments +var expectedCommandHelpInformation = [ + 'Usage: test [options] [command]', + '', + 'Options:', + ' -c, --HELP custom help output', + '', + 'Commands:', + ' somecommand', + ' anothercommand [options]', + '' +].join('\n'); + +program.parse(['node', 'test', '--HELP']); + +process.stdout.write.called.should.equal(true); + +var output = process.stdout.write.args[0]; +output[0].should.equal(expectedCommandHelpInformation); + +sinon.restore(); diff --git a/test/test.command.helpSubCommand.customFlags.js b/test/test.command.helpSubCommand.customFlags.js new file mode 100644 index 000000000..c2d7575d0 --- /dev/null +++ b/test/test.command.helpSubCommand.customFlags.js @@ -0,0 +1,65 @@ +var program = require('../'), + sinon = require('sinon').sandbox.create(), + should = require('should'); + +sinon.stub(process, 'exit'); +sinon.stub(process.stdout, 'write'); + +// Test that subcommands inherit the help flags +// but can also override help flags +program + .helpOption('-i, --ihelp', 'foo foo'); + +program + .command('child') + .option('--gender', 'specific gender of child') + .action((cmd) => { + console.log('Childsubcommand...'); + }); + +program + .command('family') + .helpOption('-h, --help') + .action((cmd) => { + console.log('Familysubcommand...'); + }); + +// Test arguments +var expectedCommandHelpInformation = [ + 'Usage: child [options]', + '', + 'Options:', + ' --gender specific gender of child', + ' -i, --ihelp foo foo', + '' +].join('\n'); + +program.parse(['node', 'test', 'child', '-i']); + +process.stdout.write.called.should.equal(true); + +var output = process.stdout.write.args[0]; +output[0].should.equal(expectedCommandHelpInformation); + +// Test other command +sinon.restore(); + +sinon.stub(process, 'exit'); +sinon.stub(process.stdout, 'write'); + +var expectedFamilyCommandHelpInformation = [ + 'Usage: family [options]', + '', + 'Options:', + ' -h, --help foo foo', + '' +].join('\n'); + +program.parse(['node', 'test', 'family', '-h']); + +process.stdout.write.called.should.equal(true); + +var output2 = process.stdout.write.args[0]; +output2[0].should.equal(expectedFamilyCommandHelpInformation); + +sinon.restore(); diff --git a/test/test.options.version.customHelpDescription.js b/test/test.options.version.customHelpDescription.js new file mode 100644 index 000000000..7471e7310 --- /dev/null +++ b/test/test.options.version.customHelpDescription.js @@ -0,0 +1,22 @@ +var program = require('../') + , sinon = require('sinon').sandbox.create() + , should = require('should'); + +program.version('1.0.0', undefined, 'custom version output'); +program.command('somecommand'); +program.command('anothercommand [options]'); + +var expectedHelpInformation = [ + 'Usage: [options] [command]', + '', + 'Options:', + ' -V, --version custom version output', + ' -h, --help output usage information', + '', + 'Commands:', + ' somecommand', + ' anothercommand [options]', + '' +].join('\n'); + +program.helpInformation().should.equal(expectedHelpInformation); diff --git a/typings/index.d.ts b/typings/index.d.ts index bcda2771e..d2a5332a0 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -36,16 +36,15 @@ declare namespace local { constructor(name?: string); /** - * Set the program version to `str`. + * Set the program version to `str`. * * This method auto-registers the "-V, --version" flag * which will print the version number when passed. + * + * You can optionally supply the flags and description to override the defaults. * - * @param {string} str - * @param {string} [flags] - * @returns {Command} for chaining */ - version(str: string, flags?: string): Command; + version(str: string, flags?: string, description?: string): Command; /** * Add command `name`. @@ -268,13 +267,21 @@ declare namespace local { /** * Output help information for this command. * + * When listener(s) are available for the helpLongFlag + * those callbacks are invoked. + * * @param {(str: string) => string} [cb] */ outputHelp(cb?: (str: string) => string): void; - /** Output help information and exit. - * - * @param {(str: string) => string} [cb] + /** + * You can pass in flags and a description to override the help + * flags and help description for your command. + */ + helpOption(flags?: string, description?: string): Command; + + /** + * Output help information and exit. */ help(cb?: (str: string) => string): never; }