Skip to content

Commit

Permalink
Extend .argument() to add default value and custom parse for command-…
Browse files Browse the repository at this point in the history
…argument (#1508)

* First cut at adding default value and parse for Argument

* Simplify reduce call, good match for pattern

* Refactor new code into routine

* Add custom processing argument tests

* Add default value tests for argument

* Rename tests

* Add error for ignored default value

* Add argument default to help information

* Default only makes sense for optional argument

* Extend argument typings

* Add argument custom processing to README

* Add InvalidArgumentError and deprecate InvalidOptionArgumentError

* Fix JSDoc

* Add example of varidic argument custom processing

* Update comment

* Remove low value comment
  • Loading branch information
shadowspawn committed May 3, 2021
1 parent 3d67542 commit f99d6fa
Show file tree
Hide file tree
Showing 16 changed files with 446 additions and 39 deletions.
30 changes: 27 additions & 3 deletions Readme.md
Expand Up @@ -23,6 +23,7 @@ Read this in other languages: English | [简体中文](./Readme_zh-CN.md)
- [Custom option processing](#custom-option-processing)
- [Commands](#commands)
- [Specify the argument syntax](#specify-the-argument-syntax)
- [Custom argument processing](#custom-argument-processing)
- [Action handler](#action-handler)
- [Stand-alone executable (sub)commands](#stand-alone-executable-subcommands)
- [Automated help](#automated-help)
Expand Down Expand Up @@ -342,7 +343,7 @@ function myParseInt(value, dummyPrevious) {
// parseInt takes a string and a radix
const parsedValue = parseInt(value, 10);
if (isNaN(parsedValue)) {
throw new commander.InvalidOptionArgumentError('Not a number.');
throw new commander.InvalidArgumentError('Not a number.');
}
return parsedValue;
}
Expand Down Expand Up @@ -434,17 +435,19 @@ you can instead use the following method.
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]`.
You can specify a default value for an optional command-argument.
Example file: [argument.js](./examples/argument.js)
```js
program
.version('0.1.0')
.argument('<username>', 'user to login')
.argument('[password]', 'password for user, if required')
.argument('[password]', 'password for user, if required', 'no password given')
.action((username, password) => {
console.log('username:', username);
console.log('password:', password || 'no password given');
console.log('password:', password);
});
```
Expand All @@ -470,6 +473,27 @@ program
.arguments('<username> <password>');
```
### Custom argument processing
You may specify a function to do custom processing of command-arguments before they are passed to the action handler.
The callback function receives two parameters, the user specified command-argument and the previous value for the argument.
It returns the new value for the argument.
You can optionally specify the default/starting value for the argument after the function parameter.
Example file: [arguments-custom-processing.js](./examples/arguments-custom-processing.js)
```js
program
.command('add')
.argument('<first>', 'integer argument', myParseInt)
.argument('[second]', 'integer argument', myParseInt, 1000)
.action((first, second) => {
console.log(`${first} + ${second} = ${first + second}`);
})
;
```
### Action handler
The action handler gets passed a parameter for each command-argument you declared, and two additional parameters
Expand Down
4 changes: 2 additions & 2 deletions Readme_zh-CN.md
Expand Up @@ -314,7 +314,7 @@ function myParseInt(value, dummyPrevious) {
// parseInt takes a string and a radix
const parsedValue = parseInt(value, 10);
if (isNaN(parsedValue)) {
throw new commander.InvalidOptionArgumentError('Not a number.');
throw new commander.InvalidArgumentError('Not a number.');
}
return parsedValue;
}
Expand Down Expand Up @@ -877,4 +877,4 @@ program.parse(process.argv);
现在 Commander 已作为 Tidelift 订阅的一部分。
Commander 和很多其他包的维护者已与 Tidelift 合作,面向企业提供开源依赖的商业支持与维护。企业可以向相关依赖包的维护者支付一定的费用,帮助企业节省时间,降低风险,改进代码运行情况。[了解更多](https://tidelift.com/subscription/pkg/npm-commander?utm_source=npm-commander&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
Commander 和很多其他包的维护者已与 Tidelift 合作,面向企业提供开源依赖的商业支持与维护。企业可以向相关依赖包的维护者支付一定的费用,帮助企业节省时间,降低风险,改进代码运行情况。[了解更多](https://tidelift.com/subscription/pkg/npm-commander?utm_source=npm-commander&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
29 changes: 29 additions & 0 deletions docs/deprecated.md
Expand Up @@ -111,3 +111,32 @@ program

Deprecated from Commander v8.

## InvalidOptionArgumentError

This was used for throwing an error from custom option processing, for a nice error message.

```js
function myParseInt(value, dummyPrevious) {
// parseInt takes a string and a radix
const parsedValue = parseInt(value, 10);
if (isNaN(parsedValue)) {
throw new commander.InvalidOptionArgumentError('Not a number.');
}
return parsedValue;
}
```

The replacement is `InvalidArgumentError` since can be used now for custom command-argument processing too.

```js
function myParseInt(value, dummyPrevious) {
// parseInt takes a string and a radix
const parsedValue = parseInt(value, 10);
if (isNaN(parsedValue)) {
throw new commander.InvalidArgumentError('Not a number.');
}
return parsedValue;
}
```

Deprecated from Commander v8.
2 changes: 1 addition & 1 deletion esm.mjs
Expand Up @@ -7,7 +7,7 @@ export const {
createArgument,
createOption,
CommanderError,
InvalidOptionArgumentError,
InvalidArgumentError,
Command,
Argument,
Option,
Expand Down
4 changes: 2 additions & 2 deletions examples/argument.js
Expand Up @@ -9,11 +9,11 @@ const program = new Command();
program
.version('0.1.0')
.argument('<username>', 'user to login')
.argument('[password]', 'password for user, if required')
.argument('[password]', 'password for user, if required', 'no password given')
.description('example program for argument')
.action((username, password) => {
console.log('username:', username);
console.log('password:', password || 'no password given');
console.log('password:', password);
});

program.parse();
Expand Down
47 changes: 47 additions & 0 deletions examples/arguments-custom-processing.js
@@ -0,0 +1,47 @@
#!/usr/bin/env node

// This is used as an example in the README for:
// Custom argument processing
// You may specify a function to do custom processing of argument values.

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

function myParseInt(value, dummyPrevious) {
// parseInt takes a string and a radix
const parsedValue = parseInt(value, 10);
if (isNaN(parsedValue)) {
throw new commander.InvalidArgumentError('Not a number.');
}
return parsedValue;
}

// The previous value passed to the custom processing is used when processing variadic values.
function mySum(value, total) {
return total + myParseInt(value);
}

program
.command('add')
.argument('<first>', 'integer argument', myParseInt)
.argument('[second]', 'integer argument', myParseInt, 1000)
.action((first, second) => {
console.log(`${first} + ${second} = ${first + second}`);
});

program
.command('sum')
.argument('<value...>', 'values to be summed', mySum, 0)
.action((total) => {
console.log(`sum is ${total}`);
});

program.parse();

// Try the following:
// node arguments-custom-processing add --help
// node arguments-custom-processing add 2
// node arguments-custom-processing add 12 56
// node arguments-custom-processing sum 1 2 3
// node arguments-custom-processing sum silly
2 changes: 1 addition & 1 deletion examples/options-custom-processing.js
Expand Up @@ -12,7 +12,7 @@ function myParseInt(value, dummyPrevious) {
// parseInt takes a string and a radix
const parsedValue = parseInt(value, 10);
if (isNaN(parsedValue)) {
throw new commander.InvalidOptionArgumentError('Not a number.');
throw new commander.InvalidArgumentError('Not a number.');
}
return parsedValue;
}
Expand Down

0 comments on commit f99d6fa

Please sign in to comment.