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.
- package/lib/Compiler.js +27 -3
- package/lib/ContextModuleFactory.js +45 -38
- package/lib/MultiCompiler.js +14 -0
- package/lib/NormalModule.js +459 -41
- package/lib/ProgressPlugin.js +1 -1
- package/lib/SourceMapDevToolPlugin.js +102 -25
- package/lib/buildChunkGraph.js +24 -2
- package/lib/cache/getLazyHashedEtag.js +9 -2
- package/lib/dependencies/HarmonyExportInitFragment.js +8 -9
- package/lib/dependencies/HtmlInlineScriptDependency.js +3 -14
- package/lib/dependencies/HtmlInlineStyleDependency.js +17 -0
- package/lib/dependencies/HtmlScriptSrcDependency.js +6 -45
- package/lib/dependencies/HtmlSourceDependency.js +18 -0
- package/lib/dependencies/WorkerPlugin.js +18 -4
- package/lib/hmr/LazyCompilationPlugin.js +104 -0
- package/lib/html/HtmlGenerator.js +81 -33
- package/lib/html/HtmlModulesPlugin.js +86 -23
- package/lib/javascript/JavascriptParser.js +1 -1
- package/lib/library/ModuleLibraryPlugin.js +30 -24
- package/lib/node/NodeWatchFileSystem.js +37 -22
- package/lib/optimize/ConcatenatedModule.js +3 -2
- package/lib/util/fs.js +6 -1
- package/package.json +3 -3
- package/types.d.ts +117 -5
package/lib/NormalModule.js
CHANGED
|
@@ -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
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
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 {
|
|
177
|
+
* @returns {ConnectionState} the side-effects connection state
|
|
166
178
|
*/
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
173
|
-
|
|
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
|
-
|
|
176
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
212
|
-
//
|
|
213
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
1543
|
-
//
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
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
|
/**
|
package/lib/ProgressPlugin.js
CHANGED
|
@@ -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) => {
|