@module-federation/vite 1.14.3 → 1.14.5

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 CHANGED
@@ -190,15 +190,20 @@ const RemoteMFE = defineAsyncComponent( 👈
190
190
  </template>
191
191
  ```
192
192
 
193
- ## ⚠️ `codeSplitting: false` is not supported
193
+ ## ⚠️ `codeSplitting` settings are controlled by the plugin
194
194
 
195
- Do not set `build.rolldownOptions.output.codeSplitting` to `false` with this plugin — it will be **automatically ignored**.
196
- Module federation requires chunk splitting to isolate shared dependencies and remote entries into separate chunks.
195
+ Do not set either `build.rollupOptions.output.codeSplitting` or
196
+ `build.rolldownOptions.output.codeSplitting` to `false` with this plugin it will be **automatically ignored**.
197
+
198
+ `codeSplitting.groups` is also ignored because grouping shared-runtime chunks can break MF init order.
199
+ Module Federation needs `loadShare` and `runtimeInitStatus` isolated into separate chunks for correct bootstrap behavior.
197
200
 
198
201
  ## ⚠️ `manualChunks` is not supported
199
202
 
200
- Do not use `build.rollupOptions.output.manualChunks` with this plugin — it will be **automatically ignored**.
203
+ Do not use `build.rollupOptions.output.manualChunks` or
204
+ `build.rolldownOptions.output.manualChunks` with this plugin — it will be **automatically ignored**.
201
205
  Module federation transforms shared dependency imports with top-level `await`, and grouping these transformed modules into a single chunk creates circular async dependencies that cause the application to silently hang.
206
+ The plugin injects its own split so `runtimeInitStatus` and `loadShare` are kept isolated.
202
207
 
203
208
  ### So far so good 🎉
204
209
 
package/lib/index.cjs CHANGED
@@ -117,6 +117,15 @@ function setPackageDetectionCwd(cwd) {
117
117
  function getPackageDetectionCwd() {
118
118
  return packageDetectionCwd || process.cwd();
119
119
  }
120
+ function resolveExportsEntry(exportsField) {
121
+ if (typeof exportsField === "string") return exportsField;
122
+ if (!exportsField || typeof exportsField !== "object") return void 0;
123
+ const rootExport = exportsField["."];
124
+ if (typeof rootExport === "string") return rootExport;
125
+ if (!rootExport || typeof rootExport !== "object") return void 0;
126
+ const rootExportObject = rootExport;
127
+ return typeof rootExportObject.import === "string" && rootExportObject.import || typeof rootExportObject.default === "string" && rootExportObject.default || typeof rootExportObject.require === "string" && rootExportObject.require || void 0;
128
+ }
120
129
  /**
121
130
  * Escaping rules:
122
131
  * Convert using the format __${mapping}__, where _ and $ are not allowed in npm package names but can be used in variable names.
@@ -152,6 +161,84 @@ function removePathFromNpmPackage(packageString) {
152
161
  const match = packageString.match(/^(?:@[^/]+\/)?[^/]+/);
153
162
  return match ? match[0] : packageString;
154
163
  }
164
+ function getInstalledPackageJson(pkg, opts) {
165
+ const cwd = opts?.cwd || getPackageDetectionCwd();
166
+ const packageName = opts?.packageName || removePathFromNpmPackage(pkg);
167
+ const tryReadPackageJson = (packageJsonPath) => {
168
+ if (!(0, fs.existsSync)(packageJsonPath)) return void 0;
169
+ try {
170
+ return {
171
+ path: packageJsonPath,
172
+ dir: pathe.default.dirname(packageJsonPath),
173
+ packageJson: JSON.parse((0, fs.readFileSync)(packageJsonPath, "utf-8"))
174
+ };
175
+ } catch {
176
+ return;
177
+ }
178
+ };
179
+ const findPackageInPnpmStore = (startDir) => {
180
+ let currentDir = startDir;
181
+ const rootDir = pathe.default.parse(currentDir).root;
182
+ while (true) {
183
+ const pnpmStoreDir = pathe.default.join(currentDir, "node_modules", ".pnpm");
184
+ if ((0, fs.existsSync)(pnpmStoreDir)) try {
185
+ for (const entry of (0, fs.readdirSync)(pnpmStoreDir, { withFileTypes: true })) {
186
+ if (!entry.isDirectory()) continue;
187
+ const candidate = tryReadPackageJson(pathe.default.join(pnpmStoreDir, entry.name, "node_modules", packageName, "package.json"));
188
+ if (candidate?.packageJson.name === packageName) return candidate;
189
+ }
190
+ } catch {}
191
+ if (currentDir === rootDir) break;
192
+ currentDir = pathe.default.dirname(currentDir);
193
+ }
194
+ };
195
+ try {
196
+ const projectRequire = (0, module$1.createRequire)(new URL(`file://${pathe.default.join(cwd, "package.json")}`));
197
+ let resolvedPath;
198
+ try {
199
+ resolvedPath = projectRequire.resolve(pkg);
200
+ } catch {
201
+ resolvedPath = projectRequire.resolve(packageName);
202
+ }
203
+ let currentDir = pathe.default.dirname(resolvedPath);
204
+ const rootDir = pathe.default.parse(currentDir).root;
205
+ while (true) {
206
+ const packageJsonPath = pathe.default.join(currentDir, "package.json");
207
+ if ((0, fs.existsSync)(packageJsonPath)) {
208
+ const packageJsonContent = (0, fs.readFileSync)(packageJsonPath, "utf-8");
209
+ try {
210
+ const packageJson = JSON.parse(packageJsonContent);
211
+ if (packageJson.name === packageName) return {
212
+ path: packageJsonPath,
213
+ dir: currentDir,
214
+ packageJson
215
+ };
216
+ } catch (error) {
217
+ if (!(error instanceof SyntaxError)) throw error;
218
+ }
219
+ }
220
+ if (currentDir === rootDir) break;
221
+ currentDir = pathe.default.dirname(currentDir);
222
+ }
223
+ } catch {
224
+ let currentDir = cwd;
225
+ const rootDir = pathe.default.parse(currentDir).root;
226
+ while (true) {
227
+ const directCandidate = tryReadPackageJson(pathe.default.join(currentDir, "node_modules", packageName, "package.json"));
228
+ if (directCandidate?.packageJson.name === packageName) return directCandidate;
229
+ if (currentDir === rootDir) break;
230
+ currentDir = pathe.default.dirname(currentDir);
231
+ }
232
+ return findPackageInPnpmStore(cwd);
233
+ }
234
+ }
235
+ function getInstalledPackageEntry(pkg, opts) {
236
+ const installed = getInstalledPackageJson(pkg, opts);
237
+ if (!installed) return void 0;
238
+ const packageJson = installed.packageJson;
239
+ const explicitEntry = resolveExportsEntry(packageJson.exports) || (typeof packageJson.module === "string" ? packageJson.module : void 0) || (typeof packageJson.main === "string" ? packageJson.main : void 0) || "index.js";
240
+ return pathe.default.join(installed.dir, explicitEntry);
241
+ }
155
242
  /**
156
243
  * Detect whether the current runtime is Vite 8+ by checking for a Vite version flag
157
244
  * on the plugin hook context, with Rolldown metadata kept as a compatibility fallback.
@@ -877,6 +964,14 @@ function pluginDts(options) {
877
964
  }
878
965
  //#endregion
879
966
  //#region src/utils/normalizeModuleFederationOptions.ts
967
+ const INTERNAL_NAME_PREFIX = "__mfe_internal__";
968
+ function toInternalModuleFederationName(name) {
969
+ return name.startsWith(INTERNAL_NAME_PREFIX) ? name : `${INTERNAL_NAME_PREFIX}${name}`;
970
+ }
971
+ function warnOnReservedInternalNamePrefix(name, kind) {
972
+ if (!name.startsWith(INTERNAL_NAME_PREFIX)) return;
973
+ mfWarn(`Reserved internal ${kind} prefix "${INTERNAL_NAME_PREFIX}" detected in public ${kind} "${name}". This prefix is reserved for internal module federation names and may cause conflicts.`);
974
+ }
880
975
  function normalizeExposesItem(key, item) {
881
976
  let importPath = "";
882
977
  if (typeof item === "string") importPath = item;
@@ -900,6 +995,7 @@ function normalizeRemotes(remotes) {
900
995
  return result;
901
996
  }
902
997
  function normalizeRemoteItem(key, remote) {
998
+ warnOnReservedInternalNamePrefix(key, "remoteAlias");
903
999
  if (typeof remote === "string") {
904
1000
  const separatorIndex = remote.startsWith("@") ? remote.indexOf("@", 1) : remote.indexOf("@");
905
1001
  let entryGlobalName;
@@ -914,6 +1010,7 @@ function normalizeRemoteItem(key, remote) {
914
1010
  return {
915
1011
  type: "var",
916
1012
  name: key,
1013
+ internalName: toInternalModuleFederationName(key),
917
1014
  entry,
918
1015
  entryGlobalName,
919
1016
  shareScope: "default"
@@ -922,9 +1019,13 @@ function normalizeRemoteItem(key, remote) {
922
1019
  return Object.assign({
923
1020
  type: "var",
924
1021
  name: key,
1022
+ internalName: toInternalModuleFederationName(key),
925
1023
  shareScope: "default",
926
1024
  entryGlobalName: key
927
- }, remote);
1025
+ }, {
1026
+ ...remote,
1027
+ internalName: toInternalModuleFederationName(remote.name || key)
1028
+ });
928
1029
  }
929
1030
  /**
930
1031
  * Tries to find the package.json's version of a shared package
@@ -956,6 +1057,12 @@ function inferVersionFromRequiredVersion(requiredVersion) {
956
1057
  if (!requiredVersion) return void 0;
957
1058
  return requiredVersion.match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/)?.[0];
958
1059
  }
1060
+ function getLitExportSubpathShares(sharedName) {
1061
+ if (sharedName !== "lit") return [];
1062
+ const exportsField = getInstalledPackageJson(sharedName, { packageName: sharedName })?.packageJson.exports;
1063
+ if (!exportsField || typeof exportsField === "string") return [];
1064
+ return Object.keys(exportsField).filter((key) => key.startsWith("./") && key !== "." && !key.includes("*")).map((key) => `${sharedName}/${key.slice(2)}`);
1065
+ }
959
1066
  function normalizeShareItem(key, shareItem) {
960
1067
  let version;
961
1068
  if (!(typeof shareItem === "object" && shareItem.import === false)) try {
@@ -1000,14 +1107,21 @@ function normalizeShareItem(key, shareItem) {
1000
1107
  function normalizeShared(shared) {
1001
1108
  if (!shared) return {};
1002
1109
  const result = {};
1003
- if (Array.isArray(shared)) {
1004
- shared.forEach((key) => {
1005
- result[key] = normalizeShareItem(key, key);
1006
- });
1007
- return result;
1008
- }
1009
- if (typeof shared === "object") Object.keys(shared).forEach((key) => {
1010
- result[key] = normalizeShareItem(key, shared[key]);
1110
+ const sourceEntries = [];
1111
+ if (Array.isArray(shared)) shared.forEach((key) => {
1112
+ result[key] = normalizeShareItem(key, key);
1113
+ sourceEntries.push([key, key]);
1114
+ });
1115
+ else if (typeof shared === "object") Object.keys(shared).forEach((key) => {
1116
+ const value = shared[key];
1117
+ result[key] = normalizeShareItem(key, value);
1118
+ sourceEntries.push([key, value]);
1119
+ });
1120
+ sourceEntries.forEach(([key, value]) => {
1121
+ for (const subpathShare of getLitExportSubpathShares(key)) {
1122
+ if (result[subpathShare]) continue;
1123
+ result[subpathShare] = normalizeShareItem(subpathShare, value);
1124
+ }
1011
1125
  });
1012
1126
  return result;
1013
1127
  }
@@ -1032,10 +1146,12 @@ function getNormalizeShareItem(key) {
1032
1146
  return options.shared[key] || options.shared[removePathFromNpmPackage(key)] || options.shared[removePathFromNpmPackage(key) + "/"];
1033
1147
  }
1034
1148
  function normalizeModuleFederationOptions(options) {
1149
+ warnOnReservedInternalNamePrefix(options.name, "containerName");
1035
1150
  if (options.virtualModuleDir && options.virtualModuleDir.includes("/")) throw createModuleFederationError(`Invalid virtualModuleDir: "${options.virtualModuleDir}". The virtualModuleDir option cannot contain slashes (/). Please use a single directory name like '__mf__virtual__your_app_name'.`);
1036
1151
  return config = {
1037
1152
  exposes: normalizeExposes(options.exposes),
1038
1153
  filename: options.filename || "remoteEntry-[hash]",
1154
+ internalName: toInternalModuleFederationName(options.name),
1039
1155
  library: normalizeLibrary(options.library),
1040
1156
  name: options.name,
1041
1157
  remotes: normalizeRemotes(options.remotes),
@@ -1210,7 +1326,7 @@ var VirtualModule = class {
1210
1326
  return (0, pathe.resolve)(getNodeModulesDir(), this.getImportId());
1211
1327
  }
1212
1328
  getImportId() {
1213
- const { name: mfName, virtualModuleDir } = getNormalizeModuleFederationOptions();
1329
+ const { internalName: mfName, virtualModuleDir } = getNormalizeModuleFederationOptions();
1214
1330
  return `${virtualModuleDir}/${packageNameEncode(`${mfName}${this.tag}${this.name}${this.tag}`)}${this.suffix}`;
1215
1331
  }
1216
1332
  writeSync(code, force) {
@@ -1233,7 +1349,7 @@ function getExposesCssMapPlaceholder() {
1233
1349
  return EXPOSES_CSS_MAP_PLACEHOLDER;
1234
1350
  }
1235
1351
  function getVirtualExposesId(options) {
1236
- return `virtual:mf-exposes:${`${options.name}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
1352
+ return `virtual:mf-exposes:${`${options.internalName}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
1237
1353
  }
1238
1354
  function generateExposes(options) {
1239
1355
  return `
@@ -1467,52 +1583,6 @@ function resolvePackageEntryFromProjectRoot(pkg) {
1467
1583
  return;
1468
1584
  }
1469
1585
  }
1470
- function getInstalledPackageJsonPath(pkg) {
1471
- try {
1472
- const packageName = removePathFromNpmPackage(pkg);
1473
- const projectRequire = (0, module$1.createRequire)(new URL(`file://${pathe.default.join(getPackageDetectionCwd(), "package.json")}`));
1474
- let resolvedPath;
1475
- try {
1476
- resolvedPath = projectRequire.resolve(pkg);
1477
- } catch {
1478
- resolvedPath = projectRequire.resolve(packageName);
1479
- }
1480
- let currentDir = pathe.default.dirname(resolvedPath);
1481
- const rootDir = pathe.default.parse(currentDir).root;
1482
- while (currentDir !== rootDir) {
1483
- const packageJsonPath = pathe.default.join(currentDir, "package.json");
1484
- if ((0, fs.existsSync)(packageJsonPath)) {
1485
- const packageJsonContent = (0, fs.readFileSync)(packageJsonPath, "utf-8");
1486
- try {
1487
- if (JSON.parse(packageJsonContent).name === packageName) return packageJsonPath;
1488
- } catch (error) {
1489
- if (!(error instanceof SyntaxError)) throw error;
1490
- }
1491
- }
1492
- currentDir = pathe.default.dirname(currentDir);
1493
- }
1494
- const rootPackageJsonPath = pathe.default.join(rootDir, "package.json");
1495
- if ((0, fs.existsSync)(rootPackageJsonPath)) {
1496
- const rootPackageJsonContent = (0, fs.readFileSync)(rootPackageJsonPath, "utf-8");
1497
- try {
1498
- if (JSON.parse(rootPackageJsonContent).name === packageName) return rootPackageJsonPath;
1499
- } catch (error) {
1500
- if (!(error instanceof SyntaxError)) throw error;
1501
- }
1502
- }
1503
- } catch {
1504
- const packageName = removePathFromNpmPackage(pkg);
1505
- let currentDir = getPackageDetectionCwd();
1506
- const rootDir = pathe.default.parse(currentDir).root;
1507
- while (currentDir !== rootDir) {
1508
- const packageJsonPath = pathe.default.join(currentDir, "node_modules", packageName, "package.json");
1509
- if ((0, fs.existsSync)(packageJsonPath)) return packageJsonPath;
1510
- currentDir = pathe.default.dirname(currentDir);
1511
- }
1512
- const rootPackageJsonPath = pathe.default.join(rootDir, "node_modules", packageName, "package.json");
1513
- return (0, fs.existsSync)(rootPackageJsonPath) ? rootPackageJsonPath : void 0;
1514
- }
1515
- }
1516
1586
  function resolveImportTarget(exportsField) {
1517
1587
  if (typeof exportsField === "string") return exportsField;
1518
1588
  if (!exportsField || typeof exportsField !== "object") return void 0;
@@ -1533,14 +1603,14 @@ function resolveImportTarget(exportsField) {
1533
1603
  function getPackageEsmEntryPath(pkg) {
1534
1604
  try {
1535
1605
  const resolvedEntryPath = resolvePackageEntryFromProjectRoot(pkg);
1536
- const packageJsonPath = getInstalledPackageJsonPath(pkg);
1537
- if (!packageJsonPath) return resolvedEntryPath;
1606
+ const installedPackageJson = getInstalledPackageJson(pkg);
1607
+ if (!installedPackageJson) return resolvedEntryPath;
1538
1608
  const packageName = removePathFromNpmPackage(pkg);
1539
- const packageJson = JSON.parse((0, fs.readFileSync)(packageJsonPath, "utf-8"));
1609
+ const packageJson = installedPackageJson.packageJson;
1540
1610
  const subpath = pkg === packageName ? "." : `.${pkg.slice(packageName.length)}`;
1541
1611
  const target = resolveImportTarget(typeof packageJson.exports === "string" ? subpath === "." ? packageJson.exports : void 0 : packageJson.exports?.[subpath] ?? (subpath === "." ? packageJson.exports?.["."] ?? (packageJson.exports && !Object.keys(packageJson.exports).some((key) => key.startsWith(".")) ? packageJson.exports : void 0) : void 0)) || packageJson.module;
1542
1612
  if (!target) return resolvedEntryPath;
1543
- return pathe.default.resolve(pathe.default.dirname(packageJsonPath), target);
1613
+ return pathe.default.resolve(installedPackageJson.dir, target);
1544
1614
  } catch {
1545
1615
  return resolvePackageEntryFromProjectRoot(pkg);
1546
1616
  }
@@ -1684,8 +1754,11 @@ function getSharedImportSource(pkg, shareItem) {
1684
1754
  }
1685
1755
  const LOAD_SHARE_TAG = "__loadShare__";
1686
1756
  const loadShareCacheMap = {};
1757
+ function shouldUseEsmLoadShare(pkg, command, isRolldown) {
1758
+ return command === "build" || !!isRolldown || pkg === "lit" || pkg.startsWith("lit/");
1759
+ }
1687
1760
  function getLoadShareImportId(pkg, isRolldown, command) {
1688
- if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, isRolldown || command === "build" ? ".mjs" : ".js");
1761
+ if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, shouldUseEsmLoadShare(pkg, command, isRolldown) ? ".mjs" : ".js");
1689
1762
  return loadShareCacheMap[pkg].getImportId();
1690
1763
  }
1691
1764
  function getLoadShareModulePath(pkg, isRolldown, command) {
@@ -1693,8 +1766,8 @@ function getLoadShareModulePath(pkg, isRolldown, command) {
1693
1766
  return loadShareCacheMap[pkg].getPath();
1694
1767
  }
1695
1768
  function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1696
- if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, isRolldown || command === "build" ? ".mjs" : ".js");
1697
- const useESM = command === "build" || isRolldown;
1769
+ if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, shouldUseEsmLoadShare(pkg, command, isRolldown) ? ".mjs" : ".js");
1770
+ const useESM = shouldUseEsmLoadShare(pkg, command, isRolldown);
1698
1771
  const importLine = command === "build" ? getRuntimeInitPromiseBootstrapCode() : useESM ? `${getRuntimeInitBootstrapCode()}
1699
1772
  const { initPromise } = globalThis[globalKey];` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
1700
1773
  const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
@@ -1728,6 +1801,7 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1728
1801
  const devImportSource = concreteSharedImportSource || pkg;
1729
1802
  const localProviderPath = getLocalProviderImportPath(pkg);
1730
1803
  const isWorkspacePackage = isWorkspaceFilePath(localProviderPath) || isWorkspaceFilePath(concreteSharedImportSource);
1804
+ const skipServePrebuildWarmup = command !== "build" && (pkg === "lit" || pkg.startsWith("lit/"));
1731
1805
  const providerImportId = localProviderPath || concreteSharedImportSource || sharedImportSource;
1732
1806
  const namedExports = getPackageNamedExports(pkg);
1733
1807
  let exportLine;
@@ -1736,8 +1810,8 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1736
1810
  const namedExportLine = `export { ${namedExports.map((name, i) => `__mf_${i} as ${name}`).join(", ")} };`;
1737
1811
  exportLine = useESM ? `export default exportModule.default ?? exportModule;\n ${destructure}\n ${namedExportLine}` : `module.exports = exportModule;\n ${destructure}\n Object.assign(module.exports, { ${namedExports.map((name, i) => `"${name}": __mf_${i}`).join(", ")} });`;
1738
1812
  } else exportLine = useESM ? `export default exportModule.default ?? exportModule\n export * from ${escapeGeneratedStringLiteral(sharedImportSource)}` : "module.exports = exportModule";
1739
- const prebuildImportLine = isWorkspacePackage && command !== "build" ? "" : `import ${escapeGeneratedStringLiteral(sharedImportSource)};`;
1740
- const devDynamicImportLine = isWorkspacePackage ? "" : command !== "build" ? `;() => import(${escapeGeneratedStringLiteral(devImportSource)}).catch(() => {});` : "";
1813
+ const prebuildImportLine = isWorkspacePackage && command !== "build" || skipServePrebuildWarmup ? "" : `import ${escapeGeneratedStringLiteral(sharedImportSource)};`;
1814
+ const devDynamicImportLine = isWorkspacePackage ? "" : command !== "build" && !skipServePrebuildWarmup ? `;() => import(${escapeGeneratedStringLiteral(devImportSource)}).catch(() => {});` : "";
1741
1815
  loadShareCacheMap[pkg].writeSync(`
1742
1816
  ${prebuildImportLine}
1743
1817
  ${devDynamicImportLine}
@@ -1811,7 +1885,7 @@ function generateLocalSharedImportMap() {
1811
1885
  version: ${JSON.stringify(shareItem.version)},
1812
1886
  scope: [${JSON.stringify(shareItem.scope)}],
1813
1887
  loaded: false,
1814
- from: ${JSON.stringify(options.name)},
1888
+ from: ${JSON.stringify(options.internalName)},
1815
1889
  async get () {
1816
1890
  if (${shareItem.shareConfig.import === false}) {
1817
1891
  throw new Error(\`[Module Federation] Shared module '\${${JSON.stringify(key)}}' must be provided by host\`);
@@ -1862,7 +1936,7 @@ function generateLocalSharedImportMap() {
1862
1936
  }
1863
1937
  const REMOTE_ENTRY_ID = "virtual:mf-REMOTE_ENTRY_ID";
1864
1938
  function getRemoteEntryId(options) {
1865
- return `${REMOTE_ENTRY_ID}:${`${options.name}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
1939
+ return `${REMOTE_ENTRY_ID}:${`${options.internalName}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
1866
1940
  }
1867
1941
  function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(options), command = "build") {
1868
1942
  const pluginImportNames = options.runtimePlugins.map((p, i) => {
@@ -1891,7 +1965,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
1891
1965
  ${command === "build" ? getRuntimeInitResolveBootstrapCode() : getRuntimeInitBootstrapCode() + "\n const { initResolve } = globalThis[globalKey];"}
1892
1966
  const initTokens = {}
1893
1967
  const shareScopeName = ${JSON.stringify(options.shareScope)}
1894
- const mfName = ${JSON.stringify(options.name)}
1968
+ const mfName = ${JSON.stringify(options.internalName)}
1895
1969
  let localSharedImportMapPromise
1896
1970
  let exposesMapPromise
1897
1971
 
@@ -2458,7 +2532,7 @@ function pluginModuleParseEnd_default(excludeFn, options) {
2458
2532
  }
2459
2533
  //#endregion
2460
2534
  //#region src/plugins/pluginProxyRemoteEntry.ts
2461
- const filter = (0, _rollup_pluginutils.createFilter)();
2535
+ const filter$1 = (0, _rollup_pluginutils.createFilter)();
2462
2536
  function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposesId }) {
2463
2537
  let viteConfig, _command, root;
2464
2538
  return {
@@ -2498,7 +2572,7 @@ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposes
2498
2572
  },
2499
2573
  transform(code, id) {
2500
2574
  return mapCodeToCodeWithSourcemap((() => {
2501
- if (!filter(id)) return;
2575
+ if (!filter$1(id)) return;
2502
2576
  if (id.includes(remoteEntryId)) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId, _command));
2503
2577
  if (id === virtualExposesId) return generateExposes(options);
2504
2578
  if (id.includes(getHostAutoInitPath())) {
@@ -2571,25 +2645,47 @@ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposes
2571
2645
  }
2572
2646
  //#endregion
2573
2647
  //#region src/plugins/pluginProxyRemotes.ts
2574
- (0, _rollup_pluginutils.createFilter)();
2648
+ const filter = (0, _rollup_pluginutils.createFilter)();
2649
+ function isNodeModulesImporter(importer) {
2650
+ return importer?.includes("/node_modules/") || importer?.includes("\\node_modules\\");
2651
+ }
2575
2652
  function pluginProxyRemotes_default(options) {
2653
+ let command;
2654
+ let root = process.cwd();
2576
2655
  const { remotes } = options;
2656
+ function resolveRemoteId(source, importer, remoteName, isRolldown) {
2657
+ if (source === remoteName) {
2658
+ const installedPackageEntry = getInstalledPackageEntry(source, { cwd: root });
2659
+ if (installedPackageEntry && (importer === void 0 || isNodeModulesImporter(importer))) return installedPackageEntry;
2660
+ }
2661
+ const remoteModule = getRemoteVirtualModule(source, command, isRolldown);
2662
+ addUsedRemote(remoteName, source);
2663
+ return remoteModule.getPath();
2664
+ }
2577
2665
  return {
2578
2666
  name: "proxyRemotes",
2579
2667
  config(config, { command: _command }) {
2668
+ command = _command;
2669
+ root = config.root || process.cwd();
2580
2670
  const isRolldown = getIsRolldown(this);
2581
2671
  Object.keys(remotes).forEach((key) => {
2582
2672
  const remote = remotes[key];
2583
2673
  config.resolve.alias.push({
2584
2674
  find: new RegExp(`^(${remote.name}(\/.*|$))`),
2585
2675
  replacement: "$1",
2586
- customResolver(source) {
2587
- const remoteModule = getRemoteVirtualModule(source, _command, isRolldown);
2588
- addUsedRemote(remote.name, source);
2589
- return remoteModule.getPath();
2676
+ customResolver(source, importer) {
2677
+ return resolveRemoteId(source, importer, remote.name, isRolldown);
2590
2678
  }
2591
2679
  });
2592
2680
  });
2681
+ },
2682
+ resolveId(source, importer) {
2683
+ if (!filter(source)) return;
2684
+ const isRolldown = getIsRolldown(this);
2685
+ for (const remote of Object.values(remotes)) {
2686
+ if (source !== remote.name) continue;
2687
+ return resolveRemoteId(source, importer, remote.name, isRolldown);
2688
+ }
2593
2689
  }
2594
2690
  };
2595
2691
  }
@@ -3065,24 +3161,31 @@ function collectFromRegex(code, isRemoteImport) {
3065
3161
  }
3066
3162
  function pluginRemoteNamedExports(options) {
3067
3163
  const remoteNames = Object.keys(options.remotes);
3068
- function isRemoteImport(source) {
3069
- return remoteNames.some((name) => source === name || source.startsWith(name + "/")) || source.includes("__loadRemote__");
3164
+ const isNodeModulesId = (id) => id.includes("/node_modules/") || id.includes("\\node_modules\\");
3165
+ function isRemoteImport(source, importerId) {
3166
+ return remoteNames.some((name) => {
3167
+ if (source.startsWith(name + "/")) return true;
3168
+ if (source !== name) return false;
3169
+ return !isNodeModulesId(importerId);
3170
+ }) || source.includes("__loadRemote__");
3070
3171
  }
3071
3172
  return {
3072
3173
  name: "module-federation-remote-named-exports",
3073
3174
  enforce: "post",
3074
3175
  async transform(code, id) {
3176
+ if (!getIsRolldown(this)) return;
3075
3177
  if (remoteNames.length === 0) return;
3076
3178
  if (id.includes("__loadRemote__") || id.includes("__loadShare__")) return;
3077
3179
  if (!JS_EXTENSIONS_RE.test(id)) return;
3078
3180
  if (!remoteNames.some((name) => code.includes(name))) return;
3181
+ const matchesRemoteImport = (source) => isRemoteImport(source, id);
3079
3182
  let imports;
3080
3183
  try {
3081
- imports = await collectFromAST(this.parse(code), code, isRemoteImport);
3184
+ imports = await collectFromAST(this.parse(code), code, matchesRemoteImport);
3082
3185
  } catch {
3083
- imports = await collectFromEsLexer(code, isRemoteImport);
3186
+ imports = await collectFromEsLexer(code, matchesRemoteImport);
3084
3187
  }
3085
- if ((!imports || imports.length === 0) && REGEX_FALLBACK_EXTENSIONS_RE.test(id)) imports = collectFromRegex(code, isRemoteImport);
3188
+ if ((!imports || imports.length === 0) && REGEX_FALLBACK_EXTENSIONS_RE.test(id)) imports = collectFromRegex(code, matchesRemoteImport);
3086
3189
  if (!imports) return;
3087
3190
  return applyRewrites(code, imports, id);
3088
3191
  }
@@ -3330,6 +3433,7 @@ function insertAfterLastTopLevelImport(code, snippet) {
3330
3433
  */
