Skip to content

Commit

Permalink
Make new excess arguments error opt-in (#1429)
Browse files Browse the repository at this point in the history
* Make allowExcessArguments opt-in (again) and inherited configuration

* Remove stale JSDoc

* Update documentation for allowExcessArguments being opt-in

* Make comment more specific about what is being checked for excess arguments

* Rework example class override
  • Loading branch information
shadowspawn committed Jan 7, 2021
1 parent 8ac84ec commit e2670f4
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 66 deletions.
29 changes: 1 addition & 28 deletions CHANGELOG.md
Expand Up @@ -12,8 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

### Added

- *Breaking:* error message if there are too many command-arguments on command line for the action handler ([#1409])
- if should be allowed then declare extra arguments, or use `.allowExcessArguments()`
- `.allowExcessArguments(false)` to show an error message if there are too many command-arguments on command line for the action handler ([#1409])
- `.configureOutput()` to modify use of stdout and stderr or customise display of errors ([#1387])
- use `.addHelpText()` to add text before or after the built-in help, for just current command or also for all subcommands ([#1296])
- enhance Option class ([#1331])
Expand Down Expand Up @@ -120,32 +119,6 @@ program
});
```

**excess command-arguments**

There is now an error if there are too many command-arguments on the command line (only checked if there is an action handler).
If the extra arguments are supported by your command then you can either declare the expected arguments, or allow excess arguments.

```js
// Old code before Commander 7
program
.action(() => {});
program.parse(['a', 'b', 'c'], { from: 'user' }); // now causes an error
```

```js
// New code, declare arguments
program
.arguments('[args...]')
.action(() => {});
```

```js
// New code, allow excess arguments
program
.allowExcessArguments()
.action(() => {});
```

## [7.0.0-2] (2020-12-14)

(Released in 7.0.0)
Expand Down
7 changes: 5 additions & 2 deletions Readme.md
Expand Up @@ -483,7 +483,8 @@ async function main() {
}
```
A command's options and arguments on the command line are validated when the command is used. Any unknown options or unexpected command-arguments will be reported as an error, or you can suppress these checks with `.allowUnknownOption()` and `.allowExcessArguments()`.
A command's options and arguments on the command line are validated when the command is used. Any unknown options or missing arguments will be reported as an error. You can suppress the unknown option checks with `.allowUnknownOption()`. By default it is not an error to
pass more arguments than declared, but you can make this an error with `.allowExcessArguments(false)`.
### Stand-alone executable (sub)commands
Expand Down Expand Up @@ -715,9 +716,11 @@ program --port=80 arg
program arg --port=80
```
By default the option processing shows an error for an unknown option. To have an unknown option treated as an ordinary command-argument and continue looking for options, use `.allowUnknownOption()`. This lets you mix known and unknown options.
By default the argument processing does not display an error for more command-arguments than expected.
To display an error for excess arguments, use`.allowExcessArguments(false)`.
### Legacy options as properties
Before Commander 7, the option values were stored as properties on the command.
Expand Down
54 changes: 27 additions & 27 deletions examples/custom-command-class.js
Expand Up @@ -4,47 +4,47 @@
const commander = require('../'); // include commander in git clone of commander repo

// Use a class override to customise the command and its subcommands.
//
// Configuring the command for compatibility with Commander v6 defaults behaviours.

class Command6 extends commander.Command {
constructor(name) {
super(name);

// Revert to Commander v6 behaviours.
this.storeOptionsAsProperties();
this.allowExcessArguments();
}

class CommandWithTrace extends commander.Command {
createCommand(name) {
return new Command6(name);
const cmd = new CommandWithTrace(name);
// Add an option to subcommands created using `.command()`
cmd.option('-t, --trace', 'display extra information when run command');
return cmd;
}
};

function inspectCommand(command, optionName) {
function inpectCommand(command) {
// The option value is stored as property on command because we called .storeOptionsAsProperties()
console.log(`Inspecting '${command.name()}'`);
console.log(`option '${optionName}': ${command[optionName]}`);
console.log(`Called '${command.name()}'`);
console.log(`args: ${command.args}`);
console.log('opts: %o', command.opts());
};

const program = new Command6('program')
.option('-p, --port <number>')
.action(() => {
inspectCommand(program, 'port');
const program = new CommandWithTrace('program')
.option('-v, ---verbose')
.action((options, command) => {
inpectCommand(command);
});

program
.command('serve [params...]')
.option('-p, --port <number>', 'port number')
.action((params, options, command) => {
inpectCommand(command);
});

program
.command('sub')
.option('-d, --debug')
.action((options, command) => {
inspectCommand(command, 'debug');
.command('build <target>')
.action((buildTarget, options, command) => {
inpectCommand(command);
});

program.parse();

// We can pass excess arguments without an error as we called .allowExcessArguments()
//
// Try the following:
// node custom-command-class.js --port 80 extra arguments
// node custom-command-class.js sub --debug extra arguments
// node custom-command-class.js --help
// node custom-command-class.js serve --help
// node custom-command-class.js serve -t -p 80 a b c
// node custom-command-class.js build --help
// node custom-command-class.js build --trace foo
8 changes: 4 additions & 4 deletions index.js
Expand Up @@ -535,7 +535,7 @@ class Command extends EventEmitter {
this.options = [];
this.parent = null;
this._allowUnknownOption = false;
this._allowExcessArguments = false;
this._allowExcessArguments = true;
this._args = [];
this.rawArgs = null;
this._scriptPath = null;
Expand Down Expand Up @@ -635,6 +635,7 @@ class Command extends EventEmitter {
cmd._exitCallback = this._exitCallback;
cmd._storeOptionsAsProperties = this._storeOptionsAsProperties;
cmd._combineFlagAndOptionalValue = this._combineFlagAndOptionalValue;
cmd._allowExcessArguments = this._allowExcessArguments;
cmd._enablePositionalOptions = this._enablePositionalOptions;

cmd._executableFile = opts.executableFile || null; // Custom name for executable file, set missing to null to match constructor
Expand Down Expand Up @@ -1126,7 +1127,7 @@ class Command extends EventEmitter {
};

/**
* Allow excess arguments on the command line.
* Allow excess command-arguments on the command line. Pass false to make excess arguments an error.
*
* @param {Boolean} [allowExcess=true] - if `true` or omitted, no error will be thrown
* for excess arguments.
Expand Down Expand Up @@ -1724,10 +1725,9 @@ class Command extends EventEmitter {
};

/**
* `Option` is missing an argument, but received `flag` or nothing.
* `Option` is missing an argument.
*
* @param {Option} option
* @param {string} [flag]
* @api private
*/

Expand Down
8 changes: 4 additions & 4 deletions tests/command.allowExcessArguments.test.js
Expand Up @@ -18,15 +18,15 @@ describe('allowUnknownOption', () => {
writeErrorSpy.mockRestore();
});

test('when specify excess program argument then error by default', () => {
test('when specify excess program argument then no error by default', () => {
const program = new commander.Command();
program
.exitOverride()
.action(() => {});

expect(() => {
program.parse(['excess'], { from: 'user' });
}).toThrow();
}).not.toThrow();
});

test('when specify excess program argument and allowExcessArguments(false) then error', () => {
Expand Down Expand Up @@ -65,7 +65,7 @@ describe('allowUnknownOption', () => {
}).not.toThrow();
});

test('when specify excess command argument then error (by default)', () => {
test('when specify excess command argument then no error (by default)', () => {
const program = new commander.Command();
program
.exitOverride()
Expand All @@ -74,7 +74,7 @@ describe('allowUnknownOption', () => {

expect(() => {
program.parse(['sub', 'excess'], { from: 'user' });
}).toThrow();
}).not.toThrow();
});

test('when specify excess command argument and allowExcessArguments(false) then error', () => {
Expand Down
2 changes: 1 addition & 1 deletion typings/index.d.ts
Expand Up @@ -402,7 +402,7 @@ declare namespace commander {
allowUnknownOption(allowUnknown?: boolean): this;

/**
* Allow excess arguments on the command line.
* Allow excess command-arguments on the command line. Pass false to make excess arguments an error.
*
* @returns `this` command for chaining
*/
Expand Down

0 comments on commit e2670f4

Please sign in to comment.