| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 | const objFilter = require('./obj-filter')// validation-type-stuff, missing params,// bad implications, custom checks.module.exports = function (yargs, usage, y18n) {  const __ = y18n.__  const __n = y18n.__n  const self = {}  // validate appropriate # of non-option  // arguments were provided, i.e., '_'.  self.nonOptionCount = function (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 (required, observed) {    if (observed < required) {      usage.fail(        __('Not enough non-option arguments: got %s, need at least %s', observed, required)      )    }  }  // make sure that any args that require an  // value (--foo=bar), have a value.  self.missingArgumentValue = function (argv) {    const defaultValues = [true, false, '']    const options = yargs.getOptions()    if (options.requiresArg.length > 0) {      const missingRequiredArgs = []      options.requiresArg.forEach(function (key) {        const value = argv[key]        // if a value is explicitly requested,        // flag argument as missing if it does not        // look like foo=bar was entered.        if (~defaultValues.indexOf(value) ||          (Array.isArray(value) && !value.length)) {          missingRequiredArgs.push(key)        }      })      if (missingRequiredArgs.length > 0) {        usage.fail(__n(          'Missing argument value: %s',          'Missing argument values: %s',          missingRequiredArgs.length,          missingRequiredArgs.join(', ')        ))      }    }  }  // make sure all the required arguments are present.  self.requiredArguments = function (argv) {    const demandedOptions = yargs.getDemandedOptions()    var missing = null    Object.keys(demandedOptions).forEach(function (key) {      if (!argv.hasOwnProperty(key) || typeof argv[key] === 'undefined') {        missing = missing || {}        missing[key] = demandedOptions[key]      }    })    if (missing) {      const customMsgs = []      Object.keys(missing).forEach(function (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 (argv, aliases, positionalMap) {    const aliasLookup = {}    const descriptions = usage.getDescriptions()    const demandedOptions = yargs.getDemandedOptions()    const commandKeys = yargs.getCommandInstance().getCommands()    const unknown = []    const currentContext = yargs.getContext()    Object.keys(aliases).forEach(function (key) {      aliases[key].forEach(function (alias) {        aliasLookup[alias] = key      })    })    Object.keys(argv).forEach(function (key) {      if (key !== '$0' && key !== '_' &&        !descriptions.hasOwnProperty(key) &&        !demandedOptions.hasOwnProperty(key) &&        !positionalMap.hasOwnProperty(key) &&        !yargs._getParseContext().hasOwnProperty(key) &&        !aliasLookup.hasOwnProperty(key)) {        unknown.push(key)      }    })    if (commandKeys.length > 0) {      argv._.slice(currentContext.commands.length).forEach(function (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 (argv) {    const options = yargs.getOptions()    const invalid = {}    if (!Object.keys(options.choices).length) return    Object.keys(argv).forEach(function (key) {      if (key !== '$0' && key !== '_' &&        options.choices.hasOwnProperty(key)) {        [].concat(argv[key]).forEach(function (value) {          // TODO case-insensitive configurability          if (options.choices[key].indexOf(value) === -1) {            invalid[key] = (invalid[key] || []).concat(value)          }        })      }    })    const invalidKeys = Object.keys(invalid)    if (!invalidKeys.length) return    var msg = __('Invalid values:')    invalidKeys.forEach(function (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.  var checks = []  self.check = function (f, global) {    checks.push({      func: f,      global: global    })  }  self.customChecks = function (argv, aliases) {    for (var i = 0, f; (f = checks[i]) !== undefined; i++) {      var func = f.func      var 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.  var implied = {}  self.implies = function (key, value) {    if (typeof key === 'object') {      Object.keys(key).forEach(function (k) {        self.implies(k, key[k])      })    } else {      yargs.global(key)      implied[key] = value    }  }  self.getImplied = function () {    return implied  }  self.implications = function (argv) {    const implyFail = []    Object.keys(implied).forEach(function (key) {      var num      const origKey = key      var value = implied[key]      // 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)      }    })    if (implyFail.length) {      var msg = __('Implications failed:') + '\n'      implyFail.forEach(function (key) {        msg += ('  ' + key + ' -> ' + implied[key])      })      usage.fail(msg)    }  }  var conflicting = {}  self.conflicts = function (key, value) {    if (typeof key === 'object') {      Object.keys(key).forEach(function (k) {        self.conflicts(k, key[k])      })    } else {      yargs.global(key)      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.    potentialCommands = potentialCommands.sort(function (a, b) { return b.length - a.length })    var recommended = null    var bestDistance = Infinity    for (var i = 0, candidate; (candidate = potentialCommands[i]) !== undefined; i++) {      var 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 (localLookup) {    implied = objFilter(implied, function (k, v) {      return !localLookup[k]    })    conflicting = objFilter(conflicting, function (k, v) {      return !localLookup[k]    })    checks = checks.filter(function (c) {      return c.global    })    return self  }  var frozen  self.freeze = function () {    frozen = {}    frozen.implied = implied    frozen.checks = checks    frozen.conflicting = conflicting  }  self.unfreeze = function () {    implied = frozen.implied    checks = frozen.checks    conflicting = frozen.conflicting    frozen = undefined  }  return self}
 |