webpack 5.53.0 → 5.56.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.

Potentially problematic release.


This version of webpack might be problematic. Click here for more details.

Files changed (64) hide show
  1. package/lib/AsyncDependenciesBlock.js +9 -2
  2. package/lib/CacheFacade.js +10 -3
  3. package/lib/ChunkGraph.js +19 -8
  4. package/lib/CodeGenerationResults.js +7 -2
  5. package/lib/Compilation.js +586 -143
  6. package/lib/Compiler.js +13 -4
  7. package/lib/DefinePlugin.js +13 -8
  8. package/lib/Dependency.js +11 -0
  9. package/lib/DependencyTemplates.js +8 -2
  10. package/lib/EvalDevToolModulePlugin.js +2 -1
  11. package/lib/EvalSourceMapDevToolPlugin.js +2 -1
  12. package/lib/ExternalModule.js +18 -9
  13. package/lib/FileSystemInfo.js +101 -170
  14. package/lib/FlagDependencyExportsPlugin.js +25 -16
  15. package/lib/JavascriptMetaInfoPlugin.js +6 -1
  16. package/lib/ModuleFactory.js +1 -0
  17. package/lib/ModuleFilenameHelpers.js +21 -7
  18. package/lib/ModuleGraph.js +90 -21
  19. package/lib/NormalModuleFactory.js +8 -43
  20. package/lib/SourceMapDevToolPlugin.js +7 -3
  21. package/lib/WebpackOptionsApply.js +19 -1
  22. package/lib/cache/PackFileCacheStrategy.js +2 -1
  23. package/lib/cache/getLazyHashedEtag.js +35 -8
  24. package/lib/config/defaults.js +18 -7
  25. package/lib/dependencies/CachedConstDependency.js +4 -3
  26. package/lib/dependencies/CommonJsExportRequireDependency.js +19 -9
  27. package/lib/dependencies/CommonJsFullRequireDependency.js +11 -9
  28. package/lib/dependencies/ConstDependency.js +12 -4
  29. package/lib/dependencies/ContextDependency.js +8 -0
  30. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +179 -163
  31. package/lib/dependencies/HarmonyImportDependency.js +4 -1
  32. package/lib/dependencies/JsonExportsDependency.js +7 -1
  33. package/lib/dependencies/ModuleDecoratorDependency.js +5 -2
  34. package/lib/dependencies/ModuleDependency.js +8 -0
  35. package/lib/dependencies/NullDependency.js +8 -4
  36. package/lib/dependencies/ProvidedDependency.js +6 -2
  37. package/lib/dependencies/PureExpressionDependency.js +5 -1
  38. package/lib/dependencies/RuntimeRequirementsDependency.js +5 -1
  39. package/lib/dependencies/WebAssemblyExportImportedDependency.js +9 -0
  40. package/lib/ids/IdHelpers.js +21 -8
  41. package/lib/ids/NamedChunkIdsPlugin.js +3 -0
  42. package/lib/ids/NamedModuleIdsPlugin.js +3 -1
  43. package/lib/index.js +6 -0
  44. package/lib/javascript/BasicEvaluatedExpression.js +3 -0
  45. package/lib/javascript/JavascriptParser.js +22 -4
  46. package/lib/javascript/JavascriptParserHelpers.js +0 -2
  47. package/lib/optimize/ConcatenatedModule.js +25 -4
  48. package/lib/optimize/InnerGraph.js +22 -2
  49. package/lib/optimize/ModuleConcatenationPlugin.js +2 -1
  50. package/lib/schemes/HttpUriPlugin.js +1 -2
  51. package/lib/serialization/BinaryMiddleware.js +11 -2
  52. package/lib/serialization/FileMiddleware.js +24 -7
  53. package/lib/serialization/ObjectMiddleware.js +19 -8
  54. package/lib/util/WeakTupleMap.js +95 -92
  55. package/lib/util/createHash.js +10 -0
  56. package/lib/util/hash/BatchedHash.js +65 -0
  57. package/lib/util/hash/xxhash64.js +154 -0
  58. package/lib/util/serialization.js +4 -4
  59. package/package.json +10 -6
  60. package/schemas/WebpackOptions.check.js +1 -1
  61. package/schemas/WebpackOptions.json +12 -0
  62. package/schemas/plugins/HashedModuleIdsPlugin.check.js +1 -1
  63. package/schemas/plugins/HashedModuleIdsPlugin.json +20 -2
  64. package/types.d.ts +205 -20
@@ -25,6 +25,7 @@ const ChunkRenderError = require("./ChunkRenderError");
25
25
  const ChunkTemplate = require("./ChunkTemplate");
26
26
  const CodeGenerationError = require("./CodeGenerationError");
27
27
  const CodeGenerationResults = require("./CodeGenerationResults");
28
+ const Dependency = require("./Dependency");
28
29
  const DependencyTemplates = require("./DependencyTemplates");
29
30
  const Entrypoint = require("./Entrypoint");
30
31
  const ErrorHelpers = require("./ErrorHelpers");
@@ -60,6 +61,7 @@ const { equals: arrayEquals } = require("./util/ArrayHelpers");
60
61
  const AsyncQueue = require("./util/AsyncQueue");
61
62
  const LazySet = require("./util/LazySet");
62
63
  const { provide } = require("./util/MapHelpers");
64
+ const WeakTupleMap = require("./util/WeakTupleMap");
63
65
  const { cachedCleverMerge } = require("./util/cleverMerge");
