@module-federation/vite 1.23.1 → 1.23.3

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.
@@ -363,4 +363,4 @@ const moduleFederationConsole = {
363
363
  const mfWarn = moduleFederationConsole.warn;
364
364
  const mfError = moduleFederationConsole.error;
365
365
  //#endregion
366
- export { CodeRewriter as a, normalizePathForImport as c, createCodePositionMap as i, rebaseImport as l, mfError as n, createSourceMap as o, mfWarn as r, EXTERNAL_URL_RE as s, createModuleFederationError as t, resolveHashPlaceholderFileName as u };
366
+ export { CodeRewriter as a, isAbsoluteUrl as c, resolveHashPlaceholderFileName as d, createCodePositionMap as i, normalizePathForImport as l, mfError as n, createSourceMap as o, mfWarn as r, EXTERNAL_URL_RE as s, createModuleFederationError as t, rebaseImport as u };
@@ -0,0 +1,303 @@
1
+ import { moduleFederationPlugin } from "@module-federation/sdk";
2
+ import { ShareStrategy } from "@module-federation/runtime/types";
3
+ //#region src/utils/normalizeModuleFederationOptions.d.ts
4
+ interface RemoteObjectConfig {
5
+ type?: string;
6
+ name: string;
7
+ internalName?: string;
8
+ entry: string;
9
+ entryGlobalName?: string;
10
+ shareScope?: string | string[];
11
+ }
12
+ interface TreeShakingConfig {
13
+ mode: 'server-calc' | 'runtime-infer';
14
+ usedExports?: string[];
15
+ }
16
+ interface PluginManifestOptions {
17
+ filePath?: string;
18
+ disableAssetsAnalyze?: boolean;
19
+ fileName?: string;
20
+ additionalData?: (options: {
21
+ stats: Record<string, unknown>;
22
+ manifest?: Record<string, unknown>;
23
+ pluginOptions: Record<string, unknown>;
24
+ compiler?: unknown;
25
+ compilation?: unknown;
26
+ bundler: 'vite';
27
+ }) => Promise<Record<string, unknown> | void> | Record<string, unknown> | void;
28
+ }
29
+ type ModuleFederationOptions = {
30
+ exposes?: Record<string, string | {
31
+ import: string;
32
+ }> | undefined;
33
+ filename?: string;
34
+ /**
35
+ * Exact file name of the SSR remote entry emitted next to `filename`.
36
+ * Defaults to `<filename base>.ssr<ext>` so it never clobbers the browser
37
+ * entry when both builds share an output directory. `[hash]` is not supported.
38
+ */
39
+ ssrFilename?: string;
40
+ library?: any;
41
+ name: string;
42
+ remotes?: Record<string, string | RemoteObjectConfig> | undefined;
43
+ runtime?: any;
44
+ shareScope?: string | string[];
45
+ /**
46
+ * Override the public path used for remote entries
47
+ * Defaults to Vite's base config or "auto" if base is empty
48
+ */
49
+ publicPath?: string;
50
+ /**
51
+ * Controls whether all CSS assets from the bundle should be added to every exposed module.
52
+ * When false (default), the plugin will not process any CSS assets.
53
+ * When true, all CSS assets are bundled into every exposed module.
54
+ */
55
+ bundleAllCSS?: boolean;
56
+ /** Directory reserved for deploy-service generated secondary shared artifacts. */
57
+ treeShakingDir?: string;
58
+ /** Whether inferred usedExports metadata is injected into generated runtime records. */
59
+ injectTreeShakingUsedExports?: boolean;
60
+ treeShakingSharedPlugins?: string[];
61
+ treeShakingSharedExcludePlugins?: string[];
62
+ shared?: string[] | Record<string, string | {
63
+ name?: string;
64
+ version?: string;
65
+ shareScope?: string;
66
+ request?: moduleFederationPlugin.SharedConfig['request'];
67
+ shareKey?: moduleFederationPlugin.SharedConfig['shareKey'];
68
+ singleton?: boolean;
69
+ eager?: boolean;
70
+ requiredVersion?: moduleFederationPlugin.SharedConfig['requiredVersion'];
71
+ strictVersion?: boolean;
72
+ allowNodeModulesSuffixMatch?: boolean;
73
+ /** Suppress the missing local dependency warning for `import: false` shares. */
74
+ suppressMissingImportWarning?: boolean;
75
+ treeShaking?: TreeShakingConfig;
76
+ import?: moduleFederationPlugin.SharedConfig['import'];
77
+ }> | undefined;
78
+ runtimePlugins?: Array<string | [string, Record<string, unknown>]>;
79
+ getPublicPath?: string;
80
+ implementation?: string;
81
+ manifest?: PluginManifestOptions | boolean;
82
+ dev?: boolean | PluginDevOptions;
83
+ dts?: boolean | PluginDtsOptions;
84
+ shareStrategy?: ShareStrategy;
85
+ ignoreOrigin?: boolean;
86
+ virtualModuleDir?: string;
87
+ hostInitInjectLocation?: HostInitInjectLocationOptions;
88
+ /**
89
+ * Timeout for parsing modules in seconds.
90
+ * Defaults to 10 seconds.
91
+ */
92
+ moduleParseTimeout?: number;
93
+ /**
94
+ * Idle timeout for parsing modules in seconds. When set, the timeout resets
95
+ * on every parsed module and only fires when there has been no module activity
96
+ * for the configured duration. Prefer this over `moduleParseTimeout` for large
97
+ * codebases where the total build time may exceed the fixed timeout.
98
+ */
99
+ moduleParseIdleTimeout?: number;
100
+ /**
101
+ * Allows generate additional remoteEntry file for "var" host environment
102
+ */
103
+ varFilename?: string;
104
+ /**
105
+ * Target environment for the build to enable effective tree-shaking.
106
+ *
107
+ * @see https://module-federation.io/configure/experiments#target
108
+ * @default 'web' (or 'node' if build.ssr is enabled)
109
+ */
110
+ target?: 'web' | 'node';
111
+ /**
112
+ * Removes remote-consumption support from the federation runtime.
113
+ * Only enable this for builds that never load remotes.
114
+ *
115
+ * @default false
116
+ */
117
+ disableRemote?: boolean;
118
+ /**
119
+ * Removes shared-dependency support from the federation runtime.
120
+ * Only enable this when the build has no shared dependencies.
121
+ *
122
+ * @default false
123
+ */
124
+ disableShared?: boolean;
125
+ /**
126
+ * Removes snapshot support, including manifest-based remotes, preload,
127
+ * dynamic type hints, HMR, and devtools integration.
128
+ *
129
+ * @default false (true for Node/SSR builds)
130
+ */
131
+ disableSnapshot?: boolean;
132
+ /**
133
+ * Additional packages to mark as external in the SSR remote entry build.
134
+ * Shared packages and MF runtime packages are external by default. Use this to
135
+ * add any other Node-only packages that should not be bundled into the SSR entry.
136
+ * To emit a self-contained SSR entry (loadable by hosts that cannot resolve the
137
+ * MF runtime from Node, such as the `@module-federation/sdk` reference loader),
138
+ * opt the MF runtime packages back in through Vite's `ssr.noExternal`.
139
+ */
140
+ ssrExternals?: string[];
141
+ /**
142
+ * Options for the auto-injected `@module-federation/vite/ssrEntryLoader`.
143
+ * When omitted, the loader uses the `'temp-file'` strategy. Set
144
+ * `strategy: 'vm'` to opt into `vm.SourceTextModule` evaluation while keeping
145
+ * the computed `resolvedShared` map. Optional `maxAgeMs`, `fetchTimeoutMs`,
146
+ * and `fetchMaxBytes` are forwarded to the loader when set.
147
+ */
148
+ ssrEntryLoader?: SsrEntryLoaderConfig;
149
+ /**
150
+ * Experimental Module Federation capabilities.
151
+ *
152
+ * @see https://module-federation.io/configure/experiments
153
+ */
154
+ experiments?: PluginExperimentsOptions;
155
+ };
156
+ interface PluginExperimentsOptions {
157
+ /**
158
+ * Treat `@module-federation/runtime-core` as an external that reads
159
+ * `globalThis._FEDERATION_RUNTIME_CORE` at runtime. Pair with a host that
160
+ * sets `provideExternalRuntime: true`.
161
+ */
162
+ externalRuntime?: boolean;
163
+ /**
164
+ * Injects a local runtime plugin that publishes `runtime-core` on
165
+ * `globalThis._FEDERATION_RUNTIME_CORE`. Set it on exactly one container
166
+ * per page; that container may also `exposes`.
167
+ */
168
+ provideExternalRuntime?: boolean;
169
+ /** Generate the React SSR/hydration island capability for eligible exposes. */
170
+ ssrMode?: 'ISLAND';
171
+ /**
172
+ * The host provides every shared dependency; remotes bring none. Shared keys
173
+ * are resolved by a native browser import map instead of the Module
174
+ * Federation runtime: the host emits one entry chunk per shared key and
175
+ * injects `<script type="importmap">`, remotes keep shared imports as bare
176
+ * specifiers and emit one fixed-name entry per expose plus a standard
177
+ * `mf-manifest.json` (without `remoteEntry`) the host maps from.
178
+ *
179
+ * Shared modules become ordinary static ES imports, so there is no
180
+ * `loadShare` glue and no `eager` is needed for code that reads a shared
181
+ * value at module-evaluation time. There is no version negotiation and no
182
+ * runtime API: `requiredVersion`, `strictVersion`, `shareStrategy`,
183
+ * `runtimePlugins` and `manifest` are ignored.
184
+ *
185
+ * @see https://github.com/module-federation/vite/issues/1381
186
+ */
187
+ hostProvidesAllShared?: boolean;
188
+ }
189
+ type SsrEntryLoaderStrategy = 'temp-file' | 'vm';
190
+ type SsrEntryLoaderConfig = {
191
+ /**
192
+ * How the auto-injected `@module-federation/vite/ssrEntryLoader` evaluates
193
+ * remote SSR entries.
194
+ *
195
+ * - `'temp-file'` (default when omitted): fetch the ESM graph, rewrite
196
+ * specifiers, write temp files and `import()` them. Works on stock Node;
197
+ * shared packages are pinned to the host's copies via `resolvedShared`
198
+ * (no version negotiation).
199
+ * - `'vm'`: evaluate the graph with `vm.SourceTextModule` and link bare
200
+ * shared imports through the host's federation share scope (`loadShare`),
201
+ * restoring version negotiation. Requires `--experimental-vm-modules`; the
202
+ * loader emits a single warning and falls back to `'temp-file'` when that
203
+ * API is unavailable.
204
+ */
205
+ strategy?: SsrEntryLoaderStrategy;
206
+ /**
207
+ * Re-check each remote's manifest when the cached SSR entry resolution is
208
+ * older than this many milliseconds. When the manifest's version changes
209
+ * (remote redeployed at the same URL), the loader drops its caches for that
210
+ * remote so subsequent loads use the new build. Omit to cache until process
211
+ * exit or an explicit `revalidate()` call. Only manifest-resolved entries
212
+ * can be revalidated this way — convention-resolved entries have no version
213
+ * source.
214
+ */
215
+ maxAgeMs?: number;
216
+ /**
217
+ * Maximum time in milliseconds for each SSR network request. Defaults to
218
+ * 10 seconds. Set to `0` to disable the timeout.
219
+ */
220
+ fetchTimeoutMs?: number;
221
+ /**
222
+ * Maximum response body size in bytes for each SSR network request. Defaults
223
+ * to 10 MiB. Set to `0` to disable the limit.
224
+ */
225
+ fetchMaxBytes?: number;
226
+ };
227
+ type HostInitInjectLocationOptions = 'entry' | 'html';
228
+ interface PluginDevOptions {
229
+ disableLiveReload?: boolean;
230
+ disableHotTypesReload?: boolean;
231
+ disableDynamicRemoteTypeHints?: boolean;
232
+ /**
233
+ * Controls cross-federation HMR for remote modules.
234
+ *
235
+ * - `false` / `undefined` — HMR disabled (default).
236
+ * - `true` — HMR enabled with auto-detected strategy. When a framework
237
+ * plugin with cross-federation HMR support is detected, broadcast/relay
238
+ * is suppressed and the framework's native HMR handles updates:
239
+ * - React (`@vitejs/plugin-react` / `@vitejs/plugin-react-swc`) — the
240
+ * plugin serves a `/@react-refresh` proxy on remotes that delegates
241
+ * to the host's `RefreshRuntime`, unifying the component registry.
242
+ * - Vue (`@vitejs/plugin-vue` / `@vitejs/plugin-vue-jsx`) — the plugin
243
+ * injects a `__VUE_HMR_RUNTIME__` guard into the host page so the
244
+ * first-loaded (host) Vue runtime is pinned and remote-loaded Vue
245
+ * copies cannot overwrite it.
246
+ * For any host, the plugin also injects a script that clears the
247
+ * federation `moduleCache` on `vite:beforeUpdate` so subsequent
248
+ * `loadRemote()` calls return the freshly patched module.
249
+ * Other frameworks fall back to full page reloads.
250
+ * - `'full-reload'` — HMR enabled, always use full page reloads even when
251
+ * a framework with native cross-federation HMR is detected.
252
+ */
253
+ remoteHmr?: boolean | 'full-reload';
254
+ }
255
+ interface RemoteTypeUrl {
256
+ alias?: string;
257
+ api: string;
258
+ zip: string;
259
+ }
260
+ interface RemoteTypeUrls {
261
+ [remoteName: string]: RemoteTypeUrl;
262
+ }
263
+ interface PluginDtsOptions {
264
+ generateTypes?: boolean | DtsRemoteOptions;
265
+ consumeTypes?: boolean | DtsHostOptions;
266
+ tsConfigPath?: string;
267
+ extraOptions?: Record<string, unknown>;
268
+ implementation?: string;
269
+ cwd?: string;
270
+ displayErrorInTerminal?: boolean;
271
+ }
272
+ interface DtsRemoteOptions {
273
+ tsConfigPath?: string;
274
+ typesFolder?: string;
275
+ compiledTypesFolder?: string;
276
+ deleteTypesFolder?: boolean;
277
+ additionalFilesToCompile?: string[];
278
+ compilerInstance?: 'tsc' | 'vue-tsc' | 'tspc' | string;
279
+ compileInChildProcess?: boolean;
280
+ generateAPITypes?: boolean;
281
+ extractThirdParty?: boolean | {
282
+ exclude?: Array<string | RegExp>;
283
+ };
284
+ extractRemoteTypes?: boolean;
285
+ abortOnError?: boolean;
286
+ deleteTsConfig?: boolean;
287
+ afterGenerate?: (options: moduleFederationPlugin.DtsGenerateTypesHookOptions) => Promise<void> | void;
288
+ }
289
+ interface DtsHostOptions {
290
+ typesFolder?: string;
291
+ abortOnError?: boolean;
292
+ remoteTypesFolder?: string;
293
+ deleteTypesFolder?: boolean;
294
+ maxRetries?: number;
295
+ consumeAPITypes?: boolean;
296
+ runtimePkgs?: string[];
297
+ remoteTypeUrls?: (() => Promise<RemoteTypeUrls>) | RemoteTypeUrls;
298
+ timeout?: number;
299
+ family?: 0 | 4 | 6;
300
+ typesOnBuild?: boolean;
301
+ }
302
+ //#endregion
303
+ export { SsrEntryLoaderStrategy as a, SsrEntryLoaderConfig as i, PluginExperimentsOptions as n, TreeShakingConfig as o, PluginManifestOptions as r, ModuleFederationOptions as t };
@@ -1,9 +1,9 @@
1
- import { c as normalizePathForImport, n as mfError, t as createModuleFederationError } from "./logger--uKHOVjR.js";
2
- import { g as resolveImportPath, u as hasPackageDependency } from "./dtsConstants-Bs4FukGL.js";
1
+ import { l as normalizePathForImport, n as mfError, t as createModuleFederationError } from "./logger-DOwfTlw5.js";
2
+ import { g as resolveImportPath, u as hasPackageDependency } from "./dtsConstants-DSTzyKSG.js";
3
3
  import fs from "fs";
4
4
  import * as path$1 from "node:path";
5
- import os from "os";
6
5
  import { normalizeOptions } from "@module-federation/sdk";
6
+ import os from "os";
7
7
  import { consumeTypesAPI, generateTypesAPI, isTSProject, normalizeConsumeTypesOptions, normalizeDtsOptions, normalizeGenerateTypesOptions } from "@module-federation/dts-plugin";
8
8
  import { rpc } from "@module-federation/dts-plugin/core";
9
9
  //#region src/plugins/pluginDts.ts
@@ -1,4 +1,5 @@
1
- import { a as CodeRewriter, i as createCodePositionMap, r as mfWarn, s as EXTERNAL_URL_RE } from "./logger--uKHOVjR.js";
1
+ import { a as CodeRewriter, i as createCodePositionMap, r as mfWarn, s as EXTERNAL_URL_RE } from "./logger-DOwfTlw5.js";
2
+ import { simpleJoinRemoteEntry } from "@module-federation/sdk";
2
3
  //#region src/utils/fetchWithTimeout.ts
3
4
  const DEFAULT_SSR_FETCH_TIMEOUT_MS = 1e4;
4
5
  const DEFAULT_SSR_FETCH_MAX_BYTES = 10 * 1024 * 1024;
@@ -361,15 +362,14 @@ function getEntryFilename(entryUrl) {
361
362
  }
362
363
  function resolveEntryAssetUrl(entry, manifestUrl) {
363
364
  const base = manifestUrl.replace(/\/[^/]+$/, "/");
364
- return new URL(`${entry.path || ""}${entry.name}`, base).href;
365
+ return new URL(simpleJoinRemoteEntry(entry.path || "", entry.name), base).href;
365
366
  }
366
367
  function resolveSSREntryUrl(manifest, manifestUrl) {
367
368
  const entry = parseManifestEntry(manifest.metaData?.ssrRemoteEntry);
368
369
  if (!entry) return null;
369
370
  const base = manifestUrl.replace(/\/[^/]+$/, "/");
370
- const entryPath = entry.path + entry.name;
371
371
  return {
372
- url: new URL(entryPath, base).href,
372
+ url: new URL(simpleJoinRemoteEntry(entry.path, entry.name), base).href,
373
373
  type: entry.type,
374
374
  versionKey: computeManifestVersionKey(manifest)
375
375
  };
@@ -483,25 +483,30 @@ async function getSSREntry(remoteEntryUrl, maxAgeMs, fetchTimeoutMs, fetchMaxByt
483
483
  return record.promise;
484
484
  }
485
485
  /**
486
- * Drop per-remote temp-file caches after a version change so old artifacts stop
487
- * being reused. Temp-file cache keys hold SSR entry/chunk URLs (not the
488
- * browser entry URL), so scope the invalidation by origin.
486
+ * Drop a remote graph's temp-file caches after a version change so old
487
+ * artifacts stop being reused. A generated module can be shared by multiple
488
+ * remote graphs, so cleanup is scoped by graph owner rather than URL origin.
489
489
  */
490
490
  function dropRemoteTempFileCaches(remoteEntryUrl) {
491
- const origin = getUrlOrigin(remoteEntryUrl);
492
- if (!origin) return;
493
- const staleRecords = [...tempFileRecords.values()].filter((record) => record.remoteOrigin === origin);
494
- for (const [key] of tempFileCache) if (JSON.parse(key).find((part) => typeof part === "string" && /^https?:\/\//.test(part))?.startsWith(origin)) {
491
+ const staleRecords = [];
492
+ for (const [key, record] of tempFileRecords) {
493
+ if (!record.owners.delete(remoteEntryUrl) || record.owners.size > 0) continue;
495
494
  tempFileCache.delete(key);
496
495
  tempFilePathCache.delete(key);
496
+ staleRecords.push(record);
497
497
  }
498
498
  scheduleTempFileCleanup(staleRecords);
499
499
  }
500
- /** Invalidate all SSR loader caches owned by a remote. */
501
- async function invalidateRemoteCaches(remoteEntryUrl) {
500
+ /**
501
+ * Invalidate all SSR loader caches owned by a remote. The runtime loading map
502
+ * is cleared synchronously so `revalidate()` can safely clear module caches
503
+ * immediately after invoking this function.
504
+ */
505
+ function invalidateRemoteCaches(remoteEntryUrl) {
506
+ invalidateRuntimeRemoteEntry(remoteEntryUrl);
502
507
  dropRemoteTempFileCaches(remoteEntryUrl);
503
508
  scheduleVmCacheCleanup(remoteEntryUrl);
504
- await clearRunnerCaches(remoteEntryUrl);
509
+ return clearRunnerCaches(remoteEntryUrl);
505
510
  }
506
511
  /**
507
512
  * The VM strategy owns separate in-memory module-graph caches. Keep their
@@ -512,6 +517,12 @@ function scheduleVmCacheCleanup(remoteEntryUrl) {
512
517
  if (!vmStrategyModulePromise) return;
513
518
  vmStrategyModulePromise.then(({ clearVmStrategyCaches }) => clearVmStrategyCaches(remoteEntryUrl)).catch(() => {});
514
519
  }
520
+ /**
521
+ * A dev-mode ModuleRunner is created per origin (one Vite dev server) and is
522
+ * shared by every remote that server hosts, so its module cache can only be
523
+ * cleared as a whole. Origin scoping is therefore the finest granularity here;
524
+ * clearing only costs a re-evaluation, no generated files are removed.
525
+ */
515
526
  async function clearRunnerCaches(remoteEntryUrl) {
516
527
  let remoteOrigin;
517
528
  if (remoteEntryUrl) {
@@ -526,7 +537,9 @@ async function clearRunnerCaches(remoteEntryUrl) {
526
537
  }
527
538
  /** Invalidate all SSR loader caches across every remote. */
528
539
  function invalidateAllRemoteCaches() {
540
+ invalidateRuntimeRemoteEntry();
529
541
  const staleRecords = [...tempFileRecords.values()];
542
+ for (const record of staleRecords) record.owners.clear();
530
543
  tempFileCache.clear();
531
544
  tempFilePathCache.clear();
532
545
  scheduleTempFileCleanup(staleRecords);
@@ -534,6 +547,17 @@ function invalidateAllRemoteCaches() {
534
547
  clearRunnerCaches();
535
548
  }
536
549
  /**
550
+ * runtime-core deduplicates remote entry loads in a process-global map. The
551
+ * SSR loader's module cache invalidation must evict the matching runtime entry
552
+ * too, otherwise the next loadRemote() returns the pre-revalidation container
553
+ * without invoking this loader again.
554
+ */
555
+ function invalidateRuntimeRemoteEntry(remoteEntryUrl) {
556
+ const globalLoading = globalThis.__GLOBAL_LOADING_REMOTE_ENTRY__;
557
+ if (!globalLoading) return;
558
+ for (const key of Object.keys(globalLoading)) if (remoteEntryUrl === void 0 || key === remoteEntryUrl || key.endsWith(`:${remoteEntryUrl}`)) delete globalLoading[key];
559
+ }
560
+ /**
537
561
  * Drop the loader's caches so the next `loadEntry` re-resolves and re-fetches
538
562
  * remote SSR entries. Pass a remote entry URL to scope the invalidation to one
539
563
  * remote; call with no arguments to invalidate everything.
@@ -579,6 +603,7 @@ function scheduleTempFileCleanup(records) {
579
603
  async function cleanupTempFileRecord(record) {
580
604
  try {
581
605
  await record.promise.catch(() => void 0);
606
+ if (record.owners.size > 0) return;
582
607
  if (tempFileRecords.get(record.cacheKey) !== record) return;
583
608
  const filePath = await record.filePathPromise;
584
609
  const { rmSync } = await _fs();
@@ -733,7 +758,7 @@ function getTempFileImportUrl(filePath, versionKey) {
733
758
  * a remote redeploy (new manifest → new key) produces new files and bypasses
734
759
  * Node's ESM module cache instead of serving the stale build.
735
760
  */
736
- async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, versionKey = UNVERSIONED, fetchTimeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS, contextKey = "default", fetchMaxBytes = DEFAULT_SSR_FETCH_MAX_BYTES, extension = ".mjs") {
761
+ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, versionKey = UNVERSIONED, fetchTimeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS, contextKey = "default", fetchMaxBytes = DEFAULT_SSR_FETCH_MAX_BYTES, extension = ".mjs", owner) {
737
762
  const cacheKey = JSON.stringify([
738
763
  fetchTimeoutMs,
739
764
  fetchMaxBytes,
@@ -745,6 +770,7 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
745
770
  if (visited.has(url)) return visited.get(url);
746
771
  const cached = tempFileCache.get(cacheKey);
747
772
  if (cached) {
773
+ if (owner) tempFileRecords.get(cacheKey)?.owners.add(owner);
748
774
  pending.add(cached);
749
775
  const reserved = tempFilePathCache.get(cacheKey);
750
776
  const tmpFile = reserved ? await reserved : await cached;
@@ -768,7 +794,7 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
768
794
  const relImports = specifiers.map(({ value }) => value).filter((value) => isRelativeSpecifier(value) && !isVitePreloadHelperSpecifier(value)).map((value) => new URL(value, base).href);
769
795
  const subMap = /* @__PURE__ */ new Map();
770
796
  await Promise.all([...new Set(relImports)].filter((u) => u.startsWith("http://") || u.startsWith("https://")).map(async (u) => {
771
- const tmpPath = await fetchEsmToTempFile(u, tmpDir, visited, pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey, fetchMaxBytes, extension);
797
+ const tmpPath = await fetchEsmToTempFile(u, tmpDir, visited, pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey, fetchMaxBytes, extension, owner);
772
798
  subMap.set(u, getTempFileImportUrl(tmpPath, versionKey));
773
799
  }));
774
800
  const requireShimUrl = sharedPkgMap?.size && specifiers.some(({ value }) => isNodeModuleSpecifier(value)) ? await getRequireShimUrl(tmpDir, sharedPkgMap, contextKey) : void 0;
@@ -781,7 +807,7 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
781
807
  cacheKey,
782
808
  filePathPromise: tmpFilePromise,
783
809
  promise,
784
- remoteOrigin: getUrlOrigin(url)
810
+ owners: owner ? /* @__PURE__ */ new Set([owner]) : /* @__PURE__ */ new Set()
785
811
  };
786
812
  tempFileRecords.set(cacheKey, record);
787
813
  tempFileCache.set(cacheKey, promise);
@@ -793,9 +819,9 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
793
819
  });
794
820
  return promise;
795
821
  }
796
- async function fetchEsmGraphToTempFile(url, tmpDir, sharedPkgMap, versionKey = UNVERSIONED, fetchTimeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS, contextKey = "default", fetchMaxBytes = DEFAULT_SSR_FETCH_MAX_BYTES, extension = ".mjs") {
822
+ async function fetchEsmGraphToTempFile(url, tmpDir, sharedPkgMap, versionKey = UNVERSIONED, fetchTimeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS, contextKey = "default", fetchMaxBytes = DEFAULT_SSR_FETCH_MAX_BYTES, extension = ".mjs", owner) {
797
823
  const pending = /* @__PURE__ */ new Set();
798
- const rootFile = await fetchEsmToTempFile(url, tmpDir, /* @__PURE__ */ new Map(), pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey, fetchMaxBytes, extension);
824
+ const rootFile = await fetchEsmToTempFile(url, tmpDir, /* @__PURE__ */ new Map(), pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey, fetchMaxBytes, extension, owner);
799
825
  await Promise.all(pending);
800
826
  return rootFile;
801
827
  }
@@ -808,7 +834,7 @@ async function importTempModule(filePath, versionKey) {
808
834
  let warnedVmUnavailable = false;
809
835
  let vmStrategyModulePromise;
810
836
  function getVmStrategyModule() {
811
- if (!vmStrategyModulePromise) vmStrategyModulePromise = import("./ssrVmStrategy-B_e3yqa2.js");
837
+ if (!vmStrategyModulePromise) vmStrategyModulePromise = import("./ssrVmStrategy-Dk5xXVvR.js");
812
838
  return vmStrategyModulePromise;
813
839
  }
814
840
  /**
@@ -819,7 +845,7 @@ function getVmStrategyModule() {
819
845
  function warnStrategyFallback(strategy, url, fallback, error) {
820
846
  mfWarn(`SSR entry loader: strategy "${strategy}" failed to load ${url}; falling back to ${fallback}.`, error);
821
847
  }
822
- async function tryVmStrategy(ssrEntry, options) {
848
+ async function tryVmStrategy(ssrEntry, options, rootEntryUrl) {
823
849
  const { loadViaVmStrategy, isVmStrategyAvailable } = await getVmStrategyModule();
824
850
  if (!await isVmStrategyAvailable()) {
825
851
  if (!warnedVmUnavailable) {
@@ -835,10 +861,11 @@ async function tryVmStrategy(ssrEntry, options) {
835
861
  fetchTimeoutMs: options.fetchTimeoutMs,
836
862
  fetchMaxBytes: options.fetchMaxBytes,
837
863
  cacheContext: options.cacheContext,
838
- federationInstance: options.federationInstance
864
+ federationInstance: options.federationInstance,
865
+ rootEntryUrl
839
866
  });
840
867
  }
841
- async function loadSSRRemoteEntry(ssrEntry, options) {
868
+ async function loadSSRRemoteEntry(ssrEntry, options, rootEntryUrl) {
842
869
  const { url, type, versionKey } = ssrEntry;
843
870
  const { resolvedShared } = options;
844
871
  if (type === "commonjs-module" || type === "commonjs") {
@@ -865,7 +892,7 @@ async function loadSSRRemoteEntry(ssrEntry, options) {
865
892
  }
866
893
  }
867
894
  if (options.strategy === "vm") try {
868
- const fromVm = await tryVmStrategy(ssrEntry, options);
895
+ const fromVm = await tryVmStrategy(ssrEntry, options, rootEntryUrl);
869
896
  if (fromVm) return fromVm;
870
897
  } catch (error) {
871
898
  if (isSsrEntryHttpError(error) || isSsrFetchBodyTooLargeError(error)) throw error;
@@ -875,9 +902,12 @@ async function loadSSRRemoteEntry(ssrEntry, options) {
875
902
  const cacheDir = await getSSRCacheDir();
876
903
  mkdirSync(cacheDir, { recursive: true });
877
904
  const sharedPkgMap = new Map(Object.entries(resolvedShared));
878
- const extension = type === "commonjs-module" || type === "commonjs" ? ".js" : ".mjs";
905
+ const isCommonJs = type === "commonjs-module" || type === "commonjs";
906
+ const extension = isCommonJs ? ".cjs" : ".mjs";
879
907
  try {
880
- return await importTempModule(await fetchEsmGraphToTempFile(url, cacheDir, sharedPkgMap, versionKey, options.fetchTimeoutMs, getSsrTransformContextKey(resolvedShared, options.shareScopeName), options.fetchMaxBytes, extension), versionKey);
908
+ const tmpFile = await fetchEsmGraphToTempFile(url, cacheDir, sharedPkgMap, versionKey, options.fetchTimeoutMs, getSsrTransformContextKey(resolvedShared, options.shareScopeName), options.fetchMaxBytes, extension, rootEntryUrl);
909
+ if (isCommonJs) return (await _module()).createRequire(import.meta.url)(tmpFile);
910
+ return await importTempModule(tmpFile, versionKey);
881
911
  } catch (error) {
882
912
  if (isSsrEntryHttpError(error) || isSsrFetchBodyTooLargeError(error)) throw error;
883
913
  warnStrategyFallback("temp-file", url, "the federation runtime's loader", error);
@@ -914,11 +944,14 @@ function ssrEntryLoaderPlugin(options = {}) {
914
944
  } : resolved;
915
945
  const ssrEntry = await getSSREntry(remoteInfo.entry, loadOptions.maxAgeMs, loadOptions.fetchTimeoutMs, loadOptions.fetchMaxBytes);
916
946
  if (!ssrEntry) return;
917
- const mod = await loadSSRRemoteEntry(ssrEntry, loadOptions);
947
+ const mod = await loadSSRRemoteEntry(ssrEntry.url === remoteInfo.entry && remoteInfo.type ? {
948
+ ...ssrEntry,
949
+ type: remoteInfo.type
950
+ } : ssrEntry, loadOptions, remoteInfo.entry);
918
951
  if (!mod) return;
919
952
  return mod;
920
953
  }
921
954
  };
922
955
  }
923
956
  //#endregion
924
- export { getUrlOrigin as a, fetchWithTimeout as c, ssrEntryLoaderPlugin as i, readResponseTextBounded as l, neutralizeBrowserPreloadHelpers as n, DEFAULT_SSR_FETCH_MAX_BYTES as o, revalidate as r, DEFAULT_SSR_FETCH_TIMEOUT_MS as s, SsrEntryHttpError as t };
957
+ export { DEFAULT_SSR_FETCH_MAX_BYTES as a, readResponseTextBounded as c, ssrEntryLoaderPlugin as i, neutralizeBrowserPreloadHelpers as n, DEFAULT_SSR_FETCH_TIMEOUT_MS as o, revalidate as r, fetchWithTimeout as s, SsrEntryHttpError as t };
@@ -1,5 +1,5 @@
1
1
  import { t as findSharedKey } from "./sharedKeyMatcher-Od_nYxJA.js";
2
- import { a as getUrlOrigin, c as fetchWithTimeout, l as readResponseTextBounded, n as neutralizeBrowserPreloadHelpers, t as SsrEntryHttpError } from "./ssrEntryLoader-DmqLXUmm.js";
2
+ import { c as readResponseTextBounded, n as neutralizeBrowserPreloadHelpers, s as fetchWithTimeout, t as SsrEntryHttpError } from "./ssrEntryLoader-DjZj-5Lw.js";
3
3
  //#region src/utils/ssrVmStrategy.ts
4
4
  /**
5
5
  * vm.SourceTextModule strategy for loading remote SSR entries.
@@ -144,30 +144,24 @@ function addVmCacheOwner(ownersByCacheKey, cacheKey, rootEntryUrl) {
144
144
  owners.add(rootEntryUrl);
145
145
  ownersByCacheKey.set(cacheKey, owners);
146
146
  }
147
- function clearOwnedVmCache(cache, ownersByCacheKey, remoteOrigin) {
147
+ function clearOwnedVmCache(cache, ownersByCacheKey, remoteEntryUrl) {
148
148
  for (const [cacheKey, owners] of ownersByCacheKey) {
149
- if (!remoteOrigin) {
150
- cache.delete(cacheKey);
151
- ownersByCacheKey.delete(cacheKey);
152
- continue;
153
- }
154
- for (const owner of owners) if (getUrlOrigin(owner) === remoteOrigin) owners.delete(owner);
155
- if (owners.size === 0) {
156
- cache.delete(cacheKey);
157
- ownersByCacheKey.delete(cacheKey);
158
- }
149
+ if (remoteEntryUrl !== void 0 && !owners.delete(remoteEntryUrl)) continue;
150
+ if (remoteEntryUrl !== void 0 && owners.size > 0) continue;
151
+ cache.delete(cacheKey);
152
+ ownersByCacheKey.delete(cacheKey);
159
153
  }
160
154
  }
161
155
  /**
162
156
  * Drop cached VM module graphs after a remote revalidation. A module graph can
163
- * contain URLs from another origin, so ownership is tracked by the root entry
164
- * rather than inferred from each module URL.
157
+ * contain URLs from another origin, and one origin can serve several remotes,
158
+ * so ownership is tracked by the owning remote entry URL rather than inferred
159
+ * from each module URL. A module shared by several graphs stays cached while
160
+ * another remote still owns it.
165
161
  */
166
162
  function clearVmStrategyCaches(remoteEntryUrl) {
167
- const remoteOrigin = remoteEntryUrl === void 0 ? void 0 : getUrlOrigin(remoteEntryUrl);
168
- if (remoteEntryUrl !== void 0 && !remoteOrigin) return;
169
- clearOwnedVmCache(httpModuleCache, httpModuleCacheOwners, remoteOrigin);
170
- clearOwnedVmCache(namespaceCache, namespaceCacheOwners, remoteOrigin);
163
+ clearOwnedVmCache(httpModuleCache, httpModuleCacheOwners, remoteEntryUrl);
164
+ clearOwnedVmCache(namespaceCache, namespaceCacheOwners, remoteEntryUrl);
171
165
  }
172
166
  function getBodyPreview(body) {
173
167
  return body.slice(0, 240).replace(/\s+/g, " ").trim();
@@ -243,12 +237,13 @@ async function importDynamically(vm, specifier, referencingModule, options) {
243
237
  async function loadViaVmStrategy(entryUrl, options) {
244
238
  const vm = await getVmApi();
245
239
  if (!vm) return null;
240
+ const rootEntryUrl = options.rootEntryUrl ?? entryUrl;
246
241
  const graphOptions = {
247
242
  ...options,
248
- rootEntryUrl: entryUrl
243
+ rootEntryUrl
249
244
  };
250
245
  const cacheKey = `${getVmCacheContextKey(options)}::${options.versionKey}::${entryUrl}`;
251
- addVmCacheOwner(namespaceCacheOwners, cacheKey, entryUrl);
246
+ addVmCacheOwner(namespaceCacheOwners, cacheKey, rootEntryUrl);
252
247
  if (!namespaceCache.has(cacheKey)) namespaceCache.set(cacheKey, (async () => {
253
248
  const entryModule = await getHttpModule(vm, entryUrl, graphOptions);
254
249
  const linker = (specifier, referencingModule) => linkModule(vm, specifier, referencingModule, graphOptions);