index.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758
  1. var path = require('path')
  2. var e2c = require('electron-to-chromium/versions')
  3. var agents = require('caniuse-lite/dist/unpacker/agents').agents
  4. var BrowserslistError = require('./error')
  5. var env = require('./node') // Will load browser.js in webpack
  6. var FLOAT_RANGE = /^\d+(\.\d+)?(-\d+(\.\d+)?)*$/
  7. function normalize (versions) {
  8. return versions.filter(function (version) {
  9. return typeof version === 'string'
  10. })
  11. }
  12. function nameMapper (name) {
  13. return function mapName (version) {
  14. return name + ' ' + version
  15. }
  16. }
  17. function getMajor (version) {
  18. return parseInt(version.split('.')[0])
  19. }
  20. function getMajorVersions (released, number) {
  21. if (released.length === 0) return []
  22. var minimum = getMajor(released[released.length - 1]) - parseInt(number) + 1
  23. var selected = []
  24. for (var i = released.length - 1; i >= 0; i--) {
  25. if (minimum > getMajor(released[i])) break
  26. selected.unshift(released[i])
  27. }
  28. return selected
  29. }
  30. function uniq (array) {
  31. var filtered = []
  32. for (var i = 0; i < array.length; i++) {
  33. if (filtered.indexOf(array[i]) === -1) filtered.push(array[i])
  34. }
  35. return filtered
  36. }
  37. // Helpers
  38. function fillUsage (result, name, data) {
  39. for (var i in data) {
  40. result[name + ' ' + i] = data[i]
  41. }
  42. }
  43. function generateFilter (sign, version) {
  44. version = parseFloat(version)
  45. if (sign === '>') {
  46. return function (v) {
  47. return parseFloat(v) > version
  48. }
  49. } else if (sign === '>=') {
  50. return function (v) {
  51. return parseFloat(v) >= version
  52. }
  53. } else if (sign === '<') {
  54. return function (v) {
  55. return parseFloat(v) < version
  56. }
  57. } else {
  58. return function (v) {
  59. return parseFloat(v) <= version
  60. }
  61. }
  62. }
  63. function compareStrings (a, b) {
  64. if (a < b) return -1
  65. if (a > b) return +1
  66. return 0
  67. }
  68. function normalizeVersion (data, version) {
  69. if (data.versions.indexOf(version) !== -1) {
  70. return version
  71. } else if (browserslist.versionAliases[data.name][version]) {
  72. return browserslist.versionAliases[data.name][version]
  73. } else if (data.versions.length === 1) {
  74. return data.versions[0]
  75. } else {
  76. return false
  77. }
  78. }
  79. function filterByYear (since) {
  80. return Object.keys(agents).reduce(function (selected, name) {
  81. var data = byName(name)
  82. if (!data) return selected
  83. var versions = Object.keys(data.releaseDate).filter(function (v) {
  84. return data.releaseDate[v] >= since
  85. })
  86. return selected.concat(versions.map(nameMapper(data.name)))
  87. }, [])
  88. }
  89. function byName (name) {
  90. name = name.toLowerCase()
  91. name = browserslist.aliases[name] || name
  92. return browserslist.data[name]
  93. }
  94. function checkName (name) {
  95. var data = byName(name)
  96. if (!data) throw new BrowserslistError('Unknown browser ' + name)
  97. return data
  98. }
  99. function unknownQuery (query) {
  100. return new BrowserslistError('Unknown browser query `' + query + '`')
  101. }
  102. function resolve (queries, context) {
  103. return queries.reduce(function (result, selection, index) {
  104. selection = selection.trim()
  105. if (selection === '') return result
  106. var isExclude = selection.indexOf('not ') === 0
  107. if (isExclude) {
  108. if (index === 0) {
  109. throw new BrowserslistError(
  110. 'Write any browsers query (for instance, `defaults`) ' +
  111. 'before `' + selection + '`')
  112. }
  113. selection = selection.slice(4)
  114. }
  115. for (var i = 0; i < QUERIES.length; i++) {
  116. var type = QUERIES[i]
  117. var match = selection.match(type.regexp)
  118. if (match) {
  119. var args = [context].concat(match.slice(1))
  120. var array = type.select.apply(browserslist, args)
  121. if (isExclude) {
  122. array = array.concat(array.map(function (j) {
  123. return j.replace(/\s\S+/, ' 0')
  124. }))
  125. return result.filter(function (j) {
  126. return array.indexOf(j) === -1
  127. })
  128. }
  129. return result.concat(array)
  130. }
  131. }
  132. throw unknownQuery(selection)
  133. }, [])
  134. }
  135. /**
  136. * Return array of browsers by selection queries.
  137. *
  138. * @param {(string|string[])} [queries=browserslist.defaults] Browser queries.
  139. * @param {object} [opts] Options.
  140. * @param {string} [opts.path="."] Path to processed file.
  141. * It will be used to find config files.
  142. * @param {string} [opts.env="production"] Processing environment.
  143. * It will be used to take right
  144. * queries from config file.
  145. * @param {string} [opts.config] Path to config file with queries.
  146. * @param {object} [opts.stats] Custom browser usage statistics
  147. * for "> 1% in my stats" query.
  148. * @param {boolean} [opts.ignoreUnknownVersions=false] Do not throw on unknown
  149. * version in direct query.
  150. * @param {boolean} [opts.dangerousExtend] Disable security checks
  151. * for extend query.
  152. * @return {string[]} Array with browser names in Can I Use.
  153. *
  154. * @example
  155. * browserslist('IE >= 10, IE 8') //=> ['ie 11', 'ie 10', 'ie 8']
  156. */
  157. function browserslist (queries, opts) {
  158. if (typeof opts === 'undefined') opts = { }
  159. if (typeof opts.path === 'undefined') {
  160. opts.path = path.resolve ? path.resolve('.') : '.'
  161. }
  162. if (typeof queries === 'undefined' || queries === null) {
  163. var config = browserslist.loadConfig(opts)
  164. if (config) {
  165. queries = config
  166. } else {
  167. queries = browserslist.defaults
  168. }
  169. }
  170. if (typeof queries === 'string') {
  171. queries = queries.split(/,\s*/)
  172. }
  173. if (!Array.isArray(queries)) {
  174. throw new BrowserslistError(
  175. 'Browser queries must be an array. Got ' + typeof queries + '.')
  176. }
  177. var context = {
  178. ignoreUnknownVersions: opts.ignoreUnknownVersions,
  179. dangerousExtend: opts.dangerousExtend
  180. }
  181. var stats = env.getStat(opts)
  182. if (stats) {
  183. context.customUsage = { }
  184. for (var browser in stats) {
  185. fillUsage(context.customUsage, browser, stats[browser])
  186. }
  187. }
  188. var result = resolve(queries, context).map(function (i) {
  189. var parts = i.split(' ')
  190. var name = parts[0]
  191. var version = parts[1]
  192. if (version === '0') {
  193. return name + ' ' + byName(name).versions[0]
  194. } else {
  195. return i
  196. }
  197. }).sort(function (name1, name2) {
  198. name1 = name1.split(' ')
  199. name2 = name2.split(' ')
  200. if (name1[0] === name2[0]) {
  201. if (FLOAT_RANGE.test(name1[1]) && FLOAT_RANGE.test(name2[1])) {
  202. return parseFloat(name2[1]) - parseFloat(name1[1])
  203. } else {
  204. return compareStrings(name2[1], name1[1])
  205. }
  206. } else {
  207. return compareStrings(name1[0], name2[0])
  208. }
  209. })
  210. return uniq(result)
  211. }
  212. // Will be filled by Can I Use data below
  213. browserslist.data = { }
  214. browserslist.usage = {
  215. global: { },
  216. custom: null
  217. }
  218. // Default browsers query
  219. browserslist.defaults = [
  220. '> 0.5%',
  221. 'last 2 versions',
  222. 'Firefox ESR',
  223. 'not dead'
  224. ]
  225. // Browser names aliases
  226. browserslist.aliases = {
  227. fx: 'firefox',
  228. ff: 'firefox',
  229. ios: 'ios_saf',
  230. explorer: 'ie',
  231. blackberry: 'bb',
  232. explorermobile: 'ie_mob',
  233. operamini: 'op_mini',
  234. operamobile: 'op_mob',
  235. chromeandroid: 'and_chr',
  236. firefoxandroid: 'and_ff',
  237. ucandroid: 'and_uc',
  238. qqandroid: 'and_qq'
  239. }
  240. // Aliases to work with joined versions like `ios_saf 7.0-7.1`
  241. browserslist.versionAliases = { }
  242. browserslist.clearCaches = env.clearCaches
  243. browserslist.parseConfig = env.parseConfig
  244. browserslist.readConfig = env.readConfig
  245. browserslist.findConfig = env.findConfig
  246. browserslist.loadConfig = env.loadConfig
  247. /**
  248. * Return browsers market coverage.
  249. *
  250. * @param {string[]} browsers Browsers names in Can I Use.
  251. * @param {string|object} [stats="global"] Which statistics should be used.
  252. * Country code or custom statistics.
  253. * Pass `"my stats"` to load statistics
  254. * from Browserslist files.
  255. *
  256. * @return {number} Total market coverage for all selected browsers.
  257. *
  258. * @example
  259. * browserslist.coverage(browserslist('> 1% in US'), 'US') //=> 83.1
  260. */
  261. browserslist.coverage = function (browsers, stats) {
  262. var data
  263. if (typeof stats === 'undefined') {
  264. data = browserslist.usage.global
  265. } else if (stats === 'my stats') {
  266. var opts = {}
  267. opts.path = path.resolve ? path.resolve('.') : '.'
  268. var customStats = env.getStat(opts)
  269. if (!customStats) {
  270. throw new BrowserslistError('Custom usage statistics was not provided')
  271. }
  272. data = {}
  273. for (var browser in customStats) {
  274. fillUsage(data, browser, customStats[browser])
  275. }
  276. } else if (typeof stats === 'string') {
  277. if (stats.length > 2) {
  278. stats = stats.toLowerCase()
  279. } else {
  280. stats = stats.toUpperCase()
  281. }
  282. env.loadCountry(browserslist.usage, stats)
  283. data = browserslist.usage[stats]
  284. } else {
  285. if ('dataByBrowser' in stats) {
  286. stats = stats.dataByBrowser
  287. }
  288. data = { }
  289. for (var name in stats) {
  290. for (var version in stats[name]) {
  291. data[name + ' ' + version] = stats[name][version]
  292. }
  293. }
  294. }
  295. return browsers.reduce(function (all, i) {
  296. var usage = data[i]
  297. if (usage === undefined) {
  298. usage = data[i.replace(/ \S+$/, ' 0')]
  299. }
  300. return all + (usage || 0)
  301. }, 0)
  302. }
  303. var QUERIES = [
  304. {
  305. regexp: /^last\s+(\d+)\s+major versions?$/i,
  306. select: function (context, versions) {
  307. return Object.keys(agents).reduce(function (selected, name) {
  308. var data = byName(name)
  309. if (!data) return selected
  310. var array = getMajorVersions(data.released, versions)
  311. array = array.map(nameMapper(data.name))
  312. return selected.concat(array)
  313. }, [])
  314. }
  315. },
  316. {
  317. regexp: /^last\s+(\d+)\s+versions?$/i,
  318. select: function (context, versions) {
  319. return Object.keys(agents).reduce(function (selected, name) {
  320. var data = byName(name)
  321. if (!data) return selected
  322. var array = data.released.slice(-versions)
  323. array = array.map(nameMapper(data.name))
  324. return selected.concat(array)
  325. }, [])
  326. }
  327. },
  328. {
  329. regexp: /^last\s+(\d+)\s+electron\s+major versions?$/i,
  330. select: function (context, versions) {
  331. var validVersions = getMajorVersions(Object.keys(e2c).reverse(), versions)
  332. return validVersions.map(function (i) {
  333. return 'chrome ' + e2c[i]
  334. })
  335. }
  336. },
  337. {
  338. regexp: /^last\s+(\d+)\s+(\w+)\s+major versions?$/i,
  339. select: function (context, versions, name) {
  340. var data = checkName(name)
  341. var validVersions = getMajorVersions(data.released, versions)
  342. return validVersions.map(nameMapper(data.name))
  343. }
  344. },
  345. {
  346. regexp: /^last\s+(\d+)\s+electron\s+versions?$/i,
  347. select: function (context, versions) {
  348. return Object.keys(e2c).reverse().slice(-versions).map(function (i) {
  349. return 'chrome ' + e2c[i]
  350. })
  351. }
  352. },
  353. {
  354. regexp: /^last\s+(\d+)\s+(\w+)\s+versions?$/i,
  355. select: function (context, versions, name) {
  356. var data = checkName(name)
  357. return data.released.slice(-versions).map(nameMapper(data.name))
  358. }
  359. },
  360. {
  361. regexp: /^unreleased\s+versions$/i,
  362. select: function () {
  363. return Object.keys(agents).reduce(function (selected, name) {
  364. var data = byName(name)
  365. if (!data) return selected
  366. var array = data.versions.filter(function (v) {
  367. return data.released.indexOf(v) === -1
  368. })
  369. array = array.map(nameMapper(data.name))
  370. return selected.concat(array)
  371. }, [])
  372. }
  373. },
  374. {
  375. regexp: /^unreleased\s+electron\s+versions?$/i,
  376. select: function () {
  377. return []
  378. }
  379. },
  380. {
  381. regexp: /^unreleased\s+(\w+)\s+versions?$/i,
  382. select: function (context, name) {
  383. var data = checkName(name)
  384. return data.versions.filter(function (v) {
  385. return data.released.indexOf(v) === -1
  386. }).map(nameMapper(data.name))
  387. }
  388. },
  389. {
  390. regexp: /^last\s+(\d+)\s+years?$/i,
  391. select: function (context, years) {
  392. var date = new Date()
  393. var since = date.setFullYear(date.getFullYear() - years) / 1000
  394. return filterByYear(since)
  395. }
  396. },
  397. {
  398. regexp: /^since (\d+)(?:-(\d+))?(?:-(\d+))?$/i,
  399. select: function (context, year, month, date) {
  400. year = parseInt(year)
  401. month = parseInt(month || '01') - 1
  402. date = parseInt(date || '01')
  403. var since = Date.UTC(year, month, date, 0, 0, 0) / 1000
  404. return filterByYear(since)
  405. }
  406. },
  407. {
  408. regexp: /^(>=?|<=?)\s*(\d*\.?\d+)%$/,
  409. select: function (context, sign, popularity) {
  410. popularity = parseFloat(popularity)
  411. var usage = browserslist.usage.global
  412. return Object.keys(usage).reduce(function (result, version) {
  413. if (sign === '>') {
  414. if (usage[version] > popularity) {
  415. result.push(version)
  416. }
  417. } else if (sign === '<') {
  418. if (usage[version] < popularity) {
  419. result.push(version)
  420. }
  421. } else if (sign === '<=') {
  422. if (usage[version] <= popularity) {
  423. result.push(version)
  424. }
  425. } else if (usage[version] >= popularity) {
  426. result.push(version)
  427. }
  428. return result
  429. }, [])
  430. }
  431. },
  432. {
  433. regexp: /^(>=?|<=?)\s*(\d*\.?\d+)%\s+in\s+my\s+stats$/,
  434. select: function (context, sign, popularity) {
  435. popularity = parseFloat(popularity)
  436. if (!context.customUsage) {
  437. throw new BrowserslistError('Custom usage statistics was not provided')
  438. }
  439. var usage = context.customUsage
  440. return Object.keys(usage).reduce(function (result, version) {
  441. if (sign === '>') {
  442. if (usage[version] > popularity) {
  443. result.push(version)
  444. }
  445. } else if (sign === '<') {
  446. if (usage[version] < popularity) {
  447. result.push(version)
  448. }
  449. } else if (sign === '<=') {
  450. if (usage[version] <= popularity) {
  451. result.push(version)
  452. }
  453. } else if (usage[version] >= popularity) {
  454. result.push(version)
  455. }
  456. return result
  457. }, [])
  458. }
  459. },
  460. {
  461. regexp: /^(>=?|<=?)\s*(\d*\.?\d+)%\s+in\s+((alt-)?\w\w)$/,
  462. select: function (context, sign, popularity, place) {
  463. popularity = parseFloat(popularity)
  464. if (place.length === 2) {
  465. place = place.toUpperCase()
  466. } else {
  467. place = place.toLowerCase()
  468. }
  469. env.loadCountry(browserslist.usage, place)
  470. var usage = browserslist.usage[place]
  471. return Object.keys(usage).reduce(function (result, version) {
  472. if (sign === '>') {
  473. if (usage[version] > popularity) {
  474. result.push(version)
  475. }
  476. } else if (sign === '<') {
  477. if (usage[version] < popularity) {
  478. result.push(version)
  479. }
  480. } else if (sign === '<=') {
  481. if (usage[version] <= popularity) {
  482. result.push(version)
  483. }
  484. } else if (usage[version] >= popularity) {
  485. result.push(version)
  486. }
  487. return result
  488. }, [])
  489. }
  490. },
  491. {
  492. regexp: /^cover\s+(\d*\.?\d+)%(\s+in\s+(my\s+stats|(alt-)?\w\w))?$/,
  493. select: function (context, coverage, statMode) {
  494. coverage = parseFloat(coverage)
  495. var usage = browserslist.usage.global
  496. if (statMode) {
  497. if (statMode.match(/^\s+in\s+my\s+stats$/)) {
  498. if (!context.customUsage) {
  499. throw new BrowserslistError(
  500. 'Custom usage statistics was not provided'
  501. )
  502. }
  503. usage = context.customUsage
  504. } else {
  505. var match = statMode.match(/\s+in\s+((alt-)?\w\w)/)
  506. var place = match[1]
  507. if (place.length === 2) {
  508. place = place.toUpperCase()
  509. } else {
  510. place = place.toLowerCase()
  511. }
  512. env.loadCountry(browserslist.usage, place)
  513. usage = browserslist.usage[place]
  514. }
  515. }
  516. var versions = Object.keys(usage).sort(function (a, b) {
  517. return usage[b] - usage[a]
  518. })
  519. var coveraged = 0
  520. var result = []
  521. var version
  522. for (var i = 0; i <= versions.length; i++) {
  523. version = versions[i]
  524. if (usage[version] === 0) break
  525. coveraged += usage[version]
  526. result.push(version)
  527. if (coveraged >= coverage) break
  528. }
  529. return result
  530. }
  531. },
  532. {
  533. regexp: /^electron\s+([\d.]+)\s*-\s*([\d.]+)$/i,
  534. select: function (context, from, to) {
  535. if (!e2c[from]) {
  536. throw new BrowserslistError('Unknown version ' + from + ' of electron')
  537. }
  538. if (!e2c[to]) {
  539. throw new BrowserslistError('Unknown version ' + to + ' of electron')
  540. }
  541. from = parseFloat(from)
  542. to = parseFloat(to)
  543. return Object.keys(e2c).filter(function (i) {
  544. var parsed = parseFloat(i)
  545. return parsed >= from && parsed <= to
  546. }).map(function (i) {
  547. return 'chrome ' + e2c[i]
  548. })
  549. }
  550. },
  551. {
  552. regexp: /^(\w+)\s+([\d.]+)\s*-\s*([\d.]+)$/i,
  553. select: function (context, name, from, to) {
  554. var data = checkName(name)
  555. from = parseFloat(normalizeVersion(data, from) || from)
  556. to = parseFloat(normalizeVersion(data, to) || to)
  557. function filter (v) {
  558. var parsed = parseFloat(v)
  559. return parsed >= from && parsed <= to
  560. }
  561. return data.released.filter(filter).map(nameMapper(data.name))
  562. }
  563. },
  564. {
  565. regexp: /^electron\s*(>=?|<=?)\s*([\d.]+)$/i,
  566. select: function (context, sign, version) {
  567. return Object.keys(e2c)
  568. .filter(generateFilter(sign, version))
  569. .map(function (i) {
  570. return 'chrome ' + e2c[i]
  571. })
  572. }
  573. },
  574. {
  575. regexp: /^(\w+)\s*(>=?|<=?)\s*([\d.]+)$/,
  576. select: function (context, name, sign, version) {
  577. var data = checkName(name)
  578. var alias = browserslist.versionAliases[data.name][version]
  579. if (alias) {
  580. version = alias
  581. }
  582. return data.released
  583. .filter(generateFilter(sign, version))
  584. .map(function (v) {
  585. return data.name + ' ' + v
  586. })
  587. }
  588. },
  589. {
  590. regexp: /^(firefox|ff|fx)\s+esr$/i,
  591. select: function () {
  592. return ['firefox 52', 'firefox 60']
  593. }
  594. },
  595. {
  596. regexp: /(operamini|op_mini)\s+all/i,
  597. select: function () {
  598. return ['op_mini all']
  599. }
  600. },
  601. {
  602. regexp: /^electron\s+([\d.]+)$/i,
  603. select: function (context, version) {
  604. var chrome = e2c[version]
  605. if (!chrome) {
  606. throw new BrowserslistError(
  607. 'Unknown version ' + version + ' of electron')
  608. }
  609. return ['chrome ' + chrome]
  610. }
  611. },
  612. {
  613. regexp: /^(\w+)\s+(tp|[\d.]+)$/i,
  614. select: function (context, name, version) {
  615. if (/^tp$/i.test(version)) version = 'TP'
  616. var data = checkName(name)
  617. var alias = normalizeVersion(data, version)
  618. if (alias) {
  619. version = alias
  620. } else {
  621. if (version.indexOf('.') === -1) {
  622. alias = version + '.0'
  623. } else {
  624. alias = version.replace(/\.0$/, '')
  625. }
  626. alias = normalizeVersion(data, alias)
  627. if (alias) {
  628. version = alias
  629. } else if (context.ignoreUnknownVersions) {
  630. return []
  631. } else {
  632. throw new BrowserslistError(
  633. 'Unknown version ' + version + ' of ' + name)
  634. }
  635. }
  636. return [data.name + ' ' + version]
  637. }
  638. },
  639. {
  640. regexp: /^extends (.+)$/i,
  641. select: function (context, name) {
  642. return resolve(env.loadQueries(context, name), context)
  643. }
  644. },
  645. {
  646. regexp: /^defaults$/i,
  647. select: function () {
  648. return browserslist(browserslist.defaults)
  649. }
  650. },
  651. {
  652. regexp: /^dead$/i,
  653. select: function (context) {
  654. var dead = ['ie <= 10', 'ie_mob <= 10', 'bb <= 10', 'op_mob <= 12.1']
  655. return resolve(dead, context)
  656. }
  657. },
  658. {
  659. regexp: /^(\w+)$/i,
  660. select: function (context, name) {
  661. if (byName(name)) {
  662. throw new BrowserslistError(
  663. 'Specify versions in Browserslist query for browser ' + name)
  664. } else {
  665. throw unknownQuery(name)
  666. }
  667. }
  668. }
  669. ];
  670. // Get and convert Can I Use data
  671. (function () {
  672. for (var name in agents) {
  673. var browser = agents[name]
  674. browserslist.data[name] = {
  675. name: name,
  676. versions: normalize(agents[name].versions),
  677. released: normalize(agents[name].versions.slice(0, -3)),
  678. releaseDate: agents[name].release_date
  679. }
  680. fillUsage(browserslist.usage.global, name, browser.usage_global)
  681. browserslist.versionAliases[name] = { }
  682. for (var i = 0; i < browser.versions.length; i++) {
  683. var full = browser.versions[i]
  684. if (!full) continue
  685. if (full.indexOf('-') !== -1) {
  686. var interval = full.split('-')
  687. for (var j = 0; j < interval.length; j++) {
  688. browserslist.versionAliases[name][interval[j]] = full
  689. }
  690. }
  691. }
  692. }
  693. }())
  694. module.exports = browserslist