webpack
Version:
Packs ECMAScript/CommonJs/AMD modules for the browser. Allows you to split your codebase into multiple bundles, which can be loaded on demand. Supports loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.
566 lines (548 loc) • 12.8 kB
JavaScript
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Sergey Melyukov @smelukov
*/
"use strict";
const path = require("path");
const browserslist = require("browserslist");
/**
* @import {
* ApiTargetProperties,
* EcmaTargetProperties,
* PlatformTargetProperties
* } from "./target"
*/
// [[C:]/path/to/config][:env]
const inputRx = /^(?:((?:[A-Z]:)?[/\\].*?))?(?::(.+?))?$/i;
// One resolution per `[input, context]` — several callers ask for the same
// target, and each miss re-reads the config and re-runs the queries. The list is
// handed out as it stands, so no caller may write to it.
/** @type {Map<string, string[] | undefined> | null} */
const cache = process.env.BROWSERSLIST_DISABLE_CACHE ? null : new Map();
/**
* 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) => {
if (cache === null) return loadBrowsers(input, context);
// A newline separates the two: an input carries a config path, an env or a
// query, and a directory carries none.
const key = `${input || ""}\n${context}`;
let browsers = cache.get(key);
if (browsers === undefined && !cache.has(key)) {
browsers = loadBrowsers(input, context);
cache.set(key, browsers);
}
return browsers;
};
/**
* Read the selection out of the browserslist config or query.
* @param {string | null | undefined} input input string
* @param {string} context the context directory
* @returns {string[] | undefined} selected browsers
*/
const loadBrowsers = (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} 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];
});
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
}),
/* 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
};