diff --git a/README.md b/README.md
index 8835faee5..8dfdf5a95 100644
--- a/README.md
+++ b/README.md
@@ -1099,15 +1099,31 @@ var yargs = require('yargs')(['--help'])
Options:
--help Show help [boolean]
-.help([option, [description]])
-------------------------------
+.help()
+-----------------------------------------
+.help([option | boolean])
+-----------------------------------------
+.help([option, [description | boolean]])
+-----------------------------------------
+.help([option, [description, [boolean]]])
+-----------------------------------------
+
+Add an option (e.g. `--help`) and implicit command that displays the usage
+string and exits the process.
-Add an option (e.g. `--help`) that displays the usage string and exits the
-process. If present, the `description` parameter customizes the description of
+If present, the `description` parameter customizes the description of
the help option in the usage string.
-If invoked without parameters, `.help()` will make `--help` the option to trigger
-help output.
+If a boolean argument is provided, it will enable or disable the use of an
+implicit command. The implicit command is enabled by default, but it can be
+disabled by passing `false`.
+
+Note that any multi-char aliases (e.g. `help`) used for the help option will
+also be used for the implicit command. If there are no multi-char aliases (e.g.
+`h`), then all single-char aliases will be used for the command.
+
+If invoked without parameters, `.help()` will use `--help` as the option and
+`help` as the implicit command to trigger help output.
Example:
diff --git a/test/command.js b/test/command.js
index 96ad4c4ee..ae126c2f1 100644
--- a/test/command.js
+++ b/test/command.js
@@ -470,6 +470,83 @@ describe('Command', function () {
})
})
+ describe('help command', function () {
+ it('displays command help appropriately', function () {
+ var sub = {
+ command: 'sub',
+ desc: 'Run the subcommand',
+ builder: {},
+ handler: function (argv) {}
+ }
+
+ var cmd = {
+ command: 'cmd ',
+ desc: 'Try a command',
+ builder: function (yargs) {
+ return yargs.command(sub)
+ },
+ handler: function (argv) {}
+ }
+
+ var helpCmd = checkOutput(function () {
+ return yargs('help cmd')
+ .help().wrap(null)
+ .command(cmd)
+ .argv
+ }, [ './command' ])
+
+ var cmdHelp = checkOutput(function () {
+ return yargs('cmd help')
+ .help().wrap(null)
+ .command(cmd)
+ .argv
+ }, [ './command' ])
+
+ var helpCmdSub = checkOutput(function () {
+ return yargs('help cmd sub')
+ .help().wrap(null)
+ .command(cmd)
+ .argv
+ }, [ './command' ])
+
+ var cmdHelpSub = checkOutput(function () {
+ return yargs('cmd help sub')
+ .help().wrap(null)
+ .command(cmd)
+ .argv
+ }, [ './command' ])
+
+ var cmdSubHelp = checkOutput(function () {
+ return yargs('cmd sub help')
+ .help().wrap(null)
+ .command(cmd)
+ .argv
+ }, [ './command' ])
+
+ var expectedCmd = [
+ './command cmd ',
+ 'Commands:',
+ ' sub Run the subcommand',
+ 'Options:',
+ ' --help Show help [boolean]',
+ ''
+ ]
+
+ var expectedSub = [
+ './command cmd sub',
+ 'Options:',
+ ' --help Show help [boolean]',
+ ''
+ ]
+
+ helpCmd.logs.join('\n').split(/\n+/).should.deep.equal(expectedCmd)
+ cmdHelp.logs.join('\n').split(/\n+/).should.deep.equal(expectedCmd)
+ helpCmdSub.logs.join('\n').split(/\n+/).should.deep.equal(expectedSub)
+ cmdHelpSub.logs.join('\n').split(/\n+/).should.deep.equal(expectedSub)
+ cmdSubHelp.logs.join('\n').split(/\n+/).should.deep.equal(expectedSub)
+ })
+ })
+
// addresses https://github.com/yargs/yargs/issues/514.
it('respects order of positional arguments when matching commands', function () {
var output = []
diff --git a/test/yargs.js b/test/yargs.js
index aacf7b8bb..32c6a9179 100644
--- a/test/yargs.js
+++ b/test/yargs.js
@@ -686,6 +686,11 @@ describe('yargs dsl tests', function () {
a1.why.should.equal('hello world')
a2.why.should.equal('hello world')
})
+
+ it('ignores implicit help command (with short-circuit)', function () {
+ var parsed = yargs.help().parse('help', true)
+ parsed._.should.deep.equal(['help'])
+ })
})
describe('config', function () {
@@ -1008,6 +1013,262 @@ describe('yargs dsl tests', function () {
argv.koala.should.equal(true)
})
})
+
+ describe('.help()', function () {
+ it('enables `--help` option and `help` command without arguments', function () {
+ var option = checkOutput(function () {
+ return yargs('--help')
+ .help()
+ .wrap(null)
+ .argv
+ })
+ var command = checkOutput(function () {
+ return yargs('help')
+ .help()
+ .wrap(null)
+ .argv
+ })
+ var expected = [
+ 'Options:',
+ ' --help Show help [boolean]',
+ ''
+ ]
+ option.logs[0].split('\n').should.deep.equal(expected)
+ command.logs[0].split('\n').should.deep.equal(expected)
+ })
+
+ it('enables `--help` option and `help` command with `true` argument', function () {
+ var option = checkOutput(function () {
+ return yargs('--help')
+ .help(true)
+ .wrap(null)
+ .argv
+ })
+ var command = checkOutput(function () {
+ return yargs('help')
+ .help(true)
+ .wrap(null)
+ .argv
+ })
+ var expected = [
+ 'Options:',
+ ' --help Show help [boolean]',
+ ''
+ ]
+ option.logs[0].split('\n').should.deep.equal(expected)
+ command.logs[0].split('\n').should.deep.equal(expected)
+ })
+
+ it('enables only `--help` option with `false` argument', function () {
+ var option = checkOutput(function () {
+ return yargs('--help')
+ .help(false)
+ .wrap(null)
+ .argv
+ })
+ var command = checkOutput(function () {
+ return yargs('help')
+ .help(false)
+ .wrap(null)
+ .argv
+ })
+ option.logs[0].split('\n').should.deep.equal([
+ 'Options:',
+ ' --help Show help [boolean]',
+ ''
+ ])
+ command.result.should.have.property('_').and.deep.equal(['help'])
+ })
+
+ it('enables given string as help option and command with string argument', function () {
+ var option = checkOutput(function () {
+ return yargs('--info')
+ .help('info')
+ .wrap(null)
+ .argv
+ })
+ var command = checkOutput(function () {
+ return yargs('info')
+ .help('info')
+ .wrap(null)
+ .argv
+ })
+ var helpOption = checkOutput(function () {
+ return yargs('--help')
+ .help('info')
+ .wrap(null)
+ .argv
+ })
+ var expected = [
+ 'Options:',
+ ' --info Show help [boolean]',
+ ''
+ ]
+ option.logs[0].split('\n').should.deep.equal(expected)
+ command.logs[0].split('\n').should.deep.equal(expected)
+ helpOption.result.should.have.property('help').and.be.true
+ })
+
+ it('enables given string as help option and command with string argument and `true` argument', function () {
+ var option = checkOutput(function () {
+ return yargs('--info')
+ .help('info', true)
+ .wrap(null)
+ .argv
+ })
+ var command = checkOutput(function () {
+ return yargs('info')
+ .help('info', true)
+ .wrap(null)
+ .argv
+ })
+ var helpOption = checkOutput(function () {
+ return yargs('--help')
+ .help('info', true)
+ .wrap(null)
+ .argv
+ })
+ var expected = [
+ 'Options:',
+ ' --info Show help [boolean]',
+ ''
+ ]
+ option.logs[0].split('\n').should.deep.equal(expected)
+ command.logs[0].split('\n').should.deep.equal(expected)
+ helpOption.result.should.have.property('help').and.be.true
+ })
+
+ it('enables given string as help option only with string argument and `false` argument', function () {
+ var option = checkOutput(function () {
+ return yargs('--info')
+ .help('info', false)
+ .wrap(null)
+ .argv
+ })
+ var command = checkOutput(function () {
+ return yargs('info')
+ .help('info', false)
+ .wrap(null)
+ .argv
+ })
+ option.logs[0].split('\n').should.deep.equal([
+ 'Options:',
+ ' --info Show help [boolean]',
+ ''
+ ])
+ command.result.should.have.property('_').and.deep.equal(['info'])
+ })
+
+ it('enables given string as help option and command with custom description with two string arguments', function () {
+ var option = checkOutput(function () {
+ return yargs('--info')
+ .help('info', 'Display info')
+ .wrap(null)
+ .argv
+ })
+ var command = checkOutput(function () {
+ return yargs('info')
+ .help('info', 'Display info')
+ .wrap(null)
+ .argv
+ })
+ var expected = [
+ 'Options:',
+ ' --info Display info [boolean]',
+ ''
+ ]
+ option.logs[0].split('\n').should.deep.equal(expected)
+ command.logs[0].split('\n').should.deep.equal(expected)
+ })
+
+ it('enables given string as help option and command with custom description with two string arguments and `true` argument', function () {
+ var option = checkOutput(function () {
+ return yargs('--info')
+ .help('info', 'Display info', true)
+ .wrap(null)
+ .argv
+ })
+ var command = checkOutput(function () {
+ return yargs('info')
+ .help('info', 'Display info', true)
+ .wrap(null)
+ .argv
+ })
+ var expected = [
+ 'Options:',
+ ' --info Display info [boolean]',
+ ''
+ ]
+ option.logs[0].split('\n').should.deep.equal(expected)
+ command.logs[0].split('\n').should.deep.equal(expected)
+ })
+
+ it('enables given string as help option only and custom description with two string arguments and `false` argument', function () {
+ var option = checkOutput(function () {
+ return yargs('--info')
+ .help('info', 'Display info', false)
+ .wrap(null)
+ .argv
+ })
+ var command = checkOutput(function () {
+ return yargs('info')
+ .help('info', 'Display info', false)
+ .wrap(null)
+ .argv
+ })
+ option.logs[0].split('\n').should.deep.equal([
+ 'Options:',
+ ' --info Display info [boolean]',
+ ''
+ ])
+ command.result.should.have.property('_').and.deep.equal(['info'])
+ })
+ })
+
+ describe('.help() with .alias()', function () {
+ it('uses multi-char (but not single-char) help alias as command', function () {
+ var info = checkOutput(function () {
+ return yargs('info')
+ .help().alias('h', 'help').alias('h', 'info')
+ .wrap(null)
+ .argv
+ })
+ var h = checkOutput(function () {
+ return yargs('h')
+ .help().alias('h', 'help').alias('h', 'info')
+ .wrap(null)
+ .argv
+ })
+ info.logs[0].split('\n').should.deep.equal([
+ 'Options:',
+ ' -h, --help, --info Show help [boolean]',
+ ''
+ ])
+ h.result.should.have.property('_').and.deep.equal(['h'])
+ })
+
+ it('uses single-char help alias as command if there are no multi-char aliases', function () {
+ var h = checkOutput(function () {
+ return yargs('h')
+ .help('h').alias('h', '?')
+ .wrap(null)
+ .argv
+ })
+ var q = checkOutput(function () {
+ return yargs('?')
+ .help('h').alias('h', '?')
+ .wrap(null)
+ .argv
+ })
+ var expected = [
+ 'Options:',
+ ' -h, -? Show help [boolean]',
+ ''
+ ]
+ h.logs[0].split('\n').should.deep.equal(expected)
+ q.logs[0].split('\n').should.deep.equal(expected)
+ })
+ })
})
describe('yargs context', function () {
diff --git a/yargs.js b/yargs.js
index ed6fd5685..3377b1ddf 100644
--- a/yargs.js
+++ b/yargs.js
@@ -520,12 +520,33 @@ function Yargs (processArgs, cwd, parentRequire) {
}
var helpOpt = null
- self.addHelpOpt = self.help = function (opt, msg) {
- opt = opt || 'help'
- helpOpt = opt
- self.boolean(opt)
- self.global(opt)
- self.describe(opt, msg || usage.deferY18nLookup('Show help'))
+ var useHelpOptAsCommand = false // a call to .help() will enable this
+ self.addHelpOpt = self.help = function (opt, msg, addImplicitCmd) {
+ // argument shuffle
+ if (arguments.length === 0) {
+ useHelpOptAsCommand = true
+ } else if (arguments.length === 1) {
+ if (typeof opt === 'boolean') {
+ useHelpOptAsCommand = opt
+ opt = null
+ } else {
+ useHelpOptAsCommand = true
+ }
+ } else if (arguments.length === 2) {
+ if (typeof msg === 'boolean') {
+ useHelpOptAsCommand = msg
+ msg = null
+ } else {
+ useHelpOptAsCommand = true
+ }
+ } else {
+ useHelpOptAsCommand = Boolean(addImplicitCmd)
+ }
+ // use arguments, fallback to defaults for opt and msg
+ helpOpt = opt || 'help'
+ self.boolean(helpOpt)
+ self.global(helpOpt)
+ self.describe(helpOpt, msg || usage.deferY18nLookup('Show help'))
return self
}
@@ -654,22 +675,45 @@ function Yargs (processArgs, cwd, parentRequire) {
return argv
}
- // if there's a handler associated with a
- // command defer processing to it.
- var handlerKeys = command.getCommands()
- for (var i = 0, cmd; (cmd = argv._[i]) !== undefined; i++) {
- if (~handlerKeys.indexOf(cmd) && cmd !== completionCommand) {
- setPlaceholderKeys(argv)
- return command.runCommand(cmd, self, parsed)
+ if (argv._.length) {
+ // check for helpOpt in argv._ before running commands
+ // assumes helpOpt must be valid if useHelpOptAsCommand is true
+ if (useHelpOptAsCommand) {
+ // consider any multi-char helpOpt alias as a valid help command
+ // unless all helpOpt aliases are single-char
+ // note that parsed.aliases is a normalized bidirectional map :)
+ var helpCmds = [helpOpt].concat(aliases[helpOpt])
+ var multiCharHelpCmds = helpCmds.filter(function (k) {
+ return k.length > 1
+ })
+ if (multiCharHelpCmds.length) helpCmds = multiCharHelpCmds
+ // look for and strip any helpCmds from argv._
+ argv._ = argv._.filter(function (cmd) {
+ if (~helpCmds.indexOf(cmd)) {
+ argv[helpOpt] = true
+ return false
+ }
+ return true
+ })
+ }
+
+ // if there's a handler associated with a
+ // command defer processing to it.
+ var handlerKeys = command.getCommands()
+ for (var i = 0, cmd; (cmd = argv._[i]) !== undefined; i++) {
+ if (~handlerKeys.indexOf(cmd) && cmd !== completionCommand) {
+ setPlaceholderKeys(argv)
+ return command.runCommand(cmd, self, parsed)
+ }
}
- }
- // generate a completion script for adding to ~/.bashrc.
- if (completionCommand && ~argv._.indexOf(completionCommand) && !argv[completion.completionKey]) {
- if (exitProcess) setBlocking(true)
- self.showCompletionScript()
- if (exitProcess) {
- process.exit(0)
+ // generate a completion script for adding to ~/.bashrc.
+ if (completionCommand && ~argv._.indexOf(completionCommand) && !argv[completion.completionKey]) {
+ if (exitProcess) setBlocking(true)
+ self.showCompletionScript()
+ if (exitProcess) {
+ process.exit(0)
+ }
}
}