-
Notifications
You must be signed in to change notification settings - Fork 118
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
Version 21.1.0 breaks "--"-related configuration #453
Comments
@Toberumono this issue is most likely caused by: Can you coordinate a fix with @kherock? |
@bcoe From what I can tell, yargs-parser doesn't have any way of knowing what a command is (valid or otherwise) - it just handles "commands" as positional arguments. Therefore, we have two options:
I would recommend option 2 because it is new functionality and option 1 would require some significant changes in yargs-parser and yargs because the tree of commands would have to be computed before parsing takes place. While option 1's requirements are certainly not insurmountable, they is more complicated than necessary. As for flag names, I'm thinking, "treat-all-non-flags-as-invalid" or something similar. |
@Toberumono I think that the changes fixing the bug in my PR is valid, I don't want to make the old behavior the default since it definitely wasn't intuitive. The crux of that issue was that Just so I understand the issue correctly, the issue is that instead of |
@kherock The reason why I was suggesting a flag is that your change breaks scripts that use yargs to process the command to execute and the command's arguments. What you set the flags to stops mattering after applying your change because everything without a '-' at the start is treated as permanently invalid. For a more complete example, assume the following script: const yargs = require('yargs');
yargs(process.argv.slice(2)) // Node's process.argv array includes both itself and the executed filename
.parserConfiguration({"populate--": true, "unknown-options-as-args": true, "halt-at-non-option": true})
.option("thing", {
type: "boolean",
"etc": "etc"
})
.command("valid", "A valid command", innerYargs => innerYargs
.command("command", "A valid command", () => {}, args => console.log("Do something else here", args.thing)),
args => console.log("Do something here", args.thing))
.parse(); Before version 21.1.0, that script would properly handle:
Additionally, it would place anything that doesn't obey the valid command tree in the "--" section except for defined options, which would be properly parsed into the arguments object (creating a full example for that would take a while, so I'd rather not provide it unless it's necessary). After version 21.1.0, that script treats all of the above examples as not having any commands. |
I was actually able to reproduce a simpler version of this issue on v21.0.1, just enable
The same issue will occur where args end up in yargs-parser/lib/yargs-parser.ts Lines 397 to 402 in c474bc1
For your use, you can see that |
👋 not ignoring this thread, but will need to set aside an hour to fully grok these two competing expectations for functionality. |
@Toberumono your example:
Would work with yargs' default behaviour without setting:
Could you explain to me, with some illustrative examples of inputs, the specific behaviour you're trying to achieve using these three settings in tandem. Context: I'm on the fence about rolling back @kherock's change. I'm inclined to not call it breaking, even though it obviously broke some people in the wild. Because the behaviour is not part of the documented contract of the API. I would rather figure out if there's a way to get the behavior you're looking for without such a complex parser configuration. |
@bcoe Regarding rolling back kherock's change, I think that the agreement that rolling it back isn't necessary is somewhere in the spiderweb of related PRs and issues, but I don't remember where. In summary, this has morphed into a, "the functionality is generally unexpected and should probably be updated a bit". For specific functionality, I basically want "unknown-options-as-args" and "halt-at-non-option" to work with commands subcommands. As of right now, if you combine the two ("populate--" isn't strictly part of the problem), the parser treats all commands and subcommands as invalid because yargs-parser does not receive non-flag tokens from yargs. For my expanded example, I'll be assuming the following:
Valid flags:
The examples assume that every command points to a magic function that prints out all valid tokens in one array followed by the leftover tokens after hitting the first invalid one.
If the examples aren't sufficient, I can try to simplify the script I'm currently working with to a usable (and sharable - it's internal code, so I can't post it publicly) version, but that would take quite a while (it's a process automation script that's pushing half a megabyte of code across 40-ish files). |
@Toberumono on my mind, is given these examples, something close to @kherock's patch that populates "_" rather than "--" for unknown options and positionals, rather than "--". If we could extend this fix to address all your examples, would we be on the right track? |
The problem is definitely linked to I also can't replicate the description of the expected behavior exactly - there's no config where import yargs from 'yargs';
const parse = (argv) => yargs(argv)
.parserConfiguration({ 'unknown-options-as-args': true, 'halt-at-non-option': true })
.option('first', { type: 'boolean' })
.option('second', { type: 'boolean' })
.option('third', { type: 'boolean' })
.command('$0', 'no command', () => { }, argv => console.log(argv))
.command(
'valid',
'A command',
yargs => yargs
.command('command', 'A valid command', () => { }, argv => console.log('valid command', argv))
.command('thing', 'A valid command', () => { }, argv => console.log('valid thing', argv)),
argv => console.log('valid', argv),
)
.command(
'foo',
'A command',
yargs => yargs
.command('bar', 'A foo command', {}, argv => console.log('foo bar', argv)),
argv => console.log('foo', argv),
)
.command(
'bar',
'A command',
() => { },
argv => console.log('bar', argv)
)
.parse();
parse(['valid']); // valid { _: [ 'valid' ], '$0': 'script.js' }
parse(['valid', '--first']); // valid { _: [ 'valid' ], first: true, '$0': 'script.js' }
parse(['--first']); // { _: [], first: true, '$0': 'script.js' }
parse(['valid', '--what', '--first']); // valid { _: [ 'valid', '--what' ], first: true, '$0': 'script.js' }
parse(['valid', 'command']); // valid command { _: [ 'valid', 'command' ], '$0': 'script.js' }
parse(['command']); // { _: [ 'command' ], '$0': 'script.js' }
parse(['valid', 'bar']); // valid { _: [ 'valid', 'bar' ], '$0': 'script.js' } Then when I change
I realize that in an ideal world, the logic governing Lastly, additionally turning off Hopefully we can agree that the configuration being discussed here still has a bug pre-v21.1 where
|
@bcoe If we could extend the fix, it would be on the right track; however, as I stated on that same PR, I'm concerned that this is just shunting the confusing behavior of these parameters from one spot to another. I'm assuming that there is a reason that yargs cannot provide yargs-parser with a tree of legal commands? @kherock For that feature, I think that option 3 would be the best. The caveat in option 1 is something that I'm currently dealing with in my code, and isn't necessarily rocket science to account for, but would be nice to avoid. The same concerns I had with changing the behavior of My initial idea for how to implement it was to provide a valid command tree, but given the descriptions of how yargs works under the hood, I doubt that a prefix tree starting from root would be possible. |
Commands are lazy loaded, so for nested commands (the way yargs is designed) there's no way to know the full set prior to calling parse. Also, since order matters This aside, I like the separation of concerns that commands live in Next stepsIf you submit a few failing tests to If you centre these tests around some real world user behavior, e.g., the types of things people would enter into your command line app, then it will help determine if there is additional functionality we should add to |
@bcoe RE separation of functionality: I agree on that front. The main reason that I suggested providing a prefix tree of tokens was that it avoids adding the full concept of commands to yargs-parser (to be clear, I'm not recommending going that route here - given commands are lazy-loaded, pre-computing a prefix tree isn't an option). RE the more general stuff / next steps: At this point I've handled issues that I encountered through more rigorously defining commands that are allowed to have arbitrary arguments and extracting "--" through a bit of preprocessing (I was already futzing with the arguments list to get help displaying in a manner more semantically logical to my coworkers). I highly doubt that any of this is worth making drastic changes to yargs (or any related libraries) to support, and it sounds like the changes would need to be quite drastic indeed. Regardless, I'm going to be having to switch away to some other stuff for the time being (technically I already have). If I stumble on a more widely relevant use case, I'll create some demonstrative tests. |
@Toberumono sorry for the pain in the neck -- unfortunately config options aren't tested together rigorously, regressions like this have bit people like this a few times. |
As of version 21.1.0, configuring yargs-parser with
{"populate--": true, "unknown-options-as-args": true, "halt-at-non-option": true}
results in every non-flag being put in "--" regardless of whether it is part of a valid command.Prior to version 21.1.0, non-flags were excluded from "--" provided that they were part of a valid command.
Example
Command: node foo.js validCommand
Expected behavior: executes the command "validCommand"
Actual: places "validCommand" in the "--" array.
Effectively, it is treating every command as invalid.
Unfortunately, I cannot build directly because version 21.1.0's type bindings are broken, and I'm using TypeScript, so I can't dig into it properly at this time.
The text was updated successfully, but these errors were encountered: