@module-federation/vite 1.17.1 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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(() => {
@@ -2183,7 +2185,7 @@ 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
2187
  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/);
2188
+ 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
2189
  if (!importMatch) return code;
2188
2190
  const quote = importMatch[1];
2189
2191
  return `import * as __mfLocalShare from ${quote}${importMatch[2]}${quote};\n${code}`;
@@ -2223,10 +2225,15 @@ function generateShareModuleUnwrapCode({ source, preserveNamedExports, stopWithR
2223
2225
  return current;`;
2224
2226
  }
2225
2227
  const normalizeLocalShareModuleCode = `const __mfNormalizeShareModule = (mod) => {
2226
- ${generateShareModuleUnwrapCode({
2228
+ const normalized = (() => {
2229
+ ${generateShareModuleUnwrapCode({
2227
2230
  source: "mod",
2228
2231
  preserveNamedExports: true
2229
2232
  })}
2233
+ })();
2234
+ return normalized && Object.getPrototypeOf(normalized) === null
2235
+ ? Object.assign({}, normalized)
2236
+ : normalized;
2230
2237
  };`;
2231
2238
  function writeLoadShareModule(pkg, shareItem, command, _isRolldown) {
2232
2239
  if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, ".js");
@@ -2265,6 +2272,7 @@ function writeLoadShareModule(pkg, shareItem, command, _isRolldown) {
2265
2272
  const isWorkspaceSingleton = isWorkspacePackage && shareItem.shareConfig.singleton === true;
2266
2273
  const isDefaultShareScope = shareItem.scope === void 0 || shareItem.scope === "default" || Array.isArray(shareItem.scope) && shareItem.scope[0] === "default";
2267
2274
  const usesDeferredSingletonFallback = hasCompleteExportCoverage && (isWorkspaceSingleton || command !== "build" && isRemoteOnlyContainer() && shareItem.shareConfig.singleton === true || command === "build" && isRemoteOnlyContainer() && shareItem.shareConfig.singleton === true && !isDefaultShareScope);
2275
+ const servesRemoteSingletonFallback = command !== "build" && isRemoteOnlyContainer() && shareItem.shareConfig.singleton === true;
2268
2276
  const isConsumedByPeerSingleton = isSharedSingletonConsumedByPeer(pkg);
2269
2277
  const usesEntryInjectedRemoteFallback = hasCompleteExportCoverage && command !== "build" && !isWorkspaceSingleton && isRemoteOnlyContainer() && shareItem.shareConfig.singleton === true && getNormalizeModuleFederationOptions().hostInitInjectLocation === "entry" && isConsumedByPeerSingleton;
2270
2278
  const usesEagerWorkspaceFallback = hasCompleteExportCoverage && isWorkspaceSingleton && isConsumedByPeerSingleton;
@@ -2273,11 +2281,11 @@ function writeLoadShareModule(pkg, shareItem, command, _isRolldown) {
2273
2281
  let initBlock = "";
2274
2282
  if (usesDeferredTreeShakingFallback) {
2275
2283
  importLine = `${getRuntimeInitPromiseBootstrapCode()}\n ${importLine}`;
2276
- exportLine = generateLazyWorkspaceSingletonExports(namedExports, lazyLocalFallbackSource, cacheDescriptor, cacheOwner, treeShakingConsumer);
2284
+ exportLine = generateLazyWorkspaceSingletonExports(namedExports, lazyLocalFallbackSource, cacheDescriptor, cacheOwner, treeShakingConsumer, command !== "build" && (isWorkspaceSingleton || isWorkspacePackage || servesRemoteSingletonFallback));
2277
2285
  } else if (usesEagerWorkspaceFallback || usesEntryInjectedRemoteFallback) exportLine = generateEagerWorkspaceSingletonExports(namedExports, lazyLocalFallbackSource, cacheDescriptor, cacheOwner, treeShakingConsumer);
2278
2286
  else if (usesDeferredSingletonFallback) {
2279
2287
  importLine = `${getRuntimeInitPromiseBootstrapCode()}\n ${importLine}`;
2280
- exportLine = generateLazyWorkspaceSingletonExports(namedExports, lazyLocalFallbackSource, cacheDescriptor, cacheOwner, treeShakingConsumer);
2288
+ exportLine = generateLazyWorkspaceSingletonExports(namedExports, lazyLocalFallbackSource, cacheDescriptor, cacheOwner, treeShakingConsumer, command !== "build" && (isWorkspaceSingleton || isWorkspacePackage || servesRemoteSingletonFallback));
2281
2289
  } else if (detectedNamedExports === void 0) {
2282
2290
  exportLine = `const __mfDefaultExport = (() => {
2283
2291
  ${generateShareModuleUnwrapCode({
@@ -2339,7 +2347,7 @@ function writeLoadShareModule(pkg, shareItem, command, _isRolldown) {
2339
2347
  initBlock = `exportModule = __mfNormalizeShareModule(__mfLocalShare);
2340
2348
  __mfWriteSharedCache(__mfModuleCache.share, ${cacheDescriptor}, exportModule, ${cacheOwner});`;
2341
2349
  }
2342
- const prebuildImportLine = usesDeferredSingletonFallback || usesDeferredTreeShakingFallback ? "" : `import * as __mfLocalShare from ${escapeGeneratedStringLiteral(detectedNamedExports === void 0 ? coherentLocalSource : skipServePrebuildWarmup ? devImportSource : sharedImportSource)};`;
2350
+ const prebuildImportLine = 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
2351
  const devDynamicImportLine = isWorkspacePackage ? "" : usesDeferredSingletonFallback || usesDeferredTreeShakingFallback ? "" : command !== "build" && !skipServePrebuildWarmup ? `;() => import(${escapeGeneratedStringLiteral(devImportSource)}).catch(() => {});` : "";
2344
2352
  const moduleBody = usesDeferredSingletonFallback || usesDeferredTreeShakingFallback ? `
2345
2353
  ${prebuildImportLine}
@@ -2454,10 +2462,12 @@ function generateLocalSharedImportMap() {
2454
2462
  ? (res?.default ?? res)
2455
2463
  : {...res}
2456
2464
  // All npm packages pre-built by vite will be converted to esm
2457
- Object.defineProperty(exportModule, "__esModule", {
2458
- value: true,
2459
- enumerable: false
2460
- })
2465
+ if (exportModule.__esModule !== true) {
2466
+ Object.defineProperty(exportModule, "__esModule", {
2467
+ value: true,
2468
+ enumerable: false
2469
+ })
2470
+ }
2461
2471
  return function () {
2462
2472
  return exportModule
2463
2473
  }
@@ -2550,7 +2560,17 @@ function orderSharedDependenciesFirst(sharedPackages) {
2550
2560
  const sharedDependency = sharedKeyByPackageName.get(dependency);
2551
2561
  if (sharedDependency) visit(sharedDependency);
2552
2562
  });
2553
- if (pkg === packageName) (subpathKeysByPackageName.get(packageName) || []).forEach(visit);
2563
+ if (pkg === packageName) {
2564
+ const subpaths = subpathKeysByPackageName.get(packageName) || [];
2565
+ if (packageName === "react" || packageName === "react-dom") {
2566
+ visiting.delete(pkg);
2567
+ visited.add(pkg);
2568
+ ordered.push(pkg);
2569
+ subpaths.forEach(visit);
2570
+ return;
2571
+ }
2572
+ subpaths.forEach(visit);
2573
+ }
2554
2574
  visiting.delete(pkg);
2555
2575
  visited.add(pkg);
2556
2576
  ordered.push(pkg);
@@ -2572,7 +2592,7 @@ function generateSharedCacheSeedItem(pkg, shareItem, importPath) {
2572
2592
  ${normalizeRuntimeShareCode}
2573
2593
  const normalizedModule = __mfNormalizeRuntimeShare(mod);
2574
2594
  const exportModule = normalizedModule === mod ? {...mod} : normalizedModule;
2575
- Object.defineProperty(exportModule, "__esModule", {
2595
+ if (exportModule.__esModule !== true) Object.defineProperty(exportModule, "__esModule", {
2576
2596
  value: true,
2577
2597
  enumerable: false
2578
2598
  });
@@ -2789,7 +2809,7 @@ function generateRuntimeSharedCacheSeedCode(shareStrategy) {
2789
2809
  ${normalizeRuntimeShareCode}
2790
2810
  const normalizedModule = __mfNormalizeRuntimeShare(resolved);
2791
2811
  const exportModule = normalizedModule === resolved ? {...resolved} : normalizedModule;
2792
- Object.defineProperty(exportModule, "__esModule", {
2812
+ if (exportModule.__esModule !== true) Object.defineProperty(exportModule, "__esModule", {
2793
2813
  value: true,
2794
2814
  enumerable: false
2795
2815
  });
@@ -3047,6 +3067,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3047
3067
  const needsSharedProviderSelectionHelper = Object.keys(options.shared ?? {}).length > 0;
3048
3068
  const hasTreeShakingShared = Object.values(options.shared ?? {}).some((share) => !!share?.shareConfig.treeShaking);
3049
3069
  const hasEagerShared = Object.values(options.shared ?? {}).some((share) => share?.shareConfig.eager === true && share.shareConfig.import !== false);
3070
+ const hasMultipleShareScopes = Array.isArray(options.shareScope);
3050
3071
  const runtimeImports = [
3051
3072
  "init as runtimeInit",
3052
3073
  "loadRemote",
@@ -3065,7 +3086,19 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3065
3086
  serializeRuntimeOptions(p[1])
3066
3087
  ];
3067
3088
  });
3068
- const initializeSharingCode = `try {
3089
+ const initializeSharingCode = hasMultipleShareScopes ? `for (const shareScopeName of shareScopeNamesToInitialize) {
3090
+ try {
3091
+ await retrySharedInit(async () => {
3092
+ await Promise.all(await initRes.initializeSharing(shareScopeName, {
3093
+ strategy: '${options.shareStrategy}',
3094
+ from: "build",
3095
+ initScope
3096
+ }));
3097
+ });
3098
+ } catch (e) {
3099
+ console.error('[Module Federation]', e)
3100
+ }
3101
+ }` : `try {
3069
3102
  await retrySharedInit(async () => {
3070
3103
  await Promise.all(await initRes.initializeSharing('${options.shareScope}', {
3071
3104
  strategy: '${options.shareStrategy}',
@@ -3092,7 +3125,8 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3092
3125
  ${command === "build" ? getRuntimeInitResolveBootstrapCode() : getRuntimeInitBootstrapCode() + "\n const { initResolve } = globalThis[globalKey];"}
3093
3126
  ${getRuntimeModuleCacheBootstrapCode()}
3094
3127
  const initTokens = {}
3095
- const shareScopeName = ${JSON.stringify(options.shareScope)}
3128
+ const shareScopeNames = Array.isArray(${JSON.stringify(options.shareScope)}) ? ${JSON.stringify(options.shareScope)} : [${JSON.stringify(options.shareScope)}]
3129
+ const shareScopeName = ${JSON.stringify(hasMultipleShareScopes ? options.shareScope[0] : options.shareScope)}
3096
3130
  const mfName = ${JSON.stringify(options.name)}
3097
3131
  let localSharedImportMapPromise
3098
3132
  let exposesMapPromise
@@ -3139,31 +3173,66 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3139
3173
 
3140
3174
  async function init(shared = {}, initScope = []) {
3141
3175
  ${sharedCacheHelperCode}
3176
+ const getShareScope = (scopeName) => ${hasMultipleShareScopes} ? (shared?.[scopeName] || {}) : shared;
3177
+ const getShareScopeNames = (share) => {
3178
+ const configuredScopes = Array.isArray(share?.scope) ? share.scope : [share?.scope || shareScopeName];
3179
+ if (!${hasMultipleShareScopes}) return configuredScopes;
3180
+ return [...new Set([...configuredScopes, ...shareScopeNames])];
3181
+ };
3182
+ const getShareScopeName = (pkg, share) => {
3183
+ for (const scopeName of getShareScopeNames(share)) {
3184
+ if (getShareScope(scopeName)?.[pkg]) return scopeName;
3185
+ }
3186
+ return shareScopeName;
3187
+ };
3188
+ const getShareVersions = (pkg, share) => {
3189
+ for (const scopeName of getShareScopeNames(share)) {
3190
+ const versions = getShareScope(scopeName)?.[pkg];
3191
+ if (versions) return versions;
3192
+ }
3193
+ return getShareScope(shareScopeName)?.[pkg];
3194
+ };
3142
3195
  const federationInstances = globalThis.__FEDERATION__?.__INSTANCES__ || [];
3143
3196
  const initRootName = initScope.find((token) => token?.from)?.from;
3144
3197
  const scopeRoot = federationInstances.find((instance) =>
3145
3198
  instance?.options?.name === initRootName &&
3146
- instance?.shareScopeMap?.['${options.shareScope}'] === shared
3199
+ ${hasMultipleShareScopes ? "shareScopeNames.some((scopeName) => instance?.shareScopeMap?.[scopeName] === getShareScope(scopeName))" : `instance?.shareScopeMap?.['${options.shareScope}'] === shared`}
3147
3200
  ) || federationInstances.find((instance) =>
3148
3201
  instance?.options?.name !== mfName &&
3149
- instance?.shareScopeMap?.['${options.shareScope}'] === shared
3202
+ ${hasMultipleShareScopes ? "shareScopeNames.some((scopeName) => instance?.shareScopeMap?.[scopeName] === getShareScope(scopeName))" : `instance?.shareScopeMap?.['${options.shareScope}'] === shared`}
3150
3203
  );
3151
3204
  const initialShared = Object.create(null);
3152
- for (const [pkg, versions] of Object.entries(shared)) {
3205
+ ${hasMultipleShareScopes ? `for (const scopeName of shareScopeNames) {
3206
+ for (const [pkg, versions] of Object.entries(getShareScope(scopeName))) {
3207
+ if (initialShared[pkg]) continue;
3208
+ const initialVersions = initialShared[pkg] = Object.create(null);
3209
+ for (const [version, provider] of Object.entries(versions)) {
3210
+ initialVersions[version] = Object.assign({}, provider);
3211
+ }
3212
+ }
3213
+ }` : `for (const [pkg, versions] of Object.entries(shared)) {
3153
3214
  const initialVersions = initialShared[pkg] = Object.create(null);
3154
3215
  for (const [version, provider] of Object.entries(versions)) {
3155
3216
  // Runtime registration mutates provider records in-place, notably their origin.
3156
3217
  // Preserve the parent-visible provider and its original provenance.
3157
3218
  initialVersions[version] = Object.assign({}, provider);
3158
3219
  }
3159
- }
3220
+ }`}
3160
3221
  const {usedShared, usedRemotes} = await getLocalSharedImportMap()
3161
3222
  // handling circular init calls before an external provider can re-enter this container
3162
- var initToken = initTokens[shareScopeName];
3223
+ ${hasMultipleShareScopes ? `const shareScopeNamesToInitialize = [];
3224
+ for (const shareScopeName of shareScopeNames) {
3225
+ let initToken = initTokens[shareScopeName];
3226
+ if (!initToken) initToken = initTokens[shareScopeName] = { from: mfName };
3227
+ if (initScope.indexOf(initToken) >= 0) continue;
3228
+ initScope.push(initToken);
3229
+ shareScopeNamesToInitialize.push(shareScopeName);
3230
+ }
3231
+ if (shareScopeNamesToInitialize.length === 0) return;` : `var initToken = initTokens[shareScopeName];
3163
3232
  if (!initToken)
3164
3233
  initToken = initTokens[shareScopeName] = { from: mfName };
3165
3234
  if (initScope.indexOf(initToken) >= 0) return;
3166
- initScope.push(initToken);
3235
+ initScope.push(initToken);`}
3167
3236
  ${normalizeRuntimeShareCode}
3168
3237
  const __browserPlugins = [${pluginImportNames.filter((item) => !isSsrOnlyPlugin(item[1])).map((item) => `${item[0]}(${item[2]})`).join(", ")}];
3169
3238
  const __ssrPlugins = typeof globalThis.window === 'undefined'
@@ -3184,7 +3253,10 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3184
3253
  plugins: [__mfSharePinLifecyclePlugin(), ${hasTreeShakingShared ? "__mfTreeShakingSnapshotPlugin()," : ""} ...__browserPlugins, ...__ssrPlugins],
3185
3254
  ${options.shareStrategy ? `shareStrategy: '${options.shareStrategy}'` : ""}
3186
3255
  });
3187
- initRes.initShareScopeMap('${options.shareScope}', shared);
3256
+ ${hasMultipleShareScopes ? `for (const shareScopeName of shareScopeNamesToInitialize) {
3257
+ const scopeShare = getShareScope(shareScopeName);
3258
+ initRes.initShareScopeMap(shareScopeName, scopeShare);
3259
+ }` : `initRes.initShareScopeMap('${options.shareScope}', shared);`}
3188
3260
  function __mfSharePinLifecyclePlugin() {
3189
3261
  return {
3190
3262
  name: "vite-share-pin-lifecycle-plugin",
@@ -3232,7 +3304,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3232
3304
  if (!versionMap || versionMap[version] !== currentProvider) return undefined;
3233
3305
  const pinnedProvider = Object.assign({}, provider, {
3234
3306
  version: provider.version ?? version,
3235
- scope: provider.scope ?? currentProvider?.scope ?? ['${options.shareScope}'],
3307
+ scope: provider.scope ?? currentProvider?.scope ?? ${JSON.stringify(hasMultipleShareScopes ? options.shareScope : [options.shareScope])},
3236
3308
  strategy: 'loaded-first'
3237
3309
  });
3238
3310
  const providerFrom = provider.from;
@@ -3440,7 +3512,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3440
3512
  usedShare.scope
3441
3513
  );
3442
3514
  if (__mfReadSharedCache(__mfModuleCache.share, usedCacheDescriptor) !== undefined) return;
3443
- const liveVersionMap = shared[pkg];
3515
+ const liveVersionMap = ${hasMultipleShareScopes ? "getShareVersions(pkg, usedShare)" : "shared[pkg]"};
3444
3516
  const liveProvider = liveVersionMap?.[version];
3445
3517
  if (providerEntry.registered && !__mfMatchesSharedProvider(liveProvider, provider)) return;
3446
3518
  let loadedShare;
@@ -3526,8 +3598,8 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3526
3598
  const resolvedExternalProvider = __mfResolveExternalSharedProvider(
3527
3599
  federationInstances,
3528
3600
  scopeRoot,
3529
- shared,
3530
- '${options.shareScope}',
3601
+ ${hasMultipleShareScopes ? "getShareScope(getShareScopeName(pkg, usedShare))" : "shared"},
3602
+ ${hasMultipleShareScopes ? "getShareScopeName(pkg, usedShare)" : `'${options.shareScope}'`},
3531
3603
  pkg,
3532
3604
  providerEntry,
3533
3605
  selectedExternalProvider,
@@ -3549,7 +3621,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3549
3621
  if (cachedShare !== undefined && cachedShareOwner !== mfName) return;
3550
3622
  // Registration can replace an unloaded same-version root provider in-place.
3551
3623
  // Pin the chosen provider while loadShare() runs its implicit registration.
3552
- const liveVersionMap = shared[pkg];
3624
+ const liveVersionMap = ${hasMultipleShareScopes ? "getShareVersions(pkg, usedShare)" : "shared[pkg]"};
3553
3625
  const liveProvider = liveVersionMap?.[version];
3554
3626
  if (
3555
3627
  providerEntry.registered &&
@@ -3631,7 +3703,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3631
3703
  await __mfBridgeExternalSharedProvider(
3632
3704
  pkg,
3633
3705
  usedShare,
3634
- shared[pkg],
3706
+ ${hasMultipleShareScopes ? "getShareVersions(pkg, usedShare)" : "shared[pkg]"},
3635
3707
  initialShared[pkg],
3636
3708
  undefined
3637
3709
  );
@@ -3641,9 +3713,10 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3641
3713
  const globalVersionsByPackage = Object.create(null);
3642
3714
  if (allInstances) {
3643
3715
  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)) {
3716
+ for (const scopeName of shareScopeNames) {
3717
+ const scopeShare = scopes?.[scopeName];
3718
+ if (!scopeShare) continue;
3719
+ for (const [pkg, versionMap] of Object.entries(scopeShare)) {
3647
3720
  const usedShare = usedShared?.[pkg];
3648
3721
  const passedVersions = initialShared[pkg];
3649
3722
  const bridgeSelection = bridgeSelections.get(pkg);
@@ -3664,6 +3737,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3664
3737
  if (provider === usedShare || (usedShare.from && provider.from === usedShare.from)) continue;
3665
3738
  if (globalVersions[version] === undefined) globalVersions[version] = provider;
3666
3739
  }
3740
+ }
3667
3741
  }
3668
3742
  }
3669
3743
  }
@@ -3687,7 +3761,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
3687
3761
  : __mfReadSharedCache(__mfModuleCache.share, cacheDescriptor);
3688
3762
  if (share.shareConfig?.import !== false || cachedShare !== undefined) return;
3689
3763
  ${normalizeRuntimeShareCode}
3690
- const versionMap = shared?.[pkg];
3764
+ const versionMap = ${hasMultipleShareScopes ? "getShareVersions(pkg, share)" : "shared?.[pkg]"};
3691
3765
  const provider = __mfSelectSharedProvider(
3692
3766
  versionMap,
3693
3767
  pkg,
@@ -3861,7 +3935,7 @@ function getRemoteVirtualModule(remote, command, enableSsrInit = false, consumer
3861
3935
  const { shareStrategy } = getNormalizeModuleFederationOptions();
3862
3936
  const cacheKey = `${remote}__${command}__${shareStrategy}__${consumer}__${enableSsrInit ? "ssr-init" : "no-ssr-init"}`;
3863
3937
  if (!cacheRemoteMap[cacheKey]) {
3864
- cacheRemoteMap[cacheKey] = new VirtualModule(remote, LOAD_REMOTE_TAG, ".js");
3938
+ cacheRemoteMap[cacheKey] = new VirtualModule(consumer === "unified" ? remote : `${remote}__mf_consumer__${consumer}`, LOAD_REMOTE_TAG, ".js");
3865
3939
  cacheRemoteMap[cacheKey].writeSync(generateRemotes(remote, command, enableSsrInit, consumer));
3866
3940
  }
3867
3941
  return cacheRemoteMap[cacheKey];
@@ -3904,9 +3978,24 @@ function shouldIncludeDeferredProxy(initMode, consumer, eagerLoadClientRemote, d
3904
3978
  function getRemoteModuleRuntimeHelpers() {
3905
3979
  return `
3906
3980
  function __mfUnwrapRemoteDefault(mod) {
3907
- if (mod == null) return mod;
3908
- if (mod.__esModule && mod.default != null) return mod.default;
3909
- return mod.default ?? mod;
3981
+ let value = mod;
3982
+ // A federated expose can pass through more than one ESM/CJS namespace
3983
+ // wrapper (notably with React/Preact lazy imports). Keep unwrapping
3984
+ // explicit default namespaces until the actual component is reached.
3985
+ const seen = new Set();
3986
+ while (value != null && typeof value === "object" && !seen.has(value)) {
3987
+ seen.add(value);
3988
+ if (value.__esModule && value.default != null) {
3989
+ value = value.default;
3990
+ continue;
3991
+ }
3992
+ if (!value.__esModule && value.default != null) {
3993
+ value = value.default;
3994
+ continue;
3995
+ }
3996
+ break;
3997
+ }
3998
+ return value;
3910
3999
  }
3911
4000
  let __mfDefaultExport;
3912
4001
  function __mfSyncDefaultExport() {
@@ -4210,9 +4299,10 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry", forceClien
4210
4299
  const blockEnd = body.lastIndexOf("}");
4211
4300
  if (blockStart === -1 || blockEnd <= blockStart) return scriptTag;
4212
4301
  return `<script>${body.slice(0, blockStart + 1) + `
4302
+ const __mfCurrentScript = document.currentScript;
4213
4303
  (async () => {
4214
4304
  await import(${JSON.stringify(initPath)}).then(({ initHost }) => initHost());
4215
- ` + body.slice(blockStart + 1, blockEnd) + `
4305
+ ` + body.slice(blockStart + 1, blockEnd).replaceAll("document.currentScript", "__mfCurrentScript") + `
4216
4306
  })();
4217
4307
  ` + body.slice(blockEnd)}<\/script>`;
4218
4308
  });
@@ -5521,15 +5611,25 @@ function generateRemoteEntrySSR(options) {
5521
5611
  const initToken = { from: ${JSON.stringify(options.name)} };
5522
5612
  if (initScope.indexOf(initToken) >= 0) return;
5523
5613
  initScope.push(initToken);
5524
- initRes.initShareScopeMap(${JSON.stringify(options.shareScope)}, shared);
5614
+ const shareScopeNames = Array.isArray(${JSON.stringify(options.shareScope)})
5615
+ ? ${JSON.stringify(options.shareScope)}
5616
+ : [${JSON.stringify(options.shareScope)}];
5525
5617
  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
- );
5618
+ for (const scopeName of shareScopeNames) {
5619
+ try {
5620
+ const scopeShare = Array.isArray(${JSON.stringify(options.shareScope)}) ? (shared?.[scopeName] || {}) : shared;
5621
+ initRes.initShareScopeMap(scopeName, scopeShare);
5622
+ await Promise.all(
5623
+ await initRes.initializeSharing(scopeName, {
5624
+ strategy: ${JSON.stringify(options.shareStrategy ?? "version-first")},
5625
+ from: 'build',
5626
+ initScope,
5627
+ })
5628
+ );
5629
+ } catch (e) {
5630
+ console.error('[Module Federation SSR]', e);
5631
+ }
5632
+ }
5533
5633
  } catch (e) {
5534
5634
  console.error('[Module Federation SSR]', e);
5535
5635
  }
@@ -6114,7 +6214,7 @@ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposes
6114
6214
  },
6115
6215
  async buildStart() {
6116
6216
  await refreshExposeRemoteDependencies(this);
6117
- if (_command !== "build") return;
6217
+ if (_command !== "build" || hasPackageDependency("@tanstack/react-start", root)) return;
6118
6218
  for (const expose of Object.values(options.exposes)) {
6119
6219
  const resolved = await this.resolve(expose.import);
6120
6220
  if (resolved) this.emitFile({
@@ -6966,15 +7066,22 @@ function isPathWithinAllowedDirectories(filePath, allowedDirectories) {
6966
7066
  return allowedDirectories.some((directory) => isPathWithinDirectory(filePath, directory));
6967
7067
  }
6968
7068
  function isSafeRunnerFetchModuleId(id, config) {
6969
- if (typeof id !== "string" || !id || id.includes("\0")) return false;
6970
- const decoded = decodeViteId(id).replace(/^\0+/, "");
6971
- if (!decoded || decoded.startsWith("virtual:")) return !!decoded;
6972
- if (/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(decoded) || decoded.startsWith("//")) return false;
7069
+ if (typeof id !== "string" || !id) return false;
7070
+ const rawDecoded = decodeViteId(id);
7071
+ const decoded = rawDecoded.replace(/^\0+/, "");
7072
+ if (!decoded || rawDecoded.startsWith("\0") || decoded.startsWith("virtual:")) return !!decoded;
7073
+ if (decoded.startsWith("file://")) try {
7074
+ const filePath = decodeURIComponent(new URL(decoded).pathname);
7075
+ return path$1.isAbsolute(filePath) && isPathWithinAllowedDirectories(filePath, getRunnerAllowedDirectories(config));
7076
+ } catch {
7077
+ return false;
7078
+ }
7079
+ if (/^(?:https?|data|blob|javascript):/i.test(decoded) || decoded.startsWith("//")) return false;
6973
7080
  const cleanId = decodeRunnerFilePath(stripQueryAndHash(decoded));
6974
7081
  if (!cleanId || hasRelativeTraversal(cleanId)) return false;
6975
7082
  const allowedDirectories = getRunnerAllowedDirectories(config);
6976
7083
  if (cleanId.startsWith(VITE_FS_PREFIX)) {
6977
- const fsPath = cleanId.slice(5);
7084
+ const fsPath = `/${cleanId.slice(5)}`;
6978
7085
  return path$1.isAbsolute(fsPath) && isPathWithinAllowedDirectories(fsPath, allowedDirectories);
6979
7086
  }
6980
7087
  if (path$1.isAbsolute(cleanId)) {
@@ -7591,8 +7698,8 @@ function createEarlyVirtualModulesPlugin(options) {
7591
7698
  name: "module-federation:optimize-shared-resolver",
7592
7699
  load(id) {
7593
7700
  if (id !== "module-federation:optimized-require-react") return;
7594
- const optimizedLoadSharePath = toViteOptimizedDepVirtualId(getLoadShareModulePath("react", isRolldown));
7595
- const source = JSON.stringify(optimizedLoadSharePath);
7701
+ const loadSharePath = getLoadShareModulePath("react", isRolldown);
7702
+ const source = JSON.stringify(loadSharePath);
7596
7703
  return "import * as __mfShared from " + source + ";\nexport * from " + source + ";\nexport default __mfShared.default ?? __mfShared;";
7597
7704
  },
7598
7705
  resolveId(source, importer, options) {
@@ -8105,6 +8212,11 @@ function federation(mfUserOptions) {
8105
8212
  if (resolvedTarget === "node" && !("FEDERATION_OPTIMIZE_NO_SNAPSHOT_PLUGIN" in config.define)) config.define["FEDERATION_OPTIMIZE_NO_SNAPSHOT_PLUGIN"] = "true";
8106
8213
  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
8214
  },
8215
+ configResolved(config) {
8216
+ if (!hasPackageDependency("nitro")) return;
8217
+ const prematureExit = config.plugins.find((plugin) => plugin.name === "tanstack-build-exit");
8218
+ if (prematureExit) prematureExit.closeBundle = void 0;
8219
+ },
8108
8220
  configEnvironment(name, config) {
8109
8221
  if (!(config.consumer === "server" || name === "ssr" || name === "server" || config.build?.ssr === true)) return;
8110
8222
  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.0",
4
4
  "description": "Vite plugin for Module Federation",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",