@module-federation/vite 1.13.6 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,6 +19,7 @@ Examples live in [`gioboa/module-federation-vite-examples`](https://github.com/g
19
19
  | [Angular](https://github.com/gioboa/module-federation-vite-examples/tree/main/angular) | `angular-host` | `angular-remote` | Angular |
20
20
  | [Lit](https://github.com/gioboa/module-federation-vite-examples/tree/main/lit) | `lit-host` | `lit-remote` | Lit |
21
21
  | [Nuxt](https://github.com/gioboa/module-federation-vite-examples/tree/main/nuxt) | `nuxt-host` | `nuxt-remote` | Nuxt 4 |
22
+ | [Preact](https://github.com/gioboa/module-federation-vite-examples/tree/main/preact) | `preact-host` | `preact-remote` | Preact 10 |
22
23
  | [React](https://github.com/gioboa/module-federation-vite-examples/tree/main/react) | `react-host` | `react-remote` | React 19 |
23
24
  | [Solid](https://github.com/gioboa/module-federation-vite-examples/tree/main/solid) | `solid-host` | `solid-remote` | Solid |
24
25
  | [Svelte](https://github.com/gioboa/module-federation-vite-examples/tree/main/svelte) | `svelte-host` | `svelte-remote` | Svelte 5 |
package/lib/index.cjs CHANGED
@@ -28,6 +28,7 @@ fs = __toESM(fs);
28
28
  let module$1 = require("module");
29
29
  let pathe = require("pathe");
30
30
  pathe = __toESM(pathe);
31
+ let vite = require("vite");
31
32
  let magic_string = require("magic-string");
32
33
  magic_string = __toESM(magic_string);
33
34
  let _rollup_pluginutils = require("@rollup/pluginutils");
@@ -227,17 +228,21 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
227
228
  next();
228
229
  });
229
230
  },
230
- transformIndexHtml(c) {
231
- if (!injectHtml()) return;
232
- clientInjected = true;
233
- const html = rewriteEntryScripts(c, (originalSrc) => {
234
- const query = new URLSearchParams({
235
- init: sanitizeDevEntryPath(devEntryPath),
236
- entry: originalSrc
237
- }).toString();
238
- return `${viteConfig.base}@id/${DEV_HTML_PROXY_PREFIX}${query}`;
239
- });
240
- return html === c ? injectEntryScript(c, devEntryPath) : html;
231
+ transformIndexHtml: {
232
+ order: "pre",
233
+ handler(c) {
234
+ if (!injectHtml()) return;
235
+ clientInjected = true;
236
+ const base = viteConfig.base.replace(/\/$/, "");
237
+ const stripBase = (p) => base && p.startsWith(base) ? p.slice(base.length) : p;
238
+ const html = rewriteEntryScripts(c, (originalSrc) => {
239
+ return `/@id/${DEV_HTML_PROXY_PREFIX}${new URLSearchParams({
240
+ init: sanitizeDevEntryPath(stripBase(devEntryPath)),
241
+ entry: originalSrc
242
+ }).toString()}`;
243
+ });
244
+ return html === c ? injectEntryScript(c, stripBase(devEntryPath)) : html;
245
+ }
241
246
  },
242
247
  resolveId(id) {
243
248
  if (id.startsWith(DEV_HTML_PROXY_PREFIX)) return id;
@@ -456,6 +461,191 @@ function PluginDevProxyModuleTopLevelAwait() {
456
461
  };
457
462
  }
458
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
+ server.httpServer?.once("close", teardown);
644
+ }
645
+ }
646
+ };
647
+ }
648
+ //#endregion
459
649
  //#region src/plugins/pluginDts.ts
460
650
  const DEFAULT_DEV_OPTIONS = {
461
651
  disableLiveReload: true,
@@ -1236,6 +1426,10 @@ function escapeGeneratedStringLiteral(value) {
1236
1426
  function isValidJsIdentifier(name) {
1237
1427
  return /^[$_\p{ID_Start}][$_\u200C\u200D\p{ID_Continue}]*$/u.test(name);
1238
1428
  }
1429
+ function isValidEsmExportName(name) {
1430
+ return !!name && name !== "default" && name !== "__esModule" && isValidJsIdentifier(name);
1431
+ }
1432
+ const JS_IDENTIFIER_PATTERN = `[\$_\\p{ID_Start}][\$_\\u200C\\u200D\\p{ID_Continue}]*`;
1239
1433
  const localRequire = (0, module$1.createRequire)(require("url").pathToFileURL(__filename).href);
1240
1434
  function resolvePackageEntryFromProjectRoot(pkg) {
1241
1435
  try {
@@ -1313,21 +1507,89 @@ function getPackageEsmEntryPath(pkg) {
1313
1507
  }
1314
1508
  }
1315
1509
  function getEsmNamedExports(pkg) {
1510
+ let source = "";
1511
+ let entryPath;
1316
1512
  try {
1317
- const entryPath = getPackageEsmEntryPath(pkg);
1513
+ entryPath = getPackageEsmEntryPath(pkg);
1318
1514
  if (!entryPath) return [];
1319
1515
  const { initSync, parse } = localRequire("es-module-lexer");
1320
1516
  initSync();
1321
- const [, exports] = parse((0, fs.readFileSync)(entryPath, "utf-8"), entryPath);
1322
- return exports.map((item) => item.n).filter((name) => !!name && name !== "default" && name !== "__esModule" && isValidJsIdentifier(name));
1517
+ source = (0, fs.readFileSync)(entryPath, "utf-8");
1518
+ const [, exports] = parse(source, entryPath);
1519
+ const names = exports.map((item) => item.n).filter((name) => isValidEsmExportName(name));
1520
+ const regexNames = getNamedExportsViaRegex(source, entryPath);
1521
+ const filteredNames = names.filter((name) => name !== "type" || regexNames.includes(name));
1522
+ if (filteredNames.length > 0) return [...new Set([...filteredNames, ...regexNames])];
1523
+ return regexNames;
1323
1524
  } catch {
1324
- return [];
1525
+ return source ? getNamedExportsViaRegex(source, entryPath) : [];
1325
1526
  }
1326
1527
  }
1528
+ function resolveRelativeModule(filePath, specifier) {
1529
+ const dir = pathe.default.dirname(filePath);
1530
+ const exact = pathe.default.resolve(dir, specifier);
1531
+ if ((0, fs.existsSync)(exact) && !(0, fs.statSync)(exact).isDirectory()) return exact;
1532
+ const extensions = [
1533
+ ".ts",
1534
+ ".tsx",
1535
+ ".js",
1536
+ ".jsx",
1537
+ ".mjs",
1538
+ ".mts"
1539
+ ];
1540
+ for (const ext of extensions) {
1541
+ const candidate = pathe.default.resolve(dir, specifier + ext);
1542
+ if ((0, fs.existsSync)(candidate) && !(0, fs.statSync)(candidate).isDirectory()) return candidate;
1543
+ }
1544
+ const resolved = pathe.default.resolve(dir, specifier);
1545
+ for (const ext of extensions) {
1546
+ const candidate = pathe.default.join(resolved, "index" + ext);
1547
+ if ((0, fs.existsSync)(candidate)) return candidate;
1548
+ }
1549
+ }
1550
+ function getNamedExportsViaRegex(source, filePath, visited) {
1551
+ const names = /* @__PURE__ */ new Set();
1552
+ visited = visited || /* @__PURE__ */ new Set();
1553
+ if (filePath) visited.add(filePath);
1554
+ const declRegex = new RegExp(`export\\s+(?:async\\s+)?(?:function(?:\\*\\s*|\\s+\\*?\\s*)|const\\s+|let\\s+|var\\s+|class\\s+)(${JS_IDENTIFIER_PATTERN})`, "gu");
1555
+ let match;
1556
+ while ((match = declRegex.exec(source)) !== null) {
1557
+ const name = match[1];
1558
+ if (isValidEsmExportName(name)) names.add(name);
1559
+ }
1560
+ const listRegex = /export\s*\{([^}]+)\}/g;
1561
+ const typeOnlySpecifierRegex = new RegExp(`^type\\s+${JS_IDENTIFIER_PATTERN}(?:\\s+as\\s+${JS_IDENTIFIER_PATTERN})?$`, "u");
1562
+ const exportSpecifierRegex = new RegExp(`(?:\\S+\\s+as\\s+)?(${JS_IDENTIFIER_PATTERN})$`, "u");
1563
+ while ((match = listRegex.exec(source)) !== null) {
1564
+ const specifiers = match[1].split(",");
1565
+ for (const specifier of specifiers) {
1566
+ const trimmed = specifier.trim();
1567
+ if (typeOnlySpecifierRegex.test(trimmed)) continue;
1568
+ const asMatch = trimmed.match(exportSpecifierRegex);
1569
+ if (!asMatch) continue;
1570
+ const name = asMatch[1];
1571
+ if (isValidEsmExportName(name)) names.add(name);
1572
+ }
1573
+ }
1574
+ if (filePath) {
1575
+ const starExportRegex = /export\s+\*\s+from\s+['"]([^'"]+)['"]/g;
1576
+ while ((match = starExportRegex.exec(source)) !== null) {
1577
+ const specifier = match[1];
1578
+ if (!specifier.startsWith(".")) continue;
1579
+ const resolvedPath = resolveRelativeModule(filePath, specifier);
1580
+ if (!resolvedPath || visited.has(resolvedPath)) continue;
1581
+ try {
1582
+ const reExportNames = getNamedExportsViaRegex((0, fs.readFileSync)(resolvedPath, "utf-8"), resolvedPath, visited);
1583
+ for (const name of reExportNames) names.add(name);
1584
+ } catch {}
1585
+ }
1586
+ }
1587
+ return [...names];
1588
+ }
1327
1589
  function getPackageNamedExports(pkg) {
1328
1590
  try {
1329
1591
  const mod = (0, module$1.createRequire)(new URL(`file://${pathe.default.join(getPackageDetectionCwd(), "package.json")}`))(pkg);
1330
- return Object.keys(mod).filter((k) => k !== "default" && k !== "__esModule" && isValidJsIdentifier(k));
1592
+ return Object.keys(mod).filter((k) => isValidEsmExportName(k));
1331
1593
  } catch {
1332
1594
  return getEsmNamedExports(pkg);
1333
1595
  }
@@ -1335,11 +1597,14 @@ function getPackageNamedExports(pkg) {
1335
1597
  function getLocalProviderImportPath(pkg) {
1336
1598
  try {
1337
1599
  const resolved = (0, module$1.createRequire)(new URL(`file://${pathe.default.join(getPackageDetectionCwd(), "package.json")}`)).resolve(pkg);
1338
- return resolved.includes("/node_modules/") || resolved.includes("\\node_modules\\") ? void 0 : resolved;
1600
+ return isWorkspaceFilePath(resolved) ? resolved : void 0;
1339
1601
  } catch {
1340
1602
  return;
1341
1603
  }
1342
1604
  }
1605
+ function isWorkspaceFilePath(resolved) {
1606
+ return !!resolved && !resolved.includes("/node_modules/") && !resolved.includes("\\node_modules\\");
1607
+ }
1343
1608
  function tryResolveImportFromPackageRoot(pkg, root) {
1344
1609
  try {
1345
1610
  return (0, module$1.createRequire)(new URL(`file://${pathe.default.join(root, "package.json")}`)).resolve(pkg);
@@ -1422,7 +1687,9 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1422
1687
  const concreteSharedImportSource = getConcreteSharedImportSource(pkg, shareItem);
1423
1688
  const sharedImportSource = concreteSharedImportSource || getPreBuildLibImportId(pkg);
1424
1689
  const devImportSource = concreteSharedImportSource || pkg;
1425
- const providerImportId = getLocalProviderImportPath(pkg) || concreteSharedImportSource || sharedImportSource;
1690
+ const localProviderPath = getLocalProviderImportPath(pkg);
1691
+ const isWorkspacePackage = isWorkspaceFilePath(localProviderPath) || isWorkspaceFilePath(concreteSharedImportSource);
1692
+ const providerImportId = localProviderPath || concreteSharedImportSource || sharedImportSource;
1426
1693
  const namedExports = getPackageNamedExports(pkg);
1427
1694
  let exportLine;
1428
1695
  if (namedExports.length > 0) {
@@ -1430,9 +1697,11 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1430
1697
  const namedExportLine = `export { ${namedExports.map((name, i) => `__mf_${i} as ${name}`).join(", ")} };`;
1431
1698
  exportLine = useESM ? `export default exportModule.default ?? exportModule;\n ${destructure}\n ${namedExportLine}` : `module.exports = exportModule;\n ${destructure}\n Object.assign(module.exports, { ${namedExports.map((name, i) => `"${name}": __mf_${i}`).join(", ")} });`;
1432
1699
  } else exportLine = useESM ? `export default exportModule.default ?? exportModule\n export * from ${escapeGeneratedStringLiteral(sharedImportSource)}` : "module.exports = exportModule";
1700
+ const prebuildImportLine = isWorkspacePackage && command !== "build" ? "" : `import ${escapeGeneratedStringLiteral(sharedImportSource)};`;
1701
+ const devDynamicImportLine = isWorkspacePackage ? "" : command !== "build" ? `;() => import(${escapeGeneratedStringLiteral(devImportSource)}).catch(() => {});` : "";
1433
1702
  loadShareCacheMap[pkg].writeSync(`
1434
- import ${escapeGeneratedStringLiteral(sharedImportSource)};
1435
- ${command !== "build" ? `;() => import(${escapeGeneratedStringLiteral(devImportSource)}).catch(() => {});` : ""}
1703
+ ${prebuildImportLine}
1704
+ ${devDynamicImportLine}
1436
1705
  ${importLine}
1437
1706
  ${useSsrProviderFallback ? `const providerModulePromise = typeof window === "undefined"
1438
1707
  ? import(${escapeGeneratedStringLiteral(providerImportId)})
@@ -1476,6 +1745,10 @@ function generateLocalSharedImportMap() {
1476
1745
  const isAstro = hasPackageDependency("astro");
1477
1746
  const useDirectReactImport = isVinext || isAstro;
1478
1747
  const options = getNormalizeModuleFederationOptions();
1748
+ const getPackagePath = (pkg, shareItem) => {
1749
+ if (useDirectReactImport && pkg === "react") return "react";
1750
+ return getConcreteSharedImportSource(pkg, shareItem) || getLocalProviderImportPath(pkg) || getSharedImportSource(pkg, shareItem);
1751
+ };
1479
1752
  return `
1480
1753
  import {loadShare} from "@module-federation/runtime";
1481
1754
  const importMap = {
@@ -1483,8 +1756,7 @@ function generateLocalSharedImportMap() {
1483
1756
  const shareItem = getNormalizeShareItem(pkg);
1484
1757
  return `
1485
1758
  ${JSON.stringify(pkg)}: async () => {
1486
- ${shareItem?.shareConfig.import === false ? `throw new Error(\`[Module Federation] Shared module '\${${JSON.stringify(pkg)}}' must be provided by host\`);` : useDirectReactImport && pkg === "react" ? `let pkg = await import("react");
1487
- return pkg;` : `let pkg = await import(${JSON.stringify(getSharedImportSource(pkg, shareItem))});
1759
+ ${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))});
1488
1760
  return pkg;`}
1489
1761
  }
1490
1762
  `;
@@ -1929,7 +2201,7 @@ const Manifest = () => {
1929
2201
  root = config.root;
1930
2202
  let base = config.base;
1931
2203
  if (_command === "serve") base = (config.server.origin || "") + config.base;
1932
- publicPath = resolvePublicPath(mfOptions, base, _originalConfigBase);
2204
+ publicPath = mfOptions.publicPath === "auto" ? "auto" : resolvePublicPath(mfOptions, base, _originalConfigBase);
1933
2205
  },
1934
2206
  async generateBundle(options, bundle) {
1935
2207
  if (!mfManifestName) return;
@@ -2991,6 +3263,7 @@ function federation(mfUserOptions) {
2991
3263
  const remoteEntryId = getRemoteEntryId(options);
2992
3264
  const virtualExposesId = getVirtualExposesId(options);
2993
3265
  let command;
3266
+ let depsDir = "/node_modules/.vite/deps/";
2994
3267
  return [
2995
3268
  createEarlyVirtualModulesPlugin(options),
2996
3269
  ...isVinext ? [{
@@ -3018,6 +3291,11 @@ function federation(mfUserOptions) {
3018
3291
  },
3019
3292
  configResolved(config) {
3020
3293
  VirtualModule.setRoot(config.root);
3294
+ const cacheDir = config.cacheDir;
3295
+ if (cacheDir) {
3296
+ const resolved = pathe.default.isAbsolute(cacheDir) ? cacheDir : pathe.default.resolve(config.root, cacheDir);
3297
+ depsDir = (0, vite.normalizePath)(pathe.default.join(resolved, "deps")) + "/";
3298
+ } else depsDir = (0, vite.normalizePath)(pathe.default.join(config.root, "node_modules", ".vite", "deps")) + "/";
3021
3299
  VirtualModule.ensureVirtualPackageExists();
3022
3300
  initVirtualModules(command, remoteEntryId);
3023
3301
  }
@@ -3026,6 +3304,7 @@ function federation(mfUserOptions) {
3026
3304
  checkAliasConflicts({ shared }),
3027
3305
  normalizeOptimizeDeps_default,
3028
3306
  ...pluginDts(options),
3307
+ pluginDevRemoteHmr(options),
3029
3308
  ...addEntry({
3030
3309
  entryName: "remoteEntry",
3031
3310
  entryPath: remoteEntryId,
@@ -3282,7 +3561,7 @@ function federation(mfUserOptions) {
3282
3561
  apply: "serve",
3283
3562
  enforce: "post",
3284
3563
  transform(code, id) {
3285
- if (!id.includes(".vite/deps/")) return;
3564
+ if (!(0, vite.normalizePath)(id).split("?")[0].startsWith(depsDir)) return;
3286
3565
  const initPattern = /\b(init_\w+__loadShare__\w+)\b/g;
3287
3566
  const initFns = /* @__PURE__ */ new Set();
3288
3567
  let match;
package/lib/index.d.cts CHANGED
@@ -84,6 +84,7 @@ interface PluginDevOptions {
84
84
  disableLiveReload?: boolean;
85
85
  disableHotTypesReload?: boolean;
86
86
  disableDynamicRemoteTypeHints?: boolean;
87
+ remoteHmr?: boolean;
87
88
  }
88
89
  interface RemoteTypeUrl {
89
90
  alias?: string;
package/lib/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { sharePlugin } from "@module-federation/sdk";
2
1
  import { Plugin } from "vite";
2
+ import { sharePlugin } from "@module-federation/sdk";
3
3
  import { ShareStrategy } from "@module-federation/runtime/types";
4
4
 
5
5
  //#region src/utils/normalizeModuleFederationOptions.d.ts
@@ -84,6 +84,7 @@ interface PluginDevOptions {
84
84
  disableLiveReload?: boolean;
85
85
  disableHotTypesReload?: boolean;
86
86
  disableDynamicRemoteTypeHints?: boolean;
87
+ remoteHmr?: boolean;
87
88
  }
88
89
  interface RemoteTypeUrl {
89
90
  alias?: string;
package/lib/index.mjs CHANGED
@@ -1,10 +1,11 @@
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";
8
+ import { normalizePath } from "vite";
8
9
  import MagicString from "magic-string";
9
10
  import { createFilter } from "@rollup/pluginutils";
10
11
  import { normalizeOptions } from "@module-federation/sdk";
@@ -205,17 +206,21 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
205
206
  next();
206
207
  });
207
208
  },
208
- transformIndexHtml(c) {
209
- if (!injectHtml()) return;
210
- clientInjected = true;
211
- const html = rewriteEntryScripts(c, (originalSrc) => {
212
- const query = new URLSearchParams({
213
- init: sanitizeDevEntryPath(devEntryPath),
214
- entry: originalSrc
215
- }).toString();
216
- return `${viteConfig.base}@id/${DEV_HTML_PROXY_PREFIX}${query}`;
217
- });
218
- return html === c ? injectEntryScript(c, devEntryPath) : html;
209
+ transformIndexHtml: {
210
+ order: "pre",
211
+ handler(c) {
212
+ if (!injectHtml()) return;
213
+ clientInjected = true;
214
+ const base = viteConfig.base.replace(/\/$/, "");
215
+ const stripBase = (p) => base && p.startsWith(base) ? p.slice(base.length) : p;
216
+ const html = rewriteEntryScripts(c, (originalSrc) => {
217
+ return `/@id/${DEV_HTML_PROXY_PREFIX}${new URLSearchParams({
218
+ init: sanitizeDevEntryPath(stripBase(devEntryPath)),
219
+ entry: originalSrc
220
+ }).toString()}`;
221
+ });
222
+ return html === c ? injectEntryScript(c, stripBase(devEntryPath)) : html;
223
+ }
219
224
  },
220
225
  resolveId(id) {
221
226
  if (id.startsWith(DEV_HTML_PROXY_PREFIX)) return id;
@@ -434,6 +439,191 @@ function PluginDevProxyModuleTopLevelAwait() {
434
439
  };
435
440
  }
436
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
+ server.httpServer?.once("close", teardown);
622
+ }
623
+ }
624
+ };
625
+ }
626
+ //#endregion
437
627
  //#region src/plugins/pluginDts.ts
438
628
  const DEFAULT_DEV_OPTIONS = {
439
629
  disableLiveReload: true,
@@ -1213,6 +1403,10 @@ function escapeGeneratedStringLiteral(value) {
1213
1403
  function isValidJsIdentifier(name) {
1214
1404
  return /^[$_\p{ID_Start}][$_\u200C\u200D\p{ID_Continue}]*$/u.test(name);
1215
1405
  }
1406
+ function isValidEsmExportName(name) {
1407
+ return !!name && name !== "default" && name !== "__esModule" && isValidJsIdentifier(name);
1408
+ }
1409
+ const JS_IDENTIFIER_PATTERN = `[\$_\\p{ID_Start}][\$_\\u200C\\u200D\\p{ID_Continue}]*`;
1216
1410
  const localRequire = createRequire$1(import.meta.url);
1217
1411
  function resolvePackageEntryFromProjectRoot(pkg) {
1218
1412
  try {
@@ -1290,21 +1484,89 @@ function getPackageEsmEntryPath(pkg) {
1290
1484
  }
1291
1485
  }
1292
1486
  function getEsmNamedExports(pkg) {
1487
+ let source = "";
1488
+ let entryPath;
1293
1489
  try {
1294
- const entryPath = getPackageEsmEntryPath(pkg);
1490
+ entryPath = getPackageEsmEntryPath(pkg);
1295
1491
  if (!entryPath) return [];
1296
1492
  const { initSync, parse } = localRequire("es-module-lexer");
1297
1493
  initSync();
1298
- const [, exports] = parse(readFileSync(entryPath, "utf-8"), entryPath);
1299
- return exports.map((item) => item.n).filter((name) => !!name && name !== "default" && name !== "__esModule" && isValidJsIdentifier(name));
1494
+ source = readFileSync(entryPath, "utf-8");
1495
+ const [, exports] = parse(source, entryPath);
1496
+ const names = exports.map((item) => item.n).filter((name) => isValidEsmExportName(name));
1497
+ const regexNames = getNamedExportsViaRegex(source, entryPath);
1498
+ const filteredNames = names.filter((name) => name !== "type" || regexNames.includes(name));
1499
+ if (filteredNames.length > 0) return [...new Set([...filteredNames, ...regexNames])];
1500
+ return regexNames;
1300
1501
  } catch {
1301
- return [];
1502
+ return source ? getNamedExportsViaRegex(source, entryPath) : [];
1302
1503
  }
1303
1504
  }
1505
+ function resolveRelativeModule(filePath, specifier) {
1506
+ const dir = path.dirname(filePath);
1507
+ const exact = path.resolve(dir, specifier);
1508
+ if (existsSync(exact) && !statSync(exact).isDirectory()) return exact;
1509
+ const extensions = [
1510
+ ".ts",
1511
+ ".tsx",
1512
+ ".js",
1513
+ ".jsx",
1514
+ ".mjs",
1515
+ ".mts"
1516
+ ];
1517
+ for (const ext of extensions) {
1518
+ const candidate = path.resolve(dir, specifier + ext);
1519
+ if (existsSync(candidate) && !statSync(candidate).isDirectory()) return candidate;
1520
+ }
1521
+ const resolved = path.resolve(dir, specifier);
1522
+ for (const ext of extensions) {
1523
+ const candidate = path.join(resolved, "index" + ext);
1524
+ if (existsSync(candidate)) return candidate;
1525
+ }
1526
+ }
1527
+ function getNamedExportsViaRegex(source, filePath, visited) {
1528
+ const names = /* @__PURE__ */ new Set();
1529
+ visited = visited || /* @__PURE__ */ new Set();
1530
+ if (filePath) visited.add(filePath);
1531
+ const declRegex = new RegExp(`export\\s+(?:async\\s+)?(?:function(?:\\*\\s*|\\s+\\*?\\s*)|const\\s+|let\\s+|var\\s+|class\\s+)(${JS_IDENTIFIER_PATTERN})`, "gu");
1532
+ let match;
1533
+ while ((match = declRegex.exec(source)) !== null) {
1534
+ const name = match[1];
1535
+ if (isValidEsmExportName(name)) names.add(name);
1536
+ }
1537
+ const listRegex = /export\s*\{([^}]+)\}/g;
1538
+ const typeOnlySpecifierRegex = new RegExp(`^type\\s+${JS_IDENTIFIER_PATTERN}(?:\\s+as\\s+${JS_IDENTIFIER_PATTERN})?$`, "u");
1539
+ const exportSpecifierRegex = new RegExp(`(?:\\S+\\s+as\\s+)?(${JS_IDENTIFIER_PATTERN})$`, "u");
1540
+ while ((match = listRegex.exec(source)) !== null) {
1541
+ const specifiers = match[1].split(",");
1542
+ for (const specifier of specifiers) {
1543
+ const trimmed = specifier.trim();
1544
+ if (typeOnlySpecifierRegex.test(trimmed)) continue;
1545
+ const asMatch = trimmed.match(exportSpecifierRegex);
1546
+ if (!asMatch) continue;
1547
+ const name = asMatch[1];
1548
+ if (isValidEsmExportName(name)) names.add(name);
1549
+ }
1550
+ }
1551
+ if (filePath) {
1552
+ const starExportRegex = /export\s+\*\s+from\s+['"]([^'"]+)['"]/g;
1553
+ while ((match = starExportRegex.exec(source)) !== null) {
1554
+ const specifier = match[1];
1555
+ if (!specifier.startsWith(".")) continue;
1556
+ const resolvedPath = resolveRelativeModule(filePath, specifier);
1557
+ if (!resolvedPath || visited.has(resolvedPath)) continue;
1558
+ try {
1559
+ const reExportNames = getNamedExportsViaRegex(readFileSync(resolvedPath, "utf-8"), resolvedPath, visited);
1560
+ for (const name of reExportNames) names.add(name);
1561
+ } catch {}
1562
+ }
1563
+ }
1564
+ return [...names];
1565
+ }
1304
1566
  function getPackageNamedExports(pkg) {
1305
1567
  try {
1306
1568
  const mod = createRequire$1(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`))(pkg);
1307
- return Object.keys(mod).filter((k) => k !== "default" && k !== "__esModule" && isValidJsIdentifier(k));
1569
+ return Object.keys(mod).filter((k) => isValidEsmExportName(k));
1308
1570
  } catch {
1309
1571
  return getEsmNamedExports(pkg);
1310
1572
  }
@@ -1312,11 +1574,14 @@ function getPackageNamedExports(pkg) {
1312
1574
  function getLocalProviderImportPath(pkg) {
1313
1575
  try {
1314
1576
  const resolved = createRequire$1(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`)).resolve(pkg);
1315
- return resolved.includes("/node_modules/") || resolved.includes("\\node_modules\\") ? void 0 : resolved;
1577
+ return isWorkspaceFilePath(resolved) ? resolved : void 0;
1316
1578
  } catch {
1317
1579
  return;
1318
1580
  }
1319
1581
  }
1582
+ function isWorkspaceFilePath(resolved) {
1583
+ return !!resolved && !resolved.includes("/node_modules/") && !resolved.includes("\\node_modules\\");
1584
+ }
1320
1585
  function tryResolveImportFromPackageRoot(pkg, root) {
1321
1586
  try {
1322
1587
  return createRequire$1(new URL(`file://${path.join(root, "package.json")}`)).resolve(pkg);
@@ -1399,7 +1664,9 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1399
1664
  const concreteSharedImportSource = getConcreteSharedImportSource(pkg, shareItem);
1400
1665
  const sharedImportSource = concreteSharedImportSource || getPreBuildLibImportId(pkg);
1401
1666
  const devImportSource = concreteSharedImportSource || pkg;
1402
- const providerImportId = getLocalProviderImportPath(pkg) || concreteSharedImportSource || sharedImportSource;
1667
+ const localProviderPath = getLocalProviderImportPath(pkg);
1668
+ const isWorkspacePackage = isWorkspaceFilePath(localProviderPath) || isWorkspaceFilePath(concreteSharedImportSource);
1669
+ const providerImportId = localProviderPath || concreteSharedImportSource || sharedImportSource;
1403
1670
  const namedExports = getPackageNamedExports(pkg);
1404
1671
  let exportLine;
1405
1672
  if (namedExports.length > 0) {
@@ -1407,9 +1674,11 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1407
1674
  const namedExportLine = `export { ${namedExports.map((name, i) => `__mf_${i} as ${name}`).join(", ")} };`;
1408
1675
  exportLine = useESM ? `export default exportModule.default ?? exportModule;\n ${destructure}\n ${namedExportLine}` : `module.exports = exportModule;\n ${destructure}\n Object.assign(module.exports, { ${namedExports.map((name, i) => `"${name}": __mf_${i}`).join(", ")} });`;
1409
1676
  } else exportLine = useESM ? `export default exportModule.default ?? exportModule\n export * from ${escapeGeneratedStringLiteral(sharedImportSource)}` : "module.exports = exportModule";
1677
+ const prebuildImportLine = isWorkspacePackage && command !== "build" ? "" : `import ${escapeGeneratedStringLiteral(sharedImportSource)};`;
1678
+ const devDynamicImportLine = isWorkspacePackage ? "" : command !== "build" ? `;() => import(${escapeGeneratedStringLiteral(devImportSource)}).catch(() => {});` : "";
1410
1679
  loadShareCacheMap[pkg].writeSync(`
1411
- import ${escapeGeneratedStringLiteral(sharedImportSource)};
1412
- ${command !== "build" ? `;() => import(${escapeGeneratedStringLiteral(devImportSource)}).catch(() => {});` : ""}
1680
+ ${prebuildImportLine}
1681
+ ${devDynamicImportLine}
1413
1682
  ${importLine}
1414
1683
  ${useSsrProviderFallback ? `const providerModulePromise = typeof window === "undefined"
1415
1684
  ? import(${escapeGeneratedStringLiteral(providerImportId)})
@@ -1453,6 +1722,10 @@ function generateLocalSharedImportMap() {
1453
1722
  const isAstro = hasPackageDependency("astro");
1454
1723
  const useDirectReactImport = isVinext || isAstro;
1455
1724
  const options = getNormalizeModuleFederationOptions();
1725
+ const getPackagePath = (pkg, shareItem) => {
1726
+ if (useDirectReactImport && pkg === "react") return "react";
1727
+ return getConcreteSharedImportSource(pkg, shareItem) || getLocalProviderImportPath(pkg) || getSharedImportSource(pkg, shareItem);
1728
+ };
1456
1729
  return `
1457
1730
  import {loadShare} from "@module-federation/runtime";
1458
1731
  const importMap = {
@@ -1460,8 +1733,7 @@ function generateLocalSharedImportMap() {
1460
1733
  const shareItem = getNormalizeShareItem(pkg);
1461
1734
  return `
1462
1735
  ${JSON.stringify(pkg)}: async () => {
1463
- ${shareItem?.shareConfig.import === false ? `throw new Error(\`[Module Federation] Shared module '\${${JSON.stringify(pkg)}}' must be provided by host\`);` : useDirectReactImport && pkg === "react" ? `let pkg = await import("react");
1464
- return pkg;` : `let pkg = await import(${JSON.stringify(getSharedImportSource(pkg, shareItem))});
1736
+ ${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))});
1465
1737
  return pkg;`}
1466
1738
  }
1467
1739
  `;
@@ -1906,7 +2178,7 @@ const Manifest = () => {
1906
2178
  root = config.root;
1907
2179
  let base = config.base;
1908
2180
  if (_command === "serve") base = (config.server.origin || "") + config.base;
1909
- publicPath = resolvePublicPath(mfOptions, base, _originalConfigBase);
2181
+ publicPath = mfOptions.publicPath === "auto" ? "auto" : resolvePublicPath(mfOptions, base, _originalConfigBase);
1910
2182
  },
1911
2183
  async generateBundle(options, bundle) {
1912
2184
  if (!mfManifestName) return;
@@ -2968,6 +3240,7 @@ function federation(mfUserOptions) {
2968
3240
  const remoteEntryId = getRemoteEntryId(options);
2969
3241
  const virtualExposesId = getVirtualExposesId(options);
2970
3242
  let command;
3243
+ let depsDir = "/node_modules/.vite/deps/";
2971
3244
  return [
2972
3245
  createEarlyVirtualModulesPlugin(options),
2973
3246
  ...isVinext ? [{
@@ -2995,6 +3268,11 @@ function federation(mfUserOptions) {
2995
3268
  },
2996
3269
  configResolved(config) {
2997
3270
  VirtualModule.setRoot(config.root);
3271
+ const cacheDir = config.cacheDir;
3272
+ if (cacheDir) {
3273
+ const resolved = path.isAbsolute(cacheDir) ? cacheDir : path.resolve(config.root, cacheDir);
3274
+ depsDir = normalizePath(path.join(resolved, "deps")) + "/";
3275
+ } else depsDir = normalizePath(path.join(config.root, "node_modules", ".vite", "deps")) + "/";
2998
3276
  VirtualModule.ensureVirtualPackageExists();
2999
3277
  initVirtualModules(command, remoteEntryId);
3000
3278
  }
@@ -3003,6 +3281,7 @@ function federation(mfUserOptions) {
3003
3281
  checkAliasConflicts({ shared }),
3004
3282
  normalizeOptimizeDeps_default,
3005
3283
  ...pluginDts(options),
3284
+ pluginDevRemoteHmr(options),
3006
3285
  ...addEntry({
3007
3286
  entryName: "remoteEntry",
3008
3287
  entryPath: remoteEntryId,
@@ -3259,7 +3538,7 @@ function federation(mfUserOptions) {
3259
3538
  apply: "serve",
3260
3539
  enforce: "post",
3261
3540
  transform(code, id) {
3262
- if (!id.includes(".vite/deps/")) return;
3541
+ if (!normalizePath(id).split("?")[0].startsWith(depsDir)) return;
3263
3542
  const initPattern = /\b(init_\w+__loadShare__\w+)\b/g;
3264
3543
  const initFns = /* @__PURE__ */ new Set();
3265
3544
  let match;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/vite",
3
- "version": "1.13.6",
3
+ "version": "1.14.0",
4
4
  "description": "Vite plugin for Module Federation",
5
5
  "type": "module",
6
6
  "main": "./lib/index.cjs",
@@ -70,9 +70,9 @@
70
70
  "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
71
71
  },
72
72
  "dependencies": {
73
- "@module-federation/dts-plugin": "2.3.0",
74
- "@module-federation/runtime": "2.3.0",
75
- "@module-federation/sdk": "2.3.0",
73
+ "@module-federation/dts-plugin": "2.3.1",
74
+ "@module-federation/runtime": "2.3.1",
75
+ "@module-federation/sdk": "2.3.1",
76
76
  "@rollup/pluginutils": "^5.3.0",
77
77
  "defu": "^6.1.4",
78
78
  "es-module-lexer": "^2.0.0",