Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/release/12.x' into feature/exi…
Browse files Browse the repository at this point in the history
…t-code-after-sub-signal
  • Loading branch information
shadowspawn committed Sep 23, 2023
2 parents 564b3f5 + 9afbabd commit b0b6992
Show file tree
Hide file tree
Showing 15 changed files with 121 additions and 123 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
os: [ubuntu-latest, windows-latest, macos-latest]

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
Expand Down
17 changes: 17 additions & 0 deletions docs/deprecated.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ They are currently still available for backwards compatibility, but should not b
- [InvalidOptionArgumentError](#invalidoptionargumenterror)
- [Short option flag longer than a single character](#short-option-flag-longer-than-a-single-character)
- [Import from `commander/esm.mjs`](#import-from-commanderesmmjs)
- [cmd.\_args](#cmd_args)

## RegExp .option() parameter

Expand Down Expand Up @@ -208,3 +209,19 @@ import { Command } from 'commander';
```

README updated in Commander v9. Deprecated from Commander v9.

## cmd._args

This was always private, but was previously the only way to access the command `Argument` array.

```js
const registeredArguments = program._args;
```

The registered command arguments are now accessible via `.registeredArguments`.

```js
const registeredArguments = program.registeredArguments;
```

Deprecated from Commander v11.
7 changes: 4 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ exports.createArgument = (name, description) => new Argument(name, description);
* Expose classes
*/

exports.Argument = Argument;
exports.Command = Command;
exports.CommanderError = CommanderError;
exports.Option = Option;
exports.Argument = Argument;
exports.Help = Help;

exports.CommanderError = CommanderError;
exports.InvalidArgumentError = InvalidArgumentError;
exports.InvalidOptionArgumentError = InvalidArgumentError; // Deprecated
exports.Option = Option;
2 changes: 1 addition & 1 deletion lib/argument.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class Argument {
/**
* Set the default value, and optionally supply the description to be displayed in the help.
*
* @param {any} value
* @param {*} value
* @param {string} [description]
* @return {Argument}
*/
Expand Down
87 changes: 30 additions & 57 deletions lib/command.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ class Command extends EventEmitter {
this._allowUnknownOption = false;
this._allowExcessArguments = true;
/** @type {Argument[]} */
this._args = [];
this.registeredArguments = [];
this._args = this.registeredArguments; // deprecated old name
/** @type {string[]} */
this.args = []; // cli args with options removed
this.rawArgs = [];
Expand Down Expand Up @@ -358,14 +359,14 @@ class Command extends EventEmitter {
* @return {Command} `this` command for chaining
*/
addArgument(argument) {
const previousArgument = this._args.slice(-1)[0];
const previousArgument = this.registeredArguments.slice(-1)[0];
if (previousArgument && previousArgument.variadic) {
throw new Error(`only the last argument can be variadic '${previousArgument.name()}'`);
}
if (argument.required && argument.defaultValue !== undefined && argument.parseArg === undefined) {
throw new Error(`a default value for a required argument is never used: '${argument.name()}'`);
}
this._args.push(argument);
this.registeredArguments.push(argument);
return this;
}

Expand Down Expand Up @@ -485,7 +486,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
action(fn) {
const listener = (args) => {
// The .action callback takes an extra parameter which is the command or options.
const expectedArgsCount = this._args.length;
const expectedArgsCount = this.registeredArguments.length;
const actionArgs = args.slice(0, expectedArgsCount);
if (this._storeOptionsAsProperties) {
actionArgs[expectedArgsCount] = this; // backwards compatible "options"
Expand Down Expand Up @@ -520,7 +521,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
*
* @param {Option | Argument} target
* @param {string} value
* @param {any} previous
* @param {*} previous
* @param {string} invalidArgumentMessage
* @private
*/
Expand Down Expand Up @@ -634,57 +635,29 @@ Expecting one of '${allowedValues.join("', '")}'`);
}

/**
* Define option with `flags`, `description` and optional
* coercion `fn`.
* Define option with `flags`, `description`, and optional argument parsing function or `defaultValue` or both.
*
* The `flags` string contains the short and/or long flags,
* separated by comma, a pipe or space. The following are all valid
* all will output this way when `--help` is used.
* The `flags` string contains the short and/or long flags, separated by comma, a pipe or space. A required
* option-argument is indicated by `<>` and an optional option-argument by `[]`.
*
* "-p, --pepper"
* "-p|--pepper"
* "-p --pepper"
* See the README for more details, and see also addOption() and requiredOption().
*
* @example
* // simple boolean defaulting to undefined
* program.option('-p, --pepper', 'add pepper');
*
* program.pepper
* // => undefined
*
* --pepper
* program.pepper
* // => true
*
* // simple boolean defaulting to true (unless non-negated option is also defined)
* program.option('-C, --no-cheese', 'remove cheese');
*
* program.cheese
* // => true
*
* --no-cheese
* program.cheese
* // => false
*
* // required argument
* program.option('-C, --chdir <path>', 'change the working directory');
*
* --chdir /tmp
* program.chdir
* // => "/tmp"
*
* // optional argument
* program.option('-c, --cheese [type]', 'add cheese [marble]');
* program
* .option('-p, --pepper', 'add pepper')
* .option('-p, --pizza-type <TYPE>', 'type of pizza') // required option-argument
* .option('-c, --cheese [CHEESE]', 'add extra cheese', 'mozzarella') // optional option-argument with default
* .option('-t, --tip <VALUE>', 'add tip to purchase cost', parseFloat) // custom parse function
*
* @param {string} flags
* @param {string} [description]
* @param {Function|*} [fn] - custom option processing function or default value
* @param {Function|*} [parseArg] - custom option processing function or default value
* @param {*} [defaultValue]
* @return {Command} `this` command for chaining
*/

option(flags, description, fn, defaultValue) {
return this._optionEx({}, flags, description, fn, defaultValue);
option(flags, description, parseArg, defaultValue) {
return this._optionEx({}, flags, description, parseArg, defaultValue);
}

/**
Expand All @@ -695,13 +668,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
*
* @param {string} flags
* @param {string} [description]
* @param {Function|*} [fn] - custom option processing function or default value
* @param {Function|*} [parseArg] - custom option processing function or default value
* @param {*} [defaultValue]
* @return {Command} `this` command for chaining
*/

requiredOption(flags, description, fn, defaultValue) {
return this._optionEx({ mandatory: true }, flags, description, fn, defaultValue);
requiredOption(flags, description, parseArg, defaultValue) {
return this._optionEx({ mandatory: true }, flags, description, parseArg, defaultValue);
}

/**
Expand Down Expand Up @@ -1147,29 +1120,29 @@ Expecting one of '${allowedValues.join("', '")}'`);
}

/**
* Check this.args against expected this._args.
* Check this.args against expected this.registeredArguments.
*
* @private
*/

_checkNumberOfArguments() {
// too few
this._args.forEach((arg, i) => {
this.registeredArguments.forEach((arg, i) => {
if (arg.required && this.args[i] == null) {
this.missingArgument(arg.name());
}
});
// too many
if (this._args.length > 0 && this._args[this._args.length - 1].variadic) {
if (this.registeredArguments.length > 0 && this.registeredArguments[this.registeredArguments.length - 1].variadic) {
return;
}
if (this.args.length > this._args.length) {
if (this.args.length > this.registeredArguments.length) {
this._excessArguments(this.args);
}
}

/**
* Process this.args using this._args and save as this.processedArgs!
* Process this.args using this.registeredArguments and save as this.processedArgs!
*
* @private
*/
Expand All @@ -1188,7 +1161,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
this._checkNumberOfArguments();

const processedArgs = [];
this._args.forEach((declaredArg, index) => {
this.registeredArguments.forEach((declaredArg, index) => {
let value = declaredArg.defaultValue;
if (declaredArg.variadic) {
// Collect together remaining arguments for passing together as an array.
Expand Down Expand Up @@ -1803,7 +1776,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
_excessArguments(receivedArgs) {
if (this._allowExcessArguments) return;

const expected = this._args.length;
const expected = this.registeredArguments.length;
const s = (expected === 1) ? '' : 's';
const forSubcommand = this.parent ? ` for '${this.name()}'` : '';
const message = `error: too many arguments${forSubcommand}. Expected ${expected} argument${s} but got ${receivedArgs.length}.`;
Expand Down Expand Up @@ -1943,13 +1916,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
if (str === undefined) {
if (this._usage) return this._usage;

const args = this._args.map((arg) => {
const args = this.registeredArguments.map((arg) => {
return humanReadableArgName(arg);
});
return [].concat(
(this.options.length || this._hasHelpOption ? '[options]' : []),
(this.commands.length ? '[command]' : []),
(this._args.length ? args : [])
(this.registeredArguments.length ? args : [])
).join(' ');
}

Expand Down
8 changes: 4 additions & 4 deletions lib/help.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,14 @@ class Help {
visibleArguments(cmd) {
// Side effect! Apply the legacy descriptions before the arguments are displayed.
if (cmd._argsDescription) {
cmd._args.forEach(argument => {
cmd.registeredArguments.forEach(argument => {
argument.description = argument.description || cmd._argsDescription[argument.name()] || '';
});
}

// If there are any arguments with a description then return all the arguments.
if (cmd._args.find(argument => argument.description)) {
return cmd._args;
if (cmd.registeredArguments.find(argument => argument.description)) {
return cmd.registeredArguments;
}
return [];
}
Expand All @@ -142,7 +142,7 @@ class Help {

subcommandTerm(cmd) {
// Legacy. Ignores custom usage string, and nested commands.
const args = cmd._args.map(arg => humanReadableArgName(arg)).join(' ');
const args = cmd.registeredArguments.map(arg => humanReadableArgName(arg)).join(' ');
return cmd._name +
(cmd._aliases[0] ? '|' + cmd._aliases[0] : '') +
(cmd.options.length ? ' [options]' : '') + // simplistic check for non-help option
Expand Down
6 changes: 3 additions & 3 deletions lib/option.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class Option {
/**
* Set the default value, and optionally supply the description to be displayed in the help.
*
* @param {any} value
* @param {*} value
* @param {string} [description]
* @return {Option}
*/
Expand All @@ -59,7 +59,7 @@ class Option {
* new Option('--color').default('GREYSCALE').preset('RGB');
* new Option('--donate [amount]').preset('20').argParser(parseFloat);
*
* @param {any} arg
* @param {*} arg
* @return {Option}
*/

Expand Down Expand Up @@ -274,7 +274,7 @@ class DualOptions {
/**
* Did the value come from the option, and not from possible matching dual option?
*
* @param {any} value
* @param {*} value
* @param {Option} option
* @returns {boolean}
*/
Expand Down
18 changes: 9 additions & 9 deletions tests/command.argumentVariations.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const commander = require('../');
// and not exhaustively testing all methods elsewhere.

test.each(getSingleArgCases('<explicit-required>'))('when add "<arg>" using %s then argument required', (methodName, cmd) => {
const argument = cmd._args[0];
const argument = cmd.registeredArguments[0];
const expectedShape = {
_name: 'explicit-required',
required: true,
Expand All @@ -15,7 +15,7 @@ test.each(getSingleArgCases('<explicit-required>'))('when add "<arg>" using %s t
});

test.each(getSingleArgCases('implicit-required'))('when add "arg" using %s then argument required', (methodName, cmd) => {
const argument = cmd._args[0];
const argument = cmd.registeredArguments[0];
const expectedShape = {
_name: 'implicit-required',
required: true,
Expand All @@ -26,7 +26,7 @@ test.each(getSingleArgCases('implicit-required'))('when add "arg" using %s then
});

test.each(getSingleArgCases('[optional]'))('when add "[arg]" using %s then argument optional', (methodName, cmd) => {
const argument = cmd._args[0];
const argument = cmd.registeredArguments[0];
const expectedShape = {
_name: 'optional',
required: false,
Expand All @@ -37,7 +37,7 @@ test.each(getSingleArgCases('[optional]'))('when add "[arg]" using %s then argum
});

test.each(getSingleArgCases('<explicit-required...>'))('when add "<arg...>" using %s then argument required and variadic', (methodName, cmd) => {
const argument = cmd._args[0];
const argument = cmd.registeredArguments[0];
const expectedShape = {
_name: 'explicit-required',
required: true,
Expand All @@ -48,7 +48,7 @@ test.each(getSingleArgCases('<explicit-required...>'))('when add "<arg...>" usin
});

test.each(getSingleArgCases('implicit-required...'))('when add "arg..." using %s then argument required and variadic', (methodName, cmd) => {
const argument = cmd._args[0];
const argument = cmd.registeredArguments[0];
const expectedShape = {
_name: 'implicit-required',
required: true,
Expand All @@ -59,7 +59,7 @@ test.each(getSingleArgCases('implicit-required...'))('when add "arg..." using %s
});

test.each(getSingleArgCases('[optional...]'))('when add "[arg...]" using %s then argument optional and variadic', (methodName, cmd) => {
const argument = cmd._args[0];
const argument = cmd.registeredArguments[0];
const expectedShape = {
_name: 'optional',
required: false,
Expand All @@ -79,8 +79,8 @@ function getSingleArgCases(arg) {
}

test.each(getMultipleArgCases('<first>', '[second]'))('when add two arguments using %s then two arguments', (methodName, cmd) => {
expect(cmd._args[0].name()).toEqual('first');
expect(cmd._args[1].name()).toEqual('second');
expect(cmd.registeredArguments[0].name()).toEqual('first');
expect(cmd.registeredArguments[1].name()).toEqual('second');
});

function getMultipleArgCases(arg1, arg2) {
Expand All @@ -99,6 +99,6 @@ test('when add arguments using multiple methods then all added', () => {
cmd.arguments('<arg3> <arg4>');
cmd.argument('<arg5>');
cmd.addArgument(new commander.Argument('arg6'));
const argNames = cmd._args.map(arg => arg.name());
const argNames = cmd.registeredArguments.map(arg => arg.name());
expect(argNames).toEqual(['arg1', 'arg2', 'arg3', 'arg4', 'arg5', 'arg6']);
});
Loading

0 comments on commit b0b6992

Please sign in to comment.