| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415 | 
							- // info about each config option.
 
- var debug = process.env.DEBUG_NOPT || process.env.NOPT_DEBUG
 
-   ? function () { console.error.apply(console, arguments) }
 
-   : function () {}
 
- var url = require("url")
 
-   , path = require("path")
 
-   , Stream = require("stream").Stream
 
-   , abbrev = require("abbrev")
 
- module.exports = exports = nopt
 
- exports.clean = clean
 
- exports.typeDefs =
 
-   { String  : { type: String,  validate: validateString  }
 
-   , Boolean : { type: Boolean, validate: validateBoolean }
 
-   , url     : { type: url,     validate: validateUrl     }
 
-   , Number  : { type: Number,  validate: validateNumber  }
 
-   , path    : { type: path,    validate: validatePath    }
 
-   , Stream  : { type: Stream,  validate: validateStream  }
 
-   , Date    : { type: Date,    validate: validateDate    }
 
-   }
 
- function nopt (types, shorthands, args, slice) {
 
-   args = args || process.argv
 
-   types = types || {}
 
-   shorthands = shorthands || {}
 
-   if (typeof slice !== "number") slice = 2
 
-   debug(types, shorthands, args, slice)
 
-   args = args.slice(slice)
 
-   var data = {}
 
-     , key
 
-     , remain = []
 
-     , cooked = args
 
-     , original = args.slice(0)
 
-   parse(args, data, remain, types, shorthands)
 
-   // now data is full
 
-   clean(data, types, exports.typeDefs)
 
-   data.argv = {remain:remain,cooked:cooked,original:original}
 
-   Object.defineProperty(data.argv, 'toString', { value: function () {
 
-     return this.original.map(JSON.stringify).join(" ")
 
-   }, enumerable: false })
 
-   return data
 
- }
 
- function clean (data, types, typeDefs) {
 
-   typeDefs = typeDefs || exports.typeDefs
 
-   var remove = {}
 
-     , typeDefault = [false, true, null, String, Array]
 
-   Object.keys(data).forEach(function (k) {
 
-     if (k === "argv") return
 
-     var val = data[k]
 
-       , isArray = Array.isArray(val)
 
-       , type = types[k]
 
-     if (!isArray) val = [val]
 
-     if (!type) type = typeDefault
 
-     if (type === Array) type = typeDefault.concat(Array)
 
-     if (!Array.isArray(type)) type = [type]
 
-     debug("val=%j", val)
 
-     debug("types=", type)
 
-     val = val.map(function (val) {
 
-       // if it's an unknown value, then parse false/true/null/numbers/dates
 
-       if (typeof val === "string") {
 
-         debug("string %j", val)
 
-         val = val.trim()
 
-         if ((val === "null" && ~type.indexOf(null))
 
-             || (val === "true" &&
 
-                (~type.indexOf(true) || ~type.indexOf(Boolean)))
 
-             || (val === "false" &&
 
-                (~type.indexOf(false) || ~type.indexOf(Boolean)))) {
 
-           val = JSON.parse(val)
 
-           debug("jsonable %j", val)
 
-         } else if (~type.indexOf(Number) && !isNaN(val)) {
 
-           debug("convert to number", val)
 
-           val = +val
 
-         } else if (~type.indexOf(Date) && !isNaN(Date.parse(val))) {
 
-           debug("convert to date", val)
 
-           val = new Date(val)
 
-         }
 
-       }
 
-       if (!types.hasOwnProperty(k)) {
 
-         return val
 
-       }
 
-       // allow `--no-blah` to set 'blah' to null if null is allowed
 
-       if (val === false && ~type.indexOf(null) &&
 
-           !(~type.indexOf(false) || ~type.indexOf(Boolean))) {
 
-         val = null
 
-       }
 
-       var d = {}
 
-       d[k] = val
 
-       debug("prevalidated val", d, val, types[k])
 
-       if (!validate(d, k, val, types[k], typeDefs)) {
 
-         if (exports.invalidHandler) {
 
-           exports.invalidHandler(k, val, types[k], data)
 
-         } else if (exports.invalidHandler !== false) {
 
-           debug("invalid: "+k+"="+val, types[k])
 
-         }
 
-         return remove
 
-       }
 
-       debug("validated val", d, val, types[k])
 
-       return d[k]
 
-     }).filter(function (val) { return val !== remove })
 
-     if (!val.length) delete data[k]
 
-     else if (isArray) {
 
-       debug(isArray, data[k], val)
 
-       data[k] = val
 
-     } else data[k] = val[0]
 
-     debug("k=%s val=%j", k, val, data[k])
 
-   })
 
- }
 
- function validateString (data, k, val) {
 
-   data[k] = String(val)
 
- }
 
- function validatePath (data, k, val) {
 
-   if (val === true) return false
 
-   if (val === null) return true
 
-   val = String(val)
 
-   var homePattern = process.platform === 'win32' ? /^~(\/|\\)/ : /^~\//
 
-   if (val.match(homePattern) && process.env.HOME) {
 
-     val = path.resolve(process.env.HOME, val.substr(2))
 
-   }
 
-   data[k] = path.resolve(String(val))
 
-   return true
 
- }
 
