helper.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. 'use strict'
  2. const fs = require('graceful-fs')
  3. const path = require('path')
  4. const _ = require('lodash')
  5. const useragent = require('useragent')
  6. const Promise = require('bluebird')
  7. const mm = require('minimatch')
  8. exports.browserFullNameToShort = (fullName) => {
  9. const agent = useragent.parse(fullName)
  10. const isKnown = agent.family !== 'Other' && agent.os.family !== 'Other'
  11. return isKnown ? agent.toAgent() + ' (' + agent.os + ')' : fullName
  12. }
  13. exports.isDefined = (value) => {
  14. return !_.isUndefined(value)
  15. }
  16. const parser = (pattern, out) => {
  17. if (pattern.length === 0) return out
  18. const p = /^(\[[^\]]*\]|[*+@?]\((.+?)\))/g
  19. const matches = p.exec(pattern)
  20. if (!matches) {
  21. const c = pattern[0]
  22. let t = 'word'
  23. if (c === '*') {
  24. t = 'star'
  25. } else if (c === '?') {
  26. t = 'optional'
  27. }
  28. out[t]++
  29. return parser(pattern.substring(1), out)
  30. }
  31. if (matches[2] !== undefined) {
  32. out.ext_glob++
  33. parser(matches[2], out)
  34. return parser(pattern.substring(matches[0].length), out)
  35. }
  36. out.range++
  37. return parser(pattern.substring(matches[0].length), out)
  38. }
  39. const gsParser = (pattern, out) => {
  40. if (pattern === '**') {
  41. out.glob_star++
  42. return out
  43. }
  44. return parser(pattern, out)
  45. }
  46. const compareWeightObject = (w1, w2) => {
  47. return exports.mmComparePatternWeights(
  48. [w1.glob_star, w1.star, w1.ext_glob, w1.range, w1.optional],
  49. [w2.glob_star, w2.star, w2.ext_glob, w2.range, w2.optional]
  50. )
  51. }
  52. exports.mmPatternWeight = (pattern) => {
  53. const m = new mm.Minimatch(pattern)
  54. if (!m.globParts) return [0, 0, 0, 0, 0, 0]
  55. const result = m.globParts.reduce((prev, p) => {
  56. const r = p.reduce((prev, p) => {
  57. return gsParser(p, prev)
  58. }, {glob_star: 0, ext_glob: 0, word: 0, star: 0, optional: 0, range: 0})
  59. if (prev === undefined) return r
  60. return compareWeightObject(r, prev) > 0 ? r : prev
  61. }, undefined)
  62. result.glob_sets = m.set.length
  63. return [result.glob_sets, result.glob_star, result.star, result.ext_glob, result.range, result.optional]
  64. }
  65. exports.mmComparePatternWeights = (weight1, weight2) => {
  66. let n1, n2, diff
  67. n1 = weight1[0]
  68. n2 = weight2[0]
  69. diff = n1 - n2
  70. if (diff !== 0) return diff / Math.abs(diff)
  71. return weight1.length > 1 ? exports.mmComparePatternWeights(weight1.slice(1), weight2.slice(1)) : 0
  72. }
  73. exports.isFunction = _.isFunction
  74. exports.isString = _.isString
  75. exports.isObject = _.isObject
  76. exports.isArray = _.isArray
  77. exports.isNumber = _.isNumber
  78. const ABS_URL = /^https?:\/\//
  79. exports.isUrlAbsolute = (url) => {
  80. return ABS_URL.test(url)
  81. }
  82. exports.camelToSnake = (camelCase) => {
  83. return camelCase.replace(/[A-Z]/g, (match, pos) => {
  84. return (pos > 0 ? '_' : '') + match.toLowerCase()
  85. })
  86. }
  87. exports.ucFirst = (word) => {
  88. return word.charAt(0).toUpperCase() + word.substr(1)
  89. }
  90. exports.dashToCamel = (dash) => {
  91. const words = dash.split('-')
  92. return words.shift() + words.map(exports.ucFirst).join('')
  93. }
  94. exports.arrayRemove = (collection, item) => {
  95. const idx = collection.indexOf(item)
  96. if (idx !== -1) {
  97. collection.splice(idx, 1)
  98. return true
  99. }
  100. return false
  101. }
  102. exports.merge = function () {
  103. const args = Array.prototype.slice.call(arguments, 0)
  104. args.unshift({})
  105. return _.merge.apply({}, args)
  106. }
  107. exports.formatTimeInterval = (time) => {
  108. const mins = Math.floor(time / 60000)
  109. const secs = (time - mins * 60000) / 1000
  110. let str = secs + (secs === 1 ? ' sec' : ' secs')
  111. if (mins) {
  112. str = mins + (mins === 1 ? ' min ' : ' mins ') + str
  113. }
  114. return str
  115. }
  116. const replaceWinPath = (path) => {
  117. return _.isString(path) ? path.replace(/\\/g, '/') : path
  118. }
  119. exports.normalizeWinPath = process.platform === 'win32' ? replaceWinPath : _.identity
  120. exports.mkdirIfNotExists = (directory, done) => {
  121. // TODO(vojta): handle if it's a file
  122. /* eslint-disable handle-callback-err */
  123. fs.stat(directory, (err, stat) => {
  124. if (stat && stat.isDirectory()) {
  125. done()
  126. } else {
  127. exports.mkdirIfNotExists(path.dirname(directory), () => {
  128. fs.mkdir(directory, done)
  129. })
  130. }
  131. })
  132. /* eslint-enable handle-callback-err */
  133. }
  134. exports.defer = () => {
  135. let res
  136. let rej
  137. const promise = new Promise((resolve, reject) => {
  138. res = resolve
  139. rej = reject
  140. })
  141. return {
  142. resolve: res,
  143. reject: rej,
  144. promise: promise
  145. }
  146. }