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

Potentially problematic release.


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

@@ -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");
@@ -38,7 +39,6 @@ const {
38
39
  tryRunOrWebpackError
39
40
  } = require("./HookWebpackError");
40
41
  const MainTemplate = require("./MainTemplate");
41
- const MemCache = require("./MemCache");
42
42
  const Module = require("./Module");
43
43
  const ModuleDependencyError = require("./ModuleDependencyError");
44
44
  const ModuleDependencyWarning = require("./ModuleDependencyWarning");
@@ -61,6 +61,7 @@ const { equals: arrayEquals } = require("./util/ArrayHelpers");
61
61
  const AsyncQueue = require("./util/AsyncQueue");
62
62
  const LazySet = require("./util/LazySet");
63
63
  const { provide } = require("./util/MapHelpers");
64
+ const WeakTupleMap = require("./util/WeakTupleMap");
64
65
  const { cachedCleverMerge } = require("./util/cleverMerge");
65
66
  const {
66
67
  compareLocations,
@@ -77,7 +78,7 @@ const {
77
78
  createFakeHook
78
79
  } = require("./util/deprecation");
79
80
  const processAsyncTree = require("./util/processAsyncTree");
80
- const { getRuntimeKey, RuntimeSpecMap } = require("./util/runtime");
81
+ const { getRuntimeKey } = require("./util/runtime");
81
82
  const { isSourceEqual } = require("./util/source");
82
83
 
83
84
  /** @template T @typedef {import("tapable").AsArray<T>} AsArray<T> */
@@ -92,8 +93,8 @@ const { isSourceEqual } = require("./util/source");
92
93
  /** @typedef {import("./CacheFacade")} CacheFacade */
93
94
  /** @typedef {import("./ChunkGroup").ChunkGroupOptions} ChunkGroupOptions */
94
95
  /** @typedef {import("./Compiler")} Compiler */
96
+ /** @typedef {import("./Compiler").CompilationParams} CompilationParams */
95
97
  /** @typedef {import("./DependenciesBlock")} DependenciesBlock */
96
- /** @typedef {import("./Dependency")} Dependency */
97
98
  /** @typedef {import("./Dependency").DependencyLocation} DependencyLocation */
98
99
  /** @typedef {import("./Dependency").ReferencedExport} ReferencedExport */
99
100
  /** @typedef {import("./DependencyTemplate")} DependencyTemplate */
@@ -101,6 +102,7 @@ const { isSourceEqual } = require("./util/source");
101
102
  /** @typedef {import("./Module").CodeGenerationResult} CodeGenerationResult */
102
103
  /** @typedef {import("./ModuleFactory")} ModuleFactory */
103
104
  /** @typedef {import("./ModuleFactory").ModuleFactoryCreateDataContextInfo} ModuleFactoryCreateDataContextInfo */
105
+ /** @typedef {import("./ModuleFactory").ModuleFactoryResult} ModuleFactoryResult */
104
106
  /** @typedef {import("./RequestShortener")} RequestShortener */
105
107
  /** @typedef {import("./RuntimeModule")} RuntimeModule */
106
108
  /** @typedef {import("./Template").RenderManifestEntry} RenderManifestEntry */
@@ -125,6 +127,20 @@ const { isSourceEqual } = require("./util/source");
125
127
  * @returns {void}
126
128
  */
127
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
+
128
144
  /**
129
145
  * @callback ExecuteModuleCallback
130
146
  * @param {WebpackError=} err
@@ -400,12 +416,19 @@ const byLocation = compareSelect(err => err.loc, compareLocations);
400
416
 
401
417
  const compareErrors = concatComparators(byModule, byLocation, byMessage);
402
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
+
403
425
  class Compilation {
404
426
  /**
405
427
  * Creates an instance of Compilation.
406
428
  * @param {Compiler} compiler the compiler which created the compilation
429
+ * @param {CompilationParams} params the compilation parameters
407
430
  */
408
- constructor(compiler) {
431
+ constructor(compiler, params) {
409
432
  const getNormalModuleLoader = () => deprecatedNormalModuleLoaderHook(this);
410
433
  /** @typedef {{ additionalAssets?: true | Function }} ProcessAssetsAdditionalOptions */
411
434
  /** @type {AsyncSeriesHook<[CompilationAssets], ProcessAssetsAdditionalOptions>} */
@@ -880,6 +903,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
880
903
  /** @type {boolean} */
881
904
  this.profile = (options && options.profile) || false;
882
905
 
906
+ this.params = params;
883
907
  this.mainTemplate = new MainTemplate(this.outputOptions, this);
884
908
  this.chunkTemplate = new ChunkTemplate(this.outputOptions, this);
885
909
  this.runtimeTemplate = new RuntimeTemplate(
@@ -893,9 +917,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
893
917
  };
894
918
  defineRemovedModuleTemplates(this.moduleTemplates);
895
919
 
896
- /** @type {MemCache | undefined} */
897
- this.memCache = undefined;
898
- /** @type {WeakMap<Module, MemCache> | undefined} */
920
+ /** @type {WeakMap<Module, WeakTupleMap<any, any>> | undefined} */
899
921
  this.moduleMemCaches = undefined;
900
922
  this.moduleGraph = new ModuleGraph();
901
923
  /** @type {ChunkGraph} */
@@ -916,7 +938,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
916
938
  getKey: module => module.identifier(),
917
939
  processor: this._addModule.bind(this)
918
940
  });
919
- /** @type {AsyncQueue<FactorizeModuleOptions, string, Module>} */
941
+ /** @type {AsyncQueue<FactorizeModuleOptions, string, Module | ModuleFactoryResult>} */
920
942
  this.factorizeQueue = new AsyncQueue({
921
943
  name: "factorize",
922
944
  parent: this.addModuleQueue,
@@ -999,6 +1021,8 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
999
1021
  this.usedModuleIds = null;
1000
1022
  /** @type {boolean} */
1001
1023
  this.needAdditionalPass = false;
1024
+ /** @type {Set<Module>} */
1025
+ this._restoredUnsafeCacheEntries = new Set();
1002
1026
  /** @type {WeakSet<Module>} */
1003
1027
  this.builtModules = new WeakSet();
1004
1028
  /** @type {WeakSet<Module>} */
@@ -1031,6 +1055,11 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1031
1055
  this._modulesCache = this.getCache("Compilation/modules");
1032
1056
  this._assetsCache = this.getCache("Compilation/assets");
1033
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;
1034
1063
  }
1035
1064
 
1036
1065
  getStats() {
@@ -1415,12 +1444,44 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1415
1444
  /** @type {Dependency[]} */
1416
1445
  let listCacheValue;
1417
1446
 
1447
+ const unsafeRestoredModules = new Set();
1448
+
1418
1449
  /**
1419
1450
  * @param {Dependency} dep dependency
1420
1451
  * @returns {void}
1421
1452
  */
1422
1453
  const processDependency = dep => {
1423
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
+ }
1424
1485
  const resourceIdent = dep.getResourceIdentifier();
1425
1486
  if (resourceIdent !== undefined && resourceIdent !== null) {
1426
1487
  const category = dep.category;
@@ -1504,7 +1565,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1504
1565
  return callback(e);
1505
1566
  }
1506
1567
 
1507
- if (sortedDependencies.length === 0) {
1568
+ if (sortedDependencies.length === 0 && unsafeRestoredModules.size === 0) {
1508
1569
  callback();
1509
1570
  return;
1510
1571
  }
@@ -1512,27 +1573,78 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1512
1573
  // This is nested so we need to allow one additional task
1513
1574
  this.processDependenciesQueue.increaseParallelism();
1514
1575
 
1515
- asyncLib.forEach(
1516
- sortedDependencies,
1517
- (item, callback) => {
1518
- this.handleModuleCreation(item, err => {
1519
- // In V8, the Error objects keep a reference to the functions on the stack. These warnings &
1520
- // errors are created inside closures that keep a reference to the Compilation, so errors are
1521
- // leaking the Compilation object.
1522
- if (err && this.bail) {
1523
- // eslint-disable-next-line no-self-assign
1524
- err.stack = err.stack;
1525
- return callback(err);
1526
- }
1527
- callback();
1528
- });
1529
- },
1530
- err => {
1531
- 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
+ };
1532
1589
 
1533
- return callback(err);
1534
- }
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
1535
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);
1536
1648
  }
1537
1649
 
1538
1650
  /**
@@ -1572,12 +1684,27 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1572
1684
  currentProfile,
1573
1685
  factory,
1574
1686
  dependencies,
1687
+ factoryResult: true,
1575
1688
  originModule,
1576
1689
  contextInfo,
1577
1690
  context
1578
1691
  },
1579
- (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
+ };
1580
1706
  if (err) {
1707
+ if (factoryResult) applyFactoryResultDependencies();
1581
1708
  if (dependencies.every(d => d.optional)) {
1582
1709
  this.warnings.push(err);
1583
1710
  return callback();
@@ -1587,7 +1714,10 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1587
1714
  }
1588
1715
  }
1589
1716
 
1717
+ const newModule = factoryResult.module;
1718
+
1590
1719
  if (!newModule) {
1720
+ applyFactoryResultDependencies();
1591
1721
  return callback();
1592
1722
  }
1593
1723
 
@@ -1597,6 +1727,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1597
1727
 
1598
1728
  this.addModule(newModule, (err, module) => {
1599
1729
  if (err) {
1730
+ applyFactoryResultDependencies();
1600
1731
  if (!err.module) {
1601
1732
  err.module = module;
1602
1733
  }
@@ -1605,13 +1736,37 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1605
1736
  return callback(err);
1606
1737
  }
1607
1738
 
1608
- for (let i = 0; i < dependencies.length; i++) {
1609
- const dependency = dependencies[i];
1610
- moduleGraph.setResolvedModule(
1611
- connectOrigin ? originModule : null,
1612
- dependency,
1613
- module
1614
- );
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
+ }
1615
1770
  }
1616
1771
 
1617
1772
  moduleGraph.setIssuerIfUnset(
@@ -1629,99 +1784,89 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1629
1784
  }
1630
1785
  }
1631
1786
 
1632
- // Check for cycles when build is trigger inside another build
1633
- let creatingModuleDuringBuildSet = undefined;
1634
- if (!recursive && this.buildQueue.isProcessing(originModule)) {
1635
- // Track build dependency
1636
- creatingModuleDuringBuildSet =
1637
- this.creatingModuleDuringBuild.get(originModule);
1638
- if (creatingModuleDuringBuildSet === undefined) {
1639
- creatingModuleDuringBuildSet = new Set();
1640
- this.creatingModuleDuringBuild.set(
1641
- originModule,
1642
- creatingModuleDuringBuildSet
1643
- );
1644
- }
1645
- creatingModuleDuringBuildSet.add(originModule);
1646
-
1647
- // When building is blocked by another module
1648
- // search for a cycle, cancel the cycle by throwing
1649
- // an error (otherwise this would deadlock)
1650
- const blockReasons = this.creatingModuleDuringBuild.get(module);
1651
- if (blockReasons !== undefined) {
1652
- const set = new Set(blockReasons);
1653
- for (const item of set) {
1654
- const blockReasons = this.creatingModuleDuringBuild.get(item);
1655
- if (blockReasons !== undefined) {
1656
- for (const m of blockReasons) {
1657
- if (m === module) {
1658
- return callback(new BuildCycleError(module));
1659
- }
1660
- set.add(m);
1661
- }
1662
- }
1663
- }
1664
- }
1665
- }
1787
+ this._handleModuleBuildAndDependencies(
1788
+ originModule,
1789
+ module,
1790
+ recursive,
1791
+ callback
1792
+ );
1793
+ });
1794
+ }
1795
+ );
1796
+ }
1666
1797
 
1667
- this.buildModule(module, err => {
1668
- if (creatingModuleDuringBuildSet !== undefined) {
1669
- creatingModuleDuringBuildSet.delete(module);
1670
- }
1671
- if (err) {
1672
- if (!err.module) {
1673
- 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));
1674
1826
  }
1675
- this.errors.push(err);
1676
-
1677
- return callback(err);
1827
+ set.add(m);
1678
1828
  }
1829
+ }
1830
+ }
1831
+ }
1832
+ }
1679
1833
 
1680
- if (!recursive) {
1681
- this.processModuleDependenciesNonRecursive(module);
1682
- callback(null, module);
1683
- return;
1684
- }
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);
1685
1843
 
1686
- // This avoids deadlocks for circular dependencies
1687
- if (this.processDependenciesQueue.isProcessing(module)) {
1688
- return callback();
1689
- }
1844
+ return callback(err);
1845
+ }
1690
1846
 
1691
- this.processModuleDependencies(module, err => {
1692
- if (err) {
1693
- return callback(err);
1694
- }
1695
- callback(null, module);
1696
- });
1697
- });
1698
- });
1847
+ if (!recursive) {
1848
+ this.processModuleDependenciesNonRecursive(module);
1849
+ callback(null, module);
1850
+ return;
1699
1851
  }
1700
- );
1701
- }
1702
1852
 
1703
- /**
1704
- * @typedef {Object} FactorizeModuleOptions
1705
- * @property {ModuleProfile} currentProfile
1706
- * @property {ModuleFactory} factory
1707
- * @property {Dependency[]} dependencies
1708
- * @property {Module | null} originModule
1709
- * @property {Partial<ModuleFactoryCreateDataContextInfo>=} contextInfo
1710
- * @property {string=} context
1711
- */
1853
+ // This avoids deadlocks for circular dependencies
1854
+ if (this.processDependenciesQueue.isProcessing(module)) {
1855
+ return callback();
1856
+ }
1712
1857
 
1713
- /**
1714
- * @param {FactorizeModuleOptions} options options object
1715
- * @param {ModuleCallback} callback callback
1716
- * @returns {void}
1717
- */
1718
- factorizeModule(options, callback) {
1719
- this.factorizeQueue.add(options, callback);
1858
+ this.processModuleDependencies(module, err => {
1859
+ if (err) {
1860
+ return callback(err);
1861
+ }
1862
+ callback(null, module);
1863
+ });
1864
+ });
1720
1865
  }
