Advertisement
Guest User

Untitled

a guest
Jan 16th, 2017
88
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.80 KB | None | 0 0
  1. 'use strict'
  2.  
  3. const OPTION_REGEXP = /^--(?:(no)-)?(\w{2,}(?:[\w-]+)?)$/
  4. const FLOAT_REGEXP = /^[+-]?(\d+)?(\.\d+)?$/
  5. const CAMEL_REGEXP = /[-_]+([A-z])/g
  6. const FLAG_REGEXP = /^-(\w+)$/
  7.  
  8. const TYPE = {
  9. boolean: boolean,
  10. number: number,
  11. list: {
  12. append: true,
  13. transform: value
  14. },
  15. value: value,
  16. auto: auto
  17. }
  18.  
  19. class Clint {
  20.  
  21. constructor (options, argTransform = TYPE.auto) {
  22. this.aliases = {}
  23. this.transforms = {}
  24. this.appends = {}
  25.  
  26. this.argTransform = argTransform
  27.  
  28. options = Object.assign({
  29. '*': {
  30. transform: TYPE.auto
  31. },
  32. '--*': {
  33. transform: TYPE.auto
  34. },
  35. input: {
  36. transform: TYPE.auto,
  37. alias: 'i'
  38. },
  39. output: {
  40. transform: TYPE.auto,
  41. alias: 'o'
  42. },
  43. version: {
  44. transform: TYPE.boolean,
  45. alias: 'v'
  46. },
  47. help: {
  48. transform: TYPE.boolean,
  49. alias: 'h'
  50. },
  51. force: {
  52. transform: TYPE.boolean,
  53. alias: 'f'
  54. },
  55. add: {
  56. transform: TYPE.boolean,
  57. alias: 'a'
  58. },
  59. remove: {
  60. transform: TYPE.boolean,
  61. alias: 'r'
  62. }
  63. }, options)
  64.  
  65. for (let name in options) {
  66. let option = options[name]
  67. let optionName = camelize(name)
  68. if (option == null || option === false) continue
  69.  
  70. let aliases = []
  71.  
  72. if (option === true) {
  73. this.transforms[optionName] = (v) => v
  74. } else if (typeof option === 'function') {
  75. this.transforms[optionName] = option
  76. } else {
  77. if (option.alias) aliases.push(option.alias)
  78. if (option.aliases) aliases.push(...option.aliases)
  79. this.transforms[optionName] = option.transform
  80. if (option.append) this.appends[optionName] = option.append
  81. }
  82.  
  83. for (let alias of aliases) this.aliases[alias] = optionName
  84. }
  85.  
  86. // check for aliases recursion
  87. for (let alias in this.aliases) {
  88. let value = this.aliases[alias]
  89. if (this.aliases[value] === alias) {
  90. let message = `recursive alias ${alias} > ${value}, ${value} > ${alias}`
  91. throw new Error(message)
  92. }
  93. }
  94. }
  95.  
  96. parse (argv) {
  97. const args = []
  98. const opts = {}
  99.  
  100. const aliases = this.aliases
  101. let prev = false
  102.  
  103. // recursively resolve aliases
  104. const alias = function (key) {
  105. while (aliases[key]) key = aliases[key]
  106. return key
  107. }
  108.  
  109. const setOpt = (key, value) => {
  110. let transform = this.transforms[key] || this.transforms['--*']
  111. if (!transform) return
  112.  
  113. let list
  114. if (this.appends[key]) list = opts[key] || (opts[key] = [])
  115.  
  116. value = transform(value)
  117. if (value == null) return
  118.  
  119. if (list) {
  120. list.push(value)
  121. return true // special return true for successful append
  122. } else {
  123. opts[key] = value
  124. }
  125. }
  126.  
  127. const setPrev = (value) => {
  128. if (prev) {
  129. let isList = setOpt(prev, value)
  130. if (!isList) prev = false
  131. }
  132. }
  133.  
  134. for (let arg of argv) {
  135. const match = arg.match(OPTION_REGEXP)
  136.  
  137. // IS NOT "--(no-)option"
  138. if (!match) {
  139. const match = arg.match(FLAG_REGEXP)
  140. // IS "-o"
  141. if (match) {
  142. setPrev(true)
  143.  
  144. for (let f of match[1].split('')) setOpt(alias(f), true)
  145. // IS "value"
  146. } else {
  147. const transform = this.transforms['*']
  148. // set value for the previous waiting option
  149. if (prev) setPrev(arg)
  150. // otherwise push the value to the current list (def: args)
  151. else args.push(transform ? transform(arg) : arg)
  152. }
  153. // IS "--(no-)option"
  154. } else {
  155. // set true if last one was waiting
  156. setPrev(true)
  157.  
  158. const tag = match[1]
  159. const option = alias(camelize(match[2]))
  160. // const ls = match[3]
  161.  
  162. // IS "--no-option"
  163. if (tag === 'no') {
  164. setOpt(option, 'no')
  165. } else {
  166. // wait for value
  167. prev = option
  168. }
  169. }
  170. }
  171.  
  172. // set true if last one was waiting
  173. setPrev(true)
  174.  
  175. return { args, opts }
  176. }
  177.  
  178. }
  179.  
  180. function camelize (string) {
  181. return string.replace(CAMEL_REGEXP, (full, match) => match.toUpperCase())
  182. }
  183.  
  184. function boolean (string) {
  185. if (/^(no|n|false)$/.test(string)) return false
  186. if (/^(yes|y|true)$/.test(string)) return true
  187. }
  188.  
  189. function number (string) {
  190. let m = FLOAT_REGEXP.test(string)
  191. if (m) return parseFloat(string)
  192. }
  193.  
  194. function auto (arg) {
  195. let value = boolean(arg)
  196. if (value == null) value = number(arg)
  197. if (value == null) value = arg
  198. return value
  199. }
  200.  
  201. function value (arg) {
  202. if (typeof arg !== 'string') return
  203. let value = number(arg)
  204. if (value == null) value = arg
  205. return value
  206. }
  207.  
  208. function clint (...args) {
  209. return new Clint(...args)
  210. }
  211.  
  212. clint.parse = function () {
  213. return new Clint().parse(...arguments)
  214. }
  215.  
  216. clint.TYPE = TYPE
  217. clint.Clint = Clint
  218.  
  219. module.exports = clint
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement