Skip to content

Commit

Permalink
More consistent way of adding command arguments (#1490)
Browse files Browse the repository at this point in the history
* added addArguments method

* fix undefined args

* added tests, docs and typings

* code review fixes

* throw error on bad arg

* Parse command-argument details in constructor

* Handle argument descriptions separately for legacy and new support

* Add text to distinguish test names with .each

* Match nameAndArgs into two parts, rather than split on spaces.

* Update release date post-release to be more accurate

* Fix test naming

* Simplify and tidy argument example

* Typing and typings test for .argument

* Expand argument section to include existing multiple-argument approaches.

* Add name method and improve Argument typings and tests

* Fix copy-and-paste JSDoc error

* Update example to match new method and README

* Deprecate old way of adding command argument descriptions

* Be lenient about Argument construction to allow lazy building

* Call first param to .argument "name", and expand jsdoc

* Add low-level check that get same Argument from multiple ways of specifying argument

* Minor wording tweaks

* Add low-level tests for multiple arg variations

* Simplify test. Use .argument now.

* Restore simple test, use .argument

* Big switch from .arguments to .argument in tests

* Expand help to explain argument variations

* Keep Argument properties private for now (like Command, unlike Option)

* Argument should follow Option for properties, make public again

* Generate help for arguments using same methods as for option and subcommand

* Simplify Argument .name(), just getter

* Expand test coverage for visibleArguments

* Rework the multiple ways of specifying command-arguments

* Add Argument to esm exports (and createOption)

Co-authored-by: Nir Yosef <niryo@wix.com>
  • Loading branch information
shadowspawn and Niryo committed Apr 8, 2021
1 parent 327a3dd commit b286da9
Show file tree
Hide file tree
Showing 33 changed files with 622 additions and 189 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Expand Up @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
<!-- markdownlint-disable MD024 -->
<!-- markdownlint-disable MD004 -->

## [7.2.0] (2021-03-26)
## [7.2.0] (2021-03-22)

### Added

Expand Down
39 changes: 22 additions & 17 deletions Readme.md
Expand Up @@ -394,7 +394,7 @@ $ custom --list x,y,z
You can specify (sub)commands using `.command()` or `.addCommand()`. There are two ways these can be implemented: using an action handler attached to the command, or as a stand-alone executable file (described in more detail later). The subcommands may be nested ([example](./examples/nestedCommands.js)).
In the first parameter to `.command()` you specify the command name and any command-arguments. The arguments may be `<required>` or `[optional]`, and the last argument may also be `variadic...`.
In the first parameter to `.command()` you specify the command name. You may append the command-arguments after the command name, or specify them separately using `.argument()`. The arguments may be `<required>` or `[optional]`, and the last argument may also be `variadic...`.
You can use `.addCommand()` to add an already configured subcommand to the program.
Expand All @@ -410,7 +410,7 @@ program
console.log('clone command called');
});
// Command implemented using stand-alone executable file (description is second parameter to `.command`)
// Command implemented using stand-alone executable file, indicated by adding description as second parameter to `.command`.
// Returns `this` for adding more commands.
program
.command('start <service>', 'start named service')
Expand All @@ -428,42 +428,47 @@ subcommand is specified ([example](./examples/defaultCommand.js)).
### Specify the argument syntax
You use `.arguments` to specify the expected command-arguments for the top-level command, and for subcommands they are usually
included in the `.command` call. Angled brackets (e.g. `<required>`) indicate required command-arguments.
Square brackets (e.g. `[optional]`) indicate optional command-arguments.
You can optionally describe the arguments in the help by supplying a hash as second parameter to `.description()`.
For subcommands, you can specify the argument syntax in the call to `.command()` (as shown above). This
is the only method usable for subcommands implemented using a stand-alone executable, but for other subcommands
you can instead use the following method.
Example file: [arguments.js](./examples/arguments.js)
To configure a command, you can use `.argument()` to specify each expected command-argument.
You supply the argument name and an optional description. The argument may be `<required>` or `[optional]`.
Example file: [argument.js](./examples/argument.js)
```js
program
.version('0.1.0')
.arguments('<username> [password]')
.description('test command', {
username: 'user to login',
password: 'password for user, if required'
})
.argument('<username>', 'user to login')
.argument('[password]', 'password for user, if required')
.action((username, password) => {
console.log('username:', username);
console.log('environment:', password || 'no password given');
console.log('password:', password || 'no password given');
});
```
The last argument of a command can be variadic, and only the last argument. To make an argument variadic you
append `...` to the argument name. For example:
append `...` to the argument name. A variadic argument is passed to the action handler as an array. For example:
```js
program
.version('0.1.0')
.command('rmdir <dirs...>')
.command('rmdir')
.argument('<dirs...>')
.action(function (dirs) {
dirs.forEach((dir) => {
console.log('rmdir %s', dir);
});
});
```
The variadic argument is passed to the action handler as an array.
There is a convenience method to add multiple arguments at once, but without descriptions:
```js
program
.arguments('<username> <password>');
```
### Action handler
Expand All @@ -474,7 +479,7 @@ Example file: [thank.js](./examples/thank.js)
```js
program
.arguments('<name>')
.argument('<name>')
.option('-t, --title <honorific>', 'title to use before name')
.option('-d, --debug', 'display some debugging')
.action((name, options, command) => {
Expand Down
25 changes: 25 additions & 0 deletions docs/deprecated.md
Expand Up @@ -86,3 +86,28 @@ Examples:
```

Deprecated from Commander v7.

## cmd.description(cmdDescription, argDescriptions)

This was used to add command argument descriptions for the help.

```js
program
.command('price <book>')
.description('show price of book', {
book: 'ISBN number for book'
});
```

The new approach is to use the `.argument()` method.

```js
program
.command('price')
.description('show price of book')
.argument('<book>', 'ISBN number for book');
```


Deprecated from Commander v8.

4 changes: 2 additions & 2 deletions docs/options-taking-varying-arguments.md
Expand Up @@ -5,7 +5,7 @@ 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: 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)
Expand Down Expand Up @@ -34,7 +34,7 @@ intend the argument following the option as a command or command-argument.
```js
program
.name('cook')
.arguments('[technique]')
.argument('[technique]')
.option('-i, --ingredient [ingredient]', 'add cheese or given ingredient')
.action((technique, options) => {
console.log(`technique: ${technique}`);
Expand Down
4 changes: 2 additions & 2 deletions docs/zh-CN/可变参数的选项.md
Expand Up @@ -31,7 +31,7 @@ Commander 首先解析选项的参数,而用户有可能想将选项后面跟
```js
program
.name('cook')
.arguments('[technique]')
.argument('[technique]')
.option('-i, --ingredient [ingredient]', 'add cheese or given ingredient')
.action((technique, options) => {
console.log(`technique: ${technique}`);
Expand Down Expand Up @@ -190,4 +190,4 @@ halal servings: true
```js
.combineFlagAndOptionalValue(true) // `-v45` 被视为 `--vegan=45`,这是默认的行为
.combineFlagAndOptionalValue(false) // `-vl` 被视为 `-v -l`
```
```
2 changes: 1 addition & 1 deletion esm.mjs
@@ -1,4 +1,4 @@
import commander from './index.js';

// wrapper to provide named exports for ESM.
export const { program, Option, Command, CommanderError, InvalidOptionArgumentError, Help, createCommand } = commander;
export const { program, Option, Command, Argument, CommanderError, InvalidOptionArgumentError, Help, createCommand, createOption } = commander;
24 changes: 24 additions & 0 deletions examples/argument.js
@@ -0,0 +1,24 @@
#!/usr/bin/env node

// This example shows specifying the arguments using argument() function.

// const { Command } = require('commander'); // (normal include)
const { Command } = require('../'); // include commander in git clone of commander repo
const program = new Command();

program
.version('0.1.0')
.argument('<username>', 'user to login')
.argument('[password]', 'password for user, if required')
.description('example program for argument')
.action((username, password) => {
console.log('username:', username);
console.log('password:', password || 'no password given');
});

program.parse();

// Try the following:
// node arguments.js --help
// node arguments.js user
// node arguments.js user secret
2 changes: 1 addition & 1 deletion examples/arguments.js
Expand Up @@ -15,7 +15,7 @@ program
})
.action((username, password) => {
console.log('username:', username);
console.log('environment:', password || 'no password given');
console.log('password:', password || 'no password given');
});

program.parse();
Expand Down
3 changes: 2 additions & 1 deletion examples/pass-through-options.js
Expand Up @@ -5,7 +5,8 @@ const { Command } = require('../'); // include commander in git clone of command
const program = new Command();

program
.arguments('<utility> [args...]')
.argument('<utility>')
.argument('[args...]')
.passThroughOptions()
.option('-d, --dry-run')
.action((utility, args, options) => {
Expand Down
2 changes: 1 addition & 1 deletion examples/thank.js
Expand Up @@ -7,7 +7,7 @@ const { Command } = require('../'); // include commander in git clone of command
const program = new Command();

program
.arguments('<name>')
.argument('<name>')
.option('-t, --title <honorific>', 'title to use before name')
.option('-d, --debug', 'display some debugging')
.action((name, options, command) => {
Expand Down

0 comments on commit b286da9

Please sign in to comment.