64
66
  const {
65
67
  compareLocations,
@@ -91,8 +93,8 @@ const { isSourceEqual } = require("./util/source");
91
93
  /** @typedef {import("./CacheFacade")} CacheFacade */
92
94
  /** @typedef {import("./ChunkGroup").ChunkGroupOptions} ChunkGroupOptions */
93
95
  /** @typedef {import("./Compiler")} Compiler */
96
+ /** @typedef {import("./Compiler").CompilationParams} CompilationParams */
94
97
  /** @typedef {import("./DependenciesBlock")} DependenciesBlock */
95
- /** @typedef {import("./Dependency")} Dependency */
96
98
  /** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */
97
99
  /** @typedef {import("./Dependency").ReferencedExport} ReferencedExport */
98
100
  /** @typedef {import("./DependencyTemplate")} DependencyTemplate */
@@ -100,6 +102,7 @@ const { isSourceEqual } = require("./util/source");
100
102
  /** @typedef {import("./Module").CodeGenerationResult} CodeGenerationResult */
101
103
  /** @typedef {import("./ModuleFactory")} ModuleFactory */
102
104
  /** @typedef {import("./ModuleFactory").ModuleFactoryCreateDataContextInfo} ModuleFactoryCreateDataContextInfo */
105
+ /** @typedef {import("./ModuleFactory").ModuleFactoryResult} ModuleFactoryResult */
103
106
  /** @typedef {import("./RequestShortener")} RequestShortener */
104
107
  /** @typedef {import("./RuntimeModule")} RuntimeModule */
105
108
  /** @typedef {import("./Template").RenderManifestEntry} RenderManifestEntry */
@@ -124,6 +127,20 @@ const { isSourceEqual } = require("./util/source");
124
127
  * @returns {void}
125
128
  */
126
129
 
130
+ /**
131
+ * @callback ModuleFactoryResultCallback
132
+ * @param {WebpackError=} err
133
+ * @param {ModuleFactoryResult=} result
134
+ * @returns {void}
135
+ */
136
+
137
+ /**
138
+ * @callback ModuleOrFactoryResultCallback
139
+ * @param {WebpackError=} err
140
+ * @param {Module | ModuleFactoryResult=} result
141
+ * @returns {void}
142
+ */
143
+
127
144
  /**
128
145
  * @callback ExecuteModuleCallback
129
146
  * @param {WebpackError=} err
@@ -399,12 +416,19 @@ const byLocation = compareSelect(err => err.loc, compareLocations);
399
416
 
400
417
  const compareErrors = concatComparators(byModule, byLocation, byMessage);
401
418
 
419
+ /** @type {WeakMap<Dependency, Module & { restoreFromUnsafeCache: Function }>} */
420
+ const unsafeCacheDependencies = new WeakMap();
421
+
422
+ /** @type {WeakMap<Module, object>} */
423
+ const unsafeCacheData = new WeakMap();
424
+
402
425
  class Compilation {
403
426
  /**
404
427
  * Creates an instance of Compilation.
405
428
  * @param {Compiler} compiler the compiler which created the compilation
429
+ * @param {CompilationParams} params the compilation parameters
406
430
  */
407
- constructor(compiler) {
431
+ constructor(compiler, params) {
408
432
  const getNormalModuleLoader = () => deprecatedNormalModuleLoaderHook(this);
409
433
  /** @typedef {{ additionalAssets?: true | Function }} ProcessAssetsAdditionalOptions */
410
434
  /** @type {AsyncSeriesHook<[CompilationAssets], ProcessAssetsAdditionalOptions>} */
@@ -852,7 +876,8 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
852
876
  this.fileSystemInfo = new FileSystemInfo(this.inputFileSystem, {
853
877
  managedPaths: compiler.managedPaths,
854
878
  immutablePaths: compiler.immutablePaths,
855
- logger: this.getLogger("webpack.FileSystemInfo")
879
+ logger: this.getLogger("webpack.FileSystemInfo"),
880
+ hashFunction: compiler.options.output.hashFunction
856
881
  });
857
882
  if (compiler.fileTimestamps) {
858
883
  this.fileSystemInfo.addFileTimestamps(compiler.fileTimestamps, true);
@@ -878,6 +903,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
878
903
  /** @type {boolean} */
879
904
  this.profile = (options && options.profile) || false;
880
905
 
906
+ this.params = params;
881
907
  this.mainTemplate = new MainTemplate(this.outputOptions, this);
882
908
  this.chunkTemplate = new ChunkTemplate(this.outputOptions, this);
883
909
  this.runtimeTemplate = new RuntimeTemplate(
@@ -891,6 +917,8 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
891
917
  };
892
918
  defineRemovedModuleTemplates(this.moduleTemplates);
893
919
 
920
+ /** @type {WeakMap<Module, WeakTupleMap<any, any>> | undefined} */
921
+ this.moduleMemCaches = undefined;
894
922
  this.moduleGraph = new ModuleGraph();
895
923
  /** @type {ChunkGraph} */
896
924
  this.chunkGraph = undefined;
@@ -910,7 +938,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
910
938
  getKey: module => module.identifier(),
911
939
  processor: this._addModule.bind(this)
912
940
  });
913
- /** @type {AsyncQueue<FactorizeModuleOptions, string, Module>} */
941
+ /** @type {AsyncQueue<FactorizeModuleOptions, string, Module | ModuleFactoryResult>} */
914
942
  this.factorizeQueue = new AsyncQueue({
915
943
  name: "factorize",
916
944
  parent: this.addModuleQueue,
@@ -993,6 +1021,8 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
993
1021
  this.usedModuleIds = null;
994
1022
  /** @type {boolean} */
995
1023
  this.needAdditionalPass = false;
1024
+ /** @type {Set<Module>} */
1025
+ this._restoredUnsafeCacheEntries = new Set();
996
1026
  /** @type {WeakSet<Module>} */
997
1027
  this.builtModules = new WeakSet();
998
1028
  /** @type {WeakSet<Module>} */
@@ -1025,6 +1055,11 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1025
1055
  this._modulesCache = this.getCache("Compilation/modules");
1026
1056
  this._assetsCache = this.getCache("Compilation/assets");
1027
1057
  this._codeGenerationCache = this.getCache("Compilation/codeGeneration");
1058
+
1059
+ const unsafeCache = options.module.unsafeCache;
1060
+ this._unsafeCache = !!unsafeCache;
1061
+ this._unsafeCachePredicate =
1062
+ typeof unsafeCache === "function" ? unsafeCache : () => true;
1028
1063
  }
1029
1064
 
1030
1065
  getStats() {
@@ -1409,12 +1444,44 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1409
1444
  /** @type {Dependency[]} */
1410
1445
  let listCacheValue;
1411
1446
 
1447
+ const unsafeRestoredModules = new Set();
1448
+
1412
1449
  /**
1413
1450
  * @param {Dependency} dep dependency
1414
1451
  * @returns {void}
1415
1452
  */
1416
1453
  const processDependency = dep => {
1417
1454
  this.moduleGraph.setParents(dep, currentBlock, module);
1455
+ if (this._unsafeCache) {
1456
+ try {
1457
+ const cachedModule = unsafeCacheDependencies.get(dep);
1458
+ if (cachedModule === null) return;
1459
+ if (cachedModule !== undefined) {
1460
+ if (!this._restoredUnsafeCacheEntries.has(cachedModule)) {
1461
+ const data = unsafeCacheData.get(cachedModule);
1462
+ cachedModule.restoreFromUnsafeCache(
1463
+ data,
1464
+ this.params.normalModuleFactory,
1465
+ this.params
1466
+ );
1467
+ this._restoredUnsafeCacheEntries.add(cachedModule);
1468
+ if (!this.modules.has(cachedModule)) {
1469
+ this._handleNewModuleFromUnsafeCache(module, dep, cachedModule);
1470
+ unsafeRestoredModules.add(cachedModule);
1471
+ return;
1472
+ }
1473
+ }
1474
+ this._handleExistingModuleFromUnsafeCache(
1475
+ module,
1476
+ dep,
1477
+ cachedModule
1478
+ );
1479
+ return;
1480
+ }
1481
+ } catch (e) {
1482
+ console.error(e);
1483
+ }
1484
+ }
1418
1485
  const resourceIdent = dep.getResourceIdentifier();
1419
1486
  if (resourceIdent !== undefined && resourceIdent !== null) {
1420
1487
  const category = dep.category;
@@ -1498,7 +1565,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1498
1565
  return callback(e);
1499
1566
  }
1500
1567
 
1501
- if (sortedDependencies.length === 0) {
1568
+ if (sortedDependencies.length === 0 && unsafeRestoredModules.size === 0) {
1502
1569
  callback();
1503
1570
  return;
1504
1571
  }
@@ -1506,27 +1573,78 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1506
1573
  // This is nested so we need to allow one additional task
1507
1574
  this.processDependenciesQueue.increaseParallelism();
1508
1575
 
1509
- asyncLib.forEach(
1510
- sortedDependencies,
1511
- (item, callback) => {
1512
- this.handleModuleCreation(item, err => {
1513
- // In V8, the Error objects keep a reference to the functions on the stack. These warnings &
1514
- // errors are created inside closures that keep a reference to the Compilation, so errors are
1515
- // leaking the Compilation object.
1516
- if (err && this.bail) {
1517
- // eslint-disable-next-line no-self-assign
1518
- err.stack = err.stack;
1519
- return callback(err);
1520
- }
1521
- callback();
1522
- });
1523
- },
1524
- err => {
1525
- this.processDependenciesQueue.decreaseParallelism();
1576
+ const processSortedDependency = (item, callback) => {
1577
+ this.handleModuleCreation(item, err => {
1578
+ // In V8, the Error objects keep a reference to the functions on the stack. These warnings &
1579
+ // errors are created inside closures that keep a reference to the Compilation, so errors are
1580
+ // leaking the Compilation object.
1581
+ if (err && this.bail) {
1582
+ // eslint-disable-next-line no-self-assign
1583
+ err.stack = err.stack;
1584
+ return callback(err);
1585
+ }
1586
+ callback();
1587
+ });
1588
+ };
1526
1589
 
1527
- return callback(err);
1528
- }
1590
+ const processUnsafeRestoredModule = (item, callback) => {
1591
+ this._handleModuleBuildAndDependencies(module, item, true, callback);
1592
+ };
1593
+
1594
+ const finalCallback = err => {
1595
+ this.processDependenciesQueue.decreaseParallelism();
1596
+
1597
+ return callback(err);
1598
+ };
1599
+
1600
+ if (sortedDependencies.length === 0) {
1601
+ asyncLib.forEach(
1602
+ unsafeRestoredModules,
1603
+ processUnsafeRestoredModule,
1604
+ finalCallback
1605
+ );
1606
+ } else if (unsafeRestoredModules.size === 0) {
1607
+ asyncLib.forEach(
1608
+ sortedDependencies,
1609
+ processSortedDependency,
1610
+ finalCallback
1611
+ );
1612
+ } else {
1613
+ asyncLib.parallel(
1614
+ [
1615
+ cb =>
1616
+ asyncLib.forEach(
1617
+ unsafeRestoredModules,
1618
+ processUnsafeRestoredModule,
1619
+ cb
1620
+ ),
1621
+ cb =>
1622
+ asyncLib.forEach(sortedDependencies, processSortedDependency, cb)
1623
+ ],
1624
+ finalCallback
1625
+ );
1626
+ }
1627
+ }
1628
+
1629
+ _handleNewModuleFromUnsafeCache(originModule, dependency, module) {
1630
+ const moduleGraph = this.moduleGraph;
1631
+
1632
+ moduleGraph.setResolvedModule(originModule, dependency, module);
1633
+
1634
+ moduleGraph.setIssuerIfUnset(
1635
+ module,
1636
+ originModule !== undefined ? originModule : null
1529
1637
  );
1638
+
1639
+ this._modules.set(module.identifier(), module);
1640
+ this.modules.add(module);
1641
+ ModuleGraph.setModuleGraphForModule(module, this.moduleGraph);
1642
+ }
1643
+
1644
+ _handleExistingModuleFromUnsafeCache(originModule, dependency, module) {
1645
+ const moduleGraph = this.moduleGraph;
1646
+
1647
+ moduleGraph.setResolvedModule(originModule, dependency, module);
1530
1648
  }
1531
1649
 
1532
1650
  /**
@@ -1566,12 +1684,27 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1566
1684
  currentProfile,
1567
1685
  factory,
1568
1686
  dependencies,
1687
+ factoryResult: true,
1569
1688
  originModule,
1570
1689
  contextInfo,
1571
1690
  context
1572
1691
  },
1573
- (err, newModule) => {
1692
+ (err, factoryResult) => {
1693
+ const applyFactoryResultDependencies = () => {
1694
+ const { fileDependencies, contextDependencies, missingDependencies } =
1695
+ factoryResult;
1696
+ if (fileDependencies) {
1697
+ this.fileDependencies.addAll(fileDependencies);
1698
+ }
1699
+ if (contextDependencies) {
1700
+ this.contextDependencies.addAll(contextDependencies);
1701
+ }
1702
+ if (missingDependencies) {
1703
+ this.missingDependencies.addAll(missingDependencies);
1704
+ }
1705
+ };
1574
1706
  if (err) {
1707
+ if (factoryResult) applyFactoryResultDependencies();
1575
1708
  if (dependencies.every(d => d.optional)) {
1576
1709
  this.warnings.push(err);
1577
1710
  return callback();
@@ -1581,7 +1714,10 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1581
1714
  }
1582
1715
  }
1583
1716
 
1717
+ const newModule = factoryResult.module;
1718
+
1584
1719
  if (!newModule) {
1720
+ applyFactoryResultDependencies();
1585
1721
  return callback();
1586
1722
  }
1587
1723
 
@@ -1591,6 +1727,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1591
1727
 
1592
1728
  this.addModule(newModule, (err, module) => {
1593
1729
  if (err) {
1730
+ applyFactoryResultDependencies();
1594
1731
  if (!err.module) {
1595
1732
  err.module = module;
1596
1733
  }
@@ -1599,13 +1736,37 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1599
1736
  return callback(err);
1600
1737
  }
1601
1738
 
1602
- for (let i = 0; i < dependencies.length; i++) {
1603
- const dependency = dependencies[i];
1604
- moduleGraph.setResolvedModule(
1605
- connectOrigin ? originModule : null,
1606
- dependency,
1607
- module
1608
- );
1739
+ if (
1740
+ this._unsafeCache &&
1741
+ factoryResult.cacheable !== false &&
1742
+ /** @type {any} */ (module).restoreFromUnsafeCache &&
1743
+ this._unsafeCachePredicate(module)
1744
+ ) {
1745
+ for (let i = 0; i < dependencies.length; i++) {
1746
+ const dependency = dependencies[i];
1747
+ moduleGraph.setResolvedModule(
1748
+ connectOrigin ? originModule : null,
1749
+ dependency,
1750
+ module
1751
+ );
1752
+ unsafeCacheDependencies.set(
1753
+ dependency,
1754
+ /** @type {any} */ (module)
1755
+ );
1756
+ }
1757
+ if (!unsafeCacheData.has(module)) {
1758
+ unsafeCacheData.set(module, module.getUnsafeCacheData());
1759
+ }
1760
+ } else {
1761
+ applyFactoryResultDependencies();
1762
+ for (let i = 0; i < dependencies.length; i++) {
1763
+ const dependency = dependencies[i];
1764
+ moduleGraph.setResolvedModule(
1765
+ connectOrigin ? originModule : null,
1766
+ dependency,
1767
+ module
1768
+ );
1769
+ }
1609
1770
  }
1610
1771
 
1611
1772
  moduleGraph.setIssuerIfUnset(
@@ -1623,99 +1784,89 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1623
1784
  }
1624
1785
  }
1625
1786
 
1626
- // Check for cycles when build is trigger inside another build
1627
- let creatingModuleDuringBuildSet = undefined;
1628
- if (!recursive && this.buildQueue.isProcessing(originModule)) {
1629
- // Track build dependency
1630
- creatingModuleDuringBuildSet =
1631
- this.creatingModuleDuringBuild.get(originModule);
1632
- if (creatingModuleDuringBuildSet === undefined) {
1633
- creatingModuleDuringBuildSet = new Set();
1634
- this.creatingModuleDuringBuild.set(
1635
- originModule,
1636
- creatingModuleDuringBuildSet
1637
- );
1638
- }
1639
- creatingModuleDuringBuildSet.add(originModule);
1640
-
1641
- // When building is blocked by another module
1642
- // search for a cycle, cancel the cycle by throwing
1643
- // an error (otherwise this would deadlock)
1644
- const blockReasons = this.creatingModuleDuringBuild.get(module);
1645
- if (blockReasons !== undefined) {
1646
- const set = new Set(blockReasons);
1647
- for (const item of set) {
1648
- const blockReasons = this.creatingModuleDuringBuild.get(item);
1649
- if (blockReasons !== undefined) {
1650
- for (const m of blockReasons) {
1651
- if (m === module) {
1652
- return callback(new BuildCycleError(module));
1653
- }
1654
- set.add(m);
1655
- }
1656
- }
1657
- }
1658
- }
1659
- }
1787
+ this._handleModuleBuildAndDependencies(
1788
+ originModule,
1789
+ module,
1790
+ recursive,
1791
+ callback
1792
+ );
1793
+ });
1794
+ }
1795
+ );
1796
+ }
1660
1797
 
