Skip to content

Commit 99286e2

Browse files
fix: match harmony accept dependencies by module reference (#21303)
The HMR accept template related dependencies via chunkGraph.getModuleId. An unresolved import (ignored/failed, or a deferred lazy-barrel re-export) has no module, and a module not in any chunk has a null id, so comparing ids could crash ("Invalid value used as weak map key") or miss real matches. Compare modules by reference and skip dependencies without a module instead.
1 parent 0abec4d commit 99286e2

6 files changed

Lines changed: 46 additions & 15 deletions

File tree

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+
Match harmony accept dependencies by module reference so HMR codegen handles imports without a module or chunk id.

lib/dependencies/HarmonyAcceptDependency.js

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ const NullDependency = require("./NullDependency");
1919
/** @typedef {import("./HarmonyAcceptImportDependency")} HarmonyAcceptImportDependency */
2020
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext<[Range, HarmonyAcceptImportDependency[], boolean]>} ObjectDeserializerContext */
2121
/** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext<[Range, HarmonyAcceptImportDependency[], boolean]>} ObjectSerializerContext */
22-
/** @typedef {import("../Module")} Module */
23-
/** @typedef {import("../Module").ModuleId} ModuleId */
2422

2523
class HarmonyAcceptDependency extends NullDependency {
2624
/**
@@ -91,17 +89,6 @@ HarmonyAcceptDependency.Template = class HarmonyAcceptDependencyTemplate extends
9189
chunkGraph
9290
} = templateContext;
9391

94-
/**
95-
* Gets dependency module id.
96-
* @param {Dependency} dependency the dependency to get module id for
97-
* @returns {ModuleId | null} the module id or null if not found
98-
*/
99-
const getDependencyModuleId = (dependency) => {
100-
const mod = moduleGraph.getModule(dependency);
101-
if (!mod) return null;
102-
return chunkGraph.getModuleId(mod);
103-
};
104-
10592
/**
10693
* Checks whether this harmony accept dependency is related harmony import dependency.
10794
* @param {Dependency} a the first dependency
@@ -110,8 +97,11 @@ HarmonyAcceptDependency.Template = class HarmonyAcceptDependencyTemplate extends
11097
*/
11198
const isRelatedHarmonyImportDependency = (a, b) => {
11299
if (a === b || !(b instanceof HarmonyImportDependency)) return false;
113-
const idA = getDependencyModuleId(a);
114-
return idA !== null && idA === getDependencyModuleId(b);
100+
// Compare modules by reference: an unresolved import (ignored/failed, or a
101+
// deferred lazy-barrel re-export) has no module, and a module not in any
102+
// chunk has a null id — so comparing ids would crash or miss real matches.
103+
const moduleA = moduleGraph.getModule(a);
104+
return moduleA !== null && moduleA === moduleGraph.getModule(b);
115105
};
116106

117107
/**
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { value } from "./file";
2+
3+
// "./ignored" is dropped by IgnorePlugin, so its accept dependency resolves to
4+
// no module. Generating this accept must not crash (issue #21300). This entry
5+
// is compiled but never executed by the runner, so the missing-module runtime
6+
// stub is never reached.
7+
if (value) module.hot.accept(["./file", "./ignored"], function () {});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export var value = 1;
2+
---
3+
export var value = 2;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { value } from "./file";
2+
3+
it("should compile HMR accept code for a module whose accept references a dependency without a module", function (done) {
4+
expect(value).toBe(1);
5+
module.hot.accept("./file", function () {
6+
expect(value).toBe(2);
7+
done();
8+
});
9+
NEXT(require("../../update")(done));
10+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"use strict";
2+
3+
const webpack = require("../../../../");
4+
5+
/** @type {import("../../../../").Configuration} */
6+
module.exports = {
7+
// `broken` reproduces the codegen crash; the runner only executes `main`.
8+
entry: {
9+
main: "./index.js",
10+
broken: "./broken.js"
11+
},
12+
output: {
13+
filename: "[name].js"
14+
},
15+
plugins: [new webpack.IgnorePlugin({ resourceRegExp: /ignored/ })]
16+
};

0 commit comments

Comments
 (0)