Spaces:
Sleeping
Sleeping
File size: 11,398 Bytes
e2f726f |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
# jackspeak
A very strict and proper argument parser.
Validate string, boolean, and number options, from the command
line and the environment.
Call the `jack` method with a config object, and then chain
methods off of it.
At the end, call the `.parse()` method, and you'll get an object
with `positionals` and `values` members.
Any unrecognized configs or invalid values will throw an error.
As long as you define configs using object literals, types will
be properly inferred and TypeScript will know what kinds of
things you got.
If you give it a prefix for environment variables, then defaults
will be read from the environment, and parsed values written back
to it, so you can easily pass configs through to child processes.
Automatically generates a `usage`/`help` banner by calling the
`.usage()` method.
Unless otherwise noted, all methods return the object itself.
## USAGE
```js
import { jack } from 'jackspeak'
// this works too:
// const { jack } = require('jackspeak')
const { positionals, values } = jack({ envPrefix: 'FOO' })
.flag({
asdf: { description: 'sets the asfd flag', short: 'a', default: true },
'no-asdf': { description: 'unsets the asdf flag', short: 'A' },
foo: { description: 'another boolean', short: 'f' },
})
.optList({
'ip-addrs': {
description: 'addresses to ip things',
delim: ',', // defaults to '\n'
default: ['127.0.0.1'],
},
})
.parse([
'some',
'positional',
'--ip-addrs',
'192.168.0.1',
'--ip-addrs',
'1.1.1.1',
'args',
'--foo', // sets the foo flag
'-A', // short for --no-asdf, sets asdf flag to false
])
console.log(process.env.FOO_ASDF) // '0'
console.log(process.env.FOO_FOO) // '1'
console.log(values) // {
// 'ip-addrs': ['192.168.0.1', '1.1.1.1'],
// foo: true,
// asdf: false,
// }
console.log(process.env.FOO_IP_ADDRS) // '192.168.0.1,1.1.1.1'
console.log(positionals) // ['some', 'positional', 'args']
```
## `jack(options: JackOptions = {}) => Jack`
Returns a `Jack` object that can be used to chain and add
field definitions. The other methods (apart from `validate()`,
`parse()`, and `usage()` obviously) return the same Jack object,
updated with the new types, so they can be chained together as
shown in the code examples.
Options:
- `allowPositionals` Defaults to true. Set to `false` to not
allow any positional arguments.
- `envPrefix` Set to a string to write configs to and read
configs from the environment. For example, if set to `MY_APP`
then the `foo-bar` config will default based on the value of
`env.MY_APP_FOO_BAR` and will write back to that when parsed.
Boolean values are written as `'1'` and `'0'`, and will be
treated as `true` if they're `'1'` or false otherwise.
Number values are written with their `toString()`
representation.
Strings are just strings.
Any value with `multiple: true` will be represented in the
environment split by a delimiter, which defaults to `\n`.
- `env` The place to read/write environment variables. Defaults
to `process.env`.
- `usage` A short usage string to print at the top of the help
banner.
- `stopAtPositional` Boolean, default false. Stop parsing opts
and flags at the first positional argument. This is useful if
you want to pass certain options to subcommands, like some
programs do, so you can stop parsing and pass the positionals
to the subcommand to parse.
- `stopAtPositionalTest` Conditional `stopAtPositional`. Provide
a function that takes a positional argument string and returns
boolean. If it returns `true`, then parsing will stop. Useful
when _some_ subcommands should parse the rest of the command
line options, and others should not.
### `Jack.heading(text: string, level?: 1 | 2 | 3 | 4 | 5 | 6)`
Define a short string heading, used in the `usage()` output.
Indentation of the heading and subsequent description/config
usage entries (up until the next heading) is set by the heading
level.
If the first usage item defined is a heading, it is always
treated as level 1, regardless of the argument provided.
Headings level 1 and 2 will have a line of padding underneath
them. Headings level 3 through 6 will not.
### `Jack.description(text: string, { pre?: boolean } = {})`
Define a long string description, used in the `usage()` output.
If the `pre` option is set to `true`, then whitespace will not be
normalized. However, if any line is too long for the width
allotted, it will still be wrapped.
## Option Definitions
Configs are defined by calling the appropriate field definition
method with an object where the keys are the long option name,
and the value defines the config.
Options:
- `type` Only needed for the `addFields` method, as the others
set it implicitly. Can be `'string'`, `'boolean'`, or
`'number'`.
- `multiple` Only needed for the `addFields` method, as the
others set it implicitly. Set to `true` to define an array
type. This means that it can be set on the CLI multiple times,
set as an array in the `values`
and it is represented in the environment as a delimited string.
- `short` A one-character shorthand for the option.
- `description` Some words to describe what this option is and
why you'd set it.
- `hint` (Only relevant for non-boolean types) The thing to show
in the usage output, like `--option=<hint>`
- `validate` A function that returns false (or throws) if an
option value is invalid.
- `validOptions` An array of strings or numbers that define the
valid values that can be set. This is not allowed on `boolean`
(flag) options. May be used along with a `validate()` method.
- `default` A default value for the field. Note that this may be
overridden by an environment variable, if present.
### `Jack.flag({ [option: string]: definition, ... })`
Define one or more boolean fields.
Boolean options may be set to `false` by using a
`--no-${optionName}` argument, which will be implicitly created
if it's not defined to be something else.
If a boolean option named `no-${optionName}` with the same
`multiple` setting is in the configuration, then that will be
treated as a negating flag.
### `Jack.flagList({ [option: string]: definition, ... })`
Define one or more boolean array fields.
### `Jack.num({ [option: string]: definition, ... })`
Define one or more number fields. These will be set in the
environment as a stringified number, and included in the `values`
object as a number.
### `Jack.numList({ [option: string]: definition, ... })`
Define one or more number list fields. These will be set in the
environment as a delimited set of stringified numbers, and
included in the `values` as a number array.
### `Jack.opt({ [option: string]: definition, ... })`
Define one or more string option fields.
### `Jack.optList({ [option: string]: definition, ... })`
Define one or more string list fields.
### `Jack.addFields({ [option: string]: definition, ... })`
Define one or more fields of any type. Note that `type` and
`multiple` must be set explicitly on each definition when using
this method.
## Actions
Use these methods on a Jack object that's already had its config
fields defined.
### `Jack.parse(args: string[] = process.argv): { positionals: string[], values: OptionsResults }`
Parse the arguments list, write to the environment if `envPrefix`
is set, and returned the parsed values and remaining positional
arguments.
### `Jack.validate(o: any): asserts o is OptionsResults`
Throws an error if the object provided is not a valid result set,
for the configurations defined thusfar.
### `Jack.usage(): string`
Returns the compiled `usage` string, with all option descriptions
and heading/description text, wrapped to the appropriate width
for the terminal.
### `Jack.setConfigValues(options: OptionsResults, src?: string)`
Validate the `options` argument, and set the default value for
each field that appears in the options.
Values provided will be overridden by environment variables or
command line arguments.
### `Jack.usageMarkdown(): string`
Returns the compiled `usage` string, with all option descriptions
and heading/description text, but as markdown instead of
formatted for a terminal, for generating HTML documentation for
your CLI.
## Some Example Code
Also see [the examples
folder](https://github.com/isaacs/jackspeak/tree/master/examples)
```js
import { jack } from 'jackspeak'
const j = jack({
// Optional
// This will be auto-generated from the descriptions if not supplied
// top level usage line, printed by -h
// will be auto-generated if not specified
usage: 'foo [options] <files>',
})
.heading('The best Foo that ever Fooed')
.description(
`
Executes all the files and interprets their output as
TAP formatted test result data.
To parse TAP data from stdin, specify "-" as a filename.
`,
)
// flags don't take a value, they're boolean on or off, and can be
// turned off by prefixing with `--no-`
// so this adds support for -b to mean --bail, or -B to mean --no-bail
.flag({
flag: {
// specify a short value if you like. this must be a single char
short: 'f',
// description is optional as well.
description: `Make the flags wave`,
// default value for flags is 'false', unless you change it
default: true,
},
'no-flag': {
// you can can always negate a flag with `--no-flag`
// specifying a negate option will let you define a short
// single-char option for negation.
short: 'F',
description: `Do not wave the flags`,
},
})
// Options that take a value are specified with `opt()`
.opt({
reporter: {
short: 'R',
description: 'the style of report to display',
},
})
// if you want a number, say so, and jackspeak will enforce it
.num({
jobs: {
short: 'j',
description: 'how many jobs to run in parallel',
default: 1,
},
})
// A list is an option that can be specified multiple times,
// to expand into an array of all the settings. Normal opts
// will just give you the last value specified.
.optList({
'node-arg': {},
})
// a flagList is an array of booleans, so `-ddd` is [true, true, true]
// count the `true` values to treat it as a counter.
.flagList({
debug: { short: 'd' },
})
// opts take a value, and is set to the string in the results
// you can combine multiple short-form flags together, but
// an opt will end the combine chain, posix-style. So,
// -bofilename would be like --bail --output-file=filename
.opt({
'output-file': {
short: 'o',
// optional: make it -o<file> in the help output insead of -o<value>
hint: 'file',
description: `Send the raw output to the specified file.`,
},
})
// now we can parse argv like this:
const { values, positionals } = j.parse(process.argv)
// or decide to show the usage banner
console.log(j.usage())
// or validate an object config we got from somewhere else
try {
j.validate(someConfig)
} catch (er) {
console.error('someConfig is not valid!', er)
}
```
## Name
The inspiration for this module is [yargs](http://npm.im/yargs), which
is pirate talk themed. Yargs has all the features, and is infinitely
flexible. "Jackspeak" is the slang of the royal navy. This module
does not have all the features. It is declarative and rigid by design.
|