-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
passThroughOptions
and unknown option handling
#1936
Comments
Unknown options normally generate an error, so this only comes up when also using
Quoting from https://github.com/tj/commander.js#parsing-configuration
So the combination of features means the unknown option is treated as a command-argument, and |
Thanks, this made the reasoning clear for me. Still, the discrepancy between how the setting is implemented and how it is presented in the docs and in the code comments had had me scratching my head for a while when trying to figure out the internals of
|
Includes a partial fix for tj#1936 (docs need an update, too)
#1942 should fix this issue. |
I don't consider Recent related comment: #1944 (comment) |
The PR is mainly useful for contributors trying to understand |
Another attempt at a more clear comment for the early exit due to // If using passThroughOptions and no subcommand has been found,
// stop processing as soon as recognised options end.
// All remaining arguments are treated as command-arguments,
// except when the first one is an unknown option.
if (this._passThroughOptions) {
/* ... */ The last two comment lines are optional. What do you think? |
A variant for #1934: // If using passThroughOptions and no subcommand has been found,
// stop consuming all options except help as soon as recognised options end.
// Only consume the help option if found before command-arguments.
// Exit loop as early as possible.
// All remaining arguments are treated as command-arguments,
// except when the first one is an unknown option.
if (this._passThroughOptions) {
onlyConsumeHelpOption = true;
if (!this._hasHelpOption || !isArgFlag) {
dest.push(arg, ...args);
break;
}
} The last two lines are optional here, too. |
Edit: this was in reply to:
First two lines are ok. I would leave out the "and no subcommand has been found". (I had another go too, but didn't do any better!) |
That would make the "as soon as recognised options end" part sound a little too strong for someone reading the comment before all the code above it, for example someone who was only looking for implementation details of Update: The part you'd like to leave out will also be necessary if it is decided to change the semantics for |
Hmm. There is lots of implied context from the code above, and the comment could be interpreted as being checked in the line below (i.e. I'll have another go at my abandoned attempt. How about:
|
It is all summarized very well in the two lines I suggested, so a lazy reader would not have to go look at the code above to figure it out.
Not sure I understand this. If your concern is that a reader might think there is a subcommand check below, then we could rephrase the comment to // No subcommand will be dispatched if this line is reached.
// If using passThroughOptions, stop processing as soon as recognised options end. Technically, those are two completely separate comments. Could even put an extra line break between them.
A combination like // If using passThroughOptions and no subcommand has been found,
// stop processing as soon as an argument that is not a recognised option is encountered.
// All remaining arguments (including options) are treated as command-arguments and passed through
// unless the first one is an option, in which case it is reported as unknown. with the last line being optional would be nice. Might be a little too long, but I don't think it's a big problem as long as the explanation is clear and doesn't contain unnecessary details. |
Summary of my suggestion…with a couple of new improvements to the comment. // If using passThroughOptions and no subcommand has been found (always the case when this line is reached),
// stop processing as soon as an argument that is not a recognised option is encountered.
// All remaining arguments (including options) are treated as command-arguments and passed through
// unless the first one is an option, in which case it is reported as unknown
// because dest is unknown in that case.
if (this._passThroughOptions) {
/* ... */ If I had managed to convince you in #1945, the last two line could be made more precise: // If using passThroughOptions and no subcommand has been found (always the case when this line is reached),
// stop processing as soon as an argument that is not a recognised option is encountered.
// All remaining arguments (including options) are treated as command-arguments and passed through
// unless allowUnknownOption is off and the first argument is an option, in which case it is reported as unknown
// because dest is unknown in that case.
if (this._passThroughOptions) {
/* ... */ If the functional overlap with // If using passThroughOptions and no subcommand has been found,
// stop processing as soon as an argument that is not a recognised option is encountered.
// All remaining arguments (including options) are treated as command-arguments and passed through
// unless the first one is an option, in which case it is reported as unknown
// because dest is unknown in that case.
if (this._passThroughOptions && !reprocessedBySubcommand) {
/* ... */ Here, A variant for #1934: // If using passThroughOptions and no subcommand has been found (always the case when this line is reached),
// stop consuming options except help as soon as an argument that is not a recognised option is encountered.
// Only consume the help option if found before command-arguments.
// Stop processing and exit the loop as early as possible.
// All remaining arguments (including options) are treated as command-arguments and passed through
// unless the first one is an option, in which case it is reported as unknown
// because dest is unknown in that case.
if (this._passThroughOptions) {
onlyConsumeHelpOption = true;
if (!this._hasHelpOption || !isArgFlag) {
dest.push(arg, ...args);
break;
}
} With all the tweaks combined (my dream): // If using passThroughOptions and no subcommand has been found,
// stop consuming options except help as soon as an argument that is not a recognised option is encountered.
// Only consume the help option if found before command-arguments.
// Stop processing and exit the loop as early as possible.
// All remaining arguments (including options) are treated as command-arguments and passed through
// unless allowUnknownOption is off and the first argument is an option, in which case it is reported as unknown
// because dest is unknown in that case.
if (this._passThroughOptions && !reprocessedBySubcommand) {
onlyConsumeHelpOption = true;
if (!this._hasHelpOption || !isArgFlag) {
dest.push(arg, ...args);
break;
}
} |
The related PR has already been rejected. To a large extent, I don't like Thank you for your contributions. |
Is there a particular reason why
.parseOptions()
stops processing args when an unknown option is encountered in thepassThroughOptions
mode? That complicates the help option refactor I am currently working on in #1934 that aims to achieve maximum consistency between how the help option and all other options are handled, because help flags end up ignored if they come after an unknown option and there is no subcommand.Checkout 02fde0e and run the following code for a demo:
I tried to change the behavior to the one implied by the docs and comments in code so that the processing is only stopped when a command-argument is encountered (see fcfbec1), but that resulted in a test failing.
Relevant test from
tests/command.positionalOptions.test.js
(line 423)The behavior I would expect:
--debug
is consumed and soprogram.opts()
equals to{ debug: true }
whileprogram.args
equals to['--unknown']
.Relevant lines in
.parseOptions()
code (on currentdevelop
branch)The condition on line 2 should be
this._passThroughOptions && !maybeOption(arg)
so that the comment on line 1 describes what happens well and does not miss the fact the processing is also stopped at unknown options (currently missed in all explanations ofpassThroughOptions
).The text was updated successfully, but these errors were encountered: