@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/README.md +6 -0
- package/lib/index.js +706 -553
- package/lib/{ssrEntryLoader-Bh2Tq1cZ.js → ssrEntryLoader-DKpOrW8D.js} +69 -5
- package/lib/{ssrVmStrategy-BCaiSEbA.js → ssrVmStrategy-D1cSKUV-.js} +4 -7
- package/lib/utils/ssrEntryLoader.d.ts +7 -1
- package/lib/utils/ssrEntryLoader.js +2 -2
- package/package.json +1 -1
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/
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
"
|
|
1485
|
-
|
|
1486
|
-
".
|
|
1487
|
-
|
|
1488
|
-
|
|
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
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
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 (
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
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
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
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
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
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
|
|
1640
|
-
|
|
1641
|
-
|
|
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
|
|
1644
|
-
|
|
1645
|
-
return
|
|
1515
|
+
function isInvalidTypeArgumentTerminator(source, index, followsNamedExpression) {
|
|
1516
|
+
const char = source[index];
|
|
1517
|
+
return char === ";" || char === "=" && source[index + 1] !== ">" && followsNamedExpression;
|
|
1646
1518
|
}
|
|
1647
|
-
/**
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
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
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
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
|
-
|
|
1673
|
-
|
|
1674
|
-
return
|
|
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
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
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
|
|
1710
|
-
|
|
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
|
|
1909
|
-
|
|
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:
|
|
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
|
-
|
|
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.
|
|
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(
|
|
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 {
|
|
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
|
|
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" ?
|
|
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" ?
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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() +
|
|
11415
|
+
const entry = getScriptUrl() + ${toSafeJsLiteral(remoteEntryFile)};
|
|
11292
11416
|
|
|
11293
|
-
|
|
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])
|
|
12309
|
-
|
|
12310
|
-
} catch {
|
|
12458
|
+
for (const pkg of [...commonSharedPkgs, ...sharedKeys]) {
|
|
12459
|
+
const resolutionSpecifier = removeTrailingSlash(pkg);
|
|
12311
12460
|
try {
|
|
12312
|
-
resolvedShared[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 {
|