package.lib.dependencies.ImportMetaContextDependencyParserPlugin.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of webpack Show documentation
Show all versions of webpack Show documentation
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.
The newest version!
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Ivan Kopeykin @vankop
*/
"use strict";
const WebpackError = require("../WebpackError");
const {
evaluateToIdentifier
} = require("../javascript/JavascriptParserHelpers");
const ImportMetaContextDependency = require("./ImportMetaContextDependency");
/** @typedef {import("estree").Expression} Expression */
/** @typedef {import("estree").ObjectExpression} ObjectExpression */
/** @typedef {import("estree").Property} Property */
/** @typedef {import("estree").SourceLocation} SourceLocation */
/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
/** @typedef {import("../javascript/JavascriptParser").Range} Range */
/** @typedef {import("../ContextModule").ContextModuleOptions} ContextModuleOptions */
/** @typedef {import("../ChunkGroup").RawChunkGroupOptions} RawChunkGroupOptions */
/** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
/** @typedef {import("../javascript/BasicEvaluatedExpression")} BasicEvaluatedExpression */
/** @typedef {Pick&{groupOptions: RawChunkGroupOptions, exports?: ContextModuleOptions["referencedExports"]}} ImportMetaContextOptions */
/**
* @param {TODO} prop property
* @param {string} expect except message
* @returns {WebpackError} error
*/
function createPropertyParseError(prop, expect) {
return createError(
`Parsing import.meta.webpackContext options failed. Unknown value for property ${JSON.stringify(
prop.key.name
)}, expected type ${expect}.`,
prop.value.loc
);
}
/**
* @param {string} msg message
* @param {DependencyLocation} loc location
* @returns {WebpackError} error
*/
function createError(msg, loc) {
const error = new WebpackError(msg);
error.name = "ImportMetaContextError";
error.loc = loc;
return error;
}
module.exports = class ImportMetaContextDependencyParserPlugin {
/**
* @param {JavascriptParser} parser the parser
* @returns {void}
*/
apply(parser) {
parser.hooks.evaluateIdentifier
.for("import.meta.webpackContext")
.tap("ImportMetaContextDependencyParserPlugin", expr => {
return evaluateToIdentifier(
"import.meta.webpackContext",
"import.meta",
() => ["webpackContext"],
true
)(expr);
});
parser.hooks.call
.for("import.meta.webpackContext")
.tap("ImportMetaContextDependencyParserPlugin", expr => {
if (expr.arguments.length < 1 || expr.arguments.length > 2) return;
const [directoryNode, optionsNode] = expr.arguments;
if (optionsNode && optionsNode.type !== "ObjectExpression") return;
const requestExpr = parser.evaluateExpression(
/** @type {Expression} */ (directoryNode)
);
if (!requestExpr.isString()) return;
const request = /** @type {string} */ (requestExpr.string);
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) {
for (const prop of /** @type {ObjectExpression} */ (optionsNode)
.properties) {
if (prop.type !== "Property" || prop.key.type !== "Identifier") {
errors.push(
createError(
"Parsing import.meta.webpackContext options failed.",
/** @type {DependencyLocation} */ (optionsNode.loc)
)
);
break;
}
switch (prop.key.name) {
case "regExp": {
const regExpExpr = parser.evaluateExpression(
/** @type {Expression} */ (prop.value)
);
if (!regExpExpr.isRegExp()) {
errors.push(createPropertyParseError(prop, "RegExp"));
} else {
regExp = /** @type {RegExp} */ (regExpExpr.regExp);
}
break;
}
case "include": {
const regExpExpr = parser.evaluateExpression(
/** @type {Expression} */ (prop.value)
);
if (!regExpExpr.isRegExp()) {
errors.push(createPropertyParseError(prop, "RegExp"));
} else {
include = regExpExpr.regExp;
}
break;
}
case "exclude": {
const regExpExpr = parser.evaluateExpression(
/** @type {Expression} */ (prop.value)
);
if (!regExpExpr.isRegExp()) {
errors.push(createPropertyParseError(prop, "RegExp"));
} else {
exclude = regExpExpr.regExp;
}
break;
}
case "mode": {
const modeExpr = parser.evaluateExpression(
/** @type {Expression} */ (prop.value)
);
if (!modeExpr.isString()) {
errors.push(createPropertyParseError(prop, "string"));
} else {
mode = /** @type {ContextModuleOptions["mode"]} */ (
modeExpr.string
);
}
break;
}
case "chunkName": {
const expr = parser.evaluateExpression(
/** @type {Expression} */ (prop.value)
);
if (!expr.isString()) {
errors.push(createPropertyParseError(prop, "string"));
} else {
chunkName = expr.string;
}
break;
}
case "exports": {
const expr = parser.evaluateExpression(
/** @type {Expression} */ (prop.value)
);
if (expr.isString()) {
exports = [[/** @type {string} */ (expr.string)]];
} else if (expr.isArray()) {
const items =
/** @type {BasicEvaluatedExpression[]} */
(expr.items);
if (
items.every(i => {
if (!i.isArray()) return false;
const innerItems =
/** @type {BasicEvaluatedExpression[]} */ (i.items);
return innerItems.every(i => i.isString());
})
) {
exports = [];
for (const i1 of items) {
/** @type {string[]} */
const export_ = [];
for (const i2 of /** @type {BasicEvaluatedExpression[]} */ (
i1.items
)) {
export_.push(/** @type {string} */ (i2.string));
}
exports.push(export_);
}
} else {
errors.push(
createPropertyParseError(prop, "string|string[][]")
);
}
} else {
errors.push(
createPropertyParseError(prop, "string|string[][]")
);
}
break;
}
case "prefetch": {
const expr = parser.evaluateExpression(
/** @type {Expression} */ (prop.value)
);
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(
/** @type {Expression} */ (prop.value)
);
if (expr.isBoolean()) {
groupOptions.preloadOrder = 0;
} else if (expr.isNumber()) {
groupOptions.preloadOrder = expr.number;
} else {
errors.push(createPropertyParseError(prop, "boolean|number"));
}
break;
}
case "fetchPriority": {
const expr = parser.evaluateExpression(
/** @type {Expression} */ (prop.value)
);
if (
expr.isString() &&
["high", "low", "auto"].includes(
/** @type {string} */ (expr.string)
)
) {
groupOptions.fetchPriority =
/** @type {RawChunkGroupOptions["fetchPriority"]} */ (
expr.string
);
} else {
errors.push(
createPropertyParseError(prop, '"high"|"low"|"auto"')
);
}
break;
}
case "recursive": {
const recursiveExpr = parser.evaluateExpression(
/** @type {Expression} */ (prop.value)
);
if (!recursiveExpr.isBoolean()) {
errors.push(createPropertyParseError(prop, "boolean"));
} else {
recursive = /** @type {boolean} */ (recursiveExpr.bool);
}
break;
}
default:
errors.push(
createError(
`Parsing import.meta.webpackContext options failed. Unknown property ${JSON.stringify(
prop.key.name
)}.`,
/** @type {DependencyLocation} */ (optionsNode.loc)
)
);
}
}
}
if (errors.length) {
for (const error of errors) parser.state.current.addError(error);
return;
}
const dep = new ImportMetaContextDependency(
{
request,
include,
exclude,
recursive,
regExp,
groupOptions,
chunkName,
referencedExports: exports,
mode,
category: "esm"
},
/** @type {Range} */ (expr.range)
);
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
});
}
};