UNPKG

nix-clap

Version:

Simple, lightweight, flexible, and comprehensive Un*x Command Line Argument Parsing for NodeJS

657 lines (501 loc) 23.1 kB
[![NPM version][npm-image]][npm-url] [![Build Status][build-image]][build-url] [![Dependency Status][daviddm-image]][daviddm-url] [![devDependency Status][daviddm-dev-image]][daviddm-dev-url] [![coverage][coverage-image]][coverage-url] # NixClap Simple, lightweight, flexible, and comprehensive Un\*x Command Line Argument Parsing for NodeJS. # Features - Lightweight with minimal dependencies. - Comprehensive and flexible parsing capabilities similar to conventional Un\*x parsing. - Flexible handling of options and commands that can take variadic params. - A simple and straightforward JSON interface for specifying options and commands. - Very informative result that tells you where each option came from. - [Webpack] friendly - allows bundling your cli into a single JS file with webpack. # Examples Options only: ```js const NixClap = require("nix-clap"); const options = { names: { desc: "specify names", alias: ["n", "m"], type: "string array" } }; const parsed = new NixClap() .version("1.0.0") .usage("$0 [options]") .init(options) .parse(); console.log("names", parsed.opts.names); ``` With commands: ```js const NixClap = require("nix-clap"); const options = { verbose: { desc: "enable verbose mode", alias: "v", type: "boolean", default: false } }; const commands = { compile: { desc: "run compile on the files", args: "<string files...>", exec: parsed => { console.log("compile", parsed.args.files, "verbose", parsed.opts.verbose); } } }; const parsed = new NixClap() .version("1.0.0") .usage("$0 [options] <command> [options]") .init(options, commands) .parse(); ``` > `version`, `help`, and `usage` must be called before `init` Usage: ```bash $ my-prog compile --verbose file1.jsx file2.jsx file3.jsx ``` ## More Examples See [examples](./examples) folder for more working samples. # Parsing Capabilities ## Options Example: `prog -xazvf=hello --foo-option hello bar -. --enable-blah` - Support `-` single char options or `--` long form options. - Options can have aliases. - Both option forms can have argument specified with `=` or space. - ie: long form `--foo-option=bar` or `--foo-option bar` - ie: short form `-f=bar` or `-f bar` - Both option forms can have variadic array args. - ie: `--foo-option hello bar` or `-f hello bar` - array args can have an optional type - `-` options can be compounded, like `-xazvf`. - Last char can have args, like `-xazvf=hello` or `-xazvf hello`. - Other chars are treated as `boolean` options automatically. - Variadic array args are terminated by any other options such as `-x` or `--xyz`, or explicitly with `-.` or `--.` - ie: `cmd1 arg1 arg2 --some-array abc def ghi -. cmd2 arg1 arg2`. - Allow arbitrary unknown options but with arguments specified through `=` only. - Since it's ambiguous whether to take a non-option arg following an unknown option as an argument or a command. - Counting number of option occurrences. - Boolean option can be negated with `--no-` prefix. - Allow custom value type coercions with a function or RegExp. ## Commands Example: `prog sum 1 2 3 4` - Commands can have optional or required arguments. - Each argument type defaults to `string`, but can have an optional type - Commands can have aliases. - Possible to specify multiple commands. - Commands can have variadic array arguments. - Variadic array args are terminated by any other options such as `-x` or `--xyz`, or explicitly with `-.` or `--.` - ie: `prog order pizza soda -. pickup` (specifies two commands: `order` and `pickup`) - Command can have its own options that are binded to it only. - Top level options can be binded to specific commands only. - Unbind top level options can be specified before or after commands. - Allow arbitrary unknown commands that do not have arguments. - Allow multiple custom value type coercions for each command. ## Terminating and Resuming - `--` terminates parsing, with remaining args returned in `parsed._`. - Parsing can be resumed after it's terminated. - `-.` or `--.` can terminate variadic params for commands and options. # Install ```bash npm i nix-clap --save ``` # Interface This module exposes a class with a few methods. See [APIs](#apis) for more details. ## `options spec` ```js const options = { "some-option": { alias: ["s", "so"], type: "string", desc: "description", default: "foo", require: true, requireArg: true, allowCmd: ["cmd1", "cmd2"] }, "another-option": {} }; ``` Where: | field | description | | ------------ | --------------------------------------------------------------------------------------------------------------------------------- | | `alias` | Specify aliases for the option, as a single string or an array of strings. | | `type` | Type of argument for the option, one of: `string`, `number`, `float`, `boolean`, `array`, `count`, or [coercion](#value-coercion) | | | `array` can set type of elements as one of `string`, `number`, `float`, `boolean` like this: `number array` or `float array` | | `desc` | Description for the option - a string or a function that returns string. | | `default` | Default value to use for argument | | `require` | `true`/`false` whether this option must be specified. | | `requireArg` | `true`/`false` whether argument for the option is required. | | `allowCmd` | list of command names this option is allow to follow only. | ## `commands spec` ```js const commands = { cmd1: { alias: ["c"], args: "<arg1> [arg2..]", usage: "$0 $1", desc: "description", exec: argv => {}, default: true, options: {} }, cmd2: {} }; ``` Where: | field | description | | --------- | ---------------------------------------------------------------------------------------------------------------------------------- | | `alias` | Specify aliases for the command, as a single string or an array of strings. | | `args` | Specify arguments for the command. `<>` means it's required and `[]` optional. See [rules](#rules-for-command-args) for more info. | | `usage` | usage message when help for the command is invoked - a string or a function that returns a string. | | | `$0` will be replaced with program name and `$1` with command name. | | `desc` | Description for the command - can be a string or a function that returns a string. | | `exec` | The callback handler for the command - see [here](#command-exec-handler) for more details. | | `default` | If `true`, set the command as default, which is invoked when no command was given in command line. | | | - Only one command can be default. | | | - Default command cannot have required args and must have the `exec` handler | | `options` | List of options arguments private to the command. Follows the same spec as [top level options](#options-spec) | ### Rules for Command `args` Rules for when specifying `args` for the command: - all required args must be before optional args - last one can specify variadic args with `..`, like `<names..>` or `[names..]` - If you just want to get the list of args without naming it, you can specify with `<..>` or `[..]` - named args can have an optional type like `<number value>` or `[number values..]` - supported types are `number`, `float`, `string`, `boolean`, or [coercion](#value-coercion) ## Value Coercion If none of the predefined types work for you, you can specify your own as a function or a RegExp, or any value. You use any valid identifier for the value type, and then you define a field with the same name in your spec that can be: - `function` - will be called with the value to convert - `RegExp` - will be used to match the value. `undefined` is returned if it didn't match. - Anything else - will be used as the converted value. For example: ```js const options = { customFn: { type: "fnval", fnval: value => { return value.substr(0, 1); } }, customRegex: { type: "rx", rx: /^test$/i }, customAny: { type: "foo", foo: "bar" } }; const commands = { foo: { args: "<type1 value1> <type2 value2>", type1: value => `test-${value}`, type2: /^test$/i } }; ``` ## Parse Result Use the method [`parse`](#parseargv-start-parsed) to parse command line arguments. It will return a parse result object. ```js { source: {}, opts: {}, verbatim: {}, commands: [], index: 5, error, _: [], argv: [] } ``` Where: - `index` - the index in `argv` parse stopped - `error` - If parse failed and your `parse-fail` event handler throws, then this will contain the parse error. See [skip default event behaviors](#skip-default-event-behaviors) for more details. - `source`, `opts`, `verbatim` - objects containing info for the options. See [details here](#parse-result-source-and-opts-objects) - `commands` - array of parsed command objects. See [`commands`](#parse-result-commands-object) for more details. - `argv` - original array of argv - `_` - remaining args in the `argv` array in case parsing was terminated by `--`. If any command with [`exec` handlers](#command-exec-handler) were specified, then `parse` will invoke them before returning the parse result object. ### Parse Result `source` and `opts` objects - `opts` - contains actual value for each option - `source` - contains info about where the option value came from - `cli` - option specified by user in the command line - `default` - default value in your [options spec](#options-spec) - `user` - values you applied by calling the [`applyConfig`](#applyconfigconfig-parsed-src) method - `verbatim` - contains original unprocessed value as given by the user in the command line - This is an array of values if there was actual values from the user - If there's no explicit value (ie. boolean or counting options), then this doesn't contain a field for the option. - If it's a boolean but the user specified with `--no-` prefix, then this contains a field with the value `["no-"]` For example, with the following conditions: 1. User specified `--foo-bar=test` in the command line 2. You have an option `fooDefault` with default value `bar` 3. You called `applyConfig` with `applyConfig({fooConfig: 1, fooBar: "oops"}, parsed)` You would get the following in the parse result object: ```js { source: { fooBar: "cli", fooDefault: "default", fooConfig: "user" }, opts: { fooBar: "test", fooDefault: "bar", fooConfig: 1 }, verbatim: { fooBar: ["test"] } } ``` > Note that the value `oops` for `fooBar` passed to `applyConfig` is not used since user's specified value is used. ### Parse Result `commands` object The `commands` object is an array of parsed commands: ```js { commands: [ { name: "cmdName", long: "cmdName", unknown: false, args: { foo: "bar", variadic: ["a", "b"] }, argList: ["bar", "a", "b"], opts: {}, source: {}, verbatim: {} } ]; } ``` - `name` is the name of the command used by the user in the command line that could be an alias - `long` is the original form of the command name (not the alias) - `unknown` - `true` if the command is not known - `args` - the processed named arguments - `argList` - list of all the arguments in unprocessed string form - `opts`, `source`, `verbatim` - info for the options private to the command ### Command `exec` handler If the command has an `exec` handler, then it will be called with two arguments: ```js exec(result, parsed); ``` - First one is the object for the command - Second one is the overall parsed object Info about the command object: ```js { name: "cmdName", long: "cmdName", args: { foo: "bar", variadic: [ "a", "b" ] }, argList: [ "bar", "a", "b" ], opts: {}, source: {}, verbatim: {} } ``` Where `opts` and `source` contain both the command's private options and top level options. > You can turn this off with the `skipExec` config flag passed to [`NixClap` constructor](#constructorconfig) ## Events `NixClap` emits these events: - `help` - when `--help` is invoked, emitted with the parse result object. - `pre-help` - before output for `--help` - `post-help` - after output for `--help` - `help` - when `--help` is invoked, emitted with the parse result object. - `version` - when `--version` is invoked, emitted with the parse result object. - `parsed` - when all parsing is done but before command `exec` are invoked, emitted with `{ nixClap, parsed }` where `nixClap` is the NixClap instance. - `parse-fail` - when parse failed, emitted with parse result object, which has `error` field. - `unknown-option` - when an unknown option is found, emitted with option name - `unknown-command` - when an unknown command is found, emitted with command context, which has `name` field. - `no-action` - when you have commands with `exec` and user specified no command that triggered an `exec` call. - `exit` - When program is expected to terminate, emit with exit code. ### Default Event Handlers NixClap has default handlers for these events: - `help` - Output help and emit `exit` - `version` - If `version` has been set, then output version and emit `exit`. - `parse-fail` - Output help and error message, and emit `exit`. - `unknown-option` - Throws Error `Unknown option ${name}` - `unknown-command` - Throws Error `Unkown command ${ctx.name}` - `no-action` - Output help with error `No command given` and emit `exit` - `exit` - calls `process.exit(code)` #### Skip Default Event Behaviors You can remove the default event handlers with one of these approaches: - With the [`removeDefaultHandlers`](#removedefaulthandlers) method. - By passing in `handlers` object in the `config` for the constructor. For example, using `removeDefaultHandlers`: ```js const nc = new NixClap().init(options, commands); const parsed = nc.removeDefaultHandlers("parse-fail").parse(); if (parsed.error) { // handle the parse error here } ``` Using constructor config. ```js const parsed = new NixClap({ handlers: { "parse-fail": false } }).parse(); if (parsed.error) { // handle the parse error here } ``` ## APIs These are methods `NixClap` class supports. - [NixClap](#nixclap) - [Features](#features) - [Examples](#examples) - [More Examples](#more-examples) - [Parsing Capabilities](#parsing-capabilities) - [Options](#options) - [Commands](#commands) - [Terminating and Resuming](#terminating-and-resuming) - [Install](#install) - [Interface](#interface) - [`options spec`](#options-spec) - [`commands spec`](#commands-spec) - [Rules for Command `args`](#rules-for-command-args) - [Value Coercion](#value-coercion) - [Parse Result](#parse-result) - [Parse Result `source` and `opts` objects](#parse-result-source-and-opts-objects) - [Parse Result `commands` object](#parse-result-commands-object) - [Command `exec` handler](#command-exec-handler) - [Events](#events) - [Default Event Handlers](#default-event-handlers) - [Skip Default Event Behaviors](#skip-default-event-behaviors) - [APIs](#apis) - [`constructor(config)`](#constructorconfig) - [`version(v)`](#versionv) - [`help(setting)`](#helpsetting) - [`usage(msg)`, `cmdUsage(msg)`](#usagemsg-cmdusagemsg) - [`init(options, commands)`](#initoptions-commands) - [`parse(argv, start, parsed)`](#parseargv-start-parsed) - [`parseAsync(argv, start, parsed)`](#parseasyncargv-start-parsed) - [`showHelp(err, cmdName)`](#showhelperr-cmdname) - [`removeDefaultHandlers()`](#removedefaulthandlers) - [`applyConfig(config, parsed, src)`](#applyconfigconfig-parsed-src) - [`runExec(parsed, skipDefault)`](#runexecparsed-skipdefault) - [`runExecAsync(parsed, skipDefault)`](#runexecasyncparsed-skipdefault) - [Others](#others) ### `constructor(config)` `config` is object with: - `name` - set the program name. Will auto detect from `process.argv` if not specified. - `version` - set the program version. Can also set with [`version`](#versionv) method. - `help` - custom help option setting. Can also set with [`help`](#helpsetting) method. - `usage` - usage message. Can also set with [`usage`](#usagemsg-cmdusagemsg) method. - `cmdUsage` - generic usage message for commands. Can also set with [`cmdUsage`](#usagemsg-cmdusagemsg) method. - `skipExec` - If true, will not call command `exec` handlers after parse. - `skipExecDefault` - if true, will not call default command `exec` handler after parse. - In case you need to do something before invoking the `exec` handlers, you can set these flags and call the [`runExec(parsed, skipDefault)`](#runexecparsed-skipdefault) method yourself. - `output` - callback for printing to console. Should take string as param. Default to calling `process.stdout.write` - `handlers` - custom event handlers. The `handlers` object can specify a function for each of the [events](#events) or set it to `false` to turn off the default handler. For example, this config will replace handler for `parse-fail` and turn off the default `unknown-option` handler. ```js const nc = new NixClap({ handlers: { "parse-fail": (parsed) => { ... }, "unknown-option": false } }); ``` ### `version(v)` Set program version with a string. ie: `1.0.0` Return: The `NixClap` instance itself. > Must be called before the [`init`](#initoptions-commands) method. ### `help(setting)` Set a custom option setting for invoking help. Default is: Return: The `NixClap` instance itself. ```js { alias: "h", desc: "Show help" } ``` Option name is always `help`. Call `help(false)` to turn off the default `--help` option. > Must be called before the [`init`](#initoptions-commands) method. ### `usage(msg)`, `cmdUsage(msg)` Set usage message for the program or command, which can be override by individual command's own usage. `msg` format is any string. `$0` will be replaced with program name and `$1` with command name. Return: The `NixClap` instance itself. > Must be called before the [`init`](#initoptions-commands) method. ### `init(options, commands)` Initialize your options and commands Return: The `NixClap` instance itself. ### `parse(argv, start, parsed)` Parse command line. Call without any params to parse `process.argv`. Return: The parse result object. - `argv` - array of CLI args. Defaults to `process.argv`. - `start` - index for argv from where to start parsing - `parsed` - previous result from `parse`. If passed, then parsing will add new data to it. ### `parseAsync(argv, start, parsed)` async version of [parse](#parseargv-start-parsed). - It will use [runExecAsync](#runexecasyncparsed-skipdefault) to invoke command `exec` handlers serially. - The command handler can return a Promise, which will be awaited. Return: A promise the resolve with the parse result object. ### `showHelp(err, cmdName)` Show help message and then emit `exit`. - `err` - if valid, then `err.message` will be printed after help message and exit with code `1`. - `cmdName` - if valid, then will print help for the specific command. ### `removeDefaultHandlers()` Remove NixClap's default handlers for the list of [event names](#events). If you've replaced the handler through specifying `handlers` in `config` for the constructor, then this will not remove your handler. Return: The `NixClap` instance itself. - You can pass in `"*"` to remove all default handlers. - You can pass in the event names you want to remove. ie: ```js nc.removeDefaultHandlers("parse-fail", "unknown-option", "unknown-command"); ``` ### `applyConfig(config, parsed, src)` Allow you to apply extra config to the parsed object, overriding any `opts` with `source` not equal to `cli`. For example, you can allow user to specify options in their `package.json` file, and apply those after the command line is parsed. - `config` - Config object containing user options config - `parsed` - The parse result object from NixClap. - `src` - String, source to set if override. Default to `user` Example on applying user config from `package.json`: ```js const pkg = require(path.resolve("package.json")); const parsed = nc.parse(); nc.applyConfig(pkg.cliConfig, parsed); ``` ### `runExec(parsed, skipDefault)` Go through the commands in parsed and call their `exec` handler. > The [`parse`](#parseargv-start-parsed) method will call this at the end unless `skipExec` flag is set. Return: The number of commands with `exec` was invoked. - `parsed` - The parse result object. - `skipDefault` - `boolean`, if `true` then do not invoke default command's `exec` handler when no command with `exec` handler was given. ### `runExecAsync(parsed, skipDefault)` async version of [runExec](#runexecparsed-skipdefault) Return: A promise that resolve with the number of commands with `exec` invoked. # Others - [argparse] - [yargs] - [commander] - [optimist] - [clap] - [clap.js] [optimist]: https://www.npmjs.com/package/optimist [clap]: https://github.com/lahmatiy/clap [clap.js]: https://github.com/litert/clap.js [argparse]: https://github.com/nodeca/argparse [yargs]: https://github.com/yargs/yargs [commander]: https://github.com/tj/commander.js [build-image]: https://github.com/jchip/nix-clap/actions/workflows/node.js.yml/badge.svg [build-url]: https://github.com/jchip/nix-clap/actions/workflows/node.js.yml [npm-image]: https://badge.fury.io/js/nix-clap.svg [npm-url]: https://npmjs.org/package/nix-clap [daviddm-image]: https://david-dm.org/jchip/nix-clap/status.svg [daviddm-url]: https://david-dm.org/jchip/nix-clap [daviddm-dev-image]: https://david-dm.org/jchip/nix-clap/dev-status.svg [daviddm-dev-url]: https://david-dm.org/jchip/nix-clap?type=dev [webpack]: https://webpack.js.org/ [coverage-image]: https://coveralls.io/repos/github/jchip/nix-clap/badge.svg?branch=master [coverage-url]: https://coveralls.io/github/jchip/nix-clap?branch=master