commander 12.0.0-0 → 12.0.0-1

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
@@ -37,7 +37,7 @@ Read this in other languages: English | [简体中文](./Readme_zh-CN.md)
37
37
  - [.usage](#usage)
38
38
  - [.description and .summary](#description-and-summary)
39
39
  - [.helpOption(flags, description)](#helpoptionflags-description)
40
- - [.addHelpCommand()](#addhelpcommand)
40
+ - [.helpCommand()](#helpcommand)
41
41
  - [More configuration](#more-configuration-2)
42
42
  - [Custom event listeners](#custom-event-listeners)
43
43
  - [Bits and pieces](#bits-and-pieces)
@@ -904,16 +904,20 @@ program
904
904
  .helpOption('-e, --HELP', 'read more information');
905
905
  ```
906
906
 
907
- ### .addHelpCommand()
907
+ (Or use `.addHelpOption()` to add an option you construct yourself.)
908
908
 
909
- A help command is added by default if your command has subcommands. You can explicitly turn on or off the implicit help command with `.addHelpCommand()` and `.addHelpCommand(false)`.
909
+ ### .helpCommand()
910
+
911
+ A help command is added by default if your command has subcommands. You can explicitly turn on or off the implicit help command with `.helpCommand(true)` and `.helpCommand(false)`.
910
912
 
911
913
  You can both turn on and customise the help command by supplying the name and description:
912
914
 
913
915
  ```js
914
- program.addHelpCommand('assist [command]', 'show assistance');
916
+ program.helpCommand('assist [command]', 'show assistance');
915
917
  ```
916
918
 
919
+ (Or use `.addHelpCommand()` to add a command you construct yourself.)
920
+
917
921
  ### More configuration
918
922
 
919
923
  The built-in help is formatted using the Help class.
package/lib/command.js CHANGED
@@ -7,7 +7,7 @@ const process = require('process');
7
7
  const { Argument, humanReadableArgName } = require('./argument.js');
8
8
  const { CommanderError } = require('./error.js');
9
9
  const { Help } = require('./help.js');
10
- const { Option, splitOptionFlags, DualOptions } = require('./option.js');
10
+ const { Option, DualOptions } = require('./option.js');
11
11
  const { suggestSimilar } = require('./suggestSimilar');
12
12
 
13
13
  class Command extends EventEmitter {
@@ -52,7 +52,7 @@ class Command extends EventEmitter {
52
52
  this._enablePositionalOptions = false;
53
53
  this._passThroughOptions = false;
54
54
  this._lifeCycleHooks = {}; // a hash of arrays
55
- /** @type {boolean | string} */
55
+ /** @type {(boolean | string)} */
56
56
  this._showHelpAfterError = false;
57
57
  this._showSuggestionAfterError = true;
58
58
 
@@ -66,15 +66,11 @@ class Command extends EventEmitter {
66
66
  };
67
67
 
68
68
  this._hidden = false;
69
- this._hasHelpOption = true;
70
- this._helpFlags = '-h, --help';
71
- this._helpDescription = 'display help for command';
72
- this._helpShortFlag = '-h';
73
- this._helpLongFlag = '--help';
74
- this._addImplicitHelpCommand = undefined; // Deliberately undefined, not decided whether true or false
75
- this._helpCommandName = 'help';
76
- this._helpCommandnameAndArgs = 'help [command]';
77
- this._helpCommandDescription = 'display help for command';
69
+ /** @type {(Option | null | undefined)} */
70
+ this._helpOption = undefined; // Lazy created on demand. May be null if help option is disabled.
71
+ this._addImplicitHelpCommand = undefined; // undecided whether true or false yet, not inherited
72
+ /** @type {Command} */
73
+ this._helpCommand = undefined; // lazy initialised, inherited
78
74
  this._helpConfiguration = {};
79
75
  }
80
76
 
@@ -88,14 +84,8 @@ class Command extends EventEmitter {
88
84
  */
89
85
  copyInheritedSettings(sourceCommand) {
90
86
  this._outputConfiguration = sourceCommand._outputConfiguration;
91
- this._hasHelpOption = sourceCommand._hasHelpOption;
92
- this._helpFlags = sourceCommand._helpFlags;
93
- this._helpDescription = sourceCommand._helpDescription;
94
- this._helpShortFlag = sourceCommand._helpShortFlag;
95
- this._helpLongFlag = sourceCommand._helpLongFlag;
96
- this._helpCommandName = sourceCommand._helpCommandName;
97
- this._helpCommandnameAndArgs = sourceCommand._helpCommandnameAndArgs;
98
- this._helpCommandDescription = sourceCommand._helpCommandDescription;
87
+ this._helpOption = sourceCommand._helpOption;
88
+ this._helpCommand = sourceCommand._helpCommand;
99
89
  this._helpConfiguration = sourceCommand._helpConfiguration;
100
90
  this._exitCallback = sourceCommand._exitCallback;
101
91
  this._storeOptionsAsProperties = sourceCommand._storeOptionsAsProperties;
@@ -141,7 +131,7 @@ class Command extends EventEmitter {
141
131
  * .command('stop [service]', 'stop named service, or all if no name supplied');
142
132
  *
143
133
  * @param {string} nameAndArgs - command name and arguments, args are `<required>` or `[optional]` and last may also be `variadic...`
144
- * @param {Object|string} [actionOptsOrExecDesc] - configuration options (for action), or description (for executable)
134
+ * @param {(Object|string)} [actionOptsOrExecDesc] - configuration options (for action), or description (for executable)
145
135
  * @param {Object} [execOpts] - configuration options (for executable)
146
136
  * @return {Command} returns new command for action handler, or `this` for executable command
147
137
  */
@@ -203,7 +193,7 @@ class Command extends EventEmitter {
203
193
  * or with a subclass of Help by overriding createHelp().
204
194
  *
205
195
  * @param {Object} [configuration] - configuration options
206
- * @return {Command|Object} `this` command for chaining, or stored configuration
196
+ * @return {(Command|Object)} `this` command for chaining, or stored configuration
207
197
  */
208
198
 
209
199
  configureHelp(configuration) {
@@ -229,7 +219,7 @@ class Command extends EventEmitter {
229
219
  * outputError(str, write) // used for displaying errors, and not used for displaying help
230
220
  *
231
221
  * @param {Object} [configuration] - configuration options
232
- * @return {Command|Object} `this` command for chaining, or stored configuration
222
+ * @return {(Command|Object)} `this` command for chaining, or stored configuration
233
223
  */
234
224
 
235
225
  configureOutput(configuration) {
@@ -242,7 +232,7 @@ class Command extends EventEmitter {
242
232
  /**
243
233
  * Display the help or a custom message after an error occurs.
244
234
  *
245
- * @param {boolean|string} [displayHelp]
235
+ * @param {(boolean|string)} [displayHelp]
246
236
  * @return {Command} `this` command for chaining
247
237
  */
248
238
  showHelpAfterError(displayHelp = true) {
@@ -316,7 +306,7 @@ class Command extends EventEmitter {
316
306
  *
317
307
  * @param {string} name
318
308
  * @param {string} [description]
319
- * @param {Function|*} [fn] - custom argument processing function
309
+ * @param {(Function|*)} [fn] - custom argument processing function
320
310
  * @param {*} [defaultValue]
321
311
  * @return {Command} `this` command for chaining
322
312
  */
@@ -369,39 +359,76 @@ class Command extends EventEmitter {
369
359
  }
370
360
 
371
361
  /**
372
- * Override default decision whether to add implicit help command.
362
+ * Customise or override default help command. By default a help command is automatically added if your command has subcommands.
373
363
  *
374
- * addHelpCommand() // force on
375
- * addHelpCommand(false); // force off
376
- * addHelpCommand('help [cmd]', 'display help for [cmd]'); // force on with custom details
364
+ * program.helpCommand('help [cmd]');
365
+ * program.helpCommand('help [cmd]', 'show help');
366
+ * program.helpCommand(false); // suppress default help command
367
+ * program.helpCommand(true); // add help command even if no subcommands
377
368
  *
369
+ * @param {string|boolean} enableOrNameAndArgs - enable with custom name and/or arguments, or boolean to override whether added
370
+ * @param {string} [description] - custom description
378
371
  * @return {Command} `this` command for chaining
379
372
  */
380
373
 
381
- addHelpCommand(enableOrNameAndArgs, description) {
382
- if (enableOrNameAndArgs === false) {
383
- this._addImplicitHelpCommand = false;
384
- } else {
385
- this._addImplicitHelpCommand = true;
386
- if (typeof enableOrNameAndArgs === 'string') {
387
- this._helpCommandName = enableOrNameAndArgs.split(' ')[0];
388
- this._helpCommandnameAndArgs = enableOrNameAndArgs;
389
- }
390
- this._helpCommandDescription = description || this._helpCommandDescription;
374
+ helpCommand(enableOrNameAndArgs, description) {
375
+ if (typeof enableOrNameAndArgs === 'boolean') {
376
+ this._addImplicitHelpCommand = enableOrNameAndArgs;
377
+ return this;
391
378
  }
379
+
380
+ enableOrNameAndArgs = enableOrNameAndArgs ?? 'help [command]';
381
+ const [, helpName, helpArgs] = enableOrNameAndArgs.match(/([^ ]+) *(.*)/);
382
+ const helpDescription = description ?? 'display help for command';
383
+
384
+ const helpCommand = this.createCommand(helpName);
385
+ helpCommand.helpOption(false);
386
+ if (helpArgs) helpCommand.arguments(helpArgs);
387
+ if (helpDescription) helpCommand.description(helpDescription);
388
+
389
+ this._addImplicitHelpCommand = true;
390
+ this._helpCommand = helpCommand;
391
+
392
392
  return this;
393
393
  }
394
394
 
395
395
  /**
396
- * @return {boolean}
397
- * @package internal use only
396
+ * Add prepared custom help command.
397
+ *
398
+ * @param {(Command|string|boolean)} helpCommand - custom help command, or deprecated enableOrNameAndArgs as for `.helpCommand()`
399
+ * @param {string} [deprecatedDescription] - deprecated custom description used with custom name only
400
+ * @return {Command} `this` command for chaining
401
+ */
402
+ addHelpCommand(helpCommand, deprecatedDescription) {
403
+ // If not passed an object, call through to helpCommand for backwards compatibility,
404
+ // as addHelpCommand was originally used like helpCommand is now.
405
+ if (typeof helpCommand !== 'object') {
406
+ this.helpCommand(helpCommand, deprecatedDescription);
407
+ return this;
408
+ }
409
+
410
+ this._addImplicitHelpCommand = true;
411
+ this._helpCommand = helpCommand;
412
+ return this;
413
+ }
414
+
415
+ /**
416
+ * Lazy create help command.
417
+ *
418
+ * @return {(Command|null)}
419
+ * @package
398
420
  */
421
+ _getHelpCommand() {
422
+ const hasImplicitHelpCommand = this._addImplicitHelpCommand ??
423
+ (this.commands.length && !this._actionHandler && !this._findCommand('help'));
399
424
 
400
- _hasImplicitHelpCommand() {
401
- if (this._addImplicitHelpCommand === undefined) {
402
- return this.commands.length && !this._actionHandler && !this._findCommand('help');
425
+ if (hasImplicitHelpCommand) {
426
+ if (this._helpCommand === undefined) {
427
+ this.helpCommand(undefined, undefined); // use default name and description
428
+ }
429
+ return this._helpCommand;
403
430
  }
404
- return this._addImplicitHelpCommand;
431
+ return null;
405
432
  }
406
433
 
407
434
  /**
@@ -517,7 +544,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
517
544
  /**
518
545
  * Wrap parseArgs to catch 'commander.invalidArgument'.
519
546
  *
520
- * @param {Option | Argument} target
547
+ * @param {(Option | Argument)} target
521
548
  * @param {string} value
522
549
  * @param {*} previous
523
550
  * @param {string} invalidArgumentMessage
@@ -691,7 +718,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
691
718
  *
692
719
  * @param {string} flags
693
720
  * @param {string} [description]
694
- * @param {Function|*} [parseArg] - custom option processing function or default value
721
+ * @param {(Function|*)} [parseArg] - custom option processing function or default value
695
722
  * @param {*} [defaultValue]
696
723
  * @return {Command} `this` command for chaining
697
724
  */
@@ -708,7 +735,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
708
735
  *
709
736
  * @param {string} flags
710
737
  * @param {string} [description]
711
- * @param {Function|*} [parseArg] - custom option processing function or default value
738
+ * @param {(Function|*)} [parseArg] - custom option processing function or default value
712
739
  * @param {*} [defaultValue]
713
740
  * @return {Command} `this` command for chaining
714
741
  */
@@ -725,7 +752,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
725
752
  * program.combineFlagAndOptionalValue(true); // `-f80` is treated like `--flag=80`, this is the default behaviour
726
753
  * program.combineFlagAndOptionalValue(false) // `-fb` is treated like `-f -b`
727
754
  *
728
- * @param {Boolean} [combine=true] - if `true` or omitted, an optional value can be specified directly after the flag.
755
+ * @param {boolean} [combine=true] - if `true` or omitted, an optional value can be specified directly after the flag.
729
756
  */
730
757
  combineFlagAndOptionalValue(combine = true) {
731
758
  this._combineFlagAndOptionalValue = !!combine;
@@ -735,7 +762,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
735
762
  /**
736
763
  * Allow unknown options on the command line.
737
764
  *
738
- * @param {Boolean} [allowUnknown=true] - if `true` or omitted, no error will be thrown
765
+ * @param {boolean} [allowUnknown=true] - if `true` or omitted, no error will be thrown
739
766
  * for unknown options.
740
767
  */
741
768
  allowUnknownOption(allowUnknown = true) {
@@ -746,7 +773,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
746
773
  /**
747
774
  * Allow excess command-arguments on the command line. Pass false to make excess arguments an error.
748
775
  *
749
- * @param {Boolean} [allowExcess=true] - if `true` or omitted, no error will be thrown
776
+ * @param {boolean} [allowExcess=true] - if `true` or omitted, no error will be thrown
750
777
  * for excess arguments.
751
778
  */
752
779
  allowExcessArguments(allowExcess = true) {
@@ -759,7 +786,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
759
786
  * subcommands reuse the same option names, and also enables subcommands to turn on passThroughOptions.
760
787
  * The default behaviour is non-positional and global options may appear anywhere on the command line.
761
788
  *
762
- * @param {Boolean} [positional=true]
789
+ * @param {boolean} [positional=true]
763
790
  */
764
791
  enablePositionalOptions(positional = true) {
765
792
  this._enablePositionalOptions = !!positional;
@@ -772,7 +799,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
772
799
  * positional options to have been enabled on the program (parent commands).
773
800
  * The default behaviour is non-positional and options may appear before or after command-arguments.
774
801
  *
775
- * @param {Boolean} [passThrough=true]
802
+ * @param {boolean} [passThrough=true]
776
803
  * for unknown options.
777
804
  */
778
805
  passThroughOptions(passThrough = true) {
@@ -1155,7 +1182,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1155
1182
 
1156
1183
  // Fallback to parsing the help flag to invoke the help.
1157
1184
  return this._dispatchSubcommand(subcommandName, [], [
1158
- this._helpLongFlag || this._helpShortFlag
1185
+ this._getHelpOption()?.long ?? this._getHelpOption()?.short ?? '--help'
1159
1186
  ]);
1160
1187
  }
1161
1188
 
@@ -1229,9 +1256,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
1229
1256
  /**
1230
1257
  * Once we have a promise we chain, but call synchronously until then.
1231
1258
  *
1232
- * @param {Promise|undefined} promise
1259
+ * @param {(Promise|undefined)} promise
1233
1260
  * @param {Function} fn
1234
- * @return {Promise|undefined}
1261
+ * @return {(Promise|undefined)}
1235
1262
  * @private
1236
1263
  */
1237
1264
 
@@ -1247,9 +1274,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
1247
1274
 
1248
1275
  /**
1249
1276
  *
1250
- * @param {Promise|undefined} promise
1277
+ * @param {(Promise|undefined)} promise
1251
1278
  * @param {string} event
1252
- * @return {Promise|undefined}
1279
+ * @return {(Promise|undefined)}
1253
1280
  * @private
1254
1281
  */
1255
1282
 
@@ -1278,10 +1305,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
1278
1305
 
1279
1306
  /**
1280
1307
  *
1281
- * @param {Promise|undefined} promise
1308
+ * @param {(Promise|undefined)} promise
1282
1309
  * @param {Command} subCommand
1283
1310
  * @param {string} event
1284
- * @return {Promise|undefined}
1311
+ * @return {(Promise|undefined)}
1285
1312
  * @private
1286
1313
  */
1287
1314
 
@@ -1315,7 +1342,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1315
1342
  if (operands && this._findCommand(operands[0])) {
1316
1343
  return this._dispatchSubcommand(operands[0], operands.slice(1), unknown);
1317
1344
  }
1318
- if (this._hasImplicitHelpCommand() && operands[0] === this._helpCommandName) {
1345
+ if (this._getHelpCommand() && operands[0] === this._getHelpCommand().name()) {
1319
1346
  return this._dispatchHelpCommand(operands[1]);
1320
1347
  }
1321
1348
  if (this._defaultCommandName) {
@@ -1477,8 +1504,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
1477
1504
  * sub --unknown uuu op => [sub], [--unknown uuu op]
1478
1505
  * sub -- --unknown uuu op => [sub --unknown uuu op], []
1479
1506
  *
1480
- * @param {String[]} argv
1481
- * @return {{operands: String[], unknown: String[]}}
1507
+ * @param {string[]} argv
1508
+ * @return {{operands: string[], unknown: string[]}}
1482
1509
  */
1483
1510
 
1484
1511
  parseOptions(argv) {
@@ -1572,7 +1599,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1572
1599
  operands.push(arg);
1573
1600
  if (args.length > 0) unknown.push(...args);
1574
1601
  break;
1575
- } else if (arg === this._helpCommandName && this._hasImplicitHelpCommand()) {
1602
+ } else if (this._getHelpCommand() && arg === this._getHelpCommand().name()) {
1576
1603
  operands.push(arg);
1577
1604
  if (args.length > 0) operands.push(...args);
1578
1605
  break;
@@ -1857,7 +1884,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1857
1884
  * @param {string} [str]
1858
1885
  * @param {string} [flags]
1859
1886
  * @param {string} [description]
1860
- * @return {this | string | undefined} `this` command for chaining, or version string if no arguments
1887
+ * @return {(this | string | undefined)} `this` command for chaining, or version string if no arguments
1861
1888
  */
1862
1889
 
1863
1890
  version(str, flags, description) {
@@ -1881,7 +1908,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1881
1908
  *
1882
1909
  * @param {string} [str]
1883
1910
  * @param {Object} [argsDescription]
1884
- * @return {string|Command}
1911
+ * @return {(string|Command)}
1885
1912
  */
1886
1913
  description(str, argsDescription) {
1887
1914
  if (str === undefined && argsDescription === undefined) return this._description;
@@ -1896,7 +1923,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1896
1923
  * Set the summary. Used when listed as subcommand of parent.
1897
1924
  *
1898
1925
  * @param {string} [str]
1899
- * @return {string|Command}
1926
+ * @return {(string|Command)}
1900
1927
  */
1901
1928
  summary(str) {
1902
1929
  if (str === undefined) return this._summary;
@@ -1910,7 +1937,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1910
1937
  * You may call more than once to add multiple aliases. Only the first alias is shown in the auto-generated help.
1911
1938
  *
1912
1939
  * @param {string} [alias]
1913
- * @return {string|Command}
1940
+ * @return {(string|Command)}
1914
1941
  */
1915
1942
 
1916
1943
  alias(alias) {
@@ -1941,7 +1968,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1941
1968
  * Only the first alias is shown in the auto-generated help.
1942
1969
  *
1943
1970
  * @param {string[]} [aliases]
1944
- * @return {string[]|Command}
1971
+ * @return {(string[]|Command)}
1945
1972
  */
1946
1973
 
1947
1974
  aliases(aliases) {
@@ -1956,7 +1983,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1956
1983
  * Set / get the command usage `str`.
1957
1984
  *
1958
1985
  * @param {string} [str]
1959
- * @return {String|Command}
1986
+ * @return {(string|Command)}
1960
1987
  */
1961
1988
 
1962
1989
  usage(str) {
@@ -1967,7 +1994,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1967
1994
  return humanReadableArgName(arg);
1968
1995
  });
1969
1996
  return [].concat(
1970
- (this.options.length || this._hasHelpOption ? '[options]' : []),
1997
+ (this.options.length || (this._helpOption !== null) ? '[options]' : []),
1971
1998
  (this.commands.length ? '[command]' : []),
1972
1999
  (this.registeredArguments.length ? args : [])
1973
2000
  ).join(' ');
@@ -1981,7 +2008,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1981
2008
  * Get or set the name of the command.
1982
2009
  *
1983
2010
  * @param {string} [str]
1984
- * @return {string|Command}
2011
+ * @return {(string|Command)}
1985
2012
  */
1986
2013
 
1987
2014
  name(str) {
@@ -2018,7 +2045,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2018
2045
  * program.executableDir('subcommands');
2019
2046
  *
2020
2047
  * @param {string} [path]
2021
- * @return {string|null|Command}
2048
+ * @return {(string|null|Command)}
2022
2049
  */
2023
2050
 
2024
2051
  executableDir(path) {
@@ -2088,35 +2115,69 @@ Expecting one of '${allowedValues.join("', '")}'`);
2088
2115
  }
2089
2116
  context.write(helpInformation);
2090
2117
 
2091
- if (this._helpLongFlag) {
2092
- this.emit(this._helpLongFlag); // deprecated
2118
+ if (this._getHelpOption()?.long) {
2119
+ this.emit(this._getHelpOption().long); // deprecated
2093
2120
  }
2094
2121
  this.emit('afterHelp', context);
2095
2122
  this._getCommandAndAncestors().forEach(command => command.emit('afterAllHelp', context));
2096
2123
  }
2097
2124
 
2098
2125
  /**
2099
- * You can pass in flags and a description to override the help
2100
- * flags and help description for your command. Pass in false to
2101
- * disable the built-in help option.
2126
+ * You can pass in flags and a description to customise the built-in help option.
2127
+ * Pass in false to disable the built-in help option.
2128
+ *
2129
+ * @example
2130
+ * program.helpOption('-?, --help' 'show help'); // customise
2131
+ * program.helpOption(false); // disable
2102
2132
  *
2103
- * @param {string | boolean} [flags]
2133
+ * @param {(string | boolean)} flags
2104
2134
  * @param {string} [description]
2105
2135
  * @return {Command} `this` command for chaining
2106
2136
  */
2107
2137
 
2108
2138
  helpOption(flags, description) {
2139
+ // Support disabling built-in help option.
2109
2140
  if (typeof flags === 'boolean') {
2110
- this._hasHelpOption = flags;
2141
+ if (flags) {
2142
+ this._helpOption = this._helpOption ?? undefined; // preserve existing option
2143
+ } else {
2144
+ this._helpOption = null; // disable
2145
+ }
2111
2146
  return this;
2112
2147
  }
2113
- this._helpFlags = flags || this._helpFlags;
2114
- this._helpDescription = description || this._helpDescription;
2115
2148
 
2116
- const helpFlags = splitOptionFlags(this._helpFlags);
2117
- this._helpShortFlag = helpFlags.shortFlag;
2118
- this._helpLongFlag = helpFlags.longFlag;
2149
+ // Customise flags and description.
2150
+ flags = flags ?? '-h, --help';
2151
+ description = description ?? 'display help for command';
2152
+ this._helpOption = this.createOption(flags, description);
2153
+
2154
+ return this;
2155
+ }
2156
+
2157
+ /**
2158
+ * Lazy create help option.
2159
+ * Returns null if has been disabled with .helpOption(false).
2160
+ *
2161
+ * @returns {(Option | null)} the help option
2162
+ * @package internal use only
2163
+ */
2164
+ _getHelpOption() {
2165
+ // Lazy create help option on demand.
2166
+ if (this._helpOption === undefined) {
2167
+ this.helpOption(undefined, undefined);
2168
+ }
2169
+ return this._helpOption;
2170
+ }
2119
2171
 
2172
+ /**
2173
+ * Supply your own option to use for the built-in help option.
2174
+ * This is an alternative to using helpOption() to customise the flags and description etc.
2175
+ *
2176
+ * @param {Option} option
2177
+ * @return {Command} `this` command for chaining
2178
+ */
2179
+ addHelpOption(option) {
2180
+ this._helpOption = option;
2120
2181
  return this;
2121
2182
  }
2122
2183
 
@@ -2145,7 +2206,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2145
2206
  * and 'beforeAll' or 'afterAll' to affect this command and all its subcommands.
2146
2207
  *
2147
2208
  * @param {string} position - before or after built-in help
2148
- * @param {string | Function} text - string to add, or a function returning a string
2209
+ * @param {(string | Function)} text - string to add, or a function returning a string
2149
2210
  * @return {Command} `this` command for chaining
2150
2211
  */
2151
2212
  addHelpText(position, text) {
@@ -2178,8 +2239,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
2178
2239
  */
2179
2240
 
2180
2241
  _outputHelpIfRequested(args) {
2181
- const helpOption = this._hasHelpOption && args.find(arg => arg === this._helpLongFlag || arg === this._helpShortFlag);
2182
- if (helpOption) {
2242
+ const helpOption = this._getHelpOption();
2243
+ const helpRequested = helpOption && args.find(arg => helpOption.is(arg));
2244
+ if (helpRequested) {
2183
2245
  this.outputHelp();
2184
2246
  // (Do not have all displayed text available so only passing placeholder.)
2185
2247
  this._exit(0, 'commander.helpDisplayed', '(outputHelp)');
package/lib/help.js CHANGED
@@ -26,13 +26,8 @@ class Help {
26
26
 
27
27
  visibleCommands(cmd) {
28
28
  const visibleCommands = cmd.commands.filter(cmd => !cmd._hidden);
29
- if (cmd._hasImplicitHelpCommand()) {
30
- // Create a command matching the implicit help command.
31
- const [, helpName, helpArgs] = cmd._helpCommandnameAndArgs.match(/([^ ]+) *(.*)/);
32
- const helpCommand = cmd.createCommand(helpName)
33
- .helpOption(false);
34
- helpCommand.description(cmd._helpCommandDescription);
35
- if (helpArgs) helpCommand.arguments(helpArgs);
29
+ const helpCommand = cmd._getHelpCommand();
30
+ if (helpCommand && !helpCommand._hidden) {
36
31
  visibleCommands.push(helpCommand);
37
32
  }
38
33
  if (this.sortSubcommands) {
@@ -68,19 +63,19 @@ class Help {
68
63
 
69
64
  visibleOptions(cmd) {
70
65
  const visibleOptions = cmd.options.filter((option) => !option.hidden);
71
- // Implicit help
72
- const showShortHelpFlag = cmd._hasHelpOption && cmd._helpShortFlag && !cmd._findOption(cmd._helpShortFlag);
73
- const showLongHelpFlag = cmd._hasHelpOption && !cmd._findOption(cmd._helpLongFlag);
74
- if (showShortHelpFlag || showLongHelpFlag) {
75
- let helpOption;
76
- if (!showShortHelpFlag) {
77
- helpOption = cmd.createOption(cmd._helpLongFlag, cmd._helpDescription);
78
- } else if (!showLongHelpFlag) {
79
- helpOption = cmd.createOption(cmd._helpShortFlag, cmd._helpDescription);
80
- } else {
81
- helpOption = cmd.createOption(cmd._helpFlags, cmd._helpDescription);
66
+ // Built-in help option.
67
+ const helpOption = cmd._getHelpOption();
68
+ if (helpOption && !helpOption.hidden) {
69
+ // Automatically hide conflicting flags. Bit dubious but a historical behaviour that is convenient for single-command programs.
70
+ const removeShort = helpOption.short && cmd._findOption(helpOption.short);
71
+ const removeLong = helpOption.long && cmd._findOption(helpOption.long);
72
+ if (!removeShort && !removeLong) {
73
+ visibleOptions.push(helpOption); // no changes needed
74
+ } else if (helpOption.long && !removeLong) {
75
+ visibleOptions.push(cmd.createOption(helpOption.long, helpOption.description));
76
+ } else if (helpOption.short && !removeShort) {
77
+ visibleOptions.push(cmd.createOption(helpOption.short, helpOption.description));
82
78
  }
83
- visibleOptions.push(helpOption);
84
79
  }
85
80
  if (this.sortOptions) {
86
81
  visibleOptions.sort(this.compareOptions);
package/lib/option.js CHANGED
@@ -74,7 +74,7 @@ class Option {
74
74
  * new Option('--rgb').conflicts('cmyk');
75
75
  * new Option('--js').conflicts(['ts', 'jsx']);
76
76
  *
77
- * @param {string | string[]} names
77
+ * @param {(string | string[])} names
78
78
  * @return {Option}
79
79
  */
80
80
 
@@ -324,5 +324,4 @@ function splitOptionFlags(flags) {
324
324
  }
325
325
 
326
326
  exports.Option = Option;
327
- exports.splitOptionFlags = splitOptionFlags;
328
327
  exports.DualOptions = DualOptions;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commander",
3
- "version": "12.0.0-0",
3
+ "version": "12.0.0-1",
4
4
  "description": "the complete solution for node.js command-line programs",
5
5
  "keywords": [
6
6
  "commander",
@@ -16,7 +16,7 @@
16
16
  "license": "MIT",
17
17
  "repository": {
18
18
  "type": "git",
19
- "url": "https://github.com/tj/commander.js.git"
19
+ "url": "git+https://github.com/tj/commander.js.git"
20
20
  },
21
21
  "scripts": {
22
22
  "lint": "npm run lint:javascript && npm run lint:typescript",
@@ -62,7 +62,7 @@
62
62
  "@typescript-eslint/parser": "^6.7.5",
63
63
  "eslint": "^8.30.0",
64
64
  "eslint-config-standard": "^17.0.0",
65
- "eslint-config-standard-with-typescript": "^39.1.1",
65
+ "eslint-config-standard-with-typescript": "^40.0.0",
66
66
  "eslint-plugin-import": "^2.26.0",
67
67
  "eslint-plugin-jest": "^27.1.7",
68
68
  "eslint-plugin-n": "^16.2.0",
@@ -419,18 +419,27 @@ export class Command {
419
419
  arguments(names: string): this;
420
420
 
421
421
  /**
422
- * Override default decision whether to add implicit help command.
422
+ * Customise or override default help command. By default a help command is automatically added if your command has subcommands.
423
423
  *
424
424
  * @example
425
+ * ```ts
426
+ * program.helpCommand('help [cmd]');
427
+ * program.helpCommand('help [cmd]', 'show help');
428
+ * program.helpCommand(false); // suppress default help command
429
+ * program.helpCommand(true); // add help command even if no subcommands
425
430
  * ```
426
- * addHelpCommand() // force on
427
- * addHelpCommand(false); // force off
428
- * addHelpCommand('help [cmd]', 'display help for [cmd]'); // force on with custom details
429
- * ```
430
- *
431
- * @returns `this` command for chaining
432
431
  */
433
- addHelpCommand(enableOrNameAndArgs?: string | boolean, description?: string): this;
432
+ helpCommand(nameAndArgs: string, description?: string): this;
433
+ helpCommand(enable: boolean): this;
434
+
435
+ /**
436
+ * Add prepared custom help command.
437
+ */
438
+ addHelpCommand(cmd: Command): this;
439
+ /** @deprecated since v12, instead use helpCommand */
440
+ addHelpCommand(nameAndArgs: string, description?: string): this;
441
+ /** @deprecated since v12, instead use helpCommand */
442
+ addHelpCommand(enable?: boolean): this;
434
443
 
435
444
  /**
436
445
  * Add hook for life cycle event.
@@ -838,6 +847,12 @@ export class Command {
838
847
  */
839
848
  helpOption(flags?: string | boolean, description?: string): this;
840
849
 
850
+ /**
851
+ * Supply your own option to use for the built-in help option.
852
+ * This is an alternative to using helpOption() to customise the flags and description etc.
853
+ */
854
+ addHelpOption(option: Option): this;
855
+
841
856
  /**
842
857
  * Output help information and exit.
843
858
  *