@module-federation/vite 1.23.6 → 1.23.7

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/lib/index.js CHANGED
@@ -981,16 +981,6 @@ function getSsrCapabilities(viteMajor, command, hasRemotes, isSsr = false) {
981
981
  };
982
982
  }
983
983
  //#endregion
984
- //#region src/utils/runtimePluginSpecifier.ts
985
- function getRuntimePluginSpecifier(plugin) {
986
- return typeof plugin === "string" ? plugin : plugin[0];
987
- }
988
- /** Drop SSR-only plugins from a client graph; a server graph keeps them all. */
989
- function filterRuntimePluginsForTarget(runtimePlugins, ssrOnlySpecifiers, isServer) {
990
- if (isServer) return [...runtimePlugins];
991
- return runtimePlugins.filter((plugin) => !ssrOnlySpecifiers.has(getRuntimePluginSpecifier(plugin)));
992
- }
993
- //#endregion
994
984
  //#region src/utils/serializeRuntimeOptions.ts
995
985
  const UNSAFE_JS_CHAR_MAP = {
996
986
  "<": "\\u003C",
@@ -1003,6 +993,11 @@ function toSafeJsLiteral(value) {
1003
993
  if (json === void 0) return "undefined";
1004
994
  return json.replace(UNSAFE_JS_CHAR_PATTERN, (char) => UNSAFE_JS_CHAR_MAP[char]);
1005
995
  }
996
+ const JS_IDENTIFIER_PATTERN = `[\$_\\p{ID_Start}][\$_\\u200C\\u200D\\p{ID_Continue}]*`;
997
+ const JS_IDENTIFIER_REGEX = new RegExp(`^${JS_IDENTIFIER_PATTERN}$`, "u");
998
+ function isValidJsIdentifier(name) {
999
+ return JS_IDENTIFIER_REGEX.test(name);
1000
+ }
1006
1001
  /**
1007
1002
  * Serializes a JavaScript object into a string of source code that can be evaluated.
1008
1003
  * This function is used to create runtime plugin options without relying solely on JSON.stringify,
@@ -1477,240 +1472,117 @@ async function collectTreeShakingImports(code, id, shared, findSharedKey, record
1477
1472
  await Promise.all(pending);
1478
1473
  }
1479
1474
  //#endregion
1480
- //#region src/utils/reactIsland.ts
1481
- const SOURCE_EXTENSIONS$1 = [
1482
- ".tsx",
1483
- ".jsx",
1484
- ".ts",
1485
- ".js",
1486
- ".mts",
1487
- ".mjs",
1488
- ".cts",
1489
- ".cjs"
1490
- ];
1491
- function resolveSourceFile(importPath, root) {
1492
- const cleanImport = stripQueryAndHash(importPath);
1493
- if (!cleanImport.startsWith(".") && !path$1.isAbsolute(cleanImport)) return;
1494
- const candidate = path$1.isAbsolute(cleanImport) ? cleanImport : path$1.resolve(root, cleanImport);
1495
- return [
1496
- candidate,
1497
- ...SOURCE_EXTENSIONS$1.map((extension) => `${candidate}${extension}`),
1498
- ...SOURCE_EXTENSIONS$1.map((extension) => path$1.join(candidate, `index${extension}`))
1499
- ].find((filePath) => {
1500
- try {
1501
- return fs$1.statSync(filePath).isFile();
1502
- } catch {
1503
- return false;
1504
- }
1505
- });
1506
- }
1507
- function localDefaultReexport(source) {
1508
- const match = source.match(/export\s*{\s*(?:default(?:\s+as\s+default)?|[A-Za-z_$][\w$]*\s+as\s+default)\s*}\s*from\s*["']([^"']+)["']/);
1509
- return match?.[1]?.startsWith(".") ? match[1] : void 0;
1510
- }
1511
- function isReactComponentSource(source, filePath = "component.tsx") {
1512
- if (!(/\bexport\s+default\b/.test(source) || /\bexport\s*{[^}]*\bdefault\b[^}]*}(?:\s*from\s*["'][^"']+["'])?/.test(source))) return false;
1513
- const extension = path$1.extname(stripQueryAndHash(filePath)).toLowerCase();
1514
- const canContainJsx = extension === ".tsx" || extension === ".jsx";
1515
- const hasJsx = /<>|<\s*[A-Za-z][\w.:-]*(?:\s[^<>]*?)?\s*\/?>/.test(source);
1516
- const importsReact = /\bfrom\s*["']react["']|\brequire\(\s*["']react["']\s*\)/.test(source);
1517
- const usesReactApi = /\b(?:React\.)?(?:createElement|jsx|jsxs)\s*\(/.test(source);
1518
- const isClientModule = /^\s*["']use client["']\s*;?/m.test(source);
1519
- return canContainJsx && hasJsx || importsReact && (hasJsx || usesReactApi || isClientModule);
1475
+ //#region src/utils/typeArgumentScanner.ts
1476
+ function getTypeArgumentStartContext(source, start) {
1477
+ let previous = start - 1;
1478
+ while (previous >= 0 && /\s/.test(source[previous])) previous--;
1479
+ const previousChar = source[previous] || "";
1480
+ const followsNamedExpression = /[$_\u200C\u200D\p{ID_Continue})\]>]/u.test(previousChar);
1481
+ const startsStandaloneGeneric = previousChar !== "" && "=([{,:".includes(previousChar);
1482
+ if (!followsNamedExpression && !startsStandaloneGeneric) return void 0;
1483
+ return { followsNamedExpression };
1520
1484
  }
1521
- function isReactComponentFile(filePath, seen = /* @__PURE__ */ new Set()) {
1522
- const normalizedPath = path$1.resolve(filePath);
1523
- if (seen.has(normalizedPath)) return false;
1524
- seen.add(normalizedPath);
1525
- let source;
1526
- try {
1527
- source = fs$1.readFileSync(normalizedPath, "utf8");
1528
- } catch {
1529
- return false;
1485
+ function updateTypeArgumentGroupDepth(char, state) {
1486
+ if (char === "(" || char === "[" || char === "{") {
1487
+ state.groupDepth++;
1488
+ return "handled";
1530
1489
  }
1531
- if (isReactComponentSource(source, normalizedPath)) return true;
1532
- const reexport = localDefaultReexport(source);
1533
- if (!reexport) return false;
1534
- const reexportPath = resolveSourceFile(reexport, path$1.dirname(normalizedPath));
1535
- return reexportPath ? isReactComponentFile(reexportPath, seen) : false;
1490
+ if (char !== ")" && char !== "]" && char !== "}") return void 0;
1491
+ if (state.groupDepth === 0) return "invalid";
1492
+ state.groupDepth--;
1493
+ return "handled";
1536
1494
  }
1537
- /**
1538
- * React is shared by the normal MF runtime when explicitly configured. When it
1539
- * is local, UI exposes can safely advertise an island capability in addition
1540
- * to their unchanged default export.
1541
- */
1542
- function getReactIslandExposes(options, root) {
1543
- if (options.experiments.ssrMode !== "ISLAND") return /* @__PURE__ */ new Set();
1544
- if (Object.hasOwn(options.shared, "react")) return /* @__PURE__ */ new Set();
1545
- const islandExposes = /* @__PURE__ */ new Set();
1546
- for (const [key, expose] of Object.entries(options.exposes)) {
1547
- const sourceFile = resolveSourceFile(expose.import, root);
1548
- if (sourceFile && isReactComponentFile(sourceFile)) islandExposes.add(key);
1495
+ function updateTypeArgumentAngleDepth(source, index, state) {
1496
+ const char = source[index];
1497
+ if (char === "<") {
1498
+ if (source[index + 1] === "=" || source[index + 1] === "<") return "invalid";
1499
+ state.angleDepth++;
1500
+ return "handled";
1549
1501
  }
1550
- return islandExposes;
1551
- }
1552
- function generateReactIslandBrowserDefinition(enabled) {
1553
- if (!enabled) return "";
1554
- return `
1555
- exportModule.__mf_island = {
1556
- version: 1,
1557
- renderToHtml() {
1558
- return Promise.reject(new Error("[Module Federation] renderToHtml is only available in the SSR remote entry"));
1559
- },
1560
- hydrate(element, props) {
1561
- const root = element && element.hasAttribute && element.hasAttribute("data-mf-island-state")
1562
- ? element
1563
- : element && element.querySelector
1564
- ? element.querySelector("[data-mf-island-state]") || element
1565
- : element;
1566
- if (!root) {
1567
- return Promise.reject(new Error("[Module Federation] Cannot hydrate an island without a root element"));
1568
- }
1569
- let serverProps = {};
1570
- const encodedState = root.getAttribute && root.getAttribute("data-mf-island-state");
1571
- if (encodedState) {
1572
- try {
1573
- serverProps = JSON.parse(decodeURIComponent(encodedState));
1574
- } catch {
1575
- serverProps = {};
1576
- }
1577
- }
1578
- const finalProps = Object.assign({}, serverProps, props || {});
1579
- return Promise.all([import("react"), import("react-dom/client")]).then(([React, ReactDOMClient]) => {
1580
- if (typeof ReactDOMClient.hydrateRoot !== "function") {
1581
- throw new Error("[Module Federation] react-dom/client does not provide hydrateRoot");
1582
- }
1583
- return ReactDOMClient.hydrateRoot(
1584
- root,
1585
- React.createElement(importModule.default, finalProps)
1586
- );
1587
- });
1588
- }
1589
- }`;
1590
- }
1591
- function generateReactIslandSSRDefinition(enabled) {
1592
- if (!enabled) return "";
1593
- return `
1594
- exportModule.__mf_island = {
1595
- version: 1,
1596
- async renderToHtml(props) {
1597
- if (typeof importModule.default !== "function" && typeof importModule.default !== "object") {
1598
- throw new Error("[Module Federation] A React island expose must have a default component export");
1599
- }
1600
- const loadedProps = typeof importModule.load === "function" ? await importModule.load() : {};
1601
- const finalProps = Object.assign({}, loadedProps || {}, props || {});
1602
- const [React, ReactDOMServer] = await Promise.all([
1603
- import("react"),
1604
- import("react-dom/server")
1605
- ]);
1606
- const body = ReactDOMServer.renderToString(
1607
- React.createElement(importModule.default, finalProps)
1608
- );
1609
- const state = encodeURIComponent(JSON.stringify(finalProps));
1610
- return '<div data-mf-island-state="' + state + '">' + body + '</div>';
1611
- },
1612
- hydrate() {
1613
- return Promise.reject(new Error("[Module Federation] hydrate is only available in the browser remote entry"));
1614
- }
1615
- }`;
1616
- }
1617
- const REACT_ISLAND_CLIENT_ID_PREFIX = "virtual:mf-react-island-client:";
1618
- const REACT_ISLAND_SERVER_ID_PREFIX = "virtual:mf-react-island-server:";
1619
- const RESOLVED_REACT_ISLAND_CLIENT_ID_PREFIX = `\0${REACT_ISLAND_CLIENT_ID_PREFIX}`;
1620
- const RESOLVED_REACT_ISLAND_SERVER_ID_PREFIX = `\0${REACT_ISLAND_SERVER_ID_PREFIX}`;
1621
- function encodeIslandRemoteId(remoteId) {
1622
- return encodeURIComponent(remoteId);
1623
- }
1624
- function decodeIslandRemoteId(encodedRemoteId) {
1625
- return decodeURIComponent(encodedRemoteId);
1626
- }
1627
- function getReactIslandImportRemoteId(source) {
1628
- const queryIndex = source.indexOf("?");
1629
- if (queryIndex === -1) return;
1630
- if (!new URLSearchParams(source.slice(queryIndex + 1)).has("mf-island")) return;
1631
- return source.slice(0, queryIndex);
1632
- }
1633
- function getReactIslandServerImportId(remoteId) {
1634
- return `${REACT_ISLAND_SERVER_ID_PREFIX}${encodeIslandRemoteId(remoteId)}`;
1635
- }
1636
- function getReactIslandClientImportId(remoteId) {
1637
- return `${REACT_ISLAND_CLIENT_ID_PREFIX}${encodeIslandRemoteId(remoteId)}`;
1502
+ if (char !== ">") return void 0;
1503
+ if (source[index - 1] === "=" || source[index + 1] === "=") return "handled";
1504
+ state.angleDepth--;
1505
+ return state.angleDepth === 0 ? "closed" : "handled";
1638
1506
  }
1639
- function resolveReactIslandConsumerId(id) {
1640
- if (id.startsWith("virtual:mf-react-island-server:")) return `\0${id}`;
1641
- if (id.startsWith("virtual:mf-react-island-client:")) return `\0${id}`;
1507
+ function hasLikelyTypeArgumentFollower(source, end, codePositions, followsNamedExpression) {
1508
+ let next = end + 1;
1509
+ while (next < source.length && (!codePositions[next] || /\s/.test(source[next]))) next++;
1510
+ if (next >= source.length || /[([.!?=;,)\]}:|&]/.test(source[next])) return true;
1511
+ if (source.slice(end + 1, next).includes("\n")) return true;
1512
+ const followingToken = source.slice(next).match(/^[$_\p{ID_Start}][$_\u200C\u200D\p{ID_Continue}]*/u)?.[0];
1513
+ return followingToken === "as" || followingToken === "satisfies" || !followsNamedExpression && followingToken !== void 0;
1642
1514
  }
1643
- function remoteIdFromResolvedIslandId(id, prefix) {
1644
- if (!id.startsWith(prefix)) return;
1645
- return decodeIslandRemoteId(id.slice(prefix.length));
1515
+ function isInvalidTypeArgumentTerminator(source, index, followsNamedExpression) {
1516
+ const char = source[index];
1517
+ return char === ";" || char === "=" && source[index + 1] !== ">" && followsNamedExpression;
1646
1518
  }
1647
- /** Generates the server half of the opt-in `?mf-island` consumer component. */
1648
- function generateReactIslandConsumerServer(remoteId) {
1649
- const source = JSON.stringify(remoteId);
1650
- return `import * as React from "react";
1651
- import IslandClient from ${JSON.stringify(getReactIslandClientImportId(remoteId))};
1652
-
1653
- const islandModulePromise = import(${source});
1654
-
1655
- async function loadIslandModule() {
1656
- const namespace = await islandModulePromise;
1657
- const pending = namespace && namespace.__mf_remote_pending;
1658
- if (pending && typeof pending.then === "function") return pending;
1659
- return namespace && namespace.__moduleExports || namespace;
1519
+ /**
1520
+ * Finds the end of a balanced, type-like angle-bracket range that contains a
1521
+ * comma. Ambiguous syntax returns `undefined` so callers can fail closed.
1522
+ */
1523
+ function findLikelyTypeArgumentEnd(source, start, codePositions) {
1524
+ const context = getTypeArgumentStartContext(source, start);
1525
+ if (!context) return void 0;
1526
+ const state = {
1527
+ angleDepth: 1,
1528
+ groupDepth: 0,
1529
+ sawTypeComma: false
1530
+ };
1531
+ for (let index = start + 1; index < source.length; index++) {
1532
+ if (!codePositions[index]) continue;
1533
+ const char = source[index];
1534
+ const groupAction = updateTypeArgumentGroupDepth(char, state);
1535
+ if (groupAction === "invalid") return void 0;
1536
+ if (groupAction === "handled") continue;
1537
+ const angleAction = updateTypeArgumentAngleDepth(source, index, state);
1538
+ if (angleAction === "invalid") return void 0;
1539
+ if (angleAction === "closed") return state.sawTypeComma && hasLikelyTypeArgumentFollower(source, index, codePositions, context.followsNamedExpression) ? index : void 0;
1540
+ if (angleAction === "handled") continue;
1541
+ if (char === "," && state.groupDepth === 0) {
1542
+ state.sawTypeComma = true;
1543
+ continue;
1544
+ }
1545
+ if (state.angleDepth === 1 && state.groupDepth === 0 && isInvalidTypeArgumentTerminator(source, index, context.followsNamedExpression)) return;
1546
+ }
1660
1547
  }
1661
-
1662
- export default async function ModuleFederationIsland(props) {
1663
- const remoteModule = await loadIslandModule();
1664
- const shell = remoteModule && remoteModule.__mf_island;
1665
- if (!shell || typeof shell.renderToHtml !== "function") {
1666
- throw new Error(${JSON.stringify(`[Module Federation] ${remoteId} does not expose an SSR island capability`)});
1667
- }
1668
- const html = await shell.renderToHtml(props);
1669
- return React.createElement(IslandClient, { html, islandProps: props });
1670
- }`;
1548
+ //#endregion
1549
+ //#region src/utils/sharedExportConditions.ts
1550
+ const DEFAULT_CLIENT_EXPORT_CONDITIONS = [
1551
+ "browser",
1552
+ "import",
1553
+ "module",
1554
+ "default"
1555
+ ];
1556
+ const DEFAULT_NODE_SSR_EXPORT_CONDITIONS = [
1557
+ "node",
1558
+ "import",
1559
+ "module",
1560
+ "default"
1561
+ ];
1562
+ const DEFAULT_WEBWORKER_SSR_EXPORT_CONDITIONS = [
1563
+ "worker",
1564
+ "browser",
1565
+ "import",
1566
+ "module",
1567
+ "default"
1568
+ ];
1569
+ const VITE_DEV_PROD_CONDITION = "development|production";
1570
+ function appendConditions(conditions, fallbackConditions) {
1571
+ return [.../* @__PURE__ */ new Set([...conditions, ...fallbackConditions])];
1671
1572
  }
1672
- /** Generates the client boundary which hydrates with the remote-owned React. */
1673
- function generateReactIslandConsumerClient(remoteId) {
1674
- return `"use client";
1675
-
1676
- import * as React from "react";
1677
-
1678
- const islandModulePromise = import(${JSON.stringify(remoteId)});
1679
-
1680
- async function loadIslandModule() {
1681
- const namespace = await islandModulePromise;
1682
- const pending = namespace && namespace.__mf_remote_pending;
1683
- if (pending && typeof pending.then === "function") return pending;
1684
- return namespace && namespace.__moduleExports || namespace;
1573
+ function resolveViteModeCondition(conditions, isProduction) {
1574
+ const modeCondition = isProduction ? "production" : "development";
1575
+ return [...new Set(conditions.map((condition) => condition === VITE_DEV_PROD_CONDITION ? modeCondition : condition))];
1685
1576
  }
1686
-
1687
- export default function ModuleFederationIslandClient({ html, islandProps }) {
1688
- const ref = React.useRef(null);
1689
-
1690
- React.useEffect(() => {
1691
- const element = ref.current;
1692
- if (!element) return;
1693
- void loadIslandModule().then((remoteModule) => {
1694
- const shell = remoteModule && remoteModule.__mf_island;
1695
- if (!shell || typeof shell.hydrate !== "function") {
1696
- throw new Error(${JSON.stringify(`[Module Federation] ${remoteId} does not expose a client island capability`)});
1697
- }
1698
- return shell.hydrate(element, islandProps);
1699
- });
1700
- }, []);
1701
-
1702
- return React.createElement("div", {
1703
- ref,
1704
- suppressHydrationWarning: true,
1705
- dangerouslySetInnerHTML: { __html: html },
1706
- });
1707
- }`;
1577
+ function getSharedExportConditions({ environmentConditions, isProduction, isSsr, rootConditions, ssrConditions, ssrTarget = "node" }) {
1578
+ if (environmentConditions !== void 0) return resolveViteModeCondition(appendConditions(environmentConditions, ["import", "default"]), isProduction);
1579
+ const defaultConditions = isSsr ? ssrTarget === "webworker" ? DEFAULT_WEBWORKER_SSR_EXPORT_CONDITIONS : DEFAULT_NODE_SSR_EXPORT_CONDITIONS : DEFAULT_CLIENT_EXPORT_CONDITIONS;
1580
+ const configuredConditions = isSsr ? ssrConditions ?? rootConditions : rootConditions;
1581
+ if (configuredConditions !== void 0) return resolveViteModeCondition(appendConditions(configuredConditions, defaultConditions), isProduction);
1582
+ return [...defaultConditions];
1708
1583
  }
1709
- function loadReactIslandConsumerModule(id) {
1710
- const serverRemoteId = remoteIdFromResolvedIslandId(id, RESOLVED_REACT_ISLAND_SERVER_ID_PREFIX);
1711
- if (serverRemoteId !== void 0) return generateReactIslandConsumerServer(serverRemoteId);
1712
- const clientRemoteId = remoteIdFromResolvedIslandId(id, RESOLVED_REACT_ISLAND_CLIENT_ID_PREFIX);
1713
- if (clientRemoteId !== void 0) return generateReactIslandConsumerClient(clientRemoteId);
1584
+ function isReactServerConditions(conditions) {
1585
+ return Boolean(conditions?.includes("react-server"));
1714
1586
  }
1715
1587
  //#endregion
1716
1588
  //#region src/virtualModules/virtualModuleScope.ts
@@ -1753,160 +1625,19 @@ function stableConfigValue(value, ancestors = /* @__PURE__ */ new WeakSet()) {
1753
1625
  };
1754
1626
  } else result = Array.isArray(value) ? value.map((item) => stableConfigValue(item, ancestors)) : Object.fromEntries(Object.entries(value).filter(([key]) => key !== "implementation").sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, item]) => [key, stableConfigValue(item, ancestors)]));
1755
1627
  ancestors.delete(value);
1756
- return result;
1757
- }
1758
- function getFederationScopeKey(options) {
1759
- const identity = JSON.stringify(stableConfigValue(options));
1760
- const ownerId = BigInt(`0x${createHash("sha256").update(identity).digest("hex").slice(0, 12)}`);
1761
- return `${options.internalName}${MF_OWNER_INFIX}${ownerId}`;
1762
- }
1763
- //#endregion
1764
- //#region src/virtualModules/virtualExposes.ts
1765
- const EXPOSES_CSS_MAP_PLACEHOLDER = "__MF_EXPOSES_CSS_MAP__";
1766
- function getExposesCssMapPlaceholder() {
1767
- return EXPOSES_CSS_MAP_PLACEHOLDER;
1768
- }
1769
- function getVirtualExposesId(options) {
1770
- return `virtual:mf-exposes:${getVirtualModuleScopeKey(options)}`;
1771
- }
1772
- function generateExposes(options, remoteDependencyMap = {}, command = "build", reactIslandExposes = /* @__PURE__ */ new Set()) {
1773
- return `
1774
- const cssAssetMap = ${JSON.stringify(options.bundleAllCSS ? EXPOSES_CSS_MAP_PLACEHOLDER : {})};
1775
- const injectedCssHrefs = new Set();
1776
- const exposeLoadPromises = new Map();
1777
-
1778
- // Shared and remote readiness is handled by explicit Promise barriers below
1779
- // and by remoteEntry's pendingShareLoads barrier. Keep this map scoped to a
1780
- // single expose: unrelated exposes must stay independent and load in parallel.
1781
- function loadExposedModule(exposeKey, loader) {
1782
- let load = exposeLoadPromises.get(exposeKey);
1783
- if (!load) {
1784
- load = Promise.resolve()
1785
- .then(loader)
1786
- .catch((error) => {
1787
- exposeLoadPromises.delete(exposeKey);
1788
- throw error;
1789
- });
1790
- exposeLoadPromises.set(exposeKey, load);
1791
- }
1792
- return load;
1793
- }
1794
-
1795
- async function injectCssAssets(exposeKey) {
1796
- if (typeof document === "undefined") {
1797
- return;
1798
- }
1799
-
1800
- // Replaced at build time with expose -> css asset paths.
1801
- const cssAssets = cssAssetMap[exposeKey] || [];
1802
-
1803
- await Promise.all(
1804
- cssAssets.map((cssAsset) => {
1805
- const href = new URL(cssAsset, import.meta.url).href;
1806
-
1807
- // Same expose can be resolved multiple times in one page.
1808
- if (injectedCssHrefs.has(href)) {
1809
- return Promise.resolve();
1810
- }
1811
- injectedCssHrefs.add(href);
1812
-
1813
- // Check for any existing stylesheet with the same href, not just
1814
- // MF-injected ones. This prevents duplicate <link> tags when Vite's
1815
- // own CSS module injection or MF runtime's createLink has already
1816
- // created a <link rel="stylesheet"> for the same URL.
1817
- const existingLink = document.querySelector(
1818
- \`link[rel="stylesheet"][href="\${href}"]\`
1819
- );
1820
- if (existingLink) {
1821
- return Promise.resolve();
1822
- }
1823
-
1824
- return new Promise((resolve, reject) => {
1825
- const link = document.createElement("link");
1826
- link.rel = "stylesheet";
1827
- link.href = href;
1828
- link.onload = () => resolve();
1829
- link.onerror = () => {
1830
- injectedCssHrefs.delete(href);
1831
- reject(new Error(\`[Module Federation] Failed to load CSS asset: \${href}\`));
1832
- };
1833
- document.head.appendChild(link);
1834
- });
1835
- })
1836
- );
1837
- }
1838
-
1839
- export default {
1840
- ${Object.keys(options.exposes).map((key) => {
1841
- const remoteDependencyPreloads = (remoteDependencyMap[key] ?? []).map((remoteId) => {
1842
- const virtualRemote = getRemoteVirtualModule(remoteId, command, false, "unified", options);
1843
- return `import(${JSON.stringify(virtualRemote.getImportId())})
1844
- .then((mod) => mod.__mf_remote_pending)`;
1845
- }).join(",");
1846
- return `
1847
- ${JSON.stringify(key)}: async () => {
1848
- await injectCssAssets(${JSON.stringify(key)})
1849
- await Promise.all([${remoteDependencyPreloads}])
1850
- const importModule = await loadExposedModule(
1851
- ${JSON.stringify(key)},
1852
- () => import(${JSON.stringify(options.exposes[key].import)})
1853
- )
1854
- const dependencyPending = importModule && importModule.__mf_remote_dependency_pending;
1855
- if (dependencyPending && typeof dependencyPending.then === "function") {
1856
- await dependencyPending;
1857
- }
1858
- const exportModule = {}
1859
- Object.assign(exportModule, importModule)
1860
- ${generateReactIslandBrowserDefinition(reactIslandExposes.has(key))}
1861
- Object.defineProperty(exportModule, "__esModule", {
1862
- value: true,
1863
- enumerable: false
1864
- })
1865
- return exportModule
1866
- }
1867
- `;
1868
- }).join(",")}
1869
- }
1870
- `;
1871
- }
1872
- //#endregion
1873
- //#region src/utils/sharedExportConditions.ts
1874
- const DEFAULT_CLIENT_EXPORT_CONDITIONS = [
1875
- "browser",
1876
- "import",
1877
- "module",
1878
- "default"
1879
- ];
1880
- const DEFAULT_NODE_SSR_EXPORT_CONDITIONS = [
1881
- "node",
1882
- "import",
1883
- "module",
1884
- "default"
1885
- ];
1886
- const DEFAULT_WEBWORKER_SSR_EXPORT_CONDITIONS = [
1887
- "worker",
1888
- "browser",
1889
- "import",
1890
- "module",
1891
- "default"
1892
- ];
1893
- const VITE_DEV_PROD_CONDITION = "development|production";
1894
- function appendConditions(conditions, fallbackConditions) {
1895
- return [.../* @__PURE__ */ new Set([...conditions, ...fallbackConditions])];
1896
- }
1897
- function resolveViteModeCondition(conditions, isProduction) {
1898
- const modeCondition = isProduction ? "production" : "development";
1899
- return [...new Set(conditions.map((condition) => condition === VITE_DEV_PROD_CONDITION ? modeCondition : condition))];
1900
- }
1901
- function getSharedExportConditions({ environmentConditions, isProduction, isSsr, rootConditions, ssrConditions, ssrTarget = "node" }) {
1902
- if (environmentConditions !== void 0) return resolveViteModeCondition(appendConditions(environmentConditions, ["import", "default"]), isProduction);
1903
- const defaultConditions = isSsr ? ssrTarget === "webworker" ? DEFAULT_WEBWORKER_SSR_EXPORT_CONDITIONS : DEFAULT_NODE_SSR_EXPORT_CONDITIONS : DEFAULT_CLIENT_EXPORT_CONDITIONS;
1904
- const configuredConditions = isSsr ? ssrConditions ?? rootConditions : rootConditions;
1905
- if (configuredConditions !== void 0) return resolveViteModeCondition(appendConditions(configuredConditions, defaultConditions), isProduction);
1906
- return [...defaultConditions];
1628
+ return result;
1907
1629
  }
1908
- function isReactServerConditions(conditions) {
1909
- return Boolean(conditions?.includes("react-server"));
1630
+ function hashConfig(value) {
1631
+ const identity = JSON.stringify(stableConfigValue(value));
1632
+ return BigInt(`0x${createHash("sha256").update(identity).digest("hex").slice(0, 12)}`);
1633
+ }
1634
+ const ownerIdFilenames = /* @__PURE__ */ new Map();
1635
+ function getFederationScopeKey(options) {
1636
+ const { filename, ...identity } = options;
1637
+ let ownerId = hashConfig(identity);
1638
+ if (!ownerIdFilenames.has(ownerId)) ownerIdFilenames.set(ownerId, filename);
1639
+ else if (ownerIdFilenames.get(ownerId) !== filename) ownerId = hashConfig(options);
1640
+ return `${options.internalName}${MF_OWNER_INFIX}${ownerId}`;
1910
1641
  }
1911
1642
  //#endregion
1912
1643
  //#region src/virtualModules/virtualRuntimeInitStatus.ts
@@ -2091,80 +1822,6 @@ ${exportStatement}
2091
1822
  `);
2092
1823
  }
2093
1824
  //#endregion
2094
- //#region src/utils/typeArgumentScanner.ts
2095
- function getTypeArgumentStartContext(source, start) {
2096
- let previous = start - 1;
2097
- while (previous >= 0 && /\s/.test(source[previous])) previous--;
2098
- const previousChar = source[previous] || "";
2099
- const followsNamedExpression = /[$_\u200C\u200D\p{ID_Continue})\]>]/u.test(previousChar);
2100
- const startsStandaloneGeneric = previousChar !== "" && "=([{,:".includes(previousChar);
2101
- if (!followsNamedExpression && !startsStandaloneGeneric) return void 0;
2102
- return { followsNamedExpression };
2103
- }
2104
- function updateTypeArgumentGroupDepth(char, state) {
2105
- if (char === "(" || char === "[" || char === "{") {
2106
- state.groupDepth++;
2107
- return "handled";
2108
- }
2109
- if (char !== ")" && char !== "]" && char !== "}") return void 0;
2110
- if (state.groupDepth === 0) return "invalid";
2111
- state.groupDepth--;
2112
- return "handled";
2113
- }
2114
- function updateTypeArgumentAngleDepth(source, index, state) {
2115
- const char = source[index];
2116
- if (char === "<") {
2117
- if (source[index + 1] === "=" || source[index + 1] === "<") return "invalid";
2118
- state.angleDepth++;
2119
- return "handled";
2120
- }
2121
- if (char !== ">") return void 0;
2122
- if (source[index - 1] === "=" || source[index + 1] === "=") return "handled";
2123
- state.angleDepth--;
2124
- return state.angleDepth === 0 ? "closed" : "handled";
2125
- }
2126
- function hasLikelyTypeArgumentFollower(source, end, codePositions, followsNamedExpression) {
2127
- let next = end + 1;
2128
- while (next < source.length && (!codePositions[next] || /\s/.test(source[next]))) next++;
2129
- if (next >= source.length || /[([.!?=;,)\]}:|&]/.test(source[next])) return true;
2130
- if (source.slice(end + 1, next).includes("\n")) return true;
2131
- const followingToken = source.slice(next).match(/^[$_\p{ID_Start}][$_\u200C\u200D\p{ID_Continue}]*/u)?.[0];
2132
- return followingToken === "as" || followingToken === "satisfies" || !followsNamedExpression && followingToken !== void 0;
2133
- }
2134
- function isInvalidTypeArgumentTerminator(source, index, followsNamedExpression) {
2135
- const char = source[index];
2136
- return char === ";" || char === "=" && source[index + 1] !== ">" && followsNamedExpression;
2137
- }
2138
- /**
2139
- * Finds the end of a balanced, type-like angle-bracket range that contains a
2140
- * comma. Ambiguous syntax returns `undefined` so callers can fail closed.
2141
- */
2142
- function findLikelyTypeArgumentEnd(source, start, codePositions) {
2143
- const context = getTypeArgumentStartContext(source, start);
2144
- if (!context) return void 0;
2145
- const state = {
2146
- angleDepth: 1,
2147
- groupDepth: 0,
2148
- sawTypeComma: false
2149
- };
2150
- for (let index = start + 1; index < source.length; index++) {
2151
- if (!codePositions[index]) continue;
2152
- const char = source[index];
2153
- const groupAction = updateTypeArgumentGroupDepth(char, state);
2154
- if (groupAction === "invalid") return void 0;
2155
- if (groupAction === "handled") continue;
2156
- const angleAction = updateTypeArgumentAngleDepth(source, index, state);
2157
- if (angleAction === "invalid") return void 0;
2158
- if (angleAction === "closed") return state.sawTypeComma && hasLikelyTypeArgumentFollower(source, index, codePositions, context.followsNamedExpression) ? index : void 0;
2159
- if (angleAction === "handled") continue;
2160
- if (char === "," && state.groupDepth === 0) {
2161
- state.sawTypeComma = true;
2162
- continue;
2163
- }
2164
- if (state.angleDepth === 1 && state.groupDepth === 0 && isInvalidTypeArgumentTerminator(source, index, context.followsNamedExpression)) return;
2165
- }
2166
- }
2167
- //#endregion
2168
1825
  //#region src/virtualModules/shareTags.ts
2169
1826
  const LOAD_SHARE_TAG = "__loadShare__";
2170
1827
  /** A share's local copy. Reachable only through `import()`, so it stays lazy. */
@@ -2247,7 +1904,6 @@ function findEagerFallbacksInSharedChunk(bundle) {
2247
1904
  * 1. __prebuild__: export shareModule (pre-built source code of modules such as vue, react, etc.)
2248
1905
  * 2. __loadShare__: load shareModule (mfRuntime.loadShare('vue'))
2249
1906
  */
2250
- const JS_IDENTIFIER_REGEX = /* @__PURE__ */ new RegExp("^[$_\\p{ID_Start}][$_\\u200C\\u200D\\p{ID_Continue}]*$", "u");
2251
1907
  function escapeGeneratedStringLiteral(value) {
2252
1908
  return JSON.stringify(value).replace(/[<>\u2028\u2029]/g, (char) => {
2253
1909
  switch (char) {
@@ -2265,13 +1921,9 @@ function getDefaultOrNamespaceExpression(namespace) {
2265
1921
  function getSharedCacheDescriptorLiteral(pkg, shareItem) {
2266
1922
  return JSON.stringify(getSharedCacheDescriptor(pkg, shareItem));
2267
1923
  }
2268
- function isValidJsIdentifier(name) {
2269
- return JS_IDENTIFIER_REGEX.test(name);
2270
- }
2271
1924
  function isValidEsmExportName(name) {
2272
1925
  return !!name && name !== "default" && name !== "__esModule" && isValidJsIdentifier(name);
2273
1926
  }
2274
- const JS_IDENTIFIER_PATTERN = `[\$_\\p{ID_Start}][\$_\\u200C\\u200D\\p{ID_Continue}]*`;
2275
1927
  function resolvePackageEntryFromProjectRoot(pkg) {
2276
1928
  try {
2277
1929
  return resolveModulePath(pkg, path$1.join(getPackageDetectionCwd(), "package.json"));
@@ -2279,14 +1931,18 @@ function resolvePackageEntryFromProjectRoot(pkg) {
2279
1931
  return;
2280
1932
  }
2281
1933
  }
1934
+ let workspaceEntryConditions = [
1935
+ "browser",
1936
+ "import",
1937
+ "module",
1938
+ "default"
1939
+ ];
1940
+ function setWorkspaceEntryConditions(conditions) {
1941
+ workspaceEntryConditions = conditions;
1942
+ }
2282
1943
  function getPackageEsmEntryPath(pkg) {
2283
1944
  return getInstalledPackageEntry(pkg, {
2284
- conditions: [
2285
- "browser",
2286
- "import",
2287
- "module",
2288
- "default"
2289
- ],
1945
+ conditions: workspaceEntryConditions,
2290
1946
  resolveSubpathWithRequire: false
2291
1947
  }) || resolvePackageEntryFromProjectRoot(pkg);
2292
1948
  }
@@ -2796,12 +2452,7 @@ function getLocalProviderImportPath(pkg) {
2796
2452
  return isWorkspaceFilePath(resolved) ? resolved : void 0;
2797
2453
  } catch {
2798
2454
  const resolved = getInstalledPackageEntry(pkg, {
2799
- conditions: [
2800
- "browser",
2801
- "import",
2802
- "module",
2803
- "default"
2804
- ],
2455
+ conditions: workspaceEntryConditions,
2805
2456
  resolveSubpathWithRequire: false
2806
2457
  });
2807
2458
  return isWorkspaceFilePath(resolved) ? resolved : void 0;
@@ -2839,12 +2490,7 @@ function resolveWorkspaceEsmEntry(pkg, resolved, cwd = getPackageDetectionCwd())
2839
2490
  if (!isWorkspaceFilePath(resolved)) return resolved;
2840
2491
  const esmEntry = getInstalledPackageEntry(pkg, {
2841
2492
  cwd,
2842
- conditions: [
2843
- "browser",
2844
- "import",
2845
- "module",
2846
- "default"
2847
- ],
2493
+ conditions: workspaceEntryConditions,
2848
2494
  resolveSubpathWithRequire: false
2849
2495
  });
2850
2496
  if (esmEntry && isWorkspaceFilePath(esmEntry)) return esmEntry;
@@ -2965,7 +2611,7 @@ function resolveConcreteSharedImportSource(pkg, projectRoot) {
2965
2611
  currentDir = path$1.dirname(currentDir);
2966
2612
  }
2967
2613
  return tryResolveImportFromPackageRoot(pkg, currentDir) ?? getInstalledPackageEntry(pkg, {
2968
- conditions: DEFAULT_SHARED_EXPORT_CONDITIONS,
2614
+ conditions: workspaceEntryConditions,
2969
2615
  resolveSubpathWithRequire: false
2970
2616
  });
2971
2617
  }
@@ -3630,9 +3276,426 @@ function writeLoadShareModule(pkg, shareItem, command, options, exportConditions
3630
3276
  if (exportModule === undefined) {
3631
3277
  ${initBlock}
3632
3278
  }
3633
- ${exportLine}
3279
+ ${exportLine}
3280
+ `;
3281
+ loadShareCacheMap[pkg].writeSync(moduleBody, true);
3282
+ }
3283
+ //#endregion
3284
+ //#region src/utils/localShareImports.ts
3285
+ /**
3286
+ * The source file of a share whose `import` points at a project file rather than an installed
3287
+ * package, such as an application module shared with its micro frontends. Installed packages
3288
+ * describe their dependencies in `package.json`, so a path import of an installed package (a shim)
3289
+ * is not local. Undefined when the file cannot be found.
3290
+ */
3291
+ function getLocalShareSourceFile(pkg, shareItem) {
3292
+ const importSource = shareItem?.shareConfig?.import;
3293
+ if (typeof importSource !== "string") return void 0;
3294
+ if (!importSource.startsWith(".") && !path$1.isAbsolute(importSource)) return void 0;
3295
+ if (getInstalledPackageJson(getPackageName(pkg)) !== void 0) return void 0;
3296
+ return resolveConfiguredImportPath(importSource);
3297
+ }
3298
+ const specifiersByFile = /* @__PURE__ */ new Map();
3299
+ /** Specifiers a file evaluates (static, not type-only), cached until the file changes; empty when it cannot be read. */
3300
+ function readStaticImportSpecifiers(file) {
3301
+ try {
3302
+ const { mtimeMs } = statSync(file);
3303
+ const cached = specifiersByFile.get(file);
3304
+ if (cached && cached.mtimeMs === mtimeMs) return cached.specifiers;
3305
+ const specifiers = findModuleImportDescriptors(readFileSync(file, "utf-8")).filter(({ kind, typeOnly }) => kind === "static" && !typeOnly).map(({ source }) => source);
3306
+ specifiersByFile.set(file, {
3307
+ mtimeMs,
3308
+ specifiers
3309
+ });
3310
+ return specifiers;
3311
+ } catch {
3312
+ return [];
3313
+ }
3314
+ }
3315
+ /**
3316
+ * Keys of the other local shares that `sourceFile` imports at evaluation time, by key (or request)
3317
+ * or by source file, directly or through relative imports of project files that are not shared.
3318
+ * `localShareFiles` maps each local share's key to its source file; `requests` maps a share's
3319
+ * request to its key when they differ.
3320
+ */
3321
+ function getImportedLocalShares(sourceFile, localShareFiles, requests = /* @__PURE__ */ new Map()) {
3322
+ const keysByFile = new Map(Array.from(localShareFiles, ([key, file]) => [file, key]));
3323
+ const imported = /* @__PURE__ */ new Set();
3324
+ const visited = /* @__PURE__ */ new Set();
3325
+ const visit = (file) => {
3326
+ if (visited.has(file)) return;
3327
+ visited.add(file);
3328
+ for (const specifier of readStaticImportSpecifiers(file)) if (specifier.startsWith(".")) {
3329
+ const resolved = resolveRelativeModule(file, specifier);
3330
+ if (!resolved || resolved.split(path$1.sep).includes("node_modules")) continue;
3331
+ const key = keysByFile.get(resolved);
3332
+ if (key !== void 0) imported.add(key);
3333
+ else visit(resolved);
3334
+ } else {
3335
+ const key = localShareFiles.has(specifier) ? specifier : requests.get(specifier);
3336
+ if (key !== void 0) imported.add(key);
3337
+ }
3338
+ };
3339
+ visit(sourceFile);
3340
+ const self = keysByFile.get(sourceFile);
3341
+ if (self !== void 0) imported.delete(self);
3342
+ return [...imported];
3343
+ }
3344
+ //#endregion
3345
+ //#region src/utils/runtimePluginSpecifier.ts
3346
+ function getRuntimePluginSpecifier(plugin) {
3347
+ return typeof plugin === "string" ? plugin : plugin[0];
3348
+ }
3349
+ /** Drop SSR-only plugins from a client graph; a server graph keeps them all. */
3350
+ function filterRuntimePluginsForTarget(runtimePlugins, ssrOnlySpecifiers, isServer) {
3351
+ if (isServer) return [...runtimePlugins];
3352
+ return runtimePlugins.filter((plugin) => !ssrOnlySpecifiers.has(getRuntimePluginSpecifier(plugin)));
3353
+ }
3354
+ //#endregion
3355
+ //#region src/utils/reactIsland.ts
3356
+ const SOURCE_EXTENSIONS$1 = [
3357
+ ".tsx",
3358
+ ".jsx",
3359
+ ".ts",
3360
+ ".js",
3361
+ ".mts",
3362
+ ".mjs",
3363
+ ".cts",
3364
+ ".cjs"
3365
+ ];
3366
+ function resolveSourceFile(importPath, root) {
3367
+ const cleanImport = stripQueryAndHash(importPath);
3368
+ if (!cleanImport.startsWith(".") && !path$1.isAbsolute(cleanImport)) return;
3369
+ const candidate = path$1.isAbsolute(cleanImport) ? cleanImport : path$1.resolve(root, cleanImport);
3370
+ return [
3371
+ candidate,
3372
+ ...SOURCE_EXTENSIONS$1.map((extension) => `${candidate}${extension}`),
3373
+ ...SOURCE_EXTENSIONS$1.map((extension) => path$1.join(candidate, `index${extension}`))
3374
+ ].find((filePath) => {
3375
+ try {
3376
+ return fs$1.statSync(filePath).isFile();
3377
+ } catch {
3378
+ return false;
3379
+ }
3380
+ });
3381
+ }
3382
+ function localDefaultReexport(source) {
3383
+ const match = source.match(/export\s*{\s*(?:default(?:\s+as\s+default)?|[A-Za-z_$][\w$]*\s+as\s+default)\s*}\s*from\s*["']([^"']+)["']/);
3384
+ return match?.[1]?.startsWith(".") ? match[1] : void 0;
3385
+ }
3386
+ function isReactComponentSource(source, filePath = "component.tsx") {
3387
+ if (!(/\bexport\s+default\b/.test(source) || /\bexport\s*{[^}]*\bdefault\b[^}]*}(?:\s*from\s*["'][^"']+["'])?/.test(source))) return false;
3388
+ const extension = path$1.extname(stripQueryAndHash(filePath)).toLowerCase();
3389
+ const canContainJsx = extension === ".tsx" || extension === ".jsx";
3390
+ const hasJsx = /<>|<\s*[A-Za-z][\w.:-]*(?:\s[^<>]*?)?\s*\/?>/.test(source);
3391
+ const importsReact = /\bfrom\s*["']react["']|\brequire\(\s*["']react["']\s*\)/.test(source);
3392
+ const usesReactApi = /\b(?:React\.)?(?:createElement|jsx|jsxs)\s*\(/.test(source);
3393
+ const isClientModule = /^\s*["']use client["']\s*;?/m.test(source);
3394
+ return canContainJsx && hasJsx || importsReact && (hasJsx || usesReactApi || isClientModule);
3395
+ }
3396
+ function isReactComponentFile(filePath, seen = /* @__PURE__ */ new Set()) {
3397
+ const normalizedPath = path$1.resolve(filePath);
3398
+ if (seen.has(normalizedPath)) return false;
3399
+ seen.add(normalizedPath);
3400
+ let source;
3401
+ try {
3402
+ source = fs$1.readFileSync(normalizedPath, "utf8");
3403
+ } catch {
3404
+ return false;
3405
+ }
3406
+ if (isReactComponentSource(source, normalizedPath)) return true;
3407
+ const reexport = localDefaultReexport(source);
3408
+ if (!reexport) return false;
3409
+ const reexportPath = resolveSourceFile(reexport, path$1.dirname(normalizedPath));
3410
+ return reexportPath ? isReactComponentFile(reexportPath, seen) : false;
3411
+ }
3412
+ /**
3413
+ * React is shared by the normal MF runtime when explicitly configured. When it
3414
+ * is local, UI exposes can safely advertise an island capability in addition
3415
+ * to their unchanged default export.
3416
+ */
3417
+ function getReactIslandExposes(options, root) {
3418
+ if (options.experiments.ssrMode !== "ISLAND") return /* @__PURE__ */ new Set();
3419
+ if (Object.hasOwn(options.shared, "react")) return /* @__PURE__ */ new Set();
3420
+ const islandExposes = /* @__PURE__ */ new Set();
3421
+ for (const [key, expose] of Object.entries(options.exposes)) {
3422
+ const sourceFile = resolveSourceFile(expose.import, root);
3423
+ if (sourceFile && isReactComponentFile(sourceFile)) islandExposes.add(key);
3424
+ }
3425
+ return islandExposes;
3426
+ }
3427
+ function generateReactIslandBrowserDefinition(enabled) {
3428
+ if (!enabled) return "";
3429
+ return `
3430
+ exportModule.__mf_island = {
3431
+ version: 1,
3432
+ renderToHtml() {
3433
+ return Promise.reject(new Error("[Module Federation] renderToHtml is only available in the SSR remote entry"));
3434
+ },
3435
+ hydrate(element, props) {
3436
+ const root = element && element.hasAttribute && element.hasAttribute("data-mf-island-state")
3437
+ ? element
3438
+ : element && element.querySelector
3439
+ ? element.querySelector("[data-mf-island-state]") || element
3440
+ : element;
3441
+ if (!root) {
3442
+ return Promise.reject(new Error("[Module Federation] Cannot hydrate an island without a root element"));
3443
+ }
3444
+ let serverProps = {};
3445
+ const encodedState = root.getAttribute && root.getAttribute("data-mf-island-state");
3446
+ if (encodedState) {
3447
+ try {
3448
+ serverProps = JSON.parse(decodeURIComponent(encodedState));
3449
+ } catch {
3450
+ serverProps = {};
3451
+ }
3452
+ }
3453
+ const finalProps = Object.assign({}, serverProps, props || {});
3454
+ return Promise.all([import("react"), import("react-dom/client")]).then(([React, ReactDOMClient]) => {
3455
+ if (typeof ReactDOMClient.hydrateRoot !== "function") {
3456
+ throw new Error("[Module Federation] react-dom/client does not provide hydrateRoot");
3457
+ }
3458
+ return ReactDOMClient.hydrateRoot(
3459
+ root,
3460
+ React.createElement(importModule.default, finalProps)
3461
+ );
3462
+ });
3463
+ }
3464
+ }`;
3465
+ }
3466
+ function generateReactIslandSSRDefinition(enabled) {
3467
+ if (!enabled) return "";
3468
+ return `
3469
+ exportModule.__mf_island = {
3470
+ version: 1,
3471
+ async renderToHtml(props) {
3472
+ if (typeof importModule.default !== "function" && typeof importModule.default !== "object") {
3473
+ throw new Error("[Module Federation] A React island expose must have a default component export");
3474
+ }
3475
+ const loadedProps = typeof importModule.load === "function" ? await importModule.load() : {};
3476
+ const finalProps = Object.assign({}, loadedProps || {}, props || {});
3477
+ const [React, ReactDOMServer] = await Promise.all([
3478
+ import("react"),
3479
+ import("react-dom/server")
3480
+ ]);
3481
+ const body = ReactDOMServer.renderToString(
3482
+ React.createElement(importModule.default, finalProps)
3483
+ );
3484
+ const state = encodeURIComponent(JSON.stringify(finalProps));
3485
+ return '<div data-mf-island-state="' + state + '">' + body + '</div>';
3486
+ },
3487
+ hydrate() {
3488
+ return Promise.reject(new Error("[Module Federation] hydrate is only available in the browser remote entry"));
3489
+ }
3490
+ }`;
3491
+ }
3492
+ const REACT_ISLAND_CLIENT_ID_PREFIX = "virtual:mf-react-island-client:";
3493
+ const REACT_ISLAND_SERVER_ID_PREFIX = "virtual:mf-react-island-server:";
3494
+ const RESOLVED_REACT_ISLAND_CLIENT_ID_PREFIX = `\0${REACT_ISLAND_CLIENT_ID_PREFIX}`;
3495
+ const RESOLVED_REACT_ISLAND_SERVER_ID_PREFIX = `\0${REACT_ISLAND_SERVER_ID_PREFIX}`;
3496
+ function encodeIslandRemoteId(remoteId) {
3497
+ return encodeURIComponent(remoteId);
3498
+ }
3499
+ function decodeIslandRemoteId(encodedRemoteId) {
3500
+ return decodeURIComponent(encodedRemoteId);
3501
+ }
3502
+ function getReactIslandImportRemoteId(source) {
3503
+ const queryIndex = source.indexOf("?");
3504
+ if (queryIndex === -1) return;
3505
+ if (!new URLSearchParams(source.slice(queryIndex + 1)).has("mf-island")) return;
3506
+ return source.slice(0, queryIndex);
3507
+ }
3508
+ function getReactIslandServerImportId(remoteId) {
3509
+ return `${REACT_ISLAND_SERVER_ID_PREFIX}${encodeIslandRemoteId(remoteId)}`;
3510
+ }
3511
+ function getReactIslandClientImportId(remoteId) {
3512
+ return `${REACT_ISLAND_CLIENT_ID_PREFIX}${encodeIslandRemoteId(remoteId)}`;
3513
+ }
3514
+ function resolveReactIslandConsumerId(id) {
3515
+ if (id.startsWith("virtual:mf-react-island-server:")) return `\0${id}`;
3516
+ if (id.startsWith("virtual:mf-react-island-client:")) return `\0${id}`;
3517
+ }
3518
+ function remoteIdFromResolvedIslandId(id, prefix) {
3519
+ if (!id.startsWith(prefix)) return;
3520
+ return decodeIslandRemoteId(id.slice(prefix.length));
3521
+ }
3522
+ /** Generates the server half of the opt-in `?mf-island` consumer component. */
3523
+ function generateReactIslandConsumerServer(remoteId) {
3524
+ const source = JSON.stringify(remoteId);
3525
+ return `import * as React from "react";
3526
+ import IslandClient from ${JSON.stringify(getReactIslandClientImportId(remoteId))};
3527
+
3528
+ const islandModulePromise = import(${source});
3529
+
3530
+ async function loadIslandModule() {
3531
+ const namespace = await islandModulePromise;
3532
+ const pending = namespace && namespace.__mf_remote_pending;
3533
+ if (pending && typeof pending.then === "function") return pending;
3534
+ return namespace && namespace.__moduleExports || namespace;
3535
+ }
3536
+
3537
+ export default async function ModuleFederationIsland(props) {
3538
+ const remoteModule = await loadIslandModule();
3539
+ const shell = remoteModule && remoteModule.__mf_island;
3540
+ if (!shell || typeof shell.renderToHtml !== "function") {
3541
+ throw new Error(${JSON.stringify(`[Module Federation] ${remoteId} does not expose an SSR island capability`)});
3542
+ }
3543
+ const html = await shell.renderToHtml(props);
3544
+ return React.createElement(IslandClient, { html, islandProps: props });
3545
+ }`;
3546
+ }
3547
+ /** Generates the client boundary which hydrates with the remote-owned React. */
3548
+ function generateReactIslandConsumerClient(remoteId) {
3549
+ return `"use client";
3550
+
3551
+ import * as React from "react";
3552
+
3553
+ const islandModulePromise = import(${JSON.stringify(remoteId)});
3554
+
3555
+ async function loadIslandModule() {
3556
+ const namespace = await islandModulePromise;
3557
+ const pending = namespace && namespace.__mf_remote_pending;
3558
+ if (pending && typeof pending.then === "function") return pending;
3559
+ return namespace && namespace.__moduleExports || namespace;
3560
+ }
3561
+
3562
+ export default function ModuleFederationIslandClient({ html, islandProps }) {
3563
+ const ref = React.useRef(null);
3564
+
3565
+ React.useEffect(() => {
3566
+ const element = ref.current;
3567
+ if (!element) return;
3568
+ void loadIslandModule().then((remoteModule) => {
3569
+ const shell = remoteModule && remoteModule.__mf_island;
3570
+ if (!shell || typeof shell.hydrate !== "function") {
3571
+ throw new Error(${JSON.stringify(`[Module Federation] ${remoteId} does not expose a client island capability`)});
3572
+ }
3573
+ return shell.hydrate(element, islandProps);
3574
+ });
3575
+ }, []);
3576
+
3577
+ return React.createElement("div", {
3578
+ ref,
3579
+ suppressHydrationWarning: true,
3580
+ dangerouslySetInnerHTML: { __html: html },
3581
+ });
3582
+ }`;
3583
+ }
3584
+ function loadReactIslandConsumerModule(id) {
3585
+ const serverRemoteId = remoteIdFromResolvedIslandId(id, RESOLVED_REACT_ISLAND_SERVER_ID_PREFIX);
3586
+ if (serverRemoteId !== void 0) return generateReactIslandConsumerServer(serverRemoteId);
3587
+ const clientRemoteId = remoteIdFromResolvedIslandId(id, RESOLVED_REACT_ISLAND_CLIENT_ID_PREFIX);
3588
+ if (clientRemoteId !== void 0) return generateReactIslandConsumerClient(clientRemoteId);
3589
+ }
3590
+ //#endregion
3591
+ //#region src/virtualModules/virtualExposes.ts
3592
+ const EXPOSES_CSS_MAP_PLACEHOLDER = "__MF_EXPOSES_CSS_MAP__";
3593
+ function getExposesCssMapPlaceholder() {
3594
+ return EXPOSES_CSS_MAP_PLACEHOLDER;
3595
+ }
3596
+ function getVirtualExposesId(options) {
3597
+ return `virtual:mf-exposes:${getVirtualModuleScopeKey(options)}`;
3598
+ }
3599
+ function generateExposes(options, remoteDependencyMap = {}, command = "build", reactIslandExposes = /* @__PURE__ */ new Set()) {
3600
+ return `
3601
+ const cssAssetMap = ${JSON.stringify(options.bundleAllCSS ? EXPOSES_CSS_MAP_PLACEHOLDER : {})};
3602
+ const injectedCssHrefs = new Set();
3603
+ const exposeLoadPromises = new Map();
3604
+
3605
+ // Shared and remote readiness is handled by explicit Promise barriers below
3606
+ // and by remoteEntry's pendingShareLoads barrier. Keep this map scoped to a
3607
+ // single expose: unrelated exposes must stay independent and load in parallel.
3608
+ function loadExposedModule(exposeKey, loader) {
3609
+ let load = exposeLoadPromises.get(exposeKey);
3610
+ if (!load) {
3611
+ load = Promise.resolve()
3612
+ .then(loader)
3613
+ .catch((error) => {
3614
+ exposeLoadPromises.delete(exposeKey);
3615
+ throw error;
3616
+ });
3617
+ exposeLoadPromises.set(exposeKey, load);
3618
+ }
3619
+ return load;
3620
+ }
3621
+
3622
+ async function injectCssAssets(exposeKey) {
3623
+ if (typeof document === "undefined") {
3624
+ return;
3625
+ }
3626
+
3627
+ // Replaced at build time with expose -> css asset paths.
3628
+ const cssAssets = cssAssetMap[exposeKey] || [];
3629
+
3630
+ await Promise.all(
3631
+ cssAssets.map((cssAsset) => {
3632
+ const href = new URL(cssAsset, import.meta.url).href;
3633
+
3634
+ // Same expose can be resolved multiple times in one page.
3635
+ if (injectedCssHrefs.has(href)) {
3636
+ return Promise.resolve();
3637
+ }
3638
+ injectedCssHrefs.add(href);
3639
+
3640
+ // Check for any existing stylesheet with the same href, not just
3641
+ // MF-injected ones. This prevents duplicate <link> tags when Vite's
3642
+ // own CSS module injection or MF runtime's createLink has already
3643
+ // created a <link rel="stylesheet"> for the same URL.
3644
+ const existingLink = document.querySelector(
3645
+ \`link[rel="stylesheet"][href="\${href}"]\`
3646
+ );
3647
+ if (existingLink) {
3648
+ return Promise.resolve();
3649
+ }
3650
+
3651
+ return new Promise((resolve, reject) => {
3652
+ const link = document.createElement("link");
3653
+ link.rel = "stylesheet";
3654
+ link.href = href;
3655
+ link.onload = () => resolve();
3656
+ link.onerror = () => {
3657
+ injectedCssHrefs.delete(href);
3658
+ link.remove();
3659
+ reject(new Error(\`[Module Federation] Failed to load CSS asset: \${href}\`));
3660
+ };
3661
+ document.head.appendChild(link);
3662
+ });
3663
+ })
3664
+ );
3665
+ }
3666
+
3667
+ export default {
3668
+ ${Object.keys(options.exposes).map((key) => {
3669
+ const remoteDependencyPreloads = (remoteDependencyMap[key] ?? []).map((remoteId) => {
3670
+ const virtualRemote = getRemoteVirtualModule(remoteId, command, false, "unified", options);
3671
+ return `import(${JSON.stringify(virtualRemote.getImportId())})
3672
+ .then((mod) => mod.__mf_remote_pending)`;
3673
+ }).join(",");
3674
+ return `
3675
+ ${JSON.stringify(key)}: async () => {
3676
+ await injectCssAssets(${JSON.stringify(key)})
3677
+ await Promise.all([${remoteDependencyPreloads}])
3678
+ const importModule = await loadExposedModule(
3679
+ ${JSON.stringify(key)},
3680
+ () => import(${JSON.stringify(options.exposes[key].import)})
3681
+ )
3682
+ const dependencyPending = importModule && importModule.__mf_remote_dependency_pending;
3683
+ if (dependencyPending && typeof dependencyPending.then === "function") {
3684
+ await dependencyPending;
3685
+ }
3686
+ const exportModule = {}
3687
+ Object.assign(exportModule, importModule)
3688
+ ${generateReactIslandBrowserDefinition(reactIslandExposes.has(key))}
3689
+ Object.defineProperty(exportModule, "__esModule", {
3690
+ value: true,
3691
+ enumerable: false
3692
+ })
3693
+ return exportModule
3694
+ }
3695
+ `;
3696
+ }).join(",")}
3697
+ }
3634
3698
  `;
3635
- loadShareCacheMap[pkg].writeSync(moduleBody, true);
3636
3699
  }
3637
3700
  //#endregion
3638
3701
  //#region src/virtualModules/virtualRemoteEntry.ts
@@ -3947,7 +4010,7 @@ function getOrderedUsedShares(options) {
3947
4010
  return orderSharedDependenciesFirst(Array.from(shares).sort((a, b) => {
3948
4011
  const priority = (pkg) => pkg === "react" ? 0 : pkg === "react-dom" ? 1 : pkg.startsWith("react/") ? 2 : 3;
3949
4012
  return priority(a) - priority(b) || a.localeCompare(b);
3950
- }));
4013
+ }), options);
3951
4014
  }
3952
4015
  function getMaterializedShares(options) {
3953
4016
  const shares = new Set(materializedSharesByOptions.get(options));
@@ -3959,14 +4022,20 @@ function getMaterializedShares(options) {
3959
4022
  else shares.add(pkg);
3960
4023
  }
3961
4024
  const configured = /* @__PURE__ */ new Map();
3962
- for (const pkg of getOrderedUsedShares(options)) {
4025
+ const orderedUsed = getOrderedUsedShares(options);
4026
+ for (const pkg of orderedUsed) {
3963
4027
  const packageName = getPackageName(pkg);
3964
4028
  if (!configured.has(packageName) || pkg === packageName) configured.set(packageName, pkg);
3965
4029
  }
4030
+ const localImports = getLocalShareImports(orderedUsed, options);
3966
4031
  const pending = [...shares];
3967
4032
  while (pending.length) {
3968
4033
  const pkg = pending.pop();
3969
4034
  const share = getNormalizeShareItem(pkg, options);
4035
+ for (const imported of localImports.get(pkg) ?? []) if (!shares.has(imported) && getNormalizeShareItem(imported, options)?.scope === share?.scope) {
4036
+ shares.add(imported);
4037
+ pending.push(imported);
4038
+ }
3970
4039
  const packageName = getPackageName(pkg);
3971
4040
  const packageJson = getInstalledPackageJson(pkg)?.packageJson ?? (pkg !== packageName ? getInstalledPackageJson(packageName)?.packageJson : void 0);
3972
4041
  const dependencies = {
@@ -3991,10 +4060,39 @@ function getMaterializedShares(options) {
3991
4060
  return orderSharedDependenciesFirst([...new Set(sorted.map((pkg) => {
3992
4061
  const shareItem = getNormalizeShareItem(pkg, options);
3993
4062
  return shareItem ? getCanonicalSharedKey(pkg, shareItem) : pkg;
3994
- }))]);
4063
+ }))], options);
4064
+ }
4065
+ /**
4066
+ * The other local shares each local share (one whose `import` is a project file) imports at module
4067
+ * evaluation, found through the static imports of its source and of the unshared project files it
4068
+ * imports, keyed by share. Only local shares have an entry.
4069
+ */
4070
+ function getLocalShareImports(shares, options) {
4071
+ const localShareFiles = /* @__PURE__ */ new Map();
4072
+ const requests = /* @__PURE__ */ new Map();
4073
+ for (const pkg of shares) {
4074
+ const shareItem = getNormalizeShareItem(pkg, options);
4075
+ const sourceFile = getLocalShareSourceFile(pkg, shareItem);
4076
+ if (sourceFile === void 0) continue;
4077
+ localShareFiles.set(pkg, sourceFile);
4078
+ const request = getSharedRequest(pkg, shareItem);
4079
+ if (request !== pkg) requests.set(request, pkg);
4080
+ }
4081
+ return new Map(Array.from(localShareFiles, ([pkg, sourceFile]) => [pkg, getImportedLocalShares(sourceFile, localShareFiles, requests)]));
4082
+ }
4083
+ /**
4084
+ * Shares that must be materialized before each local share, keyed by share. A project file has no
4085
+ * package.json to read dependencies from and may import any shared package, directly or through
4086
+ * other project files, so every installed-package share comes first, followed by the local shares
4087
+ * its source imports (see #1507).
4088
+ */
4089
+ function getLocalSharePrerequisites(shares, options) {
4090
+ const localImports = getLocalShareImports(shares, options);
4091
+ const packageShares = shares.filter((pkg) => !localImports.has(pkg));
4092
+ return new Map(Array.from(localImports, ([pkg, imports]) => [pkg, [...packageShares, ...imports]]));
3995
4093
  }
