UNPKG

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