lukeed / mri Goto Github PK
View Code? Open in Web Editor NEWQuickly scan for CLI flags and arguments
License: MIT License
Quickly scan for CLI flags and arguments
License: MIT License
const mri = require("mri")
console.log(mri(['--bar= 132568])) // { _: [], bar: 132568 } TRUE
console.log(mri(['--bar= 0132568])) // { _: [], bar: 132568 } FALSE
console.log(mri(['--bar= "0132568"])) // { _: [], bar: 132568 } FALSE
as an example, number zero
does not include in, even when I put a number in double quotations, mri
convert the string to a number and this converting omit zero!
Hey Luke 👋
Related: lukeed/sade#17
Was using sade
in a project where I wanted an option to be coerced to a string
always, so I specified that a default value of ""
. However I noticed that when using the alias of the option, the value was not turned into a string (repo below). For me, the use case is for simple file matching, where some file names may begin with a number. So the option may be passed a value of "01"
and I want that as a string, not the number 1
.
I think lukeed/sade#17 is related to what I want here, but seeing as how that issue is old and closed without discussion I thought maybe opening a new issue focused around the use case would be helpful. Apologies if I thought wrong here 😬
test.js:
const sade = require("sade"); // v1.7.3
// Using `isOne` just to simplify this program
const { args } = sade("test", true)
.option("-a --arg", "Test option", "")
.parse(process.argv, { lazy: true });
console.log(args);
Output with --arg
:
$ node .\test.js --arg 01
[ { _: [], arg: '01', a: '01' } ]
Output with -a
alias:
(expected to be the same as --arg
output)
$ node .\test.js -a 01
[ { _: [], a: 1, arg: 1 } ]
If you think this is something that should/can be fixed, happy to open a PR. My assumption is that we need to pass defaults for option names and aliases to mri
?
Thanks!
P.S. Love all awesome libraries you build - thanks for giving these to us 😁
I'm using [email protected]
.
const headerAliased = {alias: {H: 'header'}}
mri(['-H', 'one', '--header=two'])
// as expected: { _: [], H: 'one', header: 'two' }
mri(['-H', 'one', '--header', 'two'])
// as expected: { _: [], H: 'one', header: 'two' }
mri(['-H', 'one', '-H', 'two'], headerAliased)
// as expected: { _: [], H: [ 'one', 'two' ], header: [ 'one', 'two' ] }
mri(['--header', 'one', '--header', 'two'], headerAliased)
// as expected: { _: [], header: [ 'one', 'two' ], H: [ 'one', 'two' ] }
mri(['-H', 'one', '--header=two'], headerAliased)
// not expected: { _: [], H: 'one', header: 'one' }
mri(['-H', 'one', '--header', 'two'], headerAliased)
// not expected: { _: [], H: 'one', header: 'one' }
var mri = require("mri")
console.log(mri(['--bar=1'])) // { _: [], bar: 1 }
console.log(mri(['--bar=0'])) // { _: [], bar: '0' }
Expected behavior:
Either add explicit number parsing, or fix the toNum
function here to handle parsing the string '0'
I encountered this error in microbundle, which uses mri via sade. One of microbundle's arguments is a boolean, but the default is null because the actual default depends on other arguments (--compress
defaults to true when targetting web, and false when targetting node). A change introduced in [email protected] causes parsing to break with the error:
Cannot read property 'push' of undefined
...which is being thrown here. Presumably this is because it only accepts, strings numbers and booleans. I don't know if null
is a valid value for defaults, but it seems like this is a regression.
It's a little odd that the unknown function requires that aliases be defined to match defined flags. It means that exact matches "fail", but because of the way the parser works, prefixed flags do not. Example:
const mri = require('mri')
const opts = { boolean: ['check'], unknown: (arg) => console.log('unknown arg:', arg) }
// this hits the unknown handler
mri(['--check'], opts)
// this just works
const args = mri(['--no-check'], opts)
I'm curious why the alias requirement exists. Why not just continue if there's an exact flag match?
Currently consumers consume the types from @types/mri
— are you open to shipping types natively and or a typescript re-write?
Hi Luke, I'm pretty naive in the node.js and all the surrounding infrastructure (I have been programming mostly in C++, Haskell, Ocaml, C#, F# and Bash during the last 20 years); my node.js experience is limited to building a single production typescript app, albeit not quite a helloworld—integrating GCB build with a BitBucket.org repo to run configurable matrix CI pipelines and drop artifacts on premises, so there is actually more than a single app). I certainly liked the language, especially with TS type support crutch. I am now looking for a solid high-level cli parser for my next project.
What seems missing from the documentation (which may possibly only seem due to my naivete) is the reason why it is important to parse command line quickly. Frankly, seeing the benchmarks I started to worry, after a negative experience with Python's slowness (the language has got a very apt name!); but node's V8 engine surprised me with its speed. I never thought of a CLI parser as a speed bottleneck.
My eyes are currently at the https://github.com/yargs/yargs CLI framework, although I'm going to eval a few. I admit I cannot put the benchmark in context, because it does not define what an "op" is; if you could explain it better (e.g., how many Kops/Mops does it take to parse an averagely complex exemplar Git or Docker command line on a random modern box), but the ×40 perf difference is indeed impressive! What is hard to grok is how it relates to the real world applications and absolute time. 100ms vs 4s is indeed a huge difference (4s is a show-stopper), 10ms vs 400ms is a nuisance to do away with, but 1ms vs 40ms is rather in the "who cares" range.
Also, it would be great if you added a bit of instructions on replacing the https://github.com/yargs/yargs-parser with your one in the yargs context, if that's doable.
Thank you! And, of course, all this probably doesn't make sense if that's a basic knowledge among node.js programmers; I'm certainly not one.
const mri = require("mri")
const args = ["hello", "-foo_bar"]
const argv = mri(args)
console.log(argv._)
["hello", true]
["hello", "-foo_bar"]
Any command that can receive optional parameters and no actual arguments, receives them as integers. Here is a simple test case where the action
handler will always receive a number as a passed value although none have been passed. I'm not sure why it's a number instead of undefined
or null
.
const sade = require("sade");
const assert = require("assert");
var prog = sade("microbundle");
prog
.option("--raw", "", false)
.command("build [...entries]", "", {
default: true
})
.action(str => {
// ERROR: `str` will always be `1`
assert.notEqual(str, 1);
});
prog.parse(["", "", "build", "--raw"]);
// x.js
import minimist from "minimist"
import mri from "mri"
const OPTIONS = { boolean: [ 'flag' ], array: [ { key: '_', number: true } ] }
console.log('minimist', minimist(process.argv.slice(2), OPTIONS))
console.log('mri', mri(process.argv.slice(2), OPTIONS))
$ node x.js 10 20 xx 40
minimist { _: [ 10, 20, 'xx', 40 ], flag: false }
mri { _: [ '10', '20', 'xx', '40' ] }
$ node x.js --flag 10 20 xx 40
minimist { _: [ 10, 20, 'xx', 40 ], flag: true }
mri { _: [ 10, '20', 'xx', '40' ], flag: true }
$ node x.js 10 --flag 20 xx 40
minimist { _: [ 10, 20, 'xx', 40 ], flag: true }
mri { _: [ '10', 20, 'xx', '40' ], flag: true }
It appears mri
converts a single argument after a flag to number. I would expect it to convert all (like minimist) or none.
description
when passing options with hyphens mri behaves really weirdly
reproduce
code
import mri from "mri"
console.log(mri([ "-n" ], { alias: { n: "no-install" }}))
console.log(mri([ "--no-install" ], { alias: { n: "no-install" }}))
logs
$ node .
{ _: [], n: true, 'no-install': true }
{ _: [], install: false }
expectation
$ node .
{ _: [], n: true, 'no-install': true }
{ _: [], n: true, 'no-install': true}
https://www.npmjs.com/package/getopts is another package like this one with a primary claim to speed.
#!/usr/bin/env node
const mri = require('mri')
console.log(process.argv)
console.log(mri(process.argv.slice(2)))
./args.js -a -b '-c foo'
[
'/usr/local/Cellar/node/16.0.0_1/bin/node',
'/Users/j/web/pev2-cli/foo.js',
'-a',
'-b',
'-c foo'
]
{
_: [],
a: true,
b: true,
c: true,
' ': true,
f: true,
o: [ true, true ]
}
Is that intended?
I would have expected this:
{
_: ['-c foo'],
a: true,
b: true
}
Or maybe this (but I find the result above much more intuitive):
{
_: [],
a: true,
b: true,
c: 'foo'
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.