@module-federation/vite 1.13.7 → 1.14.1
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.cjs +284 -15
- package/lib/index.d.cts +1 -0
- package/lib/index.d.mts +1 -0
- package/lib/index.mjs +285 -16
- package/package.json +1 -1
package/lib/index.cjs
CHANGED
|
@@ -461,6 +461,198 @@ function PluginDevProxyModuleTopLevelAwait() {
|
|
|
461
461
|
};
|
|
462
462
|
}
|
|
463
463
|
//#endregion
|
|
464
|
+
//#region src/plugins/pluginDevRemoteHmr.ts
|
|
465
|
+
const REMOTE_HMR_ENDPOINT = "__mf_hmr";
|
|
466
|
+
const REMOTE_HMR_EVENT = "mf:remote-update";
|
|
467
|
+
const REMOTE_HMR_CONNECT_RETRY_DELAY_MS = 1e3;
|
|
468
|
+
const REMOTE_HMR_CONNECT_MAX_RETRIES = 10;
|
|
469
|
+
function getBasePath(base) {
|
|
470
|
+
if (!base) return "/";
|
|
471
|
+
if (base.startsWith("http://") || base.startsWith("https://")) try {
|
|
472
|
+
return new URL(base).pathname || "/";
|
|
473
|
+
} catch {
|
|
474
|
+
return "/";
|
|
475
|
+
}
|
|
476
|
+
return base;
|
|
477
|
+
}
|
|
478
|
+
function getRemoteHmrPath(base) {
|
|
479
|
+
return `${getBasePath(base).replace(/\/?$/, "/")}${REMOTE_HMR_ENDPOINT}`.replace(/\/{2,}/g, "/");
|
|
480
|
+
}
|
|
481
|
+
function getHmrWsPath(base, hmrPath) {
|
|
482
|
+
const normalizedBase = getBasePath(base);
|
|
483
|
+
const normalizedPath = getBasePath(hmrPath || "");
|
|
484
|
+
if (!normalizedPath || normalizedPath === "/") return normalizedBase;
|
|
485
|
+
return `${normalizedBase.endsWith("/") ? normalizedBase.slice(0, -1) : normalizedBase}/${normalizedPath.startsWith("/") ? normalizedPath.slice(1) : normalizedPath}`;
|
|
486
|
+
}
|
|
487
|
+
function shouldIgnoreFile(file, options) {
|
|
488
|
+
return file.includes("/node_modules/") || file.includes("\\node_modules\\") || file.includes(`/${options.virtualModuleDir}/`) || file.includes(`\\${options.virtualModuleDir}\\`) || file.includes("/.vite/") || file.includes("\\.vite\\") || file.includes("/.__mf__temp/") || file.includes("\\.__mf__temp\\");
|
|
489
|
+
}
|
|
490
|
+
function getRemoteHmrWsUrl(server) {
|
|
491
|
+
const hmr = server.config.server.hmr;
|
|
492
|
+
return `${hmr && typeof hmr === "object" && hmr.protocol ? hmr.protocol : server.config.server.https ? "wss" : "ws"}://${hmr && typeof hmr === "object" && hmr.host ? hmr.host : typeof server.config.server.host === "string" && server.config.server.host !== "0.0.0.0" ? server.config.server.host : "localhost"}:${hmr && typeof hmr === "object" && (hmr.clientPort || hmr.port) ? hmr.clientPort || hmr.port : server.config.server.port}${getHmrWsPath(server.config.base, hmr && typeof hmr === "object" ? hmr.path : "")}?token=${server.config.webSocketToken}`;
|
|
493
|
+
}
|
|
494
|
+
function getLocalFallbackOrigin(server) {
|
|
495
|
+
return `${server.config.server.https ? "https" : "http"}://${typeof server.config.server.host === "string" && server.config.server.host !== "0.0.0.0" && server.config.server.host !== "::" ? server.config.server.host : "localhost"}:${server.config.server.port || 5173}`;
|
|
496
|
+
}
|
|
497
|
+
function getRemoteHmrEndpoint(remoteEntry, server) {
|
|
498
|
+
try {
|
|
499
|
+
const remoteManifestUrl = new URL(remoteEntry, getLocalFallbackOrigin(server));
|
|
500
|
+
remoteManifestUrl.pathname = `/${remoteManifestUrl.pathname.split("/").filter(Boolean).slice(0, -1).join("/")}`;
|
|
501
|
+
if (!remoteManifestUrl.pathname.endsWith("/")) remoteManifestUrl.pathname += "/";
|
|
502
|
+
remoteManifestUrl.search = "";
|
|
503
|
+
remoteManifestUrl.hash = "";
|
|
504
|
+
return new URL(REMOTE_HMR_ENDPOINT, remoteManifestUrl).toString();
|
|
505
|
+
} catch {
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
function parseRemoteHmrMessage(rawData) {
|
|
510
|
+
if (typeof rawData !== "string") return null;
|
|
511
|
+
try {
|
|
512
|
+
const parsed = JSON.parse(rawData);
|
|
513
|
+
if (parsed?.type !== "custom" || typeof parsed?.event !== "string") return null;
|
|
514
|
+
return parsed;
|
|
515
|
+
} catch {
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
function getStringPreview(value, max = 180) {
|
|
520
|
+
let rawValue = "";
|
|
521
|
+
if (typeof value === "string") rawValue = value;
|
|
522
|
+
else if (value instanceof Error) rawValue = `${value.name}: ${value.message}`;
|
|
523
|
+
else if (typeof value === "object" && value !== null) try {
|
|
524
|
+
rawValue = JSON.stringify(value);
|
|
525
|
+
} catch {}
|
|
526
|
+
return rawValue.slice(0, max);
|
|
527
|
+
}
|
|
528
|
+
function isRemoteHmrEnabled(dev) {
|
|
529
|
+
return typeof dev === "object" && dev !== null && dev.remoteHmr === true;
|
|
530
|
+
}
|
|
531
|
+
function pluginDevRemoteHmr(options) {
|
|
532
|
+
return {
|
|
533
|
+
name: "module-federation-dev-remote-hmr",
|
|
534
|
+
apply: "serve",
|
|
535
|
+
configureServer(server) {
|
|
536
|
+
if (!isRemoteHmrEnabled(options.dev)) return;
|
|
537
|
+
const isRemote = Object.keys(options.exposes).length > 0;
|
|
538
|
+
const isHost = Object.keys(options.remotes).length > 0;
|
|
539
|
+
if (isRemote) {
|
|
540
|
+
const endpointPath = getRemoteHmrPath(server.config.base);
|
|
541
|
+
const wsUrl = getRemoteHmrWsUrl(server);
|
|
542
|
+
server.middlewares.use((req, res, next) => {
|
|
543
|
+
if (req.url?.replace(/\?.*/, "") !== endpointPath) {
|
|
544
|
+
next();
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
res.setHeader("Content-Type", "application/json");
|
|
548
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
549
|
+
res.end(JSON.stringify({
|
|
550
|
+
remote: options.name,
|
|
551
|
+
event: REMOTE_HMR_EVENT,
|
|
552
|
+
wsUrl
|
|
553
|
+
}));
|
|
554
|
+
});
|
|
555
|
+
const broadcast = (file) => {
|
|
556
|
+
if (shouldIgnoreFile(file, options)) return;
|
|
557
|
+
server.ws.send({
|
|
558
|
+
type: "custom",
|
|
559
|
+
event: REMOTE_HMR_EVENT,
|
|
560
|
+
data: {
|
|
561
|
+
remote: options.name,
|
|
562
|
+
file,
|
|
563
|
+
ts: Date.now()
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
};
|
|
567
|
+
server.watcher.on("change", broadcast);
|
|
568
|
+
server.watcher.on("add", broadcast);
|
|
569
|
+
server.watcher.on("unlink", broadcast);
|
|
570
|
+
server.httpServer?.once("close", () => {
|
|
571
|
+
server.watcher.off("change", broadcast);
|
|
572
|
+
server.watcher.off("add", broadcast);
|
|
573
|
+
server.watcher.off("unlink", broadcast);
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
if (isHost) {
|
|
577
|
+
const connections = [];
|
|
578
|
+
const reconnectTimers = /* @__PURE__ */ new Map();
|
|
579
|
+
let isTearingDown = false;
|
|
580
|
+
const clearReconnectTimer = (remoteName) => {
|
|
581
|
+
const timer = reconnectTimers.get(remoteName);
|
|
582
|
+
if (!timer) return;
|
|
583
|
+
clearTimeout(timer);
|
|
584
|
+
reconnectTimers.delete(remoteName);
|
|
585
|
+
};
|
|
586
|
+
const scheduleReconnect = (remoteName, remote, attempt, reason) => {
|
|
587
|
+
if (isTearingDown) return;
|
|
588
|
+
if (attempt >= REMOTE_HMR_CONNECT_MAX_RETRIES) {
|
|
589
|
+
mfWarn(`Remote "${remoteName}" full HMR reconnect skipped after ${REMOTE_HMR_CONNECT_MAX_RETRIES} attempts: ${reason}`);
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
clearReconnectTimer(remoteName);
|
|
593
|
+
const timer = setTimeout(() => {
|
|
594
|
+
reconnectTimers.delete(remoteName);
|
|
595
|
+
connectRemote(remoteName, remote, attempt + 1);
|
|
596
|
+
}, REMOTE_HMR_CONNECT_RETRY_DELAY_MS);
|
|
597
|
+
reconnectTimers.set(remoteName, timer);
|
|
598
|
+
};
|
|
599
|
+
const connectRemote = async (remoteName, remote, attempt = 0) => {
|
|
600
|
+
if (isTearingDown) return;
|
|
601
|
+
const endpoint = getRemoteHmrEndpoint(remote.entry, server);
|
|
602
|
+
if (!endpoint) {
|
|
603
|
+
mfWarn(`Failed to build HMR endpoint URL for remote "${remoteName}"`);
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
try {
|
|
607
|
+
const metadataResponse = await fetch(endpoint);
|
|
608
|
+
if (!metadataResponse.ok) {
|
|
609
|
+
mfWarn(`Failed to fetch remote HMR metadata from "${remoteName}": ${metadataResponse.status}`);
|
|
610
|
+
scheduleReconnect(remoteName, remote, attempt, `HTTP ${metadataResponse.status}`);
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
const metadata = await metadataResponse.json();
|
|
614
|
+
if (metadata.event !== REMOTE_HMR_EVENT || !metadata.wsUrl) {
|
|
615
|
+
mfWarn(`Remote "${remoteName}" returned unexpected HMR metadata shape`);
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
const ws = new WebSocket(metadata.wsUrl, "vite-hmr");
|
|
619
|
+
ws.onmessage = (rawEvent) => {
|
|
620
|
+
const message = parseRemoteHmrMessage(rawEvent.data);
|
|
621
|
+
if (!message || message.event !== REMOTE_HMR_EVENT) return;
|
|
622
|
+
server.ws.send({ type: "full-reload" });
|
|
623
|
+
};
|
|
624
|
+
ws.onopen = () => clearReconnectTimer(remoteName);
|
|
625
|
+
ws.onerror = (error) => mfWarn(`Remote HMR socket error for "${remoteName}":`, error);
|
|
626
|
+
ws.onclose = () => scheduleReconnect(remoteName, remote, attempt, "socket closed");
|
|
627
|
+
connections.push(ws);
|
|
628
|
+
} catch (error) {
|
|
629
|
+
mfWarn(`Failed to connect remote HMR for "${remoteName}" on attempt ${attempt + 1}: ${getStringPreview(error)}`);
|
|
630
|
+
scheduleReconnect(remoteName, remote, attempt, getStringPreview(error));
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
const teardown = () => {
|
|
634
|
+
isTearingDown = true;
|
|
635
|
+
reconnectTimers.forEach((timer) => clearTimeout(timer));
|
|
636
|
+
reconnectTimers.clear();
|
|
637
|
+
connections.forEach((connection) => {
|
|
638
|
+
if (connection.readyState !== connection.CLOSING && connection.readyState !== connection.CLOSED) connection.close();
|
|
639
|
+
});
|
|
640
|
+
connections.length = 0;
|
|
641
|
+
};
|
|
642
|
+
for (const [remoteName, remote] of Object.entries(options.remotes)) connectRemote(remoteName, remote);
|
|
643
|
+
const triggerHostReload = (file) => {
|
|
644
|
+
if (shouldIgnoreFile(file, options)) return;
|
|
645
|
+
server.ws.send({ type: "full-reload" });
|
|
646
|
+
};
|
|
647
|
+
server.watcher.on("change", triggerHostReload);
|
|
648
|
+
server.watcher.on("add", triggerHostReload);
|
|
649
|
+
server.watcher.on("unlink", triggerHostReload);
|
|
650
|
+
server.httpServer?.once("close", teardown);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
//#endregion
|
|
464
656
|
//#region src/plugins/pluginDts.ts
|
|
465
657
|
const DEFAULT_DEV_OPTIONS = {
|
|
466
658
|
disableLiveReload: true,
|
|
@@ -713,11 +905,20 @@ function normalizeRemotes(remotes) {
|
|
|
713
905
|
}
|
|
714
906
|
function normalizeRemoteItem(key, remote) {
|
|
715
907
|
if (typeof remote === "string") {
|
|
716
|
-
const
|
|
908
|
+
const separatorIndex = remote.startsWith("@") ? remote.indexOf("@", 1) : remote.indexOf("@");
|
|
909
|
+
let entryGlobalName;
|
|
910
|
+
let entry;
|
|
911
|
+
if (separatorIndex > 0) {
|
|
912
|
+
entryGlobalName = remote.slice(0, separatorIndex);
|
|
913
|
+
entry = remote.slice(separatorIndex + 1);
|
|
914
|
+
} else {
|
|
915
|
+
entryGlobalName = remote;
|
|
916
|
+
entry = remote;
|
|
917
|
+
}
|
|
717
918
|
return {
|
|
718
919
|
type: "var",
|
|
719
920
|
name: key,
|
|
720
|
-
entry
|
|
921
|
+
entry,
|
|
721
922
|
entryGlobalName,
|
|
722
923
|
shareScope: "default"
|
|
723
924
|
};
|
|
@@ -743,8 +944,13 @@ function searchPackageVersion(sharedName) {
|
|
|
743
944
|
while (pathe.parse(potentialPackageJsonDir).base !== "node_modules" && potentialPackageJsonDir !== rootDir) {
|
|
744
945
|
const potentialPackageJsonPath = pathe.join(potentialPackageJsonDir, "package.json");
|
|
745
946
|
if (fs.existsSync(potentialPackageJsonPath)) {
|
|
746
|
-
const
|
|
747
|
-
|
|
947
|
+
const potentialPackageJsonContent = fs.readFileSync(potentialPackageJsonPath, "utf-8");
|
|
948
|
+
try {
|
|
949
|
+
const potentialPackageJson = JSON.parse(potentialPackageJsonContent);
|
|
950
|
+
if (typeof potentialPackageJson == "object" && potentialPackageJson !== null && typeof potentialPackageJson.version === "string" && potentialPackageJson.name === sharedName) return potentialPackageJson.version;
|
|
951
|
+
} catch (error) {
|
|
952
|
+
if (!(error instanceof SyntaxError)) throw error;
|
|
953
|
+
}
|
|
748
954
|
}
|
|
749
955
|
potentialPackageJsonDir = pathe.dirname(potentialPackageJsonDir);
|
|
750
956
|
}
|
|
@@ -1268,13 +1474,23 @@ function getInstalledPackageJsonPath(pkg) {
|
|
|
1268
1474
|
while (currentDir !== rootDir) {
|
|
1269
1475
|
const packageJsonPath = pathe.default.join(currentDir, "package.json");
|
|
1270
1476
|
if ((0, fs.existsSync)(packageJsonPath)) {
|
|
1271
|
-
|
|
1477
|
+
const packageJsonContent = (0, fs.readFileSync)(packageJsonPath, "utf-8");
|
|
1478
|
+
try {
|
|
1479
|
+
if (JSON.parse(packageJsonContent).name === packageName) return packageJsonPath;
|
|
1480
|
+
} catch (error) {
|
|
1481
|
+
if (!(error instanceof SyntaxError)) throw error;
|
|
1482
|
+
}
|
|
1272
1483
|
}
|
|
1273
1484
|
currentDir = pathe.default.dirname(currentDir);
|
|
1274
1485
|
}
|
|
1275
1486
|
const rootPackageJsonPath = pathe.default.join(rootDir, "package.json");
|
|
1276
1487
|
if ((0, fs.existsSync)(rootPackageJsonPath)) {
|
|
1277
|
-
|
|
1488
|
+
const rootPackageJsonContent = (0, fs.readFileSync)(rootPackageJsonPath, "utf-8");
|
|
1489
|
+
try {
|
|
1490
|
+
if (JSON.parse(rootPackageJsonContent).name === packageName) return rootPackageJsonPath;
|
|
1491
|
+
} catch (error) {
|
|
1492
|
+
if (!(error instanceof SyntaxError)) throw error;
|
|
1493
|
+
}
|
|
1278
1494
|
}
|
|
1279
1495
|
} catch {
|
|
1280
1496
|
const packageName = removePathFromNpmPackage(pkg);
|
|
@@ -1323,24 +1539,49 @@ function getPackageEsmEntryPath(pkg) {
|
|
|
1323
1539
|
}
|
|
1324
1540
|
function getEsmNamedExports(pkg) {
|
|
1325
1541
|
let source = "";
|
|
1542
|
+
let entryPath;
|
|
1326
1543
|
try {
|
|
1327
|
-
|
|
1544
|
+
entryPath = getPackageEsmEntryPath(pkg);
|
|
1328
1545
|
if (!entryPath) return [];
|
|
1329
1546
|
const { initSync, parse } = localRequire("es-module-lexer");
|
|
1330
1547
|
initSync();
|
|
1331
1548
|
source = (0, fs.readFileSync)(entryPath, "utf-8");
|
|
1332
1549
|
const [, exports] = parse(source, entryPath);
|
|
1333
1550
|
const names = exports.map((item) => item.n).filter((name) => isValidEsmExportName(name));
|
|
1334
|
-
const regexNames = getNamedExportsViaRegex(source);
|
|
1551
|
+
const regexNames = getNamedExportsViaRegex(source, entryPath);
|
|
1335
1552
|
const filteredNames = names.filter((name) => name !== "type" || regexNames.includes(name));
|
|
1336
1553
|
if (filteredNames.length > 0) return [...new Set([...filteredNames, ...regexNames])];
|
|
1337
1554
|
return regexNames;
|
|
1338
1555
|
} catch {
|
|
1339
|
-
return source ? getNamedExportsViaRegex(source) : [];
|
|
1556
|
+
return source ? getNamedExportsViaRegex(source, entryPath) : [];
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
function resolveRelativeModule(filePath, specifier) {
|
|
1560
|
+
const dir = pathe.default.dirname(filePath);
|
|
1561
|
+
const exact = pathe.default.resolve(dir, specifier);
|
|
1562
|
+
if ((0, fs.existsSync)(exact) && !(0, fs.statSync)(exact).isDirectory()) return exact;
|
|
1563
|
+
const extensions = [
|
|
1564
|
+
".ts",
|
|
1565
|
+
".tsx",
|
|
1566
|
+
".js",
|
|
1567
|
+
".jsx",
|
|
1568
|
+
".mjs",
|
|
1569
|
+
".mts"
|
|
1570
|
+
];
|
|
1571
|
+
for (const ext of extensions) {
|
|
1572
|
+
const candidate = pathe.default.resolve(dir, specifier + ext);
|
|
1573
|
+
if ((0, fs.existsSync)(candidate) && !(0, fs.statSync)(candidate).isDirectory()) return candidate;
|
|
1574
|
+
}
|
|
1575
|
+
const resolved = pathe.default.resolve(dir, specifier);
|
|
1576
|
+
for (const ext of extensions) {
|
|
1577
|
+
const candidate = pathe.default.join(resolved, "index" + ext);
|
|
1578
|
+
if ((0, fs.existsSync)(candidate)) return candidate;
|
|
1340
1579
|
}
|
|
1341
1580
|
}
|
|
1342
|
-
function getNamedExportsViaRegex(source) {
|
|
1581
|
+
function getNamedExportsViaRegex(source, filePath, visited) {
|
|
1343
1582
|
const names = /* @__PURE__ */ new Set();
|
|
1583
|
+
visited = visited || /* @__PURE__ */ new Set();
|
|
1584
|
+
if (filePath) visited.add(filePath);
|
|
1344
1585
|
const declRegex = new RegExp(`export\\s+(?:async\\s+)?(?:function(?:\\*\\s*|\\s+\\*?\\s*)|const\\s+|let\\s+|var\\s+|class\\s+)(${JS_IDENTIFIER_PATTERN})`, "gu");
|
|
1345
1586
|
let match;
|
|
1346
1587
|
while ((match = declRegex.exec(source)) !== null) {
|
|
@@ -1361,6 +1602,19 @@ function getNamedExportsViaRegex(source) {
|
|
|
1361
1602
|
if (isValidEsmExportName(name)) names.add(name);
|
|
1362
1603
|
}
|
|
1363
1604
|
}
|
|
1605
|
+
if (filePath) {
|
|
1606
|
+
const starExportRegex = /export\s+\*\s+from\s+['"]([^'"]+)['"]/g;
|
|
1607
|
+
while ((match = starExportRegex.exec(source)) !== null) {
|
|
1608
|
+
const specifier = match[1];
|
|
1609
|
+
if (!specifier.startsWith(".")) continue;
|
|
1610
|
+
const resolvedPath = resolveRelativeModule(filePath, specifier);
|
|
1611
|
+
if (!resolvedPath || visited.has(resolvedPath)) continue;
|
|
1612
|
+
try {
|
|
1613
|
+
const reExportNames = getNamedExportsViaRegex((0, fs.readFileSync)(resolvedPath, "utf-8"), resolvedPath, visited);
|
|
1614
|
+
for (const name of reExportNames) names.add(name);
|
|
1615
|
+
} catch {}
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1364
1618
|
return [...names];
|
|
1365
1619
|
}
|
|
1366
1620
|
function getPackageNamedExports(pkg) {
|
|
@@ -1522,6 +1776,10 @@ function generateLocalSharedImportMap() {
|
|
|
1522
1776
|
const isAstro = hasPackageDependency("astro");
|
|
1523
1777
|
const useDirectReactImport = isVinext || isAstro;
|
|
1524
1778
|
const options = getNormalizeModuleFederationOptions();
|
|
1779
|
+
const getPackagePath = (pkg, shareItem) => {
|
|
1780
|
+
if (useDirectReactImport && pkg === "react") return "react";
|
|
1781
|
+
return getConcreteSharedImportSource(pkg, shareItem) || getLocalProviderImportPath(pkg) || getSharedImportSource(pkg, shareItem);
|
|
1782
|
+
};
|
|
1525
1783
|
return `
|
|
1526
1784
|
import {loadShare} from "@module-federation/runtime";
|
|
1527
1785
|
const importMap = {
|
|
@@ -1529,8 +1787,7 @@ function generateLocalSharedImportMap() {
|
|
|
1529
1787
|
const shareItem = getNormalizeShareItem(pkg);
|
|
1530
1788
|
return `
|
|
1531
1789
|
${JSON.stringify(pkg)}: async () => {
|
|
1532
|
-
${shareItem?.shareConfig.import === false ? `throw new Error(\`[Module Federation] Shared module '\${${JSON.stringify(pkg)}}' must be provided by host\`);` :
|
|
1533
|
-
return pkg;` : `let pkg = await import(${JSON.stringify(getSharedImportSource(pkg, shareItem))});
|
|
1790
|
+
${shareItem?.shareConfig.import === false ? `throw new Error(\`[Module Federation] Shared module '\${${JSON.stringify(pkg)}}' must be provided by host\`);` : `let pkg = await import(${JSON.stringify(getPackagePath(pkg, shareItem))});
|
|
1534
1791
|
return pkg;`}
|
|
1535
1792
|
}
|
|
1536
1793
|
`;
|
|
@@ -1975,7 +2232,7 @@ const Manifest = () => {
|
|
|
1975
2232
|
root = config.root;
|
|
1976
2233
|
let base = config.base;
|
|
1977
2234
|
if (_command === "serve") base = (config.server.origin || "") + config.base;
|
|
1978
|
-
publicPath = resolvePublicPath(mfOptions, base, _originalConfigBase);
|
|
2235
|
+
publicPath = mfOptions.publicPath === "auto" ? "auto" : resolvePublicPath(mfOptions, base, _originalConfigBase);
|
|
1979
2236
|
},
|
|
1980
2237
|
async generateBundle(options, bundle) {
|
|
1981
2238
|
if (!mfManifestName) return;
|
|
@@ -2885,6 +3142,16 @@ var aliasToArrayPlugin_default = {
|
|
|
2885
3142
|
}
|
|
2886
3143
|
};
|
|
2887
3144
|
//#endregion
|
|
3145
|
+
//#region src/utils/isTestEnv.ts
|
|
3146
|
+
/**
|
|
3147
|
+
* Detects whether the current process is running in a test environment
|
|
3148
|
+
* Set `MFE_VITE_NO_TEST_ENV_CHECK=true` to load federation plugins during tests.
|
|
3149
|
+
*/
|
|
3150
|
+
function isTestEnv() {
|
|
3151
|
+
if (process.env.MFE_VITE_NO_TEST_ENV_CHECK === "true") return false;
|
|
3152
|
+
return process.env.NODE_ENV === "test" || process.env.VITEST != null || process.env.JEST_WORKER_ID != null;
|
|
3153
|
+
}
|
|
3154
|
+
//#endregion
|
|
2888
3155
|
//#region src/utils/controlChunkSanitizer.ts
|
|
2889
3156
|
const FEDERATION_CONTROL_CHUNK_HINTS = [
|
|
2890
3157
|
"hostInit",
|
|
@@ -3030,6 +3297,7 @@ function createEarlyVirtualModulesPlugin(options) {
|
|
|
3030
3297
|
};
|
|
3031
3298
|
}
|
|
3032
3299
|
function federation(mfUserOptions) {
|
|
3300
|
+
if (isTestEnv()) return [];
|
|
3033
3301
|
const options = normalizeModuleFederationOptions(mfUserOptions);
|
|
3034
3302
|
const isVinext = hasPackageDependency("vinext");
|
|
3035
3303
|
const { name, remotes, shared, filename, hostInitInjectLocation } = options;
|
|
@@ -3078,6 +3346,7 @@ function federation(mfUserOptions) {
|
|
|
3078
3346
|
checkAliasConflicts({ shared }),
|
|
3079
3347
|
normalizeOptimizeDeps_default,
|
|
3080
3348
|
...pluginDts(options),
|
|
3349
|
+
pluginDevRemoteHmr(options),
|
|
3081
3350
|
...addEntry({
|
|
3082
3351
|
entryName: "remoteEntry",
|
|
3083
3352
|
entryPath: remoteEntryId,
|
|
@@ -3424,12 +3693,12 @@ function federation(mfUserOptions) {
|
|
|
3424
3693
|
const prefixToRoot = chunkDir === "." ? "" : `${pathe.default.relative(chunkDir, ".")}/`;
|
|
3425
3694
|
const replacementExpr = prefixToRoot ? `${escapeUnsafeJsSourceChars(JSON.stringify(prefixToRoot))}+$1` : "$1";
|
|
3426
3695
|
const replacement = `=function($1){return new URL(${replacementExpr},import.meta.url).href}`;
|
|
3427
|
-
const replaced = chunk.code.replace(/=\(?(\w+)(?:,\w+)?\)?\s*=>\s*["'
|
|
3696
|
+
const replaced = chunk.code.replace(/=\(?(\w+)(?:,\w+)?\)?\s*=>\s*["'][./][^"']*["']\s*\+\s*\1/, replacement);
|
|
3428
3697
|
if (replaced !== chunk.code) {
|
|
3429
3698
|
chunk.code = replaced;
|
|
3430
3699
|
continue;
|
|
3431
3700
|
}
|
|
3432
|
-
chunk.code = chunk.code.replace(/=function\((\w+)(?:,\w+)?\)\{return\s*["'
|
|
3701
|
+
chunk.code = chunk.code.replace(/=function\((\w+)(?:,\w+)?\)\{return\s*["'][./][^"']*["']\s*\+\s*\1\s*\}/, replacement);
|
|
3433
3702
|
chunk.code = chunk.code.replace(/=function\((\w+)(?:,\w+)?\)\{return new URL\("\.\.\/"\+\1,import\.meta\.url\)\.href\}/, replacement);
|
|
3434
3703
|
chunk.code = chunk.code.replace(/new URL\("\.\.\/"\+(\w+),import\.meta\.url\)\.href/g, `new URL(${replacementExpr},import.meta.url).href`);
|
|
3435
3704
|
}
|
package/lib/index.d.cts
CHANGED
package/lib/index.d.mts
CHANGED
package/lib/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import defu from "defu";
|
|
3
3
|
import * as fs from "fs";
|
|
4
|
-
import { existsSync, mkdirSync, readFileSync, writeFile, writeFileSync } from "fs";
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, statSync, writeFile, writeFileSync } from "fs";
|
|
5
5
|
import { createRequire as createRequire$1 } from "module";
|
|
6
6
|
import * as path$1 from "pathe";
|
|
7
7
|
import path, { basename, dirname, join, parse, resolve } from "pathe";
|
|
@@ -439,6 +439,198 @@ function PluginDevProxyModuleTopLevelAwait() {
|
|
|
439
439
|
};
|
|
440
440
|
}
|
|
441
441
|
//#endregion
|
|
442
|
+
//#region src/plugins/pluginDevRemoteHmr.ts
|
|
443
|
+
const REMOTE_HMR_ENDPOINT = "__mf_hmr";
|
|
444
|
+
const REMOTE_HMR_EVENT = "mf:remote-update";
|
|
445
|
+
const REMOTE_HMR_CONNECT_RETRY_DELAY_MS = 1e3;
|
|
446
|
+
const REMOTE_HMR_CONNECT_MAX_RETRIES = 10;
|
|
447
|
+
function getBasePath(base) {
|
|
448
|
+
if (!base) return "/";
|
|
449
|
+
if (base.startsWith("http://") || base.startsWith("https://")) try {
|
|
450
|
+
return new URL(base).pathname || "/";
|
|
451
|
+
} catch {
|
|
452
|
+
return "/";
|
|
453
|
+
}
|
|
454
|
+
return base;
|
|
455
|
+
}
|
|
456
|
+
function getRemoteHmrPath(base) {
|
|
457
|
+
return `${getBasePath(base).replace(/\/?$/, "/")}${REMOTE_HMR_ENDPOINT}`.replace(/\/{2,}/g, "/");
|
|
458
|
+
}
|
|
459
|
+
function getHmrWsPath(base, hmrPath) {
|
|
460
|
+
const normalizedBase = getBasePath(base);
|
|
461
|
+
const normalizedPath = getBasePath(hmrPath || "");
|
|
462
|
+
if (!normalizedPath || normalizedPath === "/") return normalizedBase;
|
|
463
|
+
return `${normalizedBase.endsWith("/") ? normalizedBase.slice(0, -1) : normalizedBase}/${normalizedPath.startsWith("/") ? normalizedPath.slice(1) : normalizedPath}`;
|
|
464
|
+
}
|
|
465
|
+
function shouldIgnoreFile(file, options) {
|
|
466
|
+
return file.includes("/node_modules/") || file.includes("\\node_modules\\") || file.includes(`/${options.virtualModuleDir}/`) || file.includes(`\\${options.virtualModuleDir}\\`) || file.includes("/.vite/") || file.includes("\\.vite\\") || file.includes("/.__mf__temp/") || file.includes("\\.__mf__temp\\");
|
|
467
|
+
}
|
|
468
|
+
function getRemoteHmrWsUrl(server) {
|
|
469
|
+
const hmr = server.config.server.hmr;
|
|
470
|
+
return `${hmr && typeof hmr === "object" && hmr.protocol ? hmr.protocol : server.config.server.https ? "wss" : "ws"}://${hmr && typeof hmr === "object" && hmr.host ? hmr.host : typeof server.config.server.host === "string" && server.config.server.host !== "0.0.0.0" ? server.config.server.host : "localhost"}:${hmr && typeof hmr === "object" && (hmr.clientPort || hmr.port) ? hmr.clientPort || hmr.port : server.config.server.port}${getHmrWsPath(server.config.base, hmr && typeof hmr === "object" ? hmr.path : "")}?token=${server.config.webSocketToken}`;
|
|
471
|
+
}
|
|
472
|
+
function getLocalFallbackOrigin(server) {
|
|
473
|
+
return `${server.config.server.https ? "https" : "http"}://${typeof server.config.server.host === "string" && server.config.server.host !== "0.0.0.0" && server.config.server.host !== "::" ? server.config.server.host : "localhost"}:${server.config.server.port || 5173}`;
|
|
474
|
+
}
|
|
475
|
+
function getRemoteHmrEndpoint(remoteEntry, server) {
|
|
476
|
+
try {
|
|
477
|
+
const remoteManifestUrl = new URL(remoteEntry, getLocalFallbackOrigin(server));
|
|
478
|
+
remoteManifestUrl.pathname = `/${remoteManifestUrl.pathname.split("/").filter(Boolean).slice(0, -1).join("/")}`;
|
|
479
|
+
if (!remoteManifestUrl.pathname.endsWith("/")) remoteManifestUrl.pathname += "/";
|
|
480
|
+
remoteManifestUrl.search = "";
|
|
481
|
+
remoteManifestUrl.hash = "";
|
|
482
|
+
return new URL(REMOTE_HMR_ENDPOINT, remoteManifestUrl).toString();
|
|
483
|
+
} catch {
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
function parseRemoteHmrMessage(rawData) {
|
|
488
|
+
if (typeof rawData !== "string") return null;
|
|
489
|
+
try {
|
|
490
|
+
const parsed = JSON.parse(rawData);
|
|
491
|
+
if (parsed?.type !== "custom" || typeof parsed?.event !== "string") return null;
|
|
492
|
+
return parsed;
|
|
493
|
+
} catch {
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
function getStringPreview(value, max = 180) {
|
|
498
|
+
let rawValue = "";
|
|
499
|
+
if (typeof value === "string") rawValue = value;
|
|
500
|
+
else if (value instanceof Error) rawValue = `${value.name}: ${value.message}`;
|
|
501
|
+
else if (typeof value === "object" && value !== null) try {
|
|
502
|
+
rawValue = JSON.stringify(value);
|
|
503
|
+
} catch {}
|
|
504
|
+
return rawValue.slice(0, max);
|
|
505
|
+
}
|
|
506
|
+
function isRemoteHmrEnabled(dev) {
|
|
507
|
+
return typeof dev === "object" && dev !== null && dev.remoteHmr === true;
|
|
508
|
+
}
|
|
509
|
+
function pluginDevRemoteHmr(options) {
|
|
510
|
+
return {
|
|
511
|
+
name: "module-federation-dev-remote-hmr",
|
|
512
|
+
apply: "serve",
|
|
513
|
+
configureServer(server) {
|
|
514
|
+
if (!isRemoteHmrEnabled(options.dev)) return;
|
|
515
|
+
const isRemote = Object.keys(options.exposes).length > 0;
|
|
516
|
+
const isHost = Object.keys(options.remotes).length > 0;
|
|
517
|
+
if (isRemote) {
|
|
518
|
+
const endpointPath = getRemoteHmrPath(server.config.base);
|
|
519
|
+
const wsUrl = getRemoteHmrWsUrl(server);
|
|
520
|
+
server.middlewares.use((req, res, next) => {
|
|
521
|
+
if (req.url?.replace(/\?.*/, "") !== endpointPath) {
|
|
522
|
+
next();
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
res.setHeader("Content-Type", "application/json");
|
|
526
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
527
|
+
res.end(JSON.stringify({
|
|
528
|
+
remote: options.name,
|
|
529
|
+
event: REMOTE_HMR_EVENT,
|
|
530
|
+
wsUrl
|
|
531
|
+
}));
|
|
532
|
+
});
|
|
533
|
+
const broadcast = (file) => {
|
|
534
|
+
if (shouldIgnoreFile(file, options)) return;
|
|
535
|
+
server.ws.send({
|
|
536
|
+
type: "custom",
|
|
537
|
+
event: REMOTE_HMR_EVENT,
|
|
538
|
+
data: {
|
|
539
|
+
remote: options.name,
|
|
540
|
+
file,
|
|
541
|
+
ts: Date.now()
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
};
|
|
545
|
+
server.watcher.on("change", broadcast);
|
|
546
|
+
server.watcher.on("add", broadcast);
|
|
547
|
+
server.watcher.on("unlink", broadcast);
|
|
548
|
+
server.httpServer?.once("close", () => {
|
|
549
|
+
server.watcher.off("change", broadcast);
|
|
550
|
+
server.watcher.off("add", broadcast);
|
|
551
|
+
server.watcher.off("unlink", broadcast);
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
if (isHost) {
|
|
555
|
+
const connections = [];
|
|
556
|
+
const reconnectTimers = /* @__PURE__ */ new Map();
|
|
557
|
+
let isTearingDown = false;
|
|
558
|
+
const clearReconnectTimer = (remoteName) => {
|
|
559
|
+
const timer = reconnectTimers.get(remoteName);
|
|
560
|
+
if (!timer) return;
|
|
561
|
+
clearTimeout(timer);
|
|
562
|
+
reconnectTimers.delete(remoteName);
|
|
563
|
+
};
|
|
564
|
+
const scheduleReconnect = (remoteName, remote, attempt, reason) => {
|
|
565
|
+
if (isTearingDown) return;
|
|
566
|
+
if (attempt >= REMOTE_HMR_CONNECT_MAX_RETRIES) {
|
|
567
|
+
mfWarn(`Remote "${remoteName}" full HMR reconnect skipped after ${REMOTE_HMR_CONNECT_MAX_RETRIES} attempts: ${reason}`);
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
clearReconnectTimer(remoteName);
|
|
571
|
+
const timer = setTimeout(() => {
|
|
572
|
+
reconnectTimers.delete(remoteName);
|
|
573
|
+
connectRemote(remoteName, remote, attempt + 1);
|
|
574
|
+
}, REMOTE_HMR_CONNECT_RETRY_DELAY_MS);
|
|
575
|
+
reconnectTimers.set(remoteName, timer);
|
|
576
|
+
};
|
|
577
|
+
const connectRemote = async (remoteName, remote, attempt = 0) => {
|
|
578
|
+
if (isTearingDown) return;
|
|
579
|
+
const endpoint = getRemoteHmrEndpoint(remote.entry, server);
|
|
580
|
+
if (!endpoint) {
|
|
581
|
+
mfWarn(`Failed to build HMR endpoint URL for remote "${remoteName}"`);
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
try {
|
|
585
|
+
const metadataResponse = await fetch(endpoint);
|
|
586
|
+
if (!metadataResponse.ok) {
|
|
587
|
+
mfWarn(`Failed to fetch remote HMR metadata from "${remoteName}": ${metadataResponse.status}`);
|
|
588
|
+
scheduleReconnect(remoteName, remote, attempt, `HTTP ${metadataResponse.status}`);
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
const metadata = await metadataResponse.json();
|
|
592
|
+
if (metadata.event !== REMOTE_HMR_EVENT || !metadata.wsUrl) {
|
|
593
|
+
mfWarn(`Remote "${remoteName}" returned unexpected HMR metadata shape`);
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
const ws = new WebSocket(metadata.wsUrl, "vite-hmr");
|
|
597
|
+
ws.onmessage = (rawEvent) => {
|
|
598
|
+
const message = parseRemoteHmrMessage(rawEvent.data);
|
|
599
|
+
if (!message || message.event !== REMOTE_HMR_EVENT) return;
|
|
600
|
+
server.ws.send({ type: "full-reload" });
|
|
601
|
+
};
|
|
602
|
+
ws.onopen = () => clearReconnectTimer(remoteName);
|
|
603
|
+
ws.onerror = (error) => mfWarn(`Remote HMR socket error for "${remoteName}":`, error);
|
|
604
|
+
ws.onclose = () => scheduleReconnect(remoteName, remote, attempt, "socket closed");
|
|
605
|
+
connections.push(ws);
|
|
606
|
+
} catch (error) {
|
|
607
|
+
mfWarn(`Failed to connect remote HMR for "${remoteName}" on attempt ${attempt + 1}: ${getStringPreview(error)}`);
|
|
608
|
+
scheduleReconnect(remoteName, remote, attempt, getStringPreview(error));
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
const teardown = () => {
|
|
612
|
+
isTearingDown = true;
|
|
613
|
+
reconnectTimers.forEach((timer) => clearTimeout(timer));
|
|
614
|
+
reconnectTimers.clear();
|
|
615
|
+
connections.forEach((connection) => {
|
|
616
|
+
if (connection.readyState !== connection.CLOSING && connection.readyState !== connection.CLOSED) connection.close();
|
|
617
|
+
});
|
|
618
|
+
connections.length = 0;
|
|
619
|
+
};
|
|
620
|
+
for (const [remoteName, remote] of Object.entries(options.remotes)) connectRemote(remoteName, remote);
|
|
621
|
+
const triggerHostReload = (file) => {
|
|
622
|
+
if (shouldIgnoreFile(file, options)) return;
|
|
623
|
+
server.ws.send({ type: "full-reload" });
|
|
624
|
+
};
|
|
625
|
+
server.watcher.on("change", triggerHostReload);
|
|
626
|
+
server.watcher.on("add", triggerHostReload);
|
|
627
|
+
server.watcher.on("unlink", triggerHostReload);
|
|
628
|
+
server.httpServer?.once("close", teardown);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
//#endregion
|
|
442
634
|
//#region src/plugins/pluginDts.ts
|
|
443
635
|
const DEFAULT_DEV_OPTIONS = {
|
|
444
636
|
disableLiveReload: true,
|
|
@@ -691,11 +883,20 @@ function normalizeRemotes(remotes) {
|
|
|
691
883
|
}
|
|
692
884
|
function normalizeRemoteItem(key, remote) {
|
|
693
885
|
if (typeof remote === "string") {
|
|
694
|
-
const
|
|
886
|
+
const separatorIndex = remote.startsWith("@") ? remote.indexOf("@", 1) : remote.indexOf("@");
|
|
887
|
+
let entryGlobalName;
|
|
888
|
+
let entry;
|
|
889
|
+
if (separatorIndex > 0) {
|
|
890
|
+
entryGlobalName = remote.slice(0, separatorIndex);
|
|
891
|
+
entry = remote.slice(separatorIndex + 1);
|
|
892
|
+
} else {
|
|
893
|
+
entryGlobalName = remote;
|
|
894
|
+
entry = remote;
|
|
895
|
+
}
|
|
695
896
|
return {
|
|
696
897
|
type: "var",
|
|
697
898
|
name: key,
|
|
698
|
-
entry
|
|
899
|
+
entry,
|
|
699
900
|
entryGlobalName,
|
|
700
901
|
shareScope: "default"
|
|
701
902
|
};
|
|
@@ -721,8 +922,13 @@ function searchPackageVersion(sharedName) {
|
|
|
721
922
|
while (path$1.parse(potentialPackageJsonDir).base !== "node_modules" && potentialPackageJsonDir !== rootDir) {
|
|
722
923
|
const potentialPackageJsonPath = path$1.join(potentialPackageJsonDir, "package.json");
|
|
723
924
|
if (fs.existsSync(potentialPackageJsonPath)) {
|
|
724
|
-
const
|
|
725
|
-
|
|
925
|
+
const potentialPackageJsonContent = fs.readFileSync(potentialPackageJsonPath, "utf-8");
|
|
926
|
+
try {
|
|
927
|
+
const potentialPackageJson = JSON.parse(potentialPackageJsonContent);
|
|
928
|
+
if (typeof potentialPackageJson == "object" && potentialPackageJson !== null && typeof potentialPackageJson.version === "string" && potentialPackageJson.name === sharedName) return potentialPackageJson.version;
|
|
929
|
+
} catch (error) {
|
|
930
|
+
if (!(error instanceof SyntaxError)) throw error;
|
|
931
|
+
}
|
|
726
932
|
}
|
|
727
933
|
potentialPackageJsonDir = path$1.dirname(potentialPackageJsonDir);
|
|
728
934
|
}
|
|
@@ -1245,13 +1451,23 @@ function getInstalledPackageJsonPath(pkg) {
|
|
|
1245
1451
|
while (currentDir !== rootDir) {
|
|
1246
1452
|
const packageJsonPath = path.join(currentDir, "package.json");
|
|
1247
1453
|
if (existsSync(packageJsonPath)) {
|
|
1248
|
-
|
|
1454
|
+
const packageJsonContent = readFileSync(packageJsonPath, "utf-8");
|
|
1455
|
+
try {
|
|
1456
|
+
if (JSON.parse(packageJsonContent).name === packageName) return packageJsonPath;
|
|
1457
|
+
} catch (error) {
|
|
1458
|
+
if (!(error instanceof SyntaxError)) throw error;
|
|
1459
|
+
}
|
|
1249
1460
|
}
|
|
1250
1461
|
currentDir = path.dirname(currentDir);
|
|
1251
1462
|
}
|
|
1252
1463
|
const rootPackageJsonPath = path.join(rootDir, "package.json");
|
|
1253
1464
|
if (existsSync(rootPackageJsonPath)) {
|
|
1254
|
-
|
|
1465
|
+
const rootPackageJsonContent = readFileSync(rootPackageJsonPath, "utf-8");
|
|
1466
|
+
try {
|
|
1467
|
+
if (JSON.parse(rootPackageJsonContent).name === packageName) return rootPackageJsonPath;
|
|
1468
|
+
} catch (error) {
|
|
1469
|
+
if (!(error instanceof SyntaxError)) throw error;
|
|
1470
|
+
}
|
|
1255
1471
|
}
|
|
1256
1472
|
} catch {
|
|
1257
1473
|
const packageName = removePathFromNpmPackage(pkg);
|
|
@@ -1300,24 +1516,49 @@ function getPackageEsmEntryPath(pkg) {
|
|
|
1300
1516
|
}
|
|
1301
1517
|
function getEsmNamedExports(pkg) {
|
|
1302
1518
|
let source = "";
|
|
1519
|
+
let entryPath;
|
|
1303
1520
|
try {
|
|
1304
|
-
|
|
1521
|
+
entryPath = getPackageEsmEntryPath(pkg);
|
|
1305
1522
|
if (!entryPath) return [];
|
|
1306
1523
|
const { initSync, parse } = localRequire("es-module-lexer");
|
|
1307
1524
|
initSync();
|
|
1308
1525
|
source = readFileSync(entryPath, "utf-8");
|
|
1309
1526
|
const [, exports] = parse(source, entryPath);
|
|
1310
1527
|
const names = exports.map((item) => item.n).filter((name) => isValidEsmExportName(name));
|
|
1311
|
-
const regexNames = getNamedExportsViaRegex(source);
|
|
1528
|
+
const regexNames = getNamedExportsViaRegex(source, entryPath);
|
|
1312
1529
|
const filteredNames = names.filter((name) => name !== "type" || regexNames.includes(name));
|
|
1313
1530
|
if (filteredNames.length > 0) return [...new Set([...filteredNames, ...regexNames])];
|
|
1314
1531
|
return regexNames;
|
|
1315
1532
|
} catch {
|
|
1316
|
-
return source ? getNamedExportsViaRegex(source) : [];
|
|
1533
|
+
return source ? getNamedExportsViaRegex(source, entryPath) : [];
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
function resolveRelativeModule(filePath, specifier) {
|
|
1537
|
+
const dir = path.dirname(filePath);
|
|
1538
|
+
const exact = path.resolve(dir, specifier);
|
|
1539
|
+
if (existsSync(exact) && !statSync(exact).isDirectory()) return exact;
|
|
1540
|
+
const extensions = [
|
|
1541
|
+
".ts",
|
|
1542
|
+
".tsx",
|
|
1543
|
+
".js",
|
|
1544
|
+
".jsx",
|
|
1545
|
+
".mjs",
|
|
1546
|
+
".mts"
|
|
1547
|
+
];
|
|
1548
|
+
for (const ext of extensions) {
|
|
1549
|
+
const candidate = path.resolve(dir, specifier + ext);
|
|
1550
|
+
if (existsSync(candidate) && !statSync(candidate).isDirectory()) return candidate;
|
|
1551
|
+
}
|
|
1552
|
+
const resolved = path.resolve(dir, specifier);
|
|
1553
|
+
for (const ext of extensions) {
|
|
1554
|
+
const candidate = path.join(resolved, "index" + ext);
|
|
1555
|
+
if (existsSync(candidate)) return candidate;
|
|
1317
1556
|
}
|
|
1318
1557
|
}
|
|
1319
|
-
function getNamedExportsViaRegex(source) {
|
|
1558
|
+
function getNamedExportsViaRegex(source, filePath, visited) {
|
|
1320
1559
|
const names = /* @__PURE__ */ new Set();
|
|
1560
|
+
visited = visited || /* @__PURE__ */ new Set();
|
|
1561
|
+
if (filePath) visited.add(filePath);
|
|
1321
1562
|
const declRegex = new RegExp(`export\\s+(?:async\\s+)?(?:function(?:\\*\\s*|\\s+\\*?\\s*)|const\\s+|let\\s+|var\\s+|class\\s+)(${JS_IDENTIFIER_PATTERN})`, "gu");
|
|
1322
1563
|
let match;
|
|
1323
1564
|
while ((match = declRegex.exec(source)) !== null) {
|
|
@@ -1338,6 +1579,19 @@ function getNamedExportsViaRegex(source) {
|
|
|
1338
1579
|
if (isValidEsmExportName(name)) names.add(name);
|
|
1339
1580
|
}
|
|
1340
1581
|
}
|
|
1582
|
+
if (filePath) {
|
|
1583
|
+
const starExportRegex = /export\s+\*\s+from\s+['"]([^'"]+)['"]/g;
|
|
1584
|
+
while ((match = starExportRegex.exec(source)) !== null) {
|
|
1585
|
+
const specifier = match[1];
|
|
1586
|
+
if (!specifier.startsWith(".")) continue;
|
|
1587
|
+
const resolvedPath = resolveRelativeModule(filePath, specifier);
|
|
1588
|
+
if (!resolvedPath || visited.has(resolvedPath)) continue;
|
|
1589
|
+
try {
|
|
1590
|
+
const reExportNames = getNamedExportsViaRegex(readFileSync(resolvedPath, "utf-8"), resolvedPath, visited);
|
|
1591
|
+
for (const name of reExportNames) names.add(name);
|
|
1592
|
+
} catch {}
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1341
1595
|
return [...names];
|
|
1342
1596
|
}
|
|
1343
1597
|
function getPackageNamedExports(pkg) {
|
|
@@ -1499,6 +1753,10 @@ function generateLocalSharedImportMap() {
|
|
|
1499
1753
|
const isAstro = hasPackageDependency("astro");
|
|
1500
1754
|
const useDirectReactImport = isVinext || isAstro;
|
|
1501
1755
|
const options = getNormalizeModuleFederationOptions();
|
|
1756
|
+
const getPackagePath = (pkg, shareItem) => {
|
|
1757
|
+
if (useDirectReactImport && pkg === "react") return "react";
|
|
1758
|
+
return getConcreteSharedImportSource(pkg, shareItem) || getLocalProviderImportPath(pkg) || getSharedImportSource(pkg, shareItem);
|
|
1759
|
+
};
|
|
1502
1760
|
return `
|
|
1503
1761
|
import {loadShare} from "@module-federation/runtime";
|
|
1504
1762
|
const importMap = {
|
|
@@ -1506,8 +1764,7 @@ function generateLocalSharedImportMap() {
|
|
|
1506
1764
|
const shareItem = getNormalizeShareItem(pkg);
|
|
1507
1765
|
return `
|
|
1508
1766
|
${JSON.stringify(pkg)}: async () => {
|
|
1509
|
-
${shareItem?.shareConfig.import === false ? `throw new Error(\`[Module Federation] Shared module '\${${JSON.stringify(pkg)}}' must be provided by host\`);` :
|
|
1510
|
-
return pkg;` : `let pkg = await import(${JSON.stringify(getSharedImportSource(pkg, shareItem))});
|
|
1767
|
+
${shareItem?.shareConfig.import === false ? `throw new Error(\`[Module Federation] Shared module '\${${JSON.stringify(pkg)}}' must be provided by host\`);` : `let pkg = await import(${JSON.stringify(getPackagePath(pkg, shareItem))});
|
|
1511
1768
|
return pkg;`}
|
|
1512
1769
|
}
|
|
1513
1770
|
`;
|
|
@@ -1952,7 +2209,7 @@ const Manifest = () => {
|
|
|
1952
2209
|
root = config.root;
|
|
1953
2210
|
let base = config.base;
|
|
1954
2211
|
if (_command === "serve") base = (config.server.origin || "") + config.base;
|
|
1955
|
-
publicPath = resolvePublicPath(mfOptions, base, _originalConfigBase);
|
|
2212
|
+
publicPath = mfOptions.publicPath === "auto" ? "auto" : resolvePublicPath(mfOptions, base, _originalConfigBase);
|
|
1956
2213
|
},
|
|
1957
2214
|
async generateBundle(options, bundle) {
|
|
1958
2215
|
if (!mfManifestName) return;
|
|
@@ -2862,6 +3119,16 @@ var aliasToArrayPlugin_default = {
|
|
|
2862
3119
|
}
|
|
2863
3120
|
};
|
|
2864
3121
|
//#endregion
|
|
3122
|
+
//#region src/utils/isTestEnv.ts
|
|
3123
|
+
/**
|
|
3124
|
+
* Detects whether the current process is running in a test environment
|
|
3125
|
+
* Set `MFE_VITE_NO_TEST_ENV_CHECK=true` to load federation plugins during tests.
|
|
3126
|
+
*/
|
|
3127
|
+
function isTestEnv() {
|
|
3128
|
+
if (process.env.MFE_VITE_NO_TEST_ENV_CHECK === "true") return false;
|
|
3129
|
+
return process.env.NODE_ENV === "test" || process.env.VITEST != null || process.env.JEST_WORKER_ID != null;
|
|
3130
|
+
}
|
|
3131
|
+
//#endregion
|
|
2865
3132
|
//#region src/utils/controlChunkSanitizer.ts
|
|
2866
3133
|
const FEDERATION_CONTROL_CHUNK_HINTS = [
|
|
2867
3134
|
"hostInit",
|
|
@@ -3007,6 +3274,7 @@ function createEarlyVirtualModulesPlugin(options) {
|
|
|
3007
3274
|
};
|
|
3008
3275
|
}
|
|
3009
3276
|
function federation(mfUserOptions) {
|
|
3277
|
+
if (isTestEnv()) return [];
|
|
3010
3278
|
const options = normalizeModuleFederationOptions(mfUserOptions);
|
|
3011
3279
|
const isVinext = hasPackageDependency("vinext");
|
|
3012
3280
|
const { name, remotes, shared, filename, hostInitInjectLocation } = options;
|
|
@@ -3055,6 +3323,7 @@ function federation(mfUserOptions) {
|
|
|
3055
3323
|
checkAliasConflicts({ shared }),
|
|
3056
3324
|
normalizeOptimizeDeps_default,
|
|
3057
3325
|
...pluginDts(options),
|
|
3326
|
+
pluginDevRemoteHmr(options),
|
|
3058
3327
|
...addEntry({
|
|
3059
3328
|
entryName: "remoteEntry",
|
|
3060
3329
|
entryPath: remoteEntryId,
|
|
@@ -3401,12 +3670,12 @@ function federation(mfUserOptions) {
|
|
|
3401
3670
|
const prefixToRoot = chunkDir === "." ? "" : `${path.relative(chunkDir, ".")}/`;
|
|
3402
3671
|
const replacementExpr = prefixToRoot ? `${escapeUnsafeJsSourceChars(JSON.stringify(prefixToRoot))}+$1` : "$1";
|
|
3403
3672
|
const replacement = `=function($1){return new URL(${replacementExpr},import.meta.url).href}`;
|
|
3404
|
-
const replaced = chunk.code.replace(/=\(?(\w+)(?:,\w+)?\)?\s*=>\s*["'
|
|
3673
|
+
const replaced = chunk.code.replace(/=\(?(\w+)(?:,\w+)?\)?\s*=>\s*["'][./][^"']*["']\s*\+\s*\1/, replacement);
|
|
3405
3674
|
if (replaced !== chunk.code) {
|
|
3406
3675
|
chunk.code = replaced;
|
|
3407
3676
|
continue;
|
|
3408
3677
|
}
|
|
3409
|
-
chunk.code = chunk.code.replace(/=function\((\w+)(?:,\w+)?\)\{return\s*["'
|
|
3678
|
+
chunk.code = chunk.code.replace(/=function\((\w+)(?:,\w+)?\)\{return\s*["'][./][^"']*["']\s*\+\s*\1\s*\}/, replacement);
|
|
3410
3679
|
chunk.code = chunk.code.replace(/=function\((\w+)(?:,\w+)?\)\{return new URL\("\.\.\/"\+\1,import\.meta\.url\)\.href\}/, replacement);
|
|
3411
3680
|
chunk.code = chunk.code.replace(/new URL\("\.\.\/"\+(\w+),import\.meta\.url\)\.href/g, `new URL(${replacementExpr},import.meta.url).href`);
|
|
3412
3681
|
}
|