webpack 5.107.1 → 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.
@@ -86,7 +86,6 @@ const parseJson = require("./util/parseJson");
86
86
  /** @typedef {import("./Module").UnsafeCacheData} UnsafeCacheData */
87
87
  /** @typedef {import("./ModuleGraph")} ModuleGraph */
88
88
  /** @typedef {import("./ModuleGraphConnection").ConnectionState} ConnectionState */
89
- /** @typedef {Iterator<SideEffectsWalk, ConnectionState, ConnectionState>} SideEffectsWalk */
90
89
  /** @typedef {import("./NormalModuleFactory")} NormalModuleFactory */
91
90
  /** @typedef {import("./NormalModuleFactory").NormalModuleTypes} NormalModuleTypes */
92
91
  /** @typedef {import("./NormalModuleFactory").ParserByType} ParserByType */
@@ -156,27 +155,401 @@ const recordSideEffectsBailout = (mod, moduleGraph, dep) => {
156
155
  );
157
156
  };
158
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
+
159
168
  /**
160
- * Generator form of `getSideEffectsConnectionState` descends through
161
- * `HarmonyImportSideEffectDependency` via `yield` so the trampoline in
162
- * `getSideEffectsConnectionState` can drive the walk iteratively (#20986).
163
- * @param {NormalModule} mod the module being evaluated
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
164
176
  * @param {ModuleGraph} moduleGraph the module graph
165
- * @returns {SideEffectsWalk} the generator
177
+ * @returns {ConnectionState} the side-effects connection state
166
178
  */
167
- function* walkSideEffects(mod, moduleGraph) {
168
- if (mod.factoryMeta !== undefined) {
169
- if (mod.factoryMeta.sideEffectFree) return false;
170
- if (mod.factoryMeta.sideEffectFree === false) return true;
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
+ }
171
387
  }
172
- if (!(mod.buildMeta !== undefined && mod.buildMeta.sideEffectFree)) {
173
- return true;
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;
174
541
  }
175
- if (mod._isEvaluatingSideEffects) {
176
- return ModuleGraphConnection.CIRCULAR_CONNECTION;
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
+ );
177
551
  }
178
552
 
179
- const SideEffectDep = getHarmonyImportSideEffectDependency();
180
553
  mod._isEvaluatingSideEffects = true;
181
554
  /** @type {ConnectionState} */
182
555
  let current = false;
@@ -189,8 +562,21 @@ function* walkSideEffects(mod, moduleGraph) {
189
562
  if (!refModule) {
190
563
  state = true;
191
564
  } else if (refModule instanceof NormalModule) {
192
- state = yield walkSideEffects(refModule, moduleGraph);
565
+ state = walkSideEffectsRecursive(
566
+ refModule,
567
+ moduleGraph,
568
+ depth + 1,
569
+ SideEffectDep
570
+ );
193
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;
194
580
  state = refModule.getSideEffectsConnectionState(moduleGraph);
195
581
  }
196
582
  } else {
@@ -200,7 +586,12 @@ function* walkSideEffects(mod, moduleGraph) {
200
586
  if (state === true) {
201
587
  recordSideEffectsBailout(mod, moduleGraph, dep);
202
588
  mod._isEvaluatingSideEffects = false;
203
- return true;
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);
204
595
  }
205
596
  if (state !== ModuleGraphConnection.CIRCULAR_CONNECTION) {
206
597
  current = ModuleGraphConnection.addConnectionStates(current, state);
@@ -208,10 +599,19 @@ function* walkSideEffects(mod, moduleGraph) {
208
599
  }
209
600
 
210
601
  mod._isEvaluatingSideEffects = false;
211
- // When caching is implemented here, make sure to not cache when
212
- // at least one circular connection was folded into `current`.
213
- return current;
214
- }
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
+ };
215
615
 
216
616
  const ABSOLUTE_PATH_REGEX = /^(?:[a-z]:\\|\\\\|\/)/i;
217
617
 
