Skip to content

Commit 2bdd631

Browse files
committed
Merge branch 'master' into release/10.x
2 parents 5f3f004 + 7a5bd06 commit 2bdd631

16 files changed

+840
-280
lines changed

CHANGELOG.md

+19-8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
88
<!-- markdownlint-disable MD024 -->
99
<!-- markdownlint-disable MD004 -->
1010

11+
## [9.5.0] (2023-01-07)
12+
13+
### Added
14+
15+
- `.getOptionValueSourceWithGlobals()` ([#1832])
16+
- `showGlobalOptions` for `.configureHelp{}` and `Help` ([#1828])
17+
1118
## [9.4.1] (2022-09-30)
1219

1320
### Fixed
@@ -93,8 +100,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
93100
- use command name as prefix for subcommand stand-alone executable name (with fallback to script name for backwards compatibility) ([#1571])
94101
- allow absolute path with `executableFile` ([#1571])
95102
- removed restriction that nested subcommands must specify `executableFile` ([#1571])
96-
- TypeScript: allow passing readonly string array to `.choices()` [(#1667)]
97-
- TypeScript: allow passing readonly string array to `.parse()`, `.parseAsync()`, `.aliases()` [(#1669)]
103+
- TypeScript: allow passing readonly string array to `.choices()` ([#1667])
104+
- TypeScript: allow passing readonly string array to `.parse()`, `.parseAsync()`, `.aliases()` ([#1669])
98105

99106
### Fixed
100107

@@ -104,7 +111,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
104111

105112
- *Breaking:* removed internal fallback to `require.main.filename` when script not known from arguments passed to `.parse()` (can supply details using `.name()`, and `.executableDir()` or `executableFile`) ([#1571])
106113

107-
108114
## [9.0.0-1] (2022-01-15)
109115

110116
(Released in 9.0.0)
@@ -286,8 +292,8 @@ program.showHelpAfterError();
286292
### Changed
287293

288294
- *Breaking:* options are stored safely by default, not as properties on the command ([#1409])
289-
- this especially affects accessing options on program, use `program.opts()`
290-
- revert behaviour with `.storeOptionsAsProperties()`
295+
- this especially affects accessing options on program, use `program.opts()`
296+
- revert behaviour with `.storeOptionsAsProperties()`
291297
- *Breaking:* action handlers are passed options and command separately ([#1409])
292298
- deprecated callback parameter to `.help()` and `.outputHelp()` (removed from README) ([#1296])
293299
- *Breaking:* errors now displayed using `process.stderr.write()` instead of `console.error()`
@@ -306,9 +312,9 @@ program.showHelpAfterError();
306312
### Deleted
307313

308314
- *Breaking:* `.passCommandToAction()` ([#1409])
309-
- no longer needed as action handler is passed options and command
315+
- no longer needed as action handler is passed options and command
310316
- *Breaking:* "extra arguments" parameter to action handler ([#1409])
311-
- if being used to detect excess arguments, there is now an error available by setting `.allowExcessArguments(false)`
317+
- if being used to detect excess arguments, there is now an error available by setting `.allowExcessArguments(false)`
312318

313319
### Migration Tips
314320

@@ -393,7 +399,7 @@ program
393399

394400
### Fixed
395401

396-
- some tests failed if directory path included a space ([1390])
402+
- some tests failed if directory path included a space ([#1390])
397403

398404
## [6.2.0] (2020-10-25)
399405

@@ -1065,6 +1071,7 @@ program
10651071
[#1490]: https://github.com/tj/commander.js/pull/1490
10661072
[#1497]: https://github.com/tj/commander.js/pull/1497
10671073
[#1500]: https://github.com/tj/commander.js/pull/1500
1074+
[#1502]: https://github.com/tj/commander.js/pull/1502
10681075
[#1508]: https://github.com/tj/commander.js/pull/1508
10691076
[#1513]: https://github.com/tj/commander.js/pull/1513
10701077
[#1514]: https://github.com/tj/commander.js/pull/1514
@@ -1109,6 +1116,8 @@ program
11091116
[#1767]: https://github.com/tj/commander.js/pull/1767
11101117
[#1794]: https://github.com/tj/commander.js/pull/1794
11111118
[#1795]: https://github.com/tj/commander.js/pull/1795
1119+
[#1832]: https://github.com/tj/commander.js/pull/1832
1120+
[#1828]: https://github.com/tj/commander.js/pull/1828
11121121

11131122
<!-- Referenced in 5.x -->
11141123
[#1]: https://github.com/tj/commander.js/issues/1
@@ -1149,6 +1158,7 @@ program
11491158
[#1248]: https://github.com/tj/commander.js/pull/1248
11501159

11511160
<!-- Referenced in 4.x -->
1161+
[#933]: https://github.com/tj/commander.js/pull/933
11521162
[#1027]: https://github.com/tj/commander.js/pull/1027
11531163
[#1035]: https://github.com/tj/commander.js/pull/1035
11541164
[#1040]: https://github.com/tj/commander.js/pull/1040
@@ -1187,6 +1197,7 @@ program
11871197
[#1028]: https://github.com/tj/commander.js/pull/1028
11881198

11891199
[Unreleased]: https://github.com/tj/commander.js/compare/master...develop
1200+
[9.5.0]: https://github.com/tj/commander.js/compare/v9.4.1...v9.5.0
11901201
[9.4.1]: https://github.com/tj/commander.js/compare/v9.4.0...v9.4.1
11911202
[9.4.0]: https://github.com/tj/commander.js/compare/v9.3.0...v9.4.0
11921203
[9.3.0]: https://github.com/tj/commander.js/compare/v9.2.0...v9.3.0

Readme.md

+3
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,8 @@ Configuration options can be passed with the call to `.command()` and `.addComma
543543
remove the command from the generated help output. Specifying `isDefault: true` will run the subcommand if no other
544544
subcommand is specified ([example](./examples/defaultCommand.js)).
545545

546+
You can add alternative names for a command with `.alias()`. ([example](./examples/alias.js))
547+
546548
For safety, `.addCommand()` does not automatically copy the inherited settings from the parent command. There is a helper routine `.copyInheritedSettings()` for copying the settings when they are wanted.
547549

548550
### Command-arguments
@@ -915,6 +917,7 @@ The data properties are:
915917
- `helpWidth`: specify the wrap width, useful for unit tests
916918
- `sortSubcommands`: sort the subcommands alphabetically
917919
- `sortOptions`: sort the options alphabetically
920+
- `showGlobalOptions`: show a section with the global options from the parent command(s)
918921

919922
There are methods getting the visible lists of arguments, options, and subcommands. There are methods for formatting the items in the lists, with each item having a _term_ and _description_. Take a look at `.formatHelp()` to see how they are used.
920923

examples/alias.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/usr/bin/env node
2+
3+
// This example shows giving alternative names for a command.
4+
5+
// const { Command } = require('commander'); // (normal include)
6+
const { Command } = require('../'); // include commander in git clone of commander repo
7+
const program = new Command();
8+
9+
program
10+
.command('exec')
11+
.argument('<script>')
12+
.alias('ex')
13+
.action((script) => {
14+
console.log(`execute: ${script}`);
15+
});
16+
17+
program
18+
.command('print')
19+
.argument('<file>')
20+
// Multiple aliases is unusual but supported! You can call alias multiple times,
21+
// and/or add multiple aliases at once. Only the first alias is displayed in the help.
22+
.alias('p')
23+
.alias('pr')
24+
.aliases(['display', 'show'])
25+
.action((file) => {
26+
console.log(`print: ${file}`);
27+
});
28+
29+
program.parse();
30+
31+
// Try the following:
32+
// node alias.js --help
33+
// node alias.js exec script
34+
// node alias.js ex script
35+
// node alias.js print file
36+
// node alias.js pr file
37+
// node alias.js show file

examples/global-options.js renamed to examples/global-options-added.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// The code in this example assumes there is just one level of subcommands.
88
//
99
// (A different pattern for a "global" option is to add it to the root command, rather
10-
// than to the subcommand. That is not shown here.)
10+
// than to the subcommand. See global-options-nested.js.)
1111

1212
// const { Command } = require('commander'); // (normal include)
1313
const { Command } = require('../'); // include commander in git clone of commander repo
@@ -45,7 +45,7 @@ program.commands.forEach((cmd) => {
4545
program.parse();
4646

4747
// Try the following:
48-
// node common-options.js --help
49-
// node common-options.js print --help
50-
// node common-options.js serve --help
51-
// node common-options.js serve --debug --verbose
48+
// node global-options-added.js --help
49+
// node global-options-added.js print --help
50+
// node global-options-added.js serve --help
51+
// node global-options-added.js serve --debug --verbose

examples/global-options-nested.js

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/usr/bin/env node
2+
3+
// This example shows global options on the program which affect all the subcommands.
4+
// See how to work with global options in the subcommand and display them in the help.
5+
//
6+
// (A different pattern for a "global" option is to add it to the subcommands, rather
7+
// than to the program. See global-options-added.js.)
8+
9+
// const { Command } = require('commander'); // (normal include)
10+
const { Command } = require('../'); // include commander in git clone of commander repo
11+
12+
const program = new Command();
13+
14+
program
15+
.configureHelp({ showGlobalOptions: true })
16+
.option('-g, --global');
17+
18+
program
19+
.command('sub')
20+
.option('-l, --local')
21+
.action((options, cmd) => {
22+
console.log({
23+
opts: cmd.opts(),
24+
optsWithGlobals: cmd.optsWithGlobals()
25+
});
26+
});
27+
28+
program.parse();
29+
30+
// Try the following:
31+
// node global-options-nested.js --global sub --local
32+
// node global-options-nested.js sub --help

examples/optsWithGlobals.js

-24
This file was deleted.

lib/command.js

+19
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,25 @@ Expecting one of '${allowedValues.join("', '")}'`);
814814
return this._optionValueSources[key];
815815
}
816816

817+
/**
818+
* Get source of option value. See also .optsWithGlobals().
819+
* Expected values are default | config | env | cli | implied
820+
*
821+
* @param {string} key
822+
* @return {string}
823+
*/
824+
825+
getOptionValueSourceWithGlobals(key) {
826+
// global overwrites local, like optsWithGlobals
827+
let source;
828+
getCommandAndParents(this).forEach((cmd) => {
829+
if (cmd.getOptionValueSource(key) !== undefined) {
830+
source = cmd.getOptionValueSource(key);
831+
}
832+
});
833+
return source;
834+
}
835+
817836
/**
818837
* Get user arguments from implied or explicit arguments.
819838
* Side-effects: set _scriptPath if args included script. Used for default program name, and subcommand searches.

lib/help.js

+62-7
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class Help {
1616
this.helpWidth = undefined;
1717
this.sortSubcommands = false;
1818
this.sortOptions = false;
19+
this.showGlobalOptions = false;
1920
}
2021

2122
/**
@@ -45,6 +46,21 @@ class Help {
4546
return visibleCommands;
4647
}
4748

49+
/**
50+
* Compare options for sort.
51+
*
52+
* @param {Option} a
53+
* @param {Option} b
54+
* @returns number
55+
*/
56+
compareOptions(a, b) {
57+
const getSortKey = (option) => {
58+
// WYSIWYG for order displayed in help. Short used for comparison if present. No special handling for negated.
59+
return option.short ? option.short.replace(/^-/, '') : option.long.replace(/^--/, '');
60+
};
61+
return getSortKey(a).localeCompare(getSortKey(b));
62+
}
63+
4864
/**
4965
* Get an array of the visible options. Includes a placeholder for the implicit help option, if there is one.
5066
*
@@ -69,17 +85,32 @@ class Help {
6985
visibleOptions.push(helpOption);
7086
}
7187
if (this.sortOptions) {
72-
const getSortKey = (option) => {
73-
// WYSIWYG for order displayed in help with short before long, no special handling for negated.
74-
return option.short ? option.short.replace(/^-/, '') : option.long.replace(/^--/, '');
75-
};
76-
visibleOptions.sort((a, b) => {
77-
return getSortKey(a).localeCompare(getSortKey(b));
78-
});
88+
visibleOptions.sort(this.compareOptions);
7989
}
8090
return visibleOptions;
8191
}
8292

93+
/**
94+
* Get an array of the visible global options. (Not including help.)
95+
*
96+
* @param {Command} cmd
97+
* @returns {Option[]}
98+
*/
99+
100+
visibleGlobalOptions(cmd) {
101+
if (!this.showGlobalOptions) return [];
102+
103+
const globalOptions = [];
104+
for (let parentCmd = cmd.parent; parentCmd; parentCmd = parentCmd.parent) {
105+
const visibleOptions = parentCmd.options.filter((option) => !option.hidden);
106+
globalOptions.push(...visibleOptions);
107+
}
108+
if (this.sortOptions) {
109+
globalOptions.sort(this.compareOptions);
110+
}
111+
return globalOptions;
112+
}
113+
83114
/**
84115
* Get an array of the arguments if any have a description.
85116
*
@@ -168,6 +199,20 @@ class Help {
168199
}, 0);
169200
}
170201

202+
/**
203+
* Get the longest global option term length.
204+
*
205+
* @param {Command} cmd
206+
* @param {Help} helper
207+
* @returns {number}
208+
*/
209+
210+
longestGlobalOptionTermLength(cmd, helper) {
211+
return helper.visibleGlobalOptions(cmd).reduce((max, option) => {
212+
return Math.max(max, helper.optionTerm(option).length);
213+
}, 0);
214+
}
215+
171216
/**
172217
* Get the longest argument term length.
173218
*
@@ -341,6 +386,15 @@ class Help {
341386
output = output.concat(['Options:', formatList(optionList), '']);
342387
}
343388

389+
if (this.showGlobalOptions) {
390+
const globalOptionList = helper.visibleGlobalOptions(cmd).map((option) => {
391+
return formatItem(helper.optionTerm(option), helper.optionDescription(option));
392+
});
393+
if (globalOptionList.length > 0) {
394+
output = output.concat(['Global Options:', formatList(globalOptionList), '']);
395+
}
396+
}
397+
344398
// Commands
345399
const commandList = helper.visibleCommands(cmd).map((cmd) => {
346400
return formatItem(helper.subcommandTerm(cmd), helper.subcommandDescription(cmd));
@@ -363,6 +417,7 @@ class Help {
363417
padWidth(cmd, helper) {
364418
return Math.max(
365419
helper.longestOptionTermLength(cmd, helper),
420+
helper.longestGlobalOptionTermLength(cmd, helper),
366421
helper.longestSubcommandTermLength(cmd, helper),
367422
helper.longestArgumentTermLength(cmd, helper)
368423
);

0 commit comments

Comments
 (0)