1721
1866
 
1722
1867
  /**
1723
1868
  * @param {FactorizeModuleOptions} options options object
1724
- * @param {ModuleCallback} callback callback
1869
+ * @param {ModuleOrFactoryResultCallback} callback callback
1725
1870
  * @returns {void}
1726
1871
  */
1727
1872
  _factorizeModule(
@@ -1730,6 +1875,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1730
1875
  factory,
1731
1876
  dependencies,
1732
1877
  originModule,
1878
+ factoryResult,
1733
1879
  contextInfo,
1734
1880
  context
1735
1881
  },
@@ -1763,16 +1909,21 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1763
1909
  module: result
1764
1910
  };
1765
1911
  }
1766
- const { fileDependencies, contextDependencies, missingDependencies } =
1767
- result;
1768
- if (fileDependencies) {
1769
- this.fileDependencies.addAll(fileDependencies);
1770
- }
1771
- if (contextDependencies) {
1772
- this.contextDependencies.addAll(contextDependencies);
1773
- }
1774
- if (missingDependencies) {
1775
- 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
+ }
1776
1927
  }
1777
1928
  }
1778
1929
  if (err) {
@@ -1781,20 +1932,17 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
1781
1932
  err,
1782
1933
  dependencies.map(d => d.loc).filter(Boolean)[0]
1783
1934
  );
