webpack 5.107.0 → 5.107.2

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 (64) hide show
  1. package/lib/BannerPlugin.js +3 -4
  2. package/lib/Chunk.js +21 -25
  3. package/lib/ChunkGroup.js +57 -15
  4. package/lib/Compilation.js +33 -11
  5. package/lib/Compiler.js +27 -3
  6. package/lib/ContextModuleFactory.js +45 -38
  7. package/lib/EvalSourceMapDevToolPlugin.js +0 -1
  8. package/lib/ExportsInfo.js +30 -34
  9. package/lib/ExternalModule.js +15 -11
  10. package/lib/ExternalModuleFactoryPlugin.js +2 -1
  11. package/lib/Module.js +1 -1
  12. package/lib/ModuleNotFoundError.js +10 -0
  13. package/lib/ModuleSourceTypeConstants.js +24 -22
  14. package/lib/MultiCompiler.js +14 -0
  15. package/lib/NormalModule.js +531 -53
  16. package/lib/NormalModuleFactory.js +38 -26
  17. package/lib/ProgressPlugin.js +1 -1
  18. package/lib/RuntimePlugin.js +1 -1
  19. package/lib/SourceMapDevToolPlugin.js +335 -57
  20. package/lib/Template.js +1 -1
  21. package/lib/TemplatedPathPlugin.js +22 -4
  22. package/lib/asset/AssetBytesGenerator.js +6 -6
  23. package/lib/asset/AssetGenerator.js +14 -14
  24. package/lib/asset/AssetModulesPlugin.js +3 -7
  25. package/lib/asset/AssetSourceGenerator.js +6 -6
  26. package/lib/buildChunkGraph.js +24 -2
  27. package/lib/cache/getLazyHashedEtag.js +9 -2
  28. package/lib/css/CssModulesPlugin.js +2 -2
  29. package/lib/dependencies/CommonJsImportsParserPlugin.js +108 -1
  30. package/lib/dependencies/CssUrlDependency.js +3 -2
  31. package/lib/dependencies/HarmonyDetectionParserPlugin.js +21 -1
  32. package/lib/dependencies/HarmonyExportInitFragment.js +8 -9
  33. package/lib/dependencies/HtmlInlineScriptDependency.js +3 -14
  34. package/lib/dependencies/HtmlInlineStyleDependency.js +17 -0
  35. package/lib/dependencies/HtmlScriptSrcDependency.js +265 -65
  36. package/lib/dependencies/HtmlSourceDependency.js +21 -2
  37. package/lib/dependencies/WorkerPlugin.js +18 -4
  38. package/lib/hmr/LazyCompilationPlugin.js +104 -0
  39. package/lib/html/HtmlGenerator.js +81 -33
  40. package/lib/html/HtmlModulesPlugin.js +87 -28
  41. package/lib/html/walkHtmlTokens.js +641 -125
  42. package/lib/index.js +2 -0
  43. package/lib/javascript/JavascriptModulesPlugin.js +2 -2
  44. package/lib/javascript/JavascriptParser.js +1 -1
  45. package/lib/library/ModuleLibraryPlugin.js +30 -24
  46. package/lib/node/NodeWatchFileSystem.js +37 -22
  47. package/lib/optimize/ConcatenatedModule.js +3 -2
  48. package/lib/optimize/SideEffectsFlagPlugin.js +1 -2
  49. package/lib/optimize/SplitChunksPlugin.js +4 -4
  50. package/lib/runtime/AutoPublicPathRuntimeModule.js +3 -3
  51. package/lib/runtime/GetChunkFilenameRuntimeModule.js +5 -5
  52. package/lib/sharing/ConsumeSharedPlugin.js +2 -8
  53. package/lib/sharing/ProvideSharedPlugin.js +4 -4
  54. package/lib/util/fs.js +6 -1
  55. package/lib/wasm-async/AsyncWebAssemblyModulesPlugin.js +1 -2
  56. package/package.json +5 -5
  57. package/schemas/WebpackOptions.check.js +1 -1
  58. package/schemas/WebpackOptions.json +11 -9
  59. package/schemas/plugins/container/ContainerReferencePlugin.check.js +1 -1
  60. package/schemas/plugins/container/ContainerReferencePlugin.json +1 -0
  61. package/schemas/plugins/container/ExternalsType.check.js +1 -1
  62. package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
  63. package/schemas/plugins/container/ModuleFederationPlugin.json +1 -0
  64. package/types.d.ts +472 -149
@@ -61,6 +61,7 @@ const parseJson = require("./util/parseJson");
61
61
  /** @typedef {import("../declarations/WebpackOptions").ResolveOptions} ResolveOptions */
62
62
  /** @typedef {import("../declarations/WebpackOptions").NoParse} NoParse */
63
63
  /** @typedef {import("./config/defaults").WebpackOptionsNormalizedWithDefaults} WebpackOptions */
