pxi-args
Version:
A command-line arguments parser for pxi (pixie), the small, fast, and magical command-line data processor
249 lines (190 loc) • 12.1 kB
Markdown
![pxi teaser][teaser]
`pxi-args` is a command-line arguments parser for [`pxi`][pxi] (pixie), the small, fast, and magical command-line data processor.
[![node version][shield-node]][node]
[![npm version][shield-npm]][npm-package]
[![license][shield-license]][license]
[![PRs Welcome][shield-prs]][contribute]
[![linux unit tests status][shield-unit-tests-linux]][actions]
[![macos unit tests status][shield-unit-tests-macos]][actions]
[![windows unit tests status][shield-unit-tests-windows]][actions]
## Installation
Installation is done using [`npm`][npm-install].
```bash
$ npm i --save pxi-args
```
## Work in Progress
This project is work in progress.
Use it at your own risk!
## `pxi-args`
`pxi-args` is the command-line argument parser used by [`pxi`][pxi].
### Defining Command-Line Arguments
The following definitions of `answerCmd` are identical:
```js
const answerCmd = {key: 'answer', args: ['--answer', '-a'], types: ['number'], only: [42]}
const answerCmd = number('answer', ['--answer', '-a'], {only: [42]})
const questionCmd = string('question', ['--question'])
```
You may either describe command-line arguments using a plain object
or a type function like `number` or `string` from the functional DSL.
The definition of `answerCmd` reads as follows:
> `answerCmd` is a command-line argument that is read to the `answer` key
> and is set using either `--answer` or `-a`.
> It must be followed by exactly one number that can only be `42`.
The object syntax takes `key`, `args`, and `types` as keys,
while the type function syntax takes `key` and `args` as the first two arguments
and sets `types` depending on the type function.
Additional fields are passed as additional keys in the object syntax
or in an object as the third parameter in the type function syntax.
#### Fields
The following command-line argument fields are available:
| Field | Value | Default | Description |
|---------|--------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------|
| `key` | string | `null` | The command-line argument's value is assigned to a key of this name. |
| `args` | array of strings | `[]` | A list of options that may be used to set the command-line option. |
| `desc` | string | `''` | Description of the command-line argument for use in the usage text. |
| `only` | array of values | `null` | The command-line argument's value can only be one of the values in this list. If `only` is `null`, the value may be set freely. |
| `opts` | [command-line option](#defining-command-line-options) object | `null` | This field is only used if the command-line argument is a command (if `types` is `null`). |
| `types` | `['number']` | | This command-line argument takes exactly one number. |
| `types` | `['string']` | | This command-line argument takes exactly one string. |
| `types` | `['bool']` | | This command-line argument takes exactly one boolean, `true` or `false`. |
| `types` | `['number','string']` | | This command-line argument takes exactly two values, a number and a string. |
| `types` | `[]` | | This command-line argument takes no value. It is a flag that is `true` if used and `false` if not used. |
| `types` | `null` | | This command-line argument is a command. It may have its own list of arguments (see `opts`) and is terminated by either `--` or a line ending. |
#### Functional DSL
The following type functions are available to generate command-line arguments:
| Function | Description |
|-----------------------------------|------------------------------------------------------------|
| `array(types)(key, args, fields)` | Assigns `types`, `key`, and `args` to `fields`. |
| `bool(key, args, fields)` | Assigns `types: ['bool']`, `key` and `args` to `fields`. |
| `command(key, args, fields)` | Assigns `types: null`, `key` and `args` to `fields`. |
| `flag(key, args, fields)` | Assigns `types: []`, `key` and `args` to `fields`. |
| `number(key, args, fields)` | Assigns `types: ['number']`, `key` and `args` to `fields`. |
| `string(key, args, fields)` | Assigns `types: ['string']`, `key` and `args` to `fields`. |
### Defining Command-Line Options
Command-line options are very similar to command-line arguments.
In fact, most [command-line parsers](#defining-command-line-parsers) use the `option` function
to generate them based on command-line arguments.
The following definitions of `answerOpt` are identical:
```js
const answerOpt = {
errs: [],
args: {
'--answer': [{key: 'answer', types: ['number'], only: [42]}],
'-a': [{key: 'answer', types: ['number'], only: [42]}]
}
}
const answerOpt = option(answerCmd)
```
You may either describe command-line options using a plain object
or the `option` function that takes a [command-line argument](#defining-command-line-arguments).
The definition of `answerOpt` reads as follows:
> `answerOpt` is a command-line option that has no errors and the following arguments:
> `--answer` is an argument that is read to the `answer` key and
> must be followed by exactly one number that can only be `42`.
> `-a` is an argument that is read to the `answer` key and
> must be followed by exactly one number that can only be `42`.
The reason why options are defined redundant is because that makes it easier for parsers to look them up.
Command-line arguments exist, because their syntax is easier to parse for usage generators.
Several Command-line options may be combined:
```js
const questionOpt = option(questionCmd)
const combinedOpt = combine(questionOpt, answerOpt)
const combinedOpt = {
errs: [],
args: {
'--question': [{key: 'question', types: ['string']}],
'--answer': [{key: 'answer', types: ['number'], only: [42]}],
'-a': [{key: 'answer', types: ['number'], only: [42]}]
}
}
```
The `combinedOpt` includes `questionOpt` as well as `answerOpt`.
If either one has errors, they are gathered in the `errs` array.
Setting errors explicitly does not make much sense.
However, `option` uses `errs` if e.g. a command-line argument does not set an `arg` or has an empty `args`.
In some cases (most cases actually), an answer is not a number but a string.
```js
const answerStrCmd = string('answerStr', ['-a']})
const answerStrOpt = option(answerStrCmd)
const combinedOpt = combine(answerCmd, answerStrCmd)
const combinedOpt = {
errs: [],
args: {
'--answer': [
{key: 'answer', types: ['number'], only: [42]}
],
'-a': [
{key: 'answer', types: ['number'], only: [42]}
{key: 'answerStr', types: ['string']}
]
}
}
```
Here, a new command-line argument `answerStr` is defined that has the same argument `-a` as `answer`.
If options of both command-line arguments are combined, the `-a` argument is interpreted twice:
By `answer` as a number that can only be `42`, as well as by `answerStr` as a string without restrictions.
An option can only be written two several keys, if the number of its arguments match.
If `combine` tries to adds another interpretation to an argument that does not have the same number of keys,
an error is recorded instead.
### Defining Command-Line Parsers
Command-line parsers combine command-line options with parser plugins
to process the command-line string given to a command-line program.
The following parser uses `combinedOpts`:
```js
const deepThought = opts => parser(
splitShortOptions,
parseArgs(opts),
mergeArgs()
)(opts)
const parse = deepThought(combinedOpt)
const result = parse(sliceArgv({argv: process.argv}))
```
The `deepThought` parser lets you parse command line input of the following kind:
```bash
$ node index.js --question "What is the answer to everything?" -a 42
```
Which would be equivalent to the following:
```js
const result = {
errs: [],
argv: {
"_": [],
question: "What is the answer to everything?",
answer: 42
}
}
```
#### Parser Plugins
The following parser plugins are available:
| Plugin | Description |
|-----------------------------------|-----------------------------------------------------------------------------------------------------------------|
| `sliceArgv({errs, argv})` | Removes the first two elements from a process.argv array, as they are meta data. |
| `splitShortOptions({errs, argv})` | Splits argument groups of shape `-vs` to `-v -s`. Only works if the arguments are preceded by a single dash. |
| `parseArgs({args})({errs, argv})` | Groups arguments together with their values, types, and options if an argument is a command. |
| `mergeArgs(parser)({errs, argv})` | Casts values to their types, validates values, and outputs the parsing result in a hierarchical JSON structure. |
Note that not every combination of plugins produces a valid parser.
## Reporting Issues
Please report issues [in the tracker][issues]!
## Contributing
We are open to, and grateful for, any contributions made by the community.
By contributing to pixie, you agree to abide by the [code of conduct][code].
Please read the [contributing guide][contribute].
## License
`pxi-args` is [MIT licensed][license].
[actions]: https://github.com/Yord/pxi-args/actions
[code]: https://github.com/Yord/pxi-args/blob/master/CODE_OF_CONDUCT.md
[contribute]: https://github.com/Yord/pxi-args/blob/master/CONTRIBUTING.md
[issues]: https://github.com/Yord/pxi-args/issues
[license]: https://github.com/Yord/pxi-args/blob/master/LICENSE
[node]: https://nodejs.org/
[npm-install]: https://docs.npmjs.com/downloading-and-installing-packages-globally
[npm-package]: https://www.npmjs.com/package/pxi-args
[pxi]: https://github.com/Yord/pxi
[shield-license]: https://img.shields.io/npm/l/pxi-args?color=yellow&labelColor=313A42
[shield-node]: https://img.shields.io/node/v/pxi-args?color=red&labelColor=313A42
[shield-npm]: https://img.shields.io/npm/v/pxi-args.svg?color=orange&labelColor=313A42
[shield-prs]: https://img.shields.io/badge/PRs-welcome-green.svg?labelColor=313A42
[shield-unit-tests-linux]: https://github.com/Yord/pxi-args/workflows/linux/badge.svg?branch=master
[shield-unit-tests-macos]: https://github.com/Yord/pxi-args/workflows/macos/badge.svg?branch=master
[shield-unit-tests-windows]: https://github.com/Yord/pxi-args/workflows/windows/badge.svg?branch=master
[teaser]: https://github.com/Yord/pxi/blob/master/teaser.gif?raw=true