| var jsReleases = require('node-releases/data/processed/envs.json') |
| var agents = require('caniuse-lite/dist/unpacker/agents').agents |
| var jsEOL = require('node-releases/data/release-schedule/release-schedule.json') |
| var path = require('path') |
| var e2c = require('electron-to-chromium/versions') |
| |
| var BrowserslistError = require('./error') |
| var env = require('./node') // Will load browser.js in webpack |
| |
| var FLOAT_RANGE = /^\d+(\.\d+)?(-\d+(\.\d+)?)*$/ |
| var YEAR = 365.259641 * 24 * 60 * 60 * 1000 |
| |
| // Enum values MUST be powers of 2, so combination are safe |
| /** @constant {number} */ |
| var QUERY_OR = 1 |
| /** @constant {number} */ |
| var QUERY_AND = 2 |
| |
| function isVersionsMatch (versionA, versionB) { |
| return (versionA + '.').indexOf(versionB + '.') === 0 |
| } |
| |
| function isEolReleased (name) { |
| var version = name.slice(1) |
| return jsReleases.some(function (i) { |
| return isVersionsMatch(i.version, version) |
| }) |
| } |
| |
| function normalize (versions) { |
| return versions.filter(function (version) { |
| return typeof version === 'string' |
| }) |
| } |
| |
| function normalizeElectron (version) { |
| var versionToUse = version |
| if (version.split('.').length === 3) { |
| versionToUse = version |
| .split('.') |
| .slice(0, -1) |
| .join('.') |
| } |
| return versionToUse |
| } |
| |
| function nameMapper (name) { |
| return function mapName (version) { |
| return name + ' ' + version |
| } |
| } |
| |
| function getMajor (version) { |
| return parseInt(version.split('.')[0]) |
| } |
| |
| function getMajorVersions (released, number) { |
| if (released.length === 0) return [] |
| var minimum = getMajor(released[released.length - 1]) - parseInt(number) + 1 |
| var selected = [] |
| for (var i = released.length - 1; i >= 0; i--) { |
| if (minimum > getMajor(released[i])) break |
| selected.unshift(released[i]) |
| } |
| return selected |
| } |
| |
| function uniq (array) { |
| var filtered = [] |
| for (var i = 0; i < array.length; i++) { |
| if (filtered.indexOf(array[i]) === -1) filtered.push(array[i]) |
| } |
| return filtered |
| } |
| |
| // Helpers |
| |
| function fillUsage (result, name, data) { |
| for (var i in data) { |
| result[name + ' ' + i] = data[i] |
| } |
| } |
| |
| function generateFilter (sign, version) { |
| version = parseFloat(version) |
| if (sign === '>') { |
| return function (v) { |
| return parseFloat(v) > version |
| } |
| } else if (sign === '>=') { |
| return function (v) { |
| return parseFloat(v) >= version |
| } |
| } else if (sign === '<') { |
| return function (v) { |
| return parseFloat(v) < version |
| } |
| } else { |
| return function (v) { |
| return parseFloat(v) <= version |
| } |
| } |
| } |
| |
| function generateSemverFilter (sign, version) { |
| version = version.split('.').map(parseSimpleInt) |
| version[1] = version[1] || 0 |
| version[2] = version[2] || 0 |
| if (sign === '>') { |
| return function (v) { |
| v = v.split('.').map(parseSimpleInt) |
| return compareSemver(v, version) > 0 |
| } |
| } else if (sign === '>=') { |
| return function (v) { |
| v = v.split('.').map(parseSimpleInt) |
| return compareSemver(v, version) >= 0 |
| } |
| } else if (sign === '<') { |
| return function (v) { |
| v = v.split('.').map(parseSimpleInt) |
| return compareSemver(version, v) > 0 |
| } |
| } else { |
| return function (v) { |
| v = v.split('.').map(parseSimpleInt) |
| return compareSemver(version, v) >= 0 |
| } |
| } |
| } |
| |
| function parseSimpleInt (x) { |
| return parseInt(x) |
| } |
| |
| function compare (a, b) { |
| if (a < b) return -1 |
| if (a > b) return +1 |
| return 0 |
| } |
| |
| function compareSemver (a, b) { |
| return ( |
| compare(a[0], b[0]) || |
| compare(a[1], b[1]) || |
| compare(a[2], b[2]) |
| ) |
| } |
| |
| function resolveVersion (data, version) { |
| if (data.versions.indexOf(version) !== -1) { |
| return version |
| } else if (browserslist.versionAliases[data.name][version]) { |
| return browserslist.versionAliases[data.name][version] |
| } else { |
| return false |
| } |
| } |
| |
| function normalizeVersion (data, version, context) { |
| var resolved = resolveVersion(data, version) |
| if ( |
| !resolved && |
| context.mobileToDesktop && |
| browserslist.desktopNames[data.name] |
| ) { |
| var alias = checkName(browserslist.desktopNames[data.name]) |
| resolved = resolveVersion(alias, version) |
| } |
| if (resolved) { |
| return resolved |
| } else if (data.versions.length === 1) { |
| return data.versions[0] |
| } else { |
| return false |
| } |
| } |
| |
| function filterByYear (since) { |
| since = since / 1000 |
| return Object.keys(agents).reduce(function (selected, name) { |
| var data = byName(name) |
| if (!data) return selected |
| var versions = Object.keys(data.releaseDate).filter(function (v) { |
| return data.releaseDate[v] >= since |
| }) |
| return selected.concat(versions.map(nameMapper(data.name))) |
| }, []) |
| } |
| |
| function byName (name) { |
| name = name.toLowerCase() |
| name = browserslist.aliases[name] || name |
| return browserslist.data[name] |
| } |
| |
| function checkName (name) { |
| var data = byName(name) |
| if (!data) throw new BrowserslistError('Unknown browser ' + name) |
| return data |
| } |
| |
| function unknownQuery (query) { |
| return new BrowserslistError( |
| 'Unknown browser query `' + query + '`. ' + |
| 'Maybe you are using old Browserslist or made typo in query.' |
| ) |
| } |
| |
| function filterAndroid (list, versions) { |
| var released = browserslist.data.android.released |
| var firstEvergreen = 37 |
| var last = released[released.length - 1] |
| var diff = last - firstEvergreen - versions // First Android Evergreen |
| if (diff > 0) { |
| return list.slice(-1) |
| } else { |
| return list.slice(diff - 1) |
| } |
| } |
| |
| /** |
| * Resolves queries into a browser list. |
| * @param {string|string[]} queries Queries to combine. |
| * Either an array of queries or a long string of queries. |
| * @param {object} [context] Optional arguments to |
| * the select function in `queries`. |
| * @returns {string[]} A list of browsers |
| */ |
| function resolve (queries, context) { |
| if (Array.isArray(queries)) { |
| queries = flatten(queries.map(parse)) |
| } else { |
| queries = parse(queries) |
| } |
| |
| return queries.reduce(function (result, query, index) { |
| var selection = query.queryString |
| |
| var isExclude = selection.indexOf('not ') === 0 |
| if (isExclude) { |
| if (index === 0) { |
| throw new BrowserslistError( |
| 'Write any browsers query (for instance, `defaults`) ' + |
| 'before `' + selection + '`') |
| } |
| selection = selection.slice(4) |
| } |
| |
| for (var i = 0; i < QUERIES.length; i++) { |
| var type = QUERIES[i] |
| var match = selection.match(type.regexp) |
| if (match) { |
| var args = [context].concat(match.slice(1)) |
| var array = type.select.apply(browserslist, args).map(function (j) { |
| var parts = j.split(' ') |
| if (parts[1] === '0') { |
| return parts[0] + ' ' + byName(parts[0]).versions[0] |
| } else { |
| return j |
| } |
| }) |
| |
| switch (query.type) { |
| case QUERY_AND: |
| if (isExclude) { |
| return result.filter(function (j) { |
| // remove result items that are in array |
| // (the relative complement of array in result) |
| return array.indexOf(j) === -1 |
| }) |
| } else { |
| return result.filter(function (j) { |
| // remove result items not in array |
| // (intersect of result and array) |
| return array.indexOf(j) !== -1 |
| }) |
| } |
| case QUERY_OR: |
| default: |
| if (isExclude) { |
| var filter = { } |
| array.forEach(function (j) { |
| filter[j] = true |
| }) |
| return result.filter(function (j) { |
| return !filter[j] |
| }) |
| } |
| // union of result and array |
| return result.concat(array) |
| } |
| } |
| } |
| |
| throw unknownQuery(selection) |
| }, []) |
| } |
| |
| /** |
| * Return array of browsers by selection queries. |
| * |
| * @param {(string|string[])} [queries=browserslist.defaults] Browser queries. |
| * @param {object} [opts] Options. |
| * @param {string} [opts.path="."] Path to processed file. |
| * It will be used to find config files. |
| * @param {string} [opts.env="production"] Processing environment. |
| * It will be used to take right |
| * queries from config file. |
| * @param {string} [opts.config] Path to config file with queries. |
| * @param {object} [opts.stats] Custom browser usage statistics |
| * for "> 1% in my stats" query. |
| * @param {boolean} [opts.ignoreUnknownVersions=false] Do not throw on unknown |
| * version in direct query. |
| * @param {boolean} [opts.dangerousExtend] Disable security checks |
| * for extend query. |
| * @param {boolean} [opts.mobileToDesktop] Alias mobile browsers to the desktop |
| * version when Can I Use doesn't have |
| * data about the specified version. |
| * @returns {string[]} Array with browser names in Can I Use. |
| * |
| * @example |
| * browserslist('IE >= 10, IE 8') //=> ['ie 11', 'ie 10', 'ie 8'] |
| */ |
| function browserslist (queries, opts) { |
| if (typeof opts === 'undefined') opts = { } |
| |
| if (typeof opts.path === 'undefined') { |
| opts.path = path.resolve ? path.resolve('.') : '.' |
| } |
| |
| if (typeof queries === 'undefined' || queries === null) { |
| var config = browserslist.loadConfig(opts) |
| if (config) { |
| queries = config |
| } else { |
| queries = browserslist.defaults |
| } |
| } |
| |
| if (!(typeof queries === 'string' || Array.isArray(queries))) { |
| throw new BrowserslistError( |
| 'Browser queries must be an array or string. Got ' + typeof queries + '.') |
| } |
| |
| var context = { |
| ignoreUnknownVersions: opts.ignoreUnknownVersions, |
| dangerousExtend: opts.dangerousExtend, |
| mobileToDesktop: opts.mobileToDesktop |
| } |
| |
| env.oldDataWarning(browserslist.data) |
| var stats = env.getStat(opts, browserslist.data) |
| if (stats) { |
| context.customUsage = { } |
| for (var browser in stats) { |
| fillUsage(context.customUsage, browser, stats[browser]) |
| } |
| } |
| |
| var result = resolve(queries, context).sort(function (name1, name2) { |
| name1 = name1.split(' ') |
| name2 = name2.split(' ') |
| if (name1[0] === name2[0]) { |
| if (FLOAT_RANGE.test(name1[1]) && FLOAT_RANGE.test(name2[1])) { |
| return parseFloat(name2[1]) - parseFloat(name1[1]) |
| } else { |
| return compare(name2[1], name1[1]) |
| } |
| } else { |
| return compare(name1[0], name2[0]) |
| } |
| }) |
| |
| return uniq(result) |
| } |
| |
| /** |
| * @typedef {object} BrowserslistQuery |
| * @property {number} type A type constant like QUERY_OR @see QUERY_OR. |
| * @property {string} queryString A query like "not ie < 11". |
| */ |
| |
| /** |
| * Parse a browserslist string query |
| * @param {string} queries One or more queries as a string |
| * @returns {BrowserslistQuery[]} An array of BrowserslistQuery |
| */ |
| function parse (queries) { |
| var qs = [] |
| |
| do { |
| queries = doMatch(queries, qs) |
| } while (queries) |
| |
| return qs |
| } |
| |
| /** |
| * Find query matches in a string. This function is meant to be called |
| * repeatedly with the returned query string until there is no more matches. |
| * @param {string} string A string with one or more queries. |
| * @param {BrowserslistQuery[]} qs Out parameter, |
| * will be filled with `BrowserslistQuery`. |
| * @returns {string} The rest of the query string minus the matched part. |
| */ |
| function doMatch (string, qs) { |
| var or = /^(?:,\s*|\s+OR\s+)(.*)/i |
| var and = /^\s+AND\s+(.*)/i |
| |
| return find(string, function (parsed, n, max) { |
| if (and.test(parsed)) { |
| qs.unshift({ type: QUERY_AND, queryString: parsed.match(and)[1] }) |
| return true |
| } else if (or.test(parsed)) { |
| qs.unshift({ type: QUERY_OR, queryString: parsed.match(or)[1] }) |
| return true |
| } else if (n === max) { |
| qs.unshift({ type: QUERY_OR, queryString: parsed.trim() }) |
| return true |
| } |
| return false |
| }) |
| } |
| |
| function find (string, predicate) { |
| for (var n = 1, max = string.length; n <= max; n++) { |
| var parsed = string.substr(-n, n) |
| if (predicate(parsed, n, max)) { |
| return string.slice(0, -n) |
| } |
| } |
| return '' |
| } |
| |
| function flatten (array) { |
| if (!Array.isArray(array)) return [array] |
| return array.reduce(function (a, b) { |
| return a.concat(flatten(b)) |
| }, []) |
| } |
| |
| // Will be filled by Can I Use data below |
| browserslist.data = { } |
| browserslist.usage = { |
| global: { }, |
| custom: null |
| } |
| |
| // Default browsers query |
| browserslist.defaults = [ |
| '> 0.5%', |
| 'last 2 versions', |
| 'Firefox ESR', |
| 'not dead' |
| ] |
| |
| // Browser names aliases |
| browserslist.aliases = { |
| fx: 'firefox', |
| ff: 'firefox', |
| ios: 'ios_saf', |
| explorer: 'ie', |
| blackberry: 'bb', |
| explorermobile: 'ie_mob', |
| operamini: 'op_mini', |
| operamobile: 'op_mob', |
| chromeandroid: 'and_chr', |
| firefoxandroid: 'and_ff', |
| ucandroid: 'and_uc', |
| qqandroid: 'and_qq' |
| } |
| |
| // Can I Use only provides a few versions for some browsers (e.g. and_chr). |
| // Fallback to a similar browser for unknown versions |
| browserslist.desktopNames = { |
| and_chr: 'chrome', |
| and_ff: 'firefox', |
| ie_mob: 'ie', |
| op_mob: 'opera' |
| } |
| |
| // Aliases to work with joined versions like `ios_saf 7.0-7.1` |
| browserslist.versionAliases = { } |
| |
| browserslist.clearCaches = env.clearCaches |
| browserslist.parseConfig = env.parseConfig |
| browserslist.readConfig = env.readConfig |
| browserslist.findConfig = env.findConfig |
| browserslist.loadConfig = env.loadConfig |
| |
| /** |
| * Return browsers market coverage. |
| * |
| * @param {string[]} browsers Browsers names in Can I Use. |
| * @param {string|object} [stats="global"] Which statistics should be used. |
| * Country code or custom statistics. |
| * Pass `"my stats"` to load statistics |
| * from Browserslist files. |
| * |
| * @return {number} Total market coverage for all selected browsers. |
| * |
| * @example |
| * browserslist.coverage(browserslist('> 1% in US'), 'US') //=> 83.1 |
| */ |
| browserslist.coverage = function (browsers, stats) { |
| var data |
| if (typeof stats === 'undefined') { |
| data = browserslist.usage.global |
| } else if (stats === 'my stats') { |
| var opts = {} |
| opts.path = path.resolve ? path.resolve('.') : '.' |
| var customStats = env.getStat(opts) |
| if (!customStats) { |
| throw new BrowserslistError('Custom usage statistics was not provided') |
| } |
| data = {} |
| for (var browser in customStats) { |
| fillUsage(data, browser, customStats[browser]) |
| } |
| } else if (typeof stats === 'string') { |
| if (stats.length > 2) { |
| stats = stats.toLowerCase() |
| } else { |
| stats = stats.toUpperCase() |
| } |
| env.loadCountry(browserslist.usage, stats) |
| data = browserslist.usage[stats] |
| } else { |
| if ('dataByBrowser' in stats) { |
| stats = stats.dataByBrowser |
| } |
| data = { } |
| for (var name in stats) { |
| for (var version in stats[name]) { |
| data[name + ' ' + version] = stats[name][version] |
| } |
| } |
| } |
| |
| return browsers.reduce(function (all, i) { |
| var usage = data[i] |
| if (usage === undefined) { |
| usage = data[i.replace(/ \S+$/, ' 0')] |
| } |
| return all + (usage || 0) |
| }, 0) |
| } |
| |
| var QUERIES = [ |
| { |
| regexp: /^last\s+(\d+)\s+major\s+versions?$/i, |
| select: function (context, versions) { |
| return Object.keys(agents).reduce(function (selected, name) { |
| var data = byName(name) |
| if (!data) return selected |
| var list = getMajorVersions(data.released, versions) |
| list = list.map(nameMapper(data.name)) |
| if (data.name === 'android') list = filterAndroid(list, versions) |
| return selected.concat(list) |
| }, []) |
| } |
| }, |
| { |
| regexp: /^last\s+(\d+)\s+versions?$/i, |
| select: function (context, versions) { |
| return Object.keys(agents).reduce(function (selected, name) { |
| var data = byName(name) |
| if (!data) return selected |
| var list = data.released.slice(-versions) |
| list = list.map(nameMapper(data.name)) |
| if (data.name === 'android') list = filterAndroid(list, versions) |
| return selected.concat(list) |
| }, []) |
| } |
| }, |
| { |
| regexp: /^last\s+(\d+)\s+electron\s+major\s+versions?$/i, |
| select: function (context, versions) { |
| var validVersions = getMajorVersions(Object.keys(e2c).reverse(), versions) |
| return validVersions.map(function (i) { |
| return 'chrome ' + e2c[i] |
| }) |
| } |
| }, |
| { |
| regexp: /^last\s+(\d+)\s+(\w+)\s+major\s+versions?$/i, |
| select: function (context, versions, name) { |
| var data = checkName(name) |
| var validVersions = getMajorVersions(data.released, versions) |
| var list = validVersions.map(nameMapper(data.name)) |
| if (data.name === 'android') list = filterAndroid(list, versions) |
| return list |
| } |
| }, |
| { |
| regexp: /^last\s+(\d+)\s+electron\s+versions?$/i, |
| select: function (context, versions) { |
| return Object.keys(e2c).reverse().slice(-versions).map(function (i) { |
| return 'chrome ' + e2c[i] |
| }) |
| } |
| }, |
| { |
| regexp: /^last\s+(\d+)\s+(\w+)\s+versions?$/i, |
| select: function (context, versions, name) { |
| var data = checkName(name) |
| var list = data.released.slice(-versions).map(nameMapper(data.name)) |
| if (data.name === 'android') list = filterAndroid(list, versions) |
| return list |
| } |
| }, |
| { |
| regexp: /^unreleased\s+versions$/i, |
| select: function () { |
| return Object.keys(agents).reduce(function (selected, name) { |
| var data = byName(name) |
| if (!data) return selected |
| var list = data.versions.filter(function (v) { |
| return data.released.indexOf(v) === -1 |
| }) |
| list = list.map(nameMapper(data.name)) |
| return selected.concat(list) |
| }, []) |
| } |
| }, |
| { |
| regexp: /^unreleased\s+electron\s+versions?$/i, |
| select: function () { |
| return [] |
| } |
| }, |
| { |
| regexp: /^unreleased\s+(\w+)\s+versions?$/i, |
| select: function (context, name) { |
| var data = checkName(name) |
| return data.versions.filter(function (v) { |
| return data.released.indexOf(v) === -1 |
| }).map(nameMapper(data.name)) |
| } |
| }, |
| { |
| regexp: /^last\s+(\d*.?\d+)\s+years?$/i, |
| select: function (context, years) { |
| return filterByYear(Date.now() - YEAR * years) |
| } |
| }, |
| { |
| regexp: /^since (\d+)(?:-(\d+))?(?:-(\d+))?$/i, |
| select: function (context, year, month, date) { |
| year = parseInt(year) |
| month = parseInt(month || '01') - 1 |
| date = parseInt(date || '01') |
| return filterByYear(Date.UTC(year, month, date, 0, 0, 0)) |
| } |
| }, |
| { |
| regexp: /^(>=?|<=?)\s*(\d*\.?\d+)%$/, |
| select: function (context, sign, popularity) { |
| popularity = parseFloat(popularity) |
| var usage = browserslist.usage.global |
| return Object.keys(usage).reduce(function (result, version) { |
| if (sign === '>') { |
| if (usage[version] > popularity) { |
| result.push(version) |
| } |
| } else if (sign === '<') { |
| if (usage[version] < popularity) { |
| result.push(version) |
| } |
| } else if (sign === '<=') { |
| if (usage[version] <= popularity) { |
| result.push(version) |
| } |
| } else if (usage[version] >= popularity) { |
| result.push(version) |
| } |
| return result |
| }, []) |
| } |
| }, |
| { |
| regexp: /^(>=?|<=?)\s*(\d*\.?\d+)%\s+in\s+my\s+stats$/, |
| select: function (context, sign, popularity) { |
| popularity = parseFloat(popularity) |
| if (!context.customUsage) { |
| throw new BrowserslistError('Custom usage statistics was not provided') |
| } |
| var usage = context.customUsage |
| return Object.keys(usage).reduce(function (result, version) { |
| if (sign === '>') { |
| if (usage[version] > popularity) { |
| result.push(version) |
| } |
| } else if (sign === '<') { |
| if (usage[version] < popularity) { |
| result.push(version) |
| } |
| } else if (sign === '<=') { |
| if (usage[version] <= popularity) { |
| result.push(version) |
| } |
| } else if (usage[version] >= popularity) { |
| result.push(version) |
| } |
| return result |
| }, []) |
| } |
| }, |
| { |
| regexp: /^(>=?|<=?)\s*(\d*\.?\d+)%\s+in\s+((alt-)?\w\w)$/, |
| select: function (context, sign, popularity, place) { |
| popularity = parseFloat(popularity) |
| if (place.length === 2) { |
| place = place.toUpperCase() |
| } else { |
| place = place.toLowerCase() |
| } |
| env.loadCountry(browserslist.usage, place) |
| var usage = browserslist.usage[place] |
| return Object.keys(usage).reduce(function (result, version) { |
| if (sign === '>') { |
| if (usage[version] > popularity) { |
| result.push(version) |
| } |
| } else if (sign === '<') { |
| if (usage[version] < popularity) { |
| result.push(version) |
| } |
| } else if (sign === '<=') { |
| if (usage[version] <= popularity) { |
| result.push(version) |
| } |
| } else if (usage[version] >= popularity) { |
| result.push(version) |
| } |
| return result |
| }, []) |
| } |
| }, |
| { |
| regexp: /^cover\s+(\d*\.?\d+)%(\s+in\s+(my\s+stats|(alt-)?\w\w))?$/, |
| select: function (context, coverage, statMode) { |
| coverage = parseFloat(coverage) |
| var usage = browserslist.usage.global |
| if (statMode) { |
| if (statMode.match(/^\s+in\s+my\s+stats$/)) { |
| if (!context.customUsage) { |
| throw new BrowserslistError( |
| 'Custom usage statistics was not provided' |
| ) |
| } |
| usage = context.customUsage |
| } else { |
| var match = statMode.match(/\s+in\s+((alt-)?\w\w)/) |
| var place = match[1] |
| if (place.length === 2) { |
| place = place.toUpperCase() |
| } else { |
| place = place.toLowerCase() |
| } |
| env.loadCountry(browserslist.usage, place) |
| usage = browserslist.usage[place] |
| } |
| } |
| var versions = Object.keys(usage).sort(function (a, b) { |
| return usage[b] - usage[a] |
| }) |
| var coveraged = 0 |
| var result = [] |
| var version |
| for (var i = 0; i <= versions.length; i++) { |
| version = versions[i] |
| if (usage[version] === 0) break |
| coveraged += usage[version] |
| result.push(version) |
| if (coveraged >= coverage) break |
| } |
| return result |
| } |
| }, |
| { |
| regexp: /^electron\s+([\d.]+)\s*-\s*([\d.]+)$/i, |
| select: function (context, from, to) { |
| var fromToUse = normalizeElectron(from) |
| var toToUse = normalizeElectron(to) |
| if (!e2c[fromToUse]) { |
| throw new BrowserslistError('Unknown version ' + from + ' of electron') |
| } |
| if (!e2c[toToUse]) { |
| throw new BrowserslistError('Unknown version ' + to + ' of electron') |
| } |
| from = parseFloat(from) |
| to = parseFloat(to) |
| return Object.keys(e2c).filter(function (i) { |
| var parsed = parseFloat(i) |
| return parsed >= from && parsed <= to |
| }).map(function (i) { |
| return 'chrome ' + e2c[i] |
| }) |
| } |
| }, |
| { |
| regexp: /^(\w+)\s+([\d.]+)\s*-\s*([\d.]+)$/i, |
| select: function (context, name, from, to) { |
| var data = checkName(name) |
| from = parseFloat(normalizeVersion(data, from, context) || from) |
| to = parseFloat(normalizeVersion(data, to, context) || to) |
| function filter (v) { |
| var parsed = parseFloat(v) |
| return parsed >= from && parsed <= to |
| } |
| return data.released.filter(filter).map(nameMapper(data.name)) |
| } |
| }, |
| { |
| regexp: /^electron\s*(>=?|<=?)\s*([\d.]+)$/i, |
| select: function (context, sign, version) { |
| var versionToUse = normalizeElectron(version) |
| return Object.keys(e2c) |
| .filter(generateFilter(sign, versionToUse)) |
| .map(function (i) { |
| return 'chrome ' + e2c[i] |
| }) |
| } |
| }, |
| { |
| regexp: /^node\s*(>=?|<=?)\s*([\d.]+)$/i, |
| select: function (context, sign, version) { |
| var nodeVersions = jsReleases.filter(function (i) { |
| return i.name === 'nodejs' |
| }).map(function (i) { |
| return i.version |
| }) |
| return nodeVersions |
| .filter(generateSemverFilter(sign, version)) |
| .map(function (v) { |
| return 'node ' + v |
| }) |
| } |
| }, |
| { |
| regexp: /^(\w+)\s*(>=?|<=?)\s*([\d.]+)$/, |
| select: function (context, name, sign, version) { |
| var data = checkName(name) |
| var alias = browserslist.versionAliases[data.name][version] |
| if (alias) { |
| version = alias |
| } |
| return data.released |
| .filter(generateFilter(sign, version)) |
| .map(function (v) { |
| return data.name + ' ' + v |
| }) |
| } |
| }, |
| { |
| regexp: /^(firefox|ff|fx)\s+esr$/i, |
| select: function () { |
| return ['firefox 68', 'firefox 60'] |
| } |
| }, |
| { |
| regexp: /(operamini|op_mini)\s+all/i, |
| select: function () { |
| return ['op_mini all'] |
| } |
| }, |
| { |
| regexp: /^electron\s+([\d.]+)$/i, |
| select: function (context, version) { |
| var versionToUse = normalizeElectron(version) |
| var chrome = e2c[versionToUse] |
| if (!chrome) { |
| throw new BrowserslistError( |
| 'Unknown version ' + version + ' of electron') |
| } |
| return ['chrome ' + chrome] |
| } |
| }, |
| { |
| regexp: /^node\s+(\d+(\.\d+)?(\.\d+)?)$/i, |
| select: function (context, version) { |
| var nodeReleases = jsReleases.filter(function (i) { |
| return i.name === 'nodejs' |
| }) |
| var matched = nodeReleases.filter(function (i) { |
| return isVersionsMatch(i.version, version) |
| }) |
| if (matched.length === 0) { |
| if (context.ignoreUnknownVersions) { |
| return [] |
| } else { |
| throw new BrowserslistError( |
| 'Unknown version ' + version + ' of Node.js') |
| } |
| } |
| return ['node ' + matched[matched.length - 1].version] |
| } |
| }, |
| { |
| regexp: /^current\s+node$/i, |
| select: function (context) { |
| return [env.currentNode(resolve, context)] |
| } |
| }, |
| { |
| regexp: /^maintained\s+node\s+versions$/i, |
| select: function (context) { |
| var now = Date.now() |
| var queries = Object.keys(jsEOL).filter(function (key) { |
| return now < Date.parse(jsEOL[key].end) && |
| now > Date.parse(jsEOL[key].start) && |
| isEolReleased(key) |
| }).map(function (key) { |
| return 'node ' + key.slice(1) |
| }) |
| return resolve(queries, context) |
| } |
| }, |
| { |
| regexp: /^(\w+)\s+(tp|[\d.]+)$/i, |
| select: function (context, name, version) { |
| if (/^tp$/i.test(version)) version = 'TP' |
| var data = checkName(name) |
| var alias = normalizeVersion(data, version, context) |
| if (alias) { |
| version = alias |
| } else { |
| if (version.indexOf('.') === -1) { |
| alias = version + '.0' |
| } else { |
| alias = version.replace(/\.0$/, '') |
| } |
| alias = normalizeVersion(data, alias, context) |
| if (alias) { |
| version = alias |
| } else if (context.ignoreUnknownVersions) { |
| return [] |
| } else { |
| throw new BrowserslistError( |
| 'Unknown version ' + version + ' of ' + name) |
| } |
| } |
| return [data.name + ' ' + version] |
| } |
| }, |
| { |
| regexp: /^extends (.+)$/i, |
| select: function (context, name) { |
| return resolve(env.loadQueries(context, name), context) |
| } |
| }, |
| { |
| regexp: /^defaults$/i, |
| select: function () { |
| return browserslist(browserslist.defaults) |
| } |
| }, |
| { |
| regexp: /^android all$/i, |
| select: function () { |
| /* istanbul ignore next */ |
| console.warn('BrowserslistError: Unknown browser query `android all`') |
| /* istanbul ignore next */ |
| return [] |
| } |
| }, |
| { |
| regexp: /^dead$/i, |
| select: function (context) { |
| var dead = [ |
| 'ie <= 10', 'ie_mob <= 10', |
| 'bb <= 10', |
| 'op_mob <= 12.1', |
| 'safari <= 11', |
| 'ios_saf <= 11', |
| 'samsung 4' |
| ] |
| return resolve(dead, context) |
| } |
| }, |
| { |
| regexp: /^(\w+)$/i, |
| select: function (context, name) { |
| if (byName(name)) { |
| throw new BrowserslistError( |
| 'Specify versions in Browserslist query for browser ' + name) |
| } else { |
| throw unknownQuery(name) |
| } |
| } |
| } |
| ]; |
| |
| // Get and convert Can I Use data |
| |
| (function () { |
| for (var name in agents) { |
| var browser = agents[name] |
| browserslist.data[name] = { |
| name: name, |
| versions: normalize(agents[name].versions), |
| released: normalize(agents[name].versions.slice(0, -3)), |
| releaseDate: agents[name].release_date |
| } |
| fillUsage(browserslist.usage.global, name, browser.usage_global) |
| |
| browserslist.versionAliases[name] = { } |
| for (var i = 0; i < browser.versions.length; i++) { |
| var full = browser.versions[i] |
| if (!full) continue |
| |
| if (full.indexOf('-') !== -1) { |
| var interval = full.split('-') |
| for (var j = 0; j < interval.length; j++) { |
| browserslist.versionAliases[name][interval[j]] = full |
| } |
| } |
| } |
| } |
| }()) |
| |
| module.exports = browserslist |