1661
- this.buildModule(module, err => {
1662
- if (creatingModuleDuringBuildSet !== undefined) {
1663
- creatingModuleDuringBuildSet.delete(module);
1664
- }
1665
- if (err) {
1666
- if (!err.module) {
1667
- err.module = module;
1798
+ _handleModuleBuildAndDependencies(originModule, module, recursive, callback) {
1799
+ // Check for cycles when build is trigger inside another build
1800
+ let creatingModuleDuringBuildSet = undefined;
1801
+ if (!recursive && this.buildQueue.isProcessing(originModule)) {
1802
+ // Track build dependency
1803
+ creatingModuleDuringBuildSet =
1804
+ this.creatingModuleDuringBuild.get(originModule);
1805
+ if (creatingModuleDuringBuildSet === undefined) {
1806
+ creatingModuleDuringBuildSet = new Set();
1807
+ this.creatingModuleDuringBuild.set(
1808
+ originModule,
1809
+ creatingModuleDuringBuildSet
1810
+ );
1811
+ }
1812
+ creatingModuleDuringBuildSet.add(originModule);
1813
+
1814
+ // When building is blocked by another module
1815
+ // search for a cycle, cancel the cycle by throwing
1816
+ // an error (otherwise this would deadlock)
1817
+ const blockReasons = this.creatingModuleDuringBuild.get(module);
1818
+ if (blockReasons !== undefined) {
1819
+ const set = new Set(blockReasons);
1820
+ for (const item of set) {
1821
+ const blockReasons = this.creatingModuleDuringBuild.get(item);
1822
+ if (blockReasons !== undefined) {
1823
+ for (const m of blockReasons) {
1824
+ if (m === module) {
1825
+ return callback(new BuildCycleError(module));
1668
1826
  }
1669
- this.errors.push(err);
1670
-
1671
- return callback(err);
1827
+ set.add(m);
1672
1828
  }
1829
+ }
1830
+ }
1831
+ }
1832
+ }
1673
1833
 
1674
- if (!recursive) {
1675
- this.processModuleDependenciesNonRecursive(module);
1676
- callback(null, module);
1677
- return;
1678
- }
1834
+ this.buildModule(module, err => {
1835
+ if (creatingModuleDuringBuildSet !== undefined) {
1836
+ creatingModuleDuringBuildSet.delete(module);
1837
+ }
1838
+ if (err) {
1839
+ if (!err.module) {
1840
+ err.module = module;
1841
+ }
1842
+ this.errors.push(err);
1679
1843
 
1680
- // This avoids deadlocks for circular dependencies
1681
- if (this.processDependenciesQueue.isProcessing(module)) {
1682
- return callback();
1683
- }
1844
+ return callback(err);
1845
+ }
1684
1846
 
1685
- this.processModuleDependencies(module, err => {
1686
- if (err) {
1687
- return callback(err);
1688
- }
1689
- callback(null, module);
1690
- });
1691
- });
1692
- });
1847
+ if (!recursive) {
1848
+ this.processModuleDependenciesNonRecursive(module);
1849
+ callback(null, module);
1850
+ return;
1693
1851
  }
1694
- );
1695
- }
1696
1852
 
1697
- /**
1698
- * @typedef {Object} FactorizeModuleOptions
1699
- * @property {ModuleProfile} currentProfile
1700
- * @property {ModuleFactory} factory
1701
- * @property {Dependency[]} dependencies
1702
- * @property {Module | null} originModule
1703
- * @property {Partial<ModuleFactoryCreateDataContextInfo>=} contextInfo
1704
- * @property {string=} context
1705
- */
1853
+ // This avoids deadlocks for circular dependencies
1854
+ if (this.processDependenciesQueue.isProcessing(module)) {
1855
+ return callback();
1856
+ }
1706
1857
 
1707
- /**
1708
- * @param {FactorizeModuleOptions} options options object
1709
- * @param {ModuleCallback} callback callback
1710
- * @returns {void}
1711
- */
1712
- factorizeModule(options, callback) {
1713
- this.factorizeQueue.add(options, callback);
1858
+ this.processModuleDependencies(module, err => {
1859
+ if (err) {
1860
+ return callback(err);
1861
+ }
1862
+ callback(null, module);
1863
+ });
1864
+ });
1714
1865
  }
