forked from tj/commander.js
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from shadowspawn/feature/more-about-options-fork
Rework
- Loading branch information
Showing
2 changed files
with
220 additions
and
149 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,220 @@ | ||
# Options taking varying numbers of option-arguments | ||
|
||
The README covers declaring and using options, and mostly parsing will work the way you and your users expect. This page covers some special cases | ||
and subtle issues in depth. | ||
|
||
- [Options taking varying numbers of option-arguments](#options-taking-varying-numbers-of-option-arguments) | ||
- [Parsing ambiguity](#parsing-ambiguity) | ||
- [Alternative: Make `--` part of your syntax](#alternative-make----part-of-your-syntax) | ||
- [Alternative: Put options last](#alternative-put-options-last) | ||
- [Alternative: Use options instead of command-arguments](#alternative-use-options-instead-of-command-arguments) | ||
- [Combining short options, and options taking arguments](#combining-short-options-and-options-taking-arguments) | ||
- [Combining short options as if boolean](#combining-short-options-as-if-boolean) | ||
- [Terminology](#terminology) | ||
|
||
Certain options take a varying number of arguments: | ||
|
||
```js | ||
program | ||
.option('-c, --compress [percentage]') // 0 or 1 | ||
.option('--preprocess <file...>') // 1 or more | ||
.option('--test [name...]') // 0 or more | ||
``` | ||
|
||
This page uses examples with options taking 0 or 1 arguments, but the discussions also apply to variadic options taking more arguments. | ||
|
||
## Parsing ambiguity | ||
|
||
There is a potential downside to be aware of. If a command has both | ||
command-arguments and options with varying option-arguments, this introduces a parsing ambiguity which may affect the user of your program. | ||
Commander looks for option-arguments first, but the user may | ||
intend the argument following the option as a command or command-argument. | ||
|
||
```js | ||
program | ||
.name('cook') | ||
.arguments('[technique]') | ||
.option('-i, --ingredient [ingredient]', 'add cheese or given ingredient') | ||
.action((technique, options) => { | ||
console.log(`technique: ${technique}`); | ||
const ingredient = (options.ingredient === true) ? 'cheese' : options.ingredient; | ||
console.log(`ingredient: ${ingredient}`); | ||
}); | ||
|
||
program.parse(); | ||
``` | ||
|
||
```sh | ||
$ cook scrambled | ||
technique: scrambled | ||
ingredient: undefined | ||
|
||
$ cook -i | ||
technique: undefined | ||
ingredient: cheese | ||
|
||
$ cook -i egg | ||
technique: undefined | ||
ingredient: egg | ||
|
||
$ cook -i scrambled # oops | ||
technique: undefined | ||
ingredient: scrambled | ||
``` | ||
|
||
The explicit way to resolve this is use `--` to indicate the end of the options and option-arguments: | ||
|
||
```sh | ||
$ node cook.js -i -- egg | ||
technique: egg | ||
ingredient: cheese | ||
``` | ||
|
||
If you want to avoid your users needing to learn when to use `--`, there are a few approaches you could take. | ||
|
||
### Alternative: Make `--` part of your syntax | ||
|
||
Rather than trying to teach your users what `--` does, you could just make it part of your syntax. | ||
|
||
```js | ||
program.usage('[options] -- [technique]'); | ||
``` | ||
|
||
```sh | ||
$ cook --help | ||
Usage: cook [options] -- [technique] | ||
|
||
Options: | ||
-i, --ingredient [ingredient] add cheese or given ingredient | ||
-h, --help display help for command | ||
|
||
$ cook -- scrambled | ||
technique: scrambled | ||
ingredient: undefined | ||
|
||
$ cook -i -- scrambled | ||
technique: scrambled | ||
ingredient: cheese | ||
``` | ||
|
||
### Alternative: Put options last | ||
|
||
Commander follows the GNU convention for parsing and allows options before or after the command-arguments, or intermingled. | ||
So by putting the options last, the command-arguments do not get confused with the option-arguments. | ||
|
||
```js | ||
program.usage('[technique] [options]'); | ||
``` | ||
|
||
```sh | ||
$ cook --help | ||
Usage: cook [technique] [options] | ||
|
||
Options: | ||
-i, --ingredient [ingredient] add cheese or given ingredient | ||
-h, --help display help for command | ||
|
||
$ node cook.js scrambled -i | ||
technique: scrambled | ||
ingredient: cheese | ||
``` | ||
|
||
### Alternative: Use options instead of command-arguments | ||
|
||
This is a bit more radical, but completely avoids the parsing ambiguity! | ||
|
||
```js | ||
program | ||
.name('cook') | ||
.option('-t, --technique <technique>', 'cooking technique') | ||
.option('-i, --ingredient [ingredient]', 'add cheese or given ingredient') | ||
.action((options) => { | ||
console.log(`technique: ${options.technique}`); | ||
const ingredient = (options.ingredient === true) ? 'cheese' : options.ingredient; | ||
console.log(`ingredient: ${ingredient}`); | ||
}); | ||
``` | ||
|
||
```sh | ||
$ cook -i -t scrambled | ||
technique: scrambled | ||
ingredient: cheese | ||
``` | ||
## Combining short options, and options taking arguments | ||
|
||
Multiple boolean short options can be combined after a single `-`, like `ls -al`. You can also include just | ||
a single short option which might take a value, as any following characters will | ||
be taken as the value. | ||
|
||
This means that by default you can not combine short options which may take an argument. | ||
|
||
```js | ||
program | ||
.name('collect') | ||
.option("-o, --other [count]", "other serving(s)") | ||
.option("-v, --vegan [count]", "vegan serving(s)") | ||
.option("-l, --halal [count]", "halal serving(s)"); | ||
program.parse(process.argv); | ||
|
||
const opts = program.opts(); | ||
if (opts.other) console.log(`other servings: ${opts.other}`); | ||
if (opts.vegan) console.log(`vegan servings: ${opts.vegan}`); | ||
if (opts.halal) console.log(`halal servings: ${opts.halal}`); | ||
``` | ||
|
||
```sh | ||
$ collect -o 3 | ||
other servings: 3 | ||
$ collect -o3 | ||
other servings: 3 | ||
$ collect -l -v | ||
vegan servings: true | ||
halal servings: true | ||
$ collect -lv # oops | ||
halal servings: v | ||
``` | ||
|
||
If you wish to use options taking varying arguments as boolean options, you need to specify them separately. | ||
|
||
``` | ||
$ collect -a -v -l | ||
any servings: true | ||
vegan servings: true | ||
halal servings: true | ||
``` | ||
|
||
### Combining short options as if boolean | ||
|
||
Before Commander v5, combining a short option and the value was not supported, and combined short flags were always expanded. | ||
So `-avl` expanded to `-a -v -l`. | ||
|
||
If you want backwards compatible behaviour, or prefer combining short options as booleans to combining short option and value, | ||
you may change the behavior. | ||
|
||
To modify the parsing of options taking an optional value: | ||
|
||
```js | ||
.combineFlagAndOptionalValue(true) // `-v45` is treated like `--vegan=45`, this is the default behaviour | ||
.combineFlagAndOptionalValue(false) // `-vl` is treated like `-v -l` | ||
``` | ||
|
||
## Terminology | ||
|
||
_Work in progress: this section may move to the main README, or a page of its own._ | ||
|
||
The command line arguments are made up of options, option-arguments, commands, and command-arguments. | ||
|
||
| Term | Explanation | | ||
| --- | --- | | ||
| option | an argument which is a `-` followed by a character, or `--` followed by a word (or hyphenated words), like `-s` or `--short` | | ||
| option-argument| some options can take an argument | | ||
| command | a program or command can have subcommands | | ||
| command-argument | argument for the command (and not an option or option-argument) | | ||
|
||
For example: | ||
|
||
```sh | ||
my-utility command -o --option option-argument command-argument-1 command-argument-2 | ||
``` | ||
|
||
In other references options are sometimes called flags, and command-arguments are sometimes called positional arguments or operands. |
This file was deleted.
Oops, something went wrong.