Skip to content

Commit

Permalink
Correctly handle allowUnknownOption in parseOptions()
Browse files Browse the repository at this point in the history
Partially borrowed from tj#1934
  • Loading branch information
aweebit committed Aug 7, 2023
1 parent d038570 commit 5e35531
Showing 1 changed file with 40 additions and 19 deletions.
59 changes: 40 additions & 19 deletions lib/command.js
Original file line number Diff line number Diff line change
Expand Up @@ -1445,6 +1445,8 @@ Expecting one of '${allowedValues.join("', '")}'`);

// parse options
let activeVariadicOption = null;
let onlyKnownOptionsSoFar = true;
let reprocessedBySubcommand = false;
while (args.length) {
const arg = args.shift();

Expand Down Expand Up @@ -1510,31 +1512,50 @@ Expecting one of '${allowedValues.join("', '")}'`);
}
}

// Not a recognised option by this command.
// Might be a command-argument, or subcommand option, or unknown option, or help command or option.

// An unknown option means further arguments also classified as unknown so can be reprocessed by subcommands.
if (maybeOption(arg)) {
dest = unknown;
}

// If using positionalOptions, stop processing our options at subcommand.
if ((this._enablePositionalOptions || this._passThroughOptions) && operands.length === 0 && unknown.length === 0) {
// Not a recognised option by this command. Might be
// - a subcommand,
// - the help option encountered after a subcommand (considered unknown),
// - any other option unknown to this command,
// - or a command-argument.

if (onlyKnownOptionsSoFar && !reprocessedBySubcommand) {
reprocessedBySubcommand = true; // reset to false later if not entering subcommand
const stopAtSubcommand = (
this._enablePositionalOptions || this._passThroughOptions
);
if (this._findCommand(arg)) {
operands.push(arg);
if (args.length > 0) unknown.push(...args);
break;
if (stopAtSubcommand) {
operands.push(arg);
unknown.push(...args);
break;
}
} else if (arg === this._helpCommandName && this._hasImplicitHelpCommand()) {
operands.push(arg);
if (args.length > 0) operands.push(...args);
break;
if (stopAtSubcommand) {
operands.push(arg, ...args);
break;
}
} else if (this._defaultCommandName) {
unknown.push(arg);
if (args.length > 0) unknown.push(...args);
break;
if (stopAtSubcommand) {
unknown.push(arg, ...args);
break;
}
} else {
reprocessedBySubcommand = false;
}
}

onlyKnownOptionsSoFar = false;

// Respect the fact that the working principle of allowUnknownOption is to treat unknown options as command-arguments.
const treatUnknownOptionAsCommandArgument = (
this._allowUnknownOption && !reprocessedBySubcommand
);

// An unknown option means further arguments also classified as unknown so can be reprocessed by subcommands.
if (maybeOption(arg) && !treatUnknownOptionAsCommandArgument) {
dest = unknown;
}

// If using passThroughOptions, stop processing options at first command-argument.
if (this._passThroughOptions) {
dest.push(arg);
Expand Down

0 comments on commit 5e35531

Please sign in to comment.