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

Variadic arguments in options don't work #913

Closed
flotwig opened this issue Feb 7, 2019 · 4 comments
Closed

Variadic arguments in options don't work #913

flotwig opened this issue Feb 7, 2019 · 4 comments

Comments

@flotwig
Copy link

flotwig commented Feb 7, 2019

Not sure if this is intended or not... but I'm seeing some unexpected behavior when trying to use variadic arguments as an option.

Here's some demo code:

const commander = require('commander')

const program = new commander.Command()

program
.command('run')
.usage('[options]')
.description('Runs some stuff')
.option('-d, --dev', 'Dev mode')
/** bunch more options here */
.option('-s, --spec <specs...>', 'Specs to run')
.action((opts) => {
  console.log(opts)
})

program.parse(process.argv)

If I run node index.js run --spec a, it works as expected and opts is a Command object with spec === "a".

However, if I try to use the optional var args like node index.js run --spec a b c, opts is a string with value b. It seems to take the value of whatever that middle argument is.

Is there a better way to do what I'm trying to do, or is this a bug? Or both? 😄

I want to avoid putting the varargs in .usage if possible, since this argument is optional and I don't want to confuse users.

Commander 2.19.0

@shadowspawn
Copy link
Collaborator

You can have variadic arguments, but not variadic options. You can have a repeatable option with multiple values specified but you do need to add a routine to do the collecting. See <file...> and --collect [value] under coercion in the README.

So for your option would be called like:

run --spec a --spec b --spec c

@flotwig
Copy link
Author

flotwig commented Feb 8, 2019

Ah, thanks for clearing that up. I was trying to get globbing to work intuitively for users, so if they pass --spec * and the shell expands it to --spec a b c, it'll still work. I came up with a workaround that parses the extra arguments from Commander:

Sample code:

const commander = require('commander')

const parseVariableOpts = (fnArgs, args) => {
  const opts = fnArgs.pop()

  opts.spec = [opts.spec]

  if (fnArgs.length && opts.spec) {
    // this will parse space-delimited specs after --spec spec1 but before the next option

    const argIndex = args.findIndex(arg => arg === '--spec') + 2
    const nextOptOffset = args.slice(argIndex).findIndex(arg => arg.slice(0, 2) === '--')
    const endIndex = nextOptOffset !== -1 ? argIndex + nextOptOffset : args.length

    const extraSpecs = args.slice(argIndex, endIndex).filter(maybeSpec => {
      return fnArgs.find(fnArg => {
        return maybeSpec === fnArg
      })
    })

    opts.spec = opts.spec.concat(extraSpecs)
  }

  return opts
}

const program = new commander.Command()

program
.command('run')
.usage('[options]')
.description('Runs some stuff')
.option('-d, --dev', 'Dev mode')
/** bunch more options here */
.option('--spec <specs>', 'Specs to run')
.action((...fnArgs) => {
  opts = parseVariableOpts(fnArgs, process.argv)
  console.log(opts.spec)
})

program.parse(process.argv)

Example output:

➜  node index.js run --spec a c
[ 'a', 'c' ]
➜  node index.js run --spec a
[ 'a' ]
➜  node index.js run --spec a b c d e f g
[ 'a', 'b', 'c', 'd', 'e', 'f', 'g' ]
➜  node index.js run --spec a b c d e f g --dev
[ 'a', 'b', 'c', 'd', 'e', 'f', 'g' ]
➜  node index.js run --spec a b c d e f --dev g
[ 'a', 'b', 'c', 'd', 'e', 'f' ]
➜  node index.js run --dev --spec a b c d e f
[ 'a', 'b', 'c', 'd', 'e', 'f' ]

@shadowspawn
Copy link
Collaborator

There is also discussion of variadic options in #571

@shadowspawn
Copy link
Collaborator

Closing in favour of #571

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

No branches or pull requests

2 participants