Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions lib/command.js
Original file line number Diff line number Diff line change
Expand Up @@ -1128,14 +1128,18 @@ Expecting one of '${allowedValues.join("', '")}'`);
this.getOptionValue(option.attributeName()) === undefined,
)
.forEach((option) => {
// check for lone negated option: --no-foo without a --foo option
const positiveLongFlag = option.long.replace(/^--no-/, '--');
if (!this._findOption(positiveLongFlag)) {
this.setOptionValueWithSource(
option.attributeName(),
true,
'default',
);
// Check for lone negated option: a --no-foo without any positive
// option sharing its attribute. Matching by attribute (like
// DualOptions) rather than by reconstructing the --foo flag string
// also recognises a differently-spelled positive such as -f or
// --fooBar as the pair, so it is not wrongly treated as lone.
const attributeName = option.attributeName();
const hasPositiveOption = this.options.some(
(candidate) =>
!candidate.negate && candidate.attributeName() === attributeName,
);
if (!hasPositiveOption) {
this.setOptionValueWithSource(attributeName, true, 'default');
}
});

Expand Down
23 changes: 23 additions & 0 deletions tests/options.bool.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,29 @@ describe('boolean option with non-boolean default', () => {
});
});

describe('negated option paired with a differently-spelled positive', () => {
test('when short-only positive and negated then no implicit default', () => {
const program = new commander.Command();
program.option('-c', 'add cheese').option('--no-c', 'remove cheese');
program.parse([], { from: 'user' });
assert.equal(program.opts().c, undefined);
});

test('when camelCase positive and kebab-case negated then no implicit default', () => {
const program = new commander.Command();
program.option('--fooBar').option('--no-foo-bar');
program.parse([], { from: 'user' });
assert.equal(program.opts().fooBar, undefined);
});

test('when only negated (lone) then implicit default is still true', () => {
const program = new commander.Command();
program.option('--no-sauce');
program.parse([], { from: 'user' });
assert.equal(program.opts().sauce, true);
});
});

// Regression test for #1301 with `-no-` in middle of option
describe('regression test for -no- in middle of option name', () => {
test('when option not specified then value is undefined', () => {
Expand Down