Skip to content

Commit 4a3ba87

Browse files
authored
util: add parseArgs module
Adds util.parseArgs helper for higher level command-line argument parsing. PR-URL: #42675 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Ruy Adorno <[email protected]> Reviewed-By: Darshan Sen <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]> Reviewed-By: Joe Sepi <[email protected]> Reviewed-By: Ian Sutherland <[email protected]>
1 parent 304e06a commit 4a3ba87

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
@@ -2355,6 +2355,40 @@ The `package.json` [`"exports"`][] field does not export the requested subpath.
23552355
Because exports are encapsulated, private internal modules that are not exported
23562356
cannot be imported through the package resolution, unless using an absolute URL.
23572357

2358+
<a id="ERR_PARSE_ARGS_INVALID_OPTION_VALUE"></a>
2359+
2360+
### `ERR_PARSE_ARGS_INVALID_OPTION_VALUE`
2361+
2362+
<!-- YAML
2363+
added: REPLACEME
2364+
-->
2365+
2366+
When `strict` set to `true`, thrown by [`util.parseArgs()`][] if a {boolean}
2367+
value is provided for an option of type {string}, or if a {string}
2368+
value is provided for an option of type {boolean}.
2369+
2370+
<a id="ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL"></a>
2371+
2372+
### `ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL`
2373+
2374+
<!-- YAML
2375+
added: REPLACEME
2376+
-->
2377+
2378+
Thrown by [`util.parseArgs()`][], when a postional argument is provided and
2379+
`allowPositionals` is set to `false`.
2380+
2381+
<a id="ERR_PARSE_ARGS_UNKNOWN_OPTION"></a>
2382+
2383+
### `ERR_PARSE_ARGS_UNKNOWN_OPTION`
2384+
2385+
<!-- YAML
2386+
added: REPLACEME
2387+
-->
2388+
2389+
When `strict` set to `true`, thrown by [`util.parseArgs()`][] if an argument
2390+
is not configured in `options`.
2391+
23582392
<a id="ERR_PERFORMANCE_INVALID_TIMESTAMP"></a>
23592393

23602394
### `ERR_PERFORMANCE_INVALID_TIMESTAMP`
@@ -3466,6 +3500,7 @@ The native call from `process.cpuUsage` could not be processed.
34663500
[`subprocess.send()`]: child_process.md#subprocesssendmessage-sendhandle-options-callback
34673501
[`url.parse()`]: url.md#urlparseurlstring-parsequerystring-slashesdenotehost
34683502
[`util.getSystemErrorName(error.errno)`]: util.md#utilgetsystemerrornameerr
3503+
[`util.parseArgs()`]: util.md#utilparseargsconfig
34693504
[`zlib`]: zlib.md
34703505
[crypto digest algorithm]: crypto.md#cryptogethashes
34713506
[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
@@ -1481,6 +1481,15 @@ E('ERR_PACKAGE_PATH_NOT_EXPORTED', (pkgPath, subpath, base = undefined) => {
14811481
return `Package subpath '${subpath}' is not defined by "exports" in ${
14821482
pkgPath}package.json${base ? ` imported from ${base}` : ''}`;
14831483
}, Error);
1484+
E('ERR_PARSE_ARGS_INVALID_OPTION_VALUE', '%s', TypeError);
1485+
E('ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL', "Unexpected argument '%s'. This " +
1486+
'command does not take positional arguments', TypeError);
1487+
E('ERR_PARSE_ARGS_UNKNOWN_OPTION', (option, allowPositionals) => {
1488+
const suggestDashDash = allowPositionals ? '. To specify a positional ' +
1489+
"argument starting with a '-', place it at the end of the command after " +
1490+
`'--', as in '-- ${JSONStringify(option)}` : '';
1491+
return `Unknown option '${option}'${suggestDashDash}`;
1492+
}, TypeError);
14841493
E('ERR_PERFORMANCE_INVALID_TIMESTAMP',
14851494
'%d is not a valid timestamp', TypeError);
14861495
E('ERR_PERFORMANCE_MEASURE_INVALID_OPTIONS', '%s', TypeError);

0 commit comments

Comments
 (0)