UNPKG

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