argp
Command-line option parser
Inspired by the extremly well-known argp C library, this module parses GNU-style command-line options. Help, usage and version messages are automatically generated and line-wrapped at 80 columns. The module checks for errors, can be easily adapted to your needs thanks to its evented system and it also works when Node.js is in debug mode. The module is uncached and each property is deleted once all the input parameters have been parsed, so there's no memory footprint.
This module it's made for you if you want:
- Robust solution that reads GNU-style options.
- Command configuration.
- Basic error checking.
- Nice help messages without caring about indentation, multilines, etc.
- Zero memory footprint.
A common configuration looks like this:
var argv = require ("argp").createParser ({ once: true })
.description ("Sample app.")
.email ("a@b.c")
.body ()
.text (" Arguments:")
.argument ("arg", { description: "Sample argument" })
.text ("\n Options:")
.option ({ short: "o", long: "opt", description: "Sample option" })
.help ()
.version ("v1.2.3")
.argv ();
console.log (argv);
If you have a package.json
file you can take from it the description, email and version using the readPackage()
function. Take into account that this function calls a synchronous fs
operation. Doesn't really matter because this module is one of the first things you're going to execute in your program.
var argv = require ("argp")
.readPackage ("path/to/package.json")
.body ()
.text (" Arguments:")
.argument ("arg", { description: "Sample argument" })
.text ("\n Options:")
.option ({ short: "o", long: "opt", description: "Sample option" })
.help ()
.argv ();
Documentation
Functions
Objects
What's new in v1?
Two things will break your code:
-
Parser instances are introduced. To create one you need to call to createParser().
In most cases you only need to append .createParser ({ once: true })
, for example:
var argv = require ("argp").createParser ({ once: true })
...
argv ();
The once
option will uncache the module when argv() is called.
-
The second argument from the end event is removed.
Before:
.on ("end", function (argv, fns){
})
After:
.on ("end", function (argv){
})
Quick examples with no configuration
If an option has not been defined the type of the value is converted automatically from a string to a number, boolean, null or undefined.
By default the parser doesn't allow undefined arguments and options because you typically want to have an absolute control over the calls to your program in order to avoid unexpected behaviours. Allowing undefined arguments and options is as simple as this:
var argv = require ("argp").createParser ({ once: true })
.allowUndefinedArguments ()
.allowUndefinedOptions ()
.argv ();
$ node script.js -a -b -c
{ a: true, b: true, c: true }
$ node script.js a b c
{ a: true, b: true, c: true }
$ node script.js -abc null
{ a: true, b: true, c: null }
$ node script.js --a --b 1 --d=e
{ a: true, b: 1, d: "e" }
$ node script.js --no-a b
{ a: false, b: true }
$ node script.js --a -- -b --c d
{ a: true, "-b": true, "--c": true, d: true }
Configuring options
Example: options.js.
Considerations:
- By default the options are flags. If the option requires a value, the
metavar
property must be defined. This property is a string and can be seen when the --help and --usage messages are printed.
$ node script.js --help
...
o, --opt=STR Sample option
...
Where STR
is the metavar
property.
- By default, the value of the options is a string. Configure the
type
property if the value is a number, boolean (rarely used, use a flag instead) or array (comma-separated values and multiple assignments). - Each option has an id which is used to store the value in the final object. This id is the long name. If the long name has not been defined then the id is the short name.
.option ({ short: "a", long: "aa" })
.option ({ long: "aa" })
.option ({ short: "a" })
- Mandatory options (aka
required
) are not implemented because options are optional. Use a command if you need mandatory parameters.
Common properties between flags and options with a value:
- description - String
The description. - hidden - Boolean
If true, the option is not displayed in the --help and --usage messages. Default is false. - long - String
The long name. Cannot contain white spaces. - short - String
The short name. It must be an alphanumeric character.
Flags:
-
negate - Boolean
If true, the flag is negated. The default value is true and it becomes false when the short name or the negated long name (eg. --no-flag) is present.
.option ({ short: "a", long: "aaa" })
.option ({ short: "b", long: "bbb", negate: true })
$ node script.js
{ aaa: false, bbb: true }
$ node script.js -a -b
{ aaa: true, bbb: false }
$ node script.js --aaa --bbb
{ aaa: true, bbb: true }
$ node script.js --no-aaa --no-bbb
{ aaa: false, bbb: false }
Options with a value:
-
aliases - Array
An alias it's a long-name option that points to another option.
.body ()
.option ({ long: "name", aliases: ["foo", "bar"] })
.help ()
.usage ()
$ node script.js --foo
{ name: true }
$ node script.js --usage
Usage: script [--name|--foo|--bar] [-h|--help] [--usage]
$ node script.js --help
Usage: script [options]
--name, --foo, --bar
-h, --help Display this help message and exit
--usage Display a short usage message and exit
The options()
function returns an object with all the configured options:
{
name: { ... },
foo: { ... },
bar: { ... },
help: { ... },
usage: { ... }
}
Where name
, foo
and bar
point to the same object:
.on ("start", function (){
var options = this.options ();
assert.ok (options.name === options.foo && options.name === options.bar);
})
-
choices - Array
The input value must be one of these choices. If the option is optional
, the choices
property is ignored.
.option ({ long: "opt", metavar: "NUM", type: Number, choices: [1, 2, 3] })
$ node script.js --opt=1
{ opt: 1 }
$ node script.js --opt=7
When default
and choices
are defined in the same option the default value doesn't need to match any choice:
.option ({ long: "opt", metavar: "STR", default: "d", choices: ["a", "b", "c"] })
$ node script.js
{ opt: "d" }
-
default - Object
The default value.
.option ({ long: "name", metavar: "STR", default: "bar", optional: true })
$ node script.js
{ name: "bar" }
$ node script.js --name
{ name: "bar" }
$ node script.js --name foo
{ name: "foo" }
-
metavar - String
Must be defined if the option requires a value. If metavar
is not defined, the option is a flag. The string is used when the --help and --usage messages are printed.
.option ({ long: "name", metavar: "STR" })
$ node script.js --help
...
--name=STR
...
-
optional - Boolean
If true, the value is optional. Default is false. If the option doesn't receive any value the default value is set and it depends on the default
and type
properties.
Types and default values:
- String:
null
- Number:
0
- Array:
[]
- Boolean:
false
.option ({ long: "name1", metavar: "STR", optional: true })
.option ({ long: "name2", metavar: "STR", optional: true, type: String })
.option ({ long: "name3", metavar: "NUM", optional: true, type: Number })
.option ({ long: "name4", metavar: "ARR", optional: true, type: Array })
.option ({ long: "name5", metavar: "BOOL", optional: true, type: Boolean })
$ node script.js --name1 --name2 --name3 --name4 --name5
{ name1: null, name2: null, name3: 0, name4: [], name5: false }
$ node script.js --name1 foo --name2 bar --name3 12 --name4 -12.34,foo,true --name4 false --name5 true
{ name1: "foo", ame2: "bar", name3: 12, name4: [-12.34, "foo", true, false], name5: true }
-
reviver - Function
The function is executed when the option is parsed. It is similar to the reviver parameter of the JSON.parse()
function. This is the right place where you can validate the input data and fail()
if it's not valid. For example, if the option requires a number you can validate the range here.
.option ({ long: "name", metavar: "STR", reviver: function (value){
return value + "bar";
}})
$ node script.js --name foo
{ name: "foobar" }
.option ({ long: "opt", metavar: "NUM", type: Number,
reviver: function (value){
if (value < 1 || value > 3){
this.fail ("Option 'opt': Invalid range.");
}
return value;
}})
-
type - String | Number | Boolean | Array
The type of the value. Default is String.
If the type is an Array, comma-separated values are automatically stored in an array and each element is converted to the type it represents. Multiple assignments are also supported.
.option ({ long: "name", metavar: "ARR", type: Array })
$ node script.js --name 1,true,foo
{ name: [1, true, "foo"] }
$ node script.js --name 1 --name true --name foo
{ name: [1, true, "foo"] }
$ node script.js --name 1,2 --name true,false --name foo,bar
{ name: [1, 2, true, false, "foo", "bar"] }
Configuring arguments
Example: arguments.js.
An argument is an individual name like login
, reset
, build
, etc. They are basically flags.
Properties:
- description - String
The description. - hidden - Boolean
If true, the option is not displayed in the --help and --usage messages. Default is false.
Note: synopsis
and trailing
properties can be also configured but they have meaning only with commands.
.argument ("arg1")
.argument ("arg2", { description: "foo" })
.argument ("arg3", { description: "bar", hidden: true })
$ node script.js arg1
{ arg1: true, arg2: false, arg3: false }
$ node script.js --help
...
arg1 foo
arg2 bar
...
Note that an argument is just a flag but it can be present without any prefixed hyphen. It is a flag with more weight, it is used to denote important actions.
As you can see, the arguments are also stored in a hash like regular options, undefined arguments inclusive. For example:
var argv = require ("argp").createParser ({ once: true })
.allowUndefinedArguments ()
.allowUndefinedOptions ()
.argv ();
console.log (argv);
$ node script.js a b c
{ a: true, b: true, c: true }
This feature is different from other cli parsers that store the arguments in an array. If you need to read undefined arguments and save them in an array you can use the events. For more details: to-upper-case.js
Configuring commands
A command is an argument followed by other arguments and options. NPM is an example:
npm config set <key> [<value>]
npm install [<package>...] -g
config
is a command and set
an argument with 2 trailing arguments: minimum 1, maximum 2.
install
is a command with infinite trailing arguments: minimum 0, maximum Infinity. -g
is an option which only applies to the install
command.
If you have a very few commands you can configure them in the same file (commands.js example), but you typically want to modularize them, one command per file. Then you should check the npm.js example.
The commands are configured exactly the same way as the Argp
instance with only one difference: argument()
accepts 2 new properties:
-
synopsis - String
The string replaces the argument name in the --help and --usage messages.
.argument ("set", { description: "Sample argument" });
.argument ("set", { synopsis: "set <key> [<value>]", description: "Sample argument" });
-
trailing - Object
Configures how many trailing arguments must follow this argument.
There are 3 properties: eq
, min
and max
. eq
cannot be used with min
or max
. If min
and max
are used, by default min
is 0 and max
is Infinity. A trailing
object without any of these 3 properties defaults to min
0 and max
Infinity.
Some examples:
-
2 trailing arguments required: cmd arg <x> <x>
.
.argument ("arg", { trailing: { eq: 2 } })
-
1 required, 1 optional: cmd arg <x> [<x>]
.
.argument ("arg", { trailing: { min 1, max: 2 } })
-
1 optional: cmd arg [<x>]
.
.argument ("arg", { trailing: { max: 1 } })
-
1 required, infinite optional: cmd arg <x> [<x>...]
.
.argument ("arg", { trailing: { min: 1 } })
-
Infinite: cmd arg [<x>...]
.
.argument ("arg", { trailing: {} })
-
No trailing arguments: cmd arg
.
.argument ("arg")
-
Multiple arguments with trailing in the same line. Argument arg1
with 1 required, and argument arg2
with infinite trailing arguments: cmd arg1 <x> arg2 [<y>...]
. Note that writing cmd arg1 <x> arg2 [<y>...]
is not the same as cmd arg2 [<y>...] arg1 <x>
. In the latter case, arg1 <x>
will be eaten by the trailing arguments of arg2
.
.argument ("arg1", { trailing: { eq: 1 } })
.argument ("arg2", { trailing: {} })
Real examples
- brainfuck: Interpreter for the Brainfuck esoteric language.
- ntftp: Streaming TFTP client and server.
module.createParser([options]) : Argp
Returns a new Argp instance.
Options:
- once - Boolean
Set it to true if you want to uncache the whole module when argv() finishes. This will guarantee a zero memory footprint. Default is false.
Argp
The module returns an instance of Argp
. It inherits from an EventEmitter.
The parser follows the GNU-style rules: -a
, -abc
, --a
, --no-a
, --a=b
, --
, etc. Long-option abbreviation is also supported.
If you don't want to configure anything simply require the module, allow undefined arguments and options and call to argv()
.
var argv = require ("argp").createParser ({ once: true })
.allowUndefinedArguments ()
.allowUndefinedOptions ()
.argv ();
Note: If no configuration is provided you cannot join a short name with its value in the same token, eg: -Dvalue
, all the characters following a hyhen are interpreted as individual flags.
Events
With the event system you can fully adapt this module to yours needs. Examples: to-upper-case.js, mkdir.js.
Methods
Objects
argument
Emitted when an argument is found.
Parameters:
- argv - Object
The final object. - argument - String
The name of the argument. - ignore - Function
When the function is called the parser ignores the argument, hence it isn't stored in the final object.
end
Emitted when all the options and arguments have been parsed.
Parameters:
- argv - Object
The final object.
error
Emitted when an error occurs. If you don't listen for error
events, the message will be printed to stderr and the process will exit. If you attach an error handler and an error occurs, argv() returns null.
You typically want to listen for error
events when you need to do something with the error and then quit the process, or simply because you want to continue executing the process (maybe because you are executing a shell prompt) and display the error in the console.
.on ("error", function (error){
doSomethingWith (error);
this.fail (error);
})
For example, the ntftp module uses two parsers. One is the main parser. If something is not correct, it prints the error to stderr and finishes. The second parser is used when the program is executing a shell prompt. It is being reused and the errors are simply printed to console.
option
Emitted when an option is found.
Parameters:
- argv - Object
The final object. - option - String
The name of the option. - value - String
The value of the option after calling the reviver, if any. - long - Boolean
True if the option is a long name, otherwise false. - ignore - Function
When the function is called the parser ignores the argument, hence it isn't stored in the final object.
start
Emitted just before the parser begins to read the cli parameters.
Parameters:
- argv - Object
The final object. The default values are already set.
Argp#allowUndefinedArguments() : Argp
Allows undefined arguments.
Argp#allowUndefinedOptions() : Argp
Allows undefined options.
Argp#arguments() : Object
Returns the configured arguments. Look at the internal-data.js example for further details.
Argp#argv([input]) : Object | null
Parses the process.argv
array or the given input array.
If you don't need to reuse the parser and want a zero memory footprint, you shouldn't cache the module and the parser instance. Remember to also set once
to true.
var argv = require ("argp").createParser ({ once: true })
...
argv ();
If you need to reuse a parser, you probably want to listen for error events. If an error occurs, argv() returns null.
var argp = require ("argp");
var parser = argp.createParser ()
.on ("error", ...)
...;
var argv;
argv = parser.argv (["-a", "--b", ...]);
argv = parser.argv (["c", "d", ...]);
If you pass an input array, then the functions printHelp(), printUsage() and printVersion() will print the message but won't terminate the process.
Argp#body() : Argp
Returns a Body
instance.
Argp#columns(columns) : Argp
Sets the maximum line length. By default lines are wrapped at 80 columns.
Argp#command(name[, configuration]) : Command
Configures a command. A command it's like a new fresh cli program. It behaves exactly like an Argp
. See Configuring commands.
Argp#commands() : Object
Returns the configured commands.
Look at the internal-data.js example for further details.
Argp#description(str) : Argp
Sets a description. The description is printed at the start of the --help message, after the "Usage" lines.
Argp#email(str) : Argp
Sets a contact email. The email is printed at the end of the --help message.
Argp#exitStatus(code) : Argp
Sets the exit code that will be used is some methods. Default is 1.
Argp#fail(str[, code]) : undefined
Prints a message to the stderr and exits with the given code number or if not given, uses the code configured with exitStatus() or if not configured, exits with code 1.
Argp#main() : Argp
Returns de main Argp
instance. It's a no-op function, just for a better visual organization when configuring commands.
Look at the npm.js example for further details.
Argp#options([filter]) : Object
Returns the configured options. filter
is an object which can be used to return the options that have a short name or a long name.
.options ()
.options ({ short: true })
.options ({ long: true })
Look at the internal-data.js example for further details.
Argp#printHelp([code]) : undefined
Prints the help message and exits with the given code number or if not given, uses the code configured with exitStatus() or if not configured, exits with code 0.
If argv() is called with an input array, the process doesn't exit.
Argp#printUsage([code]) : undefined
Prints the usage message and exits with the given code number or if not given, uses the code configured with exitStatus() or if not configured, exits with code 0.
If argv() is called with an input array, the process doesn't exit.
Argp#printVersion([code]) : undefined
Prints the version message (if it was configured) and exits with the given code number or if not given, uses the code configured with exitStatus() or if not configured, exits with code 0.
If argv() is called with an input array, the process doesn't exit.
Argp#readPackage([path][, options]) : Argp
Reads a package.json
file and configures the parser with the description, email and version. If no path is provided it tries to read the ./package.json
path. The description, email and version labels can be ignored individually with the options
parameter. For example, if you only want to ignore the email and read the description and version:
.readPackage ("path/to/package.json", { email: false })
Options are: description
, version
, email
. By default they are true. Set them to false to ignore them.
Argp#usages(usages) : Argp
Changes the "Usage" line from the --help and --usage messages. usages
is an array of strings.
Look at the custom-usages.js example for further details.
Argp#sort() : Argp
If sort()
is enabled, the options are parsed before the arguments, if not, the options and arguments are parsed in the same order they come.
Body
The Body
instance is returned by calling Argp#body(). All the following functions (except argv()
, command()
and main()
) print a message in the same order they are configured, this allows you to fully customize the --help message very easily.
Look at fully-descriptive-help.js for further details.
Methods
Body#argument(name[, configuration]) : Body
Defines an argument. See Configuring arguments.
Body#argv([input]) : Object
Same as Argp#argv().
Body#columns(column1, column2) : Body
Prints a line with 2 columns. The first column shouldn't contain line breaks (\n
). This functionality is used to print the options and arguments.
Body#command(name[, configuration]) : Command
Same as Argp#command().
Body#help([options]) : Body
Enables the -h, --help
option. The short option can be disabled using the options
parameter.
.help ({ short: false })
$ node script.js --help
...
--help Display this help message and exit
...
Body#main() : Argp
Same as Argp#main().
Body#option(o) : Body
Defines an option. See Configuring options.
Body#text(str[, prefix]) : Body
Prints a text message. By default it's line-wrapped at 80 columns and supports multilines (line breaks, \n
). The prefix
is a string that is printed before each line. It's mainly used to indent the text with some spaces.
Body#usage() : Body
Enables the --usage
option.
Body#version(str[, options]) : Body
Enables the -v, --version
option. str
is the text to print when the option is called. The short option can be disabled using the options
parameter.
.version ("v1.2.3", { short: false })
$ node script.js --help
...
--version Output version information and exit
...