webpack 5.108.3 → 5.109.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 (191) hide show
  1. package/lib/APIPlugin.js +48 -29
  2. package/lib/CacheFacade.js +8 -0
  3. package/lib/ChunkGroup.js +1 -0
  4. package/lib/CompatibilityPlugin.js +31 -29
  5. package/lib/Compilation.js +50 -8
  6. package/lib/ConstPlugin.js +11 -11
  7. package/lib/ContextModule.js +239 -6
  8. package/lib/ContextModuleFactory.js +139 -14
  9. package/lib/DefinePlugin.js +396 -101
  10. package/lib/Entrypoint.js +1 -1
  11. package/lib/ExportsInfoApiPlugin.js +2 -2
  12. package/lib/ExternalModule.js +107 -5
  13. package/lib/ExternalModuleFactoryPlugin.js +20 -1
  14. package/lib/FileSystemInfo.js +130 -74
  15. package/lib/FlagDependencyExportsPlugin.js +55 -8
  16. package/lib/HotModuleReplacementPlugin.js +18 -15
  17. package/lib/LazyBarrel.js +30 -3
  18. package/lib/Module.js +3 -0
  19. package/lib/ModuleFilenameHelpers.js +2 -2
  20. package/lib/ModuleTypeConstants.js +8 -1
  21. package/lib/MultiCompiler.js +13 -2
  22. package/lib/MultiWatching.js +1 -0
  23. package/lib/NodeStuffPlugin.js +51 -33
  24. package/lib/NormalModule.js +169 -405
  25. package/lib/NormalModuleFactory.js +76 -2
  26. package/lib/ProgressPlugin.js +211 -119
  27. package/lib/ProvidePlugin.js +2 -2
  28. package/lib/RuntimeGlobals.js +47 -0
  29. package/lib/RuntimePlugin.js +33 -0
  30. package/lib/RuntimeTemplate.js +304 -16
  31. package/lib/UseStrictPlugin.js +1 -1
  32. package/lib/WebpackIsIncludedPlugin.js +1 -1
  33. package/lib/WebpackOptionsApply.js +24 -19
  34. package/lib/asset/AssetGenerator.js +110 -28
  35. package/lib/asset/AssetModulesPlugin.js +92 -2
  36. package/lib/asset/WebManifestGenerator.js +164 -0
  37. package/lib/asset/WebManifestParser.js +130 -0
  38. package/lib/async-modules/AwaitDependenciesInitFragment.js +14 -4
  39. package/lib/async-modules/isGeneratorLowered.js +26 -0
  40. package/lib/cache/PackFileCacheStrategy.js +9 -3
  41. package/lib/config/browserslistTargetHandler.js +40 -0
  42. package/lib/config/defaults.js +222 -32
  43. package/lib/config/normalization.js +26 -0
  44. package/lib/config/target.js +9 -1
  45. package/lib/css/CssGenerator.js +73 -73
  46. package/lib/css/CssLoadingRuntimeModule.js +5 -1
  47. package/lib/css/CssModulesPlugin.js +92 -53
  48. package/lib/css/CssParser.js +843 -302
  49. package/lib/css/syntax.js +1219 -483
  50. package/lib/dependencies/AMDDefineDependencyParserPlugin.js +6 -6
  51. package/lib/dependencies/AMDPlugin.js +2 -2
  52. package/lib/dependencies/AMDRequireDependenciesBlockParserPlugin.js +13 -13
  53. package/lib/dependencies/CommonJsDependencyHelpers.js +39 -0
  54. package/lib/dependencies/CommonJsExportsDependency.js +47 -2
  55. package/lib/dependencies/CommonJsExportsParserPlugin.js +10 -10
  56. package/lib/dependencies/CommonJsImportsParserPlugin.js +18 -18
  57. package/lib/dependencies/CommonJsPlugin.js +2 -2
  58. package/lib/dependencies/CommonJsRequireDependency.js +26 -1
  59. package/lib/dependencies/CommonJsSelfReferenceDependency.js +32 -1
  60. package/lib/dependencies/ContextDependencyHelpers.js +3 -3
  61. package/lib/dependencies/CreateRequireParserPlugin.js +6 -6
  62. package/lib/dependencies/CssIcssExportDependency.js +190 -87
  63. package/lib/dependencies/CssUrlDependency.js +35 -4
  64. package/lib/dependencies/ExternalModuleInitFragmentDependency.js +2 -2
  65. package/lib/dependencies/HarmonyCompatibilityDependency.js +32 -14
  66. package/lib/dependencies/HarmonyDetectionParserPlugin.js +43 -13
  67. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +7 -19
  68. package/lib/dependencies/HarmonyImportDependency.js +12 -1
  69. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +34 -107
  70. package/lib/dependencies/HarmonyImportGuard.js +192 -20
  71. package/lib/dependencies/HarmonyModulesPlugin.js +6 -0
  72. package/lib/dependencies/HarmonyTopLevelThisParserPlugin.js +1 -1
  73. package/lib/dependencies/HtmlEntryDependency.js +797 -172
  74. package/lib/dependencies/HtmlSourceDependency.js +26 -0
  75. package/lib/dependencies/ImportDependency.js +43 -1
  76. package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +31 -18
  77. package/lib/dependencies/ImportMetaContextPlugin.js +38 -7
  78. package/lib/dependencies/ImportMetaGlobDependency.js +81 -0
  79. package/lib/dependencies/ImportMetaGlobDependencyParserPlugin.js +78 -0
  80. package/lib/dependencies/ImportMetaGlobHelpers.js +660 -0
  81. package/lib/dependencies/ImportMetaPlugin.js +532 -180
  82. package/lib/dependencies/ImportMetaResolveDependency.js +90 -0
  83. package/lib/dependencies/ImportParserPlugin.js +87 -26
  84. package/lib/dependencies/ImportPhase.js +10 -8
  85. package/lib/dependencies/LoaderPlugin.js +5 -1
  86. package/lib/dependencies/RequireContextDependencyParserPlugin.js +1 -1
  87. package/lib/dependencies/RequireEnsureDependenciesBlockParserPlugin.js +3 -6
  88. package/lib/dependencies/RequireIncludeDependencyParserPlugin.js +4 -11
  89. package/lib/dependencies/SystemPlugin.js +2 -4
  90. package/lib/dependencies/TopLevelAwaitDependency.js +93 -0
  91. package/lib/dependencies/URLDependency.js +141 -21
  92. package/lib/dependencies/{WorkerPlugin.js → WorkerAndWorkletPlugin.js} +324 -112
  93. package/lib/dependencies/WorkerDependency.js +86 -18
  94. package/lib/dependencies/WorkletDependency.js +278 -0
  95. package/lib/dll/DelegatedModuleFactoryPlugin.js +19 -0
  96. package/lib/errors/EnvironmentNotSupportAsyncWarning.js +1 -1
  97. package/lib/errors/LoaderLoadingError.js +20 -0
  98. package/lib/errors/ModuleParseError.js +39 -22
  99. package/lib/errors/NodeStuffInWebError.js +1 -1
  100. package/lib/esm/ModuleChunkFormatPlugin.js +47 -14
  101. package/lib/esm/ModuleChunkLoadingPlugin.js +8 -1
  102. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +29 -1
  103. package/lib/hmr/lazyCompilationBackend.js +32 -8
  104. package/lib/html/HtmlGenerator.js +940 -91
  105. package/lib/html/HtmlModule.js +3 -2
  106. package/lib/html/HtmlModulesPlugin.js +625 -170
  107. package/lib/html/HtmlParser.js +812 -340
  108. package/lib/html/favicon.svg +1 -0
  109. package/lib/html/syntax.js +2116 -834
  110. package/lib/ids/IdHelpers.js +1 -1
  111. package/lib/index.js +6 -0
  112. package/lib/javascript/ArrayPushCallbackChunkFormatPlugin.js +4 -5
  113. package/lib/javascript/BasicEvaluatedExpression.js +366 -48
  114. package/lib/javascript/JavascriptGenerator.js +197 -49
  115. package/lib/javascript/JavascriptModulesPlugin.js +178 -24
  116. package/lib/javascript/JavascriptParser.js +711 -355
  117. package/lib/javascript/JavascriptParserHelpers.js +3 -6
  118. package/lib/javascript/syntax.js +4380 -0
  119. package/lib/library/ModuleLibraryPlugin.js +38 -1
  120. package/lib/loaders/LoaderRunner.js +714 -0
  121. package/lib/loaders/loadLoader.js +100 -0
  122. package/lib/node/ReadFileCompileAsyncWasmPlugin.js +19 -0
  123. package/lib/node/ReadFileCompileWasmPlugin.js +14 -0
  124. package/lib/optimize/ConcatenatedModule.js +120 -16
  125. package/lib/optimize/FlagIncludedChunksPlugin.js +13 -3
  126. package/lib/optimize/InnerGraphPlugin.js +50 -30
  127. package/lib/optimize/ModuleConcatenationPlugin.js +69 -3
  128. package/lib/optimize/SideEffectsFlagPlugin.js +86 -43
  129. package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +15 -0
  130. package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +24 -13
  131. package/lib/prefetch/ResourceHintPlugin.js +663 -0
  132. package/lib/prefetch/ResourceHintRuntimeModule.js +149 -0
  133. package/lib/prefetch/StartupAssetHintRuntimeModule.js +210 -0
  134. package/lib/prefetch/parseResourceHintOptions.js +103 -0
  135. package/lib/rules/RuleSetCompiler.js +114 -0
  136. package/lib/runtime/AsyncModuleGeneratorRuntimeModule.js +56 -0
  137. package/lib/runtime/AsyncModuleRuntimeModule.js +24 -8
  138. package/lib/runtime/AutoPublicPathRuntimeModule.js +7 -1
  139. package/lib/runtime/CommonJsWrapRuntimeModule.js +37 -0
  140. package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +59 -0
  141. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +204 -8
  142. package/lib/runtime/SetAnonymousDefaultNameRuntimeModule.js +4 -1
  143. package/lib/schemes/HttpUriPlugin.js +40 -1
  144. package/lib/schemes/VirtualUrlPlugin.js +1 -2
  145. package/lib/serialization/FileMiddleware.js +29 -3
  146. package/lib/stats/DefaultStatsFactoryPlugin.js +32 -3
  147. package/lib/stats/DefaultStatsPresetPlugin.js +2 -0
  148. package/lib/stats/DefaultStatsPrinterPlugin.js +12 -2
  149. package/lib/stats/StatsFactory.js +15 -8
  150. package/lib/url/URLParserPlugin.js +98 -19
  151. package/lib/util/AsyncQueue.js +122 -72
  152. package/lib/util/LocConverter.js +9 -8
  153. package/lib/util/SetHelpers.js +1 -1
  154. package/lib/util/SourceProcessor.js +41 -28
  155. package/lib/util/StackedMap.js +122 -48
  156. package/lib/util/binarySearchBounds.js +2 -2
  157. package/lib/util/concatenate.js +40 -14
  158. package/lib/util/createHooksRegistry.js +13 -5
  159. package/lib/util/dataURL.js +2 -2
  160. package/lib/util/deterministicGrouping.js +7 -5
  161. package/lib/util/formatSize.js +6 -2
  162. package/lib/util/globUtils.js +710 -0
  163. package/lib/util/identifier.js +23 -6
  164. package/lib/util/implicitTypeLoaderFallback.js +62 -0
  165. package/lib/util/internalSerializables.js +88 -71
  166. package/lib/util/magicComment.js +121 -25
  167. package/lib/util/numberHash.js +2 -2
  168. package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +24 -18
  169. package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +33 -22
  170. package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +79 -16
  171. package/lib/web/FetchCompileAsyncWasmPlugin.js +19 -0
  172. package/lib/web/FetchCompileWasmPlugin.js +14 -0
  173. package/lib/web/JsonpChunkLoadingRuntimeModule.js +2 -0
  174. package/lib/webpack.js +65 -0
  175. package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +3 -0
  176. package/module.d.ts +21 -0
  177. package/package.json +14 -13
  178. package/schemas/WebpackOptions.check.js +1 -1
  179. package/schemas/WebpackOptions.json +759 -36
  180. package/schemas/plugins/HtmlParserOptions.check.js +1 -1
  181. package/schemas/plugins/ProgressPlugin.check.js +1 -1
  182. package/schemas/plugins/ProgressPlugin.json +17 -1
  183. package/schemas/plugins/container/ContainerReferencePlugin.check.js +1 -1
  184. package/schemas/plugins/container/ContainerReferencePlugin.json +1 -0
  185. package/schemas/plugins/container/ExternalsType.check.js +1 -1
  186. package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
  187. package/schemas/plugins/container/ModuleFederationPlugin.json +1 -0
  188. package/schemas/plugins/css/CssAutoOrModuleParserOptions.check.js +1 -1
  189. package/schemas/plugins/css/CssModuleParserOptions.check.js +1 -1
  190. package/schemas/plugins/css/CssParserOptions.check.js +1 -1
  191. package/types.d.ts +1300 -325
