commander 13.0.0-0 → 13.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Readme.md +13 -5
- package/lib/command.js +86 -9
- package/lib/option.js +34 -15
- package/package.json +2 -2
- package/typings/index.d.ts +19 -3
package/Readme.md
CHANGED
|
@@ -175,7 +175,15 @@ const program = new Command();
|
|
|
175
175
|
|
|
176
176
|
## Options
|
|
177
177
|
|
|
178
|
-
Options are defined with the `.option()` method, also serving as documentation for the options. Each option can have a short flag (single character) and a long name, separated by a comma or space or vertical bar ('|').
|
|
178
|
+
Options are defined with the `.option()` method, also serving as documentation for the options. Each option can have a short flag (single character) and a long name, separated by a comma or space or vertical bar ('|'). To allow a wider range of short-ish flags than just
|
|
179
|
+
single characters, you may also have two long options. Examples:
|
|
180
|
+
|
|
181
|
+
```js
|
|
182
|
+
program
|
|
183
|
+
.option('-p, --port <number>', 'server port number')
|
|
184
|
+
.option('--trace', 'add extra debugging output')
|
|
185
|
+
.option('--ws, --workspace <name>', 'use a custom workspace')
|
|
186
|
+
```
|
|
179
187
|
|
|
180
188
|
The parsed options can be accessed by calling `.opts()` on a `Command` object, and are passed to the action handler.
|
|
181
189
|
|
|
@@ -921,9 +929,11 @@ program.helpCommand('assist [command]', 'show assistance');
|
|
|
921
929
|
### More configuration
|
|
922
930
|
|
|
923
931
|
The built-in help is formatted using the Help class.
|
|
924
|
-
You can configure the
|
|
932
|
+
You can configure the help by modifying data properties and methods using `.configureHelp()`, or by subclassing Help using `.createHelp()` .
|
|
933
|
+
|
|
934
|
+
Simple properties include `sortSubcommands`, `sortOptions`, and `showGlobalOptions`. You can add color using the style methods like `styleTitle()`.
|
|
925
935
|
|
|
926
|
-
For more detail see (./docs/help-in-depth.md)
|
|
936
|
+
For more detail and examples of changing the displayed text, color, and layout see (./docs/help-in-depth.md)
|
|
927
937
|
|
|
928
938
|
## Custom event listeners
|
|
929
939
|
|
|
@@ -957,8 +967,6 @@ program.parse(['--port', '80'], { from: 'user' }); // just user supplied argumen
|
|
|
957
967
|
|
|
958
968
|
Use parseAsync instead of parse if any of your action handlers are async.
|
|
959
969
|
|
|
960
|
-
If you want to parse multiple times, create a new program each time. Calling parse does not clear out any previous state.
|
|
961
|
-
|
|
962
970
|
### Parsing Configuration
|
|
963
971
|
|
|
964
972
|
If the default parsing does not suit your needs, there are some behaviours to support other usage patterns.
|
package/lib/command.js
CHANGED
|
@@ -55,6 +55,7 @@ class Command extends EventEmitter {
|
|
|
55
55
|
/** @type {(boolean | string)} */
|
|
56
56
|
this._showHelpAfterError = false;
|
|
57
57
|
this._showSuggestionAfterError = true;
|
|
58
|
+
this._savedState = null; // used in save/restoreStateBeforeParse
|
|
58
59
|
|
|
59
60
|
// see configureOutput() for docs
|
|
60
61
|
this._outputConfiguration = {
|
|
@@ -755,7 +756,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
755
756
|
* @example
|
|
756
757
|
* program
|
|
757
758
|
* .option('-p, --pepper', 'add pepper')
|
|
758
|
-
* .option('
|
|
759
|
+
* .option('--pt, --pizza-type <TYPE>', 'type of pizza') // required option-argument
|
|
759
760
|
* .option('-c, --cheese [CHEESE]', 'add extra cheese', 'mozzarella') // optional option-argument with default
|
|
760
761
|
* .option('-t, --tip <VALUE>', 'add tip to purchase cost', parseFloat) // custom parse function
|
|
761
762
|
*
|
|
@@ -1069,6 +1070,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1069
1070
|
*/
|
|
1070
1071
|
|
|
1071
1072
|
parse(argv, parseOptions) {
|
|
1073
|
+
this._prepareForParse();
|
|
1072
1074
|
const userArgs = this._prepareUserArgs(argv, parseOptions);
|
|
1073
1075
|
this._parseCommand([], userArgs);
|
|
1074
1076
|
|
|
@@ -1097,12 +1099,82 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1097
1099
|
*/
|
|
1098
1100
|
|
|
1099
1101
|
async parseAsync(argv, parseOptions) {
|
|
1102
|
+
this._prepareForParse();
|
|
1100
1103
|
const userArgs = this._prepareUserArgs(argv, parseOptions);
|
|
1101
1104
|
await this._parseCommand([], userArgs);
|
|
1102
1105
|
|
|
1103
1106
|
return this;
|
|
1104
1107
|
}
|
|
1105
1108
|
|
|
1109
|
+
_prepareForParse() {
|
|
1110
|
+
if (this._savedState === null) {
|
|
1111
|
+
this.saveStateBeforeParse();
|
|
1112
|
+
} else {
|
|
1113
|
+
this.restoreStateBeforeParse();
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
/**
|
|
1118
|
+
* Called the first time parse is called to save state and allow a restore before subsequent calls to parse.
|
|
1119
|
+
* Not usually called directly, but available for subclasses to save their custom state.
|
|
1120
|
+
*
|
|
1121
|
+
* This is called in a lazy way. Only commands used in parsing chain will have state saved.
|
|
1122
|
+
*/
|
|
1123
|
+
saveStateBeforeParse() {
|
|
1124
|
+
this._savedState = {
|
|
1125
|
+
// name is stable if supplied by author, but may be unspecified for root command and deduced during parsing
|
|
1126
|
+
_name: this._name,
|
|
1127
|
+
// option values before parse have default values (including false for negated options)
|
|
1128
|
+
// shallow clones
|
|
1129
|
+
_optionValues: { ...this._optionValues },
|
|
1130
|
+
_optionValueSources: { ...this._optionValueSources },
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
/**
|
|
1135
|
+
* Restore state before parse for calls after the first.
|
|
1136
|
+
* Not usually called directly, but available for subclasses to save their custom state.
|
|
1137
|
+
*
|
|
1138
|
+
* This is called in a lazy way. Only commands used in parsing chain will have state restored.
|
|
1139
|
+
*/
|
|
1140
|
+
restoreStateBeforeParse() {
|
|
1141
|
+
if (this._storeOptionsAsProperties)
|
|
1142
|
+
throw new Error(`Can not call parse again when storeOptionsAsProperties is true.
|
|
1143
|
+
- either make a new Command for each call to parse, or stop storing options as properties`);
|
|
1144
|
+
|
|
1145
|
+
// clear state from _prepareUserArgs
|
|
1146
|
+
this._name = this._savedState._name;
|
|
1147
|
+
this._scriptPath = null;
|
|
1148
|
+
this.rawArgs = [];
|
|
1149
|
+
// clear state from setOptionValueWithSource
|
|
1150
|
+
this._optionValues = { ...this._savedState._optionValues };
|
|
1151
|
+
this._optionValueSources = { ...this._savedState._optionValueSources };
|
|
1152
|
+
// clear state from _parseCommand
|
|
1153
|
+
this.args = [];
|
|
1154
|
+
// clear state from _processArguments
|
|
1155
|
+
this.processedArgs = [];
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
/**
|
|
1159
|
+
* Throw if expected executable is missing. Add lots of help for author.
|
|
1160
|
+
*
|
|
1161
|
+
* @param {string} executableFile
|
|
1162
|
+
* @param {string} executableDir
|
|
1163
|
+
* @param {string} subcommandName
|
|
1164
|
+
*/
|
|
1165
|
+
_checkForMissingExecutable(executableFile, executableDir, subcommandName) {
|
|
1166
|
+
if (fs.existsSync(executableFile)) return;
|
|
1167
|
+
|
|
1168
|
+
const executableDirMessage = executableDir
|
|
1169
|
+
? `searched for local subcommand relative to directory '${executableDir}'`
|
|
1170
|
+
: 'no directory for search for local subcommand, use .executableDir() to supply a custom directory';
|
|
1171
|
+
const executableMissing = `'${executableFile}' does not exist
|
|
1172
|
+
- if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
|
|
1173
|
+
- if the default executable name is not suitable, use the executableFile option to supply a custom name or path
|
|
1174
|
+
- ${executableDirMessage}`;
|
|
1175
|
+
throw new Error(executableMissing);
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1106
1178
|
/**
|
|
1107
1179
|
* Execute a sub-command executable.
|
|
1108
1180
|
*
|
|
@@ -1186,6 +1258,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1186
1258
|
proc = childProcess.spawn(executableFile, args, { stdio: 'inherit' });
|
|
1187
1259
|
}
|
|
1188
1260
|
} else {
|
|
1261
|
+
this._checkForMissingExecutable(
|
|
1262
|
+
executableFile,
|
|
1263
|
+
executableDir,
|
|
1264
|
+
subcommand._name,
|
|
1265
|
+
);
|
|
1189
1266
|
args.unshift(executableFile);
|
|
1190
1267
|
// add executable arguments to spawn
|
|
1191
1268
|
args = incrementNodeInspectorPort(process.execArgv).concat(args);
|
|
@@ -1224,14 +1301,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1224
1301
|
proc.on('error', (err) => {
|
|
1225
1302
|
// @ts-ignore: because err.code is an unknown property
|
|
1226
1303
|
if (err.code === 'ENOENT') {
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
- if the default executable name is not suitable, use the executableFile option to supply a custom name or path
|
|
1233
|
-
- ${executableDirMessage}`;
|
|
1234
|
-
throw new Error(executableMissing);
|
|
1304
|
+
this._checkForMissingExecutable(
|
|
1305
|
+
executableFile,
|
|
1306
|
+
executableDir,
|
|
1307
|
+
subcommand._name,
|
|
1308
|
+
);
|
|
1235
1309
|
// @ts-ignore: because err.code is an unknown property
|
|
1236
1310
|
} else if (err.code === 'EACCES') {
|
|
1237
1311
|
throw new Error(`'${executableFile}' not executable`);
|
|
@@ -1261,6 +1335,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1261
1335
|
const subCommand = this._findCommand(commandName);
|
|
1262
1336
|
if (!subCommand) this.help({ error: true });
|
|
1263
1337
|
|
|
1338
|
+
subCommand._prepareForParse();
|
|
1264
1339
|
let promiseChain;
|
|
1265
1340
|
promiseChain = this._chainOrCallSubCommandHook(
|
|
1266
1341
|
promiseChain,
|
|
@@ -1638,6 +1713,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1638
1713
|
* Parse options from `argv` removing known options,
|
|
1639
1714
|
* and return argv split into operands and unknown arguments.
|
|
1640
1715
|
*
|
|
1716
|
+
* Side effects: modifies command by storing options. Does not reset state if called again.
|
|
1717
|
+
*
|
|
1641
1718
|
* Examples:
|
|
1642
1719
|
*
|
|
1643
1720
|
* argv => operands, unknown
|
package/lib/option.js
CHANGED
|
@@ -18,7 +18,7 @@ class Option {
|
|
|
18
18
|
this.variadic = /\w\.\.\.[>\]]$/.test(flags); // The option can take multiple values.
|
|
19
19
|
this.mandatory = false; // The option must have a value after parsing, which usually means it must be specified on command line.
|
|
20
20
|
const optionFlags = splitOptionFlags(flags);
|
|
21
|
-
this.short = optionFlags.shortFlag;
|
|
21
|
+
this.short = optionFlags.shortFlag; // May be a short flag, undefined, or even a long flag (if option has two long flags).
|
|
22
22
|
this.long = optionFlags.longFlag;
|
|
23
23
|
this.negate = false;
|
|
24
24
|
if (this.long) {
|
|
@@ -321,25 +321,44 @@ function splitOptionFlags(flags) {
|
|
|
321
321
|
const longFlagExp = /^--[^-]/;
|
|
322
322
|
|
|
323
323
|
const flagParts = flags.split(/[ |,]+/).concat('guard');
|
|
324
|
+
// Normal is short and/or long.
|
|
324
325
|
if (shortFlagExp.test(flagParts[0])) shortFlag = flagParts.shift();
|
|
325
326
|
if (longFlagExp.test(flagParts[0])) longFlag = flagParts.shift();
|
|
327
|
+
// Long then short. Rarely used but fine.
|
|
328
|
+
if (!shortFlag && shortFlagExp.test(flagParts[0]))
|
|
329
|
+
shortFlag = flagParts.shift();
|
|
330
|
+
// Allow two long flags, like '--ws, --workspace'
|
|
331
|
+
// This is the supported way to have a shortish option flag.
|
|
332
|
+
if (!shortFlag && longFlagExp.test(flagParts[0])) {
|
|
333
|
+
shortFlag = longFlag;
|
|
334
|
+
longFlag = flagParts.shift();
|
|
335
|
+
}
|
|
326
336
|
|
|
327
|
-
// Check for
|
|
328
|
-
if (
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
)
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
+
// Check for unprocessed flag. Fail noisily rather than silently ignore.
|
|
338
|
+
if (flagParts[0].startsWith('-')) {
|
|
339
|
+
const unsupportedFlag = flagParts[0];
|
|
340
|
+
const baseError = `option creation failed due to '${unsupportedFlag}' in option flags '${flags}'`;
|
|
341
|
+
if (/^-[^-][^-]/.test(unsupportedFlag))
|
|
342
|
+
throw new Error(
|
|
343
|
+
`${baseError}
|
|
344
|
+
- a short flag is a single dash and a single character
|
|
345
|
+
- either use a single dash and a single character (for a short flag)
|
|
346
|
+
- or use a double dash for a long option (and can have two, like '--ws, --workspace')`,
|
|
347
|
+
);
|
|
348
|
+
if (shortFlagExp.test(unsupportedFlag))
|
|
349
|
+
throw new Error(`${baseError}
|
|
350
|
+
- too many short flags`);
|
|
351
|
+
if (longFlagExp.test(unsupportedFlag))
|
|
352
|
+
throw new Error(`${baseError}
|
|
353
|
+
- too many long flags`);
|
|
354
|
+
|
|
355
|
+
throw new Error(`${baseError}
|
|
356
|
+
- unrecognised flag format`);
|
|
357
|
+
}
|
|
358
|
+
if (shortFlag === undefined && longFlag === undefined)
|
|
337
359
|
throw new Error(
|
|
338
|
-
`
|
|
360
|
+
`option creation failed due to no flags found in '${flags}'.`,
|
|
339
361
|
);
|
|
340
|
-
// Generic error if failed to find a flag or an unexpected flag left over.
|
|
341
|
-
if (!(shortFlag || longFlag) || flagParts[0].startsWith('-'))
|
|
342
|
-
throw new Error(`invalid Option flags: '${flags}'`);
|
|
343
362
|
|
|
344
363
|
return { shortFlag, longFlag };
|
|
345
364
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "commander",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.1.0",
|
|
4
4
|
"description": "the complete solution for node.js command-line programs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"commander",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"@eslint/js": "^9.4.0",
|
|
64
64
|
"@types/jest": "^29.2.4",
|
|
65
65
|
"@types/node": "^22.7.4",
|
|
66
|
-
"eslint": "^
|
|
66
|
+
"eslint": "^9.17.0",
|
|
67
67
|
"eslint-config-prettier": "^9.1.0",
|
|
68
68
|
"eslint-plugin-jest": "^28.3.0",
|
|
69
69
|
"globals": "^15.7.0",
|
package/typings/index.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
// Type definitions for commander
|
|
2
2
|
// Original definitions by: Alan Agius <https://github.com/alan-agius4>, Marcelo Dezem <https://github.com/mdezem>, vvakame <https://github.com/vvakame>, Jules Randolph <https://github.com/sveinburne>
|
|
3
3
|
|
|
4
|
-
// Using method rather than property for method-signature-style, to document method overloads separately. Allow either.
|
|
5
|
-
/* eslint-disable @typescript-eslint/method-signature-style */
|
|
6
4
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
7
5
|
|
|
8
6
|
// This is a trick to encourage editor to suggest the known literals while still
|
|
@@ -619,7 +617,7 @@ export class Command {
|
|
|
619
617
|
* ```js
|
|
620
618
|
* program
|
|
621
619
|
* .option('-p, --pepper', 'add pepper')
|
|
622
|
-
* .option('
|
|
620
|
+
* .option('--pt, --pizza-type <TYPE>', 'type of pizza') // required option-argument
|
|
623
621
|
* .option('-c, --cheese [CHEESE]', 'add extra cheese', 'mozzarella') // optional option-argument with default
|
|
624
622
|
* .option('-t, --tip <VALUE>', 'add tip to purchase cost', parseFloat) // custom parse function
|
|
625
623
|
* ```
|
|
@@ -823,10 +821,28 @@ export class Command {
|
|
|
823
821
|
parseOptions?: ParseOptions,
|
|
824
822
|
): Promise<this>;
|
|
825
823
|
|
|
824
|
+
/**
|
|
825
|
+
* Called the first time parse is called to save state and allow a restore before subsequent calls to parse.
|
|
826
|
+
* Not usually called directly, but available for subclasses to save their custom state.
|
|
827
|
+
*
|
|
828
|
+
* This is called in a lazy way. Only commands used in parsing chain will have state saved.
|
|
829
|
+
*/
|
|
830
|
+
saveStateBeforeParse(): void;
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* Restore state before parse for calls after the first.
|
|
834
|
+
* Not usually called directly, but available for subclasses to save their custom state.
|
|
835
|
+
*
|
|
836
|
+
* This is called in a lazy way. Only commands used in parsing chain will have state restored.
|
|
837
|
+
*/
|
|
838
|
+
restoreStateBeforeParse(): void;
|
|
839
|
+
|
|
826
840
|
/**
|
|
827
841
|
* Parse options from `argv` removing known options,
|
|
828
842
|
* and return argv split into operands and unknown arguments.
|
|
829
843
|
*
|
|
844
|
+
* Side effects: modifies command by storing options. Does not reset state if called again.
|
|
845
|
+
*
|
|
830
846
|
* argv => operands, unknown
|
|
831
847
|
* --known kkk op => [op], []
|
|
832
848
|
* op --known kkk => [op], []
|