1715
1866
 
1716
1867
  /**
1717
1868
  * @param {FactorizeModuleOptions} options options object
1718
- * @param {ModuleCallback} callback callback
1869
+ * @param {ModuleOrFactoryResultCallback} callback callback
1719
1870
  * @returns {void}
1720
1871
  */
1721
1872
  _factorizeModule(
@@ -1724,6 +1875,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1724
1875
  factory,
1725
1876
  dependencies,
1726
1877
  originModule,
1878
+ factoryResult,
1727
1879
  contextInfo,
1728
1880
  context
1729
1881
  },
@@ -1757,16 +1909,21 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1757
1909
  module: result
1758
1910
  };
1759
1911
  }
1760
- const { fileDependencies, contextDependencies, missingDependencies } =
1761
- result;
1762
- if (fileDependencies) {
1763
- this.fileDependencies.addAll(fileDependencies);
1764
- }
1765
- if (contextDependencies) {
1766
- this.contextDependencies.addAll(contextDependencies);
1767
- }
1768
- if (missingDependencies) {
1769
- this.missingDependencies.addAll(missingDependencies);
1912
+ if (!factoryResult) {
1913
+ const {
1914
+ fileDependencies,
1915
+ contextDependencies,
1916
+ missingDependencies
1917
+ } = result;
1918
+ if (fileDependencies) {
1919
+ this.fileDependencies.addAll(fileDependencies);
1920
+ }
1921
+ if (contextDependencies) {
1922
+ this.contextDependencies.addAll(contextDependencies);
1923
+ }
1924
+ if (missingDependencies) {
1925
+ this.missingDependencies.addAll(missingDependencies);
1926
+ }
1770
1927
  }
1771
1928
  }
