@module-federation/vite 1.16.8 → 1.16.9

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
@@ -2,6 +2,10 @@
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/@module-federation/vite.svg)](https://www.npmjs.com/package/@module-federation/vite)
4
4
 
5
+ ## Vite and VoidZero recommend this plugin
6
+
7
+ [Read the announcement](https://www.linkedin.com/posts/voidzero_github-module-federationvite-vite-plugin-activity-7449452398202241024-JyAL).
8
+
5
9
  ## Reason why 🤔
6
10
 
7
11
  [Microservices](https://martinfowler.com/articles/microservices.html) nowadays is a well-known concept and maybe you are using it in your current company.
@@ -47,7 +51,7 @@ pnpm run multi-example
47
51
 
48
52
  ## Getting started 🚀
49
53
 
50
- [https://module-federation.io/guide/build-plugins/plugins-vite.html](https://module-federation.io/guide/build-plugins/plugins-vite.html)
54
+ [https://module-federation.io/integrations/build-tool/vite](https://module-federation.io/integrations/build-tool/vite)
51
55
 
52
56
  With **@module-federation/vite**, the process becomes delightfully simple, you will only find the differences from a normal Vite configuration.
53
57
 
@@ -143,7 +147,9 @@ export default defineConfig({
143
147
  // Optional parameter that controls where the host initialization script is injected.
144
148
  // By default, it is injected into the index.html file.
145
149
  // You can set this to "entry" to inject it into the entry script instead.
146
- // Useful if your application does not load from index.html.
150
+ // Recommended for SSR hosts without index.html (Nitro, TanStack Start) so
151
+ // initHost() completes before hydrateRoot and @module-federation/bridge-react
152
+ // remotes render on first paint.
147
153
  hostInitInjectLocation: "html", // or "entry"
148
154
  // Controls whether all CSS assets from the bundle should be added to every exposed module.
149
155
  // When false (default), the plugin will not process any CSS assets.
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { _ as mfWarn, a as getIsRolldown, c as getPackageNameFromNodeModulePath, d as isNuxtProjectRoot, f as packageNameDecode, g as createModuleFederationError, h as setPackageDetectionCwd, i as getInstalledPackageJson, l as getSharedCacheKey, m as resolveImportPath, o as getPackageDetectionCwd, p as packageNameEncode, r as getInstalledPackageEntry, s as getPackageName, u as hasPackageDependency, v as normalizePathForImport, y as rebaseImport } from "./pluginDts-Cpmdbbr0.js";
1
+ import { _ as mfWarn, a as getIsRolldown, c as getPackageNameFromNodeModulePath, d as isNuxtProjectRoot, f as packageNameDecode, g as createModuleFederationError, h as setPackageDetectionCwd, i as getInstalledPackageJson, l as getSharedCacheKey, m as resolveImportPath, o as getPackageDetectionCwd, p as packageNameEncode, r as getInstalledPackageEntry, s as getPackageName, u as hasPackageDependency, v as normalizePathForImport, y as rebaseImport } from "./pluginDts-CrSsDUnT.js";
2
2
  import { createRequire } from "node:module";
3
3
  import * as fs$1 from "fs";
4
4
  import { existsSync, readFileSync, realpathSync, statSync, writeFileSync } from "fs";
@@ -171,6 +171,71 @@ function injectEntryScript(html, initSrc) {
171
171
  return html.replace("<head>", `<head><script type="module" src=${JSON.stringify(src)}><\/script>`);
172
172
  }
173
173
  //#endregion
174
+ //#region src/utils/pathNormalization.ts
175
+ const COMMON_SHARED_SUBPATHS = {
176
+ react: [
177
+ "react/jsx-runtime",
178
+ "react/jsx-dev-runtime",
179
+ "react/compiler-runtime"
180
+ ],
181
+ "react-dom": [
182
+ "react-dom/client",
183
+ "react-dom/server",
184
+ "react-dom/server.browser"
185
+ ],
186
+ "solid-js": [
187
+ "solid-js/web",
188
+ "solid-js/store",
189
+ "solid-js/html",
190
+ "solid-js/h"
191
+ ],
192
+ zustand: ["zustand/vanilla", "zustand/react"]
193
+ };
194
+ function removeTrailingSlash(value) {
195
+ return value.endsWith("/") ? value.slice(0, -1) : value;
196
+ }
197
+ function ensureTrailingSlash(value) {
198
+ return `${removeTrailingSlash(value)}/`;
199
+ }
200
+ function getBasePath$1(base) {
201
+ return removeTrailingSlash(base || "/");
202
+ }
203
+ function isNuxtClientBase(base) {
204
+ return getBasePath$1(base).endsWith("/_nuxt");
205
+ }
206
+ function normalizeNodeModulePath(source) {
207
+ return source.replace(/\\/g, "/").replace(/\?.*$/, "");
208
+ }
209
+ function isNodeModulePath(source) {
210
+ return source.includes("/node_modules/") || source.includes("\\node_modules\\");
211
+ }
212
+ function filterId(id) {
213
+ return typeof id === "string" && !id.includes("\0");
214
+ }
215
+ function getMatchingNodeModuleSubpath(source, candidates) {
216
+ const normalized = normalizeNodeModulePath(source);
217
+ return [...candidates].sort((a, b) => b.length - a.length).find((candidate) => normalized.includes(`/node_modules/${candidate}/`) || normalized.includes(`/node_modules/${candidate}.`));
218
+ }
219
+ function getCommonSharedSubpaths(sharedKey) {
220
+ return COMMON_SHARED_SUBPATHS[removeTrailingSlash(sharedKey)] || [];
221
+ }
222
+ function getCommonSharedSubpathFromNodeModulePath(source, sharedKey) {
223
+ return getMatchingNodeModuleSubpath(source, getCommonSharedSubpaths(removeTrailingSlash(sharedKey)));
224
+ }
225
+ /**
226
+ * Resolves the public path for remote entries
227
+ * @param options - Module Federation options
228
+ * @param viteBase - Vite's base config value
229
+ * @param originalBase - Original base config before any transformations
230
+ * @returns The resolved public path
231
+ */
232
+ function resolvePublicPath(options, viteBase, originalBase) {
233
+ if (options.publicPath && options.publicPath !== "auto") return options.publicPath;
234
+ if (!originalBase) return "auto";
235
+ if (viteBase) return ensureTrailingSlash(viteBase);
236
+ return "auto";
237
+ }
238
+ //#endregion
174
239
  //#region src/utils/normalizeModuleFederationOptions.ts
175
240
  const INTERNAL_NAME_PREFIX = "__mfe_internal__";
176
241
  function toInternalModuleFederationName(name) {
@@ -257,7 +322,9 @@ function getLitExportSubpathShares(sharedName) {
257
322
  }
258
323
  function normalizeShareItem(key, shareItem) {
259
324
  const isImportFalse = typeof shareItem === "object" && shareItem.import === false;
260
- const version = (typeof shareItem === "object" ? shareItem.version || inferVersionFromRequiredVersion(shareItem.requiredVersion) : void 0) || searchPackageVersion(key);
325
+ const explicitVersion = typeof shareItem === "object" ? shareItem.version : void 0;
326
+ const inferredVersion = typeof shareItem === "object" ? inferVersionFromRequiredVersion(shareItem.requiredVersion) : void 0;
327
+ const version = explicitVersion || searchPackageVersion(key) || inferredVersion;
261
328
  if (typeof shareItem === "string") return {
262
329
  name: shareItem,
263
330
  version,
@@ -282,6 +349,11 @@ function normalizeShareItem(key, shareItem) {
282
349
  }
283
350
  };
284
351
  }
352
+ function normalizeSharedKey(key) {
353
+ if (!key.endsWith("/")) return key;
354
+ const baseKey = key.slice(0, -1);
355
+ return getCommonSharedSubpaths(baseKey).length > 0 ? baseKey : key;
356
+ }
285
357
  function normalizeShared(shared) {
286
358
  explicitSharedKeys = /* @__PURE__ */ new Set();
287
359
  if (!shared) {
@@ -306,16 +378,18 @@ function normalizeShared(shared) {
306
378
  const sourceEntries = [];
307
379
  if (Array.isArray(shared)) shared.forEach((key) => {
308
380
  if (isModuleFederationRuntimePackage(key)) return;
309
- result[key] = normalizeShareItem(key, key);
310
- explicitSharedKeys.add(key);
311
- sourceEntries.push([key, key]);
381
+ const normalizedKey = normalizeSharedKey(key);
382
+ result[normalizedKey] = normalizeShareItem(normalizedKey, normalizedKey);
383
+ explicitSharedKeys.add(normalizedKey);
384
+ sourceEntries.push([normalizedKey, normalizedKey]);
312
385
  });
313
386
  else if (typeof shared === "object") Object.keys(shared).forEach((key) => {
314
387
  if (isModuleFederationRuntimePackage(key)) return;
388
+ const normalizedKey = normalizeSharedKey(key);
315
389
  const value = shared[key];
316
- result[key] = normalizeShareItem(key, value);
317
- explicitSharedKeys.add(key);
318
- sourceEntries.push([key, value]);
390
+ result[normalizedKey] = normalizeShareItem(normalizedKey, value);
391
+ explicitSharedKeys.add(normalizedKey);
392
+ sourceEntries.push([normalizedKey, value]);
319
393
  });
320
394
  sourceEntries.forEach(([key, value]) => {
321
395
  for (const subpathShare of getLitExportSubpathShares(key)) {
@@ -985,6 +1059,49 @@ function isWorkspacePackageEntry(pkg, resolved) {
985
1059
  fromResolvedEntry: resolved
986
1060
  });
987
1061
  }
1062
+ function getWorkspacePackageJson(pkg) {
1063
+ const resolved = getLocalProviderImportPath(pkg) || getProjectResolvedImportPath(pkg);
1064
+ if (!isWorkspacePackageEntry(pkg, resolved)) return;
1065
+ return getInstalledPackageJson(pkg, {
1066
+ packageName: getPackageName(pkg),
1067
+ fromResolvedEntry: resolved
1068
+ })?.packageJson;
1069
+ }
1070
+ function getDependencyNames(packageJson) {
1071
+ if (!packageJson) return [];
1072
+ const names = /* @__PURE__ */ new Set();
1073
+ for (const field of [
1074
+ "dependencies",
1075
+ "peerDependencies",
1076
+ "optionalDependencies"
1077
+ ]) {
1078
+ const deps = packageJson[field];
1079
+ if (!deps || typeof deps !== "object") continue;
1080
+ for (const dep of Object.keys(deps)) names.add(dep);
1081
+ }
1082
+ return Array.from(names);
1083
+ }
1084
+ function isWorkspaceSingletonConsumedByPeer(pkg) {
1085
+ const shared = getNormalizeModuleFederationOptions()?.shared || {};
1086
+ const sharedKeyByPackageName = /* @__PURE__ */ new Map();
1087
+ Object.entries(shared).filter(([, item]) => item.shareConfig.singleton === true).forEach(([key]) => {
1088
+ const packageName = getPackageName(key);
1089
+ if (!sharedKeyByPackageName.get(packageName) || key === packageName) sharedKeyByPackageName.set(packageName, key);
1090
+ });
1091
+ const reachesPkg = (current, seen) => {
1092
+ const packageJson = getWorkspacePackageJson(current);
1093
+ for (const dependency of getDependencyNames(packageJson)) {
1094
+ const sharedDependency = sharedKeyByPackageName.get(dependency);
1095
+ if (!sharedDependency) continue;
1096
+ if (sharedDependency === pkg) return true;
1097
+ if (seen.has(sharedDependency)) continue;
1098
+ seen.add(sharedDependency);
1099
+ if (reachesPkg(sharedDependency, seen)) return true;
1100
+ }
1101
+ return false;
1102
+ };
1103
+ return Array.from(sharedKeyByPackageName.values()).some((sharedPkg) => sharedPkg !== pkg && reachesPkg(sharedPkg, new Set([sharedPkg])));
1104
+ }
988
1105
  function tryResolveImportFromPackageRoot(pkg, root) {
989
1106
  try {
990
1107
  return createRequire$1(pathToFileURL(path$1.join(root, "package.json"))).resolve(pkg);
@@ -1114,26 +1231,46 @@ function materializeCachedLoadShareModule(options) {
1114
1231
  options.addUsedShares(pkg);
1115
1232
  options.writeLocalSharedImportMap();
1116
1233
  }
1234
+ function generateEagerWorkspaceSingletonExports(namedExports, importSource, cacheKey) {
1235
+ const namedExportLine = namedExports.length > 0 ? `\n export { ${namedExports.join(", ")} } from ${escapeGeneratedStringLiteral(importSource)};` : "";
1236
+ return `import * as __mfLocalShare from ${escapeGeneratedStringLiteral(importSource)};
1237
+ let exportModule = __mfModuleCache.share[${escapeGeneratedStringLiteral(cacheKey)}];
1238
+ if (exportModule === undefined) {
1239
+ Promise.resolve().then(() => {
1240
+ if (__mfModuleCache.share[${escapeGeneratedStringLiteral(cacheKey)}] === undefined) {
1241
+ __mfModuleCache.share[${escapeGeneratedStringLiteral(cacheKey)}] = __mfNormalizeShareModule(__mfLocalShare);
1242
+ }
1243
+ });
1244
+ exportModule = __mfLocalShare;
1245
+ }
1246
+ const __mf_default = exportModule.default ?? exportModule;
1247
+ export { __mf_default as default };${namedExportLine}`;
1248
+ }
1117
1249
  function generateLazyWorkspaceSingletonExports(namedExports, importSource, cacheKey, eagerLocalFallback) {
1118
1250
  const namedExportVars = namedExports.map((_name, i) => `__mf_${i}`);
1119
1251
  const declarations = namedExports.length > 0 ? ["let __mf_default;", ...namedExportVars.map((name) => `let ${name};`)].join("\n ") : "let __mf_default;";
1120
1252
  const assignments = namedExports.length > 0 ? [...namedExports.map((name, i) => `${namedExportVars[i]} = mod[${escapeGeneratedStringLiteral(name)}];`), "__mf_default = mod.default ?? mod;"].join("\n ") : "__mf_default = mod.default ?? mod;";
1121
1253
  const namedExportLine = namedExports.length > 0 ? `\n export { ${namedExports.map((name, i) => `${namedExportVars[i]} as ${name}`).join(", ")} };` : "";
1254
+ const applyLocalFallback = `exportModule = __mfNormalizeShareModule(__mfLocalShare);
1255
+ __mfModuleCache.share[${escapeGeneratedStringLiteral(cacheKey)}] = exportModule;
1256
+ __mfApplyLazyShareExports(exportModule);`;
1122
1257
  const body = `${declarations}
1123
1258
  const __mfApplyLazyShareExports = (mod) => {
1124
1259
  ${assignments}
1125
1260
  };
1126
1261
  let exportModule = __mfModuleCache.share[${escapeGeneratedStringLiteral(cacheKey)}];
1127
1262
  if (exportModule === undefined) {
1128
- ${eagerLocalFallback ? `exportModule = __mfNormalizeShareModule(__mfLocalShare);
1129
- __mfModuleCache.share[${escapeGeneratedStringLiteral(cacheKey)}] = exportModule;
1130
- __mfApplyLazyShareExports(exportModule);` : `initPromise.then(() =>
1131
- import(${escapeGeneratedStringLiteral(importSource)}).then((mod) => {
1132
- exportModule = __mfNormalizeShareModule(mod);
1133
- __mfModuleCache.share[${escapeGeneratedStringLiteral(cacheKey)}] = exportModule;
1134
- __mfApplyLazyShareExports(exportModule);
1135
- })
1136
- );`}
1263
+ ${eagerLocalFallback ? applyLocalFallback : `if (import.meta.env.SSR) {
1264
+ ${applyLocalFallback}
1265
+ } else {
1266
+ initPromise.then(() =>
1267
+ import(${escapeGeneratedStringLiteral(importSource)}).then((mod) => {
1268
+ exportModule = __mfNormalizeShareModule(mod);
1269
+ __mfModuleCache.share[${escapeGeneratedStringLiteral(cacheKey)}] = exportModule;
1270
+ __mfApplyLazyShareExports(exportModule);
1271
+ })
1272
+ );
1273
+ }`}
1137
1274
  } else {
1138
1275
  __mfApplyLazyShareExports(exportModule);
1139
1276
  }
@@ -1141,6 +1278,16 @@ function generateLazyWorkspaceSingletonExports(namedExports, importSource, cache
1141
1278
  return eagerLocalFallback ? `import * as __mfLocalShare from ${escapeGeneratedStringLiteral(importSource)};
1142
1279
  ${body}` : body;
1143
1280
  }
1281
+ const WORKSPACE_SINGLETON_SSR_LOCAL_SHARE = "__mfNormalizeShareModule(__mfLocalShare)";
1282
+ function prependWorkspaceSingletonSsrImport(code) {
1283
+ if (!code.includes("if (import.meta.env.SSR)")) return code;
1284
+ if (!code.includes(WORKSPACE_SINGLETON_SSR_LOCAL_SHARE)) return code;
1285
+ if (code.includes("import * as __mfLocalShare")) return code;
1286
+ const importMatch = code.match(/initPromise\.then\(\(\)\s*=>\s*\n\s*import\((["'])(.+?)\1\)\.then\(\(mod\)\s*=>\s*\{[\s\S]*?__mfApplyLazyShareExports/);
1287
+ if (!importMatch) return code;
1288
+ const quote = importMatch[1];
1289
+ return `import * as __mfLocalShare from ${quote}${importMatch[2]}${quote};\n${code}`;
1290
+ }
1144
1291
  function generateDeferredHostProvidedExports(namedExports, pkg, cacheKey) {
1145
1292
  const namedExportVars = namedExports.map((_name, i) => `__mf_${i}`);
1146
1293
  const declarations = ["let __mf_default;", ...namedExportVars.map((name) => `let ${name};`)].join("\n ");
@@ -1207,11 +1354,13 @@ function writeLoadShareModule(pkg, shareItem, command, _isRolldown) {
1207
1354
  const isWorkspacePackage = isWorkspacePackageEntry(pkg, localProviderPath) || isWorkspacePackageEntry(pkg, concreteSharedImportSource);
1208
1355
  const lazyLocalFallbackSource = concreteSharedImportSource || localProviderPath || sharedImportSource;
1209
1356
  const skipServePrebuildWarmup = command !== "build" && (pkg === "lit" || pkg.startsWith("lit/"));
1210
- const usesLazyLocalFallback = isWorkspacePackage && shareItem.shareConfig.singleton === true;
1357
+ const isWorkspaceSingleton = isWorkspacePackage && shareItem.shareConfig.singleton === true;
1358
+ const usesEagerWorkspaceFallback = isWorkspaceSingleton && isWorkspaceSingletonConsumedByPeer(pkg);
1211
1359
  const namedExports = getSharedNamedExports(pkg, shareItem);
1212
1360
  let exportLine;
1213
1361
  let initBlock = "";
1214
- if (usesLazyLocalFallback) {
1362
+ if (usesEagerWorkspaceFallback) exportLine = generateEagerWorkspaceSingletonExports(namedExports, lazyLocalFallbackSource, cacheKey);
1363
+ else if (isWorkspaceSingleton) {
1215
1364
  importLine = `${getRuntimeInitPromiseBootstrapCode()}\n ${importLine}`;
1216
1365
  exportLine = generateLazyWorkspaceSingletonExports(namedExports, lazyLocalFallbackSource, cacheKey, command !== "build");
1217
1366
  } else if (namedExports.length > 0) {
@@ -1234,9 +1383,9 @@ function writeLoadShareModule(pkg, shareItem, command, _isRolldown) {
1234
1383
  initBlock = `exportModule = __mfNormalizeShareModule(__mfLocalShare);
1235
1384
  __mfModuleCache.share[${escapeGeneratedStringLiteral(cacheKey)}] = exportModule;`;
1236
1385
  }
1237
- const prebuildImportLine = usesLazyLocalFallback || isWorkspacePackage && command !== "build" ? "" : `import * as __mfLocalShare from ${escapeGeneratedStringLiteral(skipServePrebuildWarmup ? devImportSource : sharedImportSource)};`;
1386
+ const prebuildImportLine = isWorkspaceSingleton || isWorkspacePackage && command !== "build" ? "" : `import * as __mfLocalShare from ${escapeGeneratedStringLiteral(skipServePrebuildWarmup ? devImportSource : sharedImportSource)};`;
1238
1387
  const devDynamicImportLine = isWorkspacePackage ? "" : command !== "build" && !skipServePrebuildWarmup ? `;() => import(${escapeGeneratedStringLiteral(devImportSource)}).catch(() => {});` : "";
1239
- const moduleBody = usesLazyLocalFallback ? `
1388
+ const moduleBody = isWorkspaceSingleton ? `
1240
1389
  ${prebuildImportLine}
1241
1390
  ${devDynamicImportLine}
1242
1391
  ${importLine}
@@ -1372,6 +1521,8 @@ function generateUsedSharedPreloadConfig() {
1372
1521
  const shareItem = getShareItemForPreload(pkg);
1373
1522
  if (!shareItem) return null;
1374
1523
  return `${JSON.stringify(pkg)}: {
1524
+ version: ${JSON.stringify(shareItem.version)},
1525
+ scope: ${JSON.stringify(shareItem.scope)},
1375
1526
  shareConfig: {
1376
1527
  singleton: ${shareItem.shareConfig.singleton},
1377
1528
  requiredVersion: ${JSON.stringify(shareItem.shareConfig.requiredVersion)},
@@ -1394,7 +1545,11 @@ function getOrderedUsedShares() {
1394
1545
  }));
1395
1546
  }
1396
1547
  function orderSharedDependenciesFirst(sharedPackages) {
1397
- const sharedKeyByPackageName = new Map(sharedPackages.map((pkg) => [getPackageName(pkg), pkg]));
1548
+ const sharedKeyByPackageName = /* @__PURE__ */ new Map();
1549
+ sharedPackages.forEach((pkg) => {
1550
+ const packageName = getPackageName(pkg);
1551
+ if (!sharedKeyByPackageName.get(packageName) || pkg === packageName) sharedKeyByPackageName.set(packageName, pkg);
1552
+ });
1398
1553
  const visiting = /* @__PURE__ */ new Set();
1399
1554
  const visited = /* @__PURE__ */ new Set();
1400
1555
  const ordered = [];
@@ -1439,6 +1594,11 @@ function generateSharedCacheSeedItem(pkg, shareItem, importPath) {
1439
1594
  __mfModuleCache.share[${JSON.stringify(cacheKey)}] = exportModule;
1440
1595
  }`;
1441
1596
  }
1597
+ const sharedCacheKeyHelperCode = `const __mfGetSharedCacheKey = (pkg, singleton, version, scope) => {
1598
+ const normalizedScope = Array.isArray(scope) ? scope[0] : scope;
1599
+ const prefix = (normalizedScope || "default") + ":";
1600
+ return singleton || !version ? prefix + pkg : prefix + pkg + "@" + version;
1601
+ };`;
1442
1602
  const normalizeRuntimeShareCode = `const __mfNormalizeRuntimeShare = (mod) => {
1443
1603
  let current = mod;
1444
1604
  for (let i = 0; i < 5; i++) {
@@ -1555,6 +1715,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
1555
1715
  }
1556
1716
 
1557
1717
  async function init(shared = {}, initScope = []) {
1718
+ ${sharedCacheKeyHelperCode}
1558
1719
  const {usedShared, usedRemotes} = await getLocalSharedImportMap()
1559
1720
  try {
1560
1721
  const allInstances = globalThis.__FEDERATION__?.__SHARE__;
@@ -1566,11 +1727,19 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
1566
1727
  for (const [pkg, versionMap] of Object.entries(scopeShare)) {
1567
1728
  for (const [version, provider] of Object.entries(versionMap)) {
1568
1729
  if (!provider.lib) continue;
1569
- const cacheKey = provider.shareConfig?.singleton ? pkg : \`\${pkg}@\${version}\`;
1730
+ const cacheKey = __mfGetSharedCacheKey(pkg, provider.shareConfig?.singleton, version, ${JSON.stringify(options.shareScope)});
1570
1731
  if (__mfModuleCache.share[cacheKey] !== undefined) continue;
1571
1732
  const mod = typeof provider.lib === "function" ? provider.lib() : provider.lib;
1572
1733
  const resolved = await Promise.resolve(mod);
1573
- __mfModuleCache.share[cacheKey] = __mfNormalizeRuntimeShare(resolved);
1734
+ const normalized = __mfNormalizeRuntimeShare(resolved);
1735
+ __mfModuleCache.share[cacheKey] = normalized;
1736
+ const usedShare = usedShared?.[pkg];
1737
+ if (provider.shareConfig?.singleton && usedShare) {
1738
+ const usedCacheKey = __mfGetSharedCacheKey(pkg, usedShare.shareConfig?.singleton, usedShare.version, usedShare.scope);
1739
+ if (__mfModuleCache.share[usedCacheKey] === undefined) {
1740
+ __mfModuleCache.share[usedCacheKey] = normalized;
1741
+ }
1742
+ }
1574
1743
  }
1575
1744
  }
1576
1745
  }
@@ -1578,6 +1747,14 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
1578
1747
  } catch (e) {
1579
1748
  console.error('[Module Federation] Failed to bridge external shared modules', e)
1580
1749
  }
1750
+ for (const [pkg, share] of Object.entries(usedShared)) {
1751
+ const cacheKey = __mfGetSharedCacheKey(pkg, share.shareConfig?.singleton, share.version, share.scope);
1752
+ if (__mfModuleCache.share[cacheKey] !== undefined) continue;
1753
+ const singletonCacheKey = __mfGetSharedCacheKey(pkg, true, share.version, share.scope);
1754
+ if (__mfModuleCache.share[singletonCacheKey] !== undefined) {
1755
+ __mfModuleCache.share[cacheKey] = __mfModuleCache.share[singletonCacheKey];
1756
+ }
1757
+ }
1581
1758
  ${generateDirectSharedCacheSeedCode(command)}
1582
1759
  const __browserPlugins = [${pluginImportNames.filter((item) => !isSsrOnlyPlugin(item[1])).map((item) => `${item[0]}(${item[2]})`).join(", ")}];
1583
1760
  const __ssrPlugins = typeof globalThis.window === 'undefined'
@@ -1614,7 +1791,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
1614
1791
  console.error('[Module Federation]', e)
1615
1792
  }
1616
1793
  for (const [pkg, share] of Object.entries(usedShared)) {
1617
- const cacheKey = share.shareConfig?.singleton || !share.version ? pkg : \`\${pkg}@\${share.version}\`;
1794
+ const cacheKey = __mfGetSharedCacheKey(pkg, share.shareConfig?.singleton, share.version, share.scope);
1618
1795
  if (share.shareConfig?.import !== false || __mfModuleCache.share[cacheKey] !== undefined) continue;
1619
1796
  ${normalizeRuntimeShareCode}
1620
1797
  const versions = shared?.[pkg];
@@ -1654,10 +1831,11 @@ function generateHostAutoInitCode(remoteEntryImport, _command = "build") {
1654
1831
  const remoteEntry = await import(${remoteEntryImport});
1655
1832
  const runtime = await remoteEntry.init();
1656
1833
  const usedShared = ${generateUsedSharedPreloadConfig()};
1834
+ ${sharedCacheKeyHelperCode}
1657
1835
  ${normalizeRuntimeShareCode}
1658
1836
  ${shouldPreloadShares ? `
1659
1837
  for (const [pkg, share] of Object.entries(usedShared)) {
1660
- const cacheKey = share.shareConfig?.singleton || !share.version ? pkg : \`\${pkg}@\${share.version}\`;
1838
+ const cacheKey = __mfGetSharedCacheKey(pkg, share.shareConfig?.singleton, share.version, share.scope);
1661
1839
  if (__mfModuleCache.share[cacheKey] !== undefined) {
1662
1840
  continue;
1663
1841
  }
@@ -1866,7 +2044,7 @@ function getRemoteExportBlock(command, deferRemoteLoad, consumer) {
1866
2044
  if (command !== "serve" && command !== "build") return `__mfSyncDefaultExport();
1867
2045
  export { __mfDefaultExport as default };`;
1868
2046
  return `__mfSyncDefaultExport();
1869
- __mfRemotePending?.then(__mfSyncDefaultExport);
2047
+ __mfRemotePending?.then(__mfSyncDefaultExport, () => {});
1870
2048
  export { exportModule as __moduleExports };
1871
2049
  ${deferRemoteLoad ? getLazyRemotePendingExport() : getEagerRemotePendingExport()}
1872
2050
  ${command === "serve" && consumer === "server" ? getServerThenExport() : ""}
@@ -1897,8 +2075,9 @@ function generateRemotes(id, command, enableSsrInit = false, consumer = "unified
1897
2075
  const remoteLoadFailureHandler = command === "build" ? `.catch((error) => {
1898
2076
  delete __mfModuleCache.remote[pendingKey];
1899
2077
  throw error;
1900
- })` : `.catch(() => {
2078
+ })` : `.catch((error) => {
1901
2079
  delete __mfModuleCache.remote[pendingKey];
2080
+ throw error;
1902
2081
  })`;
1903
2082
  const remoteLoadCode = `
1904
2083
  function __mfStartRemoteLoad() {
@@ -2024,9 +2203,11 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry", forceClien
2024
2203
  let _command;
2025
2204
  let emitFileId;
2026
2205
  let viteConfig;
2027
- let clientInjected = forceClientInjected ?? false;
2206
+ let skipHtmlDevFallback = forceClientInjected ?? false;
2207
+ let clientInjected = false;
2028
2208
  let emittedFileName;
2029
2209
  let skipTransformIds = /* @__PURE__ */ new Set();
2210
+ let injectedTransformIds = /* @__PURE__ */ new Set();
2030
2211
  let bootstrapDir = "";
2031
2212
  function skipSvelteKitSsrBuild() {
2032
2213
  return (_command === "build" || viteConfig?.command === "build") && viteConfig?.build?.ssr && hasPackageDependency("@sveltejs/kit");
@@ -2148,6 +2329,10 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry", forceClien
2148
2329
  if (id.startsWith("\0") || id.startsWith("virtual:")) return normalizeModuleId(id);
2149
2330
  return normalizeModuleId(path$1.isAbsolute(id) ? id : path$1.resolve(viteConfig.root, id));
2150
2331
  }
2332
+ function isFederationInternalVirtualId(id) {
2333
+ const normalized = decodeViteId(id).replace(/^\0+/, "");
2334
+ return normalized.includes("virtual:mf:") || /__(?:loadShare|prebuild|loadRemote)__/.test(normalized);
2335
+ }
2151
2336
  function addEntryFile(file) {
2152
2337
  const normalized = normalizeModuleId(file);
2153
2338
  if (!entryFiles.includes(normalized)) entryFiles.push(normalized);
@@ -2255,9 +2440,9 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry", forceClien
2255
2440
  if (envName?.name && envName.name !== "client") return;
2256
2441
  const inputOptions = getBuildInput(config);
2257
2442
  if (!inputOptions) htmlFilePath = path$1.resolve(config.root, "index.html");
2258
- else if (typeof inputOptions === "string") entryFiles = [normalizeModuleId(inputOptions)];
2259
- else if (Array.isArray(inputOptions)) entryFiles = inputOptions.map(normalizeModuleId);
2260
- else if (typeof inputOptions === "object") entryFiles = Object.values(inputOptions).map((input) => normalizeModuleId(String(input)));
2443
+ else if (typeof inputOptions === "string") entryFiles = [resolveProjectId(inputOptions)];
2444
+ else if (Array.isArray(inputOptions)) entryFiles = inputOptions.map(resolveProjectId);
2445
+ else if (typeof inputOptions === "object") entryFiles = Object.values(inputOptions).map((input) => resolveProjectId(String(input)));
2261
2446
  if (entryFiles.length > 0) htmlFilePath = getFirstHtmlEntryFile(entryFiles);
2262
2447
  if (htmlFilePath) addHtmlScriptEntries(htmlFilePath);
2263
2448
  },
@@ -2357,7 +2542,8 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry", forceClien
2357
2542
  if (isSvelteKitServerModule(id)) return;
2358
2543
  if (hasEntryBootstrapParam(id)) return;
2359
2544
  if (normalizeModuleId(id).endsWith(".html")) return;
2360
- if (skipTransformIds.has(resolveProjectId(id))) return;
2545
+ const projectId = resolveProjectId(id);
2546
+ if (skipTransformIds.has(projectId)) return;
2361
2547
  const transformCtx = this;
2362
2548
  const transformEnv = transformCtx != null && typeof transformCtx === "object" ? transformCtx["environment"] : void 0;
2363
2549
  if (transformEnv?.name && transformEnv.name !== "client") return;
@@ -2382,12 +2568,13 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry", forceClien
2382
2568
  const injection = `await import(${JSON.stringify(getEntryPath())}).then(({ initHost }) => initHost());\n `;
2383
2569
  return mapCodeToCodeWithSourcemap(code.replace("vueApp.mount(vueAppRootContainer);", `${injection}vueApp.mount(vueAppRootContainer);`));
2384
2570
  }
2385
- const isHydrationEntryFallback = inject === "entry" && entryFiles.length === 0 && (!htmlFilePath || !fs$1.existsSync(htmlFilePath)) && !clientInjected && !id.includes("node_modules") && (id.startsWith("\0") || /\.(js|ts|mjs|vue|jsx|tsx)(\?|$)/.test(id)) && /hydrateRoot|createRoot|ReactDOM\.render/.test(code);
2571
+ const isHydrationEntryFallback = inject === "entry" && entryFiles.length === 0 && (!htmlFilePath || !fs$1.existsSync(htmlFilePath)) && !clientInjected && !isFederationInternalVirtualId(id) && !id.includes("node_modules") && (id.startsWith("\0") || /\.(js|ts|mjs|vue|jsx|tsx)(\?|$)/.test(id)) && (/hydrateRoot|createRoot|ReactDOM\.render/.test(code) || /\.mount\s*\(\s*['"#]/.test(code) || /\.mount\s*\(/.test(code) && /createSSRApp|createApp/.test(code));
2386
2572
  const isNuxtEntryAsyncModule = /(?:^|\/)nuxt\/dist\/app\/entry\.async\.js(?:\?|$)/.test(id) && code.includes("entry();");
2387
- const isNuxtClientEntryFallback = _command === "serve" && inject === "entry" && (!htmlFilePath || !fs$1.existsSync(htmlFilePath)) && !clientInjected && !hasEntryBootstrapParam(id) && !id.includes("node_modules/.vite") && isNuxtEntryAsyncModule && !entryFiles.some((file) => resolveProjectId(id) === file);
2388
- if (!(_command === "serve" && isNuxtEntryAsyncModule) && (injectEntry() && entryFiles.some((file) => resolveProjectId(id) === file) || _command === "serve" && inject === "html" && !isVinext && !clientInjected && !id.startsWith("\0") && !id.includes("node_modules") && /\.(js|ts|mjs|vue|jsx|tsx)(\?|$)/.test(id) || isHydrationEntryFallback || isNuxtClientEntryFallback)) {
2573
+ const isNuxtClientEntryFallback = _command === "serve" && inject === "entry" && (!htmlFilePath || !fs$1.existsSync(htmlFilePath)) && !clientInjected && !hasEntryBootstrapParam(id) && !id.includes("node_modules/.vite") && isNuxtEntryAsyncModule && !entryFiles.some((file) => projectId === file);
2574
+ if (!(_command === "serve" && isNuxtEntryAsyncModule) && (injectedTransformIds.has(projectId) || injectEntry() && entryFiles.some((file) => projectId === file) || _command === "serve" && inject === "html" && !isVinext && !clientInjected && !skipHtmlDevFallback && !id.startsWith("\0") && !id.includes("node_modules") && /\.(js|ts|mjs|vue|jsx|tsx)(\?|$)/.test(id) || isHydrationEntryFallback || isNuxtClientEntryFallback)) {
2389
2575
  clientInjected = true;
2390
- if (!waitsForInit || _command === "serve" && inject === "entry" && isHydrationEntryFallback) return mapCodeToCodeWithSourcemap(`import ${JSON.stringify(getEntryPath())};\n` + code);
2576
+ injectedTransformIds.add(projectId);
2577
+ if (!waitsForInit) return mapCodeToCodeWithSourcemap(`import ${JSON.stringify(getEntryPath())};\n` + code);
2391
2578
  const entrySrc = id.includes("?") ? `${id}&${ENTRY_BOOTSTRAP_QUERY.slice(1)}` : `${id}${ENTRY_BOOTSTRAP_QUERY}`;
2392
2579
  return mapCodeToCodeWithSourcemap(getBootstrapSource(getEntryPath(), entrySrc, false, { skipRemotePreload: _command === "serve" && isNuxtEntryAsyncModule }));
2393
2580
  }
@@ -2592,7 +2779,7 @@ const REMOTE_HMR_ENDPOINT = "__mf_hmr";
2592
2779
  const REMOTE_HMR_EVENT = "mf:remote-update";
2593
2780
  const REMOTE_HMR_CONNECT_RETRY_DELAY_MS = 1e3;
2594
2781
  const REMOTE_HMR_CONNECT_MAX_RETRIES = 10;
2595
- function getBasePath$1(base) {
2782
+ function getBasePath(base) {
2596
2783
  if (!base) return "/";
2597
2784
  if (base.startsWith("http://") || base.startsWith("https://")) try {
2598
2785
  return new URL(base).pathname || "/";
@@ -2602,11 +2789,11 @@ function getBasePath$1(base) {
2602
2789
  return base;
2603
2790
  }
2604
2791
  function getRemoteHmrPath(base) {
2605
- return `${getBasePath$1(base).replace(/\/?$/, "/")}${REMOTE_HMR_ENDPOINT}`.replace(/\/{2,}/g, "/");
2792
+ return `${getBasePath(base).replace(/\/?$/, "/")}${REMOTE_HMR_ENDPOINT}`.replace(/\/{2,}/g, "/");
2606
2793
  }
2607
2794
  function getHmrWsPath(base, hmrPath) {
2608
- const normalizedBase = getBasePath$1(base);
2609
- const normalizedPath = getBasePath$1(hmrPath || "");
2795
+ const normalizedBase = getBasePath(base);
2796
+ const normalizedPath = getBasePath(hmrPath || "");
2610
2797
  if (!normalizedPath || normalizedPath === "/") return normalizedBase;
2611
2798
  return `${normalizedBase.endsWith("/") ? normalizedBase.slice(0, -1) : normalizedBase}/${normalizedPath.startsWith("/") ? normalizedPath.slice(1) : normalizedPath}`;
2612
2799
  }
@@ -3255,70 +3442,6 @@ const buildFileToShareKeyMap = async (shareKeys, resolveFn) => {
3255
3442
  return fileToShareKey;
3256
3443
  };
3257
3444
  //#endregion
3258
- //#region src/utils/pathNormalization.ts
3259
- const COMMON_SHARED_SUBPATHS = {
3260
- react: [
3261
- "react/jsx-runtime",
3262
- "react/jsx-dev-runtime",
3263
- "react/compiler-runtime"
3264
- ],
3265
- "react-dom": [
3266
- "react-dom/client",
3267
- "react-dom/server",
3268
- "react-dom/server.browser"
3269
- ],
3270
- "solid-js": [
3271
- "solid-js/web",
3272
- "solid-js/store",
3273
- "solid-js/html",
3274
- "solid-js/h"
3275
- ]
3276
- };
3277
- function removeTrailingSlash(value) {
3278
- return value.endsWith("/") ? value.slice(0, -1) : value;
3279
- }
3280
- function ensureTrailingSlash(value) {
3281
- return `${removeTrailingSlash(value)}/`;
3282
- }
3283
- function getBasePath(base) {
3284
- return removeTrailingSlash(base || "/");
3285
- }
3286
- function isNuxtClientBase(base) {
3287
- return getBasePath(base).endsWith("/_nuxt");
3288
- }
3289
- function normalizeNodeModulePath(source) {
3290
- return source.replace(/\\/g, "/").replace(/\?.*$/, "");
3291
- }
3292
- function isNodeModulePath(source) {
3293
- return source.includes("/node_modules/") || source.includes("\\node_modules\\");
3294
- }
3295
- function filterId(id) {
3296
- return typeof id === "string" && !id.includes("\0");
3297
- }
3298
- function getMatchingNodeModuleSubpath(source, candidates) {
3299
- const normalized = normalizeNodeModulePath(source);
3300
- return [...candidates].sort((a, b) => b.length - a.length).find((candidate) => normalized.includes(`/node_modules/${candidate}/`) || normalized.includes(`/node_modules/${candidate}.`));
3301
- }
3302
- function getCommonSharedSubpaths(sharedKey) {
3303
- return COMMON_SHARED_SUBPATHS[removeTrailingSlash(sharedKey)] || [];
3304
- }
3305
- function getCommonSharedSubpathFromNodeModulePath(source, sharedKey) {
3306
- return getMatchingNodeModuleSubpath(source, getCommonSharedSubpaths(removeTrailingSlash(sharedKey)));
3307
- }
3308
- /**
3309
- * Resolves the public path for remote entries
3310
- * @param options - Module Federation options
3311
- * @param viteBase - Vite's base config value
3312
- * @param originalBase - Original base config before any transformations
3313
- * @returns The resolved public path
3314
- */
3315
- function resolvePublicPath(options, viteBase, originalBase) {
3316
- if (options.publicPath && options.publicPath !== "auto") return options.publicPath;
3317
- if (!originalBase) return "auto";
3318
- if (viteBase) return ensureTrailingSlash(viteBase);
3319
- return "auto";
3320
- }
3321
- //#endregion
3322
3445
  //#region src/virtualModules/virtualExposesSSR.ts
3323
3446
  /**
3324
3447
  * Virtual module ID for the SSR exposes map.
@@ -3471,6 +3594,24 @@ function resolveTypesMeta(dts) {
3471
3594
  api: `${typesFolder}.d.ts`
3472
3595
  };
3473
3596
  }
3597
+ function resolveDevRemoteEntryFileName(fileName) {
3598
+ if (!fileName.includes("[hash")) return fileName;
3599
+ const normalized = fileName.replace(/(?:[._-]?\[hash(?::\d+)?\])/g, "");
3600
+ const baseName = path$1.basename(normalized);
3601
+ return path$1.extname(baseName) ? normalized : `${normalized}.js`;
3602
+ }
3603
+ function createRemoteEntryAssetMap(fileName) {
3604
+ return {
3605
+ js: {
3606
+ async: [],
3607
+ sync: [fileName]
3608
+ },
3609
+ css: {
3610
+ async: [],
3611
+ sync: []
3612
+ }
3613
+ };
3614
+ }
3474
3615
  const Manifest = () => {
3475
3616
  const mfOptions = getNormalizeModuleFederationOptions();
3476
3617
  const { name, filename, getPublicPath, manifest: manifestOptions, varFilename } = mfOptions;
@@ -3504,6 +3645,12 @@ const Manifest = () => {
3504
3645
  next();
3505
3646
  return;
3506
3647
  }
3648
+ const devRemoteEntryFile = resolveDevRemoteEntryFileName(filename);
3649
+ if (devRemoteEntryFile !== filename && req.url?.startsWith((viteConfig.base + devRemoteEntryFile).replace(/^\/?/, "/"))) {
3650
+ req.url = req.url.replace(devRemoteEntryFile, filename);
3651
+ next();
3652
+ return;
3653
+ }
3507
3654
  if (req.url?.replace(/\?.*/, "") === (viteConfig.base + mfManifestName).replace(/^\/?/, "/")) {
3508
3655
  res.setHeader("Content-Type", "application/json");
3509
3656
  res.setHeader("Access-Control-Allow-Origin", "*");
@@ -3520,12 +3667,12 @@ const Manifest = () => {
3520
3667
  buildName: name
3521
3668
  },
3522
3669
  remoteEntry: {
3523
- name: filename,
3670
+ name: devRemoteEntryFile,
3524
3671
  path: "",
3525
3672
  type: "module"
3526
3673
  },
3527
3674
  ssrRemoteEntry: {
3528
- name: getSsrRemoteEntryFileName(filename),
3675
+ name: getSsrRemoteEntryFileName(devRemoteEntryFile),
3529
3676
  path: "/__mf_ssr__/",
3530
3677
  type: "module"
3531
3678
  },
@@ -3566,10 +3713,10 @@ const Manifest = () => {
3566
3713
  if (!mfManifestName) return;
3567
3714
  let filesMap = {};
3568
3715
  const foundRemoteEntryFile = findRemoteEntryFile(mfOptions.filename, bundle);
3569
- const expectedSsrRemoteEntryFile = getSsrRemoteEntryFileName(mfOptions.filename);
3716
+ const expectedSsrRemoteEntryFile = getSsrRemoteEntryFileName(foundRemoteEntryFile || mfOptions.filename);
3570
3717
  const foundSsrRemoteEntryFile = Object.values(bundle).find((file) => file.fileName === expectedSsrRemoteEntryFile)?.fileName;
3571
3718
  if (foundRemoteEntryFile) remoteEntryFile = foundRemoteEntryFile;
3572
- ssrRemoteEntryFile = foundSsrRemoteEntryFile || expectedSsrRemoteEntryFile;
3719
+ ssrRemoteEntryFile = foundSsrRemoteEntryFile || (_command === "serve" ? getSsrRemoteEntryFileName(resolveDevRemoteEntryFileName(mfOptions.filename)) : expectedSsrRemoteEntryFile);
3573
3720
  const allCssAssets = mfOptions.bundleAllCSS && !disableAssetsAnalyze ? collectCssAssets(bundle) : /* @__PURE__ */ new Set();
3574
3721
  if (!disableAssetsAnalyze) {
3575
3722
  const exposesModules = Object.keys(mfOptions.exposes).map((item) => mfOptions.exposes[item].import);
@@ -3610,13 +3757,14 @@ const Manifest = () => {
3610
3757
  function generateMFManifest(preloadMap, disableAssetsAnalyze = false) {
3611
3758
  const options = getNormalizeModuleFederationOptions();
3612
3759
  const { name, varFilename } = options;
3760
+ const resolvedRemoteEntryFile = _command === "serve" ? remoteEntryFile || resolveDevRemoteEntryFileName(filename) : remoteEntryFile;
3613
3761
  const remoteEntry = {
3614
- name: remoteEntryFile,
3762
+ name: resolvedRemoteEntryFile,
3615
3763
  path: "",
3616
3764
  type: "module"
3617
3765
  };
3618
3766
  const ssrRemoteEntry = {
3619
- name: ssrRemoteEntryFile || getSsrRemoteEntryFileName(filename),
3767
+ name: ssrRemoteEntryFile || getSsrRemoteEntryFileName(_command === "serve" ? resolveDevRemoteEntryFileName(filename) : filename),
3620
3768
  path: _command === "serve" ? "/__mf_ssr__/" : "",
3621
3769
  type: "module"
3622
3770
  };
@@ -3634,7 +3782,7 @@ const Manifest = () => {
3634
3782
  const shared = Array.from(getUsedShares()).flatMap((shareKey) => {
3635
3783
  const shareItem = getNormalizeShareItem(shareKey);
3636
3784
  if (!shareItem) return [];
3637
- const assets = preloadMap[shareKey] || createEmptyAssetMap();
3785
+ const assets = preloadMap[shareKey] || (_command === "serve" && resolvedRemoteEntryFile ? createRemoteEntryAssetMap(resolvedRemoteEntryFile) : createEmptyAssetMap());
3638
3786
  return [{
3639
3787
  id: `${name}:${shareKey}`,
3640
3788
  name: shareKey,
@@ -3655,7 +3803,7 @@ const Manifest = () => {
3655
3803
  });
3656
3804
  const exposes = Object.entries(options.exposes).map(([key, value]) => {
3657
3805
  const formatKey = key.replace("./", "");
3658
- const assets = preloadMap[value.import] || createEmptyAssetMap();
3806
+ const assets = preloadMap[value.import] || (_command === "serve" && resolvedRemoteEntryFile ? createRemoteEntryAssetMap(resolvedRemoteEntryFile) : createEmptyAssetMap());
3659
3807
  return {
3660
3808
  id: `${name}:${formatKey}`,
3661
3809
  name: formatKey,
@@ -4651,7 +4799,7 @@ function pluginSSRRemoteEntry(options) {
4651
4799
  },
4652
4800
  configureServer(server) {
4653
4801
  const base = "/__mf_ssr__";
4654
- const basePath = getBasePath(viteConfig?.base);
4802
+ const basePath = getBasePath$1(viteConfig?.base);
4655
4803
  const ssrEntryFileName = getSsrRemoteEntryFileName(options.filename);
4656
4804
  if (isNuxtProject || isNuxtClientBase(basePath)) server.middlewares.use((req, _res, next) => {
4657
4805
  if (req.url?.replace(/\?.*/, "") === `${basePath}/${ssrEntryFileName}`) req.url = `${basePath}/__mf_ssr__/${ssrEntryFileName}`;
@@ -4955,6 +5103,13 @@ var normalizeOptimizeDeps_default = {
4955
5103
  if (!optimizeDeps.include) optimizeDeps.include = [];
4956
5104
  if (!optimizeDeps.exclude) optimizeDeps.exclude = [];
4957
5105
  if (!optimizeDeps.needsInterop) optimizeDeps.needsInterop = [];
5106
+ },
5107
+ configResolved: (config) => {
5108
+ const include = config.optimizeDeps?.include;
5109
+ const exclude = config.optimizeDeps?.exclude;
5110
+ if (!include?.length || !exclude?.length) return;
5111
+ const included = new Set(include);
5112
+ config.optimizeDeps.exclude = exclude.filter((dep) => !included.has(dep));
4958
5113
  }
4959
5114
  };
4960
5115
  //#endregion
@@ -5108,6 +5263,7 @@ function createEarlyVirtualModulesPlugin(options) {
5108
5263
  external: true
5109
5264
  }));
5110
5265
  build.onResolve({ filter: /.*/ }, (args) => {
5266
+ if (args.kind === "entry-point") return;
5111
5267
  if (!args.importer || args.namespace === "mf-shared") return;
5112
5268
  if (isSharedResolverInternalImporter(args.importer)) return;
5113
5269
  if (!findSharedKey(args.path, shared) || args.path.endsWith(".css")) return;
@@ -5221,7 +5377,7 @@ export default __mfShared.default ?? __mfShared;`
5221
5377
  const SSR_ONLY_PLUGINS = new Set(["@module-federation/vite/ssrEntryLoader"]);
5222
5378
  function loadPluginDts(options) {
5223
5379
  if (options.dts === false) return [];
5224
- return [import("./pluginDts-Cpmdbbr0.js").then((n) => n.n).then(({ default: pluginDts }) => pluginDts(options))];
5380
+ return [import("./pluginDts-CrSsDUnT.js").then((n) => n.n).then(({ default: pluginDts }) => pluginDts(options))];
5225
5381
  }
5226
5382
  function federation(mfUserOptions) {
5227
5383
  if (isTestEnv()) return [];
@@ -5233,6 +5389,7 @@ function federation(mfUserOptions) {
5233
5389
  const virtualExposesId = getVirtualExposesId(options);
5234
5390
  let command;
5235
5391
  let desiredRolldownOutput;
5392
+ let isSsrBuild = false;
5236
5393
  return [
5237
5394
  {
5238
5395
  name: "vite:module-federation-virtual-modules",
@@ -5344,6 +5501,7 @@ function federation(mfUserOptions) {
5344
5501
  enforce: "pre",
5345
5502
  apply: "build",
5346
5503
  config(config) {
5504
+ isSsrBuild = config.build?.ssr === true;
5347
5505
  const runtimeInitId = virtualRuntimeInitStatus.getImportId();
5348
5506
  config.build = config.build || {};
5349
5507
  if (config.build.modulePreload !== false) {
@@ -5465,6 +5623,8 @@ function federation(mfUserOptions) {
5465
5623
  load(id) {
5466
5624
  if (id.includes("__loadShare__") || id.includes("__loadRemote__")) {
5467
5625
  let code = VirtualModule.findById(id)?.code ?? readFileSync(id, "utf-8");
5626
+ const environmentName = this.environment?.name;
5627
+ if (environmentName && environmentName !== "client" || !environmentName && isSsrBuild) code = prependWorkspaceSingletonSsrImport(code);
5468
5628
  code = code.replace(/import\s+["'][^"']*__prebuild__[^"']*["']\s*;?/g, "");
5469
5629
  code = code.replace(/export\s+\*\s+from\s+["'][^"']*__prebuild__[^"']*["']\s*;?/g, "");
5470
5630
  if (!(/\b(?:var|let|const)\s+__moduleExports\b/.test(code) || /\bexport\s+const\s+__moduleExports\b/.test(code) || /\bexport\s*\{[^}]*__moduleExports/.test(code))) {
@@ -5526,6 +5686,7 @@ function federation(mfUserOptions) {
5526
5686
  _options: options,
5527
5687
  config(config, { command: _command }) {
5528
5688
  const isRolldown = getIsRolldown(this);
5689
+ isSsrBuild = _command === "build" && config.build?.ssr === true;
5529
5690
  appendResolveAlias(config, {
5530
5691
  find: "@module-federation/runtime",
5531
5692
  replacement: options.implementation
@@ -5550,7 +5711,17 @@ function federation(mfUserOptions) {
5550
5711
  const envTargetDefineValue = !options.target && isAstro ? "undefined" : JSON.stringify(resolvedTarget);
5551
5712
  if (!config.define) config.define = {};
5552
5713
  if (!("ENV_TARGET" in config.define)) config.define["ENV_TARGET"] = envTargetDefineValue;
5714
+ if (resolvedTarget === "node" && !("FEDERATION_OPTIMIZE_NO_SNAPSHOT_PLUGIN" in config.define)) config.define["FEDERATION_OPTIMIZE_NO_SNAPSHOT_PLUGIN"] = "true";
5553
5715
  if (options.target && "ENV_TARGET" in config.define && config.define["ENV_TARGET"] !== JSON.stringify(options.target)) mfWarn(`ENV_TARGET define (${config.define["ENV_TARGET"]}) differs from target option ("${options.target}"). ENV_TARGET will not be overridden.`);
5716
+ },
5717
+ configEnvironment(name, config) {
5718
+ if (!(config.consumer === "server" || name === "ssr" || name === "server" || config.build?.ssr === true)) return;
5719
+ const isAstro = hasPackageDependency("astro");
5720
+ const envTargetDefineValue = !options.target && isAstro ? "undefined" : JSON.stringify(options.target ?? "node");
5721
+ config.define = { ...config.define ?? {} };
5722
+ if (!("ENV_TARGET" in config.define)) config.define["ENV_TARGET"] = envTargetDefineValue;
5723
+ if (!("FEDERATION_OPTIMIZE_NO_SNAPSHOT_PLUGIN" in config.define)) config.define["FEDERATION_OPTIMIZE_NO_SNAPSHOT_PLUGIN"] = "true";
5724
+ if (options.target && config.define["ENV_TARGET"] !== JSON.stringify(options.target)) mfWarn(`ENV_TARGET define (${config.define["ENV_TARGET"]}) differs from target option ("${options.target}"). ENV_TARGET will not be overridden.`);
5554
5725
  }
5555
5726
  },
5556
5727
  ...Manifest(),
@@ -194,7 +194,8 @@ function getPackageNameFromNodeModulePath(source) {
194
194
  return parts[0];
195
195
  }
196
196
  function getSharedCacheKey(pkg, shareItem) {
197
- return shareItem.shareConfig.singleton || !shareItem.version ? pkg : `${pkg}@${shareItem.version}`;
197
+ const prefix = `${shareItem.scope || "default"}:`;
198
+ return shareItem.shareConfig.singleton || !shareItem.version ? `${prefix}${pkg}` : `${prefix}${pkg}@${shareItem.version}`;
198
199
  }
199
200
  function getInstalledPackageJson(pkg, opts) {
200
201
  const cwd = opts?.cwd || getPackageDetectionCwd();
@@ -87,7 +87,8 @@ const _path = () => nodeImport("path");
87
87
  const _fs = () => nodeImport("fs");
88
88
  const _crypto = () => nodeImport("crypto");
89
89
  const _module = () => nodeImport("module");
90
- const manifestCache = /* @__PURE__ */ new Map();
90
+ const ssrEntryCache = /* @__PURE__ */ new Map();
91
+ const manifestFetchCache = /* @__PURE__ */ new Map();
91
92
  async function fetchManifest(manifestUrl) {
92
93
  try {
93
94
  const res = await fetch(manifestUrl);
@@ -97,9 +98,33 @@ async function fetchManifest(manifestUrl) {
97
98
  return null;
98
99
  }
99
100
  }
101
+ async function fetchManifestCached(manifestUrl) {
102
+ if (!manifestFetchCache.has(manifestUrl)) manifestFetchCache.set(manifestUrl, fetchManifest(manifestUrl));
103
+ return manifestFetchCache.get(manifestUrl);
104
+ }
105
+ /** True when the host configured a manifest URL as the remote entry (any .json name). */
106
+ function isManifestEntry(remoteEntryUrl) {
107
+ try {
108
+ const { pathname } = new URL(remoteEntryUrl);
109
+ return /\.json$/i.test(pathname);
110
+ } catch {
111
+ return /\.json(?:[?#]|$)/i.test(remoteEntryUrl);
112
+ }
113
+ }
114
+ function isSsrEntry(remoteEntryUrl) {
115
+ return /\.ssr\.js(?:[?#].*)?$/.test(remoteEntryUrl);
116
+ }
100
117
  function getManifestUrl(remoteEntryUrl) {
118
+ if (isManifestEntry(remoteEntryUrl)) return remoteEntryUrl;
101
119
  return remoteEntryUrl.replace(/\/[^/]+$/, "/mf-manifest.json");
102
120
  }
121
+ function getEntryFilename(entryUrl) {
122
+ return entryUrl.split("/").pop()?.replace(/[?#].*$/, "").replace(/\.[^.]+$/, "") ?? "remoteEntry";
123
+ }
124
+ function resolveEntryAssetUrl(entry, manifestUrl) {
125
+ const base = manifestUrl.replace(/\/[^/]+$/, "/");
126
+ return new URL(`${entry.path || ""}${entry.name}`, base).href;
127
+ }
103
128
  function resolveSSREntryUrl(manifest, manifestUrl) {
104
129
  const meta = manifest?.metaData;
105
130
  if (!meta?.ssrRemoteEntry?.name) return null;
@@ -124,48 +149,72 @@ async function headCheckSsrEntry(candidate) {
124
149
  } catch {}
125
150
  return null;
126
151
  }
127
- async function getSSREntryByConvention(remoteEntryUrl, options = {}) {
128
- const base = remoteEntryUrl.replace(/\.[^.]+$/, "");
129
- const remoteOrigin = remoteEntryUrl.replace(/\/[^/]+$/, "");
130
- const filename = remoteEntryUrl.split("/").pop()?.replace(/\.[^.]+$/, "") ?? "remoteEntry";
131
- const candidates = [
132
- ...options.skipServerBuild ? [] : [{
133
- url: `${remoteOrigin}/__mf_server__/${filename}.ssr.js`,
134
- type: "module"
135
- }],
136
- {
137
- url: `${base}.ssr.js`,
138
- type: "module"
139
- },
140
- {
141
- url: `${remoteOrigin}/__mf_ssr__/${filename}.ssr.js`,
142
- type: "module"
143
- }
144
- ];
152
+ function resolveAssetBaseUrl(entryUrl, manifest, manifestUrl) {
153
+ const remoteEntry = manifest?.metaData?.remoteEntry;
154
+ if (remoteEntry?.name) return resolveEntryAssetUrl(remoteEntry, manifestUrl);
155
+ if (!isManifestEntry(entryUrl)) return entryUrl;
156
+ return new URL("remoteEntry.js", manifestUrl.replace(/\/[^/]+$/, "/")).href;
157
+ }
158
+ async function buildEntryContext(entryUrl) {
159
+ const manifestUrl = getManifestUrl(entryUrl);
160
+ const manifest = await fetchManifestCached(manifestUrl);
161
+ const assetBaseUrl = resolveAssetBaseUrl(entryUrl, manifest, manifestUrl);
162
+ return {
163
+ entryUrl,
164
+ manifestUrl,
165
+ manifest,
166
+ assetBaseUrl,
167
+ filename: getEntryFilename(assetBaseUrl),
168
+ remoteOrigin: assetBaseUrl.replace(/\/[^/]+$/, "")
169
+ };
170
+ }
171
+ function buildSsrEntryCandidates(ctx, options = {}) {
172
+ const { assetBaseUrl, filename, remoteOrigin } = ctx;
173
+ const base = assetBaseUrl.replace(/\.[^.]+$/, "");
174
+ const candidates = [];
175
+ if (!options.skipServerBuild) candidates.push({
176
+ url: `${remoteOrigin}/__mf_server__/${filename}.ssr.js`,
177
+ type: "module"
178
+ });
179
+ candidates.push({
180
+ url: `${base}.ssr.js`,
181
+ type: "module"
182
+ }, {
183
+ url: `${remoteOrigin}/__mf_ssr__/${filename}.ssr.js`,
184
+ type: "module"
185
+ });
186
+ return candidates;
187
+ }
188
+ async function resolveFirstReachableCandidate(candidates) {
145
189
  for (const candidate of candidates) {
146
190
  const hit = await headCheckSsrEntry(candidate);
147
191
  if (hit) return hit;
148
192
  }
149
193
  return null;
150
194
  }
151
- async function getSSREntry(remoteEntryUrl) {
152
- const remoteOrigin = remoteEntryUrl.replace(/\/[^/]+$/, "");
153
- const filename = remoteEntryUrl.split("/").pop()?.replace(/\.[^.]+$/, "") ?? "remoteEntry";
154
- const manifestUrl = getManifestUrl(remoteEntryUrl);
155
- if (!manifestCache.has(manifestUrl)) manifestCache.set(manifestUrl, (async () => {
195
+ async function resolveSSREntryImpl(remoteEntryUrl) {
196
+ if (isSsrEntry(remoteEntryUrl)) return {
197
+ url: remoteEntryUrl,
198
+ type: "module"
199
+ };
200
+ if (!isManifestEntry(remoteEntryUrl)) {
201
+ const filename = getEntryFilename(remoteEntryUrl);
156
202
  const fromServerBuild = await headCheckSsrEntry({
157
- url: `${remoteOrigin}/__mf_server__/${filename}.ssr.js`,
203
+ url: `${remoteEntryUrl.replace(/\/[^/]+$/, "")}/__mf_server__/${filename}.ssr.js`,
158
204
  type: "module"
159
205
  });
160
206
  if (fromServerBuild) return fromServerBuild;
161
- const manifest = await fetchManifest(manifestUrl);
162
- if (manifest) {
163
- const fromManifest = resolveSSREntryUrl(manifest, manifestUrl);
164
- if (fromManifest) return fromManifest;
165
- }
166
- return getSSREntryByConvention(remoteEntryUrl, { skipServerBuild: true });
167
- })());
168
- return manifestCache.get(manifestUrl);
207
+ }
208
+ const ctx = await buildEntryContext(remoteEntryUrl);
209
+ if (ctx.manifest) {
210
+ const fromManifest = resolveSSREntryUrl(ctx.manifest, ctx.manifestUrl);
211
+ if (fromManifest) return fromManifest;
212
+ }
213
+ return resolveFirstReachableCandidate(buildSsrEntryCandidates(ctx, { skipServerBuild: !isManifestEntry(remoteEntryUrl) }));
214
+ }
215
+ async function getSSREntry(remoteEntryUrl) {
216
+ if (!ssrEntryCache.has(remoteEntryUrl)) ssrEntryCache.set(remoteEntryUrl, resolveSSREntryImpl(remoteEntryUrl));
217
+ return ssrEntryCache.get(remoteEntryUrl);
169
218
  }
170
219
  const tempFileCache = /* @__PURE__ */ new Map();
171
220
  let ssrCacheDirPromise;
@@ -257,11 +306,14 @@ async function loadSSRRemoteEntry(ssrEntry, resolvedShared = {}) {
257
306
  if (urlObj.pathname.includes("/__mf_ssr__/")) {
258
307
  const remoteOrigin = urlObj.origin;
259
308
  const runner = await getOrCreateRunner(remoteOrigin);
260
- if (!runner) return null;
261
- try {
262
- return await runner.import(urlObj.pathname);
309
+ if (!runner) {
310
+ if (process.env.NODE_ENV !== "production") return null;
311
+ } else try {
312
+ const mod = await runner.import(urlObj.pathname);
313
+ if (mod && typeof mod === "object" && "init" in mod) return mod;
314
+ if (process.env.NODE_ENV !== "production") return null;
263
315
  } catch {
264
- return null;
316
+ if (process.env.NODE_ENV !== "production") return null;
265
317
  }
266
318
  }
267
319
  const { mkdirSync } = await _fs();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/vite",
3
- "version": "1.16.8",
3
+ "version": "1.16.9",
4
4
  "description": "Vite plugin for Module Federation",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",