- function validateNumber (data, k, val) {
 
-   debug("validate Number %j %j %j", k, val, isNaN(val))
 
-   if (isNaN(val)) return false
 
-   data[k] = +val
 
- }
 
- function validateDate (data, k, val) {
 
-   debug("validate Date %j %j %j", k, val, Date.parse(val))
 
-   var s = Date.parse(val)
 
-   if (isNaN(s)) return false
 
-   data[k] = new Date(val)
 
- }
 
- function validateBoolean (data, k, val) {
 
-   if (val instanceof Boolean) val = val.valueOf()
 
-   else if (typeof val === "string") {
 
-     if (!isNaN(val)) val = !!(+val)
 
-     else if (val === "null" || val === "false") val = false
 
-     else val = true
 
-   } else val = !!val
 
-   data[k] = val
 
- }
 
- function validateUrl (data, k, val) {
 
-   val = url.parse(String(val))
 
-   if (!val.host) return false
 
-   data[k] = val.href
 
- }
 
- function validateStream (data, k, val) {
 
-   if (!(val instanceof Stream)) return false
 
-   data[k] = val
 
- }
 
- function validate (data, k, val, type, typeDefs) {
 
-   // arrays are lists of types.
 
-   if (Array.isArray(type)) {
 
-     for (var i = 0, l = type.length; i < l; i ++) {
 
-       if (type[i] === Array) continue
 
-       if (validate(data, k, val, type[i], typeDefs)) return true
 
-     }
 
-     delete data[k]
 
-     return false
 
-   }
 
-   // an array of anything?
 
-   if (type === Array) return true
 
-   // NaN is poisonous.  Means that something is not allowed.
 
-   if (type !== type) {
 
-     debug("Poison NaN", k, val, type)
 
-     delete data[k]
 
-     return false
 
-   }
 
-   // explicit list of values
 
-   if (val === type) {
 
-     debug("Explicitly allowed %j", val)
 
-     // if (isArray) (data[k] = data[k] || []).push(val)
 
-     // else data[k] = val
 
-     data[k] = val
 
-     return true
 
-   }
 
-   // now go through the list of typeDefs, validate against each one.
 
-   var ok = false
 
-     , types = Object.keys(typeDefs)
 
-   for (var i = 0, l = types.length; i < l; i ++) {
 
-     debug("test type %j %j %j", k, val, types[i])
 
-     var t = typeDefs[types[i]]
 
-     if (t &&
 
-       ((type && type.name && t.type && t.type.name) ? (type.name === t.type.name) : (type === t.type))) {
 
-       var d = {}
 
-       ok = false !== t.validate(d, k, val)
 
-       val = d[k]
 
-       if (ok) {
 
-         // if (isArray) (data[k] = data[k] || []).push(val)
 
-         // else data[k] = val
 
-         data[k] = val
 
-         break
 
-       }
 
-     }
 
-   }
 
-   debug("OK? %j (%j %j %j)", ok, k, val, types[i])
 
-   if (!ok) delete data[k]
 
-   return ok
 
- }
 