1772
1929
  if (err) {
@@ -1775,20 +1932,17 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1775
1932
  err,
1776
1933
  dependencies.map(d => d.loc).filter(Boolean)[0]
1777
1934
  );
1778
- return callback(notFoundError);
1935
+ return callback(notFoundError, factoryResult ? result : undefined);
1779
1936
  }
1780
1937
  if (!result) {
1781
1938
  return callback();
1782
1939
  }
1783
- const newModule = result.module;
1784
- if (!newModule) {
1785
- return callback();
1786
- }
1940
+
1787
1941
  if (currentProfile !== undefined) {
1788
1942
  currentProfile.markFactoryEnd();
1789
1943
  }
1790
1944
 
1791
- callback(null, newModule);
1945
+ callback(null, factoryResult ? result : result.module);
1792
1946
  }
1793
1947
  );
1794
1948
  }
@@ -2011,6 +2165,158 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
2011
2165
  });
2012
2166
  }
2013
2167
 
2168
+ _computeAffectedModules(modules) {
2169
+ const moduleMemCacheCache = this.compiler.moduleMemCaches;
2170
+ if (!moduleMemCacheCache) return;
2171
+ if (!this.moduleMemCaches) {
2172
+ this.moduleMemCaches = new WeakMap();
2173
+ this.moduleGraph.setModuleMemCaches(this.moduleMemCaches);
2174
+ }
2175
+ const { moduleGraph, moduleMemCaches } = this;
2176
+ const affectedModules = new Set();
2177
+ const infectedModules = new Set();
2178
+ let statNew = 0;
2179
+ let statChanged = 0;
2180
+ let statUnchanged = 0;
2181
+ let statReferencesChanged = 0;
2182
+ let statWithoutHash = 0;
2183
+
2184
+ const computeReferences = module => {
2185
+ /** @type {WeakMap<Dependency, Module>} */
2186
+ let references = undefined;
2187
+ for (const connection of moduleGraph.getOutgoingConnections(module)) {
2188
+ const d = connection.dependency;
2189
+ const m = connection.module;
2190
+ if (!d || !m || unsafeCacheDependencies.has(d)) continue;
2191
+ if (references === undefined) references = new WeakMap();
2192
+ references.set(d, m);
2193
+ }
2194
+ return references;
2195
+ };
2196
+
2197
+ /**
2198
+ * @param {Module} module the module
2199
+ * @param {WeakMap<Dependency, Module>} references references
2200
+ * @returns {boolean} true, when the references differ
2201
+ */
2202
+ const compareReferences = (module, references) => {
2203
+ if (references === undefined) return true;
2204
+ for (const connection of moduleGraph.getOutgoingConnections(module)) {
2205
+ const d = connection.dependency;
2206
+ if (!d) continue;
2207
+ const entry = references.get(d);
2208
+ if (entry === undefined) continue;
2209
+ if (entry !== connection.module) return false;
2210
+ }
2211
+ return true;
2212
+ };
2213
+
2214
+ for (const module of modules) {
2215
+ const hash = module.buildInfo && module.buildInfo.hash;
2216
+ if (typeof hash === "string") {
2217
+ const cachedMemCache = moduleMemCacheCache.get(module);
2218
+ if (cachedMemCache === undefined) {
2219
+ // create a new entry
2220
+ const memCache = new WeakTupleMap();
2221
+ moduleMemCacheCache.set(module, {
2222
+ hash: hash,
2223
+ references: computeReferences(module),
2224
+ memCache
2225
+ });
2226
+ moduleMemCaches.set(module, memCache);
2227
+ affectedModules.add(module);
2228
+ statNew++;
2229
+ } else if (cachedMemCache.hash !== hash) {
2230
+ // use a new one
2231
+ const memCache = new WeakTupleMap();
2232
+ moduleMemCaches.set(module, memCache);
2233
+ affectedModules.add(module);
2234
+ cachedMemCache.hash = hash;
2235
+ cachedMemCache.references = computeReferences(module);
2236
+ cachedMemCache.memCache = memCache;
2237
+ statChanged++;
2238
+ } else if (!compareReferences(module, cachedMemCache.references)) {
2239
+ // use a new one
2240
+ const memCache = new WeakTupleMap();
2241
+ moduleMemCaches.set(module, memCache);
2242
+ affectedModules.add(module);
2243
+ cachedMemCache.references = computeReferences(module);
2244
+ cachedMemCache.memCache = memCache;
2245
+ statReferencesChanged++;
2246
+ } else {
2247
+ // keep the old mem cache
2248
+ moduleMemCaches.set(module, cachedMemCache.memCache);
2249
+ statUnchanged++;
2250
+ }
2251
+ } else {
2252
+ infectedModules.add(module);
2253
+ statWithoutHash++;
2254
+ }
2255
+ }
2256
+ const reduceAffectType = connections => {
2257
+ let affected = false;
2258
+ for (const { dependency } of connections) {
2259
+ if (!dependency) continue;
2260
+ const type = dependency.couldAffectReferencingModule();
2261
+ if (type === Dependency.TRANSITIVE) return Dependency.TRANSITIVE;
2262
+ if (type === false) continue;
2263
+ affected = true;
2264
+ }
2265
+ return affected;
2266
+ };
2267
+ const directOnlyInfectedModules = new Set();
2268
+ for (const module of infectedModules) {
2269
+ for (const [
2270
+ referencingModule,
2271
+ connections
2272
+ ] of moduleGraph.getIncomingConnectionsByOriginModule(module)) {
2273
+ if (!referencingModule) continue;
2274
+ if (infectedModules.has(referencingModule)) continue;
2275
+ const type = reduceAffectType(connections);
2276
+ if (!type) continue;
2277
+ if (type === true) {
2278
+ directOnlyInfectedModules.add(referencingModule);
2279
+ } else {
2280
+ infectedModules.add(referencingModule);
2281
+ }
2282
+ }
2283
+ }
2284
+ for (const module of directOnlyInfectedModules) infectedModules.add(module);
2285
+ const directOnlyAffectModules = new Set();
2286
+ for (const module of affectedModules) {
2287
+ for (const [
2288
+ referencingModule,
2289
+ connections
2290
+ ] of moduleGraph.getIncomingConnectionsByOriginModule(module)) {
2291
+ if (!referencingModule) continue;
2292
+ if (infectedModules.has(referencingModule)) continue;
2293
+ if (affectedModules.has(referencingModule)) continue;
2294
+ const type = reduceAffectType(connections);
2295
+ if (!type) continue;
2296
+ if (type === true) {
2297
+ directOnlyAffectModules.add(referencingModule);
2298
+ } else {
2299
+ affectedModules.add(referencingModule);
2300
+ }
2301
+ const memCache = new WeakTupleMap();
2302
+ const cache = moduleMemCacheCache.get(referencingModule);
2303
+ cache.memCache = memCache;
2304
+ moduleMemCaches.set(referencingModule, memCache);
2305
+ }
2306
+ }
2307
+ for (const module of directOnlyAffectModules) affectedModules.add(module);
2308
+ this.logger.log(
2309
+ `${Math.round(
2310
+ (100 * (affectedModules.size + infectedModules.size)) /
2311
+ this.modules.size
2312
+ )}% (${affectedModules.size} affected + ${
2313
+ infectedModules.size
2314
+ } infected of ${
2315
+ this.modules.size
2316
+ }) modules flagged as affected (${statNew} new modules, ${statChanged} changed, ${statReferencesChanged} references changed, ${statUnchanged} unchanged, ${statWithoutHash} without hash)`
2317
+ );
2318
+ }
2319
+
2014
2320
  finish(callback) {
2015
2321
  this.factorizeQueue.clear();
2016
2322
  if (this.profile) {
@@ -2191,17 +2497,29 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
2191
2497
  );
2192
2498
  this.logger.timeEnd("finish module profiles");
2193
2499
  }