@@ -500,6 +900,18 @@ class NormalModule extends Module {
500
900
  * @type {WeakSet<ModuleGraph> | undefined}
501
901
  */
502
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;
503
915
  /**
504
916
  * @private
505
917
  * @type {CodeGenerationResultData}
@@ -607,6 +1019,11 @@ class NormalModule extends Module {
607
1019
  this.parserOptions = undefined;
608
1020
  this.generator = undefined;
609
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;
610
1027
  }
611
1028
 
612
1029
  /**
@@ -779,11 +1196,8 @@ class NormalModule extends Module {
779
1196
  /** @type {NormalModuleLoaderContext<T>} */
780
1197
  const loaderContext = {
781
1198
  version: 2,
782
- /**
783
- * @param {import("../declarations/LoaderContext").Schema=} schema schema
784
- * @returns {T} options
785
- */
786
- getOptions: (schema) => {
1199
+ /** @type {LoaderContext<EXPECTED_ANY>["getOptions"]} */
1200
+ getOptions: (/** @type {EXPECTED_ANY} */ schema = undefined) => {
787
1201
  const loader = this.getCurrentLoader(
788
1202
  /** @type {AnyLoaderContext} */
789
1203
  (loaderContext)
@@ -830,6 +1244,7 @@ class NormalModule extends Module {
830
1244
 
831
1245
  return /** @type {T} */ (options);
832
1246
  },
1247
+ /** @type {LoaderContext<EXPECTED_ANY>["emitWarning"]} */
833
1248
  emitWarning: (warning) => {
834
1249
  if (!(warning instanceof Error)) {
835
1250
  warning = new NonErrorEmittedError(warning);
@@ -840,6 +1255,7 @@ class NormalModule extends Module {
840
1255
  })
841
1256
  );
842
1257
  },
1258
+ /** @type {LoaderContext<EXPECTED_ANY>["emitError"]} */
843
1259
  emitError: (error) => {
844
1260
  if (!(error instanceof Error)) {
845
1261
  error = new NonErrorEmittedError(error);
@@ -850,6 +1266,7 @@ class NormalModule extends Module {
850
1266
  })
851
1267
  );
852
1268
  },
1269
+ /** @type {LoaderContext<EXPECTED_ANY>["getLogger"]} */
853
1270
  getLogger: (name) => {
854
1271
  const currentLoader = this.getCurrentLoader(
855
1272
  /** @type {AnyLoaderContext} */
@@ -861,9 +1278,11 @@ class NormalModule extends Module {
861
1278
  .join("|")
862
1279
  );
863
1280
  },
1281
+ /** @type {LoaderContext<EXPECTED_ANY>["resolve"]} */
864
1282
  resolve(context, request, callback) {
865
1283
  resolver.resolve({}, context, request, getResolveContext(), callback);
866
1284
  },
1285
+ /** @type {LoaderContext<EXPECTED_ANY>["getResolve"]} */
867
1286
  getResolve(options) {
868
1287
  const child = options ? resolver.withOptions(options) : resolver;
869
1288
  return /** @type {ReturnType<import("../declarations/LoaderContext").NormalModuleLoaderContext<T>["getResolve"]>} */ (
@@ -893,6 +1312,7 @@ class NormalModule extends Module {
893
1312
  }
894
1313
  );
895
1314
  },
1315
+ /** @type {LoaderContext<EXPECTED_ANY>["emitFile"]} */
896
1316
  emitFile: (name, content, sourceMap, assetInfo) => {
897
1317
  const buildInfo = /** @type {BuildInfo} */ (this.buildInfo);
898
1318
 
@@ -1539,21 +1959,19 @@ class NormalModule extends Module {
1539
1959
  * @returns {ConnectionState} how this module should be connected to referencing modules when consumed for side-effects only
1540
1960
  */
1541
1961
  getSideEffectsConnectionState(moduleGraph) {
1542
- // Trampoline `walkSideEffects` so the descent doesn't consume the
1543
- // call stack (#20986).
1544
- const stack = [walkSideEffects(this, moduleGraph)];
1545
- /** @type {ConnectionState} */
1546
- let r = false;
1547
- while (stack.length > 0) {
1548
- const step = stack[stack.length - 1].next(r);
1549
- if (step.done) {
1550
- stack.pop();
1551
- r = step.value;
1552
- } else {
1553
- stack.push(step.value);
1554
- }
1555
- }
1556
- return r;
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;
1557
1975
  }
1558
1976
 
1559
1977
  /**
@@ -150,7 +150,7 @@ const createReportBar = (name, color) => {
150
150
  * Creates a default handler.
151
151
  * @param {boolean | null | undefined} profile need profile
152
152
  * @param {Logger} logger logger
153
- * @param {ProgressBarOptions | false} progressBar render bar
153
+ * @param {ProgressBarOptions | false=} progressBar render bar
154
154
  * @returns {HandlerFn} default handler
155
155
  */
156
156
  const createDefaultHandler = (profile, logger, progressBar) => {