~cordova-ubuntu/cordova-cli/trunk

« back to all changes in this revision

Viewing changes to node_modules/cordova/_vendor/nopt/2.2.0/lib/nopt.js

  • Committer: Robert Bruce Park
  • Date: 2014-02-26 21:27:56 UTC
  • mfrom: (44.1.5 3.4-release)
  • Revision ID: robert.park@canonical.com-20140226212756-6jmoiqugw0f1ebxb
Update to 3.4.0 stable release.

Show diffs side-by-side

added added

removed removed

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