Skip to content

Commit

Permalink
Merge branch 'master' into release/10.x
Browse files Browse the repository at this point in the history
  • Loading branch information
shadowspawn committed Jan 9, 2023
2 parents 5f3f004 + 7a5bd06 commit 2bdd631
Show file tree
Hide file tree
Showing 16 changed files with 840 additions and 280 deletions.
27 changes: 19 additions & 8 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
<!-- markdownlint-disable MD024 -->
<!-- markdownlint-disable MD004 -->

## [9.5.0] (2023-01-07)

### Added

- `.getOptionValueSourceWithGlobals()` ([#1832])
- `showGlobalOptions` for `.configureHelp{}` and `Help` ([#1828])

## [9.4.1] (2022-09-30)

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

### Fixed

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

- *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])


## [9.0.0-1] (2022-01-15)

(Released in 9.0.0)
Expand Down Expand Up @@ -286,8 +292,8 @@ program.showHelpAfterError();
### Changed

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

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

### Migration Tips

Expand Down Expand Up @@ -393,7 +399,7 @@ program

### Fixed

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

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

Expand Down Expand Up @@ -1065,6 +1071,7 @@ program
[#1490]: https://github.com/tj/commander.js/pull/1490
[#1497]: https://github.com/tj/commander.js/pull/1497
[#1500]: https://github.com/tj/commander.js/pull/1500
[#1502]: https://github.com/tj/commander.js/pull/1502
[#1508]: https://github.com/tj/commander.js/pull/1508
[#1513]: https://github.com/tj/commander.js/pull/1513
[#1514]: https://github.com/tj/commander.js/pull/1514
Expand Down Expand Up @@ -1109,6 +1116,8 @@ program
[#1767]: https://github.com/tj/commander.js/pull/1767
[#1794]: https://github.com/tj/commander.js/pull/1794
[#1795]: https://github.com/tj/commander.js/pull/1795
[#1832]: https://github.com/tj/commander.js/pull/1832
[#1828]: https://github.com/tj/commander.js/pull/1828

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

<!-- Referenced in 4.x -->
[#933]: https://github.com/tj/commander.js/pull/933
[#1027]: https://github.com/tj/commander.js/pull/1027
[#1035]: https://github.com/tj/commander.js/pull/1035
[#1040]: https://github.com/tj/commander.js/pull/1040
Expand Down Expand Up @@ -1187,6 +1197,7 @@ program
[#1028]: https://github.com/tj/commander.js/pull/1028

[Unreleased]: https://github.com/tj/commander.js/compare/master...develop
[9.5.0]: https://github.com/tj/commander.js/compare/v9.4.1...v9.5.0
[9.4.1]: https://github.com/tj/commander.js/compare/v9.4.0...v9.4.1
[9.4.0]: https://github.com/tj/commander.js/compare/v9.3.0...v9.4.0
[9.3.0]: https://github.com/tj/commander.js/compare/v9.2.0...v9.3.0
Expand Down
3 changes: 3 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,8 @@ Configuration options can be passed with the call to `.command()` and `.addComma
remove the command from the generated help output. Specifying `isDefault: true` will run the subcommand if no other
subcommand is specified ([example](./examples/defaultCommand.js)).

You can add alternative names for a command with `.alias()`. ([example](./examples/alias.js))

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.

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

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.

Expand Down
37 changes: 37 additions & 0 deletions examples/alias.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env node

// This example shows giving alternative names for a command.

// const { Command } = require('commander'); // (normal include)
const { Command } = require('../'); // include commander in git clone of commander repo
const program = new Command();

program
.command('exec')
.argument('<script>')
.alias('ex')
.action((script) => {
console.log(`execute: ${script}`);
});

program
.command('print')
.argument('<file>')
// Multiple aliases is unusual but supported! You can call alias multiple times,
// and/or add multiple aliases at once. Only the first alias is displayed in the help.
.alias('p')
.alias('pr')
.aliases(['display', 'show'])
.action((file) => {
console.log(`print: ${file}`);
});

program.parse();

// Try the following:
// node alias.js --help
// node alias.js exec script
// node alias.js ex script
// node alias.js print file
// node alias.js pr file
// node alias.js show file
10 changes: 5 additions & 5 deletions examples/global-options.js → examples/global-options-added.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// The code in this example assumes there is just one level of subcommands.
//
// (A different pattern for a "global" option is to add it to the root command, rather
// than to the subcommand. That is not shown here.)
// than to the subcommand. See global-options-nested.js.)

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

// Try the following:
// node common-options.js --help
// node common-options.js print --help
// node common-options.js serve --help
// node common-options.js serve --debug --verbose
// node global-options-added.js --help
// node global-options-added.js print --help
// node global-options-added.js serve --help
// node global-options-added.js serve --debug --verbose
32 changes: 32 additions & 0 deletions examples/global-options-nested.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env node

// This example shows global options on the program which affect all the subcommands.
// See how to work with global options in the subcommand and display them in the help.
//
// (A different pattern for a "global" option is to add it to the subcommands, rather
// than to the program. See global-options-added.js.)

// const { Command } = require('commander'); // (normal include)
const { Command } = require('../'); // include commander in git clone of commander repo

const program = new Command();

program
.configureHelp({ showGlobalOptions: true })
.option('-g, --global');

program
.command('sub')
.option('-l, --local')
.action((options, cmd) => {
console.log({
opts: cmd.opts(),
optsWithGlobals: cmd.optsWithGlobals()
});
});

program.parse();

// Try the following:
// node global-options-nested.js --global sub --local
// node global-options-nested.js sub --help
24 changes: 0 additions & 24 deletions examples/optsWithGlobals.js

This file was deleted.

19 changes: 19 additions & 0 deletions lib/command.js
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,25 @@ Expecting one of '${allowedValues.join("', '")}'`);
return this._optionValueSources[key];
}

/**
* Get source of option value. See also .optsWithGlobals().
* Expected values are default | config | env | cli | implied
*
* @param {string} key
* @return {string}
*/

getOptionValueSourceWithGlobals(key) {
// global overwrites local, like optsWithGlobals
let source;
getCommandAndParents(this).forEach((cmd) => {
if (cmd.getOptionValueSource(key) !== undefined) {
source = cmd.getOptionValueSource(key);
}
});
return source;
}

/**
* Get user arguments from implied or explicit arguments.
* Side-effects: set _scriptPath if args included script. Used for default program name, and subcommand searches.
Expand Down
69 changes: 62 additions & 7 deletions lib/help.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class Help {
this.helpWidth = undefined;
this.sortSubcommands = false;
this.sortOptions = false;
this.showGlobalOptions = false;
}

/**
Expand Down Expand Up @@ -45,6 +46,21 @@ class Help {
return visibleCommands;
}

/**
* Compare options for sort.
*
* @param {Option} a
* @param {Option} b
* @returns number
*/
compareOptions(a, b) {
const getSortKey = (option) => {
// WYSIWYG for order displayed in help. Short used for comparison if present. No special handling for negated.
return option.short ? option.short.replace(/^-/, '') : option.long.replace(/^--/, '');
};
return getSortKey(a).localeCompare(getSortKey(b));
}

/**
* Get an array of the visible options. Includes a placeholder for the implicit help option, if there is one.
*
Expand All @@ -69,17 +85,32 @@ class Help {
visibleOptions.push(helpOption);
}
if (this.sortOptions) {
const getSortKey = (option) => {
// WYSIWYG for order displayed in help with short before long, no special handling for negated.
return option.short ? option.short.replace(/^-/, '') : option.long.replace(/^--/, '');
};
visibleOptions.sort((a, b) => {
return getSortKey(a).localeCompare(getSortKey(b));
});
visibleOptions.sort(this.compareOptions);
}
return visibleOptions;
}

/**
* Get an array of the visible global options. (Not including help.)
*
* @param {Command} cmd
* @returns {Option[]}
*/

visibleGlobalOptions(cmd) {
if (!this.showGlobalOptions) return [];

const globalOptions = [];
for (let parentCmd = cmd.parent; parentCmd; parentCmd = parentCmd.parent) {
const visibleOptions = parentCmd.options.filter((option) => !option.hidden);
globalOptions.push(...visibleOptions);
}
if (this.sortOptions) {
globalOptions.sort(this.compareOptions);
}
return globalOptions;
}

/**
* Get an array of the arguments if any have a description.
*
Expand Down Expand Up @@ -168,6 +199,20 @@ class Help {
}, 0);
}

/**
* Get the longest global option term length.
*
* @param {Command} cmd
* @param {Help} helper
* @returns {number}
*/

longestGlobalOptionTermLength(cmd, helper) {
return helper.visibleGlobalOptions(cmd).reduce((max, option) => {
return Math.max(max, helper.optionTerm(option).length);
}, 0);
}

/**
* Get the longest argument term length.
*
Expand Down Expand Up @@ -341,6 +386,15 @@ class Help {
output = output.concat(['Options:', formatList(optionList), '']);
}

if (this.showGlobalOptions) {
const globalOptionList = helper.visibleGlobalOptions(cmd).map((option) => {
return formatItem(helper.optionTerm(option), helper.optionDescription(option));
});
if (globalOptionList.length > 0) {
output = output.concat(['Global Options:', formatList(globalOptionList), '']);
}
}

// Commands
const commandList = helper.visibleCommands(cmd).map((cmd) => {
return formatItem(helper.subcommandTerm(cmd), helper.subcommandDescription(cmd));
Expand All @@ -363,6 +417,7 @@ class Help {
padWidth(cmd, helper) {
return Math.max(
helper.longestOptionTermLength(cmd, helper),
helper.longestGlobalOptionTermLength(cmd, helper),
helper.longestSubcommandTermLength(cmd, helper),
helper.longestArgumentTermLength(cmd, helper)
);
Expand Down
Loading

0 comments on commit 2bdd631

Please sign in to comment.