commander 11.0.0 → 11.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 CHANGED
@@ -191,7 +191,7 @@ serve --port=80
191
191
 
192
192
  You can use `--` to indicate the end of the options, and any remaining arguments will be used without being interpreted.
193
193
 
194
- By default options on the command line are not positional, and can be specified before or after other arguments.
194
+ By default, options on the command line are not positional, and can be specified before or after other arguments.
195
195
 
196
196
  There are additional related routines for when `.opts()` is not enough:
197
197
 
@@ -234,7 +234,7 @@ pizza details:
234
234
  - cheese
235
235
  ```
236
236
 
237
- Multiple boolean short options may be combined together following the dash, and may be followed by a single short option taking a value.
237
+ Multiple boolean short options may be combined following the dash, and may be followed by a single short option taking a value.
238
238
  For example `-d -s -p cheese` may be written as `-ds -p cheese` or even `-dsp cheese`.
239
239
 
240
240
  Options with an expected option-argument are greedy and will consume the following argument whatever the value.
@@ -327,7 +327,7 @@ add cheese type mozzarella
327
327
  Options with an optional option-argument are not greedy and will ignore arguments starting with a dash.
328
328
  So `id` behaves as a boolean option for `--id -5`, but you can use a combined form if needed like `--id=-5`.
329
329
 
330
- For information about possible ambiguous cases, see [options taking varying arguments](./docs/options-taking-varying-arguments.md).
330
+ For information about possible ambiguous cases, see [options taking varying arguments](./docs/options-in-depth.md).
331
331
 
332
332
  ### Required option
333
333
 
@@ -379,7 +379,7 @@ Options: { number: [ '1', '2', '3' ], letter: true }
379
379
  Remaining arguments: [ 'operand' ]
380
380
  ```
381
381
 
382
- For information about possible ambiguous cases, see [options taking varying arguments](./docs/options-taking-varying-arguments.md).
382
+ For information about possible ambiguous cases, see [options taking varying arguments](./docs/options-in-depth.md).
383
383
 
384
384
  ### Version option
385
385
 
@@ -546,6 +546,8 @@ subcommand is specified ([example](./examples/defaultCommand.js)).
546
546
 
547
547
  You can add alternative names for a command with `.alias()`. ([example](./examples/alias.js))
548
548
 
549
+ `.command()` automatically copies the inherited settings from the parent command to the newly created subcommand. This is only done during creation, any later setting changes to the parent are not inherited.
550
+
549
551
  For safety, `.addCommand()` does not automatically copy the inherited settings from the parent command. There is a helper routine `.copyInheritedSettings()` for copying the settings when they are wanted.
550
552
 
551
553
  ### Command-arguments
@@ -676,7 +678,7 @@ async function main() {
676
678
  }
677
679
  ```
678
680
 
679
- 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
681
+ 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
680
682
  pass more arguments than declared, but you can make this an error with `.allowExcessArguments(false)`.
681
683
 
682
684
  ### Stand-alone executable (sub)commands
@@ -764,7 +766,7 @@ shell help spawn
764
766
  shell spawn --help
765
767
  ```
766
768
 
767
- Long descriptions are wrapped to fit the available width. (However, a description that includes a line-break followed by whitespace is assumed to be pre-formatted and not wrapped.)
769
+ Long descriptions are wrapped to fit the available width. (However, a description that includes a line-break followed by whitespace is assumed to be pre-formatted and not wrapped.)
768
770
 
769
771
  ### Custom help
770
772
 
@@ -852,8 +854,8 @@ error: unknown option '--hepl'
852
854
  The command name appears in the help, and is also used for locating stand-alone executable subcommands.
853
855
 
854
856
  You may specify the program name using `.name()` or in the Command constructor. For the program, Commander will
855
- fallback to using the script name from the full arguments passed into `.parse()`. However, the script name varies
856
- depending on how your program is launched so you may wish to specify it explicitly.
857
+ fall back to using the script name from the full arguments passed into `.parse()`. However, the script name varies
858
+ depending on how your program is launched, so you may wish to specify it explicitly.
857
859
 
858
860
  ```js
859
861
  program.name('pizza');
@@ -895,7 +897,7 @@ This may require additional disk space.
895
897
 
896
898
  ### .helpOption(flags, description)
897
899
 
898
- By default every command has a help option. You may change the default help flags and description. Pass false to disable the built-in help option.
900
+ By default, every command has a help option. You may change the default help flags and description. Pass false to disable the built-in help option.
899
901
 
900
902
  ```js
901
903
  program
@@ -969,7 +971,7 @@ program.parse(['-f', 'filename'], { from: 'user' });
969
971
 
970
972
  If the default parsing does not suit your needs, there are some behaviours to support other usage patterns.
971
973
 
972
- By default program options are recognised before and after subcommands. To only look for program options before subcommands, use `.enablePositionalOptions()`. This lets you use
974
+ By default, program options are recognised before and after subcommands. To only look for program options before subcommands, use `.enablePositionalOptions()`. This lets you use
973
975
  an option for a different purpose in subcommands.
974
976
 
975
977
  Example file: [positional-options.js](./examples/positional-options.js)
@@ -981,8 +983,8 @@ program -b subcommand
981
983
  program subcommand -b
982
984
  ```
983
985
 
984
- By default options are recognised before and after command-arguments. To only process options that come
985
- before the command-arguments, use `.passThroughOptions()`. This lets you pass the arguments and following options through to another program
986
+ By default, options are recognised before and after command-arguments. To only process options that come
987
+ before the command-arguments, use `.passThroughOptions()`. This lets you pass the arguments and following options through to another program
986
988
  without needing to use `--` to end the option processing.
987
989
  To use pass through options in a subcommand, the program needs to enable positional options.
988
990
 
@@ -995,15 +997,15 @@ program --port=80 arg
995
997
  program arg --port=80
996
998
  ```
997
999
 
998
- 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.
1000
+ 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.
999
1001
 
