Skip to content

Commit a44cca7

Browse files
shadowspawntargos
authored andcommittedJul 31, 2022
util: add tokens to parseArgs
Offer additional meta-data for building custom and additional behaviour on top of parseArgs. PR-URL: #43459 Reviewed-By: Ben Coe <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]>
1 parent 7377092 commit a44cca7

File tree

3 files changed

+515
-104
lines changed

3 files changed

+515
-104
lines changed
 

‎doc/api/util.md

+121-2
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,11 @@ equality.
10181018

10191019
<!-- YAML
10201020
added: REPLACEME
1021+
changes:
1022+
- version: REPLACEME
1023+
pr-url: https://github.com/nodejs/node/pull/43459
1024+
description: add support for returning detailed parse information
1025+
using `tokens` in input `config` and returned properties.
10211026
-->
10221027

10231028
> Stability: 1 - Experimental
@@ -1034,18 +1039,24 @@ added: REPLACEME
10341039
times. If `true`, all values will be collected in an array. If
10351040
`false`, values for the option are last-wins. **Default:** `false`.
10361041
* `short` {string} A single character alias for the option.
1037-
* `strict`: {boolean} Should an error be thrown when unknown arguments
1042+
* `strict` {boolean} Should an error be thrown when unknown arguments
10381043
are encountered, or when arguments are passed that do not match the
10391044
`type` configured in `options`.
10401045
**Default:** `true`.
1041-
* `allowPositionals`: {boolean} Whether this command accepts positional
1046+
* `allowPositionals` {boolean} Whether this command accepts positional
10421047
arguments.
10431048
**Default:** `false` if `strict` is `true`, otherwise `true`.
1049+
* `tokens` {boolean} Return the parsed tokens. This is useful for extending
1050+
the built-in behavior, from adding additional checks through to reprocessing
1051+
the tokens in different ways.
1052+
**Default:** `false`.
10441053

10451054
* Returns: {Object} The parsed command line arguments:
10461055
* `values` {Object} A mapping of parsed option names with their {string}
10471056
or {boolean} values.
10481057
* `positionals` {string\[]} Positional arguments.
1058+
* `tokens` {Object\[] | undefined} See [parseArgs tokens](#parseargs-tokens)
1059+
section. Only returned if `config` includes `tokens: true`.
10491060

10501061
Provides a higher level API for command-line argument parsing than interacting
10511062
with `process.argv` directly. Takes a specification for the expected arguments
@@ -1094,6 +1105,114 @@ console.log(values, positionals);
10941105
`util.parseArgs` is experimental and behavior may change. Join the
10951106
conversation in [pkgjs/parseargs][] to contribute to the design.
10961107

1108+
### `parseArgs` `tokens`
1109+
1110+
Detailed parse information is available for adding custom behaviours by
1111+
specifying `tokens: true` in the configuration.
1112+
The returned tokens have properties describing:
1113+
1114+
* all tokens
1115+
* `kind` {string} One of 'option', 'positional', or 'option-terminator'.
1116+
* `index` {number} Index of element in `args` containing token. So the
1117+
source argument for a token is `args[token.index]`.
1118+
* option tokens
1119+
* `name` {string} Long name of option.
1120+
* `rawName` {string} How option used in args, like `-f` of `--foo`.
1121+
* `value` {string | undefined} Option value specified in args.
1122+
Undefined for boolean options.
1123+
* `inlineValue` {boolean | undefined} Whether option value specified inline,
1124+
like `--foo=bar`.
1125+
* positional tokens
1126+
* `value` {string} The value of the positional argument in args (i.e. `args[index]`).
1127+
* option-terminator token
1128+
1129+
The returned tokens are in the order encountered in the input args. Options
1130+
that appear more than once in args produce a token for each use. Short option
1131+
groups like `-xy` expand to a token for each option. So `-xxx` produces
1132+
three tokens.
1133+
1134+
For example to use the returned tokens to add support for a negated option
1135+
like `--no-color`, the tokens can be reprocessed to change the value stored
1136+
for the negated option.
1137+
1138+
```mjs
1139+
import { parseArgs } from 'node:util';
1140+
1141+
const options = {
1142+
'color': { type: 'boolean' },
1143+
'no-color': { type: 'boolean' },
1144+
'logfile': { type: 'string' },
1145+
'no-logfile': { type: 'boolean' },
1146+
};
1147+
const { values, tokens } = parseArgs({ options, tokens: true });
1148+
1149+
// Reprocess the option tokens and overwrite the returned values.
1150+
tokens
1151+
.filter((token) => token.kind === 'option')
1152+
.forEach((token) => {
1153+
if (token.name.startsWith('no-')) {
1154+
// Store foo:false for --no-foo
1155+
const positiveName = token.name.slice(3);
1156+
values[positiveName] = false;
1157+
delete values[token.name];
1158+
} else {
1159+
// Resave value so last one wins if both --foo and --no-foo.
1160+
values[token.name] = token.value ?? true;
1161+
}
1162+
});
1163+
1164+
const color = values.color;
1165+
const logfile = values.logfile ?? 'default.log';
1166+
1167+
console.log({ logfile, color });
1168+
```
1169+
1170+
```cjs
1171+
const { parseArgs } = require('node:util');
1172+
1173+
const options = {
1174+
'color': { type: 'boolean' },
1175+
'no-color': { type: 'boolean' },
1176+
'logfile': { type: 'string' },
1177+
'no-logfile': { type: 'boolean' },
1178+
};
1179+
const { values, tokens } = parseArgs({ options, tokens: true });
1180+
1181+
// Reprocess the option tokens and overwrite the returned values.
1182+
tokens
1183+
.filter((token) => token.kind === 'option')
1184+
.forEach((token) => {
1185+
if (token.name.startsWith('no-')) {
1186+
// Store foo:false for --no-foo
1187+
const positiveName = token.name.slice(3);
1188+
values[positiveName] = false;
1189+
delete values[token.name];
1190+
} else {
1191+
// Resave value so last one wins if both --foo and --no-foo.
1192+
values[token.name] = token.value ?? true;
1193+
}
1194+
});
1195+
1196+
const color = values.color;
1197+
const logfile = values.logfile ?? 'default.log';
1198+
1199+
console.log({ logfile, color });
1200+
```
1201+
1202+
Example usage showing negated options, and when an option is used
1203+
multiple ways then last one wins.
1204+
1205+
```console
1206+
$ node negate.js
1207+
{ logfile: 'default.log', color: undefined }
1208+
$ node negate.js --no-logfile --no-color
1209+
{ logfile: false, color: false }
1210+
$ node negate.js --logfile=test.log --color
1211+
{ logfile: 'test.log', color: true }
1212+
$ node negate.js --no-logfile --logfile=test.log --color --no-color
1213+
{ logfile: 'test.log', color: false }
1214+
```
1215+
10971216
## `util.promisify(original)`
10981217
10991218
<!-- YAML

0 commit comments

Comments
 (0)
Please sign in to comment.