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 +32 -23
- package/lib/argument.js +16 -0
- package/lib/command.js +226 -122
- package/lib/help.js +8 -5
- package/lib/option.js +14 -0
- package/lib/suggestSimilar.js +100 -0
- package/package.json +9 -9
- package/typings/index.d.ts +133 -91
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
|
|
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
|
-
*
|
|
80
|
+
* Copy settings that are useful to have in common across root command and subcommands.
|
|
78
81
|
*
|
|
79
|
-
*
|
|
82
|
+
* (Used internally when adding a command using `.command()` so subcommands inherit parent settings.)
|
|
80
83
|
*
|
|
81
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
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
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
442
|
-
*
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
*
|
|
446
|
-
*
|
|
447
|
-
*
|
|
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.
|
|
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
|
-
//
|
|
516
|
-
|
|
517
|
-
|
|
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 =
|
|
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.
|
|
540
|
-
? false
|
|
541
|
-
: defaultValue || true);
|
|
561
|
+
this.setOptionValueWithSource(name, option.negate ? false : defaultValue || true, valueSource);
|
|
542
562
|
} else {
|
|
543
|
-
this.
|
|
563
|
+
this.setOptionValueWithSource(name, val, valueSource);
|
|
544
564
|
}
|
|
545
565
|
} else if (val !== null) {
|
|
546
566
|
// reassign
|
|
547
|
-
this.
|
|
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
|
-
*
|
|
588
|
-
*
|
|
589
|
-
*
|
|
619
|
+
* "-p, --pepper"
|
|
620
|
+
* "-p|--pepper"
|
|
621
|
+
* "-p --pepper"
|
|
590
622
|
*
|
|
591
|
-
*
|
|
592
|
-
*
|
|
593
|
-
*
|
|
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
|
-
*
|
|
597
|
-
*
|
|
627
|
+
* program.pepper
|
|
628
|
+
* // => undefined
|
|
598
629
|
*
|
|
599
|
-
*
|
|
600
|
-
*
|
|
601
|
-
*
|
|
630
|
+
* --pepper
|
|
631
|
+
* program.pepper
|
|
632
|
+
* // => true
|
|
602
633
|
*
|
|
603
|
-
*
|
|
604
|
-
*
|
|
634
|
+
* // simple boolean defaulting to true (unless non-negated option is also defined)
|
|
635
|
+
* program.option('-C, --no-cheese', 'remove cheese');
|
|
605
636
|
*
|
|
606
|
-
*
|
|
607
|
-
*
|
|
637
|
+
* program.cheese
|
|
638
|
+
* // => true
|
|
608
639
|
*
|
|
609
|
-
*
|
|
610
|
-
*
|
|
611
|
-
*
|
|
640
|
+
* --no-cheese
|
|
641
|
+
* program.cheese
|
|
642
|
+
* // => false
|
|
612
643
|
*
|
|
613
|
-
*
|
|
614
|
-
*
|
|
644
|
+
* // required argument
|
|
645
|
+
* program.option('-C, --chdir <path>', 'change the working directory');
|
|
615
646
|
*
|
|
616
|
-
*
|
|
617
|
-
*
|
|
618
|
-
*
|
|
647
|
+
* --chdir /tmp
|
|
648
|
+
* program.chdir
|
|
649
|
+
* // => "/tmp"
|
|
619
650
|
*
|
|
620
|
-
*
|
|
621
|
-
*
|
|
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
|
-
*
|
|
655
|
-
*
|
|
656
|
-
*
|
|
657
|
-
*
|
|
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
|
-
*
|
|
828
|
-
*
|
|
829
|
-
*
|
|
830
|
-
*
|
|
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
|
-
*
|
|
855
|
-
*
|
|
856
|
-
*
|
|
857
|
-
*
|
|
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
|
-
*
|
|
1249
|
-
*
|
|
1250
|
-
*
|
|
1251
|
-
*
|
|
1252
|
-
*
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
43
|
-
"@typescript-eslint/eslint-plugin": "^4.
|
|
44
|
-
"@typescript-eslint/parser": "^4.
|
|
45
|
-
"eslint": "^7.
|
|
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.
|
|
48
|
+
"jest": "^27.0.4",
|
|
49
49
|
"standard": "^16.0.3",
|
|
50
|
-
"ts-jest": "^27.0.
|
|
51
|
-
"tsd": "^0.
|
|
52
|
-
"typescript": "^4.3.
|
|
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": {
|
package/typings/index.d.ts
CHANGED
|
@@ -13,9 +13,9 @@ export class CommanderError extends Error {
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Constructs the CommanderError class
|
|
16
|
-
* @param
|
|
17
|
-
* @param
|
|
18
|
-
* @param
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
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
|
-
*
|
|
311
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
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
|
-
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
*
|
|
386
|
-
*
|
|
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
|
-
*
|
|
402
|
-
*
|
|
403
|
-
*
|
|
404
|
-
*
|
|
405
|
-
*
|
|
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
|
-
*
|
|
421
|
-
*
|
|
422
|
-
*
|
|
447
|
+
* "-p, --pepper"
|
|
448
|
+
* "-p|--pepper"
|
|
449
|
+
* "-p --pepper"
|
|
423
450
|
*
|
|
424
451
|
* @example
|
|
425
|
-
*
|
|
426
|
-
*
|
|
452
|
+
* ```
|
|
453
|
+
* // simple boolean defaulting to false
|
|
454
|
+
* program.option('-p, --pepper', 'add pepper');
|
|
427
455
|
*
|
|
428
|
-
*
|
|
429
|
-
*
|
|
430
|
-
*
|
|
456
|
+
* --pepper
|
|
457
|
+
* program.pepper
|
|
458
|
+
* // => Boolean
|
|
431
459
|
*
|
|
432
|
-
*
|
|
433
|
-
*
|
|
460
|
+
* // simple boolean defaulting to true
|
|
461
|
+
* program.option('-C, --no-cheese', 'remove cheese');
|
|
434
462
|
*
|
|
435
|
-
*
|
|
436
|
-
*
|
|
463
|
+
* program.cheese
|
|
464
|
+
* // => true
|
|
437
465
|
*
|
|
438
|
-
*
|
|
439
|
-
*
|
|
440
|
-
*
|
|
466
|
+
* --no-cheese
|
|
467
|
+
* program.cheese
|
|
468
|
+
* // => false
|
|
441
469
|
*
|
|
442
|
-
*
|
|
443
|
-
*
|
|
470
|
+
* // required argument
|
|
471
|
+
* program.option('-C, --chdir <path>', 'change the working directory');
|
|
444
472
|
*
|
|
445
|
-
*
|
|
446
|
-
*
|
|
447
|
-
*
|
|
473
|
+
* --chdir /tmp
|
|
474
|
+
* program.chdir
|
|
475
|
+
* // => "/tmp"
|
|
448
476
|
*
|
|
449
|
-
*
|
|
450
|
-
*
|
|
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
|
-
*
|
|
511
|
-
*
|
|
512
|
-
*
|
|
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
|
-
*
|
|
560
|
-
*
|
|
561
|
-
*
|
|
562
|
-
*
|
|
563
|
-
*
|
|
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
|
-
*
|
|
578
|
-
*
|
|
579
|
-
*
|
|
580
|
-
*
|
|
581
|
-
*
|
|
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
|
-
*
|
|
592
|
-
*
|
|
593
|
-
*
|
|
594
|
-
*
|
|
595
|
-
*
|
|
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
|
|