Commander.js: Provide Array of permitted values for an option.

Created on 26 Mar 2016  路  14Comments  路  Source: tj/commander.js

I have an .option() that is only allowed to be one of "all", "debug" or "none".

I'd like an easy way to pass an Array to an option() and have Commander automatically check that the value passed by the user is one of the permitted values.

Something like this:

.option('-t, --target <target>', 'Specifies the target.', ["all", "debug", "none"])

Also including the permitted values in the help output could be useful, provided it's not a very large Array.

I'd gladly do the PR if I can get a response from @tj or a collaborator that this is useful.

Thanks!!

enhancement

Most helpful comment

Included in Commander 7.0.0.

All 14 comments

I second this, Would be great to be able to do this and validate options.

1) The .option call is quite flexible already and it is not easy to add more functionality to the current signature. I am wondering about allowing building options to make it possible to add less common functionality without complicating the existing usage. See https://github.com/tj/commander.js/issues/1106#issuecomment-558379166

2) As a different approach, I have been wondering if there could be a collection of validators available from Commander to use as custom option processing functions, but my first attempts were a bit clumsy due to option name not being easily available for error messages. For an example of a simple bring-your-own validator for an array of expected values: https://github.com/tj/commander.js/issues/1130#issuecomment-569565495

(I suspect that neither of these are as "easy" as OP had in mind when said "I'd like an easy way to pass an Array to an option() and have Commander automatically check that the value passed by the user is one of the permitted values."! See next two comments for what code could look like.)

I have been wondering about the syntax and here is one approach, fluent (chaining) calls on Option object like with Command object:

program
   .addOption(new Option('-t, --target <target>', 'Specifies the target (all | debug | none).')
      .valueIncludedIn(["all", "debug", "none"])
   )
);

This style might be used for other enhancements for options, like:

   .addOption(new Option('-f, --devFeature <feature-flag>').hidden());

And another approach, convenience routines for custom option processing with existing api:

program
   .option('-t, --target <target>', 
      'Specifies the target (all | debug | none).', 
      OptionValue.includedIn(["all", "debug", "none"])()
   )

This style might be used for adding other custom option processing including from the examples, like:

program
   .option('-t, --target <target>', 
      'Specifies the target(s), comma separated', 
      OptionValue.csv
   )

Related link from https://github.com/tj/commander.js/issues/215#issuecomment-660456539

Python argparser has choices for restricting allowed values: https://docs.python.org/3/library/argparse.html#choices

Yargs has choices too.

Experimenting with Option approach in #1331:

program
   .addOption(new Option('-s, --size <value>').choices(['big', 'little']));

Included in v7.0.0 pre-release #1386

I might be late to the game, but what if you could return or throw an error in the coercion function? Then you could do any validation you want. commander would automatically generate the error message. `--the-flag: ${error.message}`

Using an exception is actually the approach I took for the choice method, see commander.optionArgumentRejected at:

But I had not thought of adding the option name in the catch rather than the throw. That would be a solution to #1207

Included in Commander 7.0.0.

Nice work @shadowspawn! I'll try and make some time to give this a go.

@shadowspawn this works very well. The only feature missing right now is to make the newly created option required. I would suggest something like setRequired()

new commander.Options("-t, --test <feature>", "test the feature").choices(["feature1", "feature2"]).setRequired()

What do you think?

@fernetmatt I think that鈥檚 .makeOptionMandatory().

@lydell is correct.

Went with mandatory as the Option class already used required and optional for whether the option-value is required or optional, like '-r <value>' and '-o [value]'. (I tried some naming variations on whether the "value" was required or the "option" was required, but it was too subtle.)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mwittig picture mwittig  路  4Comments

DeoLeung picture DeoLeung  路  4Comments

shadowspawn picture shadowspawn  路  3Comments

mtrabelsi picture mtrabelsi  路  3Comments

0505gonzalez picture 0505gonzalez  路  3Comments