Built motion from commit 6a09e18b.|2.6.11
[motion2.git] / legacy-libs / grpc / node_modules / yargs / lib / parser.js
1 // fancy-pants parsing of argv, originally forked
2 // from minimist: https://www.npmjs.com/package/minimist
3 var camelCase = require('camelcase')
4 var path = require('path')
5
6 function increment (orig) {
7   return orig !== undefined ? orig + 1 : 0
8 }
9
10 module.exports = function (args, opts, y18n) {
11   if (!opts) opts = {}
12
13   var __ = y18n.__
14   var error = null
15   var flags = { arrays: {}, bools: {}, strings: {}, counts: {}, normalize: {}, configs: {}, defaulted: {} }
16
17   ;[].concat(opts['array']).filter(Boolean).forEach(function (key) {
18     flags.arrays[key] = true
19   })
20
21   ;[].concat(opts['boolean']).filter(Boolean).forEach(function (key) {
22     flags.bools[key] = true
23   })
24
25   ;[].concat(opts.string).filter(Boolean).forEach(function (key) {
26     flags.strings[key] = true
27   })
28
29   ;[].concat(opts.count).filter(Boolean).forEach(function (key) {
30     flags.counts[key] = true
31   })
32
33   ;[].concat(opts.normalize).filter(Boolean).forEach(function (key) {
34     flags.normalize[key] = true
35   })
36
37   Object.keys(opts.config).forEach(function (k) {
38     flags.configs[k] = opts.config[k]
39   })
40
41   var aliases = {}
42   var newAliases = {}
43
44   extendAliases(opts.key)
45   extendAliases(opts.alias)
46   extendAliases(opts.default)
47
48   var defaults = opts['default'] || {}
49   Object.keys(defaults).forEach(function (key) {
50     if (/-/.test(key) && !opts.alias[key]) {
51       aliases[key] = aliases[key] || []
52     }
53     (aliases[key] || []).forEach(function (alias) {
54       defaults[alias] = defaults[key]
55     })
56   })
57
58   var argv = { _: [] }
59
60   Object.keys(flags.bools).forEach(function (key) {
61     setArg(key, !(key in defaults) ? false : defaults[key])
62     setDefaulted(key)
63   })
64
65   var notFlags = []
66   if (args.indexOf('--') !== -1) {
67     notFlags = args.slice(args.indexOf('--') + 1)
68     args = args.slice(0, args.indexOf('--'))
69   }
70
71   for (var i = 0; i < args.length; i++) {
72     var arg = args[i]
73     var broken
74     var key
75     var letters
76     var m
77     var next
78     var value
79
80     // -- seperated by =
81     if (arg.match(/^--.+=/)) {
82       // Using [\s\S] instead of . because js doesn't support the
83       // 'dotall' regex modifier. See:
84       // http://stackoverflow.com/a/1068308/13216
85       m = arg.match(/^--([^=]+)=([\s\S]*)$/)
86
87       // nargs format = '--f=monkey washing cat'
88       if (checkAllAliases(m[1], opts.narg)) {
89         args.splice(i + 1, m[1], m[2])
90         i = eatNargs(i, m[1], args)
91       // arrays format = '--f=a b c'
92       } else if (checkAllAliases(m[1], flags.arrays) && args.length > i + 1) {
93         args.splice(i + 1, m[1], m[2])
94         i = eatArray(i, m[1], args)
95       } else {
96         setArg(m[1], m[2])
97       }
98     } else if (arg.match(/^--no-.+/)) {
99       key = arg.match(/^--no-(.+)/)[1]
100       setArg(key, false)
101
102     // -- seperated by space.
103     } else if (arg.match(/^--.+/)) {
104       key = arg.match(/^--(.+)/)[1]
105
106       // nargs format = '--foo a b c'
107       if (checkAllAliases(key, opts.narg)) {
108         i = eatNargs(i, key, args)
109       // array format = '--foo a b c'
110       } else if (checkAllAliases(key, flags.arrays) && args.length > i + 1) {
111         i = eatArray(i, key, args)
112       } else {
113         next = args[i + 1]
114
115         if (next !== undefined && !next.match(/^-/) &&
116           !checkAllAliases(key, flags.bools) &&
117           !checkAllAliases(key, flags.counts)) {
118           setArg(key, next)
119           i++
120         } else if (/^(true|false)$/.test(next)) {
121           setArg(key, next)
122           i++
123         } else {
124           setArg(key, defaultForType(guessType(key, flags)))
125         }
126       }
127
128     // dot-notation flag seperated by '='.
129     } else if (arg.match(/^-.\..+=/)) {
130       m = arg.match(/^-([^=]+)=([\s\S]*)$/)
131       setArg(m[1], m[2])
132
133     // dot-notation flag seperated by space.
134     } else if (arg.match(/^-.\..+/)) {
135       next = args[i + 1]
136       key = arg.match(/^-(.\..+)/)[1]
137
138       if (next !== undefined && !next.match(/^-/) &&
139         !checkAllAliases(key, flags.bools) &&
140         !checkAllAliases(key, flags.counts)) {
141         setArg(key, next)
142         i++
143       } else {
144         setArg(key, defaultForType(guessType(key, flags)))
145       }
146     } else if (arg.match(/^-[^-]+/)) {
147       letters = arg.slice(1, -1).split('')
148       broken = false
149
150       for (var j = 0; j < letters.length; j++) {
151         next = arg.slice(j + 2)
152
153         if (letters[j + 1] && letters[j + 1] === '=') {
154           value = arg.slice(j + 3)
155           key = letters[j]
156
157           // nargs format = '-f=monkey washing cat'
158           if (checkAllAliases(letters[j], opts.narg)) {
159             args.splice(i + 1, 0, value)
160             i = eatNargs(i, key, args)
161           // array format = '-f=a b c'
162           } else if (checkAllAliases(key, flags.arrays) && args.length > i + 1) {
163             args.splice(i + 1, 0, value)
164             i = eatArray(i, key, args)
165           } else {
166             setArg(key, value)
167           }
168
169           broken = true
170           break
171         }
172
173         if (next === '-') {
174           setArg(letters[j], next)
175           continue
176         }
177
178         if (/[A-Za-z]/.test(letters[j]) &&
179           /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
180           setArg(letters[j], next)
181           broken = true
182           break
183         }
184
185         if (letters[j + 1] && letters[j + 1].match(/\W/)) {
186           setArg(letters[j], arg.slice(j + 2))
187           broken = true
188           break
189         } else {
190           setArg(letters[j], defaultForType(guessType(letters[j], flags)))
191         }
192       }
193
194       key = arg.slice(-1)[0]
195
196       if (!broken && key !== '-') {
197         // nargs format = '-f a b c'
198         if (checkAllAliases(key, opts.narg)) {
199           i = eatNargs(i, key, args)
200         // array format = '-f a b c'
201         } else if (checkAllAliases(key, flags.arrays) && args.length > i + 1) {
202           i = eatArray(i, key, args)
203         } else {
204           if (args[i + 1] && !/^(-|--)[^-]/.test(args[i + 1]) &&
205             !checkAllAliases(key, flags.bools) &&
206             !checkAllAliases(key, flags.counts)) {
207             setArg(key, args[i + 1])
208             i++
209           } else if (args[i + 1] && /true|false/.test(args[i + 1])) {
210             setArg(key, args[i + 1])
211             i++
212           } else {
213             setArg(key, defaultForType(guessType(key, flags)))
214           }
215         }
216       }
217     } else {
218       argv._.push(
219         flags.strings['_'] || !isNumber(arg) ? arg : Number(arg)
220       )
221     }
222   }
223
224   // order of precedence:
225   // 1. command line arg
226   // 2. value from config file
227   // 3. value from env var
228   // 4. configured default value
229   applyEnvVars(opts, argv, true) // special case: check env vars that point to config file
230   setConfig(argv)
231   applyEnvVars(opts, argv, false)
232   applyDefaultsAndAliases(argv, aliases, defaults)
233
234   Object.keys(flags.counts).forEach(function (key) {
235     setArg(key, defaults[key])
236   })
237
238   notFlags.forEach(function (key) {
239     argv._.push(key)
240   })
241
242   // how many arguments should we consume, based
243   // on the nargs option?
244   function eatNargs (i, key, args) {
245     var toEat = checkAllAliases(key, opts.narg)
246
247     if (args.length - (i + 1) < toEat) error = Error(__('Not enough arguments following: %s', key))
248
249     for (var ii = i + 1; ii < (toEat + i + 1); ii++) {
250       setArg(key, args[ii])
251     }
252
253     return (i + toEat)
254   }
255
256   // if an option is an array, eat all non-hyphenated arguments
257   // following it... YUM!
258   // e.g., --foo apple banana cat becomes ["apple", "banana", "cat"]
259   function eatArray (i, key, args) {
260     for (var ii = i + 1; ii < args.length; ii++) {
261       if (/^-/.test(args[ii])) break
262       i = ii
263       setArg(key, args[ii])
264     }
265
266     return i
267   }
268
269   function setArg (key, val) {
270     unsetDefaulted(key)
271
272     // handle parsing boolean arguments --foo=true --bar false.
273     if (checkAllAliases(key, flags.bools) || checkAllAliases(key, flags.counts)) {
274       if (typeof val === 'string') val = val === 'true'
275     }
276
277     if (/-/.test(key) && !(aliases[key] && aliases[key].length)) {
278       var c = camelCase(key)
279       aliases[key] = [c]
280       newAliases[c] = true
281     }
282
283     var value = !checkAllAliases(key, flags.strings) && isNumber(val) ? Number(val) : val
284
285     if (checkAllAliases(key, flags.counts)) {
286       value = increment
287     }
288
289     var splitKey = key.split('.')
290     setKey(argv, splitKey, value)
291
292     // alias references an inner-value within
293     // a dot-notation object. see #279.
294     if (~key.indexOf('.') && aliases[key]) {
295       aliases[key].forEach(function (x) {
296         x = x.split('.')
297         setKey(argv, x, value)
298       })
299     }
300
301     ;(aliases[splitKey[0]] || []).forEach(function (x) {
302       x = x.split('.')
303
304       // handle populating dot notation for both
305       // the key and its aliases.
306       if (splitKey.length > 1) {
307         var a = [].concat(splitKey)
308         a.shift() // nuke the old key.
309         x = x.concat(a)
310       }
311
312       setKey(argv, x, value)
313     })
314
315     var keys = [key].concat(aliases[key] || [])
316     for (var i = 0, l = keys.length; i < l; i++) {
317       if (flags.normalize[keys[i]]) {
318         keys.forEach(function (key) {
319           argv.__defineSetter__(key, function (v) {
320             val = path.normalize(v)
321           })
322
323           argv.__defineGetter__(key, function () {
324             return typeof val === 'string' ? path.normalize(val) : val
325           })
326         })
327         break
328       }
329     }
330   }
331
332   // set args from config.json file, this should be
333   // applied last so that defaults can be applied.
334   function setConfig (argv) {
335     var configLookup = {}
336
337     // expand defaults/aliases, in-case any happen to reference
338     // the config.json file.
339     applyDefaultsAndAliases(configLookup, aliases, defaults)
340
341     Object.keys(flags.configs).forEach(function (configKey) {
342       var configPath = argv[configKey] || configLookup[configKey]
343       if (configPath) {
344         try {
345           var config = null
346           var resolvedConfigPath = path.resolve(process.cwd(), configPath)
347
348           if (typeof flags.configs[configKey] === 'function') {
349             try {
350               config = flags.configs[configKey](resolvedConfigPath)
351             } catch (e) {
352               config = e
353             }
354             if (config instanceof Error) {
355               error = config
356               return
357             }
358           } else {
359             config = require(resolvedConfigPath)
360           }
361
362           Object.keys(config).forEach(function (key) {
363             // setting arguments via CLI takes precedence over
364             // values within the config file.
365             if (argv[key] === undefined || (flags.defaulted[key])) {
366               delete argv[key]
367               setArg(key, config[key])
368             }
369           })
370         } catch (ex) {
371           if (argv[configKey]) error = Error(__('Invalid JSON config file: %s', configPath))
372         }
373       }
374     })
375   }
376
377   function applyEnvVars (opts, argv, configOnly) {
378     if (typeof opts.envPrefix === 'undefined') return
379
380     var prefix = typeof opts.envPrefix === 'string' ? opts.envPrefix : ''
381     Object.keys(process.env).forEach(function (envVar) {
382       if (prefix === '' || envVar.lastIndexOf(prefix, 0) === 0) {
383         var key = camelCase(envVar.substring(prefix.length))
384         if (((configOnly && flags.configs[key]) || !configOnly) && (!(key in argv) || flags.defaulted[key])) {
385           setArg(key, process.env[envVar])
386         }
387       }
388     })
389   }
390
391   function applyDefaultsAndAliases (obj, aliases, defaults) {
392     Object.keys(defaults).forEach(function (key) {
393       if (!hasKey(obj, key.split('.'))) {
394         setKey(obj, key.split('.'), defaults[key])
395
396         ;(aliases[key] || []).forEach(function (x) {
397           if (hasKey(obj, x.split('.'))) return
398           setKey(obj, x.split('.'), defaults[key])
399         })
400       }
401     })
402   }
403
404   function hasKey (obj, keys) {
405     var o = obj
406     keys.slice(0, -1).forEach(function (key) {
407       o = (o[key] || {})
408     })
409
410     var key = keys[keys.length - 1]
411
412     if (typeof o !== 'object') return false
413     else return key in o
414   }
415
416   function setKey (obj, keys, value) {
417     var o = obj
418     keys.slice(0, -1).forEach(function (key) {
419       if (o[key] === undefined) o[key] = {}
420       o = o[key]
421     })
422
423     var key = keys[keys.length - 1]
424     if (value === increment) {
425       o[key] = increment(o[key])
426     } else if (o[key] === undefined && checkAllAliases(key, flags.arrays)) {
427       o[key] = Array.isArray(value) ? value : [value]
428     } else if (o[key] === undefined || typeof o[key] === 'boolean') {
429       o[key] = value
430     } else if (Array.isArray(o[key])) {
431       o[key].push(value)
432     } else {
433       o[key] = [ o[key], value ]
434     }
435   }
436
437   // extend the aliases list with inferred aliases.
438   function extendAliases (obj) {
439     Object.keys(obj || {}).forEach(function (key) {
440       // short-circuit if we've already added a key
441       // to the aliases array, for example it might
442       // exist in both 'opts.default' and 'opts.key'.
443       if (aliases[key]) return
444
445       aliases[key] = [].concat(opts.alias[key] || [])
446       // For "--option-name", also set argv.optionName
447       aliases[key].concat(key).forEach(function (x) {
448         if (/-/.test(x)) {
449           var c = camelCase(x)
450           aliases[key].push(c)
451           newAliases[c] = true
452         }
453       })
454       aliases[key].forEach(function (x) {
455         aliases[x] = [key].concat(aliases[key].filter(function (y) {
456           return x !== y
457         }))
458       })
459     })
460   }
461
462   // check if a flag is set for any of a key's aliases.
463   function checkAllAliases (key, flag) {
464     var isSet = false
465     var toCheck = [].concat(aliases[key] || [], key)
466
467     toCheck.forEach(function (key) {
468       if (flag[key]) isSet = flag[key]
469     })
470
471     return isSet
472   }
473
474   function setDefaulted (key) {
475     [].concat(aliases[key] || [], key).forEach(function (k) {
476       flags.defaulted[k] = true
477     })
478   }
479
480   function unsetDefaulted (key) {
481     [].concat(aliases[key] || [], key).forEach(function (k) {
482       delete flags.defaulted[k]
483     })
484   }
485
486   // return a default value, given the type of a flag.,
487   // e.g., key of type 'string' will default to '', rather than 'true'.
488   function defaultForType (type) {
489     var def = {
490       boolean: true,
491       string: '',
492       array: []
493     }
494
495     return def[type]
496   }
497
498   // given a flag, enforce a default type.
499   function guessType (key, flags) {
500     var type = 'boolean'
501
502     if (flags.strings && flags.strings[key]) type = 'string'
503     else if (flags.arrays && flags.arrays[key]) type = 'array'
504
505     return type
506   }
507
508   function isNumber (x) {
509     if (typeof x === 'number') return true
510     if (/^0x[0-9a-f]+$/i.test(x)) return true
511     return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x)
512   }
513
514   return {
515     argv: argv,
516     aliases: aliases,
517     error: error,
518     newAliases: newAliases
519   }
520 }