arg
is an unopinionated, no-frills CLI argument parser.
npm install arg
arg()
takes either 1 or 2 arguments:
- Command line specification object (see below)
- Parse options (Optional, defaults to
{permissive: false, argv: process.argv.slice(2), stopAtPositional: false}
)
It returns an object with any values present on the command-line (missing options are thus missing from the resulting object). Arg performs no validation/requirement checking - we leave that up to the application.
All parameters that aren't consumed by options (commonly referred to as "extra" parameters)
are added to result._
, which is always an array (even if no extra parameters are passed,
in which case an empty array is returned).
const arg = require('arg');
// `options` is an optional parameter
const args = arg(
spec,
(options = { permissive: false, argv: process.argv.slice(2) })
);
For example:
$ node ./hello.js --verbose -vvv --port=1234 -n 'My name' foo bar --tag qux --tag=qix -- --foobar
// hello.js
const arg = require('arg');
const args = arg({
// Types
'--help': Boolean,
'--version': Boolean,
'--verbose': arg.COUNT, // Counts the number of times --verbose is passed
'--port': Number, // --port <number> or --port=<number>
'--name': String, // --name <string> or --name=<string>
'--tag': [String], // --tag <string> or --tag=<string>
// Aliases
'-v': '--verbose',
'-n': '--name', // -n <string>; result is stored in --name
'--label': '--name' // --label <string> or --label=<string>;
// result is stored in --name
});
console.log(args);
/*
{
_: ["foo", "bar", "--foobar"],
'--port': 1234,
'--verbose': 4,
'--name': "My name",
'--tag': ["qux", "qix"]
}
*/
The values for each key=>value pair is either a type (function or [function]) or a string (indicating an alias).
-
In the case of a function, the string value of the argument's value is passed to it, and the return value is used as the ultimate value.
-
In the case of an array, the only element must be a type function. Array types indicate that the argument may be passed multiple times, and as such the resulting value in the returned object is an array with all of the values that were passed using the specified flag.
-
In the case of a string, an alias is established. If a flag is passed that matches the key, then the value is substituted in its place.
Type functions are passed three arguments:
- The parameter value (always a string)
- The parameter name (e.g.
--label
) - The previous value for the destination (useful for reduce-like operations or for supporting
-v
multiple times, etc.)
This means the built-in String
, Number
, and Boolean
type constructors "just work" as type functions.
Note that Boolean
and [Boolean]
have special treatment - an option argument is not consumed or passed, but instead true
is
returned. These options are called "flags".
For custom handlers that wish to behave as flags, you may pass the function through arg.flag()
:
const arg = require('arg');
const argv = [
'--foo',
'bar',
'-ff',
'baz',
'--foo',
'--foo',
'qux',
'-fff',
'qix'
];
function myHandler(value, argName, previousValue) {
/* `value` is always `true` */
return 'na ' + (previousValue || 'batman!');
}
const args = arg(
{
'--foo': arg.flag(myHandler),
'-f': '--foo'
},
{
argv
}
);
console.log(args);
/*
{
_: ['bar', 'baz', 'qux', 'qix'],
'--foo': 'na na na na na na na na batman!'
}
*/
As well, arg
supplies a helper argument handler called arg.COUNT
, which equivalent to a [Boolean]
argument's .length
property - effectively counting the number of times the boolean flag, denoted by the key, is passed on the command line..
For example, this is how you could implement ssh
's multiple levels of verbosity (-vvvv
being the most verbose).
const arg = require('arg');
const argv = ['-AAAA', '-BBBB'];
const args = arg(
{
'-A': arg.COUNT,
'-B': [Boolean]
},
{
argv
}
);
console.log(args);
/*
{
_: [],
'-A': 4,
'-B': [true, true, true, true]
}
*/
If a second parameter is specified and is an object, it specifies parsing options to modify the behavior of arg()
.
If you have already sliced or generated a number of raw arguments to be parsed (as opposed to letting arg
slice them from process.argv
) you may specify them in the argv
option.
For example:
const args = arg(
{
'--foo': String
},
{
argv: ['hello', '--foo', 'world']
}
);
results in:
const args = {
_: ['hello'],
'--foo': 'world'
};
When permissive
set to true
, arg
will push any unknown arguments
onto the "extra" argument array (result._
) instead of throwing an error about
an unknown flag.
For example:
const arg = require('arg');
const argv = [
'--foo',
'hello',
'--qux',
'qix',
'--bar',
'12345',
'hello again'
];
const args = arg(
{
'--foo': String,
'--bar': Number
},
{
argv,
permissive: true
}
);
results in:
const args = {
_: ['--qux', 'qix', 'hello again'],
'--foo': 'hello',
'--bar': 12345
};
When stopAtPositional
is set to true
, arg
will halt parsing at the first
positional argument.
For example:
const arg = require('arg');
const argv = ['--foo', 'hello', '--bar'];
const args = arg(
{
'--foo': Boolean,
'--bar': Boolean
},
{
argv,
stopAtPositional: true
}
);
results in:
const args = {
_: ['hello', '--bar'],
'--foo': true
};
Some errors that arg
throws provide a .code
property in order to aid in recovering from user error, or to
differentiate between user error and developer error (bug).
If an unknown option (not defined in the spec object) is passed, an error with code ARG_UNKNOWN_OPTION
will be thrown:
// cli.js
try {
require('arg')({ '--hi': String });
} catch (err) {
if (err.code === 'ARG_UNKNOWN_OPTION') {
console.log(err.message);
} else {
throw err;
}
}
node cli.js --extraneous true
Unknown or unexpected option: --extraneous
A few questions and answers that have been asked before:
Do the assertion yourself, such as:
const args = arg({ '--name': String });
if (!args['--name']) throw new Error('missing required argument: --name');
Released under the MIT License.
arg's People
Forkers
apalm herber timneutkens macklinu blakeembrey gondar00 shaunstanislauslau await-ovo devhttps saksohail arjunsajeev deckhandfirststar01 aurorasparkdev syed-umair pmuellr j12934 yuler 0xflotus zheeeng flexdinesh stikerz the-cc-dev planeight r-tamura isabella232 lordofthelake wolfenberg dhruvkb augustarchive cj2 himanshilt gr-qft kiprasmel nevilm-lt rasata jakehamilton mchccn clean-code-studio collisioncataclysm gholk alihaydar44 juanm04 william3johnson 00mjk smartercow guieco zivanovicb morni-andoka qasura-harg's Issues
Everything after "=" gets stripped off in long args with "=" in value
When an arg has =
in the value, everything after (inc) =
gets stripped off from the arg.
Pass this command to arg()
—
yarn keystone dev --connect-to=mongodb://user:pass@localhost:27017/keystone?authSource=admin&w=1
Current output
--connect-to
= mongodb://user:pass@localhost:27017/keystone?authSource
Expected output
--connect-to
= mongodb://user:pass@localhost:27017/keystone?authSource=admin&w=1
This is essential for passing URLs as arg value.
Make sure that single-hyphen properties only have one letter
Since we plan to accept no-space short arg parsing, we should enforce that single-hyphen arguments only have one character.
Separate `--` and `_` arguments
I need the ability to get _
arguments and --
separately. Currently everything after --
gets placed into args._
so I can't tell which was after the --
argument or which was part of the command. This is required to use in projects such as https://github.com/Qard/onchange.
TypeScript typings error for handlers
The current types in index.d.ts
produce an error when attempting to use them in a purely TypeScript project:
Exported variable 'args' has or is using name 'flagSymbol' from external module
"/path/to/project/node_modules/arg/index" but cannot be named.ts(4023)
After a brief bit of tweaking, I found that this can be fixed by moving flagSymbol
into the arg
namespace and exporting it along with other members. This makes the type accessible and resolves the error. I'm unsure if there is a better way to handle it, though this should be fine.
My Local arg.d.ts file
declare module "arg" {
function arg<T extends arg.Spec>(
spec: T,
options?: arg.Options
): arg.Result<T>;
namespace arg {
export const flagSymbol: unique symbol;
export function flag<T>(fn: T): T & { [arg.flagSymbol]: true };
export const COUNT: Handler<number> & { [arg.flagSymbol]: true };
export type Handler<T = any> = (
value: string,
name: string,
previousValue?: T
) => T;
export class ArgError extends Error {
constructor(message: string, code: string);
code: string;
}
export interface Spec {
[key: string]: string | Handler | [Handler];
}
export type Result<T extends Spec> = { _: string[] } & {
[K in keyof T]?: T[K] extends Handler
? ReturnType<T[K]>
: T[K] extends [Handler]
? Array<ReturnType<T[K][0]>>
: never;
};
export interface Options {
argv?: string[];
permissive?: boolean;
stopAtPositional?: boolean;
}
}
export = arg;
}
Note: This is occurring for me using typescript@^4.6.3
. I'm not sure if this is an issue with a new version of TypeScript or has always been a problem since I've typically only used arg
in JavaScript projects up until now.
Convert to ES Modules
Typescript build error
When implement the module in typescript and built it, I get the next error when I trying to run
const args = (0, arg_1.default)({ '--v': Boolean, '--a': Boolean, '-v': '--v', '-a': '--a' });
UnhandledPromiseRejectionWarning: TypeError: (0 , arg_1.default) is not a function
What's the default value for Boolean type?
Hi, I don't understand this part from Readme.md
// hello.js
const arg = require('arg');
const args = arg({
// Types
'--help': Boolean,
'--version': Boolean,
'--verbose': arg.COUNT, // Counts the number of times --verbose is passed
'--port': Number, // --port <number> or --port=<number>
'--name': String, // --name <string> or --name=<string>
'--tag': [String], // --tag <string> or --tag=<string>
// Aliases
'-v': '--verbose',
'-n': '--name', // -n <string>; result is stored in --name
'--label': '--name' // --label <string> or --label=<string>;
// result is stored in --name
});
console.log(args);
/*
{
_: ["foo", "bar", "--foobar"],
'--port': 1234,
'--verbose': 4,
'--name': "My name",
'--tag': ["qux", "qix"]
}
*/
I think --verbose
should be true
, not 4
, because I got true
from my practice.
// t.ts
const arg = require("arg");
const args = arg(
{
"--project": String,
"--pass": Boolean,
"-p": "--project"
},
{
argv: process.argv.slice(2)
}
);
// package.json
"start": "ts-node ./t.ts -p hello --pass"
// Result
{ _: [], '--project': 'hello', '--pass': true }
So, can someone tell me why it is 4
? If it should'be fixed, I'll send a pr.
Print help?
I'm looking for a package to help me write CLIs and very much like arg's simplicity for parsing args. I was thinking that if the spec format was a bit enhanced, it could perhaps help me print help as well, like this:
const spec = {
"--help": { type: Boolean, description: "Show help" },
"--port": { type: Number, description: "Port number" }
};
const args = arg(spec);
if (args['--help']) {
console.log(getHelp(spec));
}
What do you think? I know it might be out of the scope of this package but arg parsing and printing help are closely related things and I'd like to stay DRY.
Ability to "stop early"
In some projects, I need the ability to "stop early" when parsing the arguments. I want it to parse up until the first _
argument and then break. This is useful for projects such as https://github.com/TypeStrong/ts-node (like node.js core), where the arguments before the script file are part of the execution and after the script name part of the script arguments.
Handle boolean array arguments better
This is justified since Boolean
types already get a bit of special treatment.
In the case I want multiple levels of verbosity, for example, the following should work:
// ./my-program -vvvv
const arg = require('arg');
const args = arg({
'-v': [Boolean]
});
console.log(args);
/*
{
_: [],
'-v': 4
}
*/
Currently, you have to do -v -v -v -v
separately, and in turn you get [true, true, true, true]
, which isn't exactly elegant.
Make the subcommand use case work
Consider:
now --debug scale --no-verify
We need to be able to parse this only with the known global options. For example:
{
"--debug": Boolean
}
When it inevitable throws on the unknown --no-verify
option, we need to recover everything else it was able to parse.
Support for required arguments?
Would it make sense to support required arguments? For example:
const args = arg({
'--help': Boolean,
'--port': arg.req(Number),
'--name': String,
'--tag': arg.req([String]),
});
Alternatively:
const args = arg({
'--help': Boolean,
'--path': String,
});
const {
'--help': printHelp = false,
'--path': thePath = arg.required('--path', String),
} = args;
arg.required()
would print an error message and exit Node.js. Downsides of this approach: Doesn’t handle aliases, redundancy.
If this is out of scope, it would be a good FAQ, because many shell argument parsing libraries support this feature.
Action required: Greenkeeper could not be activated 🚨
🚨 You need to enable Continuous Integration on all branches of this repository. 🚨
To enable Greenkeeper, you need to make sure that a commit status is reported on all branches. This is required by Greenkeeper because it uses your CI build statuses to figure out when to notify you about breaking changes.
Since we didn’t receive a CI status on the greenkeeper/initial
branch, it’s possible that you don’t have CI set up yet. We recommend using Travis CI, but Greenkeeper will work with every other CI service as well.
If you have already set up a CI for this repository, you might need to check how it’s configured. Make sure it is set to run on all new branches. If you don’t want it to run on absolutely every branch, you can whitelist branches starting with greenkeeper/
.
Once you have installed and configured CI on this repository correctly, you’ll need to re-trigger Greenkeeper’s initial pull request. To do this, please delete the greenkeeper/initial
branch in this repository, and then remove and re-add this repository to the Greenkeeper App’s white list on Github. You'll find this list on your repo or organization’s settings page, under Installed GitHub Apps.
Short options get splited in permissive being true
As my realization, the {permissive: true}
should left the unknown option being unparsed and treat them as positional arguments, but currently the permissive will still split the unknown short options. Is this a expected behavor? or can we change this behavor?
const arg = require('arg')
const args = arg({}, {argv: '-abc', permissive: true})
console.assert(args._[0] != '-abc')
console.assert(args._.join(' ') == '-a -b -c')
Add support for running in the browser
Line 13 in 99b578e
The library takes the default argument vector from process.env
. As a result, the library does not work in the browser. Removing the default fixes this.
I understand that this is an unconventional use-case and that arg
is an opinionated library so this might be outside the scope, but if it isn't I'm more than happy to make a PR from my fork (that I currently use to work around this limitation).
ReDoS vulnerability in index.js
Description
ReDoS vulnerability is an algorithmic complexity vulnerability that usually appears in backtracking-kind regex engines, e.g. the javascript default regex engine. The attacker can construct malicious input to trigger the worst-case time complexity of the regex engine to make a denial-of-service attack.
In this project, here has used the ReDoS vulnerable regex ^-?\d*(\.(?=\d))?\d*$
that can be triggered by the below PoC:
const arg = require('arg');
const args = arg(
{
'--foo': String
}, {
argv: ['hello', '--foo', '-' + '0'.repeat(60000) + '-']
}
);
How to repair
The cause of this vulnerability is the use of the backtracking-kind regex engine. I recommend the author to use the RE2 regex engine developed by google, but it doesn't support lookaround and backreference extension features, so we need to change the original regex and add additional code constraints. Here is my repair solution:
function safeMatch(string) {
const RE2 = require("re2")
let re = new RE2(/^-?\d*(\.)?(\d*)$/)
let res = re.match(string)
if (res != null) {
group1 = res[1]
if (group1 !== null) {
group2 = res[2]
if (/^\d/.test(group2)) {
return res
} else {
return null
}
}
return res
}
return res
}
console.log(safeMatch("-1.1")) // [ '-1.1', '.', '1', index: 0, input: '-1.1', groups: undefined ]
console.log(safeMatch("-1.")) // null
console.log(safeMatch(".")) // null
Using this code snippet to replace the code in line 156 argv[i + 1].match(/^-?\d*(\.(?=\d))?\d*$/)
can repair this vulnerability. The match semantics of the new regex + code constraint above is equivalent to the original regex.
I hope the author can adopt this repair solution and I would be very grateful. Thanks!
Automatic help documentation
It doesn't seem like it's currently possible to output human-readable documentation as expected when --help
is passed to a command line utility. It would be nice if an optional description
could be passed to each argument and have all arguments with their descriptions output into a neat string that could be logged to the console when --help
is set to true
.
Support defaults
It'd be sweet if this library also supported default arguments. The API could look like this:
arg({
'--help': Boolean,
'-h': '--help',
'--branch': String,
'-b': '--branch',
'--config': './config',
'-c': '--config'
})
You could distinguish between String
and a String argument with a default by doing:
if (value === String) {
// it's a string argument
}
if (typeof value === 'string') {
// it's a string argument with a default
}
Is there other way to input array of strings?
For example, I have this arg "--tte": [String],
so whenever I call the program I need to write myScript --tte event
is there a way to write multiple string without repeating --tte multiple times and splinting with commas?
Flags with optional values
Is there a way to have optional flag values? Something like the following:
const arg = require('arg');
const args = arg({
'--port': Number, // --port <number>
'--host': String.optional, // --host [string]
});
Given --port 5432 --host localhost
it should return { _: [], port: 5432, host: 'localhost' }
,
given --port 5432 --host
it should return something like { _: [], port: 5432, host: true }
,
and given --port 5432
it should return { _: [], port: 5432 }
,
Issues parsing negative numbers when passed without `=`
in node shell:
> const arg = require('arg')
undefined
> arg({"--int": parseInt}, {argv:["--int=-100"]})
{ _: [], '--int': -100 }
> arg({"--int": parseInt}, {argv:["--int -100"]})
Thrown:
{ Error: Unknown or unexpected option: --int -100
at arg (/run/media/karfau/hdd-data/dev/node-cli-arguments-options/arg/node_modules/arg/index.js:88:19) code: 'ARG_UNKNOWN_OPTION' }
> arg({"--num": parseFloat}, {argv:["--num=-1.0"]})
{ _: [], '--num': -1 }
> arg({"--num": parseFloat}, {argv:["--num -1.0"]})
Thrown:
{ Error: Unknown or unexpected option: --num -1.0
at arg (node-cli-arguments-options/arg/node_modules/arg/index.js:88:19) code: 'ARG_UNKNOWN_OPTION' }
I also tried to wrap the numbers with different kinds of quotes, but it didn't have any effect.
I don't know if this is a "feature" or a "bug".
Update: The example above is bad in the sense that argv
is different from what it would look like when using from the shell, which influences the error messages. A better example is:
> const arg = require('arg')
undefined
> arg({"--int": parseInt}, {argv:["--int", "-100"]})
Thrown:
Error: Option requires argument: --int
at arg (node-cli-arguments-options/arg/node_modules/arg/index.js:105:13)
> arg({"--float": parseFloat}, {argv:["--float", "-0.1"]})
Thrown:
Error: Option requires argument: --float
at arg (node-cli-arguments-options/arg/node_modules/arg/index.js:105:13)
Since the repo doesn't seem to be maintained (sorry for that, it's not true, must have switched the name with one of the other repos), just filing this to make people aware since I discovered it while comparing features of different argument parsers (WIP)
`arg` parses quoted argument values
It seems like arg
is parsing quoted strings.
Here is an online example: https://repl.it/repls/IllegalTraumaticLifecycles
Input string
--string "ES_JAVA_OPTS=-Xms512m -Xmx512m"
Output
Error: Unknown or unexpected option: -X
Short args can't be used with =
Short args can't be used with =
. Is this intentional?
const arg = require('arg')
arg({
'-a': String
})
node example.js -a=a
Results in:
throw new TypeError(`Option requires argument (but was followed by another short argument): ${originalArgName}`);
^
TypeError: Option requires argument (but was followed by another short argument): -a
at arg (<path>/node_modules/arg/index.js:97:12)
at Object.<anonymous> (<path>/example.js:5:1)
A similar error happens for aliases:
const arg = require('arg')
arg({
'--a': String,
'-a': '--a'
})
Check that all properties start with a hyphen
Need to make sure all properties start with a hyphen
Add `.npmignore`
I'm a huge fan of whitelists to make sure no garbage ever ends up in npm accidentally.
It would look approximately like this:
*
!index.js
!index.d.ts
Add code coverage analysis + badge
We've been using codecov.io
for this purpose with great results
Better name needed
After checking out the new spec and reading about @rauchg's and @Qix-' arguments for this, I think it makes sense to try it out in more of our projects in the future.
However, in order for this to happen, we need a name that's not ZEIT-dependent. Open source projects that focus on the author won't make it far (no matter if it's the code or just the name). The name (just like the functionality) needs to be much more general and independent (see "chalk", "hazel", "serve", "release", "micro", etc). People won't be interested in contributing otherwise.
In addition, "zarg" (spelled "z-arg") is not that easy to say, imo.
Add CI step for tests
CI should be added. @rauchg is ZEIT still in favor of tests-in-docker approach?
Add support for Deno
Title speaks for itself (I believe). It would largely just work by using esm
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.