- function parse (args, data, remain, types, shorthands) {
 
-   debug("parse", args, data, remain)
 
-   var key = null
 
-     , abbrevs = abbrev(Object.keys(types))
 
-     , shortAbbr = abbrev(Object.keys(shorthands))
 
-   for (var i = 0; i < args.length; i ++) {
 
-     var arg = args[i]
 
-     debug("arg", arg)
 
-     if (arg.match(/^-{2,}$/)) {
 
-       // done with keys.
 
-       // the rest are args.
 
-       remain.push.apply(remain, args.slice(i + 1))
 
-       args[i] = "--"
 
-       break
 
-     }
 
-     var hadEq = false
 
-     if (arg.charAt(0) === "-" && arg.length > 1) {
 
-       if (arg.indexOf("=") !== -1) {
 
-         hadEq = true
 
-         var v = arg.split("=")
 
-         arg = v.shift()
 
-         v = v.join("=")
 
-         args.splice.apply(args, [i, 1].concat([arg, v]))
 
-       }
 
-       // see if it's a shorthand
 
-       // if so, splice and back up to re-parse it.
 
-       var shRes = resolveShort(arg, shorthands, shortAbbr, abbrevs)
 
-       debug("arg=%j shRes=%j", arg, shRes)
 
-       if (shRes) {
 
-         debug(arg, shRes)
 
-         args.splice.apply(args, [i, 1].concat(shRes))
 
-         if (arg !== shRes[0]) {
 
-           i --
 
-           continue
 
-         }
 
-       }
 
-       arg = arg.replace(/^-+/, "")
 
-       var no = null
 
-       while (arg.toLowerCase().indexOf("no-") === 0) {
 
-         no = !no
 
-         arg = arg.substr(3)
 
-       }
 
-       if (abbrevs[arg]) arg = abbrevs[arg]
 
-       var isArray = types[arg] === Array ||
 
-         Array.isArray(types[arg]) && types[arg].indexOf(Array) !== -1
 
-       // allow unknown things to be arrays if specified multiple times.
 
-       if (!types.hasOwnProperty(arg) && data.hasOwnProperty(arg)) {
 
-         if (!Array.isArray(data[arg]))
 
-           data[arg] = [data[arg]]
 
-         isArray = true
 
-       }
 
-       var val
 
-         , la = args[i + 1]
 
-       var isBool = typeof no === 'boolean' ||
 
-         types[arg] === Boolean ||
 
-         Array.isArray(types[arg]) && types[arg].indexOf(Boolean) !== -1 ||
 
-         (typeof types[arg] === 'undefined' && !hadEq) ||
 
-         (la === "false" &&
 
-          (types[arg] === null ||
 
-           Array.isArray(types[arg]) && ~types[arg].indexOf(null)))
 
-       if (isBool) {
 
-         // just set and move along
 
-         val = !no
 
-         // however, also support --bool true or --bool false
 
-         if (la === "true" || la === "false") {
 
-           val = JSON.parse(la)
 
-           la = null
 
-           if (no) val = !val
 
-           i ++
 
-         }
 
-         // also support "foo":[Boolean, "bar"] and "--foo bar"
 
-         if (Array.isArray(types[arg]) && la) {
 
-           if (~types[arg].indexOf(la)) {
 
-             // an explicit type
 
-             val = la
 
-             i ++
 
-           } else if ( la === "null" && ~types[arg].indexOf(null) ) {
 
-             // null allowed
 
-             val = null
 
-             i ++
 
-           } else if ( !la.match(/^-{2,}[^-]/) &&
 
-                       !isNaN(la) &&
 
-                       ~types[arg].indexOf(Number) ) {
 
-             // number
 
-             val = +la
 
-             i ++
 
-           } else if ( !la.match(/^-[^-]/) && ~types[arg].indexOf(String) ) {
 
-             // string
 
-             val = la
 
-             i ++
 
-           }
 
-         }
 
-         if (isArray) (data[arg] = data[arg] || []).push(val)
 
-         else data[arg] = val
 
-         continue
 
-       }
 
-       if (types[arg] === String && la === undefined)
 
-         la = ""
 
-       if (la && la.match(/^-{2,}$/)) {
 
-         la = undefined
 
-         i --
 
-       }
 
-       val = la === undefined ? true : la
 
-       if (isArray) (data[arg] = data[arg] || []).push(val)
 
-       else data[arg] = val
 
-       i ++
 
-       continue
 
-     }
 
-     remain.push(arg)
 
-   }
 
- }
 
- function resolveShort (arg, shorthands, shortAbbr, abbrevs) {
 
-   // handle single-char shorthands glommed together, like
 
-   // npm ls -glp, but only if there is one dash, and only if
 
-   // all of the chars are single-char shorthands, and it's
 
-   // not a match to some other abbrev.
 
-   arg = arg.replace(/^-+/, '')
 
-   // if it's an exact known option, then don't go any further
 
-   if (abbrevs[arg] === arg)
 
-     return null
 
-   // if it's an exact known shortopt, same deal
 
-   if (shorthands[arg]) {
 
-     // make it an array, if it's a list of words
 
-     if (shorthands[arg] && !Array.isArray(shorthands[arg]))
 
-       shorthands[arg] = shorthands[arg].split(/\s+/)
 
-     return shorthands[arg]
 
-   }
 
-   // first check to see if this arg is a set of single-char shorthands
 
-   var singles = shorthands.___singles
 
-   if (!singles) {
 
-     singles = Object.keys(shorthands).filter(function (s) {
 
-       return s.length === 1
 
-     }).reduce(function (l,r) {
 
-       l[r] = true
 
-       return l
 
-     }, {})
 
-     shorthands.___singles = singles
 
-     debug('shorthand singles', singles)
 
-   }
 
-   var chrs = arg.split("").filter(function (c) {
 
-     return singles[c]
 
-   })
 
-   if (chrs.join("") === arg) return chrs.map(function (c) {
 
-     return shorthands[c]
 
-   }).reduce(function (l, r) {
 
-     return l.concat(r)
 
-   }, [])
 
-   // if it's an arg abbrev, and not a literal shorthand, then prefer the arg
 
-   if (abbrevs[arg] && !shorthands[arg])
 
-     return null
 
-   // if it's an abbr for a shorthand, then use that
 
-   if (shortAbbr[arg])
 
-     arg = shortAbbr[arg]
 
-   // make it an array, if it's a list of words
 
-   if (shorthands[arg] && !Array.isArray(shorthands[arg]))
 
-     shorthands[arg] = shorthands[arg].split(/\s+/)
 
-   return shorthands[arg]
 
- }
 
 
  |