@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.mjs CHANGED
@@ -10,6 +10,7 @@ import { walk } from "estree-walker";
10
10
  import { normalizeOptions } from "@module-federation/sdk";
11
11
  import { consumeTypesAPI, generateTypesAPI, isTSProject, normalizeConsumeTypesOptions, normalizeDtsOptions, normalizeGenerateTypesOptions } from "@module-federation/dts-plugin";
12
12
  import { rpc } from "@module-federation/dts-plugin/core";
13
+ import { createRequire as createRequire$1 } from "module";
13
14
  import { fileURLToPath } from "url";
14
15
 
15
16
  //#region \0rolldown/runtime.js
@@ -746,6 +747,22 @@ function serializeRuntimeOptions(options) {
746
747
 
747
748
  //#endregion
748
749
  //#region src/utils/VirtualModule.ts
750
+ /**
751
+ * Initialize virtual module infrastructure BEFORE VirtualModule class is used.
752
+ * This must be called in the config hook to ensure the directory exists
753
+ * before Vite's optimization phase.
754
+ */
755
+ function initVirtualModuleInfrastructure(root, virtualModuleDir = "__mf__virtual") {
756
+ const virtualPackagePath = join(join(root, "node_modules"), virtualModuleDir);
757
+ if (!existsSync(virtualPackagePath)) {
758
+ mkdirSync(virtualPackagePath, { recursive: true });
759
+ writeFileSync(join(virtualPackagePath, "empty.js"), "");
760
+ writeFileSync(join(virtualPackagePath, "package.json"), JSON.stringify({
761
+ name: virtualModuleDir,
762
+ main: "empty.js"
763
+ }));
764
+ }
765
+ }
749
766
  let rootDir;
750
767
  function findNodeModulesDir(root = process.cwd()) {
751
768
  let currentDir = root;
@@ -834,9 +851,10 @@ var VirtualModule = class {
834
851
 
835
852
  //#endregion
836
853
  //#region src/virtualModules/virtualExposes.ts
837
- const VIRTUAL_EXPOSES = "virtual:mf-exposes";
838
- function generateExposes() {
839
- const options = getNormalizeModuleFederationOptions();
854
+ function getVirtualExposesId(options) {
855
+ return `virtual:mf-exposes:${`${options.name}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
856
+ }
857
+ function generateExposes(options) {
840
858
  return `
841
859
  export default {
842
860
  ${Object.keys(options.exposes).map((key) => {
@@ -886,10 +904,10 @@ ${exportStatement}
886
904
  //#region src/virtualModules/virtualRemotes.ts
887
905
  const cacheRemoteMap = {};
888
906
  const LOAD_REMOTE_TAG = "__loadRemote__";
889
- function getRemoteVirtualModule(remote, command) {
907
+ function getRemoteVirtualModule(remote, command, isRolldown) {
890
908
  if (!cacheRemoteMap[remote]) {
891
- cacheRemoteMap[remote] = new VirtualModule(remote, LOAD_REMOTE_TAG, ".js");
892
- cacheRemoteMap[remote].writeSync(generateRemotes(remote, command));
909
+ cacheRemoteMap[remote] = new VirtualModule(remote, LOAD_REMOTE_TAG, isRolldown ? ".mjs" : ".js");
910
+ cacheRemoteMap[remote].writeSync(generateRemotes(remote, command, isRolldown));
893
911
  }
894
912
  return cacheRemoteMap[remote];
895
913
  }
@@ -901,11 +919,11 @@ function addUsedRemote(remoteKey, remoteModule) {
901
919
  function getUsedRemotesMap() {
902
920
  return usedRemotesMap;
903
921
  }
904
- function generateRemotes(id, command) {
905
- const isBuild = command === "build";
906
- const importLine = isBuild ? `import { initPromise } from "${virtualRuntimeInitStatus.getImportId()}"` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
907
- const awaitOrPlaceholder = isBuild ? "await " : "/*mf top-level-await placeholder replacement mf*/";
908
- const exportLine = isBuild ? "export default exportModule" : "module.exports = exportModule";
922
+ function generateRemotes(id, command, isRolldown) {
923
+ const useESM = command === "build" || isRolldown;
924
+ const importLine = useESM ? `import { initPromise } from "${virtualRuntimeInitStatus.getImportId()}"` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
925
+ const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
926
+ const exportLine = command === "serve" && useESM ? "export default exportModule.default ?? exportModule" : useESM ? "export default exportModule" : "module.exports = exportModule";
909
927
  return `
910
928
  ${importLine}
911
929
  const res = initPromise.then(runtime => runtime.loadRemote(${JSON.stringify(id)}))
@@ -916,6 +934,24 @@ function generateRemotes(id, command) {
916
934
 
917
935
  //#endregion
918
936
  //#region src/virtualModules/virtualShared_preBuild.ts
937
+ /**
938
+ * Even the resolveId hook cannot interfere with vite pre-build,
939
+ * and adding query parameter virtual modules will also fail.
940
+ * You can only proxy to the real file through alias
941
+ */
942
+ /**
943
+ * shared will be proxied:
944
+ * 1. __prebuild__: export shareModule (pre-built source code of modules such as vue, react, etc.)
945
+ * 2. __loadShare__: load shareModule (mfRuntime.loadShare('vue'))
946
+ */
947
+ function getPackageNamedExports(pkg) {
948
+ try {
949
+ const mod = createRequire$1(new URL("file://" + process.cwd() + "/package.json"))(pkg);
950
+ return Object.keys(mod).filter((k) => k !== "default" && k !== "__esModule");
951
+ } catch {
952
+ return [];
953
+ }
954
+ }
919
955
  const preBuildCacheMap = {};
920
956
  const PREBUILD_TAG = "__prebuild__";
921
957
  function writePreBuildLibPath(pkg) {
@@ -928,17 +964,24 @@ function getPreBuildLibImportId(pkg) {
928
964
  }
929
965
  const LOAD_SHARE_TAG = "__loadShare__";
930
966
  const loadShareCacheMap = {};
931
- function getLoadShareModulePath(pkg) {
932
- if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, ".js");
967
+ function getLoadShareModulePath(pkg, isRolldown, command) {
968
+ if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, isRolldown || command === "build" ? ".mjs" : ".js");
933
969
  return loadShareCacheMap[pkg].getPath();
934
970
  }
935
- function writeLoadShareModule(pkg, shareItem, command) {
936
- const isBuild = command === "build";
937
- const importLine = isBuild ? `import { initPromise } from "${virtualRuntimeInitStatus.getImportId()}"` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
938
- const awaitOrPlaceholder = isBuild ? "await " : "/*mf top-level-await placeholder replacement mf*/";
939
- const exportLine = isBuild ? "export default exportModule" : "module.exports = exportModule";
971
+ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
972
+ if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, isRolldown || command === "build" ? ".mjs" : ".js");
973
+ const useESM = command === "build" || isRolldown;
974
+ const importLine = useESM ? `import { initPromise } from "${virtualRuntimeInitStatus.getImportId()}"` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
975
+ const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
976
+ const namedExports = getPackageNamedExports(pkg);
977
+ let exportLine;
978
+ if (namedExports.length > 0) {
979
+ const destructure = `const { ${namedExports.join(", ")} } = exportModule;`;
980
+ const namedExportLine = `export { ${namedExports.join(", ")} };`;
981
+ exportLine = useESM ? `export default exportModule;\n ${destructure}\n ${namedExportLine}` : `module.exports = exportModule;\n ${destructure}\n Object.assign(module.exports, { ${namedExports.join(", ")} });`;
982
+ } else exportLine = useESM ? `export default exportModule\n export * from ${JSON.stringify(getPreBuildLibImportId(pkg))}` : "module.exports = exportModule";
940
983
  loadShareCacheMap[pkg].writeSync(`
941
- ;() => import(${JSON.stringify(getPreBuildLibImportId(pkg))}).catch(() => {});
984
+ import ${JSON.stringify(getPreBuildLibImportId(pkg))};
942
985
  ${command !== "build" ? `;() => import(${JSON.stringify(pkg)}).catch(() => {});` : ""}
943
986
  ${importLine}
944
987
  const res = initPromise.then(runtime => runtime.loadShare(${JSON.stringify(pkg)}, {
@@ -948,7 +991,7 @@ function writeLoadShareModule(pkg, shareItem, command) {
948
991
  requiredVersion: ${JSON.stringify(shareItem.shareConfig.requiredVersion)}
949
992
  }}
950
993
  }))
951
- const exportModule = ${awaitOrPlaceholder}res.then(factory => factory())
994
+ const exportModule = ${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory))
952
995
  ${exportLine}
953
996
  `);
954
997
  }
@@ -1047,7 +1090,10 @@ function generateLocalSharedImportMap() {
1047
1090
  `;
1048
1091
  }
1049
1092
  const REMOTE_ENTRY_ID = "virtual:mf-REMOTE_ENTRY_ID";
1050
- function generateRemoteEntry(options) {
1093
+ function getRemoteEntryId(options) {
1094
+ return `${REMOTE_ENTRY_ID}:${`${options.name}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
1095
+ }
1096
+ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(options)) {
1051
1097
  const pluginImportNames = options.runtimePlugins.map((p, i) => {
1052
1098
  if (typeof p === "string") return [
1053
1099
  `$runtimePlugin_${i}`,
@@ -1063,7 +1109,7 @@ function generateRemoteEntry(options) {
1063
1109
  return `
1064
1110
  import {init as runtimeInit, loadRemote} from "@module-federation/runtime";
1065
1111
  ${pluginImportNames.map((item) => item[1]).join("\n")}
1066
- import exposesMap from "${VIRTUAL_EXPOSES}"
1112
+ import exposesMap from "${virtualExposesId}"
1067
1113
  import {usedShared, usedRemotes} from "${getLocalSharedImportMapPath()}"
1068
1114
  import {
1069
1115
  initResolve
@@ -1115,9 +1161,9 @@ function generateRemoteEntry(options) {
1115
1161
  */
1116
1162
  const HOST_AUTO_INIT_TAG = "__H_A_I__";
1117
1163
  const hostAutoInitModule = new VirtualModule("hostAutoInit", HOST_AUTO_INIT_TAG);
1118
- function writeHostAutoInit() {
1164
+ function writeHostAutoInit(remoteEntryId = REMOTE_ENTRY_ID) {
1119
1165
  hostAutoInitModule.writeSync(`
1120
- const remoteEntryPromise = import("${REMOTE_ENTRY_ID}")
1166
+ const remoteEntryPromise = import("${remoteEntryId}")
1121
1167
  // __tla only serves as a hack for vite-plugin-top-level-await.
1122
1168
  Promise.resolve(remoteEntryPromise)
1123
1169
  .then(remoteEntry => {
@@ -1135,9 +1181,9 @@ function getHostAutoInitPath() {
1135
1181
 
1136
1182
  //#endregion
1137
1183
  //#region src/virtualModules/index.ts
1138
- function initVirtualModules(command) {
1184
+ function initVirtualModules(command, remoteEntryId) {
1139
1185
  writeLocalSharedImportMap();
1140
- writeHostAutoInit();
1186
+ writeHostAutoInit(remoteEntryId);
1141
1187
  writeRuntimeInitStatus(command);
1142
1188
  }
1143
1189
 
@@ -1544,7 +1590,7 @@ function pluginModuleParseEnd_default(excludeFn, options) {
1544
1590
  apply: "build",
1545
1591
  moduleParsed(module) {
1546
1592
  const id = module.id;
1547
- if (id === VIRTUAL_EXPOSES) exposesParseEnd = true;
1593
+ if (id === options.virtualExposesId) exposesParseEnd = true;
1548
1594
  if (excludeFn(id)) return;
1549
1595
  parseEndSet.add(id);
1550
1596
  if (exposesParseEnd && parseStartSet.size === parseEndSet.size) _resolve(1);
@@ -1556,7 +1602,7 @@ function pluginModuleParseEnd_default(excludeFn, options) {
1556
1602
  //#endregion
1557
1603
  //#region src/plugins/pluginProxyRemoteEntry.ts
1558
1604
  const filter = createFilter();
1559
- function pluginProxyRemoteEntry_default() {
1605
+ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposesId }) {
1560
1606
  let viteConfig, _command;
1561
1607
  return {
1562
1608
  name: "proxyRemoteEntry",
@@ -1567,28 +1613,37 @@ function pluginProxyRemoteEntry_default() {
1567
1613
  config(config, { command }) {
1568
1614
  _command = command;
1569
1615
  },
1616
+ async buildStart() {
1617
+ if (_command !== "build") return;
1618
+ for (const expose of Object.values(options.exposes)) {
1619
+ const resolved = await this.resolve(expose.import);
1620
+ if (resolved) this.emitFile({
1621
+ type: "chunk",
1622
+ id: resolved.id
1623
+ });
1624
+ }
1625
+ },
1570
1626
  async resolveId(id, importer) {
1571
- if (id === REMOTE_ENTRY_ID) return REMOTE_ENTRY_ID;
1572
- if (id === VIRTUAL_EXPOSES) return VIRTUAL_EXPOSES;
1627
+ if (id === remoteEntryId) return remoteEntryId;
1628
+ if (id === virtualExposesId) return virtualExposesId;
1573
1629
  if (_command === "serve" && id.includes(getHostAutoInitPath())) return id;
1574
- if (importer === REMOTE_ENTRY_ID && !id.startsWith(".") && !id.startsWith("/") && !id.startsWith("\0") && !id.startsWith("virtual:")) {
1575
- typeof __filename === "string" ? __filename : fileURLToPath(import.meta.url);
1576
- const resolved = await this.resolve(id, __filename, { skipSelf: true });
1630
+ if (importer === remoteEntryId && !id.startsWith(".") && !id.startsWith("/") && !id.startsWith("\0") && !id.startsWith("virtual:")) {
1631
+ const importPath = typeof __filename === "string" ? __filename : fileURLToPath(import.meta.url);
1632
+ const resolved = await this.resolve(id, importPath, { skipSelf: true });
1577
1633
  if (resolved) return resolved;
1578
1634
  }
1579
1635
  },
1580
1636
  load(id) {
1581
- if (id === REMOTE_ENTRY_ID) return parsePromise.then((_) => generateRemoteEntry(getNormalizeModuleFederationOptions()));
1582
- if (id === VIRTUAL_EXPOSES) return generateExposes();
1637
+ if (id === remoteEntryId) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId));
1638
+ if (id === virtualExposesId) return generateExposes(options);
1583
1639
  if (_command === "serve" && id.includes(getHostAutoInitPath())) return id;
1584
1640
  },
1585
1641
  transform(code, id) {
1586
1642
  return mapCodeToCodeWithSourcemap((() => {
1587
1643
  if (!filter(id)) return;
1588
- if (id.includes(REMOTE_ENTRY_ID)) return parsePromise.then((_) => generateRemoteEntry(getNormalizeModuleFederationOptions()));
1589
- if (id === VIRTUAL_EXPOSES) return generateExposes();
1644
+ if (id.includes(remoteEntryId)) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId));
1645
+ if (id === virtualExposesId) return generateExposes(options);
1590
1646
  if (id.includes(getHostAutoInitPath())) {
1591
- const options = getNormalizeModuleFederationOptions();
1592
1647
  if (_command === "serve") {
1593
1648
  const host = typeof viteConfig.server?.host === "string" && viteConfig.server.host !== "0.0.0.0" ? viteConfig.server.host : "localhost";
1594
1649
  const publicPath = JSON.stringify(resolvePublicPath(options, viteConfig.base) + options.filename);
@@ -1618,13 +1673,14 @@ function pluginProxyRemotes_default(options) {
1618
1673
  return {
1619
1674
  name: "proxyRemotes",
1620
1675
  config(config, { command: _command }) {
1676
+ const isRolldown = !!this?.meta?.rolldownVersion;
1621
1677
  Object.keys(remotes).forEach((key) => {
1622
1678
  const remote = remotes[key];
1623
1679
  config.resolve.alias.push({
1624
1680
  find: new RegExp(`^(${remote.name}(\/.*|$))`),
1625
1681
  replacement: "$1",
1626
1682
  customResolver(source) {
1627
- const remoteModule = getRemoteVirtualModule(source, _command);
1683
+ const remoteModule = getRemoteVirtualModule(source, _command, isRolldown);
1628
1684
  addUsedRemote(remote.name, source);
1629
1685
  return remoteModule.getPath();
1630
1686
  }
@@ -1671,8 +1727,10 @@ var PromiseStore = class {
1671
1727
  //#endregion
1672
1728
  //#region src/plugins/pluginProxySharedModule_preBuild.ts
1673
1729
  function proxySharedModule(options) {
1674
- let { shared = {}, include, exclude } = options;
1730
+ const { shared = {} } = options;
1675
1731
  let _config;
1732
+ let _command = "serve";
1733
+ const savePrebuild = new PromiseStore();
1676
1734
  return [{
1677
1735
  name: "generateLocalSharedImportMap",
1678
1736
  enforce: "post",
@@ -1685,19 +1743,23 @@ function proxySharedModule(options) {
1685
1743
  }, {
1686
1744
  name: "proxyPreBuildShared",
1687
1745
  enforce: "post",
1688
- configResolved(config) {
1689
- _config = config;
1690
- },
1691
1746
  config(config, { command }) {
1747
+ const isRolldown = !!this?.meta?.rolldownVersion;
1748
+ _command = command;
1692
1749
  config.resolve.alias.push(...Object.keys(shared).map((key) => {
1693
- const pattern = key.endsWith("/") ? `(^${key.replace(/\/$/, "")}(\/.+)?$)` : `(^${key}$)`;
1750
+ const keyBase = key.endsWith("/") ? key.slice(0, -1) : key;
1751
+ const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1752
+ const escapedKeyBase = escapeRegex(keyBase);
1753
+ const pattern = key.endsWith("/") ? `^(${escapedKeyBase}(?:\\/.*)?)$` : `^(${escapedKeyBase})$`;
1694
1754
  return {
1695
1755
  find: new RegExp(pattern),
1696
1756
  replacement: "$1",
1697
1757
  customResolver(source, importer) {
1698
1758
  if (/\.css$/.test(source)) return;
1699
- const loadSharePath = getLoadShareModulePath(source);
1700
- writeLoadShareModule(source, shared[key], command);
1759
+ if (importer && importer.includes("localSharedImportMap")) return;
1760
+ if (key.endsWith("/") && source !== key.slice(0, -1)) return;
1761
+ const loadSharePath = getLoadShareModulePath(source, isRolldown, command);
1762
+ writeLoadShareModule(source, shared[key], command, isRolldown);
1701
1763
  writePreBuildLibPath(source);
1702
1764
  addUsedShares(source);
1703
1765
  writeLocalSharedImportMap();
@@ -1705,7 +1767,6 @@ function proxySharedModule(options) {
1705
1767
  }
1706
1768
  };
1707
1769
  }));
1708
- const savePrebuild = new PromiseStore();
1709
1770
  config.resolve.alias.push(...Object.keys(shared).map((key) => {
1710
1771
  return command === "build" ? {
1711
1772
  find: new RegExp(`(.*${PREBUILD_TAG}.*)`),
@@ -1718,11 +1779,22 @@ function proxySharedModule(options) {
1718
1779
  async customResolver(source, importer) {
1719
1780
  const pkgName = assertModuleFound(PREBUILD_TAG, source).name;
1720
1781
  const result = await this.resolve(pkgName, importer).then((item) => item.id);
1721
- if (!result.includes(_config.cacheDir)) savePrebuild.set(pkgName, Promise.resolve(result));
1782
+ if (_config && !result.includes(_config.cacheDir)) savePrebuild.set(pkgName, Promise.resolve(result));
1722
1783
  return await this.resolve(await savePrebuild.get(pkgName), importer);
1723
1784
  }
1724
1785
  };
1725
1786
  }));
1787
+ },
1788
+ configResolved(config) {
1789
+ _config = config;
1790
+ const isRolldown = !!config.experimental?.rolldownDev;
1791
+ Object.keys(shared).forEach((key) => {
1792
+ if (key.endsWith("/")) return;
1793
+ writeLoadShareModule(key, shared[key], _command, isRolldown);
1794
+ writePreBuildLibPath(key);
1795
+ addUsedShares(key);
1796
+ });
1797
+ writeLocalSharedImportMap();
1726
1798
  }
1727
1799
  }];
1728
1800
  }
@@ -1833,20 +1905,60 @@ var normalizeOptimizeDeps_default = {
1833
1905
  config.optimizeDeps = {};
1834
1906
  optimizeDeps = config.optimizeDeps;
1835
1907
  }
1836
- optimizeDeps.force = true;
1837
1908
  if (!optimizeDeps.include) optimizeDeps.include = [];
1909
+ if (!optimizeDeps.exclude) optimizeDeps.exclude = [];
1838
1910
  if (!optimizeDeps.needsInterop) optimizeDeps.needsInterop = [];
1839
1911
  }
1840
1912
  };
1841
1913
 
1842
1914
  //#endregion
1843
1915
  //#region src/index.ts
1916
+ /**
1917
+ * Plugin that runs FIRST to create virtual module files in the config hook.
1918
+ * This prevents 504 "Outdated Optimize Dep" errors by ensuring files exist
1919
+ * before Vite's optimization phase.
1920
+ */
1921
+ function createEarlyVirtualModulesPlugin(options) {
1922
+ const { shared, remotes, virtualModuleDir } = options;
1923
+ return {
1924
+ name: "vite:module-federation-early-init",
1925
+ enforce: "pre",
1926
+ config(config, { command: _command }) {
1927
+ if (_command !== "serve") return;
1928
+ const isRolldown = !!this?.meta?.rolldownVersion;
1929
+ const root = config.root || process.cwd();
1930
+ initVirtualModuleInfrastructure(root, virtualModuleDir);
1931
+ VirtualModule.setRoot(root);
1932
+ VirtualModule.ensureVirtualPackageExists();
1933
+ initVirtualModules(_command, getRemoteEntryId(options));
1934
+ if (remotes && Object.keys(remotes).length > 0) for (const key of Object.keys(remotes)) addUsedRemote(key, key);
1935
+ if (shared && Object.keys(shared).length > 0) {
1936
+ config.optimizeDeps = config.optimizeDeps || {};
1937
+ config.optimizeDeps.include = config.optimizeDeps.include || [];
1938
+ config.optimizeDeps.include.push(virtualRuntimeInitStatus.getImportId());
1939
+ for (const key of Object.keys(shared)) {
1940
+ if (key.endsWith("/")) continue;
1941
+ const shareItem = shared[key];
1942
+ getLoadShareModulePath(key, isRolldown);
1943
+ writeLoadShareModule(key, shareItem, _command, isRolldown);
1944
+ writePreBuildLibPath(key);
1945
+ addUsedShares(key);
1946
+ config.optimizeDeps.include.push(getPreBuildLibImportId(key));
1947
+ }
1948
+ writeLocalSharedImportMap();
1949
+ }
1950
+ }
1951
+ };
1952
+ }
1844
1953
  function federation(mfUserOptions) {
1845
1954
  const options = normalizeModuleFederationOptions(mfUserOptions);
1846
1955
  const { name, remotes, shared, filename, hostInitInjectLocation } = options;
1847
1956
  if (!name) throw new Error("name is required");
1957
+ const remoteEntryId = getRemoteEntryId(options);
1958
+ const virtualExposesId = getVirtualExposesId(options);
1848
1959
  let command;
1849
1960
  return [
1961
+ createEarlyVirtualModulesPlugin(options),
1850
1962
  {
1851
1963
  name: "vite:module-federation-config",
1852
1964
  enforce: "pre",
@@ -1856,7 +1968,7 @@ function federation(mfUserOptions) {
1856
1968
  configResolved(config) {
1857
1969
  VirtualModule.setRoot(config.root);
1858
1970
  VirtualModule.ensureVirtualPackageExists();
1859
- initVirtualModules(command);
1971
+ initVirtualModules(command, remoteEntryId);
1860
1972
  }
1861
1973
  },
1862
1974
  aliasToArrayPlugin_default,
@@ -1865,7 +1977,7 @@ function federation(mfUserOptions) {
1865
1977
  ...pluginDts(options),
1866
1978
  ...addEntry({
1867
1979
  entryName: "remoteEntry",
1868
- entryPath: REMOTE_ENTRY_ID,
1980
+ entryPath: remoteEntryId,
1869
1981
  fileName: filename
1870
1982
  }),
1871
1983
  ...addEntry({
@@ -1875,22 +1987,51 @@ function federation(mfUserOptions) {
1875
1987
  }),
1876
1988
  ...addEntry({
1877
1989
  entryName: "virtualExposes",
1878
- entryPath: VIRTUAL_EXPOSES
1990
+ entryPath: virtualExposesId
1991
+ }),
1992
+ pluginProxyRemoteEntry_default({
1993
+ options,
1994
+ remoteEntryId,
1995
+ virtualExposesId
1879
1996
  }),
1880
- pluginProxyRemoteEntry_default(),
1881
1997
  pluginProxyRemotes_default(options),
1882
1998
  ...pluginModuleParseEnd_default((id) => {
1883
- return id.includes(getHostAutoInitImportId()) || id.includes(REMOTE_ENTRY_ID) || id.includes(VIRTUAL_EXPOSES) || id.includes(getLocalSharedImportMapPath());
1884
- }, { moduleParseTimeout: options.moduleParseTimeout }),
1999
+ return id.includes(getHostAutoInitImportId()) || id.includes(remoteEntryId) || id.includes(virtualExposesId) || id.includes(getLocalSharedImportMapPath());
2000
+ }, {
2001
+ moduleParseTimeout: options.moduleParseTimeout,
2002
+ virtualExposesId
2003
+ }),
1885
2004
  ...proxySharedModule({ shared }),
1886
2005
  {
1887
2006
  name: "module-federation-esm-shims",
1888
2007
  enforce: "pre",
1889
2008
  apply: "build",
2009
+ config(config) {
2010
+ const runtimeInitId = virtualRuntimeInitStatus.getImportId();
2011
+ config.build = config.build || {};
2012
+ config.build.rollupOptions = config.build.rollupOptions || {};
2013
+ if (!Array.isArray(config.build.rollupOptions.output)) {
2014
+ const output = config.build.rollupOptions.output ||= {};
2015
+ const existingManualChunks = output.manualChunks;
2016
+ output.manualChunks = function(id) {
2017
+ if (id.includes(runtimeInitId)) return "runtimeInit";
2018
+ if (id.includes(LOAD_SHARE_TAG)) {
2019
+ const match = id.match(/([^/\\]+__loadShare__[^/\\]+)/);
2020
+ return match ? match[1] : "loadShare";
2021
+ }
2022
+ if (typeof existingManualChunks === "function") return existingManualChunks.apply(this, arguments);
2023
+ if (existingManualChunks && typeof existingManualChunks === "object") {
2024
+ for (const [key, ids] of Object.entries(existingManualChunks)) if (Array.isArray(ids) && ids.some((v) => id.includes(v))) return key;
2025
+ }
2026
+ };
2027
+ }
2028
+ },
1890
2029
  load(id) {
1891
2030
  if (id.startsWith("\0")) return;
1892
2031
  if (id.includes(LOAD_SHARE_TAG) || id.includes(LOAD_REMOTE_TAG)) {
1893
2032
  let code = readFileSync(id, "utf-8");
2033
+ code = code.replace(/import\s+["'][^"']*__prebuild__[^"']*["']\s*;?/g, "");
2034
+ code = code.replace(/export\s+\*\s+from\s+["'][^"']*__prebuild__[^"']*["']\s*;?/g, "");
1894
2035
  /**
1895
2036
  * Shared/remote shims only have `export default exportModule`.
1896
2037
  *
@@ -1913,6 +2054,134 @@ function federation(mfUserOptions) {
1913
2054
  syntheticNamedExports: "__moduleExports"
1914
2055
  };
1915
2056
  }
2057
+ },
2058
+ generateBundle(_, bundle) {
2059
+ for (const [fileName, chunk] of Object.entries(bundle)) {
2060
+ if (chunk.type !== "chunk") continue;
2061
+ if (fileName.includes(LOAD_SHARE_TAG)) continue;
2062
+ let code = chunk.code;
2063
+ let m;
2064
+ const importedFromLoadShare = /* @__PURE__ */ new Set();
2065
+ const importRegex = /import\s*\{([^}]+)\}\s*from\s*["'][^"']*__loadShare__[^"']*["']/g;
2066
+ while ((m = importRegex.exec(code)) !== null) for (const spec of m[1].split(",")) {
2067
+ const parts = spec.trim().split(/\s+as\s+/);
2068
+ const local = (parts[1] || parts[0]).trim();
2069
+ if (local) importedFromLoadShare.add(local);
2070
+ }
2071
+ const allInits = [];
2072
+ for (const v of importedFromLoadShare) if (new RegExp("\\(" + v + "\\(\\)\\s*,\\s*\\w+\\(\\w+\\)\\)").test(code)) allInits.push(v);
2073
+ if (allInits.length === 0) continue;
2074
+ const awaits = allInits.map((v) => `await ${v}();`).join("");
2075
+ const lastFromRegex = /\bfrom\s*["'][^"']*["']\s*;?/g;
2076
+ let lastFromEnd = -1;
2077
+ while ((m = lastFromRegex.exec(code)) !== null) lastFromEnd = m.index + m[0].length;
2078
+ if (lastFromEnd !== -1) {
2079
+ chunk.code = code.slice(0, lastFromEnd) + awaits + code.slice(lastFromEnd);
2080
+ continue;
2081
+ }
2082
+ const exportIdx = code.search(/\bexport\s*[{d]/);
2083
+ if (exportIdx !== -1) {
2084
+ chunk.code = code.slice(0, exportIdx) + awaits + code.slice(exportIdx);
2085
+ continue;
2086
+ }
2087
+ }
2088
+ const proxyChunks = /* @__PURE__ */ new Map();
2089
+ for (const [fileName, chunk] of Object.entries(bundle)) {
2090
+ if (chunk.type !== "chunk") continue;
2091
+ if (fileName.includes(LOAD_SHARE_TAG) && fileName.includes("commonjs-proxy")) proxyChunks.set(fileName, {
2092
+ code: chunk.code,
2093
+ fileName
2094
+ });
2095
+ }
2096
+ if (proxyChunks.size === 0) return;
2097
+ for (const [fileName, chunk] of Object.entries(bundle)) {
2098
+ if (chunk.type !== "chunk") continue;
2099
+ if (fileName.includes(LOAD_SHARE_TAG)) continue;
2100
+ let code = chunk.code;
2101
+ let modified = false;
2102
+ for (const [proxyFileName, proxyInfo] of proxyChunks) {
2103
+ const proxyBaseName = proxyFileName.replace(/^.*\//, "").replace(/\.js$/, "").replace(/-[A-Za-z0-9_-]+$/, "");
2104
+ const importMatch = new RegExp(`import\\s*\\{([^}]+)\\}\\s*from\\s*["']([^"']*${proxyBaseName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[^"']*)["']\\s*;?`).exec(code);
2105
+ if (!importMatch) continue;
2106
+ const fullImport = importMatch[0];
2107
+ const bindings = importMatch[1].split(",").map((s) => {
2108
+ const parts = s.trim().split(/\s+as\s+/);
2109
+ return {
2110
+ imported: parts[0].trim(),
2111
+ local: (parts[1] || parts[0]).trim()
2112
+ };
2113
+ });
2114
+ const proxyCode = proxyInfo.code;
2115
+ const exportMapMatch = proxyCode.match(/export\s*\{([^}]+)\}/);
2116
+ if (!exportMapMatch) continue;
2117
+ const exportMap = {};
2118
+ for (const entry of exportMapMatch[1].split(",")) {
2119
+ const parts = entry.trim().split(/\s+as\s+/);
2120
+ if (parts.length === 2) exportMap[parts[1].trim()] = parts[0].trim();
2121
+ }
2122
+ const inlineable = [];
2123
+ const nonInlineable = [];
2124
+ for (const b of bindings) {
2125
+ const proxyLocal = exportMap[b.imported];
2126
+ if (!proxyLocal) {
2127
+ nonInlineable.push(b);
2128
+ continue;
2129
+ }
2130
+ const funcRe = new RegExp(`function\\s+${proxyLocal}\\s*\\([^)]*\\)\\s*\\{`);
2131
+ if (funcRe.test(proxyCode)) {
2132
+ const funcStart = proxyCode.search(funcRe);
2133
+ let depth = 0;
2134
+ let funcEnd = funcStart;
2135
+ for (let i = proxyCode.indexOf("{", funcStart); i < proxyCode.length; i++) if (proxyCode[i] === "{") depth++;
2136
+ else if (proxyCode[i] === "}") {
2137
+ depth--;
2138
+ if (depth === 0) {
2139
+ funcEnd = i + 1;
2140
+ break;
2141
+ }
2142
+ }
2143
+ const renamedFunc = proxyCode.slice(funcStart, funcEnd).replace(new RegExp(`function\\s+${proxyLocal}\\s*\\(`), `function ${b.local}(`);
2144
+ inlineable.push({
2145
+ local: b.local,
2146
+ funcBody: renamedFunc
2147
+ });
2148
+ } else nonInlineable.push(b);
2149
+ }
2150
+ if (inlineable.length === 0) continue;
2151
+ let replacement = "";
2152
+ if (nonInlineable.length > 0) replacement = `import{${nonInlineable.map((b) => b.imported === b.local ? b.imported : `${b.imported} as ${b.local}`).join(",")}}from"${importMatch[2]}";`;
2153
+ replacement += inlineable.map((f) => f.funcBody).join("");
2154
+ code = code.replace(fullImport, replacement);
2155
+ modified = true;
2156
+ }
2157
+ if (modified) chunk.code = code;
2158
+ }
2159
+ }
2160
+ },
2161
+ {
2162
+ name: "module-federation-dev-await-shared-init",
2163
+ apply: "serve",
2164
+ enforce: "post",
2165
+ transform(code, id) {
2166
+ if (!id.includes(".vite/deps/")) return;
2167
+ const initPattern = /\b(init_\w+__loadShare__\w+)\b/g;
2168
+ const initFns = /* @__PURE__ */ new Set();
2169
+ let match;
2170
+ while ((match = initPattern.exec(code)) !== null) initFns.add(match[1]);
2171
+ if (initFns.size === 0) return;
2172
+ if (![...initFns].some((fn) => {
2173
+ return code.includes(`${fn}(),`) || code.includes(`${fn}()`);
2174
+ })) return;
2175
+ if (/await\s+init_\w+__loadShare__/.test(code)) return;
2176
+ if (code.includes("__esmMin")) return;
2177
+ const awaits = [...initFns].map((fn) => `await ${fn}();`).join("\n");
2178
+ const topLevelImportRe = /^import\s/gm;
2179
+ let lastImportIdx = -1;
2180
+ let importMatch;
2181
+ while ((importMatch = topLevelImportRe.exec(code)) !== null) lastImportIdx = importMatch.index;
2182
+ if (lastImportIdx === -1) return;
2183
+ const lineEnd = code.indexOf("\n", lastImportIdx);
2184
+ return code.slice(0, lineEnd + 1) + awaits + "\n" + code.slice(lineEnd + 1);
1916
2185
  }
1917
2186
  },
1918
2187
  PluginDevProxyModuleTopLevelAwait(),
@@ -1921,16 +2190,22 @@ function federation(mfUserOptions) {
1921
2190
  enforce: "post",
1922
2191
  _options: options,
1923
2192
  config(config, { command: _command }) {
2193
+ const isRolldown = !!this?.meta?.rolldownVersion;
2194
+ let implementation = options.implementation;
2195
+ if (isRolldown) implementation = implementation.replace(/\.cjs\.cjs$/, ".esm.js");
1924
2196
  config.resolve.alias.push({
1925
2197
  find: "@module-federation/runtime",
1926
- replacement: options.implementation
2198
+ replacement: implementation
1927
2199
  });
1928
2200
  config.build = defu(config.build || {}, { commonjsOptions: { strictRequires: "auto" } });
1929
2201
  const virtualDir = options.virtualModuleDir || "__mf__virtual";
1930
2202
  config.optimizeDeps?.include?.push("@module-federation/runtime");
1931
2203
  config.optimizeDeps?.include?.push(virtualDir);
1932
- config.optimizeDeps?.needsInterop?.push(virtualDir);
1933
- config.optimizeDeps?.needsInterop?.push(getLocalSharedImportMapPath());
2204
+ if (isRolldown) config.build = defu(config.build || {}, { target: "esnext" });
2205
+ else {
2206
+ config.optimizeDeps?.needsInterop?.push(virtualDir);
2207
+ config.optimizeDeps?.needsInterop?.push(getLocalSharedImportMapPath());
2208
+ }
1934
2209
  const resolvedTarget = options.target ?? (config.build?.ssr ? "node" : "web");
1935
2210
  if (!config.define) config.define = {};
1936
2211
  if (!("ENV_TARGET" in config.define)) config.define["ENV_TARGET"] = JSON.stringify(resolvedTarget);
@@ -1938,7 +2213,25 @@ function federation(mfUserOptions) {
1938
2213
  }
1939
2214
  },
1940
2215
  ...Manifest(),
1941
- ...VarRemoteEntry()
2216
+ ...VarRemoteEntry(),
2217
+ ...Object.keys(options.exposes).length > 0 ? [{
2218
+ name: "module-federation-fix-preload",
2219
+ enforce: "post",
2220
+ apply: "build",
2221
+ generateBundle(_, bundle) {
2222
+ for (const chunk of Object.values(bundle)) {
2223
+ if (chunk.type !== "chunk") continue;
2224
+ if (!chunk.code.includes("modulepreload")) continue;
2225
+ const replacement = "=function($1){return new URL(\"../\"+$1,import.meta.url).href}";
2226
+ const replaced = chunk.code.replace(/=\(?(\w+)(?:,\w+)?\)?\s*=>\s*["'`][^"'`]*["'`]\s*\+\s*\1/, replacement);
2227
+ if (replaced !== chunk.code) {
2228
+ chunk.code = replaced;
2229
+ continue;
2230
+ }
2231
+ chunk.code = chunk.code.replace(/=function\((\w+)(?:,\w+)?\)\{return\s*["'`][^"'`]*["'`]\s*\+\s*\1\s*\}/, replacement);
2232
+ }
2233
+ }
2234
+ }] : []
1942
2235
  ];
1943
2236
  }
1944
2237