64
+ /** @typedef {import("./Dependency")} Dependency */
64
65
  /** @typedef {import("./Dependency").UpdateHashContext} UpdateHashContext */
65
66
  /** @typedef {import("./Generator")} Generator */
66
67
  /** @typedef {import("./Generator").GenerateErrorFn} GenerateErrorFn */
@@ -87,6 +88,10 @@ const parseJson = require("./util/parseJson");
87
88
  /** @typedef {import("./ModuleGraphConnection").ConnectionState} ConnectionState */
88
89
  /** @typedef {import("./NormalModuleFactory")} NormalModuleFactory */
89
90
  /** @typedef {import("./NormalModuleFactory").NormalModuleTypes} NormalModuleTypes */
91
+ /** @typedef {import("./NormalModuleFactory").ParserByType} ParserByType */
92
+ /** @typedef {import("./NormalModuleFactory").ParserOptionsByType} ParserOptionsByType */
93
+ /** @typedef {import("./NormalModuleFactory").GeneratorByType} GeneratorByType */
94
+ /** @typedef {import("./NormalModuleFactory").GeneratorOptionsByType} GeneratorOptionsByType */
90
95
  /** @typedef {import("./NormalModuleFactory").ResourceSchemeData} ResourceSchemeData */
91
96
  /** @typedef {import("./Parser")} Parser */
92
97
  /** @typedef {import("./Parser").PreparsedAst} PreparsedAst */
@@ -126,6 +131,488 @@ const getExtractSourceMap = memoize(() => require("./util/extractSourceMap"));
126
131
 
127
132
  const getValidate = memoize(() => require("schema-utils").validate);
128
133
 
