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

Using Option.parseArgs with variadic and default options #1641

Closed
joshuakarp opened this issue Nov 17, 2021 · 3 comments
Closed

Using Option.parseArgs with variadic and default options #1641

joshuakarp opened this issue Nov 17, 2021 · 3 comments

Comments

@joshuakarp
Copy link

I'm trying to create an Option that supports:

  • argument validation (through a parseArgs function)
  • being variadic
  • retrieval from an environment variable, and
  • a default value (when none of the prior sources are available)

However, I'm noticing that there's some interference between my parseArgs function and specification of a default value. Consider the following minimal example, where I hypothetically want to provide multiple ports in the arguments, but want to default to a single port 80 if none are provided:

#!/usr/bin/env node

import commander from 'commander';
const program = new commander.Command();

function parsePorts(curr, prev) {
  return prev.concat([parseInt(curr)]);
}

program.addOption(new commander.Option('-p, --port [number...]', 'specify port number')
  .argParser(parsePorts)
  .default([80])
);

program.parse();
console.log(program.opts());

Executing this with --port 123 124 provides the following output:

{ port: [ 80, 123, 124 ] }

Without the argParser added, I get the expected { port: [ '123', '124' ] }, but I'm no longer able to validate the provided arguments. It seems that argParser is also parsing the default port as well, and appending this to the returned list. Is there a way to circumvent this?

@shadowspawn
Copy link
Collaborator

Nice example and description, thanks.

You have hit a tricky part of the option-argument processing! Long story...

The "default value" for an option is used in two ways: it is set as the option value when the option is added to the command, and also the default value gets passed into the argument parser as the "previous" value the first time the parser is called. This often works fine.

With a variadic option you probably want the command-line items in a fresh array without the default value, and Commander does some extra work to make this happen when you don't have a custom parser. The internal support for adding to variadic options makes a fresh array if the previous value is the default value.

if (previous === this.defaultValue || !Array.isArray(previous)) {

Four different work-around ideas for your use case:

  1. use a default value of an empty array, and supply port 80 afterwards if the option value is empty
  2. store the default value yourself and do the same test as Commander in your parsing function
  3. use the built-in variadic support with strings and convert to numbers afterwards
  4. subclass Option so your parser has access to the default value without needing to store it separately

Ask if you would like to see code for one of those approaches.

(I have wondered about adding the ability to set the Option default value and the starting value separately. And wondered about passing in Option object and even Command into parse function. But not many reports of problems to justify the additional complexity so far, so only been thinking about it!)

@shadowspawn
Copy link
Collaborator

FYI: a custom argParser gets called on the string values from the command-line, or the string value of an environment variable. A custom argParser does not get called on the default value as that is supplied by code and is assumed to be pre-parsed.

@joshuakarp
Copy link
Author

Thanks a lot for all this information - those work-arounds will work just fine for me. Appreciate the help.

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