diff --git a/README.md b/README.md index e62b314..088297f 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ It is exceedingly difficult to provide an API which would both be friendly to th - [🙌 Contributing](#-contributing) - [💡 `process.mainArgs` Proposal](#-processmainargs-proposal) - [Implementation:](#implementation) -- [💡 `util.parseArgs(argv)` Proposal](#-utilparseargsargv-proposal) +- [💡 `util.parseArgs([config])` Proposal](#-utilparseargsconfig-proposal) - [📃 Examples](#-examples) - [F.A.Qs](#faqs) @@ -74,19 +74,16 @@ process.mainArgs = process.argv.slice(process._exec ? 1 : 2) ---- -## 💡 `util.parseArgs([argv][, options])` Proposal +## 💡 `util.parseArgs([config])` Proposal -* `argv` {string[]} (Optional) Array of argument strings; defaults - to [`process.mainArgs`](process_argv) -* `options` {Object} (Optional) The `options` parameter is an +* `config` {Object} (Optional) The `config` parameter is an object supporting the following properties: - * `withValue` {string[]} (Optional) An `Array` of argument - strings which expect a value to be defined in `argv` (see [Options][] - for details) - * `multiples` {string[]} (Optional) An `Array` of argument - strings which, when appearing multiple times in `argv`, will be concatenated -into an `Array` - * `short` {Object} (Optional) An `Object` of key, value pairs of strings which map a "short" alias to an argument; When appearing multiples times in `argv`; Respects `withValue` & `multiples` + * `args` {string[]} (Optional) Array of argument strings; defaults + to [`process.mainArgs`](process_argv) + * `options` {Object} (Optional) An object describing the known options to look for in `args`; `options` keys are the long names of the known options, and the values are objects with the following properties: + * `type` {'string'|'boolean'} (Optional) Type of known option; defaults to `'boolean'`; + * `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 * Returns: {Object} An object having properties: * `flags` {Object}, having properties and `Boolean` values corresponding to parsed options passed @@ -104,9 +101,9 @@ const { parseArgs } = require('@pkgjs/parseargs'); ```js // unconfigured const { parseArgs } = require('@pkgjs/parseargs'); -const argv = ['-f', '--foo=a', '--bar', 'b']; +const args = ['-f', '--foo=a', '--bar', 'b']; const options = {}; -const { flags, values, positionals } = parseArgs(argv, options); +const { flags, values, positionals } = parseArgs({ args, options }); // flags = { f: true, bar: true } // values = { foo: 'a' } // positionals = ['b'] @@ -115,11 +112,13 @@ const { flags, values, positionals } = parseArgs(argv, options); ```js const { parseArgs } = require('@pkgjs/parseargs'); // withValue -const argv = ['-f', '--foo=a', '--bar', 'b']; +const args = ['-f', '--foo=a', '--bar', 'b']; const options = { - withValue: ['bar'] + foo: { + type: 'string', + }, }; -const { flags, values, positionals } = parseArgs(argv, options); +const { flags, values, positionals } = parseArgs({ args, options }); // flags = { f: true } // values = { foo: 'a', bar: 'b' } // positionals = [] @@ -127,13 +126,15 @@ const { flags, values, positionals } = parseArgs(argv, options); ```js const { parseArgs } = require('@pkgjs/parseargs'); -// withValue & multiples -const argv = ['-f', '--foo=a', '--foo', 'b']; +// withValue & multiple +const args = ['-f', '--foo=a', '--foo', 'b']; const options = { - withValue: ['foo'], - multiples: ['foo'] + foo: { + type: 'string', + multiple: true, + }, }; -const { flags, values, positionals } = parseArgs(argv, options); +const { flags, values, positionals } = parseArgs({ args, options }); // flags = { f: true } // values = { foo: ['a', 'b'] } // positionals = [] @@ -142,11 +143,13 @@ const { flags, values, positionals } = parseArgs(argv, options); ```js const { parseArgs } = require('@pkgjs/parseargs'); // shorts -const argv = ['-f', 'b']; +const args = ['-f', 'b']; const options = { - short: { f: 'foo' } + foo: { + short: 'f', + }, }; -const { flags, values, positionals } = parseArgs(argv, options); +const { flags, values, positionals } = parseArgs({ args, options }); // flags = { foo: true } // values = {} // positionals = ['b'] diff --git a/index.js b/index.js index 1b5eb3b..cf73e7c 100644 --- a/index.js +++ b/index.js @@ -2,11 +2,13 @@ const { ArrayPrototypeConcat, - ArrayPrototypeIncludes, + ArrayPrototypeFind, + ArrayPrototypeForEach, ArrayPrototypeSlice, ArrayPrototypeSplice, ArrayPrototypePush, ObjectHasOwn, + ObjectEntries, StringPrototypeCharAt, StringPrototypeIncludes, StringPrototypeIndexOf, @@ -16,7 +18,10 @@ const { const { validateArray, - validateObject + validateObject, + validateString, + validateUnion, + validateBoolean, } = require('./validators'); function getMainArgs() { @@ -53,41 +58,57 @@ function getMainArgs() { return ArrayPrototypeSlice(process.argv, 2); } -function storeOptionValue(parseOptions, option, value, result) { - const multiple = parseOptions.multiples && - ArrayPrototypeIncludes(parseOptions.multiples, option); +function storeOptionValue(options, longOption, value, result) { + const optionConfig = options[longOption] || {}; // Flags - result.flags[option] = true; + result.flags[longOption] = true; // Values - if (multiple) { + if (optionConfig.multiple) { // Always store value in array, including for flags. - // result.values[option] starts out not present, + // result.values[longOption] starts out not present, // first value is added as new array [newValue], // subsequent values are pushed to existing array. const usedAsFlag = value === undefined; const newValue = usedAsFlag ? true : value; - if (result.values[option] !== undefined) - ArrayPrototypePush(result.values[option], newValue); + if (result.values[longOption] !== undefined) + ArrayPrototypePush(result.values[longOption], newValue); else - result.values[option] = [newValue]; + result.values[longOption] = [newValue]; } else { - result.values[option] = value; + result.values[longOption] = value; } } -const parseArgs = ( - argv = getMainArgs(), +const parseArgs = ({ + args = getMainArgs(), options = {} -) => { - validateArray(argv, 'argv'); +} = {}) => { + validateArray(args, 'args'); validateObject(options, 'options'); - for (const key of ['withValue', 'multiples']) { - if (ObjectHasOwn(options, key)) { - validateArray(options[key], `options.${key}`); + ArrayPrototypeForEach( + ObjectEntries(options), + ([longOption, optionConfig]) => { + validateObject(optionConfig, `options.${longOption}`); + + if (ObjectHasOwn(optionConfig, 'type')) { + validateUnion(optionConfig.type, `options.${longOption}.type`, ['string', 'boolean']); + } + + if (ObjectHasOwn(optionConfig, 'short')) { + const shortOption = optionConfig.short; + validateString(shortOption, `options.${longOption}.short`); + if (shortOption.length !== 1) { + throw new Error(`options.${longOption}.short must be a single character, got '${shortOption}'`); + } + } + + if (ObjectHasOwn(optionConfig, 'multiple')) { + validateBoolean(optionConfig.multiple, `options.${longOption}.multiple`); + } } - } + ); const result = { flags: {}, @@ -96,8 +117,8 @@ const parseArgs = ( }; let pos = 0; - while (pos < argv.length) { - let arg = argv[pos]; + while (pos < args.length) { + let arg = args[pos]; if (StringPrototypeStartsWith(arg, '-')) { if (arg === '-') { @@ -110,30 +131,36 @@ const parseArgs = ( // and is returned verbatim result.positionals = ArrayPrototypeConcat( result.positionals, - ArrayPrototypeSlice(argv, ++pos) + ArrayPrototypeSlice(args, ++pos) ); return result; } else if (StringPrototypeCharAt(arg, 1) !== '-') { // Look for shortcodes: -fXzy and expand them to -f -X -z -y: if (arg.length > 2) { for (let i = 2; i < arg.length; i++) { - const short = StringPrototypeCharAt(arg, i); + const shortOption = StringPrototypeCharAt(arg, i); // Add 'i' to 'pos' such that short options are parsed in order // of definition: - ArrayPrototypeSplice(argv, pos + (i - 1), 0, `-${short}`); + ArrayPrototypeSplice(args, pos + (i - 1), 0, `-${shortOption}`); } } arg = StringPrototypeCharAt(arg, 1); // short - if (options.short && options.short[arg]) - arg = options.short[arg]; // now long! + + const [longOption] = ArrayPrototypeFind( + ObjectEntries(options), + ([, optionConfig]) => optionConfig.short === arg + ) || []; + + arg = longOption ?? arg; + // ToDo: later code tests for `=` in arg and wrong for shorts } else { arg = StringPrototypeSlice(arg, 2); // remove leading -- } if (StringPrototypeIncludes(arg, '=')) { - // Store option=value same way independent of `withValue` as: + // Store option=value same way independent of `type: "string"` as: // - looks like a value, store as a value // - match the intention of the user // - preserve information for author to process further @@ -143,18 +170,18 @@ const parseArgs = ( StringPrototypeSlice(arg, 0, index), StringPrototypeSlice(arg, index + 1), result); - } else if (pos + 1 < argv.length && - !StringPrototypeStartsWith(argv[pos + 1], '-') + } else if (pos + 1 < args.length && + !StringPrototypeStartsWith(args[pos + 1], '-') ) { - // withValue option should also support setting values when '= + // `type: "string"` option should also support setting values when '=' // isn't used ie. both --foo=b and --foo b should work - // If withValue option is specified, take next position argument as - // value and then increment pos so that we don't re-evaluate that + // If `type: "string"` option is specified, take next position argument + // as value and then increment pos so that we don't re-evaluate that // arg, else set value as undefined ie. --foo b --bar c, after setting // b as the value for foo, evaluate --bar next and skip 'b' - const val = options.withValue && - ArrayPrototypeIncludes(options.withValue, arg) ? argv[++pos] : + const val = options[arg] && options[arg].type === 'string' ? + args[++pos] : undefined; storeOptionValue(options, arg, val, result); } else { @@ -163,7 +190,6 @@ const parseArgs = ( // save value as undefined storeOptionValue(options, arg, undefined, result); } - } else { // Arguments without a dash prefix are considered "positional" ArrayPrototypePush(result.positionals, arg); diff --git a/test/index.js b/test/index.js index 8487923..a393bf8 100644 --- a/test/index.js +++ b/test/index.js @@ -9,7 +9,7 @@ const { parseArgs } = require('../index.js'); test('when short option used as flag then stored as flag', function(t) { const passedArgs = ['-f']; const expected = { flags: { f: true }, values: { f: undefined }, positionals: [] }; - const args = parseArgs(passedArgs); + const args = parseArgs({ args: passedArgs }); t.deepEqual(args, expected); @@ -19,18 +19,18 @@ test('when short option used as flag then stored as flag', function(t) { test('when short option used as flag before positional then stored as flag and positional (and not value)', function(t) { const passedArgs = ['-f', 'bar']; const expected = { flags: { f: true }, values: { f: undefined }, positionals: [ 'bar' ] }; - const args = parseArgs(passedArgs); + const args = parseArgs({ args: passedArgs }); t.deepEqual(args, expected); t.end(); }); -test('when short option withValue used with value then stored as value', function(t) { +test('when short option `type: "string"` used with value then stored as value', function(t) { const passedArgs = ['-f', 'bar']; - const passedOptions = { withValue: ['f'] }; + const passedOptions = { f: { type: 'string' } }; const expected = { flags: { f: true }, values: { f: 'bar' }, positionals: [] }; - const args = parseArgs(passedArgs, passedOptions); + const args = parseArgs({ args: passedArgs, options: passedOptions }); t.deepEqual(args, expected); @@ -39,31 +39,31 @@ test('when short option withValue used with value then stored as value', functio test('when short option listed in short used as flag then long option stored as flag', function(t) { const passedArgs = ['-f']; - const passedOptions = { short: { f: 'foo' } }; + const passedOptions = { foo: { short: 'f' } }; const expected = { flags: { foo: true }, values: { foo: undefined }, positionals: [] }; - const args = parseArgs(passedArgs, passedOptions); + const args = parseArgs({ args: passedArgs, options: passedOptions }); t.deepEqual(args, expected); t.end(); }); -test('when short option listed in short and long listed in withValue and used with value then long option stored as value', function(t) { +test('when short option listed in short and long listed in `type: "string"` and used with value then long option stored as value', function(t) { const passedArgs = ['-f', 'bar']; - const passedOptions = { short: { f: 'foo' }, withValue: ['foo'] }; + const passedOptions = { foo: { short: 'f', type: 'string' } }; const expected = { flags: { foo: true }, values: { foo: 'bar' }, positionals: [] }; - const args = parseArgs(passedArgs, passedOptions); + const args = parseArgs({ args: passedArgs, options: passedOptions }); t.deepEqual(args, expected); t.end(); }); -test('when short option withValue used without value then stored as flag', function(t) { +test('when short option `type: "string"` used without value then stored as flag', function(t) { const passedArgs = ['-f']; - const passedOptions = { withValue: ['f'] }; + const passedOptions = { f: { type: 'string' } }; const expected = { flags: { f: true }, values: { f: undefined }, positionals: [] }; - const args = parseArgs(passedArgs, passedOptions); + const args = parseArgs({ args: passedArgs, options: passedOptions }); t.deepEqual(args, expected); @@ -74,7 +74,7 @@ test('short option group behaves like multiple short options', function(t) { const passedArgs = ['-rf']; const passedOptions = { }; const expected = { flags: { r: true, f: true }, values: { r: undefined, f: undefined }, positionals: [] }; - const args = parseArgs(passedArgs, passedOptions); + const args = parseArgs({ args: passedArgs, options: passedOptions }); t.deepEqual(args, expected); @@ -85,18 +85,18 @@ test('short option group does not consume subsequent positional', function(t) { const passedArgs = ['-rf', 'foo']; const passedOptions = { }; const expected = { flags: { r: true, f: true }, values: { r: undefined, f: undefined }, positionals: ['foo'] }; - const args = parseArgs(passedArgs, passedOptions); + const args = parseArgs({ args: passedArgs, options: passedOptions }); t.deepEqual(args, expected); t.end(); }); -// See: Guideline 5 https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html -test('if terminal of short-option group configured withValue, subsequent positional is stored', function(t) { +// // See: Guideline 5 https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html +test('if terminal of short-option group configured `type: "string"`, subsequent positional is stored', function(t) { const passedArgs = ['-rvf', 'foo']; - const passedOptions = { withValue: ['f'] }; + const passedOptions = { f: { type: 'string' } }; const expected = { flags: { r: true, f: true, v: true }, values: { r: undefined, v: undefined, f: 'foo' }, positionals: [] }; - const args = parseArgs(passedArgs, passedOptions); + const args = parseArgs({ args: passedArgs, options: passedOptions }); t.deepEqual(args, expected); t.end(); @@ -104,9 +104,9 @@ test('if terminal of short-option group configured withValue, subsequent positio test('handles short-option groups in conjunction with long-options', function(t) { const passedArgs = ['-rf', '--foo', 'foo']; - const passedOptions = { withValue: ['foo'] }; + const passedOptions = { foo: { type: 'string' } }; const expected = { flags: { r: true, f: true, foo: true }, values: { r: undefined, f: undefined, foo: 'foo' }, positionals: [] }; - const args = parseArgs(passedArgs, passedOptions); + const args = parseArgs({ args: passedArgs, options: passedOptions }); t.deepEqual(args, expected); t.end(); @@ -114,9 +114,9 @@ test('handles short-option groups in conjunction with long-options', function(t) test('handles short-option groups with "short" alias configured', function(t) { const passedArgs = ['-rf']; - const passedOptions = { short: { r: 'remove' } }; + const passedOptions = { remove: { short: 'r' } }; const expected = { flags: { remove: true, f: true }, values: { remove: undefined, f: undefined }, positionals: [] }; - const args = parseArgs(passedArgs, passedOptions); + const args = parseArgs({ args: passedArgs, options: passedOptions }); t.deepEqual(args, expected); t.end(); @@ -125,7 +125,7 @@ test('handles short-option groups with "short" alias configured', function(t) { test('Everything after a bare `--` is considered a positional argument', function(t) { const passedArgs = ['--', 'barepositionals', 'mopositionals']; const expected = { flags: {}, values: {}, positionals: ['barepositionals', 'mopositionals'] }; - const args = parseArgs(passedArgs); + const args = parseArgs({ args: passedArgs }); t.deepEqual(args, expected, 'testing bare positionals'); @@ -135,7 +135,7 @@ test('Everything after a bare `--` is considered a positional argument', functio test('args are true', function(t) { const passedArgs = ['--foo', '--bar']; const expected = { flags: { foo: true, bar: true }, values: { foo: undefined, bar: undefined }, positionals: [] }; - const args = parseArgs(passedArgs); + const args = parseArgs({ args: passedArgs }); t.deepEqual(args, expected, 'args are true'); @@ -145,18 +145,18 @@ test('args are true', function(t) { test('arg is true and positional is identified', function(t) { const passedArgs = ['--foo=a', '--foo', 'b']; const expected = { flags: { foo: true }, values: { foo: undefined }, positionals: ['b'] }; - const args = parseArgs(passedArgs); + const args = parseArgs({ args: passedArgs }); t.deepEqual(args, expected, 'arg is true and positional is identified'); t.end(); }); -test('args equals are passed "withValue"', function(t) { +test('args equals are passed `type: "string"`', function(t) { const passedArgs = ['--so=wat']; - const passedOptions = { withValue: ['so'] }; + const passedOptions = { so: { type: 'string' } }; const expected = { flags: { so: true }, values: { so: 'wat' }, positionals: [] }; - const args = parseArgs(passedArgs, passedOptions); + const args = parseArgs({ args: passedArgs, options: passedOptions }); t.deepEqual(args, expected, 'arg value is passed'); @@ -166,29 +166,29 @@ test('args equals are passed "withValue"', function(t) { test('when args include single dash then result stores dash as positional', function(t) { const passedArgs = ['-']; const expected = { flags: { }, values: { }, positionals: ['-'] }; - const args = parseArgs(passedArgs); + const args = parseArgs({ args: passedArgs }); t.deepEqual(args, expected); t.end(); }); -test('zero config args equals are parsed as if "withValue"', function(t) { +test('zero config args equals are parsed as if `type: "string"`', function(t) { const passedArgs = ['--so=wat']; const passedOptions = { }; const expected = { flags: { so: true }, values: { so: 'wat' }, positionals: [] }; - const args = parseArgs(passedArgs, passedOptions); + const args = parseArgs({ args: passedArgs, options: passedOptions }); t.deepEqual(args, expected, 'arg value is passed'); t.end(); }); -test('same arg is passed twice "withValue" and last value is recorded', function(t) { +test('same arg is passed twice `type: "string"` and last value is recorded', function(t) { const passedArgs = ['--foo=a', '--foo', 'b']; - const passedOptions = { withValue: ['foo'] }; + const passedOptions = { foo: { type: 'string' } }; const expected = { flags: { foo: true }, values: { foo: 'b' }, positionals: [] }; - const args = parseArgs(passedArgs, passedOptions); + const args = parseArgs({ args: passedArgs, options: passedOptions }); t.deepEqual(args, expected, 'last arg value is passed'); @@ -197,31 +197,36 @@ test('same arg is passed twice "withValue" and last value is recorded', function test('args equals pass string including more equals', function(t) { const passedArgs = ['--so=wat=bing']; - const passedOptions = { withValue: ['so'] }; + const passedOptions = { so: { type: 'string' } }; const expected = { flags: { so: true }, values: { so: 'wat=bing' }, positionals: [] }; - const args = parseArgs(passedArgs, passedOptions); + const args = parseArgs({ args: passedArgs, options: passedOptions }); t.deepEqual(args, expected, 'arg value is passed'); t.end(); }); -test('first arg passed for "withValue" and "multiples" is in array', function(t) { +test('first arg passed for `type: "string"` and "multiple" is in array', function(t) { const passedArgs = ['--foo=a']; - const passedOptions = { withValue: ['foo'], multiples: ['foo'] }; + const passedOptions = { foo: { type: 'string', multiple: true } }; const expected = { flags: { foo: true }, values: { foo: ['a'] }, positionals: [] }; - const args = parseArgs(passedArgs, passedOptions); + const args = parseArgs({ args: passedArgs, options: passedOptions }); t.deepEqual(args, expected, 'first multiple in array'); t.end(); }); -test('args are passed "withValue" and "multiples"', function(t) { +test('args are passed `type: "string"` and "multiple"', function(t) { const passedArgs = ['--foo=a', '--foo', 'b']; - const passedOptions = { withValue: ['foo'], multiples: ['foo'] }; + const passedOptions = { + foo: { + type: 'string', + multiple: true, + }, + }; const expected = { flags: { foo: true }, values: { foo: ['a', 'b'] }, positionals: [] }; - const args = parseArgs(passedArgs, passedOptions); + const args = parseArgs({ args: passedArgs, options: passedOptions }); t.deepEqual(args, expected, 'both arg values are passed'); @@ -231,11 +236,11 @@ test('args are passed "withValue" and "multiples"', function(t) { test('order of option and positional does not matter (per README)', function(t) { const passedArgs1 = ['--foo=bar', 'baz']; const passedArgs2 = ['baz', '--foo=bar']; - const passedOptions = { withValue: ['foo'] }; + const passedOptions = { foo: { type: 'string' } }; const expected = { flags: { foo: true }, values: { foo: 'bar' }, positionals: ['baz'] }; - t.deepEqual(parseArgs(passedArgs1, passedOptions), expected, 'option then positional'); - t.deepEqual(parseArgs(passedArgs2, passedOptions), expected, 'positional then option'); + t.deepEqual(parseArgs({ args: passedArgs1, options: passedOptions }), expected, 'option then positional'); + t.deepEqual(parseArgs({ args: passedArgs2, options: passedOptions }), expected, 'positional then option'); t.end(); }); @@ -334,7 +339,7 @@ test('excess leading dashes on options are retained', function(t) { values: { '-triple': undefined }, positionals: [] }; - const result = parseArgs(passedArgs, passedOptions); + const result = parseArgs({ args: passedArgs, options: passedOptions }); t.deepEqual(result, expected, 'excess option dashes are retained'); @@ -345,32 +350,42 @@ test('excess leading dashes on options are retained', function(t) { test('invalid argument passed for options', function(t) { const passedArgs = ['--so=wat']; + const passedOptions = 'bad value'; - t.throws(function() { parseArgs(passedArgs, 'bad value'); }, { + t.throws(function() { parseArgs({ args: passedArgs, options: passedOptions }); }, { code: 'ERR_INVALID_ARG_TYPE' }); t.end(); }); -test('boolean passed to "withValue" option', function(t) { +test('boolean passed to "type" option', function(t) { const passedArgs = ['--so=wat']; - const passedOptions = { withValue: true }; + const passedOptions = { foo: { type: true } }; - t.throws(function() { parseArgs(passedArgs, passedOptions); }, { + t.throws(function() { parseArgs({ args: passedArgs, options: passedOptions }); }, { code: 'ERR_INVALID_ARG_TYPE' }); t.end(); }); -test('string passed to "withValue" option', function(t) { +test('invalid union value passed to "type" option', function(t) { const passedArgs = ['--so=wat']; - const passedOptions = { withValue: 'so' }; + const passedOptions = { foo: { type: 'str' } }; - t.throws(function() { parseArgs(passedArgs, passedOptions); }, { + t.throws(function() { parseArgs({ args: passedArgs, options: passedOptions }); }, { code: 'ERR_INVALID_ARG_TYPE' }); t.end(); }); + +test('invalid short option length', function(t) { + const passedArgs = []; + const passedOptions = { foo: { short: 'fo' } }; + + t.throws(function() { parseArgs({ args: passedArgs, options: passedOptions }); }); + + t.end(); +}); diff --git a/validators.js b/validators.js index a8be5ff..9dae8e9 100644 --- a/validators.js +++ b/validators.js @@ -2,6 +2,8 @@ const { ArrayIsArray, + ArrayPrototypeIncludes, + ArrayPrototypeJoin, } = require('./primordials'); const { @@ -10,6 +12,24 @@ const { } } = require('./errors'); +function validateString(value, name) { + if (typeof value !== 'string') { + throw new ERR_INVALID_ARG_TYPE(name, 'String', value); + } +} + +function validateUnion(value, name, union) { + if (!ArrayPrototypeIncludes(union, value)) { + throw new ERR_INVALID_ARG_TYPE(name, `('${ArrayPrototypeJoin(union, '|')}')`, value); + } +} + +function validateBoolean(value, name) { + if (typeof value !== 'boolean') { + throw new ERR_INVALID_ARG_TYPE(name, 'Boolean', value); + } +} + function validateArray(value, name) { if (!ArrayIsArray(value)) { throw new ERR_INVALID_ARG_TYPE(name, 'Array', value); @@ -42,4 +62,7 @@ function validateObject(value, name, options) { module.exports = { validateArray, validateObject, + validateString, + validateUnion, + validateBoolean, };