@module-federation/vite 1.23.1 → 1.23.2

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
@@ -402,6 +402,42 @@ federation({
402
402
  `provideExternalRuntime` injects a local runtime plugin that publishes `runtime-core` on `globalThis._FEDERATION_RUNTIME_CORE`. `externalRuntime` rewrites imports of `@module-federation/runtime-core` to read that global. A container that also `exposes` (e.g. a host consumed by its own remotes) may provide the runtime too, as long as exactly one container on the page does and it is loaded before any `externalRuntime` remote evaluates (a second provider is ignored with a `Detect multiple module federation runtime!` warning; a remote evaluated before the provider throws `_FEDERATION_RUNTIME_CORE is missing`).
403
403
  The `externalRuntime` rewrite applies to the browser remote graph; SSR remote entries continue to resolve `@module-federation/runtime-core` from Node so they do not depend on the browser global.
404
404
 
405
+ ## Host provides all shared (`experiments.hostProvidesAllShared`)
406
+
407
+ For topologies where **one host provides every shared dependency** and remotes only consume them, shared dependencies can be resolved by a native browser [import map](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) instead of the Module Federation runtime. No `loadShare` / prebuild glue is generated and `eager` is not needed: shared modules are ordinary static ES imports, so code that reads a shared value at module-evaluation time (`class X extends SharedBase`, decorator metadata) just works.
408
+
409
+ **Remote** (a container with `exposes`):
410
+
411
+ ```ts
412
+ federation({
413
+ name: "remote",
414
+ exposes: { "./Button": "./src/Button.tsx" },
415
+ shared: { react: {}, "react-dom": {}, "react/jsx-runtime": {} },
416
+ experiments: { hostProvidesAllShared: true },
417
+ });
418
+ ```
419
+
420
+ The remote build emits one fixed-name entry per expose (`./Button` → `Button.js`), keeps every shared key as a bare external import and writes a standard `mf-manifest.json` next to the entries, with no `remoteEntry` since there is no runtime container. `metaData.publicPath` comes from Vite's `base` (or `publicPath`); `auto` is inferred from the manifest URL by the host.
421
+
422
+ **Host** (no `exposes`):
423
+
424
+ ```ts
425
+ federation({
426
+ name: "host",
427
+ remotes: { remote: "https://cdn.example.com/remote/mf-manifest.json" },
428
+ shared: { react: {}, "react-dom": {}, "react/jsx-runtime": {} },
429
+ experiments: { hostProvidesAllShared: true },
430
+ });
431
+ ```
432
+
433
+ The host emits one re-export chunk per shared key, which the bundler dedupes with the host's own imports, and injects a `<script type="importmap">` into `index.html` mapping each shared key to that chunk and each remote expose (`remote/Button`) to the entry listed in the manifest. A manifest may also be a path relative to the host root (for example `../remote/dist/mf-manifest.json`). A manifest that has a `remoteEntry` belongs to a Module Federation runtime remote and is rejected. The dev server serves the same map, pointing shared keys at virtual modules that resolve to the host's pre-bundled dependencies, so host and remotes share one instance in dev too.
434
+
435
+ A remote that imports an unshared subpath of a shared package (`rxjs/operators` with only `rxjs` shared) bundles a private copy of that package's internals; the remote build warns and names the key to add to `shared`.
436
+
437
+ `dts` keeps working through the manifest's `metaData.types`: a remote emits its types archive next to `mf-manifest.json` and a host referencing the manifest by URL consumes it from there.
438
+
439
+ Not supported in this mode, since the Module Federation runtime is not loaded: version negotiation and fallbacks, share aliasing (`request` / `shareKey`), `import: false`, runtime plugins, SSR, tree-shaking artifacts, `loadRemote` / runtime-registered remotes, and interop with webpack / rspack containers. Options that only the runtime or `remoteEntry.js` pipeline reads (for example `filename`, `shareStrategy`, `runtimePlugins`, `eager`, `requiredVersion`) are ignored; one warning names them. `singleton` needs no setting: every shared key has exactly one instance. The import map is fixed when the host is built, so a remote must keep its expose entry names stable across deploys. The inline import map changes with every host build; a strict CSP needs a nonce or hash for it.
440
+
405
441
  ## ⚠️ `codeSplitting` is managed by the plugin
406
442
 
407
443
  Do not set `build.rollupOptions.output.codeSplitting` or
@@ -1,5 +1,5 @@
1
1
  import { d as getNodeModulesSuffix, r as getSharedRuntimeKey } from "./sharedKeyMatcher-Od_nYxJA.js";
2
- import { r as mfWarn, t as createModuleFederationError } from "./logger--uKHOVjR.js";
2
+ import { r as mfWarn, t as createModuleFederationError } from "./logger-DOwfTlw5.js";
3
3
  import { existsSync, readFileSync, readdirSync, realpathSync } from "fs";
4
4
  import { createRequire } from "module";
5
5
  import * as path$1 from "node:path";
package/lib/index.d.ts CHANGED
@@ -167,6 +167,23 @@ interface PluginExperimentsOptions {
167
167
  provideExternalRuntime?: boolean;
168
168
  /** Generate the React SSR/hydration island capability for eligible exposes. */
169
169
  ssrMode?: 'ISLAND';
170
+ /**
171
+ * The host provides every shared dependency; remotes bring none. Shared keys
172
+ * are resolved by a native browser import map instead of the Module
173
+ * Federation runtime: the host emits one entry chunk per shared key and
174
+ * injects `<script type="importmap">`, remotes keep shared imports as bare
175
+ * specifiers and emit one fixed-name entry per expose plus a standard
176
+ * `mf-manifest.json` (without `remoteEntry`) the host maps from.
177
+ *
178
+ * Shared modules become ordinary static ES imports, so there is no
179
+ * `loadShare` glue and no `eager` is needed for code that reads a shared
180
+ * value at module-evaluation time. There is no version negotiation and no
181
+ * runtime API: `requiredVersion`, `strictVersion`, `shareStrategy`,
182
+ * `runtimePlugins` and `manifest` are ignored.
183
+ *
184
+ * @see https://github.com/module-federation/vite/issues/1381
185
+ */
186
+ hostProvidesAllShared?: boolean;
170
187
  }
171
188
  type SsrEntryLoaderStrategy = 'temp-file' | 'vm';
172
189
  type SsrEntryLoaderConfig = {
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { _ as resolvePublicPath, a as matchesSharedSource, c as filterId, d as getNodeModulesSuffix, f as isAssetLikeImport, g as normalizeNodeModulePath, h as isViteOptimizableEntry, i as invalidateSharedKeyMatcher, l as getBasePath$1, m as isNuxtClientBase, n as getSharedRequest, p as isNodeModulePath, r as getSharedRuntimeKey, s as ensureTrailingSlash, t as findSharedKey, u as getCommonSharedSubpaths, v as stripQueryAndHash, y as stripViteFsPrefix } from "./sharedKeyMatcher-Od_nYxJA.js";
2
- import { a as CodeRewriter, c as normalizePathForImport, i as createCodePositionMap, l as rebaseImport, o as createSourceMap, r as mfWarn, t as createModuleFederationError, u as resolveHashPlaceholderFileName } from "./logger--uKHOVjR.js";
3
- import { C as isReactCoreShare, S as REACT_DOM_CLIENT_SHARE, _ as resolveModulePath, a as getIsRolldown, b as supportsCodeSplittingGroupDebugName, c as getPackageNameFromNodeModulePath, d as isNuxtProjectRoot, f as isPackageExportAvailable, g as resolveImportPath, h as packageNameEncode, i as getInstalledPackageJson, l as getSharedCacheDescriptor, m as packageNameDecode, n as getDependencyNames, o as getPackageDetectionCwd, p as isPackageInstalled, r as getInstalledPackageEntry, s as getPackageName, u as hasPackageDependency, v as setPackageDetectionCwd, x as REACT_CLIENT_INTERNALS_KEY, y as sharedCacheHelperCode } from "./dtsConstants-Bs4FukGL.js";
2
+ import { a as CodeRewriter, c as isAbsoluteUrl, d as resolveHashPlaceholderFileName, i as createCodePositionMap, l as normalizePathForImport, o as createSourceMap, r as mfWarn, t as createModuleFederationError, u as rebaseImport } from "./logger-DOwfTlw5.js";
3
+ import { C as isReactCoreShare, S as REACT_DOM_CLIENT_SHARE, _ as resolveModulePath, a as getIsRolldown, b as supportsCodeSplittingGroupDebugName, c as getPackageNameFromNodeModulePath, d as isNuxtProjectRoot, f as isPackageExportAvailable, g as resolveImportPath, h as packageNameEncode, i as getInstalledPackageJson, l as getSharedCacheDescriptor, m as packageNameDecode, n as getDependencyNames, o as getPackageDetectionCwd, p as isPackageInstalled, r as getInstalledPackageEntry, s as getPackageName, u as hasPackageDependency, v as setPackageDetectionCwd, x as REACT_CLIENT_INTERNALS_KEY, y as sharedCacheHelperCode } from "./dtsConstants-r5OfegUr.js";
4
4
  import { createRequire } from "node:module";
5
5
  import * as fs$2 from "fs";
6
6
  import { existsSync, readFileSync, readdirSync, realpathSync, statSync, writeFileSync } from "fs";
@@ -15,6 +15,8 @@ import * as fs$1 from "node:fs";
15
15
  import { existsSync as existsSync$1, readFileSync as readFileSync$1 } from "node:fs";
16
16
  import { pathToFileURL as pathToFileURL$1 } from "node:url";
17
17
  import { isIPv6 } from "node:net";
18
+ import { inferAutoPublicPath } from "@module-federation/sdk";
19
+ import { readFile } from "node:fs/promises";
18
20
  //#region src/utils/regexEscape.ts
19
21
  const REGEXP_SPECIAL_CHARS_RE = /[.*+?^${}()|[\]\\]/g;
20
22
  /**
@@ -723,7 +725,8 @@ function normalizeExperiments(experiments) {
723
725
  return {
724
726
  externalRuntime: experiments?.externalRuntime === true,
725
727
  provideExternalRuntime: experiments?.provideExternalRuntime === true,
726
- ssrMode: experiments?.ssrMode === "ISLAND" ? "ISLAND" : void 0
728
+ ssrMode: experiments?.ssrMode === "ISLAND" ? "ISLAND" : void 0,
729
+ hostProvidesAllShared: experiments?.hostProvidesAllShared === true
727
730
  };
728
731
  }
729
732
  function normalizeSsrEntryLoader(ssrEntryLoader) {
@@ -3738,16 +3741,22 @@ function generateLocalSharedImportMap(options) {
3738
3741
  const __mfGetCachedReactFamily = (keys, reactKeys, localVersion, requiredExport) => {
3739
3742
  const cache = globalThis.__mf_module_cache__?.share;
3740
3743
  const react = reactKeys.map((key) => cache?.[key]).find((value) => value !== undefined);
3741
- const reactVersion = react?.version ?? react?.default?.version;
3742
- const actual = String(reactVersion || '').split(/[^0-9]+/).map(Number);
3743
- const expected = String(localVersion || '').split(/[^0-9]+/).map(Number);
3744
- for (let index = 0; index < Math.max(actual.length, expected.length); index++) {
3745
- if ((actual[index] || 0) < (expected[index] || 0)) return undefined;
3746
- if ((actual[index] || 0) > (expected[index] || 0)) break;
3747
- }
3744
+ const isOlder = (version) => {
3745
+ const actual = String(version || '').split(/[^0-9]+/).map(Number);
3746
+ const expected = String(localVersion || '').split(/[^0-9]+/).map(Number);
3747
+ for (let index = 0; index < Math.max(actual.length, expected.length); index++) {
3748
+ if ((actual[index] || 0) < (expected[index] || 0)) return true;
3749
+ if ((actual[index] || 0) > (expected[index] || 0)) return false;
3750
+ }
3751
+ return false;
3752
+ };
3753
+ if (isOlder(react?.version ?? react?.default?.version)) return undefined;
3748
3754
  for (const key of keys) {
3749
3755
  const cached = cache?.[key];
3750
3756
  if (cached === undefined) continue;
3757
+ // A host seed from an older React family is not coherent with the newer cached react
3758
+ const cachedVersion = cached?.version ?? cached?.default?.version;
3759
+ if (cachedVersion !== undefined && isOlder(cachedVersion)) continue;
3751
3760
  if (!requiredExport || typeof cached?.[requiredExport] === 'function' || typeof cached?.default?.[requiredExport] === 'function') return cached;
3752
3761
  }
3753
3762
  return undefined;
@@ -5830,6 +5839,22 @@ function generateHostAutoInitCode(remoteEntryImport, _command = "build", options
5830
5839
  }));
5831
5840
  }
5832
5841
  ` : ""}
5842
+ // The seed called this container's get() directly, so the Runtime's
5843
+ // provider never learned it is in use and a remote could load its own
5844
+ // copy (#1396). Mark it once the host is bound to the local copy.
5845
+ for (const [pkg, share] of Object.entries(usedShared)) {
5846
+ if (!share.lib || share.treeShaking || share.shareConfig?.import === false) continue;
5847
+ const cacheDescriptor = __mfGetSharedCacheDescriptor(pkg, share.shareConfig?.singleton, share.version, share.scope);
5848
+ if (__mfReadSharedCacheOwner(__mfModuleCache.share, cacheDescriptor) !== ${cacheOwner}) continue;
5849
+ for (const scopeName of Array.isArray(share.scope) ? share.scope : [share.scope || "default"]) {
5850
+ const versions = runtime.shareScopeMap?.[scopeName]?.[pkg];
5851
+ const provider = versions?.[share.version];
5852
+ if (!provider || provider.get !== share.get || provider.lib) continue;
5853
+ if (Object.values(versions).some((other) => other !== provider && (other?.lib || other?.loaded))) continue;
5854
+ provider.lib = share.lib;
5855
+ provider.loaded = true;
5856
+ }
5857
+ }
5833
5858
  return runtime;
5834
5859
  })();
5835
5860
  }
@@ -8058,7 +8083,7 @@ function ${helperName}(names) {
8058
8083
  }
8059
8084
  //#endregion
8060
8085
  //#region package.json
8061
- var version$1 = "1.23.1";
8086
+ var version$1 = "1.23.2";
8062
8087
  //#endregion
8063
8088
  //#region src/virtualModules/virtualExposesSSR.ts
8064
8089
  /**
@@ -8160,6 +8185,8 @@ function generateRemoteEntrySSR(options) {
8160
8185
  const sharedSingletons = ${JSON.stringify(sharedSingletons)};
8161
8186
  const moduleCacheKey = Symbol.for(${JSON.stringify(MODULE_CACHE_SHARE_SCOPE_KEY)});
8162
8187
  let exposesMapPromise;
8188
+ // One identity per container, so a re-entrant init with the same initScope finds it.
8189
+ const initToken = { from: ${JSON.stringify(options.name)} };
8163
8190
 
8164
8191
  function createShareInitError(errors) {
8165
8192
  const details = errors.map(({ scopeName, pkg, error }) => {
@@ -8188,7 +8215,6 @@ function generateRemoteEntrySSR(options) {
8188
8215
  remotes: [],
8189
8216
  shared: {},
8190
8217
  });
8191
- const initToken = { from: ${JSON.stringify(options.name)} };
8192
8218
  if (initScope.indexOf(initToken) >= 0) return;
8193
8219
  initScope.push(initToken);
8194
8220
  const shareScopeNames = Array.isArray(${JSON.stringify(options.shareScope)})
@@ -8525,18 +8551,21 @@ const Manifest = (providedOptions) => {
8525
8551
  publicPath = mfOptions.publicPath === "auto" ? "auto" : resolvePublicPath(mfOptions, base, _originalConfigBase);
8526
8552
  },
8527
8553
  /**
8528
- * A standalone `vite build --ssr` may run with its own `filename`, e.g.
8529
- * `defineConfig(({ isSsrBuild }) => ...)`. The manifest written by the
8530
- * earlier client build then advertises an SSR entry derived from the
8531
- * browser filename, which is not the file this build emitted. Point the
8532
- * manifest and stats on disk at the emitted SSR entry.
8554
+ * The client manifest only advertises an SSR entry its own bundle
8555
+ * emitted. The SSR entry usually lands in a separate build: a standalone
8556
+ * `vite build --ssr` (possibly with its own `filename`, e.g.
8557
+ * `defineConfig(({ isSsrBuild }) => ...)`) or the `ssr` environment,
8558
+ * which the builder may run before or after `client`. Whichever build
8559
+ * finishes with the SSR entry on disk points the manifest and stats at it.
8533
8560
  */
8534
8561
  writeBundle(outputOptions) {
8535
8562
  if (!mfManifestName || _command !== "build" || isConsumerProject) return;
8536
- const environment = this.environment;
8537
- if (!(environment ? environment.name === "ssr" : Boolean(viteConfig?.build?.ssr))) return;
8538
8563
  const ssrEntryName = getSsrRemoteEntryFileName(mfOptions);
8539
- const outDirs = [...new Set([outputOptions.dir, viteConfig?.environments?.client?.build?.outDir].filter((dir) => typeof dir === "string").map((dir) => path$1.resolve(root, dir)))];
8564
+ const outDirs = [...new Set([
8565
+ outputOptions.dir,
8566
+ viteConfig?.environments?.client?.build?.outDir,
8567
+ viteConfig?.environments?.ssr?.build?.outDir
8568
+ ].filter((dir) => typeof dir === "string").map((dir) => path$1.resolve(root, dir)))];
8540
8569
  if (!outDirs.some((dir) => fs$1.existsSync(path$1.resolve(dir, ssrEntryName)))) return;
8541
8570
  for (const dir of outDirs) for (const fileName of [mfManifestName, mfManifestStatsName]) {
8542
8571
  if (!fileName) continue;
@@ -8545,8 +8574,12 @@ const Manifest = (providedOptions) => {
8545
8574
  try {
8546
8575
  const data = JSON.parse(fs$1.readFileSync(file, "utf8"));
8547
8576
  const entry = data?.metaData?.ssrRemoteEntry;
8548
- if (!entry || typeof entry !== "object" || entry.name === ssrEntryName) continue;
8549
- entry.name = ssrEntryName;
8577
+ if (!data?.metaData || entry?.name === ssrEntryName) continue;
8578
+ data.metaData.ssrRemoteEntry = Object.assign({
8579
+ name: ssrEntryName,
8580
+ path: "",
8581
+ type: "module"
8582
+ }, typeof entry === "object" ? entry : null, { name: ssrEntryName });
8550
8583
  fs$1.writeFileSync(file, JSON.stringify(data));
8551
8584
  } catch {}
8552
8585
  }
@@ -8564,7 +8597,7 @@ const Manifest = (providedOptions) => {
8564
8597
  const expectedSsrRemoteEntryFile = getSsrRemoteEntryFileName(mfOptions);
8565
8598
  const foundSsrRemoteEntryFile = Object.values(bundle).find((file) => file.fileName === expectedSsrRemoteEntryFile)?.fileName;
8566
8599
  if (foundRemoteEntryFile) remoteEntryFile = foundRemoteEntryFile;
8567
- ssrRemoteEntryFile = foundSsrRemoteEntryFile || (_command === "serve" ? getSsrRemoteEntryFileName(mfOptions, resolveHashPlaceholderFileName(mfOptions.filename)) : expectedSsrRemoteEntryFile);
8600
+ ssrRemoteEntryFile = foundSsrRemoteEntryFile || (_command === "serve" ? getSsrRemoteEntryFileName(mfOptions, resolveHashPlaceholderFileName(mfOptions.filename)) : void 0);
8568
8601
  const allCssAssets = mfOptions.bundleAllCSS && !disableAssetsAnalyze ? collectCssAssets(bundle) : /* @__PURE__ */ new Set();
8569
8602
  if (allCssAssets.size > 0) {
8570
8603
  const secondaryCss = /* @__PURE__ */ new Set();
@@ -8640,11 +8673,12 @@ const Manifest = (providedOptions) => {
8640
8673
  path: "",
8641
8674
  type: "module"
8642
8675
  };
8643
- const ssrRemoteEntry = {
8644
- name: ssrRemoteEntryFile || getSsrRemoteEntryFileName(mfOptions, _command === "serve" ? resolveHashPlaceholderFileName(filename) : filename),
8676
+ const resolvedSsrRemoteEntryFile = _command === "serve" ? ssrRemoteEntryFile || getSsrRemoteEntryFileName(mfOptions, resolveHashPlaceholderFileName(filename)) : ssrRemoteEntryFile;
8677
+ const ssrRemoteEntry = resolvedSsrRemoteEntryFile ? {
8678
+ name: resolvedSsrRemoteEntryFile,
8645
8679
  path: _command === "serve" ? "/__mf_ssr__/" : "",
8646
8680
  type: "module"
8647
- };
8681
+ } : void 0;
8648
8682
  const varRemoteEntry = varFilename ? {
8649
8683
  name: varFilename,
8650
8684
  path: "",
@@ -8769,6 +8803,106 @@ function getStatsFileName(manifestFileName) {
8769
8803
  return parsed.dir ? normalizePathForImport(path$1.join(parsed.dir, fileName)) : fileName;
8770
8804
  }
8771
8805
  //#endregion
8806
+ //#region src/utils/sharedSource.ts
8807
+ const pendingEntryLookups = /* @__PURE__ */ new Map();
8808
+ const getEntryLookupKey = (source, importer) => `${source}\0${importer}`;
8809
+ /** Whether a resolveId call is one of the shared source resolver's own entry lookups. */
8810
+ function isSharedEntryLookup(source, importer, options) {
8811
+ return options.custom?.__mfSharedEntryLookup === true || pendingEntryLookups.has(getEntryLookupKey(source, importer));
8812
+ }
8813
+ /**
8814
+ * Runs one of the plugin's own `this.resolve()` calls (a shared entry lookup, a
8815
+ * parse-barrier external probe), marked so `isSharedEntryLookup` recognizes it
8816
+ * and the share hooks neither proxy it nor register a used share for it.
8817
+ */
8818
+ async function resolveInternally(context, source, importer, options = {}) {
8819
+ const lookupKey = getEntryLookupKey(source, importer);
8820
+ pendingEntryLookups.set(lookupKey, (pendingEntryLookups.get(lookupKey) ?? 0) + 1);
8821
+ try {
8822
+ return await context.resolve(source, importer, {
8823
+ ...options,
8824
+ skipSelf: true,
8825
+ custom: {
8826
+ ...options.custom,
8827
+ __mfSharedEntryLookup: true
8828
+ }
8829
+ });
8830
+ } finally {
8831
+ const remaining = pendingEntryLookups.get(lookupKey) - 1;
8832
+ if (remaining) pendingEntryLookups.set(lookupKey, remaining);
8833
+ else pendingEntryLookups.delete(lookupKey);
8834
+ }
8835
+ }
8836
+ /** Identify shared entries through Vite, with a cache scoped to this federation instance. */
8837
+ function createSharedSourceResolver(shared, getResolutionConfig) {
8838
+ const caches = /* @__PURE__ */ new Map();
8839
+ async function matchEntry(source, suffix, resolveEntry) {
8840
+ const packageName = getPackageNameFromNodeModulePath(source);
8841
+ if (!packageName) return;
8842
+ const candidates = /* @__PURE__ */ new Set();
8843
+ if (findSharedKey(packageName, shared)) candidates.add(packageName);
8844
+ if (findSharedKey(suffix, shared)) candidates.add(suffix);
8845
+ for (const key of Object.keys(shared)) {
8846
+ const request = getSharedRequest(key, shared[key]);
8847
+ if (getPackageName(request) !== packageName) continue;
8848
+ if (!request.endsWith("/")) candidates.add(request);
8849
+ for (const subpath of getCommonSharedSubpaths(request)) if (findSharedKey(subpath, shared)) candidates.add(subpath);
8850
+ }
8851
+ if (candidates.size === 0) return;
8852
+ const resolvedSource = await resolveEntry(source);
8853
+ if (!resolvedSource) return;
8854
+ for (const candidate of candidates) {
8855
+ const entry = await resolveEntry(candidate);
8856
+ if (!entry) continue;
8857
+ const allowNodeModulesSuffixMatch = shared[findSharedKey(candidate, shared) ?? candidate]?.shareConfig?.allowNodeModulesSuffixMatch === true;
8858
+ if (normalizeNodeModulePath(entry) === normalizeNodeModulePath(resolvedSource) || allowNodeModulesSuffixMatch && getNodeModulesSuffix(entry) === suffix) return candidate;
8859
+ }
8860
+ }
8861
+ return {
8862
+ clear() {
8863
+ caches.clear();
8864
+ },
8865
+ async resolve(context, source, options = {}) {
8866
+ if (source.startsWith("\0") || source.includes("#")) return;
8867
+ const queryIndex = source.indexOf("?");
8868
+ const query = queryIndex === -1 ? "" : source.slice(queryIndex + 1);
8869
+ if (query && [...new URLSearchParams(query).keys()].some((key) => ![
8870
+ "v",
8871
+ "t",
8872
+ "import"
8873
+ ].includes(key))) return;
8874
+ if (findSharedKey(source, shared)) return source;
8875
+ const suffix = getNodeModulesSuffix(source);
8876
+ if (!suffix) return;
8877
+ const normalizedSource = stripViteFsPrefix(normalizeNodeModulePath(source));
8878
+ const environment = context.environment ?? !!options.ssr;
8879
+ const cacheable = !options.isEntry && !options.scan && (!options.kind || options.kind === "import-statement") && Object.keys(options.attributes ?? {}).length === 0 && Object.keys(options.custom ?? {}).length === 0;
8880
+ let cache = cacheable ? caches.get(environment) : void 0;
8881
+ if (cacheable && !cache) {
8882
+ cache = /* @__PURE__ */ new Map();
8883
+ caches.set(environment, cache);
8884
+ }
8885
+ if (cache?.has(normalizedSource)) return cache.get(normalizedSource);
8886
+ const { root, conditions } = getResolutionConfig(context, options);
8887
+ const result = await matchEntry(normalizedSource, suffix, async (request) => {
8888
+ if (!path$1.isAbsolute(request) && !isPackageExportAvailable(request, {
8889
+ cwd: root,
8890
+ conditions
8891
+ })) return void 0;
8892
+ const importer = path$1.join(root, "package.json");
8893
+ try {
8894
+ const resolved = await resolveInternally(context, request, importer, options);
8895
+ return resolved && !resolved.external ? resolved.id : void 0;
8896
+ } catch {
8897
+ return;
8898
+ }
8899
+ });
8900
+ cache?.set(normalizedSource, result);
8901
+ return result;
8902
+ }
8903
+ };
8904
+ }
8905
+ //#endregion
8772
8906
  //#region src/plugins/pluginModuleParseEnd.ts
8773
8907
  function createModuleParseController() {
8774
8908
  return {
@@ -8910,7 +9044,7 @@ function pluginModuleParseEnd_default(excludeFn, options, controller = createMod
8910
9044
  if (typeof this.resolve !== "function") return;
8911
9045
  if (controller.resolutionProbed.has(pendingId)) return;
8912
9046
  controller.resolutionProbed.add(pendingId);
8913
- this.resolve(pendingId, id, { skipSelf: true }).then((resolved) => {
9047
+ resolveInternally(this, pendingId, id).then((resolved) => {
8914
9048
  if (!resolved?.external) return;
8915
9049
  controller.externalSet.add(pendingId);
8916
9050
  controller.parseStartSet.delete(pendingId);
@@ -8919,7 +9053,7 @@ function pluginModuleParseEnd_default(excludeFn, options, controller = createMod
8919
9053
  };
8920
9054
  const addPendingIds = (ids) => {
8921
9055
  for (const pendingId of ids || []) {
8922
- if (!this.getModuleInfo(pendingId) || controller.externalSet.has(pendingId) || matchesExternal(configuredExternal, pendingId, id) || excludeFn(pendingId)) continue;
9056
+ if (controller.externalSet.has(pendingId) || matchesExternal(configuredExternal, pendingId, id) || excludeFn(pendingId)) continue;
8923
9057
  controller.parseStartSet.add(pendingId);
8924
9058
  if (!controller.parseEndSet.has(pendingId)) probeExternal(pendingId);
8925
9059
  }
@@ -9219,96 +9353,6 @@ function pluginProxyRemotes_default(options) {
9219
9353
  };
9220
9354
  }
9221
9355
  //#endregion
9222
- //#region src/utils/sharedSource.ts
9223
- const pendingEntryLookups = /* @__PURE__ */ new Map();
9224
- const getEntryLookupKey = (source, importer) => `${source}\0${importer}`;
9225
- /** Whether a resolveId call is one of the shared source resolver's own entry lookups. */
9226
- function isSharedEntryLookup(source, importer, options) {
9227
- return options.custom?.__mfSharedEntryLookup === true || pendingEntryLookups.has(getEntryLookupKey(source, importer));
9228
- }
9229
- /** Identify shared entries through Vite, with a cache scoped to this federation instance. */
9230
- function createSharedSourceResolver(shared, getResolutionConfig) {
9231
- const caches = /* @__PURE__ */ new Map();
9232
- async function matchEntry(source, suffix, resolveEntry) {
9233
- const packageName = getPackageNameFromNodeModulePath(source);
9234
- if (!packageName) return;
9235
- const candidates = /* @__PURE__ */ new Set();
9236
- if (findSharedKey(packageName, shared)) candidates.add(packageName);
9237
- if (findSharedKey(suffix, shared)) candidates.add(suffix);
9238
- for (const key of Object.keys(shared)) {
9239
- const request = getSharedRequest(key, shared[key]);
9240
- if (getPackageName(request) !== packageName) continue;
9241
- if (!request.endsWith("/")) candidates.add(request);
9242
- for (const subpath of getCommonSharedSubpaths(request)) if (findSharedKey(subpath, shared)) candidates.add(subpath);
9243
- }
9244
- if (candidates.size === 0) return;
9245
- const resolvedSource = await resolveEntry(source);
9246
- if (!resolvedSource) return;
9247
- for (const candidate of candidates) {
9248
- const entry = await resolveEntry(candidate);
9249
- if (!entry) continue;
9250
- const allowNodeModulesSuffixMatch = shared[findSharedKey(candidate, shared) ?? candidate]?.shareConfig?.allowNodeModulesSuffixMatch === true;
9251
- if (normalizeNodeModulePath(entry) === normalizeNodeModulePath(resolvedSource) || allowNodeModulesSuffixMatch && getNodeModulesSuffix(entry) === suffix) return candidate;
9252
- }
9253
- }
9254
- return {
9255
- clear() {
9256
- caches.clear();
9257
- },
9258
- async resolve(context, source, options = {}) {
9259
- if (source.startsWith("\0") || source.includes("#")) return;
9260
- const queryIndex = source.indexOf("?");
9261
- const query = queryIndex === -1 ? "" : source.slice(queryIndex + 1);
9262
- if (query && [...new URLSearchParams(query).keys()].some((key) => ![
9263
- "v",
9264
- "t",
9265
- "import"
9266
- ].includes(key))) return;
9267
- if (findSharedKey(source, shared)) return source;
9268
- const suffix = getNodeModulesSuffix(source);
9269
- if (!suffix) return;
9270
- const normalizedSource = stripViteFsPrefix(normalizeNodeModulePath(source));
9271
- const environment = context.environment ?? !!options.ssr;
9272
- const cacheable = !options.isEntry && !options.scan && (!options.kind || options.kind === "import-statement") && Object.keys(options.attributes ?? {}).length === 0 && Object.keys(options.custom ?? {}).length === 0;
9273
- let cache = cacheable ? caches.get(environment) : void 0;
9274
- if (cacheable && !cache) {
9275
- cache = /* @__PURE__ */ new Map();
9276
- caches.set(environment, cache);
9277
- }
9278
- if (cache?.has(normalizedSource)) return cache.get(normalizedSource);
9279
- const { root, conditions } = getResolutionConfig(context, options);
9280
- const result = await matchEntry(normalizedSource, suffix, async (request) => {
9281
- if (!path$1.isAbsolute(request) && !isPackageExportAvailable(request, {
9282
- cwd: root,
9283
- conditions
9284
- })) return void 0;
9285
- const importer = path$1.join(root, "package.json");
9286
- const lookupKey = getEntryLookupKey(request, importer);
9287
- pendingEntryLookups.set(lookupKey, (pendingEntryLookups.get(lookupKey) ?? 0) + 1);
9288
- try {
9289
- const resolved = await context.resolve(request, importer, {
9290
- ...options,
9291
- skipSelf: true,
9292
- custom: {
9293
- ...options.custom,
9294
- __mfSharedEntryLookup: true
9295
- }
9296
- });
9297
- return resolved && !resolved.external ? resolved.id : void 0;
9298
- } catch {
9299
- return;
9300
- } finally {
9301
- const remaining = pendingEntryLookups.get(lookupKey) - 1;
9302
- if (remaining) pendingEntryLookups.set(lookupKey, remaining);
9303
- else pendingEntryLookups.delete(lookupKey);
9304
- }
9305
- });
9306
- cache?.set(normalizedSource, result);
9307
- return result;
9308
- }
9309
- };
9310
- }
9311
- //#endregion
9312
9356
  //#region src/utils/PromiseStore.ts
9313
9357
  /**
9314
9358
  * example:
@@ -9522,8 +9566,9 @@ function isSharedPackageDependency(sharedKey, dependency) {
9522
9566
  function isConfiguredSharedPackage(pkg, shared) {
9523
9567
  return Object.keys(shared).some((key) => getPackageName(key) === pkg);
9524
9568
  }
9525
- function shouldKeepSharedImportLocal(source, importer, sharedKey, shared, importerPackage) {
9569
+ function shouldKeepSharedImportLocal(source, importer, sharedKey, shared, importerPackage, federationOptions) {
9526
9570
  if (!importer || shared[sharedKey]?.shareConfig.import === false) return false;
9571
+ if (hasRemotes(federationOptions)) return false;
9527
9572
  const prebuildImporter = importer.includes("__prebuild__") ? VirtualModule.findModule(PREBUILD_TAG, importer) : void 0;
9528
9573
  const fallbackPackage = prebuildImporter ? getPackageName(prebuildImporter.name) : importerPackage;
9529
9574
  if (!fallbackPackage || !isConfiguredSharedPackage(fallbackPackage, shared)) return false;
@@ -9941,7 +9986,7 @@ function proxySharedModule(options) {
9941
9986
  if (shouldSkipTaggedImporterProxy(key, "__loadShare__")) return;
9942
9987
  if (shouldSkipTaggedImporterProxy(key, "__prebuild__")) return;
9943
9988
  const shareSource = key === "vue" && sharedSource.startsWith("vue/dist/") ? key : sharedSource;
9944
- if (shouldKeepSharedImportLocal(sharedSource, importer, key, shared, importerPackage)) {
9989
+ if (shouldKeepSharedImportLocal(shareSource, importer, key, shared, importerPackage, federationOptions)) {
9945
9990
  const localSource = getPrebuildResolutionSource(shareSource, shared[key]);
9946
9991
  return tryResolveFromProjectRoot(localSource) || localSource;
9947
9992
  }
@@ -10900,6 +10945,360 @@ function isTestEnv() {
10900
10945
  return process.env.NODE_ENV === "test" || process.env.VITEST != null || process.env.JEST_WORKER_ID != null;
10901
10946
  }
10902
10947
  //#endregion
10948
+ //#region src/utils/hostProvidesAllShared.ts
10949
+ /** Virtual-module prefix of the host's per-share re-export entries. */
10950
+ const SHARE_MODULE_PREFIX = "\0mf-host-provided-share:";
10951
+ /** `./a/b` → `a/b`, `.` → `index`: the fixed entry name of an expose, without `.js`. */
10952
+ function exposeEntryName(exposeKey) {
10953
+ return exposeKey === "." ? "index" : exposeKey.replace(/^\.\//, "");
10954
+ }
10955
+ /** Bare specifier a host uses for an expose: `remote` for `.`, `remote/a/b` otherwise. */
10956
+ function remoteSpecifier(remoteName, exposeKey) {
10957
+ const entry = exposeEntryName(exposeKey);
10958
+ return entry === "index" ? remoteName : `${remoteName}/${entry}`;
10959
+ }
10960
+ /** `id` is a remote alias or a subpath of one. */
10961
+ function isRemoteSpecifier(id, remoteKeys) {
10962
+ return remoteKeys.some((key) => id === key || id.startsWith(`${key}/`));
10963
+ }
10964
+ /**
10965
+ * The shared key that owns `id`: an exact match, else the longest key `id` is a subpath
10966
+ * of (`lodash-es/debounce` → `lodash-es`). Used to spot an unshared subpath of a shared
10967
+ * package, which would bundle a private copy of that package into a remote.
10968
+ */
10969
+ function sharedKeyOwning(id, sharedKeys) {
10970
+ let owner;
10971
+ for (const key of sharedKeys) {
10972
+ if (id !== key && !id.startsWith(`${key}/`)) continue;
10973
+ if (!owner || key.length > owner.length) owner = key;
10974
+ }
10975
+ return owner;
10976
+ }
10977
+ /** Chunk name of the host's re-export entry for a shared key. */
10978
+ function shareChunkName(key) {
10979
+ return `shared/${key.replace(/^@/, "").replace(/[^\w-]+/g, "-")}`;
10980
+ }
10981
+ /** Reads a remote's `mf-manifest.json` from a URL or a path relative to `root`. */
10982
+ async function readHostProvidesAllSharedManifest(location, root) {
10983
+ let source;
10984
+ if (isAbsoluteUrl(location)) {
10985
+ const response = await fetch(location);
10986
+ if (!response.ok) throw new Error(`Failed to fetch ${location}: ${response.status} ${response.statusText}`);
10987
+ source = await response.text();
10988
+ } else source = await readFile(path$1.resolve(root, location), "utf8");
10989
+ const manifest = JSON.parse(source);
10990
+ if (!manifest.metaData || !Array.isArray(manifest.exposes)) throw new Error(`${location} is not a Module Federation manifest.`);
10991
+ if (manifest.metaData.remoteEntry) throw new Error(`${location} describes a Module Federation runtime remote (it has a remoteEntry). Build the remote with \`experiments: { hostProvidesAllShared: true }\`.`);
10992
+ return manifest;
10993
+ }
10994
+ /** URL prefix of a remote's files: `metaData.publicPath`, `auto` inferred from the manifest URL. */
10995
+ function remotePublicPath(manifest, location) {
10996
+ const publicPath = manifest.metaData.publicPath ?? "auto";
10997
+ if (publicPath !== "auto") return ensureTrailingSlash(publicPath);
10998
+ if (!isAbsoluteUrl(location)) throw new Error(`${location}: publicPath is "auto", which can only be inferred from a manifest URL. Reference the manifest by URL or set \`base\` / \`publicPath\` in the remote build.`);
10999
+ return ensureTrailingSlash(inferAutoPublicPath(location));
11000
+ }
11001
+ /** Import-map entries (`remote/Expose` → URL) for every expose of every remote. */
11002
+ async function collectRemoteImports(remotes, root) {
11003
+ const imports = {};
11004
+ for (const [alias, remote] of Object.entries(remotes)) {
11005
+ const manifest = await readHostProvidesAllSharedManifest(remote.entry, root);
11006
+ const publicPath = remotePublicPath(manifest, remote.entry);
11007
+ for (const expose of manifest.exposes) {
11008
+ const file = expose.assets.js.sync[0];
11009
+ if (!expose.path || !file) continue;
11010
+ imports[remoteSpecifier(alias, expose.path)] = isAbsoluteUrl(remote.entry) ? new URL(file, new URL(publicPath, remote.entry)).href : publicPath + file;
11011
+ }
11012
+ }
11013
+ return imports;
11014
+ }
11015
+ /** Top-level options that only the Module Federation runtime or `remoteEntry.js` pipeline reads. */
11016
+ const IGNORED_OPTIONS = [
11017
+ "filename",
11018
+ "ssrFilename",
11019
+ "varFilename",
11020
+ "library",
11021
+ "runtime",
11022
+ "shareScope",
11023
+ "shareStrategy",
11024
+ "runtimePlugins",
11025
+ "implementation",
11026
+ "getPublicPath",
11027
+ "ignoreOrigin",
11028
+ "virtualModuleDir",
11029
+ "hostInitInjectLocation",
11030
+ "bundleAllCSS",
11031
+ "treeShakingDir",
11032
+ "injectTreeShakingUsedExports",
11033
+ "treeShakingSharedPlugins",
11034
+ "treeShakingSharedExcludePlugins",
11035
+ "moduleParseTimeout",
11036
+ "moduleParseIdleTimeout",
11037
+ "target",
11038
+ "ssrExternals",
11039
+ "ssrEntryLoader",
11040
+ "disableRemote",
11041
+ "disableShared",
11042
+ "disableSnapshot"
11043
+ ];
11044
+ const IGNORED_EXPERIMENTS = [
11045
+ "externalRuntime",
11046
+ "provideExternalRuntime",
11047
+ "ssrMode"
11048
+ ];
11049
+ /** Per-share options: version negotiation, aliasing and fallbacks do not exist in this mode. */
11050
+ const IGNORED_SHARE_OPTIONS = [
11051
+ "eager",
11052
+ "requiredVersion",
11053
+ "strictVersion",
11054
+ "import",
11055
+ "request",
11056
+ "shareKey",
11057
+ "name",
11058
+ "version",
11059
+ "shareScope",
11060
+ "treeShaking",
11061
+ "allowNodeModulesSuffixMatch",
11062
+ "suppressMissingImportWarning"
11063
+ ];
11064
+ const isSet = (value) => value !== void 0 && value !== false && !(Array.isArray(value) && value.length === 0);
11065
+ /** A single warning naming every option the user set that import-map mode ignores. */
11066
+ function getIgnoredOptionWarning(options) {
11067
+ const shared = options.shared;
11068
+ const ignored = [
11069
+ ...IGNORED_OPTIONS.filter((key) => isSet(options[key])).map((key) => `\`${key}\``),
11070
+ ...IGNORED_EXPERIMENTS.filter((key) => isSet(options.experiments?.[key])).map((key) => `\`experiments.${key}\``),
11071
+ ...shared && !Array.isArray(shared) ? Object.entries(shared).flatMap(([key, config]) => typeof config === "object" && config !== null ? IGNORED_SHARE_OPTIONS.filter((option) => option === "import" ? config.import !== void 0 : isSet(config[option])).map((option) => `\`shared.${key}.${option}\``) : []) : []
11072
+ ];
11073
+ if (ignored.length === 0) return void 0;
11074
+ return `experiments.hostProvidesAllShared: ${ignored.join(", ")} ignored. The Module Federation runtime is not used and the host provides the only copy of every shared key.`;
11075
+ }
11076
+ //#endregion
11077
+ //#region src/plugins/pluginHostProvidesAllSharedHost.ts
11078
+ const ESM_SYNTAX_RE = /(^|[\s;])(export|import)[\s{*]/m;
11079
+ const DEFAULT_EXPORT_RE = /export\s+default\b|export\s*\{[^}]*\bas\s+default\b/;
11080
+ const IDENTIFIER_RE = /^[A-Za-z_$][\w$]*$/;
11081
+ /**
11082
+ * Export names of a CommonJS package, read by loading it in Node from the project root
11083
+ * (what the bundler's interop exposes as named imports). Empty when it cannot be loaded
11084
+ * in Node, e.g. because it touches `window`.
11085
+ */
11086
+ function cjsExportNames(key, root) {
11087
+ try {
11088
+ const exported = createRequire(path$1.join(root, "package.json"))(key);
11089
+ return Object.keys(exported ?? {}).filter((name) => name !== "default" && name !== "__esModule" && IDENTIFIER_RE.test(name));
11090
+ } catch {
11091
+ return [];
11092
+ }
11093
+ }
11094
+ /** Shape of a shared module, sniffed from its real package entry (never a pre-bundle). */
11095
+ function detectShareShape(key, entry, root) {
11096
+ const source = entry && fs$1.existsSync(entry) ? fs$1.readFileSync(entry, "utf8") : "";
11097
+ if (source !== "" && !ESM_SYNTAX_RE.test(source)) return {
11098
+ kind: "cjs",
11099
+ names: cjsExportNames(key, root)
11100
+ };
11101
+ return {
11102
+ kind: "esm",
11103
+ hasDefault: DEFAULT_EXPORT_RE.test(source)
11104
+ };
11105
+ }
11106
+ /**
11107
+ * Body of the re-export entry for one shared key. `export *` never forwards `default` and
11108
+ * forwards nothing named from CommonJS, so an ESM module adds `default` only when it has
11109
+ * one, and a CommonJS module re-exports each key explicitly (`react/jsx-runtime` → `jsx`,
11110
+ * `jsxs`) so remotes can `import { jsxs }`.
11111
+ */
11112
+ function shareModuleCode(key, shape) {
11113
+ const spec = JSON.stringify(key);
11114
+ if (shape.kind === "cjs") return `import __mf_cjs from ${spec};\nexport default __mf_cjs;\n${shape.names.length ? `export const { ${shape.names.join(", ")} } = __mf_cjs;\n` : ""}`;
11115
+ return `export * from ${spec};\n${shape.hasDefault ? `export { default } from ${spec};\n` : ""}`;
11116
+ }
11117
+ /** BUILD: shared key → URL of its emitted entry chunk. */
11118
+ function bundledShareImports(bundle, base) {
11119
+ const imports = {};
11120
+ for (const chunk of Object.values(bundle)) if (chunk.type === "chunk" && chunk.facadeModuleId?.startsWith("\0mf-host-provided-share:")) imports[chunk.facadeModuleId.slice(24)] = `${base}${chunk.fileName}`;
11121
+ return imports;
11122
+ }
11123
+ /**
11124
+ * DEV: shared key → dev-server URL of its virtual re-export module. Vite serves `\0` ids
11125
+ * at `/@id/__x00__<id>`; import analysis then rewrites the module's own `from '<key>'` to
11126
+ * the same pre-bundle URL the host's code gets, so host and remotes share one instance.
11127
+ */
11128
+ function devShareImports(sharedKeys, base) {
11129
+ const prefix = `${base}@id/__x00__${SHARE_MODULE_PREFIX.slice(1)}`;
11130
+ return Object.fromEntries(sharedKeys.map((key) => [key, `${prefix}${key}`]));
11131
+ }
11132
+ function importMapTag(imports) {
11133
+ return {
11134
+ tag: "script",
11135
+ attrs: { type: "importmap" },
11136
+ children: JSON.stringify({ imports }, null, 2),
11137
+ injectTo: "head-prepend"
11138
+ };
11139
+ }
11140
+ /**
11141
+ * Host side of `experiments.hostProvidesAllShared`. The host provides every shared key:
11142
+ *
11143
+ * 1. one re-export module per shared key, emitted in a build as a `preserveSignature:
11144
+ * 'strict'` entry chunk that the bundler dedupes with the host's own imports, and served
11145
+ * in dev as a virtual module; either way host and remotes land on one module instance;
11146
+ * 2. an import map in `index.html` pointing each shared key at that module and each remote
11147
+ * expose at the entry listed in the remote's `mf-manifest.json`.
11148
+ */
11149
+ function pluginHostProvidesAllSharedHost(options) {
11150
+ const sharedKeys = Object.keys(options.shared);
11151
+ const remoteKeys = Object.keys(options.remotes);
11152
+ let isBuild = true;
11153
+ let base = "/";
11154
+ let root = process.cwd();
11155
+ let remoteImports = {};
11156
+ let resolveEntry;
11157
+ return {
11158
+ name: "module-federation:host-provides-all-shared-host",
11159
+ enforce: "pre",
11160
+ configResolved(config) {
11161
+ isBuild = config.command === "build";
11162
+ base = ensureTrailingSlash(config.base);
11163
+ root = config.root;
11164
+ resolveEntry = isBuild ? void 0 : config.createResolver();
11165
+ },
11166
+ async buildStart() {
11167
+ remoteImports = await collectRemoteImports(options.remotes, root);
11168
+ if (!isBuild) return;
11169
+ for (const key of sharedKeys) this.emitFile({
11170
+ type: "chunk",
11171
+ id: `${SHARE_MODULE_PREFIX}${key}`,
11172
+ name: shareChunkName(key),
11173
+ preserveSignature: "strict"
11174
+ });
11175
+ },
11176
+ resolveId(id) {
11177
+ if (id.startsWith("\0mf-host-provided-share:")) return id;
11178
+ if (!isRemoteSpecifier(id, remoteKeys)) return null;
11179
+ return {
11180
+ id: isBuild ? id : remoteImports[id] ?? id,
11181
+ external: true
11182
+ };
11183
+ },
11184
+ async load(id) {
11185
+ if (!id.startsWith("\0mf-host-provided-share:")) return null;
11186
+ const key = id.slice(24);
11187
+ const entry = resolveEntry ? await resolveEntry(key) : (await this.resolve(key, void 0, { skipSelf: true }))?.id;
11188
+ return shareModuleCode(key, detectShareShape(key, entry && stripQueryAndHash(entry), root));
11189
+ },
11190
+ transformIndexHtml: {
11191
+ order: "post",
11192
+ handler(html, context) {
11193
+ const shares = context.bundle ? bundledShareImports(context.bundle, base) : devShareImports(sharedKeys, base);
11194
+ return {
11195
+ html,
11196
+ tags: [importMapTag({
11197
+ ...remoteImports,
11198
+ ...shares
11199
+ })]
11200
+ };
11201
+ }
11202
+ }
11203
+ };
11204
+ }
11205
+ //#endregion
11206
+ //#region src/plugins/pluginHostProvidesAllSharedRemote.ts
11207
+ /**
11208
+ * Remote side of `experiments.hostProvidesAllShared`. The remote ships only its own code:
11209
+ *
11210
+ * - one fixed-name, signature-preserving entry per expose (`./Button` → `Button.js`), so
11211
+ * the host can map `<remote>/Button` to a stable URL;
11212
+ * - every shared key, and every configured remote, stays an external bare import that the
11213
+ * host's import map resolves at runtime;
11214
+ * - a standard `mf-manifest.json` without `remoteEntry`, listing each expose's entry file.
11215
+ */
11216
+ function pluginHostProvidesAllSharedRemote(options) {
11217
+ const sharedKeys = Object.keys(options.shared);
11218
+ const remoteKeys = Object.keys(options.remotes);
11219
+ const isExternal = (id) => sharedKeys.includes(id) || isRemoteSpecifier(id, remoteKeys);
11220
+ const unsharedSubpaths = /* @__PURE__ */ new Map();
11221
+ let publicPath = "/";
11222
+ return {
11223
+ name: "module-federation:host-provides-all-shared-remote",
11224
+ enforce: "pre",
11225
+ apply: "build",
11226
+ config(config) {
11227
+ const root = path$1.resolve(config.root ?? process.cwd());
11228
+ return { build: { rollupOptions: {
11229
+ input: Object.fromEntries(Object.entries(options.exposes).map(([key, expose]) => [exposeEntryName(key), path$1.resolve(root, expose.import)])),
11230
+ external: isExternal,
11231
+ preserveEntrySignatures: "strict",
11232
+ output: {
11233
+ entryFileNames: "[name].js",
11234
+ chunkFileNames: "assets/[name]-[hash].js"
11235
+ }
11236
+ } } };
11237
+ },
11238
+ configResolved(config) {
11239
+ publicPath = options.publicPath ?? config.base;
11240
+ },
11241
+ buildStart() {
11242
+ unsharedSubpaths.clear();
11243
+ },
11244
+ resolveId(id, importer) {
11245
+ if (importer && !isExternal(id) && sharedKeyOwning(id, sharedKeys)) unsharedSubpaths.set(id, importer);
11246
+ return null;
11247
+ },
11248
+ buildEnd() {
11249
+ for (const [id, importer] of unsharedSubpaths) this.warn(`"${id}" (imported by ${importer}) is a subpath of a shared package but is not shared itself, so a private copy is bundled into this remote. Add it to \`shared\`.`);
11250
+ },
11251
+ generateBundle(_outputOptions, bundle) {
11252
+ const { name } = options;
11253
+ const exposes = [];
11254
+ for (const key of Object.keys(options.exposes)) {
11255
+ const entryName = exposeEntryName(key);
11256
+ const chunk = Object.values(bundle).find((item) => item.type === "chunk" && item.isEntry && item.name === entryName);
11257
+ if (!chunk) continue;
11258
+ exposes.push({
11259
+ id: `${name}:${entryName}`,
11260
+ name: entryName,
11261
+ path: key,
11262
+ assets: {
11263
+ js: {
11264
+ sync: [chunk.fileName],
11265
+ async: []
11266
+ },
11267
+ css: {
11268
+ sync: [],
11269
+ async: []
11270
+ }
11271
+ }
11272
+ });
11273
+ }
11274
+ const manifest = {
11275
+ id: name,
11276
+ name,
11277
+ metaData: {
11278
+ name,
11279
+ type: "app",
11280
+ buildInfo: {
11281
+ buildVersion: getBuildVersion(),
11282
+ buildName: name
11283
+ },
11284
+ globalName: name,
11285
+ pluginVersion: version$1,
11286
+ publicPath,
11287
+ types: resolveTypesMeta(options.dts)
11288
+ },
11289
+ shared: [],
11290
+ remotes: [],
11291
+ exposes
11292
+ };
11293
+ this.emitFile({
11294
+ type: "asset",
11295
+ fileName: typeof options.manifest === "object" && options.manifest.fileName || "mf-manifest.json",
11296
+ source: `${JSON.stringify(manifest, null, 2)}\n`
11297
+ });
11298
+ }
11299
+ };
11300
+ }
11301
+ //#endregion
10903
11302
  //#region src/utils/normalizeOptimizeDeps.ts
10904
11303
  var normalizeOptimizeDeps_default = {
10905
11304
  name: "normalizeOptimizeDeps",
@@ -11514,9 +11913,21 @@ function applyBuildTimeRuntimeDefines(define, options, { target, isAstro, defaul
11514
11913
  });
11515
11914
  if (options.target && define.ENV_TARGET !== JSON.stringify(options.target)) mfWarn(`ENV_TARGET define (${define.ENV_TARGET}) differs from target option ("${options.target}"). ENV_TARGET will not be overridden.`);
11516
11915
  }
11916
+ /** Dynamic remote type hints are a runtime plugin; hostProvidesAllShared has no runtime. */
11917
+ function withoutDynamicRemoteTypeHints(options) {
11918
+ if (options.dev === false) return options;
11919
+ const dev = options.dev === true || options.dev === void 0 ? {} : options.dev;
11920
+ return {
11921
+ ...options,
11922
+ dev: {
11923
+ ...dev,
11924
+ disableDynamicRemoteTypeHints: true
11925
+ }
11926
+ };
11927
+ }
11517
11928
  function loadPluginDts(options) {
11518
11929
  if (options.dts === false) return [];
11519
- return [import("./pluginDts-DxgS2FEd.js").then(({ default: pluginDts }) => pluginDts(options))];
11930
+ return [import("./pluginDts-DnEX3cp_.js").then(({ default: pluginDts }) => pluginDts(options))];
11520
11931
  }
11521
11932
  const INJECT_EXTERNAL_RUNTIME_CORE_PLUGIN = "@module-federation/vite/injectExternalRuntimeCorePlugin";
11522
11933
  function isInjectExternalRuntimeCorePlugin(specifier) {
@@ -11545,11 +11956,16 @@ function applyExternalRuntimeExperiments(options) {
11545
11956
  function federation(mfUserOptions) {
11546
11957
  if (isTestEnv()) return [];
11547
11958
  const options = normalizeModuleFederationOptions(mfUserOptions);
11959
+ if (!options.name) throw createModuleFederationError("name is required");
11960
+ if (options.experiments.hostProvidesAllShared) {
11961
+ const ignored = getIgnoredOptionWarning(mfUserOptions);
11962
+ if (ignored) mfWarn(ignored);
11963
+ return [Object.keys(options.exposes).length > 0 ? pluginHostProvidesAllSharedRemote(options) : pluginHostProvidesAllSharedHost(options), ...loadPluginDts(withoutDynamicRemoteTypeHints(options))];
11964
+ }
11548
11965
  applyExternalRuntimeExperiments(options);
11549
11966
  const isVinext = hasPackageDependency("vinext");
11550
- const { name, shared, filename, hostInitInjectLocation } = options;
11967
+ const { shared, filename, hostInitInjectLocation } = options;
11551
11968
  const hasTreeShakingShared = Object.values(shared).some((share) => !!share.shareConfig.treeShaking);
11552
- if (!name) throw createModuleFederationError("name is required");
11553
11969
  const remoteEntryId = getRemoteEntryId(options);
11554
11970
  const virtualExposesId = getVirtualExposesId(options);
11555
11971
  const moduleParseController = createModuleParseController();
@@ -11567,6 +11983,7 @@ function federation(mfUserOptions) {
11567
11983
  let rootResolveConditions;
11568
11984
  let ssrResolveConditions;
11569
11985
  let ssrTarget = "node";
11986
+ let inheritedEnvTarget;
11570
11987
  const emittedRuntimeCapabilityWarnings = /* @__PURE__ */ new Set();
11571
11988
  const getLoadHookExportConditions = (context, loadOptions) => {
11572
11989
  const environment = context.environment;
@@ -12041,11 +12458,13 @@ function federation(mfUserOptions) {
12041
12458
  const isAstro = hasPackageDependency("astro");
12042
12459
  const resolvedTarget = options.target ?? (config.build?.ssr ? "node" : "web");
12043
12460
  if (!config.define) config.define = {};
12461
+ const userSetEnvTarget = "ENV_TARGET" in config.define;
12044
12462
  applyBuildTimeRuntimeDefines(config.define, options, {
12045
12463
  target: resolvedTarget,
12046
12464
  isAstro,
12047
12465
  defaultDisableSnapshot: resolvedTarget === "node" ? true : void 0
12048
12466
  });
12467
+ inheritedEnvTarget = userSetEnvTarget ? void 0 : config.define.ENV_TARGET;
12049
12468
  for (const warning of getRuntimeCapabilityConfigurationWarnings(options)) {
12050
12469
  if (emittedRuntimeCapabilityWarnings.has(warning)) continue;
12051
12470
  emittedRuntimeCapabilityWarnings.add(warning);
@@ -12061,6 +12480,7 @@ function federation(mfUserOptions) {
12061
12480
  if (!isServerEnvironment(name, config)) return;
12062
12481
  const isAstro = hasPackageDependency("astro");
12063
12482
  config.define = { ...config.define ?? {} };
12483
+ if (inheritedEnvTarget !== void 0 && config.define.ENV_TARGET === inheritedEnvTarget) delete config.define.ENV_TARGET;
12064
12484
  applyBuildTimeRuntimeDefines(config.define, options, {
12065
12485
  target: options.target ?? "node",
12066
12486
  isAstro,
@@ -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 };
@@ -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-r5OfegUr.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,4 @@
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
2
  //#region src/utils/fetchWithTimeout.ts
3
3
  const DEFAULT_SSR_FETCH_TIMEOUT_MS = 1e4;
4
4
  const DEFAULT_SSR_FETCH_MAX_BYTES = 10 * 1024 * 1024;
@@ -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-COMHOCN2.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;
@@ -877,7 +904,7 @@ async function loadSSRRemoteEntry(ssrEntry, options) {
877
904
  const sharedPkgMap = new Map(Object.entries(resolvedShared));
878
905
  const extension = type === "commonjs-module" || type === "commonjs" ? ".js" : ".mjs";
879
906
  try {
880
- return await importTempModule(await fetchEsmGraphToTempFile(url, cacheDir, sharedPkgMap, versionKey, options.fetchTimeoutMs, getSsrTransformContextKey(resolvedShared, options.shareScopeName), options.fetchMaxBytes, extension), versionKey);
907
+ return await importTempModule(await fetchEsmGraphToTempFile(url, cacheDir, sharedPkgMap, versionKey, options.fetchTimeoutMs, getSsrTransformContextKey(resolvedShared, options.shareScopeName), options.fetchMaxBytes, extension, rootEntryUrl), versionKey);
881
908
  } catch (error) {
882
909
  if (isSsrEntryHttpError(error) || isSsrFetchBodyTooLargeError(error)) throw error;
883
910
  warnStrategyFallback("temp-file", url, "the federation runtime's loader", error);
@@ -914,11 +941,11 @@ function ssrEntryLoaderPlugin(options = {}) {
914
941
  } : resolved;
915
942
  const ssrEntry = await getSSREntry(remoteInfo.entry, loadOptions.maxAgeMs, loadOptions.fetchTimeoutMs, loadOptions.fetchMaxBytes);
916
943
  if (!ssrEntry) return;
917
- const mod = await loadSSRRemoteEntry(ssrEntry, loadOptions);
944
+ const mod = await loadSSRRemoteEntry(ssrEntry, loadOptions, remoteInfo.entry);
918
945
  if (!mod) return;
919
946
  return mod;
920
947
  }
921
948
  };
922
949
  }
923
950
  //#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 };
951
+ 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-DEH3eV1e.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);
@@ -1,2 +1,2 @@
1
- import { i as ssrEntryLoaderPlugin, n as neutralizeBrowserPreloadHelpers, r as revalidate, t as SsrEntryHttpError } from "../ssrEntryLoader-DmqLXUmm.js";
1
+ import { i as ssrEntryLoaderPlugin, n as neutralizeBrowserPreloadHelpers, r as revalidate, t as SsrEntryHttpError } from "../ssrEntryLoader-DEH3eV1e.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.23.1",
3
+ "version": "1.23.2",
4
4
  "description": "Vite plugin for Module Federation",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",