@@ -6,7 +6,6 @@
6
6
  "use strict";
7
7
 
8
8
  const querystring = require("querystring");
9
- const { getContext, runLoaders } = require("loader-runner");
10
9
  const {
11
10
  AsyncSeriesBailHook,
12
11
  HookMap,
@@ -31,6 +30,11 @@ const ModuleParseError = require("./errors/ModuleParseError");
31
30
  const ModuleWarning = require("./errors/ModuleWarning");
32
31
  const NonErrorEmittedError = require("./errors/NonErrorEmittedError");
33
32
  const UnhandledSchemeError = require("./errors/UnhandledSchemeError");
33
+ const {
34
+ createLoaderContext,
35
+ getContext,
36
+ runLoaders
37
+ } = require("./loaders/LoaderRunner");
34
38
  const LazySet = require("./util/LazySet");
35
39
  const { isSubset } = require("./util/SetHelpers");
36
40
  const { getScheme } = require("./util/URLAbsoluteSpecifier");
@@ -157,83 +161,132 @@ const recordSideEffectsBailout = (mod, moduleGraph, dep) => {
157
161
  );
158
162
  };
159
163
 
160
- // Maximum recursive descent depth before switching to the iterative walker.
161
- // #20986 reported overflow around 1300 modules in webpack 5.107.0 where each
162
- // step consumed two stack frames (`NormalModule.getSideEffectsConnectionState`
163
- // plus `HarmonyImportSideEffectDependency.getModuleEvaluationSideEffectsState`).
164
- // `walkSideEffectsRecursive` folds the second call into the first and uses
165
- // one frame per module, so this limit caps the native stack at half the depth
166
- // where the original code overflowed — well within the safe range across
167
- // platforms while keeping the common (shallow) case purely recursive.
168
- const SIDE_EFFECTS_RECURSION_LIMIT = 2000;
169
-
170
164
  /**
171
- * Iterative form of the side-effects walker. Used as a fallback once the
172
- * recursive form reaches `SIDE_EFFECTS_RECURSION_LIMIT` so deep chains
173
- * (#20986) don't overflow V8's stack. Safe to enter while ancestors set
174
- * `_isEvaluatingSideEffects` on their own modules — those are treated as
175
- * `CIRCULAR_CONNECTION` if revisited, matching the original recursive
176
- * behavior.
177
- * @param {NormalModule} rootMod the module to walk
165
+ * Walks the `HarmonyImportSideEffectDependency` graph to decide how `rootMod`
166
+ * connects to a referrer when consumed for its side effects only.
167
+ *
168
+ * Iterative (explicit work stack) so deep import chains never approach V8's
169
+ * recursion limit — the overflow behind #20986 — with no separate recursive
170
+ * form or depth cap. Cycles use Tarjan's `index`/`lowlink`: a module's result
171
+ * is memoized on `_sideEffectsStateGraph` only when it is context-independent
172
+ * (cycle-free, or the root of its strongly-connected component), i.e. exactly
173
+ * when the result cannot depend on which ancestors are mid-walk.
174
+ *
175
+ * The previous walker disabled memoization for the whole walk the moment any
176
+ * cycle appeared (one global `circular` flag). With diamond-shaped graphs that
177
+ * made the walk exponential, so a single circular import could blow up an
178
+ * otherwise-linear graph. Per-module lowlink keeps the cycle-free majority
179
+ * memoized while a cycle exists elsewhere.
180
+ *
181
+ * Caching under `lowlink === index` is conservative: an imprecise lowlink can
182
+ * only skip caching (a later recompute yields the same value), never memoize a
183
+ * state that depended on stack context. `true` is monotonic and always cached.
184
+ * A non-`NormalModule` dependency (e.g. `ConcatenatedModule` re-entering this
185
+ * method) is treated as a taint so its chain is never memoized, and any module
186
+ * already flagged `_isEvaluatingSideEffects` (this walk or an outer re-entrant
187
+ * one) counts as circular, matching the previous walker.
188
+ * @param {NormalModule} rootMod the module being walked
178
189
  * @param {ModuleGraph} moduleGraph the module graph
179
- * @param {SideEffectsWalkContext} ctx per-walk cycle-tracking context
190
+ * @param {EXPECTED_ANY} SideEffectDep `HarmonyImportSideEffectDependency` constructor, resolved once at the entry
180
191
  * @returns {ConnectionState} the side-effects connection state
181
192
  */
