commander 8.0.0-2 → 8.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Readme.md CHANGED
@@ -93,13 +93,13 @@ import { Command } from 'commander';
93
93
  const program = new Command();
94
94
  ```
95
95
 
96
-
97
96
  ## Options
98
97
 
99
98
  Options are defined with the `.option()` method, also serving as documentation for the options. Each option can have a short flag (single character) and a long name, separated by a comma or space or vertical bar ('|').
100
99
 
101
100
  The parsed options can be accessed by calling `.opts()` on a `Command` object, and are passed to the action handler.
102
- You can also use `.getOptionValue()` and `.setOptionValue()` to work with a single option value.
101
+ (You can also use `.getOptionValue()` and `.setOptionValue()` to work with a single option value,
102
+ and `.getOptionValueSource()` and `.setOptionValueWithSource()` when it matters where the option value came from.)
103
103
 
104
104
  Multi-word options such as "--template-engine" are camel-cased, becoming `program.opts().templateEngine` etc.
105
105
 
@@ -113,7 +113,7 @@ By default options on the command line are not positional, and can be specified
113
113
  ### Common option types, boolean and value
114
114
 
115
115
  The two most used option types are a boolean option, and an option which takes its value
116
- from the following argument (declared with angle brackets like `--expect <value>`). Both are `undefined` unless specified on command line.
116
+ from the following argument (declared with angle brackets like `--expect <value>`). Both are `undefined` unless specified on command line.
117
117
 
118
118
  Example file: [options-common.js](./examples/options-common.js)
119
119
 
@@ -308,13 +308,14 @@ program.version('0.0.1', '-v, --vers', 'output the current version');
308
308
  You can add most options using the `.option()` method, but there are some additional features available
309
309
  by constructing an `Option` explicitly for less common cases.
310
310
 
311
- Example file: [options-extra.js](./examples/options-extra.js)
311
+ Example files: [options-extra.js](./examples/options-extra.js), [options-env.js](./examples/options-env.js)
312
312
 
313
313
  ```js
314
314
  program
315
315
  .addOption(new Option('-s, --secret').hideHelp())
316
316
  .addOption(new Option('-t, --timeout <delay>', 'timeout in seconds').default(60, 'one minute'))
317
- .addOption(new Option('-d, --drink <size>', 'drink size').choices(['small', 'medium', 'large']));
317
+ .addOption(new Option('-d, --drink <size>', 'drink size').choices(['small', 'medium', 'large']))
318
+ .addOption(new Option('-p, --port <number>', 'port number').env('PORT'));
318
319
  ```
319
320
 
320
321
  ```bash
@@ -324,10 +325,14 @@ Usage: help [options]
324
325
  Options:
325
326
  -t, --timeout <delay> timeout in seconds (default: one minute)
326
327
  -d, --drink <size> drink cup size (choices: "small", "medium", "large")
328
+ -p, --port <number> port number (env: PORT)
327
329
  -h, --help display help for command
328
330
 
329
331
  $ extra --drink huge
330
332
  error: option '-d, --drink <size>' argument 'huge' is invalid. Allowed choices are small, medium, large.
333
+
334
+ $ PORT=80 extra
335
+ Options: { timeout: 60, port: '80' }
331
336
  ```
332
337
 
333
338
  ### Custom option processing
@@ -426,7 +431,7 @@ program
426
431
  .addCommand(build.makeBuildCommand());
427
432
  ```
428
433
 
429
- Configuration options can be passed with the call to `.command()` and `.addCommand()`. Specifying `hidden: true` will
434
+ Configuration options can be passed with the call to `.command()` and `.addCommand()`. Specifying `hidden: true` will
430
435
  remove the command from the generated help output. Specifying `isDefault: true` will run the subcommand if no other
431
436
  subcommand is specified ([example](./examples/defaultCommand.js)).
432
437
 
@@ -436,7 +441,7 @@ For subcommands, you can specify the argument syntax in the call to `.command()`
436
441
  is the only method usable for subcommands implemented using a stand-alone executable, but for other subcommands
437
442
  you can instead use the following method.
438
443
 
439
- To configure a command, you can use `.argument()` to specify each expected command-argument.
444
+ To configure a command, you can use `.argument()` to specify each expected command-argument.
440
445
  You supply the argument name and an optional description. The argument may be `<required>` or `[optional]`.
441
446
  You can specify a default value for an optional command-argument.
442
447
 
@@ -513,7 +518,7 @@ program
513
518
  ### Action handler
514
519
 
515
520
  The action handler gets passed a parameter for each command-argument you declared, and two additional parameters
516
- which are the parsed options and the command object itself.
521
+ which are the parsed options and the command object itself.
517
522
 
518
523
  Example file: [thank.js](./examples/thank.js)
519
524
 
@@ -630,7 +635,7 @@ shell spawn --help
630
635
 
631
636
  ### Custom help
632
637
 
633
- You can add extra text to be displayed along with the built-in help.
638
+ You can add extra text to be displayed along with the built-in help.
634
639
 
635
640
  Example file: [custom-help](./examples/custom-help)
636
641
 
@@ -664,7 +669,7 @@ The positions in order displayed are:
664
669
  - `after`: display extra information after built-in help
665
670
  - `afterAll`: add to the program for a global footer (epilog)
666
671
 
667
- The positions "beforeAll" and "afterAll" apply to the command and all its subcommands.
672
+ The positions "beforeAll" and "afterAll" apply to the command and all its subcommands.
668
673
 
669
674
  The second parameter can be a string, or a function returning a string. The function is passed a context object for your convenience. The properties are:
670
675
 
@@ -673,7 +678,7 @@ The second parameter can be a string, or a function returning a string. The func
673
678
 
674
679
  ### Display help after errors
675
680
 
676
- The default behaviour for usage errors is to just display a short error message.
681
+ The default behaviour for usage errors is to just display a short error message.
677
682
  You can change the behaviour to show the full help or a custom help message after an error.
678
683
 
679
684
  ```js
@@ -688,6 +693,18 @@ error: unknown option '--unknown'
688
693
  (add --help for additional information)
689
694
  ```
690
695
 
696
+ You can also show suggestions after an error for an unknown command or option.
697
+
698
+ ```js
699
+ program.showSuggestionAfterError();
700
+ ```
701
+
702
+ ```sh
703
+ $ pizza --hepl
704
+ error: unknown option '--hepl'
705
+ (Did you mean --help?)
706
+ ```
707
+
691
708
  ### Display help from code
692
709
 
693
710
  `.help()`: display help information and exit immediately. You can optionally pass `{ error: true }` to display on stderr and exit with an error status.
@@ -747,7 +764,7 @@ There are methods getting the visible lists of arguments, options, and subcomman
747
764
 
748
765
  Example file: [configure-help.js](./examples/configure-help.js)
749
766
 
