@module-federation/vite 1.17.1 → 1.18.1

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
@@ -31,6 +31,7 @@ Examples live in [`gioboa/module-federation-vite-examples`](https://github.com/g
31
31
  | ---------------------------------------------------------------------------------------- | --------------- | ----------------- | --------------------------- |
32
32
  | [Alpine](https://github.com/gioboa/module-federation-vite-examples/tree/main/alpine) | `alpine-host` | `alpine-remote` | Alpine.js |
33
33
  | [Angular](https://github.com/gioboa/module-federation-vite-examples/tree/main/angular) | `angular-host` | `angular-remote` | Angular |
34
+ | [Ember](https://github.com/gioboa/module-federation-vite-examples/tree/main/ember) | `ember-host` | `ember-remote` | Ember 7 |
34
35
  | [Lit](https://github.com/gioboa/module-federation-vite-examples/tree/main/lit) | `lit-host` | `lit-remote` | Lit |
35
36
  | [Nuxt](https://github.com/gioboa/module-federation-vite-examples/tree/main/nuxt) | `nuxt-host` | `nuxt-remote` | Nuxt 4 |
36
37
  | [Nx](https://github.com/gioboa/react-nx-microfrontend-demo) | `host` | `remote` | React + Nx |
package/lib/index.d.ts CHANGED
@@ -8,7 +8,7 @@ interface RemoteObjectConfig {
8
8
  internalName?: string;
9
9
  entry: string;
10
10
  entryGlobalName?: string;
11
- shareScope?: string;
11
+ shareScope?: string | string[];
12
12
  }
13
13
  interface TreeShakingConfig {
14
14
  mode: 'server-calc' | 'runtime-infer';
@@ -36,7 +36,7 @@ type ModuleFederationOptions = {
36
36
  name: string;
37
37
  remotes?: Record<string, string | RemoteObjectConfig> | undefined;
38
38
  runtime?: any;
39
- shareScope?: string;
39
+ shareScope?: string | string[];
40
40
  /**
41
41
  * Override the public path used for remote entries
42
42
  * Defaults to Vite's base config or "auto" if base is empty
package/lib/index.js CHANGED
@@ -639,7 +639,7 @@ var VirtualModule = class VirtualModule {
639
639
  //#endregion
640
640
  //#region src/utils/ssrCapabilities.ts
641
641
  /** A browser-safe generated expression that is true only in Node.js. */
642
- const SERVER_ENV_GUARD = "typeof process !== 'undefined' && !!process.versions && !!process.versions.node";
642
+ const SERVER_ENV_GUARD = "import.meta.env.SSR";
643
643
  /**
644
644
  * Single source of truth for SSR-related feature gates.
645
645
  *
@@ -1854,6 +1854,7 @@ function getDependencyNames(packageJson) {
1854
1854
  }
1855
1855
  function isSharedSingletonConsumedByPeer(pkg) {
1856
1856
  const shared = getNormalizeModuleFederationOptions()?.shared || {};
1857
+ if (Object.entries(shared).some(([key, item]) => key !== pkg && key.startsWith(`${pkg}/`) && item.shareConfig.singleton === true)) return true;
1857
1858
  const sharedKeyByPackageName = /* @__PURE__ */ new Map();
1858
1859
  Object.entries(shared).filter(([, item]) => item.shareConfig.singleton === true).forEach(([key]) => {
1859
1860
  const packageName = getPackageName(key);
@@ -2144,13 +2145,14 @@ function generateEagerWorkspaceSingletonExports(namedExports, importSource, cach
2144
2145
  __mfApplyEagerShareExports(exportModule);
2145
2146
  export { __mf_default as default };${namedExportLine}`;
2146
2147
  }
2147
- function generateLazyWorkspaceSingletonExports(namedExports, importSource, cacheDescriptor, cacheOwner, treeShakingConsumer) {
2148
+ function generateLazyWorkspaceSingletonExports(namedExports, importSource, cacheDescriptor, cacheOwner, treeShakingConsumer, serveLocalFallback = false) {
2148
2149
  const namedExportVars = namedExports.map((_name, i) => `__mf_${i}`);
2149
2150
  const declarations = namedExports.length > 0 ? ["let __mf_default;", ...namedExportVars.map((name) => `let ${name};`)].join("\n ") : "let __mf_default;";
2150
2151
  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;";
2151
2152
  const namedExportLine = namedExports.length > 0 ? `\n export { ${namedExports.map((name, i) => `${namedExportVars[i]} as ${name}`).join(", ")} };` : "";
2152
2153
  const applyLocalFallback = `exportModule = __mfNormalizeShareModule(__mfLocalShare);
2153
- __mfWriteSharedCache(__mfModuleCache.share, ${cacheDescriptor}, exportModule, ${cacheOwner});`;
2154
+ __mfWriteSharedCache(__mfModuleCache.share, ${cacheDescriptor}, exportModule, ${cacheOwner});
2155
+ __mfApplyLazyShareExports(exportModule);`;
2154
2156
  return `${declarations}
2155
2157
  const __mfApplyLazyShareExports = (mod) => {
2156
2158
  ${assignments}
@@ -2158,7 +2160,7 @@ function generateLazyWorkspaceSingletonExports(namedExports, importSource, cache
2158
2160
  __mfSubscribeSharedCache(__mfModuleCache.share, ${cacheDescriptor}, __mfApplyLazyShareExports);
2159
2161
  let exportModule = ${getSharedCacheReadExpression(cacheDescriptor, treeShakingConsumer)};
2160
2162
  if (exportModule === undefined) {
2161
- if (import.meta.env.SSR) {
2163
+ if (import.meta.env.SSR${serveLocalFallback ? " || (import.meta.env.DEV && typeof __mfLocalShare !== 'undefined')" : ""}) {
2162
2164
  ${applyLocalFallback}
2163
2165
  } else {
2164
2166
  (__mfModuleCache.pendingShareLoads ||= []).push(initPromise.then(() => {
@@ -2182,8 +2184,15 @@ const WORKSPACE_SINGLETON_SSR_LOCAL_SHARE = "__mfNormalizeShareModule(__mfLocalS
2182
2184
  function prependWorkspaceSingletonSsrImport(code) {
2183
2185
  if (!code.includes("if (import.meta.env.SSR)")) return code;
2184
2186
  if (!code.includes(WORKSPACE_SINGLETON_SSR_LOCAL_SHARE)) return code;
2185
- if (code.includes("import * as __mfLocalShare")) return code;
2186
- const importMatch = code.match(/initPromise\.then\(\(\)\s*=>\s*\{[\s\S]*?\breturn import\((["'])(.+?)\1\)\.then\(\(mod\)\s*=>\s*\{[\s\S]*?__mfApplyLazyShareExports/) ?? code.match(/initPromise\.then\(\(\)\s*=>\s*\n\s*import\((["'])(.+?)\1\)\.then\(\(mod\)\s*=>\s*\{[\s\S]*?__mfApplyLazyShareExports/);
2187
+ const localShareImport = /^[ \t]*import\s+\*\s+as\s+__mfLocalShare\s+from\s+(['"])(.+?)\1\s*;?[ \t]*\r?\n?/gm;
2188
+ let hasLocalShareImport = false;
2189
+ code = code.replace(localShareImport, (statement) => {
2190
+ if (hasLocalShareImport) return "";
2191
+ hasLocalShareImport = true;
2192
+ return statement;
2193
+ });
2194
+ if (hasLocalShareImport) return code;
2195
+ const importMatch = code.match(/initPromise\.then\(\(\)\s*=>\s*\{[\s\S]*?\breturn import\((["'])(.+?)\1\)\.then\(\(mod\)\s*=>\s*\{[\s\S]*?__mfApplyLazyShareExports/) ?? code.match(/initPromise\.then\(\(\)\s*=>\s*\n\s*import\((["'])(.+?)\1\)\.then\(\(mod\)\s*=>\s*\{[\s\S]*?__mfApplyLazyShareExports/) ?? code.match(/import\((["'])(.+?)\1\)/);
2187
2196
  if (!importMatch) return code;
2188
2197
  const quote = importMatch[1];
2189
2198
  return `import * as __mfLocalShare from ${quote}${importMatch[2]}${quote};\n${code}`;
@@ -2223,10 +2232,15 @@ function generateShareModuleUnwrapCode({ source, preserveNamedExports, stopWithR
2223
2232
  return current;`;
2224
2233
  }
2225
2234
  const normalizeLocalShareModuleCode = `const __mfNormalizeShareModule = (mod) => {
2226
- ${generateShareModuleUnwrapCode({
2235
+ const normalized = (() => {
2236
+ ${generateShareModuleUnwrapCode({
2227
2237
  source: "mod",
2228
2238
  preserveNamedExports: true
2229
2239
  })}
2240
+ })();
2241
+ return normalized && Object.getPrototypeOf(normalized) === null
2242
+ ? Object.assign({}, normalized)
2243
+ : normalized;
2230
2244
  };`;
2231
2245
  function writeLoadShareModule(pkg, shareItem, command, _isRolldown) {
2232
2246
  if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, ".js");
@@ -2265,6 +2279,7 @@ function writeLoadShareModule(pkg, shareItem, command, _isRolldown) {
2265
2279
  const isWorkspaceSingleton = isWorkspacePackage && shareItem.shareConfig.singleton === true;
2266
2280
  const isDefaultShareScope = shareItem.scope === void 0 || shareItem.scope === "default" || Array.isArray(shareItem.scope) && shareItem.scope[0] === "default";
2267
2281
  const usesDeferredSingletonFallback = hasCompleteExportCoverage && (isWorkspaceSingleton || command !== "build" && isRemoteOnlyContainer() && shareItem.shareConfig.singleton === true || command === "build" && isRemoteOnlyContainer() && shareItem.shareConfig.singleton === true && !isDefaultShareScope);
2282
+ const servesRemoteSingletonFallback = command !== "build" && isRemoteOnlyContainer() && shareItem.shareConfig.singleton === true;
2268
2283
  const isConsumedByPeerSingleton = isSharedSingletonConsumedByPeer(pkg);
2269
2284
  const usesEntryInjectedRemoteFallback = hasCompleteExportCoverage && command !== "build" && !isWorkspaceSingleton && isRemoteOnlyContainer() && shareItem.shareConfig.singleton === true && getNormalizeModuleFederationOptions().hostInitInjectLocation === "entry" && isConsumedByPeerSingleton;
2270
2285
  const usesEagerWorkspaceFallback = hasCompleteExportCoverage && isWorkspaceSingleton && isConsumedByPeerSingleton;
@@ -2273,11 +2288,11 @@ function writeLoadShareModule(pkg, shareItem, command, _isRolldown) {
2273
2288
  let initBlock = "";
2274
2289
  if (usesDeferredTreeShakingFallback) {
2275
2290
  importLine = `${getRuntimeInitPromiseBootstrapCode()}\n ${importLine}`;
2276
- exportLine = generateLazyWorkspaceSingletonExports(namedExports, lazyLocalFallbackSource, cacheDescriptor, cacheOwner, treeShakingConsumer);
2291
+ exportLine = generateLazyWorkspaceSingletonExports(namedExports, lazyLocalFallbackSource, cacheDescriptor, cacheOwner, treeShakingConsumer, command !== "build" && (isWorkspaceSingleton || isWorkspacePackage || servesRemoteSingletonFallback));
2277
2292
  } else if (usesEagerWorkspaceFallback || usesEntryInjectedRemoteFallback) exportLine = generateEagerWorkspaceSingletonExports(namedExports, lazyLocalFallbackSource, cacheDescriptor, cacheOwner, treeShakingConsumer);
2278
2293
  else if (usesDeferredSingletonFallback) {
2279
2294
  importLine = `${getRuntimeInitPromiseBootstrapCode()}\n ${importLine}`;
2280
- exportLine = generateLazyWorkspaceSingletonExports(namedExports, lazyLocalFallbackSource, cacheDescriptor, cacheOwner, treeShakingConsumer);
2295
+ exportLine = generateLazyWorkspaceSingletonExports(namedExports, lazyLocalFallbackSource, cacheDescriptor, cacheOwner, treeShakingConsumer, command !== "build" && (isWorkspaceSingleton || isWorkspacePackage || servesRemoteSingletonFallback));
2281
2296
  } else if (detectedNamedExports === void 0) {
2282
2297
  exportLine = `const __mfDefaultExport = (() => {
2283
2298
  ${generateShareModuleUnwrapCode({
@@ -2339,7 +2354,7 @@ function writeLoadShareModule(pkg, shareItem, command, _isRolldown) {
2339
2354
  initBlock = `exportModule = __mfNormalizeShareModule(__mfLocalShare);
2340
2355
  __mfWriteSharedCache(__mfModuleCache.share, ${cacheDescriptor}, exportModule, ${cacheOwner});`;
2341
2356
  }
2342
- const prebuildImportLine = usesDeferredSingletonFallback || usesDeferredTreeShakingFallback ? "" : `import * as __mfLocalShare from ${escapeGeneratedStringLiteral(detectedNamedExports === void 0 ? coherentLocalSource : skipServePrebuildWarmup ? devImportSource : sharedImportSource)};`;
2357
+ const prebuildImportLine = usesEagerWorkspaceFallback || usesEntryInjectedRemoteFallback ? "" : usesDeferredSingletonFallback || usesDeferredTreeShakingFallback ? servesRemoteSingletonFallback || usesDeferredSingletonFallback && command !== "build" && (isWorkspaceSingleton || isWorkspacePackage) ? `import * as __mfLocalShare from ${escapeGeneratedStringLiteral(lazyLocalFallbackSource)};` : "" : `import * as __mfLocalShare from ${escapeGeneratedStringLiteral(detectedNamedExports === void 0 ? coherentLocalSource : skipServePrebuildWarmup ? devImportSource : sharedImportSource)};`;
2343
2358
  const devDynamicImportLine = isWorkspacePackage ? "" : usesDeferredSingletonFallback || usesDeferredTreeShakingFallback ? "" : command !== "build" && !skipServePrebuildWarmup ? `;() => import(${escapeGeneratedStringLiteral(devImportSource)}).catch(() => {});` : "";
2344
2359
  const moduleBody = usesDeferredSingletonFallback || usesDeferredTreeShakingFallback ? `
2345
2360
  ${prebuildImportLine}
@@ -2454,10 +2469,12 @@ function generateLocalSharedImportMap() {
2454
2469
  ? (res?.default ?? res)
2455
2470
  : {...res}
2456
2471
  // All npm packages pre-built by vite will be converted to esm
2457
- Object.defineProperty(exportModule, "__esModule", {
2458
- value: true,
2459
- enumerable: false
2460
- })
2472
+ if (exportModule.__esModule !== true) {
2473
+ Object.defineProperty(exportModule, "__esModule", {
2474
+ value: true,
2475
+ enumerable: false
2476
+ })
2477
+ }
2461
2478
  return function () {
2462
2479
  return exportModule
2463
2480
  }
@@ -2550,7 +2567,17 @@ function orderSharedDependenciesFirst(sharedPackages) {
2550
2567
  const sharedDependency = sharedKeyByPackageName.get(dependency);
2551
2568
  if (sharedDependency) visit(sharedDependency);
2552
2569
  });
2553
- if (pkg === packageName) (subpathKeysByPackageName.get(packageName) || []).forEach(visit);
2570
+ if (pkg === packageName) {
2571
+ const subpaths = subpathKeysByPackageName.get(packageName) || [];
2572
+ if (packageName === "react" || packageName === "react-dom") {
2573
+ visiting.delete(pkg);
2574
+ visited.add(pkg);
2575
+ ordered.push(pkg);
2576
+ subpaths.forEach(visit);
2577
+ return;
2578
+ }
2579
+ subpaths.forEach(visit);
2580
+ }
2554
2581
  visiting.delete(pkg);
2555
2582
  visited.add(pkg);
2556
2583
  ordered.push(pkg);
@@ -2572,7 +2599,7 @@ function generateSharedCacheSeedItem(pkg, shareItem, importPath) {
2572
2599
  ${normalizeRuntimeShareCode}
2573
2600
  const normalizedModule = __mfNormalizeRuntimeShare(mod);
2574
2601
  const exportModule = normalizedModule === mod ? {...mod} : normalizedModule;
2575
- Object.defineProperty(exportModule, "__esModule", {
2602
+ if (exportModule.__esModule !== true) Object.defineProperty(exportModule, "__esModule", {
2576
2603
  value: true,
2577
2604
  enumerable: false
2578
2605
  });
@@ -2789,7 +2816,7 @@ function generateRuntimeSharedCacheSeedCode(shareStrategy) {
2789
2816
  ${normalizeRuntimeShareCode}
2790
2817
  const normalizedModule = __mfNormalizeRuntimeShare(resolved);
2791
2818
  const exportModule = normalizedModule === resolved ? {...resolved} : normalizedModule;
2792
- Object.defineProperty(exportModule, "__esModule", {
2819
+ if (exportModule.__esModule !== true) Object.defineProperty(exportModule, "__esModule", {
2793
2820
  value: true,
2794
2821
  enumerable: false
2795
2822
  });
@@ -3047,6 +3074,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3047
3074
  const needsSharedProviderSelectionHelper = Object.keys(options.shared ?? {}).length > 0;
3048
3075
  const hasTreeShakingShared = Object.values(options.shared ?? {}).some((share) => !!share?.shareConfig.treeShaking);
3049
3076
  const hasEagerShared = Object.values(options.shared ?? {}).some((share) => share?.shareConfig.eager === true && share.shareConfig.import !== false);
3077
+ const hasMultipleShareScopes = Array.isArray(options.shareScope);
3050
3078
  const runtimeImports = [
3051
3079
  "init as runtimeInit",
3052
3080
  "loadRemote",
@@ -3065,7 +3093,19 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3065
3093
  serializeRuntimeOptions(p[1])
3066
3094
  ];
3067
3095
  });
3068
- const initializeSharingCode = `try {
3096
+ const initializeSharingCode = hasMultipleShareScopes ? `for (const shareScopeName of shareScopeNamesToInitialize) {
3097
+ try {
3098
+ await retrySharedInit(async () => {
3099
+ await Promise.all(await initRes.initializeSharing(shareScopeName, {
3100
+ strategy: '${options.shareStrategy}',
3101
+ from: "build",
3102
+ initScope
3103
+ }));
3104
+ });
3105
+ } catch (e) {
3106
+ console.error('[Module Federation]', e)
3107
+ }
3108
+ }` : `try {
3069
3109
  await retrySharedInit(async () => {
3070
3110
  await Promise.all(await initRes.initializeSharing('${options.shareScope}', {
3071
3111
  strategy: '${options.shareStrategy}',
@@ -3092,7 +3132,8 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3092
3132
  ${command === "build" ? getRuntimeInitResolveBootstrapCode() : getRuntimeInitBootstrapCode() + "\n const { initResolve } = globalThis[globalKey];"}
3093
3133
  ${getRuntimeModuleCacheBootstrapCode()}
3094
3134
  const initTokens = {}
3095
- const shareScopeName = ${JSON.stringify(options.shareScope)}
3135
+ const shareScopeNames = Array.isArray(${JSON.stringify(options.shareScope)}) ? ${JSON.stringify(options.shareScope)} : [${JSON.stringify(options.shareScope)}]
3136
+ const shareScopeName = ${JSON.stringify(hasMultipleShareScopes ? options.shareScope[0] : options.shareScope)}
3096
3137
  const mfName = ${JSON.stringify(options.name)}
3097
3138
  let localSharedImportMapPromise
3098
3139
  let exposesMapPromise
@@ -3139,31 +3180,66 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3139
3180
 
3140
3181
  async function init(shared = {}, initScope = []) {
3141
3182
  ${sharedCacheHelperCode}
3183
+ const getShareScope = (scopeName) => ${hasMultipleShareScopes} ? (shared?.[scopeName] || {}) : shared;
3184
+ const getShareScopeNames = (share) => {
3185
+ const configuredScopes = Array.isArray(share?.scope) ? share.scope : [share?.scope || shareScopeName];
3186
+ if (!${hasMultipleShareScopes}) return configuredScopes;
3187
+ return [...new Set([...configuredScopes, ...shareScopeNames])];
3188
+ };
3189
+ const getShareScopeName = (pkg, share) => {
3190
+ for (const scopeName of getShareScopeNames(share)) {
3191
+ if (getShareScope(scopeName)?.[pkg]) return scopeName;
3192
+ }
3193
+ return shareScopeName;
3194
+ };
3195
+ const getShareVersions = (pkg, share) => {
3196
+ for (const scopeName of getShareScopeNames(share)) {
3197
+ const versions = getShareScope(scopeName)?.[pkg];
3198
+ if (versions) return versions;
3199
+ }
3200
+ return getShareScope(shareScopeName)?.[pkg];
3201
+ };
3142
3202
  const federationInstances = globalThis.__FEDERATION__?.__INSTANCES__ || [];
3143
3203
  const initRootName = initScope.find((token) => token?.from)?.from;
3144
3204
  const scopeRoot = federationInstances.find((instance) =>
3145
3205
  instance?.options?.name === initRootName &&
3146
- instance?.shareScopeMap?.['${options.shareScope}'] === shared
3206
+ ${hasMultipleShareScopes ? "shareScopeNames.some((scopeName) => instance?.shareScopeMap?.[scopeName] === getShareScope(scopeName))" : `instance?.shareScopeMap?.['${options.shareScope}'] === shared`}
3147
3207
  ) || federationInstances.find((instance) =>
3148
3208
  instance?.options?.name !== mfName &&
3149
- instance?.shareScopeMap?.['${options.shareScope}'] === shared
3209
+ ${hasMultipleShareScopes ? "shareScopeNames.some((scopeName) => instance?.shareScopeMap?.[scopeName] === getShareScope(scopeName))" : `instance?.shareScopeMap?.['${options.shareScope}'] === shared`}
3150
3210
  );
3151
3211
  const initialShared = Object.create(null);
3152
- for (const [pkg, versions] of Object.entries(shared)) {
3212
+ ${hasMultipleShareScopes ? `for (const scopeName of shareScopeNames) {
3213
+ for (const [pkg, versions] of Object.entries(getShareScope(scopeName))) {
3214
+ if (initialShared[pkg]) continue;
3215
+ const initialVersions = initialShared[pkg] = Object.create(null);
3216
+ for (const [version, provider] of Object.entries(versions)) {
3217
+ initialVersions[version] = Object.assign({}, provider);
3218
+ }
3219
+ }
3220
+ }` : `for (const [pkg, versions] of Object.entries(shared)) {
3153
3221
  const initialVersions = initialShared[pkg] = Object.create(null);
3154
3222
  for (const [version, provider] of Object.entries(versions)) {
3155
3223
  // Runtime registration mutates provider records in-place, notably their origin.
3156
3224
  // Preserve the parent-visible provider and its original provenance.
3157
3225
  initialVersions[version] = Object.assign({}, provider);
3158
3226
  }
3159
- }
3227
+ }`}
3160
3228
  const {usedShared, usedRemotes} = await getLocalSharedImportMap()
3161
3229
  // handling circular init calls before an external provider can re-enter this container
3162
- var initToken = initTokens[shareScopeName];
3230
+ ${hasMultipleShareScopes ? `const shareScopeNamesToInitialize = [];
3231
+ for (const shareScopeName of shareScopeNames) {
3232
+ let initToken = initTokens[shareScopeName];
3233
+ if (!initToken) initToken = initTokens[shareScopeName] = { from: mfName };
3234
+ if (initScope.indexOf(initToken) >= 0) continue;
3235
+ initScope.push(initToken);
3236
+ shareScopeNamesToInitialize.push(shareScopeName);
3237
+ }
3238
+ if (shareScopeNamesToInitialize.length === 0) return;` : `var initToken = initTokens[shareScopeName];
3163
3239
  if (!initToken)
3164
3240
  initToken = initTokens[shareScopeName] = { from: mfName };
3165
3241
  if (initScope.indexOf(initToken) >= 0) return;
3166
- initScope.push(initToken);
3242
+ initScope.push(initToken);`}
3167
3243
  ${normalizeRuntimeShareCode}
3168
3244
  const __browserPlugins = [${pluginImportNames.filter((item) => !isSsrOnlyPlugin(item[1])).map((item) => `${item[0]}(${item[2]})`).join(", ")}];
3169
3245
  const __ssrPlugins = typeof globalThis.window === 'undefined'
@@ -3184,7 +3260,10 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3184
3260
  plugins: [__mfSharePinLifecyclePlugin(), ${hasTreeShakingShared ? "__mfTreeShakingSnapshotPlugin()," : ""} ...__browserPlugins, ...__ssrPlugins],
3185
3261
  ${options.shareStrategy ? `shareStrategy: '${options.shareStrategy}'` : ""}
3186
3262
  });
3187
- initRes.initShareScopeMap('${options.shareScope}', shared);
3263
+ ${hasMultipleShareScopes ? `for (const shareScopeName of shareScopeNamesToInitialize) {
3264
+ const scopeShare = getShareScope(shareScopeName);
3265
+ initRes.initShareScopeMap(shareScopeName, scopeShare);
3266
+ }` : `initRes.initShareScopeMap('${options.shareScope}', shared);`}
3188
3267
  function __mfSharePinLifecyclePlugin() {
3189
3268
  return {
3190
3269
  name: "vite-share-pin-lifecycle-plugin",
@@ -3232,7 +3311,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3232
3311
  if (!versionMap || versionMap[version] !== currentProvider) return undefined;
3233
3312
  const pinnedProvider = Object.assign({}, provider, {
3234
3313
  version: provider.version ?? version,
3235
- scope: provider.scope ?? currentProvider?.scope ?? ['${options.shareScope}'],
3314
+ scope: provider.scope ?? currentProvider?.scope ?? ${JSON.stringify(hasMultipleShareScopes ? options.shareScope : [options.shareScope])},
3236
3315
  strategy: 'loaded-first'
3237
3316
  });
3238
3317
  const providerFrom = provider.from;
@@ -3440,7 +3519,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3440
3519
  usedShare.scope
3441
3520
  );
3442
3521
  if (__mfReadSharedCache(__mfModuleCache.share, usedCacheDescriptor) !== undefined) return;
3443
- const liveVersionMap = shared[pkg];
3522
+ const liveVersionMap = ${hasMultipleShareScopes ? "getShareVersions(pkg, usedShare)" : "shared[pkg]"};
3444
3523
  const liveProvider = liveVersionMap?.[version];
3445
3524
  if (providerEntry.registered && !__mfMatchesSharedProvider(liveProvider, provider)) return;
3446
3525
  let loadedShare;
@@ -3526,8 +3605,8 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3526
3605
  const resolvedExternalProvider = __mfResolveExternalSharedProvider(
3527
3606
  federationInstances,
3528
3607
  scopeRoot,
3529
- shared,
3530
- '${options.shareScope}',
3608
+ ${hasMultipleShareScopes ? "getShareScope(getShareScopeName(pkg, usedShare))" : "shared"},
3609
+ ${hasMultipleShareScopes ? "getShareScopeName(pkg, usedShare)" : `'${options.shareScope}'`},
3531
3610
  pkg,
3532
3611
  providerEntry,
3533
3612
  selectedExternalProvider,
@@ -3549,7 +3628,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3549
3628
  if (cachedShare !== undefined && cachedShareOwner !== mfName) return;
3550
3629
  // Registration can replace an unloaded same-version root provider in-place.
3551
3630
  // Pin the chosen provider while loadShare() runs its implicit registration.
3552
- const liveVersionMap = shared[pkg];
3631
+ const liveVersionMap = ${hasMultipleShareScopes ? "getShareVersions(pkg, usedShare)" : "shared[pkg]"};
3553
3632
  const liveProvider = liveVersionMap?.[version];
3554
3633
  if (
3555
3634
  providerEntry.registered &&
@@ -3631,7 +3710,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3631
3710
  await __mfBridgeExternalSharedProvider(
3632
3711
  pkg,
3633
3712
  usedShare,
3634
- shared[pkg],
3713
+ ${hasMultipleShareScopes ? "getShareVersions(pkg, usedShare)" : "shared[pkg]"},
3635
3714
  initialShared[pkg],
3636
3715
  undefined
3637
3716
  );
@@ -3641,9 +3720,10 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3641
3720
  const globalVersionsByPackage = Object.create(null);
3642
3721
  if (allInstances) {
3643
3722
  for (const [, scopes] of Object.entries(allInstances)) {
3644
- const scopeShare = scopes?.['${options.shareScope}'];
3645
- if (!scopeShare) continue;
3646
- for (const [pkg, versionMap] of Object.entries(scopeShare)) {
3723
+ for (const scopeName of shareScopeNames) {
3724
+ const scopeShare = scopes?.[scopeName];
3725
+ if (!scopeShare) continue;
3726
+ for (const [pkg, versionMap] of Object.entries(scopeShare)) {
3647
3727
  const usedShare = usedShared?.[pkg];
3648
3728
  const passedVersions = initialShared[pkg];
3649
3729
  const bridgeSelection = bridgeSelections.get(pkg);
@@ -3664,6 +3744,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3664
3744
  if (provider === usedShare || (usedShare.from && provider.from === usedShare.from)) continue;
3665
3745
  if (globalVersions[version] === undefined) globalVersions[version] = provider;
3666
3746
  }
3747
+ }
3667
3748
  }
3668
3749
  }
3669
3750
  }
@@ -3687,7 +3768,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3687
3768
  : __mfReadSharedCache(__mfModuleCache.share, cacheDescriptor);
3688
3769
  if (share.shareConfig?.import !== false || cachedShare !== undefined) return;
3689
3770
  ${normalizeRuntimeShareCode}
3690
- const versionMap = shared?.[pkg];
3771
+ const versionMap = ${hasMultipleShareScopes ? "getShareVersions(pkg, share)" : "shared?.[pkg]"};
3691
3772
  const provider = __mfSelectSharedProvider(
3692
3773
  versionMap,
3693
3774
  pkg,
@@ -3861,7 +3942,7 @@ function getRemoteVirtualModule(remote, command, enableSsrInit = false, consumer
3861
3942
  const { shareStrategy } = getNormalizeModuleFederationOptions();
3862
3943
  const cacheKey = `${remote}__${command}__${shareStrategy}__${consumer}__${enableSsrInit ? "ssr-init" : "no-ssr-init"}`;
3863
3944
  if (!cacheRemoteMap[cacheKey]) {
3864
- cacheRemoteMap[cacheKey] = new VirtualModule(remote, LOAD_REMOTE_TAG, ".js");
3945
+ cacheRemoteMap[cacheKey] = new VirtualModule(consumer === "unified" ? remote : `${remote}__mf_consumer__${consumer}`, LOAD_REMOTE_TAG, ".js");
3865
3946
  cacheRemoteMap[cacheKey].writeSync(generateRemotes(remote, command, enableSsrInit, consumer));
3866
3947
  }
3867
3948
  return cacheRemoteMap[cacheKey];
@@ -3904,9 +3985,24 @@ function shouldIncludeDeferredProxy(initMode, consumer, eagerLoadClientRemote, d
3904
3985
  function getRemoteModuleRuntimeHelpers() {
3905
3986
  return `
3906
3987
  function __mfUnwrapRemoteDefault(mod) {
3907
- if (mod == null) return mod;
3908
- if (mod.__esModule && mod.default != null) return mod.default;
3909
- return mod.default ?? mod;
3988
+ let value = mod;
3989
+ // A federated expose can pass through more than one ESM/CJS namespace
3990
+ // wrapper (notably with React/Preact lazy imports). Keep unwrapping
3991
+ // explicit default namespaces until the actual component is reached.
3992
+ const seen = new Set();
3993
+ while (value != null && typeof value === "object" && !seen.has(value)) {
3994
+ seen.add(value);
3995
+ if (value.__esModule && value.default != null) {
3996
+ value = value.default;
3997
+ continue;
3998
+ }
3999
+ if (!value.__esModule && value.default != null) {
4000
+ value = value.default;
4001
+ continue;
4002
+ }
4003
+ break;
4004
+ }
4005
+ return value;
3910
4006
  }
3911
4007
  let __mfDefaultExport;
3912
4008
  function __mfSyncDefaultExport() {
@@ -4210,9 +4306,10 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry", forceClien
4210
4306
  const blockEnd = body.lastIndexOf("}");
4211
4307
  if (blockStart === -1 || blockEnd <= blockStart) return scriptTag;
4212
4308
  return `<script>${body.slice(0, blockStart + 1) + `
4309
+ const __mfCurrentScript = document.currentScript;
4213
4310
  (async () => {
4214
4311
  await import(${JSON.stringify(initPath)}).then(({ initHost }) => initHost());
4215
- ` + body.slice(blockStart + 1, blockEnd) + `
4312
+ ` + body.slice(blockStart + 1, blockEnd).replaceAll("document.currentScript", "__mfCurrentScript") + `
4216
4313
  })();
4217
4314
  ` + body.slice(blockEnd)}<\/script>`;
4218
4315
  });
@@ -5521,15 +5618,25 @@ function generateRemoteEntrySSR(options) {
5521
5618
  const initToken = { from: ${JSON.stringify(options.name)} };
5522
5619
  if (initScope.indexOf(initToken) >= 0) return;
5523
5620
  initScope.push(initToken);
5524
- initRes.initShareScopeMap(${JSON.stringify(options.shareScope)}, shared);
5621
+ const shareScopeNames = Array.isArray(${JSON.stringify(options.shareScope)})
5622
+ ? ${JSON.stringify(options.shareScope)}
5623
+ : [${JSON.stringify(options.shareScope)}];
5525
5624
  try {
5526
- await Promise.all(
5527
- await initRes.initializeSharing(${JSON.stringify(options.shareScope)}, {
5528
- strategy: ${JSON.stringify(options.shareStrategy ?? "version-first")},
5529
- from: 'build',
5530
- initScope,
5531
- })
5532
- );
5625
+ for (const scopeName of shareScopeNames) {
5626
+ try {
5627
+ const scopeShare = Array.isArray(${JSON.stringify(options.shareScope)}) ? (shared?.[scopeName] || {}) : shared;
5628
+ initRes.initShareScopeMap(scopeName, scopeShare);
5629
+ await Promise.all(
5630
+ await initRes.initializeSharing(scopeName, {
5631
+ strategy: ${JSON.stringify(options.shareStrategy ?? "version-first")},
5632
+ from: 'build',
5633
+ initScope,
5634
+ })
5635
+ );
5636
+ } catch (e) {
5637
+ console.error('[Module Federation SSR]', e);
5638
+ }
5639
+ }
5533
5640
  } catch (e) {
5534
5641
  console.error('[Module Federation SSR]', e);
5535
5642
  }
@@ -6114,7 +6221,7 @@ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposes
6114
6221
  },
6115
6222
  async buildStart() {
6116
6223
  await refreshExposeRemoteDependencies(this);
6117
- if (_command !== "build") return;
6224
+ if (_command !== "build" || hasPackageDependency("@tanstack/react-start", root)) return;
6118
6225
  for (const expose of Object.values(options.exposes)) {
6119
6226
  const resolved = await this.resolve(expose.import);
6120
6227
  if (resolved) this.emitFile({
@@ -6925,11 +7032,27 @@ function pluginRemoteNamedExports(options) {
6925
7032
  //#endregion
6926
7033
  //#region src/plugins/pluginSSRRemoteEntry.ts
6927
7034
  const MAX_RUNNER_BODY_BYTES = 1024 * 1024;
7035
+ const MAX_RUNNER_START_OFFSET = 1024 * 1024;
6928
7036
  const ALLOWED_RUNNER_INVOKE_NAMES = new Set(["fetchModule", "getBuiltins"]);
6929
7037
  const VITE_FS_PREFIX = "/@fs/";
6930
7038
  function isPlainObject(value) {
6931
7039
  return !!value && typeof value === "object" && !Array.isArray(value);
6932
7040
  }
7041
+ function isSafeRunnerFetchModuleOptions(value) {
7042
+ if (!isPlainObject(value)) return false;
7043
+ const allowedKeys = new Set([
7044
+ "cached",
7045
+ "startOffset",
7046
+ "inlineSourceMap"
7047
+ ]);
7048
+ for (const [key, option] of Object.entries(value)) {
7049
+ if (!allowedKeys.has(key)) return false;
7050
+ if (key === "startOffset") {
7051
+ if (typeof option !== "number" || !Number.isSafeInteger(option) || option < 0 || option > MAX_RUNNER_START_OFFSET) return false;
7052
+ } else if (typeof option !== "boolean") return false;
7053
+ }
7054
+ return true;
7055
+ }
6933
7056
  function stripQueryAndHash(id) {
6934
7057
  const queryIndex = id.indexOf("?");
6935
7058
  const hashIndex = id.indexOf("#");
@@ -6966,15 +7089,21 @@ function isPathWithinAllowedDirectories(filePath, allowedDirectories) {
6966
7089
  return allowedDirectories.some((directory) => isPathWithinDirectory(filePath, directory));
6967
7090
  }
6968
7091
  function isSafeRunnerFetchModuleId(id, config) {
6969
- if (typeof id !== "string" || !id || id.includes("\0")) return false;
7092
+ if (typeof id !== "string" || !id) return false;
6970
7093
  const decoded = decodeViteId(id).replace(/^\0+/, "");
6971
7094
  if (!decoded || decoded.startsWith("virtual:")) return !!decoded;
6972
- if (/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(decoded) || decoded.startsWith("//")) return false;
7095
+ if (decoded.startsWith("file://")) try {
7096
+ const filePath = decodeURIComponent(new URL(decoded).pathname);
7097
+ return path$1.isAbsolute(filePath) && isPathWithinAllowedDirectories(filePath, getRunnerAllowedDirectories(config));
7098
+ } catch {
7099
+ return false;
7100
+ }
7101
+ if (/^(?:https?|data|blob|javascript):/i.test(decoded) || decoded.startsWith("//")) return false;
6973
7102
  const cleanId = decodeRunnerFilePath(stripQueryAndHash(decoded));
6974
7103
  if (!cleanId || hasRelativeTraversal(cleanId)) return false;
6975
7104
  const allowedDirectories = getRunnerAllowedDirectories(config);
6976
7105
  if (cleanId.startsWith(VITE_FS_PREFIX)) {
6977
- const fsPath = cleanId.slice(5);
7106
+ const fsPath = `/${cleanId.slice(5)}`;
6978
7107
  return path$1.isAbsolute(fsPath) && isPathWithinAllowedDirectories(fsPath, allowedDirectories);
6979
7108
  }
6980
7109
  if (path$1.isAbsolute(cleanId)) {
@@ -6994,7 +7123,7 @@ function isRunnerInvokePayload(payload, config) {
6994
7123
  if (name === "getBuiltins") return args.length === 0;
6995
7124
  if (args.length < 1 || args.length > 3) return false;
6996
7125
  const [id, importer, opts] = args;
6997
- return isSafeRunnerFetchModuleId(id, config) && (importer === void 0 || importer === null || isSafeRunnerFetchModuleId(importer, config)) && (opts === void 0 || isPlainObject(opts));
7126
+ return isSafeRunnerFetchModuleId(id, config) && (importer === void 0 || importer === null || isSafeRunnerFetchModuleId(importer, config)) && (opts === void 0 || isSafeRunnerFetchModuleOptions(opts));
6998
7127
  }
6999
7128
  function readBoundedRunnerBody(req, res) {
7000
7129
  return new Promise((resolve) => {
@@ -7110,14 +7239,6 @@ function pluginSSRRemoteEntry(options) {
7110
7239
  const clientEnv = server.environments?.client;
7111
7240
  const runnerEnv = typeof ssrEnv?.hot?.handleInvoke === "function" ? ssrEnv : typeof clientEnv?.hot?.handleInvoke === "function" ? clientEnv : void 0;
7112
7241
  if (typeof (ssrEnv?.fetchModule ?? clientEnv?.fetchModule) === "function" && runnerEnv) server.middlewares.use("/__mf_runner__", async (req, res) => {
7113
- res.setHeader("Access-Control-Allow-Origin", "*");
7114
- if (req.method === "OPTIONS") {
7115
- res.setHeader("Access-Control-Allow-Methods", "POST");
7116
- res.setHeader("Access-Control-Allow-Headers", "Content-Type");
7117
- res.statusCode = 204;
7118
- res.end();
7119
- return;
7120
- }
7121
7242
  if (req.method !== "POST") {
7122
7243
  res.statusCode = 405;
7123
7244
  res.end("Method not allowed");
@@ -7548,6 +7669,43 @@ function canResolveSharedSubpath(subpath, projectRoot) {
7548
7669
  }
7549
7670
  }
7550
7671
  /**
7672
+ * Vite's dependency scanner cannot see through the virtual loadShare modules
7673
+ * generated for shared packages. As a result, dependencies of a linked/shared
7674
+ * package may be discovered one request at a time and each discovery starts a
7675
+ * new optimizer pass. Seed the optimizer with the complete dependency graph
7676
+ * before the first request instead.
7677
+ *
7678
+ * Vite then resolves the package's own dependency graph using its normal
7679
+ * scanner, preserving package and peer-dependency resolution semantics.
7680
+ */
7681
+ function includeLinkedSharedEntries(optimizeDeps, shared, projectRoot, exposes, outDir) {
7682
+ const additions = /* @__PURE__ */ new Set();
7683
+ const entries = new Set(Array.isArray(optimizeDeps.entries) ? optimizeDeps.entries : optimizeDeps.entries ? [optimizeDeps.entries] : [
7684
+ "**/*.html",
7685
+ "!**/node_modules/**",
7686
+ `!**/${outDir.replace(/\\/g, "/")}/**`,
7687
+ "!**/__tests__/**",
7688
+ "!**/coverage/**"
7689
+ ]);
7690
+ for (const [packageName, share] of Object.entries(shared ?? {})) {
7691
+ if (share?.shareConfig?.import === false) continue;
7692
+ const installed = getInstalledPackageJson(packageName, { cwd: projectRoot });
7693
+ if (!installed || installed.dir.replaceAll("\\", "/").includes("/node_modules/")) continue;
7694
+ const entry = getInstalledPackageEntry(packageName, { cwd: projectRoot });
7695
+ if (entry && existsSync(entry)) additions.add(entry);
7696
+ }
7697
+ for (const expose of Object.values(exposes ?? {})) {
7698
+ const source = expose.import;
7699
+ if (source.startsWith(".") || path$1.isAbsolute(source)) {
7700
+ const entry = path$1.resolve(projectRoot, source);
7701
+ if (existsSync(entry)) additions.add(entry);
7702
+ }
7703
+ }
7704
+ if (additions.size === 0) return;
7705
+ for (const entry of additions) entries.add(entry);
7706
+ optimizeDeps.entries = [...entries];
7707
+ }
7708
+ /**
7551
7709
  * Plugin that runs FIRST to register generated virtual modules in the config hook.
7552
7710
  * This prevents 504 "Outdated Optimize Dep" errors by ensuring ids are known
7553
7711
  * before Vite's optimization phase.
@@ -7591,8 +7749,8 @@ function createEarlyVirtualModulesPlugin(options) {
7591
7749
  name: "module-federation:optimize-shared-resolver",
7592
7750
  load(id) {
7593
7751
  if (id !== "module-federation:optimized-require-react") return;
7594
- const optimizedLoadSharePath = toViteOptimizedDepVirtualId(getLoadShareModulePath("react", isRolldown));
7595
- const source = JSON.stringify(optimizedLoadSharePath);
7752
+ const loadSharePath = getLoadShareModulePath("react", isRolldown);
7753
+ const source = JSON.stringify(loadSharePath);
7596
7754
  return "import * as __mfShared from " + source + ";\nexport * from " + source + ";\nexport default __mfShared.default ?? __mfShared;";
7597
7755
  },
7598
7756
  resolveId(source, importer, options) {
@@ -7711,6 +7869,10 @@ export default __mfShared.default ?? __mfShared;`
7711
7869
  }
7712
7870
  writeLocalSharedImportMap();
7713
7871
  }
7872
+ if (_command === "serve") {
7873
+ config.optimizeDeps ??= {};
7874
+ includeLinkedSharedEntries(config.optimizeDeps, shared, root, options.exposes, config.build?.outDir ?? "dist");
7875
+ }
7714
7876
  },
7715
7877
  configResolved(config) {
7716
7878
  const viteMajor = parseInt(version, 10);
@@ -8105,6 +8267,11 @@ function federation(mfUserOptions) {
8105
8267
  if (resolvedTarget === "node" && !("FEDERATION_OPTIMIZE_NO_SNAPSHOT_PLUGIN" in config.define)) config.define["FEDERATION_OPTIMIZE_NO_SNAPSHOT_PLUGIN"] = "true";
8106
8268
  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.`);
8107
8269
  },
8270
+ configResolved(config) {
8271
+ if (!hasPackageDependency("nitro")) return;
8272
+ const prematureExit = config.plugins.find((plugin) => plugin.name === "tanstack-build-exit");
8273
+ if (prematureExit) prematureExit.closeBundle = void 0;
8274
+ },
8108
8275
  configEnvironment(name, config) {
8109
8276
  if (!(config.consumer === "server" || name === "ssr" || name === "server" || config.build?.ssr === true)) return;
8110
8277
  const isAstro = hasPackageDependency("astro");
@@ -1,14 +1,38 @@
1
1
  //#region src/utils/fetchWithTimeout.ts
2
2
  const DEFAULT_SSR_FETCH_TIMEOUT_MS = 1e4;
3
+ function getFetchUrl(input) {
4
+ const raw = typeof input === "string" || input instanceof URL ? String(input) : input.url;
5
+ return new URL(raw);
6
+ }
7
+ function getSecureFetchUrl(input) {
8
+ const url = getFetchUrl(input);
9
+ const isLoopback = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]";
10
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && isLoopback)) throw new TypeError(`Refusing to fetch SSR resource over an insecure connection: ${url}`);
11
+ return url;
12
+ }
3
13
  /** Fetch with a bounded wait. Set timeoutMs to 0 to disable the timeout. */
4
- function fetchWithTimeout(input, init = {}, timeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS) {
5
- if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return fetch(input, init);
6
- const timeoutSignal = AbortSignal.timeout(timeoutMs);
7
- const signal = init.signal ? AbortSignal.any([init.signal, timeoutSignal]) : timeoutSignal;
8
- return fetch(input, {
9
- ...init,
10
- signal
11
- });
14
+ async function fetchWithTimeout(input, init = {}, timeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS) {
15
+ const inputUrl = getSecureFetchUrl(input);
16
+ const request = (target) => {
17
+ const requestInit = {
18
+ ...init,
19
+ redirect: "error"
20
+ };
21
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return fetch(target.href, requestInit);
22
+ const timeoutSignal = AbortSignal.timeout(timeoutMs);
23
+ const signal = init.signal ? AbortSignal.any([init.signal, timeoutSignal]) : timeoutSignal;
24
+ return fetch(target.href, {
25
+ ...requestInit,
26
+ signal
27
+ });
28
+ };
29
+ try {
30
+ return await request(inputUrl);
31
+ } catch (error) {
32
+ if (inputUrl.hostname !== "localhost") throw error;
33
+ inputUrl.hostname = "[::1]";
34
+ return request(inputUrl);
35
+ }
12
36
  }
13
37
  //#endregion
14
38
  //#region src/utils/ssrEntryLoader.ts
@@ -347,6 +371,9 @@ function revalidate(remoteEntryUrl) {
347
371
  }
348
372
  const tempFileCache = /* @__PURE__ */ new Map();
349
373
  const tempFilePathCache = /* @__PURE__ */ new Map();
374
+ function getSsrTransformContextKey(resolvedShared, shareScopeName) {
375
+ return JSON.stringify([shareScopeName, Object.entries(resolvedShared).sort(([left], [right]) => left.localeCompare(right))]);
376
+ }
350
377
  let ssrCacheDirPromise;
351
378
  async function getSSRCacheDir() {
352
379
  if (!ssrCacheDirPromise) ssrCacheDirPromise = (async () => {
@@ -402,11 +429,12 @@ function isVitePreloadHelperSpecifier(specifier) {
402
429
  * a remote redeploy (new manifest → new key) produces new files and bypasses
403
430
  * Node's ESM module cache instead of serving the stale build.
404
431
  */
405
- async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, versionKey = UNVERSIONED, fetchTimeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS) {
432
+ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, versionKey = UNVERSIONED, fetchTimeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS, contextKey = "default") {
406
433
  const cacheKey = JSON.stringify([
407
434
  fetchTimeoutMs,
408
435
  versionKey,
409
- url
436
+ url,
437
+ contextKey
410
438
  ]);
411
439
  if (visited.has(url)) return visited.get(url);
412
440
  const cached = tempFileCache.get(cacheKey);
@@ -435,7 +463,7 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
435
463
  while ((m = relRegex.exec(code)) !== null) if ((m[1].startsWith("./") || m[1].startsWith("../")) && !isVitePreloadHelperSpecifier(m[1])) relImports.push(new URL(m[1], base).href);
436
464
  const subMap = /* @__PURE__ */ new Map();
437
465
  await Promise.all([...new Set(relImports)].filter((u) => u.startsWith("http://") || u.startsWith("https://")).map(async (u) => {
438
- const tmpPath = await fetchEsmToTempFile(u, tmpDir, visited, pending, sharedPkgMap, versionKey, fetchTimeoutMs);
466
+ const tmpPath = await fetchEsmToTempFile(u, tmpDir, visited, pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey);
439
467
  subMap.set(u, `file://${tmpPath}`);
440
468
  }));
441
469
  code = transformSsrCode(code, base, sharedPkgMap);
@@ -452,18 +480,21 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
452
480
  });
453
481
  return promise;
454
482
  }
455
- async function fetchEsmGraphToTempFile(url, tmpDir, sharedPkgMap, versionKey = UNVERSIONED, fetchTimeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS) {
483
+ async function fetchEsmGraphToTempFile(url, tmpDir, sharedPkgMap, versionKey = UNVERSIONED, fetchTimeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS, contextKey = "default") {
456
484
  const pending = /* @__PURE__ */ new Set();
457
- const rootFile = await fetchEsmToTempFile(url, tmpDir, /* @__PURE__ */ new Map(), pending, sharedPkgMap, versionKey, fetchTimeoutMs);
485
+ const rootFile = await fetchEsmToTempFile(url, tmpDir, /* @__PURE__ */ new Map(), pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey);
458
486
  await Promise.all(pending);
459
487
  return rootFile;
460
488
  }
461
489
  async function importTempModule(filePath, versionKey) {
462
- return await import(`${filePath}?v=${encodeURIComponent(versionKey)}`);
490
+ return await import(
491
+ /* @vite-ignore */
492
+ `${filePath}?v=${encodeURIComponent(versionKey)}`
493
+ );
463
494
  }
464
495
  let warnedVmUnavailable = false;
465
496
  async function tryVmStrategy(ssrEntry, options) {
466
- const { loadViaVmStrategy, isVmStrategyAvailable } = await import("./ssrVmStrategy-By_N71Dl.js");
497
+ const { loadViaVmStrategy, isVmStrategyAvailable } = await import("./ssrVmStrategy-ChF8MB7l.js");
467
498
  if (!await isVmStrategyAvailable()) {
468
499
  if (!warnedVmUnavailable) {
469
500
  warnedVmUnavailable = true;
@@ -475,7 +506,9 @@ async function tryVmStrategy(ssrEntry, options) {
475
506
  resolvedShared: options.resolvedShared,
476
507
  shareScopeName: options.shareScopeName,
477
508
  versionKey: ssrEntry.versionKey,
478
- fetchTimeoutMs: options.fetchTimeoutMs
509
+ fetchTimeoutMs: options.fetchTimeoutMs,
510
+ cacheContext: options.cacheContext,
511
+ federationInstance: options.federationInstance
479
512
  });
480
513
  }
481
514
  async function loadSSRRemoteEntry(ssrEntry, options) {
@@ -514,7 +547,7 @@ async function loadSSRRemoteEntry(ssrEntry, options) {
514
547
  mkdirSync(cacheDir, { recursive: true });
515
548
  const sharedPkgMap = new Map(Object.entries(resolvedShared));
516
549
  try {
517
- return await importTempModule(await fetchEsmGraphToTempFile(url, cacheDir, sharedPkgMap, versionKey, options.fetchTimeoutMs), versionKey);
550
+ return await importTempModule(await fetchEsmGraphToTempFile(url, cacheDir, sharedPkgMap, versionKey, options.fetchTimeoutMs, getSsrTransformContextKey(resolvedShared, options.shareScopeName)), versionKey);
518
551
  } catch (error) {
519
552
  if (isSsrEntryHttpError(error)) throw error;
520
553
  return null;
@@ -535,15 +568,21 @@ function ssrEntryLoaderPlugin(options = {}) {
535
568
  strategy: options.strategy ?? "temp-file",
536
569
  shareScopeName: options.shareScopeName ?? "default",
537
570
  maxAgeMs: options.maxAgeMs,
538
- fetchTimeoutMs: options.fetchTimeoutMs ?? 1e4
571
+ fetchTimeoutMs: options.fetchTimeoutMs ?? 1e4,
572
+ cacheContext: {}
539
573
  };
540
574
  return {
541
575
  name: "mf-vite:ssr-entry-loader",
542
- async loadEntry({ remoteInfo }) {
576
+ async loadEntry({ remoteInfo, origin }) {
543
577
  if (!isNodeServer()) return;
544
- const ssrEntry = await getSSREntry(remoteInfo.entry, resolved.maxAgeMs, resolved.fetchTimeoutMs);
578
+ const loadOptions = origin ? {
579
+ ...resolved,
580
+ cacheContext: origin,
581
+ federationInstance: origin
582
+ } : resolved;
583
+ const ssrEntry = await getSSREntry(remoteInfo.entry, loadOptions.maxAgeMs, loadOptions.fetchTimeoutMs);
545
584
  if (!ssrEntry) return;
546
- const mod = await loadSSRRemoteEntry(ssrEntry, resolved);
585
+ const mod = await loadSSRRemoteEntry(ssrEntry, loadOptions);
547
586
  if (!mod) return;
548
587
  return mod;
549
588
  }
@@ -1,4 +1,4 @@
1
- import { n as neutralizeBrowserPreloadHelpers, o as fetchWithTimeout, t as SsrEntryHttpError } from "./ssrEntryLoader-D_sUES94.js";
1
+ import { n as neutralizeBrowserPreloadHelpers, o as fetchWithTimeout, t as SsrEntryHttpError } from "./ssrEntryLoader-DgsCQOqq.js";
2
2
  //#region src/utils/ssrVmStrategy.ts
3
3
  /**
4
4
  * vm.SourceTextModule strategy for loading remote SSR entries.
@@ -47,9 +47,13 @@ function getFederationInstances() {
47
47
  * build-time resolvedShared file map, then plain host import.
48
48
  */
49
49
  async function loadBareModule(specifier, options) {
50
- for (const instance of getFederationInstances()) {
50
+ const owner = options.federationInstance;
51
+ const instances = owner ? [owner] : getFederationInstances();
52
+ for (const instance of instances) {
51
53
  if (typeof instance?.loadShare !== "function") continue;
52
- if (!instance.options?.shared || !(specifier in instance.options.shared)) continue;
54
+ const shared = instance.options?.shared?.[specifier];
55
+ if (!shared) continue;
56
+ if (!(Array.isArray(shared.scope) ? shared.scope : [shared.scope ?? "default"]).includes(options.shareScopeName)) continue;
53
57
  try {
54
58
  const factory = await instance.loadShare(specifier);
55
59
  if (typeof factory === "function") {
@@ -80,6 +84,23 @@ function createSyntheticModule(vm, specifier, namespace) {
80
84
  }
81
85
  const httpModuleCache = /* @__PURE__ */ new Map();
82
86
  const namespaceCache = /* @__PURE__ */ new Map();
87
+ const contextIds = /* @__PURE__ */ new WeakMap();
88
+ let nextContextId = 1;
89
+ function getContextId(context) {
90
+ let id = contextIds.get(context);
91
+ if (id === void 0) {
92
+ id = nextContextId++;
93
+ contextIds.set(context, id);
94
+ }
95
+ return id;
96
+ }
97
+ function getVmCacheContextKey(options) {
98
+ return JSON.stringify([
99
+ getContextId(options.cacheContext),
100
+ options.shareScopeName,
101
+ Object.entries(options.resolvedShared).sort(([left], [right]) => left.localeCompare(right))
102
+ ]);
103
+ }
83
104
  function getBodyPreview(body) {
84
105
  return body.slice(0, 240).replace(/\s+/g, " ").trim();
85
106
  }
@@ -100,6 +121,7 @@ function resolveSpecifierUrl(specifier, referencerUrl) {
100
121
  }
101
122
  function getHttpModule(vm, url, options) {
102
123
  const cacheKey = JSON.stringify([
124
+ getVmCacheContextKey(options),
103
125
  options.fetchTimeoutMs ?? 1e4,
104
126
  options.versionKey,
105
127
  url
@@ -139,7 +161,7 @@ async function importDynamically(vm, specifier, referencingModule, options) {
139
161
  async function loadViaVmStrategy(entryUrl, options) {
140
162
  const vm = await getVmApi();
141
163
  if (!vm) return null;
142
- const cacheKey = `${options.versionKey}::${entryUrl}`;
164
+ const cacheKey = `${getVmCacheContextKey(options)}::${options.versionKey}::${entryUrl}`;
143
165
  if (!namespaceCache.has(cacheKey)) namespaceCache.set(cacheKey, (async () => {
144
166
  const entryModule = await getHttpModule(vm, entryUrl, options);
145
167
  const linker = (specifier, referencingModule) => linkModule(vm, specifier, referencingModule, options);
@@ -109,9 +109,11 @@ interface SsrEntryLoaderOptions {
109
109
  declare function ssrEntryLoaderPlugin(options?: SsrEntryLoaderOptions): {
110
110
  name: string;
111
111
  loadEntry({
112
- remoteInfo
112
+ remoteInfo,
113
+ origin
113
114
  }: {
114
115
  remoteInfo: RemoteInfo;
116
+ origin?: object;
115
117
  }): Promise<{
116
118
  init: unknown;
117
119
  get: unknown;
@@ -1,2 +1,2 @@
1
- import { i as ssrEntryLoaderPlugin, n as neutralizeBrowserPreloadHelpers, r as revalidate, t as SsrEntryHttpError } from "../ssrEntryLoader-D_sUES94.js";
1
+ import { i as ssrEntryLoaderPlugin, n as neutralizeBrowserPreloadHelpers, r as revalidate, t as SsrEntryHttpError } from "../ssrEntryLoader-DgsCQOqq.js";
2
2
  export { SsrEntryHttpError, ssrEntryLoaderPlugin as default, neutralizeBrowserPreloadHelpers, revalidate };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/vite",
3
- "version": "1.17.1",
3
+ "version": "1.18.1",
4
4
  "description": "Vite plugin for Module Federation",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -70,9 +70,9 @@
70
70
  "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
71
71
  },
72
72
  "dependencies": {
73
- "@module-federation/dts-plugin": "2.7.0",
74
- "@module-federation/runtime": "2.7.0",
75
- "@module-federation/sdk": "2.7.0"
73
+ "@module-federation/dts-plugin": "2.8.0",
74
+ "@module-federation/runtime": "2.8.0",
75
+ "@module-federation/sdk": "2.8.0"
76
76
  },
77
77
  "devDependencies": {
78
78
  "@playwright/test": "1.58.2",