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: v4.0.0
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: 4.0.1
Choose a head ref
  • 5 commits
  • 5 files changed
  • 2 contributors

Commits on Nov 2, 2019

  1. Copy the full SHA
    6bfc580 View commit details

Commits on Nov 11, 2019

  1. Copy the full SHA
    36082ba View commit details
  2. Copy the full SHA
    88d1341 View commit details
  3. Bump version for 4.0.1

    shadowspawn committed Nov 11, 2019
    Copy the full SHA
    b38f262 View commit details
  4. Merge pull request #1094 from shadowspawn/release/4.0.1

    Prepare for release 4.0.1
    abetomo authored Nov 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
    5083e14 View commit details
Showing with 94 additions and 10 deletions.
  1. +24 −0 CHANGELOG.md
  2. +16 −8 index.js
  3. +1 −1 package-lock.json
  4. +1 −1 package.json
  5. +52 −0 tests/options.mandatory.test.js
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

<!-- markdownlint-disable MD024 -->

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

### Fixed

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

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

### Added
@@ -32,6 +38,22 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
* removed deprecated `customFds` option from call to `child_process.spawn` ([#1052])
* rework TypeScript declarations to bring all types into imported namespace ([#1081])

### Migration Tips

#### Testing for no arguments

If you were previously using code like:

```js
if (!program.args.length) ...
```

a partial replacement is:

```js
if (program.rawArgs.length < 3) ...
```

## [4.0.0-1] Prerelease (2019-10-08)

(Released in 4.0.0)
@@ -503,8 +525,10 @@ program
[#1053]: https://github.com/tj/commander.js/pull/1053
[#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

[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
[4.0.0]: https://github.com/tj/commander.js/compare/v3.0.2..v4.0.0
[4.0.0-1]: https://github.com/tj/commander.js/compare/v4.0.0-0..v4.0.0-1
[4.0.0-0]: https://github.com/tj/commander.js/compare/v3.0.2...v4.0.0-0
24 changes: 16 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
@@ -324,6 +324,7 @@ Command.prototype.action = function(fn) {

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

// If there are still any unknown options, then we simply
// die, unless someone asked for help, in which case we give it
@@ -563,10 +564,17 @@ Command.prototype.parse = function(argv) {

if (args[0] === 'help' && args.length === 1) this.help();

// Note for future: we could return early if we found an action handler in parseArgs, as none of following code needed?

// <cmd> --help
if (args[0] === 'help') {
args[0] = args[1];
args[1] = this._helpLongFlag;
} else {
// If calling through to executable subcommand we could check for help flags before failing,
// but a somewhat unlikely case since program options not passed to executable subcommands.
// Wait for reports to see if check needed and what usage pattern is.
this._checkForMissingMandatoryOptions();
}

// executable sub-commands
@@ -832,12 +840,14 @@ Command.prototype.optionFor = function(arg) {
*/

Command.prototype._checkForMissingMandatoryOptions = function() {
const self = this;
this.options.forEach((anOption) => {
if (anOption.mandatory && (self[anOption.attributeName()] === undefined)) {
self.missingMandatoryOptionValue(anOption);
}
});
// 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)) {
cmd.missingMandatoryOptionValue(anOption);
}
});
}
};

/**
@@ -916,8 +926,6 @@ Command.prototype.parseOptions = function(argv) {
args.push(arg);
}

this._checkForMissingMandatoryOptions();

return { args: args, unknown: unknownOptions };
};

2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "commander",
"version": "4.0.0",
"version": "4.0.1",
"description": "the complete solution for node.js command-line programs",
"keywords": [
"commander",
52 changes: 52 additions & 0 deletions tests/options.mandatory.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const commander = require('../');

// Assuming mandatory options behave as normal options apart from the mandatory aspect, not retesting all behaviour.
// Likewise, not redoing all tests on subcommand after testing on program.

describe('required program option with mandatory value specified', () => {
test('when program has required value specified then value as specified', () => {
@@ -224,3 +225,54 @@ describe('required command option with mandatory value not specified', () => {
}).not.toThrow();
});
});

describe('missing mandatory option but help requested', () => {
// Optional. Use internal knowledge to suppress output to keep test output clean.
let writeSpy;

beforeAll(() => {
writeSpy = jest.spyOn(process.stdout, 'write').mockImplementation(() => { });
});

afterEach(() => {
writeSpy.mockClear();
});

afterAll(() => {
writeSpy.mockRestore();
});

test('when program has required option not specified and --help then help', () => {
const program = new commander.Command();
program
.exitOverride()
.requiredOption('--cheese <type>', 'cheese type');

let caughtErr;
try {
program.parse(['node', 'test', '--help']);
} catch (err) {
caughtErr = err;
}

expect(caughtErr.code).toEqual('commander.helpDisplayed');
});

test('when program has required option not specified and subcommand --help then help', () => {
const program = new commander.Command();
program
.exitOverride()
.requiredOption('--cheese <type>', 'cheese type')
.command('sub')
.action(() => {});

let caughtErr;
try {
program.parse(['node', 'test', 'sub', '--help']);
} catch (err) {
caughtErr = err;
}

expect(caughtErr.code).toEqual('commander.helpDisplayed');
});
});