134
+ const getHarmonyImportSideEffectDependency = memoize(() =>
135
+ require("./dependencies/HarmonyImportSideEffectDependency")
136
+ );
137
+
138
+ /**
139
+ * @param {NormalModule} mod the module
140
+ * @param {ModuleGraph} moduleGraph the module graph
141
+ * @param {Dependency} dep the dep that triggered the bailout
142
+ */
143
+ const recordSideEffectsBailout = (mod, moduleGraph, dep) => {
144
+ if (mod._addedSideEffectsBailout === undefined) {
145
+ mod._addedSideEffectsBailout = new WeakSet();
146
+ } else if (mod._addedSideEffectsBailout.has(moduleGraph)) {
147
+ return;
148
+ }
149
+ mod._addedSideEffectsBailout.add(moduleGraph);
150
+ moduleGraph
151
+ .getOptimizationBailout(mod)
152
+ .push(
153
+ () =>
154
+ `Dependency (${dep.type}) with side effects at ${formatLocation(dep.loc)}`
155
+ );
156
+ };
157
+
158
+ // Maximum recursive descent depth before switching to the iterative walker.
159
+ // #20986 reported overflow around 1300 modules in webpack 5.107.0 where each
160
+ // step consumed two stack frames (`NormalModule.getSideEffectsConnectionState`
161
+ // plus `HarmonyImportSideEffectDependency.getModuleEvaluationSideEffectsState`).
162
+ // `walkSideEffectsRecursive` folds the second call into the first and uses
163
+ // one frame per module, so this limit caps the native stack at half the depth
164
+ // where the original code overflowed — well within the safe range across
165
+ // platforms while keeping the common (shallow) case purely recursive.
166
+ const SIDE_EFFECTS_RECURSION_LIMIT = 2000;
167
+
168
+ /**
169
+ * Iterative form of the side-effects walker. Used as a fallback once the
170
+ * recursive form reaches `SIDE_EFFECTS_RECURSION_LIMIT` so deep chains
171
+ * (#20986) don't overflow V8's stack. Safe to enter while ancestors set
172
+ * `_isEvaluatingSideEffects` on their own modules — those are treated as
173
+ * `CIRCULAR_CONNECTION` if revisited, matching the original recursive
174
+ * behavior.
175
+ * @param {NormalModule} rootMod the module to walk
176
+ * @param {ModuleGraph} moduleGraph the module graph
177
+ * @returns {ConnectionState} the side-effects connection state
178
+ */
179
+ const walkSideEffectsIterative = (rootMod, moduleGraph) => {
180
+ const SideEffectDep = getHarmonyImportSideEffectDependency();
181
+
182
+ /** @type {NormalModule[]} */
183
+ const modStack = [rootMod];
184
+ /** @type {Dependency[][]} */
185
+ const depsStack = [rootMod.dependencies];
186
+ const indexStack = [0];
187
+ /** @type {ConnectionState[]} */
188
+ const currentStack = [false];
189
+ rootMod._isEvaluatingSideEffects = true;
190
+
191
+ /**
192
+ * Result from a just-popped child frame, to be applied to the new
193
+ * top's current dep. `undefined` means "no pending; advance".
194
+ * @type {ConnectionState | undefined}
195
+ */
196
+ let pending;
197
+
198
+ while (modStack.length > 0) {
199
+ const top = modStack.length - 1;
200
+ const topMod = modStack[top];
201
+ const deps = depsStack[top];
202
+ let index = indexStack[top];
203
+ let current = currentStack[top];
204
+
205
+ if (pending !== undefined) {
206
+ const state = pending;
207
+ pending = undefined;
208
+ const dep = deps[index];
209
+
210
+ if (state === true) {
211
+ recordSideEffectsBailout(topMod, moduleGraph, dep);
212
+ topMod._isEvaluatingSideEffects = false;
213
+ // `true` is monotonic — safe to memoize regardless of cycle
214
+ // status, matching the direct-bailout branch below.
215
+ topMod._sideEffectsStateGraph = moduleGraph;
216
+ topMod._sideEffectsStateValue = true;
217
+ modStack.pop();
218
+ depsStack.pop();
219
+ indexStack.pop();
220
+ currentStack.pop();
221
+ pending = true;
222
+ continue;
223
+ }
224
+ if (state !== ModuleGraphConnection.CIRCULAR_CONNECTION) {
225
+ current = ModuleGraphConnection.addConnectionStates(current, state);
226
+ }
227
+ index++;
228
+ }
229
+
230
+ let descended = false;
231
+ const depCount = deps.length;
232
+ while (index < depCount) {
233
+ const dep = deps[index];
234
+ /** @type {ConnectionState} */
235
+ let state;
236
+
237
+ if (dep instanceof SideEffectDep) {
238
+ const refModule = moduleGraph.getModule(dep);
239
+ if (!refModule) {
240
+ state = true;
241
+ } else if (refModule instanceof NormalModule) {
242
+ // Cache hit
243
+ if (refModule._sideEffectsStateGraph === moduleGraph) {
244
+ state = /** @type {ConnectionState} */ (
245
+ refModule._sideEffectsStateValue
246
+ );
247
+ }
248
+ // Fast-path checks inlined to avoid the helper call.
249
+ else if (refModule.factoryMeta !== undefined) {
250
+ if (refModule.factoryMeta.sideEffectFree) {
251
+ state = false;
252
+ } else if (refModule.factoryMeta.sideEffectFree === false) {
253
+ state = true;
254
+ } else if (
255
+ !(
256
+ refModule.buildMeta !== undefined &&
257
+ refModule.buildMeta.sideEffectFree
258
+ )
259
+ ) {
260
+ state = true;
261
+ } else if (refModule._isEvaluatingSideEffects) {
262
+ _sideEffectsCircularSeen = true;
263
+ state = ModuleGraphConnection.CIRCULAR_CONNECTION;
264
+ } else {
265
+ // Descend
266
+ indexStack[top] = index;
267
+ currentStack[top] = current;
268
+ refModule._isEvaluatingSideEffects = true;
269
+ modStack.push(refModule);
270
+ depsStack.push(refModule.dependencies);
271
+ indexStack.push(0);
272
+ currentStack.push(false);
273
+ descended = true;
274
+ break;
275
+ }
276
+ } else if (
277
+ !(
278
+ refModule.buildMeta !== undefined &&
279
+ refModule.buildMeta.sideEffectFree
280
+ )
281
+ ) {
282
+ state = true;
283
+ } else if (refModule._isEvaluatingSideEffects) {
284
+ _sideEffectsCircularSeen = true;
285
+ state = ModuleGraphConnection.CIRCULAR_CONNECTION;
286
+ } else {
287
+ // Descend
288
+ indexStack[top] = index;
289
+ currentStack[top] = current;
290
+ refModule._isEvaluatingSideEffects = true;
291
+ modStack.push(refModule);
292
+ depsStack.push(refModule.dependencies);
293
+ indexStack.push(0);
294
+ currentStack.push(false);
295
+ descended = true;
296
+ break;
297
+ }
298
+ } else {
299
+ _sideEffectsCircularSeen = true;
300
+ state = refModule.getSideEffectsConnectionState(moduleGraph);
301
+ }
302
+ } else {
303
+ state = dep.getModuleEvaluationSideEffectsState(moduleGraph);
304
+ }
305
+
306
+ if (state === true) {
307
+ recordSideEffectsBailout(topMod, moduleGraph, dep);
308
+ topMod._isEvaluatingSideEffects = false;
309
+ topMod._sideEffectsStateGraph = moduleGraph;
310
+ topMod._sideEffectsStateValue = true;
311
+ modStack.pop();
312
+ depsStack.pop();
313
+ indexStack.pop();
314
+ currentStack.pop();
315
+ pending = true;
316
+ descended = true;
317
+ break;
318
+ }
319
+ if (state !== ModuleGraphConnection.CIRCULAR_CONNECTION) {
320
+ current = ModuleGraphConnection.addConnectionStates(current, state);
321
+ }
322
+ index++;
323
+ }
324
+
325
+ if (descended) continue;
326
+
327
+ topMod._isEvaluatingSideEffects = false;
328
+ if (!_sideEffectsCircularSeen) {
329
+ topMod._sideEffectsStateGraph = moduleGraph;
330
+ topMod._sideEffectsStateValue = current;
331
+ }
332
+ pending = current;
333
+ modStack.pop();
334
+ depsStack.pop();
335
+ indexStack.pop();
336
+ currentStack.pop();
337
+ }
338
+
339
+ return /** @type {ConnectionState} */ (pending);
340
+ };
341
+
342
+ /**
343
+ * Tracks whether a cycle (CIRCULAR_CONNECTION) was encountered anywhere
344
+ * in the currently-executing walk. The walker uses this to decide whether
345
+ * an intermediate result is safe to memoize on the module: a result
346
+ * computed in the presence of a cycle can differ from the result computed
347
+ * fresh, because cycle short-circuiting hides the contribution of the
348
+ * back-edge target. Reset to `false` at the top-level entry; read and
349
+ * propagated by each recursive frame.
350
+ */
351
+ let _sideEffectsCircularSeen = false;
352
+
353
+ /**
354
+ * Walks back up a stack of linear-chain ancestors, applying the result
355
+ * `state` of the chain's tail to each ancestor. Each ancestor in the
356
+ * stack had exactly one `HarmonyImportSideEffectDependency` returning
357
+ * `state`, so its result is just `state` (with the usual aggregation
358
+ * rules) and we can avoid building per-frame `current` accumulators.
359
+ * @param {(NormalModule | Dependency)[] | null} ancestors interleaved stack of `[mod, sideEffectDep, mod, sideEffectDep, …]` in descent order; `null` if there were none
360
+ * @param {ConnectionState} state result from the chain's tail
361
+ * @param {ModuleGraph} moduleGraph the module graph
362
+ * @returns {ConnectionState} the root ancestor's result
363
+ */
364
+ const propagateLinearResult = (ancestors, state, moduleGraph) => {
365
+ if (ancestors === null) return state;
366
+ while (ancestors.length > 0) {
367
+ const dep = /** @type {Dependency} */ (ancestors.pop());
368
+ const ancestor = /** @type {NormalModule} */ (ancestors.pop());
369
+ ancestor._isEvaluatingSideEffects = false;
370
+
371
+ if (state === true) {
372
+ recordSideEffectsBailout(ancestor, moduleGraph, dep);
373
+ // `true` is monotonic — safe to cache regardless of cycle status.
374
+ ancestor._sideEffectsStateGraph = moduleGraph;
375
+ ancestor._sideEffectsStateValue = true;
376
+ } else if (state === ModuleGraphConnection.CIRCULAR_CONNECTION) {
377
+ // CIRCULAR_CONNECTION is filtered before folding into `current`,
378
+ // so the ancestor's `current` stays at its initial `false`. From
379
+ // this point upward the propagated state is `false`, and the
380
+ // cycle taint prevents memoization further up (handled by
381
+ // `_sideEffectsCircularSeen`).
382
+ state = false;
383
+ } else if (!_sideEffectsCircularSeen) {
384
+ ancestor._sideEffectsStateGraph = moduleGraph;
385
+ ancestor._sideEffectsStateValue = state;
386
+ }
387
+ }
388
+ return state;
389
+ };
390
+
391
+ /**
392
+ * Recursive form of the side-effects walker. Folds the descent through
393
+ * `HarmonyImportSideEffectDependency.getModuleEvaluationSideEffectsState`
394
+ * directly into the loop so each module costs only one V8 stack frame
395
+ * (vs. two in the original recursive code).
396
+ *
397
+ * Linear-chain heads (modules with exactly one
398
+ * `HarmonyImportSideEffectDependency` to another `NormalModule`) are
399
+ * walked iteratively inside the function via an explicit
400
+ * `linearAncestors` stack — no additional V8 frame per descent — and
401
+ * their results are propagated back up by `propagateLinearResult`. This
402
+ * keeps stack consumption at O(1) for the common deep-import-chain
403
+ * pattern that motivated #20986 and also avoids the heavier iterative
404
+ * fallback.
405
+ *
406
+ * Falls back to `walkSideEffectsIterative` only when a *non-linear*
407
+ * walk reaches `SIDE_EFFECTS_RECURSION_LIMIT` — i.e. a deep tree, where
408
+ * each level genuinely needs its own recursion frame.
409
+ *
410
+ * Caches the result on the module when the walk did not encounter a
411
+ * cycle, so subsequent queries (e.g. repeated lookups during
412
+ * `SideEffectsFlagPlugin`'s incoming-connection optimization and its
413
+ * `exportInfo.getTarget` callbacks) return in O(1).
414
+ * @param {NormalModule} mod the module being walked
415
+ * @param {ModuleGraph} moduleGraph the module graph
416
+ * @param {number} depth current recursion depth (only counts true V8 frames)
417
+ * @param {EXPECTED_ANY} SideEffectDep `HarmonyImportSideEffectDependency` constructor, resolved once at the public entry to avoid repeated `require` lookups in the recursive call
418
+ * @returns {ConnectionState} the side-effects connection state
419
+ */
420
+ const walkSideEffectsRecursive = (mod, moduleGraph, depth, SideEffectDep) => {
421
+ // Interleaved `[mod, linearDep, mod, linearDep, …]` for the linear
422
+ // chain head; `null` until the first descent.
423
+ /** @type {(NormalModule | Dependency)[] | null} */
424
+ let linearAncestors = null;
425
+
426
+ // Walk the linear-chain head iteratively. Every loop iteration peels
427
+ // off one module without consuming a V8 stack frame.
428
+ while (true) {
429
+ if (mod._sideEffectsStateGraph === moduleGraph) {
430
+ return propagateLinearResult(
431
+ linearAncestors,
432
+ /** @type {ConnectionState} */ (mod._sideEffectsStateValue),
433
+ moduleGraph
434
+ );
435
+ }
436
+
437
+ if (mod.factoryMeta !== undefined) {
438
+ if (mod.factoryMeta.sideEffectFree) {
439
+ return propagateLinearResult(linearAncestors, false, moduleGraph);
440
+ }
441
+ if (mod.factoryMeta.sideEffectFree === false) {
442
+ return propagateLinearResult(linearAncestors, true, moduleGraph);
443
+ }
444
+ }
445
+ if (!(mod.buildMeta !== undefined && mod.buildMeta.sideEffectFree)) {
446
+ return propagateLinearResult(linearAncestors, true, moduleGraph);
447
+ }
448
+ if (mod._isEvaluatingSideEffects) {
449
+ _sideEffectsCircularSeen = true;
450
+ return propagateLinearResult(
451
+ linearAncestors,
452
+ ModuleGraphConnection.CIRCULAR_CONNECTION,
453
+ moduleGraph
454
+ );
455
+ }
456
+
457
+ // A real ESM module's `dependencies` typically include one
458
+ // `HarmonyImportSideEffectDependency` plus several non-recursive deps
459
+ // (export specifiers, const dependencies, …) that report
460
+ // `false`/`CIRCULAR_CONNECTION` from
461
+ // `getModuleEvaluationSideEffectsState`. Walk the deps in order
462
+ // here: as long as at most one is a `SideEffectDep` and no
463
+ // non-recursive dep triggers a bailout or contributes a non-`false`
464
+ // state, we can still tail-call iteratively through that one
465
+ // `SideEffectDep` — no V8 frame needed. This is what keeps the
466
+ // 1300-module cyclic chain from #20986 from overflowing V8's stack
467
+ // even though each generated module has multiple `dependencies`.
468
+ const deps = mod.dependencies;
469
+ /** @type {Dependency | null} */
470
+ let linearDep = null;
471
+ /** @type {ConnectionState} */
472
+ let nonRecursiveCurrent = false;
473
+ let linearOk = true;
474
+ for (let i = 0; i < deps.length; i++) {
475
+ const dep = deps[i];
476
+ if (dep instanceof SideEffectDep) {
477
+ if (linearDep !== null) {
478
+ // Two `SideEffectDep`s in the same module — fall back to
479
+ // the general walk so each can recurse normally.
480
+ linearOk = false;
481
+ break;
482
+ }
483
+ linearDep = dep;
484
+ } else {
485
+ const state = dep.getModuleEvaluationSideEffectsState(moduleGraph);
486
+ if (state === true) {
487
+ recordSideEffectsBailout(mod, moduleGraph, dep);
488
+ mod._sideEffectsStateGraph = moduleGraph;
489
+ mod._sideEffectsStateValue = true;
490
+ return propagateLinearResult(linearAncestors, true, moduleGraph);
491
+ }
492
+ if (state !== ModuleGraphConnection.CIRCULAR_CONNECTION) {
493
+ nonRecursiveCurrent = ModuleGraphConnection.addConnectionStates(
494
+ nonRecursiveCurrent,
495
+ state
496
+ );
497
+ }
498
+ }
499
+ }
500
+
501
+ if (!linearOk || nonRecursiveCurrent !== false) {
502
+ // Multiple `SideEffectDep`s, or a non-recursive dep contributed
503
+ // a non-`false` state (e.g. `TRANSITIVE_ONLY`). The linear
504
+ // propagation rule "ancestor's current = chain state" no longer
505
+ // holds, so fall back to the general walk.
506
+ break;
507
+ }
508
+
509
+ if (linearDep === null) {
510
+ // No `SideEffectDep` — the module is a leaf as far as the
511
+ // side-effects graph is concerned. Cache and propagate `false`.
512
+ if (!_sideEffectsCircularSeen) {
513
+ mod._sideEffectsStateGraph = moduleGraph;
514
+ mod._sideEffectsStateValue = false;
515
+ }
516
+ return propagateLinearResult(linearAncestors, false, moduleGraph);
517
+ }
518
+
519
+ const refModule = moduleGraph.getModule(linearDep);
520
+ if (!refModule) {
521
+ recordSideEffectsBailout(mod, moduleGraph, linearDep);
522
+ mod._sideEffectsStateGraph = moduleGraph;
523
+ mod._sideEffectsStateValue = true;
524
+ return propagateLinearResult(linearAncestors, true, moduleGraph);
525
+ }
526
+ if (!(refModule instanceof NormalModule)) {
527
+ // Non-NormalModule's `getSideEffectsConnectionState` re-enters
528
+ // the public method; defer to the general walk for safety.
529
+ break;
530
+ }
531
+
532
+ mod._isEvaluatingSideEffects = true;
533
+ if (linearAncestors === null) linearAncestors = [];
534
+ // Push (mod, linearDep) so `propagateLinearResult` records bailouts
535
+ // against the actual `SideEffectDep` that triggered the descent —
536
+ // which may not be `dependencies[0]` when the module also has
537
+ // export / const dependencies.
538
+ linearAncestors.push(mod, linearDep);
539
+ mod = refModule;
540
+ continue;
541
+ }
542
+
543
+ // Non-linear walk. Each genuine recursive call costs one V8 frame, so
544
+ // honour the depth limit here.
545
+ if (depth >= SIDE_EFFECTS_RECURSION_LIMIT) {
546
+ return propagateLinearResult(
547
+ linearAncestors,
548
+ walkSideEffectsIterative(mod, moduleGraph),
549
+ moduleGraph
550
+ );
551
+ }
552
+
553
+ mod._isEvaluatingSideEffects = true;
554
+ /** @type {ConnectionState} */
555
+ let current = false;
556
+
557
+ for (const dep of mod.dependencies) {
558
+ /** @type {ConnectionState} */
559
+ let state;
560
+ if (dep instanceof SideEffectDep) {
561
+ const refModule = moduleGraph.getModule(dep);
562
+ if (!refModule) {
563
+ state = true;
564
+ } else if (refModule instanceof NormalModule) {
565
+ state = walkSideEffectsRecursive(
566
+ refModule,
567
+ moduleGraph,
568
+ depth + 1,
569
+ SideEffectDep
570
+ );
571
+ } else {
572
+ // Non-NormalModule's `getSideEffectsConnectionState` (notably
573
+ // `ConcatenatedModule` delegating to its root) re-enters the
574
+ // public method and may walk through modules that the outer
575
+ // walk has marked as evaluating. We can't observe whether
576
+ // the inner walk hit a cycle that reflected the outer's
577
+ // state, so treat the walk as cycle-tainted and skip the
578
+ // cache for safety.
579
+ _sideEffectsCircularSeen = true;
580
+ state = refModule.getSideEffectsConnectionState(moduleGraph);
581
+ }
582
+ } else {
583
+ state = dep.getModuleEvaluationSideEffectsState(moduleGraph);
584
+ }
585
+
586
+ if (state === true) {
587
+ recordSideEffectsBailout(mod, moduleGraph, dep);
588
+ mod._isEvaluatingSideEffects = false;
589
+ // `true` is monotonic — once any dep declares side effects, the
590
+ // answer is `true` regardless of how cycles resolve, so it's
591
+ // always safe to memoize.
592
+ mod._sideEffectsStateGraph = moduleGraph;
593
+ mod._sideEffectsStateValue = true;
594
+ return propagateLinearResult(linearAncestors, true, moduleGraph);
595
+ }
596
+ if (state !== ModuleGraphConnection.CIRCULAR_CONNECTION) {
597
+ current = ModuleGraphConnection.addConnectionStates(current, state);
598
+ }
599
+ }
600
+
601
+ mod._isEvaluatingSideEffects = false;
602
+
603
+ // Only memoize when no cycle has been observed anywhere in the walk
604
+ // since the public entry. A cycle anywhere can affect any ancestor's
605
+ // result (the back-edge target's contribution is hidden by
606
+ // CIRCULAR_CONNECTION short-circuiting), so this single flag is the
607
+ // conservative-but-cheap approximation of "this subtree's result is
608
+ // independent of cycle context". The public entry resets the flag.
609
+ if (!_sideEffectsCircularSeen) {
610
+ mod._sideEffectsStateGraph = moduleGraph;
611
+ mod._sideEffectsStateValue = current;
612
+ }
613
+ return propagateLinearResult(linearAncestors, current, moduleGraph);
614
+ };
615
+
129
616
  const ABSOLUTE_PATH_REGEX = /^(?:[a-z]:\\|\\\\|\/)/i;
