Skip to content

Commit a9503bb

Browse files
authored
Allow boolean default for flag option (#987)
* Add support for default value for boolean flags * Expand testing for boolean flags to cover new features * Add written description of boolean default value for --foo/--no-foo * Avoid eslint warnings for requiring shouldjs in simple way
1 parent 55e88dc commit a9503bb

File tree

7 files changed

+105
-73
lines changed

7 files changed

+105
-73
lines changed

Readme.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,9 @@ cheese: stilton
100100
101101
You can specify a boolean option long name with a leading `no-` to set the option value to false when used.
102102
Defined alone this also makes the option true by default.
103-
If you define `foo` first, adding `--no-foo` does not change the default value.
104103
104+
If you define `--foo` first, adding `--no-foo` does not change the default value from what it would
105+
otherwise be. You can specify a default boolean value for a boolean flag and it can be overridden on command line.
105106
106107
```js
107108
const program = require('commander');

index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -371,8 +371,8 @@ Command.prototype.option = function(flags, description, fn, defaultValue) {
371371
}
372372
}
373373

374-
// preassign default value only for --no-*, [optional], or <required>
375-
if (option.negate || option.optional || option.required) {
374+
// preassign default value for --no-*, [optional], <required>, or plain flag if boolean value
375+
if (option.negate || option.optional || option.required || typeof defaultValue === 'boolean') {
376376
// when --no-foo we make sure default is true, unless a --foo option is already defined
377377
if (option.negate) {
378378
var opts = self.opts();
@@ -396,7 +396,7 @@ Command.prototype.option = function(flags, description, fn, defaultValue) {
396396
val = fn(val, self[name] === undefined ? defaultValue : self[name]);
397397
}
398398

399-
// unassigned or bool
399+
// unassigned or boolean value
400400
if (typeof self[name] === 'boolean' || typeof self[name] === 'undefined') {
401401
// if no value, negate false, and we have a default, then use it!
402402
if (val == null) {

test/test.options.bool.js

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
1-
/**
2-
* Module dependencies.
3-
*/
1+
const commander = require('../');
2+
require('should');
43

5-
var program = require('../')
6-
, should = require('should');
4+
// Test simple flag and negatable flag
75

8-
program
9-
.version('0.0.1')
10-
.option('-p, --pepper', 'add pepper')
11-
.option('-c, --no-cheese', 'remove cheese');
6+
function simpleFlagProgram() {
7+
const program = new commander.Command();
8+
program
9+
.option('-p, --pepper', 'add pepper')
10+
.option('-C, --no-cheese', 'remove cheese');
11+
return program;
12+
}
1213

13-
program.parse(['node', 'test', '--pepper']);
14-
program.pepper.should.be.true();
15-
program.cheese.should.be.true();
14+
const simpleFlagNoOptions = simpleFlagProgram();
15+
simpleFlagNoOptions.parse(['node', 'test']);
16+
simpleFlagNoOptions.should.not.have.property('pepper');
17+
simpleFlagNoOptions.cheese.should.be.true();
18+
19+
const simpleFlagLong = simpleFlagProgram();
20+
simpleFlagLong.parse(['node', 'test', '--pepper', '--no-cheese']);
21+
simpleFlagLong.pepper.should.be.true();
22+
simpleFlagLong.cheese.should.be.false();

test/test.options.bool.no.js

Lines changed: 63 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,63 @@
1-
/**
2-
* Module dependencies.
3-
*/
4-
5-
var program = require('../')
6-
, should = require('should');
7-
8-
program
9-
.version('0.0.1')
10-
.option('-e, --everything', 'add all of the toppings')
11-
.option('-p, --pepper', 'add pepper')
12-
.option('-P, --no-pepper', 'remove pepper')
13-
.option('-c|--no-cheese', 'remove cheese');
14-
15-
program.parse(['node', 'test']);
16-
program.should.not.have.property('everything');
17-
program.should.not.have.property('pepper');
18-
program.cheese.should.be.true();
19-
20-
program.parse(['node', 'test', '--everything']);
21-
program.everything.should.be.true();
22-
program.should.not.have.property('pepper');
23-
program.cheese.should.be.true();
24-
25-
program.parse(['node', 'test', '--pepper']);
26-
program.pepper.should.be.true();
27-
program.cheese.should.be.true();
28-
29-
program.parse(['node', 'test', '--everything', '--no-pepper', '--no-cheese']);
30-
program.pepper.should.be.false();
31-
program.cheese.should.be.false();
1+
const commander = require('../');
2+
require('should');
3+
4+
// Test combination of flag and --no-flag
5+
// (negatable flag on its own is tested in test.options.bool.js)
6+
7+
function flagProgram(defaultValue) {
8+
const program = new commander.Command();
9+
program
10+
.option('-p, --pepper', 'add pepper', defaultValue)
11+
.option('-P, --no-pepper', 'remove pepper');
12+
return program;
13+
}
14+
15+
// Flag with no default, normal usage.
16+
17+
const programNoDefaultNoOptions = flagProgram();
18+
programNoDefaultNoOptions.parse(['node', 'test']);
19+
programNoDefaultNoOptions.should.not.have.property('pepper');
20+
21+
const programNoDefaultWithFlag = flagProgram();
22+
programNoDefaultWithFlag.parse(['node', 'test', '--pepper']);
23+
programNoDefaultWithFlag.pepper.should.be.true();
24+
25+
const programNoDefaultWithNegFlag = flagProgram();
26+
programNoDefaultWithNegFlag.parse(['node', 'test', '--no-pepper']);
27+
programNoDefaultWithNegFlag.pepper.should.be.false();
28+
29+
// Flag with default, say from an environment variable.
30+
31+
const programTrueDefaultNoOptions = flagProgram(true);
32+
programTrueDefaultNoOptions.parse(['node', 'test']);
33+
programTrueDefaultNoOptions.pepper.should.be.true();
34+
35+
const programTrueDefaultWithFlag = flagProgram(true);
36+
programTrueDefaultWithFlag.parse(['node', 'test', '-p']);
37+
programTrueDefaultWithFlag.pepper.should.be.true();
38+
39+
const programTrueDefaultWithNegFlag = flagProgram(true);
40+
programTrueDefaultWithNegFlag.parse(['node', 'test', '-P']);
41+
programTrueDefaultWithNegFlag.pepper.should.be.false();
42+
43+
const programFalseDefaultNoOptions = flagProgram(false);
44+
programFalseDefaultNoOptions.parse(['node', 'test']);
45+
programFalseDefaultNoOptions.pepper.should.be.false();
46+
47+
const programFalseDefaultWithFlag = flagProgram(false);
48+
programFalseDefaultWithFlag.parse(['node', 'test', '-p']);
49+
programFalseDefaultWithFlag.pepper.should.be.true();
50+
51+
const programFalseDefaultWithNegFlag = flagProgram(false);
52+
programFalseDefaultWithNegFlag.parse(['node', 'test', '-P']);
53+
programFalseDefaultWithNegFlag.pepper.should.be.false();
54+
55+
// Flag specified both ways, last one wins.
56+
57+
const programNoYes = flagProgram();
58+
programNoYes.parse(['node', 'test', '--no-pepper', '--pepper']);
59+
programNoYes.pepper.should.be.true();
60+
61+
const programYesNo = flagProgram();
62+
programYesNo.parse(['node', 'test', '--pepper', '--no-pepper']);
63+
programYesNo.pepper.should.be.false();

test/test.options.bool.small.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
/**
2-
* Module dependencies.
3-
*/
4-
5-
var program = require('../')
6-
, should = require('should');
1+
var program = require('../');
2+
require('should');
73

84
program
95
.version('0.0.1')

test/test.options.defaults.given.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
1-
/**
2-
* Module dependencies.
3-
*/
4-
5-
var program = require('../')
6-
, should = require('should');
1+
const program = require('../');
2+
require('should');
73

84
program
9-
.version('0.0.1')
105
.option('-a, --anchovies', 'Add anchovies?')
116
.option('-o, --onions', 'Add onions?', true)
7+
.option('-O, --no-onions', 'No onions')
8+
.option('-t, --tomatoes', 'Add tomatoes?', false)
9+
.option('-T, --no-tomatoes', 'No tomatoes')
1210
.option('-v, --olives', 'Add olives? Sorry we only have black.', 'black')
1311
.option('-s, --no-sauce', 'Uh… okay')
1412
.option('-r, --crust <type>', 'What kind of crust would you like?', 'hand-tossed')
1513
.option('-c, --cheese [type]', 'optionally specify the type of cheese', 'mozzarella');
1614

17-
program.parse(['node', 'test', '--anchovies', '--onions', '--olives', '--no-sauce', '--crust', 'thin', '--cheese', 'wensleydale']);
15+
program.parse(['node', 'test', '--anchovies', '--no-onions', '--tomatoes', '--olives', '--no-sauce', '--crust', 'thin', '--cheese', 'wensleydale']);
1816
program.should.have.property('anchovies', true);
19-
program.should.have.property('onions', true);
17+
program.should.have.property('onions', false);
18+
program.should.have.property('tomatoes', true);
2019
program.should.have.property('olives', 'black');
2120
program.should.have.property('sauce', false);
2221
program.should.have.property('crust', 'thin');

test/test.options.defaults.js

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,22 @@
1-
/**
2-
* Module dependencies.
3-
*/
4-
5-
var program = require('../')
6-
, should = require('should');
1+
const program = require('../');
2+
require('should');
73

84
program
9-
.version('0.0.1')
105
.option('-a, --anchovies', 'Add anchovies?')
116
.option('-o, --onions', 'Add onions?', true)
7+
.option('-O, --no-onions', 'No onions')
8+
.option('-t, --tomatoes', 'Add tomatoes?', false)
9+
.option('-T, --no-tomatoes', 'No tomatoes')
1210
.option('-v, --olives', 'Add olives? Sorry we only have black.', 'black')
1311
.option('-s, --no-sauce', 'Uh… okay')
1412
.option('-r, --crust <type>', 'What kind of crust would you like?', 'hand-tossed')
1513
.option('-c, --cheese [type]', 'optionally specify the type of cheese', 'mozzarella');
1614

17-
program.should.have.property('_name', '');
18-
1915
program.parse(['node', 'test']);
20-
program.should.have.property('_name', 'test');
16+
2117
program.should.not.have.property('anchovies');
22-
program.should.not.have.property('onions');
18+
program.should.have.property('onions', true);
19+
program.should.have.property('tomatoes', false);
2320
program.should.not.have.property('olives');
2421
program.should.have.property('sauce', true);
2522
program.should.have.property('crust', 'hand-tossed');

0 commit comments

Comments
 (0)