3996
4094
  /** Shared keys a share's package.json depends on (roots stand in for their subpaths, subpaths for their root), keyed by share. */
3997
- function getSharePrerequisites(ordered) {
4095
+ function getSharePrerequisites(ordered, options) {
3998
4096
  const roots = /* @__PURE__ */ new Map();
3999
4097
  const subpaths = /* @__PURE__ */ new Map();
4000
4098
  for (const pkg of ordered) {
@@ -4003,7 +4101,13 @@ function getSharePrerequisites(ordered) {
4003
4101
  else subpaths.set(packageName, [...subpaths.get(packageName) ?? [], pkg]);
4004
4102
  }
4005
4103
  const prerequisitesByShare = /* @__PURE__ */ new Map();
4104
+ const localPrerequisites = getLocalSharePrerequisites(ordered, options);
4006
4105
  for (const pkg of ordered) {
4106
+ const local = localPrerequisites.get(pkg);
4107
+ if (local) {
4108
+ prerequisitesByShare.set(pkg, local);
4109
+ continue;
4110
+ }
4007
4111
  const packageName = getPackageName(pkg);
4008
4112
  const packageJson = getInstalledPackageJson(pkg)?.packageJson ?? (pkg !== packageName ? getInstalledPackageJson(packageName)?.packageJson : void 0);
4009
4113
  const dependencies = {
@@ -4023,14 +4127,15 @@ function getSharePrerequisites(ordered) {
4023
4127
  }
4024
4128
  function getShareBatches(options, materializedOnly = true) {
4025
4129
  const ordered = materializedOnly ? getMaterializedShares(options) : getOrderedUsedShares(options);
4026
- const prerequisitesByShare = getSharePrerequisites(ordered);
4130
+ const prerequisitesByShare = getSharePrerequisites(ordered, options);
4027
4131
  const levels = /* @__PURE__ */ new Map();
4028
4132
  for (const pkg of ordered) levels.set(pkg, (prerequisitesByShare.get(pkg) ?? []).reduce((level, dependency) => Math.max(level, (levels.get(dependency) ?? 0) + 1), 0));
4029
4133
  const batches = [];
4030
4134
  for (const pkg of ordered) (batches[levels.get(pkg) ?? 0] ??= []).push(pkg);
4031
4135
  return batches.filter(Boolean);
4032
4136
  }
4033
- function orderSharedDependenciesFirst(sharedPackages) {
4137
+ function orderSharedDependenciesFirst(sharedPackages, options) {
4138
+ const localPrerequisites = getLocalSharePrerequisites(sharedPackages, options);
4034
4139
  const sharedKeyByPackageName = /* @__PURE__ */ new Map();
4035
4140
  const subpathKeysByPackageName = /* @__PURE__ */ new Map();
4036
4141
  sharedPackages.forEach((pkg) => {
@@ -4049,6 +4154,14 @@ function orderSharedDependenciesFirst(sharedPackages) {
4049
4154
  if (visited.has(pkg)) return;
4050
4155
  if (visiting.has(pkg)) return;
4051
4156
  visiting.add(pkg);
4157
+ const local = localPrerequisites.get(pkg);
4158
+ if (local) {
4159
+ local.forEach(visit);
4160
+ visiting.delete(pkg);
4161
+ visited.add(pkg);
4162
+ ordered.push(pkg);
4163
+ return;
4164
+ }
4052
4165
  const packageName = getPackageName(pkg);
4053
4166
  const packageJson = getInstalledPackageJson(pkg)?.packageJson ?? (pkg !== packageName ? getInstalledPackageJson(packageName)?.packageJson : void 0);
4054
4167
  const dependencies = {
@@ -4329,7 +4442,7 @@ function generateRuntimeSharedCacheSeedCode(shareStrategy, options, command = "b
4329
4442
  const seedBatches = getShareBatches(options, false);
4330
4443
  const seedOrder = seedBatches.flat();
4331
4444
  const seedIndex = new Map(seedOrder.map((pkg, index) => [pkg, index]));
4332
- const seedPrerequisites = Object.fromEntries(Array.from(getSharePrerequisites(seedOrder)).filter(([, prerequisites]) => prerequisites.length > 0).map(([pkg, prerequisites]) => [seedIndex.get(pkg), prerequisites.map((prerequisite) => seedIndex.get(prerequisite))]));
4445
+ const seedPrerequisites = Object.fromEntries(Array.from(getSharePrerequisites(seedOrder, options)).filter(([, prerequisites]) => prerequisites.length > 0).map(([pkg, prerequisites]) => [seedIndex.get(pkg), prerequisites.map((prerequisite) => seedIndex.get(prerequisite))]));
4333
4446
  return `
4334
4447
  const __mfSeedOrder = ${toSafeJsLiteral(seedOrder)};
4335
4448
  const __mfSeedBatches = ${toSafeJsLiteral(seedBatches)};
@@ -8259,7 +8372,7 @@ function ${helperName}(names) {
8259
8372
  }
8260
8373
  //#endregion
8261
8374
  //#region package.json
8262
- var version$1 = "1.23.6";
8375
+ var version$1 = "1.23.7";
8263
8376
  //#endregion
8264
8377
  //#region src/virtualModules/virtualExposesSSR.ts
8265
8378
  /**
@@ -8306,6 +8419,15 @@ const REMOTE_ENTRY_SSR_ID = "virtual:mf-REMOTE_ENTRY_SSR_ID";
8306
8419
  function getRemoteEntrySSRId(options) {
8307
8420
  return `${REMOTE_ENTRY_SSR_ID}:${getVirtualModuleScopeKey(options)}`;
8308
8421
  }
8422
+ const SSR_HOST_INIT_ID = "virtual:mf-ssr-host-init";
8423
+ /**
8424
+ * The SSR entry's import of the host bootstrap. Keyed by config scope, not by federation()
8425
+ * instance: under the Vite builder the entry plugin is shared by every environment, so the
8426
+ * instance building each environment resolves it to its own bootstrap.
8427
+ */
8428
+ function getSsrHostInitId(options) {
8429
+ return `${SSR_HOST_INIT_ID}:${getVirtualModuleScopeKey(options)}`;
8430
+ }
8309
8431
  const FILE_EXTENSION_RE = /\.[^.]+$/;
8310
8432
  function getSsrFileNameParts(browserFilename) {
8311
8433
  const filename = resolveHashPlaceholderFileName(browserFilename);
@@ -8356,7 +8478,7 @@ function generateRemoteEntrySSR(options) {
8356
8478
  const virtualExposesSSRId = getVirtualExposesSSRId(options);
8357
8479
  const sharedSingletons = getSsrSharedSingletons(options);
8358
8480
  return `
8359
- ${hasRemotes(options) ? `import { hostInitPromise } from ${JSON.stringify(getHostAutoInitPath(options))};` : "import { init as runtimeInit } from \"@module-federation/runtime\";"}
8481
+ ${hasRemotes(options) ? `import { hostInitPromise } from ${JSON.stringify(getSsrHostInitId(options))};` : "import { init as runtimeInit } from \"@module-federation/runtime\";"}
8360
8482
 
8361
8483
  const sharedSingletons = ${JSON.stringify(sharedSingletons)};
8362
8484
  const moduleCacheKey = Symbol.for(${JSON.stringify(MODULE_CACHE_SHARE_SCOPE_KEY)});
@@ -8614,7 +8736,7 @@ function getRemoteContainerName(remoteKey, remote) {
8614
8736
  }
8615
8737
  const Manifest = (providedOptions) => {
8616
8738
  const mfOptions = providedOptions ?? getNormalizeModuleFederationOptions();
8617
- const { name, filename, getPublicPath, manifest: manifestOptions, varFilename } = mfOptions;
8739
+ const { filename, getPublicPath, manifest: manifestOptions } = mfOptions;
8618
8740
  let mfManifestName = manifestOptions === true ? "mf-manifest.json" : typeof manifestOptions === "object" ? normalizePathForImport(path$1.join(manifestOptions?.filePath || "", manifestOptions?.fileName || "mf-manifest.json")) : void 0;
8619
8741
  let mfManifestStatsName = mfManifestName ? getStatsFileName(mfManifestName) : void 0;
8620
8742
  const isConsumerProject = !isRemoteContainer(mfOptions);
@@ -8659,39 +8781,7 @@ const Manifest = (providedOptions) => {
8659
8781
  res.setHeader("Content-Type", "application/json");
8660
8782
  res.setHeader("Access-Control-Allow-Origin", "*");
8661
8783
  (async () => {
8662
- const manifest = await applyManifestAdditionalData({
8663
- ...generateMFManifest({}, disableAssetsAnalyze),
8664
- id: name,
8665
- name,
8666
- metaData: {
8667
- name,
8668
- type: "app",
8669
- buildInfo: {
8670
- buildVersion: getBuildVersion(),
8671
- buildName: name,
8672
- ...getTreeShakingBuildInfo(mfOptions)
8673
- },
8674
- remoteEntry: {
8675
- name: devRemoteEntryFile,
8676
- path: "",
8677
- type: "module"
8678
- },
8679
- ssrRemoteEntry: {
8680
- name: getSsrRemoteEntryFileName(mfOptions, devRemoteEntryFile),
8681
- path: "/__mf_ssr__/",
8682
- type: "module"
8683
- },
8684
- varRemoteEntry: varFilename ? {
8685
- name: varFilename,
8686
- path: "",
8687
- type: "var"
8688
- } : void 0,
8689
- types: resolveTypesMeta(mfOptions.dts),
8690
- globalName: name,
8691
- pluginVersion: version$1,
8692
- ...getPublicPath ? { getPublicPath } : { publicPath }
8693
- }
8694
- });
8784
+ const manifest = await applyManifestAdditionalData(generateMFManifest({}, disableAssetsAnalyze));
8695
8785
  res.end(JSON.stringify(manifest));
8696
8786
  })().catch(next);
8697
8787
  } else next();
@@ -8766,7 +8856,7 @@ const Manifest = (providedOptions) => {
8766
8856
  const expectedSsrRemoteEntryFile = getSsrRemoteEntryFileName(mfOptions);
8767
8857
  const foundSsrRemoteEntryFile = Object.values(bundle).find((file) => file.fileName === expectedSsrRemoteEntryFile)?.fileName;
8768
8858
  if (foundRemoteEntryFile) remoteEntryFile = foundRemoteEntryFile;
8769
- ssrRemoteEntryFile = foundSsrRemoteEntryFile || (_command === "serve" ? getSsrRemoteEntryFileName(mfOptions, resolveHashPlaceholderFileName(mfOptions.filename)) : void 0);
8859
+ ssrRemoteEntryFile = foundSsrRemoteEntryFile;
8770
8860
  const allCssAssets = mfOptions.bundleAllCSS && !disableAssetsAnalyze ? collectCssAssets(bundle) : /* @__PURE__ */ new Set();
8771
8861
  if (allCssAssets.size > 0) {
8772
8862
  const secondaryCss = /* @__PURE__ */ new Set();
@@ -8836,13 +8926,13 @@ const Manifest = (providedOptions) => {
8836
8926
  function generateMFManifest(preloadMap, disableAssetsAnalyze = false) {
8837
8927
  const options = mfOptions;
8838
8928
  const { name, varFilename } = options;
8839
- const resolvedRemoteEntryFile = _command === "serve" ? remoteEntryFile || resolveHashPlaceholderFileName(filename) : remoteEntryFile;
8929
+ const resolvedRemoteEntryFile = _command === "serve" ? resolveHashPlaceholderFileName(filename) : remoteEntryFile;
8840
8930
  const remoteEntry = {
8841
8931
  name: resolvedRemoteEntryFile,
8842
8932
  path: "",
8843
8933
  type: "module"
8844
8934
  };
8845
- const resolvedSsrRemoteEntryFile = _command === "serve" ? ssrRemoteEntryFile || getSsrRemoteEntryFileName(mfOptions, resolveHashPlaceholderFileName(filename)) : ssrRemoteEntryFile;
8935
+ const resolvedSsrRemoteEntryFile = _command === "serve" ? getSsrRemoteEntryFileName(mfOptions, resolvedRemoteEntryFile) : ssrRemoteEntryFile;
8846
8936
  const ssrRemoteEntry = resolvedSsrRemoteEntryFile ? {
8847
8937
  name: resolvedSsrRemoteEntryFile,
8848
8938
  path: _command === "serve" ? "/__mf_ssr__/" : "",
@@ -9478,6 +9568,21 @@ function shouldScanResolvedImport(id) {
9478
9568
  if (isNodeModulePath(id)) return false;
9479
9569
  return /\.(?:[cm]?[jt]sx?|vue|svelte)(?:\?|$)/.test(id);
9480
9570
  }
9571
+ const TS_MODULE_RE = /\.[cm]?tsx?$/;
9572
+ /**
9573
+ * The module as Vite's TypeScript transform leaves it. A value-style import used only as a type
9574
+ * (`import { Props } from 'remote/Widget'`) is dropped there, so it loads nothing at runtime.
9575
+ * `transformWithOxc` is Vite 8+; `transformWithEsbuild` needs esbuild, which Vite 8 no longer ships.
9576
+ */
9577
+ async function withoutTypeOnlyImports(id, code) {
9578
+ if (!TS_MODULE_RE.test(id)) return code;
9579
+ const { transformWithOxc } = vite;
9580
+ try {
9581
+ return (transformWithOxc ? await transformWithOxc(code, id, { jsx: "preserve" }) : await vite.transformWithEsbuild(code, id, { jsx: "preserve" })).code;
9582
+ } catch {
9583
+ return code;
9584
+ }
9585
+ }
9481
9586
  async function readModuleEdges(ctx, id, isRemoteImport) {
9482
9587
  let code;
9483
9588
  try {
@@ -9489,12 +9594,14 @@ async function readModuleEdges(ctx, id, isRemoteImport) {
9489
9594
  dynamicImports: []
9490
9595
  };
9491
9596
  }
9597
+ let sources = collectImportSources(code);
9598
+ if (sources.some(({ source }) => isRemoteImport(source))) sources = collectImportSources(await withoutTypeOnlyImports(id, code));
9492
9599
  const edges = {
9493
9600
  remotes: [],
9494
9601
  staticImports: [],
9495
9602
  dynamicImports: []
9496
9603
  };
9497
- for (const { source, dynamic } of collectImportSources(code)) {
9604
+ for (const { source, dynamic } of sources) {
9498
9605
  if (isRemoteImport(source)) {
9499
9606
  if (!dynamic) edges.remotes.push(source);
9500
9607
  continue;
@@ -10927,6 +11034,8 @@ function readBoundedRunnerBody(req, res) {
10927
11034
  */
10928
11035
  function pluginSSRRemoteEntry(options) {
10929
11036
  const remoteEntrySSRId = getRemoteEntrySSRId(options);
11037
+ const ssrHostInitId = getSsrHostInitId(options);
11038
+ let hostInitChunkRef;
10930
11039
  const virtualExposesSSRId = getVirtualExposesSSRId(options);
10931
11040
  let cachedSsrRemoteEntrySource;
10932
11041
  const ssrOutputFilename = getSsrRemoteEntryFileName(options);
@@ -10990,6 +11099,7 @@ function pluginSSRRemoteEntry(options) {
10990
11099
  resolveId(id, importer) {
10991
11100
  if (id === remoteEntrySSRId || id.startsWith(remoteEntrySSRId)) return id;
10992
11101
  if (id === virtualExposesSSRId || id.startsWith(virtualExposesSSRId)) return id;
11102
+ if (id === ssrHostInitId) return this.resolve(getHostAutoInitPath(options), importer, { skipSelf: true });
10993
11103
  if (!importer || !ssrModuleIds.has(importer)) return;
10994
11104
  if (ssrOnlyExternalPattern.test(id) && !isNoExternal(id)) return {
10995
11105
  id,
@@ -11102,9 +11212,9 @@ function pluginSSRRemoteEntry(options) {
11102
11212
  const isRolldown = getIsRolldown(this);
11103
11213
  const environment = this.environment;
11104
11214
  if (!isSsrRemoteEntryBuild(environment)) return;
11105
- if (!isRolldown && hasRemotes(options)) this.emitFile({
11215
+ if (!isRolldown && hasRemotes(options)) hostInitChunkRef = this.emitFile({
11106
11216
  type: "chunk",
11107
- id: getHostAutoInitPath(options),
11217
+ id: ssrHostInitId,
11108
11218
  name: "hostInit",
11109
11219
  preserveSignature: "strict"
11110
11220
  });
@@ -11130,11 +11240,7 @@ function pluginSSRRemoteEntry(options) {
11130
11240
  const exposesChunk = findNuxtExposesChunk(bundle) ?? findEmittedExposesChunk(bundle);
11131
11241
  if (!isRolldown && isSsrRemoteEntryBuild(environment)) {
11132
11242
  let source = getSsrRemoteEntrySource();
11133
- if (hasRemotes(options)) {
11134
- const hostInitId = getHostAutoInitPath(options);
11135
- const hostInitChunk = Object.values(bundle).find((file) => file.type === "chunk" && file.facadeModuleId?.includes(hostInitId));
11136
- if (hostInitChunk) source = source.replace(JSON.stringify(hostInitId), JSON.stringify(toRelativeImport(ssrOutputFilename, hostInitChunk.fileName)));
11137
- }
11243
+ if (hasRemotes(options) && hostInitChunkRef) source = source.replace(JSON.stringify(ssrHostInitId), JSON.stringify(toRelativeImport(ssrOutputFilename, this.getFileName(hostInitChunkRef))));
11138
11244
  if (exposesChunk) source = source.replace(/import\("virtual:mf-exposes-ssr:[^"]+"\)/g, `import("${toRelativeImport(ssrOutputFilename, exposesChunk)}")`);
11139
11245
  this.emitFile({
11140
11246
  type: "asset",
@@ -11266,7 +11372,13 @@ const VarRemoteEntry = (providedOptions) => {
11266
11372
  }
11267
11373
  }];
11268
11374
  function isValidVarName(name) {
11269
- return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
11375
+ if (!isValidJsIdentifier(name)) return false;
11376
+ try {
11377
+ new Function(`var ${name};`);
11378
+ return true;
11379
+ } catch {
11380
+ return false;
11381
+ }
11270
11382
  }
11271
11383
  /**
11272
11384
  * Generates the final "var" remote entry file
@@ -11278,23 +11390,35 @@ const VarRemoteEntry = (providedOptions) => {
11278
11390
  const isValidName = isValidVarName(name);
11279
11391
  return `
11280
11392
  ${isValidName ? `var ${name};` : ""}
11281
- ${isValidName ? name : `globalThis['${name}']`} = (function () {
11393
+ (function (global) {
11394
+ const name = ${toSafeJsLiteral(name)};
11395
+ const descriptor = Object.getOwnPropertyDescriptor(global, name);
11396
+ const prototype = Object.getPrototypeOf(global);
11397
+ const emptyVarSlot = ${isValidName} && descriptor && 'value' in descriptor && descriptor.value === undefined && descriptor.writable && !(prototype && name in prototype);
11398
+ if (name in global && !emptyVarSlot) {
11399
+ const existing = descriptor && 'value' in descriptor ? descriptor.value : undefined;
11400
+ if (existing && typeof existing.get === 'function' && typeof existing.init === 'function') {
11401
+ return;
11402
+ }
11403
+ throw new Error('[Module Federation] Cannot register var remote "' + name + '" because the global already exists. Choose a different federation name or use an ESM remote.');
11404
+ }
11405
+
11282
11406
  function getScriptUrl() {
11283
11407
  const currentScript = document.currentScript;
11284
11408
  if (!currentScript) {
11285
- console.error("[Module Federation] ${varFilename} script should be called from sync <script> tag (document.currentScript is undefined)")
11409
+ console.error(${toSafeJsLiteral(`[Module Federation] ${varFilename} script should be called from sync <script> tag (document.currentScript is undefined)`)})
11286
11410
  return '/';
11287
11411
  }
11288
11412
  return document.currentScript.src.replace(/\\/[^/]*$/, '/');
11289
11413
  }
11290
11414
 
11291
- const entry = getScriptUrl() + '${remoteEntryFile}';
11415
+ const entry = getScriptUrl() + ${toSafeJsLiteral(remoteEntryFile)};
11292
11416
 
11293
- return {
11417
+ global[name] = {
11294
11418
  get: (...args) => import(entry).then(m => m.get(...args)),
11295
11419
  init: (...args) => import(entry).then(m => m.init(...args)),
11296
11420
  };
11297
- })();
11421
+ })(globalThis);
11298
11422
  `;
11299
11423
  }
11300
11424
  };
@@ -11338,11 +11462,32 @@ function pluginSsrRuntimeInit(options) {
11338
11462
  ${pluginImports}
11339
11463
  ${getRuntimeInitBootstrapCode(false, getRuntimeInitStatusImportId(options))}
11340
11464
  globalThis[globalKey].ssrInitStarted = true;
11465
+ // These remotes are registered before application code, under owner-scoped
11466
+ // names aliased to their configured names, and the runtime resolves an id
11467
+ // against the first remote whose name or alias matches. Drop the alias once the
11468
+ // application registers a remote under that name or alias, so its entry wins.
11469
+ const bootstrapRemoteNames = new Set(usedRemotes.map((remote) => remote.name));
11470
+ const applicationRemotePlugin = {
11471
+ name: 'vite-ssr-application-remote-plugin',
11472
+ beforeRegisterRemote(args) {
11473
+ const { name, alias } = args.remote;
11474
+ if (bootstrapRemoteNames.has(name)) return args;
11475
+ for (const registered of args.origin.options.remotes) {
11476
+ if (
11477
+ bootstrapRemoteNames.has(registered.name) &&
11478
+ (registered.alias === name || registered.alias === alias)
11479
+ ) {
11480
+ delete registered.alias;
11481
+ }
11482
+ }
11483
+ return args;
11484
+ },
11485
+ };
11341
11486
  const runtime = init({
11342
11487
  name: ${toSafeJsLiteral(options.name)},
11343
11488
  remotes: usedRemotes,
11344
11489
  shared: usedShared,
11345
- plugins: [${plugins}],
11490
+ plugins: [applicationRemotePlugin, ${plugins}],
11346
11491
  shareStrategy: ${toSafeJsLiteral(options.shareStrategy)},
11347
11492
  });
11348
11493
  const hostInitPromise = Promise.resolve(runtime);
@@ -12071,13 +12216,18 @@ function createEarlyVirtualModulesPlugin(options) {
12071
12216
  return {
12072
12217
  name: "vite:module-federation-early-init",
12073
12218
  enforce: "pre",
12074
- config(config, { command: _command }) {
12219
+ config(config, { command: _command, mode }) {
12075
12220
  if (_command === "serve") ignoreFederationGeneratedFiles(config, options);
12076
12221
  const root = config.root || process.cwd();
12077
12222
  const buildInput = getBuildInput(config);
12078
12223
  const resolvedConfiguredEntryFiles = (typeof buildInput === "string" ? [buildInput] : Array.isArray(buildInput) ? buildInput : buildInput && typeof buildInput === "object" ? Object.values(buildInput) : []).map((entry) => String(entry)).filter((entry) => !isReactRouterBuildClientRouteInput(entry)).map((entry) => entry.split(/[?#]/)[0]).map((entry) => path$1.isAbsolute(entry) ? entry : path$1.resolve(root, entry));
12079
12224
  resetConcreteSharedImportSourceCache();
12080
12225
  setPackageDetectionCwd(root);
12226
+ setWorkspaceEntryConditions(getSharedExportConditions({
12227
+ isProduction: mode === "production",
12228
+ isSsr: false,
12229
+ rootConditions: config.resolve?.conditions
12230
+ }));
12081
12231
  resolveSharedVersions(shared, root);
12082
12232
  const isVinext = hasPackageDependency("vinext");
12083
12233
  initVirtualModules(_command, getRemoteEntryId(options), false, options);
@@ -12305,12 +12455,15 @@ export default __mfShared.default ?? __mfShared;`
12305
12455
  "@module-federation/sdk"
12306
12456
  ];
12307
12457
  const resolvedShared = {};
12308
- for (const pkg of [...commonSharedPkgs, ...sharedKeys]) try {
12309
- resolvedShared[pkg] = projectRequire.resolve(pkg);
12310
- } catch {
12458
+ for (const pkg of [...commonSharedPkgs, ...sharedKeys]) {
12459
+ const resolutionSpecifier = removeTrailingSlash(pkg);
12311
12460
  try {
12312
- resolvedShared[pkg] = resolveImportPath(pkg);
12313
- } catch {}
12461
+ resolvedShared[pkg] = projectRequire.resolve(resolutionSpecifier);
12462
+ } catch {
12463
+ try {
12464
+ resolvedShared[pkg] = resolveImportPath(resolutionSpecifier);
12465
+ } catch {}
12466
+ }
12314
12467
  }
12315
12468
  const ssrEntryLoaderSpecifier = SSR_ENTRY_LOADER_SPECIFIER;
12316
12469
  try {