750
- ```
767
+ ```js
751
768
  program.configureHelp({
752
769
  sortSubcommands: true,
753
770
  subcommandTerm: (cmd) => cmd.name() // Just show the name, instead of short usage.
@@ -762,13 +779,6 @@ You can execute custom actions by listening to command and option events.
762
779
  program.on('option:verbose', function () {
763
780
  process.env.VERBOSE = this.opts().verbose;
764
781
  });
765
-
766
- program.on('command:*', function (operands) {
767
- console.error(`error: unknown command '${operands[0]}'`);
768
- const availableCommands = program.commands.map(cmd => cmd.name());
769
- mySuggestBestMatch(operands[0], availableCommands);
770
- process.exitCode = 1;
771
- });
772
782
  ```
773
783
 
774
784
  ## Bits and pieces
@@ -809,7 +819,7 @@ program subcommand -b
809
819
 
810
820
  By default options are recognised before and after command-arguments. To only process options that come
811
821
  before the command-arguments, use `.passThroughOptions()`. This lets you pass the arguments and following options through to another program
812
- without needing to use `--` to end the option processing.
822
+ without needing to use `--` to end the option processing.
813
823
  To use pass through options in a subcommand, the program needs to enable positional options.
814
824
 
815
825
  Example file: [pass-through-options.js](./examples/pass-through-options.js)
@@ -826,7 +836,7 @@ By default the option processing shows an error for an unknown option. To have a
826
836
  By default the argument processing does not display an error for more command-arguments than expected.
827
837
  To display an error for excess arguments, use`.allowExcessArguments(false)`.
828
838
 
829
- ### Legacy options as properties
839
+ ### Legacy options as properties
830
840
 
831
841
  Before Commander 7, the option values were stored as properties on the command.
832
842
  This was convenient to code but the downside was possible clashes with
@@ -903,7 +913,6 @@ You can modify this behaviour for custom applications. In addition, you can modi
903
913
 
904
914
  Example file: [configure-output.js](./examples/configure-output.js)
905
915
 
906
-
907
916
  ```js
908
917
  function errorColor(str) {
909
918
  // Add ANSI escape codes to display text in red.
@@ -986,7 +995,7 @@ Examples:
986
995
  $ deploy exec sequential
987
996
  $ deploy exec async`
988
997
  );
989
-
998
+
990
999
  program.parse(process.argv);
991
1000
  ```
992
1001
 
package/lib/argument.js CHANGED
@@ -109,6 +109,22 @@ class Argument {
109
109
  };
110
110
  return this;
111
111
  };
112
+
113
+ /**
114
+ * Make option-argument required.
115
+ */
116
+ argRequired() {
117
+ this.required = true;
118
+ return this;
119
+ }
120
+
121
+ /**
122
+ * Make option-argument optional.
123
+ */
124
+ argOptional() {
125
+ this.required = false;
126
+ return this;
127
+ }
112
128
  }
113
129
 
114
130
  /**
package/lib/command.js CHANGED
@@ -7,6 +7,7 @@ const { Argument, humanReadableArgName } = require('./argument.js');
7
7
  const { CommanderError } = require('./error.js');
8
8
  const { Help } = require('./help.js');
9
9
  const { Option, splitOptionFlags } = require('./option.js');
10
+ const { suggestSimilar } = require('./suggestSimilar');
10
11
 
11
12
  // @ts-check
12
13
 
@@ -35,6 +36,7 @@ class Command extends EventEmitter {
35
36
  this._scriptPath = null;
36
37
  this._name = name || '';
37
38
  this._optionValues = {};
39
+ this._optionValueSources = {}; // default < config < env < cli
38
40
  this._storeOptionsAsProperties = false;
39
41
  this._actionHandler = null;
40
42
  this._executableHandler = false;
@@ -50,6 +52,7 @@ class Command extends EventEmitter {
50
52
  this._lifeCycleHooks = {}; // a hash of arrays
51
53
  /** @type {boolean | string} */
52
54
  this._showHelpAfterError = false;
55
+ this._showSuggestionAfterError = false;
53
56
 
54
57
  // see .configureOutput() for docs
55
58
  this._outputConfiguration = {
@@ -74,24 +77,53 @@ class Command extends EventEmitter {
74
77
  }
75
78
 
76
79
  /**
77
- * Define a command.
80
+ * Copy settings that are useful to have in common across root command and subcommands.
78
81
  *
79
- * There are two styles of command: pay attention to where to put the description.
82
+ * (Used internally when adding a command using `.command()` so subcommands inherit parent settings.)
80
83
  *
81
- * Examples:
84
+ * @param {Command} sourceCommand
85
+ * @return {Command} returns `this` for executable command
86
+ */
87
+ copyInheritedSettings(sourceCommand) {
88
+ this._outputConfiguration = sourceCommand._outputConfiguration;
89
+ this._hasHelpOption = sourceCommand._hasHelpOption;
90
+ this._helpFlags = sourceCommand._helpFlags;
91
+ this._helpDescription = sourceCommand._helpDescription;
92
+ this._helpShortFlag = sourceCommand._helpShortFlag;
93
+ this._helpLongFlag = sourceCommand._helpLongFlag;
94
+ this._helpCommandName = sourceCommand._helpCommandName;
95
+ this._helpCommandnameAndArgs = sourceCommand._helpCommandnameAndArgs;
96
+ this._helpCommandDescription = sourceCommand._helpCommandDescription;
97
+ this._helpConfiguration = sourceCommand._helpConfiguration;
98
+ this._exitCallback = sourceCommand._exitCallback;
99
+ this._storeOptionsAsProperties = sourceCommand._storeOptionsAsProperties;
100
+ this._combineFlagAndOptionalValue = sourceCommand._combineFlagAndOptionalValue;
101
+ this._allowExcessArguments = sourceCommand._allowExcessArguments;
102
+ this._enablePositionalOptions = sourceCommand._enablePositionalOptions;
103
+ this._showHelpAfterError = sourceCommand._showHelpAfterError;
104
+ this._showSuggestionAfterError = sourceCommand._showSuggestionAfterError;
105
+
106
+ return this;
107
+ }
108
+
109
+ /**
110
+ * Define a command.
82
111
  *
83
- * // Command implemented using action handler (description is supplied separately to `.command`)
84
- * program
85
- * .command('clone <source> [destination]')
86
- * .description('clone a repository into a newly created directory')
87
- * .action((source, destination) => {
88
- * console.log('clone command called');
89
- * });
112
+ * There are two styles of command: pay attention to where to put the description.
90
113
  *
91
- * // Command implemented using separate executable file (description is second parameter to `.command`)
92
- * program
93
- * .command('start <service>', 'start named service')
94
- * .command('stop [service]', 'stop named service, or all if no name supplied');
114
+ * @example
115
+ * // Command implemented using action handler (description is supplied separately to `.command`)
116
+ * program
117
+ * .command('clone <source> [destination]')
118
+ * .description('clone a repository into a newly created directory')
119
+ * .action((source, destination) => {
120
+ * console.log('clone command called');
121
+ * });
122
+ *
123
+ * // Command implemented using separate executable file (description is second parameter to `.command`)
124
+ * program
125
+ * .command('start <service>', 'start named service')
126
+ * .command('stop [service]', 'stop named service, or all if no name supplied');
95
127
  *
96
128
  * @param {string} nameAndArgs - command name and arguments, args are `<required>` or `[optional]` and last may also be `variadic...`
97
129
  * @param {Object|string} [actionOptsOrExecDesc] - configuration options (for action), or description (for executable)
@@ -108,37 +140,19 @@ class Command extends EventEmitter {
108
140
  }
109
141
  opts = opts || {};
110
142
  const [, name, args] = nameAndArgs.match(/([^ ]+) *(.*)/);
111
- const cmd = this.createCommand(name);
112
143
 
144
+ const cmd = this.createCommand(name);
113
145
  if (desc) {
114
146
  cmd.description(desc);
115
147
  cmd._executableHandler = true;
116
148
  }
117
149
  if (opts.isDefault) this._defaultCommandName = cmd._name;
118
-
119
- cmd._outputConfiguration = this._outputConfiguration;
120
-
121
150
  cmd._hidden = !!(opts.noHelp || opts.hidden); // noHelp is deprecated old name for hidden
122
- cmd._hasHelpOption = this._hasHelpOption;
123
- cmd._helpFlags = this._helpFlags;
124
- cmd._helpDescription = this._helpDescription;
125
- cmd._helpShortFlag = this._helpShortFlag;
126
- cmd._helpLongFlag = this._helpLongFlag;
127
- cmd._helpCommandName = this._helpCommandName;
128
- cmd._helpCommandnameAndArgs = this._helpCommandnameAndArgs;
129
- cmd._helpCommandDescription = this._helpCommandDescription;
130
- cmd._helpConfiguration = this._helpConfiguration;
131
- cmd._exitCallback = this._exitCallback;
132
- cmd._storeOptionsAsProperties = this._storeOptionsAsProperties;
133
- cmd._combineFlagAndOptionalValue = this._combineFlagAndOptionalValue;
134
- cmd._allowExcessArguments = this._allowExcessArguments;
135
- cmd._enablePositionalOptions = this._enablePositionalOptions;
136
- cmd._showHelpAfterError = this._showHelpAfterError;
137
-
138
151
  cmd._executableFile = opts.executableFile || null; // Custom name for executable file, set missing to null to match constructor
139
152
  if (args) cmd.arguments(args);
140
153
  this.commands.push(cmd);
141
154
  cmd.parent = this;
155
+ cmd.copyInheritedSettings(this);
142
156
 
143
157
  if (desc) return this;
144
158
  return cmd;
@@ -190,14 +204,14 @@ class Command extends EventEmitter {
190
204
  *
191
205
  * The configuration properties are all functions:
192
206
  *
193
- * // functions to change where being written, stdout and stderr
194
- * writeOut(str)
195
- * writeErr(str)
196
- * // matching functions to specify width for wrapping help
197
- * getOutHelpWidth()
198
- * getErrHelpWidth()
199
- * // functions based on what is being written out
200
- * outputError(str, write) // used for displaying errors, and not used for displaying help
207
+ * // functions to change where being written, stdout and stderr
208
+ * writeOut(str)
209
+ * writeErr(str)
210
+ * // matching functions to specify width for wrapping help
211
+ * getOutHelpWidth()
212
+ * getErrHelpWidth()
213
+ * // functions based on what is being written out
214
+ * outputError(str, write) // used for displaying errors, and not used for displaying help
201
215
  *
202
216
  * @param {Object} [configuration] - configuration options
203
217
  * @return {Command|Object} `this` command for chaining, or stored configuration
@@ -222,6 +236,17 @@ class Command extends EventEmitter {
222
236
  return this;
223
237
  }
224
238
 
239
+ /**
240
+ * Display suggestion of similar commands for unknown commands, or options for unknown options.
241
+ *
242
+ * @param {boolean} [displaySuggestion]
243
+ * @return {Command} `this` command for chaining
244
+ */
245
+ showSuggestionAfterError(displaySuggestion = true) {
246
+ this._showSuggestionAfterError = !!displaySuggestion;
247
+ return this;
248
+ }
249
+
225
250
  /**
226
251
  * Add a prepared subcommand.
227
252
  *
@@ -278,9 +303,8 @@ class Command extends EventEmitter {
278
303
  * indicate this with <> around the name. Put [] around the name for an optional argument.
279
304
  *
280
305
  * @example
281
- *
282
- * program.argument('<input-file>');
283
- * program.argument('[output-file]');
306
+ * program.argument('<input-file>');
307
+ * program.argument('[output-file]');
284
308
  *
285
309
  * @param {string} name
286
310
  * @param {string} [description]
@@ -305,8 +329,7 @@ class Command extends EventEmitter {
305
329
  * See also .argument().
306
330
  *
307
331
  * @example
308
- *
309
- * program.arguments('<cmd> [env]');
332
+ * program.arguments('<cmd> [env]');
310
333
  *
311
334
  * @param {string} names
312
335
  * @return {Command} `this` command for chaining
@@ -438,14 +461,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
438
461
  /**
439
462
  * Register callback `fn` for the command.
440
463
  *
441
- * Examples:
442
- *
443
- * program
444
- * .command('help')
445
- * .description('display verbose help')
446
- * .action(function() {
447
- * // output help here
448
- * });
464
+ * @example
465
+ * program
466
+ * .command('serve')
467
+ * .description('start service')
468
+ * .action(function() {
469
+ * // do work here
470
+ * });
449
471
  *
450
472
  * @param {Function} fn
451
473
  * @return {Command} `this` command for chaining
@@ -505,16 +527,16 @@ Expecting one of '${allowedValues.join("', '")}'`);
505
527
  }
506
528
  // preassign only if we have a default
507
529
  if (defaultValue !== undefined) {
508
- this.setOptionValue(name, defaultValue);
530
+ this.setOptionValueWithSource(name, defaultValue, 'default');
509
531
  }
510
532
  }
511
533
 
512
534
  // register the option
513
535
  this.options.push(option);
514
536
 
515
- // when it's passed assign the value
516
- // and conditionally invoke the callback
517
- this.on('option:' + oname, (val) => {
537
+ // handler for cli and env supplied values
538
+ const handleOptionValue = (val, invalidValueMessage, valueSource) => {
539
+ // Note: using closure to access lots of lexical scoped variables.
518
540
  const oldValue = this.getOptionValue(name);
519
541
 
520
542
  // custom processing
@@ -523,7 +545,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
523
545
  val = option.parseArg(val, oldValue === undefined ? defaultValue : oldValue);
524
546
  } catch (err) {
525
547
  if (err.code === 'commander.invalidArgument') {
526
- const message = `error: option '${option.flags}' argument '${val}' is invalid. ${err.message}`;
548
+ const message = `${invalidValueMessage} ${err.message}`;
527
549
  this._displayError(err.exitCode, err.code, message);
528
550
  }
529
551
  throw err;
@@ -536,18 +558,28 @@ Expecting one of '${allowedValues.join("', '")}'`);
536
558
  if (typeof oldValue === 'boolean' || typeof oldValue === 'undefined') {
537
559
  // if no value, negate false, and we have a default, then use it!
538
560
  if (val == null) {
539
- this.setOptionValue(name, option.negate
540
- ? false
541
- : defaultValue || true);
561
+ this.setOptionValueWithSource(name, option.negate ? false : defaultValue || true, valueSource);
542
562
  } else {
543
- this.setOptionValue(name, val);
563
+ this.setOptionValueWithSource(name, val, valueSource);
544
564
  }
545
565
  } else if (val !== null) {
546
566
  // reassign
547
- this.setOptionValue(name, option.negate ? false : val);
567
+ this.setOptionValueWithSource(name, option.negate ? false : val, valueSource);
548
568
  }
569
+ };
570
+
571
+ this.on('option:' + oname, (val) => {
572
+ const invalidValueMessage = `error: option '${option.flags}' argument '${val}' is invalid.`;
573
+ handleOptionValue(val, invalidValueMessage, 'cli');
549
574
  });
550
575
 
576
+ if (option.envVar) {
577
+ this.on('optionEnv:' + oname, (val) => {
578
+ const invalidValueMessage = `error: option '${option.flags}' value '${val}' from env '${option.envVar}' is invalid.`;
579
+ handleOptionValue(val, invalidValueMessage, 'env');
580
+ });
581
+ }
582
+
551
583
  return this;
552
584
  }
553
585
 
@@ -584,41 +616,40 @@ Expecting one of '${allowedValues.join("', '")}'`);
584
616
  * separated by comma, a pipe or space. The following are all valid
585
617
  * all will output this way when `--help` is used.
586
618
  *
587
- * "-p, --pepper"
588
- * "-p|--pepper"
589
- * "-p --pepper"
619
+ * "-p, --pepper"
620
+ * "-p|--pepper"
621
+ * "-p --pepper"
590
622
  *
591
- * Examples:
592
- *
593
- * // simple boolean defaulting to undefined
594
- * program.option('-p, --pepper', 'add pepper');
623
+ * @example
624
+ * // simple boolean defaulting to undefined
625
+ * program.option('-p, --pepper', 'add pepper');
595
626
  *
596
- * program.pepper
597
- * // => undefined
627
+ * program.pepper
628
+ * // => undefined
598
629
  *
599
- * --pepper
600
- * program.pepper
601
- * // => true
630
+ * --pepper
631
+ * program.pepper
632
+ * // => true
602
633
  *
603
- * // simple boolean defaulting to true (unless non-negated option is also defined)
604
- * program.option('-C, --no-cheese', 'remove cheese');
634
+ * // simple boolean defaulting to true (unless non-negated option is also defined)
635
+ * program.option('-C, --no-cheese', 'remove cheese');
605
636
  *
606
- * program.cheese
607
- * // => true
637
+ * program.cheese
638
+ * // => true
608
639
  *
609
- * --no-cheese
610
- * program.cheese
611
- * // => false
640
+ * --no-cheese
641
+ * program.cheese
642
+ * // => false
612
643
  *
613
- * // required argument
614
- * program.option('-C, --chdir <path>', 'change the working directory');
644
+ * // required argument
645
+ * program.option('-C, --chdir <path>', 'change the working directory');
615
646
  *
616
- * --chdir /tmp
617
- * program.chdir
618
- * // => "/tmp"
647
+ * --chdir /tmp
648
+ * program.chdir
649
+ * // => "/tmp"
619
650
  *
620
- * // optional argument
621
- * program.option('-c, --cheese [type]', 'add cheese [marble]');
651
+ * // optional argument
652
+ * program.option('-c, --cheese [type]', 'add cheese [marble]');
622
653
  *
623
654
  * @param {string} flags
624
655
  * @param {string} [description]
@@ -651,11 +682,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
651
682
  /**
652
683
  * Alter parsing of short flags with optional values.
653
684
  *
654
- * Examples:
655
- *
656
- * // for `.option('-f,--flag [value]'):
657
- * .combineFlagAndOptionalValue(true) // `-f80` is treated like `--flag=80`, this is the default behaviour
658
- * .combineFlagAndOptionalValue(false) // `-fb` is treated like `-f -b`
685
+ * @example
686
+ * // for `.option('-f,--flag [value]'):
687
+ * program.combineFlagAndOptionalValue(true); // `-f80` is treated like `--flag=80`, this is the default behaviour
688
+ * program.combineFlagAndOptionalValue(false) // `-fb` is treated like `-f -b`
659
689
  *
660
690
  * @param {Boolean} [combine=true] - if `true` or omitted, an optional value can be specified directly after the flag.
661
691
  */
@@ -762,6 +792,33 @@ Expecting one of '${allowedValues.join("', '")}'`);
762
792
  return this;
763
793
  };
764
794
 
795
+ /**
796
+ * Store option value and where the value came from.
797
+ *
798
+ * @param {string} key
799
+ * @param {Object} value
800
+ * @param {string} source - expected values are default/config/env/cli
801
+ * @return {Command} `this` command for chaining
802
+ */
803
+
804
+ setOptionValueWithSource(key, value, source) {
805
+ this.setOptionValue(key, value);
806
+ this._optionValueSources[key] = source;
807
+ return this;
808
+ }
809
+
810
+ /**
811
+ * Get source of option value.
812
+ * Expected values are default | config | env | cli
813
+ *
814
+ * @param {string} key
815
+ * @return {string}
816
+ */
817
+
818
+ getOptionValueSource(key) {
819
+ return this._optionValueSources[key];
820
+ };
821
+
765
822
  /**
766
823
  * Get user arguments implied or explicit arguments.
767
824
  * Side-effects: set _scriptPath if args included application, and use that to set implicit command name.
@@ -824,11 +881,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
824
881
  * The default expectation is that the arguments are from node and have the application as argv[0]
825
882
  * and the script being run in argv[1], with user parameters after that.
826
883
  *
827
- * Examples:
828
- *
829
- * program.parse(process.argv);
830
- * program.parse(); // implicitly use process.argv and auto-detect node vs electron conventions
831
- * program.parse(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0]
884
+ * @example
885
+ * program.parse(process.argv);
886
+ * program.parse(); // implicitly use process.argv and auto-detect node vs electron conventions
887
+ * program.parse(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0]
832
888
  *
833
889
  * @param {string[]} [argv] - optional, defaults to process.argv
834
890
  * @param {Object} [parseOptions] - optionally specify style of options with from: node/user/electron
@@ -851,11 +907,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
851
907
  * The default expectation is that the arguments are from node and have the application as argv[0]
852
908
  * and the script being run in argv[1], with user parameters after that.
853
909
  *
854
- * Examples:
855
- *
856
- * await program.parseAsync(process.argv);
857
- * await program.parseAsync(); // implicitly use process.argv and auto-detect node vs electron conventions
858
- * await program.parseAsync(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0]
910
+ * @example
911
+ * await program.parseAsync(process.argv);
912
+ * await program.parseAsync(); // implicitly use process.argv and auto-detect node vs electron conventions
913
+ * await program.parseAsync(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0]
859
914
  *
860
915
  * @param {string[]} [argv]
861
916
  * @param {Object} [parseOptions]
@@ -1076,6 +1131,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1076
1131
  * @param {Promise|undefined} promise
1077
1132
  * @param {Function} fn
1078
1133
  * @return {Promise|undefined}
1134
+ * @api private
1079
1135
  */
1080
1136
 
1081
1137
  _chainOrCall(promise, fn) {
@@ -1128,6 +1184,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1128
1184
 
1129
1185
  _parseCommand(operands, unknown) {
1130
1186
  const parsed = this.parseOptions(unknown);
1187
+ this._parseOptionsEnv(); // after cli, so parseArg not called on both cli and env
1131
1188
  operands = operands.concat(parsed.operands);
1132
1189
  unknown = parsed.unknown;
1133
1190
  this.args = operands.concat(unknown);
@@ -1190,6 +1247,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1190
1247
  this._processArguments();
1191
1248
  }
1192
1249
  } else if (this.commands.length) {
1250
+ checkForUnknownOptions();
1193
1251
  // This command has subcommands and nothing hooked up at this level, so display help (and exit).
1194
1252
  this.help({ error: true });
1195
1253
  } else {
@@ -1245,11 +1303,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
1245
1303
  *
1246
1304
  * Examples:
1247
1305
  *
1248
- * argv => operands, unknown
1249
- * --known kkk op => [op], []
1250
- * op --known kkk => [op], []
1251
- * sub --unknown uuu op => [sub], [--unknown uuu op]
1252
- * sub -- --unknown uuu op => [sub --unknown uuu op], []
1306
+ * argv => operands, unknown
1307
+ * --known kkk op => [op], []
1308
+ * op --known kkk => [op], []
1309
+ * sub --unknown uuu op => [sub], [--unknown uuu op]
1310
+ * sub -- --unknown uuu op => [sub --unknown uuu op], []
1253
1311
  *
1254
1312
  * @param {String[]} argv
1255
1313
  * @return {{operands: String[], unknown: String[]}}
@@ -1408,6 +1466,30 @@ Expecting one of '${allowedValues.join("', '")}'`);
1408
1466
  this._exit(exitCode, code, message);
1409
1467
  }