1000
- By default the argument processing does not display an error for more command-arguments than expected.
1002
+ By default, the argument processing does not display an error for more command-arguments than expected.
1001
1003
  To display an error for excess arguments, use`.allowExcessArguments(false)`.
1002
1004
 
1003
1005
  ### Legacy options as properties
1004
1006
 
1005
1007
  Before Commander 7, the option values were stored as properties on the command.
1006
- This was convenient to code but the downside was possible clashes with
1008
+ This was convenient to code, but the downside was possible clashes with
1007
1009
  existing properties of `Command`. You can revert to the old behaviour to run unmodified legacy code by using `.storeOptionsAsProperties()`.
1008
1010
 
1009
1011
  ```js
@@ -1027,7 +1029,7 @@ See [commander-js/extra-typings](https://github.com/commander-js/extra-typings)
1027
1029
  import { Command } from '@commander-js/extra-typings';
1028
1030
  ```
1029
1031
 
1030
- ts-node: If you use `ts-node` and stand-alone executable subcommands written as `.ts` files, you need to call your program through node to get the subcommands called correctly. e.g.
1032
+ ts-node: If you use `ts-node` and stand-alone executable subcommands written as `.ts` files, you need to call your program through node to get the subcommands called correctly. e.g.
1031
1033
 
1032
1034
  ```sh
1033
1035
  node -r ts-node/register pm.ts
@@ -1057,14 +1059,14 @@ You can enable `--harmony` option in two ways:
1057
1059
 
1058
1060
  An executable subcommand is launched as a separate child process.
1059
1061
 
1060
- If you are using the node inspector for [debugging](https://nodejs.org/en/docs/guides/debugging-getting-started/) executable subcommands using `node --inspect` et al,
1062
+ If you are using the node inspector for [debugging](https://nodejs.org/en/docs/guides/debugging-getting-started/) executable subcommands using `node --inspect` et al.,
1061
1063
  the inspector port is incremented by 1 for the spawned subcommand.
1062
1064
 
1063
1065
  If you are using VSCode to debug executable subcommands you need to set the `"autoAttachChildProcesses": true` flag in your launch.json configuration.
1064
1066
 
1065
1067
  ### npm run-script
1066
1068
 
1067
- By default when you call your program using run-script, `npm` will parse any options on the command-line and they will not reach your program. Use
1069
+ By default, when you call your program using run-script, `npm` will parse any options on the command-line and they will not reach your program. Use
1068
1070
  `--` to stop the npm option parsing and pass through all the arguments.
1069
1071
 
1070
1072
  The synopsis for [npm run-script](https://docs.npmjs.com/cli/v9/commands/npm-run-script) explicitly shows the `--` for this reason:
@@ -1087,7 +1089,7 @@ program.error('Custom processing has failed', { exitCode: 2, code: 'my.custom.er
1087
1089
 
1088
1090
  ### Override exit and output handling
1089
1091
 
1090
- By default Commander calls `process.exit` when it detects errors, or after displaying the help or version. You can override
1092
+ By default, Commander calls `process.exit` when it detects errors, or after displaying the help or version. You can override
1091
1093
  this behaviour and optionally supply a callback. The default override throws a `CommanderError`.
1092
1094
 
1093
1095
  The override callback is passed a `CommanderError` with properties `exitCode` number, `code` string, and `message`. The default override behaviour is to throw the error, except for async handling of executable subcommand completion which carries on. The normal display of error messages or version or help
@@ -1103,7 +1105,7 @@ try {
1103
1105
  }
1104
1106
  ```
1105
1107
 
1106
- By default Commander is configured for a command-line application and writes to stdout and stderr.
1108
+ By default, Commander is configured for a command-line application and writes to stdout and stderr.
1107
1109
  You can modify this behaviour for custom applications. In addition, you can modify the display of error messages.
1108
1110
 
1109
1111
  Example file: [configure-output.js](./examples/configure-output.js)
@@ -1129,7 +1131,7 @@ program
1129
1131
  There is more information available about:
1130
1132
 
1131
1133
  - [deprecated](./docs/deprecated.md) features still supported for backwards compatibility
1132
- - [options taking varying arguments](./docs/options-taking-varying-arguments.md)
1134
+ - [options taking varying arguments](./docs/options-in-depth.md)
1133
1135
  - [parsing life cycle and hooks](./docs/parsing-and-hooks.md)
1134
1136
 
1135
1137
  ## Support
package/index.js CHANGED
@@ -4,24 +4,23 @@ const { CommanderError, InvalidArgumentError } = require('./lib/error.js');
4
4
  const { Help } = require('./lib/help.js');
5
5
  const { Option } = require('./lib/option.js');
6
6
 
7
- // @ts-check
8
-
9
7
  /**
10
8
  * Expose the root command.
11
9
  */
12
10
 
13
11
  exports = module.exports = new Command();
14
12
  exports.program = exports; // More explicit access to global command.
15
- // Implicit export of createArgument, createCommand, and createOption.
13
+ // createArgument, createCommand, and createOption are implicitly available as they are methods on program.
16
14
 
17
15
  /**
18
16
  * Expose classes
19
17
  */
20
18
 
21
- exports.Argument = Argument;
22
19
  exports.Command = Command;
23
- exports.CommanderError = CommanderError;
20
+ exports.Option = Option;
21
+ exports.Argument = Argument;
24
22
  exports.Help = Help;
23
+
24
+ exports.CommanderError = CommanderError;
25
25
  exports.InvalidArgumentError = InvalidArgumentError;
26
26
  exports.InvalidOptionArgumentError = InvalidArgumentError; // Deprecated
27
- exports.Option = Option;
package/lib/argument.js CHANGED
@@ -1,7 +1,5 @@
1
1
  const { InvalidArgumentError } = require('./error.js');
2
2
 
