Skip to content

Commit 7d608ff

Browse files
fix(library): don't report modules a concatenated entry absorbs as copies
Also tests the eval bailout after a write and runs the top-level await case from Node.js 14.8.
1 parent 70d17c0 commit 7d608ff

5 files changed

Lines changed: 46 additions & 6 deletions

File tree

‎lib/library/ModuleLibraryPlugin.js‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,14 +143,21 @@ const getModuleLocalExports = (module, cache) => {
143143
/**
144144
* Why a module's exported module-level bindings cannot move to the chunk's scope.
145145
* @param {Module} module the module declaring them
146+
* @param {Module} entry the entry module exporting them
146147
* @param {ESMLiveExportDependency} declaration one of its declarations
147148
* @param {Chunk} chunk the library's chunk
148149
* @param {ChunkGraph} chunkGraph the chunk graph
149150
* @returns {string | null} the reason, empty where they are live already, null where they can move
150151
*/
151-
const getHoistRefusal = (module, declaration, chunk, chunkGraph) => {
152+
const getHoistRefusal = (module, entry, declaration, chunk, chunkGraph) => {
152153
// A concatenation puts its modules' bindings in one scope already.
153-
if (module.is("concatenated")) return "";
154+
if (
155+
module.is("concatenated") ||
156+
(entry !== module &&
157+
someInIterable(entry.getSourceModules(), (source) => source === module))
158+
) {
159+
return "";
160+
}
154161
if (declaration.readByEval) {
155162
return "the module calls eval(), which reads a binding by the name it was declared with";
156163
}
@@ -323,6 +330,7 @@ class ModuleLibraryPlugin extends AbstractLibraryPlugin {
323330
if (refusal === undefined) {
324331
refusal = getHoistRefusal(
325332
module,
333+
entry,
326334
declaration,
327335
chunk,
328336
chunkGraph

‎test/configCases/library/module-live-bindings-bailouts/index.js‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,7 @@ it("should keep exporting what a module in another chunk holds", () => {
99
it("should leave a module reading its bindings through eval as it is", () => {
1010
expect(library.readEvaluated()).toBe(1);
1111
expect(library.evaluated).toBe(1);
12+
library.setEvaluated(2);
13+
expect(library.readEvaluated()).toBe(2);
14+
expect(library.evaluated).toBe(1);
1215
});
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
export { shared, bumpShared } from "./shared.js";
2-
export { evaluated, readEvaluated } from "./evaluated.js";
2+
export { evaluated, readEvaluated, setEvaluated } from "./evaluated.js";

‎test/configCases/library/module-live-bindings-hoisted/webpack.config.js‎

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,32 @@ module.exports = [
3434
new webpack.DefinePlugin({
3535
GLOBAL_COUNTER: "counter",
3636
MINIFIED: JSON.stringify(Boolean(optimization.minimize))
37-
})
37+
}),
38+
/**
39+
* Fails the build when a module whose bindings are live reports them as copies.
40+
* @this {import("../../../../").Compiler} the compiler
41+
*/
42+
function noCopyBailouts() {
43+
this.hooks.compilation.tap("test", (compilation) => {
44+
compilation.hooks.afterSeal.tap("test", () => {
45+
for (const module of compilation.modules) {
46+
for (const source of module.getSourceModules()) {
47+
for (const reason of compilation.moduleGraph.getOptimizationBailout(
48+
source
49+
)) {
50+
if (typeof reason === "string" && reason.includes("copies")) {
51+
compilation.errors.push(
52+
new webpack.WebpackError(
53+
`${source.identifier()}: ${reason}`
54+
)
55+
);
56+
}
57+
}
58+
}
59+
}
60+
});
61+
});
62+
}
3863
],
3964
optimization
4065
}));
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
"use strict";
22

3-
// Native top-level await is required when the harness imports the library.
4-
module.exports = () => Number(process.versions.node.split(".")[0]) >= 14;
3+
// The generated library uses top-level await, supported by Node.js 14.8 onward.
4+
/** @returns {boolean} whether the runtime can execute the library */
5+
module.exports = function filter() {
6+
const [major, minor] = process.versions.node.split(".").map(Number);
7+
return major > 14 || (major === 14 && minor >= 8);
8+
};

0 commit comments

Comments
 (0)