1410
1468
 
1469
+ /**
1470
+ * Apply any option related environment variables, if option does
1471
+ * not have a value from cli or client code.
1472
+ *
1473
+ * @api private
1474
+ */
1475
+ _parseOptionsEnv() {
1476
+ this.options.forEach((option) => {
1477
+ if (option.envVar && option.envVar in process.env) {
1478
+ const optionKey = option.attributeName();
1479
+ // Priority check. Do not overwrite cli or options from unknown source (client-code).
1480
+ if (this.getOptionValue(optionKey) === undefined || ['default', 'config', 'env'].includes(this.getOptionValueSource(optionKey))) {
1481
+ if (option.required || option.optional) { // option can take a value
1482
+ // keep very simple, optional always takes value
1483
+ this.emit(`optionEnv:${option.name()}`, process.env[option.envVar]);
1484
+ } else { // boolean
1485
+ // keep very simple, only care that envVar defined and not the value
1486
+ this.emit(`optionEnv:${option.name()}`);
1487
+ }
1488
+ }
1489
+ }
1490
+ });
1491
+ }
1492
+
1411
1493
  /**
1412
1494
  * Argument `name` is missing.
1413
1495
  *
@@ -1453,7 +1535,23 @@ Expecting one of '${allowedValues.join("', '")}'`);
1453
1535
 
1454
1536
  unknownOption(flag) {
1455
1537
  if (this._allowUnknownOption) return;
1456
- const message = `error: unknown option '${flag}'`;
1538
+ let suggestion = '';
1539
+
1540
+ if (flag.startsWith('--') && this._showSuggestionAfterError) {
1541
+ // Looping to pick up the global options too
1542
+ let candidateFlags = [];
1543
+ let command = this;
1544
+ do {
1545
+ const moreFlags = command.createHelp().visibleOptions(command)
1546
+ .filter(option => option.long)
1547
+ .map(option => option.long);
1548
+ candidateFlags = candidateFlags.concat(moreFlags);
1549
+ command = command.parent;
1550
+ } while (command && !command._enablePositionalOptions);
1551
+ suggestion = suggestSimilar(flag, candidateFlags);
1552
+ }
1553
+
1554
+ const message = `error: unknown option '${flag}'${suggestion}`;
1457
1555
  this._displayError(1, 'commander.unknownOption', message);
1458
1556
  };
1459
1557
 
@@ -1481,7 +1579,20 @@ Expecting one of '${allowedValues.join("', '")}'`);
1481
1579
  */
