| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 | 
							- 'use strict'
 
- const argsert = require('./argsert')
 
- const objFilter = require('./obj-filter')
 
- const specialKeys = ['$0', '--', '_']
 
- // validation-type-stuff, missing params,
 
- // bad implications, custom checks.
 
- module.exports = function validation (yargs, usage, y18n) {
 
-   const __ = y18n.__
 
-   const __n = y18n.__n
 
-   const self = {}
 
-   // validate appropriate # of non-option
 
-   // arguments were provided, i.e., '_'.
 
-   self.nonOptionCount = function nonOptionCount (argv) {
 
-     const demandedCommands = yargs.getDemandedCommands()
 
-     // don't count currently executing commands
 
-     const _s = argv._.length - yargs.getContext().commands.length
 
-     if (demandedCommands._ && (_s < demandedCommands._.min || _s > demandedCommands._.max)) {
 
-       if (_s < demandedCommands._.min) {
 
-         if (demandedCommands._.minMsg !== undefined) {
 
-           usage.fail(
 
-             // replace $0 with observed, $1 with expected.
 
-             demandedCommands._.minMsg ? demandedCommands._.minMsg.replace(/\$0/g, _s).replace(/\$1/, demandedCommands._.min) : null
 
-           )
 
-         } else {
 
-           usage.fail(
 
-             __('Not enough non-option arguments: got %s, need at least %s', _s, demandedCommands._.min)
 
-           )
 
-         }
 
-       } else if (_s > demandedCommands._.max) {
 
-         if (demandedCommands._.maxMsg !== undefined) {
 
-           usage.fail(
 
-             // replace $0 with observed, $1 with expected.
 
-             demandedCommands._.maxMsg ? demandedCommands._.maxMsg.replace(/\$0/g, _s).replace(/\$1/, demandedCommands._.max) : null
 
-           )
 
-         } else {
 
-           usage.fail(
 
-             __('Too many non-option arguments: got %s, maximum of %s', _s, demandedCommands._.max)
 
-           )
 
-         }
 
-       }
 
-     }
 
-   }
 
-   // validate the appropriate # of <required>
 
-   // positional arguments were provided:
 
-   self.positionalCount = function positionalCount (required, observed) {
 
-     if (observed < required) {
 
-       usage.fail(
 
-         __('Not enough non-option arguments: got %s, need at least %s', observed, required)
 
-       )
 
-     }
 
-   }
 
-   // make sure all the required arguments are present.
 
-   self.requiredArguments = function requiredArguments (argv) {
 
-     const demandedOptions = yargs.getDemandedOptions()
 
-     let missing = null
 
-     Object.keys(demandedOptions).forEach((key) => {
 
-       if (!argv.hasOwnProperty(key) || typeof argv[key] === 'undefined') {
 
-         missing = missing || {}
 
-         missing[key] = demandedOptions[key]
 
-       }
 
-     })
 
-     if (missing) {
 
-       const customMsgs = []
 
-       Object.keys(missing).forEach((key) => {
 
-         const msg = missing[key]
 
-         if (msg && customMsgs.indexOf(msg) < 0) {
 
-           customMsgs.push(msg)
 
-         }
 
-       })
 
-       const customMsg = customMsgs.length ? `\n${customMsgs.join('\n')}` : ''
 
-       usage.fail(__n(
 
-         'Missing required argument: %s',
 
-         'Missing required arguments: %s',
 
-         Object.keys(missing).length,
 
-         Object.keys(missing).join(', ') + customMsg
 
-       ))
 
-     }
 
-   }
 
-   // check for unknown arguments (strict-mode).
 
-   self.unknownArguments = function unknownArguments (argv, aliases, positionalMap) {
 
-     const commandKeys = yargs.getCommandInstance().getCommands()
 
-     const unknown = []
 
-     const currentContext = yargs.getContext()
 
-     Object.keys(argv).forEach((key) => {
 
-       if (specialKeys.indexOf(key) === -1 &&
 
-         !positionalMap.hasOwnProperty(key) &&
 
-         !yargs._getParseContext().hasOwnProperty(key) &&
 
-         !aliases.hasOwnProperty(key)
 
-       ) {
 
-         unknown.push(key)
 
-       }
 
-     })
 
-     if (commandKeys.length > 0) {
 
-       argv._.slice(currentContext.commands.length).forEach((key) => {
 
-         if (commandKeys.indexOf(key) === -1) {
 
-           unknown.push(key)
 
-         }
 
-       })
 
-     }
 
-     if (unknown.length > 0) {
 
-       usage.fail(__n(
 
-         'Unknown argument: %s',
 
-         'Unknown arguments: %s',
 
-         unknown.length,
 
-         unknown.join(', ')
 
-       ))
 
-     }
 
-   }
 
-   // validate arguments limited to enumerated choices
 
-   self.limitedChoices = function limitedChoices (argv) {
 
-     const options = yargs.getOptions()
 
-     const invalid = {}
 
-     if (!Object.keys(options.choices).length) return
 
-     Object.keys(argv).forEach((key) => {
 
-       if (specialKeys.indexOf(key) === -1 &&
 
-         options.choices.hasOwnProperty(key)) {
 
-         [].concat(argv[key]).forEach((value) => {
 
-           // TODO case-insensitive configurability
 
-           if (options.choices[key].indexOf(value) === -1 &&
 
-               value !== undefined) {
 
-             invalid[key] = (invalid[key] || []).concat(value)
 
-           }
 
-         })
 
-       }
 
-     })
 
-     const invalidKeys = Object.keys(invalid)
 
-     if (!invalidKeys.length) return
 
-     let msg = __('Invalid values:')
 
-     invalidKeys.forEach((key) => {
 
-       msg += `\n  ${__(
 
-         'Argument: %s, Given: %s, Choices: %s',
 
-         key,
 
-         usage.stringifiedValues(invalid[key]),
 
-         usage.stringifiedValues(options.choices[key])
 
-       )}`
 
-     })
 
-     usage.fail(msg)
 
-   }
 
-   // custom checks, added using the `check` option on yargs.
 
-   let checks = []
 
-   self.check = function check (f, global) {
 
-     checks.push({
 
-       func: f,
 
-       global
 
-     })
 
-   }
 
-   self.customChecks = function customChecks (argv, aliases) {
 
-     for (let i = 0, f; (f = checks[i]) !== undefined; i++) {
 
-       const func = f.func
 
-       let result = null
 
-       try {
 
-         result = func(argv, aliases)
 
-       } catch (err) {
 
-         usage.fail(err.message ? err.message : err, err)
 
-         continue
 
-       }
 
-       if (!result) {
 
-         usage.fail(__('Argument check failed: %s', func.toString()))
 
-       } else if (typeof result === 'string' || result instanceof Error) {
 
-         usage.fail(result.toString(), result)
 
-       }
 
-     }
 
-   }
 
-   // check implications, argument foo implies => argument bar.
 
-   let implied = {}
 
-   self.implies = function implies (key, value) {
 
-     argsert('<string|object> [array|number|string]', [key, value], arguments.length)
 
-     if (typeof key === 'object') {
 
-       Object.keys(key).forEach((k) => {
 
-         self.implies(k, key[k])
 
-       })
 
-     } else {
 
-       yargs.global(key)
 
-       if (!implied[key]) {
 
-         implied[key] = []
 
-       }
 
-       if (Array.isArray(value)) {
 
-         value.forEach((i) => self.implies(key, i))
 
-       } else {
 
-         implied[key].push(value)
 
-       }
 
-     }
 
-   }
 
-   self.getImplied = function getImplied () {
 
-     return implied
 
-   }
 
-   self.implications = function implications (argv) {
 
-     const implyFail = []
 
-     Object.keys(implied).forEach((key) => {
 
-       const origKey = key
 
-       ;(implied[key] || []).forEach((value) => {
 
-         let num
 
-         let key = origKey
 
-         const origValue = value
 
-         // convert string '1' to number 1
 
-         num = Number(key)
 
-         key = isNaN(num) ? key : num
 
-         if (typeof key === 'number') {
 
-           // check length of argv._
 
-           key = argv._.length >= key
 
-         } else if (key.match(/^--no-.+/)) {
 
-           // check if key doesn't exist
 
-           key = key.match(/^--no-(.+)/)[1]
 
-           key = !argv[key]
 
-         } else {
 
-           // check if key exists
 
-           key = argv[key]
 
-         }
 
-         num = Number(value)
 
-         value = isNaN(num) ? value : num
 
-         if (typeof value === 'number') {
 
-           value = argv._.length >= value
 
-         } else if (value.match(/^--no-.+/)) {
 
-           value = value.match(/^--no-(.+)/)[1]
 
-           value = !argv[value]
 
-         } else {
 
-           value = argv[value]
 
-         }
 
-         if (key && !value) {
 
-           implyFail.push(` ${origKey} -> ${origValue}`)
 
-         }
 
-       })
 
-     })
 
-     if (implyFail.length) {
 
-       let msg = `${__('Implications failed:')}\n`
 
-       implyFail.forEach((value) => {
 
-         msg += (value)
 
-       })
 
-       usage.fail(msg)
 
-     }
 
-   }
 
-   let conflicting = {}
 
-   self.conflicts = function conflicts (key, value) {
 
-     argsert('<string|object> [array|string]', [key, value], arguments.length)
 
-     if (typeof key === 'object') {
 
-       Object.keys(key).forEach((k) => {
 
-         self.conflicts(k, key[k])
 
-       })
 
-     } else {
 
-       yargs.global(key)
 
-       if (!conflicting[key]) {
 
-         conflicting[key] = []
 
-       }
 
-       if (Array.isArray(value)) {
 
-         value.forEach((i) => self.conflicts(key, i))
 
-       } else {
 
-         conflicting[key].push(value)
 
-       }
 
-     }
 
-   }
 
-   self.getConflicting = () => conflicting
 
-   self.conflicting = function conflictingFn (argv) {
 
-     Object.keys(argv).forEach((key) => {
 
-       if (conflicting[key]) {
 
-         conflicting[key].forEach((value) => {
 
-           // we default keys to 'undefined' that have been configured, we should not
 
-           // apply conflicting check unless they are a value other than 'undefined'.
 
-           if (value && argv[key] !== undefined && argv[value] !== undefined) {
 
-             usage.fail(__('Arguments %s and %s are mutually exclusive', key, value))
 
-           }
 
-         })
 
-       }
 
-     })
 
-   }
 
-   self.recommendCommands = function recommendCommands (cmd, potentialCommands) {
 
-     const distance = require('./levenshtein')
 
-     const threshold = 3 // if it takes more than three edits, let's move on.
 
-     potentialCommands = potentialCommands.sort((a, b) => b.length - a.length)
 
-     let recommended = null
 
-     let bestDistance = Infinity
 
-     for (let i = 0, candidate; (candidate = potentialCommands[i]) !== undefined; i++) {
 
-       const d = distance(cmd, candidate)
 
-       if (d <= threshold && d < bestDistance) {
 
-         bestDistance = d
 
-         recommended = candidate
 
-       }
 
-     }
 
-     if (recommended) usage.fail(__('Did you mean %s?', recommended))
 
-   }
 
-   self.reset = function reset (localLookup) {
 
-     implied = objFilter(implied, (k, v) => !localLookup[k])
 
-     conflicting = objFilter(conflicting, (k, v) => !localLookup[k])
 
-     checks = checks.filter(c => c.global)
 
-     return self
 
-   }
 
-   let frozen
 
-   self.freeze = function freeze () {
 
-     frozen = {}
 
-     frozen.implied = implied
 
-     frozen.checks = checks
 
-     frozen.conflicting = conflicting
 
-   }
 
-   self.unfreeze = function unfreeze () {
 
-     implied = frozen.implied
 
-     checks = frozen.checks
 
-     conflicting = frozen.conflicting
 
-     frozen = undefined
 
-   }
 
-   return self
 
- }
 
 
  |