index.js 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215
  1. var jsReleases = require('node-releases/data/processed/envs.json')
  2. var agents = require('caniuse-lite/dist/unpacker/agents').agents
  3. var jsEOL = require('node-releases/data/release-schedule/release-schedule.json')
  4. var path = require('path')
  5. var e2c = require('electron-to-chromium/versions')
  6. var BrowserslistError = require('./error')
  7. var parse = require('./parse')
  8. var env = require('./node') // Will load browser.js in webpack
  9. var YEAR = 365.259641 * 24 * 60 * 60 * 1000
  10. var ANDROID_EVERGREEN_FIRST = '37'
  11. var OP_MOB_BLINK_FIRST = 14
  12. // Helpers
  13. function isVersionsMatch(versionA, versionB) {
  14. return (versionA + '.').indexOf(versionB + '.') === 0
  15. }
  16. function isEolReleased(name) {
  17. var version = name.slice(1)
  18. return browserslist.nodeVersions.some(function (i) {
  19. return isVersionsMatch(i, version)
  20. })
  21. }
  22. function normalize(versions) {
  23. return versions.filter(function (version) {
  24. return typeof version === 'string'
  25. })
  26. }
  27. function normalizeElectron(version) {
  28. var versionToUse = version
  29. if (version.split('.').length === 3) {
  30. versionToUse = version.split('.').slice(0, -1).join('.')
  31. }
  32. return versionToUse
  33. }
  34. function nameMapper(name) {
  35. return function mapName(version) {
  36. return name + ' ' + version
  37. }
  38. }
  39. function getMajor(version) {
  40. return parseInt(version.split('.')[0])
  41. }
  42. function getMajorVersions(released, number) {
  43. if (released.length === 0) return []
  44. var majorVersions = uniq(released.map(getMajor))
  45. var minimum = majorVersions[majorVersions.length - number]
  46. if (!minimum) {
  47. return released
  48. }
  49. var selected = []
  50. for (var i = released.length - 1; i >= 0; i--) {
  51. if (minimum > getMajor(released[i])) break
  52. selected.unshift(released[i])
  53. }
  54. return selected
  55. }
  56. function uniq(array) {
  57. var filtered = []
  58. for (var i = 0; i < array.length; i++) {
  59. if (filtered.indexOf(array[i]) === -1) filtered.push(array[i])
  60. }
  61. return filtered
  62. }
  63. function fillUsage(result, name, data) {
  64. for (var i in data) {
  65. result[name + ' ' + i] = data[i]
  66. }
  67. }
  68. function generateFilter(sign, version) {
  69. version = parseFloat(version)
  70. if (sign === '>') {
  71. return function (v) {
  72. return parseLatestFloat(v) > version
  73. }
  74. } else if (sign === '>=') {
  75. return function (v) {
  76. return parseLatestFloat(v) >= version
  77. }
  78. } else if (sign === '<') {
  79. return function (v) {
  80. return parseFloat(v) < version
  81. }
  82. } else {
  83. return function (v) {
  84. return parseFloat(v) <= version
  85. }
  86. }
  87. function parseLatestFloat(v) {
  88. return parseFloat(v.split('-')[1] || v)
  89. }
  90. }
  91. function generateSemverFilter(sign, version) {
  92. version = version.split('.').map(parseSimpleInt)
  93. version[1] = version[1] || 0
  94. version[2] = version[2] || 0
  95. if (sign === '>') {
  96. return function (v) {
  97. v = v.split('.').map(parseSimpleInt)
  98. return compareSemver(v, version) > 0
  99. }
  100. } else if (sign === '>=') {
  101. return function (v) {
  102. v = v.split('.').map(parseSimpleInt)
  103. return compareSemver(v, version) >= 0
  104. }
  105. } else if (sign === '<') {
  106. return function (v) {
  107. v = v.split('.').map(parseSimpleInt)
  108. return compareSemver(version, v) > 0
  109. }
  110. } else {
  111. return function (v) {
  112. v = v.split('.').map(parseSimpleInt)
  113. return compareSemver(version, v) >= 0
  114. }
  115. }
  116. }
  117. function parseSimpleInt(x) {
  118. return parseInt(x)
  119. }
  120. function compare(a, b) {
  121. if (a < b) return -1
  122. if (a > b) return +1
  123. return 0
  124. }
  125. function compareSemver(a, b) {
  126. return (
  127. compare(parseInt(a[0]), parseInt(b[0])) ||
  128. compare(parseInt(a[1] || '0'), parseInt(b[1] || '0')) ||
  129. compare(parseInt(a[2] || '0'), parseInt(b[2] || '0'))
  130. )
  131. }
  132. // this follows the npm-like semver behavior
  133. function semverFilterLoose(operator, range) {
  134. range = range.split('.').map(parseSimpleInt)
  135. if (typeof range[1] === 'undefined') {
  136. range[1] = 'x'
  137. }
  138. // ignore any patch version because we only return minor versions
  139. // range[2] = 'x'
  140. switch (operator) {
  141. case '<=':
  142. return function (version) {
  143. version = version.split('.').map(parseSimpleInt)
  144. return compareSemverLoose(version, range) <= 0
  145. }
  146. case '>=':
  147. default:
  148. return function (version) {
  149. version = version.split('.').map(parseSimpleInt)
  150. return compareSemverLoose(version, range) >= 0
  151. }
  152. }
  153. }
  154. // this follows the npm-like semver behavior
  155. function compareSemverLoose(version, range) {
  156. if (version[0] !== range[0]) {
  157. return version[0] < range[0] ? -1 : +1
  158. }
  159. if (range[1] === 'x') {
  160. return 0
  161. }
  162. if (version[1] !== range[1]) {
  163. return version[1] < range[1] ? -1 : +1
  164. }
  165. return 0
  166. }
  167. function resolveVersion(data, version) {
  168. if (data.versions.indexOf(version) !== -1) {
  169. return version
  170. } else if (browserslist.versionAliases[data.name][version]) {
  171. return browserslist.versionAliases[data.name][version]
  172. } else {
  173. return false
  174. }
  175. }
  176. function normalizeVersion(data, version) {
  177. var resolved = resolveVersion(data, version)
  178. if (resolved) {
  179. return resolved
  180. } else if (data.versions.length === 1) {
  181. return data.versions[0]
  182. } else {
  183. return false
  184. }
  185. }
  186. function filterByYear(since, context) {
  187. since = since / 1000
  188. return Object.keys(agents).reduce(function (selected, name) {
  189. var data = byName(name, context)
  190. if (!data) return selected
  191. var versions = Object.keys(data.releaseDate).filter(function (v) {
  192. var date = data.releaseDate[v]
  193. return date !== null && date >= since
  194. })
  195. return selected.concat(versions.map(nameMapper(data.name)))
  196. }, [])
  197. }
  198. function cloneData(data) {
  199. return {
  200. name: data.name,
  201. versions: data.versions,
  202. released: data.released,
  203. releaseDate: data.releaseDate
  204. }
  205. }
  206. function byName(name, context) {
  207. name = name.toLowerCase()
  208. name = browserslist.aliases[name] || name
  209. if (context.mobileToDesktop && browserslist.desktopNames[name]) {
  210. var desktop = browserslist.data[browserslist.desktopNames[name]]
  211. if (name === 'android') {
  212. return normalizeAndroidData(cloneData(browserslist.data[name]), desktop)
  213. } else {
  214. var cloned = cloneData(desktop)
  215. cloned.name = name
  216. return cloned
  217. }
  218. }
  219. return browserslist.data[name]
  220. }
  221. function normalizeAndroidVersions(androidVersions, chromeVersions) {
  222. var iFirstEvergreen = chromeVersions.indexOf(ANDROID_EVERGREEN_FIRST)
  223. return androidVersions
  224. .filter(function (version) {
  225. return /^(?:[2-4]\.|[34]$)/.test(version)
  226. })
  227. .concat(chromeVersions.slice(iFirstEvergreen))
  228. }
  229. function copyObject(obj) {
  230. var copy = {}
  231. for (var key in obj) {
  232. copy[key] = obj[key]
  233. }
  234. return copy
  235. }
  236. function normalizeAndroidData(android, chrome) {
  237. android.released = normalizeAndroidVersions(android.released, chrome.released)
  238. android.versions = normalizeAndroidVersions(android.versions, chrome.versions)
  239. android.releaseDate = copyObject(android.releaseDate)
  240. android.released.forEach(function (v) {
  241. if (android.releaseDate[v] === undefined) {
  242. android.releaseDate[v] = chrome.releaseDate[v]
  243. }
  244. })
  245. return android
  246. }
  247. function checkName(name, context) {
  248. var data = byName(name, context)
  249. if (!data) throw new BrowserslistError('Unknown browser ' + name)
  250. return data
  251. }
  252. function unknownQuery(query) {
  253. return new BrowserslistError(
  254. 'Unknown browser query `' +
  255. query +
  256. '`. ' +
  257. 'Maybe you are using old Browserslist or made typo in query.'
  258. )
  259. }
  260. // Adjusts last X versions queries for some mobile browsers,
  261. // where caniuse data jumps from a legacy version to the latest
  262. function filterJumps(list, name, nVersions, context) {
  263. var jump = 1
  264. switch (name) {
  265. case 'android':
  266. if (context.mobileToDesktop) return list
  267. var released = browserslist.data.chrome.released
  268. jump = released.length - released.indexOf(ANDROID_EVERGREEN_FIRST)
  269. break
  270. case 'op_mob':
  271. var latest = browserslist.data.op_mob.released.slice(-1)[0]
  272. jump = getMajor(latest) - OP_MOB_BLINK_FIRST + 1
  273. break
  274. default:
  275. return list
  276. }
  277. if (nVersions <= jump) {
  278. return list.slice(-1)
  279. }
  280. return list.slice(jump - 1 - nVersions)
  281. }
  282. function isSupported(flags, withPartial) {
  283. return (
  284. typeof flags === 'string' &&
  285. (flags.indexOf('y') >= 0 || (withPartial && flags.indexOf('a') >= 0))
  286. )
  287. }
  288. function resolve(queries, context) {
  289. return parse(QUERIES, queries).reduce(function (result, node, index) {
  290. if (node.not && index === 0) {
  291. throw new BrowserslistError(
  292. 'Write any browsers query (for instance, `defaults`) ' +
  293. 'before `' +
  294. node.query +
  295. '`'
  296. )
  297. }
  298. var type = QUERIES[node.type]
  299. var array = type.select.call(browserslist, context, node).map(function (j) {
  300. var parts = j.split(' ')
  301. if (parts[1] === '0') {
  302. return parts[0] + ' ' + byName(parts[0], context).versions[0]
  303. } else {
  304. return j
  305. }
  306. })
  307. if (node.compose === 'and') {
  308. if (node.not) {
  309. return result.filter(function (j) {
  310. return array.indexOf(j) === -1
  311. })
  312. } else {
  313. return result.filter(function (j) {
  314. return array.indexOf(j) !== -1
  315. })
  316. }
  317. } else {
  318. if (node.not) {
  319. var filter = {}
  320. array.forEach(function (j) {
  321. filter[j] = true
  322. })
  323. return result.filter(function (j) {
  324. return !filter[j]
  325. })
  326. }
  327. return result.concat(array)
  328. }
  329. }, [])
  330. }
  331. function prepareOpts(opts) {
  332. if (typeof opts === 'undefined') opts = {}
  333. if (typeof opts.path === 'undefined') {
  334. opts.path = path.resolve ? path.resolve('.') : '.'
  335. }
  336. return opts
  337. }
  338. function prepareQueries(queries, opts) {
  339. if (typeof queries === 'undefined' || queries === null) {
  340. var config = browserslist.loadConfig(opts)
  341. if (config) {
  342. queries = config
  343. } else {
  344. queries = browserslist.defaults
  345. }
  346. }
  347. return queries
  348. }
  349. function checkQueries(queries) {
  350. if (!(typeof queries === 'string' || Array.isArray(queries))) {
  351. throw new BrowserslistError(
  352. 'Browser queries must be an array or string. Got ' + typeof queries + '.'
  353. )
  354. }
  355. }
  356. var cache = {}
  357. function browserslist(queries, opts) {
  358. opts = prepareOpts(opts)
  359. queries = prepareQueries(queries, opts)
  360. checkQueries(queries)
  361. var context = {
  362. ignoreUnknownVersions: opts.ignoreUnknownVersions,
  363. dangerousExtend: opts.dangerousExtend,
  364. mobileToDesktop: opts.mobileToDesktop,
  365. path: opts.path,
  366. env: opts.env
  367. }
  368. env.oldDataWarning(browserslist.data)
  369. var stats = env.getStat(opts, browserslist.data)
  370. if (stats) {
  371. context.customUsage = {}
  372. for (var browser in stats) {
  373. fillUsage(context.customUsage, browser, stats[browser])
  374. }
  375. }
  376. var cacheKey = JSON.stringify([queries, context])
  377. if (cache[cacheKey]) return cache[cacheKey]
  378. var result = uniq(resolve(queries, context)).sort(function (name1, name2) {
  379. name1 = name1.split(' ')
  380. name2 = name2.split(' ')
  381. if (name1[0] === name2[0]) {
  382. // assumptions on caniuse data
  383. // 1) version ranges never overlaps
  384. // 2) if version is not a range, it never contains `-`
  385. var version1 = name1[1].split('-')[0]
  386. var version2 = name2[1].split('-')[0]
  387. return compareSemver(version2.split('.'), version1.split('.'))
  388. } else {
  389. return compare(name1[0], name2[0])
  390. }
  391. })
  392. if (!env.env.BROWSERSLIST_DISABLE_CACHE) {
  393. cache[cacheKey] = result
  394. }
  395. return result
  396. }
  397. browserslist.parse = function (queries, opts) {
  398. opts = prepareOpts(opts)
  399. queries = prepareQueries(queries, opts)
  400. checkQueries(queries)
  401. return parse(QUERIES, queries)
  402. }
  403. // Will be filled by Can I Use data below
  404. browserslist.cache = {}
  405. browserslist.data = {}
  406. browserslist.usage = {
  407. global: {},
  408. custom: null
  409. }
  410. // Default browsers query
  411. browserslist.defaults = ['> 0.5%', 'last 2 versions', 'Firefox ESR', 'not dead']
  412. // Browser names aliases
  413. browserslist.aliases = {
  414. fx: 'firefox',
  415. ff: 'firefox',
  416. ios: 'ios_saf',
  417. explorer: 'ie',
  418. blackberry: 'bb',
  419. explorermobile: 'ie_mob',
  420. operamini: 'op_mini',
  421. operamobile: 'op_mob',
  422. chromeandroid: 'and_chr',
  423. firefoxandroid: 'and_ff',
  424. ucandroid: 'and_uc',
  425. qqandroid: 'and_qq'
  426. }
  427. // Can I Use only provides a few versions for some browsers (e.g. and_chr).
  428. // Fallback to a similar browser for unknown versions
  429. // Note op_mob is not included as its chromium versions are not in sync with Opera desktop
  430. browserslist.desktopNames = {
  431. and_chr: 'chrome',
  432. and_ff: 'firefox',
  433. ie_mob: 'ie',
  434. android: 'chrome' // has extra processing logic
  435. }
  436. // Aliases to work with joined versions like `ios_saf 7.0-7.1`
  437. browserslist.versionAliases = {}
  438. browserslist.clearCaches = env.clearCaches
  439. browserslist.parseConfig = env.parseConfig
  440. browserslist.readConfig = env.readConfig
  441. browserslist.findConfig = env.findConfig
  442. browserslist.loadConfig = env.loadConfig
  443. browserslist.coverage = function (browsers, stats) {
  444. var data
  445. if (typeof stats === 'undefined') {
  446. data = browserslist.usage.global
  447. } else if (stats === 'my stats') {
  448. var opts = {}
  449. opts.path = path.resolve ? path.resolve('.') : '.'
  450. var customStats = env.getStat(opts)
  451. if (!customStats) {
  452. throw new BrowserslistError('Custom usage statistics was not provided')
  453. }
  454. data = {}
  455. for (var browser in customStats) {
  456. fillUsage(data, browser, customStats[browser])
  457. }
  458. } else if (typeof stats === 'string') {
  459. if (stats.length > 2) {
  460. stats = stats.toLowerCase()
  461. } else {
  462. stats = stats.toUpperCase()
  463. }
  464. env.loadCountry(browserslist.usage, stats, browserslist.data)
  465. data = browserslist.usage[stats]
  466. } else {
  467. if ('dataByBrowser' in stats) {
  468. stats = stats.dataByBrowser
  469. }
  470. data = {}
  471. for (var name in stats) {
  472. for (var version in stats[name]) {
  473. data[name + ' ' + version] = stats[name][version]
  474. }
  475. }
  476. }
  477. return browsers.reduce(function (all, i) {
  478. var usage = data[i]
  479. if (usage === undefined) {
  480. usage = data[i.replace(/ \S+$/, ' 0')]
  481. }
  482. return all + (usage || 0)
  483. }, 0)
  484. }
  485. function nodeQuery(context, node) {
  486. var matched = browserslist.nodeVersions.filter(function (i) {
  487. return isVersionsMatch(i, node.version)
  488. })
  489. if (matched.length === 0) {
  490. if (context.ignoreUnknownVersions) {
  491. return []
  492. } else {
  493. throw new BrowserslistError(
  494. 'Unknown version ' + node.version + ' of Node.js'
  495. )
  496. }
  497. }
  498. return ['node ' + matched[matched.length - 1]]
  499. }
  500. function sinceQuery(context, node) {
  501. var year = parseInt(node.year)
  502. var month = parseInt(node.month || '01') - 1
  503. var day = parseInt(node.day || '01')
  504. return filterByYear(Date.UTC(year, month, day, 0, 0, 0), context)
  505. }
  506. function coverQuery(context, node) {
  507. var coverage = parseFloat(node.coverage)
  508. var usage = browserslist.usage.global
  509. if (node.place) {
  510. if (node.place.match(/^my\s+stats$/i)) {
  511. if (!context.customUsage) {
  512. throw new BrowserslistError('Custom usage statistics was not provided')
  513. }
  514. usage = context.customUsage
  515. } else {
  516. var place
  517. if (node.place.length === 2) {
  518. place = node.place.toUpperCase()
  519. } else {
  520. place = node.place.toLowerCase()
  521. }
  522. env.loadCountry(browserslist.usage, place, browserslist.data)
  523. usage = browserslist.usage[place]
  524. }
  525. }
  526. var versions = Object.keys(usage).sort(function (a, b) {
  527. return usage[b] - usage[a]
  528. })
  529. var coveraged = 0
  530. var result = []
  531. var version
  532. for (var i = 0; i < versions.length; i++) {
  533. version = versions[i]
  534. if (usage[version] === 0) break
  535. coveraged += usage[version]
  536. result.push(version)
  537. if (coveraged >= coverage) break
  538. }
  539. return result
  540. }
  541. var QUERIES = {
  542. last_major_versions: {
  543. matches: ['versions'],
  544. regexp: /^last\s+(\d+)\s+major\s+versions?$/i,
  545. select: function (context, node) {
  546. return Object.keys(agents).reduce(function (selected, name) {
  547. var data = byName(name, context)
  548. if (!data) return selected
  549. var list = getMajorVersions(data.released, node.versions)
  550. list = list.map(nameMapper(data.name))
  551. list = filterJumps(list, data.name, node.versions, context)
  552. return selected.concat(list)
  553. }, [])
  554. }
  555. },
  556. last_versions: {
  557. matches: ['versions'],
  558. regexp: /^last\s+(\d+)\s+versions?$/i,
  559. select: function (context, node) {
  560. return Object.keys(agents).reduce(function (selected, name) {
  561. var data = byName(name, context)
  562. if (!data) return selected
  563. var list = data.released.slice(-node.versions)
  564. list = list.map(nameMapper(data.name))
  565. list = filterJumps(list, data.name, node.versions, context)
  566. return selected.concat(list)
  567. }, [])
  568. }
  569. },
  570. last_electron_major_versions: {
  571. matches: ['versions'],
  572. regexp: /^last\s+(\d+)\s+electron\s+major\s+versions?$/i,
  573. select: function (context, node) {
  574. var validVersions = getMajorVersions(Object.keys(e2c), node.versions)
  575. return validVersions.map(function (i) {
  576. return 'chrome ' + e2c[i]
  577. })
  578. }
  579. },
  580. last_node_major_versions: {
  581. matches: ['versions'],
  582. regexp: /^last\s+(\d+)\s+node\s+major\s+versions?$/i,
  583. select: function (context, node) {
  584. return getMajorVersions(browserslist.nodeVersions, node.versions).map(
  585. function (version) {
  586. return 'node ' + version
  587. }
  588. )
  589. }
  590. },
  591. last_browser_major_versions: {
  592. matches: ['versions', 'browser'],
  593. regexp: /^last\s+(\d+)\s+(\w+)\s+major\s+versions?$/i,
  594. select: function (context, node) {
  595. var data = checkName(node.browser, context)
  596. var validVersions = getMajorVersions(data.released, node.versions)
  597. var list = validVersions.map(nameMapper(data.name))
  598. list = filterJumps(list, data.name, node.versions, context)
  599. return list
  600. }
  601. },
  602. last_electron_versions: {
  603. matches: ['versions'],
  604. regexp: /^last\s+(\d+)\s+electron\s+versions?$/i,
  605. select: function (context, node) {
  606. return Object.keys(e2c)
  607. .slice(-node.versions)
  608. .map(function (i) {
  609. return 'chrome ' + e2c[i]
  610. })
  611. }
  612. },
  613. last_node_versions: {
  614. matches: ['versions'],
  615. regexp: /^last\s+(\d+)\s+node\s+versions?$/i,
  616. select: function (context, node) {
  617. return browserslist.nodeVersions
  618. .slice(-node.versions)
  619. .map(function (version) {
  620. return 'node ' + version
  621. })
  622. }
  623. },
  624. last_browser_versions: {
  625. matches: ['versions', 'browser'],
  626. regexp: /^last\s+(\d+)\s+(\w+)\s+versions?$/i,
  627. select: function (context, node) {
  628. var data = checkName(node.browser, context)
  629. var list = data.released.slice(-node.versions).map(nameMapper(data.name))
  630. list = filterJumps(list, data.name, node.versions, context)
  631. return list
  632. }
  633. },
  634. unreleased_versions: {
  635. matches: [],
  636. regexp: /^unreleased\s+versions$/i,
  637. select: function (context) {
  638. return Object.keys(agents).reduce(function (selected, name) {
  639. var data = byName(name, context)
  640. if (!data) return selected
  641. var list = data.versions.filter(function (v) {
  642. return data.released.indexOf(v) === -1
  643. })
  644. list = list.map(nameMapper(data.name))
  645. return selected.concat(list)
  646. }, [])
  647. }
  648. },
  649. unreleased_electron_versions: {
  650. matches: [],
  651. regexp: /^unreleased\s+electron\s+versions?$/i,
  652. select: function () {
  653. return []
  654. }
  655. },
  656. unreleased_browser_versions: {
  657. matches: ['browser'],
  658. regexp: /^unreleased\s+(\w+)\s+versions?$/i,
  659. select: function (context, node) {
  660. var data = checkName(node.browser, context)
  661. return data.versions
  662. .filter(function (v) {
  663. return data.released.indexOf(v) === -1
  664. })
  665. .map(nameMapper(data.name))
  666. }
  667. },
  668. last_years: {
  669. matches: ['years'],
  670. regexp: /^last\s+(\d*.?\d+)\s+years?$/i,
  671. select: function (context, node) {
  672. return filterByYear(Date.now() - YEAR * node.years, context)
  673. }
  674. },
  675. since_y: {
  676. matches: ['year'],
  677. regexp: /^since (\d+)$/i,
  678. select: sinceQuery
  679. },
  680. since_y_m: {
  681. matches: ['year', 'month'],
  682. regexp: /^since (\d+)-(\d+)$/i,
  683. select: sinceQuery
  684. },
  685. since_y_m_d: {
  686. matches: ['year', 'month', 'day'],
  687. regexp: /^since (\d+)-(\d+)-(\d+)$/i,
  688. select: sinceQuery
  689. },
  690. popularity: {
  691. matches: ['sign', 'popularity'],
  692. regexp: /^(>=?|<=?)\s*(\d+|\d+\.\d+|\.\d+)%$/,
  693. select: function (context, node) {
  694. var popularity = parseFloat(node.popularity)
  695. var usage = browserslist.usage.global
  696. return Object.keys(usage).reduce(function (result, version) {
  697. if (node.sign === '>') {
  698. if (usage[version] > popularity) {
  699. result.push(version)
  700. }
  701. } else if (node.sign === '<') {
  702. if (usage[version] < popularity) {
  703. result.push(version)
  704. }
  705. } else if (node.sign === '<=') {
  706. if (usage[version] <= popularity) {
  707. result.push(version)
  708. }
  709. } else if (usage[version] >= popularity) {
  710. result.push(version)
  711. }
  712. return result
  713. }, [])
  714. }
  715. },
  716. popularity_in_my_stats: {
  717. matches: ['sign', 'popularity'],
  718. regexp: /^(>=?|<=?)\s*(\d+|\d+\.\d+|\.\d+)%\s+in\s+my\s+stats$/,
  719. select: function (context, node) {
  720. var popularity = parseFloat(node.popularity)
  721. if (!context.customUsage) {
  722. throw new BrowserslistError('Custom usage statistics was not provided')
  723. }
  724. var usage = context.customUsage
  725. return Object.keys(usage).reduce(function (result, version) {
  726. var percentage = usage[version]
  727. if (percentage == null) {
  728. return result
  729. }
  730. if (node.sign === '>') {
  731. if (percentage > popularity) {
  732. result.push(version)
  733. }
  734. } else if (node.sign === '<') {
  735. if (percentage < popularity) {
  736. result.push(version)
  737. }
  738. } else if (node.sign === '<=') {
  739. if (percentage <= popularity) {
  740. result.push(version)
  741. }
  742. } else if (percentage >= popularity) {
  743. result.push(version)
  744. }
  745. return result
  746. }, [])
  747. }
  748. },
  749. popularity_in_config_stats: {
  750. matches: ['sign', 'popularity', 'config'],
  751. regexp: /^(>=?|<=?)\s*(\d+|\d+\.\d+|\.\d+)%\s+in\s+(\S+)\s+stats$/,
  752. select: function (context, node) {
  753. var popularity = parseFloat(node.popularity)
  754. var stats = env.loadStat(context, node.config, browserslist.data)
  755. if (stats) {
  756. context.customUsage = {}
  757. for (var browser in stats) {
  758. fillUsage(context.customUsage, browser, stats[browser])
  759. }
  760. }
  761. if (!context.customUsage) {
  762. throw new BrowserslistError('Custom usage statistics was not provided')
  763. }
  764. var usage = context.customUsage
  765. return Object.keys(usage).reduce(function (result, version) {
  766. var percentage = usage[version]
  767. if (percentage == null) {
  768. return result
  769. }
  770. if (node.sign === '>') {
  771. if (percentage > popularity) {
  772. result.push(version)
  773. }
  774. } else if (node.sign === '<') {
  775. if (percentage < popularity) {
  776. result.push(version)
  777. }
  778. } else if (node.sign === '<=') {
  779. if (percentage <= popularity) {
  780. result.push(version)
  781. }
  782. } else if (percentage >= popularity) {
  783. result.push(version)
  784. }
  785. return result
  786. }, [])
  787. }
  788. },
  789. popularity_in_place: {
  790. matches: ['sign', 'popularity', 'place'],
  791. regexp: /^(>=?|<=?)\s*(\d+|\d+\.\d+|\.\d+)%\s+in\s+((alt-)?\w\w)$/,
  792. select: function (context, node) {
  793. var popularity = parseFloat(node.popularity)
  794. var place = node.place
  795. if (place.length === 2) {
  796. place = place.toUpperCase()
  797. } else {
  798. place = place.toLowerCase()
  799. }
  800. env.loadCountry(browserslist.usage, place, browserslist.data)
  801. var usage = browserslist.usage[place]
  802. return Object.keys(usage).reduce(function (result, version) {
  803. var percentage = usage[version]
  804. if (percentage == null) {
  805. return result
  806. }
  807. if (node.sign === '>') {
  808. if (percentage > popularity) {
  809. result.push(version)
  810. }
  811. } else if (node.sign === '<') {
  812. if (percentage < popularity) {
  813. result.push(version)
  814. }
  815. } else if (node.sign === '<=') {
  816. if (percentage <= popularity) {
  817. result.push(version)
  818. }
  819. } else if (percentage >= popularity) {
  820. result.push(version)
  821. }
  822. return result
  823. }, [])
  824. }
  825. },
  826. cover: {
  827. matches: ['coverage'],
  828. regexp: /^cover\s+(\d+|\d+\.\d+|\.\d+)%$/i,
  829. select: coverQuery
  830. },
  831. cover_in: {
  832. matches: ['coverage', 'place'],
  833. regexp: /^cover\s+(\d+|\d+\.\d+|\.\d+)%\s+in\s+(my\s+stats|(alt-)?\w\w)$/i,
  834. select: coverQuery
  835. },
  836. supports: {
  837. matches: ['supportType', 'feature'],
  838. regexp: /^(?:(fully|partially)\s+)?supports\s+([\w-]+)$/,
  839. select: function (context, node) {
  840. env.loadFeature(browserslist.cache, node.feature)
  841. var withPartial = node.supportType !== 'fully'
  842. var features = browserslist.cache[node.feature]
  843. var result = []
  844. for (var name in features) {
  845. var data = byName(name, context)
  846. // Only check desktop when latest released mobile has support
  847. var iMax = data.released.length - 1
  848. while (iMax >= 0) {
  849. if (data.released[iMax] in features[name]) break
  850. iMax--
  851. }
  852. var checkDesktop =
  853. context.mobileToDesktop &&
  854. name in browserslist.desktopNames &&
  855. isSupported(features[name][data.released[iMax]], withPartial)
  856. data.versions.forEach(function (version) {
  857. var flags = features[name][version]
  858. if (flags === undefined && checkDesktop) {
  859. flags = features[browserslist.desktopNames[name]][version]
  860. }
  861. if (isSupported(flags, withPartial)) {
  862. result.push(name + ' ' + version)
  863. }
  864. })
  865. }
  866. return result
  867. }
  868. },
  869. electron_range: {
  870. matches: ['from', 'to'],
  871. regexp: /^electron\s+([\d.]+)\s*-\s*([\d.]+)$/i,
  872. select: function (context, node) {
  873. var fromToUse = normalizeElectron(node.from)
  874. var toToUse = normalizeElectron(node.to)
  875. var from = parseFloat(node.from)
  876. var to = parseFloat(node.to)
  877. if (!e2c[fromToUse]) {
  878. throw new BrowserslistError('Unknown version ' + from + ' of electron')
  879. }
  880. if (!e2c[toToUse]) {
  881. throw new BrowserslistError('Unknown version ' + to + ' of electron')
  882. }
  883. return Object.keys(e2c)
  884. .filter(function (i) {
  885. var parsed = parseFloat(i)
  886. return parsed >= from && parsed <= to
  887. })
  888. .map(function (i) {
  889. return 'chrome ' + e2c[i]
  890. })
  891. }
  892. },
  893. node_range: {
  894. matches: ['from', 'to'],
  895. regexp: /^node\s+([\d.]+)\s*-\s*([\d.]+)$/i,
  896. select: function (context, node) {
  897. return browserslist.nodeVersions
  898. .filter(semverFilterLoose('>=', node.from))
  899. .filter(semverFilterLoose('<=', node.to))
  900. .map(function (v) {
  901. return 'node ' + v
  902. })
  903. }
  904. },
  905. browser_range: {
  906. matches: ['browser', 'from', 'to'],
  907. regexp: /^(\w+)\s+([\d.]+)\s*-\s*([\d.]+)$/i,
  908. select: function (context, node) {
  909. var data = checkName(node.browser, context)
  910. var from = parseFloat(normalizeVersion(data, node.from) || node.from)
  911. var to = parseFloat(normalizeVersion(data, node.to) || node.to)
  912. function filter(v) {
  913. var parsed = parseFloat(v)
  914. return parsed >= from && parsed <= to
  915. }
  916. return data.released.filter(filter).map(nameMapper(data.name))
  917. }
  918. },
  919. electron_ray: {
  920. matches: ['sign', 'version'],
  921. regexp: /^electron\s*(>=?|<=?)\s*([\d.]+)$/i,
  922. select: function (context, node) {
  923. var versionToUse = normalizeElectron(node.version)
  924. return Object.keys(e2c)
  925. .filter(generateFilter(node.sign, versionToUse))
  926. .map(function (i) {
  927. return 'chrome ' + e2c[i]
  928. })
  929. }
  930. },
  931. node_ray: {
  932. matches: ['sign', 'version'],
  933. regexp: /^node\s*(>=?|<=?)\s*([\d.]+)$/i,
  934. select: function (context, node) {
  935. return browserslist.nodeVersions
  936. .filter(generateSemverFilter(node.sign, node.version))
  937. .map(function (v) {
  938. return 'node ' + v
  939. })
  940. }
  941. },
  942. browser_ray: {
  943. matches: ['browser', 'sign', 'version'],
  944. regexp: /^(\w+)\s*(>=?|<=?)\s*([\d.]+)$/,
  945. select: function (context, node) {
  946. var version = node.version
  947. var data = checkName(node.browser, context)
  948. var alias = browserslist.versionAliases[data.name][version]
  949. if (alias) version = alias
  950. return data.released
  951. .filter(generateFilter(node.sign, version))
  952. .map(function (v) {
  953. return data.name + ' ' + v
  954. })
  955. }
  956. },
  957. firefox_esr: {
  958. matches: [],
  959. regexp: /^(firefox|ff|fx)\s+esr$/i,
  960. select: function () {
  961. return ['firefox 115', 'firefox 128']
  962. }
  963. },
  964. opera_mini_all: {
  965. matches: [],
  966. regexp: /(operamini|op_mini)\s+all/i,
  967. select: function () {
  968. return ['op_mini all']
  969. }
  970. },
  971. electron_version: {
  972. matches: ['version'],
  973. regexp: /^electron\s+([\d.]+)$/i,
  974. select: function (context, node) {
  975. var versionToUse = normalizeElectron(node.version)
  976. var chrome = e2c[versionToUse]
  977. if (!chrome) {
  978. throw new BrowserslistError(
  979. 'Unknown version ' + node.version + ' of electron'
  980. )
  981. }
  982. return ['chrome ' + chrome]
  983. }
  984. },
  985. node_major_version: {
  986. matches: ['version'],
  987. regexp: /^node\s+(\d+)$/i,
  988. select: nodeQuery
  989. },
  990. node_minor_version: {
  991. matches: ['version'],
  992. regexp: /^node\s+(\d+\.\d+)$/i,
  993. select: nodeQuery
  994. },
  995. node_patch_version: {
  996. matches: ['version'],
  997. regexp: /^node\s+(\d+\.\d+\.\d+)$/i,
  998. select: nodeQuery
  999. },
  1000. current_node: {
  1001. matches: [],
  1002. regexp: /^current\s+node$/i,
  1003. select: function (context) {
  1004. return [env.currentNode(resolve, context)]
  1005. }
  1006. },
  1007. maintained_node: {
  1008. matches: [],
  1009. regexp: /^maintained\s+node\s+versions$/i,
  1010. select: function (context) {
  1011. var now = Date.now()
  1012. var queries = Object.keys(jsEOL)
  1013. .filter(function (key) {
  1014. return (
  1015. now < Date.parse(jsEOL[key].end) &&
  1016. now > Date.parse(jsEOL[key].start) &&
  1017. isEolReleased(key)
  1018. )
  1019. })
  1020. .map(function (key) {
  1021. return 'node ' + key.slice(1)
  1022. })
  1023. return resolve(queries, context)
  1024. }
  1025. },
  1026. phantomjs_1_9: {
  1027. matches: [],
  1028. regexp: /^phantomjs\s+1.9$/i,
  1029. select: function () {
  1030. return ['safari 5']
  1031. }
  1032. },
  1033. phantomjs_2_1: {
  1034. matches: [],
  1035. regexp: /^phantomjs\s+2.1$/i,
  1036. select: function () {
  1037. return ['safari 6']
  1038. }
  1039. },
  1040. browser_version: {
  1041. matches: ['browser', 'version'],
  1042. regexp: /^(\w+)\s+(tp|[\d.]+)$/i,
  1043. select: function (context, node) {
  1044. var version = node.version
  1045. if (/^tp$/i.test(version)) version = 'TP'
  1046. var data = checkName(node.browser, context)
  1047. var alias = normalizeVersion(data, version)
  1048. if (alias) {
  1049. version = alias
  1050. } else {
  1051. if (version.indexOf('.') === -1) {
  1052. alias = version + '.0'
  1053. } else {
  1054. alias = version.replace(/\.0$/, '')
  1055. }
  1056. alias = normalizeVersion(data, alias)
  1057. if (alias) {
  1058. version = alias
  1059. } else if (context.ignoreUnknownVersions) {
  1060. return []
  1061. } else {
  1062. throw new BrowserslistError(
  1063. 'Unknown version ' + version + ' of ' + node.browser
  1064. )
  1065. }
  1066. }
  1067. return [data.name + ' ' + version]
  1068. }
  1069. },
  1070. browserslist_config: {
  1071. matches: [],
  1072. regexp: /^browserslist config$/i,
  1073. select: function (context) {
  1074. return browserslist(undefined, context)
  1075. }
  1076. },
  1077. extends: {
  1078. matches: ['config'],
  1079. regexp: /^extends (.+)$/i,
  1080. select: function (context, node) {
  1081. return resolve(env.loadQueries(context, node.config), context)
  1082. }
  1083. },
  1084. defaults: {
  1085. matches: [],
  1086. regexp: /^defaults$/i,
  1087. select: function (context) {
  1088. return resolve(browserslist.defaults, context)
  1089. }
  1090. },
  1091. dead: {
  1092. matches: [],
  1093. regexp: /^dead$/i,
  1094. select: function (context) {
  1095. var dead = [
  1096. 'Baidu >= 0',
  1097. 'ie <= 11',
  1098. 'ie_mob <= 11',
  1099. 'bb <= 10',
  1100. 'op_mob <= 12.1',
  1101. 'samsung 4'
  1102. ]
  1103. return resolve(dead, context)
  1104. }
  1105. },
  1106. unknown: {
  1107. matches: [],
  1108. regexp: /^(\w+)$/i,
  1109. select: function (context, node) {
  1110. if (byName(node.query, context)) {
  1111. throw new BrowserslistError(
  1112. 'Specify versions in Browserslist query for browser ' + node.query
  1113. )
  1114. } else {
  1115. throw unknownQuery(node.query)
  1116. }
  1117. }
  1118. }
  1119. }
  1120. // Get and convert Can I Use data
  1121. ;(function () {
  1122. for (var name in agents) {
  1123. var browser = agents[name]
  1124. browserslist.data[name] = {
  1125. name: name,
  1126. versions: normalize(agents[name].versions),
  1127. released: normalize(agents[name].versions.slice(0, -3)),
  1128. releaseDate: agents[name].release_date
  1129. }
  1130. fillUsage(browserslist.usage.global, name, browser.usage_global)
  1131. browserslist.versionAliases[name] = {}
  1132. for (var i = 0; i < browser.versions.length; i++) {
  1133. var full = browser.versions[i]
  1134. if (!full) continue
  1135. if (full.indexOf('-') !== -1) {
  1136. var interval = full.split('-')
  1137. for (var j = 0; j < interval.length; j++) {
  1138. browserslist.versionAliases[name][interval[j]] = full
  1139. }
  1140. }
  1141. }
  1142. }
  1143. browserslist.nodeVersions = jsReleases.map(function (release) {
  1144. return release.version
  1145. })
  1146. })()
  1147. module.exports = browserslist