3331
3434
  function createEarlyVirtualModulesPlugin(options) {
3332
3435
  const { shared, remotes, virtualModuleDir } = options;
3436
+ const isLitShare = (pkg) => pkg === "lit" || pkg.startsWith("lit/");
3333
3437
  return {
3334
3438
  name: "vite:module-federation-early-init",
3335
3439
  enforce: "pre",
@@ -3342,16 +3446,23 @@ function createEarlyVirtualModulesPlugin(options) {
3342
3446
  VirtualModule.ensureVirtualPackageExists();
3343
3447
  initVirtualModules(_command, getRemoteEntryId(options));
3344
3448
  const isRolldown = getIsRolldown(this);
3345
- if (remotes && Object.keys(remotes).length > 0) for (const key of Object.keys(remotes)) addUsedRemote(key, key);
3449
+ if (remotes && Object.keys(remotes).length > 0) {
3450
+ for (const key of Object.keys(remotes)) addUsedRemote(key, key);
3451
+ if (_command === "serve" && isRolldown) {
3452
+ config.optimizeDeps = config.optimizeDeps || {};
3453
+ config.optimizeDeps.exclude = config.optimizeDeps.exclude || [];
3454
+ config.optimizeDeps.include = config.optimizeDeps.include || [];
3455
+ const collidingInstalledRemotes = Object.keys(remotes || {}).filter((remoteName) => getInstalledPackageJson(remoteName, { cwd: root }));
3456
+ const collidingInstalledRemoteEntries = collidingInstalledRemotes.map((remoteName) => getInstalledPackageEntry(remoteName, { cwd: root }) || remoteName);
3457
+ config.optimizeDeps.exclude.push(...Object.keys(remotes || {}).filter((remoteName) => !collidingInstalledRemotes.includes(remoteName)));
3458
+ config.optimizeDeps.include.push(...collidingInstalledRemoteEntries);
3459
+ }
3460
+ }
3346
3461
  if (shared && Object.keys(shared).length > 0) {
3347
3462
  if (_command === "serve") {
3348
3463
  config.optimizeDeps = config.optimizeDeps || {};
3349
3464
  config.optimizeDeps.include = config.optimizeDeps.include || [];
3350
3465
  config.optimizeDeps.include.push(virtualRuntimeInitStatus.getImportId());
3351
- if (isRolldown) {
3352
- config.optimizeDeps.exclude = config.optimizeDeps.exclude || [];
3353
- config.optimizeDeps.exclude.push(...Object.keys(remotes || {}));
3354
- }
3355
3466
  }
3356
3467
  for (const key of Object.keys(shared)) {
3357
3468
  if (key.endsWith("/")) continue;
@@ -3365,8 +3476,13 @@ function createEarlyVirtualModulesPlugin(options) {
3365
3476
  if (shareItem.shareConfig?.import !== false) writePreBuildLibPath(key, shareItem);
3366
3477
  addUsedShares(key);
3367
3478
  if (_command === "serve" && shareItem.shareConfig?.import !== false) {
3368
- if (!isRolldown) config.optimizeDeps.include.push(getLoadShareImportId(key, isRolldown, _command));
3369
- config.optimizeDeps.include.push(getPreBuildLibImportId(key));
3479
+ const optimizeDeps = config.optimizeDeps ??= {};
3480
+ optimizeDeps.include ??= [];
3481
+ optimizeDeps.exclude ??= [];
3482
+ const shouldBypassOptimizeDep = isLitShare(key);
3483
+ if (shouldBypassOptimizeDep) optimizeDeps.exclude.push(key);
3484
+ if (!isRolldown && !shouldBypassOptimizeDep) optimizeDeps.include.push(getLoadShareImportId(key, isRolldown, _command));
3485
+ optimizeDeps.include.push(getPreBuildLibImportId(key));
3370
3486
  }
3371
3487
  }
3372
3488
  writeLocalSharedImportMap();
@@ -3384,6 +3500,7 @@ function federation(mfUserOptions) {
3384
3500
  const virtualExposesId = getVirtualExposesId(options);
3385
3501
  let command;
3386
3502
  let depsDir = "/node_modules/.vite/deps/";
3503
+ let desiredRolldownOutput;
3387
3504
  return [
3388
3505
  createEarlyVirtualModulesPlugin(options),
3389
3506
  ...isVinext ? [{
@@ -3474,12 +3591,22 @@ function federation(mfUserOptions) {
3474
3591
  };
3475
3592
  }
3476
3593
  let warnedAboutCodeSplitting = false;
3594
+ let warnedAboutCodeSplittingGroups = false;
3477
3595
  const ensureCodeSplitting = (output) => {
3478
- if (output?.codeSplitting !== false) return;
3479
- delete output.codeSplitting;
3480
- if (warnedAboutCodeSplitting) return;
3481
- warnedAboutCodeSplitting = true;
3482
- mfWarn("Ignoring `build.rolldownOptions.output.codeSplitting = false` because module federation requires chunk splitting.");
3596
+ if (output?.codeSplitting === false) {
3597
+ delete output.codeSplitting;
3598
+ if (warnedAboutCodeSplitting) return;
3599
+ warnedAboutCodeSplitting = true;
3600
+ mfWarn("Ignoring `output.codeSplitting = false` because module federation requires chunk splitting.");
3601
+ return;
3602
+ }
3603
+ if (!output?.codeSplitting || typeof output.codeSplitting !== "object") return;
3604
+ if (!("groups" in output.codeSplitting)) return;
3605
+ delete output.codeSplitting.groups;
3606
+ if (Object.keys(output.codeSplitting).length === 0) delete output.codeSplitting;
3607
+ if (warnedAboutCodeSplittingGroups) return;
3608
+ warnedAboutCodeSplittingGroups = true;
3609
+ mfWarn("Ignoring `output.codeSplitting.groups` because it conflicts with module federation. Grouping shared dependency init wrappers with their dependent modules can break runtime init order and cause standalone remotes to fail before mount.");
3483
3610
  };
3484
3611
  let warnedAboutManualChunks = false;
3485
3612
  const applyManualChunks = (output) => {
@@ -3487,7 +3614,7 @@ function federation(mfUserOptions) {
3487
3614
  const isPatchedByPlugin = !!(output.manualChunks && patchedManualChunks.has(output.manualChunks));
3488
3615
  if (output.manualChunks && !isPatchedByPlugin && !warnedAboutManualChunks) {
3489
3616
  warnedAboutManualChunks = true;
3490
- mfWarn("Ignoring `build.rollupOptions.output.manualChunks` because it conflicts with module federation. Module federation transforms shared dependency imports with top-level await, and grouping these transformed modules into a single chunk creates circular async dependencies that cause the application to silently hang.");
3617
+ mfWarn("Ignoring `output.manualChunks` because it conflicts with module federation. Module federation transforms shared dependency imports with top-level await, and grouping these transformed modules into a single chunk creates circular async dependencies that cause the application to silently hang.");
3491
3618
  }
3492
3619
  const mfManualChunks = function(id) {
3493
3620
  if (id.includes(runtimeInitId)) return "runtimeInit";
@@ -3500,10 +3627,46 @@ function federation(mfUserOptions) {
3500
3627
  output.manualChunks = mfManualChunks;
3501
3628
  };
3502
3629
  config.build.rollupOptions = config.build.rollupOptions || {};
3503
- if (!Array.isArray(config.build.rollupOptions.output)) applyManualChunks(config.build.rollupOptions.output ||= {});
3630
+ const rollupOutput = config.build.rollupOptions.output;
3631
+ if (Array.isArray(rollupOutput)) rollupOutput.forEach((output) => applyManualChunks(output));
3632
+ else applyManualChunks(config.build.rollupOptions.output ||= {});
3504
3633
  const buildWithRolldown = config.build;
3505
3634
  buildWithRolldown.rolldownOptions = buildWithRolldown.rolldownOptions || {};
3506
- if (!Array.isArray(buildWithRolldown.rolldownOptions.output)) applyManualChunks(buildWithRolldown.rolldownOptions.output ||= {});
3635
+ const rolldownOutput = buildWithRolldown.rolldownOptions.output;
3636
+ const snapshotRolldownOutput = (output) => ({
3637
+ entryFileNames: output.entryFileNames,
3638
+ chunkFileNames: output.chunkFileNames,
3639
+ assetFileNames: output.assetFileNames
3640
+ });
3641
+ if (Array.isArray(rolldownOutput)) {
3642
+ rolldownOutput.forEach((output) => applyManualChunks(output));
3643
+ desiredRolldownOutput = rolldownOutput.map((output) => snapshotRolldownOutput(output));
3644
+ } else {
3645
+ applyManualChunks(buildWithRolldown.rolldownOptions.output ||= {});
3646
+ desiredRolldownOutput = [snapshotRolldownOutput(buildWithRolldown.rolldownOptions.output)];
3647
+ }
3648
+ },
3649
+ async buildApp(builder) {
3650
+ if (!desiredRolldownOutput) return;
3651
+ const applyRolldownOutput = (output, restoredOutput) => {
3652
+ if (!output || !restoredOutput) return;
3653
+ if (restoredOutput.entryFileNames !== void 0) output.entryFileNames = restoredOutput.entryFileNames;
3654
+ if (restoredOutput.chunkFileNames !== void 0) output.chunkFileNames = restoredOutput.chunkFileNames;
3655
+ if (restoredOutput.assetFileNames !== void 0) output.assetFileNames = restoredOutput.assetFileNames;
3656
+ };
3657
+ for (const environment of Object.values(builder.environments)) {
3658
+ const getRolldownOptions = environment?.getRolldownOptions;
3659
+ if (typeof getRolldownOptions !== "function") continue;
3660
+ environment.getRolldownOptions = async () => {
3661
+ const rolldownOptions = await getRolldownOptions.call(environment);
3662
+ if (Array.isArray(rolldownOptions.output)) rolldownOptions.output.forEach((output, index) => applyRolldownOutput(output, desiredRolldownOutput?.[index]));
3663
+ else {
3664
+ rolldownOptions.output ||= {};
3665
+ applyRolldownOutput(rolldownOptions.output, desiredRolldownOutput[0]);
3666
+ }
3667
+ return rolldownOptions;
3668
+ };
3669
+ }
3507
3670
  },
3508
3671
  load(id) {
3509
3672
  if (id.startsWith("\0")) return;
@@ -3762,12 +3925,12 @@ function federation(mfUserOptions) {
3762
3925
  const prefixToRoot = chunkDir === "." ? "" : `${pathe.default.relative(chunkDir, ".")}/`;
3763
3926
  const replacementExpr = prefixToRoot ? `${escapeUnsafeJsSourceChars(JSON.stringify(prefixToRoot))}+$1` : "$1";
3764
3927
  const replacement = `=function($1){return new URL(${replacementExpr},import.meta.url).href}`;
3765
- const replaced = chunk.code.replace(/=\(?(\w+)(?:,\w+)?\)?\s*=>\s*["'][./][^"']*["']\s*\+\s*\1/, replacement);
3928
+ const replaced = chunk.code.replace(/=\(?(\w+)(?:,\w+)?\)?\s*=>\s*[`"'][./][^`"']*[`"']\s*\+\s*\1/, replacement);
3766
3929
  if (replaced !== chunk.code) {
3767
3930
  chunk.code = replaced;
3768
3931
  continue;
3769
3932
  }
3770
- chunk.code = chunk.code.replace(/=function\((\w+)(?:,\w+)?\)\{return\s*["'][./][^"']*["']\s*\+\s*\1\s*\}/, replacement);
3933
+ chunk.code = chunk.code.replace(/=function\((\w+)(?:,\w+)?\)\{return\s*[`"'][./][^`"']*[`"']\s*\+\s*\1\s*\}/, replacement);
3771
3934
  chunk.code = chunk.code.replace(/=function\((\w+)(?:,\w+)?\)\{return new URL\("\.\.\/"\+\1,import\.meta\.url\)\.href\}/, replacement);
3772
3935
  chunk.code = chunk.code.replace(/new URL\("\.\.\/"\+(\w+),import\.meta\.url\)\.href/g, `new URL(${replacementExpr},import.meta.url).href`);
3773
3936
  }
package/lib/index.d.cts CHANGED
@@ -6,6 +6,7 @@ import { sharePlugin } from "@module-federation/sdk";
6
6
  interface RemoteObjectConfig {
7
7
  type?: string;
8
8
  name: string;
9
+ internalName?: string;
9
10
  entry: string;
10
11
  entryGlobalName?: string;
11
12
  shareScope?: string;