Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ Options:
-h, --help display help for command

$ extra --drink huge
error: option '-d, --drink <size>' argument of 'huge' not in allowed choices: small, medium, large
error: option '-d, --drink <size>' argument 'huge' is invalid. Allowed choices are small, medium, large.
```

### Custom option processing
Expand All @@ -319,8 +319,12 @@ Example file: [options-custom-processing.js](./examples/options-custom-processin

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

function increaseVerbosity(dummyValue, previous) {
Expand Down
8 changes: 7 additions & 1 deletion examples/options-custom-processing.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,20 @@
// [ 'a', 'b', 'c' ]
// $ custom --list x,y,z
// [ 'x', 'y', 'z' ]
// $ custom --integer oops
// error: option '-i, --integer <number>' argument 'oops' is invalid. Not a number.

// 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
return parseInt(value, 10);
const parsedValue = parseInt(value, 10);
if (isNaN(parsedValue)) {
throw new commander.InvalidOptionArgumentError('Not a number.');
}
return parsedValue;
}

function increaseVerbosity(dummyValue, previous) {
Expand Down
36 changes: 23 additions & 13 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -422,16 +422,6 @@ class Option {
return this;
};

/**
* Validation of option argument failed.
* Intended for use from custom argument processing functions.
*
* @param {string} message
*/
argumentRejected(message) {
throw new CommanderError(1, 'commander.optionArgumentRejected', message);
}

/**
* Only allow option value to be one of choices.
*
Expand All @@ -443,7 +433,7 @@ class Option {
this.argChoices = values;
this.parseArg = (arg) => {
if (!values.includes(arg)) {
this.argumentRejected(`error: option '${this.flags}' argument of '${arg}' not in allowed choices: ${values.join(', ')}`);
throw new InvalidOptionArgumentError(`Allowed choices are ${values.join(', ')}.`);
}
return arg;
};
Expand Down Expand Up @@ -511,6 +501,24 @@ class CommanderError extends Error {
}
}

/**
* InvalidOptionArgumentError class
* @class
*/
class InvalidOptionArgumentError extends CommanderError {
/**
* Constructs the InvalidOptionArgumentError class
* @param {string} [message] explanation of why argument is invalid
* @constructor
*/
constructor(message) {
super(1, 'commander.invalidOptionArgument', message);
// properly capture stack trace in Node.js
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
}
}

class Command extends EventEmitter {
/**
* Initialize a new `Command`.
Expand Down Expand Up @@ -1002,8 +1010,9 @@ Read more on https://git.io/JJc0W`);
try {
val = option.parseArg(val, oldValue === undefined ? defaultValue : oldValue);
} catch (err) {
if (err.code === 'commander.optionArgumentRejected') {
this._displayError(err.exitCode, err.code, err.message);
if (err.code === 'commander.invalidOptionArgument') {
const message = `error: option '${option.flags}' argument '${val}' is invalid. ${err.message}`;
this._displayError(err.exitCode, err.code, message);
}
throw err;
}
Expand Down Expand Up @@ -2040,6 +2049,7 @@ exports.program = exports; // More explicit access to global command.
exports.Command = Command;
exports.Option = Option;
exports.CommanderError = CommanderError;
exports.InvalidOptionArgumentError = InvalidOptionArgumentError;
exports.Help = Help;

/**
Expand Down
22 changes: 21 additions & 1 deletion tests/command.exitOverride.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,26 @@ describe('.exitOverride and error details', () => {
caughtErr = err;
}

expectCommanderError(caughtErr, 1, 'commander.optionArgumentRejected', `error: option '${optionFlags}' argument of 'green' not in allowed choices: red, blue`);
expectCommanderError(caughtErr, 1, 'commander.invalidOptionArgument', "error: option '--colour <shade>' argument 'green' is invalid. Allowed choices are red, blue.");
});

test('when custom processing throws InvalidOptionArgumentError then throw CommanderError', () => {
function justSayNo(value) {
throw new commander.InvalidOptionArgumentError('NO');
}
const optionFlags = '--colour <shade>';
const program = new commander.Command();
program
.exitOverride()
.option(optionFlags, 'specify shade', justSayNo);

let caughtErr;
try {
program.parse(['--colour', 'green'], { from: 'user' });
} catch (err) {
caughtErr = err;
}

expectCommanderError(caughtErr, 1, 'commander.invalidOptionArgument', "error: option '--colour <shade>' argument 'green' is invalid. NO");
});
});
1 change: 1 addition & 0 deletions typings/commander-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const commandInstance1 = new commander.Command();
const commandInstance2 = new commander.Command('name');
const optionsInstance = new commander.Option('-f');
const errorInstance = new commander.CommanderError(1, 'code', 'message');
const invalidOptionErrorInstance = new commander.InvalidOptionArgumentError('message');

// Command properties
console.log(programWithOptions.someOption);
Expand Down
5 changes: 5 additions & 0 deletions typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ declare namespace commander {
}
type CommanderErrorConstructor = new (exitCode: number, code: string, message: string) => CommanderError;

interface InvalidOptionArgumentError extends CommanderError {
}
type InvalidOptionArgumentErrorConstructor = new (message: string) => InvalidOptionArgumentError;

interface Option {
flags: string;
description: string;
Expand Down Expand Up @@ -578,6 +582,7 @@ declare namespace commander {
Command: CommandConstructor;
Option: OptionConstructor;
CommanderError: CommanderErrorConstructor;
InvalidOptionArgumentError: InvalidOptionArgumentErrorConstructor;
Help: HelpConstructor;
}

Expand Down