182
- const walkSideEffectsIterative = (rootMod, moduleGraph, ctx) => {
183
- const SideEffectDep = getHarmonyImportSideEffectDependency();
193
+ const walkSideEffects = (rootMod, moduleGraph, SideEffectDep) => {
194
+ // Context-independent state without descending; `undefined` => walk deps.
195
+ /**
196
+ * @param {NormalModule} mod module
197
+ * @returns {ConnectionState | undefined} resolved state or undefined
198
+ */
199
+ const quick = (mod) => {
200
+ if (mod._sideEffectsStateGraph === moduleGraph) {
201
+ return /** @type {ConnectionState} */ (mod._sideEffectsStateValue);
202
+ }
203
+ const fm = mod.factoryMeta;
204
+ if (fm !== undefined) {
205
+ if (fm.sideEffectFree) return false;
206
+ if (fm.sideEffectFree === false) return true;
207
+ }
208
+ if (!(mod.buildMeta !== undefined && mod.buildMeta.sideEffectFree)) {
209
+ return true;
210
+ }
211
+ return undefined;
212
+ };
184
213
 
214
+ const q0 = quick(rootMod);
215
+ if (q0 !== undefined) return q0;
216
+
217
+ /** @type {Map<NormalModule, number>} module -> Tarjan index */
218
+ const indexOf = new Map();
219
+ /** @type {Map<NormalModule, number>} module -> Tarjan lowlink */
220
+ const lowOf = new Map();
221
+ // Parallel frame arrays, to avoid a per-frame object allocation.
185
222
  /** @type {NormalModule[]} */
186
- const modStack = [rootMod];
223
+ const fMod = [];
187
224
  /** @type {Dependency[][]} */
188
- const depsStack = [rootMod.dependencies];
189
- const indexStack = [0];
225
+ const fDeps = [];
226
+ /** @type {number[]} */
227
+ const fIdx = [];
190
228
  /** @type {ConnectionState[]} */
191
- const currentStack = [false];
192
- rootMod._isEvaluatingSideEffects = true;
229
+ const fCur = [];
230
+ let counter = 0;
193
231
 
194
232
  /**
195
- * Result from a just-popped child frame, to be applied to the new
196
- * top's current dep. `undefined` means "no pending; advance".
233
+ * @param {NormalModule} mod module to push a frame for
234
+ */
235
+ const enter = (mod) => {
236
+ indexOf.set(mod, counter);
237
+ lowOf.set(mod, counter);
238
+ counter++;
239
+ mod._isEvaluatingSideEffects = true;
240
+ fMod.push(mod);
241
+ fDeps.push(mod.dependencies);
242
+ fIdx.push(0);
243
+ fCur.push(false);
244
+ };
245
+
246
+ enter(rootMod);
247
+ /** @type {ConnectionState} the just-popped frame's result */
248
+ let result = false;
249
+ /**
250
+ * Result of a just-popped child frame, to apply to the new top's current
251
+ * dep. `undefined` means "no pending; advance normally".
197
252
  * @type {ConnectionState | undefined}
198
253
  */
199
254
  let pending;
200
255
 
201
- while (modStack.length > 0) {
202
- const top = modStack.length - 1;
203
- const topMod = modStack[top];
204
- const deps = depsStack[top];
205
- let index = indexStack[top];
206
- let current = currentStack[top];
256
+ while (fMod.length > 0) {
257
+ const top = fMod.length - 1;
258
+ const mod = fMod[top];
259
+ const deps = fDeps[top];
260
+ let i = fIdx[top];
261
+ let current = fCur[top];
207
262
 
263
+ // Apply a popped child's result to the dep that spawned it.
208
264
  if (pending !== undefined) {
209
265
  const state = pending;
210
266
  pending = undefined;
211
- const dep = deps[index];
212
-
267
+ const dep = deps[i];
213
268
  if (state === true) {
214
- recordSideEffectsBailout(topMod, moduleGraph, dep);
215
- topMod._isEvaluatingSideEffects = false;
216
- // `true` is monotonic — safe to memoize regardless of cycle
217
- // status, matching the direct-bailout branch below.
218
- topMod._sideEffectsStateGraph = moduleGraph;
219
- topMod._sideEffectsStateValue = true;
220
- modStack.pop();
221
- depsStack.pop();
222
- indexStack.pop();
223
- currentStack.pop();
269
+ recordSideEffectsBailout(mod, moduleGraph, dep);
270
+ mod._sideEffectsStateGraph = moduleGraph;
271
+ mod._sideEffectsStateValue = true;
272
+ mod._isEvaluatingSideEffects = false;
273
+ fMod.pop();
274
+ fDeps.pop();
275
+ fIdx.pop();
276
+ fCur.pop();
277
+ result = true;
224
278
  pending = true;
225
279
  continue;
226
280
  }
227
281
  if (state !== ModuleGraphConnection.CIRCULAR_CONNECTION) {
228
282
  current = ModuleGraphConnection.addConnectionStates(current, state);
229
283
  }
230
- index++;
284
+ i++;
231
285
  }
232
286
 
233
287
  let descended = false;
234
- const depCount = deps.length;
235
- while (index < depCount) {
236
- const dep = deps[index];
288
+ while (i < deps.length) {
289
+ const dep = deps[i];
237
290
  /** @type {ConnectionState} */
238
291
  let state;
239
292
 
@@ -242,79 +295,53 @@ const walkSideEffectsIterative = (rootMod, moduleGraph, ctx) => {
242
295
  if (!refModule) {
243
296
  state = true;
244
297
  } else if (refModule instanceof NormalModule) {
245
- // Cache hit
246
- if (refModule._sideEffectsStateGraph === moduleGraph) {
247
- state = /** @type {ConnectionState} */ (
248
- refModule._sideEffectsStateValue
249
- );
250
- }
251
- // Fast-path checks inlined to avoid the helper call.
252
- else if (refModule.factoryMeta !== undefined) {
253
- if (refModule.factoryMeta.sideEffectFree) {
254
- state = false;
255
- } else if (refModule.factoryMeta.sideEffectFree === false) {
256
- state = true;
257
- } else if (
258
- !(
259
- refModule.buildMeta !== undefined &&
260
- refModule.buildMeta.sideEffectFree
298
+ if (refModule._isEvaluatingSideEffects) {
299
+ // On the current stack (or an outer re-entrant walk) => cycle.
300
+ state = ModuleGraphConnection.CIRCULAR_CONNECTION;
301
+ const ri = indexOf.get(refModule);
302
+ lowOf.set(
303
+ mod,
304
+ Math.min(
305
+ /** @type {number} */ (lowOf.get(mod)),
306
+ ri === undefined ? -1 : ri
261
307
  )
262
- ) {
263
- state = true;
264
- } else if (refModule._isEvaluatingSideEffects) {
265
- ctx.circular = true;
266
- state = ModuleGraphConnection.CIRCULAR_CONNECTION;
308
+ );
309
+ } else {
310
+ const q = quick(refModule);
311
+ if (q !== undefined) {
312
+ state = q;
267
313
  } else {
268
- // Descend
269
- indexStack[top] = index;
270
- currentStack[top] = current;
271
- refModule._isEvaluatingSideEffects = true;
272
- modStack.push(refModule);
273
- depsStack.push(refModule.dependencies);
274
- indexStack.push(0);
275
- currentStack.push(false);
314
+ // Descend: pause this frame, walk the child next.
315
+ fIdx[top] = i;
316
+ fCur[top] = current;
317
+ enter(refModule);
276
318
  descended = true;
277
319
  break;
278
320
  }
279
- } else if (
280
- !(
281
- refModule.buildMeta !== undefined &&
282
- refModule.buildMeta.sideEffectFree
283
- )
284
- ) {
285
- state = true;
286
- } else if (refModule._isEvaluatingSideEffects) {
287
- ctx.circular = true;
288
- state = ModuleGraphConnection.CIRCULAR_CONNECTION;
289
- } else {
290
- // Descend
291
- indexStack[top] = index;
292
- currentStack[top] = current;
293
- refModule._isEvaluatingSideEffects = true;
294
- modStack.push(refModule);
295
- depsStack.push(refModule.dependencies);
296
- indexStack.push(0);
297
- currentStack.push(false);
298
- descended = true;
299
- break;
300
321
  }
301
322
  } else {
302
- ctx.circular = true;
323
+ // Non-NormalModule (e.g. ConcatenatedModule delegating back
324
+ // through here) resolves via a re-entrant walk that may see our
325
+ // in-progress modules as circular, so taint this chain: never
326
+ // memoize it.
303
327
  state = refModule.getSideEffectsConnectionState(moduleGraph);
328
+ lowOf.set(mod, -1);
304
329
  }
305
330
  } else {
306
331
  state = dep.getModuleEvaluationSideEffectsState(moduleGraph);
307
332
  }
308
333
 
309
334
  if (state === true) {
310
- recordSideEffectsBailout(topMod, moduleGraph, dep);
311
- topMod._isEvaluatingSideEffects = false;
312
- topMod._sideEffectsStateGraph = moduleGraph;
313
- topMod._sideEffectsStateValue = true;
314
- modStack.pop();
315
- depsStack.pop();
316
- indexStack.pop();
317
- currentStack.pop();
335
+ recordSideEffectsBailout(mod, moduleGraph, dep);
336
+ // `true` is monotonic — always safe to memoize.
337
+ mod._sideEffectsStateGraph = moduleGraph;
338
+ mod._sideEffectsStateValue = true;
339
+ mod._isEvaluatingSideEffects = false;
340
+ fMod.pop();
341
+ fDeps.pop();
342
+ fIdx.pop();
343
+ fCur.pop();
344
+ result = true;
318
345
  pending = true;
319
346
  descended = true;
320
347
  break;
@@ -322,304 +349,38 @@ const walkSideEffectsIterative = (rootMod, moduleGraph, ctx) => {
322
349
  if (state !== ModuleGraphConnection.CIRCULAR_CONNECTION) {
323
350
  current = ModuleGraphConnection.addConnectionStates(current, state);
324
351
  }
325
- index++;
352
+ i++;
326
353
  }
327
354
 
328
355
  if (descended) continue;
329
356
 
330
- topMod._isEvaluatingSideEffects = false;
331
- if (!ctx.circular) {
332
- topMod._sideEffectsStateGraph = moduleGraph;
333
- topMod._sideEffectsStateValue = current;
357
+ // `mod` finished without a `true` bailout.
358
+ mod._isEvaluatingSideEffects = false;
359
+ // Memoize only when the result is context-independent: no back-edge in
360
+ // this subtree reached above `mod` (a cycle-free module or an SCC root).
361
+ if (lowOf.get(mod) === indexOf.get(mod)) {
362
+ mod._sideEffectsStateGraph = moduleGraph;
363
+ mod._sideEffectsStateValue = current;
334
364
  }
365
+ fMod.pop();
366
+ fDeps.pop();
367
+ fIdx.pop();
368
+ fCur.pop();
369
+ result = current;
335
370
  pending = current;
336
- modStack.pop();
337
- depsStack.pop();
338
- indexStack.pop();
339
- currentStack.pop();
340
- }
341
-
342
- return /** @type {ConnectionState} */ (pending);
343
- };
344
-
345
- /**
346
- * @typedef {object} SideEffectsWalkContext
347
- * @property {boolean} circular whether a cycle was seen anywhere in this walk
348
- */
349
-
350
- /**
351
- * Walks back up a stack of linear-chain ancestors, applying the result
352
- * `state` of the chain's tail to each ancestor. Each ancestor in the
353
- * stack had exactly one `HarmonyImportSideEffectDependency` returning
354
- * `state`, so its result is just `state` (with the usual aggregation
355
- * rules) and we can avoid building per-frame `current` accumulators.
356
- * @param {(NormalModule | Dependency)[] | null} ancestors interleaved stack of `[mod, sideEffectDep, mod, sideEffectDep, …]` in descent order; `null` if there were none
357
- * @param {ConnectionState} state result from the chain's tail
358
- * @param {ModuleGraph} moduleGraph the module graph
359
- * @param {SideEffectsWalkContext} ctx per-walk cycle-tracking context
360
- * @returns {ConnectionState} the root ancestor's result
361
- */
362
- const propagateLinearResult = (ancestors, state, moduleGraph, ctx) => {
363
- if (ancestors === null) return state;
364
- while (ancestors.length > 0) {
365
- const dep = /** @type {Dependency} */ (ancestors.pop());
366
- const ancestor = /** @type {NormalModule} */ (ancestors.pop());
367
- ancestor._isEvaluatingSideEffects = false;
368
-
369
- if (state === true) {
370
- recordSideEffectsBailout(ancestor, moduleGraph, dep);
371
- // `true` is monotonic — safe to cache regardless of cycle status.
372
- ancestor._sideEffectsStateGraph = moduleGraph;
373
- ancestor._sideEffectsStateValue = true;
374
- } else if (state === ModuleGraphConnection.CIRCULAR_CONNECTION) {
375
- // CIRCULAR_CONNECTION is filtered before folding into `current`,
376
- // so the ancestor's `current` stays at its initial `false`. From
377
- // this point upward the propagated state is `false`, and the
378
- // cycle taint prevents memoization further up (handled by
379
- // `ctx.circular`).
380
- state = false;
381
- } else if (!ctx.circular) {
382
- ancestor._sideEffectsStateGraph = moduleGraph;
383
- ancestor._sideEffectsStateValue = state;
384
- }
385
- }
386
- return state;
387
- };
388
-
389
- /**
390
- * Recursive form of the side-effects walker. Folds the descent through
391
- * `HarmonyImportSideEffectDependency.getModuleEvaluationSideEffectsState`
392
- * directly into the loop so each module costs only one V8 stack frame
393
- * (vs. two in the original recursive code).
394
- *
395
- * Linear-chain heads (modules with exactly one
396
- * `HarmonyImportSideEffectDependency` to another `NormalModule`) are
397
- * walked iteratively inside the function via an explicit
398
- * `linearAncestors` stack — no additional V8 frame per descent — and
399
- * their results are propagated back up by `propagateLinearResult`. This
400
- * keeps stack consumption at O(1) for the common deep-import-chain
401
- * pattern that motivated #20986 and also avoids the heavier iterative
402
- * fallback.
403
- *
404
- * Falls back to `walkSideEffectsIterative` only when a *non-linear*
405
- * walk reaches `SIDE_EFFECTS_RECURSION_LIMIT` — i.e. a deep tree, where
406
- * each level genuinely needs its own recursion frame.
407
- *
408
- * Caches the result on the module when the walk did not encounter a
409
- * cycle, so subsequent queries (e.g. repeated lookups during
410
- * `SideEffectsFlagPlugin`'s incoming-connection optimization and its
411
- * `exportInfo.getTarget` callbacks) return in O(1).
412
- * @param {NormalModule} mod the module being walked
413
- * @param {ModuleGraph} moduleGraph the module graph
414
- * @param {number} depth current recursion depth (only counts true V8 frames)
415
- * @param {EXPECTED_ANY} SideEffectDep `HarmonyImportSideEffectDependency` constructor, resolved once at the public entry to avoid repeated `require` lookups in the recursive call
416
- * @param {SideEffectsWalkContext} ctx per-walk cycle-tracking context
417
- * @returns {ConnectionState} the side-effects connection state
418
- */
419
- const walkSideEffectsRecursive = (
420
- mod,
421
- moduleGraph,
422
- depth,
423
- SideEffectDep,
424
- ctx
425
- ) => {
426
- // Interleaved `[mod, linearDep, mod, linearDep, …]` for the linear
427
- // chain head; `null` until the first descent.
428
- /** @type {(NormalModule | Dependency)[] | null} */
429
- let linearAncestors = null;
430
-
431
- // Walk the linear-chain head iteratively. Every loop iteration peels
432
- // off one module without consuming a V8 stack frame.
433
- while (true) {
434
- if (mod._sideEffectsStateGraph === moduleGraph) {
435
- return propagateLinearResult(
436
- linearAncestors,
437
- /** @type {ConnectionState} */ (mod._sideEffectsStateValue),
438
- moduleGraph,
439
- ctx
371
+ if (fMod.length > 0) {
372
+ const parent = fMod[fMod.length - 1];
373
+ lowOf.set(
374
+ parent,
375
+ Math.min(
376
+ /** @type {number} */ (lowOf.get(parent)),
377
+ /** @type {number} */ (lowOf.get(mod))
378
+ )
440
379
  );
441
380
  }
442
-
443
- if (mod.factoryMeta !== undefined) {
444
- if (mod.factoryMeta.sideEffectFree) {
445
- return propagateLinearResult(linearAncestors, false, moduleGraph, ctx);
446
- }
447
- if (mod.factoryMeta.sideEffectFree === false) {
448
- return propagateLinearResult(linearAncestors, true, moduleGraph, ctx);
449
- }
450
- }
451
- if (!(mod.buildMeta !== undefined && mod.buildMeta.sideEffectFree)) {
452
- return propagateLinearResult(linearAncestors, true, moduleGraph, ctx);
453
- }
454
- if (mod._isEvaluatingSideEffects) {
455
- ctx.circular = true;
456
- return propagateLinearResult(
457
- linearAncestors,
458
- ModuleGraphConnection.CIRCULAR_CONNECTION,
459
- moduleGraph,
460
- ctx
461
- );
462
- }
463
-
464
- // A real ESM module's `dependencies` typically include one
465
- // `HarmonyImportSideEffectDependency` plus several non-recursive deps
466
- // (export specifiers, const dependencies, …) that report
467
- // `false`/`CIRCULAR_CONNECTION` from
468
- // `getModuleEvaluationSideEffectsState`. Walk the deps in order
469
- // here: as long as at most one is a `SideEffectDep` and no
470
- // non-recursive dep triggers a bailout or contributes a non-`false`
471
- // state, we can still tail-call iteratively through that one
472
- // `SideEffectDep` — no V8 frame needed. This is what keeps the
473
- // 1300-module cyclic chain from #20986 from overflowing V8's stack
474
- // even though each generated module has multiple `dependencies`.
475
- const deps = mod.dependencies;
476
- /** @type {Dependency | null} */
477
- let linearDep = null;
478
- /** @type {ConnectionState} */
479
- let nonRecursiveCurrent = false;
480
- let linearOk = true;
481
- for (let i = 0; i < deps.length; i++) {
482
- const dep = deps[i];
483
- if (dep instanceof SideEffectDep) {
484
- if (linearDep !== null) {
485
- // Two `SideEffectDep`s in the same module — fall back to
486
- // the general walk so each can recurse normally.
487
- linearOk = false;
488
- break;
489
- }
490
- linearDep = dep;
491
- } else {
492
- const state = dep.getModuleEvaluationSideEffectsState(moduleGraph);
493
- if (state === true) {
494
- recordSideEffectsBailout(mod, moduleGraph, dep);
495
- mod._sideEffectsStateGraph = moduleGraph;
496
- mod._sideEffectsStateValue = true;
497
- return propagateLinearResult(linearAncestors, true, moduleGraph, ctx);
498
- }
499
- if (state !== ModuleGraphConnection.CIRCULAR_CONNECTION) {
500
- nonRecursiveCurrent = ModuleGraphConnection.addConnectionStates(
501
- nonRecursiveCurrent,
502
- state
503
- );
504
- }
505
- }
506
- }
507
-
508
- if (!linearOk || nonRecursiveCurrent !== false) {
509
- // Multiple `SideEffectDep`s, or a non-recursive dep contributed
510
- // a non-`false` state (e.g. `TRANSITIVE_ONLY`). The linear
511
- // propagation rule "ancestor's current = chain state" no longer
512
- // holds, so fall back to the general walk.
513
- break;
514
- }
515
-
516
- if (linearDep === null) {
517
- // No `SideEffectDep` — the module is a leaf as far as the
518
- // side-effects graph is concerned. Cache and propagate `false`.
519
- if (!ctx.circular) {
520
- mod._sideEffectsStateGraph = moduleGraph;
521
- mod._sideEffectsStateValue = false;
522
- }
523
- return propagateLinearResult(linearAncestors, false, moduleGraph, ctx);
524
- }
525
-
526
- const refModule = moduleGraph.getModule(linearDep);
527
- if (!refModule) {
528
- recordSideEffectsBailout(mod, moduleGraph, linearDep);
529
- mod._sideEffectsStateGraph = moduleGraph;
530
- mod._sideEffectsStateValue = true;
531
- return propagateLinearResult(linearAncestors, true, moduleGraph, ctx);
532
- }
533
- if (!(refModule instanceof NormalModule)) {
534
- // Non-NormalModule's `getSideEffectsConnectionState` re-enters
535
- // the public method; defer to the general walk for safety.
536
- break;
537
- }
538
-
539
- mod._isEvaluatingSideEffects = true;
540
- if (linearAncestors === null) linearAncestors = [];
541
- // Push (mod, linearDep) so `propagateLinearResult` records bailouts
542
- // against the actual `SideEffectDep` that triggered the descent —
543
- // which may not be `dependencies[0]` when the module also has
544
- // export / const dependencies.
545
- linearAncestors.push(mod, linearDep);
546
- mod = refModule;
547
- continue;
548
- }
549
-
550
- // Non-linear walk. Each genuine recursive call costs one V8 frame, so
551
- // honour the depth limit here.
552
- if (depth >= SIDE_EFFECTS_RECURSION_LIMIT) {
553
- return propagateLinearResult(
554
- linearAncestors,
555
- walkSideEffectsIterative(mod, moduleGraph, ctx),
556
- moduleGraph,
557
- ctx
558
- );
559
381
  }
560
382
 
561
- mod._isEvaluatingSideEffects = true;
562
- /** @type {ConnectionState} */
563
- let current = false;
564
-
565
- for (const dep of mod.dependencies) {
566
- /** @type {ConnectionState} */
567
- let state;
568
- if (dep instanceof SideEffectDep) {
569
- const refModule = moduleGraph.getModule(dep);
570
- if (!refModule) {
571
- state = true;
572
- } else if (refModule instanceof NormalModule) {
573
- state = walkSideEffectsRecursive(
574
- refModule,
575
- moduleGraph,
576
- depth + 1,
577
- SideEffectDep,
578
- ctx
579
- );
580
- } else {
581
- // Non-NormalModule's `getSideEffectsConnectionState` (notably
582
- // `ConcatenatedModule` delegating to its root) re-enters the
583
- // public method and may walk through modules that the outer
584
- // walk has marked as evaluating. We can't observe whether
585
- // the inner walk hit a cycle that reflected the outer's
586
- // state, so treat the walk as cycle-tainted and skip the
587
- // cache for safety.
588
- ctx.circular = true;
589
- state = refModule.getSideEffectsConnectionState(moduleGraph);
590
- }
591
- } else {
592
- state = dep.getModuleEvaluationSideEffectsState(moduleGraph);
593
- }
594
-
595
- if (state === true) {
596
- recordSideEffectsBailout(mod, moduleGraph, dep);
597
- mod._isEvaluatingSideEffects = false;
598
- // `true` is monotonic — once any dep declares side effects, the
599
- // answer is `true` regardless of how cycles resolve, so it's
600
- // always safe to memoize.
601
- mod._sideEffectsStateGraph = moduleGraph;
602
- mod._sideEffectsStateValue = true;
603
- return propagateLinearResult(linearAncestors, true, moduleGraph, ctx);
604
- }
605
- if (state !== ModuleGraphConnection.CIRCULAR_CONNECTION) {
606
- current = ModuleGraphConnection.addConnectionStates(current, state);
607
- }
608
- }
609
-
610
- mod._isEvaluatingSideEffects = false;
611
-
612
- // Only memoize when no cycle has been observed anywhere in the walk
613
- // since the public entry. A cycle anywhere can affect any ancestor's
614
- // result (the back-edge target's contribution is hidden by
615
- // CIRCULAR_CONNECTION short-circuiting), so this single flag is the
616
- // conservative-but-cheap approximation of "this subtree's result is
617
- // independent of cycle context". The public entry resets the flag.
618
- if (!ctx.circular) {
619
- mod._sideEffectsStateGraph = moduleGraph;
620
- mod._sideEffectsStateValue = current;
621
- }
622
- return propagateLinearResult(linearAncestors, current, moduleGraph, ctx);
383
+ return result;
623
384
  };
624
385
 
625
386
  /**
@@ -924,8 +685,8 @@ class NormalModule extends Module {
924
685
  * graph slot keys the cached value to the `ModuleGraph` it was
925
686
  * computed against so stale values never leak across compilations
926
687
  * — a walk that targets a different graph just overwrites both
927
- * slots. Populated only for results computed without encountering
928
- * a circular connection (see `walkSideEffectsRecursive`).
688
+ * slots. Populated only for context-independent results (cycle-free
689
+ * or SCC-root modules; see `walkSideEffects`).
929
690
  * @type {ModuleGraph | undefined}
930
691
  */
931
692
  this._sideEffectsStateGraph = undefined;
@@ -1380,6 +1141,7 @@ class NormalModule extends Module {
1380
1141
  };
1381
1142
 
1382
1143
  Object.assign(loaderContext, options.loader);
1144
+ createLoaderContext(loaderContext);
1383
1145
 
1384
1146
  hooks.loader.call(
1385
1147
  /** @type {AnyLoaderContext} */
@@ -1677,6 +1439,9 @@ class NormalModule extends Module {
1677
1439
  buildDependencies.add(loader.loader);
1678
1440
  }
1679
1441
  buildInfo.cacheable = buildInfo.cacheable && result.cacheable;
1442
+ if (result.notCacheableReasons.length > 0) {
1443
+ buildInfo.notCacheableReasons = result.notCacheableReasons;
1444
+ }
1680
1445
  processResult(err, result.result);
1681
1446
  }
1682
1447
  );
@@ -1783,6 +1548,7 @@ class NormalModule extends Module {
1783
1548
  };
1784
1549
  this.buildInfo = {
1785
1550
  cacheable: false,
1551
+ notCacheableReasons: undefined,
1786
1552
  parsed: true,
1787
1553
  fileDependencies: undefined,
1788
1554
  contextDependencies: undefined,
@@ -1998,15 +1764,13 @@ class NormalModule extends Module {
1998
1764
  * @returns {ConnectionState} how this module should be connected to referencing modules when consumed for side-effects only
1999
1765
  */
2000
1766
  getSideEffectsConnectionState(moduleGraph) {
2001
- // Fresh per-walk context; re-entrant calls (e.g.
1767
+ // Each call owns its Tarjan bookkeeping; re-entrant calls (e.g.
2002
1768
  // `ConcatenatedModule.getSideEffectsConnectionState` delegating back
2003
- // through here) get their own and can't clobber the outer walk.
2004
- return walkSideEffectsRecursive(
1769
+ // through here) can't clobber the outer walk.
1770
+ return walkSideEffects(
2005
1771
  this,
2006
1772
  moduleGraph,
2007
- 0,
2008
- getHarmonyImportSideEffectDependency(),
2009
- { circular: false }
1773
+ getHarmonyImportSideEffectDependency()
2010
1774
  );
2011
1775
  }
2012
1776