1482
1580
 
1483
1581
  unknownCommand() {
1484
- const message = `error: unknown command '${this.args[0]}'`;
1582
+ const unknownName = this.args[0];
1583
+ let suggestion = '';
1584
+
1585
+ if (this._showSuggestionAfterError) {
1586
+ const candidateNames = [];
1587
+ this.createHelp().visibleCommands(this).forEach((command) => {
1588
+ candidateNames.push(command.name());
1589
+ // just visible alias
1590
+ if (command.alias()) candidateNames.push(command.alias());
1591
+ });
1592
+ suggestion = suggestSimilar(unknownName, candidateNames);
1593
+ }
1594
+
1595
+ const message = `error: unknown command '${unknownName}'${suggestion}`;
1485
1596
  this._displayError(1, 'commander.unknownCommand', message);
1486
1597
  };
1487
1598
 
@@ -1659,14 +1770,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1659
1770
  }
1660
1771
  const context = this._getHelpContext(contextOptions);
1661
1772
 
1662
- const groupListeners = [];
1663
- let command = this;
1664
- while (command) {
1665
- groupListeners.push(command); // ordered from current command to root
1666
- command = command.parent;
1667
- }
1668
-
1669
- groupListeners.slice().reverse().forEach(command => command.emit('beforeAllHelp', context));
1773
+ getCommandAndParents(this).reverse().forEach(command => command.emit('beforeAllHelp', context));
1670
1774
  this.emit('beforeHelp', context);
