Parser
The ArgumentParser
class is the main library component. It can be instantiated with a set of
option definitions and provides methods to parse command-line arguments into option values.
Option values
The result of argument parsing is an object that contains the option values corresponding to the option definitions given in the parser constructor. There are different ways to obtain these values, as explained below.
Using a newly-created object
This is the easiest, most common method: you just call parse
and await the returned promise. This
method accepts two optional parameters:
cmdLine
- the raw command line (string
) or the command-line arguments (string[]
)flags
- aParsingFlags
object with the parsing flags
Normally you should not need to pass these parameters, as they have sensible default values. The
command line defaults to the value of either the COMP_LINE
or BUFFER
environment variables
(whichever is present) or the process arguments:
process.env['COMP_LINE'] ?? process.env['BUFFER'] ?? process.argv.slice(2);
If you feel the need to configure the parsing procedure, make sure to first understand how word completion works.
This method is asynchronous, so you must use await
in order to resolve the returned
promise.
Using your own object
This is a more flexible method that allows you to pass an existing object or class instance, which
will be filled with the parsed values. It is named parseInto
and accepts the same parameters as
the previous one, except for an additional (first) parameter:
values
- the option values to parse into
Existing values are preserved until they get overridden by values parsed either from the command line or from environment variables.
When using TypeScript, this parameter will be type-checked against the expected type of the option values for a given set of option definitions.
Using IntelliSense, you can peak at the resulting type of the parse
method, or declare a
temporary type alias for OptionValues<typeof _your_options_>
, to see how the object is
structured.
This method returns a promise that resolves to a ParsingResult
object containing the following
properties:
warning
- a compilation of warning messages generated by the parser, if any
Parsing flags
The parsing procedure can be configured with a ParsingFlags
object that has some optional
properties, as described below.
Program name
The progName
property is a custom name for the main command, which is used to update the process
title and is rendered in the help message's usage section. It defaults to the basename of the
executing script:
process.argv[1].split(/[\\/]/).at(-1);
Completion index
The compIndex
property is the cursor position in a command line for which the user pressed Tab
.
It defaults to the value of either the COMP_POINT
or CURSOR
environment variables, or to the
length of the BUFFER
variable (whichever is present, otherwise undefined):
Number(process.env['COMP_POINT'] ?? process.env['CURSOR']) || process.env['BUFFER']?.length;
When invoking the parsing methods with a raw command line, and this flag is set, the parser will know to perform word completion for the argument ending at this position.
Cluster prefix
The clusterPrefix
property specifies the prefix for cluster arguments. If set, then eligible
arguments with this prefix may be considered a cluster. This must be used in conjunction with the
cluster letters option attribute.
Option names have precedence over the cluster prefix. For example, if the prefix is a single dash
and there's an option named '-flag'
, then an argument with this name will not be
considered a cluster argument.
Parsing features
The parser supports a set of features that should fulfill most use cases. They are listed below.
Custom callbacks
Custom parsing operations are supported through the following kinds of callbacks:
- requirement callback
- default callback
- parse callback
- complete callback
- function callback
- command callback
All of these can be asynchronous, in which case the parser awaits their resolution, either because it needs their result or to avoid data races when reading and modifying the option values.
Note that there is no synchronous version of the parsing procedure. This is a design choice: in
order to support a fully synchronous parse
method using the same code base, the parser would have
to deal with returned promises in a way that would make it hard to maintain (remember the classic
callback hell (opens in a new tab)).
Besides, the asynchronous version has the benefit of allowing us to perform some operations, such as
requirements checking, concurrently. This should not be a drawback for most applications, since
argument parsing is usually done at the top-level of a module or script, where the await
directive is available.
Argument sequence
An argument sequence is the occurrence of an option in the command line with possible parameters. The parser looks for option names or positional arguments once it finishes parsing the previous option, or if the latter is variadic.
For example, if the option initiating a sequence is a string option with a fallback value, the parser starts looking for new options as soon as it advances to the next argument, since that may be either the option's parameter or the start of a new sequence. Supposing that it is a parameter, once the parser advances past this argument, it knows that the next one must be the start of a sequence.
The same algorithm works for function options with a variable parameter count. For example, if the option expects between 1 and 3 parameters, the parser treats the next argument as a parameter to the option, regardless of whether it is an option name or not. Once the first parameter is saved, the parser resumes searching until 3 parameters are accumulated, at which point it must find an option specification (even if it is a new occurrence of the same option).
Sequence information
The information gathered by the parser is saved in a ParseInfo
object that is passed as parameter
to some of the custom callbacks. It contains details about the current argument sequence in the
parsing loop:
values
- The previously parsed values. It has an opaque type that should be cast toOptionValues<typeof your_options>
.index
- The index of the occurrence of the option name, or of the first option parameter. It will beNaN
if the sequence comes from an environment variable.name
- The option name as specified on the command-line, or the environment variable name. It will be the option's preferred name if the sequence comes from positional arguments.param
- The option parameter(s), or the parameters preceding the word being completed, if any.comp
- True if performing word completion, or the word being completed. Not available for the command callback.
Word completion
Quoting the Bash docs (opens in a new tab):
When word completion is attempted for an argument to a command for which a completion specification (a compspec) has been defined using the
complete
builtin (see Programmable Completion Builtins), the programmable completion facilities are invoked.
The parser does not use the completion facilities, because it relies on the raw command line and the cursor position into that line, which can be retrieved from the following environment variables:
COMP_LINE
orBUFFER
- the current command lineCOMP_POINT
orCURSOR
- the index of the cursor position relative to the beginning of the command line
When these variables are available, the parser enters into completion mode: it truncates the command line at the cursor position and performs completion of the last argument.
The result of completion is a list of words separated by line breaks, which should be printed on the terminal so that the completion builtins can perform the final completion step.
The parser ignores any errors thrown by callbacks during completion. If an error is thrown by a completion callback, the default completion message (an empty list) will be thrown instead.
Completion algorithm
The completion algorithm implemented by the library is explained below. In what follows, comp is the word being completed, i.e., the command-line argument ending at the completion index (it may be an empty string):
- If comp is expected to be an option name:
- If comp is a known option name, that name is returned; else
- If there is no positional option, the available option names are filtered by the prefix comp and returned; else
- comp is treated as a parameter of the positional option; go to step 2
- comp is the parameter of a non-niladic option, then either of the following is executed and its
result is combined with that of step 3:
- If the option has a complete callback, it is called with comp; else
- If it is a boolean option, the option's truth and falsity names (if any) are filtered by the prefix comp; else
- If the option has enumerated values, those values are converted to string and filtered by the prefix comp; else
- An empty list is returned
- If the option was not specified with a positional marker, then, if either of the following
holds true, the available option names are filtered by the prefix comp and returned:
- It is the first argument in a sequence of positional arguments; or
- It is not the first argument in a sequence, and is not short of the minimum expected parameter count of an option
If all of the above results in an empty list, the default completion (usually file completion, if enabled in the builtin's configuration) will be attempted. Note that the conditions of the third step exclude the possibility of it being an inline parameter, when considering option names.
Altered parsing behavior
Since the completion builtins always expect a list of words as a result, the library must suppress all errors that occur during parsing, and it does not make sense to perform some operations that might throw. Therefore, when word completion is in effect, parsing behavior will change in the following important ways:
- parsing errors, such as unknown option names, are suppressed
- unknown-valued options (help and version) are skipped
- the command callback does not get executed
- option requirements are not verified
- cluster arguments are completed with the default completion (an empty list)
Other arguments are processed as usual, which means that a completion callback can inspect the values parsed before it, in order to make better suggestions based on those values.
Requirements checking
When the parser evaluates option requirements, it compares an option's value against any value required by other options. Generally, this analysis is performed after all arguments have been parsed, but it may also be performed in two more situations:
- before calling a function callback, if the function option's
break
attribute was set - before calling a command callback
As has been mentioned, requirements are not checked when word completion is in effect.
Name suggestions
When the parser expects an option name and the current argument is not a valid one (and there is no positional option), it may select option names that are similar to the current argument using the Gestalt algorithm (opens in a new tab), and include them in the error message.
Error messages
Error messages generated by the parser can be customized via the validator configuration, which can be provided as an additional parameter to the parser constructor.
Inline parameters
Option parameters may be passed on the command-line using the syntax <name>=<param>
. This syntax
is only valid for monadic or delimited options and cannot be used with the positional marker.
As is the case with standalone parameters, inline parameters may contain any number of equal
signs, and even start with one (e.g., -opt==val
would be parsed as '=val'
).
Cluster arguments may also have inline parameters, but their syntax is different. A cluster argument is considered to have an inline parameter if it contains an unknown letter that is not the first one in the cluster. In this case, the first letter must be an option and the rest is its parameter. See cluster inline parameters for details about this syntax.
Process title
The process title will be updated to reflect the current command being executed. This includes the main script and any nested commands. For example, if the command-line is:
path/to/main/script.js -flag0 cmd1 -flag1 cmd2 args...
The process title would be updated in the following way:
node
- this the starting title, before any updatesnode script.js
- the title at the beginning of the parsing loopnode script.js cmd1
- the title at the start of thecmd1
commandnode script.js cmd1 cmd2
- the title at the start of thecmd2
command
The title will remain unchanged after the parser returns. Other option names are not used, because
they would pollute the output of process management utilities such as ps
.