Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: tj/commander.js
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 4.0.1
Choose a base ref
...
head repository: tj/commander.js
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v4.1.0
Choose a head ref

Commits on Nov 11, 2019

  1. Copy the full SHA
    35404b8 View commit details

Commits on Nov 14, 2019

  1. Copy the full SHA
    11abe21 View commit details

Commits on Nov 15, 2019

  1. Merge pull request #1096 from chdh/chdh/helpFix

    Remove trailing blanks from wrapped help text
    abetomo authored Nov 15, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    f7778ce View commit details

Commits on Nov 22, 2019

  1. Update dependencies

    shadowspawn committed Nov 22, 2019
    Copy the full SHA
    28a9a1d View commit details

Commits on Nov 23, 2019

  1. Merge pull request #1101 from shadowspawn/feature/update-dependencies

    Update dependencies
    abetomo authored Nov 23, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    04766dc View commit details

Commits on Nov 24, 2019

  1. Copy the full SHA
    df8d9de View commit details
  2. Copy the full SHA
    09c4ab4 View commit details
  3. Copy the full SHA
    7004c0a View commit details

Commits on Nov 25, 2019

  1. Merge pull request #1109 from shadowspawn/feature/support

    Reword supported node versions
    abetomo authored Nov 25, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    7920697 View commit details
  2. Merge pull request #1108 from shadowspawn/feature/timeout

    Extend timeout to hopefully reduce test failures
    abetomo authored Nov 25, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    7c22701 View commit details
  3. Merge pull request #1107 from shadowspawn/feature/version-listener

    Update version listener
    abetomo authored Nov 25, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    57e6c5d View commit details

Commits on Dec 6, 2019

  1. Copy the full SHA
    bc16bd2 View commit details

