Skip to content

Commit

Permalink
fix(array, nargs): support -o=--value and --option=--value format (#262)
Browse files Browse the repository at this point in the history
  • Loading branch information
mleguen committed Mar 26, 2020
1 parent b96b989 commit 41d3f81
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 19 deletions.
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

0 comments on commit 41d3f81

Please sign in to comment.