π¦ shargs (shell args) is a library for building command-line argument parsers.
- Compose functions to build highly customizable command-line argument parsers.
- 35+ opt-in features, e.g. (multiple) subcommands, spelling mistake detection, default values, and (best guess) casting.
- Synchronous and Promise-based asynchronous modes with async/await support.
- Automatic usage documentation generation with fine-grained control over layouts and styles.
- Easily extensible with your own custom parser stages and custom usage layouts.
- Extensively documented and very well tested (800+ unit and integration tests).
- Modular library layout with zero runtime dependencies.
Install as a bundle (recommended):
npm install --save shargs
Install as modules:
npm install --save shargs-core # core functions like parserSync (in bundle: shargs/core or shargs) npm install --save shargs-opts # a DSL for command-line options (in bundle: shargs/opts) npm install --save shargs-parser # collection of parser functions (in bundle: shargs/parser) npm install --save shargs-usage # collection of usage functions (in bundle: shargs/usage) npm install --save shargs-repl # build REPLs powered by shargs (not in bundle)
The documentation assumes the bundle is installed,
but the only difference between the bundle and modules installation is how functions are imported:
The bundle uses require('shargs/opts'), while require('shargs-opts') is used by modules
(note the use of / vs. -).
Read installing as bundle or modules for more details.
Describe your command and its options:
const opts = [
stringPos('question', {desc: 'Ask a question.', required: true}),
number('answer', ['-a', '--answer'], {desc: 'The answer.', defaultValues: [42]}),
flag('help', ['-h', '--help'], {desc: 'Print this help message and exit.'})
]
const deepThought = command('deepThought', opts, {desc: 'Ask the Ultimate Question.'})
const opts = [
stringPos('question', {desc: 'Ask a question.', required: true}),
number('answer', ['-a', '--answer'], {desc: 'The answer.', defaultValues: [42]}),
flag('help', ['-h', '--help'], {desc: 'Print this help message and exit.'})
]
const deepThought = command('deepThought', opts, {desc: 'Ask the Ultimate Question.'})The deepThought command has three command-line options:
- A
requiredstring positional argument namedquestion. - An
answernumber option specified with-aor--answerthat should default to42if not given. - A
helpcommand-line flag given by-hor--help.
You may use the shargs-opts module to get a nice DSL for describing our options.
However, you could have also written them out as objects yourself or could have used a different DSL.
Read up on the details in the command-line options section.
Declare your own command-line parser:
const parser = parserSync({
argv: [splitShortOpts],
opts: [setDefaultValues, requireOpts, cast],
args: [flagsAsBools]
})
const parser = parserSync({
argv: [splitShortOpts],
opts: [setDefaultValues, requireOpts, cast],
args: [flagsAsBools]
})Shargs gives you fine-grained control over how the options are parsed.
By using the shargs-core and shargs-parser modules, we have build the following parser:
splitShortOpts: Short option groups like-cvzfare transformed to-c -v -z -f.setDefaultValues: Options with default values that were not provided are set.requireOpts: It is verified that all required options have been given.cast: Strings are cast to other types, like numbers or booleans.flagsAsBools: Command-line flags are transformed to booleans.
Note that you did not tell parser how exactly to do those things.
Everything is nice and declarative, and the details are hidden away in the parser stages.
The parserSync function and command-line parsers sections have all the details.
Layout a usage documentation:
const docs = usage([synopsis, space, optsList, space, desc])
const style = {
line: [{width: 80}],
cols: [{width: 25}, {width: 55}]
}
const docs = usage([synopsis, space, optsList, space, desc])
const style = {
line: [{width: 80}],
cols: [{width: 25}, {width: 55}]
}You may use shargs-usage to automatically generate a usage documentation based on a command definition
(e.g. deepThought from before).
The module provides all components generally found in usage documentations, like:
- A
synopsis, summarizing available options: e.g.deepThought (<question>) [-a|--answer] [-h|--help]. - An options list (
optsList), describing option details in a tabular format.
Note that shargs-usage is declarative:
You only specify what components our usage documentation should have.
The details on how exactly those components transform command-line options into text is hidden away.
See the automatic usage documentation generation and style sections.
Use the parser and the usage documentation in your program:
const argv = process.argv.slice(2)
const {errs, args} = parser(deepThought)(argv)
errs.forEach(err => console.log(err.msg))
const help = docs(deepThought)(style)
if (args.help) console.log(help)
const argv = process.argv.slice(2)
const {errs, args} = parser(deepThought)(argv)
errs.forEach(err => console.log(err.msg))
const help = docs(deepThought)(style)
if (args.help) console.log(help)The command-line option DSL, the parser DSL, and the usage documentation DSL combined give you a very flexible way to write command-line programs.
Find out more in the building command-line parsers with shargs section.
Run your command with node ./deepThought --help:
deepThought (<question>) [-a|--answer] [-h|--help]
<question> Ask a question. [required]
-a, --answer=<number> The answer. [default: 42]
-h, --help Print this help message and exit.
Ask the Ultimate Question.
deepThought (<question>) [-a|--answer] [-h|--help]
<question> Ask a question. [required]
-a, --answer=<number> The answer. [default: 42]
-h, --help Print this help message and exit.
Ask the Ultimate Question. The automatic usage documentation generation and building command-line parsers with shargs sections have more.
- An asynchronous version of deepThought.
- A synchronous version of deepThought.
- deepThought with three layers of configuration: A config file, environment variables, and command-line arguments.
- A command-line arguments SQL parser.
- A REPL (Real Eval Print Loop) build with
shargs-repl.
This documentation encompasses the following shargs modules:
shargs-optsis documented in command-line options.shargs-coreis documented in theparserSyncfunction.shargs-parseris documented in command-line parsers.shargs-usageis documented in automatic usage documentation generation.shargs-replis documented in building REPLs with shargs.
Command-line options are the most important concept in shargs. They are the basis for its two main features: Command-line parsers and automatic usage documentation generation.
Shargs defines many different types of command-line options represented by objects with the following interfaces:
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β TypeΒ Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β | Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β InterfaceΒ Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β | Description |
|---|---|---|
| Flag Option | {key, args, types: []} |
A present or absent value. |
| Primitive Option | {key, args, types: [_]} |
A unary value given by argument. |
| Array Option | {key, args, types: [_, _, ...]} |
An array of length n given by argument. |
| Variadic Option | {key, args} |
A variable length array given by argument. |
| Subcommand Option | {key, args, opts} |
An option group given by argument. |
| Primitive Positional Argument | {key, types: [_]} |
A unary value given by position. |
| Array Positional Argument | {key, types: [_, _, ...]} |
An array of length n given by position. |
| Variadic Positional Argument | {key} |
A variable length array given by position. |
| Command Positional Argument | {key, opts} |
An option group given by position. |
| Rest | {values} |
An argument value of unknown type. |
Since writing objects following these interfaces by hand can be tedious,
shargs-opts gives you a simple type-based DSL for defining valid command-line options:
const {command, flag, number, subcommand} = require('shargs/opts')
const opts = [
subcommand(askOpts)('ask', ['ask'], {required: true, desc: 'Ask a question.'}),
number('answer', ['-a', '--answer'], {defaultValues: [42], desc: 'The answer.'}),
flag('help', ['-h', '--help'], {desc: 'Print this help message and exit.'})
]
const deepThought = command('deepThought', opts, {
desc: 'Deep Thought was created to come up with the Answer to ' +
'The Ultimate Question of Life, the Universe, and Everything.'
})In the example, using the type functions subcommand, number, flag,
and command guarantees the generation of valid objects.
The following type functions are available:
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β TypeΒ FunctionΒ Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β | Description |
|---|---|
array(types)(key, args, fields)arrayPos(types)(key, fields)
|
|
bool(key, args, fields)boolPos(key, fields)
|
|
subcommand(opts)(key, args, fields)command(key, opts, fields)
|
|
flag(key, args, fields) |
|
number(key, args, fields)numberPos(key, fields)
|
|
string(key, args, fields)stringPos(key, fields)
|
|
variadic(key, args, fields)variadicPos(key, fields)
|
|
You may write out command-line options by hand, or write your own DSLs for creating them, they are just JavaScript objects:
const askOpts = [
{key: 'format', args: ['--format'], types: ['string'], only: ['json', 'xml'], defaultValues: ['json'],
desc: 'Respond either with json or xml.'},
{key: 'html', args: ['--no-html'], types: [], reverse: true, desc: 'Remove HTML tags.'},
{key: 'help', args: ['-h', '--help'], types: [], desc: 'Print this help message and exit.'},
{key: 'question', types: ['string'], required: true, desc: 'State your question.'}
]Apart from key, args, types, opts, and values
that we have already seen and that determine an option's type,
command-line option objects may be given any other fields,
that may be used to provide information to parser stages
(e.g. defaultValues, only),
or to provide descriptions for usage documentation generation
(e.g. desc, descArg).
If you write your own parser stages, you may also define your own fields.
The following fields are used by shargs-core, shargs-parser stages
or shargs-usage functions:
| Field | Β Β Β Β Β TypeΒ Β Β Β Β | Description |
|---|---|---|
args |
string[] |
|
contradicts |
key[] |
|
defaultValues |
* |
|
desc |
string |
|
descArg |
string |
|
descDefault |
string |
|
implies |
key[] |
|
key |
string |
|
only |
any[] |
|
opts |
option[] |
opts defines the command-line options of commands
and subcommands.
|
required |
boolean |
|
reverse |
boolean |
reverse defines whether the values of a given option should be reversed by
the reverseBools or reverseFlags
parser stages.
|
types |
string[] |
|
values |
any[] |
This field should only be set by parser stages and never manually.
|
Certain changes to options are so frequent, that shargs-opts provides decorators for them.
You may think of decorators as recurring patterns that are provided as functions.
shargs-opts provides the following decorators:
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β DecoratorΒ Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β | Description |
|---|---|
complement(prefix)(opt) |
|
posArgToOpt(args)(opt) |
|
Shargs provides a function for verifying that commands have the correct structure:
const {verifyCommand} = require('shargs')
const {errs, opt} = verifyCommand(deepThought)In the example, it would return a list of errs if deepThought was invalid.
If the command is valid, the errs list is empty.
verifyCommand is used internally by parserSync and parser, but may be used independently.
The parserSync function is shargs' core abstraction.
It generates a command-line parser from a collection of parser stages
and is usually used alongside shargs-parser:
const {parserSync} = require('shargs')
const {cast, flagsAsBools, requireOpts, restrictToOnly} = require('shargs/parser')
const {reverseFlags, setDefaultValues, splitShortOpts} = require('shargs/parser')
const stages = {
argv: [splitShortOpts],
opts: [setDefaultValues, requireOpts, reverseFlags, cast],
args: [flagsAsBools]
}
const substages = {
ask: [...stages.opts, restrictToOnly]
}
const parser = parserSync(stages, substages)parserSync takes two parameters:
- A
stagesobject that takes parser stages and defines what transformations should be applied in which order. - An optional
substagesobject that defines subcommand-specificoptsparser stages.
parserSync has a twin function called parser that does the same, but works asynchronously.
Shargs has seven different processing steps called stages that are applied in a predefined order
and transform argument values (process.argv) via command-line options (opts) to arguments (args):
| Stage | Field | Type |
|---|---|---|
| 1 | toArgv |
|
| 2 | argv |
|
| 3 | toOpts |
|
| 4 | opts |
|
| 5 | toArgs |
|
| 6 | args |
|
| 7 | fromArgs |
|
The argv, opts, and args stages
are the user-facing API to declare a parser's behavior.
The toOps and toArgs stages
define the core behavior of parserSync (and parser)
and shargs defines sensible defaults that should not have to be changed in most use cases.
However, if you do have a use case that needs adjustments to those stages, you may carefully swap them out.
If you read the types from top to bottom, you get a good impression of how parserSync works.
substages define custom opts stages for subcommands.
That means, while most subcommands are parsed using the opts defined in stages,
those whose key matches a key in the substages object are parsed using the opts defined under that key.
Keys may be deeply nested to account for subcommands of subcommands:
E.g. if ask had a subcommand with the question key, {ask: {question: [...stages.opts, restrictToOnly]}} would assign custom opts to question.
The _ key is special in substages:
It is a wildcard that is used by any subcommand that is not given explicitly by key.
E.g. {ask: {_: [...stages.opts, restrictToOnly]}} and {_: {_: [...stages.opts, restrictToOnly]}} both work for question.
The parserSync function has an asynchronous alternative called parser.
It is used exactly like parserSync, but also works with stages returning
JavaScript Promises
and returns a Promise itself:
// stages, substages, deepThought, argv are taken from the Getting Started section
const {parser} = require('shargs')
const asyncParser = parser(stages, substages)
const parse = asyncParser(deepThought)
const {errs, args} = await parse(argv)In addition to parserSync's parameters,
parser's stages and substages parameters also take parser stages that return Promises:
| Stage | Field | Type |
|---|---|---|
| 1 | toArgv |
|
| 2 | argv |
|
| 3 | toOpts |
|
| 4 | opts |
|
| 5 | toArgs |
|
| 6 | args |
|
| 7 | fromArgs |
|
If you read the stages' field types from top to bottom, you get a good impression of what an asynchronous parser does. Internally, an asynchronous shargs parser really differs only in one major way from a synchronous parser: Instead of using function composition, it uses Promise.prototype.then to chain parser stages.
You do not have to write all parser stages yourself.
The shargs-parser library offers a large collection of common parser stages, you can use.
The parser stages presented here are split into checks and stages.
While checks only report errors, stages also transform their argv, opts, or args.
Usually, it makes sense to declare checks before stages.
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β | Description |
|---|---|
verifyArgv(rules) |
Reports a
|
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β | Description |
|---|---|
equalsSignAsSpace |
Allows arguments of the form
|
shortOptsNoSpace |
Allows to omit whitespaces between short arguments and their values.
Passing
|
splitShortOpts |
Allows using short option groups.
The group
|
traverseArgv(p)(f) |
Transforms arguments by applying a function
|
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β | Description |
|---|---|
contradictOpts |
Reports a
|
demandASubcommand |
Reports a
|
implyOpts |
Reports an
|
requireOpts |
Reports a
|
suggestOpts |
Corrects spelling mistakes by suggesting existing command-line arguments for all unknown provided arguments.
E.g. if
|
validatePosArgs |
Reports an
|
verifyOpts(rules) |
Reports a
|
verifyValuesArity |
Reports an error if an option's
|
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β | Description |
|---|---|
arrayOnRepeat |
Collects all repeated
|
bestGuessOpts |
Takes a best guess approach to transform rest values that did not match a command-line option
into new command-line options.
E.g.
|
broadenBools(alt) |
Transforms
|
cast |
Casts string
|
restrictToOnly |
Validates all command-line options with both,
|
reverseBools |
Transforms
|
reverseFlags |
Transforms
|
setDefaultValues |
Transforms all options that have no
|
traverseOpts(p)(f) |
Transforms
|
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β | Description |
|---|---|
failRest |
Reports an
|
verifyArgs(rules) |
Reports a
|
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β | Description |
|---|---|
bestGuessArgs |
Introduces new arguments by best guess based on rest field values
(e.g.
|
bestGuessCast |
Casts string
|
boolAsFlag(key) |
Transforms bool arguments with
|
boolsAsFlags |
Transforms all bool arguments in
|
clearRest |
Sets rest values to an empty array (i.e.
|
flagAsBool(key) |
Transforms flags with
|
flagAsNumber(key) |
Transforms flags with
|
flagsAsBools |
Transforms all flag arguments in
|
flagsAsNumbers |
Transforms all flag arguments in
|
mergeArgs(merge) |
Transforms
|
numberAsFlag(key) |
Transforms numbers with
|
numbersAsFlags |
Transforms all numbers in
|
traverseArgs(fs) |
Transforms
|
Shargs strictly separates the concerns of parsing command-line arguments and generating usage documentation.
The shargs-usage module specializes on
generating terminal-based usage documentation for --help flags
from command-line options:
const {desc, optsLists, space, synopses, usage} = require('shargs/usage')
const docs = usage([
synopses,
space,
optsLists,
space,
desc
])shargs-usage lets you define how your usage documentation should look like in a declarative way.
In the example, we tell our docs to start with synopses, have optsLists in the body,
and close with a description.
We separate these three parts with spaces and enclose everything in a usage function.
Note that we did not mention any command-line options, yet:
const {command, flag, number, stringPos} = require('shargs/opts')
const opts = [
stringPos('question', {desc: 'Ask a question.', required: true}),
number('answer', ['-a', '--answer'], {desc: 'The answer.', defaultValues: [42]}),
flag('help', ['-h', '--help'], {desc: 'Print this help message and exit.'})
]
const deepThought = command('deepThought', opts, {
desc: 'Deep Thought was created to come up with the Answer to ' +
'The Ultimate Question of Life, the Universe, and Everything.'
})
const optsDocs = docs(deepThought)optsDocs now knows what to layout (deepThought), and how to layout it (docs).
Finally, we style the different parts (lines and columns) of the documentation:
const style = {
line: [{width: 60}],
cols: [{width: 25}, {width: 35}]
}
const text = optsDocs(style)Now, if we console.log(text), the following text is printed to the console:
deepThought (<question>) [-a|--answer] [-h|--help]
<question> Ask a question. [required]
-a, --answer=<number> The answer. [default: 42]
-h, --help Print this help message and exit.
Deep Thought was created to come up with the Answer to The
Ultimate Question of Life, the Universe, and Everything. deepThought, docs, and style
are the moving parts of automatic usage documentation generation
and are defined independently.
We have already talked about command-line options before
and will talk about style in an upcoming section.
shargs-usage provides the following usage functions to declare layouts:
| Β Β Β Β Β Β Β Β Β Β Β Β Β UsageΒ FunctionΒ Β Β Β Β Β Β Β Β Β Β Β Β Β | Description |
|---|---|
descdescWith({id})
|
|
note(string)noteWith({id})(string)
|
|
notes(strings)notesWith({id})(strings)
|
|
optsDefoptsDefWith({id, pad})
|
|
optsDefsoptsDefsWith({id, pad})
|
|
optsListoptsListWith({id})
|
|
optsListsoptsListsWith({id, pad})
|
|
spacespaceWith({id, lines})
|
|
synopsessynopsesWith({id})
|
|
synopsissynopsisWith({id})
|
|
While usage functions taken for themselves are useful, they really begin to shine if they are combined by usage combinators. Usage combinators are higher-order usage functions that take other usage functions as parameters, combine them in various ways, and return a new usage function.
Let's see how usage combinators may be used to implement synopses:
const {decorate, noSubcommands, onlySubcommands} = require('shargs/usage')
const {optsMap, synopsis, usage, usageMap} = require('shargs/usage')
const prefixKey = prefix => optsMap(opts => ({...opts, key: prefix + ' ' + opts.key}))
function synopses (opt) {
return usage([
noSubcommands(synopsis),
decorate(prefixKey(opt.key), onlySubcommands)(
usageMap(synopses)
)
])(opt)
}This example uses usage decorators, that are only introduced in the next section.
The implementation of synopses uses two usage combinators:
usage and usageMap.
usage is used to combine two usage functions:
A synopsis of all opts, but subcommands, and the usage function returned by usageMap.
usageMap iterates over all subcommands
and recursively calls synopses on each subcommand's opts.
The recursion stops, if opt's opts has no more subcommands,
since usage functions with empty opts return an empty string.
Combinators are a powerful feature, as they let you build more complex things from smaller parts.
shargs-usage provides the following usage combinators:
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β UsageΒ CombinatorΒ Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β | Description |
|---|---|
usage(functions)(opt)(style) |
|
usageMap(f)({opts})(style) |
|
When defining layouts, we may want to feature some opts in one place,
and the remaining in a different place of our documentation.
Maybe the subcommands should be presented in a definition list,
while the other options are layed out as a table.
Usage decorators enable these use cases by modifying inputs of usage functions:
const {desc, optsDef, optsList, space, synopsis, usage} = require('shargs/usage')
const {decorate, noSubcommands, onlyFirstArg, onlySubcommands} = require('shargs/usage')
const decoratedDocs = usage([
noSubcommands(onlyFirstArg(synopsis)),
space,
onlySubcommands(optsDef),
space,
noSubcommands(optsList),
space,
desc
])The example uses three different decorators:
noSubcommands, onlySubcommands, and onlyFirstArg.
Each of these decorators modifies the opts array in some way,
before passing it on to their wrapped usage function.
The first two focus on filtering opts:
noSubcommands removes all subcommands,
while onlySubcommands keeps only subcommands.
onlyFirstArg goes one step further and modifies each option in opts,
removing all but the first argument in their args fields.
shargs-usage has the following usage decorators:
| Β Β Β Β Β UsageΒ DecoratorΒ Β Β Β Β | Description |
|---|---|
justArgs(args)(usageFunction) |
|
noSubcommands(usageFunction) |
|
onlySubcommands(usageFunction) |
|
onlyFirstArg(usageFunction) |
|
optsFilter(p)(usageFunction) |
|
optsMap(f)(usageFunction) |
|
If many usage decorators are applied to a usage function, things get unwieldy, fast:
const {justArgs, noSubcommands, onlyFirstArg, synopsis} = require('shargs/usage')
const briefSynopsis = noSubcommands(onlyFirstArg(justArgs(['--help'])(synopsis)))In the example, briefSynopsis is decorated three times and the code is not very readable.
Usage decorator combinators facilitate a cleaner code layout:
const {decorate, justArgs, noSubcommands, onlyFirstArg, synopsis} = require('shargs/usage')
const decorated = decorate(noSubcommands, onlyFirstArg, justArgs(['--help']))
const briefSynopsis = decorated(synopsis)This version of briefSynopsis is much more readable.
Note, that decorate applies its usage decorators from right to left.
As is apparent from the example, usage decorator combinators are usage decorators, themselves.
shargs-usage has the following usage decorator combinators:
| Β Β Β Β Β Β Β Β Β Β Β UsageΒ DecoratorΒ CombinatorΒ Β Β Β Β Β Β Β Β Β Β Β Β Β | Description |
|---|---|
decorate(decorators)(usageFunction) |
decorate takes many usage function decorators
and applies them to its usageFunction from right to left.
|
Layout functions are transformed to strings by applying styles:
const style = {
line: [{width: 80}],
cols: [{width: 25}, {width: 55}]
}In the example, style provides the details for how many columns a usage documentation text should be wide,
and whether it should have padding.
A style is an object whose values are arrays of style objects, that must have a width key,
and may have padEnd and padStart keys:
| Parameter | Type | Description |
|---|---|---|
padEnd |
number |
padEnd defines a padding to the right.
It is filled with spaces.
|
padStart |
number |
padStart defines a padding to the left.
It is filled with spaces.
|
width |
number |
width defines the length of text.
The full length of the string is the width
plus padEnd and padStart.
|
- Layout functions
- Layout combinators
- Layout decorators
- Layout decorator combinators
- Custom layout functions
- Custom usage functions
This section reuses code snippets from earlier sections:
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β SnippetsΒ Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β |
|---|
|
|
|
|
|
Imagine these snippets were located in their own modules and were imported earlier.
Then, a sample command-line program written with shargs could be:
const argv = process.argv.slice(2)
const {errs, args} = parser(deepThought)(argv)
if (args.help) {
const help = docs(deepThought)(style)
console.log(help)
process.exit(0)
}
if (errs.length > 0) {
errs.forEach(({code, msg}) => console.log(`${code}: ${msg}`))
process.exit(1)
}
console.log(`The answer is: ${args.answer}`)
process.exit(0)
const argv = process.argv.slice(2)
const {errs, args} = parser(deepThought)(argv)
if (args.help) {
const help = docs(deepThought)(style)
console.log(help)
process.exit(0)
}
if (errs.length > 0) {
errs.forEach(({code, msg}) => console.log(`${code}: ${msg}`))
process.exit(1)
}
console.log(`The answer is: ${args.answer}`)
process.exit(0)First, we skip the first two values of process.argv.
They are node and the file name and can be ignored.
We then parse the remaining argv with our deepThought parser and get two results:
A list of errs, and an args object with parsed argument values.
Based on those two results, we build our program.
If the args.help field is set, we print a help text generated from docs by applying deepThought and style.
Then, we exit with exit code 0.
If we run the program with node ./deepThought --help, the following text is printed:
deepThought [-a|--answer] [-h|--help]
deepThought (ask) [--format] [--no-html] [-h|--help] (<question>)
-a, --answer=<number> The answer. [default: 42]
-h, --help Print this help message and exit.
ask Ask a question. [required]
--format=<json|xml> Respond either with json or xml. [default: json]
--no-html Remove HTML tags.
-h, --help Print this help message and exit.
<question> State your question. [required]
Deep Thought was created to come up with the Answer to The Ultimate Question of
Life, the Universe, and Everything. If the errs array has errors, we print all errors and exit with exit code 1.
E.g. if we execute node ./deepThought --answer 5, without specifying the required ask subcommand,
the following text appears:
Required option is missing: An option that is marked as required has not been provided.Otherwise, we print the args.answer.
E.g. if we run it with node ./deepThought ask "What is the meaning of Life, the Universe, and Everything?",
it prints:
The answer is: 42Feel free to skip this section if you are new to Shargs. It introduces more advanced topics:
- Installing as Bundle or Modules
- Multiple Subcommands
- Custom Checks and Stages
- Layout Functions
- Layout Combinators
- Layout Decorators
- Layout Decorator Combinators
- Custom Layout Functions
- Custom Usage Functions
- Building REPLs with Shargs
- Error Codes
Since version 0.26.0, shargs may be installed in two different ways: Either as a bundle (recommended), or individually as modules.
npm install --save shargs # bundle installation (core, opts, parser, and usage) npm install --save shargs-core # module (in bundle: shargs/core or shargs) npm install --save shargs-opts # module (in bundle: shargs/opts) npm install --save shargs-parser # module (in bundle: shargs/parser) npm install --save shargs-usage # module (in bundle: shargs/usage) npm install --save shargs-repl # module (not in bundle)
The shargs bundle combines several modules in one distribution with its own version number.
The advantage for the user is that the module versions are guaranteed to be compatible and updates are simpler.
Installing individual modules is more flexible, e.g. if you want to use a specific set of module versions, if one module of the bundle is not needed or if one of the modules is replaced with a different implementation.
It is recommended to start with the bundle installation
and import modules like require('shargs/opts') or import 'shargs/core'.
If you want to switch to a module installation later, you may simply replace your imports with module imports:
E.g. require('shargs-opts') and import 'shargs-core'.
If you are using modules and need to know which versions are compatible,
you may refer to the module versions used by the shargs bundle.
Shargs supports specifying multiple subcommands.
E.g. you could use both, the ask and design subcommands in the same command
in the following version of deepThought:
const {command, flag, number, stringPos, subcommand} = require('shargs/opts')
const ask = subcommand([stringPos('question')])
const design = subcommand([stringPos('name')])
const opts = [
ask('ask', ['ask'], {desc: 'Ask a question.'}),
design('design', ['design'], {desc: 'Design a more powerful computer.'}),
flag('help', ['-h', '--help'], {desc: 'Print this help message and exit.'})
]
const deepThought = command('deepThought', opts, {desc: 'Ask the Ultimate Question.'})If you provide both subcommands in your argv, both are parsed:
const argv = ['design', 'Earth', 'ask', 'What is the answer to the Ultimate Question?']
const parse = parserSync()(deepThought)
const {argv, errs} = parse(argv)
console.log(argv)
/*
{
_: [],
ask: {
_: [],
question: 'What is the answer to the Ultimate Question?'
},
design: {
_: [],
name: 'Earth'
}
}
*/Note that the subcommand order is not preserved.
This is due to the default behavior of fromArgs,
that keeps only the first mention of a subcommand and merges all subcommands into an (unordered) object.
The input to fromArgs is still ordered and has duplicates,
so if your program needs the subcommand order or duplicates,
just write a custom fromArgs stage:
const merge = (obj1, obj2) => ({
...obj1,
subcommands: [
...(obj1.subcommands || []),
obj2
]
})
const fromArgs = ({errs, args}) => ({
errs,
args: args.slice(1).reduce(merge, args[0])
})This demonstration implementation of fromArgs is very simple
and lacks some features like e.g. subcommands of subcommands.
Please improve it before using it in your production programs.
π§ Work in progress: This feature is currently worked on and its API is not yet stable.
Shargs-repl lets you build REPLs with actions defined by shargs commands.
Shargs makes writing and using custom checks and stages very simple.
The only thing you have to do is to follow the correct function signatures for your check or stage,
as given in the stages and substages sections.
The following code snippets showcase very simple examples with the correct signatures.
Regardless of whether you implement a check or a stage, the most important thing to remember is: Always pass on errors!
Custom argv stage example:
function splitShortOpts ({errs = [], argv = []} = {}) {
const argv2 = argv.flatMap(arg =>
arg.length > 2 && arg[0] === '-' && arg[1] !== '-'
? arg.slice(1).split('').map(c => '-' + c)
: arg
)
return {errs, argv: argv2}
}If you write a custom argv stage, have a look at traverseArgv!
Custom opts stage example:
function demandACommand ({errs = [], opts = []} = {}) {
const errs2 = []
const aCommand = opts.some(
({key, args, types, opts}) => (
typeof key !== 'undefined' &&
typeof types === 'undefined' &&
Array.isArray(args) && Array.isArray(opts)
)
)
if (!aCommand) {
errs2.push({
code: 'CommandRequired',
msg: 'No command found. Please use at least one command!',
info: {options: opts}
})
}
return {errs: errs.concat(errs2), opts}
}If you write a custom opts stage, have a look at traverseOpts!
Custom args stage example:
const {traverseArgs} = require('shargs/parser')
function flagsAsBools ({errs = [], args = {}} = {}) {
const fs = {
flag: ({key, val, errs, args}) => ({
errs,
args: {...args, [key]: val.count > 0}
})
}
const {errs: errs2, args: args2} = traverseArgs(fs)({args})
return {errs: errs.concat(errs2), args: args2}
}If you write a custom args stage, have a look at traverseArgs!
Usage functions that are applied to an opt yield so called layout functions.
If we take a closer look at the signatures of usage and layout functions,
the connection between the two becomes apparent:
| Type | Function Signature | Description |
|---|---|---|
| Layout Function | style => string |
Layout functions take a style and return a string. |
| Usage Function | opt => style => string |
Usage functions take an opt and return a layout function. |
In shargs-usage, an opt's purpose is to provide the textual contents of layout functions
and the usage functions' only job is to specify how this textual content is extracted from the opt.
The layout functions do the actual work of formatting strings.
Let's have a look at an example:
const {br, defs, layout, table, text} = require('shargs/usage')
const askDocs = layout([
table([
['deepThought (ask)', '[--format] [--no-html] [-h|--help] (<question>)']
]),
br,
defs([
['--format=<json|xml> [default: json]', 'Respond either with json or xml.'],
['--no-html', 'Remove HTML tags.'],
['-h, --help', 'Print this help message and exit.'],
['<question> [required]', 'State your question.']
]),
br,
text(
'Deep Thought was created to come up with the Answer to ' +
'The Ultimate Question of Life, the Universe, and Everything.'
)
])In the example, askDocs is a layout that comprises four different layout functions:
table, br, defs, and text.
Depending on how we style the layout, we get different strings:
const style = {
line: [{width: 80}],
cols: [{width: 16}, {width: 64}]
}
const string = askDocs(style)If we console.log(string), the following text is printed to the console:
deepThought (ask) [--format] [--no-html] [-h|--help] (<question>)
--format=<json|xml> [default: json]
Respond either with json or xml.
--no-html
Remove HTML tags.
-h, --help
Print this help message and exit.
<question> [required]
State your question.
Deep Thought was created to come up with the Answer to The Ultimate Question of
Life, the Universe, and Everything. Experiment with style to get different layouts!
shargs-usage gives you the following layout functions:
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β LayoutΒ FunctionΒ Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β | Description |
|---|---|
brbrWith({id, lines})
|
|
cols(columns)colsWith({id})(columns)
|
|
defs(tuples)defsWith({id, pad})(tuples)
|
|
line(string)lineWith({id})(string)
|
|
lines(strings)linesWith({id})(strings)
|
|
table(rows)tableWith({id})(rows)
|
|
text(string)textWith({id})(string)
|
|
texts(strings)textsWith({id})(strings)
|
|
Layout combinators are functions that take layout functions as parameters and return new layout functions. They are the primary way of building more complex constructs from simpler components. The following examples demonstrate the use of layout combinators:
const {layout, layoutMap, textWith} = require('shargs/usage')
const defsWith = ({id}) => layoutMap(
([term, definition] = []) => layout([
textWith({id})(term),
textWith({id})(definition)
])
)defsWith is implemented in terms of layout, layoutMap,
and textWith.
It maps over a list of term and definition pairs and layouts them as texts.
shargs-usage has the following layout combinators:
| Β Β Β Β Β Β Β Β Β Β Β Β LayoutΒ CombinatorΒ Β Β Β Β Β Β Β Β Β Β Β | Description |
|---|---|
layout(functions)(style) |
|
layoutMap(f)(list)(style) |
|
When working with layout functions that take a style as input,
you sometimes want to modify this style just before it is passed to the function,
and only for this function call.
This is what layout decorators are for:
const {layout, layoutMap, pad, text} = require('shargs/usage')
const defs = layoutMap(
([term, definition] = []) => layout([
text(term),
pad(['line', 0], 4)(text(definition))
])
)The example shows a sample implementation of defs using the pad layout decorator.
Here, the term, as well as the definition have the same id, texts default id 'line'.
However, we want to add a padding of 4 spaces to the definition.
So we use pad to add 4 spaces to the id at the ['line', 0] path of style.
shargs-usage ships with the following layout decorators:
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β LayoutΒ DecoratorΒ Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β | Description |
|---|---|
pad(path, spaces)(layoutFunction)
|
|
stylePath(path, f)(layoutFunction)
|
|
If many decorators are applied to a layout function, the resulting code can get deeply nested:
const {layout, pad, table} = require('shargs/usage')
const style = {
cols: [{width: 25}, {width: 30}]
}
layout([
pad(['cols', 0], 4)(
pad(['cols', 1], 4)(
table([
['-a, --answer=<number>', 'The answer. [default: 42]']
])
)
)
])(style)Layout decorator combinators avoid nesting deeply, by first collecting layout decorators and applying them all at once:
const {decorate, layout, pad, table} = require('shargs/usage')
const style = {
cols: [{width: 25}, {width: 30}]
}
const decorated = decorate(pad(['cols', 0], 4), pad(['cols', 1], 4))
layout([
decorated(
table([
['-a, --answer=<number>', 'The answer. [default: 42]']
])
)
])(style)Note, that decorate applies layout decorators from right to left.
shargs-usage contains the following layout decorator combinators:
| Β Β Β Β Β Β Β Β Β Β Β Β Β LayoutΒ DecoratorΒ CombinatorΒ Β Β Β Β Β Β Β Β Β Β Β Β | Description |
|---|---|
decorate(decorators)(layoutFunction) |
decorate takes many layout function decorators
and applies them to its layoutFunction from right to left.
|
Using your own layout function is straightforward:
Your function only has to have the correct signature and it is ready to be used as a layout function:
It must take a style object and return a string.
The following example showcases the custom table2 layout function that takes columns instead of rows as input:
const {table} = require('shargs/usage')
const table2 = (columns = []) => style => {
const rows = []
for (let i = 0; i < columns[0].length; i++) {
const row = []
for (let j = 0; j < columns.length; j++) {
row.push(columns[j][i])
}
rows.push(row)
}
return table(rows)(style)
}You may use table2 as a layout function if you apply it to a columns array,
since that returns a function that takes a style argument and returns a string.
This is of course a very simplified example that makes many assumptions that are often not valid and should not be made in real projects. Your own function would most probably need much more validations and handling of edge cases.
Writing and using custom usage functions in shargs is very simple:
You only have to write a function with the correct signature and it can be used as a usage function.
It must take an opt object and a style object and return a string.
The following example shows the custom descs function that displays the options' descriptions:
const {text} = require('shargs/usage')
const desc = ({desc = ''} = {}) => text(desc)Using usageMap simplifies the process of defining your own functions:
const {table, usageMap} = require('shargs/usage')
const optsTable = usageMap(
({key, args, required, desc}) => table([
[(required ? '*' : '') + key, args.join(', '), desc]
])
)shargs-core and shargs-parser report errors if a
command-line option's syntax is invalid, or if checks fail.
The following table contains all error codes currently in use and where they are thrown:
| Code | Message | ThrownΒ by |
|---|---|---|
ArgumentIsNotABool |
The passed command line argument must either be 'true' or 'false'. |
cast
|
ArgumentIsNotANumber |
The passed command line argument must be a number. |
cast
|
CommandExpected |
Expected a command with a string "key" field and an "opts" array. |
verifyCommand
|
ContradictionDetected |
Some given keys contradict each other. |
contradictOpts
|
DidYouMean |
An unknown command-line argument was passed. Did you mean any of the following options? |
suggestOpts
|
FalseArgsRules |
Your args rules returned false. Please abide to the rules defined in verifyArgs. |
verifyArgs
|
FalseArgvRules |
Your argv rules returned false. Please abide to the rules defined in verifyArgv. |
verifyArgv
|
FalseOptsRules |
Your opts rules returned false. Please abide to the rules defined in verifyOpts. |
verifyOpts
|
ImplicationViolated |
Some given keys that imply each other are not all defined. |
implyOpts
|
IncompatibleTypes |
Repeated options must either both be variadic or both not. |
arrayOnRepeat
|
InvalidArgs |
The "args" field has an invalid value: "args" must be a non-empty array of strings. |
verifyCommand
|
InvalidArity |
An option's types arity does not match its values arity. |
verifyValuesArity
|
InvalidBoolMapping |
The mapping provided to broadenBools must only map from 'true' or 'false' to a list of alternatives. |
broadenBools
|
InvalidKey |
The "key" field has an invalid value: "key" must be a string, cannot be "_" or "--", and must not include whitespaces. |
verifyCommand
|
InvalidNestedCommand |
Commands cannot be nested inside commands. Did you forget an "args" field for your subcommand? |
verifyCommand
|
InvalidOptionsListInCombine |
Options list in combine was undefined, null or empty. |
verifyCommand
|
InvalidOpts |
The "opts" field has an invalid value: "opts" must be an array of command-line options and positional arguments. |
verifyCommand
|
InvalidRequiredPositionalArgument |
If a positional argument is required, all previous positional arguments must be required as well. The required field must either be undefined, true or false. |
validatePosArgs
|
InvalidTypes |
Each argument must have a types key that must be null or an array. |
verifyCommandverifyValuesArity
|
InvalidValues |
An option's values field has an invalid type. |
verifyValuesArity
|
InvalidVariadicPositionalArgument |
Only the last positional argument may be variadic. |
validatePosArgs
|
OptionExpected |
A command-line option was expected, but something else was received. |
verifyCommand
|
PosArgExpected |
A positional argument was expected, but something else was received. |
verifyCommand
|
RequiredOptionMissing |
An option that is marked as required has not been provided. |
requireOpts
|
SubcommandExpected |
A subcommand was expected, but something else was received. |
verifyCommand
|
SubcommandRequired |
No subcommand found. Please use at least one subcommand! |
demandASubcommand
|
UnexpectedArgument |
An unexpected argument was used that has no option defined. |
failRest
|
UnknownCommandLineOptionType |
The command-line option or positional argument given is of an unknown type. |
verifyCommand
|
ValueRestrictionsViolated |
A value lies outside the allowed values of an option. |
restrictToOnly
|
WrongArgsRulesType |
The args rules are of a wrong type, please provide a predicate with the following signature: (args) => boolean. |
verifyArgs
|
WrongArgvRulesType |
The argv rules are of a wrong type, please provide a predicate with the following signature: (argv) => boolean. |
verifyArgv
|
WrongContradictsType |
The contradicts field has the wrong type, please provide an array of command-line option keys. |
contradictOpts
|
WrongFormatForRequiredOption |
A required option has values or defaultValues in the wrong format. Default values are different depending on the command-line option type: Commands take objects, flags take counts, and other options take arrays of the correct length. |
requireOpts
|
WrongImpliesType |
The implies field has the wrong type, please provide an array of command-line option keys. |
implyOpts
|
WrongOptsRulesType |
The opts rules are of a wrong type, please provide a predicate with the following signature: (options) => boolean. |
verifyCommand
|
| Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β QuestionΒ Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β | Answer |
|---|---|
| How can I use config objects with shargs? |
A config object in this question denotes an object that is used to read in default values from a file or a URI.
Shargs does not include reading and merging config objects because there are other specialized libraries for this task
that are easy to use alongside shargs.
There are several simple ways to combine shargs'
|
Why do command-line options have a key field? |
The
|
Can I use custom command-line option types like date? |
Yes, you can add and use your own option types. Both, the command-line options DSL and the parser functions have been designed with this in mind:Say you want to add your own custom const {array} = require('shargs/opts')
const date = array(['date'])A Now we have an option, we may want to write parser stages that work with const {traverseOpts} = require('shargs/parser')
function dateToMillis ({errs = [], opts = []} = {}) {
const isDate = ({types}) => (
Array.isArray(types) &&
types.length === 1 &&
types[0] === 'date'
)
const toMillis = string => new Date(string).getTime()
const dateToMillis = opt => ({
opts: [{
...opt,
...(Array.isArray(opt.values)
? {values: opt.values.map(toMillis)}
: {}
)
}]
})
return traverseOpts(isDate)(dateToMillis)({errs, opts})
}This parser stage works alongside the other parser stages. Note, that a real implementation would test much more edge cases, like dates that occur in arrays. |
Can I use comma-separated values to define arrays? |
|
Why are --no-* arguments not reversed by the bestGuess* stages? |
The reason is because there is no simple way to opt-out of this functionality, once it is employed.
You could add an optOutReverse parameter to each bestGuess* stage, I guess,
but that would clutter the stages' signatures.
So shargs decided to leave interpreting these arguments to the individual programs.
|
| Can I have command-line options with 0..1 values? |
An example for such an option would be ternary logics types,
like
|
| Can I use enums? |
Yes, you can use enums with a combination of
|
Can I use keys like 'a.b', indicating object fields? |
Some command-line parsers allow arguments of the form
|
| Why do shargs' functions have several parameter lists? |
Many functions have an unusual signature, like
|
| Β | shargs |
yargs |
commander.js |
minimist |
|---|---|---|---|---|
| Self-description | Shargs turns command-line arguments parsing inside out and gives you fine-grained control over parser stages and usage docs. | Yargs helps you build interactive command line tools, by parsing arguments and generating an elegant user interface. | The complete solution for node.js command-line interfaces, inspired by Ruby's commander. | Minimist is the guts of optimist's argument parser without all the fanciful decoration. |
| Focus | A command-line parser library with a focus on enabling developers to easily and quickly build their own parsers of just the right size. | A large parser with lots of features with a focus on providing the options out of the box. | A medium parser with a strong focus on a textual DSL that makes it easy to define options. | A tiny parser, mostly without an options schema, with a strong focus on optimistic parsing. |
| License | MIT | MIT | MIT | MIT |
| First Commit | January 14th 2020 | September 10th 2010 | August 14th 2011 | June 25th 2013 |
| Customize Parsing | Pick and choose your parser checks and stages, write and use custom checks and stages, and optionally define command-specific parsers. |
You can
turn on and off
some of yargs' parsing features,
and use a kind of middleware
similar to shargs' args stages.
|
You may specify a function to do custom processing of option values. | None that I am aware of. |
| Customize Usage Docs | Use a DSL with many options to build custom usage documentation layouts with fine-grained control over styles. | Allows specifying the scriptName, a usage string, an epilogue, examples as strings, and the number of columns after which to wrap. |
Display extra information by
listening to the --help event,
customize program name and usage description,
and add custom description text.
|
None that I am aware of. |
Please report issues in the tracker!
We are open to, and grateful for, any contributions made by the community. By contributing to shargs, you agree to abide by the code of conduct. Please read the contributing guide.
Shargs is MIT licensed.
Logo created by brgfx (www.freepik.com).
