Repositorio del curso CCOM4030 el semestre B91 del proyecto Artesanías con el Instituto de Cultura

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. // info about each config option.
  2. var debug = process.env.DEBUG_NOPT || process.env.NOPT_DEBUG
  3. ? function () { console.error.apply(console, arguments) }
  4. : function () {}
  5. var url = require("url")
  6. , path = require("path")
  7. , Stream = require("stream").Stream
  8. , abbrev = require("abbrev")
  9. module.exports = exports = nopt
  10. exports.clean = clean
  11. exports.typeDefs =
  12. { String : { type: String, validate: validateString }
  13. , Boolean : { type: Boolean, validate: validateBoolean }
  14. , url : { type: url, validate: validateUrl }
  15. , Number : { type: Number, validate: validateNumber }
  16. , path : { type: path, validate: validatePath }
  17. , Stream : { type: Stream, validate: validateStream }
  18. }
  19. function nopt (types, shorthands, args, slice) {
  20. args = args || process.argv
  21. types = types || {}
  22. shorthands = shorthands || {}
  23. if (typeof slice !== "number") slice = 2
  24. debug(types, shorthands, args, slice)
  25. args = args.slice(slice)
  26. var data = {}
  27. , key
  28. , remain = []
  29. , cooked = args
  30. , original = args.slice(0)
  31. parse(args, data, remain, types, shorthands)
  32. // now data is full
  33. clean(data, types, exports.typeDefs)
  34. data.argv = {remain:remain,cooked:cooked,original:original}
  35. data.argv.toString = function () {
  36. return this.original.map(JSON.stringify).join(" ")
  37. }
  38. return data
  39. }
  40. function clean (data, types, typeDefs) {
  41. typeDefs = typeDefs || exports.typeDefs
  42. var remove = {}
  43. , typeDefault = [false, true, null, String, Number]
  44. Object.keys(data).forEach(function (k) {
  45. if (k === "argv") return
  46. var val = data[k]
  47. , isArray = Array.isArray(val)
  48. , type = types[k]
  49. if (!isArray) val = [val]
  50. if (!type) type = typeDefault
  51. if (type === Array) type = typeDefault.concat(Array)
  52. if (!Array.isArray(type)) type = [type]
  53. debug("val=%j", val)
  54. debug("types=", type)
  55. val = val.map(function (val) {
  56. // if it's an unknown value, then parse false/true/null/numbers
  57. if (typeof val === "string") {
  58. debug("string %j", val)
  59. val = val.trim()
  60. if ((val === "null" && ~type.indexOf(null))
  61. || (val === "true" &&
  62. (~type.indexOf(true) || ~type.indexOf(Boolean)))
  63. || (val === "false" &&
  64. (~type.indexOf(false) || ~type.indexOf(Boolean)))) {
  65. val = JSON.parse(val)
  66. debug("jsonable %j", val)
  67. } else if (~type.indexOf(Number) && !isNaN(val)) {
  68. debug("convert to number", val)
  69. val = +val
  70. }
  71. }
  72. if (!types.hasOwnProperty(k)) {
  73. return val
  74. }
  75. // allow `--no-blah` to set 'blah' to null if null is allowed
  76. if (val === false && ~type.indexOf(null) &&
  77. !(~type.indexOf(false) || ~type.indexOf(Boolean))) {
  78. val = null
  79. }
  80. var d = {}
  81. d[k] = val
  82. debug("prevalidated val", d, val, types[k])
  83. if (!validate(d, k, val, types[k], typeDefs)) {
  84. if (exports.invalidHandler) {
  85. exports.invalidHandler(k, val, types[k], data)
  86. } else if (exports.invalidHandler !== false) {
  87. debug("invalid: "+k+"="+val, types[k])
  88. }
  89. return remove
  90. }
  91. debug("validated val", d, val, types[k])
  92. return d[k]
  93. }).filter(function (val) { return val !== remove })
  94. if (!val.length) delete data[k]
  95. else if (isArray) {
  96. debug(isArray, data[k], val)
  97. data[k] = val
  98. } else data[k] = val[0]
  99. debug("k=%s val=%j", k, val, data[k])
  100. })
  101. }
  102. function validateString (data, k, val) {
  103. data[k] = String(val)
  104. }
  105. function validatePath (data, k, val) {
  106. data[k] = path.resolve(String(val))
  107. return true
  108. }
  109. function validateNumber (data, k, val) {
  110. debug("validate Number %j %j %j", k, val, isNaN(val))
  111. if (isNaN(val)) return false
  112. data[k] = +val
  113. }
  114. function validateBoolean (data, k, val) {
  115. if (val instanceof Boolean) val = val.valueOf()
  116. else if (typeof val === "string") {
  117. if (!isNaN(val)) val = !!(+val)
  118. else if (val === "null" || val === "false") val = false
  119. else val = true
  120. } else val = !!val
  121. data[k] = val
  122. }
  123. function validateUrl (data, k, val) {
  124. val = url.parse(String(val))
  125. if (!val.host) return false
  126. data[k] = val.href
  127. }
  128. function validateStream (data, k, val) {
  129. if (!(val instanceof Stream)) return false
  130. data[k] = val
  131. }
  132. function validate (data, k, val, type, typeDefs) {
  133. // arrays are lists of types.
  134. if (Array.isArray(type)) {
  135. for (var i = 0, l = type.length; i < l; i ++) {
  136. if (type[i] === Array) continue
  137. if (validate(data, k, val, type[i], typeDefs)) return true
  138. }
  139. delete data[k]
  140. return false
  141. }
  142. // an array of anything?
  143. if (type === Array) return true
  144. // NaN is poisonous. Means that something is not allowed.
  145. if (type !== type) {
  146. debug("Poison NaN", k, val, type)
  147. delete data[k]
  148. return false
  149. }
  150. // explicit list of values
  151. if (val === type) {
  152. debug("Explicitly allowed %j", val)
  153. // if (isArray) (data[k] = data[k] || []).push(val)
  154. // else data[k] = val
  155. data[k] = val
  156. return true
  157. }
  158. // now go through the list of typeDefs, validate against each one.
  159. var ok = false
  160. , types = Object.keys(typeDefs)
  161. for (var i = 0, l = types.length; i < l; i ++) {
  162. debug("test type %j %j %j", k, val, types[i])
  163. var t = typeDefs[types[i]]
  164. if (t && type === t.type) {
  165. var d = {}
  166. ok = false !== t.validate(d, k, val)
  167. val = d[k]
  168. if (ok) {
  169. // if (isArray) (data[k] = data[k] || []).push(val)
  170. // else data[k] = val
  171. data[k] = val
  172. break
  173. }
  174. }
  175. }
  176. debug("OK? %j (%j %j %j)", ok, k, val, types[i])
  177. if (!ok) delete data[k]
  178. return ok
  179. }
  180. function parse (args, data, remain, types, shorthands) {
  181. debug("parse", args, data, remain)
  182. var key = null
  183. , abbrevs = abbrev(Object.keys(types))
  184. , shortAbbr = abbrev(Object.keys(shorthands))
  185. for (var i = 0; i < args.length; i ++) {
  186. var arg = args[i]
  187. debug("arg", arg)
  188. if (arg.match(/^-{2,}$/)) {
  189. // done with keys.
  190. // the rest are args.
  191. remain.push.apply(remain, args.slice(i + 1))
  192. args[i] = "--"
  193. break
  194. }
  195. if (arg.charAt(0) === "-") {
  196. if (arg.indexOf("=") !== -1) {
  197. var v = arg.split("=")
  198. arg = v.shift()
  199. v = v.join("=")
  200. args.splice.apply(args, [i, 1].concat([arg, v]))
  201. }
  202. // see if it's a shorthand
  203. // if so, splice and back up to re-parse it.
  204. var shRes = resolveShort(arg, shorthands, shortAbbr, abbrevs)
  205. debug("arg=%j shRes=%j", arg, shRes)
  206. if (shRes) {
  207. debug(arg, shRes)
  208. args.splice.apply(args, [i, 1].concat(shRes))
  209. if (arg !== shRes[0]) {
  210. i --
  211. continue
  212. }
  213. }
  214. arg = arg.replace(/^-+/, "")
  215. var no = false
  216. while (arg.toLowerCase().indexOf("no-") === 0) {
  217. no = !no
  218. arg = arg.substr(3)
  219. }
  220. if (abbrevs[arg]) arg = abbrevs[arg]
  221. var isArray = types[arg] === Array ||
  222. Array.isArray(types[arg]) && types[arg].indexOf(Array) !== -1
  223. var val
  224. , la = args[i + 1]
  225. var isBool = no ||
  226. types[arg] === Boolean ||
  227. Array.isArray(types[arg]) && types[arg].indexOf(Boolean) !== -1 ||
  228. (la === "false" &&
  229. (types[arg] === null ||
  230. Array.isArray(types[arg]) && ~types[arg].indexOf(null)))
  231. if (isBool) {
  232. // just set and move along
  233. val = !no
  234. // however, also support --bool true or --bool false
  235. if (la === "true" || la === "false") {
  236. val = JSON.parse(la)
  237. la = null
  238. if (no) val = !val
  239. i ++
  240. }
  241. // also support "foo":[Boolean, "bar"] and "--foo bar"
  242. if (Array.isArray(types[arg]) && la) {
  243. if (~types[arg].indexOf(la)) {
  244. // an explicit type
  245. val = la
  246. i ++
  247. } else if ( la === "null" && ~types[arg].indexOf(null) ) {
  248. // null allowed
  249. val = null
  250. i ++
  251. } else if ( !la.match(/^-{2,}[^-]/) &&
  252. !isNaN(la) &&
  253. ~types[arg].indexOf(Number) ) {
  254. // number
  255. val = +la
  256. i ++
  257. } else if ( !la.match(/^-[^-]/) && ~types[arg].indexOf(String) ) {
  258. // string
  259. val = la
  260. i ++
  261. }
  262. }
  263. if (isArray) (data[arg] = data[arg] || []).push(val)
  264. else data[arg] = val
  265. continue
  266. }
  267. if (la && la.match(/^-{2,}$/)) {
  268. la = undefined
  269. i --
  270. }
  271. val = la === undefined ? true : la
  272. if (isArray) (data[arg] = data[arg] || []).push(val)
  273. else data[arg] = val
  274. i ++
  275. continue
  276. }
  277. remain.push(arg)
  278. }
  279. }
  280. function resolveShort (arg, shorthands, shortAbbr, abbrevs) {
  281. // handle single-char shorthands glommed together, like
  282. // npm ls -glp, but only if there is one dash, and only if
  283. // all of the chars are single-char shorthands, and it's
  284. // not a match to some other abbrev.
  285. arg = arg.replace(/^-+/, '')
  286. if (abbrevs[arg] && !shorthands[arg]) {
  287. return null
  288. }
  289. if (shortAbbr[arg]) {
  290. arg = shortAbbr[arg]
  291. } else {
  292. var singles = shorthands.___singles
  293. if (!singles) {
  294. singles = Object.keys(shorthands).filter(function (s) {
  295. return s.length === 1
  296. }).reduce(function (l,r) { l[r] = true ; return l }, {})
  297. shorthands.___singles = singles
  298. }
  299. var chrs = arg.split("").filter(function (c) {
  300. return singles[c]
  301. })
  302. if (chrs.join("") === arg) return chrs.map(function (c) {
  303. return shorthands[c]
  304. }).reduce(function (l, r) {
  305. return l.concat(r)
  306. }, [])
  307. }
  308. if (shorthands[arg] && !Array.isArray(shorthands[arg])) {
  309. shorthands[arg] = shorthands[arg].split(/\s+/)
  310. }
  311. return shorthands[arg]
  312. }
  313. if (module === require.main) {
  314. var assert = require("assert")
  315. , util = require("util")
  316. , shorthands =
  317. { s : ["--loglevel", "silent"]
  318. , d : ["--loglevel", "info"]
  319. , dd : ["--loglevel", "verbose"]
  320. , ddd : ["--loglevel", "silly"]
  321. , noreg : ["--no-registry"]
  322. , reg : ["--registry"]
  323. , "no-reg" : ["--no-registry"]
  324. , silent : ["--loglevel", "silent"]
  325. , verbose : ["--loglevel", "verbose"]
  326. , h : ["--usage"]
  327. , H : ["--usage"]
  328. , "?" : ["--usage"]
  329. , help : ["--usage"]
  330. , v : ["--version"]
  331. , f : ["--force"]
  332. , desc : ["--description"]
  333. , "no-desc" : ["--no-description"]
  334. , "local" : ["--no-global"]
  335. , l : ["--long"]
  336. , p : ["--parseable"]
  337. , porcelain : ["--parseable"]
  338. , g : ["--global"]
  339. }
  340. , types =
  341. { aoa: Array
  342. , nullstream: [null, Stream]
  343. , str: String
  344. , browser : String
  345. , cache : path
  346. , color : ["always", Boolean]
  347. , depth : Number
  348. , description : Boolean
  349. , dev : Boolean
  350. , editor : path
  351. , force : Boolean
  352. , global : Boolean
  353. , globalconfig : path
  354. , group : [String, Number]
  355. , gzipbin : String
  356. , logfd : [Number, Stream]
  357. , loglevel : ["silent","win","error","warn","info","verbose","silly"]
  358. , long : Boolean
  359. , "node-version" : [false, String]
  360. , npaturl : url
  361. , npat : Boolean
  362. , "onload-script" : [false, String]
  363. , outfd : [Number, Stream]
  364. , parseable : Boolean
  365. , pre: Boolean
  366. , prefix: path
  367. , proxy : url
  368. , "rebuild-bundle" : Boolean
  369. , registry : url
  370. , searchopts : String
  371. , searchexclude: [null, String]
  372. , shell : path
  373. , t: [Array, String]
  374. , tag : String
  375. , tar : String
  376. , tmp : path
  377. , "unsafe-perm" : Boolean
  378. , usage : Boolean
  379. , user : String
  380. , username : String
  381. , userconfig : path
  382. , version : Boolean
  383. , viewer: path
  384. , _exit : Boolean
  385. }
  386. ; [["-v", {version:true}, []]
  387. ,["---v", {version:true}, []]
  388. ,["ls -s --no-reg connect -d",
  389. {loglevel:"info",registry:null},["ls","connect"]]
  390. ,["ls ---s foo",{loglevel:"silent"},["ls","foo"]]
  391. ,["ls --registry blargle", {}, ["ls"]]
  392. ,["--no-registry", {registry:null}, []]
  393. ,["--no-color true", {color:false}, []]
  394. ,["--no-color false", {color:true}, []]
  395. ,["--no-color", {color:false}, []]
  396. ,["--color false", {color:false}, []]
  397. ,["--color --logfd 7", {logfd:7,color:true}, []]
  398. ,["--color=true", {color:true}, []]
  399. ,["--logfd=10", {logfd:10}, []]
  400. ,["--tmp=/tmp -tar=gtar",{tmp:"/tmp",tar:"gtar"},[]]
  401. ,["--tmp=tmp -tar=gtar",
  402. {tmp:path.resolve(process.cwd(), "tmp"),tar:"gtar"},[]]
  403. ,["--logfd x", {}, []]
  404. ,["a -true -- -no-false", {true:true},["a","-no-false"]]
  405. ,["a -no-false", {false:false},["a"]]
  406. ,["a -no-no-true", {true:true}, ["a"]]
  407. ,["a -no-no-no-false", {false:false}, ["a"]]
  408. ,["---NO-no-No-no-no-no-nO-no-no"+
  409. "-No-no-no-no-no-no-no-no-no"+
  410. "-no-no-no-no-NO-NO-no-no-no-no-no-no"+
  411. "-no-body-can-do-the-boogaloo-like-I-do"
  412. ,{"body-can-do-the-boogaloo-like-I-do":false}, []]
  413. ,["we are -no-strangers-to-love "+
  414. "--you-know the-rules --and so-do-i "+
  415. "---im-thinking-of=a-full-commitment "+
  416. "--no-you-would-get-this-from-any-other-guy "+
  417. "--no-gonna-give-you-up "+
  418. "-no-gonna-let-you-down=true "+
  419. "--no-no-gonna-run-around false "+
  420. "--desert-you=false "+
  421. "--make-you-cry false "+
  422. "--no-tell-a-lie "+
  423. "--no-no-and-hurt-you false"
  424. ,{"strangers-to-love":false
  425. ,"you-know":"the-rules"
  426. ,"and":"so-do-i"
  427. ,"you-would-get-this-from-any-other-guy":false
  428. ,"gonna-give-you-up":false
  429. ,"gonna-let-you-down":false
  430. ,"gonna-run-around":false
  431. ,"desert-you":false
  432. ,"make-you-cry":false
  433. ,"tell-a-lie":false
  434. ,"and-hurt-you":false
  435. },["we", "are"]]
  436. ,["-t one -t two -t three"
  437. ,{t: ["one", "two", "three"]}
  438. ,[]]
  439. ,["-t one -t null -t three four five null"
  440. ,{t: ["one", "null", "three"]}
  441. ,["four", "five", "null"]]
  442. ,["-t foo"
  443. ,{t:["foo"]}
  444. ,[]]
  445. ,["--no-t"
  446. ,{t:["false"]}
  447. ,[]]
  448. ,["-no-no-t"
  449. ,{t:["true"]}
  450. ,[]]
  451. ,["-aoa one -aoa null -aoa 100"
  452. ,{aoa:["one", null, 100]}
  453. ,[]]
  454. ,["-str 100"
  455. ,{str:"100"}
  456. ,[]]
  457. ,["--color always"
  458. ,{color:"always"}
  459. ,[]]
  460. ,["--no-nullstream"
  461. ,{nullstream:null}
  462. ,[]]
  463. ,["--nullstream false"
  464. ,{nullstream:null}
  465. ,[]]
  466. ].forEach(function (test) {
  467. var argv = test[0].split(/\s+/)
  468. , opts = test[1]
  469. , rem = test[2]
  470. , actual = nopt(types, shorthands, argv, 0)
  471. , parsed = actual.argv
  472. delete actual.argv
  473. console.log(util.inspect(actual, false, 2, true), parsed.remain)
  474. for (var i in opts) {
  475. var e = JSON.stringify(opts[i])
  476. , a = JSON.stringify(actual[i] === undefined ? null : actual[i])
  477. assert.equal(e, a)
  478. }
  479. assert.deepEqual(rem, parsed.remain)
  480. })
  481. }