Skip to content

Commit 7d23437

Browse files
shadowspawnaweebit
andauthored
Add public Arguments property (#2010)
Co-authored-by: Wee Bit <[email protected]>
1 parent 8edcfd9 commit 7d23437

7 files changed

+52
-32
lines changed

docs/deprecated.md

+17
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ They are currently still available for backwards compatibility, but should not b
1515
- [InvalidOptionArgumentError](#invalidoptionargumenterror)
1616
- [Short option flag longer than a single character](#short-option-flag-longer-than-a-single-character)
1717
- [Import from `commander/esm.mjs`](#import-from-commanderesmmjs)
18+
- [cmd.\_args](#cmd_args)
1819

1920
## RegExp .option() parameter
2021

@@ -207,3 +208,19 @@ import { Command } from 'commander';
207208
```
208209

209210
README updated in Commander v9. Deprecated from Commander v9.
211+
212+
## cmd._args
213+
214+
This was always private, but was previously the only way to access the command `Argument` array.
215+
216+
```js
217+
const registeredArguments = program._args;
218+
```
219+
220+
The registered command arguments are now accessible via `.registeredArguments`.
221+
222+
```js
223+
const registeredArguments = program.registeredArguments;
224+
```
225+
226+
Deprecated from Commander v11.

lib/command.js

+14-13
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ class Command extends EventEmitter {
2929
this._allowUnknownOption = false;
3030
this._allowExcessArguments = true;
3131
/** @type {Argument[]} */
32-
this._args = [];
32+
this.registeredArguments = [];
33+
this._args = this.registeredArguments; // deprecated old name
3334
/** @type {string[]} */
3435
this.args = []; // cli args with options removed
3536
this.rawArgs = [];
@@ -356,14 +357,14 @@ class Command extends EventEmitter {
356357
* @return {Command} `this` command for chaining
357358
*/
358359
addArgument(argument) {
359-
const previousArgument = this._args.slice(-1)[0];
360+
const previousArgument = this.registeredArguments.slice(-1)[0];
360361
if (previousArgument && previousArgument.variadic) {
361362
throw new Error(`only the last argument can be variadic '${previousArgument.name()}'`);
362363
}
363364
if (argument.required && argument.defaultValue !== undefined && argument.parseArg === undefined) {
364365
throw new Error(`a default value for a required argument is never used: '${argument.name()}'`);
365366
}
366-
this._args.push(argument);
367+
this.registeredArguments.push(argument);
367368
return this;
368369
}
369370

@@ -483,7 +484,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
483484
action(fn) {
484485
const listener = (args) => {
485486
// The .action callback takes an extra parameter which is the command or options.
486-
const expectedArgsCount = this._args.length;
487+
const expectedArgsCount = this.registeredArguments.length;
487488
const actionArgs = args.slice(0, expectedArgsCount);
488489
if (this._storeOptionsAsProperties) {
489490
actionArgs[expectedArgsCount] = this; // backwards compatible "options"
@@ -1109,29 +1110,29 @@ Expecting one of '${allowedValues.join("', '")}'`);
11091110
}
11101111

11111112
/**
1112-
* Check this.args against expected this._args.
1113+
* Check this.args against expected this.registeredArguments.
11131114
*
11141115
* @api private
11151116
*/
11161117

11171118
_checkNumberOfArguments() {
11181119
// too few
1119-
this._args.forEach((arg, i) => {
1120+
this.registeredArguments.forEach((arg, i) => {
11201121
if (arg.required && this.args[i] == null) {
11211122
this.missingArgument(arg.name());
11221123
}
11231124
});
11241125
// too many
1125-
if (this._args.length > 0 && this._args[this._args.length - 1].variadic) {
1126+
if (this.registeredArguments.length > 0 && this.registeredArguments[this.registeredArguments.length - 1].variadic) {
11261127
return;
11271128
}
1128-
if (this.args.length > this._args.length) {
1129+
if (this.args.length > this.registeredArguments.length) {
11291130
this._excessArguments(this.args);
11301131
}
11311132
}
11321133

11331134
/**
1134-
* Process this.args using this._args and save as this.processedArgs!
1135+
* Process this.args using this.registeredArguments and save as this.processedArgs!
11351136
*
11361137
* @api private
11371138
*/
@@ -1150,7 +1151,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
11501151
this._checkNumberOfArguments();
11511152

11521153
const processedArgs = [];
1153-
this._args.forEach((declaredArg, index) => {
1154+
this.registeredArguments.forEach((declaredArg, index) => {
11541155
let value = declaredArg.defaultValue;
11551156
if (declaredArg.variadic) {
11561157
// Collect together remaining arguments for passing together as an array.
@@ -1765,7 +1766,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
17651766
_excessArguments(receivedArgs) {
17661767
if (this._allowExcessArguments) return;
17671768

1768-
const expected = this._args.length;
1769+
const expected = this.registeredArguments.length;
17691770
const s = (expected === 1) ? '' : 's';
17701771
const forSubcommand = this.parent ? ` for '${this.name()}'` : '';
17711772
const message = `error: too many arguments${forSubcommand}. Expected ${expected} argument${s} but got ${receivedArgs.length}.`;
@@ -1905,13 +1906,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
19051906
if (str === undefined) {
19061907
if (this._usage) return this._usage;
19071908

1908-
const args = this._args.map((arg) => {
1909+
const args = this.registeredArguments.map((arg) => {
19091910
return humanReadableArgName(arg);
19101911
});
19111912
return [].concat(
19121913
(this.options.length || this._hasHelpOption ? '[options]' : []),
19131914
(this.commands.length ? '[command]' : []),
1914-
(this._args.length ? args : [])
1915+
(this.registeredArguments.length ? args : [])
19151916
).join(' ');
19161917
}
19171918

lib/help.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,14 @@ class Help {
121121
visibleArguments(cmd) {
122122
// Side effect! Apply the legacy descriptions before the arguments are displayed.
123123
if (cmd._argsDescription) {
124-
cmd._args.forEach(argument => {
124+
cmd.registeredArguments.forEach(argument => {
125125
argument.description = argument.description || cmd._argsDescription[argument.name()] || '';
126126
});
127127
}
128128

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

143143
subcommandTerm(cmd) {
144144
// Legacy. Ignores custom usage string, and nested commands.
145-
const args = cmd._args.map(arg => humanReadableArgName(arg)).join(' ');
145+
const args = cmd.registeredArguments.map(arg => humanReadableArgName(arg)).join(' ');
146146
return cmd._name +
147147
(cmd._aliases[0] ? '|' + cmd._aliases[0] : '') +
148148
(cmd.options.length ? ' [options]' : '') + // simplistic check for non-help option

tests/command.argumentVariations.test.js

+9-9
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const commander = require('../');
44
// and not exhaustively testing all methods elsewhere.
55

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

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

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

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

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

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

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

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

tests/command.createArgument.test.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,20 @@ class MyCommand extends commander.Command {
2121
test('when override createArgument then used for argument()', () => {
2222
const program = new MyCommand();
2323
program.argument('<file>');
24-
expect(program._args.length).toEqual(1);
25-
expect(program._args[0].myProperty).toEqual('MyArgument');
24+
expect(program.registeredArguments.length).toEqual(1);
25+
expect(program.registeredArguments[0].myProperty).toEqual('MyArgument');
2626
});
2727

2828
test('when override createArgument then used for arguments()', () => {
2929
const program = new MyCommand();
3030
program.arguments('<file>');
31-
expect(program._args.length).toEqual(1);
32-
expect(program._args[0].myProperty).toEqual('MyArgument');
31+
expect(program.registeredArguments.length).toEqual(1);
32+
expect(program.registeredArguments[0].myProperty).toEqual('MyArgument');
3333
});
3434

3535
test('when override createArgument and createCommand then used for argument of command()', () => {
3636
const program = new MyCommand();
3737
const sub = program.command('sub <file>');
38-
expect(sub._args.length).toEqual(1);
39-
expect(sub._args[0].myProperty).toEqual('MyArgument');
38+
expect(sub.registeredArguments.length).toEqual(1);
39+
expect(sub.registeredArguments[0].myProperty).toEqual('MyArgument');
4040
});

typings/index.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ export class Command {
290290
processedArgs: any[];
291291
readonly commands: readonly Command[];
292292
readonly options: readonly Option[];
293+
readonly registeredArguments: readonly Argument[];
293294
parent: Command | null;
294295

295296
constructor(name?: string);

typings/index.test-d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ expectType<string[]>(program.args);
3030
expectType<any[]>(program.processedArgs);
3131
expectType<readonly commander.Command[]>(program.commands);
3232
expectType<readonly commander.Option[]>(program.options);
33+
expectType<readonly commander.Argument[]>(program.registeredArguments);
3334
expectType<commander.Command | null>(program.parent);
3435

3536
// version

0 commit comments

Comments
 (0)