commander 13.1.0 → 14.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Readme.md +16 -6
- package/lib/argument.js +5 -4
- package/lib/command.js +154 -39
- package/lib/help.js +77 -39
- package/lib/option.js +16 -3
- package/package.json +8 -8
- package/typings/index.d.ts +69 -1
package/Readme.md
CHANGED
|
@@ -38,6 +38,7 @@ Read this in other languages: English | [简体中文](./Readme_zh-CN.md)
|
|
|
38
38
|
- [.description and .summary](#description-and-summary)
|
|
39
39
|
- [.helpOption(flags, description)](#helpoptionflags-description)
|
|
40
40
|
- [.helpCommand()](#helpcommand)
|
|
41
|
+
- [Help Groups](#help-groups)
|
|
41
42
|
- [More configuration](#more-configuration-2)
|
|
42
43
|
- [Custom event listeners](#custom-event-listeners)
|
|
43
44
|
- [Bits and pieces](#bits-and-pieces)
|
|
@@ -334,7 +335,8 @@ add cheese type mozzarella
|
|
|
334
335
|
```
|
|
335
336
|
|
|
336
337
|
Options with an optional option-argument are not greedy and will ignore arguments starting with a dash.
|
|
337
|
-
So `id` behaves as a boolean option for `--id -
|
|
338
|
+
So `id` behaves as a boolean option for `--id -ABCD`, but you can use a combined form if needed like `--id=-ABCD`.
|
|
339
|
+
Negative numbers are special and are accepted as an option-argument.
|
|
338
340
|
|
|
339
341
|
For information about possible ambiguous cases, see [options taking varying arguments](./docs/options-in-depth.md).
|
|
340
342
|
|
|
@@ -926,6 +928,14 @@ program.helpCommand('assist [command]', 'show assistance');
|
|
|
926
928
|
|
|
927
929
|
(Or use `.addHelpCommand()` to add a command you construct yourself.)
|
|
928
930
|
|
|
931
|
+
### Help Groups
|
|
932
|
+
|
|
933
|
+
The help by default lists options under the the heading `Options:` and commands under `Commands:`. You can create your own groups
|
|
934
|
+
with different headings. The high-level way is to set the desired group heading while adding the options and commands,
|
|
935
|
+
using `.optionsGroup()` and `.commandsGroup()`. The low-level way is using `.helpGroup()` on an individual `Option` or `Command`
|
|
936
|
+
|
|
937
|
+
Example file: [help-groups.js](./examples/help-groups.js)
|
|
938
|
+
|
|
929
939
|
### More configuration
|
|
930
940
|
|
|
931
941
|
The built-in help is formatted using the Help class.
|
|
@@ -933,7 +943,7 @@ You can configure the help by modifying data properties and methods using `.conf
|
|
|
933
943
|
|
|
934
944
|
Simple properties include `sortSubcommands`, `sortOptions`, and `showGlobalOptions`. You can add color using the style methods like `styleTitle()`.
|
|
935
945
|
|
|
936
|
-
For more detail and examples of changing the displayed text, color, and layout see (./docs/help-in-depth.md)
|
|
946
|
+
For more detail and examples of changing the displayed text, color, and layout see [help in depth](./docs/help-in-depth.md).
|
|
937
947
|
|
|
938
948
|
## Custom event listeners
|
|
939
949
|
|
|
@@ -999,8 +1009,8 @@ program arg --port=80
|
|
|
999
1009
|
|
|
1000
1010
|
By default, the option processing shows an error for an unknown option. To have an unknown option treated as an ordinary command-argument and continue looking for options, use `.allowUnknownOption()`. This lets you mix known and unknown options.
|
|
1001
1011
|
|
|
1002
|
-
By default, the argument processing
|
|
1003
|
-
To
|
|
1012
|
+
By default, the argument processing displays an error for more command-arguments than expected.
|
|
1013
|
+
To suppress the error for excess arguments, use`.allowExcessArguments()`.
|
|
1004
1014
|
|
|
1005
1015
|
### Legacy options as properties
|
|
1006
1016
|
|
|
@@ -1053,7 +1063,7 @@ customise the new subcommand (example file [custom-command-class.js](./examples/
|
|
|
1053
1063
|
You can enable `--harmony` option in two ways:
|
|
1054
1064
|
|
|
1055
1065
|
- Use `#! /usr/bin/env node --harmony` in the subcommands scripts. (Note Windows does not support this pattern.)
|
|
1056
|
-
- Use the `--harmony` option when
|
|
1066
|
+
- Use the `--harmony` option when calling the command, like `node --harmony examples/pm publish`. The `--harmony` option will be preserved when spawning subcommand processes.
|
|
1057
1067
|
|
|
1058
1068
|
### Debugging stand-alone executable subcommands
|
|
1059
1069
|
|
|
@@ -1137,7 +1147,7 @@ There is more information available about:
|
|
|
1137
1147
|
|
|
1138
1148
|
## Support
|
|
1139
1149
|
|
|
1140
|
-
The current version of Commander is fully supported on Long Term Support versions of Node.js, and requires at least
|
|
1150
|
+
The current version of Commander is fully supported on Long Term Support versions of Node.js, and requires at least v20.
|
|
1141
1151
|
(For older versions of Node.js, use an older version of Commander.)
|
|
1142
1152
|
|
|
1143
1153
|
The main forum for free and community support is the project [Issues](https://github.com/tj/commander.js/issues) on GitHub.
|
package/lib/argument.js
CHANGED
|
@@ -33,7 +33,7 @@ class Argument {
|
|
|
33
33
|
break;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
if (this._name.
|
|
36
|
+
if (this._name.endsWith('...')) {
|
|
37
37
|
this.variadic = true;
|
|
38
38
|
this._name = this._name.slice(0, -3);
|
|
39
39
|
}
|
|
@@ -53,12 +53,13 @@ class Argument {
|
|
|
53
53
|
* @package
|
|
54
54
|
*/
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
_collectValue(value, previous) {
|
|
57
57
|
if (previous === this.defaultValue || !Array.isArray(previous)) {
|
|
58
58
|
return [value];
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
previous.push(value);
|
|
62
|
+
return previous;
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
/**
|
|
@@ -103,7 +104,7 @@ class Argument {
|
|
|
103
104
|
);
|
|
104
105
|
}
|
|
105
106
|
if (this.variadic) {
|
|
106
|
-
return this.
|
|
107
|
+
return this._collectValue(arg, previous);
|
|
107
108
|
}
|
|
108
109
|
return arg;
|
|
109
110
|
};
|
package/lib/command.js
CHANGED
|
@@ -80,6 +80,12 @@ class Command extends EventEmitter {
|
|
|
80
80
|
/** @type {Command} */
|
|
81
81
|
this._helpCommand = undefined; // lazy initialised, inherited
|
|
82
82
|
this._helpConfiguration = {};
|
|
83
|
+
/** @type {string | undefined} */
|
|
84
|
+
this._helpGroupHeading = undefined; // soft initialised when added to parent
|
|
85
|
+
/** @type {string | undefined} */
|
|
86
|
+
this._defaultCommandGroup = undefined;
|
|
87
|
+
/** @type {string | undefined} */
|
|
88
|
+
this._defaultOptionGroup = undefined;
|
|
83
89
|
}
|
|
84
90
|
|
|
85
91
|
/**
|
|
@@ -239,7 +245,10 @@ class Command extends EventEmitter {
|
|
|
239
245
|
configureOutput(configuration) {
|
|
240
246
|
if (configuration === undefined) return this._outputConfiguration;
|
|
241
247
|
|
|
242
|
-
|
|
248
|
+
this._outputConfiguration = {
|
|
249
|
+
...this._outputConfiguration,
|
|
250
|
+
...configuration,
|
|
251
|
+
};
|
|
243
252
|
return this;
|
|
244
253
|
}
|
|
245
254
|
|
|
@@ -320,16 +329,16 @@ class Command extends EventEmitter {
|
|
|
320
329
|
*
|
|
321
330
|
* @param {string} name
|
|
322
331
|
* @param {string} [description]
|
|
323
|
-
* @param {(Function|*)} [
|
|
332
|
+
* @param {(Function|*)} [parseArg] - custom argument processing function or default value
|
|
324
333
|
* @param {*} [defaultValue]
|
|
325
334
|
* @return {Command} `this` command for chaining
|
|
326
335
|
*/
|
|
327
|
-
argument(name, description,
|
|
336
|
+
argument(name, description, parseArg, defaultValue) {
|
|
328
337
|
const argument = this.createArgument(name, description);
|
|
329
|
-
if (typeof
|
|
330
|
-
argument.default(defaultValue).argParser(
|
|
338
|
+
if (typeof parseArg === 'function') {
|
|
339
|
+
argument.default(defaultValue).argParser(parseArg);
|
|
331
340
|
} else {
|
|
332
|
-
argument.default(
|
|
341
|
+
argument.default(parseArg);
|
|
333
342
|
}
|
|
334
343
|
this.addArgument(argument);
|
|
335
344
|
return this;
|
|
@@ -365,7 +374,7 @@ class Command extends EventEmitter {
|
|
|
365
374
|
*/
|
|
366
375
|
addArgument(argument) {
|
|
367
376
|
const previousArgument = this.registeredArguments.slice(-1)[0];
|
|
368
|
-
if (previousArgument
|
|
377
|
+
if (previousArgument?.variadic) {
|
|
369
378
|
throw new Error(
|
|
370
379
|
`only the last argument can be variadic '${previousArgument.name()}'`,
|
|
371
380
|
);
|
|
@@ -400,11 +409,15 @@ class Command extends EventEmitter {
|
|
|
400
409
|
helpCommand(enableOrNameAndArgs, description) {
|
|
401
410
|
if (typeof enableOrNameAndArgs === 'boolean') {
|
|
402
411
|
this._addImplicitHelpCommand = enableOrNameAndArgs;
|
|
412
|
+
if (enableOrNameAndArgs && this._defaultCommandGroup) {
|
|
413
|
+
// make the command to store the group
|
|
414
|
+
this._initCommandGroup(this._getHelpCommand());
|
|
415
|
+
}
|
|
403
416
|
return this;
|
|
404
417
|
}
|
|
405
418
|
|
|
406
|
-
|
|
407
|
-
const [, helpName, helpArgs] =
|
|
419
|
+
const nameAndArgs = enableOrNameAndArgs ?? 'help [command]';
|
|
420
|
+
const [, helpName, helpArgs] = nameAndArgs.match(/([^ ]+) *(.*)/);
|
|
408
421
|
const helpDescription = description ?? 'display help for command';
|
|
409
422
|
|
|
410
423
|
const helpCommand = this.createCommand(helpName);
|
|
@@ -414,6 +427,8 @@ class Command extends EventEmitter {
|
|
|
414
427
|
|
|
415
428
|
this._addImplicitHelpCommand = true;
|
|
416
429
|
this._helpCommand = helpCommand;
|
|
430
|
+
// init group unless lazy create
|
|
431
|
+
if (enableOrNameAndArgs || description) this._initCommandGroup(helpCommand);
|
|
417
432
|
|
|
418
433
|
return this;
|
|
419
434
|
}
|
|
@@ -435,6 +450,7 @@ class Command extends EventEmitter {
|
|
|
435
450
|
|
|
436
451
|
this._addImplicitHelpCommand = true;
|
|
437
452
|
this._helpCommand = helpCommand;
|
|
453
|
+
this._initCommandGroup(helpCommand);
|
|
438
454
|
return this;
|
|
439
455
|
}
|
|
440
456
|
|
|
@@ -613,6 +629,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
613
629
|
- already used by option '${matchingOption.flags}'`);
|
|
614
630
|
}
|
|
615
631
|
|
|
632
|
+
this._initOptionGroup(option);
|
|
616
633
|
this.options.push(option);
|
|
617
634
|
}
|
|
618
635
|
|
|
@@ -640,6 +657,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
640
657
|
);
|
|
641
658
|
}
|
|
642
659
|
|
|
660
|
+
this._initCommandGroup(command);
|
|
643
661
|
this.commands.push(command);
|
|
644
662
|
}
|
|
645
663
|
|
|
@@ -683,7 +701,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
683
701
|
if (val !== null && option.parseArg) {
|
|
684
702
|
val = this._callParseArg(option, val, oldValue, invalidValueMessage);
|
|
685
703
|
} else if (val !== null && option.variadic) {
|
|
686
|
-
val = option.
|
|
704
|
+
val = option._collectValue(val, oldValue);
|
|
687
705
|
}
|
|
688
706
|
|
|
689
707
|
// Fill-in appropriate missing values. Long winded but easy to follow.
|
|
@@ -1462,7 +1480,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1462
1480
|
|
|
1463
1481
|
_chainOrCall(promise, fn) {
|
|
1464
1482
|
// thenable
|
|
1465
|
-
if (promise
|
|
1483
|
+
if (promise?.then && typeof promise.then === 'function') {
|
|
1466
1484
|
// already have a promise, chain callback
|
|
1467
1485
|
return promise.then(() => fn());
|
|
1468
1486
|
}
|
|
@@ -1593,7 +1611,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1593
1611
|
promiseChain = this._chainOrCallHooks(promiseChain, 'postAction');
|
|
1594
1612
|
return promiseChain;
|
|
1595
1613
|
}
|
|
1596
|
-
if (this.parent
|
|
1614
|
+
if (this.parent?.listenerCount(commandEvent)) {
|
|
1597
1615
|
checkForUnknownOptions();
|
|
1598
1616
|
this._processArguments();
|
|
1599
1617
|
this.parent.emit(commandEvent, operands, unknown); // legacy
|
|
@@ -1723,33 +1741,49 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1723
1741
|
* sub --unknown uuu op => [sub], [--unknown uuu op]
|
|
1724
1742
|
* sub -- --unknown uuu op => [sub --unknown uuu op], []
|
|
1725
1743
|
*
|
|
1726
|
-
* @param {string[]}
|
|
1744
|
+
* @param {string[]} args
|
|
1727
1745
|
* @return {{operands: string[], unknown: string[]}}
|
|
1728
1746
|
*/
|
|
1729
1747
|
|
|
1730
|
-
parseOptions(
|
|
1748
|
+
parseOptions(args) {
|
|
1731
1749
|
const operands = []; // operands, not options or values
|
|
1732
1750
|
const unknown = []; // first unknown option and remaining unknown args
|
|
1733
1751
|
let dest = operands;
|
|
1734
|
-
const args = argv.slice();
|
|
1735
1752
|
|
|
1736
1753
|
function maybeOption(arg) {
|
|
1737
1754
|
return arg.length > 1 && arg[0] === '-';
|
|
1738
1755
|
}
|
|
1739
1756
|
|
|
1757
|
+
const negativeNumberArg = (arg) => {
|
|
1758
|
+
// return false if not a negative number
|
|
1759
|
+
if (!/^-\d*\.?\d+(e[+-]?\d+)?$/.test(arg)) return false;
|
|
1760
|
+
// negative number is ok unless digit used as an option in command hierarchy
|
|
1761
|
+
return !this._getCommandAndAncestors().some((cmd) =>
|
|
1762
|
+
cmd.options
|
|
1763
|
+
.map((opt) => opt.short)
|
|
1764
|
+
.some((short) => /^-\d$/.test(short)),
|
|
1765
|
+
);
|
|
1766
|
+
};
|
|
1767
|
+
|
|
1740
1768
|
// parse options
|
|
1741
1769
|
let activeVariadicOption = null;
|
|
1742
|
-
|
|
1743
|
-
|
|
1770
|
+
let activeGroup = null; // working through group of short options, like -abc
|
|
1771
|
+
let i = 0;
|
|
1772
|
+
while (i < args.length || activeGroup) {
|
|
1773
|
+
const arg = activeGroup ?? args[i++];
|
|
1774
|
+
activeGroup = null;
|
|
1744
1775
|
|
|
1745
1776
|
// literal
|
|
1746
1777
|
if (arg === '--') {
|
|
1747
1778
|
if (dest === unknown) dest.push(arg);
|
|
1748
|
-
dest.push(...args);
|
|
1779
|
+
dest.push(...args.slice(i));
|
|
1749
1780
|
break;
|
|
1750
1781
|
}
|
|
1751
1782
|
|
|
1752
|
-
if (
|
|
1783
|
+
if (
|
|
1784
|
+
activeVariadicOption &&
|
|
1785
|
+
(!maybeOption(arg) || negativeNumberArg(arg))
|
|
1786
|
+
) {
|
|
1753
1787
|
this.emit(`option:${activeVariadicOption.name()}`, arg);
|
|
1754
1788
|
continue;
|
|
1755
1789
|
}
|
|
@@ -1760,14 +1794,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1760
1794
|
// recognised option, call listener to assign value with possible custom processing
|
|
1761
1795
|
if (option) {
|
|
1762
1796
|
if (option.required) {
|
|
1763
|
-
const value = args
|
|
1797
|
+
const value = args[i++];
|
|
1764
1798
|
if (value === undefined) this.optionMissingArgument(option);
|
|
1765
1799
|
this.emit(`option:${option.name()}`, value);
|
|
1766
1800
|
} else if (option.optional) {
|
|
1767
1801
|
let value = null;
|
|
1768
1802
|
// historical behaviour is optional value is following arg unless an option
|
|
1769
|
-
if (
|
|
1770
|
-
|
|
1803
|
+
if (
|
|
1804
|
+
i < args.length &&
|
|
1805
|
+
(!maybeOption(args[i]) || negativeNumberArg(args[i]))
|
|
1806
|
+
) {
|
|
1807
|
+
value = args[i++];
|
|
1771
1808
|
}
|
|
1772
1809
|
this.emit(`option:${option.name()}`, value);
|
|
1773
1810
|
} else {
|
|
@@ -1790,9 +1827,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1790
1827
|
// option with value following in same argument
|
|
1791
1828
|
this.emit(`option:${option.name()}`, arg.slice(2));
|
|
1792
1829
|
} else {
|
|
1793
|
-
// boolean option
|
|
1830
|
+
// boolean option
|
|
1794
1831
|
this.emit(`option:${option.name()}`);
|
|
1795
|
-
|
|
1832
|
+
// remove the processed option and keep processing group
|
|
1833
|
+
activeGroup = `-${arg.slice(2)}`;
|
|
1796
1834
|
}
|
|
1797
1835
|
continue;
|
|
1798
1836
|
}
|
|
@@ -1812,7 +1850,12 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1812
1850
|
// Might be a command-argument, or subcommand option, or unknown option, or help command or option.
|
|
1813
1851
|
|
|
1814
1852
|
// An unknown option means further arguments also classified as unknown so can be reprocessed by subcommands.
|
|
1815
|
-
|
|
1853
|
+
// A negative number in a leaf command is not an unknown option.
|
|
1854
|
+
if (
|
|
1855
|
+
dest === operands &&
|
|
1856
|
+
maybeOption(arg) &&
|
|
1857
|
+
!(this.commands.length === 0 && negativeNumberArg(arg))
|
|
1858
|
+
) {
|
|
1816
1859
|
dest = unknown;
|
|
1817
1860
|
}
|
|
1818
1861
|
|
|
@@ -1824,26 +1867,23 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1824
1867
|
) {
|
|
1825
1868
|
if (this._findCommand(arg)) {
|
|
1826
1869
|
operands.push(arg);
|
|
1827
|
-
|
|
1870
|
+
unknown.push(...args.slice(i));
|
|
1828
1871
|
break;
|
|
1829
1872
|
} else if (
|
|
1830
1873
|
this._getHelpCommand() &&
|
|
1831
1874
|
arg === this._getHelpCommand().name()
|
|
1832
1875
|
) {
|
|
1833
|
-
operands.push(arg);
|
|
1834
|
-
if (args.length > 0) operands.push(...args);
|
|
1876
|
+
operands.push(arg, ...args.slice(i));
|
|
1835
1877
|
break;
|
|
1836
1878
|
} else if (this._defaultCommandName) {
|
|
1837
|
-
unknown.push(arg);
|
|
1838
|
-
if (args.length > 0) unknown.push(...args);
|
|
1879
|
+
unknown.push(arg, ...args.slice(i));
|
|
1839
1880
|
break;
|
|
1840
1881
|
}
|
|
1841
1882
|
}
|
|
1842
1883
|
|
|
1843
1884
|
// If using passThroughOptions, stop processing options at first command-argument.
|
|
1844
1885
|
if (this._passThroughOptions) {
|
|
1845
|
-
dest.push(arg);
|
|
1846
|
-
if (args.length > 0) dest.push(...args);
|
|
1886
|
+
dest.push(arg, ...args.slice(i));
|
|
1847
1887
|
break;
|
|
1848
1888
|
}
|
|
1849
1889
|
|
|
@@ -2294,6 +2334,75 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2294
2334
|
return this;
|
|
2295
2335
|
}
|
|
2296
2336
|
|
|
2337
|
+
/**
|
|
2338
|
+
* Set/get the help group heading for this subcommand in parent command's help.
|
|
2339
|
+
*
|
|
2340
|
+
* @param {string} [heading]
|
|
2341
|
+
* @return {Command | string}
|
|
2342
|
+
*/
|
|
2343
|
+
|
|
2344
|
+
helpGroup(heading) {
|
|
2345
|
+
if (heading === undefined) return this._helpGroupHeading ?? '';
|
|
2346
|
+
this._helpGroupHeading = heading;
|
|
2347
|
+
return this;
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2350
|
+
/**
|
|
2351
|
+
* Set/get the default help group heading for subcommands added to this command.
|
|
2352
|
+
* (This does not override a group set directly on the subcommand using .helpGroup().)
|
|
2353
|
+
*
|
|
2354
|
+
* @example
|
|
2355
|
+
* program.commandsGroup('Development Commands:);
|
|
2356
|
+
* program.command('watch')...
|
|
2357
|
+
* program.command('lint')...
|
|
2358
|
+
* ...
|
|
2359
|
+
*
|
|
2360
|
+
* @param {string} [heading]
|
|
2361
|
+
* @returns {Command | string}
|
|
2362
|
+
*/
|
|
2363
|
+
commandsGroup(heading) {
|
|
2364
|
+
if (heading === undefined) return this._defaultCommandGroup ?? '';
|
|
2365
|
+
this._defaultCommandGroup = heading;
|
|
2366
|
+
return this;
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
/**
|
|
2370
|
+
* Set/get the default help group heading for options added to this command.
|
|
2371
|
+
* (This does not override a group set directly on the option using .helpGroup().)
|
|
2372
|
+
*
|
|
2373
|
+
* @example
|
|
2374
|
+
* program
|
|
2375
|
+
* .optionsGroup('Development Options:')
|
|
2376
|
+
* .option('-d, --debug', 'output extra debugging')
|
|
2377
|
+
* .option('-p, --profile', 'output profiling information')
|
|
2378
|
+
*
|
|
2379
|
+
* @param {string} [heading]
|
|
2380
|
+
* @returns {Command | string}
|
|
2381
|
+
*/
|
|
2382
|
+
optionsGroup(heading) {
|
|
2383
|
+
if (heading === undefined) return this._defaultOptionGroup ?? '';
|
|
2384
|
+
this._defaultOptionGroup = heading;
|
|
2385
|
+
return this;
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2388
|
+
/**
|
|
2389
|
+
* @param {Option} option
|
|
2390
|
+
* @private
|
|
2391
|
+
*/
|
|
2392
|
+
_initOptionGroup(option) {
|
|
2393
|
+
if (this._defaultOptionGroup && !option.helpGroupHeading)
|
|
2394
|
+
option.helpGroup(this._defaultOptionGroup);
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2397
|
+
/**
|
|
2398
|
+
* @param {Command} cmd
|
|
2399
|
+
* @private
|
|
2400
|
+
*/
|
|
2401
|
+
_initCommandGroup(cmd) {
|
|
2402
|
+
if (this._defaultCommandGroup && !cmd.helpGroup())
|
|
2403
|
+
cmd.helpGroup(this._defaultCommandGroup);
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2297
2406
|
/**
|
|
2298
2407
|
* Set the name of the command from script filename, such as process.argv[1],
|
|
2299
2408
|
* or require.main.filename, or __filename.
|
|
@@ -2448,12 +2557,14 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2448
2557
|
*/
|
|
2449
2558
|
|
|
2450
2559
|
helpOption(flags, description) {
|
|
2451
|
-
// Support disabling built-in help option.
|
|
2560
|
+
// Support enabling/disabling built-in help option.
|
|
2452
2561
|
if (typeof flags === 'boolean') {
|
|
2453
|
-
// true is not an expected value. Do something sensible but no unit-test.
|
|
2454
|
-
// istanbul ignore if
|
|
2455
2562
|
if (flags) {
|
|
2456
|
-
this._helpOption
|
|
2563
|
+
if (this._helpOption === null) this._helpOption = undefined; // reenable
|
|
2564
|
+
if (this._defaultOptionGroup) {
|
|
2565
|
+
// make the option to store the group
|
|
2566
|
+
this._initOptionGroup(this._getHelpOption());
|
|
2567
|
+
}
|
|
2457
2568
|
} else {
|
|
2458
2569
|
this._helpOption = null; // disable
|
|
2459
2570
|
}
|
|
@@ -2461,9 +2572,12 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2461
2572
|
}
|
|
2462
2573
|
|
|
2463
2574
|
// Customise flags and description.
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2575
|
+
this._helpOption = this.createOption(
|
|
2576
|
+
flags ?? '-h, --help',
|
|
2577
|
+
description ?? 'display help for command',
|
|
2578
|
+
);
|
|
2579
|
+
// init group unless lazy create
|
|
2580
|
+
if (flags || description) this._initOptionGroup(this._helpOption);
|
|
2467
2581
|
|
|
2468
2582
|
return this;
|
|
2469
2583
|
}
|
|
@@ -2492,6 +2606,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2492
2606
|
*/
|
|
2493
2607
|
addHelpOption(option) {
|
|
2494
2608
|
this._helpOption = option;
|
|
2609
|
+
this._initOptionGroup(option);
|
|
2495
2610
|
return this;
|
|
2496
2611
|
}
|
|
2497
2612
|
|
package/lib/help.js
CHANGED
|
@@ -352,7 +352,11 @@ class Help {
|
|
|
352
352
|
extraInfo.push(`env: ${option.envVar}`);
|
|
353
353
|
}
|
|
354
354
|
if (extraInfo.length > 0) {
|
|
355
|
-
|
|
355
|
+
const extraDescription = `(${extraInfo.join(', ')})`;
|
|
356
|
+
if (option.description) {
|
|
357
|
+
return `${option.description} ${extraDescription}`;
|
|
358
|
+
}
|
|
359
|
+
return extraDescription;
|
|
356
360
|
}
|
|
357
361
|
|
|
358
362
|
return option.description;
|
|
@@ -388,6 +392,46 @@ class Help {
|
|
|
388
392
|
return argument.description;
|
|
389
393
|
}
|
|
390
394
|
|
|
395
|
+
/**
|
|
396
|
+
* Format a list of items, given a heading and an array of formatted items.
|
|
397
|
+
*
|
|
398
|
+
* @param {string} heading
|
|
399
|
+
* @param {string[]} items
|
|
400
|
+
* @param {Help} helper
|
|
401
|
+
* @returns string[]
|
|
402
|
+
*/
|
|
403
|
+
formatItemList(heading, items, helper) {
|
|
404
|
+
if (items.length === 0) return [];
|
|
405
|
+
|
|
406
|
+
return [helper.styleTitle(heading), ...items, ''];
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Group items by their help group heading.
|
|
411
|
+
*
|
|
412
|
+
* @param {Command[] | Option[]} unsortedItems
|
|
413
|
+
* @param {Command[] | Option[]} visibleItems
|
|
414
|
+
* @param {Function} getGroup
|
|
415
|
+
* @returns {Map<string, Command[] | Option[]>}
|
|
416
|
+
*/
|
|
417
|
+
groupItems(unsortedItems, visibleItems, getGroup) {
|
|
418
|
+
const result = new Map();
|
|
419
|
+
// Add groups in order of appearance in unsortedItems.
|
|
420
|
+
unsortedItems.forEach((item) => {
|
|
421
|
+
const group = getGroup(item);
|
|
422
|
+
if (!result.has(group)) result.set(group, []);
|
|
423
|
+
});
|
|
424
|
+
// Add items in order of appearance in visibleItems.
|
|
425
|
+
visibleItems.forEach((item) => {
|
|
426
|
+
const group = getGroup(item);
|
|
427
|
+
if (!result.has(group)) {
|
|
428
|
+
result.set(group, []);
|
|
429
|
+
}
|
|
430
|
+
result.get(group).push(item);
|
|
431
|
+
});
|
|
432
|
+
return result;
|
|
433
|
+
}
|
|
434
|
+
|
|
391
435
|
/**
|
|
392
436
|
* Generate the built-in help text.
|
|
393
437
|
*
|
|
@@ -429,28 +473,25 @@ class Help {
|
|
|
429
473
|
helper.styleArgumentDescription(helper.argumentDescription(argument)),
|
|
430
474
|
);
|
|
431
475
|
});
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
...argumentList,
|
|
436
|
-
'',
|
|
437
|
-
]);
|
|
438
|
-
}
|
|
476
|
+
output = output.concat(
|
|
477
|
+
this.formatItemList('Arguments:', argumentList, helper),
|
|
478
|
+
);
|
|
439
479
|
|
|
440
480
|
// Options
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
481
|
+
const optionGroups = this.groupItems(
|
|
482
|
+
cmd.options,
|
|
483
|
+
helper.visibleOptions(cmd),
|
|
484
|
+
(option) => option.helpGroupHeading ?? 'Options:',
|
|
485
|
+
);
|
|
486
|
+
optionGroups.forEach((options, group) => {
|
|
487
|
+
const optionList = options.map((option) => {
|
|
488
|
+
return callFormatItem(
|
|
489
|
+
helper.styleOptionTerm(helper.optionTerm(option)),
|
|
490
|
+
helper.styleOptionDescription(helper.optionDescription(option)),
|
|
491
|
+
);
|
|
492
|
+
});
|
|
493
|
+
output = output.concat(this.formatItemList(group, optionList, helper));
|
|
446
494
|
});
|
|
447
|
-
if (optionList.length > 0) {
|
|
448
|
-
output = output.concat([
|
|
449
|
-
helper.styleTitle('Options:'),
|
|
450
|
-
...optionList,
|
|
451
|
-
'',
|
|
452
|
-
]);
|
|
453
|
-
}
|
|
454
495
|
|
|
455
496
|
if (helper.showGlobalOptions) {
|
|
456
497
|
const globalOptionList = helper
|
|
@@ -461,29 +502,26 @@ class Help {
|
|
|
461
502
|
helper.styleOptionDescription(helper.optionDescription(option)),
|
|
462
503
|
);
|
|
463
504
|
});
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
...globalOptionList,
|
|
468
|
-
'',
|
|
469
|
-
]);
|
|
470
|
-
}
|
|
505
|
+
output = output.concat(
|
|
506
|
+
this.formatItemList('Global Options:', globalOptionList, helper),
|
|
507
|
+
);
|
|
471
508
|
}
|
|
472
509
|
|
|
473
510
|
// Commands
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
511
|
+
const commandGroups = this.groupItems(
|
|
512
|
+
cmd.commands,
|
|
513
|
+
helper.visibleCommands(cmd),
|
|
514
|
+
(sub) => sub.helpGroup() || 'Commands:',
|
|
515
|
+
);
|
|
516
|
+
commandGroups.forEach((commands, group) => {
|
|
517
|
+
const commandList = commands.map((sub) => {
|
|
518
|
+
return callFormatItem(
|
|
519
|
+
helper.styleSubcommandTerm(helper.subcommandTerm(sub)),
|
|
520
|
+
helper.styleSubcommandDescription(helper.subcommandDescription(sub)),
|
|
521
|
+
);
|
|
522
|
+
});
|
|
523
|
+
output = output.concat(this.formatItemList(group, commandList, helper));
|
|
479
524
|
});
|
|
480
|
-
if (commandList.length > 0) {
|
|
481
|
-
output = output.concat([
|
|
482
|
-
helper.styleTitle('Commands:'),
|
|
483
|
-
...commandList,
|
|
484
|
-
'',
|
|
485
|
-
]);
|
|
486
|
-
}
|
|
487
525
|
|
|
488
526
|
return output.join('\n');
|
|
489
527
|
}
|
package/lib/option.js
CHANGED
|
@@ -33,6 +33,7 @@ class Option {
|
|
|
33
33
|
this.argChoices = undefined;
|
|
34
34
|
this.conflictsWith = [];
|
|
35
35
|
this.implied = undefined;
|
|
36
|
+
this.helpGroupHeading = undefined; // soft initialised when option added to command
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
/**
|
|
@@ -161,12 +162,13 @@ class Option {
|
|
|
161
162
|
* @package
|
|
162
163
|
*/
|
|
163
164
|
|
|
164
|
-
|
|
165
|
+
_collectValue(value, previous) {
|
|
165
166
|
if (previous === this.defaultValue || !Array.isArray(previous)) {
|
|
166
167
|
return [value];
|
|
167
168
|
}
|
|
168
169
|
|
|
169
|
-
|
|
170
|
+
previous.push(value);
|
|
171
|
+
return previous;
|
|
170
172
|
}
|
|
171
173
|
|
|
172
174
|
/**
|
|
@@ -185,7 +187,7 @@ class Option {
|
|
|
185
187
|
);
|
|
186
188
|
}
|
|
187
189
|
if (this.variadic) {
|
|
188
|
-
return this.
|
|
190
|
+
return this._collectValue(arg, previous);
|
|
189
191
|
}
|
|
190
192
|
return arg;
|
|
191
193
|
};
|
|
@@ -219,6 +221,17 @@ class Option {
|
|
|
219
221
|
return camelcase(this.name());
|
|
220
222
|
}
|
|
221
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Set the help group heading.
|
|
226
|
+
*
|
|
227
|
+
* @param {string} heading
|
|
228
|
+
* @return {Option}
|
|
229
|
+
*/
|
|
230
|
+
helpGroup(heading) {
|
|
231
|
+
this.helpGroupHeading = heading;
|
|
232
|
+
return this;
|
|
233
|
+
}
|
|
234
|
+
|
|
222
235
|
/**
|
|
223
236
|
* Check if `arg` matches the short or long flag.
|
|
224
237
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "commander",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "14.0.1",
|
|
4
4
|
"description": "the complete solution for node.js command-line programs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"commander",
|
|
@@ -61,22 +61,22 @@
|
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@eslint/js": "^9.4.0",
|
|
64
|
-
"@types/jest": "^
|
|
64
|
+
"@types/jest": "^30.0.0",
|
|
65
65
|
"@types/node": "^22.7.4",
|
|
66
66
|
"eslint": "^9.17.0",
|
|
67
|
-
"eslint-config-prettier": "^
|
|
68
|
-
"eslint-plugin-jest": "^
|
|
69
|
-
"globals": "^
|
|
70
|
-
"jest": "^
|
|
67
|
+
"eslint-config-prettier": "^10.0.1",
|
|
68
|
+
"eslint-plugin-jest": "^29.0.1",
|
|
69
|
+
"globals": "^16.0.0",
|
|
70
|
+
"jest": "^30.0.3",
|
|
71
71
|
"prettier": "^3.2.5",
|
|
72
72
|
"ts-jest": "^29.0.3",
|
|
73
|
-
"tsd": "^0.
|
|
73
|
+
"tsd": "^0.33.0",
|
|
74
74
|
"typescript": "^5.0.4",
|
|
75
75
|
"typescript-eslint": "^8.12.2"
|
|
76
76
|
},
|
|
77
77
|
"types": "typings/index.d.ts",
|
|
78
78
|
"engines": {
|
|
79
|
-
"node": ">=
|
|
79
|
+
"node": ">=20"
|
|
80
80
|
},
|
|
81
81
|
"support": true
|
|
82
82
|
}
|
package/typings/index.d.ts
CHANGED
|
@@ -51,6 +51,7 @@ export class Argument {
|
|
|
51
51
|
variadic: boolean;
|
|
52
52
|
defaultValue?: any;
|
|
53
53
|
defaultValueDescription?: string;
|
|
54
|
+
parseArg?: <T>(value: string, previous: T) => T;
|
|
54
55
|
argChoices?: string[];
|
|
55
56
|
|
|
56
57
|
/**
|
|
@@ -109,6 +110,7 @@ export class Option {
|
|
|
109
110
|
parseArg?: <T>(value: string, previous: T) => T;
|
|
110
111
|
hidden: boolean;
|
|
111
112
|
argChoices?: string[];
|
|
113
|
+
helpGroupHeading?: string;
|
|
112
114
|
|
|
113
115
|
constructor(flags: string, description?: string);
|
|
114
116
|
|
|
@@ -192,6 +194,11 @@ export class Option {
|
|
|
192
194
|
*/
|
|
193
195
|
attributeName(): string;
|
|
194
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Set the help group heading.
|
|
199
|
+
*/
|
|
200
|
+
helpGroup(heading: string): this;
|
|
201
|
+
|
|
195
202
|
/**
|
|
196
203
|
* Return whether a boolean option.
|
|
197
204
|
*
|
|
@@ -313,6 +320,20 @@ export class Help {
|
|
|
313
320
|
helper: Help,
|
|
314
321
|
): string;
|
|
315
322
|
|
|
323
|
+
/**
|
|
324
|
+
* Format a list of items, given a heading and an array of formatted items.
|
|
325
|
+
*/
|
|
326
|
+
formatItemList(heading: string, items: string[], helper: Help): string[];
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Group items by their help group heading.
|
|
330
|
+
*/
|
|
331
|
+
groupItems<T extends Command | Option>(
|
|
332
|
+
unsortedItems: T[],
|
|
333
|
+
visibleItems: T[],
|
|
334
|
+
getGroup: (item: T) => string,
|
|
335
|
+
): Map<string, T[]>;
|
|
336
|
+
|
|
316
337
|
/** Generate the built-in help text. */
|
|
317
338
|
formatHelp(cmd: Command, helper: Help): string;
|
|
318
339
|
}
|
|
@@ -466,7 +487,7 @@ export class Command {
|
|
|
466
487
|
argument<T>(
|
|
467
488
|
flags: string,
|
|
468
489
|
description: string,
|
|
469
|
-
|
|
490
|
+
parseArg: (value: string, previous: T) => T,
|
|
470
491
|
defaultValue?: T,
|
|
471
492
|
): this;
|
|
472
493
|
argument(name: string, description?: string, defaultValue?: unknown): this;
|
|
@@ -968,6 +989,53 @@ export class Command {
|
|
|
968
989
|
*/
|
|
969
990
|
executableDir(): string | null;
|
|
970
991
|
|
|
992
|
+
/**
|
|
993
|
+
* Set the help group heading for this subcommand in parent command's help.
|
|
994
|
+
*
|
|
995
|
+
* @returns `this` command for chaining
|
|
996
|
+
*/
|
|
997
|
+
helpGroup(heading: string): this;
|
|
998
|
+
/**
|
|
999
|
+
* Get the help group heading for this subcommand in parent command's help.
|
|
1000
|
+
*/
|
|
1001
|
+
helpGroup(): string;
|
|
1002
|
+
|
|
1003
|
+
/**
|
|
1004
|
+
* Set the default help group heading for subcommands added to this command.
|
|
1005
|
+
* (This does not override a group set directly on the subcommand using .helpGroup().)
|
|
1006
|
+
*
|
|
1007
|
+
* @example
|
|
1008
|
+
* program.commandsGroup('Development Commands:);
|
|
1009
|
+
* program.command('watch')...
|
|
1010
|
+
* program.command('lint')...
|
|
1011
|
+
* ...
|
|
1012
|
+
*
|
|
1013
|
+
* @returns `this` command for chaining
|
|
1014
|
+
*/
|
|
1015
|
+
commandsGroup(heading: string): this;
|
|
1016
|
+
/**
|
|
1017
|
+
* Get the default help group heading for subcommands added to this command.
|
|
1018
|
+
*/
|
|
1019
|
+
commandsGroup(): string;
|
|
1020
|
+
|
|
1021
|
+
/**
|
|
1022
|
+
* Set the default help group heading for options added to this command.
|
|
1023
|
+
* (This does not override a group set directly on the option using .helpGroup().)
|
|
1024
|
+
*
|
|
1025
|
+
* @example
|
|
1026
|
+
* program
|
|
1027
|
+
* .optionsGroup('Development Options:')
|
|
1028
|
+
* .option('-d, --debug', 'output extra debugging')
|
|
1029
|
+
* .option('-p, --profile', 'output profiling information')
|
|
1030
|
+
*
|
|
1031
|
+
* @returns `this` command for chaining
|
|
1032
|
+
*/
|
|
1033
|
+
optionsGroup(heading: string): this;
|
|
1034
|
+
/**
|
|
1035
|
+
* Get the default help group heading for options added to this command.
|
|
1036
|
+
*/
|
|
1037
|
+
optionsGroup(): string;
|
|
1038
|
+
|
|
971
1039
|
/**
|
|
972
1040
|
* Output help information for this command.
|
|
973
1041
|
*
|