webpack 5.109.1 → 5.109.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/lib/CleanPlugin.js +11 -9
- package/lib/Compilation.js +12 -3
- package/lib/Compiler.js +16 -4
- package/lib/DefinePlugin.js +10 -9
- package/lib/ExternalModule.js +10 -7
- package/lib/ModuleFilenameHelpers.js +13 -2
- package/lib/NormalModule.js +74 -71
- package/lib/NormalModuleFactory.js +8 -2
- package/lib/cache/PackFileCacheStrategy.js +259 -5
- package/lib/config/defaults.js +21 -2
- package/lib/container/ModuleFederationPlugin.js +15 -9
- package/lib/css/CssGenerator.js +8 -2
- package/lib/css/CssLoadingRuntimeModule.js +25 -12
- package/lib/css/CssModulesPlugin.js +29 -18
- package/lib/dependencies/CommonJsFullRequireDependency.js +33 -4
- package/lib/dependencies/CommonJsImportsParserPlugin.js +2 -0
- package/lib/dependencies/HarmonyImportGuard.js +2 -1
- package/lib/esm/ModuleChunkLoadingRuntimeModule.js +15 -9
- package/lib/html/HtmlModulesPlugin.js +30 -12
- package/lib/javascript/JavascriptModulesPlugin.js +83 -49
- package/lib/javascript/JavascriptParser.js +44 -11
- package/lib/optimize/RealContentHashPlugin.js +10 -7
- package/lib/serialization/FileMiddleware.js +299 -60
- package/lib/util/identifier.js +21 -1
- package/package.json +3 -3
- package/schemas/WebpackOptions.json +110 -55
- package/types.d.ts +832 -243
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const FileSystemInfo = require("../FileSystemInfo");
|
|
9
9
|
const ProgressPlugin = require("../ProgressPlugin");
|
|
10
|
+
const { getReferencedFilenames } = require("../serialization/FileMiddleware");
|
|
10
11
|
const SerializerMiddleware = require("../serialization/SerializerMiddleware");
|
|
11
12
|
const LazySet = require("../util/LazySet");
|
|
12
13
|
const formatSize = require("../util/formatSize");
|
|
@@ -34,6 +35,17 @@ const {
|
|
|
34
35
|
/** @typedef {Set<string>} Items */
|
|
35
36
|
/** @typedef {Set<string>} BuildDependencies */
|
|
36
37
|
/** @typedef {Map<string, PackItemInfo>} ItemInfo */
|
|
38
|
+
/** @typedef {{ firstSeen: number, size: number }} UnreferencedFile */
|
|
39
|
+
/** @typedef {Map<string, UnreferencedFile>} UnreferencedFiles */
|
|
40
|
+
|
|
41
|
+
// Unreferenced files are kept for this long to not race concurrent builds sharing the cache directory.
|
|
42
|
+
const CLEANUP_GRACE_PERIOD = 30 * 60 * 1000;
|
|
43
|
+
// Records when each unreferenced file was first seen. Aging by recorded time keeps
|
|
44
|
+
// orphans expiring across caches restored with refreshed modification times.
|
|
45
|
+
const UNREFERENCED_FILE = "unreferenced.json";
|
|
46
|
+
// A file written this recently may belong to a concurrent build that reused the name,
|
|
47
|
+
// in which case the recorded time describes the previous file and must not be trusted.
|
|
48
|
+
const CLEANUP_RECENT_WRITE_PERIOD = 60 * 1000;
|
|
37
49
|
|
|
38
50
|
class PackContainer {
|
|
39
51
|
/**
|
|
@@ -569,6 +581,41 @@ class Pack {
|
|
|
569
581
|
}
|
|
570
582
|
}
|
|
571
583
|
|
|
584
|
+
/**
|
|
585
|
+
* Drops every content whose items all expired. Unlike a partial collection this
|
|
586
|
+
* never unpacks, so it is not limited to a single content per store and lets a
|
|
587
|
+
* long unused cache shrink in one go instead of one pack per build.
|
|
588
|
+
*/
|
|
589
|
+
_gcExpiredContent() {
|
|
590
|
+
const now = Date.now();
|
|
591
|
+
let packCount = 0;
|
|
592
|
+
let itemCount = 0;
|
|
593
|
+
for (let loc = 0; loc < this.content.length; loc++) {
|
|
594
|
+
const content = this.content[loc];
|
|
595
|
+
if (!content) continue;
|
|
596
|
+
let expired = true;
|
|
597
|
+
for (const identifier of content.items) {
|
|
598
|
+
const info = this.itemInfo.get(identifier);
|
|
599
|
+
if (info !== undefined && now - info.lastAccess <= this.maxAge) {
|
|
600
|
+
expired = false;
|
|
601
|
+
break;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
if (!expired) continue;
|
|
605
|
+
for (const identifier of content.items) this.itemInfo.delete(identifier);
|
|
606
|
+
this.content[loc] = undefined;
|
|
607
|
+
packCount++;
|
|
608
|
+
itemCount += content.items.size;
|
|
609
|
+
}
|
|
610
|
+
if (packCount > 0) {
|
|
611
|
+
this.logger.log(
|
|
612
|
+
"Garbage Collected %d completely expired packs with %d items",
|
|
613
|
+
packCount,
|
|
614
|
+
itemCount
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
572
619
|
/**
|
|
573
620
|
* Find the content with the oldest item and run GC on that.
|
|
574
621
|
* Only runs for one content to avoid large invalidation.
|
|
@@ -581,11 +628,10 @@ class Pack {
|
|
|
581
628
|
oldest = info;
|
|
582
629
|
}
|
|
583
630
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
const loc = /** @type {PackItemInfo} */ (oldest).location;
|
|
631
|
+
// collecting expired content may have left no items at all
|
|
632
|
+
if (oldest === undefined) return;
|
|
633
|
+
if (Date.now() - oldest.lastAccess > this.maxAge) {
|
|
634
|
+
const loc = oldest.location;
|
|
589
635
|
if (loc < 0) return;
|
|
590
636
|
const content = /** @type {PackContent} */ (this.content[loc]);
|
|
591
637
|
const items = new Set(content.items);
|
|
@@ -621,6 +667,7 @@ class Pack {
|
|
|
621
667
|
this._persistFreshContent();
|
|
622
668
|
this._optimizeSmallContent();
|
|
623
669
|
this._optimizeUnusedContent();
|
|
670
|
+
this._gcExpiredContent();
|
|
624
671
|
this._gcOldestContent();
|
|
625
672
|
for (const identifier of this.itemInfo.keys()) {
|
|
626
673
|
write(identifier);
|
|
@@ -1166,6 +1213,8 @@ class PackFileCacheStrategy {
|
|
|
1166
1213
|
});
|
|
1167
1214
|
/** @type {Compiler} */
|
|
1168
1215
|
this.compiler = compiler;
|
|
1216
|
+
/** @type {IntermediateFileSystem} */
|
|
1217
|
+
this.fs = fs;
|
|
1169
1218
|
/** @type {string} */
|
|
1170
1219
|
this.context = context;
|
|
1171
1220
|
/** @type {string} */
|
|
@@ -1182,6 +1231,10 @@ class PackFileCacheStrategy {
|
|
|
1182
1231
|
this.readonly = readonly;
|
|
1183
1232
|
/** @type {boolean | undefined} */
|
|
1184
1233
|
this.allowCollectingMemory = allowCollectingMemory;
|
|
1234
|
+
// referenced names per on-disk file, versioned by mtime since concurrent
|
|
1235
|
+
// processes may rewrite packs in place under the same name
|
|
1236
|
+
/** @type {Map<string, { mtimeMs: number, referenced: string[] }>} */
|
|
1237
|
+
this._referencedFilesCache = new Map();
|
|
1185
1238
|
/** @type {false | "gzip" | "brotli" | "zstd" | undefined} */
|
|
1186
1239
|
this.compression = compression;
|
|
1187
1240
|
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
@@ -1588,10 +1641,17 @@ class PackFileCacheStrategy {
|
|
|
1588
1641
|
/** @type {Snapshot} */
|
|
1589
1642
|
(this.resolveBuildDependenciesSnapshot)
|
|
1590
1643
|
);
|
|
1644
|
+
const cleanup = this.fs.unlink !== undefined;
|
|
1645
|
+
/** @type {Set<string> | undefined} */
|
|
1646
|
+
const writtenFiles = cleanup ? new Set() : undefined;
|
|
1647
|
+
/** @type {Set<string> | undefined} */
|
|
1648
|
+
const retainedFiles = cleanup ? new Set() : undefined;
|
|
1591
1649
|
return this.fileSerializer
|
|
1592
1650
|
.serialize(content, {
|
|
1593
1651
|
filename: `${this.cacheLocation}/index${this._extension}`,
|
|
1594
1652
|
extension: `${this._extension}`,
|
|
1653
|
+
writtenFiles,
|
|
1654
|
+
retainedFiles,
|
|
1595
1655
|
logger: this.logger,
|
|
1596
1656
|
profile: this.profile
|
|
1597
1657
|
})
|
|
@@ -1608,9 +1668,17 @@ class PackFileCacheStrategy {
|
|
|
1608
1668
|
stats.count,
|
|
1609
1669
|
Math.round(stats.size / 1024 / 1024)
|
|
1610
1670
|
);
|
|
1671
|
+
if (writtenFiles !== undefined) {
|
|
1672
|
+
return this._cleanupUnusedFiles(
|
|
1673
|
+
writtenFiles,
|
|
1674
|
+
/** @type {Set<string>} */ (retainedFiles)
|
|
1675
|
+
);
|
|
1676
|
+
}
|
|
1611
1677
|
})
|
|
1612
1678
|
.catch((err) => {
|
|
1613
1679
|
this.logger.timeEnd("store pack");
|
|
1680
|
+
// files may be in an unknown state after a failed store
|
|
1681
|
+
this._referencedFilesCache.clear();
|
|
1614
1682
|
this.logger.warn(`Caching failed for pack: ${err}`);
|
|
1615
1683
|
this.logger.debug(err.stack);
|
|
1616
1684
|
});
|
|
@@ -1622,6 +1690,192 @@ class PackFileCacheStrategy {
|
|
|
1622
1690
|
}));
|
|
1623
1691
|
}
|
|
1624
1692
|
|
|
1693
|
+
/**
|
|
1694
|
+
* Reads when the currently unreferenced files were first seen. A missing or
|
|
1695
|
+
* unreadable file just restarts the grace period for every orphan.
|
|
1696
|
+
* @returns {Promise<UnreferencedFiles>} first seen time and size per file name
|
|
1697
|
+
*/
|
|
1698
|
+
_readUnreferencedFiles() {
|
|
1699
|
+
return new Promise((resolve) => {
|
|
1700
|
+
this.fs.readFile(
|
|
1701
|
+
`${this.cacheLocation}/${UNREFERENCED_FILE}`,
|
|
1702
|
+
(err, content) => {
|
|
1703
|
+
/** @type {UnreferencedFiles} */
|
|
1704
|
+
const result = new Map();
|
|
1705
|
+
if (err) return resolve(result);
|
|
1706
|
+
try {
|
|
1707
|
+
const data = JSON.parse(
|
|
1708
|
+
/** @type {Buffer} */ (content).toString("utf8")
|
|
1709
|
+
);
|
|
1710
|
+
for (const [file, entry] of Object.entries(data)) {
|
|
1711
|
+
const { firstSeen, size } = /** @type {UnreferencedFile} */ (
|
|
1712
|
+
entry
|
|
1713
|
+
);
|
|
1714
|
+
if (typeof firstSeen === "number" && typeof size === "number") {
|
|
1715
|
+
result.set(file, { firstSeen, size });
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
} catch (_err) {
|
|
1719
|
+
result.clear();
|
|
1720
|
+
}
|
|
1721
|
+
resolve(result);
|
|
1722
|
+
}
|
|
1723
|
+
);
|
|
1724
|
+
});
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
/**
|
|
1728
|
+
* Persists when the still unreferenced files were first seen. Failing to write
|
|
1729
|
+
* only costs the orphans another grace period, so errors are ignored.
|
|
1730
|
+
* @param {UnreferencedFiles} unreferenced first seen time and size per file name
|
|
1731
|
+
* @param {boolean} hadEntries whether a previous state exists that must be replaced
|
|
1732
|
+
* @returns {Promise<void>} promise
|
|
1733
|
+
*/
|
|
1734
|
+
_writeUnreferencedFiles(unreferenced, hadEntries) {
|
|
1735
|
+
if (unreferenced.size === 0 && !hadEntries) return Promise.resolve();
|
|
1736
|
+
/** @type {Record<string, UnreferencedFile>} */
|
|
1737
|
+
const data = {};
|
|
1738
|
+
for (const [file, entry] of unreferenced) data[file] = entry;
|
|
1739
|
+
return new Promise((resolve) => {
|
|
1740
|
+
this.fs.writeFile(
|
|
1741
|
+
`${this.cacheLocation}/${UNREFERENCED_FILE}`,
|
|
1742
|
+
JSON.stringify(data),
|
|
1743
|
+
() => resolve()
|
|
1744
|
+
);
|
|
1745
|
+
});
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
/**
|
|
1749
|
+
* Deletes files from the cache directory that are no longer referenced by the
|
|
1750
|
+
* stored pack. Retained files are walked on disk since nested lazy segments
|
|
1751
|
+
* reference files not visible during serialization. Errors only log a warning.
|
|
1752
|
+
* @param {Set<string>} writtenNames names (without extension) written by this store
|
|
1753
|
+
* @param {Set<string>} retainedNames names (without extension) referenced but not rewritten
|
|
1754
|
+
* @returns {Promise<void>} promise
|
|
1755
|
+
*/
|
|
1756
|
+
async _cleanupUnusedFiles(writtenNames, retainedNames) {
|
|
1757
|
+
this.logger.time("cleanup unused cache files");
|
|
1758
|
+
const fs = this.fs;
|
|
1759
|
+
const extension = this._extension;
|
|
1760
|
+
const cacheLocation = this.cacheLocation;
|
|
1761
|
+
const referencedFilesCache = this._referencedFilesCache;
|
|
1762
|
+
try {
|
|
1763
|
+
// rewritten files may reference different names now
|
|
1764
|
+
for (const name of writtenNames) referencedFilesCache.delete(name);
|
|
1765
|
+
/** @type {Set<string>} */
|
|
1766
|
+
const liveFiles = new Set([`index${extension}`, UNREFERENCED_FILE]);
|
|
1767
|
+
for (const name of writtenNames) liveFiles.add(`${name}${extension}`);
|
|
1768
|
+
/** @type {string[]} */
|
|
1769
|
+
const queue = [];
|
|
1770
|
+
/**
|
|
1771
|
+
* Marks a file live and queues it for walking its references.
|
|
1772
|
+
* @param {string} name file name without extension
|
|
1773
|
+
*/
|
|
1774
|
+
const enqueue = (name) => {
|
|
1775
|
+
const file = `${name}${extension}`;
|
|
1776
|
+
if (liveFiles.has(file)) return;
|
|
1777
|
+
liveFiles.add(file);
|
|
1778
|
+
queue.push(name);
|
|
1779
|
+
};
|
|
1780
|
+
for (const name of retainedNames) enqueue(name);
|
|
1781
|
+
while (queue.length > 0) {
|
|
1782
|
+
const name = /** @type {string} */ (queue.pop());
|
|
1783
|
+
const file = `${cacheLocation}/${name}${extension}`;
|
|
1784
|
+
// an unchanged mtime proves the memo entry still matches the disk
|
|
1785
|
+
const mtimeMs = await new Promise((resolve, reject) => {
|
|
1786
|
+
fs.stat(file, (err, stats) => {
|
|
1787
|
+
if (err) return reject(err);
|
|
1788
|
+
resolve(
|
|
1789
|
+
/** @type {number} */ (
|
|
1790
|
+
/** @type {import("../util/fs").IStats} */ (stats).mtimeMs
|
|
1791
|
+
)
|
|
1792
|
+
);
|
|
1793
|
+
});
|
|
1794
|
+
});
|
|
1795
|
+
const entry = referencedFilesCache.get(name);
|
|
1796
|
+
let referenced;
|
|
1797
|
+
if (entry !== undefined && entry.mtimeMs === mtimeMs) {
|
|
1798
|
+
referenced = entry.referenced;
|
|
1799
|
+
} else {
|
|
1800
|
+
referenced = await getReferencedFilenames(fs, file);
|
|
1801
|
+
referencedFilesCache.set(name, { mtimeMs, referenced });
|
|
1802
|
+
}
|
|
1803
|
+
for (const referencedName of referenced) enqueue(referencedName);
|
|
1804
|
+
}
|
|
1805
|
+
const files = await new Promise((resolve, reject) => {
|
|
1806
|
+
fs.readdir(cacheLocation, (err, files) => {
|
|
1807
|
+
if (err) return reject(err);
|
|
1808
|
+
resolve(/** @type {string[]} */ (files));
|
|
1809
|
+
});
|
|
1810
|
+
});
|
|
1811
|
+
const seenFiles = await this._readUnreferencedFiles();
|
|
1812
|
+
const now = Date.now();
|
|
1813
|
+
// every store rewrites the index backup, so a recorded time would age a file
|
|
1814
|
+
// that is in fact new; renaming carries the previous index mtime onto it
|
|
1815
|
+
const indexBackup = `index${extension}.old`;
|
|
1816
|
+
/** @type {UnreferencedFiles} */
|
|
1817
|
+
const stillUnreferenced = new Map();
|
|
1818
|
+
let deletedCount = 0;
|
|
1819
|
+
for (const file of files) {
|
|
1820
|
+
if (typeof file !== "string" || liveFiles.has(file)) continue;
|
|
1821
|
+
const path = `${cacheLocation}/${file}`;
|
|
1822
|
+
const stats = await new Promise((resolve) => {
|
|
1823
|
+
fs.stat(path, (err, stats) => {
|
|
1824
|
+
resolve(
|
|
1825
|
+
err
|
|
1826
|
+
? undefined
|
|
1827
|
+
: /** @type {import("../util/fs").IStats} */ (stats)
|
|
1828
|
+
);
|
|
1829
|
+
});
|
|
1830
|
+
});
|
|
1831
|
+
if (stats === undefined || !stats.isFile()) continue;
|
|
1832
|
+
const size = /** @type {number} */ (stats.size);
|
|
1833
|
+
const seen = seenFiles.get(file);
|
|
1834
|
+
// a differing size means the name was rewritten, so it is a new orphan
|
|
1835
|
+
const firstSeen =
|
|
1836
|
+
seen !== undefined && seen.size === size ? seen.firstSeen : now;
|
|
1837
|
+
const expireTime = now - CLEANUP_GRACE_PERIOD;
|
|
1838
|
+
const mtimeMs = /** @type {number} */ (stats.mtimeMs);
|
|
1839
|
+
// either signal is enough: modification times are lost when a cache is
|
|
1840
|
+
// restored, and recorded times are lost when the cache directory is new
|
|
1841
|
+
const expired =
|
|
1842
|
+
mtimeMs <= expireTime ||
|
|
1843
|
+
(file !== indexBackup &&
|
|
1844
|
+
firstSeen <= expireTime &&
|
|
1845
|
+
mtimeMs <= now - CLEANUP_RECENT_WRITE_PERIOD);
|
|
1846
|
+
if (expired) {
|
|
1847
|
+
const deleted = await new Promise((resolve) => {
|
|
1848
|
+
/** @type {NonNullable<IntermediateFileSystem["unlink"]>} */
|
|
1849
|
+
(fs.unlink)(path, (err) => resolve(!err));
|
|
1850
|
+
});
|
|
1851
|
+
if (deleted) {
|
|
1852
|
+
deletedCount++;
|
|
1853
|
+
continue;
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
if (file !== indexBackup) {
|
|
1857
|
+
stillUnreferenced.set(file, { firstSeen, size });
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
await this._writeUnreferencedFiles(stillUnreferenced, seenFiles.size > 0);
|
|
1861
|
+
// drop entries of files that are no longer alive
|
|
1862
|
+
for (const name of referencedFilesCache.keys()) {
|
|
1863
|
+
if (!liveFiles.has(`${name}${extension}`)) {
|
|
1864
|
+
referencedFilesCache.delete(name);
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
if (deletedCount > 0) {
|
|
1868
|
+
this.logger.log("Deleted %d unused cache files", deletedCount);
|
|
1869
|
+
}
|
|
1870
|
+
} catch (err) {
|
|
1871
|
+
this.logger.warn(
|
|
1872
|
+
`Cleanup of unused cache files failed: ${/** @type {Error} */ (err)}`
|
|
1873
|
+
);
|
|
1874
|
+
this.logger.debug(/** @type {Error} */ (err).stack);
|
|
1875
|
+
}
|
|
1876
|
+
this.logger.timeEnd("cleanup unused cache files");
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1625
1879
|
clear() {
|
|
1626
1880
|
this.fileSystemInfo.clear();
|
|
1627
1881
|
this.buildDependencies.clear();
|
package/lib/config/defaults.js
CHANGED
|
@@ -511,7 +511,13 @@ const applyWebpackOptionsDefaults = (options, compilerIndex) => {
|
|
|
511
511
|
/** @type {NonNullable<WebpackOptionsNormalized["loader"]>} */ (
|
|
512
512
|
options.loader
|
|
513
513
|
),
|
|
514
|
-
{
|
|
514
|
+
{
|
|
515
|
+
targetProperties,
|
|
516
|
+
environment: options.output.environment,
|
|
517
|
+
outputModule:
|
|
518
|
+
/** @type {NonNullable<WebpackOptionsNormalized["output"]["module"]>} */
|
|
519
|
+
(options.output.module)
|
|
520
|
+
}
|
|
515
521
|
);
|
|
516
522
|
|
|
517
523
|
F(options, "externalsType", () => {
|
|
@@ -2251,9 +2257,13 @@ const applyExternalsPresetsDefaults = (
|
|
|
2251
2257
|
* @param {object} options options
|
|
2252
2258
|
* @param {TargetProperties | false} options.targetProperties target properties
|
|
2253
2259
|
* @param {Environment} options.environment environment
|
|
2260
|
+
* @param {boolean} options.outputModule is output type is module
|
|
2254
2261
|
* @returns {void}
|
|
2255
2262
|
*/
|
|
2256
|
-
const applyLoaderDefaults = (
|
|
2263
|
+
const applyLoaderDefaults = (
|
|
2264
|
+
loader,
|
|
2265
|
+
{ targetProperties, environment, outputModule }
|
|
2266
|
+
) => {
|
|
2257
2267
|
F(loader, "target", () => {
|
|
2258
2268
|
if (targetProperties) {
|
|
2259
2269
|
if (targetProperties.electron) {
|
|
@@ -2267,6 +2277,15 @@ const applyLoaderDefaults = (loader, { targetProperties, environment }) => {
|
|
|
2267
2277
|
if (targetProperties.bun) return "bun";
|
|
2268
2278
|
if (targetProperties.node) return "node";
|
|
2269
2279
|
if (targetProperties.web) return "web";
|
|
2280
|
+
// no single platform to report: the bundle runs on both (target
|
|
2281
|
+
// `"universal"` / `["web", "node"]`), so loaders get `"universal"`
|
|
2282
|
+
if (
|
|
2283
|
+
outputModule &&
|
|
2284
|
+
targetProperties.node === null &&
|
|
2285
|
+
targetProperties.web === null
|
|
2286
|
+
) {
|
|
2287
|
+
return "universal";
|
|
2288
|
+
}
|
|
2270
2289
|
}
|
|
2271
2290
|
});
|
|
2272
2291
|
D(loader, "environment", environment);
|
|
@@ -18,11 +18,21 @@ const HoistContainerReferences = require("./HoistContainerReferencesPlugin");
|
|
|
18
18
|
/** @typedef {import("../Compiler")} Compiler */
|
|
19
19
|
/** @typedef {import("../Dependency")} Dependency */
|
|
20
20
|
|
|
21
|
+
const createCompilationHooks = () => ({
|
|
22
|
+
/**
|
|
23
|
+
* @type {SyncHook<Dependency>}
|
|
24
|
+
* @since 5.96.0
|
|
25
|
+
*/
|
|
26
|
+
addContainerEntryDependency: new SyncHook(["dependency"]),
|
|
27
|
+
/**
|
|
28
|
+
* @type {SyncHook<Dependency>}
|
|
29
|
+
* @since 5.96.0
|
|
30
|
+
*/
|
|
31
|
+
addFederationRuntimeDependency: new SyncHook(["dependency"])
|
|
32
|
+
});
|
|
33
|
+
|
|
21
34
|
/**
|
|
22
|
-
*
|
|
23
|
-
* @typedef {object} CompilationHooks
|
|
24
|
-
* @property {SyncHook<Dependency>} addContainerEntryDependency
|
|
25
|
-
* @property {SyncHook<Dependency>} addFederationRuntimeDependency
|
|
35
|
+
* @typedef {ReturnType<typeof createCompilationHooks>} CompilationHooks
|
|
26
36
|
*/
|
|
27
37
|
|
|
28
38
|
const PLUGIN_NAME = "ModuleFederationPlugin";
|
|
@@ -111,11 +121,7 @@ class ModuleFederationPlugin {
|
|
|
111
121
|
}
|
|
112
122
|
|
|
113
123
|
ModuleFederationPlugin.getCompilationHooks = createHooksRegistry(
|
|
114
|
-
|
|
115
|
-
/** @type {CompilationHooks} */ ({
|
|
116
|
-
addContainerEntryDependency: new SyncHook(["dependency"]),
|
|
117
|
-
addFederationRuntimeDependency: new SyncHook(["dependency"])
|
|
118
|
-
})
|
|
124
|
+
createCompilationHooks
|
|
119
125
|
);
|
|
120
126
|
|
|
121
127
|
module.exports = ModuleFederationPlugin;
|
package/lib/css/CssGenerator.js
CHANGED
|
@@ -33,6 +33,7 @@ const CssImportDependency = require("../dependencies/CssImportDependency");
|
|
|
33
33
|
const HarmonyImportSideEffectDependency = require("../dependencies/HarmonyImportSideEffectDependency");
|
|
34
34
|
|
|
35
35
|
const { encodeMappings } = require("../util/createMappings");
|
|
36
|
+
const { contextifySourceUrl } = require("../util/identifier");
|
|
36
37
|
const memoize = require("../util/memoize");
|
|
37
38
|
const {
|
|
38
39
|
PUBLIC_PATH_FULL_HASH,
|
|
@@ -549,8 +550,13 @@ class CssGenerator extends Generator {
|
|
|
549
550
|
}
|
|
550
551
|
|
|
551
552
|
const generatedJs = /** @type {string} */ (source.source());
|
|
552
|
-
|
|
553
|
-
|
|
553
|
+
// Context-relative identifier, like `NormalModule.createSource` — so
|
|
554
|
+
// `SourceMapDevToolPlugin` can match the module and apply the
|
|
555
|
+
// devtool filename template instead of leaking this raw name
|
|
556
|
+
const sourceName = contextifySourceUrl(
|
|
557
|
+
/** @type {string} */ (compilation.options.context),
|
|
558
|
+
module.identifier(),
|
|
559
|
+
compilation.compiler.root
|
|
554
560
|
);
|
|
555
561
|
|
|
556
562
|
if (
|
|
@@ -20,12 +20,31 @@ const { chunkHasCss } = require("./CssModulesPlugin");
|
|
|
20
20
|
/** @typedef {import("../ChunkGraph")} ChunkGraph */
|
|
21
21
|
/** @typedef {import("../Module").ReadOnlyRuntimeRequirements} ReadOnlyRuntimeRequirements */
|
|
22
22
|
|
|
23
|
+
const createCompilationHooks = () => ({
|
|
24
|
+
/**
|
|
25
|
+
* @type {SyncWaterfallHook<[string, Chunk]>}
|
|
26
|
+
* @since 5.66.0
|
|
27
|
+
*/
|
|
28
|
+
createStylesheet: new SyncWaterfallHook(["source", "chunk"]),
|
|
29
|
+
/**
|
|
30
|
+
* @type {SyncWaterfallHook<[string, Chunk]>}
|
|
31
|
+
* @since 5.91.0
|
|
32
|
+
*/
|
|
33
|
+
linkPreload: new SyncWaterfallHook(["source", "chunk"]),
|
|
34
|
+
/**
|
|
35
|
+
* @type {SyncWaterfallHook<[string, Chunk]>}
|
|
36
|
+
* @since 5.91.0
|
|
37
|
+
*/
|
|
38
|
+
linkPrefetch: new SyncWaterfallHook(["source", "chunk"]),
|
|
39
|
+
/**
|
|
40
|
+
* @type {SyncWaterfallHook<[string, Chunk]>}
|
|
41
|
+
* @since 5.107.0
|
|
42
|
+
*/
|
|
43
|
+
linkInsert: new SyncWaterfallHook(["source", "chunk"])
|
|
44
|
+
});
|
|
45
|
+
|
|
23
46
|
/**
|
|
24
|
-
* @typedef {
|
|
25
|
-
* @property {SyncWaterfallHook<[string, Chunk]>} createStylesheet
|
|
26
|
-
* @property {SyncWaterfallHook<[string, Chunk]>} linkPreload
|
|
27
|
-
* @property {SyncWaterfallHook<[string, Chunk]>} linkPrefetch
|
|
28
|
-
* @property {SyncWaterfallHook<[string, Chunk]>} linkInsert
|
|
47
|
+
* @typedef {ReturnType<typeof createCompilationHooks>} CssLoadingRuntimeModulePluginHooks
|
|
29
48
|
*/
|
|
30
49
|
|
|
31
50
|
class CssLoadingRuntimeModule extends RuntimeModule {
|
|
@@ -577,13 +596,7 @@ class CssLoadingRuntimeModule extends RuntimeModule {
|
|
|
577
596
|
}
|
|
578
597
|
|
|
579
598
|
CssLoadingRuntimeModule.getCompilationHooks = createHooksRegistry(
|
|
580
|
-
|
|
581
|
-
/** @type {CssLoadingRuntimeModulePluginHooks} */ ({
|
|
582
|
-
createStylesheet: new SyncWaterfallHook(["source", "chunk"]),
|
|
583
|
-
linkPreload: new SyncWaterfallHook(["source", "chunk"]),
|
|
584
|
-
linkPrefetch: new SyncWaterfallHook(["source", "chunk"]),
|
|
585
|
-
linkInsert: new SyncWaterfallHook(["source", "chunk"])
|
|
586
|
-
})
|
|
599
|
+
createCompilationHooks
|
|
587
600
|
);
|
|
588
601
|
|
|
589
602
|
module.exports = CssLoadingRuntimeModule;
|
|
@@ -95,14 +95,6 @@ const CssParser = require("./CssParser");
|
|
|
95
95
|
* @property {Source} moduleSourceContent content
|
|
96
96
|
*/
|
|
97
97
|
|
|
98
|
-
/**
|
|
99
|
-
* Defines the compilation hooks type used by this module.
|
|
100
|
-
* @typedef {object} CompilationHooks
|
|
101
|
-
* @property {SyncWaterfallHook<[Source, Module, ChunkRenderContext]>} renderModulePackage
|
|
102
|
-
* @property {SyncHook<[Chunk, Hash, ChunkHashContext]>} chunkHash
|
|
103
|
-
* @property {SyncBailHook<[Chunk, Module[], Compilation], Module[] | undefined | void>} orderModules called for each CSS source type (CSS_IMPORT_TYPE, CSS_TYPE) with the chunk's modules pre-sorted by full module name; return an ordered `Module[]` to override the default import-order topological sort, or return `undefined` to keep the default
|
|
104
|
-
*/
|
|
105
|
-
|
|
106
98
|
/**
|
|
107
99
|
* Defines the module factory cache entry type used by this module.
|
|
108
100
|
* @typedef {object} ModuleFactoryCacheEntry
|
|
@@ -220,6 +212,34 @@ const generatorValidationOptions = {
|
|
|
220
212
|
|
|
221
213
|
const PLUGIN_NAME = "CssModulesPlugin";
|
|
222
214
|
|
|
215
|
+
const createCompilationHooks = () => ({
|
|
216
|
+
/**
|
|
217
|
+
* @type {SyncWaterfallHook<[Source, Module, ChunkRenderContext]>}
|
|
218
|
+
* @since 5.94.0
|
|
219
|
+
*/
|
|
220
|
+
renderModulePackage: new SyncWaterfallHook([
|
|
221
|
+
"source",
|
|
222
|
+
"module",
|
|
223
|
+
"renderContext"
|
|
224
|
+
]),
|
|
225
|
+
/**
|
|
226
|
+
* @type {SyncHook<[Chunk, Hash, ChunkHashContext]>}
|
|
227
|
+
* @since 5.94.0
|
|
228
|
+
*/
|
|
229
|
+
chunkHash: new SyncHook(["chunk", "hash", "context"]),
|
|
230
|
+
/**
|
|
231
|
+
* Called for each CSS source type (CSS_IMPORT_TYPE, CSS_TYPE) with the chunk's modules pre-sorted by full module name; return an ordered `Module[]` to override the default import-order topological sort, or return `undefined` to keep the default.
|
|
232
|
+
* @type {SyncBailHook<[Chunk, Module[], Compilation], Module[] | undefined | void>}
|
|
233
|
+
* @since 5.107.0
|
|
234
|
+
*/
|
|
235
|
+
orderModules: new SyncBailHook(["chunk", "modules", "compilation"])
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Defines the compilation hooks type used by this module.
|
|
240
|
+
* @typedef {ReturnType<typeof createCompilationHooks>} CompilationHooks
|
|
241
|
+
*/
|
|
242
|
+
|
|
223
243
|
class CssModulesPlugin {
|
|
224
244
|
constructor() {
|
|
225
245
|
/** @type {WeakMap<Source, ModuleFactoryCacheEntry>} */
|
|
@@ -1175,16 +1195,7 @@ class CssModulesPlugin {
|
|
|
1175
1195
|
}
|
|
1176
1196
|
|
|
1177
1197
|
CssModulesPlugin.getCompilationHooks = createHooksRegistry(
|
|
1178
|
-
|
|
1179
|
-
/** @type {CompilationHooks} */ ({
|
|
1180
|
-
renderModulePackage: new SyncWaterfallHook([
|
|
1181
|
-
"source",
|
|
1182
|
-
"module",
|
|
1183
|
-
"renderContext"
|
|
1184
|
-
]),
|
|
1185
|
-
chunkHash: new SyncHook(["chunk", "hash", "context"]),
|
|
1186
|
-
orderModules: new SyncBailHook(["chunk", "modules", "compilation"])
|
|
1187
|
-
})
|
|
1198
|
+
createCompilationHooks
|
|
1188
1199
|
);
|
|
1189
1200
|
|
|
1190
1201
|
module.exports = CssModulesPlugin;
|
|
@@ -9,6 +9,7 @@ const Template = require("../Template");
|
|
|
9
9
|
const { equals } = require("../util/ArrayHelpers");
|
|
10
10
|
const { getTrimmedIdsAndRange } = require("../util/chainedImports");
|
|
11
11
|
const makeSerializable = require("../util/makeSerializable");
|
|
12
|
+
const memoize = require("../util/memoize");
|
|
12
13
|
const { propertyAccess } = require("../util/property");
|
|
13
14
|
const {
|
|
14
15
|
ESM_MODULE_EXPORTS_NAME,
|
|
@@ -19,6 +20,7 @@ const ModuleDependency = require("./ModuleDependency");
|
|
|
19
20
|
|
|
20
21
|
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
|
|
21
22
|
/** @typedef {import("../Dependency")} Dependency */
|
|
23
|
+
/** @typedef {import("../Dependency").GetConditionFn} GetConditionFn */
|
|
22
24
|
/** @typedef {import("../Dependency").ReferencedExports} ReferencedExports */
|
|
23
25
|
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
|
|
24
26
|
/** @typedef {import("../ModuleGraph")} ModuleGraph */
|
|
@@ -26,8 +28,11 @@ const ModuleDependency = require("./ModuleDependency");
|
|
|
26
28
|
/** @typedef {import("../ExportsInfo").ExportInfoName} ExportInfoName */
|
|
27
29
|
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
|
|
28
30
|
/** @typedef {import("../util/chainedImports").IdRanges} IdRanges */
|
|
29
|
-
/** @typedef {import("
|
|
30
|
-
/** @typedef {import("../serialization/ObjectMiddleware").
|
|
31
|
+
/** @typedef {import("./HarmonyImportGuard").DependencyGuard} DependencyGuard */
|
|
32
|
+
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext<[ExportInfoName[], IdRanges | undefined, boolean, undefined | boolean, DependencyGuard[] | undefined]>} ObjectDeserializerContext */
|
|
33
|
+
/** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext<[ExportInfoName[], IdRanges | undefined, boolean, undefined | boolean, DependencyGuard[] | undefined]>} ObjectSerializerContext */
|
|
34
|
+
|
|
35
|
+
const getHarmonyImportGuard = memoize(() => require("./HarmonyImportGuard"));
|
|
31
36
|
|
|
32
37
|
class CommonJsFullRequireDependency extends ModuleDependency {
|
|
33
38
|
/**
|
|
@@ -53,6 +58,20 @@ class CommonJsFullRequireDependency extends ModuleDependency {
|
|
|
53
58
|
this.call = false;
|
|
54
59
|
/** @type {undefined | boolean} */
|
|
55
60
|
this.asiSafe = undefined;
|
|
61
|
+
/** @type {DependencyGuard[] | undefined} */
|
|
62
|
+
this.branchGuards = undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Returns function to determine if the connection is active.
|
|
67
|
+
* @param {ModuleGraph} moduleGraph module graph
|
|
68
|
+
* @returns {null | false | GetConditionFn} function to determine if the connection is active
|
|
69
|
+
*/
|
|
70
|
+
getCondition(moduleGraph) {
|
|
71
|
+
const guards = this.branchGuards;
|
|
72
|
+
if (guards === undefined) return null;
|
|
73
|
+
return (connection, runtime) =>
|
|
74
|
+
!getHarmonyImportGuard().isDeadByGuards(guards, moduleGraph, runtime);
|
|
56
75
|
}
|
|
57
76
|
|
|
58
77
|
/**
|
|
@@ -92,7 +111,8 @@ class CommonJsFullRequireDependency extends ModuleDependency {
|
|
|
92
111
|
.write(this.names)
|
|
93
112
|
.write(this.idRanges)
|
|
94
113
|
.write(this.call)
|
|
95
|
-
.write(this.asiSafe)
|
|
114
|
+
.write(this.asiSafe)
|
|
115
|
+
.write(this.branchGuards);
|
|
96
116
|
super.serialize(context);
|
|
97
117
|
}
|
|
98
118
|
|
|
@@ -108,7 +128,9 @@ class CommonJsFullRequireDependency extends ModuleDependency {
|
|
|
108
128
|
this.call = c2.read();
|
|
109
129
|
const c3 = c2.rest;
|
|
110
130
|
this.asiSafe = c3.read();
|
|
111
|
-
|
|
131
|
+
const c4 = c3.rest;
|
|
132
|
+
this.branchGuards = c4.read();
|
|
133
|
+
super.deserialize(c4.rest);
|
|
112
134
|
}
|
|
113
135
|
|
|
114
136
|
get type() {
|
|
@@ -137,6 +159,13 @@ CommonJsFullRequireDependency.Template = class CommonJsFullRequireDependencyTemp
|
|
|
137
159
|
) {
|
|
138
160
|
const dep = /** @type {CommonJsFullRequireDependency} */ (dependency);
|
|
139
161
|
if (!dep.range) return;
|
|
162
|
+
const connection = moduleGraph.getConnection(dep);
|
|
163
|
+
// Dead branch: module is excluded and has no id; code is never executed.
|
|
164
|
+
if (connection && !connection.isTargetActive(runtime)) {
|
|
165
|
+
// Replaces the whole member chain, so no property access is left dangling
|
|
166
|
+
source.replace(dep.range[0], dep.range[1] - 1, "null /* dead branch */");
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
140
169
|
const importedModule = moduleGraph.getModule(dep);
|
|
141
170
|
let requireExpr = runtimeTemplate.moduleExports({
|
|
142
171
|
module: importedModule,
|
|
@@ -642,6 +642,7 @@ class CommonJsImportsParserPlugin {
|
|
|
642
642
|
dep.optional = Boolean(parser.scope.inTry);
|
|
643
643
|
dep.loc = parser.getLocation(expr);
|
|
644
644
|
parser.state.current.addDependency(dep);
|
|
645
|
+
getHarmonyImportGuard().attachDependencyGuards(parser, dep);
|
|
645
646
|
return true;
|
|
646
647
|
}
|
|
647
648
|
};
|
|
@@ -680,6 +681,7 @@ class CommonJsImportsParserPlugin {
|
|
|
680
681
|
dep.optional = Boolean(parser.scope.inTry);
|
|
681
682
|
dep.loc = parser.getLocation(expr.callee);
|
|
682
683
|
parser.state.current.addDependency(dep);
|
|
684
|
+
getHarmonyImportGuard().attachDependencyGuards(parser, dep);
|
|
683
685
|
parser.walkExpressions(expr.arguments);
|
|
684
686
|
return true;
|
|
685
687
|
}
|
|
@@ -14,6 +14,7 @@ const memoize = require("../util/memoize");
|
|
|
14
14
|
/** @typedef {import("../ModuleGraph")} ModuleGraph */
|
|
15
15
|
/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
|
|
16
16
|
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
|
|
17
|
+
/** @typedef {import("./CommonJsFullRequireDependency")} CommonJsFullRequireDependency */
|
|
17
18
|
/** @typedef {import("./CommonJsRequireDependency")} CommonJsRequireDependency */
|
|
18
19
|
/** @typedef {import("./HarmonyEvaluatedImportSpecifierDependency")} HarmonyEvaluatedImportSpecifierDependency */
|
|
19
20
|
/** @typedef {import("./HarmonyImportSpecifierDependency")} HarmonyImportSpecifierDependency */
|
|
@@ -29,7 +30,7 @@ const memoize = require("../util/memoize");
|
|
|
29
30
|
/** @typedef {["&&" | "||" | "??", GuardFormula, GuardFormula]} GuardLogical */
|
|
30
31
|
/** @typedef {GuardAtom | GuardPresenceAtom | GuardUnknown | GuardNot | GuardLogical} GuardFormula */
|
|
31
32
|
/** @typedef {{ formula: GuardFormula, value: boolean }} DependencyGuard branch guard: the dependency is live only when the formula evaluates to `value` */
|
|
32
|
-
/** @typedef {HarmonyImportSpecifierDependency | CommonJsRequireDependency | ImportDependency} GuardableDependency */
|
|
33
|
+
/** @typedef {HarmonyImportSpecifierDependency | CommonJsRequireDependency | CommonJsFullRequireDependency | ImportDependency} GuardableDependency */
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
36
|
* A guard frame pushed onto `parser.state.guardStack` for the duration of one
|