Skip to content

Commit 966720a

Browse files
authored
Throw for unsupported option flag setup (#2270)
1 parent 2c9051a commit 966720a

File tree

3 files changed

+62
-13
lines changed

3 files changed

+62
-13
lines changed

lib/option.js

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -312,17 +312,32 @@ function camelcase(str) {
312312
function splitOptionFlags(flags) {
313313
let shortFlag;
314314
let longFlag;
315-
// Use original very loose parsing to maintain backwards compatibility for now,
316-
// which allowed for example unintended `-sw, --short-word` [sic].
317-
const flagParts = flags.split(/[ |,]+/);
318-
if (flagParts.length > 1 && !/^[[<]/.test(flagParts[1]))
319-
shortFlag = flagParts.shift();
320-
longFlag = flagParts.shift();
321-
// Add support for lone short flag without significantly changing parsing!
322-
if (!shortFlag && /^-[^-]$/.test(longFlag)) {
323-
shortFlag = longFlag;
324-
longFlag = undefined;
325-
}
315+
// short flag, single dash and single character
316+
const shortFlagExp = /^-[^-]$/;
317+
// long flag, double dash and at least one character
318+
const longFlagExp = /^--[^-]/;
319+
320+
const flagParts = flags.split(/[ |,]+/).concat('guard');
321+
if (shortFlagExp.test(flagParts[0])) shortFlag = flagParts.shift();
322+
if (longFlagExp.test(flagParts[0])) longFlag = flagParts.shift();
323+
324+
// Check for some unsupported flags that people try.
325+
if (/^-[^-][^-]/.test(flagParts[0]))
326+
throw new Error(
327+
`invalid Option flags, short option is dash and single character: '${flags}'`,
328+
);
329+
if (shortFlag && shortFlagExp.test(flagParts[0]))
330+
throw new Error(
331+
`invalid Option flags, more than one short flag: '${flags}'`,
332+
);
333+
if (longFlag && longFlagExp.test(flagParts[0]))
334+
throw new Error(
335+
`invalid Option flags, more than one long flag: '${flags}'`,
336+
);
337+
// Generic error if failed to find a flag or an unexpected flag left over.
338+
if (!(shortFlag || longFlag) || flagParts[0].startsWith('-'))
339+
throw new Error(`invalid Option flags: '${flags}'`);
340+
326341
return { shortFlag, longFlag };
327342
}
328343

tests/option.bad-flags.test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const { Option } = require('../');
2+
3+
// Check that unsupported flags throw.
4+
test.each([
5+
{ flags: '-a, -b' }, // too many short flags
6+
{ flags: '-a, -b <value>' },
7+
{ flags: '-a, -b, --long' },
8+
{ flags: '--one, --two' }, // too many long flags
9+
{ flags: '--one, --two [value]' },
10+
{ flags: '-ws' }, // short flag with more than one character
11+
{ flags: 'sdkjhskjh' }, // oops, no flags
12+
{ flags: '-a,-b' }, // try all the separators
13+
{ flags: '-a|-b' },
14+
{ flags: '-a -b' },
15+
])('when construct Option with flags %p then throw', ({ flags }) => {
16+
expect(() => {
17+
new Option(flags);
18+
}).toThrow(/^invalid Option flags/);
19+
});
20+
21+
// Check that supported flags do not throw.
22+
test.each([
23+
{ flags: '-s' }, // single short
24+
{ flags: '--long' }, // single long
25+
{ flags: '-b, --both' }, // short and long
26+
{ flags: '-b,--both <comma>' },
27+
{ flags: '-b|--both <bar>' },
28+
{ flags: '-b --both [space]' },
29+
{ flags: '-v, --variadic <files...>' },
30+
])('when construct Option with flags %p then do not throw', ({ flags }) => {
31+
expect(() => {
32+
new Option(flags);
33+
}).not.toThrow();
34+
});

tests/options.registerClash.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ describe('.option()', () => {
2929
test('when reuse flags in subcommand then does not throw', () => {
3030
expect(() => {
3131
const program = new Command();
32-
program.option('e, --example');
33-
program.command('sub').option('e, --example');
32+
program.option('-e, --example');
33+
program.command('sub').option('-e, --example');
3434
}).not.toThrow();
3535
});
3636
});

0 commit comments

Comments
 (0)