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

Fleshing out parseArgs to support withValue and Multiples options #9

Merged
merged 8 commits into from
Oct 4, 2021
41 changes: 34 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ const parseArgs = (
if (typeof options !== 'object' || options === null) {
throw new Error('Whoops!')
}
if (options.withValue !== undefined && !Array.isArray(options.withValue)) {
throw new Error('Whoops! options.withValue should be an array.')
}

let result = {
args: {},
Expand All @@ -23,7 +26,6 @@ const parseArgs = (
// and is returned verbatim
if (arg === '--') {
result.positionals.push(...argv.slice(++pos))

return result
}
// look for shortcodes: -fXzy
Expand All @@ -36,17 +38,42 @@ const parseArgs = (
arg = arg.replace(/^-+/, '')

if (arg.includes('=')) {
//withValue equals(=) case
const argParts = arg.split('=')

result.args[argParts[0]] = true
if (options.withValue) {
result.values[argParts[0]] = argParts[1]
}
}
else {
result.args[arg] = true
//If withValue option is specified, take 2nd part after '=' as value, else set value as undefined
const val = options.withValue && options.withValue.includes(argParts[0]) ? argParts[1] : undefined
//Append value to previous arg values array for case of multiples option, else add to empty array
result.values[argParts[0]] = [].concat(
options.multiples && options.multiples.includes(argParts[0]) && result.values[argParts[0]] || [],
val,
)
} else if (pos + 1 < argv.length && !argv[pos+1].startsWith('-')) {
//withValue option should also support setting values when '=' isn't used
//ie. both --foo=b and --foo b should work

result.args[arg] = true
//If withValue option is specified, take next position arguement 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 && options.withValue.includes(arg) ? argv[++pos] : undefined
//Append value to previous arg values array for case of multiples option, else add to empty array
result.values[arg] = [].concat(
options.multiples && options.multiples.includes(arg) && result.values[arg] ? result.values[arg] : [],
val)
} else {
//cases when an arg is specified without a value, example '--foo --bar' <- 'foo' and 'bar' args should be set to true and have value as undefined
result.args[arg] = true
//Append undefined to previous arg values array for case of multiples option, else add to empty array
result.values[arg] = [].concat(
options.multiples && options.multiples.includes(arg) && result.values[arg] ? result.values[arg] : [],
undefined
)
}

} else {
//Arguements without a dash prefix are considered "positional"
result.positionals.push(arg)
}

pos++
Expand Down
60 changes: 57 additions & 3 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
const test = require('tape')
const {parseArgs} = require('../index.js')

//Test results are as we expect

test('Everything after a bare `--` is considered a positional argument', function (t) {
const passedArgs = ['--', 'barepositionals', 'mopositionals']
const expected = { args: {}, values: {}, positionals: ['barepositionals', 'mopositionals'] }
Expand All @@ -15,22 +17,74 @@ test('Everything after a bare `--` is considered a positional argument', functio

test('args are true', function (t) {
const passedArgs = ['--foo', '--bar']
const expected = { args: { foo: true, bar: true}, values: {}, positionals: [] }
const expected = { args: { foo: true, bar: true}, values: {foo: [undefined], bar: [undefined]}, positionals: [] }
const args = parseArgs(passedArgs)

t.deepEqual(args, expected, 'args are true')

t.end()
})

test('arg is true and positional is identified', function (t) {
const passedArgs = ['--foo=a', '--foo', 'b']
const expected = { args: { foo: true}, values: { foo: [undefined]}, positionals: ['b'] }
const args = parseArgs(passedArgs)

t.deepEqual(args, expected, 'arg is true and positional is identified')

t.end()
})

test('args equals are passed "withValue"', function (t) {
const passedArgs = ['--so=wat']
const passedOptions = { withValue: true }
const expected = { args: { so: true}, values: { so: "wat"}, positionals: [] }
const passedOptions = { withValue: ['so'] }
const expected = { args: { so: true}, values: { so: ["wat"]}, positionals: [] }
const args = parseArgs(passedArgs, 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) {
const passedArgs = ['--foo=a', '--foo', 'b']
const passedOptions = { withValue: ['foo'] }
const expected = { args: { foo: true}, values: { foo: ['b']}, positionals: [] }
const args = parseArgs(passedArgs, passedOptions)

t.deepEqual(args, expected, 'last arg value is passed')

t.end()
})

test('args are passed "withValue" and "multiples"', function (t) {
const passedArgs = ['--foo=a', '--foo', 'b']
const passedOptions = { withValue: ['foo'], multiples: ['foo'] }
const expected = { args: { foo: true}, values: { foo: ['a', 'b']}, positionals: [] }
const args = parseArgs(passedArgs, passedOptions)

t.deepEqual(args, expected, 'both arg values are passed')

t.end()
})


//Test bad inputs

test('boolean passed to "withValue" option', function (t) {
const passedArgs = ['--so=wat']
const passedOptions = { withValue: true }

t.throws(function() { parseArgs(passedArgs, passedOptions) });

t.end()
})

test('string passed to "withValue" option', function (t) {
const passedArgs = ['--so=wat']
const passedOptions = { withValue: 'so' }

t.throws(function() { parseArgs(passedArgs, passedOptions) });

t.end()
})