source: trip-planner-front/node_modules/nopt/lib/nopt.js@ e29cc2e

Last change on this file since e29cc2e was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 11.9 KB
Line 
1// info about each config option.
2
3var debug = process.env.DEBUG_NOPT || process.env.NOPT_DEBUG
4 ? function () { console.error.apply(console, arguments) }
5 : function () {}
6
7var url = require("url")
8 , path = require("path")
9 , Stream = require("stream").Stream
10 , abbrev = require("abbrev")
11 , os = require("os")
12
13module.exports = exports = nopt
14exports.clean = clean
15
16exports.typeDefs =
17 { String : { type: String, validate: validateString }
18 , Boolean : { type: Boolean, validate: validateBoolean }
19 , url : { type: url, validate: validateUrl }
20 , Number : { type: Number, validate: validateNumber }
21 , path : { type: path, validate: validatePath }
22 , Stream : { type: Stream, validate: validateStream }
23 , Date : { type: Date, validate: validateDate }
24 }
25
26function nopt (types, shorthands, args, slice) {
27 args = args || process.argv
28 types = types || {}
29 shorthands = shorthands || {}
30 if (typeof slice !== "number") slice = 2
31
32 debug(types, shorthands, args, slice)
33
34 args = args.slice(slice)
35 var data = {}
36 , key
37 , argv = {
38 remain: [],
39 cooked: args,
40 original: args.slice(0)
41 }
42
43 parse(args, data, argv.remain, types, shorthands)
44 // now data is full
45 clean(data, types, exports.typeDefs)
46 data.argv = argv
47 Object.defineProperty(data.argv, 'toString', { value: function () {
48 return this.original.map(JSON.stringify).join(" ")
49 }, enumerable: false })
50 return data
51}
52
53function clean (data, types, typeDefs) {
54 typeDefs = typeDefs || exports.typeDefs
55 var remove = {}
56 , typeDefault = [false, true, null, String, Array]
57
58 Object.keys(data).forEach(function (k) {
59 if (k === "argv") return
60 var val = data[k]
61 , isArray = Array.isArray(val)
62 , type = types[k]
63 if (!isArray) val = [val]
64 if (!type) type = typeDefault
65 if (type === Array) type = typeDefault.concat(Array)
66 if (!Array.isArray(type)) type = [type]
67
68 debug("val=%j", val)
69 debug("types=", type)
70 val = val.map(function (val) {
71 // if it's an unknown value, then parse false/true/null/numbers/dates
72 if (typeof val === "string") {
73 debug("string %j", val)
74 val = val.trim()
75 if ((val === "null" && ~type.indexOf(null))
76 || (val === "true" &&
77 (~type.indexOf(true) || ~type.indexOf(Boolean)))
78 || (val === "false" &&
79 (~type.indexOf(false) || ~type.indexOf(Boolean)))) {
80 val = JSON.parse(val)
81 debug("jsonable %j", val)
82 } else if (~type.indexOf(Number) && !isNaN(val)) {
83 debug("convert to number", val)
84 val = +val
85 } else if (~type.indexOf(Date) && !isNaN(Date.parse(val))) {
86 debug("convert to date", val)
87 val = new Date(val)
88 }
89 }
90
91 if (!types.hasOwnProperty(k)) {
92 return val
93 }
94
95 // allow `--no-blah` to set 'blah' to null if null is allowed
96 if (val === false && ~type.indexOf(null) &&
97 !(~type.indexOf(false) || ~type.indexOf(Boolean))) {
98 val = null
99 }
100
101 var d = {}
102 d[k] = val
103 debug("prevalidated val", d, val, types[k])
104 if (!validate(d, k, val, types[k], typeDefs)) {
105 if (exports.invalidHandler) {
106 exports.invalidHandler(k, val, types[k], data)
107 } else if (exports.invalidHandler !== false) {
108 debug("invalid: "+k+"="+val, types[k])
109 }
110 return remove
111 }
112 debug("validated val", d, val, types[k])
113 return d[k]
114 }).filter(function (val) { return val !== remove })
115
116 // if we allow Array specifically, then an empty array is how we
117 // express 'no value here', not null. Allow it.
118 if (!val.length && type.indexOf(Array) === -1) {
119 debug('VAL HAS NO LENGTH, DELETE IT', val, k, type.indexOf(Array))
120 delete data[k]
121 }
122 else if (isArray) {
123 debug(isArray, data[k], val)
124 data[k] = val
125 } else data[k] = val[0]
126
127 debug("k=%s val=%j", k, val, data[k])
128 })
129}
130
131function validateString (data, k, val) {
132 data[k] = String(val)
133}
134
135function validatePath (data, k, val) {
136 if (val === true) return false
137 if (val === null) return true
138
139 val = String(val)
140
141 var isWin = process.platform === 'win32'
142 , homePattern = isWin ? /^~(\/|\\)/ : /^~\//
143 , home = os.homedir()
144
145 if (home && val.match(homePattern)) {
146 data[k] = path.resolve(home, val.substr(2))
147 } else {
148 data[k] = path.resolve(val)
149 }
150 return true
151}
152
153function validateNumber (data, k, val) {
154 debug("validate Number %j %j %j", k, val, isNaN(val))
155 if (isNaN(val)) return false
156 data[k] = +val
157}
158
159function validateDate (data, k, val) {
160 var s = Date.parse(val)
161 debug("validate Date %j %j %j", k, val, s)
162 if (isNaN(s)) return false
163 data[k] = new Date(val)
164}
165
166function validateBoolean (data, k, val) {
167 if (val instanceof Boolean) val = val.valueOf()
168 else if (typeof val === "string") {
169 if (!isNaN(val)) val = !!(+val)
170 else if (val === "null" || val === "false") val = false
171 else val = true
172 } else val = !!val
173 data[k] = val
174}
175
176function validateUrl (data, k, val) {
177 val = url.parse(String(val))
178 if (!val.host) return false
179 data[k] = val.href
180}
181
182function validateStream (data, k, val) {
183 if (!(val instanceof Stream)) return false
184 data[k] = val
185}
186
187function validate (data, k, val, type, typeDefs) {
188 // arrays are lists of types.
189 if (Array.isArray(type)) {
190 for (var i = 0, l = type.length; i < l; i ++) {
191 if (type[i] === Array) continue
192 if (validate(data, k, val, type[i], typeDefs)) return true
193 }
194 delete data[k]
195 return false
196 }
197
198 // an array of anything?
199 if (type === Array) return true
200
201 // NaN is poisonous. Means that something is not allowed.
202 if (type !== type) {
203 debug("Poison NaN", k, val, type)
204 delete data[k]
205 return false
206 }
207
208 // explicit list of values
209 if (val === type) {
210 debug("Explicitly allowed %j", val)
211 // if (isArray) (data[k] = data[k] || []).push(val)
212 // else data[k] = val
213 data[k] = val
214 return true
215 }
216
217 // now go through the list of typeDefs, validate against each one.
218 var ok = false
219 , types = Object.keys(typeDefs)
220 for (var i = 0, l = types.length; i < l; i ++) {
221 debug("test type %j %j %j", k, val, types[i])
222 var t = typeDefs[types[i]]
223 if (t &&
224 ((type && type.name && t.type && t.type.name) ? (type.name === t.type.name) : (type === t.type))) {
225 var d = {}
226 ok = false !== t.validate(d, k, val)
227 val = d[k]
228 if (ok) {
229 // if (isArray) (data[k] = data[k] || []).push(val)
230 // else data[k] = val
231 data[k] = val
232 break
233 }
234 }
235 }
236 debug("OK? %j (%j %j %j)", ok, k, val, types[i])
237
238 if (!ok) delete data[k]
239 return ok
240}
241
242function parse (args, data, remain, types, shorthands) {
243 debug("parse", args, data, remain)
244
245 var key = null
246 , abbrevs = abbrev(Object.keys(types))
247 , shortAbbr = abbrev(Object.keys(shorthands))
248
249 for (var i = 0; i < args.length; i ++) {
250 var arg = args[i]
251 debug("arg", arg)
252
253 if (arg.match(/^-{2,}$/)) {
254 // done with keys.
255 // the rest are args.
256 remain.push.apply(remain, args.slice(i + 1))
257 args[i] = "--"
258 break
259 }
260 var hadEq = false
261 if (arg.charAt(0) === "-" && arg.length > 1) {
262 var at = arg.indexOf('=')
263 if (at > -1) {
264 hadEq = true
265 var v = arg.substr(at + 1)
266 arg = arg.substr(0, at)
267 args.splice(i, 1, arg, v)
268 }
269
270 // see if it's a shorthand
271 // if so, splice and back up to re-parse it.
272 var shRes = resolveShort(arg, shorthands, shortAbbr, abbrevs)
273 debug("arg=%j shRes=%j", arg, shRes)
274 if (shRes) {
275 debug(arg, shRes)
276 args.splice.apply(args, [i, 1].concat(shRes))
277 if (arg !== shRes[0]) {
278 i --
279 continue
280 }
281 }
282 arg = arg.replace(/^-+/, "")
283 var no = null
284 while (arg.toLowerCase().indexOf("no-") === 0) {
285 no = !no
286 arg = arg.substr(3)
287 }
288
289 if (abbrevs[arg]) arg = abbrevs[arg]
290
291 var argType = types[arg]
292 var isTypeArray = Array.isArray(argType)
293 if (isTypeArray && argType.length === 1) {
294 isTypeArray = false
295 argType = argType[0]
296 }
297
298 var isArray = argType === Array ||
299 isTypeArray && argType.indexOf(Array) !== -1
300
301 // allow unknown things to be arrays if specified multiple times.
302 if (!types.hasOwnProperty(arg) && data.hasOwnProperty(arg)) {
303 if (!Array.isArray(data[arg]))
304 data[arg] = [data[arg]]
305 isArray = true
306 }
307
308 var val
309 , la = args[i + 1]
310
311 var isBool = typeof no === 'boolean' ||
312 argType === Boolean ||
313 isTypeArray && argType.indexOf(Boolean) !== -1 ||
314 (typeof argType === 'undefined' && !hadEq) ||
315 (la === "false" &&
316 (argType === null ||
317 isTypeArray && ~argType.indexOf(null)))
318
319 if (isBool) {
320 // just set and move along
321 val = !no
322 // however, also support --bool true or --bool false
323 if (la === "true" || la === "false") {
324 val = JSON.parse(la)
325 la = null
326 if (no) val = !val
327 i ++
328 }
329
330 // also support "foo":[Boolean, "bar"] and "--foo bar"
331 if (isTypeArray && la) {
332 if (~argType.indexOf(la)) {
333 // an explicit type
334 val = la
335 i ++
336 } else if ( la === "null" && ~argType.indexOf(null) ) {
337 // null allowed
338 val = null
339 i ++
340 } else if ( !la.match(/^-{2,}[^-]/) &&
341 !isNaN(la) &&
342 ~argType.indexOf(Number) ) {
343 // number
344 val = +la
345 i ++
346 } else if ( !la.match(/^-[^-]/) && ~argType.indexOf(String) ) {
347 // string
348 val = la
349 i ++
350 }
351 }
352
353 if (isArray) (data[arg] = data[arg] || []).push(val)
354 else data[arg] = val
355
356 continue
357 }
358
359 if (argType === String) {
360 if (la === undefined) {
361 la = ""
362 } else if (la.match(/^-{1,2}[^-]+/)) {
363 la = ""
364 i --
365 }
366 }
367
368 if (la && la.match(/^-{2,}$/)) {
369 la = undefined
370 i --
371 }
372
373 val = la === undefined ? true : la
374 if (isArray) (data[arg] = data[arg] || []).push(val)
375 else data[arg] = val
376
377 i ++
378 continue
379 }
380 remain.push(arg)
381 }
382}
383
384function resolveShort (arg, shorthands, shortAbbr, abbrevs) {
385 // handle single-char shorthands glommed together, like
386 // npm ls -glp, but only if there is one dash, and only if
387 // all of the chars are single-char shorthands, and it's
388 // not a match to some other abbrev.
389 arg = arg.replace(/^-+/, '')
390
391 // if it's an exact known option, then don't go any further
392 if (abbrevs[arg] === arg)
393 return null
394
395 // if it's an exact known shortopt, same deal
396 if (shorthands[arg]) {
397 // make it an array, if it's a list of words
398 if (shorthands[arg] && !Array.isArray(shorthands[arg]))
399 shorthands[arg] = shorthands[arg].split(/\s+/)
400
401 return shorthands[arg]
402 }
403
404 // first check to see if this arg is a set of single-char shorthands
405 var singles = shorthands.___singles
406 if (!singles) {
407 singles = Object.keys(shorthands).filter(function (s) {
408 return s.length === 1
409 }).reduce(function (l,r) {
410 l[r] = true
411 return l
412 }, {})
413 shorthands.___singles = singles
414 debug('shorthand singles', singles)
415 }
416
417 var chrs = arg.split("").filter(function (c) {
418 return singles[c]
419 })
420
421 if (chrs.join("") === arg) return chrs.map(function (c) {
422 return shorthands[c]
423 }).reduce(function (l, r) {
424 return l.concat(r)
425 }, [])
426
427
428 // if it's an arg abbrev, and not a literal shorthand, then prefer the arg
429 if (abbrevs[arg] && !shorthands[arg])
430 return null
431
432 // if it's an abbr for a shorthand, then use that
433 if (shortAbbr[arg])
434 arg = shortAbbr[arg]
435
436 // make it an array, if it's a list of words
437 if (shorthands[arg] && !Array.isArray(shorthands[arg]))
438 shorthands[arg] = shorthands[arg].split(/\s+/)
439
440 return shorthands[arg]
441}
Note: See TracBrowser for help on using the repository browser.