130
617
 
131
618
  /**
@@ -229,9 +716,10 @@ const asBuffer = (input) => {
229
716
  */
230
717
 
231
718
  /**
719
+ * @template {NormalModuleTypes | ""} [T=NormalModuleTypes | ""]
232
720
  * @typedef {object} NormalModuleCreateData
233
721
  * @property {string=} layer an optional layer in which the module is
234
- * @property {NormalModuleTypes | ""} type module type. When deserializing, this is set to an empty string "".
722
+ * @property {T} type module type. When deserializing, this is set to an empty string "".
235
723
  * @property {string} request request string
236
724
  * @property {string} userRequest request intended by user (without loaders from config)
237
725
  * @property {string} rawRequest request without resolving
@@ -240,10 +728,10 @@ const asBuffer = (input) => {
240
728
  * @property {(ResourceSchemeData & Partial<ResolveRequest>)=} resourceResolveData resource resolve data
241
729
  * @property {string} context context directory for resolving
242
730
  * @property {string=} matchResource path + query of the matched resource (virtual)
243
- * @property {Parser} parser the parser used
244
- * @property {ParserOptions=} parserOptions the options of the parser used
245
- * @property {Generator} generator the generator used
246
- * @property {GeneratorOptions=} generatorOptions the options of the generator used
731
+ * @property {ParserByType[T]} parser the parser used
732
+ * @property {ParserOptionsByType[T]=} parserOptions the options of the parser used
733
+ * @property {GeneratorByType[T]} generator the generator used
734
+ * @property {GeneratorOptionsByType[T]=} generatorOptions the options of the generator used
247
735
  * @property {ResolveOptions=} resolveOptions options used for resolving requests from this module
248
736
  * @property {boolean} extractSourceMap enable/disable extracting source map
249
737
  */
