'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} */ 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} */ 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(browsersWithSupportForFeatures(...features)); // Now, normalize the input browserslist const normalizedInputBrowserslist = normalizeBrowserslist(browserslist); // Now, compare the two and see if they align. If they do, the input browserslist *does* support all of the given features. // They align if all members of the input browserslist are included in the ideal browserslist return normalizedInputBrowserslist.every(option => normalizedIdealBrowserslist.includes(option)); } /** * Generates a Browserslist based on browsers that *doesn't* support the given features */ function browsersWithoutSupportForFeatures(...features) { return browserSupportForFeaturesCommon("<", ...features).query; } /** * Returns true if the given browser should be ignored. The data reported from Caniuse is a bit lacking. * For example, only the latest version of and_ff, and_qq, and_uc and baidu is reported, and since * android went to use Chromium for the WebView, it has only reported the latest Chromium version */ function shouldIgnoreBrowser(browser, version) { return ((browser === "android" && semver.gt(coerceToString(browser, version), coerceToString(browser, "4.4.4"))) || (browser === "op_mob" && semver.gt(coerceToString(browser, version), coerceToString(browser, "12.1")))); } /** * Normalizes the given ICaniuseLiteFeature */ function getCaniuseLiteFeatureNormalized(stats, featureName) { // Check if a correction exists for this browser const featureCorrectionMatch = FEATURE_TO_BROWSER_DATA_CORRECTIONS_MAP.get(featureName); const keys = Object.keys(stats); keys.forEach(browser => { const browserDict = stats[browser]; Object.entries(browserDict).forEach(([version, support]) => { const versionMatch = version.match(NORMALIZE_BROWSER_VERSION_REGEXP); const normalizedVersion = versionMatch == null ? version : versionMatch[1]; let supportKind; if (support === CaniuseSupportKind.AVAILABLE || support === CaniuseSupportKind.UNAVAILABLE || support === CaniuseSupportKind.PARTIAL_SUPPORT || support === CaniuseSupportKind.PREFIXED) { supportKind = support; } else if (support.startsWith("y")) { supportKind = CaniuseSupportKind.AVAILABLE; } else if (support.startsWith("n")) { supportKind = CaniuseSupportKind.UNAVAILABLE; } else if (support.startsWith("a")) { supportKind = CaniuseSupportKind.PARTIAL_SUPPORT; } else { supportKind = CaniuseSupportKind.PREFIXED; } // Delete the rewritten version if (version !== normalizedVersion) { delete browserDict[version]; } if (support !== supportKind) { browserDict[normalizedVersion] = supportKind; } // If a feature correction exists for this feature, apply applicable corrections if (featureCorrectionMatch != null) { // Check if the browser has some corrections const browserMatch = featureCorrectionMatch[browser]; if (browserMatch != null) { // Apply all corrections browserMatch.forEach(correction => { browserDict[correction.version] = correction.kind; }); } } }); }); return stats; } /** * Gets the support from caniuse for the given feature */ function getCaniuseFeatureSupport(feature) { const rawStats = caniuseLite.feature(caniuseLite.features[feature]).stats; for (const browser of Object.keys(rawStats)) { const browserDict = rawStats[browser]; for (const version of Object.keys(browserDict)) { if (shouldIgnoreBrowser(browser, version)) { delete browserDict[version]; } } } return getCaniuseLiteFeatureNormalized(rawStats, feature); } /** * Returns true if the given feature is a Caniuse feature */ function isCaniuseFeature(feature) { return caniuseLite.features[feature] != null; } /** * Returns true if the given feature is a MDN feature */ function isMdnFeature(feature) { return objectPath.get(compatData, feature) != null; } /** * Asserts that the given feature is a valid Caniuse or MDN feature name */ function assertKnownFeature(feature) { if (!isCaniuseFeature(feature) && !isMdnFeature(feature)) { throw new TypeError(`The given feature: '${feature}' is unknown. It must be a valid Caniuse or MDN feature!`); } } function normalizeFeature(feature) { if (feature in CANIUSE_TO_MDN_FEATURE_MAP) { return CANIUSE_TO_MDN_FEATURE_MAP[feature]; } return feature; } /** * Gets the feature support for the given feature */ function getFeatureSupport(feature) { const normalizedFeature = normalizeFeature(feature); // First check if the cache has a match and return it if so const cacheHit = featureToCaniuseStatsCache.get(normalizedFeature); if (cacheHit != null) return cacheHit; // Assert that the feature is in fact known assertKnownFeature(normalizedFeature); const result = isMdnFeature(normalizedFeature) ? getMdnFeatureSupport(normalizedFeature) : getCaniuseFeatureSupport(normalizedFeature); // Store it in the cache before returning it featureToCaniuseStatsCache.set(normalizedFeature, result); return result; } /** * Gets the support from caniuse for the given feature */ function getMdnFeatureSupport(feature) { const match = objectPath.get(compatData, feature); const supportMap = match.__compat.support; const formatBrowser = (mdnBrowser, caniuseBrowser) => { const versionMap = supportMap[mdnBrowser]; const versionAdded = versionMap == null ? false : Array.isArray(versionMap) ? // If there are multiple entries, take the one that hasn't been removed yet, if any (() => { const versionStillInBrowser = versionMap.filter(element => element.version_removed == null)[0]; return versionStillInBrowser == null || versionStillInBrowser.version_added == null ? false : versionStillInBrowser.version_added; })() : versionMap.version_added; const dict = {}; const supportedSince = versionAdded === false ? null : versionAdded === true ? getOldestVersionOfBrowser(caniuseBrowser) : versionAdded; getSortedBrowserVersionsWithLeadingVersion(caniuseBrowser, typeof versionAdded === "string" ? versionAdded : undefined).forEach(version => { // If the features has never been supported, mark the feature as unavailable if (supportedSince == null) { dict[version] = CaniuseSupportKind.UNAVAILABLE; } else { dict[version] = version === "TP" || version === "all" || semver.gte(coerceToString(caniuseBrowser, version), coerceToString(caniuseBrowser, supportedSince)) ? CaniuseSupportKind.AVAILABLE : CaniuseSupportKind.UNAVAILABLE; } }); return dict; }; const stats = { /* eslint-disable @typescript-eslint/naming-convention */ and_chr: formatBrowser("chrome_android", "and_chr"), chrome: formatBrowser("chrome", "chrome"), and_ff: formatBrowser("firefox_android", "and_ff"), and_qq: {}, and_uc: {}, android: formatBrowser("webview_android", "android"), baidu: {}, bb: {}, edge: formatBrowser("edge", "edge"), samsung: formatBrowser("samsunginternet_android", "samsung"), ie: formatBrowser("ie", "ie"), ie_mob: formatBrowser("ie", "ie_mob"), safari: formatBrowser("safari", "safari"), ios_saf: formatBrowser("safari_ios", "ios_saf"), opera: formatBrowser("opera", "opera"), op_mini: {}, op_mob: {}, firefox: formatBrowser("firefox", "firefox") /* eslint-enable @typescript-eslint/naming-convention */ }; return getCaniuseLiteFeatureNormalized(stats, feature); } /** * Gets the first version that matches the given CaniuseSupportKind */ function getFirstVersionWithSupportKind(kind, stats) { // Sort all keys of the object const sortedKeys = Object.keys(stats).sort(compareVersions); for (const key of sortedKeys) { if (stats[key] === kind) { return key; } } return undefined; } /** * Sorts the given browserslist. Ensures that 'not' expressions come last */ function sortBrowserslist(a, b) { if (a.startsWith("not") && !b.startsWith("not")) return 1; if (!a.startsWith("not") && b.startsWith("not")) return -1; return 0; } /** * Gets a Map between browser names and the first version of them that supported the given feature */ function getFirstVersionsWithFullSupport(feature) { const normalizedFeature = normalizeFeature(feature); const support = getFeatureSupport(normalizedFeature); // A map between browser names and their required versions const browserMap = new Map(); const entries = Object.entries(support); entries.forEach(([browser, stats]) => { const fullSupportVersion = getFirstVersionWithSupportKind(CaniuseSupportKind.AVAILABLE, stats); if (fullSupportVersion != null) { browserMap.set(browser, fullSupportVersion); } }); return browserMap; } /** * Gets the Cache key for the given combination of a comparison operator and any amount of features */ function getBrowserSupportForFeaturesCacheKey(comparisonOperator, features) { return `${comparisonOperator}.${features.sort().join(",")}`; } /** * Common logic for the functions that generate browserslists based on feature support */ function browserSupportForFeaturesCommon(comparisonOperator, ...features) { const normalizedFeatures = features.map(normalizeFeature); const cacheKey = getBrowserSupportForFeaturesCacheKey(comparisonOperator, normalizedFeatures); // First check if the cache has a hit and return it if so const cacheHit = browserSupportForFeaturesCache.get(cacheKey); if (cacheHit != null) { return cacheHit; } // All of the generated browser maps const browserMaps = []; for (const normalizedFeature of normalizedFeatures) { const support = getFeatureSupport(normalizedFeature); // A map between browser names and their required versions const browserMap = new Map(); const entries = Object.entries(support); entries.forEach(([browser, stats]) => { const fullSupportVersion = getFirstVersionWithSupportKind(CaniuseSupportKind.AVAILABLE, stats); const partialSupportVersion = getFirstVersionWithSupportKind(CaniuseSupportKind.PARTIAL_SUPPORT, stats); let versionToSet; if (fullSupportVersion != null) { versionToSet = fullSupportVersion; } // Otherwise, check if partial support exists and should be allowed if (partialSupportVersion != null) { // Get all partial support allowances for this specific feature const partialSupportMatch = PARTIAL_SUPPORT_ALLOWANCES.get(normalizedFeature); // Check if partial support exists for the browser. // If no full supported version exists or if the partial supported version has a lower version number than the full supported one, use that one instead if (partialSupportMatch != null && (partialSupportMatch === "*" || partialSupportMatch.includes(browser)) && (fullSupportVersion == null || compareVersions(partialSupportVersion, fullSupportVersion) < 0)) { versionToSet = partialSupportVersion; } } if (versionToSet == null) { // Apply additional checks depending on the comparison operator switch (comparisonOperator) { case "<": case "<=": // Add all browsers with no support whatsoever, or those that require prefixing or flags versionToSet = "-1"; } } if (versionToSet != null) { browserMap.set(browser, versionToSet); } }); browserMaps.push(browserMap); } // Now, remove all browsers that isn't part of all generated browser maps for (const browserMap of browserMaps) { for (const browser of browserMap.keys()) { if (!browserMaps.every(map => map.has(browser))) { // Delete the browser if it isn't included in all of the browser maps browserMap.delete(browser); } } } // Now, prepare a combined browser map const combinedBrowserMap = new Map(); for (const browserMap of browserMaps) { for (const [browser, version] of browserMap.entries()) { // Take the existing entry from the combined map const existingVersion = combinedBrowserMap.get(browser); // The browser should be set in the map if it has no entry already const shouldSet = existingVersion !== "-1" && (existingVersion == null || version === "-1" || compareVersions(version, existingVersion) >= 0); if (shouldSet) { // Set the version in the map combinedBrowserMap.set(browser, version); } } } // Finally, generate a string array of the browsers // Make sure that 'not' expressions come last const query = [].concat .apply([], Array.from(combinedBrowserMap.entries()).map(([browser, version]) => { // The version is not a number, so we can't do comparisons on it. if (isNaN(parseFloat(version))) { switch (comparisonOperator) { case "<": case "<=": { const previousVersion = getPreviousVersionOfBrowser(browser, version); return [`not ${browser} ${version}`, ...(previousVersion == null ? [] : [`${browser} ${comparisonOperator} ${previousVersion}`])]; } case ">": case ">=": { const nextVersion = getNextVersionOfBrowser(browser, version); return [`${browser} ${version}`, ...(nextVersion == null ? [] : [`${browser} ${comparisonOperator} ${nextVersion}`])]; } } } return parseInt(version) === -1 ? [ `${comparisonOperator === ">" || comparisonOperator === ">=" ? "not " : ""}${browser} ${browser === "op_mini" ? "all" : "> 0"}`, `${comparisonOperator === ">" || comparisonOperator === ">=" ? "not " : ""}unreleased ${browser} versions` ] : [`${browser} ${comparisonOperator} ${version}`]; })) .sort(sortBrowserslist); const returnObject = { query, browsers: new Set(combinedBrowserMap.keys()) }; // Store it in the cache before returning it browserSupportForFeaturesCache.set(cacheKey, returnObject); return returnObject; } /** * Gets the matching CaniuseBrowser for the given UseragentBrowser. Not all are supported, so it may return undefined */ function getCaniuseBrowserForUseragentBrowser(parser) { var _a, _b; const browser = parser.getBrowser(); const device = parser.getDevice(); const os = parser.getOS(); const engine = parser.getEngine(); // If the OS is iOS, it is actually Safari that drives the WebView if (os.name === "iOS") { // Opera Mini with the Presto runtime actually works around // the restrictions os the Safari WebView if (browser.name === "Opera Mini" && engine.name === "Presto") { return { browser: "op_mini", version: browser.version }; } // In all other cases, it is always Safari driving the WebView return { browser: "ios_saf", version: (_a = os.version) !== null && _a !== void 0 ? _a : browser.version }; } // First, if it is a Blackberry device, it will always be the 'bb' browser if (device.vendor === "BlackBerry" || os.name === "BlackBerry") { return { browser: "bb", version: browser.version }; } // For platforms where the HeyTapBrowser doesn't report which Chrome version // it is based on, we'll have to rely on knowledge from similar user agent strings. // as far as we know, HeyTapBrowser on non-iOS is always based on Chrome 70 or 77, // seemingly at random. So we'll have to assume Chrome 70 here. if (browser.name === "HeyTapBrowser" && engine.name === "WebKit") { return { browser: "chrome", version: "70" }; } // Unfortunately, since Caniuse doesn't support PaleMoon, // we will have to remap it to its closest equivalent Firefox // version (which it is similar to and a fork of). // This is less than ideal, but unfortunately a requirement for the time being if (browser.name === "PaleMoon" && engine.name === "Goanna" && browser.version != null) { const semver$1 = ensureSemver(undefined, browser.version); // The data comes from this table: https://en.wikipedia.org/wiki/Pale_Moon_(web_browser)#Releases if (semver.lte(semver$1, "5.0.0")) { return { browser: "firefox", version: "2" }; } // Between these two versions, the version numbers followed Firefox/Gecko else if (semver.lte(semver$1, "24.0.0")) { return { browser: "firefox", version: browser.version }; } // It kept staying at Firefox 24 for all we know else if (semver.lt(semver$1, "27.0.0")) { return { browser: "firefox", version: "24.0.0" }; } // Then, from v27, it was based on a re-fork of Firefox 38. // Unfortunately, we don't have fresh data as for the versions // in between 27 and 29, so we'll have to stay at version 38 in // this range else if (semver.lt(semver$1, "29.0.0")) { return { browser: "firefox", version: "38" }; } // We know that v29 points to Firefox 68 in some of its user agents else { return { browser: "firefox", version: "68" }; } } // For the MIUIBrowser, there are some rare instances for major versions 8 and 9 where they'll have no declared Chromium engine. // as part of the UA. Under these circumstances, we have to rely on knowledge gathered from scraping related User Agents // to determine the equivalent Chromium version if (browser.name === "MIUI Browser" && browser.version != null && os.name === "Android" && engine.name == null) { const semver = ensureSemver(undefined, browser.version); if (semver.major === 8 || semver.major === 9) { return { browser: "chrome", version: "53" }; } } switch (browser.name) { case "Samsung Browser": if (browser.version != null) { return { browser: "samsung", version: browser.version }; } else if (engine.name === "Blink" && engine.version != null) { return { browser: "chrome", version: engine.version }; } else { break; } case "Android Browser": { // If the vendor is Samsung, the default browser is Samsung Internet if (device.vendor === "Samsung") { return { browser: "samsung", version: browser.version }; } // Default to the stock android browser return { browser: "android", version: browser.version }; } case "WebKit": // This will be the case if we're in an iOS Safari WebView if (device.type === "mobile" || device.type === "tablet" || device.type === "smarttv" || device.type === "wearable" || device.type === "embedded") { return { browser: "ios_saf", version: os.version }; } // Otherwise, fall back to Safari return { browser: "safari", version: browser.version }; case "Baidu": return { browser: "baidu", version: browser.version }; case "Chrome Headless": case "Chrome WebView": return { browser: "chrome", version: browser.version }; case "Facebook": // We've already asserted that this isn't iOS above, so we must be on Android and inside of a WebView return { browser: "chrome", version: browser.version }; case "Chrome": { // Check if the OS is Android, in which case this is actually Chrome for Android. Make it report as regular Chrome if (os.name === "Android") { // Handle a special case on Android where the Chrome version // is actually the WebKit version, and it is actually the stock // Android browser. if (os.version != null && browser.version != null) { const browserSemver = ensureSemver("chrome", browser.version); const osSemver = ensureSemver(undefined, os.version); if (semver.lte(osSemver, "4.4.4") && semver.gte(browserSemver, "400.0.0")) { return { browser: "android", version: os.version }; } } // Treat Android Chrome as Desktop Chrome, as these are functionally equivalent and always match each other from a feature support point of view return { browser: "chrome", version: browser.version }; } // Otherwise, fall back to chrome return { browser: "chrome", version: browser.version }; } case "Edge": { // If the Engine is Blink, it's Chrome-based if (engine.name === "Blink") { // If there is no browser version, fall back to Chrome if (browser.version == null) { return { browser: "chrome", version: engine.version }; } const semverVersion = ensureSemver("edge", browser.version); // If the Major version is in between 18 and 79, this will be Edge Mobile on Android, // which is Chromium based but has no related Caniuse browser name. Treat it as Chrome if (semverVersion.major > 18 && semverVersion.major < 79) { return { browser: "chrome", version: engine.version }; } } return { browser: "edge", version: browser.version }; } case "Firefox": // Check if the OS is Android, in which case this is actually Firefox for Android. if (os.name === "Android") { return { browser: "and_ff", version: browser.version }; } // Default to Firefox return { browser: "firefox", version: browser.version }; case "IE": return { browser: "ie", version: browser.version }; case "IE Mobile": case "IEMobile": return { browser: "ie_mob", version: browser.version }; case "Safari": // If no browser version is reported, and it is based on WebKit, // we will have to attempt to "guess" the Safari version with mapping the // WebKit version to an equivalent Safari version based on the data // here: https://en.wikipedia.org/wiki/Safari_version_history, even // though this doesn't seem to map correctly to real-world data if (browser.version == null && engine.name === "WebKit" && engine.version != null) { const semver$1 = ensureSemver(undefined, engine.version); if (semver.lt(semver$1, "412.0.0")) { return { browser: "safari", version: "1.0" }; } if (semver.lt(semver$1, "522.0.0")) { return { browser: "safari", version: "2.0" }; } if (semver.lt(semver$1, "526.0.0")) { return { browser: "safari", version: "3.0" }; } if (semver.lt(semver$1, "533.0.0")) { return { browser: "safari", version: "4.0" }; } if (semver.lt(semver$1, "536.0.0")) { return { browser: "safari", version: "5.0" }; } if (semver.lt(semver$1, "537.71.0")) { return { browser: "safari", version: "6.0" }; } if (semver.lt(semver$1, "600.0.0")) { return { browser: "safari", version: "7.0" }; } if (semver.lt(semver$1, "601.0.0")) { return { browser: "safari", version: "8.0" }; } if (semver.lt(semver$1, "602.0.0")) { return { browser: "safari", version: "9.0" }; } if (semver.lt(semver$1, "604.0.0")) { return { browser: "safari", version: "10.0" }; } if (semver.lt(semver$1, "606.0.0")) { return { browser: "safari", version: "11.0" }; } if (semver.lt(semver$1, "608.0.0")) { return { browser: "safari", version: "12.0" }; } if (semver.lt(semver$1, "610.0.0")) { return { browser: "safari", version: "13.0" }; } // Else it is the current Safari version. // Keep this updated regularly return { browser: "safari", version: "14.0" }; } return { browser: "safari", version: browser.version }; case "Mobile Safari": case "MobileSafari": case "Safari Mobile": case "SafariMobile": return { browser: "ios_saf", version: (_b = os.version) !== null && _b !== void 0 ? _b : browser.version }; case "Opera": return { browser: "opera", version: browser.version }; case "Opera Mini": return { browser: "op_mini", version: browser.version }; case "Opera Mobi": return { browser: "op_mob", version: browser.version }; case "QQBrowser": return { browser: "and_qq", version: browser.version }; case "UCBrowser": return { browser: "and_uc", version: browser.version }; default: switch (engine.name) { // If the Engine is Blink, it's Chrome case "Blink": return { browser: "chrome", version: engine.version }; case "WebKit": return { browser: "safari", version: browser.version }; case "EdgeHTML": return { browser: "edge", version: browser.version }; case "Gecko": return { browser: "firefox", version: engine.version }; case "Presto": return { browser: "opera", version: browser.version }; } } // Fall back to the unknown Caniuse browser when all // we received was the name of the OS if (browser.name == null && engine.name == null && device.type == null && os.name != null) { return UNKNOWN_CANIUSE_BROWSER; } return {}; } /** * Normalizes the version of the browser such that it plays well with Caniuse */ function getCaniuseVersionForUseragentVersion({ browser, version }, useragentBrowser, useragentOs, useragentEngine) { var _a; // Always use 'all' with Opera Mini if (browser === "op_mini") { return "all"; } else if (browser === "safari") { // Check if there is a newer version of the browser const nextBrowserVersion = getNextVersionOfBrowser(browser, version); // If there isn't we're in the Technology Preview if (nextBrowserVersion == null) { return "TP"; } } const coerced = ensureSemver(browser, version); // Make sure that we have a proper Semver version to work with if (coerced == null) throw new TypeError(`Could not detect the version of: '${version}' for browser: ${browser}`); // Unpack the semver version const { major, minor, patch } = coerced; // Generates a Semver version const buildSemverVersion = (majorVersion, minorVersion, patchVersion) => `${majorVersion}${minorVersion == null || minorVersion === 0 ? "" : `.${minorVersion}`}${patchVersion == null || patchVersion === 0 ? "" : `.${patchVersion}`}`; switch (browser) { case "chrome": if (useragentEngine.name === "Blink") { return buildSemverVersion(ensureSemver(browser, getClosestMatchingBrowserVersion(browser, (_a = useragentEngine.version) !== null && _a !== void 0 ? _a : version)).major); } return buildSemverVersion(major); case "ie": case "ie_mob": case "edge": case "bb": case "and_chr": case "and_ff": // Always use the major version of these browser return buildSemverVersion(major); case "opera": case "op_mob": // Opera may have minor versions before it went to Chromium. After that, always use major versions if (major === 10 || major === 11 || major === 12) { return buildSemverVersion(major, minor); } // For anything else, only use the major version return buildSemverVersion(major); case "ios_saf": { // For browsers that report as iOS safari, they may actually be other browsers using Safari's WebView. // We want them to report as iOS safari since they will support the same browsers, but we have to apply // some tricks in order to get the version number // If it is in fact mobile Safari, just use the reported version if (useragentBrowser.name === "Safari" || useragentBrowser.name === "Mobile Safari") { // iOS may have minor releases, but never patch releases, according to caniuse return buildSemverVersion(major, minor); } // Otherwise, try to get the assumed Safari version from the OS version else { if (useragentOs.version == null) throw new ReferenceError(`Could not detect OS version of iOS for ${useragentBrowser.name} on iOS`); // Decide the Semver version const osSemver = ensureSemver(undefined, getClosestMatchingBrowserVersion(browser, useragentOs.version)); // iOS may have minor releases, but never patch releases, according to caniuse return buildSemverVersion(osSemver.major, osSemver.minor); } } case "safari": case "firefox": { // These may have minor releases, but never patch releases, according to caniuse return buildSemverVersion(major, minor); } case "android": // Up to version 4.4.4, these could include patch releases. After that, only use major versions if (major < 4) { return buildSemverVersion(major, minor); } else if (major === 4) { return buildSemverVersion(major, minor, patch); } else { return buildSemverVersion(major); } case "and_uc": case "samsung": case "and_qq": case "baidu": // These may always contain minor versions return buildSemverVersion(major, minor); default: // For anything else, just use the major version return buildSemverVersion(major); } } /** * Generates a browserslist from the provided useragent string */ function generateBrowserslistFromUseragent(useragent) { // Check if a user agent has been generated previously for this specific user agent const cacheHit = userAgentToBrowserslistCache.get(useragent); if (cacheHit != null) return cacheHit; // Otherwise, generate a new one const parser = new UaParserWrapper(useragent); const browser = parser.getBrowser(); const os = parser.getOS(); const engine = parser.getEngine(); // Prepare a CaniuseBrowser name from the useragent string const { browser: caniuseBrowserName, version: caniuseBrowserVersion } = getCaniuseBrowserForUseragentBrowser(parser); // console.log({browser, os, engine, caniuseBrowserName, caniuseBrowserVersion}); // If the browser name or version couldn't be determined, return false immediately if (caniuseBrowserName == null || caniuseBrowserVersion == null) { throw new TypeError(`No caniuse browser and/or version could be determined for User Agent: ${useragent}`); } const closestMatchingCaniuseBrowserVersionOrSmaller = normalizeBrowserVersion(caniuseBrowserName, caniuseBrowserVersion, undefined, true); const closestMatchingCaniuseBrowserVersion = normalizeBrowserVersion(caniuseBrowserName, caniuseBrowserVersion, undefined); const caniuseBrowser = { browser: caniuseBrowserName, version: closestMatchingCaniuseBrowserVersionOrSmaller }; // Prepare a version from the useragent that plays well with caniuse const finalVersion = getCaniuseVersionForUseragentVersion(caniuseBrowser, browser, os, engine); // Prepare a browserslist from the useragent itself const normalizedBrowserslist = normalizeBrowserslist([`${caniuseBrowserName} ${finalVersion}`]); const finalVersionCoerced = ensureSemver(caniuseBrowserName, finalVersion); const closestMatchingCaniuseBrowserVersionCoerced = ensureSemver(caniuseBrowserName, closestMatchingCaniuseBrowserVersion); if (semver.lt(finalVersionCoerced, closestMatchingCaniuseBrowserVersionCoerced, { loose: true }) && normalizedBrowserslist[0] === `${caniuseBrowserName} ${closestMatchingCaniuseBrowserVersion}`) { normalizedBrowserslist[0] = `${caniuseBrowserName} ${finalVersion}`; } // Store it in the cache before returning it userAgentToBrowserslistCache.set(useragent, normalizedBrowserslist); return normalizedBrowserslist; } /** * Generates a browserslist from the provided useragent string and checks if it matches * the given browserslist */ function matchBrowserslistOnUserAgent(useragent, browserslist) { const useragentBrowserslist = generateBrowserslistFromUseragent(useragent); // Pipe the input browserslist through Browserslist to normalize it const normalizedInputBrowserslist = normalizeBrowserslist(browserslist); // Now, compare the two, and if the normalized input browserslist includes every option from the user agent, it is matched return useragentBrowserslist.every(option => normalizedInputBrowserslist.includes(option)); } /** * Returns a key to use for the cache between user agents with feature names and whether or not the user agent supports them */ function userAgentWithFeaturesCacheKey(useragent, features) { return `${useragent}.${features.join(",")}`; } /** * Returns true if the given user agent supports the given features */ function userAgentSupportsFeatures(useragent, ...features) { const normalizedFeatures = features.map(normalizeFeature); // Check if these features has been computed previously for the given user agent const cacheKey = userAgentWithFeaturesCacheKey(useragent, normalizedFeatures); const cacheHit = userAgentWithFeaturesToSupportCache.get(cacheKey); // If so, return the cache hit if (cacheHit != null) return cacheHit; // Prepare a browserslist from the useragent itself const useragentBrowserslist = generateBrowserslistFromUseragent(useragent); // Prepare a browserslist for browsers that support the given features const supportedBrowserslist = normalizeBrowserslist(browsersWithSupportForFeatures(...normalizedFeatures)); // Now, compare the two, and if the browserslist with supported browsers includes every option from the user agent, the user agent supports all of the given features const support = useragentBrowserslist.every(option => supportedBrowserslist.includes(option)); // Set it in the cache and return it userAgentWithFeaturesToSupportCache.set(cacheKey, support); return support; } exports.browsersWithSupportForEcmaVersion = browsersWithSupportForEcmaVersion; exports.browsersWithSupportForFeatures = browsersWithSupportForFeatures; exports.browsersWithoutSupportForFeatures = browsersWithoutSupportForFeatures; exports.browserslistSupportsEcmaVersion = browserslistSupportsEcmaVersion; exports.browserslistSupportsFeatures = browserslistSupportsFeatures; exports.generateBrowserslistFromUseragent = generateBrowserslistFromUseragent; exports.getAppropriateEcmaVersionForBrowserslist = getAppropriateEcmaVersionForBrowserslist; exports.getFirstVersionsWithFullSupport = getFirstVersionsWithFullSupport; exports.matchBrowserslistOnUserAgent = matchBrowserslistOnUserAgent; exports.normalizeBrowserslist = normalizeBrowserslist; exports.userAgentSupportsFeatures = userAgentSupportsFeatures; //# sourceMappingURL=index.cjs.map