Skip to content

Commit d6cf409

Browse files
bcoebengl
authored andcommitted
util: add parseArgs module
Adds util.parseArgs helper for higher level command-line argument parsing. PR-URL: #42675 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Ruy Adorno <ruyadorno@github.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Joe Sepi <sepi@joesepi.com> Reviewed-By: Ian Sutherland <ian@iansutherland.ca>
1 parent baa5d00 commit d6cf409

File tree

9 files changed

+1189
-0
lines changed

9 files changed

+1189
-0
lines changed

doc/api/errors.md

+35
Original file line numberDiff line numberDiff line change
@@ -2344,6 +2344,40 @@ The `package.json` [`"exports"`][] field does not export the requested subpath.
23442344
Because exports are encapsulated, private internal modules that are not exported
23452345
cannot be imported through the package resolution, unless using an absolute URL.
23462346

2347+
<a id="ERR_PARSE_ARGS_INVALID_OPTION_VALUE"></a>
2348+
2349+
### `ERR_PARSE_ARGS_INVALID_OPTION_VALUE`
2350+
2351+
<!-- YAML
2352+
added: REPLACEME
2353+
-->
2354+
2355+
When `strict` set to `true`, thrown by [`util.parseArgs()`][] if a {boolean}
2356+
value is provided for an option of type {string}, or if a {string}
2357+
value is provided for an option of type {boolean}.
2358+
2359+
<a id="ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL"></a>
2360+
2361+
### `ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL`
2362+
2363+
<!-- YAML
2364+
added: REPLACEME
2365+
-->
2366+
2367+
Thrown by [`util.parseArgs()`][], when a postional argument is provided and
2368+
`allowPositionals` is set to `false`.
2369+
2370+
<a id="ERR_PARSE_ARGS_UNKNOWN_OPTION"></a>
2371+
2372+
### `ERR_PARSE_ARGS_UNKNOWN_OPTION`
2373+
2374+
<!-- YAML
2375+
added: REPLACEME
2376+
-->
2377+
2378+
When `strict` set to `true`, thrown by [`util.parseArgs()`][] if an argument
2379+
is not configured in `options`.
2380+
23472381
<a id="ERR_PERFORMANCE_INVALID_TIMESTAMP"></a>
23482382

23492383
### `ERR_PERFORMANCE_INVALID_TIMESTAMP`
@@ -3455,6 +3489,7 @@ The native call from `process.cpuUsage` could not be processed.
34553489
[`subprocess.send()`]: child_process.md#subprocesssendmessage-sendhandle-options-callback
34563490
[`url.parse()`]: url.md#urlparseurlstring-parsequerystring-slashesdenotehost
34573491
[`util.getSystemErrorName(error.errno)`]: util.md#utilgetsystemerrornameerr
3492+
[`util.parseArgs()`]: util.md#utilparseargsconfig
34583493
[`zlib`]: zlib.md
34593494
[crypto digest algorithm]: crypto.md#cryptogethashes
34603495
[debugger]: debugger.md

doc/api/util.md

+81
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,86 @@ Otherwise, returns `false`.
10201020
See [`assert.deepStrictEqual()`][] for more information about deep strict
10211021
equality.
10221022