2500
+ this.logger.time("compute affected modules");
2501
+ this._computeAffectedModules(this.modules);
2502
+ this.logger.timeEnd("compute affected modules");
2194
2503
  this.logger.time("finish modules");
2195
- const { modules } = this;
2504
+ const { modules, moduleMemCaches } = this;
2196
2505
  this.hooks.finishModules.callAsync(modules, err => {
2197
2506
  this.logger.timeEnd("finish modules");
2198
2507
  if (err) return callback(err);
2199
2508
 
2200
2509
  // extract warnings and errors from modules
2510
+ this.moduleGraph.freeze("dependency errors");
2511
+ // TODO keep a cacheToken (= {}) for each module in the graph
2512
+ // create a new one per compilation and flag all updated files
2513
+ // and parents with it
2201
2514
  this.logger.time("report dependency errors and warnings");
2202
- this.moduleGraph.freeze();
2203
2515
  for (const module of modules) {
2204
- this.reportDependencyErrorsAndWarnings(module, [module]);
2516
+ // TODO only run for modules with changed cacheToken
2517
+ // global WeakMap<CacheToken, WeakSet<Module>> to keep modules without errors/warnings
2518
+ const memCache = moduleMemCaches && moduleMemCaches.get(module);
2519
+ if (memCache && memCache.get("noWarningsOrErrors")) continue;
2520
+ let hasProblems = this.reportDependencyErrorsAndWarnings(module, [
2521
+ module
2522
+ ]);
2205
2523
  const errors = module.getErrors();
2206
2524
  if (errors !== undefined) {
2207
2525
  for (const error of errors) {
@@ -2209,6 +2527,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
2209
2527
  error.module = module;
2210
2528
  }
2211
2529
  this.errors.push(error);
2530
+ hasProblems = true;
2212
2531
  }
2213
2532
  }
2214
2533
  const warnings = module.getWarnings();
@@ -2218,8 +2537,10 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
2218
2537
  warning.module = module;
2219
2538
  }
2220
2539
  this.warnings.push(warning);
2540
+ hasProblems = true;
2221
2541
  }
