2020-07-29 22:30:43 +03:00
# argv.js advanced topics
This file will cover the usage, contents and configuration topics in more detail.
## Basics
2020-07-29 22:35:55 +03:00
For basics see [README.md ](./README.md )
2020-07-29 22:30:43 +03:00
## Contents
- [argv.js advanced topics ](#argvjs-advanced-topics )
2020-07-29 22:35:55 +03:00
- [Basics ](#basics )
- [Contents ](#contents )
- [Configuration ](#configuration )
- [Option/command configuration ](#optioncommand-configuration )
- [`<option>.handler(..)` ](#optionhandler )
- [`<option>.doc` ](#optiondoc )
- [`<option>.priority` ](#optionpriority )
- [`<option>.arg` ](#optionarg )
- [`<option>.type` ](#optiontype )
- [`<option>.collect` ](#optioncollect )
- [`<option>.env` ](#optionenv )
- [`<option>.default` ](#optiondefault )
- [`<option>.required` ](#optionrequired )
- [`<option>.valueRequired` ](#optionvaluerequired )
- [Built-in options ](#built-in-options )
- [Disabling or redefining a built-in option ](#disabling-or-redefining-a-built-in-option )
- [`-` / `--` ](#----- )
- [`-*` / `@*` ](#--- )
- [`-v` / `--version` ](#-v----version )
- [`-q` / `--quiet` ](#-q----quiet )
- [`-h` / `--help` ](#-h----help )
- [Value placeholders ](#value-placeholders )
- [Automatically defined values ](#automatically-defined-values )
- [`<parser>.doc` ](#parserdoc )
- [`<parser>.usage` ](#parserusage )
2020-08-02 07:21:59 +03:00
- [`<parser>.packageJson` ](#parserpackagejson )
2020-07-29 22:35:55 +03:00
- [`<parser>.version` ](#parserversion )
2020-08-02 07:21:59 +03:00
- [`<parser>.author` ](#parserauthor )
2020-07-29 22:35:55 +03:00
- [`<parser>.license` ](#parserlicense )
- [`<parser>.examples` ](#parserexamples )
- [`<parser>.footer` ](#parserfooter )
- [More control over help... ](#more-control-over-help )
- [Nested parsers ](#nested-parsers )
- [Components and API ](#components-and-api )
- [`THEN` / `STOP` ](#then--stop )
- [`ParserError(..)` ](#parsererror )
- [`Parser(..)` ](#parser )
2020-08-23 11:09:11 +03:00
- [`<parser>.onArgs(..)` ](#parseronargs )
- [`<parser>.onNoArgs(..)` ](#parseronnoargs )
2020-07-29 22:35:55 +03:00
- [`<parser>.then(..)` ](#parserthen )
- [`<parser>.stop(..)` ](#parserstop )
- [`<parser>.error(..)` ](#parsererror-1 )
- [`<parser>.off(..)` ](#parseroff )
- [`<parser>(..)` ](#parser-1 )
2020-08-26 16:38:19 +03:00
- [`Parser.chain(..)` (EXPERIMENTAL) ](#parserchain-experimental )
2020-07-29 22:35:55 +03:00
- [Advanced parser API ](#advanced-parser-api )
- [`<parser>.print(..)` / `<parser>.printError(..)` ](#parserprint--parserprinterror )
- [`<parser>.handlerDefault(..)` ](#parserhandlerdefault )
- [`<parser>.handleArgumentValue(..)` ](#parserhandleargumentvalue )
- [`<parser>.handleErrorExit(..)` ](#parserhandleerrorexit )
- [`<parser>.handle(..)` ](#parserhandle )
- [`<parser>.setHandlerValue(..)` ](#parsersethandlervalue )
2020-08-02 20:28:56 +03:00
- [External utilities ](#external-utilities )
- [`normalizeIndent(..)` / `normalizeTextIndent(..)` ](#normalizeindent--normalizetextindent )
2020-07-29 22:35:55 +03:00
- [More... ](#more )
2020-07-29 22:30:43 +03:00
## Configuration
```
Parser(< spec > )
-> < parser >
```
The `<spec>` object is "merged" into the `<parser>` instance overriding
or extending it's API/data.
The `<parser>` expects/handles the following data in the `<spec>` object:
- the configuration attributes and methods
Attributes and methods used to configure, modify, extend or overload
parser functionality.
Note that these attributes are the same attributes inherited by `<parser>`
and are simply merged into the new instance created by `Parser(..)` , thus
there are no restrictions on what attributes/methods can be overloaded
or extended in this way, but care must be taken when overloading elements
that were not designed to be overloaded.
- option/command definitions
The keys for these are prefixed either by `"-"` for options or by `"@"`
for commands and are either _objects_ , _functions_ or _parser_ instances.
The only difference between an _option_ and a _command_ is that the former
are passed to the _script_ with a `"-"` or `"--"` prefix (by default) and
the later are passed by name without prefixes.
In all other regards options and commands are the same.
- option/command aliases
An alias is an option/command key with a _string_ value.
That value _references_ a different option or command, i.e. is an
option/command name.
Looping (referencing the original alias) or dead-end (referencing
non-existent options) aliases are ignored.
### Option/command configuration
#### `<option>.handler(..)`
Option handler.
```javascript
'-option': {
handler: function(opts, key, value){
// handle the option...
// ...
},
},
```
or a shorthand:
```javascript
'-option': function(opts, key, value){
// handle the option...
// ...
},
```
The handler gets called if the option is given or if it was not explicitly
given but has a default value set.
`opts` contains the mutable list of arguments passed to the script
starting just after the currently handled option/command. If the handler
needs to handle it's own arguments it can modify this list in place and
the _parser_ will continue from the resulting state.
One use-case for this would be and option handler that needs to handle
it's arguments in a custom manner, for example for handling multiple
arguments.
`key` is the actual normalized (`[<prefix-char>]<name-str>` )
option/command triggering the `.handler(..)` .
This can be useful to identify the actual option triggering the handler
when using aliases, if a single handler is used for multiple options, or
when it is needed to handle a specific prefix differently (a-la `find` 's
syntax with `+option` and `-option` having different semantics).
`value` gets the value passed to the option.
A _value_ can be passed either explicitly passed (via `=` syntax),
implicitly parsed from the `argv` via the `<option>.arg` definition or
is `undefined` otherwise.
A handler can return one of the `THEN` , `STOP` or `ParserError` instance
to control further parsing and/or execution.
(See: [`THEN` / `STOP` ](#then-stop ) for more info.)
#### `<option>.doc`
Option/command documentation string used in `-help` .
2020-08-10 23:17:10 +03:00
```
doc: < string > | < array-of-strings > ,
```
If an array of strings is given each string will be printed on a separate
line.
2020-07-29 22:30:43 +03:00
If this is set to `false` the option will be hidden from `-help` .
#### `<option>.priority`
Option/command priority in the `-help` .
Can be a positive or negative number or `undefined` .
Ordering is as follows:
- options in descending positive `.priority` ,
- options with undefined `.priority` in order of definition,
- options in descending negative `.priority` .
Note that options and commands are grouped separately.
The built-in options `-help` , `-version` and `-quiet` have a priority
of `99` so that they appear the the top of the `-help` list.
Any option defining `.required` and not defining an explicit `.priority`
will be sorted via `<parser>.requiredOptionPriority` (`80` by default).
#### `<option>.arg`
Option/command argument definition.
```
arg: '< arg-name > '
arg: '< arg-name > | < key > '
```
If defined and no explicit value is passed to the option command (via `=` )
then the _parser_ will consume the directly next non-option if present in
`argv` as a value, passing it to the `<option>.type` handler, if defined,
then the `<option>.handler(..)` , if defined, or setting it to `<key>`
otherwise.
Sets the option/command argument name given in `-help` for the option
and the key where the value will be written.
The `<key>` is not used if `<option>.handler(..)` is defined.
#### `<option>.type`
Option/command argument type definition.
The given type handler will be used to convert the option value before
it is passed to the handler or set to the given `<key>` .
Supported types:
- `"string"` (default behavior)
- `"bool"`
- `"int"`
- `"float"`
- `"number"`
- `"date"` – expects a `new Date(..)` compatible date string
- `"list"` – expects a `","` -separated value, split and written as
an `Array` object
Type handlers are defined in `Parser.typeHandlers` or can be overwritten
by `<spec>.typeHandlers` .
If not set values are written as strings.
Defining a new global type handler:
```javascript
// check if a value is email-compatible...
argv.Parser.typeHandlers.email = function(value, ...options){
if(!/[a-zA-Z][a-zA-Z.-]*@[a-zA-Z.-]+/.test(value)){
throw new TypeRrror('email: format error:', value) }
return value }
```
Defining a local to parser instance type handler:
```javascript
var parser = new Parser({
// Note that inheriting from the global type handlers is required
// only if one needs to use the global types, otherwise just setting
// a bare object is enough...
typeHandlers: Object.assign(Object.create(Parser.typeHandlers), {
email: function(value, ...options){
// ...
},
// ...
}),
// ...
})
```
#### `<option>.collect`
Option value collection mode.
The given handler will be used to _collect_ values passed to multiple
occurrences of the option and write the result to `<key>` .
Supported collection modes:
- `"list"` – group values into an `Array` object
- `"set"` – group values into a `Set` object
- `"string"` – concatenate values into a string.
This also supports an optional separator, for example `"string|\t"` will
collect values into a string joining them with a tab (i.e. `"\t"` ).
Default separator is: `" "`
- `"toggle"` – toggle option value (bool).
Note that the actual value assigned to an option is ignored here and can
be omitted.
Type handlers are defined in `Parser.valueCollectors` or can be overwritten
by `<spec>.valueCollectors` .
`<option>.collect` can be used in conjunction with `<option>.type` to both
convert and collect values.
If not set, each subsequent option will overwrite the previously set value.
Defining a global value collector:
```javascript
// '+' prefixed flags will add values to set while '-' prefixed flag will
// remove value from set...
argv.Parser.valueCollectors.Set = function(value, current, key){
current = current || new Set()
return key[0] != '-' ?
current.add(value)
: (cur.delete(value), current) }
```
Defining handlers local to a parser instance handler is the same as for
[type handlers ](#optiontype ) above.
#### `<option>.env`
Determines the environment variable to be used as the default value for
option/command, if set.
If this is set, the corresponding environment variable is non-zero and
`<option>.handler(..)` is defined, the handler will be called regardless
of weather the option was given by the user or not.
#### `<option>.default`
Sets the default value for option/command's value.
If this is set to a value other than `undefined` and
`<option>.handler(..)` is defined, the handler will be called regardless
of weather the option was given by the user or not.
#### `<option>.required`
Sets weather the _parser_ should complain/err if option/command is
not given.
Note that this also _implicitly_ prioritizes the option, for more info see:
[`<option>.priority` ](#optionpriority ).
#### `<option>.valueRequired`
Sets weather the _parser_ should complain/err if option/value value is
not given.
### Built-in options
#### Disabling or redefining a built-in option
To disable a built-in option simply assign `undefined` , `false` or `null` to it.
```javascript
// disable help...
'-help': undefined,
```
Redefining or extending options is done by either shadowing it completely or by overloading it partially.
```javascript
// redefine and option...
'-version': {
doc: 'an alternative version',
handler: function(){
console.log('1.2.3')
return argv.STOP } },
// extend...
'-quiet': Object.assign(
Object.create(argv.Parser.prototype['-quiet']),
{
// hide -quiet from -help
doc: false,
}),
```
#### `-` / `--`
Stop processing further options.
This can be used to terminate nested parsers or to stop option processing
in the root parser to handle the rest of the options in `<parser>.then(..)` ,
for example.
#### `-*` / `@*`
Handle options/commands for which no definition is found.
By default `-*` will print an "unhandled option/command" error and terminate.
By default `@*` is an alias to `-*` .
#### `-v` / `--version`
This will output the value of `.version` and exit.
#### `-q` / `--quiet`
This will turn quiet mode on.
In quiet mode [`<parser>.print(..)` ](#parserprint--parserprinterror ) will
not print anything.
Passing [`--help` ](#-h----help ) or [`--version` ](#-v----version ) will
disable quiet mode and print normally.
Note that this will only set `<parser>.quiet` to `true` and disable output
of [`<parser>.print(..)` ](#parserprint--parserprinterror ), any user code
needs to either also use [`<parser>.print(..)` ](#parserprint--parserprinterror )
for output (not always practical) or respect `<parser>.quiet` .
#### `-h` / `--help`
By default `-help` will output in the following format:
```
< usage >
< doc >
Options:
< option-spec > < option-val >
- < option-doc >
(< opt-required > , < opt-default > , < opt-env > )
...
Dynamic options:
...
Commands:
...
Examples:
...
< footer >
```
All sections are optional and will not be rendered if they contain no data.
##### Value placeholders
All documentation strings can contain special placeholders that
will get replaced with appropriate values when rendering help.
- `$SCRIPTNAME` replaced with the value of `.scriptName` ,
- `$VERSION` replaced with `.version` ,
- `$LICENSE` replaced with `.license` .
##### Automatically defined values
These values are set by the parser just before parsing starts:
- `.script` - full script path, usually this is the value of `argv[0]` ,
- `.scriptName` - base name of the script,
- `.scriptPath` - path of the script.
These will be overwritten when the parser is called.
##### `<parser>.doc`
Script documentation.
< spec > .doc = < string > | < function >
Default value: `undefined`
##### `<parser>.usage`
Basic usage hint.
< spec > .usage = < string > | < function > | undefined
Default value: `"$SCRIPTNAME [OPTIONS]"`
2020-08-02 07:21:59 +03:00
##### `<parser>.packageJson`
The path to the metadata JSON file.
< spec > .packageJson = < string > | < function > | undefined
If not set `package.json` will be searched for in the same directory as the
main script.
Default value: `undefined` .
2020-07-29 22:30:43 +03:00
##### `<parser>.version`
2020-08-02 07:21:59 +03:00
2020-07-29 22:30:43 +03:00
Version number.
< spec > .usage = < string > | < function > | undefined
2020-08-02 07:21:59 +03:00
If this is not defined it will be read from the project metadata, if none is
found `"0.0.0"` will be printed by `-version` .
2020-07-29 22:30:43 +03:00
Default value: `undefined`
2020-08-02 07:21:59 +03:00
##### `<parser>.author`
Author name/contacts.
< spec > .author = < string > | < function > | undefined
If not set project metadata is used, if found.
Default value: `undefined`
2020-07-29 22:30:43 +03:00
##### `<parser>.license`
2020-08-02 07:21:59 +03:00
2020-07-29 22:30:43 +03:00
Short license information.
< spec > .usage = < string > | < function > | undefined
2020-08-02 07:21:59 +03:00
If not set project metadata is used, if found.
2020-07-29 22:30:43 +03:00
Default value: `undefined`
##### `<parser>.examples`
< spec > .usage = < string > | < list > | < function > | undefined
Example list format:
[
[< example-code > , < example-doc > , ...],
...
]
Default value: `undefined`
##### `<parser>.footer`
Additional information.
< spec > .footer = < string > | < function > | undefined
Default value: `undefined`
##### More control over help...
For more info on help formatting see `<parser>.help*` attributes in the [source ](./argv.js ).
### Nested parsers
An option/command handler can be a _parser instance_ .
From the point of view of the _nested parser_ nothing is different –
it gets passed the remaining list of arguments and handles it on it's own.
The _containing parser_ treats the nested parser just like any normal
handler with it's attributes and API.
Note that if the _nested parser_ consumes the rest of the arguments,
the _containing parser_ is left with an empty list and it will stop
parsing and return normally.
A way to explicitly stop the _nested parser_ processing at a specific
point in the argument list is to pass it a `-` argument at that point.
For example:
2020-08-02 01:59:10 +03:00
```shell_session
2020-07-29 22:30:43 +03:00
$ script -a nested -b -c - -x -y -z
```
Here `script` will handle `-a` then delegate to `nested` which in turn
will consume `-b` , `-c` and on `-` return, rest of the arguments are
again handled by `script` .
This is similar to the way programming languages handle passing arguments
to functions, for example in [Lisp ](https://en.wikipedia.org/wiki/Common_Lisp )
this is similar to:
```lisp
(script a (nested b c) x y z)
```
And in _C-like-call-syntax_ languages like
[C ](https://en.wikipedia.org/wiki/C_(programming_language ))/[Python ](https://python.org )/JavaScript/...
this would (a bit less cleanly) be:
```javascript
script(a, nested(b, c), x, y, z)
```
The difference here is that `nested` has control over what it handles, and
depending on its definition, can either override the default `-` option as
well as stop handling arguments at any point it chooses (similar to _words_
in stack languages like [Fort ](https://en.wikipedia.org/wiki/Forth_(programming_language ))
or [Factor ](https://factorcode.org/ )).
<!--
XXX split ./lang.js from ./test.js...
See [lang.js ](./lang.js ) for more fun with argv and programming languages ;)
-->
## Components and API
### `THEN` / `STOP`
Values that if returned by option/command handlers can control the parse flow.
- `THEN` – Stop parsing and call `<parser>.then(..)` callbacks.
- `STOP` – Stop parsing and call `<parser>.stop(..)` callbacks,
skipping `<parser>.then(..)` .
`THEN` is useful when we want to stop option processing and trigger the
post-parse stage (i.e. calling `<parser>.then(..)` ) for example to pass
the rest of the options to some other command.
`STOP` is used for options like `-help` when no post-parsing is needed.
### `ParserError(..)`
A base error constructor.
2020-07-29 23:20:17 +03:00
If an instance of `ParserError` is _thrown_ by the handler:
- parsing is stopped,
- the error is reported via [`<parser>.printError(..)` ](#parserprint--parserprinterror ),
- [`<parsing>.error(..)` ](#parsererror-1 ) is called,
- the parser will exit with an error ([`<parser>.handleErrorExit(..)` ](#parserhandleerrorexit )).
`ParserError` can also be _returned_ form the handler, this has almost the
same effect as throwing it but the error will _not_ be automatically reported.
2020-07-29 22:30:43 +03:00
The following error constructors are also defined:
- `ParserTypeError(..)`
- `ParserValueError(..)`
### `Parser(..)`
Construct a parser instance
```
Parser(< spec > )
-> < parser >
```
See [`<parser>(..)` ](#parser-1 ) for more info.
2020-08-23 11:09:11 +03:00
#### `<parser>.onArgs(..)`
Add callback triggered when one or more arguments are passed to the parser.
```
< parser > .onArgs(< callback > )
-> < parser >
```
```
callback(< args > )
-> < obj >
```
Note that this is triggered _before_ parsing begins.
#### `<parser>.onNoArgs(..)`
Add callback triggered when no arguments are passed to the parser.
```
< parser > .onNoArgs(< callback > )
-> < parser >
```
```
callback(< args > )
-> < obj >
```
Note that this is triggered _before_ parsing begins.
2020-07-29 22:30:43 +03:00
#### `<parser>.then(..)`
Add callback to `then` "event".
```
< parser > .then(< callback > )
-> < parser >
```
```
callback(< unhandled > , < root-value > , < rest > )
-> < obj >
```
`then` is triggered when parsing is done or stopped from an option
handler by returning `THEN` .
#### `<parser>.stop(..)`
Add callback to `stop` "event".
```
< parser > .stop(< callback > )
-> < parser >
```
```
callback(< arg > , < rest > )
-> < obj >
```
`stop` is triggered when a handler returns `STOP` .
#### `<parser>.error(..)`
Add callback to `error` "event".
```
< parser > .error(< callback > )
-> < parser >
```
```
callback(< reason > , < arg > , < rest > )
-> < obj >
```
`error` is triggered when a handler returns `ERROR` .
#### `<parser>.off(..)`
Remove callback from "event".
```
< parser > .off(< event > , < callback > )
-> < parser >
```
#### `<parser>(..)`
Execute the `parser` instance.
2020-07-29 23:23:49 +03:00
Run the parser on `process.argv` implicitly:
2020-07-29 22:30:43 +03:00
```
< parser > ()
-> < result >
```
Explicitly pass a list of arguments where `<argv>[0]` is treated as
the script path.
```
< parser > (< argv > )
-> < result >
```
Explicitly pass both a list of arguments and script path.
```
< parser > (< argv > , < main > )
-> < result >
```
If `<main>` is present in `<argv>` all the arguments before it will
be ignored, otherwise the whole list is processed as if `<main>` was
its head.
2020-08-26 16:38:19 +03:00
### `Parser.chain(..)` (EXPERIMENTAL)
Chain several parsers for staggering option/command processing.
```
Parser.chain(< spec > , < spec > , ..)
-> < parser >
```
This creates a chain of parsers, each processing only the arguments it defines
and passes the rest to the next in chain. This is useful for defining arguments
that need to be processed out of order and before anything else.
This is similar to chaining parsers via `.then(..)` but with additional setup:
- all parsers except the last will have:
- `.splitOptions` set to `false`
- `"-help"` set to `undefined` enabling `-help` pass-through
- `"-*"` and `"@*"` set to `undefined` enabling arguments pass-through
- the last parser will have all the options from the other parsers merged into
it for complete docs/`-help`
XXX the resulting `<parsed>` object will only contain data from the last parser,
this may change in the future.
2020-07-29 22:30:43 +03:00
## Advanced parser API
### `<parser>.print(..)` / `<parser>.printError(..)`
Handle how `<parser>` prints things.
`<parser>.print(..)` and `<parser>.printError(..)` are very similar but handle different
cases, similar to `console.log(..)` and `console.error(..)`
```
< parser > .print(...)
-> < parser >
< parser > .printError(...)
-> < parser >
< parser > .printError(< error > , ...)
-> < error >
```
Both support callback binding:
```
< parser > .print(< func > )
-> < parser >
< parser > .printError(< func > )
-> < parser >
```
Both `<parser>.print(..)` and `<parser>.printError(..)` can safely be
overloaded if the callback feature is not going to be used by the user
– the print callbacks are not used internally.
For full callback API see: `extra.afterCallback(..)` in [argv.js ](./argv.js ).
### `<parser>.handlerDefault(..)`
Called when `<option>.handler(..)` is not defined.
By default this sets option values on the _parsed_ object.
### `<parser>.handleArgumentValue(..)`
Handle argument value conversion.
By default this handles the `<option>.type` mechanics.
If this is set to `false` values will be set as-is.
### `<parser>.handleErrorExit(..)`
Handle exit on error.
By default this will call process.exit(1) for the _root parser_ and does
nothing for _nested parsers_ .
If set to `false` the _parser_ will simply return like any normal function.
### `<parser>.handle(..)`
Manually trigger `<arg>` handling.
```
< parser > .handle(< arg > , < rest > , < key > , < value > )
-> < res >
```
This is intended to be used for delegating handling from one handler to
another. Note that this does not handle errors or other protocols handled
by `<parser>(..)` , this only calls the `<arg>` handler (or if it was not
defined the _default handler_ ) so it is not recommended for this to be
called from outside an option handler method/function.
This is not intended for overloading.
### `<parser>.setHandlerValue(..)`
Set handler value manually, this uses `<handler>.arg` and if not set `<key>` to
write `<value>` on the _parsed_ object.
```
< parser > .setHandlerValue(< handler > , < key > , < value > )
-> < parser >
```
This is useful when extending `argv.js` , for client code values can be set
directly.
This is not intended for overloading.
2020-08-02 20:28:56 +03:00
## External utilities
### `normalizeIndent(..)` / `normalizeTextIndent(..)`
`argv.js` uses and exposes [`object.js` ](https://github.com/flynx/object.js )'
text normalization functions for convenient text/code formatting, see [original documentation ](https://github.com/flynx/object.js#normalizeindent--normalizetextindent ) for more info.
2020-07-29 22:30:43 +03:00
## More...
For more info see the [source ](./argv.js ).
<!-- vim:set ts=4 sw=4 spell : -->