Skip to content

Commit d2a124d

Browse files
authored
fix: avoid module variable conflict in __webpack_module__ API (#20265)
1 parent f21d135 commit d2a124d

File tree

9 files changed

+221
-14
lines changed

9 files changed

+221
-14
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"webpack": patch
3+
---
4+
5+
Fixed a bug where declaring a `module` variable in module scope would conflict with the default `moduleArgument`.

lib/APIPlugin.js

Lines changed: 84 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ const {
1616
const RuntimeGlobals = require("./RuntimeGlobals");
1717
const WebpackError = require("./WebpackError");
1818
const ConstDependency = require("./dependencies/ConstDependency");
19+
const ModuleInitFragmentDependency = require("./dependencies/ModuleInitFragmentDependency");
20+
const RuntimeRequirementsDependency = require("./dependencies/RuntimeRequirementsDependency");
1921
const BasicEvaluatedExpression = require("./javascript/BasicEvaluatedExpression");
2022
const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin");
2123
const {
@@ -160,6 +162,10 @@ class APIPlugin {
160162
ConstDependency,
161163
new ConstDependency.Template()
162164
);
165+
compilation.dependencyTemplates.set(
166+
ModuleInitFragmentDependency,
167+
new ModuleInitFragmentDependency.Template()
168+
);
163169

164170
compilation.hooks.runtimeRequirementInTree
165171
.for(RuntimeGlobals.chunkName)
@@ -201,6 +207,40 @@ class APIPlugin {
201207
* @param {JavascriptParser} parser the parser
202208
*/
203209
const handler = (parser) => {
210+
parser.hooks.preDeclarator.tap(PLUGIN_NAME, (declarator) => {
211+
if (
212+
parser.scope.topLevelScope === true &&
213+
declarator.id.type === "Identifier" &&
214+
declarator.id.name === "module"
215+
) {
216+
/** @type {BuildInfo} */
217+
(parser.state.module.buildInfo).moduleArgument =
218+
"__webpack_module__";
219+
}
220+
});
221+
222+
parser.hooks.preStatement.tap(PLUGIN_NAME, (statement) => {
223+
if (parser.scope.topLevelScope === true) {
224+
if (
225+
statement.type === "FunctionDeclaration" &&
226+
statement.id &&
227+
statement.id.name === "module"
228+
) {
229+
/** @type {BuildInfo} */
230+
(parser.state.module.buildInfo).moduleArgument =
231+
"__webpack_module__";
232+
} else if (
233+
statement.type === "ClassDeclaration" &&
234+
statement.id &&
235+
statement.id.name === "module"
236+
) {
237+
/** @type {BuildInfo} */
238+
(parser.state.module.buildInfo).moduleArgument =
239+
"__webpack_module__";
240+
}
241+
}
242+
});
243+
204244
for (const key of Object.keys(REPLACEMENTS)) {
205245
const info = REPLACEMENTS[key];
206246
parser.hooks.expression.for(key).tap(PLUGIN_NAME, (expression) => {
@@ -275,13 +315,28 @@ class APIPlugin {
275315
/** @type {BuildInfo} */
276316
(parser.state.module.buildInfo).moduleConcatenationBailout =
277317
"__webpack_module__.id";
278-
const dep = new ConstDependency(
279-
`${parser.state.module.moduleArgument}.id`,
280-
/** @type {Range} */ (expr.range),
281-
[RuntimeGlobals.moduleId]
282-
);
283-
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
284-
parser.state.module.addPresentationalDependency(dep);
318+
const moduleArgument = parser.state.module.moduleArgument;
319+
if (moduleArgument === "__webpack_module__") {
320+
const dep = new RuntimeRequirementsDependency([
321+
RuntimeGlobals.moduleId
322+
]);
323+
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
324+
parser.state.module.addPresentationalDependency(dep);
325+
} else {
326+
const initDep = new ModuleInitFragmentDependency(
327+
`var __webpack_internal_module_id__ = ${moduleArgument}.id;\n`,
328+
[RuntimeGlobals.moduleId],
329+
"__webpack_internal_module_id__"
330+
);
331+
parser.state.module.addPresentationalDependency(initDep);
332+
const dep = new ConstDependency(
333+
"__webpack_internal_module_id__",
334+
/** @type {Range} */ (expr.range),
335+
[]
336+
);
337+
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
338+
parser.state.module.addPresentationalDependency(dep);
339+
}
285340
return true;
286341
});
287342

@@ -291,13 +346,28 @@ class APIPlugin {
291346
/** @type {BuildInfo} */
292347
(parser.state.module.buildInfo).moduleConcatenationBailout =
293348
"__webpack_module__";
294-
const dep = new ConstDependency(
295-
parser.state.module.moduleArgument,
296-
/** @type {Range} */ (expr.range),
297-
[RuntimeGlobals.module]
298-
);
299-
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
300-
parser.state.module.addPresentationalDependency(dep);
349+
const moduleArgument = parser.state.module.moduleArgument;
350+
if (moduleArgument === "__webpack_module__") {
351+
const dep = new RuntimeRequirementsDependency([
352+
RuntimeGlobals.module
353+
]);
354+
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
355+
parser.state.module.addPresentationalDependency(dep);
356+
} else {
357+
const initDep = new ModuleInitFragmentDependency(
358+
`var __webpack_internal_module__ = ${moduleArgument};\n`,
359+
[RuntimeGlobals.module],
360+
"__webpack_internal_module__"
361+
);
362+
parser.state.module.addPresentationalDependency(initDep);
363+
const dep = new ConstDependency(
364+
"__webpack_internal_module__",
365+
/** @type {Range} */ (expr.range),
366+
[]
367+
);
368+
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
369+
parser.state.module.addPresentationalDependency(dep);
370+
}
301371
return true;
302372
});
303373
parser.hooks.evaluateTypeof
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
MIT License http://www.opensource.org/licenses/mit-license.php
3+
Author Natsu @xiaoxiaojx
4+
*/
5+
6+
"use strict";
7+
8+
const InitFragment = require("../InitFragment");
9+
const makeSerializable = require("../util/makeSerializable");
10+
const NullDependency = require("./NullDependency");
11+
12+
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
13+
/** @typedef {import("../Dependency")} Dependency */
14+
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
15+
/** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */
16+
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
17+
/**
18+
* A dependency that adds an init fragment to the module
19+
*/
20+
class ModuleInitFragmentDependency extends NullDependency {
21+
/**
22+
* @param {string} initCode the initialization code
23+
* @param {string[]} runtimeRequirements runtime requirements
24+
* @param {string=} key unique key to avoid emitting the same initialization code twice
25+
*/
26+
constructor(initCode, runtimeRequirements, key) {
27+
super();
28+
this.initCode = initCode;
29+
this.runtimeRequirements = runtimeRequirements;
30+
this.key = key;
31+
}
32+
33+
/**
34+
* @param {ObjectSerializerContext} context context
35+
*/
36+
serialize(context) {
37+
const { write } = context;
38+
write(this.initCode);
39+
write(this.runtimeRequirements);
40+
write(this.key);
41+
super.serialize(context);
42+
}
43+
44+
/**
45+
* @param {ObjectDeserializerContext} context context
46+
*/
47+
deserialize(context) {
48+
const { read } = context;
49+
this.initCode = read();
50+
this.runtimeRequirements = read();
51+
this.key = read();
52+
super.deserialize(context);
53+
}
54+
}
55+
56+
makeSerializable(
57+
ModuleInitFragmentDependency,
58+
"webpack/lib/dependencies/ModuleInitFragmentDependency"
59+
);
60+
61+
ModuleInitFragmentDependency.Template = class ModuleInitFragmentDependencyTemplate extends (
62+
NullDependency.Template
63+
) {
64+
/**
65+
* @param {Dependency} dependency the dependency for which the template should be applied
66+
* @param {ReplaceSource} source the current replace source which can be modified
67+
* @param {DependencyTemplateContext} templateContext the context object
68+
* @returns {void}
69+
*/
70+
apply(dependency, source, { initFragments, runtimeRequirements }) {
71+
const dep = /** @type {ModuleInitFragmentDependency} */ (dependency);
72+
for (const req of dep.runtimeRequirements) {
73+
runtimeRequirements.add(req);
74+
}
75+
initFragments.push(
76+
new InitFragment(
77+
dep.initCode,
78+
InitFragment.STAGE_CONSTANTS,
79+
0,
80+
dep.key,
81+
undefined
82+
)
83+
);
84+
}
85+
};
86+
87+
module.exports = ModuleInitFragmentDependency;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class module {
2+
}
3+
4+
module.id = __webpack_module__.id;
5+
6+
export default module;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function module() {
2+
return __webpack_module__.id;
3+
}
4+
5+
export default module;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import moduleFn from "./fn";
2+
import moduleClass from "./class";
3+
4+
const module = "layout";
5+
if (module === __webpack_module__) {
6+
throw new Error("module is __webpack_module__, this should not happen");
7+
}
8+
9+
it("should get module id from __webpack_module__", () => {
10+
expect(__webpack_module__.id).toBeDefined();
11+
const module = "layout";
12+
expect(module).toBe("layout");
13+
expect(__webpack_module__.id).toBeDefined();
14+
expect(moduleFn()).toBeDefined();
15+
expect(moduleClass.id).toBeDefined();
16+
expect(typeof __webpack_module__).toBe("object");
17+
});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"use strict";
2+
3+
/** @type {import("../../../../").Configuration} */
4+
module.exports = {};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module.exports = "index";
2+
3+
it("should get module id from __webpack_module__", () => {
4+
expect(__webpack_module__.id).toBeDefined();
5+
const module = "layout";
6+
expect(module).toBe("layout");
7+
expect(__webpack_module__.id).toBeDefined();
8+
expect(typeof __webpack_module__).toBe("object");
9+
});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"use strict";
2+
3+
/** @type {import("../../../../").Configuration} */
4+
module.exports = {};

0 commit comments

Comments
 (0)