Initial commit
[yaffs-website] / node_modules / yargs / lib / validation.js
1 const objFilter = require('./obj-filter')
2
3 // validation-type-stuff, missing params,
4 // bad implications, custom checks.
5 module.exports = function (yargs, usage, y18n) {
6   const __ = y18n.__
7   const __n = y18n.__n
8   const self = {}
9
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
15
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) {
20         usage.fail(
21           __('Not enough non-option arguments: got %s, need at least %s', argv._.length, demanded._.count)
22         )
23       } else {
24         usage.fail(
25           __('Too many non-option arguments: got %s, maximum of %s', argv._.length, demanded._.max)
26         )
27       }
28     }
29   }
30
31   // validate the appropriate # of <required>
32   // positional arguments were provided:
33   self.positionalCount = function (required, observed) {
34     if (observed < required) {
35       usage.fail(
36         __('Not enough non-option arguments: got %s, need at least %s', observed, required)
37       )
38     }
39   }
40
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()
46
47     if (options.requiresArg.length > 0) {
48       const missingRequiredArgs = []
49
50       options.requiresArg.forEach(function (key) {
51         const value = argv[key]
52
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)
59         }
60       })
61
62       if (missingRequiredArgs.length > 0) {
63         usage.fail(__n(
64           'Missing argument value: %s',
65           'Missing argument values: %s',
66           missingRequiredArgs.length,
67           missingRequiredArgs.join(', ')
68         ))
69       }
70     }
71   }
72
73   // make sure all the required arguments are present.
74   self.requiredArguments = function (argv) {
75     const demanded = yargs.getDemanded()
76     var missing = null
77
78     Object.keys(demanded).forEach(function (key) {
79       if (!argv.hasOwnProperty(key)) {
80         missing = missing || {}
81         missing[key] = demanded[key]
82       }
83     })
84
85     if (missing) {
86       const customMsgs = []
87       Object.keys(missing).forEach(function (key) {
88         const msg = missing[key].msg
89         if (msg && customMsgs.indexOf(msg) < 0) {
90           customMsgs.push(msg)
91         }
92       })
93
94       const customMsg = customMsgs.length ? '\n' + customMsgs.join('\n') : ''
95
96       usage.fail(__n(
97         'Missing required argument: %s',
98         'Missing required arguments: %s',
99         Object.keys(missing).length,
100         Object.keys(missing).join(', ') + customMsg
101       ))
102     }
103   }
104
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()
111     const unknown = []
112     const currentContext = yargs.getContext()
113
114     Object.keys(aliases).forEach(function (key) {
115       aliases[key].forEach(function (alias) {
116         aliasLookup[alias] = key
117       })
118     })
119
120     Object.keys(argv).forEach(function (key) {
121       if (key !== '$0' && key !== '_' &&
122         !descriptions.hasOwnProperty(key) &&
123         !demanded.hasOwnProperty(key) &&
124         !aliasLookup.hasOwnProperty(key)) {
125         unknown.push(key)
126       }
127     })
128
129     if (commandKeys.length > 0) {
130       argv._.slice(currentContext.commands.length).forEach(function (key) {
131         if (commandKeys.indexOf(key) === -1) {
132           unknown.push(key)
133         }
134       })
135     }
136
137     if (unknown.length > 0) {
138       usage.fail(__n(
139         'Unknown argument: %s',
140         'Unknown arguments: %s',
141         unknown.length,
142         unknown.join(', ')
143       ))
144     }
145   }
146
147   // validate arguments limited to enumerated choices
148   self.limitedChoices = function (argv) {
149     const options = yargs.getOptions()
150     const invalid = {}
151
152     if (!Object.keys(options.choices).length) return
153
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)
161           }
162         })
163       }
164     })
165
166     const invalidKeys = Object.keys(invalid)
167
168     if (!invalidKeys.length) return
169
170     var msg = __('Invalid values:')
171     invalidKeys.forEach(function (key) {
172       msg += '\n  ' + __(
173         'Argument: %s, Given: %s, Choices: %s',
174         key,
175         usage.stringifiedValues(invalid[key]),
176         usage.stringifiedValues(options.choices[key])
177       )
178     })
179     usage.fail(msg)
180   }
181
182   // custom checks, added using the `check` option on yargs.
183   var checks = []
184   self.check = function (f) {
185     checks.push(f)
186   }
187
188   self.customChecks = function (argv, aliases) {
189     checks.forEach(function (f) {
190       try {
191         const result = f(argv, aliases)
192         if (!result) {
193           usage.fail(__('Argument check failed: %s', f.toString()))
194         } else if (typeof result === 'string') {
195           usage.fail(result)
196         }
197       } catch (err) {
198         usage.fail(err.message ? err.message : err, err)
199       }
200     })
201   }
202
203   // check implications, argument foo implies => argument bar.
204   var implied = {}
205   self.implies = function (key, value) {
206     if (typeof key === 'object') {
207       Object.keys(key).forEach(function (k) {
208         self.implies(k, key[k])
209       })
210     } else {
211       implied[key] = value
212     }
213   }
214   self.getImplied = function () {
215     return implied
216   }
217
218   self.implications = function (argv) {
219     const implyFail = []
220
221     Object.keys(implied).forEach(function (key) {
222       var booleanNegation
223       if (yargs.getOptions().configuration['boolean-negation'] === false) {
224         booleanNegation = false
225       } else {
226         booleanNegation = true
227       }
228       var num
229       const origKey = key
230       var value = implied[key]
231
232       // convert string '1' to number 1
233       num = Number(key)
234       key = isNaN(num) ? key : num
235
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]
242         key = !argv[key]
243       } else {
244         // check if key exists
245         key = argv[key]
246       }
247
248       num = Number(value)
249       value = isNaN(num) ? value : num
250
251       if (typeof value === 'number') {
252         value = argv._.length >= value
253       } else if (value.match(/^--no-.+/) && booleanNegation) {
254         value = value.match(/^--no-(.+)/)[1]
255         value = !argv[value]
256       } else {
257         value = argv[value]
258       }
259
260       if (key && !value) {
261         implyFail.push(origKey)
262       }
263     })
264
265     if (implyFail.length) {
266       var msg = __('Implications failed:') + '\n'
267
268       implyFail.forEach(function (key) {
269         msg += ('  ' + key + ' -> ' + implied[key])
270       })
271
272       usage.fail(msg)
273     }
274   }
275
276   self.reset = function (globalLookup) {
277     implied = objFilter(implied, function (k, v) {
278       return globalLookup[k]
279     })
280     checks = []
281     return self
282   }
283
284   return self
285 }