Skip to content
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 69 commits into from
Jul 20, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
52c3a2e
Proof of concept
shadowspawn May 14, 2022
5ec71b1
Return originalArgs and indices
shadowspawn May 14, 2022
f711187
Change short in AST to boolean
shadowspawn May 24, 2022
cce90bb
Store inlineValue rather than valueIndex
shadowspawn May 24, 2022
e4bc5fe
Rename symbol property in AST to kind
shadowspawn May 24, 2022
87624a1
Rename returned ast property to parseElements
shadowspawn May 24, 2022
2925639
Refactor to use parseElements to test for errors and store values
shadowspawn May 29, 2022
412287e
Merge remote-tracking branch 'upstream/main' into feature/ast
shadowspawn May 29, 2022
1897dac
Build positionals from parseElements
shadowspawn May 29, 2022
45d12e7
Replace .push with primordial
shadowspawn May 29, 2022
1d19fb2
forEach replaced with primordial
shadowspawn May 29, 2022
9d1267d
Swap isShort for optionUsed
shadowspawn May 30, 2022
c9c4d4c
Rework checkOptionLikeValue
shadowspawn May 30, 2022
ed4168d
Consistent property order and renames
shadowspawn May 30, 2022
16a9f51
Comment out new returned property until naming and tests ready
shadowspawn May 30, 2022
915e6c3
Refactor tokenize into own function
shadowspawn May 31, 2022
641197e
First test pass, get tests working by ignoring tokens in results
shadowspawn May 31, 2022
18470bd
Less magical check now checking kind
shadowspawn May 31, 2022
38d1d6c
Add some token tests
shadowspawn May 31, 2022
cfbd436
Fix index after space-separated option value
shadowspawn May 31, 2022
397dcf1
Make tokens opt-in
shadowspawn Jun 1, 2022
938bdb0
Simplify more tests with opt-in details
shadowspawn Jun 1, 2022
c5da345
Rename token.optionName to name, and restore longOption/shortOption …
shadowspawn Jun 1, 2022
4e1ec2d
Add example
shadowspawn Jun 1, 2022
bd3f574
Add side-note
shadowspawn Jun 2, 2022
b703ee8
Add example of #52, limit long syntax
shadowspawn Jun 2, 2022
82339f9
Add ordered example
shadowspawn Jun 2, 2022
fab1dc4
Add example for no repeated options
shadowspawn Jun 2, 2022
99165c9
Comment wording
shadowspawn Jun 2, 2022
9bd039d
Switch from details to tokens for configuration
shadowspawn Jun 3, 2022
6a3f637
Simplify example by removing "library" support
shadowspawn Jun 3, 2022
4cfbc29
Remove comments on how well-advised the examples goals are
shadowspawn Jun 3, 2022
9a9a740
Switch from optionUser to rawName
shadowspawn Jun 3, 2022
7cd685c
Merge remote-tracking branch 'upstream/main' into feature/ast
shadowspawn Jun 3, 2022
6f93632
Use modern syntax
shadowspawn Jun 4, 2022
642d8c0
Validate new input property
shadowspawn Jun 4, 2022
e70609a
Move strict check outside check routines
shadowspawn Jun 4, 2022
fdaa553
Tidy object setup
shadowspawn Jun 4, 2022
c293307
Simplify test
shadowspawn Jun 4, 2022
041459d
Indentation and match filename
shadowspawn Jun 4, 2022
7aafaf6
Rework with strict:true
shadowspawn Jun 4, 2022
1b6e585
Longer but simpler
shadowspawn Jun 4, 2022
20eacb0
Add textual description, and some fixes
shadowspawn Jun 4, 2022
81239d6
Add example output for tokens
shadowspawn Jun 4, 2022
84dde8e
Add example used in new documentation
shadowspawn Jun 4, 2022
40a6201
Refactor documentation
shadowspawn Jun 4, 2022
07fbb91
Update .editorconfig from Node.js
shadowspawn Jun 4, 2022
35c16f1
rework token property descriptions
shadowspawn Jun 4, 2022
038480a
Minor refactor of remaining arg processing
shadowspawn Jun 7, 2022
d4f96cc
Replace switch with if/else
shadowspawn Jun 14, 2022
122727e
Remove side-affect, per feedback
shadowspawn Jun 15, 2022
5b3518d
Add tricky case of short option group to token expansion
shadowspawn Jun 15, 2022
c8c2f84
Rework description of token.index after token example.
shadowspawn Jun 15, 2022
2b119b0
Be less clever with script naming in example
shadowspawn Jun 15, 2022
1b3c076
Remove superfluous colons in docs
shadowspawn Jun 17, 2022
e9e30a7
Upstream lint
shadowspawn Jun 17, 2022
46ab295
Merge branch 'main' into feature/ast
shadowspawn Jun 23, 2022
01baee5
Merge branch 'main' into feature/ast
shadowspawn Jun 24, 2022
46903af
Improve description
shadowspawn Jul 4, 2022
9b74c05
Merge branch 'feature/ast' of github.com:shadowspawn/parseargs into f…
shadowspawn Jul 4, 2022
89e4532
Use negate as example for tokens. Rework description a little.
shadowspawn Jul 4, 2022
3914949
Lint and feedback
shadowspawn Jul 4, 2022
d8126d1
Expand small token tests and remove uber tests
shadowspawn Jul 5, 2022
87bab7b
Use kEmptyObject per upstream suggestion. Move node lookalikes to int…
shadowspawn Jul 5, 2022
02ea585
Reworks tokens documentation for config
shadowspawn Jul 5, 2022
56fe4ca
Add version changes to YAML.
shadowspawn Jul 5, 2022
722f05e
Update README with upstream changes
shadowspawn Jul 5, 2022
ae9eca4
Update negate example calls to match documentation
shadowspawn Jul 16, 2022
f9dcc4f
Remove extra examples
shadowspawn Jul 16, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
122 changes: 76 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,13 @@ console.log(values, positionals);
```

Detailed parse information is available for adding custom behaviours by
specifying `tokens: true` in the configuration. The returned tokens have
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.
* `index` { number } Index of element in `args` containing token. So the source argument for a token is `args[token.index]`.
* option tokens
Copy link
Collaborator

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 token

Suggested change
* option tokens
* 'option' tokens

Copy link
Member

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?

Copy link
Collaborator

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 homogeneous

Copy link
Collaborator Author

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 in args containing token. So the
    source 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 in args 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 in args 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 in args containing token.

Old compact

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
    • value {string} The value of the positional argument in args (i.e. args[index]).
  • option-terminator token

(Also asked in: nodejs/node#43459 (comment))

* `name` { string } Long name of option.
* `rawName` { string } How option used in args, like `-f` of `--foo`.
Expand All @@ -103,65 +104,94 @@ Undefined for boolean options.
* `inlineValue` { boolean | undefined } Whether option value specified inline,
like `--foo=bar`.
* positional tokens
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* positional tokens
* 'positional' tokens

* `value` { string } the value of the positional argument in args (i.e. `args[index]`).
* `value` { string } The value of the positional argument in args (i.e. `args[index]`).
* option-terminator token
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* option-terminator token
* 'option-terminator' token


For example, assuming the following script which uses
automatic detection of options and no error checking:
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';
console.log(parseArgs({ strict: false, tokens: true }));

const options = {
['color']: { type: 'boolean' },
['no-color']: { type: 'boolean' },
['logfile']: { type: 'string' },
['no-logfile']: { type: 'boolean' },
shadowspawn marked this conversation as resolved.
Show resolved Hide resolved
};
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');
console.log(parseArgs({ strict: false, tokens: true }));

const options = {
['color']: { type: 'boolean' },
['no-color']: { type: 'boolean' },
['logfile']: { type: 'string' },
['no-logfile']: { type: 'boolean' },
shadowspawn marked this conversation as resolved.
Show resolved Hide resolved
};
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 });
```

