1 | var path = require('path')
|
2 | var e2c = require('electron-to-chromium/versions')
|
3 |
|
4 | var agents = require('caniuse-lite/dist/unpacker/agents').agents
|
5 |
|
6 | var BrowserslistError = require('./error')
|
7 | var env = require('./node')
|
8 |
|
9 | var FLOAT_RANGE = /^\d+(\.\d+)?(-\d+(\.\d+)?)*$/
|
10 |
|
11 | function normalize (versions) {
|
12 | return versions.filter(function (version) {
|
13 | return typeof version === 'string'
|
14 | })
|
15 | }
|
16 |
|
17 | function nameMapper (name) {
|
18 | return function mapName (version) {
|
19 | return name + ' ' + version
|
20 | }
|
21 | }
|
22 |
|
23 | function getMajor (version) {
|
24 | return parseInt(version.split('.')[0])
|
25 | }
|
26 |
|
27 | function getMajorVersions (released, number) {
|
28 | if (released.length === 0) return []
|
29 | var minimum = getMajor(released[released.length - 1]) - parseInt(number) + 1
|
30 | var selected = []
|
31 | for (var i = released.length - 1; i >= 0; i--) {
|
32 | if (minimum > getMajor(released[i])) break
|
33 | selected.unshift(released[i])
|
34 | }
|
35 | return selected
|
36 | }
|
37 |
|
38 | function uniq (array) {
|
39 | var filtered = []
|
40 | for (var i = 0; i < array.length; i++) {
|
41 | if (filtered.indexOf(array[i]) === -1) filtered.push(array[i])
|
42 | }
|
43 | return filtered
|
44 | }
|
45 |
|
46 |
|
47 |
|
48 | function fillUsage (result, name, data) {
|
49 | for (var i in data) {
|
50 | result[name + ' ' + i] = data[i]
|
51 | }
|
52 | }
|
53 |
|
54 | function generateFilter (sign, version) {
|
55 | version = parseFloat(version)
|
56 | if (sign === '>') {
|
57 | return function (v) {
|
58 | return parseFloat(v) > version
|
59 | }
|
60 | } else if (sign === '>=') {
|
61 | return function (v) {
|
62 | return parseFloat(v) >= version
|
63 | }
|
64 | } else if (sign === '<') {
|
65 | return function (v) {
|
66 | return parseFloat(v) < version
|
67 | }
|
68 | } else {
|
69 | return function (v) {
|
70 | return parseFloat(v) <= version
|
71 | }
|
72 | }
|
73 | }
|
74 |
|
75 | function compareStrings (a, b) {
|
76 | if (a < b) return -1
|
77 | if (a > b) return +1
|
78 | return 0
|
79 | }
|
80 |
|
81 | function normalizeVersion (data, version) {
|
82 | if (data.versions.indexOf(version) !== -1) {
|
83 | return version
|
84 | } else if (browserslist.versionAliases[data.name][version]) {
|
85 | return browserslist.versionAliases[data.name][version]
|
86 | } else if (data.versions.length === 1) {
|
87 | return data.versions[0]
|
88 | } else {
|
89 | return false
|
90 | }
|
91 | }
|
92 |
|
93 | function filterByYear (since) {
|
94 | return Object.keys(agents).reduce(function (selected, name) {
|
95 | var data = byName(name)
|
96 | if (!data) return selected
|
97 | var versions = Object.keys(data.releaseDate).filter(function (v) {
|
98 | return data.releaseDate[v] >= since
|
99 | })
|
100 | return selected.concat(versions.map(nameMapper(data.name)))
|
101 | }, [])
|
102 | }
|
103 |
|
104 | function byName (name) {
|
105 | name = name.toLowerCase()
|
106 | name = browserslist.aliases[name] || name
|
107 | return browserslist.data[name]
|
108 | }
|
109 |
|
110 | function checkName (name) {
|
111 | var data = byName(name)
|
112 | if (!data) throw new BrowserslistError('Unknown browser ' + name)
|
113 | return data
|
114 | }
|
115 |
|
116 | function resolve (queries, context) {
|
117 | return queries.reduce(function (result, selection, index) {
|
118 | selection = selection.trim()
|
119 | if (selection === '') return result
|
120 |
|
121 | var isExclude = selection.indexOf('not ') === 0
|
122 | if (isExclude) {
|
123 | if (index === 0) {
|
124 | throw new BrowserslistError(
|
125 | 'Write any browsers query (for instance, `defaults`) ' +
|
126 | 'before `' + selection + '`')
|
127 | }
|
128 | selection = selection.slice(4)
|
129 | }
|
130 |
|
131 | for (var i = 0; i < QUERIES.length; i++) {
|
132 | var type = QUERIES[i]
|
133 | var match = selection.match(type.regexp)
|
134 | if (match) {
|
135 | var args = [context].concat(match.slice(1))
|
136 | var array = type.select.apply(browserslist, args)
|
137 | if (isExclude) {
|
138 | array = array.concat(array.map(function (j) {
|
139 | return j.replace(/\s\d+/, ' 0')
|
140 | }))
|
141 | return result.filter(function (j) {
|
142 | return array.indexOf(j) === -1
|
143 | })
|
144 | }
|
145 | return result.concat(array)
|
146 | }
|
147 | }
|
148 |
|
149 | throw new BrowserslistError('Unknown browser query `' + selection + '`')
|
150 | }, [])
|
151 | }
|
152 |
|
153 |
|
154 |
|
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 |
|
175 | function browserslist (queries, opts) {
|
176 | if (typeof opts === 'undefined') opts = { }
|
177 |
|
178 | if (typeof opts.path === 'undefined') {
|
179 | opts.path = path.resolve ? path.resolve('.') : '.'
|
180 | }
|
181 |
|
182 | if (typeof queries === 'undefined' || queries === null) {
|
183 | var config = browserslist.loadConfig(opts)
|
184 | if (config) {
|
185 | queries = config
|
186 | } else {
|
187 | queries = browserslist.defaults
|
188 | }
|
189 | }
|
190 |
|
191 | if (typeof queries === 'string') {
|
192 | queries = queries.split(/,\s*/)
|
193 | }
|
194 |
|
195 | if (!Array.isArray(queries)) {
|
196 | throw new BrowserslistError(
|
197 | 'Browser queries must be an array. Got ' + typeof queries + '.')
|
198 | }
|
199 |
|
200 | var context = {
|
201 | ignoreUnknownVersions: opts.ignoreUnknownVersions,
|
202 | dangerousExtend: opts.dangerousExtend
|
203 | }
|
204 |
|
205 | var stats = env.getStat(opts)
|
206 | if (stats) {
|
207 | if ('dataByBrowser' in stats) {
|
208 | stats = stats.dataByBrowser
|
209 | }
|
210 | context.customUsage = { }
|
211 | for (var browser in stats) {
|
212 | fillUsage(context.customUsage, browser, stats[browser])
|
213 | }
|
214 | }
|
215 |
|
216 | var result = resolve(queries, context).map(function (i) {
|
217 | var parts = i.split(' ')
|
218 | var name = parts[0]
|
219 | var version = parts[1]
|
220 | if (version === '0') {
|
221 | return name + ' ' + byName(name).versions[0]
|
222 | } else {
|
223 | return i
|
224 | }
|
225 | }).sort(function (name1, name2) {
|
226 | name1 = name1.split(' ')
|
227 | name2 = name2.split(' ')
|
228 | if (name1[0] === name2[0]) {
|
229 | if (FLOAT_RANGE.test(name1[1]) && FLOAT_RANGE.test(name2[1])) {
|
230 | return parseFloat(name2[1]) - parseFloat(name1[1])
|
231 | } else {
|
232 | return compareStrings(name2[1], name1[1])
|
233 | }
|
234 | } else {
|
235 | return compareStrings(name1[0], name2[0])
|
236 | }
|
237 | })
|
238 |
|
239 | return uniq(result)
|
240 | }
|
241 |
|
242 |
|
243 | browserslist.data = { }
|
244 | browserslist.usage = {
|
245 | global: { },
|
246 | custom: null
|
247 | }
|
248 |
|
249 |
|
250 | browserslist.defaults = [
|
251 | '> 0.5%',
|
252 | 'last 2 versions',
|
253 | 'Firefox ESR',
|
254 | 'not dead'
|
255 | ]
|
256 |
|
257 |
|
258 | browserslist.aliases = {
|
259 | fx: 'firefox',
|
260 | ff: 'firefox',
|
261 | ios: 'ios_saf',
|
262 | explorer: 'ie',
|
263 | blackberry: 'bb',
|
264 | explorermobile: 'ie_mob',
|
265 | operamini: 'op_mini',
|
266 | operamobile: 'op_mob',
|
267 | chromeandroid: 'and_chr',
|
268 | firefoxandroid: 'and_ff',
|
269 | ucandroid: 'and_uc',
|
270 | qqandroid: 'and_qq'
|
271 | }
|
272 |
|
273 |
|
274 | browserslist.versionAliases = { }
|
275 |
|
276 | browserslist.clearCaches = env.clearCaches
|
277 | browserslist.parseConfig = env.parseConfig
|
278 | browserslist.readConfig = env.readConfig
|
279 | browserslist.findConfig = env.findConfig
|
280 | browserslist.loadConfig = env.loadConfig
|
281 |
|
282 |
|
283 |
|
284 |
|
285 |
|
286 |
|
287 |
|
288 |
|
289 |
|
290 |
|
291 |
|
292 |
|
293 |
|
294 | browserslist.coverage = function (browsers, stats) {
|
295 | var data
|
296 | if (typeof stats === 'undefined') {
|
297 | data = browserslist.usage.global
|
298 | } else if (typeof stats === 'string') {
|
299 | if (stats.length > 2) {
|
300 | stats = stats.toLowerCase()
|
301 | } else {
|
302 | stats = stats.toUpperCase()
|
303 | }
|
304 | env.loadCountry(browserslist.usage, stats)
|
305 | data = browserslist.usage[stats]
|
306 | } else {
|
307 | if ('dataByBrowser' in stats) {
|
308 | stats = stats.dataByBrowser
|
309 | }
|
310 | data = { }
|
311 | for (var name in stats) {
|
312 | for (var version in stats[name]) {
|
313 | data[name + ' ' + version] = stats[name][version]
|
314 | }
|
315 | }
|
316 | }
|
317 |
|
318 | return browsers.reduce(function (all, i) {
|
319 | var usage = data[i]
|
320 | if (usage === undefined) {
|
321 | usage = data[i.replace(/ [\d.]+$/, ' 0')]
|
322 | }
|
323 | return all + (usage || 0)
|
324 | }, 0)
|
325 | }
|
326 |
|
327 | var QUERIES = [
|
328 | {
|
329 | regexp: /^last\s+(\d+)\s+major versions?$/i,
|
330 | select: function (context, versions) {
|
331 | return Object.keys(agents).reduce(function (selected, name) {
|
332 | var data = byName(name)
|
333 | if (!data) return selected
|
334 | var array = getMajorVersions(data.released, versions)
|
335 |
|
336 | array = array.map(nameMapper(data.name))
|
337 | return selected.concat(array)
|
338 | }, [])
|
339 | }
|
340 | },
|
341 | {
|
342 | regexp: /^last\s+(\d+)\s+versions?$/i,
|
343 | select: function (context, versions) {
|
344 | return Object.keys(agents).reduce(function (selected, name) {
|
345 | var data = byName(name)
|
346 | if (!data) return selected
|
347 | var array = data.released.slice(-versions)
|
348 |
|
349 | array = array.map(nameMapper(data.name))
|
350 | return selected.concat(array)
|
351 | }, [])
|
352 | }
|
353 | },
|
354 | {
|
355 | regexp: /^last\s+(\d+)\s+electron\s+major versions?$/i,
|
356 | select: function (context, versions) {
|
357 | var validVersions = getMajorVersions(Object.keys(e2c).reverse(), versions)
|
358 | return validVersions.map(function (i) {
|
359 | return 'chrome ' + e2c[i]
|
360 | })
|
361 | }
|
362 | },
|
363 | {
|
364 | regexp: /^last\s+(\d+)\s+(\w+)\s+major versions?$/i,
|
365 | select: function (context, versions, name) {
|
366 | var data = checkName(name)
|
367 | var validVersions = getMajorVersions(data.released, versions)
|
368 | return validVersions.map(nameMapper(data.name))
|
369 | }
|
370 | },
|
371 | {
|
372 | regexp: /^last\s+(\d+)\s+electron\s+versions?$/i,
|
373 | select: function (context, versions) {
|
374 | return Object.keys(e2c).reverse().slice(-versions).map(function (i) {
|
375 | return 'chrome ' + e2c[i]
|
376 | })
|
377 | }
|
378 | },
|
379 | {
|
380 | regexp: /^last\s+(\d+)\s+(\w+)\s+versions?$/i,
|
381 | select: function (context, versions, name) {
|
382 | var data = checkName(name)
|
383 | return data.released.slice(-versions).map(nameMapper(data.name))
|
384 | }
|
385 | },
|
386 | {
|
387 | regexp: /^unreleased\s+versions$/i,
|
388 | select: function () {
|
389 | return Object.keys(agents).reduce(function (selected, name) {
|
390 | var data = byName(name)
|
391 | if (!data) return selected
|
392 | var array = data.versions.filter(function (v) {
|
393 | return data.released.indexOf(v) === -1
|
394 | })
|
395 |
|
396 | array = array.map(nameMapper(data.name))
|
397 | return selected.concat(array)
|
398 | }, [])
|
399 | }
|
400 | },
|
401 | {
|
402 | regexp: /^unreleased\s+electron\s+versions?$/i,
|
403 | select: function () {
|
404 | return []
|
405 | }
|
406 | },
|
407 | {
|
408 | regexp: /^unreleased\s+(\w+)\s+versions?$/i,
|
409 | select: function (context, name) {
|
410 | var data = checkName(name)
|
411 | return data.versions.filter(function (v) {
|
412 | return data.released.indexOf(v) === -1
|
413 | }).map(nameMapper(data.name))
|
414 | }
|
415 | },
|
416 | {
|
417 | regexp: /^last\s+(\d+)\s+years?$/i,
|
418 | select: function (context, years) {
|
419 | var date = new Date()
|
420 | var since = date.setFullYear(date.getFullYear() - years) / 1000
|
421 |
|
422 | return filterByYear(since)
|
423 | }
|
424 | },
|
425 | {
|
426 | regexp: /^since (\d+)(?:-(\d+))?(?:-(\d+))?$/i,
|
427 | select: function (context, year, month, date) {
|
428 | year = parseInt(year)
|
429 | month = parseInt(month || '01') - 1
|
430 | date = parseInt(date || '01')
|
431 | var since = Date.UTC(year, month, date, 0, 0, 0) / 1000
|
432 |
|
433 | return filterByYear(since)
|
434 | }
|
435 | },
|
436 | {
|
437 | regexp: /^(>=?|<=?)\s*(\d*\.?\d+)%$/,
|
438 | select: function (context, sign, popularity) {
|
439 | popularity = parseFloat(popularity)
|
440 | var usage = browserslist.usage.global
|
441 |
|
442 | return Object.keys(usage).reduce(function (result, version) {
|
443 | if (sign === '>') {
|
444 | if (usage[version] > popularity) {
|
445 | result.push(version)
|
446 | }
|
447 | } else if (sign === '<') {
|
448 | if (usage[version] < popularity) {
|
449 | result.push(version)
|
450 | }
|
451 | } else if (sign === '<=') {
|
452 | if (usage[version] <= popularity) {
|
453 | result.push(version)
|
454 | }
|
455 | } else if (usage[version] >= popularity) {
|
456 | result.push(version)
|
457 | }
|
458 | return result
|
459 | }, [])
|
460 | }
|
461 | },
|
462 | {
|
463 | regexp: /^(>=?|<=?)\s*(\d*\.?\d+)%\s+in\s+my\s+stats$/,
|
464 | select: function (context, sign, popularity) {
|
465 | popularity = parseFloat(popularity)
|
466 |
|
467 | if (!context.customUsage) {
|
468 | throw new BrowserslistError('Custom usage statistics was not provided')
|
469 | }
|
470 |
|
471 | var usage = context.customUsage
|
472 |
|
473 | return Object.keys(usage).reduce(function (result, version) {
|
474 | if (sign === '>') {
|
475 | if (usage[version] > popularity) {
|
476 | result.push(version)
|
477 | }
|
478 | } else if (sign === '<') {
|
479 | if (usage[version] < popularity) {
|
480 | result.push(version)
|
481 | }
|
482 | } else if (sign === '<=') {
|
483 | if (usage[version] <= popularity) {
|
484 | result.push(version)
|
485 | }
|
486 | } else if (usage[version] >= popularity) {
|
487 | result.push(version)
|
488 | }
|
489 | return result
|
490 | }, [])
|
491 | }
|
492 | },
|
493 | {
|
494 | regexp: /^(>=?|<=?)\s*(\d*\.?\d+)%\s+in\s+((alt-)?\w\w)$/,
|
495 | select: function (context, sign, popularity, place) {
|
496 | popularity = parseFloat(popularity)
|
497 |
|
498 | if (place.length === 2) {
|
499 | place = place.toUpperCase()
|
500 | } else {
|
501 | place = place.toLowerCase()
|
502 | }
|
503 |
|
504 | env.loadCountry(browserslist.usage, place)
|
505 | var usage = browserslist.usage[place]
|
506 |
|
507 | return Object.keys(usage).reduce(function (result, version) {
|
508 | if (sign === '>') {
|
509 | if (usage[version] > popularity) {
|
510 | result.push(version)
|
511 | }
|
512 | } else if (sign === '<') {
|
513 | if (usage[version] < popularity) {
|
514 | result.push(version)
|
515 | }
|
516 | } else if (sign === '<=') {
|
517 | if (usage[version] <= popularity) {
|
518 | result.push(version)
|
519 | }
|
520 | } else if (usage[version] >= popularity) {
|
521 | result.push(version)
|
522 | }
|
523 | return result
|
524 | }, [])
|
525 | }
|
526 | },
|
527 | {
|
528 | regexp: /^electron\s+([\d.]+)\s*-\s*([\d.]+)$/i,
|
529 | select: function (context, from, to) {
|
530 | if (!e2c[from]) {
|
531 | throw new BrowserslistError('Unknown version ' + from + ' of electron')
|
532 | }
|
533 | if (!e2c[to]) {
|
534 | throw new BrowserslistError('Unknown version ' + to + ' of electron')
|
535 | }
|
536 |
|
537 | from = parseFloat(from)
|
538 | to = parseFloat(to)
|
539 |
|
540 | return Object.keys(e2c).filter(function (i) {
|
541 | var parsed = parseFloat(i)
|
542 | return parsed >= from && parsed <= to
|
543 | }).map(function (i) {
|
544 | return 'chrome ' + e2c[i]
|
545 | })
|
546 | }
|
547 | },
|
548 | {
|
549 | regexp: /^(\w+)\s+([\d.]+)\s*-\s*([\d.]+)$/i,
|
550 | select: function (context, name, from, to) {
|
551 | var data = checkName(name)
|
552 | from = parseFloat(normalizeVersion(data, from) || from)
|
553 | to = parseFloat(normalizeVersion(data, to) || to)
|
554 |
|
555 | function filter (v) {
|
556 | var parsed = parseFloat(v)
|
557 | return parsed >= from && parsed <= to
|
558 | }
|
559 |
|
560 | return data.released.filter(filter).map(nameMapper(data.name))
|
561 | }
|
562 | },
|
563 | {
|
564 | regexp: /^electron\s*(>=?|<=?)\s*([\d.]+)$/i,
|
565 | select: function (context, sign, version) {
|
566 | return Object.keys(e2c)
|
567 | .filter(generateFilter(sign, version))
|
568 | .map(function (i) {
|
569 | return 'chrome ' + e2c[i]
|
570 | })
|
571 | }
|
572 | },
|
573 | {
|
574 | regexp: /^(\w+)\s*(>=?|<=?)\s*([\d.]+)$/,
|
575 | select: function (context, name, sign, version) {
|
576 | var data = checkName(name)
|
577 | var alias = browserslist.versionAliases[data.name][version]
|
578 | if (alias) {
|
579 | version = alias
|
580 | }
|
581 | return data.released
|
582 | .filter(generateFilter(sign, version))
|
583 | .map(function (v) {
|
584 | return data.name + ' ' + v
|
585 | })
|
586 | }
|
587 | },
|
588 | {
|
589 | regexp: /^(firefox|ff|fx)\s+esr$/i,
|
590 | select: function () {
|
591 | return ['firefox 52']
|
592 | }
|
593 | },
|
594 | {
|
595 | regexp: /(operamini|op_mini)\s+all/i,
|
596 | select: function () {
|
597 | return ['op_mini all']
|
598 | }
|
599 | },
|
600 | {
|
601 | regexp: /^electron\s+([\d.]+)$/i,
|
602 | select: function (context, version) {
|
603 | var chrome = e2c[version]
|
604 | if (!chrome) {
|
605 | throw new BrowserslistError(
|
606 | 'Unknown version ' + version + ' of electron')
|
607 | }
|
608 | return ['chrome ' + chrome]
|
609 | }
|
610 | },
|
611 | {
|
612 | regexp: /^(\w+)\s+(tp|[\d.]+)$/i,
|
613 | select: function (context, name, version) {
|
614 | if (/^tp$/i.test(version)) version = 'TP'
|
615 | var data = checkName(name)
|
616 | var alias = normalizeVersion(data, version)
|
617 | if (alias) {
|
618 | version = alias
|
619 | } else {
|
620 | if (version.indexOf('.') === -1) {
|
621 | alias = version + '.0'
|
622 | } else {
|
623 | alias = version.replace(/\.0$/, '')
|
624 | }
|
625 | alias = normalizeVersion(data, alias)
|
626 | if (alias) {
|
627 | version = alias
|
628 | } else if (context.ignoreUnknownVersions) {
|
629 | return []
|
630 | } else {
|
631 | throw new BrowserslistError(
|
632 | 'Unknown version ' + version + ' of ' + name)
|
633 | }
|
634 | }
|
635 | return [data.name + ' ' + version]
|
636 | }
|
637 | },
|
638 | {
|
639 | regexp: /^extends (.+)$/i,
|
640 | select: function (context, name) {
|
641 | return resolve(env.loadQueries(context, name), context)
|
642 | }
|
643 | },
|
644 | {
|
645 | regexp: /^defaults$/i,
|
646 | select: function () {
|
647 | return browserslist(browserslist.defaults)
|
648 | }
|
649 | },
|
650 | {
|
651 | regexp: /^dead$/i,
|
652 | select: function () {
|
653 | return ['ie 10', 'ie_mob 10', 'bb 10', 'bb 7']
|
654 | }
|
655 | }
|
656 | ];
|
657 |
|
658 |
|
659 |
|
660 | (function () {
|
661 | for (var name in agents) {
|
662 | var browser = agents[name]
|
663 | browserslist.data[name] = {
|
664 | name: name,
|
665 | versions: normalize(agents[name].versions),
|
666 | released: normalize(agents[name].versions.slice(0, -3)),
|
667 | releaseDate: agents[name].release_date
|
668 | }
|
669 | fillUsage(browserslist.usage.global, name, browser.usage_global)
|
670 |
|
671 | browserslist.versionAliases[name] = { }
|
672 | for (var i = 0; i < browser.versions.length; i++) {
|
673 | var full = browser.versions[i]
|
674 | if (!full) continue
|
675 |
|
676 | if (full.indexOf('-') !== -1) {
|
677 | var interval = full.split('-')
|
678 | for (var j = 0; j < interval.length; j++) {
|
679 | browserslist.versionAliases[name][interval[j]] = full
|
680 | }
|
681 | }
|
682 | }
|
683 | }
|
684 | }())
|
685 |
|
686 | module.exports = browserslist
|