Skip to content

Commit acfc33c

Browse files
shadowspawndanielleadams
authored andcommitted
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 8a8de94 commit acfc33c

File tree

3 files changed

+515
-104
lines changed

3 files changed

+515
-104
lines changed

‎doc/api/util.md‎

Lines changed: 121 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,6 +1024,11 @@ equality.
10241024

10251025
<!-- YAML
10261026
added: v18.3.0
1027+
changes:
1028+
- version: REPLACEME
1029+
pr-url: https://github.com/nodejs/node/pull/43459
1030+
description: add support for returning detailed parse information
1031+
using `tokens` in input `config` and returned properties.
10271032
-->
10281033

10291034
> Stability: 1 - Experimental
@@ -1040,18 +1045,24 @@ added: v18.3.0
10401045
times. If `true`, all values will be collected in an array. If
10411046
`false`, values for the option are last-wins. **Default:**`false`.
10421047
*`short`{string} A single character alias for the option.
1043-
*`strict`:{boolean} Should an error be thrown when unknown arguments
1048+
*`strict`{boolean} Should an error be thrown when unknown arguments
10441049
are encountered, or when arguments are passed that do not match the
10451050
`type` configured in `options`.
10461051
**Default:**`true`.
1047-
*`allowPositionals`:{boolean} Whether this command accepts positional
1052+
*`allowPositionals`{boolean} Whether this command accepts positional
10481053
arguments.
10491054
**Default:**`false` if `strict` is `true`, otherwise `true`.
1055+
*`tokens`{boolean} Return the parsed tokens. This is useful for extending
1056+
the built-in behavior, from adding additional checks through to reprocessing
1057+
the tokens in different ways.
1058+
**Default:**`false`.
10501059

10511060
* Returns:{Object} The parsed command line arguments:
10521061
*`values`{Object} A mapping of parsed option names with their{string}
10531062
or{boolean} values.
10541063
*`positionals`{string\[]} Positional arguments.
1064+
*`tokens`{Object\[] | undefined} See [parseArgs tokens](#parseargs-tokens)
1065+
section. Only returned if `config` includes `tokens: true`.
10551066

10561067
Provides a higher level API for command-line argument parsing than interacting
10571068
with `process.argv` directly. Takes a specification for the expected arguments
@@ -1100,6 +1111,114 @@ console.log(values, positionals);
11001111
`util.parseArgs` is experimental and behavior may change. Join the
11011112
conversation in [pkgjs/parseargs][] to contribute to the design.
11021113

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

0 commit comments

Comments
(0)