@@ -405,15 +893,25 @@ class NormalModule extends Module {
405
893
  */
406
894
  this._forceBuild = true;
407
895
  /**
408
- * @private
409
896
  * @type {boolean}
410
897
  */
411
898
  this._isEvaluatingSideEffects = false;
412
899
  /**
413
- * @private
414
900
  * @type {WeakSet<ModuleGraph> | undefined}
415
901
  */
416
902
  this._addedSideEffectsBailout = undefined;
903
+ /**
904
+ * Memoizes the result of `getSideEffectsConnectionState`. The
905
+ * graph slot keys the cached value to the `ModuleGraph` it was
906
+ * computed against so stale values never leak across compilations
907
+ * — a walk that targets a different graph just overwrites both
908
+ * slots. Populated only for results computed without encountering
909
+ * a circular connection (see `walkSideEffectsRecursive`).
910
+ * @type {ModuleGraph | undefined}
911
+ */
912
+ this._sideEffectsStateGraph = undefined;
913
+ /** @type {ConnectionState | undefined} */
914
+ this._sideEffectsStateValue = undefined;
417
915
  /**
418
916
  * @private
419
917
  * @type {CodeGenerationResultData}
@@ -521,6 +1019,11 @@ class NormalModule extends Module {
521
1019
  this.parserOptions = undefined;
522
1020
  this.generator = undefined;
523
1021
  this.generatorOptions = undefined;
1022
+ // Drop the side-effects memoization so a long-lived module doesn't
1023
+ // strong-reference a stale `ModuleGraph`/`Compilation` for graphs
1024
+ // that never get re-queried with a fresh one.
1025
+ this._sideEffectsStateGraph = undefined;
1026
+ this._sideEffectsStateValue = undefined;
524
1027
  }
525
1028
 
526
1029
  /**
@@ -693,11 +1196,8 @@ class NormalModule extends Module {
693
1196
  /** @type {NormalModuleLoaderContext<T>} */
694
1197
  const loaderContext = {
695
1198
  version: 2,
696
- /**
697
- * @param {import("../declarations/LoaderContext").Schema=} schema schema
698
- * @returns {T} options
699
- */
700
- getOptions: (schema) => {
1199
+ /** @type {LoaderContext<EXPECTED_ANY>["getOptions"]} */
1200
+ getOptions: (/** @type {EXPECTED_ANY} */ schema = undefined) => {
701
1201
  const loader = this.getCurrentLoader(
702
1202
  /** @type {AnyLoaderContext} */
703
1203
  (loaderContext)
@@ -744,6 +1244,7 @@ class NormalModule extends Module {
744
1244
 
745
1245
  return /** @type {T} */ (options);
746
1246
  },
1247
+ /** @type {LoaderContext<EXPECTED_ANY>["emitWarning"]} */
747
1248
  emitWarning: (warning) => {
748
1249
  if (!(warning instanceof Error)) {
749
1250
  warning = new NonErrorEmittedError(warning);
@@ -754,6 +1255,7 @@ class NormalModule extends Module {
754
1255
  })
755
1256
  );
756
1257
  },
1258
+ /** @type {LoaderContext<EXPECTED_ANY>["emitError"]} */
757
1259
  emitError: (error) => {
758
1260
  if (!(error instanceof Error)) {
759
1261
  error = new NonErrorEmittedError(error);
@@ -764,6 +1266,7 @@ class NormalModule extends Module {
764
1266
  })
765
1267
  );
766
1268
  },
1269
+ /** @type {LoaderContext<EXPECTED_ANY>["getLogger"]} */
767
1270
  getLogger: (name) => {
768
1271
  const currentLoader = this.getCurrentLoader(
769
1272
  /** @type {AnyLoaderContext} */
@@ -775,9 +1278,11 @@ class NormalModule extends Module {
775
1278
  .join("|")
776
1279
  );
777
1280
  },
1281
+ /** @type {LoaderContext<EXPECTED_ANY>["resolve"]} */
778
1282
  resolve(context, request, callback) {
779
1283
  resolver.resolve({}, context, request, getResolveContext(), callback);
780
1284
  },
1285
+ /** @type {LoaderContext<EXPECTED_ANY>["getResolve"]} */
781
1286
  getResolve(options) {
782
1287
  const child = options ? resolver.withOptions(options) : resolver;
783
1288
  return /** @type {ReturnType<import("../declarations/LoaderContext").NormalModuleLoaderContext<T>["getResolve"]>} */ (
@@ -807,6 +1312,7 @@ class NormalModule extends Module {
807
1312
  }
808
1313
  );
809
1314
  },
1315
+ /** @type {LoaderContext<EXPECTED_ANY>["emitFile"]} */
810
1316
  emitFile: (name, content, sourceMap, assetInfo) => {
811
1317
  const buildInfo = /** @type {BuildInfo} */ (this.buildInfo);
812
1318
 
@@ -1453,47 +1959,19 @@ class NormalModule extends Module {
1453
1959
  * @returns {ConnectionState} how this module should be connected to referencing modules when consumed for side-effects only
1454
1960
  */
1455
1961
  getSideEffectsConnectionState(moduleGraph) {
1456
- if (this.factoryMeta !== undefined) {
1457
- if (this.factoryMeta.sideEffectFree) return false;
1458
- if (this.factoryMeta.sideEffectFree === false) return true;
1459
- }
1460
- if (this.buildMeta !== undefined && this.buildMeta.sideEffectFree) {
1461
- if (this._isEvaluatingSideEffects) {
1462
- return ModuleGraphConnection.CIRCULAR_CONNECTION;
1463
- }
1464
- this._isEvaluatingSideEffects = true;
1465
- /** @type {ConnectionState} */
1466
- let current = false;
1467
- for (const dep of this.dependencies) {
1468
- const state = dep.getModuleEvaluationSideEffectsState(moduleGraph);
1469
- if (state === true) {
1470
- if (
1471
- this._addedSideEffectsBailout === undefined
1472
- ? ((this._addedSideEffectsBailout = new WeakSet()), true)
1473
- : !this._addedSideEffectsBailout.has(moduleGraph)
1474
- ) {
1475
- this._addedSideEffectsBailout.add(moduleGraph);
1476
- moduleGraph
1477
- .getOptimizationBailout(this)
1478
- .push(
1479
- () =>
1480
- `Dependency (${
1481
- dep.type
1482
- }) with side effects at ${formatLocation(dep.loc)}`
1483
- );
1484
- }
1485
- this._isEvaluatingSideEffects = false;
1486
- return true;
1487
- } else if (state !== ModuleGraphConnection.CIRCULAR_CONNECTION) {
1488
- current = ModuleGraphConnection.addConnectionStates(current, state);
1489
- }
1490
- }
1491
- this._isEvaluatingSideEffects = false;
1492
- // When caching is implemented here, make sure to not cache when
1493
- // at least one circular connection was in the loop above
1494
- return current;
1495
- }
1496
- return true;
1962
+ // Save and restore the cycle-tracking flag so that re-entrant calls
1963
+ // (e.g. `ConcatenatedModule.getSideEffectsConnectionState` delegating
1964
+ // back through here) don't clobber the outer walk's state.
1965
+ const savedCircular = _sideEffectsCircularSeen;
1966
+ _sideEffectsCircularSeen = false;
1967
+ const result = walkSideEffectsRecursive(
1968
+ this,
1969
+ moduleGraph,
1970
+ 0,
1971
+ getHarmonyImportSideEffectDependency()
1972
+ );
1973
+ _sideEffectsCircularSeen = savedCircular;
1974
+ return result;
1497
1975
  }
1498
1976
 
1499
1977
  /**