@depup/webpack 5.109.0-depup.0 → 5.109.2-depup.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +4 -4
  2. package/bin/webpack.js +12 -0
  3. package/changes.json +4 -4
  4. package/lib/ChunkGraph.js +17 -15
  5. package/lib/CleanPlugin.js +11 -9
  6. package/lib/CompatibilityPlugin.js +31 -0
  7. package/lib/Compilation.js +21 -5
  8. package/lib/Compiler.js +16 -4
  9. package/lib/DefinePlugin.js +47 -20
  10. package/lib/ExportsInfo.js +22 -5
  11. package/lib/ExternalModule.js +10 -7
  12. package/lib/FileSystemInfo.js +126 -26
  13. package/lib/FlagDependencyUsagePlugin.js +33 -12
  14. package/lib/InitFragment.js +20 -32
  15. package/lib/ModuleFilenameHelpers.js +13 -2
  16. package/lib/NormalModule.js +80 -71
  17. package/lib/NormalModuleFactory.js +8 -2
  18. package/lib/RuntimePlugin.js +40 -113
  19. package/lib/Template.js +23 -3
  20. package/lib/cache/PackFileCacheStrategy.js +259 -5
  21. package/lib/config/defaults.js +21 -2
  22. package/lib/container/HoistContainerReferencesPlugin.js +47 -55
  23. package/lib/container/ModuleFederationPlugin.js +15 -9
  24. package/lib/css/CssGenerator.js +8 -2
  25. package/lib/css/CssLoadingRuntimeModule.js +25 -12
  26. package/lib/css/CssModulesPlugin.js +29 -18
  27. package/lib/css/CssParser.js +135 -50
  28. package/lib/css/syntax.js +862 -765
  29. package/lib/dependencies/CommonJsExportRequireDependency.js +19 -0
  30. package/lib/dependencies/CommonJsFullRequireDependency.js +33 -4
  31. package/lib/dependencies/CommonJsImportsParserPlugin.js +2 -0
  32. package/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js +2 -1
  33. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +17 -9
  34. package/lib/dependencies/HarmonyImportDependency.js +1 -1
  35. package/lib/dependencies/HarmonyImportGuard.js +2 -1
  36. package/lib/dependencies/HarmonyImportSpecifierDependency.js +10 -7
  37. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +15 -9
  38. package/lib/hmr/lazyCompilationBackend.js +19 -4
  39. package/lib/html/HtmlGenerator.js +23 -3
  40. package/lib/html/HtmlModulesPlugin.js +30 -12
  41. package/lib/html/syntax.js +2485 -2399
  42. package/lib/ids/IdHelpers.js +24 -10
  43. package/lib/javascript/JavascriptModulesPlugin.js +83 -49
  44. package/lib/javascript/JavascriptParser.js +49 -13
  45. package/lib/javascript/syntax.js +700 -43
  46. package/lib/node/ReadFileCompileAsyncWasmPlugin.js +10 -2
  47. package/lib/node/ReadFileCompileWasmPlugin.js +10 -2
  48. package/lib/optimize/AggressiveMergingPlugin.js +19 -15
  49. package/lib/optimize/ConcatenatedModule.js +19 -15
  50. package/lib/optimize/ModuleConcatenationPlugin.js +73 -24
  51. package/lib/optimize/RealContentHashPlugin.js +10 -7
  52. package/lib/optimize/SplitChunksPlugin.js +146 -44
  53. package/lib/prefetch/ResourceHintPlugin.js +96 -72
  54. package/lib/serialization/BinaryMiddleware.js +270 -938
  55. package/lib/serialization/FileMiddleware.js +299 -60
  56. package/lib/stats/DefaultStatsFactoryPlugin.js +204 -198
  57. package/lib/stats/DefaultStatsPrinterPlugin.js +24 -166
  58. package/lib/stats/StatsFactory.js +31 -9
  59. package/lib/util/identifier.js +21 -1
  60. package/package.json +10 -10
  61. package/schemas/WebpackOptions.json +110 -55
  62. package/types.d.ts +832 -247
