UNPKG

farm-browserslist-generator

Version:

A library that makes generating and validating Browserslists a breeze!

1,075 lines (1,063 loc) 90.7 kB
'use strict'; Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const Browserslist = require('browserslist'); const caniuseLite = require('caniuse-lite'); const objectPath = require('object-path'); const semver = require('semver'); const uaParserJs = require('ua-parser-js'); const isbot = require('isbot'); const module$1 = require('module'); var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null; /** * Coerces the given version */ function ensureSemver(browser, version) { if ((browser === "op_mini" || browser === "android") && version === "all") { return semver.coerce("0.0.0"); } else if (browser === "safari" && version === "TP") { return SAFARI_TP_MAJOR_VERSION; } return semver.coerce(version, { loose: true }); } /** * Coerces the given version */ function coerceToString(browser, version) { return ensureSemver(browser, version).toString(); } /** * Compares two versions, a and b */ function compareVersions(a, b) { const normalizedA = isNaN(parseFloat(a)) ? a : parseFloat(a); const normalizedB = isNaN(parseFloat(b)) ? b : parseFloat(b); if (typeof normalizedA === "string" && typeof normalizedB !== "string") { return 1; } if (typeof normalizedB === "string" && typeof normalizedA !== "string") { return -1; } if (normalizedA < normalizedB) return -1; if (normalizedA > normalizedB) return 1; return 0; } /** * A Regular Expression that captures the part of a browser version that should be kept */ const NORMALIZE_BROWSER_VERSION_REGEXP = /(?![\d.,]+-)-*(.*)/; const SAFARI_TP_MAJOR_VERSION = (() => { const versions = getSortedBrowserVersions("safari"); const lastVersionBeforeTp = versions[versions.length - 2]; const coerced = semver.coerce(lastVersionBeforeTp); if (coerced.minor === 9) { return semver.coerce(coerced.major + 1); } else { return semver.coerce(`${coerced.major}.${coerced.minor + 1}.0`); } })(); /** * Ensures that for any given version of a browser, if it is newer than the latest known version, the last known version will be used as a fallback */ function normalizeBrowserVersion(browser, givenVersion, versions = getSortedBrowserVersions(browser), allowSmaller = false) { const givenVersionCoerced = ensureSemver(browser, givenVersion); const latestVersion = getLatestVersionOfBrowser(browser); const latestVersionCoerced = ensureSemver(browser, latestVersion); if (givenVersionCoerced == null || latestVersionCoerced == null) { throw new TypeError(`Could not detect the version of: '${givenVersion}' for browser: ${browser}`); } if (givenVersionCoerced.major > latestVersionCoerced.major || (givenVersionCoerced.major === latestVersionCoerced.major && givenVersionCoerced.minor > latestVersionCoerced.minor) || (givenVersionCoerced.major === latestVersionCoerced.major && givenVersionCoerced.minor === latestVersionCoerced.minor && givenVersionCoerced.patch > latestVersionCoerced.patch)) { return latestVersion; } const closestMatch = getClosestMatchingBrowserVersion(browser, givenVersion, versions); // Allow smaller, but not larger browser versions than the known ones if (allowSmaller && semver.lt(givenVersionCoerced, ensureSemver(browser, closestMatch), { loose: true })) { return givenVersion; } return closestMatch; } /** * Gets the known version of the given browser that is closest to the given version */ function getClosestMatchingBrowserVersion(browser, version, versions = getSortedBrowserVersions(browser)) { const coerced = ensureSemver(browser, version); if (browser === "op_mini" && version === "all") return "all"; if (browser === "safari") { if (version === "TP") return "TP"; // If the given version is greater than or equal to the latest non-technical preview version of Safari, the closest match IS TP. else if (semver.gt(ensureSemver(browser, `${coerced.major}.${coerced.minor}`), ensureSemver(browser, versions.slice(-2)[0]))) return "TP"; } let candidate = versions[0]; versions.forEach(currentVersion => { const currentCoerced = ensureSemver(browser, currentVersion); if (semver.gte(coerced, currentCoerced)) { candidate = currentVersion; } }); return candidate; } function getSortedBrowserVersionsWithLeadingVersion(browser, inputVersion) { const versions = getSortedBrowserVersions(browser); const [firstVersion] = versions; if (firstVersion != null && inputVersion != null) { const firstVersionSemver = ensureSemver(browser, firstVersion); let nextInputVersion = inputVersion; while (true) { const nextInputSemver = ensureSemver(browser, nextInputVersion); if (semver.gt(firstVersionSemver, nextInputSemver)) { versions.unshift(nextInputVersion); nextInputVersion = String(nextInputSemver.major + 1); } else { break; } } } return versions; } /** * Gets all versions of the given browser, sorted */ function getSortedBrowserVersions(browser) { // Generate the Browserslist query const queryResult = Browserslist([`>= 0%`, `unreleased versions`]); const versions = []; // First, organize the different versions of the browsers inside the Map queryResult.forEach(part => { const [currentBrowser, version] = part.split(" "); if (currentBrowser !== browser) return; const versionMatch = version.match(NORMALIZE_BROWSER_VERSION_REGEXP); const normalizedVersion = versionMatch == null ? version : versionMatch[1]; versions.push(normalizedVersion); }); return versions.sort(compareVersions); } /** * Gets the latest version of the given browser */ function getLatestVersionOfBrowser(browser) { const versions = getSortedBrowserVersions(browser); return versions[versions.length - 1]; } /** * Gets the oldest (stable) version of the given browser */ function getOldestVersionOfBrowser(browser) { const versions = getSortedBrowserVersions(browser); return versions[0]; } /** * Gets the previous version of the given browser from the given version */ function getPreviousVersionOfBrowser(browser, version) { const versions = getSortedBrowserVersions(browser); const indexOfVersion = versions.indexOf(normalizeBrowserVersion(browser, version, versions)); // If the version isn't included, or if it is the first version of it, return undefined if (indexOfVersion <= 0) return undefined; return versions[indexOfVersion - 1]; } /** * Gets the previous version of the given browser from the given version */ function getNextVersionOfBrowser(browser, version) { const versions = getSortedBrowserVersions(browser); const indexOfVersion = versions.indexOf(normalizeBrowserVersion(browser, version, versions)); // If the version isn't included, or if it is the first version of it, return undefined if (indexOfVersion <= 0) return undefined; return versions[indexOfVersion + 1]; } /** * Caniuse has only a limited set of supported browsers. * There are cases where there is simply no way to guess * a browser based on a User Agent, and in these cases * this can be used as a fallback. * Chrome is the world's most used browser, and as an evergreen * browser, it is commonly the newest version. But to be safe * This fallback browser is placed a bit in the past */ const UNKNOWN_CANIUSE_BROWSER = { browser: "chrome", version: "80" }; const ES5_FEATURES = [ "javascript.builtins.Object.create", "javascript.builtins.Object.getPrototypeOf", "javascript.builtins.Object.defineProperty", "javascript.builtins.Object.defineProperties", "javascript.builtins.Object.getOwnPropertyDescriptor", "javascript.builtins.Object.getOwnPropertyNames", "javascript.builtins.Object.keys", "javascript.builtins.Object.preventExtensions", "javascript.builtins.Object.isExtensible", "javascript.builtins.Object.seal", "javascript.builtins.Object.isSealed", "javascript.builtins.Object.freeze", "javascript.builtins.Object.isFrozen", "javascript.builtins.Function.bind", "javascript.builtins.String.trim", "javascript.builtins.Array.isArray", "javascript.builtins.Array.every", "javascript.builtins.Array.filter", "javascript.builtins.Array.forEach", "javascript.builtins.Array.indexOf", "javascript.builtins.Array.lastIndexOf", "javascript.builtins.Array.map", "javascript.builtins.Array.reduce", "javascript.builtins.Array.some", "javascript.builtins.JSON.parse", "javascript.builtins.JSON.stringify", "javascript.builtins.Date.now", "javascript.builtins.Date.toISOString" ]; const ES2015_FEATURES = [ ...ES5_FEATURES, "javascript.classes", "javascript.statements.const", "javascript.statements.let", "javascript.functions.arrow_functions", "javascript.functions.rest_parameters", "javascript.grammar.template_literals", "javascript.operators.destructuring", "javascript.operators.spread.spread_in_arrays", "javascript.functions.default_parameters", "javascript.builtins.RegExp.sticky", "javascript.operators.object_initializer.shorthand_property_names", "javascript.operators.object_initializer.computed_property_names", "javascript.operators.object_initializer.shorthand_method_names" ]; const ES2016_FEATURES = [...ES2015_FEATURES, "javascript.operators.exponentiation", "javascript.builtins.Array.includes"]; const ES2017_FEATURES = [ ...ES2016_FEATURES, "javascript.builtins.AsyncFunction", "javascript.builtins.Object.values", "javascript.builtins.Object.entries", "javascript.builtins.Object.getOwnPropertyDescriptors", "javascript.builtins.String.padStart", "javascript.builtins.String.padEnd" ]; const ES2018_FEATURES = [...ES2017_FEATURES, "javascript.operators.spread.spread_in_object_literals", "javascript.builtins.Promise.finally"]; const ES2019_FEATURES = [ ...ES2018_FEATURES, "javascript.builtins.Array.flat", "javascript.builtins.Array.flatMap", "javascript.builtins.Object.fromEntries", "javascript.builtins.String.trimStart", "javascript.builtins.String.trimEnd", "javascript.builtins.JSON.json_superset", "javascript.builtins.JSON.stringify.well_formed_stringify", "javascript.builtins.Symbol.description", "javascript.statements.try_catch.optional_catch_binding" ]; const ES2020_FEATURES = [...ES2019_FEATURES, "javascript.builtins.String.matchAll"]; const ES2021_FEATURES = [ ...ES2020_FEATURES, "javascript.operators.logical_or_assignment", "javascript.operators.nullish_coalescing_assignment", "javascript.operators.logical_and_assignment", "javascript.builtins.String.replaceAll", "javascript.grammar.numeric_separators", "javascript.builtins.Promise.any" ]; const ES2022_FEATURES = [ ...ES2021_FEATURES, "javascript.builtins.Array.at", "javascript.builtins.String.matchAll", "javascript.classes.public_class_fields", "javascript.classes.private_class_fields", "javascript.classes.private_class_fields_in", "javascript.classes.static_class_fields", "javascript.operators.await.top_level", "javascript.builtins.RegExp.hasIndices" ]; const ES2023_FEATURES = [ ...ES2022_FEATURES, "javascript.builtins.Array.findLast", "javascript.builtins.Array.findLastIndex", "javascript.grammar.hashbang_comments", "javascript.builtins.WeakMap.symbol_as_keys", "javascript.builtins.Array.toReversed", "javascript.builtins.Array.toSorted", "javascript.builtins.Array.toSpliced", "javascript.builtins.Array.with" ]; /** * Applies the given correction within the given version range */ function rangeCorrection(browser, supportKind, start, end) { const versions = getSortedBrowserVersions(browser); const corrections = []; versions.forEach(version => { let shouldSet = false; if (start == null && end == null) { shouldSet = true; } else if (start != null && end == null) { if (version === "TP") { shouldSet = true; } else if (version === "all") { shouldSet = true; } else { shouldSet = semver.gte(coerceToString(browser, version), coerceToString(browser, start)); } } else if (start == null && end != null) { if (version === "TP") { shouldSet = end === "TP"; } else if (version === "all") { shouldSet = true; } else { shouldSet = semver.lte(coerceToString(browser, version), coerceToString(browser, end)); } } else if (start != null && end != null) { if (version === "TP") { shouldSet = end === "TP"; } else if (version === "all") { shouldSet = true; } else { shouldSet = semver.gte(coerceToString(browser, version), coerceToString(browser, start)) && semver.lte(coerceToString(browser, version), coerceToString(browser, end)); } } if (shouldSet) { corrections.push({ kind: supportKind, version }); } }); return corrections; } var CaniuseSupportKind; (function (CaniuseSupportKind) { CaniuseSupportKind["AVAILABLE"] = "AVAILABLE"; CaniuseSupportKind["UNAVAILABLE"] = "UNAVAILABLE"; CaniuseSupportKind["PARTIAL_SUPPORT"] = "PARTIAL_SUPPORT"; CaniuseSupportKind["PREFIXED"] = "PREFIXED"; })(CaniuseSupportKind || (CaniuseSupportKind = {})); const FIREFOX_MATCH = /Firefox\/([\d.]+)/i; const IOS_REGEX_1 = /(iPhone)|(iPad)/i; const IOS_REGEX_2 = /(iOS)\s*([\d._]+)/i; const UNDERSCORED_VERSION_REGEX = /\d+_/; const FBSV_IOS_VERSION_REGEX = /FBSV\/([\d.]+)/i; const IOS_14_5_UA_1 = /(CFNetwork\/1237\s+Darwin\/20.4)/i; const IOS_3_2_UA_1 = /(^Mobile\/7B334b)/i; // Extend 'isbot' with more matches isbot.extend(["bitdiscovery", "Dalvik/", "placid.app/v1", "WebsiteMetadataRetriever", "(compatible; aa/1.0)"]); // These extension provide ua-parser-js with support for additional browsers // such as Sogou Explorer const PARSER_EXTENSIONS = { engine: [[/(Chrome)\/([\d.]+)/i], ["blink", "version"]], browser: [ [/(MetaSr)\s*([\d.]+)/i], ["Sogou Explorer", "version"], [/(HeyTapBrowser)\/([\d.]+)/i], ["HeyTapBrowser", "version"], [/(SamsungBrowser)\/CrossApp/i], ["Samsung Browser"], [/(Nokia\d+\/[\d.]+.*Profile\/MIDP)/i], ["WAP"] ] }; /** * A class that wraps UAParser */ class UaParserWrapper { constructor(userAgent) { this.userAgent = userAgent; this.parser = new uaParserJs.UAParser(userAgent, PARSER_EXTENSIONS); } /** * Gets the IUserAgentBrowser based on the UAParser */ getBrowser() { return this.extendGetBrowserResult(this.parser.getBrowser()); } /** * Gets the IUserAgentOS based on the UAParser */ getOS() { return this.extendGetOsResult(this.parser.getOS()); } /** * Gets the IUserAgentDevice based on the UAParser */ getDevice() { return this.parser.getDevice(); } /** * Gets the IEngine based on the UAParser */ getEngine() { return this.extendGetEngineResult(this.parser.getEngine()); } /** * Extends the result of calling 'getBrowser' on the UAParser and always takes bots into account */ extendGetBrowserResult(result) { var _a, _b; // Ensure that the EdgeHTML version is used if (result.name === "Edge") { const engine = this.parser.getEngine(); if (engine.name === "EdgeHTML") { result.version = engine.version; // noinspection JSDeprecatedSymbols result.major = String((_b = (_a = semver.coerce(engine.version)) === null || _a === void 0 ? void 0 : _a.major) !== null && _b !== void 0 ? _b : result.version); } } // Check if it is a bot and match it if so // Also treat Dalvik/ as a bot if (result.name !== "Chrome Headless" && isbot(this.userAgent)) { if (this.userAgent.includes("http://www.google.com/bot.htm") || this.userAgent.includes("http://www.google.com/adsbot.htm")) { // As far as we know, the last reported update to Googlebot was the intent // to keep it evergreen, but so far it seems 74 is the latest official version result.name = "Chrome"; result.version = "74"; // noinspection JSDeprecatedSymbols result.major = "74"; } // Treat all other bots as IE 11 else { result.name = "IE"; result.version = "11"; // noinspection JSDeprecatedSymbols result.major = "11"; } } if (result["Sogou Explorer"] != null) { result.name = "Sogou Explorer"; delete result["Sogou Explorer"]; } else if (result.HeyTapBrowser != null) { result.name = "HeyTapBrowser"; delete result.HeyTapBrowser; } else if (result["Samsung Browser"] != null) { result.name = "Samsung Browser"; delete result["Samsung Browser"]; } else if (result.WAP != null) { result.name = "IE"; result.version = "8"; delete result.WAP; } return result; } /** * Extends the result of calling 'getEngine' */ extendGetEngineResult(result) { if (result.blink != null) { result.name = "Blink"; delete result.blink; } // The User Agent may hold additional information, such as the equivalent Firefox version if (result.name === "Goanna") { const ffMatch = this.userAgent.match(FIREFOX_MATCH); if (ffMatch != null) { result.name = "Gecko"; result.version = ffMatch[1]; } } return result; } /** * Extends the result of calling 'getOS' */ extendGetOsResult(result) { if (result.version != null && UNDERSCORED_VERSION_REGEX.test(result.version)) { result.version = result.version.replace(/_/g, "."); } if ((result.name == null || result.name === "iOS") && (IOS_REGEX_1.test(this.userAgent) || IOS_REGEX_2.test(this.userAgent))) { result.name = "iOS"; if (result.version == null) { // If it is the Facebook browser, the iOS version may be reported // through its FBSV/{version} part const fbsvMatch = this.userAgent.match(FBSV_IOS_VERSION_REGEX); if (fbsvMatch != null) { result.version = fbsvMatch[1].replace(/_/g, "."); } else { const iosRegex2Match = this.userAgent.match(IOS_REGEX_2); if (iosRegex2Match != null) { result.version = iosRegex2Match[2].replace(/_/g, "."); } } } } if ((result.name == null || result.name === "iOS") && IOS_14_5_UA_1.test(this.userAgent)) { result.name = "iOS"; result.version = "14.5"; } if ((result.name == null || result.name === "iOS") && IOS_3_2_UA_1.test(this.userAgent)) { result.name = "iOS"; result.version = "3.2"; } return result; } } const require$1 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))); const compatData = require$1('@mdn/browser-compat-data'); /** * A Cache between user agent names and generated Browserslists */ const userAgentToBrowserslistCache = new Map(); /** * A Cache for retrieving browser support for some features */ const browserSupportForFeaturesCache = new Map(); /** * A Cache between feature names and their CaniuseStats */ const featureToCaniuseStatsCache = new Map(); /** * A Cache between user agents with any amount of features and whether or not they are supported by the user agent */ const userAgentWithFeaturesToSupportCache = new Map(); /** * By and large, MDN has the best compat data, especially when looking into at which point older version of Android-based browsers * received support for a feature. Therefore we generally prioritize MDN compat data and will attempt to rewrite common caniuse queries to * their respective MDN feature names */ const CANIUSE_TO_MDN_FEATURE_MAP = { pointer: "api.PointerEvent.PointerEvent", shadowdomv1: "api.ShadowRoot", "custom-elementsv1": "api.CustomElementRegistry", template: "html.elements.template", fetch: "api.fetch", promises: "javascript.builtins.Promise", "object-values": "javascript.builtins.Object.values", mutationobserver: "api.MutationObserver", "focusin-focusout-events": "api.Element.focusin_event", "high-resolution-time": "api.Performance.now", url: "api.URL", urlsearchparams: "api.URLSearchParams", "object-fit": "css.properties.object-fit", "console-basic": "api.console.info", "console-time": "api.console.time", "atob-btoa": "api.atob", blobbuilder: "api.Blob.Blob", bloburls: "api.URL.createObjectURL", requestidlecallback: "api.Window.requestIdleCallback", requestanimationframe: "api.Window.requestAnimationFrame", proxy: "javascript.builtins.Proxy" }; /** * A Map between features and browsers that has partial support for them but should be allowed anyway * @type {Map<string, string[]>} */ const PARTIAL_SUPPORT_ALLOWANCES = new Map([ ["shadowdomv1", "*"], ["custom-elementsv1", "*"], ["web-animation", "*"] ]); const TYPED_ARRAY_BASE_DATA_CORRECTIONS_INPUT = { /* eslint-disable @typescript-eslint/naming-convention */ android: rangeCorrection("android", CaniuseSupportKind.AVAILABLE, `4`), chrome: rangeCorrection("and_chr", CaniuseSupportKind.AVAILABLE, `7`), and_chr: rangeCorrection("and_chr", CaniuseSupportKind.AVAILABLE, `7`), edge: rangeCorrection("edge", CaniuseSupportKind.AVAILABLE, "12"), samsung: rangeCorrection("samsung", CaniuseSupportKind.AVAILABLE, `4`), opera: rangeCorrection("opera", CaniuseSupportKind.AVAILABLE, `12`), op_mob: rangeCorrection("op_mob", CaniuseSupportKind.AVAILABLE, `12`), firefox: rangeCorrection("firefox", CaniuseSupportKind.AVAILABLE, `4`), and_ff: rangeCorrection("and_ff", CaniuseSupportKind.AVAILABLE, `4`), safari: rangeCorrection("safari", CaniuseSupportKind.AVAILABLE, `6`), ios_saf: rangeCorrection("safari", CaniuseSupportKind.AVAILABLE, `5`), ie: rangeCorrection("ie", CaniuseSupportKind.AVAILABLE, `11`), op_mini: rangeCorrection("op_mini", CaniuseSupportKind.AVAILABLE, `all`), bb: rangeCorrection("bb", CaniuseSupportKind.AVAILABLE, `10`), and_uc: rangeCorrection("and_uc", CaniuseSupportKind.AVAILABLE, `11.8`), and_qq: rangeCorrection("and_qq", CaniuseSupportKind.AVAILABLE, `1.2`), baidu: rangeCorrection("baidu", CaniuseSupportKind.AVAILABLE, `7.12`) /* eslint-enable @typescript-eslint/naming-convention */ }; const TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT = { /* eslint-disable @typescript-eslint/naming-convention */ android: rangeCorrection("android", CaniuseSupportKind.AVAILABLE, `45`), chrome: rangeCorrection("and_chr", CaniuseSupportKind.AVAILABLE, `45`), and_chr: rangeCorrection("and_chr", CaniuseSupportKind.AVAILABLE, `45`), edge: rangeCorrection("edge", CaniuseSupportKind.AVAILABLE, "12"), samsung: rangeCorrection("samsung", CaniuseSupportKind.AVAILABLE, `5`), opera: rangeCorrection("opera", CaniuseSupportKind.AVAILABLE, `32`), op_mob: rangeCorrection("op_mob", CaniuseSupportKind.AVAILABLE, `32`), firefox: rangeCorrection("firefox", CaniuseSupportKind.AVAILABLE, `38`), and_ff: rangeCorrection("and_ff", CaniuseSupportKind.AVAILABLE, `38`), safari: rangeCorrection("safari", CaniuseSupportKind.AVAILABLE, `10`), ios_saf: rangeCorrection("safari", CaniuseSupportKind.AVAILABLE, `10`), ie: rangeCorrection("ie", CaniuseSupportKind.AVAILABLE, `11`), ie_mob: rangeCorrection("ie", CaniuseSupportKind.AVAILABLE, `11`) /* eslint-enable @typescript-eslint/naming-convention */ }; const TYPED_ARRAY_ES2016_DATA_CORRECTIONS_INPUT = { /* eslint-disable @typescript-eslint/naming-convention */ android: rangeCorrection("android", CaniuseSupportKind.AVAILABLE, `47`), chrome: rangeCorrection("and_chr", CaniuseSupportKind.AVAILABLE, `47`), and_chr: rangeCorrection("and_chr", CaniuseSupportKind.AVAILABLE, `47`), edge: rangeCorrection("edge", CaniuseSupportKind.AVAILABLE, "14"), samsung: rangeCorrection("samsung", CaniuseSupportKind.AVAILABLE, `5`), opera: rangeCorrection("opera", CaniuseSupportKind.AVAILABLE, `34`), op_mob: rangeCorrection("op_mob", CaniuseSupportKind.AVAILABLE, `34`), firefox: rangeCorrection("firefox", CaniuseSupportKind.AVAILABLE, `43`), and_ff: rangeCorrection("and_ff", CaniuseSupportKind.AVAILABLE, `43`), safari: rangeCorrection("safari", CaniuseSupportKind.AVAILABLE, `10`), ios_saf: rangeCorrection("safari", CaniuseSupportKind.AVAILABLE, `10`) /* eslint-enable @typescript-eslint/naming-convention */ }; const TYPED_ARRAY_KEYS_VALUES_ENTRIES_ITERATOR_DATA_CORRECTIONS_INPUT = { /* eslint-disable @typescript-eslint/naming-convention */ android: rangeCorrection("android", CaniuseSupportKind.AVAILABLE, `38`), chrome: rangeCorrection("and_chr", CaniuseSupportKind.AVAILABLE, `38`), and_chr: rangeCorrection("and_chr", CaniuseSupportKind.AVAILABLE, `38`), edge: rangeCorrection("edge", CaniuseSupportKind.AVAILABLE, "12"), samsung: rangeCorrection("samsung", CaniuseSupportKind.AVAILABLE, `5`), opera: rangeCorrection("opera", CaniuseSupportKind.AVAILABLE, `26`), op_mob: rangeCorrection("op_mob", CaniuseSupportKind.AVAILABLE, `26`), firefox: rangeCorrection("firefox", CaniuseSupportKind.AVAILABLE, `37`), and_ff: rangeCorrection("and_ff", CaniuseSupportKind.AVAILABLE, `37`), safari: rangeCorrection("safari", CaniuseSupportKind.AVAILABLE, `10`), ios_saf: rangeCorrection("safari", CaniuseSupportKind.AVAILABLE, `10`) /* eslint-enable @typescript-eslint/naming-convention */ }; const TYPED_ARRAY_SPECIES_DATA_CORRECTIONS_INPUT = { /* eslint-disable @typescript-eslint/naming-convention */ android: rangeCorrection("android", CaniuseSupportKind.AVAILABLE, `51`), chrome: rangeCorrection("and_chr", CaniuseSupportKind.AVAILABLE, `51`), and_chr: rangeCorrection("and_chr", CaniuseSupportKind.AVAILABLE, `51`), edge: rangeCorrection("edge", CaniuseSupportKind.AVAILABLE, "13"), samsung: rangeCorrection("samsung", CaniuseSupportKind.AVAILABLE, `5`), opera: rangeCorrection("opera", CaniuseSupportKind.AVAILABLE, `38`), op_mob: rangeCorrection("op_mob", CaniuseSupportKind.AVAILABLE, `38`), firefox: rangeCorrection("firefox", CaniuseSupportKind.AVAILABLE, `48`), and_ff: rangeCorrection("and_ff", CaniuseSupportKind.AVAILABLE, `48`), safari: rangeCorrection("safari", CaniuseSupportKind.AVAILABLE, `10`), ios_saf: rangeCorrection("safari", CaniuseSupportKind.AVAILABLE, `10`) /* eslint-enable @typescript-eslint/naming-convention */ }; /** * Not all Caniuse data is entirely correct. For some features, the data on https://kangax.github.io/compat-table/es6/ * is more correct. When a Browserslist is generated based on support for specific features, it is really important * that it is correct, especially if the browserslist will be used as an input to tools such as @babel/preset-env. * This table provides some corrections to the Caniuse data that makes it align better with actual availability * @type {[string, CaniuseBrowserCorrection][]} */ const FEATURE_TO_BROWSER_DATA_CORRECTIONS_INPUT = [ /* eslint-disable @typescript-eslint/naming-convention */ [ "xhr2", { ie: [ { // Caniuse reports that XMLHttpRequest support is partial in Internet Explorer 11, but it is in fact properly supported kind: CaniuseSupportKind.AVAILABLE, version: "11" } ] } ], [ // Caniuse reports that Safari 12.1 and iOS Safari 12.2 has partial support for Web Animations, // but they do not - They require enabling it as an experimental feature "web-animation", { safari: rangeCorrection("safari", CaniuseSupportKind.UNAVAILABLE, `0`, "13.4"), ios_saf: rangeCorrection("ios_saf", CaniuseSupportKind.UNAVAILABLE, `0`, "13.4") } ], [ "es6-class", { edge: [ { // Caniuse reports that Microsoft Edge has been supporting classes since v12, but it was prefixed until v13 kind: CaniuseSupportKind.PREFIXED, version: "12" } ], ios_saf: [ { // Caniuse reports that iOS Safari has been supporting classes since v9, but the implementation was only partial kind: CaniuseSupportKind.PARTIAL_SUPPORT, version: "9" }, { // Caniuse reports that iOS Safari has been supporting classes since v9, but the implementation was only partial kind: CaniuseSupportKind.PARTIAL_SUPPORT, version: "9.2" }, { // Caniuse reports that iOS Safari has been supporting classes since v9, but the implementation was only partial kind: CaniuseSupportKind.PARTIAL_SUPPORT, version: "9.3" } ], safari: [ { // Caniuse reports that Safari has been supporting classes since v9, but the implementation was only partial kind: CaniuseSupportKind.PARTIAL_SUPPORT, version: "9" }, { // Caniuse reports that Safari has been supporting classes since v9, but the implementation was only partial kind: CaniuseSupportKind.PARTIAL_SUPPORT, version: "9.1" } ] } ], [ "api.Element.classList", { edge: [ { // Caniuse reports that Microsoft Edge v15 has only partial support for class-list since it doesn't support SVG elements, // but we don't want feature detections to return false for that browser kind: CaniuseSupportKind.AVAILABLE, version: "15" } ], ie: [ { // Caniuse reports that IE 10 has only partial support for class-list since it doesn't support SVG elements, // but we don't want feature detections to return false for that browser kind: CaniuseSupportKind.AVAILABLE, version: "10" }, { // Caniuse reports that IE 11 has only partial support for class-list since it doesn't support SVG elements, // but we don't want feature detections to return false for that browser kind: CaniuseSupportKind.AVAILABLE, version: "11" } ] } ], ["javascript.builtins.TypedArray.from", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.of", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.subarray", TYPED_ARRAY_BASE_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.copyWithin", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.every", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.fill", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.filter", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.find", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.findIndex", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.forEach", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.indexOf", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.join", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.lastIndexOf", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.map", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.reduce", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.reduceRight", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.reverse", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.some", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.sort", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.toLocaleString", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.toString", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.slice", TYPED_ARRAY_ES2015_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.includes", TYPED_ARRAY_ES2016_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.keys", TYPED_ARRAY_KEYS_VALUES_ENTRIES_ITERATOR_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.values", TYPED_ARRAY_KEYS_VALUES_ENTRIES_ITERATOR_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.entries", TYPED_ARRAY_KEYS_VALUES_ENTRIES_ITERATOR_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.@@iterator", TYPED_ARRAY_KEYS_VALUES_ENTRIES_ITERATOR_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray.@@species", TYPED_ARRAY_SPECIES_DATA_CORRECTIONS_INPUT], ["javascript.builtins.TypedArray", TYPED_ARRAY_BASE_DATA_CORRECTIONS_INPUT], ["javascript.builtins.Int8Array", TYPED_ARRAY_BASE_DATA_CORRECTIONS_INPUT], ["javascript.builtins.Int16Array", TYPED_ARRAY_BASE_DATA_CORRECTIONS_INPUT], ["javascript.builtins.Int32Array", TYPED_ARRAY_BASE_DATA_CORRECTIONS_INPUT], ["javascript.builtins.Float32Array", TYPED_ARRAY_BASE_DATA_CORRECTIONS_INPUT], ["javascript.builtins.Float64Array", TYPED_ARRAY_BASE_DATA_CORRECTIONS_INPUT], ["javascript.builtins.Uint8Array", TYPED_ARRAY_BASE_DATA_CORRECTIONS_INPUT], ["javascript.builtins.Uint8ClampedArray", TYPED_ARRAY_BASE_DATA_CORRECTIONS_INPUT], ["javascript.builtins.Uint16ClampedArray", TYPED_ARRAY_BASE_DATA_CORRECTIONS_INPUT], ["javascript.builtins.Uint32ClampedArray", TYPED_ARRAY_BASE_DATA_CORRECTIONS_INPUT], [ "javascript.builtins.String.@@iterator", { android: rangeCorrection("chrome", CaniuseSupportKind.AVAILABLE, `38`), chrome: rangeCorrection("chrome", CaniuseSupportKind.AVAILABLE, `38`), and_chr: rangeCorrection("and_chr", CaniuseSupportKind.AVAILABLE, `38`), edge: rangeCorrection("edge", CaniuseSupportKind.AVAILABLE, `12`), opera: rangeCorrection("opera", CaniuseSupportKind.AVAILABLE, `25`), op_mob: rangeCorrection("op_mob", CaniuseSupportKind.AVAILABLE, `25`), firefox: rangeCorrection("firefox", CaniuseSupportKind.AVAILABLE, `36`), and_ff: rangeCorrection("and_ff", CaniuseSupportKind.AVAILABLE, `36`), safari: rangeCorrection("safari", CaniuseSupportKind.AVAILABLE, `9`), ios_saf: rangeCorrection("ios_saf", CaniuseSupportKind.AVAILABLE, `9`), samsung: rangeCorrection("samsung", CaniuseSupportKind.AVAILABLE, `3`) } ], [ "javascript.builtins.Symbol.asyncIterator", { android: rangeCorrection("android", CaniuseSupportKind.AVAILABLE, `63`), chrome: rangeCorrection("chrome", CaniuseSupportKind.AVAILABLE, `63`), and_chr: rangeCorrection("and_chr", CaniuseSupportKind.AVAILABLE, `63`), opera: rangeCorrection("opera", CaniuseSupportKind.AVAILABLE, `50`), op_mob: rangeCorrection("op_mob", CaniuseSupportKind.AVAILABLE, `50`), firefox: rangeCorrection("firefox", CaniuseSupportKind.AVAILABLE, `57`), and_ff: rangeCorrection("and_ff", CaniuseSupportKind.AVAILABLE, `57`), safari: rangeCorrection("safari", CaniuseSupportKind.AVAILABLE, `11.1`), ios_saf: rangeCorrection("ios_saf", CaniuseSupportKind.AVAILABLE, `11.1`) } ], [ "javascript.builtins.Array.@@species", { android: rangeCorrection("android", CaniuseSupportKind.AVAILABLE, `51`), // MDN reports that it doesn't support Array.@@species, but it does and has done since Chrome v51 chrome: rangeCorrection("chrome", CaniuseSupportKind.AVAILABLE, `51`), // MDN reports that it doesn't support Array.@@species, but it does and has done since Chrome for Android v51 and_chr: rangeCorrection("and_chr", CaniuseSupportKind.AVAILABLE, `51`), // MDN reports that it doesn't support Array.@@species, but it does and has done since Edge v14 edge: rangeCorrection("edge", CaniuseSupportKind.AVAILABLE, `14`), // MDN reports that it doesn't support Array.@@species, but it does and has done since Firefox v41 firefox: rangeCorrection("firefox", CaniuseSupportKind.AVAILABLE, `41`), // MDN reports that it doesn't support Array.@@species, but it does and has done since Firefox for Android v41 and_ff: rangeCorrection("and_ff", CaniuseSupportKind.AVAILABLE, `41`), // MDN reports that it doesn't support Array.@@species, but it does and has done since Opera v38 opera: rangeCorrection("opera", CaniuseSupportKind.AVAILABLE, `38`), // MDN reports that it doesn't support Array.@@species, but it does and has done since Opera for Android v38 op_mob: rangeCorrection("op_mob", CaniuseSupportKind.AVAILABLE, `38`), // MDN reports that it doesn't support Array.@@species, but it does and has done since Safari v10 safari: rangeCorrection("safari", CaniuseSupportKind.AVAILABLE, `10`), // MDN reports that it doesn't support Array.@@species, but it does and has done since Safari for iOS v10 ios_saf: rangeCorrection("ios_saf", CaniuseSupportKind.AVAILABLE, `10`) } ], [ "javascript.builtins.Date.@@toPrimitive", { android: rangeCorrection("android", CaniuseSupportKind.AVAILABLE, `48`), // MDN reports that it doesn't support Date.@@toPrimitive, but it does and has done since Chrome v48 chrome: rangeCorrection("chrome", CaniuseSupportKind.AVAILABLE, `48`), // MDN reports that it doesn't support Date.@@toPrimitive, but it does and has done since Chrome for Android v48 and_chr: rangeCorrection("and_chr", CaniuseSupportKind.AVAILABLE, `48`), // MDN reports that it doesn't support Date.@@toPrimitive, but it does and has done in all Edge versions edge: rangeCorrection("edge", CaniuseSupportKind.AVAILABLE), // MDN reports that it doesn't support Date.@@toPrimitive, but it does and has done since Firefox v44 firefox: rangeCorrection("firefox", CaniuseSupportKind.AVAILABLE, `44`), // MDN reports that it doesn't support Date.@@toPrimitive, but it does and has done since Firefox for Android v44 and_ff: rangeCorrection("and_ff", CaniuseSupportKind.AVAILABLE, `44`), // MDN reports that it doesn't support Date.@@toPrimitive, but it does and has done since Opera v35 opera: rangeCorrection("opera", CaniuseSupportKind.AVAILABLE, `35`), // MDN reports that it doesn't support Date.@@toPrimitive, but it does and has done since Opera for Android v35 op_mob: rangeCorrection("op_mob", CaniuseSupportKind.AVAILABLE, `35`), // MDN reports that it doesn't support Date.@@toPrimitive, but it does and has done since Safari v10 safari: rangeCorrection("safari", CaniuseSupportKind.AVAILABLE, `10`), // MDN reports that it doesn't support Date.@@toPrimitive, but it does and has done since Safari for iOS v10 ios_saf: rangeCorrection("ios_saf", CaniuseSupportKind.AVAILABLE, `10`), // MDN reports that it doesn't support the Date.@@toPrimitive method, but it does and has done for all Samsung Internet versions samsung: rangeCorrection("samsung", CaniuseSupportKind.AVAILABLE) } ], [ "fetch", { edge: [ { // Caniuse reports that Microsoft Edge has been supporting fetch since v14, but the implementation was quite unstable until v15 kind: CaniuseSupportKind.UNAVAILABLE, version: "14" } ] } ], [ "api.Window", { chrome: rangeCorrection("chrome", CaniuseSupportKind.UNAVAILABLE, `0`, `18`), safari: rangeCorrection("safari", CaniuseSupportKind.UNAVAILABLE, `0`, `5.1`), ie: rangeCorrection("ie", CaniuseSupportKind.UNAVAILABLE, `0`, `7`), opera: rangeCorrection("safari", CaniuseSupportKind.UNAVAILABLE, `0`, `11.1`) } ], [ "javascript.builtins.String.matchAll", { samsung: rangeCorrection("samsung", CaniuseSupportKind.UNAVAILABLE, `0`, `9.4`) } ], [ "resizeobserver", { safari: rangeCorrection("safari", CaniuseSupportKind.UNAVAILABLE, `0`) } ] /* eslint-enable @typescript-eslint/naming-convention */ ]; /** * A Map between caniuse features and corrections to apply (see above) * @type {Map<string, CaniuseBrowserCorrection>} */ const FEATURE_TO_BROWSER_DATA_CORRECTIONS_MAP = new Map(FEATURE_TO_BROWSER_DATA_CORRECTIONS_INPUT); /** * Returns the input query, but extended with the given options */ function extendQueryWith(query, extendWith) { const normalizedExtendWith = Array.isArray(extendWith) ? extendWith : [extendWith]; return [...new Set([...query, ...normalizedExtendWith])]; } /** * Normalizes the given Browserslist */ function normalizeBrowserslist(browserslist) { const result = Browserslist(browserslist); // Caniuse only tracks the latest Browser version for Android-based browsers, // so we'll need to add the relevant details back in after normalizing the Browserslist // to make sure comparsions won't fail const inputBrowserslist = Array.isArray(browserslist) ? browserslist : [browserslist]; for (const browser of ["and_ff", "and_chr", "and_uc", "and_qq", "baidu", "op_mini"]) { const versions = getSortedBrowserVersions(browser); for (const entry of inputBrowserslist) { if (!entry.startsWith(browser)) continue; const directMatch = entry.match(new RegExp(`${browser} (\\d+.*)`)); if (directMatch != null) { const candidate = `${browser} ${directMatch[1]}`; if (!result.includes(candidate)) { result.push(candidate); } } else { const greaterThanOrEqualsMatch = entry.match(new RegExp(`${browser} >= (\\d+)`)); if (greaterThanOrEqualsMatch != null) { let currentMajor = Number(greaterThanOrEqualsMatch[1]); while (true) { const candidate = `${browser} ${currentMajor}`; if (!result.includes(candidate)) { result.push(candidate); currentMajor++; if (Number(getClosestMatchingBrowserVersion(browser, String(currentMajor), versions)) <= currentMajor) break; } else { break; } } } } } } return result.sort(); } /** * Returns the input query, but extended with 'unreleased versions' */ function extendQueryWithUnreleasedVersions(query, browsers) { return extendQueryWith(query, Array.from(browsers).map(browser => `unreleased ${browser} versions`)); } /** * Generates a Browserslist based on browser support for the given features */ function browsersWithSupportForFeatures(...features) { const { query, browsers } = browserSupportForFeaturesCommon(">=", ...features); return extendQueryWithUnreleasedVersions(query, browsers); } /** * Returns true if the given Browserslist supports the given EcmaVersion */ function browserslistSupportsEcmaVersion(browserslist, version) { switch (version) { case "es3": // ES3 is the lowest possible target and will always be treated as supported return true; case "es5": return browserslistSupportsFeatures(browserslist, ...ES5_FEATURES); case "es2015": return browserslistSupportsFeatures(browserslist, ...ES2015_FEATURES); case "es2016": return browserslistSupportsFeatures(browserslist, ...ES2016_FEATURES); case "es2017": return browserslistSupportsFeatures(browserslist, ...ES2017_FEATURES); case "es2018": return browserslistSupportsFeatures(browserslist, ...ES2018_FEATURES); case "es2019": return browserslistSupportsFeatures(browserslist, ...ES2019_FEATURES); case "es2020": return browserslistSupportsFeatures(browserslist, ...ES2020_FEATURES); case "es2021": return browserslistSupportsFeatures(browserslist, ...ES2021_FEATURES); case "es2022": return browserslistSupportsFeatures(browserslist, ...ES2022_FEATURES); case "es2023": return browserslistSupportsFeatures(browserslist, ...ES2023_FEATURES); } } /** * Returns the appropriate Ecma version for the given Browserslist */ function getAppropriateEcmaVersionForBrowserslist(browserslist) { if (browserslistSupportsEcmaVersion(browserslist, "es2023")) return "es2023"; if (browserslistSupportsEcmaVersion(browserslist, "es2022")) return "es2022"; if (browserslistSupportsEcmaVersion(browserslist, "es2021")) return "es2021"; if (browserslistSupportsEcmaVersion(browserslist, "es2020")) return "es2020"; if (browserslistSupportsEcmaVersion(browserslist, "es2019")) return "es2019"; if (browserslistSupportsEcmaVersion(browserslist, "es2018")) return "es2018"; else if (browserslistSupportsEcmaVersion(browserslist, "es2017")) return "es2017"; else if (browserslistSupportsEcmaVersion(browserslist, "es2016")) return "es2016"; else if (browserslistSupportsEcmaVersion(browserslist, "es2015")) return "es2015"; else if (browserslistSupportsEcmaVersion(browserslist, "es5")) return "es5"; else return "es3"; } /** * Generates a Browserslist based on browser support for the given ECMA version */ function browsersWithSupportForEcmaVersion(version) { switch (version) { case "es3": return browsersWithoutSupportForFeatures(...ES5_FEATURES); case "es5": return browsersWithSupportForFeatures(...ES5_FEATURES); case "es2015": return browsersWithSupportForFeatures(...ES2015_FEATURES); case "es2016": return browsersWithSupportForFeatures(...ES2016_FEATURES); case "es2017": return browsersWithSupportForFeatures(...ES2017_FEATURES); case "es2018": return browsersWithSupportForFeatures(...ES2018_FEATURES); case "es2019": return browsersWithSupportForFeatures(...ES2019_FEATURES); case "es2020": return browsersWithSupportForFeatures(...ES2020_FEATURES); case "es2021": return browsersWithSupportForFeatures(...ES2021_FEATURES); case "es2022": return browsersWithSupportForFeatures(...ES2022_FEATURES); case "es2023": return browsersWithSupportForFeatures(...ES2023_FEATURES); } } /** * Returns true if the given browserslist support all of the given features */ function browserslistSupportsFeatures(browserslist, ...features) { // First, generate an ideal browserslist that would target the given features exactly const normalizedIdealBrowserslist = normalizeBrowserslist(bro