-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add parsed meta-data to returned properties #129
Merged
Merged
Changes from all commits
Commits
Show all changes
69 commits
Select commit
Hold shift + click to select a range
52c3a2e
Proof of concept
shadowspawn 5ec71b1
Return originalArgs and indices
shadowspawn f711187
Change short in AST to boolean
shadowspawn cce90bb
Store inlineValue rather than valueIndex
shadowspawn e4bc5fe
Rename symbol property in AST to kind
shadowspawn 87624a1
Rename returned ast property to parseElements
shadowspawn 2925639
Refactor to use parseElements to test for errors and store values
shadowspawn 412287e
Merge remote-tracking branch 'upstream/main' into feature/ast
shadowspawn 1897dac
Build positionals from parseElements
shadowspawn 45d12e7
Replace .push with primordial
shadowspawn 1d19fb2
forEach replaced with primordial
shadowspawn 9d1267d
Swap isShort for optionUsed
shadowspawn c9c4d4c
Rework checkOptionLikeValue
shadowspawn ed4168d
Consistent property order and renames
shadowspawn 16a9f51
Comment out new returned property until naming and tests ready
shadowspawn 915e6c3
Refactor tokenize into own function
shadowspawn 641197e
First test pass, get tests working by ignoring tokens in results
shadowspawn 18470bd
Less magical check now checking kind
shadowspawn 38d1d6c
Add some token tests
shadowspawn cfbd436
Fix index after space-separated option value
shadowspawn 397dcf1
Make tokens opt-in
shadowspawn 938bdb0
Simplify more tests with opt-in details
shadowspawn c5da345
Rename token.optionName to name, and restore longOption/shortOption …
shadowspawn 4e1ec2d
Add example
shadowspawn bd3f574
Add side-note
shadowspawn b703ee8
Add example of #52, limit long syntax
shadowspawn 82339f9
Add ordered example
shadowspawn fab1dc4
Add example for no repeated options
shadowspawn 99165c9
Comment wording
shadowspawn 9bd039d
Switch from details to tokens for configuration
shadowspawn 6a3f637
Simplify example by removing "library" support
shadowspawn 4cfbc29
Remove comments on how well-advised the examples goals are
shadowspawn 9a9a740
Switch from optionUser to rawName
shadowspawn 7cd685c
Merge remote-tracking branch 'upstream/main' into feature/ast
shadowspawn 6f93632
Use modern syntax
shadowspawn 642d8c0
Validate new input property
shadowspawn e70609a
Move strict check outside check routines
shadowspawn fdaa553
Tidy object setup
shadowspawn c293307
Simplify test
shadowspawn 041459d
Indentation and match filename
shadowspawn 7aafaf6
Rework with strict:true
shadowspawn 1b6e585
Longer but simpler
shadowspawn 20eacb0
Add textual description, and some fixes
shadowspawn 81239d6
Add example output for tokens
shadowspawn 84dde8e
Add example used in new documentation
shadowspawn 40a6201
Refactor documentation
shadowspawn 07fbb91
Update .editorconfig from Node.js
shadowspawn 35c16f1
rework token property descriptions
shadowspawn 038480a
Minor refactor of remaining arg processing
shadowspawn d4f96cc
Replace switch with if/else
shadowspawn 122727e
Remove side-affect, per feedback
shadowspawn 5b3518d
Add tricky case of short option group to token expansion
shadowspawn c8c2f84
Rework description of token.index after token example.
shadowspawn 2b119b0
Be less clever with script naming in example
shadowspawn 1b3c076
Remove superfluous colons in docs
shadowspawn e9e30a7
Upstream lint
shadowspawn 46ab295
Merge branch 'main' into feature/ast
shadowspawn 01baee5
Merge branch 'main' into feature/ast
shadowspawn 46903af
Improve description
shadowspawn 9b74c05
Merge branch 'feature/ast' of github.com:shadowspawn/parseargs into f…
shadowspawn 89e4532
Use negate as example for tokens. Rework description a little.
shadowspawn 3914949
Lint and feedback
shadowspawn d8126d1
Expand small token tests and remove uber tests
shadowspawn 87bab7b
Use kEmptyObject per upstream suggestion. Move node lookalikes to int…
shadowspawn 02ea585
Reworks tokens documentation for config
shadowspawn 56fe4ca
Add version changes to YAML.
shadowspawn 722f05e
Update README with upstream changes
shadowspawn ae9eca4
Update negate example calls to match documentation
shadowspawn f9dcc4f
Remove extra examples
shadowspawn File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -3,12 +3,17 @@ | |||||
|
||||||
[![Coverage][coverage-image]][coverage-url] | ||||||
|
||||||
Polyfill of proposal for `util.parseArgs()` | ||||||
Polyfill of `util.parseArgs()` | ||||||
|
||||||
## `util.parseArgs([config])` | ||||||
|
||||||
<!-- YAML | ||||||
added: REPLACEME | ||||||
added: v18.3.0 | ||||||
changes: | ||||||
- version: REPLACEME | ||||||
pr-url: https://github.com/nodejs/node/pull/43459 | ||||||
description: add support for returning detailed parse information | ||||||
using `tokens` in input `config` and returned properties. | ||||||
--> | ||||||
|
||||||
> Stability: 1 - Experimental | ||||||
|
@@ -25,18 +30,24 @@ added: REPLACEME | |||||
times. If `true`, all values will be collected in an array. If | ||||||
`false`, values for the option are last-wins. **Default:** `false`. | ||||||
* `short` {string} A single character alias for the option. | ||||||
* `strict`: {boolean} Should an error be thrown when unknown arguments | ||||||
* `strict` {boolean} Should an error be thrown when unknown arguments | ||||||
are encountered, or when arguments are passed that do not match the | ||||||
`type` configured in `options`. | ||||||
**Default:** `true`. | ||||||
* `allowPositionals`: {boolean} Whether this command accepts positional | ||||||
* `allowPositionals` {boolean} Whether this command accepts positional | ||||||
arguments. | ||||||
**Default:** `false` if `strict` is `true`, otherwise `true`. | ||||||
* `tokens` {boolean} Return the parsed tokens. This is useful for extending | ||||||
the built-in behavior, from adding additional checks through to reprocessing | ||||||
the tokens in different ways. | ||||||
**Default:** `false`. | ||||||
|
||||||
* Returns: {Object} The parsed command line arguments: | ||||||
* `values` {Object} A mapping of parsed option names with their {string} | ||||||
or {boolean} values. | ||||||
* `positionals` {string\[]} Positional arguments. | ||||||
* `tokens` {Object\[] | undefined} See [parseArgs tokens](#parseargs-tokens) | ||||||
section. Only returned if `config` includes `tokens: true`. | ||||||
|
||||||
Provides a higher level API for command-line argument parsing than interacting | ||||||
with `process.argv` directly. Takes a specification for the expected arguments | ||||||
|
@@ -79,12 +90,120 @@ const { | |||||
positionals | ||||||
} = parseArgs({ args, options }); | ||||||
console.log(values, positionals); | ||||||
// Prints: [Object: null prototype] { foo: true, bar: 'b' } []ss | ||||||
// Prints: [Object: null prototype] { foo: true, bar: 'b' } [] | ||||||
``` | ||||||
|
||||||
`util.parseArgs` is experimental and behavior may change. Join the | ||||||
conversation in [pkgjs/parseargs][] to contribute to the design. | ||||||
|
||||||
### `parseArgs` `tokens` | ||||||
|
||||||
Detailed parse information is available for adding custom behaviours by | ||||||
specifying `tokens: true` in the configuration. | ||||||
The returned tokens have properties describing: | ||||||
|
||||||
* all tokens | ||||||
* `kind` {string} One of 'option', 'positional', or 'option-terminator'. | ||||||
* `index` {number} Index of element in `args` containing token. So the | ||||||
source argument for a token is `args[token.index]`. | ||||||
* option tokens | ||||||
* `name` {string} Long name of option. | ||||||
* `rawName` {string} How option used in args, like `-f` of `--foo`. | ||||||
* `value` {string | undefined} Option value specified in args. | ||||||
Undefined for boolean options. | ||||||
* `inlineValue` {boolean | undefined} Whether option value specified inline, | ||||||
like `--foo=bar`. | ||||||
* positional tokens | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
* `value` {string} The value of the positional argument in args (i.e. `args[index]`). | ||||||
* option-terminator token | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
The returned tokens are in the order encountered in the input args. Options | ||||||
that appear more than once in args produce a token for each use. Short option | ||||||
groups like `-xy` expand to a token for each option. So `-xxx` produces | ||||||
three tokens. | ||||||
|
||||||
For example to use the returned tokens to add support for a negated option | ||||||
like `--no-color`, the tokens can be reprocessed to change the value stored | ||||||
for the negated option. | ||||||
|
||||||
```mjs | ||||||
import { parseArgs } from 'node:util'; | ||||||
|
||||||
const options = { | ||||||
'color': { type: 'boolean' }, | ||||||
'no-color': { type: 'boolean' }, | ||||||
'logfile': { type: 'string' }, | ||||||
'no-logfile': { type: 'boolean' }, | ||||||
}; | ||||||
const { values, tokens } = parseArgs({ options, tokens: true }); | ||||||
|
||||||
// Reprocess the option tokens and overwrite the returned values. | ||||||
tokens | ||||||
.filter((token) => token.kind === 'option') | ||||||
.forEach((token) => { | ||||||
if (token.name.startsWith('no-')) { | ||||||
// Store foo:false for --no-foo | ||||||
const positiveName = token.name.slice(3); | ||||||
values[positiveName] = false; | ||||||
delete values[token.name]; | ||||||
} else { | ||||||
// Resave value so last one wins if both --foo and --no-foo. | ||||||
values[token.name] = token.value ?? true; | ||||||
} | ||||||
}); | ||||||
|
||||||
const color = values.color; | ||||||
const logfile = values.logfile ?? 'default.log'; | ||||||
|
||||||
console.log({ logfile, color }); | ||||||
``` | ||||||
|
||||||
```cjs | ||||||
shadowspawn marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
const { parseArgs } = require('node:util'); | ||||||
|
||||||
const options = { | ||||||
'color': { type: 'boolean' }, | ||||||
'no-color': { type: 'boolean' }, | ||||||
'logfile': { type: 'string' }, | ||||||
'no-logfile': { type: 'boolean' }, | ||||||
}; | ||||||
const { values, tokens } = parseArgs({ options, tokens: true }); | ||||||
|
||||||
// Reprocess the option tokens and overwrite the returned values. | ||||||
tokens | ||||||
.filter((token) => token.kind === 'option') | ||||||
.forEach((token) => { | ||||||
if (token.name.startsWith('no-')) { | ||||||
// Store foo:false for --no-foo | ||||||
const positiveName = token.name.slice(3); | ||||||
values[positiveName] = false; | ||||||
delete values[token.name]; | ||||||
} else { | ||||||
// Resave value so last one wins if both --foo and --no-foo. | ||||||
values[token.name] = token.value ?? true; | ||||||
} | ||||||
}); | ||||||
|
||||||
const color = values.color; | ||||||
const logfile = values.logfile ?? 'default.log'; | ||||||
|
||||||
console.log({ logfile, color }); | ||||||
``` | ||||||
|
||||||
Example usage showing negated options, and when an option is used | ||||||
multiple ways then last one wins. | ||||||
|
||||||
```console | ||||||
$ node negate.js | ||||||
{ logfile: 'default.log', color: undefined } | ||||||
$ node negate.js --no-logfile --no-color | ||||||
{ logfile: false, color: false } | ||||||
$ node negate.js --logfile=test.log --color | ||||||
{ logfile: 'test.log', color: true } | ||||||
$ node negate.js --no-logfile --logfile=test.log --color --no-color | ||||||
{ logfile: 'test.log', color: false } | ||||||
``` | ||||||
|
||||||
----- | ||||||
|
||||||
<!-- omit in toc --> | ||||||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
'use strict'; | ||
|
||
// This example is used in the documentation. | ||
|
||
// How might I add my own support for --no-foo? | ||
|
||
// 1. const { parseArgs } = require('node:util'); // from node | ||
// 2. const { parseArgs } = require('@pkgjs/parseargs'); // from package | ||
const { parseArgs } = require('..'); // in repo | ||
|
||
const options = { | ||
'color': { type: 'boolean' }, | ||
'no-color': { type: 'boolean' }, | ||
'logfile': { type: 'string' }, | ||
'no-logfile': { type: 'boolean' }, | ||
}; | ||
const { values, tokens } = parseArgs({ options, tokens: true }); | ||
|
||
// Reprocess the option tokens and overwrite the returned values. | ||
tokens | ||
.filter((token) => token.kind === 'option') | ||
.forEach((token) => { | ||
shadowspawn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (token.name.startsWith('no-')) { | ||
// Store foo:false for --no-foo | ||
const positiveName = token.name.slice(3); | ||
values[positiveName] = false; | ||
delete values[token.name]; | ||
} else { | ||
// Resave value so last one wins if both --foo and --no-foo. | ||
values[token.name] = token.value ?? true; | ||
} | ||
}); | ||
|
||
const color = values.color; | ||
const logfile = values.logfile ?? 'default.log'; | ||
|
||
console.log({ logfile, color }); | ||
|
||
// Try the following: | ||
// node negate.js | ||
// node negate.js --no-logfile --no-color | ||
// negate.js --logfile=test.log --color | ||
// node negate.js --no-logfile --logfile=test.log --color --no-color |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To improve the readability I would explain that we mean the
kind=option
tokenThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rather than quotes, which i find confusing, how about linking the term to the appropriate section?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that works too.
I use the same syntax in the
kind
description to be homogeneousThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The compact POJO description is a bit subtle to read. How about an expanded version with all the properties listed?
Proposed expanded
A returned token has two properties which are always defined,
and some other properties which vary depending on the
kind
:kind
{string} One of 'option', 'positional', or 'option-terminator'.index
{number} Index of element inargs
containing token. So thesource argument for a token is
args[token.index]
.An option token has additional parse details
for an option detected in the input
args
:kind
='option'
index
{number} Index of element inargs
containing token.name
{string} Long name of option.rawName
{string} How option used in args, like-f
of--foo
.value
{string | undefined} Option value specified in args.Undefined for boolean options.
inlineValue
{boolean | undefined} Whether option value specified inline,like
--foo=bar
.A positional token has just one additional property with the positional value:
kind
='positional'
index
{number} Index of element inargs
containing token.value
{string} The value of the positional argument in args (i.e.args[index]
).An option-terminator token has only the base properties:
kind
='option-terminator'
index
{number} Index of element inargs
containing token.Old compact
The returned tokens have properties describing:
kind
{string} One of 'option', 'positional', or 'option-terminator'.index
{number} Index of element inargs
containing token. So thesource argument for a token is
args[token.index]
.name
{string} Long name of option.rawName
{string} How option used in args, like-f
of--foo
.value
{string | undefined} Option value specified in args.Undefined for boolean options.
inlineValue
{boolean | undefined} Whether option value specified inline,like
--foo=bar
.value
{string} The value of the positional argument in args (i.e.args[index]
).(Also asked in: nodejs/node#43459 (comment))