UNPKG

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