@@ -240,6 +240,25 @@ class CommonJsExportRequireDependency extends ModuleDependency {
240
240
  dependencies: [from.module]
241
241
  };
242
242
  }
243
+ // The imported namespace ESM hasn't been flagged by
244
+ // FlagDependencyExportsPlugin yet (no exports determined), so its
245
+ // `"module.exports"` unwrap eligibility is still unknown. Star-reexporting
246
+ // now would add `__esModule` (and names) the monotonic merge can't retract
247
+ // once the module turns out to unwrap, making the result order-dependent
248
+ // across runtimes; defer — the `from.module` dependency re-queues us once
249
+ // its exports (owned names, or dynamic `other`) become known.
250
+ if (
251
+ importedModule &&
252
+ importedModule.getExportsType(moduleGraph, false) === "namespace"
253
+ ) {
254
+ const importedExportsInfo = moduleGraph.getExportsInfo(importedModule);
255
+ if (
256
+ importedExportsInfo.otherExportsInfo.provided === false &&
257
+ importedExportsInfo.ownedExports[Symbol.iterator]().next().done
258
+ ) {
259
+ return { exports: [], dependencies: [from.module] };
260
+ }
261
+ }
243
262
  const reexportInfo = this.getStarReexports(
244
263
  moduleGraph,
245
264
  undefined,
@@ -9,6 +9,7 @@ const Template = require("../Template");
9
9
  const { equals } = require("../util/ArrayHelpers");
10
10
  const { getTrimmedIdsAndRange } = require("../util/chainedImports");
11
11
  const makeSerializable = require("../util/makeSerializable");
12
+ const memoize = require("../util/memoize");
12
13
  const { propertyAccess } = require("../util/property");
13
14
  const {
14
15
  ESM_MODULE_EXPORTS_NAME,
@@ -19,6 +20,7 @@ const ModuleDependency = require("./ModuleDependency");
19
20
 
20
21
  /** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
21
22
  /** @typedef {import("../Dependency")} Dependency */
23
+ /** @typedef {import("../Dependency").GetConditionFn} GetConditionFn */
22
24
  /** @typedef {import("../Dependency").ReferencedExports} ReferencedExports */
23
25
  /** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
24
26
  /** @typedef {import("../ModuleGraph")} ModuleGraph */
@@ -26,8 +28,11 @@ const ModuleDependency = require("./ModuleDependency");
26
28
  /** @typedef {import("../ExportsInfo").ExportInfoName} ExportInfoName */
27
29
  /** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
28
30
  /** @typedef {import("../util/chainedImports").IdRanges} IdRanges */
29
- /** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext<[ExportInfoName[], IdRanges | undefined, boolean, undefined | boolean]>} ObjectDeserializerContext */
30
- /** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext<[ExportInfoName[], IdRanges | undefined, boolean, undefined | boolean]>} ObjectSerializerContext */
31
+ /** @typedef {import("./HarmonyImportGuard").DependencyGuard} DependencyGuard */
32
+ /** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext<[ExportInfoName[], IdRanges | undefined, boolean, undefined | boolean, DependencyGuard[] | undefined]>} ObjectDeserializerContext */
33
+ /** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext<[ExportInfoName[], IdRanges | undefined, boolean, undefined | boolean, DependencyGuard[] | undefined]>} ObjectSerializerContext */
34
+
35
+ const getHarmonyImportGuard = memoize(() => require("./HarmonyImportGuard"));
31
36
 
32
37
  class CommonJsFullRequireDependency extends ModuleDependency {
33
38
  /**
@@ -53,6 +58,20 @@ class CommonJsFullRequireDependency extends ModuleDependency {
53
58
  this.call = false;
54
59
  /** @type {undefined | boolean} */
55
60
  this.asiSafe = undefined;
61
+ /** @type {DependencyGuard[] | undefined} */
62
+ this.branchGuards = undefined;
63
+ }
64
+
65
+ /**
66
+ * Returns function to determine if the connection is active.
67
+ * @param {ModuleGraph} moduleGraph module graph
68
+ * @returns {null | false | GetConditionFn} function to determine if the connection is active
69
+ */
70
+ getCondition(moduleGraph) {
71
+ const guards = this.branchGuards;
72
+ if (guards === undefined) return null;
73
+ return (connection, runtime) =>
74
+ !getHarmonyImportGuard().isDeadByGuards(guards, moduleGraph, runtime);
56
75
  }
57
76
 
58
77
  /**
@@ -92,7 +111,8 @@ class CommonJsFullRequireDependency extends ModuleDependency {
92
111
  .write(this.names)
93
112
  .write(this.idRanges)
94
113
  .write(this.call)
95
- .write(this.asiSafe);
114
+ .write(this.asiSafe)
115
+ .write(this.branchGuards);
96
116
  super.serialize(context);
97
117
  }
98
118
 
@@ -108,7 +128,9 @@ class CommonJsFullRequireDependency extends ModuleDependency {
108
128
  this.call = c2.read();
109
129
  const c3 = c2.rest;
110
130
  this.asiSafe = c3.read();
111
- super.deserialize(c3.rest);
131
+ const c4 = c3.rest;
132
+ this.branchGuards = c4.read();
133
+ super.deserialize(c4.rest);
112
134
  }
113
135
 
114
136
  get type() {
@@ -137,6 +159,13 @@ CommonJsFullRequireDependency.Template = class CommonJsFullRequireDependencyTemp
137
159
  ) {
138
160
  const dep = /** @type {CommonJsFullRequireDependency} */ (dependency);
139
161
  if (!dep.range) return;
162
+ const connection = moduleGraph.getConnection(dep);
163
+ // Dead branch: module is excluded and has no id; code is never executed.
164
+ if (connection && !connection.isTargetActive(runtime)) {
165
+ // Replaces the whole member chain, so no property access is left dangling
166
+ source.replace(dep.range[0], dep.range[1] - 1, "null /* dead branch */");
167
+ return;
168
+ }
140
169
  const importedModule = moduleGraph.getModule(dep);
141
170
  let requireExpr = runtimeTemplate.moduleExports({
142
171
  module: importedModule,
@@ -642,6 +642,7 @@ class CommonJsImportsParserPlugin {
642
642
  dep.optional = Boolean(parser.scope.inTry);
643
643
  dep.loc = parser.getLocation(expr);
644
644
  parser.state.current.addDependency(dep);
645
+ getHarmonyImportGuard().attachDependencyGuards(parser, dep);
645
646
  return true;
646
647
  }
647
648
  };
@@ -680,6 +681,7 @@ class CommonJsImportsParserPlugin {
680
681
  dep.optional = Boolean(parser.scope.inTry);
681
682
  dep.loc = parser.getLocation(expr.callee);
682
683
  parser.state.current.addDependency(dep);
684
+ getHarmonyImportGuard().attachDependencyGuards(parser, dep);
683
685
  parser.walkExpressions(expr.arguments);
684
686
  return true;
685
687
  }
@@ -179,7 +179,8 @@ HarmonyEvaluatedImportSpecifierDependency.Template = class HarmonyEvaluatedImpor
179
179
  dep,
180
180
  source,
181
181
  templateContext,
182
- ids.slice(0, -1)
182
+ ids.slice(0, -1),
183
+ connection
183
184
  );
184
185
  source.replace(
185
186
  dep.range[0],
@@ -191,8 +191,11 @@ const isStarReexportBackToParent = (moduleGraph, exportInfo, parentModule) => {
191
191
  let current = moduleGraph
192
192
  .getExportsInfo(firstTarget.module)
193
193
  .getReadOnlyExportInfo(firstTarget.export[0]);
194
- /** @type {Set<ExportInfo>} */
195
- const visited = new Set([exportInfo, current]);
194
+ // Allocated lazily: a single-extra-hop chain that terminates (the common
195
+ // `barrel -> leaf local binding` case) returns before `next` is ever
196
+ // computed, so the visited set is pure waste there.
197
+ /** @type {Set<ExportInfo> | undefined} */
198
+ let visited;
196
199
  for (;;) {
197
200
  const target = current.findTarget(moduleGraph, RETURNS_TRUE);
198
201
  if (!target || typeof target !== "object") return false;
@@ -210,6 +213,7 @@ const isStarReexportBackToParent = (moduleGraph, exportInfo, parentModule) => {
210
213
  const next = moduleGraph
211
214
  .getExportsInfo(target.module)
212
215
  .getReadOnlyExportInfo(target.export[0]);
216
+ if (visited === undefined) visited = new Set([exportInfo, current]);
213
217
  if (visited.has(next)) return false;
214
218
  visited.add(next);
215
219
  current = next;
@@ -635,7 +639,8 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
635
639
  exportsInfo.otherExportsInfo.getUsed(runtime) === UsageState.Unused;
636
640
 
637
641
  /** @type {IgnoredExports} */
638
- const ignoredExports = new Set(["default", ...this.activeExports]);
642
+ const ignoredExports = new Set(this.activeExports);
643
+ ignoredExports.add("default");
639
644
 
640
645
  /** @type {Hidden | undefined} */
641
646
  let hiddenExports;
@@ -1387,7 +1392,12 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
1387
1392
  );
1388
1393
  break;
1389
1394
 
1390
- case "normal-reexport":
1395
+ case "normal-reexport": {
1396
+ // loop-invariants hoisted out of the per-item loop below (a barrel
1397
+ // re-exporting N names otherwise repeats these lookups N times)
1398
+ const connection = moduleGraph.getConnection(dep);
1399
+ const selfExportsInfo = moduleGraph.getExportsInfo(module);
1400
+ const importedExportsInfo = moduleGraph.getExportsInfo(importedModule);
1391
1401
  for (const {
1392
1402
  name,
1393
1403
  ids,
@@ -1396,7 +1406,6 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
1396
1406
  } of /** @type {NormalReexportItem[]} */ (mode.items)) {
1397
1407
  if (hidden) continue;
1398
1408
  if (checked) {
1399
- const connection = moduleGraph.getConnection(dep);
1400
1409
  const key = `harmony reexport (checked) ${importVar} ${name}`;
1401
1410
  const runtimeCondition = dep.weak
1402
1411
  ? false
@@ -1425,11 +1434,9 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
1425
1434
  this.getReexportFragment(
1426
1435
  module,
1427
1436
  "reexport safe",
1428
- moduleGraph.getExportsInfo(module).getUsedName(name, runtime),
1437
+ selfExportsInfo.getUsedName(name, runtime),
1429
1438
  importVar,
1430
- moduleGraph
1431
- .getExportsInfo(importedModule)
1432
- .getUsedName(ids, runtime),
1439
+ importedExportsInfo.getUsedName(ids, runtime),
1433
1440
  runtimeRequirements,
1434
1441
  ids
1435
1442
  )
@@ -1437,6 +1444,7 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
1437
1444
  }
1438
1445
  }
1439
1446
  break;
1447
+ }
1440
1448
 
1441
1449
  case "dynamic-reexport": {
1442
1450
  const ignored = mode.hidden
@@ -191,7 +191,7 @@ class HarmonyImportDependency extends ModuleDependency {
191
191
 
192
192
  let importVar = importVarMap.get(importedModule);
193
193
  if (importVar) return importVar;
194
- importVar = `${Template.toIdentifier(`${this.userRequest}`)}__WEBPACK_${
194
+ importVar = `${Template.toIdentifier(this.userRequest)}__WEBPACK_${
195
195
  isDeferred ? "DEFERRED_" : ""
196
196
  }IMPORTED_MODULE_${importVarMap.size}__`;
197
197
  importVarMap.set(importedModule, importVar);
@@ -14,6 +14,7 @@ const memoize = require("../util/memoize");
14
14
  /** @typedef {import("../ModuleGraph")} ModuleGraph */
15
15
  /** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
16
16
  /** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
17
+ /** @typedef {import("./CommonJsFullRequireDependency")} CommonJsFullRequireDependency */
17
18
  /** @typedef {import("./CommonJsRequireDependency")} CommonJsRequireDependency */
18
19
  /** @typedef {import("./HarmonyEvaluatedImportSpecifierDependency")} HarmonyEvaluatedImportSpecifierDependency */
19
20
  /** @typedef {import("./HarmonyImportSpecifierDependency")} HarmonyImportSpecifierDependency */
@@ -29,7 +30,7 @@ const memoize = require("../util/memoize");
29
30
  /** @typedef {["&&" | "||" | "??", GuardFormula, GuardFormula]} GuardLogical */
30
31
  /** @typedef {GuardAtom | GuardPresenceAtom | GuardUnknown | GuardNot | GuardLogical} GuardFormula */
31
32
  /** @typedef {{ formula: GuardFormula, value: boolean }} DependencyGuard branch guard: the dependency is live only when the formula evaluates to `value` */
32
- /** @typedef {HarmonyImportSpecifierDependency | CommonJsRequireDependency | ImportDependency} GuardableDependency */
33
+ /** @typedef {HarmonyImportSpecifierDependency | CommonJsRequireDependency | CommonJsFullRequireDependency | ImportDependency} GuardableDependency */
33
34
 
34
35
  /**
35
36
  * A guard frame pushed onto `parser.state.guardStack` for the duration of one
@@ -32,6 +32,7 @@ const { ImportPhaseUtils } = require("./ImportPhase");
32
32
  /** @typedef {import("../Module")} Module */
33
33
  /** @typedef {import("../Module").BuildMeta} BuildMeta */
34
34
  /** @typedef {import("../ModuleGraph")} ModuleGraph */
35
+ /** @typedef {import("../ModuleGraphConnection")} ModuleGraphConnection */
35
36
  /** @typedef {import("../ModuleGraphConnection").ConnectionState} ConnectionState */
36
37
  /** @typedef {import("../errors/WebpackError")} WebpackError */
37
38
  /** @typedef {import("../javascript/JavascriptParser").DestructuringAssignmentProperties} DestructuringAssignmentProperties */
@@ -475,7 +476,8 @@ HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependen
475
476
  dep,
476
477
  source,
477
478
  templateContext,
478
- trimmedIds
479
+ trimmedIds,
480
+ connection
479
481
  );
480
482
  if (dep.shorthand) {
481
483
  source.insert(trimmedRangeEnd, `: ${exportExpr}`);
@@ -521,13 +523,14 @@ HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependen
521
523
  });
522
524
  }
523
525
  );
526
+ // loop-invariant: resolve the imported module's exports info once
527
+ const destructuredExportsInfo = moduleGraph.getExportsInfo(
528
+ /** @type {Module} */ (moduleGraph.getModule(dep))
529
+ );
524
530
  for (const { ids, shorthand, range } of replacementsInDestructuring) {
525
531
  /** @type {Ids} */
526
532
  const concatedIds = [...prefixedIds, ...ids];
527
- const module = /** @type {Module} */ (moduleGraph.getModule(dep));
528
- const used = moduleGraph
529
- .getExportsInfo(module)
530
- .getUsedName(concatedIds, runtime);
533
+ const used = destructuredExportsInfo.getUsedName(concatedIds, runtime);
531
534
  if (!used) {
532
535
  return;
533
536
  } else if (used instanceof InlinedUsedName) {
@@ -559,12 +562,12 @@ HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependen
559
562
  * @param {ReplaceSource} source source
560
563
  * @param {DependencyTemplateContext} templateContext context
561
564
  * @param {Ids} ids ids
565
+ * @param {ModuleGraphConnection | undefined} connection the resolved connection for dep
562
566
  * @returns {string} generated code
563
567
  */
564
- _getCodeForIds(dep, source, templateContext, ids) {
568
+ _getCodeForIds(dep, source, templateContext, ids, connection) {
565
569
  const { moduleGraph, module, runtime, concatenationScope } =
566
570
  templateContext;
567
- const connection = moduleGraph.getConnection(dep);
568
571
  /** @type {string} */
569
572
  let exportExpr;
570
573
 
@@ -25,11 +25,21 @@ const { getUndoPath } = require("../util/identifier");
25
25
  /** @typedef {import("../ChunkGraph")} ChunkGraph */
26
26
  /** @typedef {import("../Module").ReadOnlyRuntimeRequirements} ReadOnlyRuntimeRequirements */
27
27
 
28
+ const createCompilationHooks = () => ({
29
+ /**
30
+ * @type {SyncWaterfallHook<[string, Chunk]>}
31
+ * @since 5.41.0
32
+ */
33
+ linkPreload: new SyncWaterfallHook(["source", "chunk"]),
34
+ /**
35
+ * @type {SyncWaterfallHook<[string, Chunk]>}
36
+ * @since 5.41.0
37
+ */
38
+ linkPrefetch: new SyncWaterfallHook(["source", "chunk"])
39
+ });
40
+
28
41
  /**
29
- * Defines the jsonp compilation plugin hooks type used by this module.
30
- * @typedef {object} JsonpCompilationPluginHooks
31
- * @property {SyncWaterfallHook<[string, Chunk]>} linkPreload
32
- * @property {SyncWaterfallHook<[string, Chunk]>} linkPrefetch
42
+ * @typedef {ReturnType<typeof createCompilationHooks>} JsonpCompilationPluginHooks
33
43
  */
34
44
 
35
45
  class ModuleChunkLoadingRuntimeModule extends RuntimeModule {
@@ -438,11 +448,7 @@ class ModuleChunkLoadingRuntimeModule extends RuntimeModule {
438
448
  }
439
449
 
440
450
  ModuleChunkLoadingRuntimeModule.getCompilationHooks = createHooksRegistry(
441
- () =>
442
- /** @type {JsonpCompilationPluginHooks} */ ({
443
- linkPreload: new SyncWaterfallHook(["source", "chunk"]),
444
- linkPrefetch: new SyncWaterfallHook(["source", "chunk"])
445
- })
451
+ createCompilationHooks
446
452
  );
447
453
 
448
454
  module.exports = ModuleChunkLoadingRuntimeModule;
@@ -94,7 +94,9 @@ module.exports = (options) => (compiler, callback) => {
94
94
  if (timer.unref) timer.unref();
95
95
  idleTimers.add(timer);
96
96
  });
97
- req.socket.setNoDelay(true);
97
+ // Not all runtimes expose `setNoDelay` on the request socket (e.g. Deno's
98
+ // HTTP server); it's only a latency optimization, so skip it when absent.
99
+ if (req.socket.setNoDelay) req.socket.setNoDelay(true);
98
100
  res.writeHead(200, {
99
101
  "content-type": "text/event-stream",
100
102
  "Access-Control-Allow-Origin": "*",
@@ -168,12 +170,25 @@ module.exports = (options) => (compiler, callback) => {
168
170
  idleTimers.clear();
169
171
  // Removing the listener is a workaround for a memory leak in node.js
170
172
  server.off("request", requestListener);
171
- server.close((err) => {
172
- callback(err);
173
- });
174
173
  for (const socket of sockets) {
175
174
  socket.destroy(new Error("Server is disposing"));
176
175
  }
176
+ // Some runtimes (e.g. Deno) don't emit "connection", so `sockets`
177
+ // misses the open SSE connections and `server.close` would hang;
178
+ // force-close everything before waiting for it.
179
+ if (server.closeAllConnections) server.closeAllConnections();
180
+ server.close((err) => {
181
+ // `closeAllConnections()` already stops the server on some runtimes
182
+ // (e.g. Bun), so `close` then reports ERR_SERVER_NOT_RUNNING; the
183
+ // server is closed either way, so that's not a dispose failure.
184
+ callback(
185
+ err &&
186
+ /** @type {NodeJS.ErrnoException} */ (err).code !==
187
+ "ERR_SERVER_NOT_RUNNING"
188
+ ? err
189
+ : null
190
+ );
191
+ });
177
192
  },
178
193
  module(originalModule) {
179
194
  const key = `${encodeURIComponent(
@@ -161,6 +161,7 @@ const metaIsCsp = (attrs) => {
161
161
  /** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
162
162
  /** @typedef {import("../Generator").GenerateContext} GenerateContext */
163
163
  /** @typedef {import("../Generator").UpdateHashContext} UpdateHashContext */
164
+ /** @typedef {import("../Module")} Module */
164
165
  /** @typedef {import("../Module").SourceType} SourceType */
165
166
  /** @typedef {import("../Module").SourceTypes} SourceTypes */
166
167
  /** @typedef {import("../Module").ConcatenationBailoutReasonContext} ConcatenationBailoutReasonContext */
@@ -201,6 +202,27 @@ const getChunksById = (compilation) => {
201
202
  return chunksById;
202
203
  };
203
204
 
205
+ /** @type {WeakMap<Compilation, Map<string, Module>>} */
206
+ const modulesByIdentifierCache = new WeakMap();
207
+
208
+ /**
209
+ * `module.identifier()` → module lookup, memoized per compilation so the asset
210
+ * URL sentinel resolver doesn't re-scan the whole module graph per HTML module.
211
+ * @param {Compilation} compilation compilation
212
+ * @returns {Map<string, Module>} modules keyed by identifier
213
+ */
214
+ const getModulesByIdentifier = (compilation) => {
215
+ let modulesByIdentifier = modulesByIdentifierCache.get(compilation);
216
+ if (modulesByIdentifier === undefined) {
217
+ modulesByIdentifier = new Map();
218
+ for (const module of compilation.modules) {
219
+ modulesByIdentifier.set(module.identifier(), module);
220
+ }
221
+ modulesByIdentifierCache.set(compilation, modulesByIdentifier);
222
+ }
223
+ return modulesByIdentifier;
224
+ };
225
+
204
226
  // Hoisted so `resolveChunkUrlSentinels` (per chunk × module) doesn't allocate a
205
227
  // fresh `RegExp` each call. `String#replace` resets `lastIndex`, so sharing the
206
228
  // global-flag instance is safe under these synchronous, non-reentrant calls.
@@ -413,9 +435,7 @@ class HtmlGenerator extends Generator {
413
435
  */
414
436
  static resolveAssetUrlSentinels(content, compilation) {
415
437
  if (!content.includes("__WEBPACK_HTML_ASSET_URL__")) return content;
416
- /** @type {Map<string, import("../Module")>} */
417
- const byIdentifier = new Map();
418
- for (const m of compilation.modules) byIdentifier.set(m.identifier(), m);
438
+ const byIdentifier = getModulesByIdentifier(compilation);
419
439
  const codeGenerationResults =
420
440
  /** @type {import("../CodeGenerationResults")} */
421
441
  (compilation.codeGenerationResults);
@@ -72,12 +72,36 @@ const { escapeAttribute } = require("./syntax");
72
72
  * @property {boolean=} remove set true to delete the whole element
73
73
  */
74
74
  /** @typedef {{ outputName: string, html: string }} HtmlTransformTagsContext */
75
+
76
+ const createCompilationHooks = () => ({
77
+ /**
78
+ * Called with the list of extra tags to inject into each page (initially empty) plus the current HTML; push `HtmlTagDescriptor`s and return the list — webpack serializes and places them by `injectTo`. A structured alternative to the string-level `transformHtml` for adding tags; runs before CSP so injected inline tags are hashed.
79
+ * @type {AsyncSeriesWaterfallHook<[HtmlTagDescriptor[], HtmlInjectTagsContext]>}
80
+ * @since 5.109.0
81
+ */
82
+ injectTags: new AsyncSeriesWaterfallHook(["tags", "context"]),
83
+ /**
84
+ * Called with the page's `<script>`/`<link>`/`<style>`/`<meta>` tags (webpack's own and any injected) as mutable descriptors; mutate `attrs` (add a `nonce`/`data-*`, switch `defer`↔`async`, …), set `remove: true`, or change `injectTo` to move a tag between `<head>` and `<body>`, and webpack rewrites the changed tags. Add new tags with `injectTags` instead.
85
+ * @type {AsyncSeriesHook<[HtmlMutableTag[], HtmlTransformTagsContext]>}
86
+ * @since 5.109.0
87
+ */
88
+ transformTags: new AsyncSeriesHook(["tags", "context"]),
89
+ /**
90
+ * Called with each emitted page's final HTML (all sentinels resolved) just before it is written; return the (possibly transformed) HTML — e.g. to minify, inject a CSP meta, or rewrite tags.
91
+ * @type {AsyncSeriesWaterfallHook<[string, HtmlTransformHtmlContext]>}
92
+ * @since 5.109.0
93
+ */
94
+ transformHtml: new AsyncSeriesWaterfallHook(["html", "context"]),
95
+ /**
96
+ * Called once each page's HTML asset has been finalized — a post-emit notification (nothing to return).
97
+ * @type {AsyncSeriesHook<[HtmlEmittedContext]>}
98
+ * @since 5.109.0
99
+ */
100
+ htmlEmitted: new AsyncSeriesHook(["context"])
101
+ });
102
+
75
103
  /**
76
- * @typedef {object} HtmlCompilationHooks
77
- * @property {AsyncSeriesWaterfallHook<[HtmlTagDescriptor[], HtmlInjectTagsContext]>} injectTags called with the list of extra tags to inject into each page (initially empty) plus the current HTML; push `HtmlTagDescriptor`s and return the list — webpack serializes and places them by `injectTo`. A structured alternative to the string-level `transformHtml` for adding tags; runs before CSP so injected inline tags are hashed
78
- * @property {AsyncSeriesHook<[HtmlMutableTag[], HtmlTransformTagsContext]>} transformTags called with the page's `<script>`/`<link>`/`<style>`/`<meta>` tags (webpack's own and any injected) as mutable descriptors; mutate `attrs` (add a `nonce`/`data-*`, switch `defer`↔`async`, …), set `remove: true`, or change `injectTo` to move a tag between `<head>` and `<body>`, and webpack rewrites the changed tags. Add new tags with `injectTags` instead
79
- * @property {AsyncSeriesWaterfallHook<[string, HtmlTransformHtmlContext]>} transformHtml called with each emitted page's final HTML (all sentinels resolved) just before it is written; return the (possibly transformed) HTML — e.g. to minify, inject a CSP meta, or rewrite tags
80
- * @property {AsyncSeriesHook<[HtmlEmittedContext]>} htmlEmitted called once each page's HTML asset has been finalized — a post-emit notification (nothing to return)
104
+ * @typedef {ReturnType<typeof createCompilationHooks>} HtmlCompilationHooks
81
105
  */
82
106
 
83
107
  const PLUGIN_NAME = "HtmlModulesPlugin";
@@ -1099,13 +1123,7 @@ class HtmlModulesPlugin {
1099
1123
  * @returns {HtmlCompilationHooks} the hooks
1100
1124
  */
1101
1125
  HtmlModulesPlugin.getCompilationHooks = createHooksRegistry(
1102
- () =>
1103
- /** @type {HtmlCompilationHooks} */ ({
1104
- injectTags: new AsyncSeriesWaterfallHook(["tags", "context"]),
1105
- transformTags: new AsyncSeriesHook(["tags", "context"]),
1106
- transformHtml: new AsyncSeriesWaterfallHook(["html", "context"]),
1107
- htmlEmitted: new AsyncSeriesHook(["context"])
1108
- })
1126
+ createCompilationHooks
1109
1127
  );
1110
1128
 
1111
1129
  module.exports = HtmlModulesPlugin;