minimistjs / minimist

parse argument options
MIT License
515 stars 30 forks source link

restrict the number of allowed options/strict mode #37

Open tttp opened 1 year ago

tttp commented 1 year ago

Hi,

I'm not sure what's the best place to discuss feature, feel free to close and direct me to a better place

Some users of our cli mistype an argument name (eg the cli expects --dry-run and they type --dryrun).

If would be useful to have a strict mode that stops the process if there is an unknown error

(eg. invalid parameter "dryrun")

I tried to implement it using unknown

unknown: (param) => {param[0] === '-' ? console.error("invalid parameter",param) || process.exit(1) : true},

however,

1) it would be nice to have to have a second parameter "type" on unknown (ie "-" "--" "" if alias, normal, or argv.) 2) I can't figure out how to handle valid params that are int (eg aren't listed into boolean or string), is there an "any" or "integer" option?

X+

ljharb commented 1 year ago

I'm not sure what you mean - the unknown function option should only be called with a truly unknown arg (like "dryrun") and you can throw an error there to handle it.

tttp commented 1 year ago

yes, but how do you tell minimist that

so say if I have --min=12 --dry-run=dummy --mode --extra=bling

and options= {boolean:["dry-run"], string: ["mode"]}

if there an helper to make it easy to flag that:

right now, I don't know how to handle integer params (they will always be pushed to my unkown function), and if there is type error, it's silently dropped, isn't it?

Basically, minimist is awesome for quick prototyping, but once I'm clear about which parameters are expected, I'd like to "freeze" that list and display errors when the user puts unexpected parameters (eg either wrong types or unknown params)

ljharb commented 1 year ago

Your unknown would return true for the unknown things you want, and everything else throws.

Since dry-run is marked as a boolean, I'd expect it would be pretty trivial to validate that for yourself after parsing is completed?

shadowspawn commented 1 year ago

Minimist does not have much in the way of "strict" support. The unknown function is specifically just called for arguments that are unknown. It does not support checking the option-value against the option "type". Likewise, the implicit conversion of an option value to integer is a bonus from the automatic loose processing and not a "type" as such.

One way of getting an integer option to at least be recognised is to give it an alias.

Given this code:

var argv = require('minimist')(process.argv.slice(2), {
    boolean:["dry-run"], 
    string: ["mode"],
    alias: {min: 'm'},
    unknown: (unknownParam) => {
        console.log({unknownParam});
    }
});
console.log(argv);

I see this output for your example line:

% node index.js --min=12 --dry-run=dummy --mode --extra=bling
{ unknownParam: '--extra=bling' }
{ _: [], 'dry-run': true, min: 12, m: 12, mode: '', extra: 'bling' }
shadowspawn commented 1 year ago

Basically, minimist is awesome for quick prototyping, but once I'm clear about which parameters are expected, I'd like to "freeze" that list and display errors when the user puts unexpected parameters (eg either wrong types or unknown params)

You might like to take a look at parseArgs which is available from Node.js 16. Both @ljharb and I helped with design and implementation, and it is strict by default so better addresses your goals in that regard.

https://nodejs.org/dist/latest-v18.x/docs/api/util.html#utilparseargsconfig

parseArgs does not support integer conversion, so that is left up to you. However, it would handle all four of your examples in strict mode with appropriate config:

I can do an example program matching your example if you would like to see one.

tttp commented 1 year ago

nice trick to use alias!

this is what I have so far

const argv = require("minimist")(process.argv.slice(2), {
  boolean: ["help", "dry-run", "verbose"],
  string: ["to"],
  unknown: (d) => {
    const allowed = ["min"];
    if (d[0] !== "-") return true;
    if (allowed.includes(d.split("=")[0].slice(2))) return true;
    console.error("unknown param");
    help(1);
  },
});
if (argv._.length !== 1) {
  console.log(color.red("only one unamed param allowed"), argv._);
  help();
}

to make it more compact, ideally:

tttp commented 1 year ago

You might like to take a look at parseArgs

thanks for the pointer, I completely missed that one!

ljharb commented 1 year ago

parseArgs existence, however, doesn’t mean minimist can’t be improved :-) this is still worth adding.

shadowspawn commented 1 year ago

I raised the idea of a strict mode at the end of https://github.com/minimistjs/minimist/issues/39#issuecomment-1585323925