UNPKG

webpack

Version:

Packs CommonJs/AMD modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.

253 lines (245 loc) 7.66 kB
/* 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} ExpressionNode */ /** @typedef {import("estree").ObjectExpression} ObjectExpressionNode */ /** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */ /** @typedef {import("../ContextModule").ContextModuleOptions} ContextModuleOptions */ /** @typedef {import("../ChunkGroup").RawChunkGroupOptions} RawChunkGroupOptions */ /** @typedef {Pick<ContextModuleOptions, 'mode'|'recursive'|'regExp'|'include'|'exclude'|'chunkName'>&{groupOptions: RawChunkGroupOptions, exports?: ContextModuleOptions["referencedExports"]}} ImportMetaContextOptions */ 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 ); } function createError(msg, loc) { const error = new WebpackError(msg); error.name = "ImportMetaContextError"; error.loc = loc; return error; } module.exports = class ImportMetaContextDependencyParserPlugin { apply(parser) { parser.hooks.evaluateIdentifier .for("import.meta.webpackContext") .tap("HotModuleReplacementPlugin", 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(directoryNode); if (!requestExpr.isString()) return; const request = 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 optionsNode.properties) { if (prop.type !== "Property" || prop.key.type !== "Identifier") { errors.push( createError( "Parsing import.meta.webpackContext options failed.", optionsNode.loc ) ); break; } switch (prop.key.name) { case "regExp": { const regExpExpr = parser.evaluateExpression( /** @type {ExpressionNode} */ (prop.value) ); if (!regExpExpr.isRegExp()) { errors.push(createPropertyParseError(prop, "RegExp")); } else { regExp = regExpExpr.regExp; } break; } case "include": { const regExpExpr = parser.evaluateExpression( /** @type {ExpressionNode} */ (prop.value) ); if (!regExpExpr.isRegExp()) { errors.push(createPropertyParseError(prop, "RegExp")); } else { include = regExpExpr.regExp; } break; } case "exclude": { const regExpExpr = parser.evaluateExpression( /** @type {ExpressionNode} */ (prop.value) ); if (!regExpExpr.isRegExp()) { errors.push(createPropertyParseError(prop, "RegExp")); } else { exclude = regExpExpr.regExp; } break; } case "mode": { const modeExpr = parser.evaluateExpression( /** @type {ExpressionNode} */ (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 {ExpressionNode} */ (prop.value) ); if (!expr.isString()) { errors.push(createPropertyParseError(prop, "string")); } else { chunkName = expr.string; } break; } case "exports": { const expr = parser.evaluateExpression( /** @type {ExpressionNode} */ (prop.value) ); if (expr.isString()) { exports = [[expr.string]]; } else if (expr.isArray()) { const items = expr.items; if ( items.every(i => { if (!i.isArray()) return false; const innerItems = i.items; return innerItems.every(i => i.isString()); }) ) { exports = []; for (const i1 of items) { const export_ = []; for (const i2 of i1.items) { export_.push(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 {ExpressionNode} */ (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 {ExpressionNode} */ (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 "recursive": { const recursiveExpr = parser.evaluateExpression( /** @type {ExpressionNode} */ (prop.value) ); if (!recursiveExpr.isBoolean()) { errors.push(createPropertyParseError(prop, "boolean")); } else { recursive = recursiveExpr.bool; } break; } default: errors.push( createError( `Parsing import.meta.webpackContext options failed. Unknown property ${JSON.stringify( prop.key.name )}.`, 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" }, expr.range ); dep.loc = expr.loc; dep.optional = !!parser.scope.inTry; parser.state.current.addDependency(dep); return true; }); } };