Skip to content

Commit

Permalink
util: support 'option.required' in parseArgs
Browse files Browse the repository at this point in the history
Fixes: #44564
  • Loading branch information
himself65 committed Sep 7, 2022
1 parent 1f54fc2 commit a8c6229
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 2 deletions.
11 changes: 11 additions & 0 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -2464,6 +2464,17 @@ added:
When `strict` set to `true`, thrown by [`util.parseArgs()`][] if an argument
is not configured in `options`.

<a id="ERR_PARSE_ARGS_REQUIRED_OPTION"></a>

### `ERR_PARSE_ARGS_REQUIRED_OPTION`

<!--YAML
added: REPLACEME
-->

When `required` set to `true`, thrown by [`util.parseArgs()`][] if an option
is not provided in `args`.

<a id="ERR_PERFORMANCE_INVALID_TIMESTAMP"></a>

### `ERR_PERFORMANCE_INVALID_TIMESTAMP`
Expand Down
10 changes: 8 additions & 2 deletions doc/api/util.md
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,9 @@ added:
- v18.3.0
- v16.17.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/44565
description: support `required` field in option
- version:
- v18.7.0
- v16.17.0
Expand All @@ -1053,6 +1056,7 @@ changes:
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.
* `required` {boolean} Whether this option is required. **Default:** `false`.
* `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`.
Expand Down Expand Up @@ -1085,7 +1089,8 @@ const options = {
short: 'f'
},
bar: {
type: 'string'
type: 'string',
required: true
}
};
const {
Expand All @@ -1105,7 +1110,8 @@ const options = {
short: 'f'
},
bar: {
type: 'string'
type: 'string',
required: true
}
};
const {
Expand Down
1 change: 1 addition & 0 deletions lib/internal/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -1519,6 +1519,7 @@ E('ERR_PARSE_ARGS_UNKNOWN_OPTION', (option, allowPositionals) => {
`'--', as in '-- ${JSONStringify(option)}` : '';
return `Unknown option '${option}'${suggestDashDash}`;
}, TypeError);
E('ERR_PARSE_ARGS_REQUIRED_OPTION', 'Required option \'%s\'.', TypeError);
E('ERR_PERFORMANCE_INVALID_TIMESTAMP',
'%d is not a valid timestamp', TypeError);
E('ERR_PERFORMANCE_MEASURE_INVALID_OPTIONS', '%s', TypeError);
Expand Down
13 changes: 13 additions & 0 deletions lib/internal/util/parse_args/parse_args.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const {
ERR_PARSE_ARGS_INVALID_OPTION_VALUE,
ERR_PARSE_ARGS_UNKNOWN_OPTION,
ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL,
ERR_PARSE_ARGS_REQUIRED_OPTION
},
} = require('internal/errors');

Expand Down Expand Up @@ -336,6 +337,18 @@ const parseArgs = (config = kEmptyObject) => {
}
});

// Phase 3: check if some options is required
ArrayPrototypeForEach(
ObjectEntries(options),
({ 0: longOption, 1: optionConfig }) => {
if (optionConfig.required) {
if (result.values[longOption] == null) {
throw new ERR_PARSE_ARGS_REQUIRED_OPTION(longOption);
}
}
}
);

return result;
};

Expand Down
75 changes: 75 additions & 0 deletions test/parallel/test-parse-args.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -823,3 +823,78 @@ test('tokens: strict:false with -- --', () => {
const { tokens } = parseArgs({ strict: false, args, tokens: true });
assert.deepStrictEqual(tokens, expectedTokens);
});

test('strict: required option', () => {
const args = ['--foo']
parseArgs({
args,
options: {
foo: {
type: 'boolean',
required: true
}
}
})
})

test('required option', () => {
const args = ['--foo', '--goo']
parseArgs({
strict: false,
args,
options: {
foo: {
type: 'boolean',
required: true
}
}
})
})

test('strict: false required option fail', () => {
const args = []
assert.throws(() => {
parseArgs({
strict: false,
args,
options: {
foo: {
type: 'boolean',
required: true
}
}
}, {
code: 'ERR_PARSE_ARGS_REQUIRED_OPTION'
})
})
})

test('strict: no input but has required option', () => {
const args = []
assert.throws(() => {
parseArgs({
args,
options: {
foo: {
type: 'boolean',
required: true
}
}
}, {
code: 'ERR_PARSE_ARGS_REQUIRED_OPTION'
})
})
})

test('strict: no input and no required option', () => {
const args = []
parseArgs({
args,
options: {
foo: {
type: 'boolean',
required: false
}
}
})
})

0 comments on commit a8c6229

Please sign in to comment.