/
githubmirror
/
webpack
Обзор
Документация
Войти
/
githubmirror
/
webpack
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
lib/config/browserslistTargetHandler.js
619 строк
14 KB
Alexander Akait
feat(css): join adjacent rules and fold more value spellings (#21648)
10 авг 2026, 08:55
Не верифицирован
10 авг 2026, 08:55
3197f80
Код
Авторство
О чём код?
/* MIT License http://www.opensource.org/licenses/mit-license.php Author Sergey Melyukov @smelukov */ "use strict"; const path = require("path"); const browserslist = require("browserslist"); /** @typedef {import("./target").ApiTargetProperties} ApiTargetProperties */ /** @typedef {import("./target").CssTargetProperties} CssTargetProperties */ /** @typedef {import("./target").EcmaTargetProperties} EcmaTargetProperties */ /** @typedef {import("./target").PlatformTargetProperties} PlatformTargetProperties */ // [[C:]/path/to/config][:env] const inputRx = /^(?:((?:[A-Z]:)?[/\\].*?))?(?::(.+?))?$/i; /** * Returns selected browsers. * @param {string | null | undefined} input input string * @param {string} context the context directory * @returns {string[] | undefined} selected browsers */ const load = (input, context) => { // browserslist:path-to-config // browserslist:path-to-config:env if (input && path.isAbsolute(input)) { const [, configPath, env] = inputRx.exec(input) || []; const config = browserslist.loadConfig({ config: configPath, env }); return browserslist(config, { env }); } const env = input || undefined; const config = browserslist.loadConfig({ path: context, env }); // browserslist // browserslist:env if (config) { try { return browserslist(config, { env, throwOnMissing: true }); } catch (_err) { // Nothing, no `env` was found in browserslist, maybe input is `queries` } } // browserslist:query if (env) { return browserslist(env); } }; /** * Returns target properties. * @param {string[]} browsers supported browsers list * @returns {EcmaTargetProperties & PlatformTargetProperties & ApiTargetProperties & CssTargetProperties} target properties */ const resolve = (browsers) => { /** * Checks all against a version number * @param {Record<string, number | [number, number]>} versions first supported version * @returns {boolean} true if supports */ const rawChecker = (versions) => checkAll(browsers, versions); /** * Checks a subset of the list against a version number * @param {string[]} list the browsers to check * @param {Record<string, number | [number, number]>} versions first supported version * @returns {boolean} true if all support */ const checkAll = (list, versions) => list.every((v) => { const [name, parsedVersion] = v.split(" "); if (!name) return false; const requiredVersion = versions[name]; if (!requiredVersion) return false; const [parsedMajor, parserMinor] = // safari TP supports all features for normal safari parsedVersion === "TP" ? [Infinity, Infinity] : parsedVersion.includes("-") ? parsedVersion.split("-")[0].split(".") : parsedVersion.split("."); if (typeof requiredVersion === "number") { return Number(parsedMajor) >= requiredVersion; } return requiredVersion[0] === Number(parsedMajor) ? Number(parserMinor) >= requiredVersion[1] : Number(parsedMajor) > requiredVersion[0]; }); /** * Checks the rendering engines against a version number. A CSS feature is a * property of those alone, so node entries say nothing about it — and a list * with no engine at all renders no stylesheet, which leaves the modern * spelling available. * @param {Record<string, number | [number, number]>} versions first supported version * @returns {boolean} true if all rendering engines support */ const cssChecker = (versions) => { const engines = browsers.filter((b) => !b.startsWith("node ")); return engines.length === 0 || checkAll(engines, versions); }; const anyNode = browsers.some((b) => b.startsWith("node ")); const anyBrowser = browsers.some((b) => /^(?!node)/.test(b)); const browserProperty = !anyBrowser ? false : anyNode ? null : true; const nodeProperty = !anyNode ? false : anyBrowser ? null : true; return { /* eslint-disable camelcase */ const: rawChecker({ chrome: 49, and_chr: 49, edge: 12, // Prior to Firefox 13, <code>const</code> is implemented, but re-assignment is not failing. // Prior to Firefox 46, a <code>TypeError</code> was thrown on redeclaration instead of a <code>SyntaxError</code>. firefox: 36, and_ff: 36, // Not supported in for-in and for-of loops // ie: Not supported opera: 36, op_mob: 36, safari: [10, 0], ios_saf: [10, 0], // Before 5.0 supported correctly in strict mode, otherwise supported without block scope samsung: [5, 0], android: 37, and_qq: [10, 4], // Supported correctly in strict mode, otherwise supported without block scope baidu: [13, 18], and_uc: [12, 12], kaios: [2, 5], node: [6, 0] }), let: rawChecker({ chrome: 49, and_chr: 49, edge: 12, firefox: 44, and_ff: 44, // ie: Not supported opera: 36, op_mob: 36, safari: [10, 0], ios_saf: [10, 0], samsung: [5, 0], android: 49, and_qq: [10, 4], baidu: [13, 18], and_uc: [12, 12], kaios: [2, 5], node: [6, 0] }), logicalAssignment: rawChecker({ chrome: 85, and_chr: 85, edge: 85, firefox: 79, and_ff: 79, // ie: Not supported, opera: 71, op_mob: 60, safari: 14, ios_saf: 14, samsung: [14, 0], android: 85, and_qq: [14, 9], baidu: [13, 52], and_uc: [15, 5], kaios: [3, 0], node: [15, 0] }), methodShorthand: rawChecker({ chrome: 47, and_chr: 47, edge: 12, firefox: 34, and_ff: 34, // ie: Not supported, opera: 34, op_mob: 34, safari: 9, ios_saf: 9, samsung: 5, android: 47, // baidu: Not tracked, and_qq: [14, 9], and_uc: [15, 5], kaios: [2, 5], node: [4, 9] }), arrowFunction: rawChecker({ chrome: 45, and_chr: 45, edge: 12, // The initial implementation of arrow functions in Firefox made them automatically strict. This has been changed as of Firefox 24. The use of <code>'use strict';</code> is now required. // Prior to Firefox 39, a line terminator (<code>\\n</code>) was incorrectly allowed after arrow function arguments. This has been fixed to conform to the ES2015 specification and code like <code>() \\n => {}</code> will now throw a <code>SyntaxError</code> in this and later versions. firefox: 39, and_ff: 39, // ie: Not supported, opera: 32, op_mob: 32, safari: 10, ios_saf: 10, samsung: [5, 0], android: 45, and_qq: [10, 4], baidu: [7, 12], and_uc: [12, 12], kaios: [2, 5], node: [6, 0] }), forOf: rawChecker({ chrome: 38, and_chr: 38, edge: 12, // Prior to Firefox 51, using the for...of loop construct with the const keyword threw a SyntaxError ("missing = in const declaration"). firefox: 51, and_ff: 51, // ie: Not supported, opera: 25, op_mob: 25, safari: 7, ios_saf: 7, samsung: [3, 0], android: 38, and_qq: [10, 4], // baidu: Unknown support and_uc: [12, 12], kaios: [3, 0], node: [0, 12] }), destructuring: rawChecker({ chrome: 49, and_chr: 49, edge: 14, firefox: 41, and_ff: 41, // ie: Not supported, opera: 36, op_mob: 36, safari: 8, ios_saf: 8, samsung: [5, 0], android: 49, and_qq: [10, 4], // baidu: Unknown support and_uc: [12, 12], kaios: [2, 5], node: [6, 0] }), bigIntLiteral: rawChecker({ chrome: 67, and_chr: 67, edge: 79, firefox: 68, and_ff: 68, // ie: Not supported, opera: 54, op_mob: 48, safari: 14, ios_saf: 14, samsung: [9, 2], android: 67, and_qq: [13, 1], baidu: [13, 18], and_uc: [15, 5], kaios: [3, 0], node: [10, 4] }), // Support syntax `import` and `export` and no limitations and bugs on Node.js // Not include `export * as namespace` module: rawChecker({ chrome: 61, and_chr: 61, edge: 16, firefox: 60, and_ff: 60, // ie: Not supported, opera: 48, op_mob: 45, safari: [10, 1], ios_saf: [10, 3], samsung: [8, 0], android: 61, and_qq: [10, 4], baidu: [13, 18], and_uc: [15, 5], kaios: [3, 0], node: [12, 17] }), dynamicImport: rawChecker({ chrome: 63, and_chr: 63, edge: 79, firefox: 67, and_ff: 67, // ie: Not supported opera: 50, op_mob: 46, safari: [11, 1], ios_saf: [11, 3], samsung: [8, 2], android: 63, and_qq: [10, 4], baidu: [13, 18], and_uc: [15, 5], kaios: [3, 0], node: [12, 17] }), dynamicImportInWorker: rawChecker({ chrome: 80, and_chr: 80, edge: 80, firefox: 114, and_ff: 114, // ie: Not supported opera: 67, op_mob: 57, safari: [15, 0], ios_saf: [15, 0], samsung: [13, 0], android: 80, and_qq: [10, 4], baidu: [13, 18], and_uc: [15, 5], kaios: [3, 0], node: [12, 17] }), // browserslist does not have info about globalThis // so this is based on mdn-browser-compat-data globalThis: rawChecker({ chrome: 71, and_chr: 71, edge: 79, firefox: 65, and_ff: 65, // ie: Not supported, opera: 58, op_mob: 50, safari: [12, 1], ios_saf: [12, 2], samsung: [10, 1], android: 71, and_qq: [13, 1], // baidu: Unknown support and_uc: [15, 5], kaios: [3, 0], node: 12 }), optionalChaining: rawChecker({ chrome: 80, and_chr: 80, edge: 80, firefox: 74, and_ff: 79, // ie: Not supported, opera: 67, op_mob: 64, safari: [13, 1], ios_saf: [13, 4], samsung: 13, android: 80, and_qq: [13, 1], // baidu: Not supported and_uc: [15, 5], kaios: [3, 0], node: 14 }), symbol: rawChecker({ chrome: 38, and_chr: 38, edge: 12, firefox: 36, and_ff: 36, // ie: Not supported, opera: 25, op_mob: 25, safari: 9, ios_saf: 9, samsung: [3, 0], android: 38, and_qq: [10, 4], and_uc: [12, 12], kaios: [2, 5], node: [0, 12] }), hasOwn: rawChecker({ chrome: 93, and_chr: 93, edge: 93, firefox: 92, and_ff: 92, // ie: Not supported, opera: 79, op_mob: 66, safari: [15, 4], ios_saf: [15, 4], samsung: 17, android: 93, // and_qq: Not supported // baidu: Not supported // and_uc: Not supported // kaios: Not supported node: [16, 9] }), spread: rawChecker({ chrome: 60, and_chr: 60, edge: 79, firefox: 55, and_ff: 55, // ie: Not supported, opera: 47, op_mob: 44, safari: [11, 1], ios_saf: [11, 3], samsung: [8, 2], android: 60, and_qq: [13, 1], // baidu: Not supported and_uc: [15, 5], kaios: [3, 0], node: [8, 3] }), templateLiteral: rawChecker({ chrome: 41, and_chr: 41, edge: 13, firefox: 34, and_ff: 34, // ie: Not supported, opera: 29, op_mob: 64, safari: [9, 1], ios_saf: 9, samsung: 4, android: 41, and_qq: [10, 4], baidu: [7, 12], and_uc: [12, 12], kaios: [2, 5], node: 4 }), asyncFunction: rawChecker({ chrome: 55, and_chr: 55, edge: 15, firefox: 52, and_ff: 52, // ie: Not supported, opera: 42, op_mob: 42, safari: 11, ios_saf: 11, samsung: [6, 2], android: 55, and_qq: [10, 4], baidu: [13, 18], and_uc: [12, 12], kaios: 3, node: [7, 6] }), generator: rawChecker({ chrome: 39, and_chr: 39, edge: 13, firefox: 26, and_ff: 26, // ie: Not supported, opera: 26, op_mob: 26, safari: 10, ios_saf: 10, samsung: [3, 4], android: 39, and_qq: [10, 4], baidu: [7, 12], and_uc: [12, 12], kaios: [2, 5], node: 4 }), // CSS Color 4's `#rgba` / `#rrggbbaa`. cssColorHexAlpha: cssChecker({ chrome: 62, and_chr: 62, edge: 79, firefox: 49, and_ff: 49, // ie: Not supported, opera: 49, op_mob: 46, safari: 10, ios_saf: 10, samsung: [8, 2], android: 62, baidu: [13, 18], and_qq: [13, 1], and_uc: [15, 5], kaios: [3, 0] // node: Renders no CSS }), // Media Queries 4's range syntax (`(width >= 600px)`). cssMediaQueryRange: cssChecker({ chrome: 104, and_chr: 104, edge: 104, firefox: 102, and_ff: 102, // ie: Not supported, opera: 90, op_mob: 71, safari: [16, 4], ios_saf: [16, 4], samsung: 20, android: 104 // baidu / and_qq / and_uc / kaios: Not tracked }), // CSS Images 4's two-position color stop (`red 0% 50%`), which stands for // the two stops it would otherwise take. cssGradientDoublePosition: cssChecker({ chrome: 72, and_chr: 72, edge: 79, firefox: 83, and_ff: 83, // ie: Not supported, opera: 60, op_mob: 51, safari: [12, 1], ios_saf: [12, 2], samsung: [11, 1], android: 72 // baidu / and_qq / and_uc / kaios: Not tracked }), // CSS Position 3's `inset` shorthand. Newer than the `top` / `right` / // `bottom` / `left` longhands it merges, which are CSS 1. cssInsetShorthand: cssChecker({ chrome: 87, and_chr: 87, edge: 87, firefox: 66, and_ff: 66, // ie: Not supported, opera: 73, op_mob: 62, safari: [14, 1], ios_saf: [14, 5], samsung: 14, android: 87 // baidu / and_qq / and_uc / kaios: Not tracked }), /* eslint-enable camelcase */ browser: browserProperty, electron: false, node: nodeProperty, nwjs: false, web: browserProperty, webworker: false, document: browserProperty, modulePreload: browserProperty && /* eslint-disable camelcase */ rawChecker({ chrome: 66, and_chr: 66, edge: 79, firefox: 115, and_ff: 115, opera: 53, op_mob: 47, safari: 17, ios_saf: 17, samsung: [9, 0], android: 66, and_qq: [13, 1], baidu: [13, 18], and_uc: [15, 5], kaios: [3, 1] }), /* eslint-enable camelcase */ fetchWasm: browserProperty, global: nodeProperty, importScripts: false, importScriptsInWorker: Boolean(browserProperty), nodeBuiltins: nodeProperty, nodePrefixForCoreModules: nodeProperty && !browsers.some((b) => b.startsWith("node 15")) && rawChecker({ node: [14, 18] }), importMetaDirnameAndFilename: nodeProperty && rawChecker({ node: [22, 16] }), nodeBuiltinModuleGetter: nodeProperty && rawChecker({ node: [22, 3] }), require: nodeProperty }; }; module.exports = { load, resolve };