-
-
Notifications
You must be signed in to change notification settings - Fork 34k
util: add util.parseArgs() #35015
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
util: add util.parseArgs() #35015
Changes from 6 commits
1ae63aa
b180e2a
a0221e3
971039c
84b34cc
0034079
d349208
1d5cd77
d64e82a
4dbdca3
97f7e88
b2be252
694f9c6
3a7f3b1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| 'use strict'; | ||
|
|
||
| const { | ||
| ArrayIsArray, | ||
| ArrayPrototypePush, | ||
| ArrayPrototypeSlice, | ||
| SafeSet, | ||
| StringPrototypeReplace, | ||
| StringPrototypeSplit, | ||
| StringPrototypeStartsWith, | ||
| } = primordials; | ||
| const { ERR_INVALID_ARG_TYPE } = require('internal/errors').codes; | ||
|
|
||
| /** | ||
| * Returns an Object representation of command-line arguments. | ||
| * | ||
| * Default behavior: | ||
| * - All arguments are considered "boolean flags"; in the `options` property of | ||
| * returned object, the key is the argument (if present), and the value will | ||
| * be `true`. | ||
| * - Uses `process.argv.slice(2)`, but can accept an explicit array of strings. | ||
| * - Argument(s) specified in `opts.optionsWithValue` will have `string` values | ||
| * instead of `true`; the subsequent item in the `argv` array will be consumed | ||
| * if a `=` is not used | ||
| * - "Bare" arguments are those which do not begin with a `-` or `--` and those | ||
| * after a bare `--`; these will be returned as items of the `positionals` | ||
| * array | ||
| * - The `positionals` array will always be present, even if empty. | ||
| * - The `options` Object will always be present, even if empty. | ||
| * @param {string[]} [argv=process.argv.slice(2)] - Array of script arguments as | ||
| * strings | ||
| * @param {Object} [options] - Options | ||
| * @param {string[]|string} [opts.optionsWithValue] - This argument (or | ||
| * arguments) expect a value | ||
| * @param {string[]|string} [opts.multiOptions] - This argument (or arguments) | ||
| * can be specified multiple times and its values will be concatenated into an | ||
| * array | ||
| * @returns {{options: Object, positionals: string[]}} Parsed arguments | ||
| * @example | ||
| * parseArgs(['--foo', '--bar']) | ||
| * // {options: { foo: true, bar: true }, positionals: []} | ||
| * parseArgs(['--foo', '-b']) | ||
| * // {options: { foo: true, b: true }, positionals: []} | ||
| * parseArgs(['---foo']) | ||
| * // {options: { foo: true }, positionals: []} | ||
| * parseArgs(['--foo=bar']) | ||
| * // {options: { foo: true }, positionals: []} | ||
| * parseArgs([--foo', 'bar']) | ||
| * // {options: {foo: true}, positionals: ['bar']} | ||
| * parseArgs(['--foo', 'bar'], {optionsWithValue: 'foo'}) | ||
| * // {options: {foo: 'bar'}, positionals: []} | ||
| * parseArgs(['foo']) | ||
| * // {options: {}, positionals: ['foo']} | ||
| * parseArgs(['--foo', '--', '--bar']) | ||
| * // {options: {foo: true}, positionals: ['--bar']} | ||
| * parseArgs(['--foo=bar', '--foo=baz']) | ||
| * // {options: {foo: true}, positionals: []} | ||
| * parseArgs(['--foo=bar', '--foo=baz'], {optionsWithValue: 'foo'}) | ||
| * // {options: {foo: 'baz'}, positionals: []} | ||
| * parseArgs(['--foo=bar', '--foo=baz'], { | ||
| * optionsWithValue: 'foo', multiOptions: 'foo' | ||
| * }) // {options: {foo: ['bar', 'baz']}, positionals: []} | ||
| * parseArgs(['--foo', '--foo']) | ||
| * // {options: {foo: true}, positionals: []} | ||
| * parseArgs(['--foo', '--foo'], {multiOptions: ['foo']}) | ||
| * // {options: {foo: [true, true]}, positionals: []} | ||
| */ | ||
| const parseArgs = ( | ||
| argv = ArrayPrototypeSlice(process.argv, 2), | ||
| options = { optionsWithValue: [] } | ||
| ) => { | ||
| if (!ArrayIsArray(argv)) { | ||
| options = argv; | ||
| argv = ArrayPrototypeSlice(process.argv, 2); | ||
|
Member
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. Defaulting to $ node -p 'require('util').parseArgs()' foo bar
internal/validators.js:122
throw new ERR_INVALID_ARG_TYPE(name, 'string', value);
^
TypeError [ERR_INVALID_ARG_TYPE]: The "id" argument must be of type string. Received an instance of Object
at new NodeError (internal/errors.js:253:15)
at validateString (internal/validators.js:122:11)
at Module.require (internal/modules/cjs/loader.js:972:3)
at require (internal/modules/cjs/helpers.js:88:18)
at [eval]:1:1
at Script.runInThisContext (vm.js:132:18)
at Object.runInThisContext (vm.js:309:38)
at internal/process/execution.js:77:19
at [eval]-wrapper:6:22
at evalScript (internal/process/execution.js:76:60) {
code: 'ERR_INVALID_ARG_TYPE'
}In the past I've done something like IMO |
||
| } | ||
| if (typeof options !== 'object' || options === null) { | ||
| throw new ERR_INVALID_ARG_TYPE( | ||
| 'options', | ||
| 'object', | ||
| options); | ||
| } | ||
| if (typeof options.optionsWithValue === 'string') { | ||
| options.optionsWithValue = [options.optionsWithValue]; | ||
| } | ||
| if (typeof options.multiOptions === 'string') { | ||
| options.multiOptions = [options.multiOptions]; | ||
| } | ||
|
Comment on lines
+88
to
+93
Member
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. my request for changes still stands on these |
||
| const optionsWithValue = new SafeSet(options.optionsWithValue || []); | ||
| const multiOptions = new SafeSet(options.multiOptions || []); | ||
| const result = { positionals: [], options: {} }; | ||
|
|
||
| let pos = 0; | ||
| while (true) { | ||
| const arg = argv[pos]; | ||
| if (arg === undefined) { | ||
boneskull marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return result; | ||
| } | ||
boneskull marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if (StringPrototypeStartsWith(arg, '-')) { | ||
| // Everything after a bare '--' is considered a positional argument | ||
| // and is returned verbatim | ||
| if (arg === '--') { | ||
| ArrayPrototypePush( | ||
| result.positionals, ...ArrayPrototypeSlice(argv, ++pos) | ||
| ); | ||
| return result; | ||
| } | ||
| // Any number of leading dashes are allowed | ||
| const argParts = StringPrototypeSplit(StringPrototypeReplace(arg, /^-+/, ''), '='); | ||
| const optionName = argParts[0]; | ||
| let optionValue = argParts[1]; | ||
|
|
||
| // Consume the next item in the array if `=` was not used | ||
| // and the next item is not itself a flag or option | ||
| if (optionsWithValue.has(optionName)) { | ||
| if (optionValue === undefined) { | ||
| optionValue = StringPrototypeStartsWith(argv[pos + 1], '-') || | ||
boneskull marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| argv[++pos]; | ||
| } | ||
| } else { | ||
| optionValue = true; | ||
| } | ||
|
|
||
| if (multiOptions.has(optionName)) { | ||
| // Consume the next item in the array if `=` was not used | ||
| // and the next item is not itself a flag or option | ||
| if (result.options[optionName] === undefined) { | ||
| result.options[optionName] = [optionValue]; | ||
| } else { | ||
| ArrayPrototypePush(result.options[optionName], optionValue); | ||
| } | ||
| } else { | ||
| result.options[optionName] = optionValue; | ||
| } | ||
| } else { | ||
| ArrayPrototypePush(result.positionals, arg); | ||
| } | ||
| pos++; | ||
| } | ||
| }; | ||
|
|
||
| module.exports = { | ||
| parseArgs | ||
| }; | ||
Uh oh!
There was an error while loading. Please reload this page.