Skip to content
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

fix: positionals should not overwrite options #1992

Merged
merged 5 commits into from Aug 4, 2021
Merged

fix: positionals should not overwrite options #1992

merged 5 commits into from Aug 4, 2021

Conversation

jly36963
Copy link
Contributor

@jly36963 jly36963 commented Jul 21, 2021

Addresses: #1637

Problem

When executing the default command, if both positionals and options are provided under the same namespace, positionals will overwrite the options in argv. This doesn't happen if a command is provided.

yargs

Command provided

This will behave correctly. argv.foods will have both apples and carrots

yargs
  .command({
    command: "cmd [foods..]",
    desc: "cmd desc",
    builder: (yargs) =>
      yargs.positional("foods", {
        desc: "foods desc",
        type: "string",
      }),
    handler: (argv) => {
      console.log(argv);
    },
  })
  .help()
  .parse("cmd --foods apples carrots");

logs

// first time through parser (kRunYargsParserAndExecuteCommands)
{ args: 'cmd --foods apples carrots', config: { 'populate--': true } }
{ 'parsed.argv': { _: [ 'cmd', 'carrots' ], foods: 'apples' } }

// second time through parser
{ args: 'cmd --foods apples carrots', config: { 'populate--': true } }
{ 'parsed.argv': { _: [ 'cmd' ], foods: [ 'apples', 'carrots' ] } }

// argv in handler
{
  _: [ 'cmd' ],
  foods: [ 'apples', 'carrots' ],
  '$0': 'test-issue.js'
}

Default command

This will behave incorrectly. argv.foods will only have carrots.

yargs
  .command({
    command: "$0 [foods..]",
    desc: "default desc",
    builder: (yargs) =>
      yargs.positional("foods", {
        desc: "foods desc",
        type: "string",
      }),
    handler: (argv) => {
      console.log(argv);
    },
  })
  .help();
  .parse('--foods apples carrots')

logs

// first time through parser (kRunYargsParserAndExecuteCommands)
{ args: '--foods apples carrots', config: { 'populate--': true } }
{ 'parsed.argv': { _: [ 'carrots' ], foods: 'apples' } }

// second time through parser
{ args: '--foods apples carrots', config: { 'populate--': true } }
{ 'parsed.argv': { _: [ 'carrots' ], foods: 'apples' } }

// argv in handler
{
  _: [],
  foods: [ 'carrots' ],
  '$0': 'test-issue.js'
}

The logic in postProcessPositionals will cause the positionals (['carrots']) to override the option ('apples') in argv.

Solution

Check during postProcessPositionals if a particular key is already set on argv. If both positionals/options are provided and at least one is an array, combine rather than overwrite.

Questions

When both positional and option are provided, what should be the behavior?

  • if variadic, combine?
  • if single value, overwrite?

(This is the assumption I made with this fix.)

@jly36963 jly36963 marked this pull request as ready for review July 27, 2021 02:20
@bcoe
Copy link
Member

bcoe commented Jul 28, 2021

if single value, overwrite?

I think for this edge case, this assumption seems right to me. Someone writing a command line should avoid a collision like this, for arguments they define. So, this is something that will most likely come up in the edge-case of someone passing an unexpected command.

Copy link
Member

@bcoe bcoe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fix looks great to me 👍 thank you.

@bcoe bcoe merged commit 9d84309 into yargs:master Aug 4, 2021
foolip added a commit to foolip/browser-compat-data that referenced this pull request Aug 12, 2021
mdn#11916 broke the traverse
script, probably due to yargs/yargs#1992.

The problem now is that for `npm run chrome all 90`, `values`
will be ["null", "true", "90"], due to the combining of default and
given argument, where before `values` would have been ["90"].

We were using arrays for both folder and value, which doesn't really
make sense since only the last positional argument can be variadic:
https://github.com/yargs/yargs/blob/master/docs/advanced.md#variadic-positional-arguments

Since we use comma-separated values, just use strings instead to avoid
the problem.
@foolip
Copy link

foolip commented Aug 12, 2021

Hi all!

It looks like this was a breaking change, at least for how we used yargs in MDN's browser-compat-data. I've worked around it here, which touches all the relevant code: mdn/browser-compat-data#11990

Note that I tried and failed to make only the last positional argument variadic, the default values are combined with the given value even then. This seems like a limitation in yargs now, albeit one that I could work around by not using an array type at all.

@jly36963
Copy link
Contributor Author

@foolip I believe I understand the problem -- I'll work on a fix tonight, and I'll keep you posted

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants