1 const objFilter = require('./obj-filter')
3 // validation-type-stuff, missing params,
4 // bad implications, custom checks.
5 module.exports = function (yargs, usage, y18n) {
10 // validate appropriate # of non-option
11 // arguments were provided, i.e., '_'.
12 self.nonOptionCount = function (argv) {
13 const demanded = yargs.getDemanded()
14 const _s = argv._.length
16 if (demanded._ && (_s < demanded._.count || _s > demanded._.max)) {
17 if (demanded._.msg !== undefined) {
18 usage.fail(demanded._.msg)
19 } else if (_s < demanded._.count) {
21 __('Not enough non-option arguments: got %s, need at least %s', argv._.length, demanded._.count)
25 __('Too many non-option arguments: got %s, maximum of %s', argv._.length, demanded._.max)
31 // validate the appropriate # of <required>
32 // positional arguments were provided:
33 self.positionalCount = function (required, observed) {
34 if (observed < required) {
36 __('Not enough non-option arguments: got %s, need at least %s', observed, required)
41 // make sure that any args that require an
42 // value (--foo=bar), have a value.
43 self.missingArgumentValue = function (argv) {
44 const defaultValues = [true, false, '']
45 const options = yargs.getOptions()
47 if (options.requiresArg.length > 0) {
48 const missingRequiredArgs = []
50 options.requiresArg.forEach(function (key) {
51 const value = argv[key]
53 // if a value is explicitly requested,
54 // flag argument as missing if it does not
55 // look like foo=bar was entered.
56 if (~defaultValues.indexOf(value) ||
57 (Array.isArray(value) && !value.length)) {
58 missingRequiredArgs.push(key)
62 if (missingRequiredArgs.length > 0) {
64 'Missing argument value: %s',
65 'Missing argument values: %s',
66 missingRequiredArgs.length,
67 missingRequiredArgs.join(', ')
73 // make sure all the required arguments are present.
74 self.requiredArguments = function (argv) {
75 const demanded = yargs.getDemanded()
78 Object.keys(demanded).forEach(function (key) {
79 if (!argv.hasOwnProperty(key)) {
80 missing = missing || {}
81 missing[key] = demanded[key]
87 Object.keys(missing).forEach(function (key) {
88 const msg = missing[key].msg
89 if (msg && customMsgs.indexOf(msg) < 0) {
94 const customMsg = customMsgs.length ? '\n' + customMsgs.join('\n') : ''
97 'Missing required argument: %s',
98 'Missing required arguments: %s',
99 Object.keys(missing).length,
100 Object.keys(missing).join(', ') + customMsg
105 // check for unknown arguments (strict-mode).
106 self.unknownArguments = function (argv, aliases) {
107 const aliasLookup = {}
108 const descriptions = usage.getDescriptions()
109 const demanded = yargs.getDemanded()
110 const commandKeys = yargs.getCommandInstance().getCommands()
112 const currentContext = yargs.getContext()
114 Object.keys(aliases).forEach(function (key) {
115 aliases[key].forEach(function (alias) {
116 aliasLookup[alias] = key
120 Object.keys(argv).forEach(function (key) {
121 if (key !== '$0' && key !== '_' &&
122 !descriptions.hasOwnProperty(key) &&
123 !demanded.hasOwnProperty(key) &&
124 !aliasLookup.hasOwnProperty(key)) {
129 if (commandKeys.length > 0) {
130 argv._.slice(currentContext.commands.length).forEach(function (key) {
131 if (commandKeys.indexOf(key) === -1) {
137 if (unknown.length > 0) {
139 'Unknown argument: %s',
140 'Unknown arguments: %s',
147 // validate arguments limited to enumerated choices
148 self.limitedChoices = function (argv) {
149 const options = yargs.getOptions()
152 if (!Object.keys(options.choices).length) return
154 Object.keys(argv).forEach(function (key) {
155 if (key !== '$0' && key !== '_' &&
156 options.choices.hasOwnProperty(key)) {
157 [].concat(argv[key]).forEach(function (value) {
158 // TODO case-insensitive configurability
159 if (options.choices[key].indexOf(value) === -1) {
160 invalid[key] = (invalid[key] || []).concat(value)
166 const invalidKeys = Object.keys(invalid)
168 if (!invalidKeys.length) return
170 var msg = __('Invalid values:')
171 invalidKeys.forEach(function (key) {
173 'Argument: %s, Given: %s, Choices: %s',
175 usage.stringifiedValues(invalid[key]),
176 usage.stringifiedValues(options.choices[key])
182 // custom checks, added using the `check` option on yargs.
184 self.check = function (f) {
188 self.customChecks = function (argv, aliases) {
189 checks.forEach(function (f) {
191 const result = f(argv, aliases)
193 usage.fail(__('Argument check failed: %s', f.toString()))
194 } else if (typeof result === 'string') {
198 usage.fail(err.message ? err.message : err, err)
203 // check implications, argument foo implies => argument bar.
205 self.implies = function (key, value) {
206 if (typeof key === 'object') {
207 Object.keys(key).forEach(function (k) {
208 self.implies(k, key[k])
214 self.getImplied = function () {
218 self.implications = function (argv) {
221 Object.keys(implied).forEach(function (key) {
223 if (yargs.getOptions().configuration['boolean-negation'] === false) {
224 booleanNegation = false
226 booleanNegation = true
230 var value = implied[key]
232 // convert string '1' to number 1
234 key = isNaN(num) ? key : num
236 if (typeof key === 'number') {
237 // check length of argv._
238 key = argv._.length >= key
239 } else if (key.match(/^--no-.+/) && booleanNegation) {
240 // check if key doesn't exist
241 key = key.match(/^--no-(.+)/)[1]
244 // check if key exists
249 value = isNaN(num) ? value : num
251 if (typeof value === 'number') {
252 value = argv._.length >= value
253 } else if (value.match(/^--no-.+/) && booleanNegation) {
254 value = value.match(/^--no-(.+)/)[1]
261 implyFail.push(origKey)
265 if (implyFail.length) {
266 var msg = __('Implications failed:') + '\n'
268 implyFail.forEach(function (key) {
269 msg += (' ' + key + ' -> ' + implied[key])
276 self.reset = function (globalLookup) {
277 implied = objFilter(implied, function (k, v) {
278 return globalLookup[k]