--- /dev/null
+// validation-type-stuff, missing params,
+// bad implications, custom checks.
+module.exports = function (yargs, usage, y18n) {
+ var __ = y18n.__
+ var __n = y18n.__n
+ var self = {}
+
+ // validate appropriate # of non-option
+ // arguments were provided, i.e., '_'.
+ self.nonOptionCount = function (argv) {
+ var demanded = yargs.getDemanded()
+ var _s = argv._.length
+
+ if (demanded._ && (_s < demanded._.count || _s > demanded._.max)) {
+ if (demanded._.msg !== undefined) {
+ usage.fail(demanded._.msg)
+ } else if (_s < demanded._.count) {
+ usage.fail(
+ __('Not enough non-option arguments: got %s, need at least %s', argv._.length, demanded._.count)
+ )
+ } else {
+ usage.fail(
+ __('Too many non-option arguments: got %s, maximum of %s', argv._.length, demanded._.max)
+ )
+ }
+ }
+ }
+
+ // make sure that any args that require an
+ // value (--foo=bar), have a value.
+ self.missingArgumentValue = function (argv) {
+ var defaultValues = [true, false, '']
+ var options = yargs.getOptions()
+
+ if (options.requiresArg.length > 0) {
+ var missingRequiredArgs = []
+
+ options.requiresArg.forEach(function (key) {
+ var 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) {
+ var demanded = yargs.getDemanded()
+ var missing = null
+
+ Object.keys(demanded).forEach(function (key) {
+ if (!argv.hasOwnProperty(key)) {
+ missing = missing || {}
+ missing[key] = demanded[key]
+ }
+ })
+
+ if (missing) {
+ var customMsgs = []
+ Object.keys(missing).forEach(function (key) {
+ var msg = missing[key].msg
+ if (msg && customMsgs.indexOf(msg) < 0) {
+ customMsgs.push(msg)
+ }
+ })
+
+ var 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) {
+ var aliasLookup = {}
+ var descriptions = usage.getDescriptions()
+ var demanded = yargs.getDemanded()
+ var unknown = []
+
+ 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) &&
+ !demanded.hasOwnProperty(key) &&
+ !aliasLookup.hasOwnProperty(key)) {
+ 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) {
+ var options = yargs.getOptions()
+ var 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)
+ }
+ })
+ }
+ })
+
+ var 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) {
+ checks.push(f)
+ }
+
+ self.customChecks = function (argv, aliases) {
+ checks.forEach(function (f) {
+ try {
+ var result = f(argv, aliases)
+ if (!result) {
+ usage.fail(__('Argument check failed: %s', f.toString()))
+ } else if (typeof result === 'string') {
+ usage.fail(result)
+ }
+ } catch (err) {
+ usage.fail(err.message ? err.message : err)
+ }
+ })
+ }
+
+ // 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 {
+ implied[key] = value
+ }
+ }
+ self.getImplied = function () {
+ return implied
+ }
+
+ self.implications = function (argv) {
+ var implyFail = []
+
+ Object.keys(implied).forEach(function (key) {
+ var num
+ var 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)
+ }
+ }
+
+ return self
+}