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

fix(array, nargs): support -o=--value and --option=--value format #262

Merged
merged 8 commits into from Mar 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
48 changes: 29 additions & 19 deletions index.js
Expand Up @@ -167,12 +167,10 @@ function parse (args, opts) {

// arrays format = '--f=a b c'
if (checkAllAliases(m[1], flags.arrays)) {
args.splice(i + 1, 0, m[2])
i = eatArray(i, m[1], args)
} else if (checkAllAliases(m[1], flags.nargs)) {
i = eatArray(i, m[1], args, m[2])
} else if (checkAllAliases(m[1], flags.nargs) !== false) {
// nargs format = '--f=monkey washing cat'
args.splice(i + 1, 0, m[2])
i = eatNargs(i, m[1], args)
i = eatNargs(i, m[1], args, m[2])
} else {
setArg(m[1], m[2])
}
Expand Down Expand Up @@ -241,12 +239,10 @@ function parse (args, opts) {

if (checkAllAliases(key, flags.arrays)) {
// array format = '-f=a b c'
args.splice(i + 1, 0, value)
i = eatArray(i, key, args)
} else if (checkAllAliases(key, flags.nargs)) {
i = eatArray(i, key, args, value)
} else if (checkAllAliases(key, flags.nargs) !== false) {
// nargs format = '-f=monkey washing cat'
args.splice(i + 1, 0, value)
i = eatNargs(i, key, args)
i = eatNargs(i, key, args, value)
} else {
setArg(key, value)
}
Expand Down Expand Up @@ -364,22 +360,27 @@ function parse (args, opts) {

// how many arguments should we consume, based
// on the nargs option?
function eatNargs (i, key, args) {
function eatNargs (i, key, args, argAfterEqualSign) {
let ii
let toEat = checkAllAliases(key, flags.nargs)
// NaN has a special meaning for the array type, indicating that one or
// more values are expected.
toEat = isNaN(toEat) ? 1 : toEat

if (toEat === 0) {
if (!isUndefined(argAfterEqualSign)) {
error = Error(__('Argument unexpected for: %s', key))
}
setArg(key, defaultValue(key))
return i
}

let available = 0
let available = isUndefined(argAfterEqualSign) ? 0 : 1
if (configuration['nargs-eats-options']) {
// classic behavior, yargs eats positional and dash arguments.
if (args.length - (i + 1) < toEat) error = Error(__('Not enough arguments following: %s', key))
if (args.length - (i + 1) + available < toEat) {
error = Error(__('Not enough arguments following: %s', key))
}
available = toEat
} else {
// nargs will not consume flag arguments, e.g., -abc, --foo,
Expand All @@ -391,7 +392,11 @@ function parse (args, opts) {
if (available < toEat) error = Error(__('Not enough arguments following: %s', key))
}

const consumed = Math.min(available, toEat)
let consumed = Math.min(available, toEat)
if (!isUndefined(argAfterEqualSign) && consumed > 0) {
setArg(key, argAfterEqualSign)
consumed--
}
for (ii = i + 1; ii < (consumed + i + 1); ii++) {
setArg(key, args[ii])
}
Expand All @@ -402,29 +407,34 @@ function parse (args, opts) {
// if an option is an array, eat all non-hyphenated arguments
// following it... YUM!
// e.g., --foo apple banana cat becomes ["apple", "banana", "cat"]
function eatArray (i, key, args) {
function eatArray (i, key, args, argAfterEqualSign) {
let argsToSet = []
let next = args[i + 1]
let next = argAfterEqualSign || args[i + 1]
// If both array and nargs are configured, enforce the nargs count:
const nargsCount = checkAllAliases(key, flags.nargs)

if (checkAllAliases(key, flags.bools) && !(/^(true|false)$/.test(next))) {
argsToSet.push(true)
} else if (isUndefined(next) || (/^-/.test(next) && !negative.test(next) && !isUnknownOptionAsArg(next))) {
} else if (isUndefined(next) ||
(isUndefined(argAfterEqualSign) && /^-/.test(next) && !negative.test(next) && !isUnknownOptionAsArg(next))) {
// for keys without value ==> argsToSet remains an empty []
// set user default value, if available
if (defaults[key] !== undefined) {
const defVal = defaults[key]
argsToSet = Array.isArray(defVal) ? defVal : [defVal]
}
} else {
// value in --option=value is eaten as is
if (!isUndefined(argAfterEqualSign)) {
argsToSet.push(processValue(key, argAfterEqualSign))
}
for (let ii = i + 1; ii < args.length; ii++) {
if ((!configuration['greedy-arrays'] && argsToSet.length > 0) ||
(nargsCount && argsToSet.length >= nargsCount)) break
next = args[ii]
if (/^-/.test(next) && !negative.test(next) && !isUnknownOptionAsArg(next)) break
i = ii
argsToSet.push(processValue(key, next))
if (!configuration['greedy-arrays'] ||
(nargsCount && argsToSet.length >= nargsCount)) break
}
}

Expand Down
61 changes: 61 additions & 0 deletions test/yargs-parser.js
Expand Up @@ -1748,6 +1748,20 @@ describe('yargs-parser', function () {
result['1'][1].should.equal('b')
})

it('should support array for -f= and --bar= format when the value is dashed', function () {
var result = parser(['-f=--dog', 'cat', '--bar=--red', 'green'], {
array: ['f', 'bar']
})

Array.isArray(result.f).should.equal(true)
result.f[0].should.equal('--dog')
result.f[1].should.equal('cat')

Array.isArray(result.bar).should.equal(true)
result.bar[0].should.equal('--red')
result.bar[1].should.equal('green')
})

it('should create an array when passing an argument twice with same value', function () {
var result = parser(['-x', 'val1', '-x', 'val1'])
result.should.have.property('x').that.is.an('array').and.to.deep.equal(['val1', 'val1'])
Expand Down Expand Up @@ -1869,6 +1883,24 @@ describe('yargs-parser', function () {
result.foo[1].should.equal('bar')
})

it('should raise an exception if -f== format is used for a key with no expected argument', function () {
var argv = parser.detailed('-f=apple', {
narg: {
f: 0
}
})
argv.error.message.should.equal('Argument unexpected for: f')
})

it('should raise an exception if --bar== format is used for a key with no expected argument', function () {
var argv = parser.detailed('--bar=apple', {
narg: {
bar: 0
}
})
argv.error.message.should.equal('Argument unexpected for: bar')
})

it('should raise an exception if there are not enough arguments following key', function () {
var argv = parser.detailed('--foo apple', {
narg: {
Expand Down Expand Up @@ -1921,6 +1953,23 @@ describe('yargs-parser', function () {
result._[1].should.equal('cat')
})

it('should support nargs for -f= and --bar= format arguments with dashed values', function () {
var result = parser(['-f=--apple', 'bar', 'blerg', '--bar=-monkey', 'washing', 'cat'], {
narg: {
f: 2,
bar: 2
}
})

result.f[0].should.equal('--apple')
result.f[1].should.equal('bar')
result._[0].should.equal('blerg')

result.bar[0].should.equal('-monkey')
result.bar[1].should.equal('washing')
result._[1].should.equal('cat')
})

it('should not modify the input args if an = was used', function () {
var expected = ['-f=apple', 'bar', 'blerg', '--bar=monkey', 'washing', 'cat']
var args = expected.slice()
Expand Down Expand Up @@ -3680,6 +3729,18 @@ describe('yargs-parser', function () {
parse.error.message.should.equal('Not enough arguments following: a')
})

it('returns an error if not enough positionals were provided for nargs even with nargs-eats-options', () => {
var parse = parser.detailed(['-a', '33', '--cat'], {
narg: {
a: 3
},
configuration: {
'nargs-eats-options': true
}
})
parse.error.message.should.equal('Not enough arguments following: a')
})

it('does not raise error if no arguments are provided for boolean option', () => {
var parse = parser.detailed(['-a'], {
array: 'a',
Expand Down