Skip to content
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

Make new excess arguments error opt-in #1429

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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