webpack/lib/dependencies/ImportMetaContextDependency...

308 lines
9.3 KiB
JavaScript
Raw Normal View History

2022-02-27 04:16:21 +08:00
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Ivan Kopeykin @vankop
*/
"use strict";
2022-02-28 20:09:16 +08:00
const WebpackError = require("../WebpackError");
2022-02-27 04:16:21 +08:00
const {
evaluateToIdentifier
} = require("../javascript/JavascriptParserHelpers");
const ImportMetaContextDependency = require("./ImportMetaContextDependency");
2023-05-22 06:28:57 +08:00
/** @typedef {import("estree").Expression} Expression */
/** @typedef {import("estree").ObjectExpression} ObjectExpression */
/** @typedef {import("estree").Property} Property */
2025-04-07 21:09:05 +08:00
/** @typedef {import("estree").Identifier} Identifier */
2022-02-27 04:16:21 +08:00
/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
2024-03-18 23:28:40 +08:00
/** @typedef {import("../javascript/JavascriptParser").Range} Range */
2022-02-28 20:09:16 +08:00
/** @typedef {import("../ContextModule").ContextModuleOptions} ContextModuleOptions */
/** @typedef {import("../ChunkGroup").RawChunkGroupOptions} RawChunkGroupOptions */
2024-03-18 23:28:40 +08:00
/** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
/** @typedef {import("../javascript/BasicEvaluatedExpression")} BasicEvaluatedExpression */
2022-02-28 20:09:16 +08:00
/** @typedef {Pick<ContextModuleOptions, 'mode'|'recursive'|'regExp'|'include'|'exclude'|'chunkName'>&{groupOptions: RawChunkGroupOptions, exports?: ContextModuleOptions["referencedExports"]}} ImportMetaContextOptions */
2022-02-27 04:16:21 +08:00
2023-05-22 06:28:57 +08:00
/**
2025-04-07 21:09:05 +08:00
* @param {Property} prop property
2023-05-22 06:28:57 +08:00
* @param {string} expect except message
* @returns {WebpackError} error
*/
2022-02-28 20:09:16 +08:00
function createPropertyParseError(prop, expect) {
return createError(
`Parsing import.meta.webpackContext options failed. Unknown value for property ${JSON.stringify(
2025-04-07 21:09:05 +08:00
/** @type {Identifier} */
(prop.key).name
2022-02-28 20:09:16 +08:00
)}, expected type ${expect}.`,
2025-04-07 21:09:05 +08:00
/** @type {DependencyLocation} */
(prop.value.loc)
2022-02-28 20:09:16 +08:00
);
}
2022-02-27 04:16:21 +08:00
2023-05-22 06:28:57 +08:00
/**
* @param {string} msg message
2024-03-18 23:28:40 +08:00
* @param {DependencyLocation} loc location
2023-05-22 06:28:57 +08:00
* @returns {WebpackError} error
*/
2022-02-28 20:09:16 +08:00
function createError(msg, loc) {
const error = new WebpackError(msg);
error.name = "ImportMetaContextError";
error.loc = loc;
return error;
2022-02-27 04:16:21 +08:00
}
2025-04-23 20:03:37 +08:00
const PLUGIN_NAME = "ImportMetaContextDependencyParserPlugin";
2022-02-27 04:16:21 +08:00
module.exports = class ImportMetaContextDependencyParserPlugin {
2023-05-22 06:28:57 +08:00
/**
* @param {JavascriptParser} parser the parser
* @returns {void}
*/
2022-02-27 04:16:21 +08:00
apply(parser) {
parser.hooks.evaluateIdentifier
.for("import.meta.webpackContext")
.tap(PLUGIN_NAME, (expr) =>
2024-07-31 11:31:11 +08:00
evaluateToIdentifier(
2022-02-27 04:16:21 +08:00
"import.meta.webpackContext",
"import.meta",
() => ["webpackContext"],
true
2024-07-31 11:31:11 +08:00
)(expr)
);
2022-02-27 04:16:21 +08:00
parser.hooks.call
.for("import.meta.webpackContext")
.tap(PLUGIN_NAME, (expr) => {
2022-02-27 04:16:21 +08:00
if (expr.arguments.length < 1 || expr.arguments.length > 2) return;
const [directoryNode, optionsNode] = expr.arguments;
if (optionsNode && optionsNode.type !== "ObjectExpression") return;
2023-05-22 06:28:57 +08:00
const requestExpr = parser.evaluateExpression(
/** @type {Expression} */ (directoryNode)
);
2022-02-27 04:16:21 +08:00
if (!requestExpr.isString()) return;
2024-03-18 23:28:40 +08:00
const request = /** @type {string} */ (requestExpr.string);
2022-02-28 20:09:16 +08:00
const errors = [];
let regExp = /^\.\/.*$/;
let recursive = true;
/** @type {ContextModuleOptions["mode"]} */
let mode = "sync";
/** @type {ContextModuleOptions["include"]} */
let include;
/** @type {ContextModuleOptions["exclude"]} */
let exclude;
/** @type {RawChunkGroupOptions} */
const groupOptions = {};
/** @type {ContextModuleOptions["chunkName"]} */
let chunkName;
/** @type {ContextModuleOptions["referencedExports"]} */
let exports;
if (optionsNode) {
2023-05-22 06:28:57 +08:00
for (const prop of /** @type {ObjectExpression} */ (optionsNode)
.properties) {
2022-02-28 20:09:16 +08:00
if (prop.type !== "Property" || prop.key.type !== "Identifier") {
errors.push(
createError(
"Parsing import.meta.webpackContext options failed.",
2025-04-07 21:09:05 +08:00
/** @type {DependencyLocation} */
(optionsNode.loc)
2022-02-28 20:09:16 +08:00
)
);
break;
}
switch (prop.key.name) {
case "regExp": {
const regExpExpr = parser.evaluateExpression(
2023-05-22 06:28:57 +08:00
/** @type {Expression} */ (prop.value)
2022-02-28 20:09:16 +08:00
);
if (!regExpExpr.isRegExp()) {
errors.push(createPropertyParseError(prop, "RegExp"));
} else {
2024-03-18 23:28:40 +08:00
regExp = /** @type {RegExp} */ (regExpExpr.regExp);
2022-02-28 20:09:16 +08:00
}
break;
}
case "include": {
const regExpExpr = parser.evaluateExpression(
2023-05-22 06:28:57 +08:00
/** @type {Expression} */ (prop.value)
2022-02-28 20:09:16 +08:00
);
if (!regExpExpr.isRegExp()) {
errors.push(createPropertyParseError(prop, "RegExp"));
} else {
include = regExpExpr.regExp;
}
break;
}
case "exclude": {
const regExpExpr = parser.evaluateExpression(
2023-05-22 06:28:57 +08:00
/** @type {Expression} */ (prop.value)
2022-02-28 20:09:16 +08:00
);
if (!regExpExpr.isRegExp()) {
errors.push(createPropertyParseError(prop, "RegExp"));
} else {
exclude = regExpExpr.regExp;
}
break;
}
case "mode": {
const modeExpr = parser.evaluateExpression(
2023-05-22 06:28:57 +08:00
/** @type {Expression} */ (prop.value)
2022-02-28 20:09:16 +08:00
);
if (!modeExpr.isString()) {
errors.push(createPropertyParseError(prop, "string"));
} else {
mode = /** @type {ContextModuleOptions["mode"]} */ (
modeExpr.string
);
}
break;
}
case "chunkName": {
const expr = parser.evaluateExpression(
2023-05-22 06:28:57 +08:00
/** @type {Expression} */ (prop.value)
2022-02-28 20:09:16 +08:00
);
if (!expr.isString()) {
errors.push(createPropertyParseError(prop, "string"));
} else {
chunkName = expr.string;
}
break;
}
case "exports": {
const expr = parser.evaluateExpression(
2023-05-22 06:28:57 +08:00
/** @type {Expression} */ (prop.value)
2022-02-28 20:09:16 +08:00
);
if (expr.isString()) {
2024-03-18 23:28:40 +08:00
exports = [[/** @type {string} */ (expr.string)]];
2022-02-28 20:09:16 +08:00
} else if (expr.isArray()) {
2024-03-18 23:28:40 +08:00
const items =
/** @type {BasicEvaluatedExpression[]} */
(expr.items);
2022-02-28 20:09:16 +08:00
if (
items.every((i) => {
2022-02-28 20:09:16 +08:00
if (!i.isArray()) return false;
2024-03-18 23:28:40 +08:00
const innerItems =
/** @type {BasicEvaluatedExpression[]} */ (i.items);
return innerItems.every((i) => i.isString());
2022-02-28 20:09:16 +08:00
})
) {
exports = [];
2022-02-28 20:09:16 +08:00
for (const i1 of items) {
2024-03-18 23:28:40 +08:00
/** @type {string[]} */
2022-02-28 20:09:16 +08:00
const export_ = [];
2024-03-18 23:28:40 +08:00
for (const i2 of /** @type {BasicEvaluatedExpression[]} */ (
i1.items
)) {
export_.push(/** @type {string} */ (i2.string));
2022-02-28 20:09:16 +08:00
}
exports.push(export_);
}
} else {
errors.push(
createPropertyParseError(prop, "string|string[][]")
);
}
} else {
errors.push(
createPropertyParseError(prop, "string|string[][]")
);
}
break;
}
case "prefetch": {
const expr = parser.evaluateExpression(
2023-05-22 06:28:57 +08:00
/** @type {Expression} */ (prop.value)
2022-02-28 20:09:16 +08:00
);
if (expr.isBoolean()) {
groupOptions.prefetchOrder = 0;
} else if (expr.isNumber()) {
groupOptions.prefetchOrder = expr.number;
} else {
errors.push(createPropertyParseError(prop, "boolean|number"));
}
break;
}
case "preload": {
const expr = parser.evaluateExpression(
2023-05-22 06:28:57 +08:00
/** @type {Expression} */ (prop.value)
2022-02-28 20:09:16 +08:00
);
if (expr.isBoolean()) {
groupOptions.preloadOrder = 0;
} else if (expr.isNumber()) {
groupOptions.preloadOrder = expr.number;
} else {
errors.push(createPropertyParseError(prop, "boolean|number"));
}
break;
}
2023-04-23 05:24:20 +08:00
case "fetchPriority": {
const expr = parser.evaluateExpression(
2023-05-24 08:31:35 +08:00
/** @type {Expression} */ (prop.value)
2023-04-23 05:24:20 +08:00
);
2023-05-24 09:13:01 +08:00
if (
2023-04-23 05:24:20 +08:00
expr.isString() &&
2024-03-18 23:28:40 +08:00
["high", "low", "auto"].includes(
/** @type {string} */ (expr.string)
)
2023-04-23 05:24:20 +08:00
) {
2023-05-24 08:31:35 +08:00
groupOptions.fetchPriority =
/** @type {RawChunkGroupOptions["fetchPriority"]} */ (
expr.string
);
2023-04-23 05:24:20 +08:00
} else {
errors.push(
2023-05-24 09:13:01 +08:00
createPropertyParseError(prop, '"high"|"low"|"auto"')
2023-04-23 05:24:20 +08:00
);
}
break;
}
2022-02-28 20:09:16 +08:00
case "recursive": {
const recursiveExpr = parser.evaluateExpression(
2023-05-22 06:28:57 +08:00
/** @type {Expression} */ (prop.value)
2022-02-28 20:09:16 +08:00
);
if (!recursiveExpr.isBoolean()) {
errors.push(createPropertyParseError(prop, "boolean"));
} else {
2024-03-18 23:28:40 +08:00
recursive = /** @type {boolean} */ (recursiveExpr.bool);
2022-02-28 20:09:16 +08:00
}
break;
}
default:
errors.push(
createError(
`Parsing import.meta.webpackContext options failed. Unknown property ${JSON.stringify(
prop.key.name
)}.`,
2024-03-18 23:28:40 +08:00
/** @type {DependencyLocation} */ (optionsNode.loc)
2022-02-28 20:09:16 +08:00
)
);
}
}
}
if (errors.length) {
for (const error of errors) parser.state.current.addError(error);
return;
}
2022-02-27 04:16:21 +08:00
const dep = new ImportMetaContextDependency(
{
request,
2022-02-28 20:09:16 +08:00
include,
exclude,
recursive,
regExp,
groupOptions,
chunkName,
referencedExports: exports,
mode,
2022-02-27 04:16:21 +08:00
category: "esm"
},
2024-03-18 23:28:40 +08:00
/** @type {Range} */ (expr.range)
2022-02-27 04:16:21 +08:00
);
2024-03-18 23:28:40 +08:00
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
2024-07-31 11:11:11 +08:00
dep.optional = Boolean(parser.scope.inTry);
2022-02-27 04:16:21 +08:00
parser.state.current.addDependency(dep);
return true;
});
}
};