1023+
## `util.parseArgs([config])`
1024+
1025+
<!-- YAML
1026+
added: REPLACEME
1027+
-->
1028+
1029+
> Stability: 1 - Experimental
1030+
1031+
* `config` {Object} Used to provide arguments for parsing and to configure
1032+
the parser. `config` supports the following properties:
1033+
* `args` {string\[]} array of argument strings. **Default:** `process.argv`
1034+
with `execPath` and `filename` removed.
1035+
* `options` {Object} Used to describe arguments known to the parser.
1036+
Keys of `options` are the long names of options and values are an
1037+
{Object} accepting the following properties:
1038+
* `type` {string} Type of argument, which must be either `boolean` or `string`.
1039+
* `multiple` {boolean} Whether this option can be provided multiple
1040+
times. If `true`, all values will be collected in an array. If
1041+
`false`, values for the option are last-wins. **Default:** `false`.
1042+
* `short` {string} A single character alias for the option.
1043+
* `strict`: {boolean} Should an error be thrown when unknown arguments
1044+
are encountered, or when arguments are passed that do not match the
1045+
`type` configured in `options`.
1046+
**Default:** `true`.
1047+
* `allowPositionals`: {boolean} Whether this command accepts positional
1048+
arguments.
1049+
**Default:** `false` if `strict` is `true`, otherwise `true`.
1050+
1051+
* Returns: {Object} The parsed command line arguments:
1052+
* `values` {Object} A mapping of parsed option names with their {string}
1053+
or {boolean} values.
1054+
* `positionals` {string\[]} Positional arguments.
1055+
1056+
Provides a higher level API for command-line argument parsing than interacting
1057+
with `process.argv` directly. Takes a specification for the expected arguments
1058+
and returns a structured object with the parsed options and positionals.
1059+
1060+
```mjs
1061+
import { parseArgs } from 'node:util';
1062+
const args = ['-f', '--bar', 'b'];
1063+
const options = {
1064+
foo: {
1065+
type: 'boolean',
1066+
short: 'f'
1067+
},
1068+
bar: {
1069+
type: 'string'
1070+
}
1071+
};
1072+
const {
1073+
values,
1074+
positionals
1075+
} = parseArgs({ args, options });
1076+
console.log(values, positionals);
1077+
// Prints: [Object: null prototype] { foo: true, bar: 'b' } []
1078+
```
1079+
1080+
```cjs
1081+
const { parseArgs } = require('node:util');
1082+
const args = ['-f', '--bar', 'b'];
1083+
const options = {
1084+
foo: {
1085+
type: 'boolean',
1086+
short: 'f'
1087+
},
1088+
bar: {
1089+
type: 'string'
1090+
}
1091+
};
1092+
const {
1093+
values,
1094+
positionals
1095+
} = parseArgs({ args, options });
1096+
console.log(values, positionals);
1097+
// Prints: [Object: null prototype] { foo: true, bar: 'b' } []ss
1098+
```
1099+
1100+
`util.parseArgs` is experimental and behavior may change. Join the
1101+
conversation in [pkgjs/parseargs][] to contribute to the design.
1102+
10231103
## `util.promisify(original)`
10241104

10251105
<!-- YAML
@@ -2693,5 +2773,6 @@ util.log('Timestamped message.');
26932773
[default sort]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
26942774
[global symbol registry]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/for
26952775
[list of deprecated APIS]: deprecations.md#list-of-deprecated-apis
2776+
[pkgjs/parseargs]: https://github.com/pkgjs/parseargs
26962777
[semantically incompatible]: https://github.com/nodejs/node/issues/4179
26972778
[util.inspect.custom]: #utilinspectcustom

lib/internal/errors.js

+9
Original file line numberDiff line numberDiff line change
@@ -1474,6 +1474,15 @@ E('ERR_PACKAGE_PATH_NOT_EXPORTED', (pkgPath, subpath, base = undefined) => {
14741474
return `Package subpath '${subpath}' is not defined by "exports" in ${
14751475
pkgPath}package.json${base ? ` imported from ${base}` : ''}`;
14761476
}, Error);
1477+
E('ERR_PARSE_ARGS_INVALID_OPTION_VALUE', '%s', TypeError);
1478+
E('ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL', "Unexpected argument '%s'. This " +
1479+
'command does not take positional arguments', TypeError);
1480+
E('ERR_PARSE_ARGS_UNKNOWN_OPTION', (option, allowPositionals) => {
1481+
const suggestDashDash = allowPositionals ? '. To specify a positional ' +
1482+
"argument starting with a '-', place it at the end of the command after " +
1483+
`'--', as in '-- ${JSONStringify(option)}` : '';
1484+
return `Unknown option '${option}'${suggestDashDash}`;
1485+
}, TypeError);
14771486
E('ERR_PERFORMANCE_INVALID_TIMESTAMP',
14781487
'%d is not a valid timestamp', TypeError);
14791488
E('ERR_PERFORMANCE_MEASURE_INVALID_OPTIONS', '%s', TypeError);

0 commit comments

Comments
 (0)