123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- /*
- 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<ContextModuleOptions, 'mode'|'recursive'|'regExp'|'include'|'exclude'|'chunkName'>&{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;
- });
- }
- };
|