webpack 5.109.0 → 5.109.1

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 (39) hide show
  1. package/bin/webpack.js +12 -0
  2. package/lib/ChunkGraph.js +17 -15
  3. package/lib/CompatibilityPlugin.js +31 -0
  4. package/lib/Compilation.js +9 -2
  5. package/lib/DefinePlugin.js +37 -11
  6. package/lib/ExportsInfo.js +22 -5
  7. package/lib/FileSystemInfo.js +126 -26
  8. package/lib/FlagDependencyUsagePlugin.js +33 -12
  9. package/lib/InitFragment.js +20 -32
  10. package/lib/NormalModule.js +6 -0
  11. package/lib/RuntimePlugin.js +40 -113
  12. package/lib/Template.js +23 -3
  13. package/lib/container/HoistContainerReferencesPlugin.js +47 -55
  14. package/lib/css/CssParser.js +135 -50
  15. package/lib/css/syntax.js +862 -765
  16. package/lib/dependencies/CommonJsExportRequireDependency.js +19 -0
  17. package/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js +2 -1
  18. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +17 -9
  19. package/lib/dependencies/HarmonyImportDependency.js +1 -1
  20. package/lib/dependencies/HarmonyImportSpecifierDependency.js +10 -7
  21. package/lib/hmr/lazyCompilationBackend.js +19 -4
  22. package/lib/html/HtmlGenerator.js +23 -3
  23. package/lib/html/syntax.js +2485 -2399
  24. package/lib/ids/IdHelpers.js +24 -10
  25. package/lib/javascript/JavascriptParser.js +5 -2
  26. package/lib/javascript/syntax.js +700 -43
  27. package/lib/node/ReadFileCompileAsyncWasmPlugin.js +10 -2
  28. package/lib/node/ReadFileCompileWasmPlugin.js +10 -2
  29. package/lib/optimize/AggressiveMergingPlugin.js +19 -15
  30. package/lib/optimize/ConcatenatedModule.js +19 -15
  31. package/lib/optimize/ModuleConcatenationPlugin.js +73 -24
  32. package/lib/optimize/SplitChunksPlugin.js +146 -44
  33. package/lib/prefetch/ResourceHintPlugin.js +96 -72
  34. package/lib/serialization/BinaryMiddleware.js +270 -938
  35. package/lib/stats/DefaultStatsFactoryPlugin.js +204 -198
  36. package/lib/stats/DefaultStatsPrinterPlugin.js +24 -166
  37. package/lib/stats/StatsFactory.js +31 -9
  38. package/package.json +2 -2
  39. package/types.d.ts +0 -4
package/bin/webpack.js CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  "use strict";
4
4
 
5
+ // Cache compiled module bytecode on disk (available since Node.js 22.8)
6
+ // eslint-disable-next-line n/no-unsupported-features/node-builtins
7
+ const { enableCompileCache } = require("module");
8
+
9
+ if (enableCompileCache) {
10
+ try {
11
+ enableCompileCache();
12
+ } catch (_error) {
13
+ // Nothing
14
+ }
15
+ }
16
+
5
17
  /**
6
18
  * @param {string} command process to run
7
19
  * @param {string[]} args command line arguments
package/lib/ChunkGraph.js CHANGED
@@ -52,6 +52,19 @@ const EMPTY_SET = new Set();
52
52
 
53
53
  const ZERO_BIG_INT = BigInt(0);
54
54
 
55
+ /**
56
+ * Active state to string. Module-level (depends only on ModuleGraphConnection)
57
+ * so it isn't re-allocated on every getModuleGraphHash call, incl. cache hits.
58
+ * @param {ConnectionState} state state
59
+ * @returns {"F" | "T" | "O"} result
60
+ */
61
+ const activeStateToString = (state) => {
62
+ if (state === false) return "F";
63
+ if (state === true) return "T";
64
+ if (state === ModuleGraphConnection.TRANSITIVE_ONLY) return "O";
65
+ throw new Error("Not implemented active state");
66
+ };
67
+
55
68
  const compareModuleIterables = compareIterables(compareModulesByIdentifier);
56
69
 
57
70
  /** @typedef {(c: Chunk, chunkGraph: ChunkGraph) => boolean} ChunkFilterPredicate */