This call shows the three kinds of token and their properties:
Example usage showing negated options, and when option use multiple times then last one wins.

```console
$ node tokens.cjs -xy --foo=BAR -- file.txt
{
values: [Object: null prototype] { d: true, foo: 'BAR' },
positionals: [ 'file.txt' ],
tokens: [
{
kind: 'option',
name: 'x',
rawName: '-x',
index: 0,
value: undefined,
inlineValue: undefined
},
{
kind: 'option',
name: 'y',
rawName: '-y',
index: 0,
value: undefined,
inlineValue: undefined
},
{
kind: 'option',
name: 'foo',
rawName: '--foo',
index: 1,
value: 'BAR',
inlineValue: true
},
{ kind: 'option-terminator', index: 2 },
{ kind: 'positional', index: 3, value: 'file.txt' }
]
}
$ 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 }
```

The source argument for a token is `args[token.index]`.
Short option groups like `-xy` expand to a token for each option.
The `x` and `y` tokens above have the same index, since
they come from the same argument.

`util.parseArgs()` is experimental and behavior may change. Join the
conversation in [pkgjs/parseargs][] to contribute to the design.

Expand Down
6 changes: 3 additions & 3 deletions examples/negate.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
const { parseArgs } = require('..'); // in repo

const options = {
['color']: { type: 'string' },
['color']: { type: 'boolean' },
['no-color']: { type: 'boolean' },
['logfile']: { type: 'string' },
['no-logfile']: { type: 'boolean' },
shadowspawn marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -36,6 +36,6 @@ console.log({ logfile, color });

// Try the following:
// node negate.js
// node negate.js --logfile=test.log --color=red
// node negate.js --logfile=test.log --color
// node negate.js --no-logfile --no-color
// node negate.js --no-logfile --logfile=test.log --color=red --no-color
// node negate.js --no-logfile --logfile=test.log --color --no-color