2222
2542
  }
2543
+ if (!hasProblems && memCache) memCache.set("noWarningsOrErrors", true);
2223
2544
  }
2224
2545
  this.moduleGraph.unfreeze();
2225
2546
  this.logger.timeEnd("report dependency errors and warnings");
@@ -2255,7 +2576,10 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
2255
2576
  this.addModuleQueue.clear();
2256
2577
  return callback(err);
2257
2578
  };
2258
- const chunkGraph = new ChunkGraph(this.moduleGraph);
2579
+ const chunkGraph = new ChunkGraph(
2580
+ this.moduleGraph,
2581
+ this.outputOptions.hashFunction
2582
+ );
2259
2583
  this.chunkGraph = chunkGraph;
2260
2584
 
2261
2585
  for (const module of this.modules) {
@@ -2273,7 +2597,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
2273
2597
 
2274
2598
  this.logger.time("create chunks");
2275
2599
  this.hooks.beforeChunks.call();
2276
- this.moduleGraph.freeze();
2600
+ this.moduleGraph.freeze("seal");
2277
2601
  /** @type {Map<Entrypoint, Module[]>} */
2278
2602
  const chunkGraphInit = new Map();
2279
2603
  for (const [name, { dependencies, includeDependencies, options }] of this
@@ -2573,9 +2897,10 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
2573
2897
  /**
2574
2898
  * @param {Module} module module to report from
2575
2899
  * @param {DependenciesBlock[]} blocks blocks to report from
2576
- * @returns {void}
2900
+ * @returns {boolean} true, when it has warnings or errors
2577
2901
  */
2578
2902
  reportDependencyErrorsAndWarnings(module, blocks) {
2903
+ let hasProblems = false;
2579
2904
  for (let indexBlock = 0; indexBlock < blocks.length; indexBlock++) {
2580
2905
  const block = blocks[indexBlock];
2581
2906
  const dependencies = block.dependencies;
@@ -2590,6 +2915,7 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
2590
2915
 
2591
2916
  const warning = new ModuleDependencyWarning(module, w, d.loc);
2592
2917
  this.warnings.push(warning);
2918
+ hasProblems = true;
2593
2919
  }
2594
2920
  }
2595
2921
  const errors = d.getErrors(this.moduleGraph);
@@ -2599,17 +2925,22 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
2599
2925
 
2600
2926
  const error = new ModuleDependencyError(module, e, d.loc);
2601
2927
  this.errors.push(error);
2928
+ hasProblems = true;
2602
2929
  }
2603
2930
  }
2604
2931
  }
2605
2932
 
2606
- this.reportDependencyErrorsAndWarnings(module, block.blocks);
2933
+ if (this.reportDependencyErrorsAndWarnings(module, block.blocks))
2934
+ hasProblems = true;
2607
2935
  }
2936
+ return hasProblems;
2608
2937
  }
2609
2938
 
2610
2939
  codeGeneration(callback) {
2611
2940
  const { chunkGraph } = this;
2612
- this.codeGenerationResults = new CodeGenerationResults();
2941
+ this.codeGenerationResults = new CodeGenerationResults(
2942
+ this.outputOptions.hashFunction
2943
+ );
2613
2944
  /** @type {{module: Module, hash: string, runtime: RuntimeSpec, runtimes: RuntimeSpec[]}[]} */
2614
2945
  const jobs = [];
2615
2946
  for (const module of this.modules) {
@@ -2790,12 +3121,35 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
2790
3121
  chunkGraphEntries = this._getChunkGraphEntries()
2791
3122
  } = {}) {
2792
3123
  const context = { chunkGraph, codeGenerationResults };
3124
+ const { moduleMemCaches } = this;
3125
+ this.logger.time("runtime requirements.modules");
2793
3126
  const additionalModuleRuntimeRequirements =
2794
3127
  this.hooks.additionalModuleRuntimeRequirements;
2795
3128
  const runtimeRequirementInModule = this.hooks.runtimeRequirementInModule;
2796
3129
  for (const module of modules) {
2797
3130
  if (chunkGraph.getNumberOfModuleChunks(module) > 0) {
3131
+ const memCache =
3132
+ moduleMemCaches &&
3133
+ // modules with async blocks depend on the chunk graph and can't be cached that way
3134
+ module.blocks.length === 0 &&
3135
+ moduleMemCaches.get(module);
2798
3136
  for (const runtime of chunkGraph.getModuleRuntimes(module)) {
3137
+ if (memCache) {
3138
+ const cached = memCache.get(
3139
+ `moduleRuntimeRequirements-${getRuntimeKey(runtime)}`
3140
+ );
3141
+ if (cached !== undefined) {
3142
+ if (cached !== null) {
3143
+ chunkGraph.addModuleRuntimeRequirements(
3144
+ module,
3145
+ runtime,
3146
+ cached,
3147
+ false
3148
+ );
3149
+ }
3150
+ continue;
3151
+ }
3152
+ }
2799
3153
  let set;
2800
3154
  const runtimeRequirements =
2801
3155
  codeGenerationResults.getRuntimeRequirements(module, runtime);
@@ -2804,6 +3158,12 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
2804
3158
  } else if (additionalModuleRuntimeRequirements.isUsed()) {
2805
3159
  set = new Set();
2806
3160
  } else {
3161
+ if (memCache) {
3162
+ memCache.set(
3163
+ `moduleRuntimeRequirements-${getRuntimeKey(runtime)}`,
3164
+ null
3165
+ );
3166
+ }
2807
3167
  continue;
2808
3168
  }
2809
3169
  additionalModuleRuntimeRequirements.call(module, set, context);
@@ -2812,11 +3172,35 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
2812
3172
  const hook = runtimeRequirementInModule.get(r);
2813
3173
  if (hook !== undefined) hook.call(module, set, context);
2814
3174
  }
2815
- chunkGraph.addModuleRuntimeRequirements(module, runtime, set);
3175
+ if (set.size === 0) {
3176
+ if (memCache) {
3177
+ memCache.set(
3178
+ `moduleRuntimeRequirements-${getRuntimeKey(runtime)}`,
3179
+ null
3180
+ );
3181
+ }
3182
+ } else {
3183
+ if (memCache) {
3184
+ memCache.set(
3185
+ `moduleRuntimeRequirements-${getRuntimeKey(runtime)}`,
3186
+ set
3187
+ );
3188
+ chunkGraph.addModuleRuntimeRequirements(
3189
+ module,
3190
+ runtime,
3191
+ set,
3192
+ false
3193
+ );
3194
+ } else {
3195
+ chunkGraph.addModuleRuntimeRequirements(module, runtime, set);
3196
+ }
3197
+ }
2816
3198
  }
2817
3199
  }
