@module-federation/vite 1.11.1 → 1.12.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.
Files changed (3) hide show
  1. package/lib/index.cjs +352 -59
  2. package/lib/index.mjs +352 -59
  3. package/package.json +13 -12
package/lib/index.cjs CHANGED
@@ -39,6 +39,7 @@ let estree_walker = require("estree-walker");
39
39
  let _module_federation_sdk = require("@module-federation/sdk");
40
40
  let _module_federation_dts_plugin = require("@module-federation/dts-plugin");
41
41
  let _module_federation_dts_plugin_core = require("@module-federation/dts-plugin/core");
42
+ let module$1 = require("module");
42
43
  let url = require("url");
43
44
 
44
45
  //#region src/utils/mapCodeToCodeWithSourcemap.ts
@@ -771,6 +772,22 @@ function serializeRuntimeOptions(options) {
771
772
 
772
773
  //#endregion
773
774
  //#region src/utils/VirtualModule.ts
775
+ /**
776
+ * Initialize virtual module infrastructure BEFORE VirtualModule class is used.
777
+ * This must be called in the config hook to ensure the directory exists
778
+ * before Vite's optimization phase.
779
+ */
780
+ function initVirtualModuleInfrastructure(root, virtualModuleDir = "__mf__virtual") {
781
+ const virtualPackagePath = (0, pathe.join)((0, pathe.join)(root, "node_modules"), virtualModuleDir);
782
+ if (!(0, fs.existsSync)(virtualPackagePath)) {
783
+ (0, fs.mkdirSync)(virtualPackagePath, { recursive: true });
784
+ (0, fs.writeFileSync)((0, pathe.join)(virtualPackagePath, "empty.js"), "");
785
+ (0, fs.writeFileSync)((0, pathe.join)(virtualPackagePath, "package.json"), JSON.stringify({
786
+ name: virtualModuleDir,
787
+ main: "empty.js"
788
+ }));
789
+ }
790
+ }
774
791
  let rootDir;
775
792
  function findNodeModulesDir(root = process.cwd()) {
776
793
  let currentDir = root;
@@ -859,9 +876,10 @@ var VirtualModule = class {
859
876
 
860
877
  //#endregion
861
878
  //#region src/virtualModules/virtualExposes.ts
862
- const VIRTUAL_EXPOSES = "virtual:mf-exposes";
863
- function generateExposes() {
864
- const options = getNormalizeModuleFederationOptions();
879
+ function getVirtualExposesId(options) {
880
+ return `virtual:mf-exposes:${`${options.name}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
881
+ }
882
+ function generateExposes(options) {
865
883
  return `
866
884
  export default {
867
885
  ${Object.keys(options.exposes).map((key) => {
@@ -911,10 +929,10 @@ ${exportStatement}
911
929
  //#region src/virtualModules/virtualRemotes.ts
912
930
  const cacheRemoteMap = {};
913
931
  const LOAD_REMOTE_TAG = "__loadRemote__";
914
- function getRemoteVirtualModule(remote, command) {
932
+ function getRemoteVirtualModule(remote, command, isRolldown) {
915
933
  if (!cacheRemoteMap[remote]) {
916
- cacheRemoteMap[remote] = new VirtualModule(remote, LOAD_REMOTE_TAG, ".js");
917
- cacheRemoteMap[remote].writeSync(generateRemotes(remote, command));
934
+ cacheRemoteMap[remote] = new VirtualModule(remote, LOAD_REMOTE_TAG, isRolldown ? ".mjs" : ".js");
935
+ cacheRemoteMap[remote].writeSync(generateRemotes(remote, command, isRolldown));
918
936
  }
919
937
  return cacheRemoteMap[remote];
920
938
  }
@@ -926,11 +944,11 @@ function addUsedRemote(remoteKey, remoteModule) {
926
944
  function getUsedRemotesMap() {
927
945
  return usedRemotesMap;
928
946
  }
929
- function generateRemotes(id, command) {
930
- const isBuild = command === "build";
931
- const importLine = isBuild ? `import { initPromise } from "${virtualRuntimeInitStatus.getImportId()}"` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
932
- const awaitOrPlaceholder = isBuild ? "await " : "/*mf top-level-await placeholder replacement mf*/";
933
- const exportLine = isBuild ? "export default exportModule" : "module.exports = exportModule";
947
+ function generateRemotes(id, command, isRolldown) {
948
+ const useESM = command === "build" || isRolldown;
949
+ const importLine = useESM ? `import { initPromise } from "${virtualRuntimeInitStatus.getImportId()}"` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
950
+ const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
951
+ const exportLine = command === "serve" && useESM ? "export default exportModule.default ?? exportModule" : useESM ? "export default exportModule" : "module.exports = exportModule";
934
952
  return `
935
953
  ${importLine}
936
954
  const res = initPromise.then(runtime => runtime.loadRemote(${JSON.stringify(id)}))
@@ -941,6 +959,24 @@ function generateRemotes(id, command) {
941
959
 
942
960
  //#endregion
943
961
  //#region src/virtualModules/virtualShared_preBuild.ts
962
+ /**
963
+ * Even the resolveId hook cannot interfere with vite pre-build,
964
+ * and adding query parameter virtual modules will also fail.
965
+ * You can only proxy to the real file through alias
966
+ */
967
+ /**
968
+ * shared will be proxied:
969
+ * 1. __prebuild__: export shareModule (pre-built source code of modules such as vue, react, etc.)
970
+ * 2. __loadShare__: load shareModule (mfRuntime.loadShare('vue'))
971
+ */
972
+ function getPackageNamedExports(pkg) {
973
+ try {
974
+ const mod = (0, module$1.createRequire)(new URL("file://" + process.cwd() + "/package.json"))(pkg);
975
+ return Object.keys(mod).filter((k) => k !== "default" && k !== "__esModule");
976
+ } catch {
977
+ return [];
978
+ }
979
+ }
944
980
  const preBuildCacheMap = {};
945
981
  const PREBUILD_TAG = "__prebuild__";
946
982
  function writePreBuildLibPath(pkg) {
@@ -953,17 +989,24 @@ function getPreBuildLibImportId(pkg) {
953
989
  }
954
990
  const LOAD_SHARE_TAG = "__loadShare__";
955
991
  const loadShareCacheMap = {};
956
- function getLoadShareModulePath(pkg) {
957
- if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, ".js");
992
+ function getLoadShareModulePath(pkg, isRolldown, command) {
993
+ if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, isRolldown || command === "build" ? ".mjs" : ".js");
958
994
  return loadShareCacheMap[pkg].getPath();
959
995
  }
960
- function writeLoadShareModule(pkg, shareItem, command) {
961
- const isBuild = command === "build";
962
- const importLine = isBuild ? `import { initPromise } from "${virtualRuntimeInitStatus.getImportId()}"` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
963
- const awaitOrPlaceholder = isBuild ? "await " : "/*mf top-level-await placeholder replacement mf*/";
964
- const exportLine = isBuild ? "export default exportModule" : "module.exports = exportModule";
996
+ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
997
+ if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, isRolldown || command === "build" ? ".mjs" : ".js");
998
+ const useESM = command === "build" || isRolldown;
999
+ const importLine = useESM ? `import { initPromise } from "${virtualRuntimeInitStatus.getImportId()}"` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
1000
+ const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
1001
+ const namedExports = getPackageNamedExports(pkg);
1002
+ let exportLine;
1003
+ if (namedExports.length > 0) {
1004
+ const destructure = `const { ${namedExports.join(", ")} } = exportModule;`;
1005
+ const namedExportLine = `export { ${namedExports.join(", ")} };`;
1006
+ exportLine = useESM ? `export default exportModule;\n ${destructure}\n ${namedExportLine}` : `module.exports = exportModule;\n ${destructure}\n Object.assign(module.exports, { ${namedExports.join(", ")} });`;
1007
+ } else exportLine = useESM ? `export default exportModule\n export * from ${JSON.stringify(getPreBuildLibImportId(pkg))}` : "module.exports = exportModule";
965
1008
  loadShareCacheMap[pkg].writeSync(`
966
- ;() => import(${JSON.stringify(getPreBuildLibImportId(pkg))}).catch(() => {});
1009
+ import ${JSON.stringify(getPreBuildLibImportId(pkg))};
967
1010
  ${command !== "build" ? `;() => import(${JSON.stringify(pkg)}).catch(() => {});` : ""}
968
1011
  ${importLine}
969
1012
  const res = initPromise.then(runtime => runtime.loadShare(${JSON.stringify(pkg)}, {
@@ -973,7 +1016,7 @@ function writeLoadShareModule(pkg, shareItem, command) {
973
1016
  requiredVersion: ${JSON.stringify(shareItem.shareConfig.requiredVersion)}
974
1017
  }}
975
1018
  }))
976
- const exportModule = ${awaitOrPlaceholder}res.then(factory => factory())
1019
+ const exportModule = ${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory))
977
1020
  ${exportLine}
978
1021
  `);
979
1022
  }
@@ -1072,7 +1115,10 @@ function generateLocalSharedImportMap() {
1072
1115
  `;
1073
1116
  }
1074
1117
  const REMOTE_ENTRY_ID = "virtual:mf-REMOTE_ENTRY_ID";
1075
- function generateRemoteEntry(options) {
1118
+ function getRemoteEntryId(options) {
1119
+ return `${REMOTE_ENTRY_ID}:${`${options.name}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
1120
+ }
1121
+ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(options)) {
1076
1122
  const pluginImportNames = options.runtimePlugins.map((p, i) => {
1077
1123
  if (typeof p === "string") return [
1078
1124
  `$runtimePlugin_${i}`,
@@ -1088,7 +1134,7 @@ function generateRemoteEntry(options) {
1088
1134
  return `
1089
1135
  import {init as runtimeInit, loadRemote} from "@module-federation/runtime";
1090
1136
  ${pluginImportNames.map((item) => item[1]).join("\n")}
1091
- import exposesMap from "${VIRTUAL_EXPOSES}"
1137
+ import exposesMap from "${virtualExposesId}"
1092
1138
  import {usedShared, usedRemotes} from "${getLocalSharedImportMapPath()}"
1093
1139
  import {
1094
1140
  initResolve
@@ -1140,9 +1186,9 @@ function generateRemoteEntry(options) {
1140
1186
  */
1141
1187
  const HOST_AUTO_INIT_TAG = "__H_A_I__";
1142
1188
  const hostAutoInitModule = new VirtualModule("hostAutoInit", HOST_AUTO_INIT_TAG);
1143
- function writeHostAutoInit() {
1189
+ function writeHostAutoInit(remoteEntryId = REMOTE_ENTRY_ID) {
1144
1190
  hostAutoInitModule.writeSync(`
1145
- const remoteEntryPromise = import("${REMOTE_ENTRY_ID}")
1191
+ const remoteEntryPromise = import("${remoteEntryId}")
1146
1192
  // __tla only serves as a hack for vite-plugin-top-level-await.
1147
1193
  Promise.resolve(remoteEntryPromise)
1148
1194
  .then(remoteEntry => {
@@ -1160,9 +1206,9 @@ function getHostAutoInitPath() {
1160
1206
 
1161
1207
  //#endregion
1162
1208
  //#region src/virtualModules/index.ts
1163
- function initVirtualModules(command) {
1209
+ function initVirtualModules(command, remoteEntryId) {
1164
1210
  writeLocalSharedImportMap();
1165
- writeHostAutoInit();
1211
+ writeHostAutoInit(remoteEntryId);
1166
1212
  writeRuntimeInitStatus(command);
1167
1213
  }
1168
1214
 
@@ -1569,7 +1615,7 @@ function pluginModuleParseEnd_default(excludeFn, options) {
1569
1615
  apply: "build",
1570
1616
  moduleParsed(module) {
1571
1617
  const id = module.id;
1572
- if (id === VIRTUAL_EXPOSES) exposesParseEnd = true;
1618
+ if (id === options.virtualExposesId) exposesParseEnd = true;
1573
1619
  if (excludeFn(id)) return;
1574
1620
  parseEndSet.add(id);
1575
1621
  if (exposesParseEnd && parseStartSet.size === parseEndSet.size) _resolve(1);
@@ -1581,7 +1627,7 @@ function pluginModuleParseEnd_default(excludeFn, options) {
1581
1627
  //#endregion
1582
1628
  //#region src/plugins/pluginProxyRemoteEntry.ts
1583
1629
  const filter = (0, _rollup_pluginutils.createFilter)();
1584
- function pluginProxyRemoteEntry_default() {
1630
+ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposesId }) {
1585
1631
  let viteConfig, _command;
1586
1632
  return {
1587
1633
  name: "proxyRemoteEntry",
@@ -1592,28 +1638,37 @@ function pluginProxyRemoteEntry_default() {
1592
1638
  config(config, { command }) {
1593
1639
  _command = command;
1594
1640
  },
1641
+ async buildStart() {
1642
+ if (_command !== "build") return;
1643
+ for (const expose of Object.values(options.exposes)) {
1644
+ const resolved = await this.resolve(expose.import);
1645
+ if (resolved) this.emitFile({
1646
+ type: "chunk",
1647
+ id: resolved.id
1648
+ });
1649
+ }
1650
+ },
1595
1651
  async resolveId(id, importer) {
1596
- if (id === REMOTE_ENTRY_ID) return REMOTE_ENTRY_ID;
1597
- if (id === VIRTUAL_EXPOSES) return VIRTUAL_EXPOSES;
1652
+ if (id === remoteEntryId) return remoteEntryId;
1653
+ if (id === virtualExposesId) return virtualExposesId;
1598
1654
  if (_command === "serve" && id.includes(getHostAutoInitPath())) return id;
1599
- if (importer === REMOTE_ENTRY_ID && !id.startsWith(".") && !id.startsWith("/") && !id.startsWith("\0") && !id.startsWith("virtual:")) {
1600
- typeof __filename === "string" ? __filename : (0, url.fileURLToPath)(require("url").pathToFileURL(__filename).href);
1601
- const resolved = await this.resolve(id, __filename, { skipSelf: true });
1655
+ if (importer === remoteEntryId && !id.startsWith(".") && !id.startsWith("/") && !id.startsWith("\0") && !id.startsWith("virtual:")) {
1656
+ const importPath = typeof __filename === "string" ? __filename : (0, url.fileURLToPath)(require("url").pathToFileURL(__filename).href);
1657
+ const resolved = await this.resolve(id, importPath, { skipSelf: true });
1602
1658
  if (resolved) return resolved;
1603
1659
  }
1604
1660
  },
1605
1661
  load(id) {
1606
- if (id === REMOTE_ENTRY_ID) return parsePromise.then((_) => generateRemoteEntry(getNormalizeModuleFederationOptions()));
1607
- if (id === VIRTUAL_EXPOSES) return generateExposes();
1662
+ if (id === remoteEntryId) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId));
1663
+ if (id === virtualExposesId) return generateExposes(options);
1608
1664
  if (_command === "serve" && id.includes(getHostAutoInitPath())) return id;
1609
1665
  },
1610
1666
  transform(code, id) {
1611
1667
  return mapCodeToCodeWithSourcemap((() => {
1612
1668
  if (!filter(id)) return;
1613
- if (id.includes(REMOTE_ENTRY_ID)) return parsePromise.then((_) => generateRemoteEntry(getNormalizeModuleFederationOptions()));
1614
- if (id === VIRTUAL_EXPOSES) return generateExposes();
1669
+ if (id.includes(remoteEntryId)) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId));
1670
+ if (id === virtualExposesId) return generateExposes(options);
1615
1671
  if (id.includes(getHostAutoInitPath())) {
1616
- const options = getNormalizeModuleFederationOptions();
1617
1672
  if (_command === "serve") {
1618
1673
  const host = typeof viteConfig.server?.host === "string" && viteConfig.server.host !== "0.0.0.0" ? viteConfig.server.host : "localhost";
1619
1674
  const publicPath = JSON.stringify(resolvePublicPath(options, viteConfig.base) + options.filename);
@@ -1643,13 +1698,14 @@ function pluginProxyRemotes_default(options) {
1643
1698
  return {
1644
1699
  name: "proxyRemotes",
1645
1700
  config(config, { command: _command }) {
1701
+ const isRolldown = !!this?.meta?.rolldownVersion;
1646
1702
  Object.keys(remotes).forEach((key) => {
1647
1703
  const remote = remotes[key];
1648
1704
  config.resolve.alias.push({
1649
1705
  find: new RegExp(`^(${remote.name}(\/.*|$))`),
1650
1706
  replacement: "$1",
1651
1707
  customResolver(source) {
1652
- const remoteModule = getRemoteVirtualModule(source, _command);
1708
+ const remoteModule = getRemoteVirtualModule(source, _command, isRolldown);
1653
1709
  addUsedRemote(remote.name, source);
1654
1710
  return remoteModule.getPath();
1655
1711
  }
@@ -1696,8 +1752,10 @@ var PromiseStore = class {
1696
1752
  //#endregion
1697
1753
  //#region src/plugins/pluginProxySharedModule_preBuild.ts
1698
1754
  function proxySharedModule(options) {
1699
- let { shared = {}, include, exclude } = options;
1755
+ const { shared = {} } = options;
1700
1756
  let _config;
1757
+ let _command = "serve";
1758
+ const savePrebuild = new PromiseStore();
1701
1759
  return [{
1702
1760
  name: "generateLocalSharedImportMap",
1703
1761
  enforce: "post",
@@ -1710,19 +1768,23 @@ function proxySharedModule(options) {
1710
1768
  }, {
1711
1769
  name: "proxyPreBuildShared",
1712
1770
  enforce: "post",
1713
- configResolved(config) {
1714
- _config = config;
1715
- },
1716
1771
  config(config, { command }) {
1772
+ const isRolldown = !!this?.meta?.rolldownVersion;
1773
+ _command = command;
1717
1774
  config.resolve.alias.push(...Object.keys(shared).map((key) => {
1718
- const pattern = key.endsWith("/") ? `(^${key.replace(/\/$/, "")}(\/.+)?$)` : `(^${key}$)`;
1775
+ const keyBase = key.endsWith("/") ? key.slice(0, -1) : key;
1776
+ const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1777
+ const escapedKeyBase = escapeRegex(keyBase);
1778
+ const pattern = key.endsWith("/") ? `^(${escapedKeyBase}(?:\\/.*)?)$` : `^(${escapedKeyBase})$`;
1719
1779
  return {
1720
1780
  find: new RegExp(pattern),
1721
1781
  replacement: "$1",
1722
1782
  customResolver(source, importer) {
1723
1783
  if (/\.css$/.test(source)) return;
1724
- const loadSharePath = getLoadShareModulePath(source);
1725
- writeLoadShareModule(source, shared[key], command);
1784
+ if (importer && importer.includes("localSharedImportMap")) return;
1785
+ if (key.endsWith("/") && source !== key.slice(0, -1)) return;
1786
+ const loadSharePath = getLoadShareModulePath(source, isRolldown, command);
1787
+ writeLoadShareModule(source, shared[key], command, isRolldown);
1726
1788
  writePreBuildLibPath(source);
1727
1789
  addUsedShares(source);
1728
1790
  writeLocalSharedImportMap();
@@ -1730,7 +1792,6 @@ function proxySharedModule(options) {
1730
1792
  }
1731
1793
  };
1732
1794
  }));
1733
- const savePrebuild = new PromiseStore();
1734
1795
  config.resolve.alias.push(...Object.keys(shared).map((key) => {
1735
1796
  return command === "build" ? {
1736
1797
  find: new RegExp(`(.*${PREBUILD_TAG}.*)`),
@@ -1743,11 +1804,22 @@ function proxySharedModule(options) {
1743
1804
  async customResolver(source, importer) {
1744
1805
  const pkgName = assertModuleFound(PREBUILD_TAG, source).name;
1745
1806
  const result = await this.resolve(pkgName, importer).then((item) => item.id);
1746
- if (!result.includes(_config.cacheDir)) savePrebuild.set(pkgName, Promise.resolve(result));
1807
+ if (_config && !result.includes(_config.cacheDir)) savePrebuild.set(pkgName, Promise.resolve(result));
1747
1808
  return await this.resolve(await savePrebuild.get(pkgName), importer);
1748
1809
  }
1749
1810
  };
1750
1811
  }));
1812
+ },
1813
+ configResolved(config) {
1814
+ _config = config;
1815
+ const isRolldown = !!config.experimental?.rolldownDev;
1816
+ Object.keys(shared).forEach((key) => {
1817
+ if (key.endsWith("/")) return;
1818
+ writeLoadShareModule(key, shared[key], _command, isRolldown);
1819
+ writePreBuildLibPath(key);
1820
+ addUsedShares(key);
1821
+ });
1822
+ writeLocalSharedImportMap();
1751
1823
  }
1752
1824
  }];
1753
1825
  }
@@ -1858,20 +1930,60 @@ var normalizeOptimizeDeps_default = {
1858
1930
  config.optimizeDeps = {};
1859
1931
  optimizeDeps = config.optimizeDeps;
1860
1932
  }
1861
- optimizeDeps.force = true;
1862
1933
  if (!optimizeDeps.include) optimizeDeps.include = [];
1934
+ if (!optimizeDeps.exclude) optimizeDeps.exclude = [];
1863
1935
  if (!optimizeDeps.needsInterop) optimizeDeps.needsInterop = [];
1864
1936
  }
1865
1937
  };
1866
1938
 
1867
1939
  //#endregion
1868
1940
  //#region src/index.ts
1941
+ /**
1942
+ * Plugin that runs FIRST to create virtual module files in the config hook.
1943
+ * This prevents 504 "Outdated Optimize Dep" errors by ensuring files exist
1944
+ * before Vite's optimization phase.
1945
+ */
1946
+ function createEarlyVirtualModulesPlugin(options) {
1947
+ const { shared, remotes, virtualModuleDir } = options;
1948
+ return {
1949
+ name: "vite:module-federation-early-init",
1950
+ enforce: "pre",
1951
+ config(config, { command: _command }) {
1952
+ if (_command !== "serve") return;
1953
+ const isRolldown = !!this?.meta?.rolldownVersion;
1954
+ const root = config.root || process.cwd();
1955
+ initVirtualModuleInfrastructure(root, virtualModuleDir);
1956
+ VirtualModule.setRoot(root);
1957
+ VirtualModule.ensureVirtualPackageExists();
1958
+ initVirtualModules(_command, getRemoteEntryId(options));
1959
+ if (remotes && Object.keys(remotes).length > 0) for (const key of Object.keys(remotes)) addUsedRemote(key, key);
1960
+ if (shared && Object.keys(shared).length > 0) {
1961
+ config.optimizeDeps = config.optimizeDeps || {};
1962
+ config.optimizeDeps.include = config.optimizeDeps.include || [];
1963
+ config.optimizeDeps.include.push(virtualRuntimeInitStatus.getImportId());
1964
+ for (const key of Object.keys(shared)) {
1965
+ if (key.endsWith("/")) continue;
1966
+ const shareItem = shared[key];
1967
+ getLoadShareModulePath(key, isRolldown);
1968
+ writeLoadShareModule(key, shareItem, _command, isRolldown);
1969
+ writePreBuildLibPath(key);
1970
+ addUsedShares(key);
1971
+ config.optimizeDeps.include.push(getPreBuildLibImportId(key));
1972
+ }
1973
+ writeLocalSharedImportMap();
1974
+ }
1975
+ }
1976
+ };
1977
+ }
1869
1978
  function federation(mfUserOptions) {
1870
1979
  const options = normalizeModuleFederationOptions(mfUserOptions);
1871
1980
  const { name, remotes, shared, filename, hostInitInjectLocation } = options;
1872
1981
  if (!name) throw new Error("name is required");
1982
+ const remoteEntryId = getRemoteEntryId(options);
1983
+ const virtualExposesId = getVirtualExposesId(options);
1873
1984
  let command;
1874
1985
  return [
1986
+ createEarlyVirtualModulesPlugin(options),
1875
1987
  {
1876
1988
  name: "vite:module-federation-config",
1877
1989
  enforce: "pre",
@@ -1881,7 +1993,7 @@ function federation(mfUserOptions) {
1881
1993
  configResolved(config) {
1882
1994
  VirtualModule.setRoot(config.root);
1883
1995
  VirtualModule.ensureVirtualPackageExists();
1884
- initVirtualModules(command);
1996
+ initVirtualModules(command, remoteEntryId);
1885
1997
  }
1886
1998
  },
1887
1999
  aliasToArrayPlugin_default,
@@ -1890,7 +2002,7 @@ function federation(mfUserOptions) {
1890
2002
  ...pluginDts(options),
1891
2003
  ...addEntry({
1892
2004
  entryName: "remoteEntry",
1893
- entryPath: REMOTE_ENTRY_ID,
2005
+ entryPath: remoteEntryId,
1894
2006
  fileName: filename
1895
2007
  }),
1896
2008
  ...addEntry({
@@ -1900,22 +2012,51 @@ function federation(mfUserOptions) {
1900
2012
  }),
1901
2013
  ...addEntry({
1902
2014
  entryName: "virtualExposes",
1903
- entryPath: VIRTUAL_EXPOSES
2015
+ entryPath: virtualExposesId
2016
+ }),
2017
+ pluginProxyRemoteEntry_default({
2018
+ options,
2019
+ remoteEntryId,
2020
+ virtualExposesId
1904
2021
  }),
1905
- pluginProxyRemoteEntry_default(),
1906
2022
  pluginProxyRemotes_default(options),
1907
2023
  ...pluginModuleParseEnd_default((id) => {
1908
- return id.includes(getHostAutoInitImportId()) || id.includes(REMOTE_ENTRY_ID) || id.includes(VIRTUAL_EXPOSES) || id.includes(getLocalSharedImportMapPath());
1909
- }, { moduleParseTimeout: options.moduleParseTimeout }),
2024
+ return id.includes(getHostAutoInitImportId()) || id.includes(remoteEntryId) || id.includes(virtualExposesId) || id.includes(getLocalSharedImportMapPath());
2025
+ }, {
2026
+ moduleParseTimeout: options.moduleParseTimeout,
2027
+ virtualExposesId
2028
+ }),
1910
2029
  ...proxySharedModule({ shared }),
1911
2030
  {
1912
2031
  name: "module-federation-esm-shims",
1913
2032
  enforce: "pre",
1914
2033
  apply: "build",
2034
+ config(config) {
2035
+ const runtimeInitId = virtualRuntimeInitStatus.getImportId();
2036
+ config.build = config.build || {};
2037
+ config.build.rollupOptions = config.build.rollupOptions || {};
2038
+ if (!Array.isArray(config.build.rollupOptions.output)) {
2039
+ const output = config.build.rollupOptions.output ||= {};
2040
+ const existingManualChunks = output.manualChunks;
2041
+ output.manualChunks = function(id) {
2042
+ if (id.includes(runtimeInitId)) return "runtimeInit";
2043
+ if (id.includes(LOAD_SHARE_TAG)) {
2044
+ const match = id.match(/([^/\\]+__loadShare__[^/\\]+)/);
2045
+ return match ? match[1] : "loadShare";
2046
+ }
2047
+ if (typeof existingManualChunks === "function") return existingManualChunks.apply(this, arguments);
2048
+ if (existingManualChunks && typeof existingManualChunks === "object") {
2049
+ for (const [key, ids] of Object.entries(existingManualChunks)) if (Array.isArray(ids) && ids.some((v) => id.includes(v))) return key;
2050
+ }
2051
+ };
2052
+ }
2053
+ },
1915
2054
  load(id) {
1916
2055
  if (id.startsWith("\0")) return;
1917
2056
  if (id.includes(LOAD_SHARE_TAG) || id.includes(LOAD_REMOTE_TAG)) {
1918
2057
  let code = (0, fs.readFileSync)(id, "utf-8");
2058
+ code = code.replace(/import\s+["'][^"']*__prebuild__[^"']*["']\s*;?/g, "");
2059
+ code = code.replace(/export\s+\*\s+from\s+["'][^"']*__prebuild__[^"']*["']\s*;?/g, "");
1919
2060
  /**
1920
2061
  * Shared/remote shims only have `export default exportModule`.
1921
2062
  *
@@ -1938,6 +2079,134 @@ function federation(mfUserOptions) {
1938
2079
  syntheticNamedExports: "__moduleExports"
1939
2080
  };
1940
2081
  }
2082
+ },
2083
+ generateBundle(_, bundle) {
2084
+ for (const [fileName, chunk] of Object.entries(bundle)) {
2085
+ if (chunk.type !== "chunk") continue;
2086
+ if (fileName.includes(LOAD_SHARE_TAG)) continue;
2087
+ let code = chunk.code;
2088
+ let m;
2089
+ const importedFromLoadShare = /* @__PURE__ */ new Set();
2090
+ const importRegex = /import\s*\{([^}]+)\}\s*from\s*["'][^"']*__loadShare__[^"']*["']/g;
2091
+ while ((m = importRegex.exec(code)) !== null) for (const spec of m[1].split(",")) {
2092
+ const parts = spec.trim().split(/\s+as\s+/);
2093
+ const local = (parts[1] || parts[0]).trim();
2094
+ if (local) importedFromLoadShare.add(local);
2095
+ }
2096
+ const allInits = [];
2097
+ for (const v of importedFromLoadShare) if (new RegExp("\\(" + v + "\\(\\)\\s*,\\s*\\w+\\(\\w+\\)\\)").test(code)) allInits.push(v);
2098
+ if (allInits.length === 0) continue;
2099
+ const awaits = allInits.map((v) => `await ${v}();`).join("");
2100
+ const lastFromRegex = /\bfrom\s*["'][^"']*["']\s*;?/g;
2101
+ let lastFromEnd = -1;
2102
+ while ((m = lastFromRegex.exec(code)) !== null) lastFromEnd = m.index + m[0].length;
2103
+ if (lastFromEnd !== -1) {
2104
+ chunk.code = code.slice(0, lastFromEnd) + awaits + code.slice(lastFromEnd);
2105
+ continue;
2106
+ }
2107
+ const exportIdx = code.search(/\bexport\s*[{d]/);
2108
+ if (exportIdx !== -1) {
2109
+ chunk.code = code.slice(0, exportIdx) + awaits + code.slice(exportIdx);
2110
+ continue;
2111
+ }
2112
+ }
2113
+ const proxyChunks = /* @__PURE__ */ new Map();
2114
+ for (const [fileName, chunk] of Object.entries(bundle)) {
2115
+ if (chunk.type !== "chunk") continue;
2116
+ if (fileName.includes(LOAD_SHARE_TAG) && fileName.includes("commonjs-proxy")) proxyChunks.set(fileName, {
2117
+ code: chunk.code,
2118
+ fileName
2119
+ });
2120
+ }
2121
+ if (proxyChunks.size === 0) return;
2122
+ for (const [fileName, chunk] of Object.entries(bundle)) {
2123
+ if (chunk.type !== "chunk") continue;
2124
+ if (fileName.includes(LOAD_SHARE_TAG)) continue;
2125
+ let code = chunk.code;
2126
+ let modified = false;
2127
+ for (const [proxyFileName, proxyInfo] of proxyChunks) {
2128
+ const proxyBaseName = proxyFileName.replace(/^.*\//, "").replace(/\.js$/, "").replace(/-[A-Za-z0-9_-]+$/, "");
2129
+ const importMatch = new RegExp(`import\\s*\\{([^}]+)\\}\\s*from\\s*["']([^"']*${proxyBaseName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[^"']*)["']\\s*;?`).exec(code);
2130
+ if (!importMatch) continue;
2131
+ const fullImport = importMatch[0];
2132
+ const bindings = importMatch[1].split(",").map((s) => {
2133
+ const parts = s.trim().split(/\s+as\s+/);
2134
+ return {
2135
+ imported: parts[0].trim(),
2136
+ local: (parts[1] || parts[0]).trim()
2137
+ };
2138
+ });
2139
+ const proxyCode = proxyInfo.code;
2140
+ const exportMapMatch = proxyCode.match(/export\s*\{([^}]+)\}/);
2141
+ if (!exportMapMatch) continue;
2142
+ const exportMap = {};
2143
+ for (const entry of exportMapMatch[1].split(",")) {
2144
+ const parts = entry.trim().split(/\s+as\s+/);
2145
+ if (parts.length === 2) exportMap[parts[1].trim()] = parts[0].trim();
2146
+ }
2147
+ const inlineable = [];
2148
+ const nonInlineable = [];
2149
+ for (const b of bindings) {
2150
+ const proxyLocal = exportMap[b.imported];
2151
+ if (!proxyLocal) {
2152
+ nonInlineable.push(b);
2153
+ continue;
2154
+ }
2155
+ const funcRe = new RegExp(`function\\s+${proxyLocal}\\s*\\([^)]*\\)\\s*\\{`);
2156
+ if (funcRe.test(proxyCode)) {
2157
+ const funcStart = proxyCode.search(funcRe);
2158
+ let depth = 0;
2159
+ let funcEnd = funcStart;
2160
+ for (let i = proxyCode.indexOf("{", funcStart); i < proxyCode.length; i++) if (proxyCode[i] === "{") depth++;
2161
+ else if (proxyCode[i] === "}") {
2162
+ depth--;
2163
+ if (depth === 0) {
2164
+ funcEnd = i + 1;
2165
+ break;
2166
+ }
2167
+ }
2168
+ const renamedFunc = proxyCode.slice(funcStart, funcEnd).replace(new RegExp(`function\\s+${proxyLocal}\\s*\\(`), `function ${b.local}(`);
2169
+ inlineable.push({
2170
+ local: b.local,
2171
+ funcBody: renamedFunc
2172
+ });
2173
+ } else nonInlineable.push(b);
2174
+ }
2175
+ if (inlineable.length === 0) continue;
2176
+ let replacement = "";
2177
+ if (nonInlineable.length > 0) replacement = `import{${nonInlineable.map((b) => b.imported === b.local ? b.imported : `${b.imported} as ${b.local}`).join(",")}}from"${importMatch[2]}";`;
2178
+ replacement += inlineable.map((f) => f.funcBody).join("");
2179
+ code = code.replace(fullImport, replacement);
2180
+ modified = true;
2181
+ }
2182
+ if (modified) chunk.code = code;
2183
+ }
2184
+ }
2185
+ },
2186
+ {
2187
+ name: "module-federation-dev-await-shared-init",
2188
+ apply: "serve",
2189
+ enforce: "post",
2190
+ transform(code, id) {
2191
+ if (!id.includes(".vite/deps/")) return;
2192
+ const initPattern = /\b(init_\w+__loadShare__\w+)\b/g;
2193
+ const initFns = /* @__PURE__ */ new Set();
2194
+ let match;
2195
+ while ((match = initPattern.exec(code)) !== null) initFns.add(match[1]);
2196
+ if (initFns.size === 0) return;
2197
+ if (![...initFns].some((fn) => {
2198
+ return code.includes(`${fn}(),`) || code.includes(`${fn}()`);
2199
+ })) return;
2200
+ if (/await\s+init_\w+__loadShare__/.test(code)) return;
2201
+ if (code.includes("__esmMin")) return;
2202
+ const awaits = [...initFns].map((fn) => `await ${fn}();`).join("\n");
2203
+ const topLevelImportRe = /^import\s/gm;
2204
+ let lastImportIdx = -1;
2205
+ let importMatch;
2206
+ while ((importMatch = topLevelImportRe.exec(code)) !== null) lastImportIdx = importMatch.index;
2207
+ if (lastImportIdx === -1) return;
2208
+ const lineEnd = code.indexOf("\n", lastImportIdx);
2209
+ return code.slice(0, lineEnd + 1) + awaits + "\n" + code.slice(lineEnd + 1);
1941
2210
  }
1942
2211
  },
1943
2212
  PluginDevProxyModuleTopLevelAwait(),
@@ -1946,16 +2215,22 @@ function federation(mfUserOptions) {
1946
2215
  enforce: "post",
1947
2216
  _options: options,
1948
2217
  config(config, { command: _command }) {
2218
+ const isRolldown = !!this?.meta?.rolldownVersion;
2219
+ let implementation = options.implementation;
2220
+ if (isRolldown) implementation = implementation.replace(/\.cjs\.cjs$/, ".esm.js");
1949
2221
  config.resolve.alias.push({
1950
2222
  find: "@module-federation/runtime",
1951
- replacement: options.implementation
2223
+ replacement: implementation
1952
2224
  });
1953
2225
  config.build = (0, defu.default)(config.build || {}, { commonjsOptions: { strictRequires: "auto" } });
1954
2226
  const virtualDir = options.virtualModuleDir || "__mf__virtual";
1955
2227
  config.optimizeDeps?.include?.push("@module-federation/runtime");
1956
2228
  config.optimizeDeps?.include?.push(virtualDir);
1957
- config.optimizeDeps?.needsInterop?.push(virtualDir);
1958
- config.optimizeDeps?.needsInterop?.push(getLocalSharedImportMapPath());
2229
+ if (isRolldown) config.build = (0, defu.default)(config.build || {}, { target: "esnext" });
2230
+ else {
2231
+ config.optimizeDeps?.needsInterop?.push(virtualDir);
2232
+ config.optimizeDeps?.needsInterop?.push(getLocalSharedImportMapPath());
2233
+ }
1959
2234
  const resolvedTarget = options.target ?? (config.build?.ssr ? "node" : "web");
1960
2235
  if (!config.define) config.define = {};
1961
2236
  if (!("ENV_TARGET" in config.define)) config.define["ENV_TARGET"] = JSON.stringify(resolvedTarget);
@@ -1963,7 +2238,25 @@ function federation(mfUserOptions) {
1963
2238
  }
1964
2239
  },
1965
2240
  ...Manifest(),
1966
- ...VarRemoteEntry()
2241
+ ...VarRemoteEntry(),
2242
+ ...Object.keys(options.exposes).length > 0 ? [{
2243
+ name: "module-federation-fix-preload",
2244
+ enforce: "post",
2245
+ apply: "build",
2246
+ generateBundle(_, bundle) {
2247
+ for (const chunk of Object.values(bundle)) {
2248
+ if (chunk.type !== "chunk") continue;
2249
+ if (!chunk.code.includes("modulepreload")) continue;
2250
+ const replacement = "=function($1){return new URL(\"../\"+$1,import.meta.url).href}";
2251
+ const replaced = chunk.code.replace(/=\(?(\w+)(?:,\w+)?\)?\s*=>\s*["'`][^"'`]*["'`]\s*\+\s*\1/, replacement);
2252
+ if (replaced !== chunk.code) {
2253
+ chunk.code = replaced;
2254
+ continue;
2255
+ }
2256
+ chunk.code = chunk.code.replace(/=function\((\w+)(?:,\w+)?\)\{return\s*["'`][^"'`]*["'`]\s*\+\s*\1\s*\}/, replacement);
2257
+ }
2258
+ }
2259
+ }] : []
1967
2260
  ];
1968
2261
  }
1969
2262