@@ -1821,22 +1834,11 @@ Caller might not support runtime-dependent code generation (opt-out via optimiza
1821
1834
  cgm.graphHashesWithConnections = new RuntimeSpecMap();
1822
1835
  }
1823
1836
 
1824
- /**
1825
- * Active state to string.
1826
- * @param {ConnectionState} state state
1827
- * @returns {"F" | "T" | "O"} result
1828
- */
1829
- const activeStateToString = (state) => {
1830
- if (state === false) return "F";
1831
- if (state === true) return "T";
1832
- if (state === ModuleGraphConnection.TRANSITIVE_ONLY) return "O";
1833
- throw new Error("Not implemented active state");
1834
- };
1835
- const strict =
1836
- module.buildMeta &&
1837
- /** @type {JavascriptModuleBuildMeta} */ (module.buildMeta)
1838
- .strictHarmonyModule;
1839
1837
  return cgm.graphHashesWithConnections.provide(runtime, () => {
1838
+ const strict =
1839
+ module.buildMeta &&
1840
+ /** @type {JavascriptModuleBuildMeta} */ (module.buildMeta)
1841
+ .strictHarmonyModule;
1840
1842
  const graphHash = this._getModuleGraphHashBigInt(
1841
1843
  cgm,
1842
1844
  module,
@@ -181,6 +181,37 @@ class CompatibilityPlugin {
181
181
  });
182
182
  return true;
183
183
  });
184
+ // `const`/`let` are block-pre-walked, by which point the name is no
185
+ // longer free and `hooks.pattern` is not dispatched for it — tag
186
+ // them through the kind-specific declaration hooks instead.
187
+ // Returning nothing lets the variable be defined, keeping the tag.
188
+ for (const hookMap of [
189
+ parser.hooks.varDeclarationConst,
190
+ parser.hooks.varDeclarationLet
191
+ ]) {
192
+ hookMap.for(RuntimeGlobals.require).tap(PLUGIN_NAME, (ident) => {
193
+ parser.tagVariable(ident.name, nestedWebpackIdentifierTag, {
194
+ name: `__nested_webpack_require_${
195
+ /** @type {Range} */ (ident.range)[0]
196
+ }__`,
197
+ declaration: {
198
+ updated: false,
199
+ loc: parser.getLocation(ident),
200
+ range: /** @type {Range} */ (ident.range)
201
+ }
202
+ });
203
+ });
204
+ hookMap.for(RuntimeGlobals.exports).tap(PLUGIN_NAME, (ident) => {
205
+ parser.tagVariable(ident.name, nestedWebpackIdentifierTag, {
206
+ name: "__nested_webpack_exports__",
207
+ declaration: {
208
+ updated: false,
209
+ loc: parser.getLocation(ident),
210
+ range: /** @type {Range} */ (ident.range)
211
+ }
212
+ });
213
+ });
214
+ }
184
215
  // Update single `var __webpack_require__ = {};` and `var __webpack_exports__ = {};` without expression