2818
3200
  }
3201
+ this.logger.timeEnd("runtime requirements.modules");
2819
3202
 
3203
+ this.logger.time("runtime requirements.chunks");
2820
3204
  for (const chunk of chunks) {
2821
3205
  const set = new Set();
2822
3206
  for (const module of chunkGraph.getChunkModulesIterable(chunk)) {
@@ -2834,7 +3218,9 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
2834
3218
 
2835
3219
  chunkGraph.addChunkRuntimeRequirements(chunk, set);
2836
3220
  }
3221
+ this.logger.timeEnd("runtime requirements.chunks");
2837
3222
 
3223
+ this.logger.time("runtime requirements.entries");
2838
3224
  for (const treeEntry of chunkGraphEntries) {
2839
3225
  const set = new Set();
2840
3226
  for (const chunk of treeEntry.getAllReferencedChunks()) {
@@ -2857,6 +3243,7 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
2857
3243
 
2858
3244
  chunkGraph.addTreeRuntimeRequirements(treeEntry, set);
2859
3245
  }
3246
+ this.logger.timeEnd("runtime requirements.entries");
2860
3247
  }
2861
3248
 
2862
3249
  // TODO webpack 6 make chunkGraph argument non-optional
@@ -3201,12 +3588,31 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
3201
3588
 
3202
3589
  createModuleHashes() {
3203
3590
  let statModulesHashed = 0;
3204
- const { chunkGraph, runtimeTemplate } = this;
3591
+ let statModulesFromCache = 0;
3592
+ const { chunkGraph, runtimeTemplate, moduleMemCaches } = this;
3205
3593
  const { hashFunction, hashDigest, hashDigestLength } = this.outputOptions;
3206
3594
  for (const module of this.modules) {
3595
+ const memCache =
3596
+ moduleMemCaches &&
3597
+ // modules with async blocks depend on the chunk graph and can't be cached that way
3598
+ module.blocks.length === 0 &&
3599
+ moduleMemCaches.get(module);
3207
3600
  for (const runtime of chunkGraph.getModuleRuntimes(module)) {
3601
+ if (memCache) {
3602
+ const digest = memCache.get(`moduleHash-${getRuntimeKey(runtime)}`);
3603
+ if (digest !== undefined) {
3604
+ chunkGraph.setModuleHashes(
3605
+ module,
3606
+ runtime,
3607
+ digest,
3608
+ digest.substr(0, hashDigestLength)
3609
+ );
3610
+ statModulesFromCache++;
3611
+ continue;
3612
+ }
3613
+ }
3208
3614
  statModulesHashed++;
3209
- this._createModuleHash(
3615
+ const digest = this._createModuleHash(
3210
3616
  module,
3211
3617
  chunkGraph,
3212
3618
  runtime,
@@ -3215,11 +3621,16 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
3215
3621
  hashDigest,
3216
3622
  hashDigestLength
3217
3623
  );
3624
+ if (memCache) {
3625
+ memCache.set(`moduleHash-${getRuntimeKey(runtime)}`, digest);
3626
+ }
3218
3627
  }
3219
3628
  }
3220
3629
  this.logger.log(
3221
- `${statModulesHashed} modules hashed (${
3222
- Math.round((100 * statModulesHashed) / this.modules.size) / 100
3630
+ `${statModulesHashed} modules hashed, ${statModulesFromCache} from cache (${
3631
+ Math.round(
3632
+ (100 * (statModulesHashed + statModulesFromCache)) / this.modules.size
3633
+ ) / 100
3223
3634
  } variants per module in average)`
3224
3635
  );
3225
3636
  }
@@ -4071,7 +4482,10 @@ This prevents using hashes of each other and should be avoided.`);
4071
4482
  if (err) return callback(err);
4072
4483
 
4073
4484
  // Create new chunk graph, chunk and entrypoint for the build time execution
4074
- const chunkGraph = new ChunkGraph(this.moduleGraph);
4485
+ const chunkGraph = new ChunkGraph(
4486
+ this.moduleGraph,
4487
+ this.outputOptions.hashFunction
4488
+ );
4075
4489
  const runtime = "build time";
4076
4490
  const { hashFunction, hashDigest, hashDigestLength } =
4077
4491
  this.outputOptions;
@@ -4114,7 +4528,9 @@ This prevents using hashes of each other and should be avoided.`);
4114
4528
  );
4115
4529
  }
4116
4530
 
4117
- const codeGenerationResults = new CodeGenerationResults();
4531
+ const codeGenerationResults = new CodeGenerationResults(
4532
+ this.outputOptions.hashFunction
4533
+ );
4118
4534
  /** @type {WebpackError[]} */
4119
4535
  const errors = [];
4120
4536
  /**
@@ -4418,6 +4834,33 @@ This prevents using hashes of each other and should be avoided.`);
4418
4834
  }
4419
4835
  }
4420
4836
 
4837
+ /**
4838
+ * @typedef {Object} FactorizeModuleOptions
4839
+ * @property {ModuleProfile} currentProfile
4840
+ * @property {ModuleFactory} factory
4841
+ * @property {Dependency[]} dependencies
4842
+ * @property {boolean=} factoryResult return full ModuleFactoryResult instead of only module
4843
+ * @property {Module | null} originModule
4844
+ * @property {Partial<ModuleFactoryCreateDataContextInfo>=} contextInfo
4845
+ * @property {string=} context
4846
+ */
4847
+
4848
+ /**
4849
+ * @param {FactorizeModuleOptions} options options object
4850
+ * @param {ModuleCallback | ModuleFactoryResultCallback} callback callback
4851
+ * @returns {void}
4852
+ */
4853
+
4854
+ // Workaround for typescript as it doesn't support function overloading in jsdoc within a class
4855
+ Compilation.prototype.factorizeModule = /** @type {{
4856
+ (options: FactorizeModuleOptions & { factoryResult?: false }, callback: ModuleCallback): void;
4857
+ (options: FactorizeModuleOptions & { factoryResult: true }, callback: ModuleFactoryResultCallback): void;
4858
+ }} */ (
4859
+ function (options, callback) {
4860
+ this.factorizeQueue.add(options, callback);
4861
+ }
4862
+ );
4863
+
4421
4864
  // Hide from typescript
4422
4865
  const compilationPrototype = Compilation.prototype;
4423
4866