Security News
tea.xyz Spam Plagues npm and RubyGems Package Registries
Tea.xyz, a crypto project aimed at rewarding open source contributions, is once again facing backlash due to an influx of spam packages flooding public package registries.
reargs
Advanced tools
Changelog
v1.2.0
Readme
RegExp based command line arguments parser.
None of the ones I found leverage the power of RegExp ! :wink:
With great power comes great responsability !
$ prog -m swagger dump api from https://api.endpoint.io -- do not parse
Or having multiple ways of asking for the same action:
$ prog -s
$ prog sh
$ prog shell
Last but not least, I wanted to have a descriptive help text out of the box. Thanks to nunjucks you just have to call generateHelp to have the magic :rainbow: !
Behind the scenes are Regular Expressions - hence the name - and the fact that they are both very flexible and more than enough for what we want.
// CommonJS
const Reargs = require('reargs')
const myArgs = new Reargs(params = {}, opts = null, debug = false)
// ES2016
import Reargs from 'reargs'
const myArgs = new Reargs(params = {}, opts = null, debug = false)
This object contains all the parameters, which have the following properties:
attribute | optional ? | accepted types | default value | meaning | example |
---|---|---|---|---|---|
group | yes | string | _ | groups commands, see note below | 'command' |
short | yes if long option is set | string or RegExp | '' | short version of parameter | '-a' |
long | yes if short option is set | string or RegExp | '' | longer version of parameter | '--append' |
humanReadable | yes | string | autogenerated from short and long properties | human readable version of the parameters for help | '-a, --append' |
help | yes | string | 'no help' | help text | 'append to file' |
stopParse | yes | boolean | false | stop parsing when found (see note below) | |
hidden | yes | boolean | false | do not show parameter if default help template (see note below) | |
multiple | yes | boolean | false | accept multiple occurrences of a parameter (see below) | |
captureMultiple | yes | string or RegExp | undefined | allow capture of multiple values inside a parameter (see below) | |
values | yes | any including Object | false | default value for the parameter | see notes below |
A typical example for help
would be:
const params = {
help: {
group: 'command',
short: '-h',
long: '--help',
humanReadable: '-h, --help',
help: 'this help',
stopParse: true,
hidden: false,
multiple: false,
// captureMultiple: '',
values: false
}
}
Indeed, no default help entry is provided. If you want to provide such functionality (which you might want), then you have to set an entry like the one above.
Notes
group
property. It can be useful to distinguish between command
s and option
s for example.short
and long
. Although they do not have to be shorter or longer one to the other, you can provide alternate versions if you need. At least one of them is required, an exception is thrown otherwise.string
or instances of RegExp
. If you prefer using strings, please make sure you use double backslashs to escape.
E.g. the following are equivalent:const exampleWithStrings = {
help: {
help: 'this help or additional help on a given topic',
short: '((?<topic>[\\w|\\/]+) )?-h',
long: 'help( (?<topic>[\\w|\\/]+))?',
}
}
const exampleWithRegExp = {
help: {
help: 'this help or additional help on a given topic',
short: /((?<topic>[\w|/]+) )?-h/,
long: /help( (?<topic>[\w|/]+))?/,
}
}
Notes
A humanReadable
text is computed from both short
and long
inputs (with the help of an option longShortDelimiter). If this does not suit the way you want it to appear when calling generateHelp, feel free to customize !
The stopParse
parameter implies that everything after this parameter is discarded from the input.
See it as the usual --
in your shell.
The hidden
flag will still parse the parameter but it will not be shown in the help generated with generateHelp. Of course you will have to implement the same logic if you want to customize your help output.
See Help templates below.
A few options are also available to adjust Reargs
behavior. Below are the default values:
const opts = {
longShortDelimiter: '\n',
paramDescriptionSpacer: '.',
prePaddingSpaces: 2,
alignLongIfNoShort: 4,
exitOnStop: false,
nunjucksAutoEscape: false,
}
longShortDelimiter
will be used to generate the humanReadable
documentation from join
ing both short
and long
parameters variations.
paramDescriptionSpacer
is also used when calling generateHelp, and changes the spacing character used between parameters and the related help.
prePaddingSpaces
adds that many spaces (
) at the beginning of each line describing a parameter.
alignLongIfNoShort
adds that many spaces before arguments that have no short
property, to have a cleaner alignment with arguments having both short
and long
properties set.
exitOnStop
if set, stops immediately parsing when a parameter with stopParse
property set to true is found, not evaluating anything else.
nunjucksAutoEscape
is set to false by default in Reargs
but by default set to true upstream. Since we are dealing with CLI, there should not be any mishaps by disabling safety guards.
Note Although discouraged,
paramDescriptionSpacer
andprePaddingSpaces
can be overriden ultimately in the template, because they are arguments of padEndJinja2
-like modifier.
To help with debugging your configuration, a debug mechanism has been set up.
At several places in the code, some debug messages will be generated. Depending on the value of debug
they will be outputted ... or not.
debug value | meaning |
---|---|
false (default) | no debug output at all |
true | debug will be output with console.log |
(...) => { do_whatever_you_want() } | a custom function taking as many arguments like console.log to suit your needs. |
Parsing is done as simply calling the following code :
const unparsable = myArgs.parse(process.argv.slice(2))
Yep that's it ! It takes an Array
as input argument, and returns the unparsed tokens.
Note The difference between
unparsed
andremain
lies in thatremain
has never been given a chance to be parsed. On the contrary, what is returned by theparse
function has been given a chance to be parsed, unsuccesfully. Bothremain
andunparsable
will be returned as string with\x00
special character. This is by design to allow arguments to have spaces in them. When you want to deal with positional arguments, you just have tosplit('\x00')
to get a properArray
for whatever your needs would be.
There are three different functions to retrieve the result after a call to parse.
Note By design, values are flattened to a simple Object. This means that if you happen to have multiple values having the same name, unexpected behavior can arise. This is done to avoid confusing and usually this should not happen (or must not happen!).
const value = myArgs.getValue(askforId, captureGroupName = null)
const groupValues = myArgs.getGroupValues(group)
const allValues = myArgs.getAllValues()
Let's go with an example. Consider this little snippet :
// Definition
const myArgs = new Reargs({
help: {
help: 'this help or additional help on a given topic',
short: '((?<topic>[\\w|\\/]+) )?-h',
long: 'help( (?<topic>[\\w|\\/]+))?',
humanReadable: 'help [topic], [topic] -h'
}
})
const unparsable = myArgs.parse(process.argv.slice(2))
const valueOfParam = myArgs.getValue('help', 'topic')
The capturing group topic
will store the value, if present.
If the program is called with only help
as argument, the following will be true:
const topic = myArgs.getValue('help', 'topic') // topic: undefined
const help = myArgs.getValue('help') // returns { topic: undefined }
const groupValues = myArgs.getGroupValues('_') // returns { topic: undefined }
const allValues = myArgs.getAllValues() // returns { topic: undefined }
Notes
Now let's consider the following example with default values :
// Definition
const myArgs = new Reargs({
help: {
help: 'this help or additional help on a given topic',
short: '((?<topic>[\\w|\\/]+) )?-h',
long: 'help( (?<topic>[\\w|\\/]+))?',
humanReadable: 'help [topic], [topic] -h',
values: {
topic: 'general'
}
}
})
const unparsable = myArgs.parse(process.argv.slice(2))
const valueOfParam = myArgs.getValue('help', 'topic')
If the program is called with only help
as argument, the following will be true:
const topic = myArgs.getValue('help', 'topic') // topic: 'general'
const help = myArgs.getValue('help') // returns { topic: 'general' }
const groupValues = myArgs.getGroupValues('_') // returns { topic: 'general' }
const allValues = myArgs.getAllValues() // returns { topic: 'general' }
Sometimes you want to capture multiple values, let's see two examples of parsing key:value
pairs.
Consider the following command line arguments :
$ prog -e key1:value1 -e key2:value2 -e key3: -e :value4
The definition would be :
// Definition
const oneKvPerParameter = {
kv1: {
help: 'set key value pairs one at a time',
short: '-e (?<key>[\\w]+)?:(?<value>[\\w]+)?',
humanReadable: '-e key:value',
multiple: true,
values: {
key: 'defaultKey',
value: 'defaultValue'
}
}
})
When multiple
property is set to false
(default), only the latest occurrence of that parameter is taken into account, previous occurrences are simply discarded.
const kv1keys = myArgs.getValue('kv1', 'key')
// ['defaultKey']
const kv1 = myArgs.getValue('kv1')
// {
// key: ['defaultKey'],
// value: ['value4']
// }
If you set multiple: true
then the following will happen :
const kv1keys = myArgs.getValue('kv1', 'key')
// ['key1', 'key2', 'key3', 'defaultKey']
const kv1 = myArgs.getValue('kv1')
// {
// key: ['key1', 'key2', 'key3', 'defaultKey'],
// value: ['value1', 'value2', 'defaultValue', 'value4']
// }
Note If you need mapping between keys and values, this work must be done in your program.
The situation is a bit different :
$ prog -a key1:value1,key2:value2,key3:,:value4
The definition would be :
// Definition
const manyKvPerParameter = {
kv2: {
help: 'set key value pairs all together',
short: '-a ',
humanReadable: '-a key:value,key:value,...',
captureMultiple: '(?<key2>[\\w]+)?:(?<value2>[\\w]+)?,?',
values: {
key2: 'defaultKey2',
value2: 'defaultValue2'
}
}
}
Note As you can see the
short
property has aspace at the end. This is to force a space between
-a
and the rest of parameter attributes.
And therefore:
const kv2keys = myArgs.getValue('kv2', 'key')
// ['key1', 'key2', 'key3', 'defaultKey']
const kv2 = myArgs.getValue('kv2')
// {
// key: ['key1', 'key2', 'key3', 'defaultKey'],
// value: ['value1', 'value2', 'defaultValue', 'value4']
// }
And yes the previous situations, can be generalized !
$ prog -b key0:value0,key1:value2 -b key1:value1,key2:value2,key3:,:value4
The definition would be :
// Definition
kv3: {
help: 'set key value pairs all together ... multiple times !',
short: '-b ',
humanReadable: '-b key:value,key:value,...',
captureMultiple: '(?<key3>[\\w]+)?:(?<value3>[\\w]+)?,?',
multiple: true,
values: {
key3: 'defaultKey3',
value3: 'defaultValue3'
}
}
And therefore:
const kv3keys = myArgs.getValue('kv3', 'key')
// ['key0', 'key1', 'key2', 'key3', 'defaultKey']
const kv3 = myArgs.getValue('kv3')
// {
// key: ['key0', 'key1', 'key2', 'key3', 'defaultKey'],
// value: ['value0', 'value2', 'value1', 'value2', 'defaultValue', 'value4']
// }
Note Here again,
Reargs
do not make any assumption on whether keys or values should be merged and how (e.g double definition of the keykey1
). It is up to the responsability of the caller to manage that.
As you have seen, many options and properties are meant to ease the generation of help message.
Everything is handled by the generateHelp with the following parameters:
parameter name | default value | description | example |
---|---|---|---|
contextHelp | {} | some properties can be overriden by default and you can add your own custom variables as well but you will have to use your own template | { name: "foobar", version: "1.0rc1" } |
templateSource | defaultHelpTemplate | a nunjucks template | you can view the default below |
// This will generate a help message with all parameters but default context values using the default template.
const helpMessage = myArgs.generateHelp()
// You can use your package.json to grab some useful values
const context = JSON.parse('package.json')
const customContext = myArgs.generateHelp(context)
// Or you can go further by providing your own template
const helpTemplate = `...`
const customHelp = myArgs.generateHelp(context, helpTemplate)
This is the default template :
const defaultHelpTemplate = `
{{name}} v{{version}} - {{description}} - by {{author|safe}}
Usage:
{{name}}{% for group,params in groups %} [{{ group }}]{% endfor %}
{% for group,params in groups %}
{{group|title}}s:
{% for param in params %}
{% if param.hidden != true -%}
{{ param.humanReadable|padEnd(params.padding, params.prepadding, opts.paramDescriptionSpacer)|safe }} {{param.help}}
{%- endif %}
{%- endfor %}
{% endfor -%}
`
It is pretty much self explanatory, please have a look a nunjucks for details.
Licence is MIT, feel free to raise issues, provide PR or just drop a word about how you use it, what would be the next features you would like this piece of software to have.
FAQs
RegExp based command line argument parser
The npm package reargs receives a total of 22 weekly downloads. As such, reargs popularity was classified as not popular.
We found that reargs demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Tea.xyz, a crypto project aimed at rewarding open source contributions, is once again facing backlash due to an influx of spam packages flooding public package registries.
Security News
As cyber threats become more autonomous, AI-powered defenses are crucial for businesses to stay ahead of attackers who can exploit software vulnerabilities at scale.
Security News
UnitedHealth Group disclosed that the ransomware attack on Change Healthcare compromised protected health information for millions in the U.S., with estimated costs to the company expected to reach $1 billion.