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 strict mode to parser #74

Merged
merged 40 commits into from
Apr 13, 2022
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
697493f
feat: Restructure options API
aaronccasanova Feb 9, 2022
9e42db0
chore: Remove debug comments
aaronccasanova Feb 9, 2022
36ef68c
chore: Remove debug comments
aaronccasanova Feb 9, 2022
67c0b03
chore: Alias args to argv to introduce less changes
aaronccasanova Feb 9, 2022
082bec5
feat: Replace option with
Feb 12, 2022
0909008
docs: Update README to reflect updated implementation
Feb 12, 2022
042480b
chore: Revert args options to argv
Feb 12, 2022
10df671
feat: Add strict mode to parser
aaronccasanova Feb 19, 2022
e44c46c
docs: Update README from PR feedback
aaronccasanova Feb 24, 2022
4063751
Merge branch 'main' of https://github.com/aaronccasanova/parseargs in…
aaronccasanova Feb 26, 2022
a50e940
chore: Reduce changes
aaronccasanova Feb 26, 2022
062bdc9
chore: Tidy up naming conventions
aaronccasanova Feb 26, 2022
28ae7b8
Merge branch 'main' into feat/restructure-options-api
bcoe Feb 27, 2022
e6fea72
Merge branch 'feat/restructure-options-api' of https://github.com/aar…
aaronccasanova Feb 27, 2022
ef06099
fix: Guard against prototype member access
aaronccasanova Feb 27, 2022
c228484
Merge branch 'feat/restructure-options-api' of https://github.com/aar…
aaronccasanova Mar 3, 2022
dd4f718
feat: Add strict mode type validation
aaronccasanova Mar 3, 2022
26df81c
chore: Remove custom unknown option error
aaronccasanova Mar 3, 2022
a04d11c
chore: Remove unknown option error from test
aaronccasanova Mar 3, 2022
ca05b43
fix: Update value validation to check against undefined
aaronccasanova Mar 3, 2022
a1c3544
Merge branch 'main' of https://github.com/aaronccasanova/parseargs in…
aaronccasanova Apr 9, 2022
fa0775b
fix: Update error conditionals to check for null or undefined values
aaronccasanova Apr 9, 2022
5b89108
Merge branch 'main' of https://github.com/aaronccasanova/parseargs in…
aaronccasanova Apr 10, 2022
1d412da
feat: Add custom error classes for unknown and invalid options
aaronccasanova Apr 10, 2022
a16fc7b
Adjust unknown option message to be agnostic of long or short options
aaronccasanova Apr 10, 2022
c4271a0
feat: Pass shortOption to storeOption util for better error messages
aaronccasanova Apr 10, 2022
b6107bb
Merge branch 'main' of https://github.com/aaronccasanova/parseargs in…
aaronccasanova Apr 10, 2022
948ca9e
fix(WIP): Update checks to use the ERR_INVALID_ARG_VALUE class
aaronccasanova Apr 10, 2022
a2ea48d
Merge branch 'main' of https://github.com/aaronccasanova/parseargs in…
aaronccasanova Apr 10, 2022
38575cd
Merge branch 'main' of https://github.com/aaronccasanova/parseargs in…
aaronccasanova Apr 11, 2022
99355dd
fix: Revert back to custom error classes and improve error messages
aaronccasanova Apr 12, 2022
e66a8b3
WIP: Default parseArgs to strict:true and update failing tests to str…
aaronccasanova Apr 12, 2022
a4fc92a
Update README to reflect strict:true behavior
aaronccasanova Apr 12, 2022
12fd0e6
fix: Improve robustness of the short option error message
aaronccasanova Apr 12, 2022
870cffe
fix: Improve error message for type:string options missing an argument
aaronccasanova Apr 12, 2022
694c75d
fix: Namespace unique parseArgs error classes
aaronccasanova Apr 12, 2022
9b76d55
docs: Update README examples
aaronccasanova Apr 12, 2022
fcb8c8d
Merge branch 'main' of https://github.com/aaronccasanova/parseargs in…
aaronccasanova Apr 12, 2022
e56246c
docs: Update remaing examples for consistency
aaronccasanova Apr 12, 2022
b4e0daf
Update index.js
aaronccasanova Apr 13, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 20 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ process.mainArgs = process.argv.slice(process._exec ? 1 : 2)
* `type` {'string'|'boolean'} (Required) Type of known option
* `multiple` {boolean} (Optional) If true, when appearing one or more times in `args`, results are collected in an `Array`
* `short` {string} (Optional) A single character alias for an option; When appearing one or more times in `args`; Respects the `multiple` configuration
* `strict` {Boolean} (Optional) A `Boolean` on wheather or not to throw an error when unknown args are encountered
* `strict` {Boolean} (Optional) A `Boolean` for whether or not to throw an error when unknown options are encountered, `type:'string'` options are missing an options-argument, or `type:'boolean'` options are passed an options-argument; defaults to `true`
* Returns: {Object} An object having properties:
* `values` {Object}, key:value for each option found. Value is a string for string options, or `true` for boolean options, or an array (of strings or booleans) for options configured as `multiple:true`.
* `positionals` {string[]}, containing [Positionals][]
Expand All @@ -98,59 +98,59 @@ const { parseArgs } = require('@pkgjs/parseargs');
```

```js
// unconfigured
const { parseArgs } = require('@pkgjs/parseargs');
const args = ['-f', '--foo=a', '--bar', 'b'];
const options = {};
const { values, positionals } = parseArgs({ args, options });
// values = { f: true, foo: 'a', bar: true }
// positionals = ['b']
```

```js
const { parseArgs } = require('@pkgjs/parseargs');
// type:string
const args = ['-f', '--foo=a', '--bar', 'b'];
// specify the options that may be used
const options = {
bar: {
type: 'string',
},
foo: { type: 'string'},
bar: { type: 'boolean' },
};
const args = ['--foo=a', '--bar'];
const { values, positionals } = parseArgs({ args, options });
// values = { f: true, foo: 'a', bar: 'b' }
// values = { foo: 'a', bar: true }
// positionals = []
```

```js
const { parseArgs } = require('@pkgjs/parseargs');
// type:string & multiple
const args = ['-f', '--foo=a', '--foo', 'b'];
const options = {
foo: {
type: 'string',
multiple: true,
},
};
const args = ['--foo=a', '--foo', 'b'];
const { values, positionals } = parseArgs({ args, options });
// values = { f: true, foo: [ 'a', 'b' ] }
// values = { foo: [ 'a', 'b' ] }
// positionals = []
```

```js
const { parseArgs } = require('@pkgjs/parseargs');
// shorts
const args = ['-f', 'b'];
const options = {
foo: {
short: 'f',
type: 'boolean'
},
};
const args = ['-f', 'b'];
const { values, positionals } = parseArgs({ args, options });
// values = { foo: true }
// positionals = ['b']
```

```js
const { parseArgs } = require('@pkgjs/parseargs');
// unconfigured
const options = {};
const args = ['-f', '--foo=a', '--bar', 'b'];
const { values, positionals } = parseArgs({ strict: false, args, options });
// values = { f: true, foo: 'a', bar: true }
// positionals = ['b']
```


### F.A.Qs

- Is `cmd --foo=bar baz` the same as `cmd baz --foo=bar`?
Expand Down
18 changes: 17 additions & 1 deletion errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,25 @@ class ERR_INVALID_ARG_VALUE extends TypeError {
}
}

class ERR_PARSE_ARGS_INVALID_OPTION_VALUE extends Error {
constructor(message) {
super(message);
this.code = 'ERR_PARSE_ARGS_INVALID_OPTION_VALUE';
}
}

class ERR_PARSE_ARGS_UNKNOWN_OPTION extends Error {
constructor(option) {
super(`Unknown option '${option}'`);
this.code = 'ERR_PARSE_ARGS_UNKNOWN_OPTION';
}
}

module.exports = {
codes: {
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE
ERR_INVALID_ARG_VALUE,
ERR_PARSE_ARGS_INVALID_OPTION_VALUE,
ERR_PARSE_ARGS_UNKNOWN_OPTION,
}
};
60 changes: 52 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ const {
const {
codes: {
ERR_INVALID_ARG_VALUE,
ERR_PARSE_ARGS_INVALID_OPTION_VALUE,
ERR_PARSE_ARGS_UNKNOWN_OPTION,
},
} = require('./errors');

Expand Down Expand Up @@ -73,16 +75,42 @@ function getMainArgs() {
}

const protoKey = '__proto__';
function storeOptionValue(options, longOption, value, result) {
const optionConfig = options[longOption] || {};

function storeOption({
strict,
options,
result,
longOption,
shortOption,
optionValue,
}) {
const hasOptionConfig = ObjectHasOwn(options, longOption);

aaronccasanova marked this conversation as resolved.
Show resolved Hide resolved
const optionConfig = hasOptionConfig ? options[longOption] : {};
Copy link
Collaborator

Choose a reason for hiding this comment

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

I might simplify this to options[longOption] ?? {}.


if (strict) {
if (!hasOptionConfig) {
throw new ERR_PARSE_ARGS_UNKNOWN_OPTION(shortOption == null ? `--${longOption}` : `-${shortOption}`);
bcoe marked this conversation as resolved.
Show resolved Hide resolved
}

const shortOptionErr = ObjectHasOwn(optionConfig, 'short') ? `-${optionConfig.short}, ` : '';

if (options[longOption].type === 'string' && optionValue == null) {
throw new ERR_PARSE_ARGS_INVALID_OPTION_VALUE(`Option '${shortOptionErr}--${longOption} <value>' argument missing`);
}

if (options[longOption].type === 'boolean' && optionValue != null) {
throw new ERR_PARSE_ARGS_INVALID_OPTION_VALUE(`Option '${shortOptionErr}--${longOption}' does not take an argument`);
}
}

if (longOption === protoKey) {
return;
}

// Values
const usedAsFlag = value === undefined;
const newValue = usedAsFlag ? true : value;
const usedAsFlag = optionValue === undefined;
const newValue = usedAsFlag ? true : optionValue;
if (optionConfig.multiple) {
// Always store value in array, including for flags.
// result.values[longOption] starts out not present,
Expand All @@ -99,9 +127,11 @@ function storeOptionValue(options, longOption, value, result) {

const parseArgs = ({
args = getMainArgs(),
strict = true,
options = {}
} = {}) => {
validateArray(args, 'args');
validateBoolean(strict, 'strict');
validateObject(options, 'options');
ArrayPrototypeForEach(
ObjectEntries(options),
Expand Down Expand Up @@ -158,7 +188,14 @@ const parseArgs = ({
// e.g. '-f', 'bar'
optionValue = ArrayPrototypeShift(remainingArgs);
}
storeOptionValue(options, longOption, optionValue, result);
storeOption({
strict,
options,
result,
longOption,
shortOption,
optionValue,
});
continue;
}

Expand Down Expand Up @@ -188,7 +225,14 @@ const parseArgs = ({
const shortOption = StringPrototypeCharAt(arg, 1);
const longOption = findLongOptionForShort(shortOption, options);
const optionValue = StringPrototypeSlice(arg, 2);
storeOptionValue(options, longOption, optionValue, result);
storeOption({
strict,
options,
result,
longOption,
shortOption,
optionValue,
});
continue;
}

Expand All @@ -200,7 +244,7 @@ const parseArgs = ({
// e.g. '--foo', 'bar'
optionValue = ArrayPrototypeShift(remainingArgs);
}
storeOptionValue(options, longOption, optionValue, result);
storeOption({ strict, options, result, longOption, optionValue });
continue;
}

Expand All @@ -209,7 +253,7 @@ const parseArgs = ({
const index = StringPrototypeIndexOf(arg, '=');
const longOption = StringPrototypeSlice(arg, 2, index);
const optionValue = StringPrototypeSlice(arg, index + 1);
storeOptionValue(options, longOption, optionValue, result);
storeOption({ strict, options, result, longOption, optionValue });
continue;
}

Expand Down
Loading