/* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ "use strict"; const { getExternalModuleNodeCommonjsInitFragment } = require("./ExternalModule"); const { JAVASCRIPT_MODULE_TYPE_AUTO, JAVASCRIPT_MODULE_TYPE_DYNAMIC, JAVASCRIPT_MODULE_TYPE_ESM } = require("./ModuleTypeConstants"); const RuntimeGlobals = require("./RuntimeGlobals"); const WebpackError = require("./WebpackError"); const ConstDependency = require("./dependencies/ConstDependency"); const BasicEvaluatedExpression = require("./javascript/BasicEvaluatedExpression"); const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin"); const { evaluateToString, toConstantDependency } = require("./javascript/JavascriptParserHelpers"); const ChunkNameRuntimeModule = require("./runtime/ChunkNameRuntimeModule"); const GetFullHashRuntimeModule = require("./runtime/GetFullHashRuntimeModule"); /** @typedef {import("./Compiler")} Compiler */ /** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */ /** @typedef {import("./Module").BuildInfo} BuildInfo */ /** @typedef {import("./javascript/JavascriptParser")} JavascriptParser */ /** @typedef {import("./javascript/JavascriptParser").Range} Range */ /** * @returns {Record} replacements */ function getReplacements() { return { __webpack_require__: { expr: RuntimeGlobals.require, req: [RuntimeGlobals.require], type: "function", assign: false }, __webpack_public_path__: { expr: RuntimeGlobals.publicPath, req: [RuntimeGlobals.publicPath], type: "string", assign: true }, __webpack_base_uri__: { expr: RuntimeGlobals.baseURI, req: [RuntimeGlobals.baseURI], type: "string", assign: true }, __webpack_modules__: { expr: RuntimeGlobals.moduleFactories, req: [RuntimeGlobals.moduleFactories], type: "object", assign: false }, __webpack_chunk_load__: { expr: RuntimeGlobals.ensureChunk, req: [RuntimeGlobals.ensureChunk], type: "function", assign: true }, __non_webpack_require__: { expr: "require", req: null, type: undefined, // type is not known, depends on environment assign: true }, __webpack_nonce__: { expr: RuntimeGlobals.scriptNonce, req: [RuntimeGlobals.scriptNonce], type: "string", assign: true }, __webpack_hash__: { expr: `${RuntimeGlobals.getFullHash}()`, req: [RuntimeGlobals.getFullHash], type: "string", assign: false }, __webpack_chunkname__: { expr: RuntimeGlobals.chunkName, req: [RuntimeGlobals.chunkName], type: "string", assign: false }, __webpack_get_script_filename__: { expr: RuntimeGlobals.getChunkScriptFilename, req: [RuntimeGlobals.getChunkScriptFilename], type: "function", assign: true }, __webpack_runtime_id__: { expr: RuntimeGlobals.runtimeId, req: [RuntimeGlobals.runtimeId], assign: false }, "require.onError": { expr: RuntimeGlobals.uncaughtErrorHandler, req: [RuntimeGlobals.uncaughtErrorHandler], type: undefined, // type is not known, could be function or undefined assign: true // is never a pattern }, __system_context__: { expr: RuntimeGlobals.systemContext, req: [RuntimeGlobals.systemContext], type: "object", assign: false }, __webpack_share_scopes__: { expr: RuntimeGlobals.shareScopeMap, req: [RuntimeGlobals.shareScopeMap], type: "object", assign: false }, __webpack_init_sharing__: { expr: RuntimeGlobals.initializeSharing, req: [RuntimeGlobals.initializeSharing], type: "function", assign: true } }; } const PLUGIN_NAME = "APIPlugin"; class APIPlugin { /** * Apply the plugin * @param {Compiler} compiler the compiler instance * @returns {void} */ apply(compiler) { compiler.hooks.compilation.tap( PLUGIN_NAME, (compilation, { normalModuleFactory }) => { const moduleOutput = compilation.options.output.module; const nodeTarget = compiler.platform.node; const nodeEsm = moduleOutput && nodeTarget; const REPLACEMENTS = getReplacements(); if (nodeEsm) { REPLACEMENTS.__non_webpack_require__.expr = "__WEBPACK_EXTERNAL_createRequire_require"; } compilation.dependencyTemplates.set( ConstDependency, new ConstDependency.Template() ); compilation.hooks.runtimeRequirementInTree .for(RuntimeGlobals.chunkName) .tap(PLUGIN_NAME, (chunk) => { compilation.addRuntimeModule( chunk, new ChunkNameRuntimeModule(/** @type {string} */ (chunk.name)) ); return true; }); compilation.hooks.runtimeRequirementInTree .for(RuntimeGlobals.getFullHash) .tap(PLUGIN_NAME, (chunk, _set) => { compilation.addRuntimeModule(chunk, new GetFullHashRuntimeModule()); return true; }); const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation); hooks.renderModuleContent.tap( PLUGIN_NAME, (source, module, renderContext) => { if (/** @type {BuildInfo} */ (module.buildInfo).needCreateRequire) { const chunkInitFragments = [ getExternalModuleNodeCommonjsInitFragment( renderContext.runtimeTemplate ) ]; renderContext.chunkInitFragments.push(...chunkInitFragments); } return source; } ); /** * @param {JavascriptParser} parser the parser */ const handler = (parser) => { for (const key of Object.keys(REPLACEMENTS)) { const info = REPLACEMENTS[key]; parser.hooks.expression.for(key).tap(PLUGIN_NAME, (expression) => { const dep = toConstantDependency(parser, info.expr, info.req); if (key === "__non_webpack_require__" && moduleOutput) { if (nodeTarget) { /** @type {BuildInfo} */ (parser.state.module.buildInfo).needCreateRequire = true; } else { const warning = new WebpackError( `${PLUGIN_NAME}\n__non_webpack_require__ is only allowed in target node` ); warning.loc = /** @type {DependencyLocation} */ ( expression.loc ); warning.module = parser.state.module; compilation.warnings.push(warning); } } return dep(expression); }); if (info.assign === false) { parser.hooks.assign.for(key).tap(PLUGIN_NAME, (expr) => { const err = new WebpackError(`${key} must not be assigned`); err.loc = /** @type {DependencyLocation} */ (expr.loc); throw err; }); } if (info.type) { parser.hooks.evaluateTypeof .for(key) .tap(PLUGIN_NAME, evaluateToString(info.type)); } } parser.hooks.expression .for("__webpack_layer__") .tap(PLUGIN_NAME, (expr) => { const dep = new ConstDependency( JSON.stringify(parser.state.module.layer), /** @type {Range} */ (expr.range) ); dep.loc = /** @type {DependencyLocation} */ (expr.loc); parser.state.module.addPresentationalDependency(dep); return true; }); parser.hooks.evaluateIdentifier .for("__webpack_layer__") .tap(PLUGIN_NAME, (expr) => (parser.state.module.layer === null ? new BasicEvaluatedExpression().setNull() : new BasicEvaluatedExpression().setString( parser.state.module.layer ) ).setRange(/** @type {Range} */ (expr.range)) ); parser.hooks.evaluateTypeof .for("__webpack_layer__") .tap(PLUGIN_NAME, (expr) => new BasicEvaluatedExpression() .setString( parser.state.module.layer === null ? "object" : "string" ) .setRange(/** @type {Range} */ (expr.range)) ); parser.hooks.expression .for("__webpack_module__.id") .tap(PLUGIN_NAME, (expr) => { /** @type {BuildInfo} */ (parser.state.module.buildInfo).moduleConcatenationBailout = "__webpack_module__.id"; const dep = new ConstDependency( `${parser.state.module.moduleArgument}.id`, /** @type {Range} */ (expr.range), [RuntimeGlobals.moduleId] ); dep.loc = /** @type {DependencyLocation} */ (expr.loc); parser.state.module.addPresentationalDependency(dep); return true; }); parser.hooks.expression .for("__webpack_module__") .tap(PLUGIN_NAME, (expr) => { /** @type {BuildInfo} */ (parser.state.module.buildInfo).moduleConcatenationBailout = "__webpack_module__"; const dep = new ConstDependency( parser.state.module.moduleArgument, /** @type {Range} */ (expr.range), [RuntimeGlobals.module] ); dep.loc = /** @type {DependencyLocation} */ (expr.loc); parser.state.module.addPresentationalDependency(dep); return true; }); parser.hooks.evaluateTypeof .for("__webpack_module__") .tap(PLUGIN_NAME, evaluateToString("object")); }; normalModuleFactory.hooks.parser .for(JAVASCRIPT_MODULE_TYPE_AUTO) .tap(PLUGIN_NAME, handler); normalModuleFactory.hooks.parser .for(JAVASCRIPT_MODULE_TYPE_DYNAMIC) .tap(PLUGIN_NAME, handler); normalModuleFactory.hooks.parser .for(JAVASCRIPT_MODULE_TYPE_ESM) .tap(PLUGIN_NAME, handler); } ); } } module.exports = APIPlugin;