index.js 23 KB

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