1671
1775
 
1672
1776
  let helpInformation = this.helpInformation(context);
@@ -1680,7 +1784,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1680
1784
 
1681
1785
  this.emit(this._helpLongFlag); // deprecated
1682
1786
  this.emit('afterHelp', context);
1683
- groupListeners.forEach(command => command.emit('afterAllHelp', context));
1787
+ getCommandAndParents(this).forEach(command => command.emit('afterAllHelp', context));
1684
1788
  };
1685
1789
 
1686
1790
  /**
package/lib/help.js CHANGED
@@ -234,21 +234,24 @@ class Help {
234
234
  */
235
235
 
236
236
  optionDescription(option) {
237
- if (option.negate) {
238
- return option.description;
239
- }
240
237
  const extraInfo = [];
241
- if (option.argChoices) {
238
+ // Some of these do not make sense for negated boolean and suppress for backwards compatibility.
239
+
240
+ if (option.argChoices && !option.negate) {
242
241
  extraInfo.push(
243
242
  // use stringify to match the display of the default value
244
243
  `choices: ${option.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}`);
245
244
  }
246
- if (option.defaultValue !== undefined) {
245
+ if (option.defaultValue !== undefined && !option.negate) {
247
246
  extraInfo.push(`default: ${option.defaultValueDescription || JSON.stringify(option.defaultValue)}`);
248
247
  }
248
+ if (option.envVar !== undefined) {
249
+ extraInfo.push(`env: ${option.envVar}`);
250
+ }
249
251
  if (extraInfo.length > 0) {
250
252
  return `${option.description} (${extraInfo.join(', ')})`;
251
253
  }
254
+
252
255
  return option.description;
253
256
  };
254
257
 
package/lib/option.js CHANGED
@@ -28,6 +28,7 @@ class Option {
28
28
  }
29
29
  this.defaultValue = undefined;
30
30
  this.defaultValueDescription = undefined;
31
+ this.envVar = undefined;
31
32
  this.parseArg = undefined;
32
33
  this.hidden = false;
33
34
  this.argChoices = undefined;
@@ -47,6 +48,19 @@ class Option {
47
48
  return this;
48
49
  };
49
50
 
51
+ /**
52
+ * Set environment variable to check for option value.
53
+ * Priority order of option values is default < env < cli
54
+ *
55
+ * @param {string} name
56
+ * @return {Option}
57
+ */
58
+
59
+ env(name) {
60
+ this.envVar = name;
61
+ return this;
62
+ };
63
+
50
64
  /**
51
65
  * Set the custom handler for processing CLI option arguments into option values.
52
66
  *
@@ -0,0 +1,100 @@
1
+ const maxDistance = 3;
2
+
3
+ function editDistance(a, b) {
4
+ // https://en.wikipedia.org/wiki/Damerau–Levenshtein_distance
5
+ // Calculating optimal string alignment distance, no substring is edited more than once.
6
+ // (Simple implementation.)
7
+
8
+ // Quick early exit, return worst case.
9
+ if (Math.abs(a.length - b.length) > maxDistance) return Math.max(a.length, b.length);
10
+
11
+ // distance between prefix substrings of a and b
12
+ const d = [];
13
+
14
+ // pure deletions turn a into empty string
15
+ for (let i = 0; i <= a.length; i++) {
16
+ d[i] = [i];
17
+ }
18
+ // pure insertions turn empty string into b
19
+ for (let j = 0; j <= b.length; j++) {
20
+ d[0][j] = j;
21
+ }
22
+
23
+ // fill matrix
24
+ for (let j = 1; j <= b.length; j++) {
25
+ for (let i = 1; i <= a.length; i++) {
26
+ let cost = 1;
27
+ if (a[i - 1] === b[j - 1]) {
28
+ cost = 0;
29
+ } else {
30
+ cost = 1;
31
+ }
32
+ d[i][j] = Math.min(
33
+ d[i - 1][j] + 1, // deletion
34
+ d[i][j - 1] + 1, // insertion
35
+ d[i - 1][j - 1] + cost // substitution
36
+ );
37
+ // transposition
38
+ if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
39
+ d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);
40
+ }
41
+ }
42
+ }
43
+
44
+ return d[a.length][b.length];
45
+ }
46
+
47
+ /**
48
+ * Find close matches, restricted to same number of edits.
49
+ *
50
+ * @param {string} word
51
+ * @param {string[]} candidates
52
+ * @returns {string}
53
+ */
54
+
55
+ function suggestSimilar(word, candidates) {
56
+ if (!candidates || candidates.length === 0) return '';
57
+ // remove possible duplicates
58
+ candidates = Array.from(new Set(candidates));
59
+
60
+ const searchingOptions = word.startsWith('--');
61
+ if (searchingOptions) {
62
+ word = word.slice(2);
63
+ candidates = candidates.map(candidate => candidate.slice(2));
64
+ }
65
+
66
+ let similar = [];
67
+ let bestDistance = maxDistance;
68
+ const minSimilarity = 0.4;
69
+ candidates.forEach((candidate) => {
70
+ if (candidate.length <= 1) return; // no one character guesses
71
+
72
+ const distance = editDistance(word, candidate);
73
+ const length = Math.max(word.length, candidate.length);
74
+ const similarity = (length - distance) / length;
75
+ if (similarity > minSimilarity) {
76
+ if (distance < bestDistance) {
77
+ // better edit distance, throw away previous worse matches
78
+ bestDistance = distance;
79
+ similar = [candidate];
80
+ } else if (distance === bestDistance) {
81
+ similar.push(candidate);
82
+ }
83
+ }
84
+ });
85
+
86
+ similar.sort((a, b) => a.localeCompare(b));
87
+ if (searchingOptions) {
88
+ similar = similar.map(candidate => `--${candidate}`);
89
+ }
90
+
91
+ if (similar.length > 1) {
92
+ return `\n(Did you mean one of ${similar.join(', ')}?)`;
93
+ }
94
+ if (similar.length === 1) {
95
+ return `\n(Did you mean ${similar[0]}?)`;
96
+ }
97
+ return '';
98
+ }
99
+
100
+ exports.suggestSimilar = suggestSimilar;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commander",
3
- "version": "8.0.0-2",
3
+ "version": "8.3.0",
4
4
  "description": "the complete solution for node.js command-line programs",
5
5
  "keywords": [
6
6
  "commander",
@@ -39,17 +39,17 @@
39
39
  "dependencies": {},
40
40
  "devDependencies": {
41
41
  "@types/jest": "^26.0.23",
42
- "@types/node": "^14.17.1",
43
- "@typescript-eslint/eslint-plugin": "^4.25.0",
44
- "@typescript-eslint/parser": "^4.25.0",
45
- "eslint": "^7.27.0",
42
+ "@types/node": "^14.17.3",
43
+ "@typescript-eslint/eslint-plugin": "^4.27.0",
44
+ "@typescript-eslint/parser": "^4.27.0",
45
+ "eslint": "^7.29.0",
46
46
  "eslint-config-standard": "^16.0.3",
47
47
  "eslint-plugin-jest": "^24.3.6",
48
- "jest": "^27.0.1",
48
+ "jest": "^27.0.4",
49
49
  "standard": "^16.0.3",
50
- "ts-jest": "^27.0.1",
51
- "tsd": "^0.16.0",
52
- "typescript": "^4.3.2"
50
+ "ts-jest": "^27.0.3",
51
+ "tsd": "^0.17.0",
52
+ "typescript": "^4.3.4"
53
53
  },
54
54
  "types": "typings/index.d.ts",
55
55
  "jest": {
@@ -13,9 +13,9 @@ export class CommanderError extends Error {
13
13
 
14
14
  /**
15
15
  * Constructs the CommanderError class
16
- * @param {number} exitCode suggested exit code which could be used with process.exit
17
- * @param {string} code an id string representing the error
18
- * @param {string} message human-readable description of the error
16
+ * @param exitCode - suggested exit code which could be used with process.exit
17
+ * @param code - an id string representing the error
18
+ * @param message - human-readable description of the error
19
19
  * @constructor
20
20
  */
21
21
  constructor(exitCode: number, code: string, message: string);
@@ -24,7 +24,7 @@ export class CommanderError extends Error {
24
24
  export class InvalidArgumentError extends CommanderError {
25
25
  /**
26
26
  * Constructs the InvalidArgumentError class
27
- * @param {string} [message] explanation of why argument is invalid
27
+ * @param message - explanation of why argument is invalid
28
28
  * @constructor
29
29
  */
30
30
  constructor(message: string);
@@ -40,32 +40,38 @@ export class Argument {
40
40
  * Initialize a new command argument with the given name and description.
41
41
  * The default is that the argument is required, and you can explicitly
42
42
  * indicate this with <> around the name. Put [] around the name for an optional argument.
43
- *
44
- * @param {string} name
45
- * @param {string} [description]
46
43
  */
47
44
  constructor(arg: string, description?: string);
48
45
 
49
46
  /**
50
- * Return argument name.
51
- */
47
+ * Return argument name.
48
+ */
52
49
  name(): string;
53
50
 
54
51
  /**
55
52
  * Set the default value, and optionally supply the description to be displayed in the help.
56
53
  */
57
- default(value: unknown, description?: string): this;
54
+ default(value: unknown, description?: string): this;
58
55
 
59
56
  /**
60
57
  * Set the custom handler for processing CLI command arguments into argument values.
61
58
  */
62
- argParser<T>(fn: (value: string, previous: T) => T): this;
59
+ argParser<T>(fn: (value: string, previous: T) => T): this;
63
60
 
64
61
  /**
65
62
  * Only allow argument value to be one of choices.
66
63
  */
67
- choices(values: string[]): this;
64
+ choices(values: string[]): this;
65
+
66
+ /**
67
+ * Make option-argument required.
68
+ */
69
+ argRequired(): this;
68
70
 
71
+ /**
72
+ * Make option-argument optional.
73
+ */
74
+ argOptional(): this;
69
75
  }
70
76
 
71
77
  export class Option {
@@ -93,6 +99,12 @@ export class Option {
93
99
  */
94
100
  default(value: unknown, description?: string): this;
95
101
 
102
+ /**
103
+ * Set environment variable to check for option value.
104
+ * Priority order of option values is default < env < cli
105
+ */
106
+ env(name: string): this;
107
+
96
108
  /**
97
109
  * Calculate the full description, including defaultValue etc.
98
110
  */
@@ -113,12 +125,6 @@ export class Option {
113
125
  */
114
126
  hideHelp(hide?: boolean): this;
115
127
 
116
- /**
117
- * Validation of option argument failed.
118
- * Intended for use from custom argument processing functions.
119
- */
120
- argumentRejected(messsage: string): never;
121
-
122
128
  /**
123
129
  * Only allow option value to be one of choices.
124
130
  */
@@ -208,8 +214,9 @@ export interface OutputConfiguration {
208
214
 
209
215
  }
210
216
 
211
- type AddHelpTextPosition = 'beforeAll' | 'before' | 'after' | 'afterAll';
212
- type HookEvent = 'preAction' | 'postAction';
217
+ export type AddHelpTextPosition = 'beforeAll' | 'before' | 'after' | 'afterAll';
218
+ export type HookEvent = 'preAction' | 'postAction';
219
+ export type OptionValueSource = 'default' | 'env' | 'config' | 'cli';
213
220
 
214
221
  export interface OptionValues {
215
222
  [key: string]: any;
@@ -241,12 +248,12 @@ export class Command {
241
248
  *
242
249
  * @example
243
250
  * ```ts
244
- * program
245
- * .command('clone <source> [destination]')
246
- * .description('clone a repository into a newly created directory')
247
- * .action((source, destination) => {
248
- * console.log('clone command called');
249
- * });
251
+ * program
252
+ * .command('clone <source> [destination]')
253
+ * .description('clone a repository into a newly created directory')
254
+ * .action((source, destination) => {
255
+ * console.log('clone command called');
256
+ * });
250
257
  * ```
251
258
  *
252
259
  * @param nameAndArgs - command name and arguments, args are `<required>` or `[optional]` and last may also be `variadic...`
@@ -306,9 +313,10 @@ export class Command {
306
313
  * indicate this with <> around the name. Put [] around the name for an optional argument.
307
314
  *
308
315
  * @example
309
- *
310
- * program.argument('<input-file>');
311
- * program.argument('[output-file]');
316
+ * ```
317
+ * program.argument('<input-file>');
318
+ * program.argument('[output-file]');
319
+ * ```
312
320
  *
313
321
  * @returns `this` command for chaining
314
322
  */
@@ -328,8 +336,9 @@ export class Command {
328
336
  * See also .argument().
329
337
  *
330
338
  * @example
331
- *
332
- * program.arguments('<cmd> [env]');
339
+ * ```
340
+ * program.arguments('<cmd> [env]');
341
+ * ```
333
342
  *
334
343
  * @returns `this` command for chaining
335
344
  */
@@ -338,9 +347,12 @@ export class Command {
338
347
  /**
339
348
  * Override default decision whether to add implicit help command.
340
349
  *
341
- * addHelpCommand() // force on
342
- * addHelpCommand(false); // force off
343
- * addHelpCommand('help [cmd]', 'display help for [cmd]'); // force on with custom details
350
+ * @example
351
+ * ```
352
+ * addHelpCommand() // force on
353
+ * addHelpCommand(false); // force off
354
+ * addHelpCommand('help [cmd]', 'display help for [cmd]'); // force on with custom details
355
+ * ```
344
356
  *
345
357
  * @returns `this` command for chaining
346
358
  */
@@ -375,35 +387,50 @@ export class Command {
375
387
  * applications. You can also customise the display of errors by overriding outputError.
376
388
  *
377
389
  * The configuration properties are all functions:
378
- *
379
- * // functions to change where being written, stdout and stderr
380
- * writeOut(str)
381
- * writeErr(str)
382
- * // matching functions to specify width for wrapping help
383
- * getOutHelpWidth()
384
- * getErrHelpWidth()
385
- * // functions based on what is being written out
386
- * outputError(str, write) // used for displaying errors, and not used for displaying help
390
+ * ```
391
+ * // functions to change where being written, stdout and stderr
392
+ * writeOut(str)
393
+ * writeErr(str)
394
+ * // matching functions to specify width for wrapping help
395
+ * getOutHelpWidth()
396
+ * getErrHelpWidth()
397
+ * // functions based on what is being written out
398
+ * outputError(str, write) // used for displaying errors, and not used for displaying help
399
+ * ```
387
400
  */
388
401
  configureOutput(configuration: OutputConfiguration): this;
389
402
  /** Get configuration */
390
403
  configureOutput(): OutputConfiguration;
391
404
 
405
+ /**
406
+ * Copy settings that are useful to have in common across root command and subcommands.
407
+ *
408
+ * (Used internally when adding a command using `.command()` so subcommands inherit parent settings.)
409
+ */
410
+ copyInheritedSettings(sourceCommand: Command): this;
411
+
392
412
  /**
393
413
  * Display the help or a custom message after an error occurs.
394
414
  */
395
415
  showHelpAfterError(displayHelp?: boolean | string): this;
396
416
 
397
417
  /**
418
+ * Display suggestion of similar commands for unknown commands, or options for unknown options.
419
+ */
420
+ showSuggestionAfterError(displaySuggestion?: boolean): this;
421
+
422
+ /**
398
423
  * Register callback `fn` for the command.
399
424
  *
400
425
  * @example
401
- * program
402
- * .command('help')
403
- * .description('display verbose help')
404
- * .action(function() {
405
- * // output help here
406
- * });
426
+ * ```
427
+ * program
428
+ * .command('serve')
429
+ * .description('start service')
430
+ * .action(function() {
431
+ * // do work here
432
+ * });
433
+ * ```
407
434
  *
408
435
  * @returns `this` command for chaining
409
436
  */
@@ -417,37 +444,39 @@ export class Command {
417
444
  * separated by comma, a pipe or space. The following are all valid
418
445
  * all will output this way when `--help` is used.
419
446
  *
420
- * "-p, --pepper"
421
- * "-p|--pepper"
422
- * "-p --pepper"
447
+ * "-p, --pepper"
448
+ * "-p|--pepper"
449
+ * "-p --pepper"
423
450
  *
424
451
  * @example
425
- * // simple boolean defaulting to false
426
- * program.option('-p, --pepper', 'add pepper');
452
+ * ```
453
+ * // simple boolean defaulting to false
454
+ * program.option('-p, --pepper', 'add pepper');
427
455
  *
428
- * --pepper
429
- * program.pepper
430
- * // => Boolean
456
+ * --pepper
457
+ * program.pepper
458
+ * // => Boolean
431
459
  *
432
- * // simple boolean defaulting to true
433
- * program.option('-C, --no-cheese', 'remove cheese');
460
+ * // simple boolean defaulting to true
461
+ * program.option('-C, --no-cheese', 'remove cheese');
434
462
  *
435
- * program.cheese
436
- * // => true
463
+ * program.cheese
464
+ * // => true
437
465
  *
438
- * --no-cheese
439
- * program.cheese
440
- * // => false
466
+ * --no-cheese
467
+ * program.cheese
468
+ * // => false
441
469
  *
442
- * // required argument
443
- * program.option('-C, --chdir <path>', 'change the working directory');
470
+ * // required argument
471
+ * program.option('-C, --chdir <path>', 'change the working directory');
444
472
  *
445
- * --chdir /tmp
446
- * program.chdir
447
- * // => "/tmp"
473
+ * --chdir /tmp
474
+ * program.chdir
475
+ * // => "/tmp"
448
476
  *
449
- * // optional argument
450
- * program.option('-c, --cheese [type]', 'add cheese [marble]');
477
+ * // optional argument
478
+ * program.option('-c, --cheese [type]', 'add cheese [marble]');
479
+ * ```
451
480
  *
452
481
  * @returns `this` command for chaining
453
482
  */
@@ -498,18 +527,30 @@ export class Command {
498
527
  */
499
528
  getOptionValue(key: string): any;
500
529
 
501
- /**
530
+ /**
502
531
  * Store option value.
503
532
  */
504
533
  setOptionValue(key: string, value: unknown): this;
505
534
 
506
535
  /**
536
+ * Store option value and where the value came from.
537
+ */
538
+ setOptionValueWithSource(key: string, value: unknown, source: OptionValueSource): this;
539
+
540
+ /**
541
+ * Retrieve option value source.
542
+ */
543
+ getOptionValueSource(key: string): OptionValueSource;
544
+
545
+ /**
507
546
  * Alter parsing of short flags with optional values.
508
547
  *
509
548
  * @example
510
- * // for `.option('-f,--flag [value]'):
511
- * .combineFlagAndOptionalValue(true) // `-f80` is treated like `--flag=80`, this is the default behaviour
512
- * .combineFlagAndOptionalValue(false) // `-fb` is treated like `-f -b`
549
+ * ```
550
+ * // for `.option('-f,--flag [value]'):
551
+ * .combineFlagAndOptionalValue(true) // `-f80` is treated like `--flag=80`, this is the default behaviour
552
+ * .combineFlagAndOptionalValue(false) // `-fb` is treated like `-f -b`
553
+ * ```
513
554
  *
514
555
  * @returns `this` command for chaining
515
556
  */
@@ -556,11 +597,12 @@ export class Command {
556
597
  * The default expectation is that the arguments are from node and have the application as argv[0]
557
598
  * and the script being run in argv[1], with user parameters after that.
558
599
  *
559
- * Examples:
560
- *
561
- * program.parse(process.argv);
562
- * program.parse(); // implicitly use process.argv and auto-detect node vs electron conventions
563
- * program.parse(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0]
600
+ * @example
601
+ * ```
602
+ * program.parse(process.argv);
603
+ * program.parse(); // implicitly use process.argv and auto-detect node vs electron conventions
604
+ * program.parse(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0]
605
+ * ```
564
606
  *
565
607
  * @returns `this` command for chaining
566
608
  */
@@ -574,11 +616,12 @@ export class Command {
574
616
  * The default expectation is that the arguments are from node and have the application as argv[0]
575
617
  * and the script being run in argv[1], with user parameters after that.
576
618
  *
577
- * Examples:
578
- *
579
- * program.parseAsync(process.argv);
580
- * program.parseAsync(); // implicitly use process.argv and auto-detect node vs electron conventions
581
- * program.parseAsync(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0]
619
+ * @example
620
+ * ```
621
+ * program.parseAsync(process.argv);
622
+ * program.parseAsync(); // implicitly use process.argv and auto-detect node vs electron conventions
623
+ * program.parseAsync(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0]
624
+ * ```
582
625
  *
583
626
  * @returns Promise
584
627
  */
@@ -588,12 +631,11 @@ export class Command {
588
631
  * Parse options from `argv` removing known options,
589
632
  * and return argv split into operands and unknown arguments.
590
633
  *
591
- * @example
592
- * argv => operands, unknown
593
- * --known kkk op => [op], []
594
- * op --known kkk => [op], []
595
- * sub --unknown uuu op => [sub], [--unknown uuu op]
596
- * sub -- --unknown uuu op => [sub --unknown uuu op], []
634
+ * argv => operands, unknown
635
+ * --known kkk op => [op], []
636
+ * op --known kkk => [op], []
637
+ * sub --unknown uuu op => [sub], [--unknown uuu op]
638
+ * sub -- --unknown uuu op => [sub --unknown uuu op], []
597
639
  */
598
640
  parseOptions(argv: string[]): ParseOptionsResult;
599
641