@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 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 [entryGlobalName] = remote.split("@");
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: remote.replace(entryGlobalName + "@", ""),
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 potentialPackageJson = JSON.parse(fs.readFileSync(potentialPackageJsonPath, "utf-8"));
747
- if (typeof potentialPackageJson == "object" && potentialPackageJson !== null && typeof potentialPackageJson.version === "string" && potentialPackageJson.name === sharedName) return potentialPackageJson.version;
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
- if (JSON.parse((0, fs.readFileSync)(packageJsonPath, "utf-8")).name === packageName) return packageJsonPath;
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
- if (JSON.parse((0, fs.readFileSync)(rootPackageJsonPath, "utf-8")).name === packageName) return rootPackageJsonPath;
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
- const entryPath = getPackageEsmEntryPath(pkg);
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\`);` : useDirectReactImport && pkg === "react" ? `let pkg = await import("react");
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*["'`][^"'`]*["'`]\s*\+\s*\1/, replacement);
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*["'`][^"'`]*["'`]\s*\+\s*\1\s*\}/, replacement);
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
@@ -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
@@ -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,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 [entryGlobalName] = remote.split("@");
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: remote.replace(entryGlobalName + "@", ""),
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 potentialPackageJson = JSON.parse(fs.readFileSync(potentialPackageJsonPath, "utf-8"));
725
- if (typeof potentialPackageJson == "object" && potentialPackageJson !== null && typeof potentialPackageJson.version === "string" && potentialPackageJson.name === sharedName) return potentialPackageJson.version;
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
- if (JSON.parse(readFileSync(packageJsonPath, "utf-8")).name === packageName) return packageJsonPath;
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
- if (JSON.parse(readFileSync(rootPackageJsonPath, "utf-8")).name === packageName) return rootPackageJsonPath;
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
- const entryPath = getPackageEsmEntryPath(pkg);
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\`);` : useDirectReactImport && pkg === "react" ? `let pkg = await import("react");
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*["'`][^"'`]*["'`]\s*\+\s*\1/, replacement);
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*["'`][^"'`]*["'`]\s*\+\s*\1\s*\}/, replacement);
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/vite",
3
- "version": "1.13.7",
3
+ "version": "1.14.1",
4
4
  "description": "Vite plugin for Module Federation",
5
5
  "type": "module",
6
6
  "main": "./lib/index.cjs",