Commits on Dec 7, 2019

  1. Copy the full SHA
    3740834 View commit details
  2. Merge pull request #1115 from shadowspawn/feature/changelog-split

    Separate out changelog for old versions
    abetomo authored Dec 7, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    5e12c44 View commit details
  3. Merge pull request #1114 from shadowspawn/feature/support-six-months

    Change to six months support, rather than n-1
    abetomo authored Dec 7, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    45d371e View commit details
  4. doc: typo (#1113)

    bodinsamuel authored and shadowspawn committed Dec 7, 2019

    Unverified

    This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
    Copy the full SHA
    3baa16f View commit details

Commits on Dec 9, 2019

  1. Copy the full SHA
    f119fc7 View commit details
  2. Copy the full SHA
    df6284c View commit details

Commits on Dec 11, 2019

  1. Opt-in behaviour to avoid name pollution (#1102)

    * Add object to hold option values separately from properties on command. Return directly from .opts().
    
    * Add configureCommand, and support for passing options rather than command to action handler
    
    * Restore original opts() implementation when using old configuration
    
    * Use either/or new/old option storage, not both
    
    * Turn version test on again, old behaviour restored
    
    * Add tests for configureCommand, and fix bugs
    
    * Expand .opts tests to include modern configuration
    
    * Add TypeScript and inline documentation for configureCommand
    
    * Switch from modern:boolean to combo:string
    
    * Rework new behaviour with matching named routines.
    
    * Add example files for storeOptionsAsProperties
    
    * Add usage error, and make value default to true for new routines (so simpler call for that case)
    
    * Simpify description
    
    * Add section on avoiding option name clashes
    
    * Do not use else after a return
    shadowspawn authored Dec 11, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    81c6e28 View commit details
  2. 4.1.0-0

    shadowspawn committed Dec 11, 2019
    Copy the full SHA
    29a7f46 View commit details
  3. Copy the full SHA
    9de0968 View commit details

Commits on Dec 17, 2019

  1. Copy the full SHA
    b25cc3a View commit details

Commits on Dec 20, 2019

  1. Removed explicit use of ts-node (#1125)

    * Remove explicit use of ts-node, and dependency. Speed tests.
    
    * Fix typo
    shadowspawn authored Dec 20, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    808d4bc View commit details

Commits on Dec 27, 2019

  1. Update Chinese README for v4.1.0

    oGsLP authored and shadowspawn committed Dec 27, 2019
    Copy the full SHA
    03e77df View commit details

Commits on Dec 30, 2019

  1. Copy the full SHA
    4faad59 View commit details

Commits on Jan 1, 2020

  1. Copy the full SHA
    d47fb0c View commit details

Commits on Jan 4, 2020

  1. Merge pull request #1132 from shadowspawn/feature/tidy-js-2

    Improve JSDoc to match code
    abetomo authored Jan 4, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    1d9cc72 View commit details

Commits on Jan 5, 2020

  1. Add parseAsync (#1118)

    * First cut at parseAsync
    
    * Add await parseSync test
    
    * Fix typo in JSDoc
    
    * Add parseAsync to README
    
    Some noise in TOC due to changes in plugin which maintains TOC.
    shadowspawn authored Jan 5, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    7bcf117 View commit details
  2. Copy the full SHA
    6f692cf View commit details

Commits on Jan 6, 2020

  1. Bump version for release

    shadowspawn committed Jan 6, 2020
    Copy the full SHA
    81f5079 View commit details
  2. Copy the full SHA
    f16fecf View commit details
  3. add zh-CN translation for parseAsync

    oGsLP authored and shadowspawn committed Jan 6, 2020
    Copy the full SHA
    1c66935 View commit details
  4. Merge pull request #1142 from tj/develop

    Release 4.1
    abetomo authored Jan 6, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    8e1cdf5 View commit details
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# We have not configured eslint for TypeScript so avoid bogus error messages in the test file and typings file.
**/*.ts
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
"rules": {
"one-var": "off",
"semi": ["error", "always"],
"space-before-function-paren": ["error", "never"]
"space-before-function-paren": ["error", "never"],
"no-else-return": ["error", { "allowElseIf": false }]
}
}
171 changes: 30 additions & 141 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -7,11 +7,32 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

<!-- markdownlint-disable MD024 -->

## [4.1.0] (2020-01-06)

### Added

- two routines to change how option values are handled, and eliminate name clashes with command properties ([#933] [#1102])
- see storeOptionsAsProperties and passCommandToAction in README
- `.parseAsync` to use instead of `.parse` if supply async action handlers ([#806] [#1118])

### Fixed

- Remove trailing blanks from wrapped help text ([#1096])

### Changed

- update dependencies
- extend security coverage for Commander 2.x to 2020-02-03
- improvements to README
- improvements to TypeScript definition documentation
- move old versions out of main CHANGELOG
- removed explicit use of `ts-node` in tests

## [4.0.1] (2019-11-12)

### Fixed

* display help when requested, even if there are missing required options [(#1091)]
* display help when requested, even if there are missing required options ([#1091])

## [4.0.0] (2019-11-02)

@@ -355,152 +376,16 @@ program

* remove input methods (.prompt, .confirm, etc)

## 1.3.2 / 2013-07-18

* add support for sub-commands to co-exist with the original command

## 1.3.1 / 2013-07-18

* add quick .runningCommand hack so you can opt-out of other logic when running a sub command

## 1.3.0 / 2013-07-09

* add EACCES error handling
* fix sub-command --help

## 1.2.0 / 2013-06-13

* allow "-" hyphen as an option argument
* support for RegExp coercion

## 1.1.1 / 2012-11-20

* add more sub-command padding
* fix .usage() when args are present. Closes #106

## 1.1.0 / 2012-11-16

* add git-style executable subcommand support. Closes #94

## 1.0.5 / 2012-10-09

* fix `--name` clobbering. Closes #92
* fix examples/help. Closes #89

## 1.0.4 / 2012-09-03

* add `outputHelp()` method.

## 1.0.3 / 2012-08-30

* remove invalid .version() defaulting

## 1.0.2 / 2012-08-24

* add `--foo=bar` support [arv]
* fix password on node 0.8.8. Make backward compatible with 0.6 [focusaurus]

## 1.0.1 / 2012-08-03

* fix issue #56
* fix tty.setRawMode(mode) was moved to tty.ReadStream#setRawMode() (i.e. process.stdin.setRawMode())

## 1.0.0 / 2012-07-05

* add support for optional option descriptions
* add defaulting of `.version()` to package.json's version

## 0.6.1 / 2012-06-01

* Added: append (yes or no) on confirmation
* Added: allow node.js v0.7.x

## 0.6.0 / 2012-04-10

* Added `.prompt(obj, callback)` support. Closes #49
* Added default support to .choose(). Closes #41
* Fixed the choice example

## 0.5.1 / 2011-12-20

* Fixed `password()` for recent nodes. Closes #36

## 0.5.0 / 2011-12-04

* Added sub-command option support [itay]

## 0.4.3 / 2011-12-04

* Fixed custom help ordering. Closes #32

## 0.4.2 / 2011-11-24

* Added travis support
* Fixed: line-buffered input automatically trimmed. Closes #31

## 0.4.1 / 2011-11-18

* Removed listening for "close" on --help

## 0.4.0 / 2011-11-15

* Added support for `--`. Closes #24

## 0.3.3 / 2011-11-14

* Fixed: wait for close event when writing help info [Jerry Hamlet]

## 0.3.2 / 2011-11-01

* Fixed long flag definitions with values [felixge]

## 0.3.1 / 2011-10-31

* Changed `--version` short flag to `-V` from `-v`
* Changed `.version()` so it's configurable [felixge]

## 0.3.0 / 2011-10-31

* Added support for long flags only. Closes #18

## 0.2.1 / 2011-10-24

* "node": ">= 0.4.x < 0.7.0". Closes #20

## 0.2.0 / 2011-09-26

* Allow for defaults that are not just boolean. Default peassignment only occurs for --no-*, optional, and required arguments. [Jim Isaacs]

## 0.1.0 / 2011-08-24

* Added support for custom `--help` output

## 0.0.5 / 2011-08-18

* Changed: when the user enters nothing prompt for password again
* Fixed issue with passwords beginning with numbers [NuckChorris]

## 0.0.4 / 2011-08-15

* Fixed `Commander#args`

## 0.0.3 / 2011-08-15

* Added default option value support

## 0.0.2 / 2011-08-15

* Added mask support to `Command#password(str[, mask], fn)`
* Added `Command#password(str, fn)`

## 0.0.1 / 2010-01-03
## Older versions

* Initial release
* [1.x](./changelogs/CHANGELOG-1.md)
* [0.x](./changelogs/CHANGELOG-0.md)

[#599]: https://github.com/tj/commander.js/issues/599
[#611]: https://github.com/tj/commander.js/issues/611
[#697]: https://github.com/tj/commander.js/issues/697
[#795]: https://github.com/tj/commander.js/issues/795
[#806]: https://github.com/tj/commander.js/issues/806
[#915]: https://github.com/tj/commander.js/issues/915
[#938]: https://github.com/tj/commander.js/issues/938
[#963]: https://github.com/tj/commander.js/issues/963
@@ -509,6 +394,7 @@ program
[#987]: https://github.com/tj/commander.js/issues/987
[#990]: https://github.com/tj/commander.js/issues/990
[#991]: https://github.com/tj/commander.js/issues/991
[#993]: https://github.com/tj/commander.js/issues/993
[#999]: https://github.com/tj/commander.js/issues/999
[#1010]: https://github.com/tj/commander.js/pull/1010
[#1018]: https://github.com/tj/commander.js/pull/1018
@@ -526,6 +412,9 @@ program
[#1071]: https://github.com/tj/commander.js/pull/1071
[#1081]: https://github.com/tj/commander.js/pull/1081
[#1091]: https://github.com/tj/commander.js/pull/1091
[#1096]: https://github.com/tj/commander.js/pull/1096
[#1102]: https://github.com/tj/commander.js/pull/1102
[#1118]: https://github.com/tj/commander.js/pull/1118

[Unreleased]: https://github.com/tj/commander.js/compare/master...develop
[4.0.1]: https://github.com/tj/commander.js/compare/v4.0.0..v4.0.1
60 changes: 57 additions & 3 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ Read this in other languages: English | [简体中文](./Readme_zh-CN.md)

- [Commander.js](#commanderjs)
- [Installation](#installation)
- [Declaring _program_ variable](#declaring-program-variable)
- [Declaring program variable](#declaring-program-variable)
- [Options](#options)
- [Common option types, boolean and value](#common-option-types-boolean-and-value)
- [Default option value](#default-option-value)
@@ -31,8 +31,9 @@ Read this in other languages: English | [简体中文](./Readme_zh-CN.md)
- [.help(cb)](#helpcb)
- [Custom event listeners](#custom-event-listeners)
- [Bits and pieces](#bits-and-pieces)
- [Avoiding option name clashes](#avoiding-option-name-clashes)
- [TypeScript](#typescript)
- [Node options such as `--harmony`](#node-options-such-as---harmony)
- [Node options such as --harmony](#node-options-such-as---harmony)
- [Node debugging](#node-debugging)
- [Override exit handling](#override-exit-handling)
- [Examples](#examples)
@@ -70,6 +71,8 @@ Options are defined with the `.option()` method, also serving as documentation f

The options can be accessed as properties on the Command object. Multi-word options such as "--template-engine" are camel-cased, becoming `program.templateEngine` etc. Multiple short flags may be combined as a single arg, for example `-abc` is equivalent to `-a -b -c`.

See also optional new behaviour to [avoid name clashes](#avoiding-option-name-clashes).

### Common option types, boolean and value

The two most used option types are a boolean flag, and an option which takes a value (declared using angle brackets). Both are `undefined` unless specified on command line.
@@ -374,6 +377,19 @@ program
program.parse(process.argv)
```
You may supply an `async` action handler, in which case you call `.parseAsync` rather than `.parse`.
```js
async function run() { /* code goes here */ }
async function main() {
program
.command('run')
.action(run);
await program.parseAsync(process.argv);
}
```
A command's options on the command line are validated when the command is used. Any unknown options will be reported as an error. However, if an action-based command does not define an action, then the options are not validated.
Configuration options can be passed with the call to `.command()`. Specifying `true` for `opts.noHelp` will remove the command from the generated help output.
@@ -550,6 +566,43 @@ program.on('command:*', function () {
## Bits and pieces
### Avoiding option name clashes
The original and default behaviour is that the option values are stored
as properties on the program, and the action handler is passed a
command object with the options values stored as properties.
This is very convenient to code, but the downside is possible clashes with
existing properties of Command.
There are two new routines to change the behaviour, and the default behaviour may change in the future:
- `storeOptionsAsProperties`: whether to store option values as properties on command object, or store separately (specify false) and access using `.opts()`
- `passCommandToAction`: whether to pass command to action handler,
or just the options (specify false)
```js
// file: ./examples/storeOptionsAsProperties.action.js
program
.storeOptionsAsProperties(false)
.passCommandToAction(false);
program
.name('my-program-name')
.option('-n,--name <name>');
program
.command('show')
.option('-a,--action <action>')
.action((options) => {
console.log(options.action);
});
program.parse(process.argv);
const programOptions = program.opts();
console.log(programOptions.name);
```
### TypeScript
The Commander package includes its TypeScript Definition file, but also requires the node types which you need to install yourself. e.g.
@@ -648,7 +701,8 @@ More Demos can be found in the [examples](https://github.com/tj/commander.js/tre
## Support
Commander is supported on Node 8 and above. (Commander is likely to still work on older versions of Node, but is not tested below Node 8.)
Commander 4.x is supported on Node 8 and above, and is likely to work with Node 6 but not tested.
(For versions of Node below Node 6, use Commander 3.x or 2.x.)
The main forum for free and community support is the project [Issues](https://github.com/tj/commander.js/issues) on GitHub.
62 changes: 58 additions & 4 deletions Readme_zh-CN.md
Original file line number Diff line number Diff line change
@@ -31,13 +31,15 @@
- [.help(cb)](#helpcb)
- [自定义事件监听](#%e8%87%aa%e5%ae%9a%e4%b9%89%e4%ba%8b%e4%bb%b6%e7%9b%91%e5%90%ac)
- [零碎知识](#%e9%9b%b6%e7%a2%8e%e7%9f%a5%e8%af%86)
- [避免选项命名冲突](#避免选项命名冲突)
- [TypeScript](#typescript)
- [Node 选项例如 `--harmony`](#node-%e9%80%89%e9%a1%b9%e4%be%8b%e5%a6%82---harmony)
- [Node 选项例如 --harmony](#node-%e9%80%89%e9%a1%b9%e4%be%8b%e5%a6%82---harmony)
- [Node 调试](#node-%e8%b0%83%e8%af%95)
- [重载退出(exit)处理](#%e9%87%8d%e8%bd%bd%e9%80%80%e5%87%baexit%e5%a4%84%e7%90%86)
- [例子](#%e4%be%8b%e5%ad%90)
- [许可证](#%e8%ae%b8%e5%8f%af%e8%af%81)
- [支持](#%e6%94%af%e6%8c%81)
- [企业使用Commander](#企业使用Commander)

## 安装

@@ -68,6 +70,8 @@ program.version('0.0.1');

选项会被放到 Commander 对象的属性上,多词选项如"--template-engine"会被转为驼峰法`program.templateEngine`。多个短标识可以组合为一个参数,如`-a -b -c`等价于`-abc`

另请参看可选的新功能 [避免命名冲突](#避免选项命名冲突).

### 常用选项类型,boolean和值

最常用的两个选项类型是boolean(选项后面不跟值)和选项跟一个值(使用尖括号声明)。除非在命令行中指定,否则两者都是`undefined`
@@ -368,6 +372,20 @@ program
program.parse(process.argv)
```
你可以自行实现一个`async`操作处理程序,同时调用`.parseAsync`代替`.parse`
```js
async function run() { /* 在这里编写代码 */ }

async function main() {
program
.command('run')
.action(run);
await program.parseAsync(process.argv);
}
```
当一个命令在命令行上被使用时,它的选项必须是合法的。使用任何未知的选项会报错。然而如果一个基于操作的命令没有定义任何操作,那么这些选项是不合法的。
定义配置选项可以随着调用 `.command()` 传递。
@@ -540,6 +558,39 @@ program.on('command:*', function () {
## 零碎知识
### 避免选项命名冲突
Commander原本和默认的行为是将选项值作为program的属性存储的,并且给操作处理程序(action handler)传递了一个将选项值作为属性存储的command对象。
这样确实使得编程很方便,但是会带来有可能会和Command对象的属性相冲突的缺点。
这里有两种方法来改变着这样的行为,而且我们有可能在将来改变默认的行为
- `storeOptionsAsProperties`: 是否将选项值作为command对象的属性来存储,亦或者分开地存储(指定 false)并使用`.opts()`来获得。
- `passCommandToAction`: 是否把command对象传递给操作处理程序,亦或者仅仅传递这些选项(指定 false)
```js
// 文件: ./examples/storeOptionsAsProperties.action.js
program
.storeOptionsAsProperties(false)
.passCommandToAction(false);
program
.name('my-program-name')
.option('-n,--name <name>');
program
.command('show')
.option('-a,--action <action>')
.action((options) => {
console.log(options.action);
});
program.parse(process.argv);
const programOptions = program.opts();
console.log(programOptions.name);
```
### TypeScript
包里包含 TypeScript 定义文件,但是需要你自己安装 node types。如:
@@ -636,10 +687,13 @@ program.parse(process.argv);
## 支持
Commander 现在在Node 8以及更高的版本上得到支持。(尽管Commander仍有可能在更低的Node版本上工作,但是Node 8以下版本不再保证相关的测试)
Commander 4.x版本现在在Node 8以及更高的版本上得到支持,尽管仍有可能在Node 6版本上工作,但是不再保证相关的测试。
(对于Node版本低于6的情况,建议使用Commander 3.x 或 2.x版本。)
主要的社区支持的免费论坛就在Github上的项目[Issues](https://github.com/tj/commander.js/issues)
[专业支持的commander现在已经可用!](https://tidelift.com/subscription/pkg/npm-commander?utm_source=npm-commander&utm_medium=referral&utm_campaign=readme)
### 企业使用Commander
作为Tidelift订阅的一部分现在可用
Tidelift为软件开发团队提供了购买和维护其软件的单一来源,并由最了解软件的专家提供专业级保证,同时与现有工具无缝集成。
Commander和数以千计的其他包的维护者在与Tidelift合作,提供对于企业用来构筑应用的开源依赖的商业支持与维护。通过向相关依赖包的维护者支付一定费用,从而帮助企业节省时间,降低风险,改进代码运行情况。[了解更多](https://tidelift.com/subscription/pkg/npm-commander?utm_source=npm-commander&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
13 changes: 8 additions & 5 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -2,11 +2,14 @@

## Supported Versions

| Version | Supported |
| ------- | ------------------ |
| 4.x | :white_check_mark: |
| 3.x | :white_check_mark: |
| < 3 | :x: |
Old versions receive security updates for six months.

| Version | Supported |
| ------- | ------------------------------------------ |
| 4.x | :white_check_mark: |
| 3.x | :white_check_mark: support ends 2020-05-01 |
| 2.x | :white_check_mark: support ends 2020-02-03 |
| < 2 | :x: |

## Reporting a Vulnerability

88 changes: 88 additions & 0 deletions changelogs/CHANGELOG-0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Changelog for 0.x

## 0.6.1 / 2012-06-01

* Added: append (yes or no) on confirmation
* Added: allow node.js v0.7.x

## 0.6.0 / 2012-04-10

* Added `.prompt(obj, callback)` support. Closes #49
* Added default support to .choose(). Closes #41
* Fixed the choice example

## 0.5.1 / 2011-12-20

* Fixed `password()` for recent nodes. Closes #36

## 0.5.0 / 2011-12-04

* Added sub-command option support [itay]

## 0.4.3 / 2011-12-04

* Fixed custom help ordering. Closes #32

## 0.4.2 / 2011-11-24

* Added travis support
* Fixed: line-buffered input automatically trimmed. Closes #31

## 0.4.1 / 2011-11-18

* Removed listening for "close" on --help

## 0.4.0 / 2011-11-15

* Added support for `--`. Closes #24

## 0.3.3 / 2011-11-14

* Fixed: wait for close event when writing help info [Jerry Hamlet]

## 0.3.2 / 2011-11-01

* Fixed long flag definitions with values [felixge]

## 0.3.1 / 2011-10-31

* Changed `--version` short flag to `-V` from `-v`
* Changed `.version()` so it's configurable [felixge]

## 0.3.0 / 2011-10-31

* Added support for long flags only. Closes #18

## 0.2.1 / 2011-10-24

* "node": ">= 0.4.x < 0.7.0". Closes #20

## 0.2.0 / 2011-09-26

* Allow for defaults that are not just boolean. Default peassignment only occurs for --no-*, optional, and required arguments. [Jim Isaacs]

## 0.1.0 / 2011-08-24

* Added support for custom `--help` output

## 0.0.5 / 2011-08-18

* Changed: when the user enters nothing prompt for password again
* Fixed issue with passwords beginning with numbers [NuckChorris]

## 0.0.4 / 2011-08-15

* Fixed `Commander#args`

## 0.0.3 / 2011-08-15

* Added default option value support

## 0.0.2 / 2011-08-15

* Added mask support to `Command#password(str[, mask], fn)`
* Added `Command#password(str, fn)`

## 0.0.1 / 2010-01-03

* Initial release
56 changes: 56 additions & 0 deletions changelogs/CHANGELOG-1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Changelog for 1.x

## 1.3.2 / 2013-07-18

* add support for sub-commands to co-exist with the original command

## 1.3.1 / 2013-07-18

* add quick .runningCommand hack so you can opt-out of other logic when running a sub command

## 1.3.0 / 2013-07-09

* add EACCES error handling
* fix sub-command --help

## 1.2.0 / 2013-06-13

* allow "-" hyphen as an option argument
* support for RegExp coercion

## 1.1.1 / 2012-11-20

* add more sub-command padding
* fix .usage() when args are present. Closes #106

## 1.1.0 / 2012-11-16

* add git-style executable subcommand support. Closes #94

## 1.0.5 / 2012-10-09

* fix `--name` clobbering. Closes #92
* fix examples/help. Closes #89

## 1.0.4 / 2012-09-03

* add `outputHelp()` method.

## 1.0.3 / 2012-08-30

* remove invalid .version() defaulting

## 1.0.2 / 2012-08-24

* add `--foo=bar` support [arv]
* fix password on node 0.8.8. Make backward compatible with 0.6 [focusaurus]

## 1.0.1 / 2012-08-03

* fix issue #56
* fix tty.setRawMode(mode) was moved to tty.ReadStream#setRawMode() (i.e. process.stdin.setRawMode())

## 1.0.0 / 2012-07-05

* add support for optional option descriptions
* add defaulting of `.version()` to package.json's version
40 changes: 40 additions & 0 deletions examples/storeOptionsAsProperties-action.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env node

// To avoid possible name clashes, you can change the default behaviour
// of storing the options values as properties on the command object.
// In addition, you can pass just the options to the action handler
// instead of a commmand object. This allows simpler code, and is more consistent
// with the previous behaviour so less code changes from old code.
//
// Example output:
//
// $ node storeOptionsAsProperties-action.js show
// undefined
// undefined
//
// $ node storeOptionsAsProperties-action.js --name foo show --action jump
// jump
// foo

const commander = require('../');
const program = new commander.Command();

program
.storeOptionsAsProperties(false) // <--- change behaviour
.passCommandToAction(false); // <--- change behaviour

program
.name('my-program-name')
.option('-n,--name <name>');

program
.command('show')
.option('-a,--action <action>')
.action((options) => { // <--- passed options, not Command
console.log(options.action); // <--- matches old code
});

program.parse(process.argv);

const programOptions = program.opts(); // <--- use opts to access option values
console.log(programOptions.name);
38 changes: 38 additions & 0 deletions examples/storeOptionsAsProperties-opts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env node

// To avoid possible name clashes, you can change the default behaviour
// of storing the options values as properties on the command object.
// You access the option values using the .opts() function.
//
// Example output:
//
// $ node storeOptionsAsProperties-opts.js show
// undefined
// undefined
//
// $ node storeOptionsAsProperties-opts.js --name foo show --action jump
// jump
// foo

const commander = require('../');
const program = new commander.Command();

program
.storeOptionsAsProperties(false); // <--- change behaviour

program
.name('my-program-name')
.option('-n,--name <name>');

program
.command('show')
.option('-a,--action <action>')
.action((cmd) => {
const options = cmd.opts(); // <--- use opts to access option values
console.log(options.action);
});

program.parse(process.argv);

const programOptions = program.opts(); // <--- use opts to access option values
console.log(programOptions.name);
35 changes: 35 additions & 0 deletions examples/storeOptionsAsProperties-problem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env node

// The original and default behaviour is that the option values are stored
// as properties on the program (Command). The action handler is passed a
// command object (Command) with the options values also stored as properties.
// This is very convenient to code, but the downside is possible clashes with
// existing properties of Command.
//
// Example output, note the issues in the first call:
//
// $ node storeOptionsAsProperties-problem.js show
// [Function]
// [Function]
//
// $ node storeOptionsAsProperties-problem.js --name foo show --action jump
// jump
// foo

const commander = require('../');
const program = new commander.Command();

program
.name('my-program-name')
.option('-n,--name <name>'); // Oops, clash with .name()

program
.command('show')
.option('-a,--action <action>') // Oops, clash with .action()
.action((cmd) => {
console.log(cmd.action);
});

program.parse(process.argv);

console.log(program.name);
175 changes: 137 additions & 38 deletions index.js
Original file line number Diff line number Diff line change
@@ -115,7 +115,7 @@ exports.CommanderError = CommanderError;
/**
* Initialize a new `Command`.
*
* @param {String} name
* @param {String} [name]
* @api public
*/

@@ -126,6 +126,10 @@ function Command(name) {
this._allowUnknownOption = false;
this._args = [];
this._name = name || '';
this._optionValues = {};
this._storeOptionsAsProperties = true; // backwards compatible by default
this._passCommandToAction = true; // backwards compatible by default
this._actionResults = [];

this._helpFlags = '-h, --help';
this._helpDescription = 'output usage information';
@@ -151,7 +155,7 @@ function Command(name) {
* // Command implemented using separate executable file (description is second parameter to `.command`)
* program
* .command('start <service>', 'start named service')
* .command('stop [service]', 'stop named serice, or all if no name supplied');
* .command('stop [service]', 'stop named service, or all if no name supplied');
*
* @param {string} nameAndArgs - command name and arguments, args are `<required>` or `[optional]` and last may also be `variadic...`
* @param {Object|string} [actionOptsOrExecDesc] - configuration options (for action), or description (for executable)
@@ -183,6 +187,8 @@ Command.prototype.command = function(nameAndArgs, actionOptsOrExecDesc, execOpts
cmd._helpShortFlag = this._helpShortFlag;
cmd._helpLongFlag = this._helpLongFlag;
cmd._exitCallback = this._exitCallback;
cmd._storeOptionsAsProperties = this._storeOptionsAsProperties;
cmd._passCommandToAction = this._passCommandToAction;

cmd._executableFile = opts.executableFile; // Custom name for executable file
this.commands.push(cmd);
@@ -323,7 +329,7 @@ Command.prototype.action = function(fn) {
var parsed = self.parseOptions(unknown);

// Output help if necessary
outputHelpIfNecessary(self, parsed.unknown);
outputHelpIfRequested(self, parsed.unknown);
self._checkForMissingMandatoryOptions();

// If there are still any unknown options, then we simply
@@ -351,13 +357,23 @@ Command.prototype.action = function(fn) {
// The .action callback takes an extra parameter which is the command itself.
var expectedArgsCount = self._args.length;
var actionArgs = args.slice(0, expectedArgsCount);
actionArgs[expectedArgsCount] = self;
if (self._passCommandToAction) {
actionArgs[expectedArgsCount] = self;
} else {
actionArgs[expectedArgsCount] = self.opts();
}
// Add the extra arguments so available too.
if (args.length > expectedArgsCount) {
actionArgs.push(args.slice(expectedArgsCount));
}

fn.apply(self, actionArgs);
const actionResult = fn.apply(self, actionArgs);
// Remember result in case it is async. Assume parseAsync getting called on root.
let rootCommand = self;
while (rootCommand.parent) {
rootCommand = rootCommand.parent;
}
rootCommand._actionResults.push(actionResult);
};
var parent = this.parent || this;
var name = parent === this ? '*' : this._name;
@@ -405,12 +421,12 @@ Command.prototype._optionEx = function(config, flags, description, fn, defaultVa
if (option.negate || option.optional || option.required || typeof defaultValue === 'boolean') {
// when --no-foo we make sure default is true, unless a --foo option is already defined
if (option.negate) {
var opts = self.opts();
defaultValue = Object.prototype.hasOwnProperty.call(opts, name) ? opts[name] : true;
const positiveLongFlag = option.long.replace(/^--no-/, '--');
defaultValue = self.optionFor(positiveLongFlag) ? self._getOptionValue(name) : true;
}
// preassign only if we have a default
if (defaultValue !== undefined) {
self[name] = defaultValue;
self._setOptionValue(name, defaultValue);
option.defaultValue = defaultValue;
}
}
@@ -423,22 +439,22 @@ Command.prototype._optionEx = function(config, flags, description, fn, defaultVa
this.on('option:' + oname, function(val) {
// coercion
if (val !== null && fn) {
val = fn(val, self[name] === undefined ? defaultValue : self[name]);
val = fn(val, self._getOptionValue(name) === undefined ? defaultValue : self._getOptionValue(name));
}

// unassigned or boolean value
if (typeof self[name] === 'boolean' || typeof self[name] === 'undefined') {
if (typeof self._getOptionValue(name) === 'boolean' || typeof self._getOptionValue(name) === 'undefined') {
// if no value, negate false, and we have a default, then use it!
if (val == null) {
self[name] = option.negate
self._setOptionValue(name, option.negate
? false
: defaultValue || true;
: defaultValue || true);
} else {
self[name] = val;
self._setOptionValue(name, val);
}
} else if (val !== null) {
// reassign
self[name] = option.negate ? false : val;
self._setOptionValue(name, option.negate ? false : val);
}
});

@@ -532,7 +548,70 @@ Command.prototype.allowUnknownOption = function(arg) {
};

/**
* Parse `argv`, settings options and invoking commands when defined.
* Whether to store option values as properties on command object,
* or store separately (specify false). In both cases the option values can be accessed using .opts().
*
* @param {boolean} value
* @return {Command} Command for chaining
* @api public
*/

Command.prototype.storeOptionsAsProperties = function(value) {
this._storeOptionsAsProperties = (value === undefined) || value;
if (this.options.length) {
// This is for programmer, not end user.
console.error('Commander usage error: call storeOptionsAsProperties before adding options');
}
return this;
};

/**
* Whether to pass command to action handler,
* or just the options (specify false).
*
* @param {boolean} value
* @return {Command} Command for chaining
* @api public
*/

Command.prototype.passCommandToAction = function(value) {
this._passCommandToAction = (value === undefined) || value;
return this;
};

/**
* Store option value
*
* @param {String} key
* @param {Object} value
* @api private
*/

Command.prototype._setOptionValue = function(key, value) {
if (this._storeOptionsAsProperties) {
this[key] = value;
} else {
this._optionValues[key] = value;
}
};

/**
* Retrieve option value
*
* @param {String} key
* @return {Object} value
* @api private
*/

Command.prototype._getOptionValue = function(key) {
if (this._storeOptionsAsProperties) {
return this[key];
}
return this._optionValues[key];
};

/**
* Parse `argv`, setting options and invoking commands when defined.
*
* @param {Array} argv
* @return {Command} for chaining
@@ -616,13 +695,27 @@ Command.prototype.parse = function(argv) {
return result;
};

/**
* Parse `argv`, setting options and invoking commands when defined.
*
* Use parseAsync instead of parse if any of your action handlers are async. Returns a Promise.
*
* @param {Array} argv
* @return {Promise}
* @api public
*/
Command.prototype.parseAsync = function(argv) {
this.parse(argv);
return Promise.all(this._actionResults);
};

/**
* Execute a sub-command executable.
*
* @param {Array} argv
* @param {Array} args
* @param {Array} unknown
* @param {String} specifySubcommand
* @param {String} executableFile
* @api private
*/

@@ -801,7 +894,7 @@ Command.prototype.parseArgs = function(args, unknown) {
this.emit('command:*', args, unknown);
}
} else {
outputHelpIfNecessary(this, unknown);
outputHelpIfRequested(this, unknown);

// If there were no args and we have unknown options,
// then they are extraneous and we need to error.
@@ -843,7 +936,7 @@ Command.prototype._checkForMissingMandatoryOptions = function() {
// Walk up hierarchy so can call from action handler after checking for displaying help.
for (var cmd = this; cmd; cmd = cmd.parent) {
cmd.options.forEach((anOption) => {
if (anOption.mandatory && (cmd[anOption.attributeName()] === undefined)) {
if (anOption.mandatory && (cmd._getOptionValue(anOption.attributeName()) === undefined)) {
cmd.missingMandatoryOptionValue(anOption);
}
});
@@ -855,7 +948,7 @@ Command.prototype._checkForMissingMandatoryOptions = function() {
* void of these options.
*
* @param {Array} argv
* @return {Array}
* @return {{args: Array, unknown: Array}}
* @api public
*/

@@ -936,14 +1029,19 @@ Command.prototype.parseOptions = function(argv) {
* @api public
*/
Command.prototype.opts = function() {
var result = {},
len = this.options.length;

for (var i = 0; i < len; i++) {
var key = this.options[i].attributeName();
result[key] = key === this._versionOptionName ? this._version : this[key];
if (this._storeOptionsAsProperties) {
// Preserve original behaviour so backwards compatible when still using properties
var result = {},
len = this.options.length;

for (var i = 0; i < len; i++) {
var key = this.options[i].attributeName();
result[key] = key === this._versionOptionName ? this._version : this[key];
}
return result;
}
return result;

return this._optionValues;
};

/**
@@ -962,8 +1060,8 @@ Command.prototype.missingArgument = function(name) {
/**
* `Option` is missing an argument, but received `flag` or nothing.
*
* @param {String} option
* @param {String} flag
* @param {Option} option
* @param {String} [flag]
* @api private
*/

@@ -981,7 +1079,7 @@ Command.prototype.optionMissingArgument = function(option, flag) {
/**
* `Option` does not have a value, and is a mandatory option.
*
* @param {String} option
* @param {Option} option
* @api private
*/

@@ -1041,9 +1139,10 @@ Command.prototype.version = function(str, flags, description) {
var versionOption = new Option(flags, description);
this._versionOptionName = versionOption.long.substr(2) || 'version';
this.options.push(versionOption);
var self = this;
this.on('option:' + this._versionOptionName, function() {
process.stdout.write(str + '\n');
this._exit(0, 'commander.version', str);
self._exit(0, 'commander.version', str);
});
return this;
};
@@ -1052,7 +1151,7 @@ Command.prototype.version = function(str, flags, description) {
* Set the description to `str`.
*
* @param {String} str
* @param {Object} argsDescription
* @param {Object} [argsDescription]
* @return {String|Command}
* @api public
*/
@@ -1089,7 +1188,7 @@ Command.prototype.alias = function(alias) {
/**
* Set / get the command usage `str`.
*
* @param {String} str
* @param {String} [str]
* @return {String|Command}
* @api public
*/
@@ -1112,7 +1211,7 @@ Command.prototype.usage = function(str) {
/**
* Get or set the name of the command
*
* @param {String} str
* @param {String} [str]
* @return {String|Command}
* @api public
*/
@@ -1429,7 +1528,7 @@ function wrap(str, width, indent) {
if (line.slice(-1) === '\n') {
line = line.slice(0, line.length - 1);
}
return ((i > 0 && indent) ? Array(indent + 1).join(' ') : '') + line;
return ((i > 0 && indent) ? Array(indent + 1).join(' ') : '') + line.trimRight();
}).join('\n');
}

@@ -1456,14 +1555,14 @@ function optionalWrap(str, width, indent) {
}

/**
* Output help information if necessary
* Output help information if help flags specified
*
* @param {Command} command to output help for
* @param {Array} array of options to search for -h or --help
* @param {Command} cmd - command to output help for
* @param {Array} options - array of options to search for -h or --help
* @api private
*/

function outputHelpIfNecessary(cmd, options) {
function outputHelpIfRequested(cmd, options) {
options = options || [];

for (var i = 0; i < options.length; i++) {
427 changes: 302 additions & 125 deletions package-lock.json

Large diffs are not rendered by default.

13 changes: 6 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "commander",
"version": "4.0.1",
"version": "4.1.0",
"description": "the complete solution for node.js command-line programs",
"keywords": [
"commander",
@@ -26,14 +26,13 @@
],
"dependencies": {},
"devDependencies": {
"@types/jest": "^24.0.18",
"@types/node": "^12.7.5",
"eslint": "^6.4.0",
"eslint-plugin-jest": "^22.17.0",
"@types/jest": "^24.0.23",
"@types/node": "^12.12.11",
"eslint": "^6.7.0",
"eslint-plugin-jest": "^22.21.0",
"jest": "^24.8.0",
"standard": "^14.3.1",
"ts-node": "^8.4.1",
"typescript": "^3.6.3"
"typescript": "^3.7.2"
},
"typings": "typings/index.d.ts",
"engines": {
16 changes: 16 additions & 0 deletions tests/command.action.test.js
Original file line number Diff line number Diff line change
@@ -88,3 +88,19 @@ test('when .action on program with subcommand and program argument then program

expect(actionMock).toHaveBeenCalledWith('a', program);
});

test('when action is async then can await parseAsync', async() => {
let asyncFinished = false;
async function delay() {
await new Promise(resolve => setTimeout(resolve, 100));
asyncFinished = true;
};
const program = new commander.Command();
program
.action(delay);

const later = program.parseAsync(['node', 'test']);
expect(asyncFinished).toBe(false);
await later;
expect(asyncFinished).toBe(true);
});
5 changes: 4 additions & 1 deletion tests/command.executableSubcommand.lookup.test.js
Original file line number Diff line number Diff line change
@@ -89,8 +89,11 @@ testOrSkipOnWindows('when subcommand file is double symlink then lookup succeeds
});

test('when subcommand suffix is .ts then lookup succeeds', (done) => {
// We support looking for ts files for ts-node in particular, but don't need to test ts-node itself.
// The program and the subcommand `pm-install.ts` are both plain JavaScript code.
const binLinkTs = path.join(__dirname, 'fixtures-ts', 'pm.ts');
childProcess.execFile('node', ['-r', 'ts-node/register', binLinkTs, 'install'], function(_error, stdout, stderr) {
// childProcess.execFile('node', ['-r', 'ts-node/register', binLinkTs, 'install'], function(_error, stdout, stderr) {
childProcess.execFile('node', [binLinkTs, 'install'], function(_error, stdout, stderr) {
expect(stdout).toBe('install\n');
done();
});
87 changes: 87 additions & 0 deletions tests/commander.configureCommand.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
const commander = require('../');

// Mostly testing direct on program, limited check that (sub)command working same.

// Default behaviours

test('when default then options stored on command', () => {
const program = new commander.Command();
program
.option('--foo <value>', 'description');
program.parse(['node', 'test', '--foo', 'bar']);
expect(program.foo).toBe('bar');
});

test('when default then command passed to action', () => {
const program = new commander.Command();
const callback = jest.fn();
program
.arguments('<value>')
.action(callback);
program.parse(['node', 'test', 'value']);
expect(callback).toHaveBeenCalledWith('value', program);
});

// storeOptionsAsProperties

test('when storeOptionsAsProperties() then options stored on command', () => {
const program = new commander.Command();
program
.storeOptionsAsProperties()
.option('--foo <value>', 'description');
program.parse(['node', 'test', '--foo', 'bar']);
expect(program.foo).toBe('bar');
});

test('when storeOptionsAsProperties(true) then options stored on command', () => {
const program = new commander.Command();
program
.storeOptionsAsProperties(true)
.option('--foo <value>', 'description');
program.parse(['node', 'test', '--foo', 'bar']);
expect(program.foo).toBe('bar');
});

test('when storeOptionsAsProperties(false) then options not stored on command', () => {
const program = new commander.Command();
program
.storeOptionsAsProperties(false)
.option('--foo <value>', 'description');
program.parse(['node', 'test', '--foo', 'bar']);
expect(program.foo).toBeUndefined();
});

// passCommandToAction

test('when passCommandToAction() then command passed to action', () => {
const program = new commander.Command();
const callback = jest.fn();
program
.passCommandToAction()
.arguments('<value>')
.action(callback);
program.parse(['node', 'test', 'value']);
expect(callback).toHaveBeenCalledWith('value', program);
});

test('when passCommandToAction(true) then command passed to action', () => {
const program = new commander.Command();
const callback = jest.fn();
program
.passCommandToAction(true)
.arguments('<value>')
.action(callback);
program.parse(['node', 'test', 'value']);
expect(callback).toHaveBeenCalledWith('value', program);
});

test('when passCommandToAction(false) then options passed to action', () => {
const program = new commander.Command();
const callback = jest.fn();
program
.passCommandToAction(false)
.arguments('<value>')
.action(callback);
program.parse(['node', 'test', 'value']);
expect(callback).toHaveBeenCalledWith('value', program.opts());
});
8 changes: 4 additions & 4 deletions tests/helpwrap.test.js
Original file line number Diff line number Diff line change
@@ -13,8 +13,8 @@ test('when long option description then wrap and indent', () => {
`Usage: [options]
Options:
-x -extra-long-option-switch kjsahdkajshkahd kajhsd akhds kashd kajhs dkha
dkh aksd ka dkha kdh kasd ka kahs dkh sdkh
-x -extra-long-option-switch kjsahdkajshkahd kajhsd akhds kashd kajhs dkha
dkh aksd ka dkha kdh kasd ka kahs dkh sdkh
askdh aksd kashdk ahsd kahs dkha skdh
-h, --help output usage information
`;
@@ -34,7 +34,7 @@ test('when long option description and default then wrap and indent', () => {
`Usage: [options]
Options:
-x -extra-long-option <value> kjsahdkajshkahd kajhsd akhds (default: "aaa
-x -extra-long-option <value> kjsahdkajshkahd kajhsd akhds (default: "aaa
bbb ccc ddd eee fff ggg")
-h, --help output usage information
`;
@@ -59,7 +59,7 @@ Options:
-h, --help output usage information
Commands:
alpha Lorem mollit quis dolor ex do eu quis ad insa
alpha Lorem mollit quis dolor ex do eu quis ad insa
a commodo esse.
`;

80 changes: 49 additions & 31 deletions tests/options.opts.test.js
Original file line number Diff line number Diff line change
@@ -12,44 +12,62 @@ test('when .version used then version in opts', () => {
expect(program.opts()).toEqual({ version });
});

test('when boolean flag not specified then not in opts', () => {
test('when .version used with storeOptionsAsProperties(false) then version not in opts', () => {
// New behaviour, stop storing version as an option value.
const program = new commander.Command();
const version = '0.0.1';
program
.option('--pepper', 'add pepper');
.storeOptionsAsProperties(false)
.version(version);
program.parse(['node', 'test']);
expect(program.opts()).toEqual({ });
});

test('when boolean flag specified then value true', () => {
const program = new commander.Command();
program
.option('--pepper', 'add pepper');
program.parse(['node', 'test', '--pepper']);
expect(program.opts()).toEqual({ pepper: true });
});
describe.each([true, false])('storeOptionsAsProperties is %s', (storeOptionsAsProperties) => {
test('when boolean flag not specified then not in opts', () => {
const program = new commander.Command();
program.storeOptionsAsProperties(storeOptionsAsProperties);
program
.option('--pepper', 'add pepper');
program.parse(['node', 'test']);
expect(program.opts()).toEqual({ });
});

test('when option with required value not specified then not in opts', () => {
const program = new commander.Command();
program
.option('--pepper <flavour>', 'add pepper');
program.parse(['node', 'test']);
expect(program.opts()).toEqual({ });
});
test('when boolean flag specified then value true', () => {
const program = new commander.Command();
program.storeOptionsAsProperties(storeOptionsAsProperties);
program
.option('--pepper', 'add pepper');
program.parse(['node', 'test', '--pepper']);
expect(program.opts()).toEqual({ pepper: true });
});

test('when option with required value specified then value as specified', () => {
const pepperValue = 'red';
const program = new commander.Command();
program
.option('--pepper <flavour>', 'add pepper');
program.parse(['node', 'test', '--pepper', pepperValue]);
expect(program.opts()).toEqual({ pepper: pepperValue });
});
test('when option with required value not specified then not in opts', () => {
const program = new commander.Command();
program.storeOptionsAsProperties(storeOptionsAsProperties);
program
.option('--pepper <flavour>', 'add pepper');
program.parse(['node', 'test']);
expect(program.opts()).toEqual({ });
});

test('when option with default value not specified then default value in opts', () => {
const pepperDefault = 'red';
const program = new commander.Command();
program
.option('--pepper <flavour>', 'add pepper', pepperDefault);
program.parse(['node', 'test']);
expect(program.opts()).toEqual({ pepper: pepperDefault });
test('when option with required value specified then value as specified', () => {
const pepperValue = 'red';
const program = new commander.Command();
program.storeOptionsAsProperties(storeOptionsAsProperties);
program
.option('--pepper <flavour>', 'add pepper');
program.parse(['node', 'test', '--pepper', pepperValue]);
expect(program.opts()).toEqual({ pepper: pepperValue });
});

test('when option with default value not specified then default value in opts', () => {
const pepperDefault = 'red';
const program = new commander.Command();
program.storeOptionsAsProperties(storeOptionsAsProperties);
program
.option('--pepper <flavour>', 'add pepper', pepperDefault);
program.parse(['node', 'test']);
expect(program.opts()).toEqual({ pepper: pepperDefault });
});
});
9 changes: 9 additions & 0 deletions typings/commander-tests.ts
Original file line number Diff line number Diff line change
@@ -10,6 +10,9 @@ const errorInstance = new program.CommanderError(1, 'code', 'message');

const name = program.name();

program.storeOptionsAsProperties(true);
program.passCommandToAction(true);

program
.name('set name')
.version('0.0.1')
@@ -130,4 +133,10 @@ program.exitOverride((err):void => {

program.parse(process.argv);

program.parseAsync(process.argv).then(() => {
console.log('parseAsync success');
}).catch(err => {
console.log('parseAsync failed');
});

console.log('stuff');
101 changes: 55 additions & 46 deletions typings/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// Type definitions for commander 2.11
// Project: https://github.com/visionmedia/commander.js
// Definitions by: Alan Agius <https://github.com/alan-agius4>, Marcelo Dezem <https://github.com/mdezem>, vvakame <https://github.com/vvakame>, Jules Randolph <https://github.com/sveinburne>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// Type definitions for commander
// Original definitions by: Alan Agius <https://github.com/alan-agius4>, Marcelo Dezem <https://github.com/mdezem>, vvakame <https://github.com/vvakame>, Jules Randolph <https://github.com/sveinburne>

///<reference types="node" />

@@ -39,7 +37,6 @@ declare namespace commander {
* which will print the version number when passed.
*
* You can optionally supply the flags and description to override the defaults.
*
*/
version(str: string, flags?: string, description?: string): Command;

@@ -87,8 +84,7 @@ declare namespace commander {
/**
* Define argument syntax for the top-level command.
*
* @param {string} desc
* @returns {Command} for chaining
* @returns Command for chaining
*/
arguments(desc: string): Command;

@@ -97,8 +93,7 @@ declare namespace commander {
*
* For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`.
*
* @param {string[]} args
* @returns {Command} for chaining
* @returns Command for chaining
*/
parseExpectedArgs(args: string[]): Command;

@@ -118,8 +113,7 @@ declare namespace commander {
* // output help here
* });
*
* @param {(...args: any[]) => void} fn
* @returns {Command} for chaining
* @returns Command for chaining
*/
action(fn: (...args: any[]) => void): Command;

@@ -163,11 +157,7 @@ declare namespace commander {
* // optional argument
* program.option('-c, --cheese [type]', 'add cheese [marble]');
*
* @param {string} flags
* @param {string} [description]
* @param {((arg1: any, arg2: any) => void) | RegExp} [fn] function or default
* @param {*} [defaultValue]
* @returns {Command} for chaining
* @returns Command for chaining
*/
option(flags: string, description?: string, fn?: ((arg1: any, arg2: any) => void) | RegExp, defaultValue?: any): Command;
option(flags: string, description?: string, defaultValue?: any): Command;
@@ -181,77 +171,98 @@ declare namespace commander {
requiredOption(flags: string, description?: string, fn?: ((arg1: any, arg2: any) => void) | RegExp, defaultValue?: any): Command;
requiredOption(flags: string, description?: string, defaultValue?: any): Command;


/**
* Whether to store option values as properties on command object,
* or store separately (specify false). In both cases the option values can be accessed using .opts().
*
* @return Command for chaining
*/
storeOptionsAsProperties(value?: boolean): Command;

/**
* Whether to pass command to action handler,
* or just the options (specify false).
*
* @return Command for chaining
*/
passCommandToAction(value?: boolean): Command;

/**
* Allow unknown options on the command line.
*
* @param {boolean} [arg] if `true` or omitted, no error will be thrown for unknown options.
* @returns {Command} for chaining
* @param [arg] if `true` or omitted, no error will be thrown for unknown options.
* @returns Command for chaining
*/
allowUnknownOption(arg?: boolean): Command;

/**
* Parse `argv`, settings options and invoking commands when defined.
* Parse `argv`, setting options and invoking commands when defined.
*
* @param {string[]} argv
* @returns {Command} for chaining
* @returns Command for chaining
*/
parse(argv: string[]): Command;

/**
* Parse options from `argv` returning `argv` void of these options.
* Parse `argv`, setting options and invoking commands when defined.
*
* Use parseAsync instead of parse if any of your action handlers are async. Returns a Promise.
*
* @param {string[]} argv
* @returns {ParseOptionsResult}
* @returns Promise
*/
parseAsync(argv: string[]): Promise<any>;

/**
* Parse options from `argv` returning `argv` void of these options.
*/
parseOptions(argv: string[]): commander.ParseOptionsResult;

/**
* Return an object containing options as key-value pairs
*
* @returns {{[key: string]: any}}
*/
opts(): { [key: string]: any };

/**
* Set the description to `str`.
*
* @param {string} str
* @param {{[argName: string]: string}} argsDescription
* @return {(Command | string)}
* Set the description.
*
* @returns Command for chaining
*/
description(str: string, argsDescription?: {[argName: string]: string}): Command;
/**
* Get the description.
*/
description(): string;

/**
* Set an alias for the command.
*
* @param {string} alias
* @return {(Command | string)}
*
* @returns Command for chaining
*/
alias(alias: string): Command;
/**
* Get alias for the command.
*/
alias(): string;

/**
* Set or get the command usage.
*
* @param {string} str
* @return {(Command | string)}
* Set the command usage.
*
* @returns Command for chaining
*/
usage(str: string): Command;
/**
* Get the command usage.
*/
usage(): string;

/**
* Set the name of the command.
*
* @param {string} str
* @return {Command}
*
* @returns Command for chaining
*/
name(str: string): Command;

/**
* Get the name of the command.
*
* @return {string}
*/
name(): string;

@@ -260,8 +271,6 @@ declare namespace commander {
*
* When listener(s) are available for the helpLongFlag
* those callbacks are invoked.
*
* @param {(str: string) => string} [cb]
*/
outputHelp(cb?: (str: string) => string): void;