185
216
  parser.hooks.declarator.tap(PLUGIN_NAME, (declarator) => {
186
217
  if (
@@ -3504,6 +3504,10 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
3504
3504
  this.addModuleQueue.clear();
3505
3505
  // lazy barrel only acts during make; release its bookkeeping now
3506
3506
  this._lazyBarrelController.clear();
3507
+ // unsafe-cache restore tables are only read during make; release them
3508
+ // so they stop pinning restored modules for the rest of the compilation
3509
+ this._restoredUnsafeCacheModuleEntries.clear();
3510
+ this._restoredUnsafeCacheEntries.clear();
3507
3511
  return callback(err);
3508
3512
  };
3509
3513
 
@@ -4754,10 +4758,13 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
4754
4758
  for (const module of this.modules) {
4755
4759
  const memCache = moduleMemCaches2 && moduleMemCaches2.get(module);
4756
4760
  for (const runtime of chunkGraph.getModuleRuntimes(module)) {
4761
+ const cacheKey = memCache
4762
+ ? `moduleHash-${getRuntimeKey(runtime)}`
4763
+ : undefined;
4757
4764
  if (memCache) {
4758
4765
  const digest =
4759
4766
  /** @type {string} */
4760
- (memCache.get(`moduleHash-${getRuntimeKey(runtime)}`));
4767
+ (memCache.get(/** @type {string} */ (cacheKey)));
4761
4768
  if (digest !== undefined) {
4762
4769
  chunkGraph.setModuleHashes(
4763
4770
  module,
@@ -4781,7 +4788,7 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
4781
4788
  errors
4782
4789
  );
4783
4790
  if (memCache) {
4784
- memCache.set(`moduleHash-${getRuntimeKey(runtime)}`, digest);
4791
+ memCache.set(/** @type {string} */ (cacheKey), digest);
4785
4792
  }
4786
4793
  }
4787
4794
  }
@@ -628,6 +628,26 @@ const getRuntimeRequirements = (code) => {
628
628
  : [RuntimeGlobals.requireScope];
629
629
  };
630
630
 
631
+ /**
632
+ * Prefixes an evaluation error with the offending define key so a bare
633
+ * "Unexpected token" becomes actionable. The original error is mutated and
634
+ * returned (not re-wrapped) so its `loc`/`stack` survive for ModuleParseError's
635
+ * code frame instead of being dumped into the message.
636
+ * @param {string} key the defined key
637
+ * @param {CodeValue} code the defined value
638
+ * @param {unknown} error the original evaluation error
639
+ * @returns {Error} the annotated error
640
+ */
641
+ const annotateEvaluationError = (key, code, error) => {
642
+ const value = typeof code === "string" ? code : JSON.stringify(code);
643
+ const prefix = `DefinePlugin: failed to evaluate value for "${key}" (\`${value}\`)`;
644
+ if (error instanceof Error) {
645
+ error.message = `${prefix}: ${error.message}`;
646
+ return error;
647
+ }
648
+ return new WebpackError(`${prefix}: ${error}`);
649
+ };
650
+
631
651
  /**
632
652
  * Defines the define plugin hooks type used by this module.
633
653
  * @typedef {object} DefinePluginHooks
@@ -954,17 +974,23 @@ class DefinePlugin {
954
974
  });
955
975
  parser.hooks.typeof.for(key).tap(PLUGIN_NAME, (expr) => {
956
976
  addValueDependency(originalKey);
957
- const codeCode = toCode(
958
- code,
959
- parser,
960
- compilation.valueCacheVersions,
961
- originalKey,
962
- runtimeTemplate,
963
- logger,
964
- null
965
- );
966
- const typeofCode = isTypeof ? codeCode : `typeof (${codeCode})`;
967
- const res = parser.evaluate(typeofCode);
977
+ /** @type {BasicEvaluatedExpression} */
978
+ let res;
979
+ try {
980
+ const codeCode = toCode(
981
+ code,
982
+ parser,
983
+ compilation.valueCacheVersions,
984
+ originalKey,
985
+ runtimeTemplate,
986
+ logger,
987
+ null
988
+ );
989
+ const typeofCode = isTypeof ? codeCode : `typeof (${codeCode})`;
990
+ res = parser.evaluate(typeofCode);
991
+ } catch (err) {
992
+ throw annotateEvaluationError(originalKey, code, err);
993
+ }
968
994
  if (!res.isString()) return;
969
995
  return toConstantDependency(
970
996
  parser,
@@ -66,6 +66,11 @@ const UsageState = Object.freeze({
66
66
 
67
67
  const RETURNS_TRUE = () => true;
68
68
 
69
+ // Shared empty visited-set for `findTarget`: `_findTarget` only reads
70
+ // `alreadyVisited` (`.has`), never mutates it, so a fresh `new Set()` per call
71
+ // is pure churn on the per-export reexport hot path. Must stay never-mutated.
72
+ const EMPTY_VISITED = /** @type {Set<ExportInfo>} */ (new Set());
73
+
69
74
  const CIRCULAR = Symbol("circular target");
70
75
 
71
76
  // bit layout of ExportInfo._inlineFlags
@@ -1399,12 +1404,16 @@ class ExportInfo {
1399
1404
  * @returns {boolean} true, if something has changed
1400
1405
  */
1401
1406
  setTarget(key, connection, exportName, priority = 0) {
1402
- if (exportName) exportName = [...exportName];
1407
+ // `equals` compares element-wise, so the incoming `exportName` can be used
1408
+ // directly for the change-check; only copy it when actually stored, so the
1409
+ // common no-op (unchanged target) path allocates nothing.
1403
1410
  if (!this._target) {
1404
1411
  this._target = /** @type {Target} */ (new Map());
1405
1412
  this._target.set(key, {
1406
1413
  connection,
1407
- export: /** @type {ExportInfoName[]} */ (exportName),
1414
+ export: /** @type {ExportInfoName[]} */ (
1415
+ exportName ? [...exportName] : exportName
1416
+ ),
1408
1417
  priority
1409
1418
  });
1410
1419
  return true;
@@ -1414,7 +1423,9 @@ class ExportInfo {
1414
1423
  if (oldTarget === null && !connection) return false;
1415
1424
  this._target.set(key, {
1416
1425
  connection,
1417
- export: /** @type {ExportInfoName[]} */ (exportName),
1426
+ export: /** @type {ExportInfoName[]} */ (
1427
+ exportName ? [...exportName] : exportName
1428
+ ),
1418
1429
  priority
1419
1430
  });
1420
1431
  this._maxTarget = undefined;
@@ -1428,7 +1439,9 @@ class ExportInfo {
1428
1439
  : oldTarget.export)
1429
1440
  ) {
1430
1441
  oldTarget.connection = connection;
1431
- oldTarget.export = /** @type {ExportInfoName[]} */ (exportName);
1442
+ oldTarget.export = /** @type {ExportInfoName[]} */ (
1443
+ exportName ? [...exportName] : exportName
1444
+ );
1432
1445
  oldTarget.priority = priority;
1433
1446
  this._maxTarget = undefined;
1434
1447
  return true;
@@ -1578,7 +1591,11 @@ class ExportInfo {
1578
1591
  * @returns {TargetItemWithoutConnection | null | undefined | false} the target, undefined when there is no target, false when no target is valid
1579
1592
  */
1580
1593
  findTarget(moduleGraph, validTargetModuleFilter) {
1581
- return this._findTarget(moduleGraph, validTargetModuleFilter, new Set());
1594
+ return this._findTarget(
1595
+ moduleGraph,
1596
+ validTargetModuleFilter,
1597
+ EMPTY_VISITED
1598
+ );
1582
1599
  }
1583
1600
 
1584
1601
  /**
@@ -297,6 +297,22 @@ class SnapshotIterable {
297
297
  /** @typedef {Set<string>} ManagedMissing */
298
298
  /** @typedef {Set<Snapshot>} Children */
299
299
 
300
+ // `Snapshot._flags` bit for each optional field. Serialized as a whole integer,
301
+ // so these values are a persisted-cache format contract — never renumber them.
302
+ const SNAPSHOT_FLAG_START_TIME = 1;
303
+ const SNAPSHOT_FLAG_FILE_TIMESTAMPS = 2;
304
+ const SNAPSHOT_FLAG_FILE_HASHES = 4;
305
+ const SNAPSHOT_FLAG_FILE_TSHS = 8;
306
+ const SNAPSHOT_FLAG_CONTEXT_TIMESTAMPS = 0x10;
307
+ const SNAPSHOT_FLAG_CONTEXT_HASHES = 0x20;
308
+ const SNAPSHOT_FLAG_CONTEXT_TSHS = 0x40;
309
+ const SNAPSHOT_FLAG_MISSING_EXISTENCE = 0x80;
310
+ const SNAPSHOT_FLAG_MANAGED_ITEM_INFO = 0x100;
311
+ const SNAPSHOT_FLAG_MANAGED_FILES = 0x200;
312
+ const SNAPSHOT_FLAG_MANAGED_CONTEXTS = 0x400;
313
+ const SNAPSHOT_FLAG_MANAGED_MISSING = 0x800;
314
+ const SNAPSHOT_FLAG_CHILDREN = 0x1000;
315
+
300
316
  class Snapshot {
301
317
  constructor() {
302
318
  /** @type {number} */
@@ -336,7 +352,7 @@ class Snapshot {
336
352
  }
337
353
 
338
354
  hasStartTime() {
339
- return (this._flags & 1) !== 0;
355
+ return (this._flags & SNAPSHOT_FLAG_START_TIME) !== 0;
340
356
  }
341
357
 
342
358
  /**
@@ -344,7 +360,7 @@ class Snapshot {
344
360
  * @param {number} value start value
345
361
  */
346
362
  setStartTime(value) {
347
- this._flags |= 1;
363
+ this._flags |= SNAPSHOT_FLAG_START_TIME;
348
364
  this.startTime = value;
349
365
  }
350
366
 
@@ -375,7 +391,7 @@ class Snapshot {
375
391
  }
376
392
 
377
393
  hasFileTimestamps() {
378
- return (this._flags & 2) !== 0;
394
+ return (this._flags & SNAPSHOT_FLAG_FILE_TIMESTAMPS) !== 0;
379
395
  }
380
396
 
381
397
  /**
@@ -383,12 +399,12 @@ class Snapshot {
383
399
  * @param {FileTimestamps} value file timestamps
384
400
  */
385
401
  setFileTimestamps(value) {
386
- this._flags |= 2;
402
+ this._flags |= SNAPSHOT_FLAG_FILE_TIMESTAMPS;
387
403
  this.fileTimestamps = value;
388
404
  }
389
405
 
390
406
  hasFileHashes() {
391
- return (this._flags & 4) !== 0;
407
+ return (this._flags & SNAPSHOT_FLAG_FILE_HASHES) !== 0;
392
408
  }
393
409
 
394
410
  /**
@@ -396,12 +412,12 @@ class Snapshot {
396
412
  * @param {FileHashes} value file hashes
397
413
  */
398
414
  setFileHashes(value) {
399
- this._flags |= 4;
415
+ this._flags |= SNAPSHOT_FLAG_FILE_HASHES;
400
416
  this.fileHashes = value;
401
417
  }
402
418
 
403
419
  hasFileTshs() {
404
- return (this._flags & 8) !== 0;
420
+ return (this._flags & SNAPSHOT_FLAG_FILE_TSHS) !== 0;
405
421
  }
406
422
 
407
423
  /**
@@ -409,12 +425,12 @@ class Snapshot {
409
425
  * @param {FileTshs} value file tshs
410
426
  */
411
427
  setFileTshs(value) {
412
- this._flags |= 8;
428
+ this._flags |= SNAPSHOT_FLAG_FILE_TSHS;
413
429
  this.fileTshs = value;
414
430
  }
415
431
 
416
432
  hasContextTimestamps() {
417
- return (this._flags & 0x10) !== 0;
433
+ return (this._flags & SNAPSHOT_FLAG_CONTEXT_TIMESTAMPS) !== 0;
418
434
  }
419
435
 
420
436
  /**
@@ -422,12 +438,12 @@ class Snapshot {
422
438
  * @param {ContextTimestamps} value context timestamps
423
439
  */
424
440
  setContextTimestamps(value) {
425
- this._flags |= 0x10;
441
+ this._flags |= SNAPSHOT_FLAG_CONTEXT_TIMESTAMPS;
426
442
  this.contextTimestamps = value;
427
443
  }
428
444
 
429
445
  hasContextHashes() {
430
- return (this._flags & 0x20) !== 0;
446
+ return (this._flags & SNAPSHOT_FLAG_CONTEXT_HASHES) !== 0;
431
447
  }
432
448
 
433
449
  /**
@@ -435,12 +451,12 @@ class Snapshot {
435
451
  * @param {ContextHashes} value context hashes
436
452
  */
437
453
  setContextHashes(value) {
438
- this._flags |= 0x20;
454
+ this._flags |= SNAPSHOT_FLAG_CONTEXT_HASHES;
439
455
  this.contextHashes = value;
440
456
  }
441
457
 
442
458
  hasContextTshs() {
443
- return (this._flags & 0x40) !== 0;
459
+ return (this._flags & SNAPSHOT_FLAG_CONTEXT_TSHS) !== 0;
444
460
  }
445
461
 
446
462
  /**
@@ -448,12 +464,12 @@ class Snapshot {
448
464
  * @param {ContextTshs} value context tshs
449
465
  */
450
466
  setContextTshs(value) {
451
- this._flags |= 0x40;
467
+ this._flags |= SNAPSHOT_FLAG_CONTEXT_TSHS;
452
468
  this.contextTshs = value;
453
469
  }
454
470
 
455
471
  hasMissingExistence() {
456
- return (this._flags & 0x80) !== 0;
472
+ return (this._flags & SNAPSHOT_FLAG_MISSING_EXISTENCE) !== 0;
457
473
  }
458
474
 
459
475
  /**
@@ -461,12 +477,12 @@ class Snapshot {
461
477
  * @param {MissingExistence} value context tshs
462
478
  */
463
479
  setMissingExistence(value) {
464
- this._flags |= 0x80;
480
+ this._flags |= SNAPSHOT_FLAG_MISSING_EXISTENCE;
465
481
  this.missingExistence = value;
466
482
  }
467
483
 
468
484
  hasManagedItemInfo() {
469
- return (this._flags & 0x100) !== 0;
485
+ return (this._flags & SNAPSHOT_FLAG_MANAGED_ITEM_INFO) !== 0;
470
486
  }
471
487
 
472
488
  /**
@@ -474,12 +490,12 @@ class Snapshot {
474
490
  * @param {ManagedItemInfo} value managed item info
475
491
  */
476
492
  setManagedItemInfo(value) {
477
- this._flags |= 0x100;
493
+ this._flags |= SNAPSHOT_FLAG_MANAGED_ITEM_INFO;
478
494
  this.managedItemInfo = value;
479
495
  }
480
496
 
481
497
  hasManagedFiles() {
482
- return (this._flags & 0x200) !== 0;
498
+ return (this._flags & SNAPSHOT_FLAG_MANAGED_FILES) !== 0;
483
499
  }
484
500
 
485
501
  /**
@@ -487,12 +503,12 @@ class Snapshot {
487
503
  * @param {ManagedFiles} value managed files
488
504
  */
489
505
  setManagedFiles(value) {
490
- this._flags |= 0x200;
506
+ this._flags |= SNAPSHOT_FLAG_MANAGED_FILES;
491
507
  this.managedFiles = value;
492
508
  }
493
509
 
494
510
  hasManagedContexts() {
495
- return (this._flags & 0x400) !== 0;
511
+ return (this._flags & SNAPSHOT_FLAG_MANAGED_CONTEXTS) !== 0;
496
512
  }
497
513
 
498
514
  /**
@@ -500,12 +516,12 @@ class Snapshot {
500
516
  * @param {ManagedContexts} value managed contexts
501
517
  */
502
518
  setManagedContexts(value) {
503
- this._flags |= 0x400;
519
+ this._flags |= SNAPSHOT_FLAG_MANAGED_CONTEXTS;
504
520
  this.managedContexts = value;
505
521
  }
506
522
 
507
523
  hasManagedMissing() {
508
- return (this._flags & 0x800) !== 0;
524
+ return (this._flags & SNAPSHOT_FLAG_MANAGED_MISSING) !== 0;
509
525
  }
510
526
 
511
527
  /**
@@ -513,12 +529,12 @@ class Snapshot {
513
529
  * @param {ManagedMissing} value managed missing
514
530
  */
515
531
  setManagedMissing(value) {
516
- this._flags |= 0x800;
532
+ this._flags |= SNAPSHOT_FLAG_MANAGED_MISSING;
517
533
  this.managedMissing = value;
518
534
  }
519
535
 
520
536
  hasChildren() {
521
- return (this._flags & 0x1000) !== 0;
537
+ return (this._flags & SNAPSHOT_FLAG_CHILDREN) !== 0;
522
538
  }
523
539
 
524
540
  /**
@@ -526,7 +542,7 @@ class Snapshot {
526
542
  * @param {Children} value children
527
543
  */
528
544
  setChildren(value) {
529
- this._flags |= 0x1000;
545
+ this._flags |= SNAPSHOT_FLAG_CHILDREN;
530
546
  this.children = value;
531
547
  }
532
548
 
@@ -1221,6 +1237,58 @@ const addAll = (source, target) => {
1221
1237
 
1222
1238
  const getEsModuleLexer = memoize(() => require("es-module-lexer"));
1223
1239
 
1240
+ const getAcorn = memoize(/* istanbul ignore next */ () => require("acorn"));
1241
+
1242
+ // Bun doesn't populate `require.cache` `module.children`, so the CJS build-dependency
1243
+ // walk needs a source-parsing fallback there. Node/Deno populate it — never pay for it.
1244
+ const IS_BUN = Boolean(process.versions.bun);
1245
+
1246
+ /**
1247
+ * Collects static `require("literal")` specifiers from CommonJS source. Used as a
1248
+ * fallback where `require.cache` children aren't populated (e.g. Bun), so build
1249
+ * dependencies of a CJS file are still tracked. Dynamic requires can't be seen here.
1250
+ * @param {string} source source
1251
+ * @returns {Set<string>} required specifiers
1252
+ */
1253
+ // Bun-only: exercised by BuildDependencies.longtest under the Bun CI job (no coverage upload).
1254
+ /* istanbul ignore next */
1255
+ const parseCjsRequires = (source) => {
1256
+ /** @type {Set<string>} */
1257
+ const requires = new Set();
1258
+ const ast = getAcorn().parse(source, {
1259
+ ecmaVersion: "latest",
1260
+ sourceType: "script",
1261
+ allowReturnOutsideFunction: true
1262
+ });
1263
+ /** @type {EXPECTED_ANY[]} */
1264
+ const stack = [ast];
1265
+ while (stack.length > 0) {
1266
+ const node = stack.pop();
1267
+ if (!node || typeof node.type !== "string") continue;
1268
+ if (
1269
+ node.type === "CallExpression" &&
1270
+ node.callee.type === "Identifier" &&
1271
+ node.callee.name === "require" &&
1272
+ node.arguments.length === 1 &&
1273
+ node.arguments[0].type === "Literal" &&
1274
+ typeof node.arguments[0].value === "string"
1275
+ ) {
1276
+ requires.add(node.arguments[0].value);
1277
+ }
1278
+ for (const key of Object.keys(node)) {
1279
+ const value = node[key];
1280
+ if (Array.isArray(value)) {
1281
+ for (const item of value) {
1282
+ if (item && typeof item.type === "string") stack.push(item);
1283
+ }
1284
+ } else if (value && typeof value.type === "string") {
1285
+ stack.push(value);
1286
+ }
1287
+ }
1288
+ }
1289
+ return requires;
1290
+ };
1291
+
1224
1292
  /** @typedef {Set<string>} LoggedPaths */
1225
1293
 
1226
1294
  /** @typedef {FileSystemInfoEntry | ExistenceOnlyTimeEntry | "ignore" | null} FileTimestamp */
@@ -2213,6 +2281,38 @@ class FileSystemInfo {
2213
2281
  });
2214
2282
  }
2215
2283
  }
2284
+ // On Bun the children array is empty (unpopulated), so recover the
2285
+ // static require() specifiers by parsing the source; Node/Deno don't.
2286
+ /* istanbul ignore next */
2287
+ if (IS_BUN && module.children.length === 0) {
2288
+ this.fs.readFile(path, (err, content) => {
2289
+ if (err) return callback(err);
2290
+ try {
2291
+ const context = dirname(this.fs, path);
2292
+ const source = /** @type {Buffer} */ (content).toString();
2293
+ /** @type {Set<string>} */
2294
+ const added = new Set();
2295
+ for (const dependency of parseCjsRequires(source)) {
2296
+ if (dependency.startsWith("node:")) continue;
2297
+ if (builtinModules.has(dependency)) continue;
2298
+ if (added.has(dependency)) continue;
2299
+ push({
2300
+ type: RBDT_RESOLVE_CJS_FILE,
2301
+ context,
2302
+ path: dependency,
2303
+ // Best effort: tolerate specifiers that don't resolve.
2304
+ expected: false,
2305
+ issuer: job
2306
+ });
2307
+ added.add(dependency);
2308
+ }
2309
+ } catch (_err) {
2310
+ // Source we can't parse as CommonJS — ignore its dependencies.
2311
+ }
2312
+ process.nextTick(callback);
2313
+ });
2314
+ break;
2315
+ }
2216
2316
  } else if (supportsEsm && /\.m?js$/.test(path)) {
2217
2317
  if (!this._warnAboutExperimentalEsmTracking) {
2218
2318
  logger.log(
@@ -30,6 +30,19 @@ const {
30
30
  const PLUGIN_NAME = "FlagDependencyUsagePlugin";
31
31
  const PLUGIN_LOGGER_NAME = `webpack.${PLUGIN_NAME}`;
32
32
 
33
+ // Hoisted stateless predicates for setUsedConditionally on the innermost
34
+ // used-export loop, so they aren't re-allocated per export.
35
+ /**
36
+ * @param {import("./ExportsInfo").UsageStateType} used usage state
37
+ * @returns {boolean} whether unused
38
+ */
39
+ const IS_UNUSED = (used) => used === UsageState.Unused;
40
+ /**
41
+ * @param {import("./ExportsInfo").UsageStateType} used usage state
42
+ * @returns {boolean} whether not used
43
+ */
44
+ const IS_NOT_USED = (used) => used !== UsageState.Used;
45
+
33
46
  class FlagDependencyUsagePlugin {
34
47
  /**
35
48
  * Creates an instance of FlagDependencyUsagePlugin.
@@ -162,7 +175,7 @@ class FlagDependencyUsagePlugin {
162
175
  if (nestedInfo) {
163
176
  if (
164
177
  exportInfo.setUsedConditionally(
165
- (used) => used === UsageState.Unused,
178
+ IS_UNUSED,
166
179
  UsageState.OnlyPropertiesUsed,
167
180
  runtime
168
181
  )
@@ -181,7 +194,7 @@ class FlagDependencyUsagePlugin {
181
194
  }
182
195
  if (
183
196
  exportInfo.setUsedConditionally(
184
- (v) => v !== UsageState.Used,
197
+ IS_NOT_USED,
185
198
  UsageState.Used,
186
199
  runtime
187
200
  )
@@ -229,8 +242,9 @@ class FlagDependencyUsagePlugin {
229
242
  // Modules whose whole namespace object escapes in a mangleable way.
230
243
  // Tracked separately so specific member references are still merged
231
244
  // (and marked used) instead of being dropped by the escape marker.
232
- /** @type {Set<Module>} */
233
- const mangleableEscapeModules = new Set();
245
+ // Lazily allocated — usually empty.
246
+ /** @type {Set<Module> | undefined} */
247
+ let mangleableEscapeModules;
234
248
 
235
249
  /** @type {ArrayQueue<DependenciesBlock>} */
236
250
  const queue = new ArrayQueue();
@@ -273,7 +287,9 @@ class FlagDependencyUsagePlugin {
273
287
  // conservative result and always wins.
274
288
  if (referencedExports === EXPORTS_OBJECT_REFERENCED) {
275
289
  map.set(module, EXPORTS_OBJECT_REFERENCED);
276
- mangleableEscapeModules.delete(module);
290
+ if (mangleableEscapeModules) {
291
+ mangleableEscapeModules.delete(module);
292
+ }
277
293
  continue;
278
294
  }
279
295
  // A mangleable whole-object escape keeps the module's exports
@@ -284,6 +300,9 @@ class FlagDependencyUsagePlugin {
284
300
  if (
285
301
  referencedExports === EXPORTS_OBJECT_REFERENCED_MANGLEABLE
286
302
  ) {
303
+ if (mangleableEscapeModules === undefined) {
304
+ mangleableEscapeModules = new Set();
305
+ }
287
306
  mangleableEscapeModules.add(module);
288
307
  continue;
289
308
  }
@@ -358,13 +377,15 @@ class FlagDependencyUsagePlugin {
358
377
  );
359
378
  }
360
379
  }
361
- for (const module of mangleableEscapeModules) {
362
- processReferencedModule(
363
- module,
364
- EXPORTS_OBJECT_REFERENCED_MANGLEABLE,
365
- runtime,
366
- forceSideEffects
367
- );
380
+ if (mangleableEscapeModules) {
381
+ for (const module of mangleableEscapeModules) {
382
+ processReferencedModule(
383
+ module,
384
+ EXPORTS_OBJECT_REFERENCED_MANGLEABLE,
385
+ runtime,
386
+ forceSideEffects
387
+ );
388
+ }
368
389
  }
369
390
  };
370
391