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
feat: rethink how options are inherited by commands #766
Changes from 11 commits
aca9d64
5218040
1a7e703
695e15f
0dd09ef
db25fb8
0c6a1ec
8de98a8
3d7759a
bae1d2d
4cff044
5e6a042
dadd300
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 |
---|---|---|
|
@@ -408,7 +408,7 @@ explicitly set. | |
|
||
If `key` is an array, interpret all the elements as booleans. | ||
|
||
.check(fn) | ||
.check(fn, [global=true]) | ||
---------- | ||
|
||
Check that certain conditions are met in the provided arguments. | ||
|
@@ -418,6 +418,9 @@ Check that certain conditions are met in the provided arguments. | |
If `fn` throws or returns a non-truthy value, show the thrown error, usage information, and | ||
exit. | ||
|
||
`global` indicates whether or not `check()` should be enabled both | ||
at the top-level and for each sub-command. | ||
|
||
<a name="choices"></a>.choices(key, choices) | ||
---------------------- | ||
|
||
|
@@ -551,14 +554,6 @@ yargs | |
.argv | ||
``` | ||
|
||
Note that commands will not automatically inherit configuration _or_ options | ||
of their parent context. This means you'll have to re-apply configuration | ||
if necessary, and make options global manually using the [global](#global) method. | ||
|
||
Additionally, the [`help`](#help) and [`version`](#version) | ||
options (if used) **always** apply globally, just like the | ||
[`.wrap()`](#wrap) configuration. | ||
|
||
`builder` can also be a function. This function is executed | ||
with a `yargs` instance, and can be used to provide _advanced_ command specific help: | ||
|
||
|
@@ -678,7 +673,7 @@ require('yargs') | |
console.log(`setting ${argv.key} to ${argv.value}`) | ||
} | ||
}) | ||
.demandCommand(1) | ||
.demandCommand() | ||
.help() | ||
.wrap(72) | ||
.argv | ||
|
@@ -824,7 +819,7 @@ cli.js: | |
#!/usr/bin/env node | ||
require('yargs') | ||
.commandDir('cmds') | ||
.demandCommand(1) | ||
.demandCommand() | ||
.help() | ||
.argv | ||
``` | ||
|
@@ -1112,9 +1107,9 @@ Options: | |
Missing required arguments: run, path | ||
``` | ||
|
||
<a name="demandCommand"></a>.demandCommand(min, [minMsg]) | ||
<a name="demandCommand"></a>.demandCommand([min=1], [minMsg]) | ||
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. Very awesome, I love this one |
||
------------------------------ | ||
.demandCommand(min, [max], [minMsg], [maxMsg]) | ||
.demandCommand([min=1], [max], [minMsg], [maxMsg]) | ||
------------------------------ | ||
|
||
Demand in context of commands. You can demand a minimum and a maximum number a user can have within your program, as well as provide corresponding error messages if either of the demands is not met. | ||
|
@@ -1134,7 +1129,9 @@ require('yargs') | |
.help() | ||
.argv | ||
``` | ||
|
||
which will provide the following output: | ||
|
||
```bash | ||
Commands: | ||
configure <key> [value] Set a config variable [aliases: config, cfg] | ||
|
@@ -1293,7 +1290,7 @@ require('yargs') | |
|
||
Outputs the same completion choices as `./test.js --foo`<kbd>TAB</kbd>: `--foobar` and `--foobaz` | ||
|
||
<a name="global"></a>.global(globals) | ||
<a name="global"></a>.global(globals, [global=true]) | ||
------------ | ||
|
||
Indicate that an option (or group of options) should not be reset when a command | ||
|
@@ -1303,11 +1300,13 @@ is executed, as an example: | |
var argv = require('yargs') | ||
.option('a', { | ||
alias: 'all', | ||
default: true | ||
default: true, | ||
global: false | ||
}) | ||
.option('n', { | ||
alias: 'none', | ||
default: true | ||
default: true, | ||
global: false | ||
}) | ||
.command('foo', 'foo command', function (yargs) { | ||
return yargs.option('b', { | ||
|
@@ -1322,7 +1321,7 @@ var argv = require('yargs') | |
If the `foo` command is executed the `all` option will remain, but the `none` | ||
option will have been eliminated. | ||
|
||
`help`, `version`, and `completion` options default to being global. | ||
Options default to being global. | ||
|
||
<a name="group"></a>.group(key(s), groupName) | ||
-------------------- | ||
|
@@ -1778,12 +1777,15 @@ Specify --help for available options | |
Specifies either a single option key (string), or an array of options. | ||
If any of the options is present, yargs validation is skipped. | ||
|
||
.strict() | ||
.strict([global=true]) | ||
--------- | ||
|
||
Any command-line argument given that is not demanded, or does not have a | ||
corresponding description, will be reported as an error. | ||
|
||
`global` indicates whether or not `strict()` should be enabled both | ||
at the top-level and for each sub-command. | ||
|
||
<a name="string"></a>.string(key) | ||
------------ | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -131,47 +131,62 @@ module.exports = function (yargs, usage, validation) { | |
} | ||
|
||
self.runCommand = function (command, yargs, parsed) { | ||
var argv = parsed.argv | ||
var aliases = parsed.aliases | ||
var commandHandler = handlers[command] || handlers[aliasMap[command]] | ||
var innerArgv = argv | ||
var currentContext = yargs.getContext() | ||
var numFiles = currentContext.files.length | ||
var parentCommands = currentContext.commands.slice() | ||
|
||
// what does yargs look like after the buidler is run? | ||
var innerArgv = parsed.argv | ||
var innerYargs = null | ||
|
||
currentContext.commands.push(command) | ||
if (typeof commandHandler.builder === 'function') { | ||
// a function can be provided, which builds | ||
// up a yargs chain and possibly returns it. | ||
innerArgv = commandHandler.builder(yargs.reset(parsed.aliases)) | ||
innerYargs = commandHandler.builder(yargs.reset(parsed.aliases)) | ||
// if the builder function did not yet parse argv with reset yargs | ||
// and did not explicitly set a usage() string, then apply the | ||
// original command string as usage() for consistent behavior with | ||
// options object below | ||
// options object below. | ||
if (yargs.parsed === false) { | ||
if (typeof yargs.getUsageInstance().getUsage() === 'undefined') { | ||
yargs.usage('$0 ' + (parentCommands.length ? parentCommands.join(' ') + ' ' : '') + commandHandler.original) | ||
} | ||
innerArgv = innerArgv ? innerArgv.argv : yargs.argv | ||
innerArgv = innerYargs ? innerYargs._parseArgs(null, null, true) : yargs._parseArgs(null, null, true) | ||
} else { | ||
innerArgv = yargs.parsed.argv | ||
} | ||
|
||
if (innerYargs && yargs.parsed === false) aliases = innerYargs.parsed.aliases | ||
else aliases = yargs.parsed.aliases | ||
} else if (typeof commandHandler.builder === 'object') { | ||
// as a short hand, an object can instead be provided, specifying | ||
// the options that a command takes. | ||
innerArgv = yargs.reset(parsed.aliases) | ||
innerArgv.usage('$0 ' + (parentCommands.length ? parentCommands.join(' ') + ' ' : '') + commandHandler.original) | ||
innerYargs = yargs.reset(parsed.aliases) | ||
innerYargs.usage('$0 ' + (parentCommands.length ? parentCommands.join(' ') + ' ' : '') + commandHandler.original) | ||
Object.keys(commandHandler.builder).forEach(function (key) { | ||
innerArgv.option(key, commandHandler.builder[key]) | ||
innerYargs.option(key, commandHandler.builder[key]) | ||
}) | ||
innerArgv = innerArgv.argv | ||
innerArgv = innerYargs._parseArgs(null, null, true) | ||
aliases = innerYargs.parsed.aliases | ||
} | ||
|
||
if (!yargs._hasOutput()) populatePositionals(commandHandler, innerArgv, currentContext, yargs) | ||
|
||
if (commandHandler.handler && !yargs._hasOutput()) { | ||
commandHandler.handler(innerArgv) | ||
} | ||
|
||
// we apply validation post-hoc, to so that custom | ||
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. tiny typo here |
||
// checks get passed populated positional arguments. | ||
yargs._runValidation(innerArgv, aliases) | ||
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 little bit of refactoring in this method was to hold off on applying validation until after we've populated variadic values -- this was largely so that one can provide a |
||
|
||
currentContext.commands.pop() | ||
numFiles = currentContext.files.length - numFiles | ||
if (numFiles > 0) currentContext.files.splice(numFiles * -1, numFiles) | ||
|
||
return innerArgv | ||
} | ||
|
||
|
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.
nit: I think
reads a little clearer, since I don't have to think about negations and my pyrate brain don't hurt.