3
- // @ts-check
4
-
5
3
  class Argument {
6
4
  /**
7
5
  * Initialize a new command argument with the given name and description.
@@ -66,7 +64,7 @@ class Argument {
66
64
  /**
67
65
  * Set the default value, and optionally supply the description to be displayed in the help.
68
66
  *
69
- * @param {any} value
67
+ * @param {*} value
70
68
  * @param {string} [description]
71
69
  * @return {Argument}
72
70
  */
package/lib/command.js CHANGED
@@ -10,8 +10,6 @@ const { Help } = require('./help.js');
10
10
  const { Option, splitOptionFlags, DualOptions } = require('./option.js');
11
11
  const { suggestSimilar } = require('./suggestSimilar');
12
12
 
13
- // @ts-check
14
-
15
13
  class Command extends EventEmitter {
16
14
  /**
17
15
  * Initialize a new `Command`.
@@ -29,7 +27,8 @@ class Command extends EventEmitter {
29
27
  this._allowUnknownOption = false;
30
28
  this._allowExcessArguments = true;
31
29
  /** @type {Argument[]} */
32
- this._args = [];
30
+ this.registeredArguments = [];
31
+ this._args = this.registeredArguments; // deprecated old name
33
32
  /** @type {string[]} */
34
33
  this.args = []; // cli args with options removed
35
34
  this.rawArgs = [];
@@ -109,6 +108,19 @@ class Command extends EventEmitter {
109
108
  return this;
110
109
  }
111
110
 
111
+ /**
112
+ * @returns {Command[]}
113
+ * @api private
114
+ */
115
+
116
+ _getCommandAndAncestors() {
117
+ const result = [];
118
+ for (let command = this; command; command = command.parent) {
119
+ result.push(command);
120
+ }
121
+ return result;
122
+ }
123
+
112
124
  /**
113
125
  * Define a command.
114
126
  *
@@ -343,14 +355,14 @@ class Command extends EventEmitter {
343
355
  * @return {Command} `this` command for chaining
344
356
  */
345
357
  addArgument(argument) {
346
- const previousArgument = this._args.slice(-1)[0];
358
+ const previousArgument = this.registeredArguments.slice(-1)[0];
347
359
  if (previousArgument && previousArgument.variadic) {
348
360
  throw new Error(`only the last argument can be variadic '${previousArgument.name()}'`);
349
361
  }
350
362
  if (argument.required && argument.defaultValue !== undefined && argument.parseArg === undefined) {
351
363
  throw new Error(`a default value for a required argument is never used: '${argument.name()}'`);
352
364
  }
353
- this._args.push(argument);
365
+ this.registeredArguments.push(argument);
354
366
  return this;
355
367
  }
356
368
 
@@ -470,7 +482,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
470
482
  action(fn) {
471
483
  const listener = (args) => {
472
484
  // The .action callback takes an extra parameter which is the command or options.
473
- const expectedArgsCount = this._args.length;
485
+ const expectedArgsCount = this.registeredArguments.length;
474
486
  const actionArgs = args.slice(0, expectedArgsCount);
475
487
  if (this._storeOptionsAsProperties) {
476
488
  actionArgs[expectedArgsCount] = this; // backwards compatible "options"
@@ -500,6 +512,28 @@ Expecting one of '${allowedValues.join("', '")}'`);
500
512
  return new Option(flags, description);
501
513
  }
502
514
 
515
+ /**
516
+ * Wrap parseArgs to catch 'commander.invalidArgument'.
517
+ *
518
+ * @param {Option | Argument} target
519
+ * @param {string} value
520
+ * @param {*} previous
521
+ * @param {string} invalidArgumentMessage
522
+ * @api private
523
+ */
524
+
525
+ _callParseArg(target, value, previous, invalidArgumentMessage) {
526
+ try {
527
+ return target.parseArg(value, previous);
528
+ } catch (err) {
529
+ if (err.code === 'commander.invalidArgument') {
530
+ const message = `${invalidArgumentMessage} ${err.message}`;
531
+ this.error(message, { exitCode: err.exitCode, code: err.code });
532
+ }
533
+ throw err;
534
+ }
535
+ }
536
+
503
537
  /**
504
538
  * Add an option.
505
539
  *
@@ -535,15 +569,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
535
569
  // custom processing
536
570
  const oldValue = this.getOptionValue(name);
537
571
  if (val !== null && option.parseArg) {
538
- try {
539
- val = option.parseArg(val, oldValue);
540
- } catch (err) {
541
- if (err.code === 'commander.invalidArgument') {
542
- const message = `${invalidValueMessage} ${err.message}`;
543
- this.error(message, { exitCode: err.exitCode, code: err.code });
544
- }
545
- throw err;
546
- }
572
+ val = this._callParseArg(option, val, oldValue, invalidValueMessage);
547
573
  } else if (val !== null && option.variadic) {
548
574
  val = option._concatValue(val, oldValue);
549
575
  }
@@ -605,57 +631,29 @@ Expecting one of '${allowedValues.join("', '")}'`);
605
631
  }
606
632
 
607
633
  /**
608
- * Define option with `flags`, `description` and optional
609
- * coercion `fn`.
634
+ * Define option with `flags`, `description`, and optional argument parsing function or `defaultValue` or both.
610
635
  *
611
- * The `flags` string contains the short and/or long flags,
612
- * separated by comma, a pipe or space. The following are all valid
613
- * all will output this way when `--help` is used.
636
+ * The `flags` string contains the short and/or long flags, separated by comma, a pipe or space. A required
637
+ * option-argument is indicated by `<>` and an optional option-argument by `[]`.
614
638
  *
615
- * "-p, --pepper"
616
- * "-p|--pepper"
617
- * "-p --pepper"
639
+ * See the README for more details, and see also addOption() and requiredOption().
618
640
  *
619
641
  * @example
620
- * // simple boolean defaulting to undefined
621
- * program.option('-p, --pepper', 'add pepper');
622
- *
623
- * program.pepper
624
- * // => undefined
625
- *
626
- * --pepper
627
- * program.pepper
628
- * // => true
629
- *
630
- * // simple boolean defaulting to true (unless non-negated option is also defined)
631
- * program.option('-C, --no-cheese', 'remove cheese');
632
- *
633
- * program.cheese
634
- * // => true
635
- *
636
- * --no-cheese
637
- * program.cheese
638
- * // => false
639
- *
640
- * // required argument
641
- * program.option('-C, --chdir <path>', 'change the working directory');
642
- *
643
- * --chdir /tmp
644
- * program.chdir
645
- * // => "/tmp"
646
- *
647
- * // optional argument
648
- * program.option('-c, --cheese [type]', 'add cheese [marble]');
642
+ * program
643
+ * .option('-p, --pepper', 'add pepper')
644
+ * .option('-p, --pizza-type <TYPE>', 'type of pizza') // required option-argument
645
+ * .option('-c, --cheese [CHEESE]', 'add extra cheese', 'mozzarella') // optional option-argument with default
646
+ * .option('-t, --tip <VALUE>', 'add tip to purchase cost', parseFloat) // custom parse function
649
647
  *
650
648
  * @param {string} flags
651
649
  * @param {string} [description]
652
- * @param {Function|*} [fn] - custom option processing function or default value
650
+ * @param {Function|*} [parseArg] - custom option processing function or default value
653
651
  * @param {*} [defaultValue]
654
652
  * @return {Command} `this` command for chaining
655
653
  */
656
654
 
657
- option(flags, description, fn, defaultValue) {
658
- return this._optionEx({}, flags, description, fn, defaultValue);
655
+ option(flags, description, parseArg, defaultValue) {
656
+ return this._optionEx({}, flags, description, parseArg, defaultValue);
659
657
  }
660
658
 
661
659
  /**
@@ -666,13 +664,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
666
664
  *
667
665
  * @param {string} flags
668
666
  * @param {string} [description]
669
- * @param {Function|*} [fn] - custom option processing function or default value
667
+ * @param {Function|*} [parseArg] - custom option processing function or default value
670
668
  * @param {*} [defaultValue]
671
669
  * @return {Command} `this` command for chaining
672
670
  */
673
671
 
674
- requiredOption(flags, description, fn, defaultValue) {
675
- return this._optionEx({ mandatory: true }, flags, description, fn, defaultValue);
672
+ requiredOption(flags, description, parseArg, defaultValue) {
673
+ return this._optionEx({ mandatory: true }, flags, description, parseArg, defaultValue);
676
674
  }
677
675
 
678
676
  /**
@@ -750,10 +748,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
750
748
  */
751
749
 
752
750
  storeOptionsAsProperties(storeAsProperties = true) {
753
- this._storeOptionsAsProperties = !!storeAsProperties;
754
751
  if (this.options.length) {
755
752
  throw new Error('call .storeOptionsAsProperties() before adding options');
756
753
  }
754
+ // if (Object.keys(this._optionValues).length) {
755
+ // throw new Error('call .storeOptionsAsProperties() before setting option values');
756
+ // }
757
+ this._storeOptionsAsProperties = !!storeAsProperties;
757
758
  return this;
758
759
  }
759
760
 
@@ -825,7 +826,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
825
826
  getOptionValueSourceWithGlobals(key) {
826
827
  // global overwrites local, like optsWithGlobals
827
828
  let source;
828
- getCommandAndParents(this).forEach((cmd) => {
829
+ this._getCommandAndAncestors().forEach((cmd) => {
829
830
  if (cmd.getOptionValueSource(key) !== undefined) {
830
831
  source = cmd.getOptionValueSource(key);
831
832
  }
@@ -1072,16 +1073,16 @@ Expecting one of '${allowedValues.join("', '")}'`);
1072
1073
  const subCommand = this._findCommand(commandName);
1073
1074
  if (!subCommand) this.help({ error: true });
1074
1075
 
1075
- let hookResult;
1076
- hookResult = this._chainOrCallSubCommandHook(hookResult, subCommand, 'preSubcommand');
1077
- hookResult = this._chainOrCall(hookResult, () => {
1076
+ let promiseChain;
1077
+ promiseChain = this._chainOrCallSubCommandHook(promiseChain, subCommand, 'preSubcommand');
1078
+ promiseChain = this._chainOrCall(promiseChain, () => {
1078
1079
  if (subCommand._executableHandler) {
1079
1080
  this._executeSubCommand(subCommand, operands.concat(unknown));
1080
1081
  } else {
1081
1082
  return subCommand._parseCommand(operands, unknown);
1082
1083
  }
1083
1084
  });
1084
- return hookResult;
1085
+ return promiseChain;
1085
1086
  }
1086
1087
 
1087
1088
  /**
@@ -1101,33 +1102,35 @@ Expecting one of '${allowedValues.join("', '")}'`);
1101
1102
  }
1102
1103
 
1103
1104
  // Fallback to parsing the help flag to invoke the help.
1104
- return this._dispatchSubcommand(subcommandName, [], [this._helpLongFlag]);
1105
+ return this._dispatchSubcommand(subcommandName, [], [
1106
+ this._helpLongFlag || this._helpShortFlag
1107
+ ]);
1105
1108
  }
1106
1109
 
1107
1110
  /**
1108
- * Check this.args against expected this._args.
1111
+ * Check this.args against expected this.registeredArguments.
1109
1112
  *
1110
1113
  * @api private
1111
1114
  */
1112
1115
 
1113
1116
  _checkNumberOfArguments() {
1114
1117
  // too few
1115
- this._args.forEach((arg, i) => {
1118
+ this.registeredArguments.forEach((arg, i) => {
1116
1119
  if (arg.required && this.args[i] == null) {
1117
1120
  this.missingArgument(arg.name());
1118
1121
  }
1119
1122
  });
1120
1123
  // too many
1121
- if (this._args.length > 0 && this._args[this._args.length - 1].variadic) {
1124
+ if (this.registeredArguments.length > 0 && this.registeredArguments[this.registeredArguments.length - 1].variadic) {
1122
1125
  return;
1123
1126
  }
1124
- if (this.args.length > this._args.length) {
1127
+ if (this.args.length > this.registeredArguments.length) {
1125
1128
  this._excessArguments(this.args);
1126
1129
  }
1127
1130
  }
1128
1131
 
1129
1132
  /**
1130
- * Process this.args using this._args and save as this.processedArgs!
1133
+ * Process this.args using this.registeredArguments and save as this.processedArgs!
1131
1134
  *
1132
1135
  * @api private
1133
1136
  */
@@ -1137,15 +1140,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
1137
1140
  // Extra processing for nice error message on parsing failure.
1138
1141
  let parsedValue = value;
1139
1142
  if (value !== null && argument.parseArg) {
1140
- try {
1141
- parsedValue = argument.parseArg(value, previous);
1142
- } catch (err) {
1143
- if (err.code === 'commander.invalidArgument') {
1144
- const message = `error: command-argument value '${value}' is invalid for argument '${argument.name()}'. ${err.message}`;
1145
- this.error(message, { exitCode: err.exitCode, code: err.code });
1146
- }
1147
- throw err;
1148
- }
1143
+ const invalidValueMessage = `error: command-argument value '${value}' is invalid for argument '${argument.name()}'.`;
1144
+ parsedValue = this._callParseArg(argument, value, previous, invalidValueMessage);
1149
1145
  }
1150
1146
  return parsedValue;
1151
1147
  };
@@ -1153,7 +1149,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1153
1149
  this._checkNumberOfArguments();
1154
1150
 
1155
1151
  const processedArgs = [];
1156
- this._args.forEach((declaredArg, index) => {
1152
+ this.registeredArguments.forEach((declaredArg, index) => {
1157
1153
  let value = declaredArg.defaultValue;
1158
1154
  if (declaredArg.variadic) {
1159
1155
  // Collect together remaining arguments for passing together as an array.
@@ -1208,7 +1204,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1208
1204
  _chainOrCallHooks(promise, event) {
1209
1205
  let result = promise;
1210
1206
  const hooks = [];
1211
- getCommandAndParents(this)
1207
+ this._getCommandAndAncestors()
1212
1208
  .reverse()
1213
1209
  .filter(cmd => cmd._lifeCycleHooks[event] !== undefined)
1214
1210
  .forEach(hookedCommand => {
@@ -1295,16 +1291,16 @@ Expecting one of '${allowedValues.join("', '")}'`);
1295
1291
  checkForUnknownOptions();
1296
1292
  this._processArguments();
1297
1293
 
1298
- let actionResult;
1299
- actionResult = this._chainOrCallHooks(actionResult, 'preAction');
1300
- actionResult = this._chainOrCall(actionResult, () => this._actionHandler(this.processedArgs));
1294
+ let promiseChain;
1295
+ promiseChain = this._chainOrCallHooks(promiseChain, 'preAction');
1296
+ promiseChain = this._chainOrCall(promiseChain, () => this._actionHandler(this.processedArgs));
1301
1297
  if (this.parent) {
1302
- actionResult = this._chainOrCall(actionResult, () => {
1298
+ promiseChain = this._chainOrCall(promiseChain, () => {
1303
1299
  this.parent.emit(commandEvent, operands, unknown); // legacy
1304
1300
  });
1305
1301
  }
1306
- actionResult = this._chainOrCallHooks(actionResult, 'postAction');
1307
- return actionResult;
1302
+ promiseChain = this._chainOrCallHooks(promiseChain, 'postAction');
1303
+ return promiseChain;
1308
1304
  }
1309
1305
  if (this.parent && this.parent.listenerCount(commandEvent)) {
1310
1306
  checkForUnknownOptions();
@@ -1365,13 +1361,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
1365
1361
 
1366
1362
  _checkForMissingMandatoryOptions() {
1367
1363
  // Walk up hierarchy so can call in subcommand after checking for displaying help.
1368
- for (let cmd = this; cmd; cmd = cmd.parent) {
1364
+ this._getCommandAndAncestors().forEach((cmd) => {
1369
1365
  cmd.options.forEach((anOption) => {
1370
1366
  if (anOption.mandatory && (cmd.getOptionValue(anOption.attributeName()) === undefined)) {
1371
1367
  cmd.missingMandatoryOptionValue(anOption);
1372
1368
  }
1373
1369
  });
1374
- }
1370
+ });
1375
1371
  }
1376
1372
 
1377
1373
  /**
@@ -1412,9 +1408,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
1412
1408
  */
1413
1409
  _checkForConflictingOptions() {
1414
1410
  // Walk up hierarchy so can call in subcommand after checking for displaying help.
1415
- for (let cmd = this; cmd; cmd = cmd.parent) {
1411
+ this._getCommandAndAncestors().forEach((cmd) => {
1416
1412
  cmd._checkForConflictingLocalOptions();
1417
- }
1413
+ });
1418
1414
  }
1419
1415
 
1420
1416
  /**
@@ -1577,7 +1573,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1577
1573
  */
1578
1574
  optsWithGlobals() {
1579
1575
  // globals overwrite locals
1580
- return getCommandAndParents(this).reduce(
1576
+ return this._getCommandAndAncestors().reduce(
1581
1577
  (combinedOptions, cmd) => Object.assign(combinedOptions, cmd.opts()),
1582
1578
  {}
1583
1579
  );
@@ -1768,7 +1764,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1768
1764
  _excessArguments(receivedArgs) {
1769
1765
  if (this._allowExcessArguments) return;
1770
1766
 
1771
- const expected = this._args.length;
1767
+ const expected = this.registeredArguments.length;
1772
1768
  const s = (expected === 1) ? '' : 's';
1773
1769
  const forSubcommand = this.parent ? ` for '${this.name()}'` : '';
1774
1770
  const message = `error: too many arguments${forSubcommand}. Expected ${expected} argument${s} but got ${receivedArgs.length}.`;
@@ -1800,17 +1796,16 @@ Expecting one of '${allowedValues.join("', '")}'`);
1800
1796
  }
1801
1797
 
1802
1798
  /**
1803
- * Set the program version to `str`.
1799
+ * Get or set the program version.
1804
1800
  *
1805
- * This method auto-registers the "-V, --version" flag
1806
- * which will print the version number when passed.
1801
+ * This method auto-registers the "-V, --version" option which will print the version number.
1807
1802
  *
1808
- * You can optionally supply the flags and description to override the defaults.
1803
+ * You can optionally supply the flags and description to override the defaults.
1809
1804
  *
1810
- * @param {string} str
1805
+ * @param {string} [str]
1811
1806
  * @param {string} [flags]
1812
1807
  * @param {string} [description]
1813
- * @return {this | string} `this` command for chaining, or version string if no arguments
1808
+ * @return {this | string | undefined} `this` command for chaining, or version string if no arguments
1814
1809
  */
1815
1810
 
1816
1811
  version(str, flags, description) {
@@ -1819,7 +1814,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1819
1814
  flags = flags || '-V, --version';
1820
1815
  description = description || 'output the version number';
1821
1816
  const versionOption = this.createOption(flags, description);
1822
- this._versionOptionName = versionOption.attributeName();
1817
+ this._versionOptionName = versionOption.attributeName(); // [sic] not defined in constructor, partly legacy, partly only needed at root
1823
1818
  this.options.push(versionOption);
1824
1819
  this.on('option:' + versionOption.name(), () => {
1825
1820
  this._outputConfiguration.writeOut(`${str}\n`);
@@ -1909,13 +1904,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
1909
1904
  if (str === undefined) {
1910
1905
  if (this._usage) return this._usage;
1911
1906
 
1912
- const args = this._args.map((arg) => {
1907
+ const args = this.registeredArguments.map((arg) => {
1913
1908
  return humanReadableArgName(arg);
1914
1909
  });
1915
1910
  return [].concat(
1916
1911
  (this.options.length || this._hasHelpOption ? '[options]' : []),
1917
1912
  (this.commands.length ? '[command]' : []),
1918
- (this._args.length ? args : [])
1913
+ (this.registeredArguments.length ? args : [])
1919
1914
  ).join(' ');
1920
1915
  }
1921
1916
 
@@ -1964,7 +1959,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1964
1959
  * program.executableDir('subcommands');
1965
1960
  *
1966
1961
  * @param {string} [path]
1967
- * @return {string|Command}
1962
+ * @return {string|null|Command}
1968
1963
  */
1969
1964
 
1970
1965
  executableDir(path) {
@@ -2022,7 +2017,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2022
2017
  }
2023
2018
  const context = this._getHelpContext(contextOptions);
2024
2019
 
2025
- getCommandAndParents(this).reverse().forEach(command => command.emit('beforeAllHelp', context));
2020
+ this._getCommandAndAncestors().reverse().forEach(command => command.emit('beforeAllHelp', context));
2026
2021
  this.emit('beforeHelp', context);
2027
2022
 
2028
2023
  let helpInformation = this.helpInformation(context);
@@ -2034,9 +2029,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
2034
2029
  }
2035
2030
  context.write(helpInformation);
2036
2031
 
2037
- this.emit(this._helpLongFlag); // deprecated
2032
+ if (this._helpLongFlag) {
2033
+ this.emit(this._helpLongFlag); // deprecated
2034
+ }
2038
2035
  this.emit('afterHelp', context);
2039
- getCommandAndParents(this).forEach(command => command.emit('afterAllHelp', context));
2036
+ this._getCommandAndAncestors().forEach(command => command.emit('afterAllHelp', context));
2040
2037
  }
2041
2038
 
2042
2039
  /**
@@ -2179,18 +2176,4 @@ function incrementNodeInspectorPort(args) {
2179
2176
  });
2180
2177
  }
2181
2178
 
2182
- /**
2183
- * @param {Command} startCommand
2184
- * @returns {Command[]}
2185
- * @api private
2186
- */
2187
-
2188
- function getCommandAndParents(startCommand) {
2189
- const result = [];
2190
- for (let command = startCommand; command; command = command.parent) {
2191
- result.push(command);
2192
- }
2193
- return result;
2194
- }
2195
-
2196
2179
  exports.Command = Command;
package/lib/error.js CHANGED
@@ -1,5 +1,3 @@
1
- // @ts-check
2
-
3
1
  /**
4
2
  * CommanderError class
5
3
  * @class
package/lib/help.js CHANGED
@@ -8,8 +8,6 @@ const { humanReadableArgName } = require('./argument.js');
8
8
  * @typedef { import("./option.js").Option } Option
9
9
  */
10
10
 
11
- // @ts-check
12
-
13
11
  // Although this is a class, methods are static in style to allow override using subclass or just functions.
14
12
  class Help {
15
13
  constructor() {
@@ -101,8 +99,8 @@ class Help {
101
99
  if (!this.showGlobalOptions) return [];
102
100
 
103
101
  const globalOptions = [];
104
- for (let parentCmd = cmd.parent; parentCmd; parentCmd = parentCmd.parent) {
105
- const visibleOptions = parentCmd.options.filter((option) => !option.hidden);
102
+ for (let ancestorCmd = cmd.parent; ancestorCmd; ancestorCmd = ancestorCmd.parent) {
103
+ const visibleOptions = ancestorCmd.options.filter((option) => !option.hidden);
106
104
  globalOptions.push(...visibleOptions);
107
105
  }
108
106
  if (this.sortOptions) {
@@ -121,14 +119,14 @@ class Help {
121
119
  visibleArguments(cmd) {
122
120
  // Side effect! Apply the legacy descriptions before the arguments are displayed.
123
121
  if (cmd._argsDescription) {
124
- cmd._args.forEach(argument => {
122
+ cmd.registeredArguments.forEach(argument => {
125
123
  argument.description = argument.description || cmd._argsDescription[argument.name()] || '';
126
124
  });
127
125
  }
128
126
 
129
127
  // 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;
128
+ if (cmd.registeredArguments.find(argument => argument.description)) {
129
+ return cmd.registeredArguments;
132
130
  }
133
131
  return [];
134
132
  }
@@ -142,7 +140,7 @@ class Help {
142
140
 
143
141
  subcommandTerm(cmd) {
144
142
  // Legacy. Ignores custom usage string, and nested commands.
145
- const args = cmd._args.map(arg => humanReadableArgName(arg)).join(' ');
143
+ const args = cmd.registeredArguments.map(arg => humanReadableArgName(arg)).join(' ');
146
144
  return cmd._name +
147
145
  (cmd._aliases[0] ? '|' + cmd._aliases[0] : '') +
148
146
  (cmd.options.length ? ' [options]' : '') + // simplistic check for non-help option
@@ -240,11 +238,11 @@ class Help {
240
238
  if (cmd._aliases[0]) {
241
239
  cmdName = cmdName + '|' + cmd._aliases[0];
242
240
  }
243
- let parentCmdNames = '';
244
- for (let parentCmd = cmd.parent; parentCmd; parentCmd = parentCmd.parent) {
245
- parentCmdNames = parentCmd.name() + ' ' + parentCmdNames;
241
+ let ancestorCmdNames = '';
242
+ for (let ancestorCmd = cmd.parent; ancestorCmd; ancestorCmd = ancestorCmd.parent) {
243
+ ancestorCmdNames = ancestorCmd.name() + ' ' + ancestorCmdNames;
246
244
  }
247
- return parentCmdNames + cmdName + ' ' + cmd.usage();
245
+ return ancestorCmdNames + cmdName + ' ' + cmd.usage();
248
246
  }
249
247
 
250
248
  /**
package/lib/option.js CHANGED
@@ -1,7 +1,5 @@
1
1
  const { InvalidArgumentError } = require('./error.js');
2
2
 
3
- // @ts-check
4
-
5
3
  class Option {
6
4
  /**
7
5
  * Initialize a new `Option` with the given `flags` and `description`.
@@ -40,7 +38,7 @@ class Option {
40
38
  /**
41
39
  * Set the default value, and optionally supply the description to be displayed in the help.
42
40
  *
43
- * @param {any} value
41
+ * @param {*} value
44
42
  * @param {string} [description]
45
43
  * @return {Option}
46
44
  */
@@ -59,7 +57,7 @@ class Option {
59
57
  * new Option('--color').default('GREYSCALE').preset('RGB');
60
58
  * new Option('--donate [amount]').preset('20').argParser(parseFloat);
61
59
  *
62
- * @param {any} arg
60
+ * @param {*} arg
63
61
  * @return {Option}
64
62
  */
65
63
 
@@ -275,7 +273,7 @@ class DualOptions {
275
273
  /**
276
274
  * Did the value come from the option, and not from possible matching dual option?
277
275
  *
278
- * @param {any} value
276
+ * @param {*} value
279
277
  * @param {Option} option
280
278
  * @returns {boolean}
281
279
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commander",
3
- "version": "11.0.0",
3
+ "version": "11.1.0",
4
4
  "description": "the complete solution for node.js command-line programs",
5
5
  "keywords": [
6
6
  "commander",
@@ -22,11 +22,11 @@
22
22
  "lint": "npm run lint:javascript && npm run lint:typescript",
23
23
  "lint:javascript": "eslint index.js esm.mjs \"lib/*.js\" \"tests/**/*.js\"",
24
24
  "lint:typescript": "eslint typings/*.ts tests/*.ts",
25
- "test": "jest && npm run test-typings",
25
+ "test": "jest && npm run typecheck-ts",
26
26
  "test-esm": "node ./tests/esm-imports-test.mjs",
27
- "test-typings": "tsd",
28
- "typescript-checkJS": "tsc --allowJS --checkJS index.js lib/*.js --noEmit",
29
- "test-all": "npm run test && npm run lint && npm run typescript-checkJS && npm run test-esm"
27
+ "typecheck-ts": "tsd && tsc -p tsconfig.ts.json",
28
+ "typecheck-js": "tsc -p tsconfig.js.json",
29
+ "test-all": "npm run test && npm run lint && npm run typecheck-js && npm run test-esm"
30
30
  },
31
31
  "files": [
32
32
  "index.js",
@@ -73,16 +73,6 @@
73
73
  "typescript": "^5.0.4"
74
74
  },
75
75
  "types": "typings/index.d.ts",
76
- "jest": {
77
- "testEnvironment": "node",
78
- "collectCoverage": true,
79
- "transform": {
80
- "^.+\\.tsx?$": "ts-jest"
81
- },
82
- "testPathIgnorePatterns": [
83
- "/node_modules/"
84
- ]
85
- },
86
76
  "engines": {
87
77
  "node": ">=16"
88
78
  },
@@ -5,6 +5,14 @@
5
5
  /* eslint-disable @typescript-eslint/method-signature-style */
6
6
  /* eslint-disable @typescript-eslint/no-explicit-any */
7
7
 
8
+ // This is a trick to encourage editor to suggest the known literals while still
9
+ // allowing any BaseType value.
10
+ // References:
11
+ // - https://github.com/microsoft/TypeScript/issues/29729
12
+ // - https://github.com/sindresorhus/type-fest/blob/main/source/literal-union.d.ts
13
+ // - https://github.com/sindresorhus/type-fest/blob/main/source/primitive.d.ts
14
+ type LiteralUnion<LiteralType, BaseType extends string | number> = LiteralType | (BaseType & Record<never, never>);
15
+
8
16
  export class CommanderError extends Error {
9
17
  code: string;
10
18
  exitCode: number;
@@ -42,6 +50,9 @@ export class Argument {
42
50
  description: string;
43
51
  required: boolean;
44
52
  variadic: boolean;
53
+ defaultValue?: any;
54
+ defaultValueDescription?: string;
55
+ argChoices?: string[];
45
56
 
46
57
  /**
47
58
  * Initialize a new command argument with the given name and description.
@@ -94,6 +105,8 @@ export class Option {
94
105
  negate: boolean;
95
106
  defaultValue?: any;
96
107
  defaultValueDescription?: string;
108
+ presetArg?: unknown;
109
+ envVar?: string;
97
110
  parseArg?: <T>(value: string, previous: T) => T;
98
111
  hidden: boolean;
99
112
  argChoices?: string[];
@@ -272,7 +285,8 @@ export interface OutputConfiguration {
272
285
 
273
286
  export type AddHelpTextPosition = 'beforeAll' | 'before' | 'after' | 'afterAll';
274
287
  export type HookEvent = 'preSubcommand' | 'preAction' | 'postAction';
275
- export type OptionValueSource = 'default' | 'config' | 'env' | 'cli' | 'implied';
288
+ // The source is a string so author can define their own too.
289
+ export type OptionValueSource = LiteralUnion<'default' | 'config' | 'env' | 'cli' | 'implied', string> | undefined;
276
290
 
277
291
  export type OptionValues = Record<string, any>;
278
292
 
@@ -281,6 +295,7 @@ export class Command {
281
295
  processedArgs: any[];
282
296
  readonly commands: readonly Command[];
283
297
  readonly options: readonly Option[];
298
+ readonly registeredArguments: readonly Argument[];
284
299
  parent: Command | null;
285
300
 
286
301
  constructor(name?: string);
@@ -294,6 +309,10 @@ export class Command {
294
309
  * You can optionally supply the flags and description to override the defaults.
295
310
  */
296
311
  version(str: string, flags?: string, description?: string): this;
312
+ /**
313
+ * Get the program version.
314
+ */
315
+ version(): string | undefined;
297
316
 
298
317
  /**
299
318
  * Define a command, implemented using an action handler.
@@ -497,51 +516,27 @@ export class Command {
497
516
  action(fn: (...args: any[]) => void | Promise<void>): this;
498
517
 
499
518
  /**
500
- * Define option with `flags`, `description` and optional
501
- * coercion `fn`.
519
+ * Define option with `flags`, `description`, and optional argument parsing function or `defaultValue` or both.
502
520
  *
503
- * The `flags` string contains the short and/or long flags,
504
- * separated by comma, a pipe or space. The following are all valid
505
- * all will output this way when `--help` is used.
521
+ * The `flags` string contains the short and/or long flags, separated by comma, a pipe or space. A required
522
+ * option-argument is indicated by `<>` and an optional option-argument by `[]`.
506
523
  *
507
- * "-p, --pepper"
508
- * "-p|--pepper"
509
- * "-p --pepper"
524
+ * See the README for more details, and see also addOption() and requiredOption().
510
525
  *
511
526
  * @example
512
- * ```
513
- * // simple boolean defaulting to false
514
- * program.option('-p, --pepper', 'add pepper');
515
527
  *
516
- * --pepper
517
- * program.pepper
518
- * // => Boolean
519
- *
520
- * // simple boolean defaulting to true
521
- * program.option('-C, --no-cheese', 'remove cheese');
522
- *
523
- * program.cheese
524
- * // => true
525
- *
526
- * --no-cheese
527
- * program.cheese
528
- * // => false
529
- *
530
- * // required argument
531
- * program.option('-C, --chdir <path>', 'change the working directory');
532
- *
533
- * --chdir /tmp
534
- * program.chdir
535
- * // => "/tmp"
536
- *
537
- * // optional argument
538
- * program.option('-c, --cheese [type]', 'add cheese [marble]');
528
+ * ```js
529
+ * program
530
+ * .option('-p, --pepper', 'add pepper')
531
+ * .option('-p, --pizza-type <TYPE>', 'type of pizza') // required option-argument
532
+ * .option('-c, --cheese [CHEESE]', 'add extra cheese', 'mozzarella') // optional option-argument with default
533
+ * .option('-t, --tip <VALUE>', 'add tip to purchase cost', parseFloat) // custom parse function
539
534
  * ```
540
535
  *
541
536
  * @returns `this` command for chaining
542
537
  */
543
538
  option(flags: string, description?: string, defaultValue?: string | boolean | string[]): this;
544
- option<T>(flags: string, description: string, fn: (value: string, previous: T) => T, defaultValue?: T): this;
539
+ option<T>(flags: string, description: string, parseArg: (value: string, previous: T) => T, defaultValue?: T): this;
545
540
  /** @deprecated since v7, instead use choices or a custom function */
546
541
  option(flags: string, description: string, regexp: RegExp, defaultValue?: string | boolean | string[]): this;
547
542
 
@@ -552,7 +547,7 @@ export class Command {
552
547
  * The `flags` string contains the short and/or long flags, separated by comma, a pipe or space.
553
548
  */
554
549
  requiredOption(flags: string, description?: string, defaultValue?: string | boolean | string[]): this;
555
- requiredOption<T>(flags: string, description: string, fn: (value: string, previous: T) => T, defaultValue?: T): this;
550
+ requiredOption<T>(flags: string, description: string, parseArg: (value: string, previous: T) => T, defaultValue?: T): this;
556
551
  /** @deprecated since v7, instead use choices or a custom function */
557
552
  requiredOption(flags: string, description: string, regexp: RegExp, defaultValue?: string | boolean | string[]): this;
558
553
 
@@ -819,7 +814,7 @@ export class Command {
819
814
  /**
820
815
  * Get the executable search directory.
821
816
  */
822
- executableDir(): string;
817
+ executableDir(): string | null;
823
818
 
824
819
  /**
825
820
  * Output help information for this command.