Skip to content

Commit

Permalink
feat: implement conflicts() for defining mutually exclusive arguments…
Browse files Browse the repository at this point in the history
…; thanks @madcampos! (yargs#741)
  • Loading branch information
bcoe committed Dec 29, 2016
1 parent f755e27 commit 5883779
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 3 deletions.
7 changes: 7 additions & 0 deletions README.md
Expand Up @@ -985,6 +985,13 @@ $ node test.js
'$0': 'test.js' }
```

<a name="conflicts"></a>.conflicts(x, y)
----------------------------------------------

Given the key `x` is set, the key `y` must not be set.

Optionally `.conflicts()` can accept an object specifying multiple conflicting keys.

<a name="count"></a>.count(key)
------------

Expand Down
27 changes: 27 additions & 0 deletions lib/validation.js
Expand Up @@ -271,6 +271,30 @@ module.exports = function (yargs, usage, y18n) {
}
}

var conflicting = {}
self.conflicts = function (key, value) {
if (typeof key === 'object') {
Object.keys(key).forEach(function (k) {
self.conflicts(k, key[k])
})
} else {
conflicting[key] = value
}
}
self.getConflicting = function () {
return conflicting
}

self.conflicting = function (argv) {
var args = Object.getOwnPropertyNames(argv)

args.forEach(function (arg) {
if (conflicting[arg] && args.indexOf(conflicting[arg]) !== -1) {
usage.fail(__('Arguments %s and %s are mutually exclusive', arg, conflicting[arg]))
}
})
}

self.recommendCommands = function (cmd, potentialCommands) {
const distance = require('./levenshtein')
const threshold = 3 // if it takes more than three edits, let's move on.
Expand All @@ -293,6 +317,7 @@ module.exports = function (yargs, usage, y18n) {
return globalLookup[k]
})
checks = []
conflicting = {}
return self
}

Expand All @@ -301,10 +326,12 @@ module.exports = function (yargs, usage, y18n) {
frozen = {}
frozen.implied = implied
frozen.checks = checks
frozen.conflicting = conflicting
}
self.unfreeze = function () {
implied = frozen.implied
checks = frozen.checks
conflicting = frozen.conflicting
frozen = undefined
}

Expand Down
3 changes: 2 additions & 1 deletion locales/en.json
Expand Up @@ -35,5 +35,6 @@
"Path to JSON config file": "Path to JSON config file",
"Show help": "Show help",
"Show version number": "Show version number",
"Did you mean %s?": "Did you mean %s?"
"Did you mean %s?": "Did you mean %s?",
"Arguments %s and %s are mutually exclusive" : "Arguments %s and %s are mutually exclusive"
}
3 changes: 2 additions & 1 deletion locales/pt.json
Expand Up @@ -33,5 +33,6 @@
"Invalid JSON config file: %s": "Arquivo de configuração em JSON esta inválido: %s",
"Path to JSON config file": "Caminho para o arquivo de configuração em JSON",
"Show help": "Mostra ajuda",
"Show version number": "Mostra número de versão"
"Show version number": "Mostra número de versão",
"Arguments %s and %s are mutually exclusive" : "Argumentos %s e %s são mutualmente exclusivos"
}
3 changes: 2 additions & 1 deletion locales/pt_BR.json
Expand Up @@ -35,5 +35,6 @@
"Path to JSON config file": "Caminho para o arquivo JSON de configuração",
"Show help": "Exibe ajuda",
"Show version number": "Exibe a versão",
"Did you mean %s?": "Você quis dizer %s?"
"Did you mean %s?": "Você quis dizer %s?",
"Arguments %s and %s are mutually exclusive" : "Argumentos %s e %s são mutualmente exclusivos"
}
50 changes: 50 additions & 0 deletions test/validation.js
Expand Up @@ -3,6 +3,8 @@
var expect = require('chai').expect
var yargs = require('../')

require('chai').should()

describe('validation tests', function () {
beforeEach(function () {
yargs.reset()
Expand Down Expand Up @@ -123,6 +125,54 @@ describe('validation tests', function () {
})
})

describe('conflicts', function () {
it('fails if both arguments are supplied', function (done) {
yargs(['-f', '-b'])
.conflicts('f', 'b')
.fail(function (msg) {
msg.should.equal('Arguments f and b are mutually exclusive')
return done()
})
.argv
})

it('should not fail if no conflicting arguments are provided', function () {
yargs(['-b', '-c'])
.conflicts('f', 'b')
.fail(function (msg) {
expect.fail()
})
.argv
})

it('allows an object to be provided defining conflicting option pairs', function (done) {
yargs(['-t', '-s'])
.conflicts({
'c': 'a',
's': 't'
})
.fail(function (msg) {
msg.should.equal('Arguments s and t are mutually exclusive')
return done()
})
.argv
})

it('takes into account aliases when applying conflicts logic', function (done) {
yargs(['-t', '-c'])
.conflicts({
'c': 'a',
's': 't'
})
.alias('c', 's')
.fail(function (msg) {
msg.should.equal('Arguments s and t are mutually exclusive')
return done()
})
.argv
})
})

describe('demand', function () {
it('fails with standard error message if msg is not defined', function (done) {
yargs([])
Expand Down
2 changes: 2 additions & 0 deletions test/yargs.js
Expand Up @@ -217,6 +217,7 @@ describe('yargs dsl tests', function () {
.choices('foo', ['bar', 'baz'])
.coerce('foo', function (foo) { return foo + 'bar' })
.implies('foo', 'snuh')
.conflicts('qux', 'xyzzy')
.group('foo', 'Group:')
.strict()
.exitProcess(false) // defaults to true.
Expand Down Expand Up @@ -249,6 +250,7 @@ describe('yargs dsl tests', function () {
expect(y.getOptions()).to.deep.equal(emptyOptions)
expect(y.getUsageInstance().getDescriptions()).to.deep.equal({help: '__yargsString__:Show help'})
expect(y.getValidationInstance().getImplied()).to.deep.equal({})
expect(y.getValidationInstance().getConflicting()).to.deep.equal({})
expect(y.getCommandInstance().getCommandHandlers()).to.deep.equal({})
expect(y.getExitProcess()).to.equal(false)
expect(y.getStrict()).to.equal(false)
Expand Down
6 changes: 6 additions & 0 deletions yargs.js
Expand Up @@ -354,6 +354,11 @@ function Yargs (processArgs, cwd, parentRequire) {
return self
}

self.conflicts = function (key1, key2) {
validation.conflicts(key1, key2)
return self
}

self.usage = function (msg, opts) {
if (!opts && typeof msg === 'object') {
opts = msg
Expand Down Expand Up @@ -952,6 +957,7 @@ function Yargs (processArgs, cwd, parentRequire) {
validation.customChecks(argv, aliases)
validation.limitedChoices(argv)
validation.implications(argv)
validation.conflicting(argv)
}
}

Expand Down

0 comments on commit 5883779

Please sign in to comment.