commander 12.0.0 → 13.0.0-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/lib/command.js CHANGED
@@ -1,12 +1,12 @@
1
- const EventEmitter = require('events').EventEmitter;
2
- const childProcess = require('child_process');
3
- const path = require('path');
4
- const fs = require('fs');
5
- const process = require('process');
1
+ const EventEmitter = require('node:events').EventEmitter;
2
+ const childProcess = require('node:child_process');
3
+ const path = require('node:path');
4
+ const fs = require('node:fs');
5
+ const process = require('node:process');
6
6
 
7
7
  const { Argument, humanReadableArgName } = require('./argument.js');
8
8
  const { CommanderError } = require('./error.js');
9
- const { Help } = require('./help.js');
9
+ const { Help, stripColor } = require('./help.js');
10
10
  const { Option, DualOptions } = require('./option.js');
11
11
  const { suggestSimilar } = require('./suggestSimilar');
12
12
 
@@ -25,7 +25,7 @@ class Command extends EventEmitter {
25
25
  this.options = [];
26
26
  this.parent = null;
27
27
  this._allowUnknownOption = false;
28
- this._allowExcessArguments = true;
28
+ this._allowExcessArguments = false;
29
29
  /** @type {Argument[]} */
30
30
  this.registeredArguments = [];
31
31
  this._args = this.registeredArguments; // deprecated old name
@@ -56,13 +56,20 @@ class Command extends EventEmitter {
56
56
  this._showHelpAfterError = false;
57
57
  this._showSuggestionAfterError = true;
58
58
 
59
- // see .configureOutput() for docs
59
+ // see configureOutput() for docs
60
60
  this._outputConfiguration = {
61
61
  writeOut: (str) => process.stdout.write(str),
62
62
  writeErr: (str) => process.stderr.write(str),
63
- getOutHelpWidth: () => process.stdout.isTTY ? process.stdout.columns : undefined,
64
- getErrHelpWidth: () => process.stderr.isTTY ? process.stderr.columns : undefined,
65
- outputError: (str, write) => write(str)
63
+ outputError: (str, write) => write(str),
64
+ getOutHelpWidth: () =>
65
+ process.stdout.isTTY ? process.stdout.columns : undefined,
66
+ getErrHelpWidth: () =>
67
+ process.stderr.isTTY ? process.stderr.columns : undefined,
68
+ getOutHasColors: () =>
69
+ useColor() ?? (process.stdout.isTTY && process.stdout.hasColors?.()),
70
+ getErrHasColors: () =>
71
+ useColor() ?? (process.stderr.isTTY && process.stderr.hasColors?.()),
72
+ stripColor: (str) => stripColor(str),
66
73
  };
67
74
 
68
75
  this._hidden = false;
@@ -89,7 +96,8 @@ class Command extends EventEmitter {
89
96
  this._helpConfiguration = sourceCommand._helpConfiguration;
90
97
  this._exitCallback = sourceCommand._exitCallback;
91
98
  this._storeOptionsAsProperties = sourceCommand._storeOptionsAsProperties;
92
- this._combineFlagAndOptionalValue = sourceCommand._combineFlagAndOptionalValue;
99
+ this._combineFlagAndOptionalValue =
100
+ sourceCommand._combineFlagAndOptionalValue;
93
101
  this._allowExcessArguments = sourceCommand._allowExcessArguments;
94
102
  this._enablePositionalOptions = sourceCommand._enablePositionalOptions;
95
103
  this._showHelpAfterError = sourceCommand._showHelpAfterError;
@@ -105,6 +113,7 @@ class Command extends EventEmitter {
105
113
 
106
114
  _getCommandAndAncestors() {
107
115
  const result = [];
116
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
108
117
  for (let command = this; command; command = command.parent) {
109
118
  result.push(command);
110
119
  }
@@ -131,8 +140,8 @@ class Command extends EventEmitter {
131
140
  * .command('stop [service]', 'stop named service, or all if no name supplied');
132
141
  *
133
142
  * @param {string} nameAndArgs - command name and arguments, args are `<required>` or `[optional]` and last may also be `variadic...`
134
- * @param {(Object|string)} [actionOptsOrExecDesc] - configuration options (for action), or description (for executable)
135
- * @param {Object} [execOpts] - configuration options (for executable)
143
+ * @param {(object | string)} [actionOptsOrExecDesc] - configuration options (for action), or description (for executable)
144
+ * @param {object} [execOpts] - configuration options (for executable)
136
145
  * @return {Command} returns new command for action handler, or `this` for executable command
137
146
  */
138
147
 
@@ -192,8 +201,8 @@ class Command extends EventEmitter {
192
201
  * You can customise the help by overriding Help properties using configureHelp(),
193
202
  * or with a subclass of Help by overriding createHelp().
194
203
  *
195
- * @param {Object} [configuration] - configuration options
196
- * @return {(Command|Object)} `this` command for chaining, or stored configuration
204
+ * @param {object} [configuration] - configuration options
205
+ * @return {(Command | object)} `this` command for chaining, or stored configuration
197
206
  */
198
207
 
199
208
  configureHelp(configuration) {
@@ -209,17 +218,21 @@ class Command extends EventEmitter {
209
218
  *
210
219
  * The configuration properties are all functions:
211
220
  *
212
- * // functions to change where being written, stdout and stderr
221
+ * // change how output being written, defaults to stdout and stderr
213
222
  * writeOut(str)
214
223
  * writeErr(str)
215
- * // matching functions to specify width for wrapping help
224
+ * // change how output being written for errors, defaults to writeErr
225
+ * outputError(str, write) // used for displaying errors and not used for displaying help
226
+ * // specify width for wrapping help
216
227
  * getOutHelpWidth()
217
228
  * getErrHelpWidth()
218
- * // functions based on what is being written out
219
- * outputError(str, write) // used for displaying errors, and not used for displaying help
229
+ * // color support, currently only used with Help
230
+ * getOutHasColors()
231
+ * getErrHasColors()
232
+ * stripColor() // used to remove ANSI escape codes if output does not have colors
220
233
  *
221
- * @param {Object} [configuration] - configuration options
222
- * @return {(Command|Object)} `this` command for chaining, or stored configuration
234
+ * @param {object} [configuration] - configuration options
235
+ * @return {(Command | object)} `this` command for chaining, or stored configuration
223
236
  */
224
237
 
225
238
  configureOutput(configuration) {
@@ -258,7 +271,7 @@ class Command extends EventEmitter {
258
271
  * See .command() for creating an attached subcommand which inherits settings from its parent.
259
272
  *
260
273
  * @param {Command} cmd - new subcommand
261
- * @param {Object} [opts] - configuration options
274
+ * @param {object} [opts] - configuration options
262
275
  * @return {Command} `this` command for chaining
263
276
  */
264
277
 
@@ -334,9 +347,12 @@ class Command extends EventEmitter {
334
347
  */
335
348
 
336
349
  arguments(names) {
337
- names.trim().split(/ +/).forEach((detail) => {
338
- this.argument(detail);
339
- });
350
+ names
351
+ .trim()
352
+ .split(/ +/)
353
+ .forEach((detail) => {
354
+ this.argument(detail);
355
+ });
340
356
  return this;
341
357
  }
342
358
 
@@ -349,10 +365,18 @@ class Command extends EventEmitter {
349
365
  addArgument(argument) {
350
366
  const previousArgument = this.registeredArguments.slice(-1)[0];
351
367
  if (previousArgument && previousArgument.variadic) {
352
- throw new Error(`only the last argument can be variadic '${previousArgument.name()}'`);
368
+ throw new Error(
369
+ `only the last argument can be variadic '${previousArgument.name()}'`,
370
+ );
353
371
  }
354
- if (argument.required && argument.defaultValue !== undefined && argument.parseArg === undefined) {
355
- throw new Error(`a default value for a required argument is never used: '${argument.name()}'`);
372
+ if (
373
+ argument.required &&
374
+ argument.defaultValue !== undefined &&
375
+ argument.parseArg === undefined
376
+ ) {
377
+ throw new Error(
378
+ `a default value for a required argument is never used: '${argument.name()}'`,
379
+ );
356
380
  }
357
381
  this.registeredArguments.push(argument);
358
382
  return this;
@@ -361,6 +385,7 @@ class Command extends EventEmitter {
361
385
  /**
362
386
  * Customise or override default help command. By default a help command is automatically added if your command has subcommands.
363
387
  *
388
+ * @example
364
389
  * program.helpCommand('help [cmd]');
365
390
  * program.helpCommand('help [cmd]', 'show help');
366
391
  * program.helpCommand(false); // suppress default help command
@@ -419,8 +444,11 @@ class Command extends EventEmitter {
419
444
  * @package
420
445
  */
421
446
  _getHelpCommand() {
422
- const hasImplicitHelpCommand = this._addImplicitHelpCommand ??
423
- (this.commands.length && !this._actionHandler && !this._findCommand('help'));
447
+ const hasImplicitHelpCommand =
448
+ this._addImplicitHelpCommand ??
449
+ (this.commands.length &&
450
+ !this._actionHandler &&
451
+ !this._findCommand('help'));
424
452
 
425
453
  if (hasImplicitHelpCommand) {
426
454
  if (this._helpCommand === undefined) {
@@ -568,14 +596,18 @@ Expecting one of '${allowedValues.join("', '")}'`);
568
596
  * Register option if no conflicts found, or throw on conflict.
569
597
  *
570
598
  * @param {Option} option
571
- * @api private
599
+ * @private
572
600
  */
573
601
 
574
602
  _registerOption(option) {
575
- const matchingOption = (option.short && this._findOption(option.short)) ||
603
+ const matchingOption =
604
+ (option.short && this._findOption(option.short)) ||
576
605
  (option.long && this._findOption(option.long));
577
606
  if (matchingOption) {
578
- const matchingFlag = (option.long && this._findOption(option.long)) ? option.long : option.short;
607
+ const matchingFlag =
608
+ option.long && this._findOption(option.long)
609
+ ? option.long
610
+ : option.short;
579
611
  throw new Error(`Cannot add option '${option.flags}'${this._name && ` to command '${this._name}'`} due to conflicting flag '${matchingFlag}'
580
612
  - already used by option '${matchingOption.flags}'`);
581
613
  }
@@ -588,7 +620,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
588
620
  * Register command if no conflicts found, or throw on conflict.
589
621
  *
590
622
  * @param {Command} command
591
- * @api private
623
+ * @private
592
624
  */
593
625
 
594
626
  _registerCommand(command) {
@@ -596,11 +628,15 @@ Expecting one of '${allowedValues.join("', '")}'`);
596
628
  return [cmd.name()].concat(cmd.aliases());
597
629
  };
598
630
 
599
- const alreadyUsed = knownBy(command).find((name) => this._findCommand(name));
631
+ const alreadyUsed = knownBy(command).find((name) =>
632
+ this._findCommand(name),
633
+ );
600
634
  if (alreadyUsed) {
601
635
  const existingCmd = knownBy(this._findCommand(alreadyUsed)).join('|');
602
636
  const newCmd = knownBy(command).join('|');
603
- throw new Error(`cannot add command '${newCmd}' as already have command '${existingCmd}'`);
637
+ throw new Error(
638
+ `cannot add command '${newCmd}' as already have command '${existingCmd}'`,
639
+ );
604
640
  }
605
641
 
606
642
  this.commands.push(command);
@@ -623,7 +659,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
623
659
  // --no-foo is special and defaults foo to true, unless a --foo option is already defined
624
660
  const positiveLongFlag = option.long.replace(/^--no-/, '--');
625
661
  if (!this._findOption(positiveLongFlag)) {
626
- this.setOptionValueWithSource(name, option.defaultValue === undefined ? true : option.defaultValue, 'default');
662
+ this.setOptionValueWithSource(
663
+ name,
664
+ option.defaultValue === undefined ? true : option.defaultValue,
665
+ 'default',
666
+ );
627
667
  }
628
668
  } else if (option.defaultValue !== undefined) {
629
669
  this.setOptionValueWithSource(name, option.defaultValue, 'default');
@@ -676,11 +716,14 @@ Expecting one of '${allowedValues.join("', '")}'`);
676
716
  /**
677
717
  * Internal implementation shared by .option() and .requiredOption()
678
718
  *
719
+ * @return {Command} `this` command for chaining
679
720
  * @private
680
721
  */
681
722
  _optionEx(config, flags, description, fn, defaultValue) {
682
723
  if (typeof flags === 'object' && flags instanceof Option) {
683
- throw new Error('To add an Option object use addOption() instead of option() or requiredOption()');
724
+ throw new Error(
725
+ 'To add an Option object use addOption() instead of option() or requiredOption()',
726
+ );
684
727
  }
685
728
  const option = this.createOption(flags, description);
686
729
  option.makeOptionMandatory(!!config.mandatory);
@@ -728,20 +771,26 @@ Expecting one of '${allowedValues.join("', '")}'`);
728
771
  }
729
772
 
730
773
  /**
731
- * Add a required option which must have a value after parsing. This usually means
732
- * the option must be specified on the command line. (Otherwise the same as .option().)
733
- *
734
- * The `flags` string contains the short and/or long flags, separated by comma, a pipe or space.
735
- *
736
- * @param {string} flags
737
- * @param {string} [description]
738
- * @param {(Function|*)} [parseArg] - custom option processing function or default value
739
- * @param {*} [defaultValue]
740
- * @return {Command} `this` command for chaining
741
- */
774
+ * Add a required option which must have a value after parsing. This usually means
775
+ * the option must be specified on the command line. (Otherwise the same as .option().)
776
+ *
777
+ * The `flags` string contains the short and/or long flags, separated by comma, a pipe or space.
778
+ *
779
+ * @param {string} flags
780
+ * @param {string} [description]
781
+ * @param {(Function|*)} [parseArg] - custom option processing function or default value
782
+ * @param {*} [defaultValue]
783
+ * @return {Command} `this` command for chaining
784
+ */
742
785
 
743
786
  requiredOption(flags, description, parseArg, defaultValue) {
744
- return this._optionEx({ mandatory: true }, flags, description, parseArg, defaultValue);
787
+ return this._optionEx(
788
+ { mandatory: true },
789
+ flags,
790
+ description,
791
+ parseArg,
792
+ defaultValue,
793
+ );
745
794
  }
746
795
 
747
796
  /**
@@ -752,7 +801,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
752
801
  * program.combineFlagAndOptionalValue(true); // `-f80` is treated like `--flag=80`, this is the default behaviour
753
802
  * program.combineFlagAndOptionalValue(false) // `-fb` is treated like `-f -b`
754
803
  *
755
- * @param {boolean} [combine=true] - if `true` or omitted, an optional value can be specified directly after the flag.
804
+ * @param {boolean} [combine] - if `true` or omitted, an optional value can be specified directly after the flag.
805
+ * @return {Command} `this` command for chaining
756
806
  */
757
807
  combineFlagAndOptionalValue(combine = true) {
758
808
  this._combineFlagAndOptionalValue = !!combine;
@@ -762,8 +812,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
762
812
  /**
763
813
  * Allow unknown options on the command line.
764
814
  *
765
- * @param {boolean} [allowUnknown=true] - if `true` or omitted, no error will be thrown
766
- * for unknown options.
815
+ * @param {boolean} [allowUnknown] - if `true` or omitted, no error will be thrown for unknown options.
816
+ * @return {Command} `this` command for chaining
767
817
  */
768
818
  allowUnknownOption(allowUnknown = true) {
769
819
  this._allowUnknownOption = !!allowUnknown;
@@ -773,8 +823,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
773
823
  /**
774
824
  * Allow excess command-arguments on the command line. Pass false to make excess arguments an error.
775
825
  *
776
- * @param {boolean} [allowExcess=true] - if `true` or omitted, no error will be thrown
777
- * for excess arguments.
826
+ * @param {boolean} [allowExcess] - if `true` or omitted, no error will be thrown for excess arguments.
827
+ * @return {Command} `this` command for chaining
778
828
  */
779
829
  allowExcessArguments(allowExcess = true) {
780
830
  this._allowExcessArguments = !!allowExcess;
@@ -786,7 +836,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
786
836
  * subcommands reuse the same option names, and also enables subcommands to turn on passThroughOptions.
787
837
  * The default behaviour is non-positional and global options may appear anywhere on the command line.
788
838
  *
789
- * @param {boolean} [positional=true]
839
+ * @param {boolean} [positional]
840
+ * @return {Command} `this` command for chaining
790
841
  */
791
842
  enablePositionalOptions(positional = true) {
792
843
  this._enablePositionalOptions = !!positional;
@@ -799,8 +850,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
799
850
  * positional options to have been enabled on the program (parent commands).
800
851
  * The default behaviour is non-positional and options may appear before or after command-arguments.
801
852
  *
802
- * @param {boolean} [passThrough=true]
803
- * for unknown options.
853
+ * @param {boolean} [passThrough] for unknown options.
854
+ * @return {Command} `this` command for chaining
804
855
  */
805
856
  passThroughOptions(passThrough = true) {
806
857
  this._passThroughOptions = !!passThrough;
@@ -813,25 +864,33 @@ Expecting one of '${allowedValues.join("', '")}'`);
813
864
  */
814
865
 
815
866
  _checkForBrokenPassThrough() {
816
- if (this.parent && this._passThroughOptions && !this.parent._enablePositionalOptions) {
817
- throw new Error(`passThroughOptions cannot be used for '${this._name}' without turning on enablePositionalOptions for parent command(s)`);
867
+ if (
868
+ this.parent &&
869
+ this._passThroughOptions &&
870
+ !this.parent._enablePositionalOptions
871
+ ) {
872
+ throw new Error(
873
+ `passThroughOptions cannot be used for '${this._name}' without turning on enablePositionalOptions for parent command(s)`,
874
+ );
818
875
  }
819
876
  }
820
877
 
821
878
  /**
822
- * Whether to store option values as properties on command object,
823
- * or store separately (specify false). In both cases the option values can be accessed using .opts().
824
- *
825
- * @param {boolean} [storeAsProperties=true]
826
- * @return {Command} `this` command for chaining
827
- */
879
+ * Whether to store option values as properties on command object,
880
+ * or store separately (specify false). In both cases the option values can be accessed using .opts().
881
+ *
882
+ * @param {boolean} [storeAsProperties=true]
883
+ * @return {Command} `this` command for chaining
884
+ */
828
885
 
829
886
  storeOptionsAsProperties(storeAsProperties = true) {
830
887
  if (this.options.length) {
831
888
  throw new Error('call .storeOptionsAsProperties() before adding options');
832
889
  }
833
890
  if (Object.keys(this._optionValues).length) {
834
- throw new Error('call .storeOptionsAsProperties() before setting option values');
891
+ throw new Error(
892
+ 'call .storeOptionsAsProperties() before setting option values',
893
+ );
835
894
  }
836
895
  this._storeOptionsAsProperties = !!storeAsProperties;
837
896
  return this;
@@ -841,7 +900,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
841
900
  * Retrieve option value.
842
901
  *
843
902
  * @param {string} key
844
- * @return {Object} value
903
+ * @return {object} value
845
904
  */
846
905
 
847
906
  getOptionValue(key) {
@@ -855,7 +914,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
855
914
  * Store option value.
856
915
  *
857
916
  * @param {string} key
858
- * @param {Object} value
917
+ * @param {object} value
859
918
  * @return {Command} `this` command for chaining
860
919
  */
861
920
 
@@ -864,13 +923,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
864
923
  }
865
924
 
866
925
  /**
867
- * Store option value and where the value came from.
868
- *
869
- * @param {string} key
870
- * @param {Object} value
871
- * @param {string} source - expected values are default/config/env/cli/implied
872
- * @return {Command} `this` command for chaining
873
- */
926
+ * Store option value and where the value came from.
927
+ *
928
+ * @param {string} key
929
+ * @param {object} value
930
+ * @param {string} source - expected values are default/config/env/cli/implied
931
+ * @return {Command} `this` command for chaining
932
+ */
874
933
 
875
934
  setOptionValueWithSource(key, value, source) {
876
935
  if (this._storeOptionsAsProperties) {
@@ -883,24 +942,24 @@ Expecting one of '${allowedValues.join("', '")}'`);
883
942
  }
884
943
 
885
944
  /**
886
- * Get source of option value.
887
- * Expected values are default | config | env | cli | implied
888
- *
889
- * @param {string} key
890
- * @return {string}
891
- */
945
+ * Get source of option value.
946
+ * Expected values are default | config | env | cli | implied
947
+ *
948
+ * @param {string} key
949
+ * @return {string}
950
+ */
892
951
 
893
952
  getOptionValueSource(key) {
894
953
  return this._optionValueSources[key];
895
954
  }
896
955
 
897
956
  /**
898
- * Get source of option value. See also .optsWithGlobals().
899
- * Expected values are default | config | env | cli | implied
900
- *
901
- * @param {string} key
902
- * @return {string}
903
- */
957
+ * Get source of option value. See also .optsWithGlobals().
958
+ * Expected values are default | config | env | cli | implied
959
+ *
960
+ * @param {string} key
961
+ * @return {string}
962
+ */
904
963
 
905
964
  getOptionValueSourceWithGlobals(key) {
906
965
  // global overwrites local, like optsWithGlobals
@@ -926,17 +985,30 @@ Expecting one of '${allowedValues.join("', '")}'`);
926
985
  }
927
986
  parseOptions = parseOptions || {};
928
987
 
929
- // Default to using process.argv
930
- if (argv === undefined) {
931
- argv = process.argv;
932
- // @ts-ignore: unknown property
933
- if (process.versions && process.versions.electron) {
988
+ // auto-detect argument conventions if nothing supplied
989
+ if (argv === undefined && parseOptions.from === undefined) {
990
+ if (process.versions?.electron) {
934
991
  parseOptions.from = 'electron';
935
992
  }
993
+ // check node specific options for scenarios where user CLI args follow executable without scriptname
994
+ const execArgv = process.execArgv ?? [];
995
+ if (
996
+ execArgv.includes('-e') ||
997
+ execArgv.includes('--eval') ||
998
+ execArgv.includes('-p') ||
999
+ execArgv.includes('--print')
1000
+ ) {
1001
+ parseOptions.from = 'eval'; // internal usage, not documented
1002
+ }
1003
+ }
1004
+
1005
+ // default to using process.argv
1006
+ if (argv === undefined) {
1007
+ argv = process.argv;
936
1008
  }
937
1009
  this.rawArgs = argv.slice();
938
1010
 
939
- // make it a little easier for callers by supporting various argv conventions
1011
+ // extract the user args and scriptPath
940
1012
  let userArgs;
941
1013
  switch (parseOptions.from) {
942
1014
  case undefined:
@@ -945,7 +1017,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
945
1017
  userArgs = argv.slice(2);
946
1018
  break;
947
1019
  case 'electron':
948
- // @ts-ignore: unknown property
1020
+ // @ts-ignore: because defaultApp is an unknown property
949
1021
  if (process.defaultApp) {
950
1022
  this._scriptPath = argv[1];
951
1023
  userArgs = argv.slice(2);
@@ -956,12 +1028,18 @@ Expecting one of '${allowedValues.join("', '")}'`);
956
1028
  case 'user':
957
1029
  userArgs = argv.slice(0);
958
1030
  break;
1031
+ case 'eval':
1032
+ userArgs = argv.slice(1);
1033
+ break;
959
1034
  default:
960
- throw new Error(`unexpected parse option { from: '${parseOptions.from}' }`);
1035
+ throw new Error(
1036
+ `unexpected parse option { from: '${parseOptions.from}' }`,
1037
+ );
961
1038
  }
962
1039
 
963
1040
  // Find default name for program from arguments.
964
- if (!this._name && this._scriptPath) this.nameFromFilename(this._scriptPath);
1041
+ if (!this._name && this._scriptPath)
1042
+ this.nameFromFilename(this._scriptPath);
965
1043
  this._name = this._name || 'program';
966
1044
 
967
1045
  return userArgs;
@@ -970,16 +1048,22 @@ Expecting one of '${allowedValues.join("', '")}'`);
970
1048
  /**
971
1049
  * Parse `argv`, setting options and invoking commands when defined.
972
1050
  *
973
- * The default expectation is that the arguments are from node and have the application as argv[0]
974
- * and the script being run in argv[1], with user parameters after that.
1051
+ * Use parseAsync instead of parse if any of your action handlers are async.
1052
+ *
1053
+ * Call with no parameters to parse `process.argv`. Detects Electron and special node options like `node --eval`. Easy mode!
1054
+ *
1055
+ * Or call with an array of strings to parse, and optionally where the user arguments start by specifying where the arguments are `from`:
1056
+ * - `'node'`: default, `argv[0]` is the application and `argv[1]` is the script being run, with user arguments after that
1057
+ * - `'electron'`: `argv[0]` is the application and `argv[1]` varies depending on whether the electron application is packaged
1058
+ * - `'user'`: just user arguments
975
1059
  *
976
1060
  * @example
977
- * program.parse(process.argv);
978
- * program.parse(); // implicitly use process.argv and auto-detect node vs electron conventions
1061
+ * program.parse(); // parse process.argv and auto-detect electron and special node flags
1062
+ * program.parse(process.argv); // assume argv[0] is app and argv[1] is script
979
1063
  * program.parse(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0]
980
1064
  *
981
1065
  * @param {string[]} [argv] - optional, defaults to process.argv
982
- * @param {Object} [parseOptions] - optionally specify style of options with from: node/user/electron
1066
+ * @param {object} [parseOptions] - optionally specify style of options with from: node/user/electron
983
1067
  * @param {string} [parseOptions.from] - where the args are from: 'node', 'user', 'electron'
984
1068
  * @return {Command} `this` command for chaining
985
1069
  */
@@ -994,18 +1078,20 @@ Expecting one of '${allowedValues.join("', '")}'`);
994
1078
  /**
995
1079
  * Parse `argv`, setting options and invoking commands when defined.
996
1080
  *
997
- * Use parseAsync instead of parse if any of your action handlers are async. Returns a Promise.
1081
+ * Call with no parameters to parse `process.argv`. Detects Electron and special node options like `node --eval`. Easy mode!
998
1082
  *
999
- * The default expectation is that the arguments are from node and have the application as argv[0]
1000
- * and the script being run in argv[1], with user parameters after that.
1083
+ * Or call with an array of strings to parse, and optionally where the user arguments start by specifying where the arguments are `from`:
1084
+ * - `'node'`: default, `argv[0]` is the application and `argv[1]` is the script being run, with user arguments after that
1085
+ * - `'electron'`: `argv[0]` is the application and `argv[1]` varies depending on whether the electron application is packaged
1086
+ * - `'user'`: just user arguments
1001
1087
  *
1002
1088
  * @example
1003
- * await program.parseAsync(process.argv);
1004
- * await program.parseAsync(); // implicitly use process.argv and auto-detect node vs electron conventions
1089
+ * await program.parseAsync(); // parse process.argv and auto-detect electron and special node flags
1090
+ * await program.parseAsync(process.argv); // assume argv[0] is app and argv[1] is script
1005
1091
  * await program.parseAsync(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0]
1006
1092
  *
1007
1093
  * @param {string[]} [argv]
1008
- * @param {Object} [parseOptions]
1094
+ * @param {object} [parseOptions]
1009
1095
  * @param {string} parseOptions.from - where the args are from: 'node', 'user', 'electron'
1010
1096
  * @return {Promise}
1011
1097
  */
@@ -1037,7 +1123,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
1037
1123
  if (sourceExt.includes(path.extname(baseName))) return undefined;
1038
1124
 
1039
1125
  // Try all the extensions.
1040
- const foundExt = sourceExt.find(ext => fs.existsSync(`${localBin}${ext}`));
1126
+ const foundExt = sourceExt.find((ext) =>
1127
+ fs.existsSync(`${localBin}${ext}`),
1128
+ );
1041
1129
  if (foundExt) return `${localBin}${foundExt}`;
1042
1130
 
1043
1131
  return undefined;
@@ -1048,16 +1136,20 @@ Expecting one of '${allowedValues.join("', '")}'`);
1048
1136
  this._checkForConflictingOptions();
1049
1137
 
1050
1138
  // executableFile and executableDir might be full path, or just a name
1051
- let executableFile = subcommand._executableFile || `${this._name}-${subcommand._name}`;
1139
+ let executableFile =
1140
+ subcommand._executableFile || `${this._name}-${subcommand._name}`;
1052
1141
  let executableDir = this._executableDir || '';
1053
1142
  if (this._scriptPath) {
1054
1143
  let resolvedScriptPath; // resolve possible symlink for installed npm binary
1055
1144
  try {
1056
1145
  resolvedScriptPath = fs.realpathSync(this._scriptPath);
1057
- } catch (err) {
1146
+ } catch {
1058
1147
  resolvedScriptPath = this._scriptPath;
1059
1148
  }
1060
- executableDir = path.resolve(path.dirname(resolvedScriptPath), executableDir);
1149
+ executableDir = path.resolve(
1150
+ path.dirname(resolvedScriptPath),
1151
+ executableDir,
1152
+ );
1061
1153
  }
1062
1154
 
1063
1155
  // Look for a local file in preference to a command in PATH.
@@ -1066,9 +1158,15 @@ Expecting one of '${allowedValues.join("', '")}'`);
1066
1158
 
1067
1159
  // Legacy search using prefix of script name instead of command name
1068
1160
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
1069
- const legacyName = path.basename(this._scriptPath, path.extname(this._scriptPath));
1161
+ const legacyName = path.basename(
1162
+ this._scriptPath,
1163
+ path.extname(this._scriptPath),
1164
+ );
1070
1165
  if (legacyName !== this._name) {
1071
- localFile = findFile(executableDir, `${legacyName}-${subcommand._name}`);
1166
+ localFile = findFile(
1167
+ executableDir,
1168
+ `${legacyName}-${subcommand._name}`,
1169
+ );
1072
1170
  }
1073
1171
  }
1074
1172
  executableFile = localFile || executableFile;
@@ -1094,12 +1192,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
1094
1192
  proc = childProcess.spawn(process.execPath, args, { stdio: 'inherit' });
1095
1193
  }
1096
1194
 
1097
- if (!proc.killed) { // testing mainly to avoid leak warnings during unit tests with mocked spawn
1195
+ if (!proc.killed) {
1196
+ // testing mainly to avoid leak warnings during unit tests with mocked spawn
1098
1197
  const signals = ['SIGUSR1', 'SIGUSR2', 'SIGTERM', 'SIGINT', 'SIGHUP'];
1099
1198
  signals.forEach((signal) => {
1100
- // @ts-ignore
1101
1199
  process.on(signal, () => {
1102
1200
  if (proc.killed === false && proc.exitCode === null) {
1201
+ // @ts-ignore because signals not typed to known strings
1103
1202
  proc.kill(signal);
1104
1203
  }
1105
1204
  });
@@ -1108,16 +1207,22 @@ Expecting one of '${allowedValues.join("', '")}'`);
1108
1207
 
1109
1208
  // By default terminate process when spawned process terminates.
1110
1209
  const exitCallback = this._exitCallback;
1111
- proc.on('close', (code, _signal) => {
1210
+ proc.on('close', (code) => {
1112
1211
  code = code ?? 1; // code is null if spawned process terminated due to a signal
1113
1212
  if (!exitCallback) {
1114
1213
  process.exit(code);
1115
1214
  } else {
1116
- exitCallback(new CommanderError(code, 'commander.executeSubCommandAsync', '(close)'));
1215
+ exitCallback(
1216
+ new CommanderError(
1217
+ code,
1218
+ 'commander.executeSubCommandAsync',
1219
+ '(close)',
1220
+ ),
1221
+ );
1117
1222
  }
1118
1223
  });
1119
1224
  proc.on('error', (err) => {
1120
- // @ts-ignore
1225
+ // @ts-ignore: because err.code is an unknown property
1121
1226
  if (err.code === 'ENOENT') {
1122
1227
  const executableDirMessage = executableDir
1123
1228
  ? `searched for local subcommand relative to directory '${executableDir}'`
@@ -1127,14 +1232,18 @@ Expecting one of '${allowedValues.join("', '")}'`);
1127
1232
  - if the default executable name is not suitable, use the executableFile option to supply a custom name or path
1128
1233
  - ${executableDirMessage}`;
1129
1234
  throw new Error(executableMissing);
1130
- // @ts-ignore
1235
+ // @ts-ignore: because err.code is an unknown property
1131
1236
  } else if (err.code === 'EACCES') {
1132
1237
  throw new Error(`'${executableFile}' not executable`);
1133
1238
  }
1134
1239
  if (!exitCallback) {
1135
1240
  process.exit(1);
1136
1241
  } else {
1137
- const wrappedError = new CommanderError(1, 'commander.executeSubCommandAsync', '(error)');
1242
+ const wrappedError = new CommanderError(
1243
+ 1,
1244
+ 'commander.executeSubCommandAsync',
1245
+ '(error)',
1246
+ );
1138
1247
  wrappedError.nestedError = err;
1139
1248
  exitCallback(wrappedError);
1140
1249
  }
@@ -1153,7 +1262,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
1153
1262
  if (!subCommand) this.help({ error: true });
1154
1263
 
1155
1264
  let promiseChain;
1156
- promiseChain = this._chainOrCallSubCommandHook(promiseChain, subCommand, 'preSubcommand');
1265
+ promiseChain = this._chainOrCallSubCommandHook(
1266
+ promiseChain,
1267
+ subCommand,
1268
+ 'preSubcommand',
1269
+ );
1157
1270
  promiseChain = this._chainOrCall(promiseChain, () => {
1158
1271
  if (subCommand._executableHandler) {
1159
1272
  this._executeSubCommand(subCommand, operands.concat(unknown));
@@ -1181,9 +1294,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
1181
1294
  }
1182
1295
 
1183
1296
  // Fallback to parsing the help flag to invoke the help.
1184
- return this._dispatchSubcommand(subcommandName, [], [
1185
- this._getHelpOption()?.long ?? this._getHelpOption()?.short ?? '--help'
1186
- ]);
1297
+ return this._dispatchSubcommand(
1298
+ subcommandName,
1299
+ [],
1300
+ [this._getHelpOption()?.long ?? this._getHelpOption()?.short ?? '--help'],
1301
+ );
1187
1302
  }
1188
1303
 
1189
1304
  /**
@@ -1200,7 +1315,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
1200
1315
  }
1201
1316
  });
1202
1317
  // too many
1203
- if (this.registeredArguments.length > 0 && this.registeredArguments[this.registeredArguments.length - 1].variadic) {
1318
+ if (
1319
+ this.registeredArguments.length > 0 &&
1320
+ this.registeredArguments[this.registeredArguments.length - 1].variadic
1321
+ ) {
1204
1322
  return;
1205
1323
  }
1206
1324
  if (this.args.length > this.registeredArguments.length) {
@@ -1220,7 +1338,12 @@ Expecting one of '${allowedValues.join("', '")}'`);
1220
1338
  let parsedValue = value;
1221
1339
  if (value !== null && argument.parseArg) {
1222
1340
  const invalidValueMessage = `error: command-argument value '${value}' is invalid for argument '${argument.name()}'.`;
1223
- parsedValue = this._callParseArg(argument, value, previous, invalidValueMessage);
1341
+ parsedValue = this._callParseArg(
1342
+ argument,
1343
+ value,
1344
+ previous,
1345
+ invalidValueMessage,
1346
+ );
1224
1347
  }
1225
1348
  return parsedValue;
1226
1349
  };
@@ -1285,8 +1408,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
1285
1408
  const hooks = [];
1286
1409
  this._getCommandAndAncestors()
1287
1410
  .reverse()
1288
- .filter(cmd => cmd._lifeCycleHooks[event] !== undefined)
1289
- .forEach(hookedCommand => {
1411
+ .filter((cmd) => cmd._lifeCycleHooks[event] !== undefined)
1412
+ .forEach((hookedCommand) => {
1290
1413
  hookedCommand._lifeCycleHooks[event].forEach((callback) => {
1291
1414
  hooks.push({ hookedCommand, callback });
1292
1415
  });
@@ -1342,14 +1465,26 @@ Expecting one of '${allowedValues.join("', '")}'`);
1342
1465
  if (operands && this._findCommand(operands[0])) {
1343
1466
  return this._dispatchSubcommand(operands[0], operands.slice(1), unknown);
1344
1467
  }
1345
- if (this._getHelpCommand() && operands[0] === this._getHelpCommand().name()) {
1468
+ if (
1469
+ this._getHelpCommand() &&
1470
+ operands[0] === this._getHelpCommand().name()
1471
+ ) {
1346
1472
  return this._dispatchHelpCommand(operands[1]);
1347
1473
  }
1348
1474
  if (this._defaultCommandName) {
1349
1475
  this._outputHelpIfRequested(unknown); // Run the help for default command from parent rather than passing to default command
1350
- return this._dispatchSubcommand(this._defaultCommandName, operands, unknown);
1476
+ return this._dispatchSubcommand(
1477
+ this._defaultCommandName,
1478
+ operands,
1479
+ unknown,
1480
+ );
1351
1481
  }
1352
- if (this.commands.length && this.args.length === 0 && !this._actionHandler && !this._defaultCommandName) {
1482
+ if (
1483
+ this.commands.length &&
1484
+ this.args.length === 0 &&
1485
+ !this._actionHandler &&
1486
+ !this._defaultCommandName
1487
+ ) {
1353
1488
  // probably missing subcommand and no handler, user needs help (and exit)
1354
1489
  this.help({ error: true });
1355
1490
  }
@@ -1372,7 +1507,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
1372
1507
 
1373
1508
  let promiseChain;
1374
1509
  promiseChain = this._chainOrCallHooks(promiseChain, 'preAction');
1375
- promiseChain = this._chainOrCall(promiseChain, () => this._actionHandler(this.processedArgs));
1510
+ promiseChain = this._chainOrCall(promiseChain, () =>
1511
+ this._actionHandler(this.processedArgs),
1512
+ );
1376
1513
  if (this.parent) {
1377
1514
  promiseChain = this._chainOrCall(promiseChain, () => {
1378
1515
  this.parent.emit(commandEvent, operands, unknown); // legacy
@@ -1386,7 +1523,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
1386
1523
  this._processArguments();
1387
1524
  this.parent.emit(commandEvent, operands, unknown); // legacy
1388
1525
  } else if (operands.length) {
1389
- if (this._findCommand('*')) { // legacy default command
1526
+ if (this._findCommand('*')) {
1527
+ // legacy default command
1390
1528
  return this._dispatchSubcommand('*', operands, unknown);
1391
1529
  }
1392
1530
  if (this.listenerCount('command:*')) {
@@ -1413,10 +1551,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
1413
1551
  * Find matching command.
1414
1552
  *
1415
1553
  * @private
1554
+ * @return {Command | undefined}
1416
1555
  */
1417
1556
  _findCommand(name) {
1418
1557
  if (!name) return undefined;
1419
- return this.commands.find(cmd => cmd._name === name || cmd._aliases.includes(name));
1558
+ return this.commands.find(
1559
+ (cmd) => cmd._name === name || cmd._aliases.includes(name),
1560
+ );
1420
1561
  }
1421
1562
 
1422
1563
  /**
@@ -1424,11 +1565,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
1424
1565
  *
1425
1566
  * @param {string} arg
1426
1567
  * @return {Option}
1427
- * @package internal use only
1568
+ * @package
1428
1569
  */
1429
1570
 
1430
1571
  _findOption(arg) {
1431
- return this.options.find(option => option.is(arg));
1572
+ return this.options.find((option) => option.is(arg));
1432
1573
  }
1433
1574
 
1434
1575
  /**
@@ -1442,7 +1583,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
1442
1583
  // Walk up hierarchy so can call in subcommand after checking for displaying help.
1443
1584
  this._getCommandAndAncestors().forEach((cmd) => {
1444
1585
  cmd.options.forEach((anOption) => {
1445
- if (anOption.mandatory && (cmd.getOptionValue(anOption.attributeName()) === undefined)) {
1586
+ if (
1587
+ anOption.mandatory &&
1588
+ cmd.getOptionValue(anOption.attributeName()) === undefined
1589
+ ) {
1446
1590
  cmd.missingMandatoryOptionValue(anOption);
1447
1591
  }
1448
1592
  });
@@ -1455,23 +1599,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
1455
1599
  * @private
1456
1600
  */
1457
1601
  _checkForConflictingLocalOptions() {
1458
- const definedNonDefaultOptions = this.options.filter(
1459
- (option) => {
1460
- const optionKey = option.attributeName();
1461
- if (this.getOptionValue(optionKey) === undefined) {
1462
- return false;
1463
- }
1464
- return this.getOptionValueSource(optionKey) !== 'default';
1602
+ const definedNonDefaultOptions = this.options.filter((option) => {
1603
+ const optionKey = option.attributeName();
1604
+ if (this.getOptionValue(optionKey) === undefined) {
1605
+ return false;
1465
1606
  }
1466
- );
1607
+ return this.getOptionValueSource(optionKey) !== 'default';
1608
+ });
1467
1609
 
1468
1610
  const optionsWithConflicting = definedNonDefaultOptions.filter(
1469
- (option) => option.conflictsWith.length > 0
1611
+ (option) => option.conflictsWith.length > 0,
1470
1612
  );
1471
1613
 
1472
1614
  optionsWithConflicting.forEach((option) => {
1473
1615
  const conflictingAndDefined = definedNonDefaultOptions.find((defined) =>
1474
- option.conflictsWith.includes(defined.attributeName())
1616
+ option.conflictsWith.includes(defined.attributeName()),
1475
1617
  );
1476
1618
  if (conflictingAndDefined) {
1477
1619
  this._conflictingOption(option, conflictingAndDefined);
@@ -1551,7 +1693,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
1551
1693
  value = args.shift();
1552
1694
  }
1553
1695
  this.emit(`option:${option.name()}`, value);
1554
- } else { // boolean flag
1696
+ } else {
1697
+ // boolean flag
1555
1698
  this.emit(`option:${option.name()}`);
1556
1699
  }
1557
1700
  activeVariadicOption = option.variadic ? option : null;
@@ -1563,7 +1706,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
1563
1706
  if (arg.length > 2 && arg[0] === '-' && arg[1] !== '-') {
1564
1707
  const option = this._findOption(`-${arg[1]}`);
1565
1708
  if (option) {
1566
- if (option.required || (option.optional && this._combineFlagAndOptionalValue)) {
1709
+ if (
1710
+ option.required ||
1711
+ (option.optional && this._combineFlagAndOptionalValue)
1712
+ ) {
1567
1713
  // option with value following in same argument
1568
1714
  this.emit(`option:${option.name()}`, arg.slice(2));
1569
1715
  } else {
@@ -1594,12 +1740,19 @@ Expecting one of '${allowedValues.join("', '")}'`);
1594
1740
  }
1595
1741
 
1596
1742
  // If using positionalOptions, stop processing our options at subcommand.
1597
- if ((this._enablePositionalOptions || this._passThroughOptions) && operands.length === 0 && unknown.length === 0) {
1743
+ if (
1744
+ (this._enablePositionalOptions || this._passThroughOptions) &&
1745
+ operands.length === 0 &&
1746
+ unknown.length === 0
1747
+ ) {
1598
1748
  if (this._findCommand(arg)) {
1599
1749
  operands.push(arg);
1600
1750
  if (args.length > 0) unknown.push(...args);
1601
1751
  break;
1602
- } else if (this._getHelpCommand() && arg === this._getHelpCommand().name()) {
1752
+ } else if (
1753
+ this._getHelpCommand() &&
1754
+ arg === this._getHelpCommand().name()
1755
+ ) {
1603
1756
  operands.push(arg);
1604
1757
  if (args.length > 0) operands.push(...args);
1605
1758
  break;
@@ -1627,7 +1780,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1627
1780
  /**
1628
1781
  * Return an object containing local option values as key-value pairs.
1629
1782
  *
1630
- * @return {Object}
1783
+ * @return {object}
1631
1784
  */
1632
1785
  opts() {
1633
1786
  if (this._storeOptionsAsProperties) {
@@ -1637,7 +1790,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
1637
1790
 
1638
1791
  for (let i = 0; i < len; i++) {
1639
1792
  const key = this.options[i].attributeName();
1640
- result[key] = key === this._versionOptionName ? this._version : this[key];
1793
+ result[key] =
1794
+ key === this._versionOptionName ? this._version : this[key];
1641
1795
  }
1642
1796
  return result;
1643
1797
  }
@@ -1648,13 +1802,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
1648
1802
  /**
1649
1803
  * Return an object containing merged local and global option values as key-value pairs.
1650
1804
  *
1651
- * @return {Object}
1805
+ * @return {object}
1652
1806
  */
1653
1807
  optsWithGlobals() {
1654
1808
  // globals overwrite locals
1655
1809
  return this._getCommandAndAncestors().reduce(
1656
1810
  (combinedOptions, cmd) => Object.assign(combinedOptions, cmd.opts()),
1657
- {}
1811
+ {},
1658
1812
  );
1659
1813
  }
1660
1814
 
@@ -1662,13 +1816,16 @@ Expecting one of '${allowedValues.join("', '")}'`);
1662
1816
  * Display error message and exit (or call exitOverride).
1663
1817
  *
1664
1818
  * @param {string} message
1665
- * @param {Object} [errorOptions]
1819
+ * @param {object} [errorOptions]
1666
1820
  * @param {string} [errorOptions.code] - an id string representing the error
1667
1821
  * @param {number} [errorOptions.exitCode] - used with process.exit
1668
1822
  */
1669
1823
  error(message, errorOptions) {
1670
1824
  // output handling
1671
- this._outputConfiguration.outputError(`${message}\n`, this._outputConfiguration.writeErr);
1825
+ this._outputConfiguration.outputError(
1826
+ `${message}\n`,
1827
+ this._outputConfiguration.writeErr,
1828
+ );
1672
1829
  if (typeof this._showHelpAfterError === 'string') {
1673
1830
  this._outputConfiguration.writeErr(`${this._showHelpAfterError}\n`);
1674
1831
  } else if (this._showHelpAfterError) {
@@ -1694,11 +1851,18 @@ Expecting one of '${allowedValues.join("', '")}'`);
1694
1851
  if (option.envVar && option.envVar in process.env) {
1695
1852
  const optionKey = option.attributeName();
1696
1853
  // Priority check. Do not overwrite cli or options from unknown source (client-code).
1697
- if (this.getOptionValue(optionKey) === undefined || ['default', 'config', 'env'].includes(this.getOptionValueSource(optionKey))) {
1698
- if (option.required || option.optional) { // option can take a value
1854
+ if (
1855
+ this.getOptionValue(optionKey) === undefined ||
1856
+ ['default', 'config', 'env'].includes(
1857
+ this.getOptionValueSource(optionKey),
1858
+ )
1859
+ ) {
1860
+ if (option.required || option.optional) {
1861
+ // option can take a value
1699
1862
  // keep very simple, optional always takes value
1700
1863
  this.emit(`optionEnv:${option.name()}`, process.env[option.envVar]);
1701
- } else { // boolean
1864
+ } else {
1865
+ // boolean
1702
1866
  // keep very simple, only care that envVar defined and not the value
1703
1867
  this.emit(`optionEnv:${option.name()}`);
1704
1868
  }
@@ -1715,17 +1879,30 @@ Expecting one of '${allowedValues.join("', '")}'`);
1715
1879
  _parseOptionsImplied() {
1716
1880
  const dualHelper = new DualOptions(this.options);
1717
1881
  const hasCustomOptionValue = (optionKey) => {
1718
- return this.getOptionValue(optionKey) !== undefined && !['default', 'implied'].includes(this.getOptionValueSource(optionKey));
1882
+ return (
1883
+ this.getOptionValue(optionKey) !== undefined &&
1884
+ !['default', 'implied'].includes(this.getOptionValueSource(optionKey))
1885
+ );
1719
1886
  };
1720
1887
  this.options
1721
- .filter(option => (option.implied !== undefined) &&
1722
- hasCustomOptionValue(option.attributeName()) &&
1723
- dualHelper.valueFromOption(this.getOptionValue(option.attributeName()), option))
1888
+ .filter(
1889
+ (option) =>
1890
+ option.implied !== undefined &&
1891
+ hasCustomOptionValue(option.attributeName()) &&
1892
+ dualHelper.valueFromOption(
1893
+ this.getOptionValue(option.attributeName()),
1894
+ option,
1895
+ ),
1896
+ )
1724
1897
  .forEach((option) => {
1725
1898
  Object.keys(option.implied)
1726
- .filter(impliedKey => !hasCustomOptionValue(impliedKey))
1727
- .forEach(impliedKey => {
1728
- this.setOptionValueWithSource(impliedKey, option.implied[impliedKey], 'implied');
1899
+ .filter((impliedKey) => !hasCustomOptionValue(impliedKey))
1900
+ .forEach((impliedKey) => {
1901
+ this.setOptionValueWithSource(
1902
+ impliedKey,
1903
+ option.implied[impliedKey],
1904
+ 'implied',
1905
+ );
1729
1906
  });
1730
1907
  });
1731
1908
  }
@@ -1779,12 +1956,18 @@ Expecting one of '${allowedValues.join("', '")}'`);
1779
1956
  const findBestOptionFromValue = (option) => {
1780
1957
  const optionKey = option.attributeName();
1781
1958
  const optionValue = this.getOptionValue(optionKey);
1782
- const negativeOption = this.options.find(target => target.negate && optionKey === target.attributeName());
1783
- const positiveOption = this.options.find(target => !target.negate && optionKey === target.attributeName());
1784
- if (negativeOption && (
1785
- (negativeOption.presetArg === undefined && optionValue === false) ||
1786
- (negativeOption.presetArg !== undefined && optionValue === negativeOption.presetArg)
1787
- )) {
1959
+ const negativeOption = this.options.find(
1960
+ (target) => target.negate && optionKey === target.attributeName(),
1961
+ );
1962
+ const positiveOption = this.options.find(
1963
+ (target) => !target.negate && optionKey === target.attributeName(),
1964
+ );
1965
+ if (
1966
+ negativeOption &&
1967
+ ((negativeOption.presetArg === undefined && optionValue === false) ||
1968
+ (negativeOption.presetArg !== undefined &&
1969
+ optionValue === negativeOption.presetArg))
1970
+ ) {
1788
1971
  return negativeOption;
1789
1972
  }
1790
1973
  return positiveOption || option;
@@ -1818,11 +2001,14 @@ Expecting one of '${allowedValues.join("', '")}'`);
1818
2001
  if (flag.startsWith('--') && this._showSuggestionAfterError) {
1819
2002
  // Looping to pick up the global options too
1820
2003
  let candidateFlags = [];
2004
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
1821
2005
  let command = this;
1822
2006
  do {
1823
- const moreFlags = command.createHelp().visibleOptions(command)
1824
- .filter(option => option.long)
1825
- .map(option => option.long);
2007
+ const moreFlags = command
2008
+ .createHelp()
2009
+ .visibleOptions(command)
2010
+ .filter((option) => option.long)
2011
+ .map((option) => option.long);
1826
2012
  candidateFlags = candidateFlags.concat(moreFlags);
1827
2013
  command = command.parent;
1828
2014
  } while (command && !command._enablePositionalOptions);
@@ -1844,7 +2030,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1844
2030
  if (this._allowExcessArguments) return;
1845
2031
 
1846
2032
  const expected = this.registeredArguments.length;
1847
- const s = (expected === 1) ? '' : 's';
2033
+ const s = expected === 1 ? '' : 's';
1848
2034
  const forSubcommand = this.parent ? ` for '${this.name()}'` : '';
1849
2035
  const message = `error: too many arguments${forSubcommand}. Expected ${expected} argument${s} but got ${receivedArgs.length}.`;
1850
2036
  this.error(message, { code: 'commander.excessArguments' });
@@ -1862,11 +2048,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
1862
2048
 
1863
2049
  if (this._showSuggestionAfterError) {
1864
2050
  const candidateNames = [];
1865
- this.createHelp().visibleCommands(this).forEach((command) => {
1866
- candidateNames.push(command.name());
1867
- // just visible alias
1868
- if (command.alias()) candidateNames.push(command.alias());
1869
- });
2051
+ this.createHelp()
2052
+ .visibleCommands(this)
2053
+ .forEach((command) => {
2054
+ candidateNames.push(command.name());
2055
+ // just visible alias
2056
+ if (command.alias()) candidateNames.push(command.alias());
2057
+ });
1870
2058
  suggestion = suggestSimilar(unknownName, candidateNames);
1871
2059
  }
1872
2060
 
@@ -1907,11 +2095,12 @@ Expecting one of '${allowedValues.join("', '")}'`);
1907
2095
  * Set the description.
1908
2096
  *
1909
2097
  * @param {string} [str]
1910
- * @param {Object} [argsDescription]
2098
+ * @param {object} [argsDescription]
1911
2099
  * @return {(string|Command)}
1912
2100
  */
1913
2101
  description(str, argsDescription) {
1914
- if (str === undefined && argsDescription === undefined) return this._description;
2102
+ if (str === undefined && argsDescription === undefined)
2103
+ return this._description;
1915
2104
  this._description = str;
1916
2105
  if (argsDescription) {
1917
2106
  this._argsDescription = argsDescription;
@@ -1944,18 +2133,27 @@ Expecting one of '${allowedValues.join("', '")}'`);
1944
2133
  if (alias === undefined) return this._aliases[0]; // just return first, for backwards compatibility
1945
2134
 
1946
2135
  /** @type {Command} */
2136
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
1947
2137
  let command = this;
1948
- if (this.commands.length !== 0 && this.commands[this.commands.length - 1]._executableHandler) {
2138
+ if (
2139
+ this.commands.length !== 0 &&
2140
+ this.commands[this.commands.length - 1]._executableHandler
2141
+ ) {
1949
2142
  // assume adding alias for last added executable subcommand, rather than this
1950
2143
  command = this.commands[this.commands.length - 1];
1951
2144
  }
1952
2145
 
1953
- if (alias === command._name) throw new Error('Command alias can\'t be the same as its name');
2146
+ if (alias === command._name)
2147
+ throw new Error("Command alias can't be the same as its name");
1954
2148
  const matchingCommand = this.parent?._findCommand(alias);
1955
2149
  if (matchingCommand) {
1956
2150
  // c.f. _registerCommand
1957
- const existingCmd = [matchingCommand.name()].concat(matchingCommand.aliases()).join('|');
1958
- throw new Error(`cannot add alias '${alias}' to command '${this.name()}' as already have command '${existingCmd}'`);
2151
+ const existingCmd = [matchingCommand.name()]
2152
+ .concat(matchingCommand.aliases())
2153
+ .join('|');
2154
+ throw new Error(
2155
+ `cannot add alias '${alias}' to command '${this.name()}' as already have command '${existingCmd}'`,
2156
+ );
1959
2157
  }
1960
2158
 
1961
2159
  command._aliases.push(alias);
@@ -1993,11 +2191,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
1993
2191
  const args = this.registeredArguments.map((arg) => {
1994
2192
  return humanReadableArgName(arg);
1995
2193
  });
1996
- return [].concat(
1997
- (this.options.length || (this._helpOption !== null) ? '[options]' : []),
1998
- (this.commands.length ? '[command]' : []),
1999
- (this.registeredArguments.length ? args : [])
2000
- ).join(' ');
2194
+ return []
2195
+ .concat(
2196
+ this.options.length || this._helpOption !== null ? '[options]' : [],
2197
+ this.commands.length ? '[command]' : [],
2198
+ this.registeredArguments.length ? args : [],
2199
+ )
2200
+ .join(' ');
2001
2201
  }
2002
2202
 
2003
2203
  this._usage = str;
@@ -2063,28 +2263,49 @@ Expecting one of '${allowedValues.join("', '")}'`);
2063
2263
 
2064
2264
  helpInformation(contextOptions) {
2065
2265
  const helper = this.createHelp();
2066
- if (helper.helpWidth === undefined) {
2067
- helper.helpWidth = (contextOptions && contextOptions.error) ? this._outputConfiguration.getErrHelpWidth() : this._outputConfiguration.getOutHelpWidth();
2068
- }
2069
- return helper.formatHelp(this, helper);
2266
+ const context = this._getOutputContext(contextOptions);
2267
+ helper.prepareContext({
2268
+ error: context.error,
2269
+ helpWidth: context.helpWidth,
2270
+ outputHasColors: context.hasColors,
2271
+ });
2272
+ const text = helper.formatHelp(this, helper);
2273
+ if (context.hasColors) return text;
2274
+ return this._outputConfiguration.stripColor(text);
2070
2275
  }
2071
2276
 
2072
2277
  /**
2278
+ * @typedef HelpContext
2279
+ * @type {object}
2280
+ * @property {boolean} error
2281
+ * @property {number} helpWidth
2282
+ * @property {boolean} hasColors
2283
+ * @property {function} write - includes stripColor if needed
2284
+ *
2285
+ * @returns {HelpContext}
2073
2286
  * @private
2074
2287
  */
2075
2288
 
2076
- _getHelpContext(contextOptions) {
2289
+ _getOutputContext(contextOptions) {
2077
2290
  contextOptions = contextOptions || {};
2078
- const context = { error: !!contextOptions.error };
2079
- let write;
2080
- if (context.error) {
2081
- write = (arg) => this._outputConfiguration.writeErr(arg);
2291
+ const error = !!contextOptions.error;
2292
+ let baseWrite;
2293
+ let hasColors;
2294
+ let helpWidth;
2295
+ if (error) {
2296
+ baseWrite = (str) => this._outputConfiguration.writeErr(str);
2297
+ hasColors = this._outputConfiguration.getErrHasColors();
2298
+ helpWidth = this._outputConfiguration.getErrHelpWidth();
2082
2299
  } else {
2083
- write = (arg) => this._outputConfiguration.writeOut(arg);
2300
+ baseWrite = (str) => this._outputConfiguration.writeOut(str);
2301
+ hasColors = this._outputConfiguration.getOutHasColors();
2302
+ helpWidth = this._outputConfiguration.getOutHelpWidth();
2084
2303
  }
2085
- context.write = contextOptions.write || write;
2086
- context.command = this;
2087
- return context;
2304
+ const write = (str) => {
2305
+ if (!hasColors) str = this._outputConfiguration.stripColor(str);
2306
+ return baseWrite(str);
2307
+ };
2308
+ return { error, write, hasColors, helpWidth };
2088
2309
  }
2089
2310
 
2090
2311
  /**
@@ -2101,25 +2322,39 @@ Expecting one of '${allowedValues.join("', '")}'`);
2101
2322
  deprecatedCallback = contextOptions;
2102
2323
  contextOptions = undefined;
2103
2324
  }
2104
- const context = this._getHelpContext(contextOptions);
2105
2325
 
2106
- this._getCommandAndAncestors().reverse().forEach(command => command.emit('beforeAllHelp', context));
2107
- this.emit('beforeHelp', context);
2326
+ const outputContext = this._getOutputContext(contextOptions);
2327
+ /** @type {HelpTextEventContext} */
2328
+ const eventContext = {
2329
+ error: outputContext.error,
2330
+ write: outputContext.write,
2331
+ command: this,
2332
+ };
2333
+
2334
+ this._getCommandAndAncestors()
2335
+ .reverse()
2336
+ .forEach((command) => command.emit('beforeAllHelp', eventContext));
2337
+ this.emit('beforeHelp', eventContext);
2108
2338
 
2109
- let helpInformation = this.helpInformation(context);
2339
+ let helpInformation = this.helpInformation({ error: outputContext.error });
2110
2340
  if (deprecatedCallback) {
2111
2341
  helpInformation = deprecatedCallback(helpInformation);
2112
- if (typeof helpInformation !== 'string' && !Buffer.isBuffer(helpInformation)) {
2342
+ if (
2343
+ typeof helpInformation !== 'string' &&
2344
+ !Buffer.isBuffer(helpInformation)
2345
+ ) {
2113
2346
  throw new Error('outputHelp callback must return a string or a Buffer');
2114
2347
  }
2115
2348
  }
2116
- context.write(helpInformation);
2349
+ outputContext.write(helpInformation);
2117
2350
 
2118
2351
  if (this._getHelpOption()?.long) {
2119
2352
  this.emit(this._getHelpOption().long); // deprecated
2120
2353
  }
2121
- this.emit('afterHelp', context);
2122
- this._getCommandAndAncestors().forEach(command => command.emit('afterAllHelp', context));
2354
+ this.emit('afterHelp', eventContext);
2355
+ this._getCommandAndAncestors().forEach((command) =>
2356
+ command.emit('afterAllHelp', eventContext),
2357
+ );
2123
2358
  }
2124
2359
 
2125
2360
  /**
@@ -2138,6 +2373,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
2138
2373
  helpOption(flags, description) {
2139
2374
  // Support disabling built-in help option.
2140
2375
  if (typeof flags === 'boolean') {
2376
+ // true is not an expected value. Do something sensible but no unit-test.
2377
+ // istanbul ignore if
2141
2378
  if (flags) {
2142
2379
  this._helpOption = this._helpOption ?? undefined; // preserve existing option
2143
2380
  } else {
@@ -2159,7 +2396,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2159
2396
  * Returns null if has been disabled with .helpOption(false).
2160
2397
  *
2161
2398
  * @returns {(Option | null)} the help option
2162
- * @package internal use only
2399
+ * @package
2163
2400
  */
2164
2401
  _getHelpOption() {
2165
2402
  // Lazy create help option on demand.
@@ -2191,14 +2428,28 @@ Expecting one of '${allowedValues.join("', '")}'`);
2191
2428
 
2192
2429
  help(contextOptions) {
2193
2430
  this.outputHelp(contextOptions);
2194
- let exitCode = process.exitCode || 0;
2195
- if (exitCode === 0 && contextOptions && typeof contextOptions !== 'function' && contextOptions.error) {
2431
+ let exitCode = Number(process.exitCode ?? 0); // process.exitCode does allow a string or an integer, but we prefer just a number
2432
+ if (
2433
+ exitCode === 0 &&
2434
+ contextOptions &&
2435
+ typeof contextOptions !== 'function' &&
2436
+ contextOptions.error
2437
+ ) {
2196
2438
  exitCode = 1;
2197
2439
  }
2198
2440
  // message: do not have all displayed text available so only passing placeholder.
2199
2441
  this._exit(exitCode, 'commander.help', '(outputHelp)');
2200
2442
  }
2201
2443
 
2444
+ /**
2445
+ * // Do a little typing to coordinate emit and listener for the help text events.
2446
+ * @typedef HelpTextEventContext
2447
+ * @type {object}
2448
+ * @property {boolean} error
2449
+ * @property {Command} command
2450
+ * @property {function} write
2451
+ */
2452
+
2202
2453
  /**
2203
2454
  * Add additional text to be displayed with the built-in help.
2204
2455
  *
@@ -2209,14 +2460,16 @@ Expecting one of '${allowedValues.join("', '")}'`);
2209
2460
  * @param {(string | Function)} text - string to add, or a function returning a string
2210
2461
  * @return {Command} `this` command for chaining
2211
2462
  */
2463
+
2212
2464
  addHelpText(position, text) {
2213
2465
  const allowedValues = ['beforeAll', 'before', 'after', 'afterAll'];
2214
2466
  if (!allowedValues.includes(position)) {
2215
2467
  throw new Error(`Unexpected value for position to addHelpText.
2216
2468
  Expecting one of '${allowedValues.join("', '")}'`);
2217
2469
  }
2470
+
2218
2471
  const helpEvent = `${position}Help`;
2219
- this.on(helpEvent, (context) => {
2472
+ this.on(helpEvent, (/** @type {HelpTextEventContext} */ context) => {
2220
2473
  let helpStr;
2221
2474
  if (typeof text === 'function') {
2222
2475
  helpStr = text({ error: context.error, command: context.command });
@@ -2240,7 +2493,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2240
2493
 
2241
2494
  _outputHelpIfRequested(args) {
2242
2495
  const helpOption = this._getHelpOption();
2243
- const helpRequested = helpOption && args.find(arg => helpOption.is(arg));
2496
+ const helpRequested = helpOption && args.find((arg) => helpOption.is(arg));
2244
2497
  if (helpRequested) {
2245
2498
  this.outputHelp();
2246
2499
  // (Do not have all displayed text available so only passing placeholder.)
@@ -2273,7 +2526,9 @@ function incrementNodeInspectorPort(args) {
2273
2526
  if ((match = arg.match(/^(--inspect(-brk)?)$/)) !== null) {
2274
2527
  // e.g. --inspect
2275
2528
  debugOption = match[1];
2276
- } else if ((match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+)$/)) !== null) {
2529
+ } else if (
2530
+ (match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+)$/)) !== null
2531
+ ) {
2277
2532
  debugOption = match[1];
2278
2533
  if (/^\d+$/.test(match[3])) {
2279
2534
  // e.g. --inspect=1234
@@ -2282,7 +2537,9 @@ function incrementNodeInspectorPort(args) {
2282
2537
  // e.g. --inspect=localhost
2283
2538
  debugHost = match[3];
2284
2539
  }
2285
- } else if ((match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/)) !== null) {
2540
+ } else if (
2541
+ (match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/)) !== null
2542
+ ) {
2286
2543
  // e.g. --inspect=localhost:1234
2287
2544
  debugOption = match[1];
2288
2545
  debugHost = match[3];
@@ -2296,4 +2553,33 @@ function incrementNodeInspectorPort(args) {
2296
2553
  });
2297
2554
  }
2298
2555
 
2556
+ /**
2557
+ * @returns {boolean | undefined}
2558
+ * @package
2559
+ */
2560
+ function useColor() {
2561
+ // Test for common conventions.
2562
+ // NB: the observed behaviour is in combination with how author adds color! For example:
2563
+ // - we do not test NODE_DISABLE_COLORS, but util:styletext does
2564
+ // - we do test NO_COLOR, but Chalk does not
2565
+ //
2566
+ // References:
2567
+ // https://no-color.org
2568
+ // https://bixense.com/clicolors/
2569
+ // https://github.com/nodejs/node/blob/0a00217a5f67ef4a22384cfc80eb6dd9a917fdc1/lib/internal/tty.js#L109
2570
+ // https://github.com/chalk/supports-color/blob/c214314a14bcb174b12b3014b2b0a8de375029ae/index.js#L33
2571
+ // (https://force-color.org recent web page from 2023, does not match major javascript implementations)
2572
+
2573
+ if (
2574
+ process.env.NO_COLOR ||
2575
+ process.env.FORCE_COLOR === '0' ||
2576
+ process.env.FORCE_COLOR === 'false'
2577
+ )
2578
+ return false;
2579
+ if (process.env.FORCE_COLOR || process.env.CLICOLOR_FORCE !== undefined)
2580
+ return true;
2581
+ return undefined;
2582
+ }
2583
+
2299
2584
  exports.Command = Command;
2585
+ exports.useColor = useColor; // exporting for tests