Parse argv as a json object.
Rather opinionated solution for when you need a CLI wrapper for a library package that accepts complex options object and you don't want to manually define and maintain CLI arguments mapping for all of that.
-
construct JSON of any complexity from CLI args
-
unambiguous (don't accept some ways to write args that may have different interpretations)
-
compact (provide ways to reduce redundancy for deeply nested structures)
-
performance (it's CLI - it doesn't meant to be in the hot code path)
-
full JSON in arg values (what's the point? go full json then. putting keys inside values doesn't seem idiomatic for console args and it can be error-prone - would require too much attention to proper escaping - can't just copy and paste json from elsewhere)
-
explicit defaults (This intended to be a thin wrapper around a package with it's own defaults. Extra layer of defaults might be confusing. If you absolutely need it - you can merge the produced json with your default object in client code. There are also preset and file reading features for cases when multiple meaningful behaviors can be provided.)
Available here: CHANGELOG.md
> npm i aspargvs
import { handleArgv } from 'aspargvs';
handleArgv({
handlers: {
json: businessLogic
}
});
function businessLogic (optionsObject) {
console.log('Business logic start.');
console.log(JSON.stringify(optionsObject));
console.log('Business logic end.');
}
Usage:
> node example.js --foo --bar.baz[] 3 4 5
Business logic start.
{"foo":true,"bar":{"baz":[3,4,5]}}
Business logic end.
Another example: example/example.mjs
> example [commands...] [keys and values...]
Commands are only available when required conditions are met (this means specific handler is provided in most cases).
Command | Alias | Argument | Description |
---|---|---|---|
json |
-j |
<file_name> | Merge given file contents with the parsed args object. |
preset |
-p |
<preset_name> | Merge given preset into the parsed args object. |
inspect |
-i |
Pretty print the parsed args object. Useful as a dry run to check how arguments are parsed. | |
unparse |
-u |
Print the parsed args object back as args string. Can be used to check what arguments produce the result equivalent to a given json file. Albeit it's pretty dumb at this point and can't use subkeys for example. | |
help |
-h |
Print help message. | |
version |
-v |
Print version number. |
Note: short aliases cannot be merged.
Syntax | Interpretation |
---|---|
foo |
An object property key |
foo.bar |
Nested object property keys |
[0] |
Array index (Note: the root object is not an array, only subkeys can start from an index) |
foo.[0] |
0-th item inside foo array |
foo.[0].bar |
bar property of an object that is a 0-th item inside foo array |
foo[0] |
The same as foo.[0] |
foo.[_] |
Next item in array, automatic index (value will be assigned to foo[foo.length] ) |
Syntax | Interpretation |
---|---|
--<path> |
Bare key, assigned a true value |
--!<path> |
Negation, assigned a false value |
--<path>=<value> |
Key with a value (see next table) |
--<path>[] <value> <value> |
Array key followed by bare values (see next table) |
--<path>{} |
Empty object (use subkeys or full paths to fill a non-empty object) |
:<path> |
Subkey (all above is the same except : prefix, but the path is nested in the last defined object or array that is not in a subkey. Subkeys recursion is not allowed.) |
Syntax | Interpretation | Notes |
---|---|---|
null |
null |
|
true |
true |
|
false |
false |
|
1.23e+4 |
Number | |
[<value>,...] |
Array | Spaces are not allowed. If you quote the whole arg then any inner spaces will become part of values |
{} |
Empty object | Use separate args - full keys or subkeys to build a complex object |
"null" |
String | Beware - Node.js strips unescaped double quotes |
'true' |
String | Beware - some shells may strip unescaped quotes |
`false` |
String | Beware - some shells may strip unescaped quotes |
anything_else |
String | Don't need quotes unless it is ambiguous |
Syntax | Interpretation |
---|---|
--foo.bar=1 --foo.baz=2 --foo.qux |
{ foo: { bar: 1, baz: 2, qux: true } } |
--foo{} :bar=1 :baz=2 :qux |
{ foo: { bar: 1, baz: 2, qux: true } } |
--foo.bar{} :baz.qux[_]=null |
{ foo: { bar: { baz: { qux: [null] } } } } |
--foo[0]=1 --foo[1]=2 --!foo[2] |
{ foo: [ 1, 2, false ] } |
--foo[] 1 2 false |
{ foo: [ 1, 2, false ] } |
--foo[] :[_]=1 :[_]=2 :![_] |
{ foo: [ 1, 2, false ] } |
--foo=[true] :![1] :[_][] :[_]{} |
{ foo: [ true, false, [], {} ] } |
--foo={} :bar{} :!baz |
{ foo: { bar: {}, baz: false } } |
--foo[] {} :bar {} :!baz |
{ foo: [ { bar: true }, { baz: false } ] } |
--foo[] [0] :[_]=1 [] :[_]=[2,3] |
{ foo: [ [0, 1], [[2, 3]] ] } |
- Escape syntax characters inside keys and inside arrays with "
\
"; - Use quoted strings to escape syntax inside values;
- Escape quote symbol inside quoted string with "
\
".
Syntax | Interpretation |
---|---|
--foo\[0]=1 |
{ 'foo[0]': 1 } |
--foo\.bar=1 |
{ 'foo.bar': 1 } |
--foo\=1 |
{ 'foo=1': true } |
--\!foo |
{ '!foo': true } |
--foo=[a\]] |
{ foo: [ 'a]' ] } |
--foo=[a\,b] |
{ foo: [ 'a,b' ] } |
--foo=['a,b'] |
{ foo: [ 'a,b' ] } |
--foo='[a,b]' |
{ foo: '[a,b]' } |
--foo='true' |
{ foo: 'true' } |
Double quote ("
) characters are used to delimit arguments but removed before arguments get to the application. Therefore they can be placed anywhere to pass string values containing spaces.
But such bare double quotes can't help to escape something that can be taken as a grammar of this arg parser (such as "true" as a string). For that, string value has to be wrapped into escaped double quotes or different quotes.
PowerShell has special meaning for too many characters and also nontrivial escape rules.
-
All possible quotes are intercepted by PowerShell.
I find the simplest way is to use a pair of backtick characters (
``
) to pass a single backtick.Escaped string would look like this:
``true``
Backtick + single quote (
`'
) would also work to pass a single quote, but it is just not as easy to type. -
PowerShell has a special meaning for curly braces.
Escape with backtick when defining an empty object:
`{`}
I'm not using anything else daily and can't provide any specific advice. Feel free to share your experience and findings by opening an issue.
Some things left for future updates:
- Explore the possibility to support for output object type/schema - to return object of that type and generate detailed help;
unparse
command - provide escaped strings ready for use in different shells;unparse
command - avoid string quotes when not needed;- Recursive subkeys with
::
,:::
, etc prefixes; - Explore the possibility to escape syntax inside values with
\
to reduce the need for quoted strings; - ...