1784
- return callback(notFoundError);
1935
+ return callback(notFoundError, factoryResult ? result : undefined);
1785
1936
  }
1786
1937
  if (!result) {
1787
1938
  return callback();
1788
1939
  }
1789
- const newModule = result.module;
1790
- if (!newModule) {
1791
- return callback();
1792
- }
1940
+
1793
1941
  if (currentProfile !== undefined) {
1794
1942
  currentProfile.markFactoryEnd();
1795
1943
  }
1796
1944
 
1797
- callback(null, newModule);
1945
+ callback(null, factoryResult ? result : result.module);
1798
1946
  }
1799
1947
  );
1800
1948
  }
@@ -2020,64 +2168,143 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
2020
2168
  _computeAffectedModules(modules) {
2021
2169
  const moduleMemCacheCache = this.compiler.moduleMemCaches;
2022
2170
  if (!moduleMemCacheCache) return;
2023
- if (!this.moduleMemCaches) this.moduleMemCaches = new WeakMap();
2024
- if (!this.memCache) this.memCache = new MemCache();
2025
- const { moduleGraph, memCache, moduleMemCaches } = this;
2171
+ if (!this.moduleMemCaches) {
2172
+ this.moduleMemCaches = new WeakMap();
2173
+ this.moduleGraph.setModuleMemCaches(this.moduleMemCaches);
2174
+ }
2175
+ const { moduleGraph, moduleMemCaches } = this;
2026
2176
  const affectedModules = new Set();
2027
2177
  const infectedModules = new Set();
2028
2178
  let statNew = 0;
2029
2179
  let statChanged = 0;
2030
2180
  let statUnchanged = 0;
2181
+ let statReferencesChanged = 0;
2031
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
+
2032
2214
  for (const module of modules) {
2033
2215
  const hash = module.buildInfo && module.buildInfo.hash;
2034
2216
  if (typeof hash === "string") {
2035
2217
  const cachedMemCache = moduleMemCacheCache.get(module);
2036
2218
  if (cachedMemCache === undefined) {
2037
2219
  // create a new entry
2220
+ const memCache = new WeakTupleMap();
2038
2221
  moduleMemCacheCache.set(module, {
2039
2222
  hash: hash,
2223
+ references: computeReferences(module),
2040
2224
  memCache
2041
2225
  });
2042
2226
  moduleMemCaches.set(module, memCache);
2043
2227
  affectedModules.add(module);
2044
2228
  statNew++;
2045
- } else if (cachedMemCache.hash === hash) {
2046
- // keep the old mem cache
2047
- moduleMemCaches.set(module, cachedMemCache.memCache);
2048
- statUnchanged++;
2049
- } else {
2229
+ } else if (cachedMemCache.hash !== hash) {
2050
2230
  // use a new one
2231
+ const memCache = new WeakTupleMap();
2051
2232
  moduleMemCaches.set(module, memCache);
2052
2233
  affectedModules.add(module);
2053
2234
  cachedMemCache.hash = hash;
2235
+ cachedMemCache.references = computeReferences(module);
2054
2236
  cachedMemCache.memCache = memCache;
2055
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++;
2056
2250
  }
2057
2251
  } else {
2058
2252
  infectedModules.add(module);
2059
2253
  statWithoutHash++;
2060
2254
  }
2061
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();
2062
2268
  for (const module of infectedModules) {
2063
- for (const referencingModule of moduleGraph
2064
- .getIncomingConnectionsByOriginModule(module)
2065
- .keys()) {
2269
+ for (const [
2270
+ referencingModule,
2271
+ connections
2272
+ ] of moduleGraph.getIncomingConnectionsByOriginModule(module)) {
2273
+ if (!referencingModule) continue;
2066
2274
  if (infectedModules.has(referencingModule)) continue;
2067
- infectedModules.add(referencingModule);
2275
+ const type = reduceAffectType(connections);
2276
+ if (!type) continue;
2277
+ if (type === true) {
2278
+ directOnlyInfectedModules.add(referencingModule);
2279
+ } else {
2280
+ infectedModules.add(referencingModule);
2281
+ }
2068
2282
  }
2069
2283
  }
2284
+ for (const module of directOnlyInfectedModules) infectedModules.add(module);
2285
+ const directOnlyAffectModules = new Set();
2070
2286
  for (const module of affectedModules) {
2071
- for (const referencingModule of moduleGraph
2072
- .getIncomingConnectionsByOriginModule(module)
2073
- .keys()) {
2287
+ for (const [
2288
+ referencingModule,
2289
+ connections
2290
+ ] of moduleGraph.getIncomingConnectionsByOriginModule(module)) {
2074
2291
  if (!referencingModule) continue;
2075
2292
  if (infectedModules.has(referencingModule)) continue;
2076
2293
  if (affectedModules.has(referencingModule)) continue;
2077
- affectedModules.add(referencingModule);
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;
2078
2304
  moduleMemCaches.set(referencingModule, memCache);
2079
2305
  }
2080
2306
  }
2307
+ for (const module of directOnlyAffectModules) affectedModules.add(module);
2081
2308
  this.logger.log(
2082
2309
  `${Math.round(
2083
2310
  (100 * (affectedModules.size + infectedModules.size)) /
@@ -2086,7 +2313,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
2086
2313
  infectedModules.size
2087
2314
  } infected of ${
2088
2315
  this.modules.size
2089
- }) modules flagged as affected (${statNew} new modules, ${statChanged} changed, ${statUnchanged} unchanged, ${statWithoutHash} without hash)`
2316
+ }) modules flagged as affected (${statNew} new modules, ${statChanged} changed, ${statReferencesChanged} references changed, ${statUnchanged} unchanged, ${statWithoutHash} without hash)`
2090
2317
  );
2091
2318
  }
2092
2319
 
@@ -2280,7 +2507,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
2280
2507
  if (err) return callback(err);
2281
2508
 
2282
2509
  // extract warnings and errors from modules
2283
- this.moduleGraph.freeze();
2510
+ this.moduleGraph.freeze("dependency errors");
2284
2511
  // TODO keep a cacheToken (= {}) for each module in the graph
2285
2512
  // create a new one per compilation and flag all updated files
2286
2513
  // and parents with it
@@ -2289,7 +2516,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
2289
2516
  // TODO only run for modules with changed cacheToken
2290
2517
  // global WeakMap<CacheToken, WeakSet<Module>> to keep modules without errors/warnings
2291
2518
  const memCache = moduleMemCaches && moduleMemCaches.get(module);
2292
- if (memCache && memCache.get(module, "noWarningsOrErrors")) continue;
2519
+ if (memCache && memCache.get("noWarningsOrErrors")) continue;
2293
2520
  let hasProblems = this.reportDependencyErrorsAndWarnings(module, [
2294
2521
  module
2295
2522
  ]);
@@ -2313,8 +2540,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
2313
2540
  hasProblems = true;
2314
2541
  }
2315
2542
  }
2316
- if (!hasProblems && memCache)
2317
- memCache.set(module, "noWarningsOrErrors", true);
2543
+ if (!hasProblems && memCache) memCache.set("noWarningsOrErrors", true);
2318
2544
  }
2319
2545
  this.moduleGraph.unfreeze();
2320
2546
  this.logger.timeEnd("report dependency errors and warnings");
@@ -2371,7 +2597,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
2371
2597
 
2372
2598
  this.logger.time("create chunks");
2373
2599
  this.hooks.beforeChunks.call();
2374
- this.moduleGraph.freeze();
2600
+ this.moduleGraph.freeze("seal");
2375
2601
  /** @type {Map<Entrypoint, Module[]>} */
2376
2602
  const chunkGraphInit = new Map();
2377
2603
  for (const [name, { dependencies, includeDependencies, options }] of this
@@ -2907,17 +3133,11 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
2907
3133
  // modules with async blocks depend on the chunk graph and can't be cached that way
2908
3134
  module.blocks.length === 0 &&
2909
3135
  moduleMemCaches.get(module);
2910
- /** @type {RuntimeSpecMap<Set<string>>} */
2911
- const moduleRuntimeRequirementsMemCache =
2912
- memCache &&
2913
- memCache.provide(
2914
- module,
2915
- "moduleRuntimeRequirements",
2916
- () => new RuntimeSpecMap()
2917
- );
2918
3136
  for (const runtime of chunkGraph.getModuleRuntimes(module)) {
2919
- if (moduleRuntimeRequirementsMemCache) {
2920
- const cached = moduleRuntimeRequirementsMemCache.get(runtime);
3137
+ if (memCache) {
3138
+ const cached = memCache.get(
3139
+ `moduleRuntimeRequirements-${getRuntimeKey(runtime)}`
3140
+ );
2921
3141
  if (cached !== undefined) {
2922
3142
  if (cached !== null) {
2923
3143
  chunkGraph.addModuleRuntimeRequirements(
@@ -2938,8 +3158,11 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
2938
3158
  } else if (additionalModuleRuntimeRequirements.isUsed()) {
2939
3159
  set = new Set();
2940
3160
  } else {
2941
- if (moduleRuntimeRequirementsMemCache) {
2942
- moduleRuntimeRequirementsMemCache.set(runtime, null);
3161
+ if (memCache) {
3162
+ memCache.set(
3163
+ `moduleRuntimeRequirements-${getRuntimeKey(runtime)}`,
3164
+ null
3165
+ );
2943
3166
  }
2944
3167
  continue;
2945
3168
  }
@@ -2950,12 +3173,18 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
2950
3173
  if (hook !== undefined) hook.call(module, set, context);
2951
3174
  }
2952
3175
  if (set.size === 0) {
2953
- if (moduleRuntimeRequirementsMemCache) {
2954
- moduleRuntimeRequirementsMemCache.set(runtime, null);
3176
+ if (memCache) {
3177
+ memCache.set(
3178
+ `moduleRuntimeRequirements-${getRuntimeKey(runtime)}`,
3179
+ null
3180
+ );
2955
3181
  }
2956
3182
  } else {
2957
- if (moduleRuntimeRequirementsMemCache) {
2958
- moduleRuntimeRequirementsMemCache.set(runtime, set);
3183
+ if (memCache) {
3184
+ memCache.set(
3185
+ `moduleRuntimeRequirements-${getRuntimeKey(runtime)}`,
3186
+ set
3187
+ );
2959
3188
  chunkGraph.addModuleRuntimeRequirements(
2960
3189
  module,
2961
3190
  runtime,
@@ -3368,13 +3597,9 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
3368
3597
  // modules with async blocks depend on the chunk graph and can't be cached that way
3369
3598
  module.blocks.length === 0 &&
3370
3599
  moduleMemCaches.get(module);
3371
- /** @type {RuntimeSpecMap<string>} */
3372
- const moduleHashesMemCache =
3373
- memCache &&
3374
- memCache.provide(module, "moduleHashes", () => new RuntimeSpecMap());
3375
3600
  for (const runtime of chunkGraph.getModuleRuntimes(module)) {
3376
- if (moduleHashesMemCache) {
3377
- const digest = moduleHashesMemCache.get(runtime);
3601
+ if (memCache) {
3602
+ const digest = memCache.get(`moduleHash-${getRuntimeKey(runtime)}`);
3378
3603
  if (digest !== undefined) {
3379
3604
  chunkGraph.setModuleHashes(
3380
3605
  module,
@@ -3396,8 +3621,8 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
3396
3621
  hashDigest,
3397
3622
  hashDigestLength
3398
3623
  );
3399
- if (moduleHashesMemCache) {
3400
- moduleHashesMemCache.set(runtime, digest);
3624
+ if (memCache) {
3625
+ memCache.set(`moduleHash-${getRuntimeKey(runtime)}`, digest);
3401
3626
  }
3402
3627
  }
3403
3628
  }
@@ -4609,6 +4834,33 @@ This prevents using hashes of each other and should be avoided.`);
4609
4834
  }
4610
4835
  }
4611
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
+
4612
4864
  // Hide from typescript
4613
4865
  const compilationPrototype = Compilation.prototype;
4614
4866