@module-federation/vite 1.15.5 → 1.16.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/lib/index.mjs CHANGED
@@ -1,17 +1,12 @@
1
- import { createRequire } from "node:module";
1
+ import { a as getPackageName, c as isNuxtProjectRoot, d as setPackageDetectionCwd, f as createModuleFederationError, h as __require, i as getPackageDetectionCwd, l as packageNameDecode, m as mfWarn, n as getInstalledPackageJson, o as getPackageNameFromNodeModulePath, p as mfError, r as getIsRolldown, s as hasPackageDependency, t as getInstalledPackageEntry, u as packageNameEncode } from "./packageUtils-DOekOsFz.mjs";
2
2
  import * as fs$1 from "fs";
3
- import fs, { existsSync, readFileSync, readdirSync, realpathSync, statSync, writeFileSync } from "fs";
4
- import { createRequire as createRequire$1 } from "module";
3
+ import { existsSync, readFileSync, realpathSync, statSync, writeFileSync } from "fs";
4
+ import { createRequire } from "module";
5
5
  import * as path$1 from "pathe";
6
6
  import path, { basename } from "pathe";
7
- import { normalizeOptions } from "@module-federation/sdk";
8
- import { consumeTypesAPI, generateTypesAPI, isTSProject, normalizeConsumeTypesOptions, normalizeDtsOptions, normalizeGenerateTypesOptions } from "@module-federation/dts-plugin";
9
- import { rpc } from "@module-federation/dts-plugin/core";
7
+ import { version } from "vite";
10
8
  import { fileURLToPath } from "url";
11
9
  import { init, parse } from "es-module-lexer";
12
- //#region \0rolldown/runtime.js
13
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
14
- //#endregion
15
10
  //#region src/utils/codeRewriter.ts
16
11
  const BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
17
12
  var CodeRewriter = class {
@@ -173,237 +168,6 @@ function injectEntryScript(html, initSrc) {
173
168
  return html.replace("<head>", `<head><script type="module" src=${JSON.stringify(src)}><\/script>`);
174
169
  }
175
170
  //#endregion
176
- //#region src/utils/logger.ts
177
- const MODULE_FEDERATION_LOG_PREFIX = "[Module Federation]";
178
- function formatModuleFederationMessage(message) {
179
- return `${MODULE_FEDERATION_LOG_PREFIX} ${message}`;
180
- }
181
- function createModuleFederationError(message) {
182
- return new Error(formatModuleFederationMessage(message));
183
- }
184
- function toConsoleArgs(message, rest = []) {
185
- if (typeof message === "string") return [formatModuleFederationMessage(message), ...rest];
186
- if (message === void 0) return [MODULE_FEDERATION_LOG_PREFIX, ...rest];
187
- return [
188
- MODULE_FEDERATION_LOG_PREFIX,
189
- message,
190
- ...rest
191
- ];
192
- }
193
- const moduleFederationConsole = {
194
- log(message, ...rest) {
195
- console.log(...toConsoleArgs(message, rest));
196
- },
197
- warn(message, ...rest) {
198
- console.warn(...toConsoleArgs(message, rest));
199
- },
200
- error(message, ...rest) {
201
- console.error(...toConsoleArgs(message, rest));
202
- }
203
- };
204
- moduleFederationConsole.log;
205
- const mfWarn = moduleFederationConsole.warn;
206
- const mfError = moduleFederationConsole.error;
207
- //#endregion
208
- //#region src/utils/packageUtils.ts
209
- const dependencyPresenceCache = /* @__PURE__ */ new Map();
210
- let packageDetectionCwd;
211
- function getDependencyCacheKey(cwd, dependencyName) {
212
- return `${cwd}:${dependencyName}`;
213
- }
214
- function setPackageDetectionCwd(cwd) {
215
- packageDetectionCwd = cwd;
216
- }
217
- function getPackageDetectionCwd() {
218
- return packageDetectionCwd || process.cwd();
219
- }
220
- const DEFAULT_EXPORT_CONDITIONS = [
221
- "browser",
222
- "import",
223
- "module",
224
- "default",
225
- "require"
226
- ];
227
- function resolveExportsEntry(exportsField, conditions = DEFAULT_EXPORT_CONDITIONS) {
228
- if (typeof exportsField === "string") return exportsField;
229
- if (!exportsField || typeof exportsField !== "object") return void 0;
230
- const record = exportsField;
231
- const rootExport = record["."];
232
- if (rootExport) return resolveExportsEntry(rootExport);
233
- for (const condition of conditions) {
234
- const target = resolveExportsEntry(record[condition], conditions);
235
- if (target) return target;
236
- }
237
- for (const target of Object.values(record)) {
238
- const resolved = resolveExportsEntry(target, conditions);
239
- if (resolved) return resolved;
240
- }
241
- }
242
- function getPackageExportsTarget(pkg, packageName, exportsField) {
243
- if (typeof exportsField === "string") return pkg === packageName ? exportsField : void 0;
244
- if (!exportsField || typeof exportsField !== "object") return void 0;
245
- const record = exportsField;
246
- const subpath = pkg === packageName ? "." : `.${pkg.slice(packageName.length)}`;
247
- if (subpath !== ".") return record[subpath];
248
- return record["."] ?? (!Object.keys(record).some((key) => key.startsWith(".")) ? record : void 0);
249
- }
250
- /**
251
- * Escaping rules:
252
- * Convert using the format __${mapping}__, where _ and $ are not allowed in npm package names but can be used in variable names.
253
- * @ => 1
254
- * / => 2
255
- * - => 3
256
- * . => 4
257
- */
258
- /**
259
- * Encodes a package name into a valid file name.
260
- * @param {string} name - The package name, e.g., "@scope/xx-xx.xx".
261
- * @returns {string} - The encoded file name.
262
- */
263
- function packageNameEncode(name) {
264
- if (typeof name !== "string") throw createModuleFederationError("A string package name is required");
265
- return name.replace(/@/g, "_mf_0_").replace(/\//g, "_mf_1_").replace(/-/g, "_mf_2_").replace(/\./g, "_mf_3_");
266
- }
267
- /**
268
- * Decodes an encoded file name back to the original package name.
269
- * @param {string} encoded - The encoded file name, e.g., "_mf_0_scope_mf_1_xx_mf_2_xx_mf_3_xx".
270
- * @returns {string} - The decoded package name.
271
- */
272
- function packageNameDecode(encoded) {
273
- if (typeof encoded !== "string") throw createModuleFederationError("A string encoded file name is required");
274
- return encoded.replace(/_mf_0_/g, "@").replace(/_mf_1_/g, "/").replace(/_mf_2_/g, "-").replace(/_mf_3_/g, ".");
275
- }
276
- /**
277
- * Removes any subpath from an npm package specifier and returns the package name only.
278
- * @param {string} packageString - The package specifier, e.g., "@scope/pkg/runtime" or "react/jsx-runtime".
279
- * @returns {string} - The base npm package name.
280
- */
281
- function getPackageName(packageString) {
282
- const match = packageString.match(/^(?:@[^/]+\/)?[^/]+/);
283
- return match ? match[0] : packageString;
284
- }
285
- function getPackageNameFromNodeModulePath(source) {
286
- const normalized = source.replace(/\\/g, "/");
287
- const nodeModulesIndex = normalized.lastIndexOf("/node_modules/");
288
- if (nodeModulesIndex < 0) return;
289
- const parts = normalized.slice(nodeModulesIndex + 14).split("/");
290
- if (!parts[0]) return;
291
- if (parts[0].startsWith("@")) return parts[1] ? `${parts[0]}/${parts[1]}` : void 0;
292
- return parts[0];
293
- }
294
- function getInstalledPackageJson(pkg, opts) {
295
- const cwd = opts?.cwd || getPackageDetectionCwd();
296
- const packageName = opts?.packageName || getPackageName(pkg);
297
- const tryReadPackageJson = (packageJsonPath) => {
298
- if (!existsSync(packageJsonPath)) return void 0;
299
- try {
300
- return {
301
- path: packageJsonPath,
302
- dir: path.dirname(packageJsonPath),
303
- packageJson: JSON.parse(readFileSync(packageJsonPath, "utf-8"))
304
- };
305
- } catch {
306
- return;
307
- }
308
- };
309
- const findPackageInPnpmStore = (startDir) => {
310
- let currentDir = startDir;
311
- const rootDir = path.parse(currentDir).root;
312
- while (true) {
313
- const pnpmStoreDir = path.join(currentDir, "node_modules", ".pnpm");
314
- if (existsSync(pnpmStoreDir)) try {
315
- for (const entry of readdirSync(pnpmStoreDir, { withFileTypes: true })) {
316
- if (!entry.isDirectory()) continue;
317
- const candidate = tryReadPackageJson(path.join(pnpmStoreDir, entry.name, "node_modules", packageName, "package.json"));
318
- if (candidate?.packageJson.name === packageName) return candidate;
319
- }
320
- } catch {}
321
- if (currentDir === rootDir) break;
322
- currentDir = path.dirname(currentDir);
323
- }
324
- };
325
- try {
326
- const projectRequire = createRequire$1(new URL(`file://${path.join(cwd, "package.json")}`));
327
- let resolvedPath;
328
- if (opts?.fromResolvedEntry) resolvedPath = opts.fromResolvedEntry;
329
- else try {
330
- resolvedPath = projectRequire.resolve(pkg);
331
- } catch {
332
- resolvedPath = projectRequire.resolve(packageName);
333
- }
334
- let currentDir = path.dirname(resolvedPath);
335
- const rootDir = path.parse(currentDir).root;
336
- while (true) {
337
- const packageJsonPath = path.join(currentDir, "package.json");
338
- if (existsSync(packageJsonPath)) {
339
- const packageJsonContent = readFileSync(packageJsonPath, "utf-8");
340
- try {
341
- const packageJson = JSON.parse(packageJsonContent);
342
- if (packageJson.name === packageName) return {
343
- path: packageJsonPath,
344
- dir: currentDir,
345
- packageJson
346
- };
347
- } catch (error) {
348
- if (!(error instanceof SyntaxError)) throw error;
349
- }
350
- }
351
- if (currentDir === rootDir) break;
352
- currentDir = path.dirname(currentDir);
353
- }
354
- } catch {
355
- let currentDir = cwd;
356
- const rootDir = path.parse(currentDir).root;
357
- while (true) {
358
- const directCandidate = tryReadPackageJson(path.join(currentDir, "node_modules", packageName, "package.json"));
359
- if (directCandidate?.packageJson.name === packageName) return directCandidate;
360
- if (currentDir === rootDir) break;
361
- currentDir = path.dirname(currentDir);
362
- }
363
- return findPackageInPnpmStore(cwd);
364
- }
365
- }
366
- function getInstalledPackageEntry(pkg, opts) {
367
- const installed = getInstalledPackageJson(pkg, opts);
368
- if (!installed) return void 0;
369
- const cwd = opts?.cwd || getPackageDetectionCwd();
370
- const packageName = opts?.packageName || getPackageName(pkg);
371
- if (pkg !== packageName && opts?.resolveSubpathWithRequire !== false) try {
372
- return createRequire$1(new URL(`file://${path.join(cwd, "package.json")}`)).resolve(pkg);
373
- } catch {}
374
- const packageJson = installed.packageJson;
375
- const explicitEntry = resolveExportsEntry(getPackageExportsTarget(pkg, packageName, packageJson.exports), opts?.conditions) || (typeof packageJson.module === "string" ? packageJson.module : void 0) || (typeof packageJson.main === "string" ? packageJson.main : void 0) || "index.js";
376
- return path.join(installed.dir, explicitEntry);
377
- }
378
- /**
379
- * Detect whether the current runtime is Vite 8+ by checking for a Vite version flag
380
- * on the plugin hook context, with Rolldown metadata kept as a compatibility fallback.
381
- */
382
- function getIsRolldown(ctx) {
383
- const viteVersion = ctx?.meta?.viteVersion;
384
- const viteMajor = Number(String(viteVersion ?? "").split(".")[0]);
385
- return Number.isFinite(viteMajor) && viteMajor >= 8 || !!ctx?.meta?.rolldownVersion;
386
- }
387
- function hasPackageDependency(dependencyName, cwd = packageDetectionCwd || process.cwd()) {
388
- const cacheKey = getDependencyCacheKey(cwd, dependencyName);
389
- const cached = dependencyPresenceCache.get(cacheKey);
390
- if (cached !== void 0) return cached;
391
- try {
392
- const packageJson = JSON.parse(readFileSync(path.join(cwd, "package.json"), "utf8"));
393
- const hasDependency = [
394
- packageJson.dependencies,
395
- packageJson.devDependencies,
396
- packageJson.peerDependencies,
397
- packageJson.optionalDependencies
398
- ].some((deps) => !!deps?.[dependencyName]);
399
- dependencyPresenceCache.set(cacheKey, hasDependency);
400
- return hasDependency;
401
- } catch {
402
- dependencyPresenceCache.set(cacheKey, false);
403
- return false;
404
- }
405
- }
406
- //#endregion
407
171
  //#region src/utils/normalizeModuleFederationOptions.ts
408
172
  const INTERNAL_NAME_PREFIX = "__mfe_internal__";
409
173
  function toInternalModuleFederationName(name) {
@@ -475,7 +239,7 @@ function normalizeRemoteItem(key, remote) {
475
239
  * @returns {string | undefined}
476
240
  */
477
241
  function searchPackageVersion(sharedName) {
478
- const version = getInstalledPackageJson(sharedName, { packageName: sharedName })?.packageJson.version;
242
+ const version = getInstalledPackageJson(sharedName)?.packageJson.version;
479
243
  return typeof version === "string" ? version : void 0;
480
244
  }
481
245
  function inferVersionFromRequiredVersion(requiredVersion) {
@@ -598,6 +362,18 @@ function normalizeManifest(manifest) {
598
362
  }
599
363
  let config;
600
364
  let explicitSharedKeys = /* @__PURE__ */ new Set();
365
+ function resolveRuntimeImplementation() {
366
+ const fallback = __require.resolve("@module-federation/runtime");
367
+ try {
368
+ const packageJsonPath = __require.resolve("@module-federation/runtime/package.json");
369
+ const packageJson = JSON.parse(fs$1.readFileSync(packageJsonPath, "utf-8"));
370
+ const importExport = packageJson.exports?.["."];
371
+ const exportImport = typeof importExport === "object" ? typeof importExport.import === "string" ? importExport.import : importExport.import?.default : void 0;
372
+ const esmEntry = packageJson.module || exportImport;
373
+ if (esmEntry) return path$1.join(path$1.dirname(packageJsonPath), esmEntry);
374
+ } catch {}
375
+ return fallback;
376
+ }
601
377
  function getNormalizeModuleFederationOptions() {
602
378
  return config;
603
379
  }
@@ -622,7 +398,7 @@ function normalizeModuleFederationOptions(options) {
622
398
  shareScope: options.shareScope || "default",
623
399
  shared: normalizeShared(options.shared),
624
400
  runtimePlugins: options.runtimePlugins || [],
625
- implementation: options.implementation || __require.resolve("@module-federation/runtime"),
401
+ implementation: options.implementation || resolveRuntimeImplementation(),
626
402
  manifest: normalizeManifest(options.manifest),
627
403
  dev: options.dev,
628
404
  dts: options.dts,
@@ -836,11 +612,24 @@ function getDeferredInitPromiseCode() {
836
612
  initReject = rj;
837
613
  });`;
838
614
  }
839
- function getSsrNoopResolveCode() {
615
+ let _ssrRemotes = [];
616
+ function setSsrRemotes(remotes) {
617
+ _ssrRemotes = remotes;
618
+ }
619
+ function getSsrNoopResolveCode(enableSsrInit) {
620
+ if (!enableSsrInit) return "";
840
621
  return `if (typeof window === 'undefined') {
841
- initResolve({
842
- loadRemote: function() { return Promise.resolve(undefined); },
843
- loadShare: function() { return Promise.resolve(undefined); },
622
+ var _noop = { loadRemote: function() { return Promise.resolve(undefined); }, loadShare: function() { return Promise.resolve(undefined); } };
623
+ import(/* @vite-ignore */ '@module-federation/runtime').then(function(runtimeMod) {
624
+ return import(/* @vite-ignore */ '@module-federation/vite/ssrEntryLoader').then(
625
+ function(loaderMod) { return [runtimeMod, [loaderMod.default()]]; },
626
+ function() { return [runtimeMod, []]; }
627
+ );
628
+ }).then(function(pair) {
629
+ var runtime = pair[0].init({ name: '__mf_ssr_host__', remotes: ${JSON.stringify(_ssrRemotes)}, shared: {}, plugins: pair[1] });
630
+ initResolve(runtime);
631
+ }, function() {
632
+ initResolve(_noop);
844
633
  });
845
634
  }`;
846
635
  }
@@ -855,12 +644,12 @@ if (!${options.stateVar}) {
855
644
  initResolve,
856
645
  initReject,
857
646
  };
858
- ${getSsrNoopResolveCode()}
647
+ ${getSsrNoopResolveCode(options.enableSsrInit)}
859
648
  }
860
649
  const ${options.exposedConst} = ${options.stateVar}.${options.exposedProperty};
861
650
  `;
862
651
  }
863
- function getRuntimeInitBootstrapCode() {
652
+ function getRuntimeInitBootstrapCode(enableSsrInit = false) {
864
653
  return `
865
654
  const globalKey = ${JSON.stringify(getRuntimeInitGlobalKey())};
866
655
  const moduleCacheGlobalKey = ${JSON.stringify(MODULE_CACHE_GLOBAL_KEY)};
@@ -875,7 +664,7 @@ if (!globalThis[globalKey]) {
875
664
  initReject,
876
665
  moduleCache: globalThis[moduleCacheGlobalKey],
877
666
  };
878
- ${getSsrNoopResolveCode()}
667
+ ${getSsrNoopResolveCode(enableSsrInit)}
879
668
  }
880
669
  globalThis[globalKey].moduleCache ||= globalThis[moduleCacheGlobalKey];
881
670
  globalThis[globalKey].moduleCache.share ||= {};
@@ -891,27 +680,29 @@ globalThis[__mfCacheGlobalKey].remote ||= {};
891
680
  const __mfModuleCache = globalThis[__mfCacheGlobalKey];
892
681
  `;
893
682
  }
894
- function getRuntimeInitPromiseBootstrapCode() {
683
+ function getRuntimeInitPromiseBootstrapCode(enableSsrInit = false) {
895
684
  return getRuntimeInitStateBootstrapCode({
896
685
  globalKeyVar: "__mfPromiseGlobalKey",
897
686
  stateVar: "__mfPromiseState",
898
687
  exposedConst: "initPromise",
899
- exposedProperty: "initPromise"
688
+ exposedProperty: "initPromise",
689
+ enableSsrInit
900
690
  });
901
691
  }
902
- function getRuntimeInitResolveBootstrapCode() {
692
+ function getRuntimeInitResolveBootstrapCode(enableSsrInit = false) {
903
693
  return getRuntimeInitStateBootstrapCode({
904
694
  globalKeyVar: "__mfResolveGlobalKey",
905
695
  stateVar: "__mfResolveState",
906
696
  exposedConst: "initResolve",
907
- exposedProperty: "initResolve"
697
+ exposedProperty: "initResolve",
698
+ enableSsrInit
908
699
  });
909
700
  }
910
- function writeRuntimeInitStatus(command) {
701
+ function writeRuntimeInitStatus(command, enableSsrInit = false) {
911
702
  const exportStatement = command === "build" ? `const { initPromise, initResolve, initReject, moduleCache } = globalThis[globalKey];
912
703
  export { initPromise, initResolve, initReject, moduleCache };` : `module.exports = globalThis[globalKey];`;
913
704
  virtualRuntimeInitStatus.writeSync(`
914
- ${getRuntimeInitBootstrapCode()}
705
+ ${getRuntimeInitBootstrapCode(enableSsrInit)}
915
706
  ${exportStatement}
916
707
  `);
917
708
  }
@@ -946,10 +737,10 @@ function isValidEsmExportName(name) {
946
737
  return !!name && name !== "default" && name !== "__esModule" && isValidJsIdentifier(name);
947
738
  }
948
739
  const JS_IDENTIFIER_PATTERN = `[\$_\\p{ID_Start}][\$_\\u200C\\u200D\\p{ID_Continue}]*`;
949
- const localRequire = createRequire$1(import.meta.url);
740
+ const localRequire = createRequire(import.meta.url);
950
741
  function resolvePackageEntryFromProjectRoot(pkg) {
951
742
  try {
952
- return createRequire$1(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`)).resolve(pkg);
743
+ return createRequire(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`)).resolve(pkg);
953
744
  } catch {
954
745
  return;
955
746
  }
@@ -1047,7 +838,7 @@ function getNamedExportsViaRegex(source, filePath, visited) {
1047
838
  }
1048
839
  function getPackageNamedExports(pkg) {
1049
840
  try {
1050
- const mod = createRequire$1(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`))(pkg);
841
+ const mod = createRequire(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`))(pkg);
1051
842
  return Object.keys(mod).filter((k) => isValidEsmExportName(k));
1052
843
  } catch {
1053
844
  return getEsmNamedExports(pkg);
@@ -1055,7 +846,7 @@ function getPackageNamedExports(pkg) {
1055
846
  }
1056
847
  function getLocalProviderImportPath(pkg) {
1057
848
  try {
1058
- const resolved = createRequire$1(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`)).resolve(pkg);
849
+ const resolved = createRequire(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`)).resolve(pkg);
1059
850
  return isWorkspaceFilePath(resolved) ? resolved : void 0;
1060
851
  } catch {
1061
852
  const resolved = getInstalledPackageEntry(pkg, {
@@ -1076,7 +867,7 @@ function getProjectResolvedImportPath(pkg) {
1076
867
  if (esmEntry) return esmEntry;
1077
868
  }
1078
869
  try {
1079
- return createRequire$1(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`)).resolve(pkg);
870
+ return createRequire(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`)).resolve(pkg);
1080
871
  } catch {
1081
872
  return;
1082
873
  }
@@ -1098,7 +889,7 @@ function isWorkspacePackageEntry(pkg, resolved) {
1098
889
  }
1099
890
  function tryResolveImportFromPackageRoot(pkg, root) {
1100
891
  try {
1101
- return createRequire$1(new URL(`file://${path.join(root, "package.json")}`)).resolve(pkg);
892
+ return createRequire(new URL(`file://${path.join(root, "package.json")}`)).resolve(pkg);
1102
893
  } catch {
1103
894
  return;
1104
895
  }
@@ -1451,7 +1242,7 @@ const normalizeRuntimeShareCode = `const __mfNormalizeRuntimeShare = (mod) => {
1451
1242
  let current = mod;
1452
1243
  for (let i = 0; i < 5; i++) {
1453
1244
  const defaultExport = current?.default;
1454
- if (!defaultExport || typeof defaultExport !== "object") break;
1245
+ if (!defaultExport || typeof defaultExport !== "object" || Object.keys(defaultExport).length === 0) break;
1455
1246
  const namedValues = Object.keys(current).filter((key) => key !== "default").map((key) => current[key]);
1456
1247
  if (namedValues.length > 0 && namedValues.some((value) => value !== undefined)) break;
1457
1248
  current = defaultExport;
@@ -1491,6 +1282,9 @@ const REMOTE_ENTRY_ID = "virtual:mf-REMOTE_ENTRY_ID";
1491
1282
  function getRemoteEntryId(options) {
1492
1283
  return `${REMOTE_ENTRY_ID}:${`${options.internalName}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
1493
1284
  }
1285
+ const SSR_ONLY_PLUGIN_SPECIFIERS = new Set(["@module-federation/vite/ssrEntryLoader"]);
1286
+ const isSsrOnlyPlugin = (importStatement) => [...SSR_ONLY_PLUGIN_SPECIFIERS].some((s) => importStatement.includes(s));
1287
+ const getSsrOnlyPluginSpecifier = (importStatement) => [...SSR_ONLY_PLUGIN_SPECIFIERS].find((s) => importStatement.includes(s));
1494
1288
  function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(options), command = "build") {
1495
1289
  const pluginImportNames = options.runtimePlugins.map((p, i) => {
1496
1290
  if (typeof p === "string") return [
@@ -1514,7 +1308,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
1514
1308
  globalThis.__VUE_HMR_RUNTIME__ = { createRecord() {}, rerender() {}, reload() {} };
1515
1309
  }
1516
1310
  import {init as runtimeInit, loadRemote} from "@module-federation/runtime";
1517
- ${pluginImportNames.map((item) => item[1]).join("\n")}
1311
+ ${pluginImportNames.filter((item) => !isSsrOnlyPlugin(item[1])).map((item) => item[1]).join("\n")}
1518
1312
  ${command === "build" ? getRuntimeInitResolveBootstrapCode() : getRuntimeInitBootstrapCode() + "\n const { initResolve } = globalThis[globalKey];"}
1519
1313
  ${getRuntimeModuleCacheBootstrapCode()}
1520
1314
  const initTokens = {}
@@ -1562,11 +1356,19 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
1562
1356
  async function init(shared = {}, initScope = []) {
1563
1357
  const {usedShared, usedRemotes} = await getLocalSharedImportMap()
1564
1358
  ${generateDirectSharedCacheSeedCode(command)}
1359
+ const __browserPlugins = [${pluginImportNames.filter((item) => !isSsrOnlyPlugin(item[1])).map((item) => `${item[0]}(${item[2]})`).join(", ")}];
1360
+ const __ssrPlugins = typeof globalThis.window === 'undefined'
1361
+ ? await Promise.all([${pluginImportNames.filter((item) => isSsrOnlyPlugin(item[1])).map((item) => {
1362
+ const specifier = getSsrOnlyPluginSpecifier(item[1]);
1363
+ const opts = item[2];
1364
+ return `import(${JSON.stringify(specifier)}).then(m => (m.default ?? m)(${opts}))`;
1365
+ }).join(", ")}])
1366
+ : [];
1565
1367
  const initRes = runtimeInit({
1566
1368
  name: mfName,
1567
- remotes: usedRemotes,
1369
+ remotes: ${options.shareStrategy === "loaded-first" ? "[]" : "usedRemotes"},
1568
1370
  shared: usedShared,
1569
- plugins: [${pluginImportNames.map((item) => `${item[0]}(${item[2]})`).join(", ")}],
1371
+ plugins: [...__browserPlugins, ...__ssrPlugins],
1570
1372
  ${options.shareStrategy ? `shareStrategy: '${options.shareStrategy}'` : ""}
1571
1373
  });
1572
1374
  // handling circular init calls
@@ -1617,6 +1419,7 @@ const hostAutoInitModule = new VirtualModule("hostAutoInit", "__H_A_I__");
1617
1419
  let currentHostAutoInitRemoteEntryId = REMOTE_ENTRY_ID;
1618
1420
  let currentHostAutoInitCommand = "build";
1619
1421
  function generateHostAutoInitCode(remoteEntryImport, _command = "build") {
1422
+ const shouldPreloadShares = getNormalizeModuleFederationOptions().shareStrategy !== "loaded-first";
1620
1423
  return `
1621
1424
  ${getRuntimeModuleCacheBootstrapCode()}
1622
1425
  let hostInitPromise;
@@ -1628,6 +1431,7 @@ function generateHostAutoInitCode(remoteEntryImport, _command = "build") {
1628
1431
  const runtime = await remoteEntry.init();
1629
1432
  const usedShared = ${generateUsedSharedPreloadConfig()};
1630
1433
  ${normalizeRuntimeShareCode}
1434
+ ${shouldPreloadShares ? `
1631
1435
  for (const [pkg, share] of Object.entries(usedShared)) {
1632
1436
  if (__mfModuleCache.share[pkg] !== undefined) {
1633
1437
  continue;
@@ -1641,6 +1445,7 @@ function generateHostAutoInitCode(remoteEntryImport, _command = "build") {
1641
1445
  });
1642
1446
  });
1643
1447
  }
1448
+ ` : ""}
1644
1449
  const __mfRemotePreloads = [];
1645
1450
  await Promise.all(__mfRemotePreloads);
1646
1451
  return runtime;
@@ -1672,12 +1477,13 @@ function getHostAutoInitPath() {
1672
1477
  //#region src/virtualModules/virtualRemotes.ts
1673
1478
  const cacheRemoteMap = {};
1674
1479
  const LOAD_REMOTE_TAG = "__loadRemote__";
1675
- function getRemoteVirtualModule(remote, command) {
1676
- if (!cacheRemoteMap[remote]) {
1677
- cacheRemoteMap[remote] = new VirtualModule(remote, LOAD_REMOTE_TAG, ".mjs");
1678
- cacheRemoteMap[remote].writeSync(generateRemotes(remote, command));
1480
+ function getRemoteVirtualModule(remote, command, enableSsrInit = false) {
1481
+ const cacheKey = `${remote}__${command}__${enableSsrInit ? "ssr" : "no-ssr"}`;
1482
+ if (!cacheRemoteMap[cacheKey]) {
1483
+ cacheRemoteMap[cacheKey] = new VirtualModule(remote, LOAD_REMOTE_TAG, ".mjs");
1484
+ cacheRemoteMap[cacheKey].writeSync(generateRemotes(remote, command, enableSsrInit));
1679
1485
  }
1680
- return cacheRemoteMap[remote];
1486
+ return cacheRemoteMap[cacheKey];
1681
1487
  }
1682
1488
  const usedRemotesMap = {};
1683
1489
  function addUsedRemote(remoteKey, remoteModule) {
@@ -1687,40 +1493,93 @@ function addUsedRemote(remoteKey, remoteModule) {
1687
1493
  function getUsedRemotesMap() {
1688
1494
  return usedRemotesMap;
1689
1495
  }
1690
- function generateRemotes(id, command) {
1691
- const useReactProxy = command === "serve" && hasPackageDependency("react");
1496
+ function getRemoteFromId(id, remotes) {
1497
+ const remoteName = Object.keys(remotes).filter((name) => id === name || id.startsWith(name + "/")).sort((a, b) => b.length - a.length)[0];
1498
+ return remoteName ? remotes[remoteName] : void 0;
1499
+ }
1500
+ function generateRemotes(id, command, enableSsrInit = false) {
1501
+ const useReactProxy = hasPackageDependency("react");
1502
+ const options = getNormalizeModuleFederationOptions();
1503
+ const isLoadedFirst = options.shareStrategy === "loaded-first";
1504
+ const remote = getRemoteFromId(id, options.remotes);
1505
+ const registerRemoteCode = isLoadedFirst && remote ? `runtime.registerRemotes([${JSON.stringify({
1506
+ entryGlobalName: remote.entryGlobalName,
1507
+ name: remote.name,
1508
+ type: remote.type,
1509
+ entry: remote.entry,
1510
+ shareScope: remote.shareScope ?? "default"
1511
+ })}]);` : "";
1692
1512
  const reactImportLine = useReactProxy ? `import __mfReactDefault from "react";
1693
1513
  import * as __mfReactNamespace from "react";
1694
1514
  const __mfReact = __mfReactDefault ?? __mfReactNamespace.default ?? __mfReactNamespace;` : "";
1695
1515
  const importLine = command === "build" ? `${getRuntimeModuleCacheBootstrapCode()}
1696
- import { hostInitPromise as __mfHostInitPromise } from ${JSON.stringify(getHostAutoInitPath())};` : `${getRuntimeInitBootstrapCode()}
1697
- const { initPromise, moduleCache: __mfModuleCache } = globalThis[globalKey];`;
1516
+ import { hostInitPromise as __mfHostInitPromise } from ${JSON.stringify(getHostAutoInitPath())};` : `${getRuntimeInitBootstrapCode(enableSsrInit)}
1517
+ const { initPromise, initResolve, initReject, moduleCache: __mfModuleCache } = globalThis[globalKey];
1518
+ if (typeof window !== "undefined") {
1519
+ import(${JSON.stringify(getHostAutoInitPath())})
1520
+ .then((mod) => mod.hostInitPromise)
1521
+ .then(initResolve, initReject);
1522
+ }`;
1698
1523
  const exportLine = command === "serve" ? `if (__mfRemotePending) {
1699
- const mod = await __mfRemotePending;
1700
- if (mod !== undefined) exportModule = mod;
1524
+ __mfRemotePending = __mfRemotePending.then((mod) => {
1525
+ if (mod !== undefined) exportModule = mod;
1526
+ return exportModule;
1527
+ });
1701
1528
  }
1702
- export const __moduleExports = exportModule;
1703
- export const __mf_remote_pending = Promise.resolve(exportModule);
1529
+ export { exportModule as __moduleExports };
1530
+ export const __mf_remote_pending = __mfRemotePending || Promise.resolve(exportModule);
1704
1531
  export default exportModule?.__mf_is_remote_proxy ? exportModule : exportModule?.__esModule ? exportModule.default : exportModule.default ?? exportModule` : command === "build" ? `if (__mfRemotePending) {
1705
- const mod = await __mfRemotePending;
1706
- if (mod !== undefined) exportModule = mod;
1532
+ __mfRemotePending = __mfRemotePending.then((mod) => {
1533
+ if (mod !== undefined) exportModule = mod;
1534
+ return exportModule;
1535
+ });
1707
1536
  }
1708
- export const __moduleExports = exportModule;
1709
- export const __mf_remote_pending = Promise.resolve(exportModule);
1537
+ export { exportModule as __moduleExports };
1538
+ export const __mf_remote_pending = __mfRemotePending || Promise.resolve(exportModule);
1710
1539
  export default exportModule?.__mf_is_remote_proxy ? exportModule : exportModule?.__esModule ? exportModule.default : exportModule.default ?? exportModule` : "export default exportModule";
1540
+ const remoteLoadRuntimePromise = command === "build" ? "__mfHostInitPromise" : "initPromise";
1541
+ const remoteLoadFailureHandler = command === "build" ? `.catch((error) => {
1542
+ delete __mfModuleCache.remote[pendingKey];
1543
+ throw error;
1544
+ })` : `.catch(() => {
1545
+ delete __mfModuleCache.remote[pendingKey];
1546
+ })`;
1711
1547
  return `
1712
1548
  ${reactImportLine}
1713
1549
  ${importLine}
1714
- ${command !== "build" ? `
1550
+ ${`
1551
+ function __mfStartRemoteLoad() {
1552
+ ${`
1553
+ const pendingKey = ${JSON.stringify(`__mf_pending__${id}`)};
1554
+ if (!__mfModuleCache.remote[pendingKey]) {
1555
+ __mfModuleCache.remote[pendingKey] = ${remoteLoadRuntimePromise}
1556
+ .then((runtime) => {
1557
+ ${registerRemoteCode}
1558
+ return runtime.loadRemote(${JSON.stringify(id)});
1559
+ })
1560
+ .then((mod) => {
1561
+ __mfModuleCache.remote[${JSON.stringify(id)}] = mod;
1562
+ delete __mfModuleCache.remote[pendingKey];
1563
+ return mod;
1564
+ })
1565
+ ${remoteLoadFailureHandler};
1566
+ }
1567
+ return __mfModuleCache.remote[pendingKey];`}
1568
+ }
1715
1569
  function __mfCreateRemoteProxy(pendingPromise) {
1716
1570
  const listeners = new Set();
1717
- pendingPromise?.finally(() => {
1718
- for (const listener of listeners) listener();
1719
- });
1571
+ const ensurePending = () => {
1572
+ pendingPromise ||= __mfStartRemoteLoad();
1573
+ pendingPromise?.finally(() => {
1574
+ for (const listener of listeners) listener();
1575
+ });
1576
+ return pendingPromise;
1577
+ };
1720
1578
  const getModule = () => __mfModuleCache.remote[${JSON.stringify(id)}];
1721
1579
  const proxyTarget = function (...args) {
1722
1580
  ${useReactProxy ? `const [, setVersion] = __mfReact.useState(0);
1723
1581
  __mfReact.useEffect(() => {
1582
+ ensurePending();
1724
1583
  const listener = () => setVersion((value) => value + 1);
1725
1584
  listeners.add(listener);
1726
1585
  if (getModule()) listener();
@@ -1731,18 +1590,33 @@ export default exportModule?.__mf_is_remote_proxy ? exportModule : exportModule?
1731
1590
  if (fn !== undefined && fn !== null) {
1732
1591
  ${useReactProxy ? `return __mfReact.createElement(fn, args[0]);` : `return fn.apply(this, args);`}
1733
1592
  }
1734
- ${useReactProxy ? `return null;` : `throw pendingPromise;`}
1593
+ ${useReactProxy ? `return null;` : `throw ensurePending();`}
1735
1594
  };
1736
1595
  return new Proxy(proxyTarget, {
1737
1596
  get(_target, prop) {
1738
1597
  if (prop === "__mf_is_remote_proxy") return true;
1739
1598
  if (prop === "__esModule") return true;
1740
1599
  if (prop === "then") return undefined;
1600
+ // Allow React's dev-mode console.warn to stringify the proxy without
1601
+ // throwing "Cannot convert object to primitive value".
1602
+ if (prop === Symbol.toPrimitive || prop === "toString")
1603
+ return () => "[MF remote proxy: pending]";
1741
1604
  const mod = getModule();
1742
1605
  if (mod) {
1743
1606
  return prop in mod ? mod[prop] : mod.default?.[prop];
1744
1607
  }
1745
- ${useReactProxy ? `return undefined;` : `throw pendingPromise;`}
1608
+ // When the module is pending and React.lazy() checks for "default",
1609
+ // return the proxy function itself so React renders it (returns null)
1610
+ // rather than crashing on undefined.
1611
+ ${useReactProxy ? `if (prop === "default") return proxyTarget;
1612
+ return undefined;` : `throw ensurePending();`}
1613
+ },
1614
+ has(_target, prop) {
1615
+ const mod = getModule();
1616
+ if (mod) return prop in mod;
1617
+ // Tell React that "default" exists when module is pending so it
1618
+ // doesn't warn "lazy: Expected the result of a dynamic import()".
1619
+ ${useReactProxy ? `return prop === "default" || prop === "__esModule" || prop === "__mf_is_remote_proxy";` : `return false;`}
1746
1620
  },
1747
1621
  ownKeys() {
1748
1622
  const mod = getModule();
@@ -1770,40 +1644,12 @@ export default exportModule?.__mf_is_remote_proxy ? exportModule : exportModule?
1770
1644
  return target.apply(thisArg, args);
1771
1645
  }
1772
1646
  });
1773
- }` : ""}
1647
+ }`}
1774
1648
  let __mfRemotePending;
1775
1649
  let exportModule = __mfModuleCache.remote[${JSON.stringify(id)}]
1776
1650
  if (exportModule === undefined) {
1777
- ${command !== "build" ? `const pendingKey = ${JSON.stringify(`__mf_pending__${id}`)};
1778
- if (!__mfModuleCache.remote[pendingKey]) {
1779
- __mfModuleCache.remote[pendingKey] = initPromise
1780
- .then((runtime) => runtime.loadRemote(${JSON.stringify(id)}))
1781
- .then((mod) => {
1782
- __mfModuleCache.remote[${JSON.stringify(id)}] = mod;
1783
- delete __mfModuleCache.remote[pendingKey];
1784
- return mod;
1785
- })
1786
- .catch(() => {
1787
- delete __mfModuleCache.remote[pendingKey];
1788
- });
1789
- }` : ""}
1790
- ${command !== "build" ? `__mfRemotePending = __mfModuleCache.remote[pendingKey];
1791
- exportModule = __mfCreateRemoteProxy(__mfRemotePending);` : `const pendingKey = ${JSON.stringify(`__mf_pending__${id}`)};
1792
- if (!__mfModuleCache.remote[pendingKey]) {
1793
- __mfModuleCache.remote[pendingKey] = __mfHostInitPromise
1794
- .then((runtime) => runtime.loadRemote(${JSON.stringify(id)}))
1795
- .then((mod) => {
1796
- __mfModuleCache.remote[${JSON.stringify(id)}] = mod;
1797
- delete __mfModuleCache.remote[pendingKey];
1798
- return mod;
1799
- })
1800
- .catch((error) => {
1801
- delete __mfModuleCache.remote[pendingKey];
1802
- throw error;
1803
- });
1804
- }
1805
- __mfRemotePending = __mfModuleCache.remote[pendingKey];
1806
- exportModule = {};`}
1651
+ __mfRemotePending = __mfStartRemoteLoad();
1652
+ exportModule = __mfCreateRemoteProxy(__mfRemotePending);
1807
1653
  }
1808
1654
  ${exportLine}
1809
1655
  `;
@@ -1813,7 +1659,38 @@ export default exportModule?.__mf_is_remote_proxy ? exportModule : exportModule?
1813
1659
  function getFirstHtmlEntryFile(entryFiles) {
1814
1660
  return entryFiles.find((file) => file.endsWith(".html"));
1815
1661
  }
1816
- const addEntry = ({ entryName, entryPath, fileName, inject = "entry", forceClientInjected }) => {
1662
+ function stripQueryAndHash(file) {
1663
+ return file.split(/[?#]/)[0];
1664
+ }
1665
+ function getBuildInput(config) {
1666
+ return config.build?.rollupOptions?.input ?? config.build?.rolldownOptions?.input;
1667
+ }
1668
+ function patchHashEntryFileName(output, entryName, fileName) {
1669
+ const originalEntryFileNames = output.entryFileNames;
1670
+ output.entryFileNames = (chunkInfo, ...args) => {
1671
+ if (chunkInfo?.name === entryName) return fileName;
1672
+ if (typeof originalEntryFileNames === "function") return originalEntryFileNames(chunkInfo, ...args);
1673
+ return originalEntryFileNames || "assets/[name]-[hash].js";
1674
+ };
1675
+ }
1676
+ function patchHashEntryFileNames(config, entryName, fileName) {
1677
+ if (!fileName?.includes?.("[hash")) return;
1678
+ config.build ??= {};
1679
+ config.build.rollupOptions ??= {};
1680
+ config.build.rolldownOptions ??= {};
1681
+ const patchOutput = (output) => patchHashEntryFileName(output, entryName, fileName);
1682
+ const patchBundlerOutput = (bundlerOptions) => {
1683
+ const output = bundlerOptions.output;
1684
+ if (Array.isArray(output)) {
1685
+ output.forEach(patchOutput);
1686
+ return;
1687
+ }
1688
+ patchOutput(bundlerOptions.output ??= {});
1689
+ };
1690
+ patchBundlerOutput(config.build.rollupOptions);
1691
+ patchBundlerOutput(config.build.rolldownOptions);
1692
+ }
1693
+ const addEntry = ({ entryName, entryPath, fileName, inject = "entry", forceClientInjected, skipTransformFor = [] }) => {
1817
1694
  const DEV_HTML_PROXY_PREFIX = "virtual:mf-html-entry-proxy?";
1818
1695
  const ENTRY_BOOTSTRAP_QUERY = "?mf-entry-bootstrap";
1819
1696
  const waitsForInit = entryName === "hostInit";
@@ -1826,6 +1703,7 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry", forceClien
1826
1703
  let viteConfig;
1827
1704
  let clientInjected = forceClientInjected ?? false;
1828
1705
  let emittedFileName;
1706
+ let skipTransformIds = /* @__PURE__ */ new Set();
1829
1707
  function skipSvelteKitSsrBuild() {
1830
1708
  return (_command === "build" || viteConfig?.command === "build") && viteConfig?.build?.ssr && hasPackageDependency("@sveltejs/kit");
1831
1709
  }
@@ -1881,21 +1759,44 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry", forceClien
1881
1759
  return patched;
1882
1760
  }
1883
1761
  function getBootstrapSource(initSrc, entrySrc, useSystemImportFallback = false) {
1884
- const remotePreloads = Object.entries(getUsedRemotesMap()).flatMap(([remoteKey, remotes]) => Array.from(remotes).filter((remote) => remote !== remoteKey)).sort().map((remote) => `runtime.loadRemote(${JSON.stringify(remote)})`).join(",");
1885
1762
  const importHelper = useSystemImportFallback ? `const __mfImport = (src) =>
1886
1763
  globalThis.System && typeof globalThis.System.import === 'function'
1887
1764
  ? globalThis.System.import(src)
1888
1765
  : import(src);
1889
1766
  ` : "";
1890
1767
  const importExpression = (src) => useSystemImportFallback ? `__mfImport(${JSON.stringify(src)})` : `import(${JSON.stringify(src)})`;
1891
- return `${getRuntimeModuleCacheBootstrapCode()}
1892
- ${importHelper}(async () => {
1893
- const { initHost } = await ${importExpression(initSrc)};
1768
+ const remotePreloads = getNormalizeModuleFederationOptions()?.shareStrategy !== "loaded-first" ? Object.entries(getUsedRemotesMap()).flatMap(([remoteKey, remotes]) => Array.from(remotes).filter((remote) => remote !== remoteKey)).sort().map((remote) => `__mfPreloadRemote(${JSON.stringify(remote)})`).join(",") : "";
1769
+ const preloadBlock = remotePreloads ? `
1894
1770
  const runtime = await initHost();
1771
+ const __mfPreloadRemote = (remote) => {
1772
+ const pendingKey = "__mf_pending__" + remote;
1773
+ if (!__mfModuleCache.remote[pendingKey]) {
1774
+ __mfModuleCache.remote[pendingKey] = runtime.loadRemote(remote)
1775
+ .then((mod) => {
1776
+ __mfModuleCache.remote[remote] = mod;
1777
+ delete __mfModuleCache.remote[pendingKey];
1778
+ return mod;
1779
+ })
1780
+ .catch((error) => {
1781
+ delete __mfModuleCache.remote[pendingKey];
1782
+ throw error;
1783
+ });
1784
+ }
1785
+ return __mfModuleCache.remote[pendingKey];
1786
+ };
1895
1787
  const __mfRemotePreloads = [${remotePreloads}];
1896
- await Promise.all(__mfRemotePreloads);
1788
+ await Promise.allSettled(__mfRemotePreloads);` : `await initHost();`;
1789
+ const importCode = `
1790
+ (async () => {
1791
+ const { initHost } = await ${importExpression(initSrc)};
1792
+ ${preloadBlock}
1897
1793
  })().then(() => ${importExpression(entrySrc)});
1898
1794
  `;
1795
+ return [
1796
+ getRuntimeModuleCacheBootstrapCode(),
1797
+ importHelper,
1798
+ importCode
1799
+ ].join("\n");
1899
1800
  }
1900
1801
  function getSystemBootstrapSource(initSrc, entrySrc) {
1901
1802
  return getBootstrapSource(initSrc, entrySrc, true);
@@ -1910,6 +1811,29 @@ ${importHelper}(async () => {
1910
1811
  function normalizeDevHtmlProxyId(id) {
1911
1812
  return id.replace(/^\0/, "").replace(/^\/@id\//, "").replace(/^__x00__/, "");
1912
1813
  }
1814
+ function normalizeModuleId(id) {
1815
+ return id.split("?")[0].replace(/\\/g, "/");
1816
+ }
1817
+ function resolveProjectId(id) {
1818
+ if (id.startsWith("\0") || id.startsWith("virtual:")) return normalizeModuleId(id);
1819
+ return normalizeModuleId(path$1.isAbsolute(id) ? id : path$1.resolve(viteConfig.root, id));
1820
+ }
1821
+ function addEntryFile(file) {
1822
+ const normalized = normalizeModuleId(file);
1823
+ if (!entryFiles.includes(normalized)) entryFiles.push(normalized);
1824
+ }
1825
+ function addHtmlScriptEntries(htmlPath) {
1826
+ if (!fs$1.existsSync(htmlPath)) return;
1827
+ const htmlContent = fs$1.readFileSync(htmlPath, "utf-8");
1828
+ const scriptRegex = /<script\b(?=[^>]*\btype=["']module["'])(?=[^>]*\bsrc=["']([^"']+)["'])[^>]*>/gi;
1829
+ let match;
1830
+ while ((match = scriptRegex.exec(htmlContent)) !== null) {
1831
+ const scriptSrc = stripQueryAndHash(match[1]);
1832
+ if (/^(?:[a-z]+:)?\/\//i.test(scriptSrc)) continue;
1833
+ addEntryFile(scriptSrc);
1834
+ addEntryFile(scriptSrc.startsWith("/") ? path$1.resolve(viteConfig.root, scriptSrc.slice(1)) : path$1.resolve(path$1.dirname(htmlPath), scriptSrc));
1835
+ }
1836
+ }
1913
1837
  return [{
1914
1838
  name: "add-entry",
1915
1839
  apply: "serve",
@@ -1926,6 +1850,7 @@ ${importHelper}(async () => {
1926
1850
  const relativePath = normalized.startsWith(root + "/") ? normalized.slice(root.length) : "/" + normalized.replace(/^[A-Za-z]:[\\/]/, "");
1927
1851
  devEntryPath = config.base + relativePath.replace(/^\//, "");
1928
1852
  }
1853
+ skipTransformIds = new Set(skipTransformFor.map(resolveProjectId));
1929
1854
  },
1930
1855
  configureServer(server) {
1931
1856
  server.middlewares.use((req, res, next) => {
@@ -1953,7 +1878,8 @@ ${importHelper}(async () => {
1953
1878
  transformIndexHtml: {
1954
1879
  order: "pre",
1955
1880
  handler(c) {
1956
- if (!injectHtml()) return;
1881
+ const shouldWrapEntryHtml = _command === "serve" && inject === "entry" && waitsForInit;
1882
+ if (!injectHtml() && !shouldWrapEntryHtml) return;
1957
1883
  clientInjected = true;
1958
1884
  const base = viteConfig.base.replace(/\/$/, "");
1959
1885
  const stripBase = (p) => base && p.startsWith(base + "/") ? p.slice(base.length) : p;
@@ -1985,24 +1911,30 @@ ${importHelper}(async () => {
1985
1911
  }, {
1986
1912
  name: "add-entry",
1987
1913
  enforce: "post",
1914
+ applyToEnvironment() {
1915
+ return true;
1916
+ },
1917
+ config(config) {
1918
+ patchHashEntryFileNames(config, entryName, fileName);
1919
+ },
1988
1920
  configResolved(config) {
1989
1921
  viteConfig = config;
1990
- const inputOptions = config.build.rollupOptions.input;
1922
+ skipTransformIds = new Set(skipTransformFor.map(resolveProjectId));
1923
+ const ctx = this;
1924
+ const envName = ctx != null && typeof ctx === "object" ? ctx["environment"] : void 0;
1925
+ if (envName?.name && envName.name !== "client") return;
1926
+ const inputOptions = getBuildInput(config);
1991
1927
  if (!inputOptions) htmlFilePath = path$1.resolve(config.root, "index.html");
1992
- else if (typeof inputOptions === "string") entryFiles = [inputOptions];
1993
- else if (Array.isArray(inputOptions)) entryFiles = inputOptions;
1994
- else if (typeof inputOptions === "object") entryFiles = Object.values(inputOptions);
1928
+ else if (typeof inputOptions === "string") entryFiles = [normalizeModuleId(inputOptions)];
1929
+ else if (Array.isArray(inputOptions)) entryFiles = inputOptions.map(normalizeModuleId);
1930
+ else if (typeof inputOptions === "object") entryFiles = Object.values(inputOptions).map((input) => normalizeModuleId(String(input)));
1995
1931
  if (entryFiles.length > 0) htmlFilePath = getFirstHtmlEntryFile(entryFiles);
1996
- if (_command === "serve" && htmlFilePath && fs$1.existsSync(htmlFilePath)) {
1997
- const htmlContent = fs$1.readFileSync(htmlFilePath, "utf-8");
1998
- const scriptRegex = /<script\s+[^>]*src=["']([^"']+)["'][^>]*>/gi;
1999
- let match;
2000
- while ((match = scriptRegex.exec(htmlContent)) !== null) entryFiles.push(match[1]);
2001
- }
1932
+ if (htmlFilePath) addHtmlScriptEntries(htmlFilePath);
2002
1933
  },
2003
1934
  buildStart() {
2004
1935
  if (_command === "serve") return;
2005
1936
  if (skipSvelteKitSsrBuild()) return;
1937
+ if (this.environment?.name === "ssr") return;
2006
1938
  const hasHash = fileName?.includes?.("[hash");
2007
1939
  const emitFileOptions = {
2008
1940
  name: entryName,
@@ -2012,16 +1944,14 @@ ${importHelper}(async () => {
2012
1944
  };
2013
1945
  if (!hasHash) emitFileOptions.fileName = fileName;
2014
1946
  emitFileId = this.emitFile(emitFileOptions);
2015
- if (htmlFilePath && fs$1.existsSync(htmlFilePath)) {
2016
- const htmlContent = fs$1.readFileSync(htmlFilePath, "utf-8");
2017
- const scriptRegex = /<script\s+[^>]*src=["']([^"']+)["'][^>]*>/gi;
2018
- let match;
2019
- while ((match = scriptRegex.exec(htmlContent)) !== null) entryFiles.push(match[1]);
2020
- }
1947
+ if (htmlFilePath) addHtmlScriptEntries(htmlFilePath);
2021
1948
  },
2022
1949
  generateBundle(_options, bundle) {
2023
1950
  if (skipSvelteKitSsrBuild()) return;
2024
1951
  if (!injectHtml()) return;
1952
+ if (!emitFileId) return;
1953
+ const htmlFileNames = Object.keys(bundle).filter((fileName) => fileName.endsWith(".html"));
1954
+ if (htmlFileNames.length === 0) return;
2025
1955
  const file = this.getFileName(emitFileId);
2026
1956
  emittedFileName = file;
2027
1957
  const resolvePath = (htmlFileName) => {
@@ -2043,7 +1973,7 @@ ${importHelper}(async () => {
2043
1973
  return viteConfig.base + file;
2044
1974
  };
2045
1975
  let bootstrapIndex = 0;
2046
- for (const fileName in bundle) if (fileName.endsWith(".html")) {
1976
+ for (const fileName of htmlFileNames) {
2047
1977
  let htmlAsset = bundle[fileName];
2048
1978
  if (htmlAsset.type === "chunk") return;
2049
1979
  let htmlContent = htmlAsset.source.toString() || "";
@@ -2088,6 +2018,11 @@ ${importHelper}(async () => {
2088
2018
  if (skipSvelteKitSsrBuild()) return;
2089
2019
  if (isSvelteKitServerModule(id)) return;
2090
2020
  if (id.includes(ENTRY_BOOTSTRAP_QUERY)) return;
2021
+ if (normalizeModuleId(id).endsWith(".html")) return;
2022
+ if (skipTransformIds.has(resolveProjectId(id))) return;
2023
+ const transformCtx = this;
2024
+ const transformEnv = transformCtx != null && typeof transformCtx === "object" ? transformCtx["environment"] : void 0;
2025
+ if (transformEnv?.name && transformEnv.name !== "client") return;
2091
2026
  const isVinext = hasPackageDependency("vinext");
2092
2027
  if (isVinext && inject === "html" && id.includes("virtual:vite-rsc/remove-duplicate-server-css")) {
2093
2028
  const namespaceReactImport = `import * as React from 'react';`;
@@ -2104,9 +2039,16 @@ ${importHelper}(async () => {
2104
2039
  clientInjected = true;
2105
2040
  return mapCodeToCodeWithSourcemap(injection + code);
2106
2041
  }
2107
- if (injectEntry() && entryFiles.some((file) => id.endsWith(file)) || _command === "serve" && inject === "html" && !isVinext && !clientInjected && !id.startsWith("\0") && !id.includes("node_modules") && /\.(js|ts|mjs|vue|jsx|tsx)(\?|$)/.test(id)) {
2042
+ if (_command === "serve" && inject === "entry" && waitsForInit && !clientInjected && /(?:^|\/)nuxt\/dist\/app\/entry\.js(?:\?|$)/.test(id) && code.includes("vueApp.mount(vueAppRootContainer);")) {
2043
+ clientInjected = true;
2044
+ const injection = `await import(${JSON.stringify(getEntryPath())}).then(({ initHost }) => initHost());\n `;
2045
+ return mapCodeToCodeWithSourcemap(code.replace("vueApp.mount(vueAppRootContainer);", `${injection}vueApp.mount(vueAppRootContainer);`));
2046
+ }
2047
+ const isHydrationEntryFallback = inject === "entry" && entryFiles.length === 0 && (!htmlFilePath || !fs$1.existsSync(htmlFilePath)) && !clientInjected && !id.includes("node_modules") && (id.startsWith("\0") || /\.(js|ts|mjs|vue|jsx|tsx)(\?|$)/.test(id)) && /hydrateRoot|createRoot|ReactDOM\.render/.test(code);
2048
+ const isNuxtClientEntryFallback = _command === "serve" && inject === "entry" && entryFiles.length === 0 && (!htmlFilePath || !fs$1.existsSync(htmlFilePath)) && !clientInjected && !id.includes("node_modules/.vite") && /(?:^|\/)nuxt\/dist\/app\/entry\.async\.js(?:\?|$)/.test(id) && code.includes("entry();");
2049
+ if (injectEntry() && entryFiles.some((file) => resolveProjectId(id) === file) || _command === "serve" && inject === "html" && !isVinext && !clientInjected && !id.startsWith("\0") && !id.includes("node_modules") && /\.(js|ts|mjs|vue|jsx|tsx)(\?|$)/.test(id) || isHydrationEntryFallback || isNuxtClientEntryFallback) {
2108
2050
  clientInjected = true;
2109
- if (!waitsForInit) return mapCodeToCodeWithSourcemap(`import ${JSON.stringify(getEntryPath())};\n` + code);
2051
+ if (!waitsForInit || _command === "serve" && inject === "entry" && isHydrationEntryFallback) return mapCodeToCodeWithSourcemap(`import ${JSON.stringify(getEntryPath())};\n` + code);
2110
2052
  const entrySrc = id.includes("?") ? `${id}&${ENTRY_BOOTSTRAP_QUERY.slice(1)}` : `${id}${ENTRY_BOOTSTRAP_QUERY}`;
2111
2053
  return mapCodeToCodeWithSourcemap(getBootstrapSource(getEntryPath(), entrySrc));
2112
2054
  }
@@ -2158,11 +2100,7 @@ function checkAliasConflicts(options) {
2158
2100
  };
2159
2101
  }
2160
2102
  //#endregion
2161
- //#region src/plugins/pluginDevRemoteHmr.ts
2162
- const REMOTE_HMR_ENDPOINT = "__mf_hmr";
2163
- const REMOTE_HMR_EVENT = "mf:remote-update";
2164
- const REMOTE_HMR_CONNECT_RETRY_DELAY_MS = 1e3;
2165
- const REMOTE_HMR_CONNECT_MAX_RETRIES = 10;
2103
+ //#region src/plugins/hmr/react.ts
2166
2104
  /**
2167
2105
  * Proxy module served for `/@react-refresh` on MF remote dev servers.
2168
2106
  * Delegates to the host page's RefreshRuntime instance via
@@ -2182,6 +2120,112 @@ const REACT_REFRESH_PROXY_MODULE = [
2182
2120
  `export const __hmr_import = __rt.__hmr_import;`,
2183
2121
  `export default __rt.default || __rt;`
2184
2122
  ].join("\n");
2123
+ const reactAdapter = {
2124
+ name: "react",
2125
+ pluginNames: ["vite:react-refresh", "vite:react-swc:refresh"],
2126
+ remote: { configureServer({ server }) {
2127
+ server.middlewares.use((req, res, next) => {
2128
+ if (req.url?.replace(/\?.*$/, "") !== "/@react-refresh") return next();
2129
+ res.setHeader("Content-Type", "application/javascript; charset=utf-8");
2130
+ res.setHeader("Access-Control-Allow-Origin", "*");
2131
+ res.end(REACT_REFRESH_PROXY_MODULE);
2132
+ });
2133
+ } }
2134
+ };
2135
+ //#endregion
2136
+ //#region src/plugins/hmr/vue.ts
2137
+ /**
2138
+ * In dev mode each Vite dev server serves its own copy of Vue
2139
+ * (`/node_modules/.vite/deps/vue.js`). When a remote module is loaded into the
2140
+ * host page, the remote's Vue copy evaluates and runs:
2141
+ *
2142
+ * globalThis.__VUE_HMR_RUNTIME__ = createHotReloadAPI()
2143
+ *
2144
+ * which silently overwrites the host's runtime. After that, `createRecord` for
2145
+ * host components lives in the orphaned first runtime, but `reload(hmrId, ...)`
2146
+ * goes through the second runtime — the lookup misses and HMR stops working.
2147
+ *
2148
+ * This guard pins `__VUE_HMR_RUNTIME__` to the first writer (the host's Vue)
2149
+ * via a property trap on `globalThis`. Subsequent writes from remote-side Vue
2150
+ * copies are silently dropped. Must execute before any Vue module loads —
2151
+ * injected as a plain (non-module) script at `head-prepend`.
2152
+ *
2153
+ * `singleton: true` in `shared` is not sufficient: in dev mode MF's share-scope
2154
+ * does not actually dedupe Vue across dev servers, so without this guard the
2155
+ * last-loaded copy wins.
2156
+ */
2157
+ const VUE_HMR_RUNTIME_GUARD_SCRIPT = `
2158
+ (function () {
2159
+ var h = null;
2160
+ Object.defineProperty(globalThis, '__VUE_HMR_RUNTIME__', {
2161
+ get: function () { return h; },
2162
+ set: function (v) { if (h === null) h = v; },
2163
+ configurable: true,
2164
+ enumerable: true,
2165
+ });
2166
+ })();`;
2167
+ /**
2168
+ * `@vitejs/plugin-vue` derives an SFC's `__hmrId` from a hash of the file path
2169
+ * relative to Vite's `root`. With module federation, host and remote are
2170
+ * separate Vite projects with independent roots — so an SFC at `src/App.vue`
2171
+ * in both will hash to the same id. Once both copies of Vue collapse onto the
2172
+ * shared `__VUE_HMR_RUNTIME__` (see the guard above), the host's instance and
2173
+ * the remote's instance both register under that single id. A remote-only
2174
+ * file change then calls `rerender(id, newRender)`, which iterates *every*
2175
+ * instance under that id — including the host one — and applies the remote's
2176
+ * render function to the host instance. The host's `setupState` doesn't have
2177
+ * the remote's bindings, so the render throws and Vue falls back to a full
2178
+ * reload required warning.
2179
+ *
2180
+ * Fix: rewrite the remote's emitted `__hmrId` literal to be prefixed with the
2181
+ * federation `name`, so the remote's instances live under a distinct key. The
2182
+ * accept callback emitted by plugin-vue reads `_sfc_main.__hmrId` /
2183
+ * `updated.__hmrId` at runtime, so rewriting the literal once is enough.
2184
+ */
2185
+ const SFC_HMR_ID_LITERAL_RE = /(\.__hmrId\s*=\s*["'`])([^"'`]+)(["'`])/g;
2186
+ function rewriteSfcHmrId(code, federationName) {
2187
+ let matched = false;
2188
+ return {
2189
+ code: code.replace(SFC_HMR_ID_LITERAL_RE, (_match, prefix, id, suffix) => {
2190
+ matched = true;
2191
+ if (id.startsWith(`${federationName}-`)) return `${prefix}${id}${suffix}`;
2192
+ return `${prefix}${federationName}-${id}${suffix}`;
2193
+ }),
2194
+ matched
2195
+ };
2196
+ }
2197
+ let pluginVueRegressionWarned = false;
2198
+ function warnPluginVueRegression() {
2199
+ if (pluginVueRegressionWarned) return;
2200
+ pluginVueRegressionWarned = true;
2201
+ mfWarn("Detected a Vue SFC module that calls `__VUE_HMR_RUNTIME__.createRecord(` but no `.__hmrId = \"...\"` literal could be rewritten. @vitejs/plugin-vue may have changed its output format — without the rewrite, host and remote SFCs that share a path will collide on the shared HMR runtime. Please report this to @module-federation/vite.");
2202
+ }
2203
+ const vueAdapter = {
2204
+ name: "vue",
2205
+ pluginNames: ["vite:vue", "vite:vue-jsx"],
2206
+ host: { transformIndexHtml() {
2207
+ return [{
2208
+ tag: "script",
2209
+ children: VUE_HMR_RUNTIME_GUARD_SCRIPT,
2210
+ injectTo: "head-prepend"
2211
+ }];
2212
+ } },
2213
+ remote: { transform(code, _id, ctx) {
2214
+ if (!code.includes("__VUE_HMR_RUNTIME__.createRecord(")) return;
2215
+ const { code: rewritten, matched } = rewriteSfcHmrId(code, ctx.options.name);
2216
+ if (!matched) {
2217
+ warnPluginVueRegression();
2218
+ return;
2219
+ }
2220
+ return rewritten === code ? void 0 : rewritten;
2221
+ } }
2222
+ };
2223
+ //#endregion
2224
+ //#region src/plugins/hmr/fullReload.ts
2225
+ const REMOTE_HMR_ENDPOINT = "__mf_hmr";
2226
+ const REMOTE_HMR_EVENT = "mf:remote-update";
2227
+ const REMOTE_HMR_CONNECT_RETRY_DELAY_MS = 1e3;
2228
+ const REMOTE_HMR_CONNECT_MAX_RETRIES = 10;
2185
2229
  function getBasePath$1(base) {
2186
2230
  if (!base) return "/";
2187
2231
  if (base.startsWith("http://") || base.startsWith("https://")) try {
@@ -2200,9 +2244,6 @@ function getHmrWsPath(base, hmrPath) {
2200
2244
  if (!normalizedPath || normalizedPath === "/") return normalizedBase;
2201
2245
  return `${normalizedBase.endsWith("/") ? normalizedBase.slice(0, -1) : normalizedBase}/${normalizedPath.startsWith("/") ? normalizedPath.slice(1) : normalizedPath}`;
2202
2246
  }
2203
- function shouldIgnoreFile(file, options) {
2204
- 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/") || file.includes("\\.mf\\") || file.includes("/mf-manifest.json") || file.includes("\\mf-manifest.json") || file.includes("/mf-stats.json") || file.includes("\\mf-stats.json");
2205
- }
2206
2247
  function getRemoteHmrWsUrl(server) {
2207
2248
  const hmr = server.config.server.hmr;
2208
2249
  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}`;
@@ -2241,466 +2282,275 @@ function getStringPreview(value, max = 180) {
2241
2282
  } catch {}
2242
2283
  return rawValue.slice(0, max);
2243
2284
  }
2244
- function isRemoteHmrEnabled(dev) {
2245
- return typeof dev === "object" && dev !== null && !!dev.remoteHmr;
2285
+ /**
2286
+ * Installs the `/__mf_hmr` metadata endpoint on a remote dev server. The host
2287
+ * fetches this to discover the remote's HMR WebSocket URL before opening a
2288
+ * Node-to-Node relay socket. Always installed on remotes when `remoteHmr` is
2289
+ * enabled — under `'native'` strategy the endpoint is unused but harmless;
2290
+ * under `'full-reload'` it's the discovery hop for the host relay.
2291
+ */
2292
+ function setupRemoteMetadataEndpoint(server, options) {
2293
+ const endpointPath = getRemoteHmrPath(server.config.base);
2294
+ const wsUrl = getRemoteHmrWsUrl(server);
2295
+ server.middlewares.use((req, res, next) => {
2296
+ if (req.url?.replace(/\?.*/, "") !== endpointPath) {
2297
+ next();
2298
+ return;
2299
+ }
2300
+ res.setHeader("Content-Type", "application/json");
2301
+ res.setHeader("Access-Control-Allow-Origin", "*");
2302
+ res.end(JSON.stringify({
2303
+ remote: options.name,
2304
+ event: REMOTE_HMR_EVENT,
2305
+ wsUrl
2306
+ }));
2307
+ });
2246
2308
  }
2247
2309
  /**
2248
- * Detects whether the Vite plugin pipeline includes a framework with
2249
- * cross-federation HMR support (a shared runtime proxy that works
2250
- * across module federation boundaries).
2310
+ * Installs file-watcher broadcasts on a remote dev server. Every non-ignored
2311
+ * change/add/unlink emits a `mf:remote-update` custom event on the remote's
2312
+ * own WS channel. The host relay (see `setupHostFullReloadRelay`) listens
2313
+ * for these events and triggers a host-side full reload in response.
2251
2314
  *
2252
- * Currently only React is supported via the shared /@react-refresh proxy.
2315
+ * Only called under the `'full-reload'` strategy.
2253
2316
  */
2254
- function hasCrossFederationHmr(plugins) {
2255
- const supportedPlugins = ["vite:react-refresh", "vite:react-swc:refresh"];
2256
- return plugins.some((p) => supportedPlugins.includes(p.name));
2257
- }
2258
- function resolveHmrStrategy(dev, plugins) {
2259
- if (typeof dev === "object" && dev !== null && dev.remoteHmr === "full-reload") return "full-reload";
2260
- return hasCrossFederationHmr(plugins) ? "native" : "full-reload";
2261
- }
2262
- function pluginDevRemoteHmr(options) {
2263
- return {
2264
- name: "module-federation-dev-remote-hmr",
2265
- apply: "serve",
2266
- configureServer(server) {
2267
- if (!isRemoteHmrEnabled(options.dev)) return;
2268
- const isRemote = Object.keys(options.exposes).length > 0;
2269
- const isHost = Object.keys(options.remotes).length > 0;
2270
- const strategy = resolveHmrStrategy(options.dev, server.config.plugins);
2271
- if (isRemote) {
2272
- const endpointPath = getRemoteHmrPath(server.config.base);
2273
- const wsUrl = getRemoteHmrWsUrl(server);
2274
- server.middlewares.use((req, res, next) => {
2275
- if (req.url?.replace(/\?.*$/, "") !== "/@react-refresh") return next();
2276
- res.setHeader("Content-Type", "application/javascript; charset=utf-8");
2277
- res.setHeader("Access-Control-Allow-Origin", "*");
2278
- res.end(REACT_REFRESH_PROXY_MODULE);
2279
- });
2280
- server.middlewares.use((req, res, next) => {
2281
- if (req.url?.replace(/\?.*/, "") !== endpointPath) {
2282
- next();
2283
- return;
2284
- }
2285
- res.setHeader("Content-Type", "application/json");
2286
- res.setHeader("Access-Control-Allow-Origin", "*");
2287
- res.end(JSON.stringify({
2288
- remote: options.name,
2289
- event: REMOTE_HMR_EVENT,
2290
- wsUrl
2291
- }));
2292
- });
2293
- const broadcast = (file) => {
2294
- if (strategy === "native") return;
2295
- if (shouldIgnoreFile(file, options)) return;
2296
- server.ws.send({
2297
- type: "custom",
2298
- event: REMOTE_HMR_EVENT,
2299
- data: {
2300
- remote: options.name,
2301
- file,
2302
- ts: Date.now()
2303
- }
2304
- });
2305
- };
2306
- server.watcher.on("change", broadcast);
2307
- server.watcher.on("add", broadcast);
2308
- server.watcher.on("unlink", broadcast);
2309
- server.httpServer?.once("close", () => {
2310
- server.watcher.off("change", broadcast);
2311
- server.watcher.off("add", broadcast);
2312
- server.watcher.off("unlink", broadcast);
2313
- });
2317
+ function setupRemoteBroadcast(server, options) {
2318
+ const broadcast = (file) => {
2319
+ if (shouldIgnoreFile(file, options)) return;
2320
+ server.ws.send({
2321
+ type: "custom",
2322
+ event: REMOTE_HMR_EVENT,
2323
+ data: {
2324
+ remote: options.name,
2325
+ file,
2326
+ ts: Date.now()
2314
2327
  }
2315
- if (isHost) {
2316
- const connections = [];
2317
- const reconnectTimers = /* @__PURE__ */ new Map();
2318
- let isTearingDown = false;
2319
- const clearReconnectTimer = (remoteName) => {
2320
- const timer = reconnectTimers.get(remoteName);
2321
- if (!timer) return;
2322
- clearTimeout(timer);
2323
- reconnectTimers.delete(remoteName);
2324
- };
2325
- const scheduleReconnect = (remoteName, remote, attempt, reason) => {
2326
- if (isTearingDown) return;
2327
- if (attempt >= REMOTE_HMR_CONNECT_MAX_RETRIES) {
2328
- mfWarn(`Remote "${remoteName}" full HMR reconnect skipped after ${REMOTE_HMR_CONNECT_MAX_RETRIES} attempts: ${reason}`);
2329
- return;
2330
- }
2331
- clearReconnectTimer(remoteName);
2332
- const timer = setTimeout(() => {
2333
- reconnectTimers.delete(remoteName);
2334
- connectRemote(remoteName, remote, attempt + 1);
2335
- }, REMOTE_HMR_CONNECT_RETRY_DELAY_MS);
2336
- reconnectTimers.set(remoteName, timer);
2337
- };
2338
- const connectRemote = async (remoteName, remote, attempt = 0) => {
2339
- if (isTearingDown) return;
2340
- const endpoint = getRemoteHmrEndpoint(remote.entry, server);
2341
- if (!endpoint) {
2342
- mfWarn(`Failed to build HMR endpoint URL for remote "${remoteName}"`);
2343
- return;
2344
- }
2345
- try {
2346
- const metadataResponse = await fetch(endpoint);
2347
- if (!metadataResponse.ok) {
2348
- mfWarn(`Failed to fetch remote HMR metadata from "${remoteName}": ${metadataResponse.status}`);
2349
- scheduleReconnect(remoteName, remote, attempt, `HTTP ${metadataResponse.status}`);
2350
- return;
2351
- }
2352
- const metadata = await metadataResponse.json();
2353
- if (metadata.event !== REMOTE_HMR_EVENT || !metadata.wsUrl) {
2354
- mfWarn(`Remote "${remoteName}" returned unexpected HMR metadata shape`);
2355
- return;
2356
- }
2357
- const ws = new WebSocket(metadata.wsUrl, "vite-hmr");
2358
- ws.onmessage = (rawEvent) => {
2359
- if (strategy === "native") return;
2360
- const message = parseRemoteHmrMessage(rawEvent.data);
2361
- if (!message || message.event !== REMOTE_HMR_EVENT) return;
2362
- server.ws.send({ type: "full-reload" });
2363
- };
2364
- ws.onopen = () => clearReconnectTimer(remoteName);
2365
- ws.onerror = (error) => mfWarn(`Remote HMR socket error for "${remoteName}":`, error);
2366
- ws.onclose = () => scheduleReconnect(remoteName, remote, attempt, "socket closed");
2367
- connections.push(ws);
2368
- } catch (error) {
2369
- mfWarn(`Failed to connect remote HMR for "${remoteName}" on attempt ${attempt + 1}: ${getStringPreview(error)}`);
2370
- scheduleReconnect(remoteName, remote, attempt, getStringPreview(error));
2371
- }
2372
- };
2373
- const teardown = () => {
2374
- isTearingDown = true;
2375
- reconnectTimers.forEach((timer) => clearTimeout(timer));
2376
- reconnectTimers.clear();
2377
- connections.forEach((connection) => {
2378
- if (connection.readyState !== connection.CLOSING && connection.readyState !== connection.CLOSED) connection.close();
2379
- });
2380
- connections.length = 0;
2381
- };
2382
- for (const [remoteName, remote] of Object.entries(options.remotes)) connectRemote(remoteName, remote);
2383
- const triggerHostReload = (file) => {
2384
- if (strategy === "native") return;
2385
- if (shouldIgnoreFile(file, options)) return;
2386
- server.ws.send({ type: "full-reload" });
2387
- };
2388
- server.watcher.on("change", triggerHostReload);
2389
- server.watcher.on("add", triggerHostReload);
2390
- server.watcher.on("unlink", triggerHostReload);
2391
- server.httpServer?.once("close", teardown);
2392
- }
2393
- }
2394
- };
2395
- }
2396
- //#endregion
2397
- //#region src/plugins/pluginDts.ts
2398
- const DEFAULT_DEV_OPTIONS = {
2399
- disableLiveReload: true,
2400
- disableHotTypesReload: false,
2401
- disableDynamicRemoteTypeHints: false
2402
- };
2403
- const DYNAMIC_HINTS_PLUGIN = "@module-federation/dts-plugin/dynamic-remote-type-hints-plugin";
2404
- const getIPv4 = () => process.env["FEDERATION_IPV4"] || "127.0.0.1";
2405
- const DEV_TYPES_FOLDER = ".dev-server";
2406
- const DEFAULT_PUBLIC_TYPES_FOLDER = "@mf-types";
2407
- const forkDevWorkerPath = __require.resolve("@module-federation/dts-plugin/dist/fork-dev-worker.js");
2408
- var DevWorker = class {
2409
- worker = rpc.createRpcWorker(forkDevWorkerPath, {}, void 0, false);
2410
- constructor(options) {
2411
- this.worker.connect(options);
2412
- }
2413
- update() {
2414
- this.worker.process?.send?.({
2415
- type: rpc.RpcGMCallTypes.CALL,
2416
- id: this.worker.id,
2417
- args: [void 0, "update"]
2418
2328
  });
2419
- }
2420
- exit() {
2421
- this.worker.terminate();
2422
- }
2423
- };
2424
- const normalizeDevOptions = (dev) => {
2425
- if (dev === false) return false;
2426
- if (dev === true || typeof dev === "undefined") return { ...DEFAULT_DEV_OPTIONS };
2427
- return {
2428
- ...DEFAULT_DEV_OPTIONS,
2429
- ...dev
2430
2329
  };
2431
- };
2432
- const buildDtsModuleFederationConfig = (options) => {
2433
- const exposes = {};
2434
- Object.entries(options.exposes).forEach(([key, value]) => {
2435
- if (value.import) exposes[key] = value.import;
2330
+ server.watcher.on("change", broadcast);
2331
+ server.watcher.on("add", broadcast);
2332
+ server.watcher.on("unlink", broadcast);
2333
+ server.httpServer?.once("close", () => {
2334
+ server.watcher.off("change", broadcast);
2335
+ server.watcher.off("add", broadcast);
2336
+ server.watcher.off("unlink", broadcast);
2436
2337
  });
2437
- const remotes = {};
2438
- Object.entries(options.remotes).forEach(([key, remote]) => {
2439
- if (!remote.entry) return;
2440
- remotes[key] = `${remote.entryGlobalName?.startsWith("http") || remote.entryGlobalName?.includes(".json") ? remote.name || key : remote.entryGlobalName || remote.name || key}@${remote.entry}`;
2441
- });
2442
- return {
2443
- ...options,
2444
- exposes,
2445
- remotes
2446
- };
2447
- };
2448
- const resolveOutputDir = (config) => {
2449
- const { outDir } = config.build;
2450
- if (path$1.isAbsolute(outDir)) return path$1.relative(config.root, outDir);
2451
- return outDir;
2452
- };
2453
- const ensureRuntimePlugin = (options, pluginId) => {
2454
- if (!options.runtimePlugins.some((plugin) => {
2455
- if (typeof plugin === "string") return plugin === pluginId;
2456
- return plugin[0] === pluginId;
2457
- })) options.runtimePlugins.push(pluginId);
2458
- };
2459
- const getExposeImportPaths = (options) => {
2460
- return Object.values(options.exposes).map((value) => {
2461
- return value.import;
2462
- }).filter((value) => Boolean(value));
2463
- };
2464
- const usesVueSfcExposes = (options) => {
2465
- return getExposeImportPaths(options).some((value) => value.endsWith(".vue"));
2466
- };
2467
- const resolveDtsPluginOptions = (dts, options, context) => {
2468
- if (dts === false) return false;
2469
- const inferredGenerateTypesDefaults = { generateAPITypes: true };
2470
- if (usesVueSfcExposes(options) && hasPackageDependency("vue-tsc", context)) inferredGenerateTypesDefaults.compilerInstance = "vue-tsc";
2471
- if (dts === true || typeof dts === "undefined") return { generateTypes: inferredGenerateTypesDefaults };
2472
- const generateTypes = dts.generateTypes;
2473
- return {
2474
- ...dts,
2475
- generateTypes: generateTypes === false ? false : {
2476
- ...inferredGenerateTypesDefaults,
2477
- ...generateTypes === true || typeof generateTypes === "undefined" ? {} : generateTypes
2478
- }
2479
- };
2480
- };
2481
- const getBasePath = (base) => {
2482
- if (base.startsWith("http://") || base.startsWith("https://")) return new URL(base).pathname.replace(/\/$/, "") || "/";
2483
- return base.replace(/\/$/, "") || "/";
2484
- };
2485
- const joinBaseAndAsset = (base, assetFileName) => {
2486
- const basePath = getBasePath(base);
2487
- return `${basePath === "/" ? "" : basePath}/${assetFileName}`.replace(/\/{2,}/g, "/");
2488
- };
2489
- const getDevDtsAssetPaths = (options) => {
2490
- const { outputDir, publicTypesFolder, root, base } = options;
2491
- return {
2492
- apiFilePath: path$1.resolve(root, outputDir, `${DEV_TYPES_FOLDER}.d.ts`),
2493
- apiRequestPath: joinBaseAndAsset(base, `${publicTypesFolder}.d.ts`),
2494
- zipFilePath: path$1.resolve(root, outputDir, `${DEV_TYPES_FOLDER}.zip`),
2495
- zipRequestPath: joinBaseAndAsset(base, `${publicTypesFolder}.zip`)
2338
+ }
2339
+ /**
2340
+ * Installs the host-side full-reload relay. For each configured remote:
2341
+ * 1. Fetches the remote's `/__mf_hmr` metadata to get its WS URL.
2342
+ * 2. Opens a Node-to-Node WebSocket to that URL.
2343
+ * 3. On any `mf:remote-update` message, broadcasts `{ type: 'full-reload' }`
2344
+ * to the host's own browser-facing WS.
2345
+ *
2346
+ * Also reloads on local host file changes. Retries failed connections up to
2347
+ * `REMOTE_HMR_CONNECT_MAX_RETRIES` times with a fixed delay.
2348
+ *
2349
+ * Only called under the `'full-reload'` strategy.
2350
+ */
2351
+ function setupHostFullReloadRelay(server, options) {
2352
+ const connections = [];
2353
+ const reconnectTimers = /* @__PURE__ */ new Map();
2354
+ let isTearingDown = false;
2355
+ const clearReconnectTimer = (remoteName) => {
2356
+ const timer = reconnectTimers.get(remoteName);
2357
+ if (!timer) return;
2358
+ clearTimeout(timer);
2359
+ reconnectTimers.delete(remoteName);
2496
2360
  };
2497
- };
2498
- const createDevDtsAssetMiddleware = (assetPaths) => {
2499
- return (req, res, next) => {
2500
- const requestPath = req.url?.split("?")[0];
2501
- const isZipRequest = requestPath === assetPaths.zipRequestPath;
2502
- const isApiRequest = requestPath === assetPaths.apiRequestPath;
2503
- if (!isZipRequest && !isApiRequest) {
2504
- next();
2361
+ const scheduleReconnect = (remoteName, remote, attempt, reason) => {
2362
+ if (isTearingDown) return;
2363
+ if (attempt >= REMOTE_HMR_CONNECT_MAX_RETRIES) {
2364
+ mfWarn(`Remote "${remoteName}" full HMR reconnect skipped after ${REMOTE_HMR_CONNECT_MAX_RETRIES} attempts: ${reason}`);
2505
2365
  return;
2506
2366
  }
2507
- const filePath = isZipRequest ? assetPaths.zipFilePath : assetPaths.apiFilePath;
2508
- if (!fs.existsSync(filePath)) {
2509
- res.statusCode = 404;
2510
- res.end();
2367
+ clearReconnectTimer(remoteName);
2368
+ const timer = setTimeout(() => {
2369
+ reconnectTimers.delete(remoteName);
2370
+ connectRemote(remoteName, remote, attempt + 1);
2371
+ }, REMOTE_HMR_CONNECT_RETRY_DELAY_MS);
2372
+ reconnectTimers.set(remoteName, timer);
2373
+ };
2374
+ const connectRemote = async (remoteName, remote, attempt = 0) => {
2375
+ if (isTearingDown) return;
2376
+ const endpoint = getRemoteHmrEndpoint(remote.entry, server);
2377
+ if (!endpoint) {
2378
+ mfWarn(`Failed to build HMR endpoint URL for remote "${remoteName}"`);
2511
2379
  return;
2512
2380
  }
2513
- res.statusCode = 200;
2514
- res.setHeader("Content-Type", isZipRequest ? "application/x-gzip" : "application/typescript");
2515
- if (req.method === "HEAD") {
2516
- res.end();
2517
- return;
2381
+ try {
2382
+ const metadataResponse = await fetch(endpoint);
2383
+ if (!metadataResponse.ok) {
2384
+ mfWarn(`Failed to fetch remote HMR metadata from "${remoteName}": ${metadataResponse.status}`);
2385
+ scheduleReconnect(remoteName, remote, attempt, `HTTP ${metadataResponse.status}`);
2386
+ return;
2387
+ }
2388
+ const metadata = await metadataResponse.json();
2389
+ if (metadata.event !== "mf:remote-update" || !metadata.wsUrl) {
2390
+ mfWarn(`Remote "${remoteName}" returned unexpected HMR metadata shape`);
2391
+ return;
2392
+ }
2393
+ const ws = new WebSocket(metadata.wsUrl, "vite-hmr");
2394
+ ws.onmessage = (rawEvent) => {
2395
+ const message = parseRemoteHmrMessage(rawEvent.data);
2396
+ if (!message || message.event !== "mf:remote-update") return;
2397
+ server.ws.send({ type: "full-reload" });
2398
+ };
2399
+ ws.onopen = () => clearReconnectTimer(remoteName);
2400
+ ws.onerror = (error) => mfWarn(`Remote HMR socket error for "${remoteName}":`, error);
2401
+ ws.onclose = () => scheduleReconnect(remoteName, remote, attempt, "socket closed");
2402
+ connections.push(ws);
2403
+ } catch (error) {
2404
+ mfWarn(`Failed to connect remote HMR for "${remoteName}" on attempt ${attempt + 1}: ${getStringPreview(error)}`);
2405
+ scheduleReconnect(remoteName, remote, attempt, getStringPreview(error));
2518
2406
  }
2519
- const stream = fs.createReadStream(filePath);
2520
- stream.on("error", () => {
2521
- if (!res.headersSent) res.statusCode = 500;
2522
- res.end();
2523
- });
2524
- res.on("close", () => {
2525
- stream.destroy();
2407
+ };
2408
+ const teardown = () => {
2409
+ isTearingDown = true;
2410
+ reconnectTimers.forEach((timer) => clearTimeout(timer));
2411
+ reconnectTimers.clear();
2412
+ connections.forEach((connection) => {
2413
+ if (connection.readyState !== connection.CLOSING && connection.readyState !== connection.CLOSED) connection.close();
2526
2414
  });
2527
- stream.pipe(res);
2415
+ connections.length = 0;
2528
2416
  };
2529
- };
2530
- const normalizeDevDtsOptions = (dts, context) => {
2531
- return normalizeOptions(isTSProject(dts, context), {
2532
- generateTypes: { compileInChildProcess: true },
2533
- consumeTypes: { consumeAPITypes: true },
2534
- extraOptions: {},
2535
- displayErrorInTerminal: typeof dts === "object" && dts ? dts.displayErrorInTerminal : void 0
2536
- }, "mfOptions.dts")(dts);
2537
- };
2538
- const logDtsError = (error, dtsOptions) => {
2539
- if (dtsOptions === false) return;
2540
- if (typeof dtsOptions === "object" && dtsOptions && dtsOptions.displayErrorInTerminal === false) return;
2541
- mfError(error);
2542
- };
2543
- function pluginDts(options) {
2544
- if (options.dts === false) return [];
2545
- const baseDtsModuleFederationConfig = buildDtsModuleFederationConfig(options);
2546
- const getDtsModuleFederationConfig = (context) => ({
2547
- ...baseDtsModuleFederationConfig,
2548
- dts: resolveDtsPluginOptions(options.dts, options, context)
2417
+ for (const [remoteName, remote] of Object.entries(options.remotes)) connectRemote(remoteName, remote);
2418
+ const triggerHostReload = (file) => {
2419
+ if (shouldIgnoreFile(file, options)) return;
2420
+ server.ws.send({ type: "full-reload" });
2421
+ };
2422
+ server.watcher.on("change", triggerHostReload);
2423
+ server.watcher.on("add", triggerHostReload);
2424
+ server.watcher.on("unlink", triggerHostReload);
2425
+ server.httpServer?.once("close", teardown);
2426
+ }
2427
+ //#endregion
2428
+ //#region src/plugins/pluginDevRemoteHmr.ts
2429
+ /**
2430
+ * Clears the federation runtime's `moduleCache` on every Vite `vite:beforeUpdate`
2431
+ * event. Without this, after a remote SFC change Vite would patch the in-memory
2432
+ * component, but `loadRemote()` would still return the cached stale module on
2433
+ * the next call (e.g. after route navigation), making the patched version
2434
+ * effectively unreachable.
2435
+ */
2436
+ const FEDERATION_MODULE_CACHE_CLEAR_SCRIPT = `
2437
+ if (import.meta.hot) {
2438
+ import.meta.hot.on('vite:beforeUpdate', function () {
2439
+ try {
2440
+ var f = globalThis.__FEDERATION__ || globalThis.__VMOK__;
2441
+ if (!f || !f.__INSTANCES__) return;
2442
+ for (var i = 0; i < f.__INSTANCES__.length; i++) {
2443
+ if (f.__INSTANCES__[i] && f.__INSTANCES__[i].moduleCache)
2444
+ f.__INSTANCES__[i].moduleCache.clear();
2445
+ }
2446
+ } catch (e) {}
2447
+ });
2448
+ }`;
2449
+ const HMR_ADAPTERS = [reactAdapter, vueAdapter];
2450
+ function resolveAdapters(plugins) {
2451
+ const pluginNames = new Set(plugins.map((p) => p.name));
2452
+ return HMR_ADAPTERS.filter((adapter) => adapter.pluginNames.some((name) => pluginNames.has(name)));
2453
+ }
2454
+ function hasCrossFederationHmr(plugins) {
2455
+ return resolveAdapters(plugins).length > 0;
2456
+ }
2457
+ function isRemoteHmrEnabled(dev) {
2458
+ return typeof dev === "object" && dev !== null && !!dev.remoteHmr;
2459
+ }
2460
+ /**
2461
+ * `'native'` — a matched framework adapter owns HMR through Vite's native
2462
+ * channel (e.g. React Fast Refresh via the `/@react-refresh` proxy, Vue's
2463
+ * patched `__VUE_HMR_RUNTIME__`). The broadcast/relay path stays idle.
2464
+ *
2465
+ * `'full-reload'` — no adapter matched, or the user explicitly opted in with
2466
+ * `remoteHmr: 'full-reload'` to bypass adapters: the plugin's broadcast/relay
2467
+ * machinery triggers a page reload on every remote file change.
2468
+ */
2469
+ function resolveHmrStrategy(dev, plugins) {
2470
+ if (typeof dev === "object" && dev !== null && dev.remoteHmr === "full-reload") return "full-reload";
2471
+ return hasCrossFederationHmr(plugins) ? "native" : "full-reload";
2472
+ }
2473
+ function shouldIgnoreFile(file, options) {
2474
+ 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/") || file.includes("\\.mf\\") || file.includes("/mf-manifest.json") || file.includes("\\mf-manifest.json") || file.includes("/mf-stats.json") || file.includes("\\mf-stats.json");
2475
+ }
2476
+ function collectHostTags(server, options, adapters) {
2477
+ const ctx = {
2478
+ server,
2479
+ options
2480
+ };
2481
+ const tags = [];
2482
+ for (const adapter of adapters) {
2483
+ const adapterTags = adapter.host?.transformIndexHtml?.(ctx);
2484
+ if (adapterTags) tags.push(...adapterTags);
2485
+ }
2486
+ tags.push({
2487
+ tag: "script",
2488
+ attrs: { type: "module" },
2489
+ children: FEDERATION_MODULE_CACHE_CLEAR_SCRIPT,
2490
+ injectTo: "head"
2549
2491
  });
2550
- let resolvedConfig;
2551
- let devWorker;
2552
- let normalizedDevOptions;
2553
- let hasGeneratedBundle = false;
2554
- return [{
2555
- name: "module-federation-dts-dev",
2492
+ return tags;
2493
+ }
2494
+ function pluginDevRemoteHmr(options) {
2495
+ const isHost = Object.keys(options.remotes).length > 0;
2496
+ const isRemote = Object.keys(options.exposes).length > 0;
2497
+ let adapters = [];
2498
+ let strategy = "full-reload";
2499
+ return {
2500
+ name: "module-federation-dev-remote-hmr",
2556
2501
  apply: "serve",
2557
- config(config) {
2558
- normalizedDevOptions = normalizeDevOptions(options.dev);
2559
- if (!normalizedDevOptions) return;
2560
- if (normalizedDevOptions.disableDynamicRemoteTypeHints) return;
2561
- ensureRuntimePlugin(options, DYNAMIC_HINTS_PLUGIN);
2562
- const define = config.define ? { ...config.define } : {};
2563
- if (!("FEDERATION_IPV4" in define)) define.FEDERATION_IPV4 = JSON.stringify(getIPv4());
2564
- config.define = define;
2565
- },
2566
2502
  configResolved(config) {
2567
- resolvedConfig = config;
2503
+ adapters = resolveAdapters(config.plugins);
2504
+ strategy = resolveHmrStrategy(options.dev, config.plugins);
2568
2505
  },
2569
2506
  configureServer(server) {
2570
- if (!normalizedDevOptions || !resolvedConfig) return;
2571
- const devOptions = normalizedDevOptions;
2572
- if (devOptions.disableDynamicRemoteTypeHints && devOptions.disableHotTypesReload && devOptions.disableLiveReload) return;
2573
- if (!options.name) throw createModuleFederationError("name is required if you want to enable dev server!");
2574
- const outputDir = resolveOutputDir(resolvedConfig);
2575
- const dtsModuleFederationConfig = getDtsModuleFederationConfig(resolvedConfig.root);
2576
- const normalizedDtsOptions = normalizeDevDtsOptions(dtsModuleFederationConfig.dts, resolvedConfig.root);
2577
- if (typeof normalizedDtsOptions !== "object") return;
2578
- const normalizedGenerateTypes = normalizeOptions(Boolean(normalizedDtsOptions), { compileInChildProcess: true }, "mfOptions.dts.generateTypes")(normalizedDtsOptions.generateTypes);
2579
- const remote = normalizedGenerateTypes === false ? void 0 : {
2580
- implementation: normalizedDtsOptions.implementation,
2581
- context: resolvedConfig.root,
2582
- outputDir,
2583
- moduleFederationConfig: { ...dtsModuleFederationConfig },
2584
- hostRemoteTypesFolder: normalizedGenerateTypes.typesFolder || DEFAULT_PUBLIC_TYPES_FOLDER,
2585
- ...normalizedGenerateTypes,
2586
- typesFolder: DEV_TYPES_FOLDER
2587
- };
2588
- if (remote) server.middlewares.use(createDevDtsAssetMiddleware(getDevDtsAssetPaths({
2589
- outputDir,
2590
- publicTypesFolder: remote.hostRemoteTypesFolder || DEFAULT_PUBLIC_TYPES_FOLDER,
2591
- root: resolvedConfig.root,
2592
- base: resolvedConfig.base
2593
- })));
2594
- if (remote && !remote.tsConfigPath && normalizedDtsOptions.tsConfigPath) remote.tsConfigPath = normalizedDtsOptions.tsConfigPath;
2595
- const normalizedConsumeTypes = normalizeOptions(Boolean(normalizedDtsOptions), { consumeAPITypes: true }, "mfOptions.dts.consumeTypes")(normalizedDtsOptions.consumeTypes);
2596
- const host = normalizedConsumeTypes === false ? void 0 : {
2597
- implementation: normalizedDtsOptions.implementation,
2598
- context: resolvedConfig.root,
2599
- moduleFederationConfig: dtsModuleFederationConfig,
2600
- typesFolder: normalizedConsumeTypes.typesFolder || "@mf-types",
2601
- abortOnError: false,
2602
- ...normalizedConsumeTypes
2603
- };
2604
- const extraOptions = normalizedDtsOptions.extraOptions || {};
2605
- if (!remote && !host && devOptions.disableLiveReload) return;
2606
- const startDevWorker = async () => {
2607
- let remoteTypeUrls;
2608
- if (host) remoteTypeUrls = await new Promise((resolve) => {
2609
- consumeTypesAPI({
2610
- host,
2611
- extraOptions,
2612
- displayErrorInTerminal: normalizedDtsOptions.displayErrorInTerminal
2613
- }, resolve);
2614
- });
2615
- devWorker = new DevWorker({
2616
- name: options.name,
2617
- remote,
2618
- host: host ? {
2619
- ...host,
2620
- remoteTypeUrls
2621
- } : void 0,
2622
- extraOptions,
2623
- disableLiveReload: devOptions.disableLiveReload,
2624
- disableHotTypesReload: devOptions.disableHotTypesReload
2625
- });
2626
- const update = () => devWorker?.update();
2627
- server.watcher.on("change", update);
2628
- server.watcher.on("add", update);
2629
- server.watcher.on("unlink", update);
2630
- server.httpServer?.once("close", () => {
2631
- devWorker?.exit();
2632
- server.watcher.off("change", update);
2633
- server.watcher.off("add", update);
2634
- server.watcher.off("unlink", update);
2507
+ if (!isRemoteHmrEnabled(options.dev)) return;
2508
+ if (isRemote) {
2509
+ for (const adapter of adapters) adapter.remote?.configureServer?.({
2510
+ server,
2511
+ options
2635
2512
  });
2636
- };
2637
- startDevWorker().catch((error) => {
2638
- logDtsError(error, normalizedDtsOptions);
2639
- });
2640
- }
2641
- }, {
2642
- name: "module-federation-dts-build",
2643
- apply: "build",
2644
- configResolved(config) {
2645
- resolvedConfig = config;
2646
- },
2647
- async generateBundle() {
2648
- if (hasGeneratedBundle) return;
2649
- hasGeneratedBundle = true;
2650
- if (!resolvedConfig) return;
2651
- let normalizedDtsOptions;
2652
- try {
2653
- normalizedDtsOptions = normalizeDtsOptions(getDtsModuleFederationConfig(resolvedConfig.root), resolvedConfig.root);
2654
- } catch (error) {
2655
- logDtsError(error, options.dts);
2656
- return;
2513
+ setupRemoteMetadataEndpoint(server, options);
2514
+ if (strategy === "full-reload") setupRemoteBroadcast(server, options);
2657
2515
  }
2658
- if (typeof normalizedDtsOptions !== "object") return;
2659
- const context = resolvedConfig.root;
2660
- const outputDir = resolveOutputDir(resolvedConfig);
2661
- let consumeOptions;
2662
- try {
2663
- consumeOptions = normalizeConsumeTypesOptions({
2664
- context,
2665
- dtsOptions: normalizedDtsOptions,
2666
- pluginOptions: getDtsModuleFederationConfig(resolvedConfig.root)
2516
+ if (isHost) {
2517
+ for (const adapter of adapters) adapter.host?.configureServer?.({
2518
+ server,
2519
+ options
2667
2520
  });
2668
- } catch (error) {
2669
- logDtsError(error, normalizedDtsOptions);
2670
- return;
2671
- }
2672
- if (consumeOptions?.host?.typesOnBuild) try {
2673
- await consumeTypesAPI(consumeOptions);
2674
- } catch (error) {
2675
- logDtsError(error, normalizedDtsOptions);
2521
+ if (strategy === "full-reload") setupHostFullReloadRelay(server, options);
2676
2522
  }
2677
- let generateOptions;
2678
- try {
2679
- generateOptions = normalizeGenerateTypesOptions({
2680
- context,
2681
- outputDir,
2682
- dtsOptions: normalizedDtsOptions,
2683
- pluginOptions: getDtsModuleFederationConfig(resolvedConfig.root)
2684
- });
2685
- } catch (error) {
2686
- logDtsError(error, normalizedDtsOptions);
2687
- return;
2523
+ },
2524
+ transform: {
2525
+ order: "post",
2526
+ handler(code, id) {
2527
+ if (!isRemote || !isRemoteHmrEnabled(options.dev)) return;
2528
+ if (!adapters.length) return;
2529
+ let result = code;
2530
+ const adapterCtx = { options };
2531
+ for (const adapter of adapters) {
2532
+ const next = adapter.remote?.transform?.(result, id, adapterCtx);
2533
+ if (typeof next === "string") result = next;
2534
+ }
2535
+ return result === code ? void 0 : result;
2688
2536
  }
2689
- if (!generateOptions) return;
2690
- try {
2691
- await generateTypesAPI({ dtsManagerOptions: generateOptions });
2692
- } catch (error) {
2693
- logDtsError(error, normalizedDtsOptions);
2537
+ },
2538
+ transformIndexHtml: {
2539
+ order: "pre",
2540
+ handler(_html, ctx) {
2541
+ if (!isRemoteHmrEnabled(options.dev)) return;
2542
+ if (!isHost || !ctx.server) return;
2543
+ return collectHostTags(ctx.server, options, adapters);
2694
2544
  }
2695
2545
  }
2696
- }];
2546
+ };
2697
2547
  }
2698
2548
  //#endregion
2699
2549
  //#region src/virtualModules/index.ts
2700
- function initVirtualModules(command, remoteEntryId) {
2550
+ function initVirtualModules(command, remoteEntryId, enableSsrInit = false) {
2701
2551
  writeLocalSharedImportMap();
2702
2552
  writeHostAutoInit(remoteEntryId, command);
2703
- writeRuntimeInitStatus(command);
2553
+ writeRuntimeInitStatus(command, enableSsrInit);
2704
2554
  }
2705
2555
  //#endregion
2706
2556
  //#region src/utils/bundleHelpers.ts
@@ -3050,6 +2900,12 @@ function removeTrailingSlash(value) {
3050
2900
  function ensureTrailingSlash(value) {
3051
2901
  return `${removeTrailingSlash(value)}/`;
3052
2902
  }
2903
+ function getBasePath(base) {
2904
+ return removeTrailingSlash(base || "/");
2905
+ }
2906
+ function isNuxtClientBase(base) {
2907
+ return getBasePath(base).endsWith("/_nuxt");
2908
+ }
3053
2909
  function normalizeNodeModulePath(source) {
3054
2910
  return source.replace(/\\/g, "/").replace(/\?.*$/, "");
3055
2911
  }
@@ -3083,6 +2939,116 @@ function resolvePublicPath(options, viteBase, originalBase) {
3083
2939
  return "auto";
3084
2940
  }
3085
2941
  //#endregion
2942
+ //#region src/virtualModules/virtualExposesSSR.ts
2943
+ /**
2944
+ * Virtual module ID for the SSR exposes map.
2945
+ * Separate from the browser exposes: no CSS injection, no document references,
2946
+ * and shared packages are imported as bare specifiers (externals in the SSR build).
2947
+ */
2948
+ function getVirtualExposesSSRId(options) {
2949
+ return `virtual:mf-exposes-ssr:${`${options.internalName}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
2950
+ }
2951
+ /**
2952
+ * Generates the SSR exposes map module.
2953
+ *
2954
+ * Differences from the browser version (virtualExposes.ts):
2955
+ * - No CSS asset injection (document APIs unavailable on Node)
2956
+ * - Shared packages (react, react-dom, etc.) must be externals in the SSR
2957
+ * build so Node resolves them via its own module cache — this is what
2958
+ * guarantees the React singleton is shared with react-dom/server.
2959
+ */
2960
+ function generateExposesSSR(options) {
2961
+ return `
2962
+ export default {
2963
+ ${Object.keys(options.exposes).map((key) => {
2964
+ return `
2965
+ ${JSON.stringify(key)}: async () => {
2966
+ const importModule = await import(${JSON.stringify(options.exposes[key].import)})
2967
+ const exportModule = {}
2968
+ Object.assign(exportModule, importModule)
2969
+ Object.defineProperty(exportModule, "__esModule", {
2970
+ value: true,
2971
+ enumerable: false
2972
+ })
2973
+ return exportModule
2974
+ }
2975
+ `;
2976
+ }).join(",")}
2977
+ }
2978
+ `;
2979
+ }
2980
+ //#endregion
2981
+ //#region src/virtualModules/virtualRemoteEntrySSR.ts
2982
+ const REMOTE_ENTRY_SSR_ID = "virtual:mf-REMOTE_ENTRY_SSR_ID";
2983
+ function getRemoteEntrySSRId(options) {
2984
+ return `${REMOTE_ENTRY_SSR_ID}:${`${options.internalName}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
2985
+ }
2986
+ function getSsrRemoteEntryFileName(browserFilename) {
2987
+ const ext = browserFilename.match(/\.[^.]+$/)?.[0] || ".js";
2988
+ return `${browserFilename.slice(0, browserFilename.length - ext.length)}.ssr${ext}`;
2989
+ }
2990
+ /**
2991
+ * Generates the SSR remote entry module.
2992
+ *
2993
+ * This is intentionally minimal — no HMR shim, no loadShare virtual modules,
2994
+ * no browser globals. Shared packages (react, react-dom, etc.) are imported
2995
+ * as externals by the SSR build, so Node's require cache provides the singleton.
2996
+ *
2997
+ * The container API (init / get) mirrors the browser entry so the MF runtime
2998
+ * can call it the same way on the server.
2999
+ */
3000
+ function generateRemoteEntrySSR(options) {
3001
+ const virtualExposesSSRId = getVirtualExposesSSRId(options);
3002
+ return `
3003
+ import { init as runtimeInit } from "@module-federation/runtime";
3004
+
3005
+ let exposesMapPromise;
3006
+
3007
+ async function getExposesMap() {
3008
+ exposesMapPromise ??= import(${JSON.stringify(virtualExposesSSRId)}).then((mod) => mod.default ?? mod);
3009
+ return exposesMapPromise;
3010
+ }
3011
+
3012
+ /**
3013
+ * Called by the MF runtime on the host to register this remote's share scope.
3014
+ * On the server the host has already initialised the runtime, so we just need
3015
+ * to set up a minimal runtime instance for the remote container.
3016
+ */
3017
+ async function init(shared = {}, initScope = []) {
3018
+ const initRes = runtimeInit({
3019
+ name: ${JSON.stringify(options.internalName)},
3020
+ remotes: [],
3021
+ shared: {},
3022
+ });
3023
+ const initToken = { from: ${JSON.stringify(options.internalName)} };
3024
+ if (initScope.indexOf(initToken) >= 0) return;
3025
+ initScope.push(initToken);
3026
+ initRes.initShareScopeMap(${JSON.stringify(options.shareScope)}, shared);
3027
+ try {
3028
+ await Promise.all(
3029
+ await initRes.initializeSharing(${JSON.stringify(options.shareScope)}, {
3030
+ strategy: ${JSON.stringify(options.shareStrategy ?? "version-first")},
3031
+ from: 'build',
3032
+ initScope,
3033
+ })
3034
+ );
3035
+ } catch (e) {
3036
+ console.error('[Module Federation SSR]', e);
3037
+ }
3038
+ return initRes;
3039
+ }
3040
+
3041
+ async function getExposes(moduleName) {
3042
+ const exposesMap = await getExposesMap();
3043
+ if (!(moduleName in exposesMap))
3044
+ throw new Error(\`[Module Federation] Module \${moduleName} does not exist in container.\`);
3045
+ return exposesMap[moduleName]().then((res) => () => res);
3046
+ }
3047
+
3048
+ export { init, getExposes as get };
3049
+ `;
3050
+ }
3051
+ //#endregion
3086
3052
  //#region src/plugins/pluginMFManifest.ts
3087
3053
  /**
3088
3054
  * Resolves the build version for the module federation manifest.
@@ -3113,6 +3079,7 @@ const Manifest = () => {
3113
3079
  };
3114
3080
  let root;
3115
3081
  let remoteEntryFile;
3082
+ let ssrRemoteEntryFile;
3116
3083
  let publicPath;
3117
3084
  let _command;
3118
3085
  let _originalConfigBase;
@@ -3149,8 +3116,8 @@ const Manifest = () => {
3149
3116
  type: "module"
3150
3117
  },
3151
3118
  ssrRemoteEntry: {
3152
- name: filename,
3153
- path: "",
3119
+ name: getSsrRemoteEntryFileName(filename),
3120
+ path: "/__mf_ssr__/",
3154
3121
  type: "module"
3155
3122
  },
3156
3123
  varRemoteEntry: varFilename ? {
@@ -3181,6 +3148,7 @@ const Manifest = () => {
3181
3148
  _originalConfigBase = config.base;
3182
3149
  },
3183
3150
  configResolved(config) {
3151
+ viteConfig = config;
3184
3152
  root = config.root;
3185
3153
  let base = config.base;
3186
3154
  if (_command === "serve") base = (config.server.origin || "") + config.base;
@@ -3190,7 +3158,10 @@ const Manifest = () => {
3190
3158
  if (!mfManifestName) return;
3191
3159
  let filesMap = {};
3192
3160
  const foundRemoteEntryFile = findRemoteEntryFile(mfOptions.filename, bundle);
3161
+ const expectedSsrRemoteEntryFile = getSsrRemoteEntryFileName(mfOptions.filename);
3162
+ const foundSsrRemoteEntryFile = Object.values(bundle).find((file) => file.fileName === expectedSsrRemoteEntryFile)?.fileName;
3193
3163
  if (foundRemoteEntryFile) remoteEntryFile = foundRemoteEntryFile;
3164
+ ssrRemoteEntryFile = foundSsrRemoteEntryFile || expectedSsrRemoteEntryFile;
3194
3165
  const allCssAssets = mfOptions.bundleAllCSS && !disableAssetsAnalyze ? collectCssAssets(bundle) : /* @__PURE__ */ new Set();
3195
3166
  if (!disableAssetsAnalyze) {
3196
3167
  const exposesModules = Object.keys(mfOptions.exposes).map((item) => mfOptions.exposes[item].import);
@@ -3232,6 +3203,11 @@ const Manifest = () => {
3232
3203
  path: "",
3233
3204
  type: "module"
3234
3205
  };
3206
+ const ssrRemoteEntry = {
3207
+ name: ssrRemoteEntryFile || getSsrRemoteEntryFileName(filename),
3208
+ path: _command === "serve" ? "/__mf_ssr__/" : "",
3209
+ type: "module"
3210
+ };
3235
3211
  const varRemoteEntry = varFilename ? {
3236
3212
  name: varFilename,
3237
3213
  path: "",
@@ -3295,7 +3271,7 @@ const Manifest = () => {
3295
3271
  buildName: name
3296
3272
  },
3297
3273
  remoteEntry,
3298
- ssrRemoteEntry: remoteEntry,
3274
+ ssrRemoteEntry,
3299
3275
  varRemoteEntry,
3300
3276
  types: {
3301
3277
  path: "",
@@ -3534,13 +3510,14 @@ function appendAlias(config, alias) {
3534
3510
  function pluginProxyRemotes_default(options) {
3535
3511
  let command;
3536
3512
  let root = process.cwd();
3513
+ let enableSsrInit = false;
3537
3514
  const { remotes } = options;
3538
3515
  function resolveRemoteId(source, importer, remoteName) {
3539
3516
  if (source === remoteName) {
3540
3517
  const installedPackageEntry = getInstalledPackageEntry(source, { cwd: root });
3541
3518
  if (installedPackageEntry && (importer === void 0 || isNodeModulesImporter(importer))) return installedPackageEntry;
3542
3519
  }
3543
- const remoteModule = getRemoteVirtualModule(source, command);
3520
+ const remoteModule = getRemoteVirtualModule(source, command, enableSsrInit);
3544
3521
  addUsedRemote(remoteName, source);
3545
3522
  refreshHostAutoInit();
3546
3523
  return remoteModule.getImportId();
@@ -3559,6 +3536,9 @@ function pluginProxyRemotes_default(options) {
3559
3536
  });
3560
3537
  });
3561
3538
  },
3539
+ configResolved() {
3540
+ enableSsrInit = command === "serve" && parseInt(version, 10) >= 8;
3541
+ },
3562
3542
  resolveId(source, importer) {
3563
3543
  if (!filterId(source)) return;
3564
3544
  for (const remote of Object.values(remotes)) {
@@ -3609,7 +3589,7 @@ function tryResolveFromProjectRoot(source) {
3609
3589
  const browserEntry = getInstalledPackageEntry(source, { cwd: getPackageDetectionCwd() });
3610
3590
  if (browserEntry) return browserEntry;
3611
3591
  try {
3612
- return createRequire$1(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`)).resolve(source);
3592
+ return createRequire(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`)).resolve(source);
3613
3593
  } catch {
3614
3594
  return;
3615
3595
  }
@@ -3840,9 +3820,37 @@ function applyRewrites(code, imports, id) {
3840
3820
  const importParts = [];
3841
3821
  if (imp.defaultLocal) importParts.push(`default as ${imp.defaultLocal}`);
3842
3822
  importParts.push(`__moduleExports as ${nsId}`);
3843
- const destructParts = imp.named.map((s) => s.imported === s.local ? s.local : `${s.imported}: ${s.local}`);
3844
3823
  let rewrite = `import { ${importParts.join(", ")} } from ${src};`;
3845
- if (destructParts.length > 0) rewrite += `\nconst { ${destructParts.join(", ")} } = ${nsId};`;
3824
+ if (imp.named.length > 0) {
3825
+ const isProxyId = `__mf_is_proxy_${counter++}`;
3826
+ const namedProxyHelper = `function __mfCreateNamedRemoteProxy(ns, key) {
3827
+ const target = function (...args) {
3828
+ const value = ns[key];
3829
+ return typeof value === "function" ? value.apply(this, args) : value;
3830
+ };
3831
+ return new Proxy(target, {
3832
+ get(_target, prop) {
3833
+ if (prop === "then") return undefined;
3834
+ const value = ns[key];
3835
+ if (prop === Symbol.toPrimitive) return () => value;
3836
+ const item = value == null ? undefined : value[prop];
3837
+ return typeof item === "function" ? item.bind(value) : item;
3838
+ },
3839
+ apply(target, thisArg, args) {
3840
+ return target.apply(thisArg, args);
3841
+ }
3842
+ });
3843
+ }`;
3844
+ const tempNames = imp.named.map((_s) => `__mf_named_${counter++}`);
3845
+ const destructParts = imp.named.map((s, index) => `${s.imported}: ${tempNames[index]}`);
3846
+ const bindingLines = imp.named.map((s, index) => {
3847
+ const temp = tempNames[index];
3848
+ return `const ${s.local} = ${isProxyId} ? __mfCreateNamedRemoteProxy(${nsId}, ${JSON.stringify(s.imported)}) : ${temp};`;
3849
+ });
3850
+ rewrite += `\n${namedProxyHelper}\nconst ${isProxyId} = ${nsId} && ${nsId}.__mf_is_remote_proxy;`;
3851
+ rewrite += `\nconst { ${destructParts.join(", ")} } = ${isProxyId} ? {} : ${nsId};`;
3852
+ rewrite += `\n${bindingLines.join("\n")}`;
3853
+ }
3846
3854
  ms.overwrite(imp.start, imp.end, rewrite);
3847
3855
  }
3848
3856
  changed = true;
@@ -4118,6 +4126,205 @@ function pluginRemoteNamedExports(options) {
4118
4126
  };
4119
4127
  }
4120
4128
  //#endregion
4129
+ //#region src/plugins/pluginSSRRemoteEntry.ts
4130
+ /**
4131
+ * Emits a Node-compatible SSR remote entry alongside the browser entry.
4132
+ *
4133
+ * Format strategy:
4134
+ * - Emit a dedicated ESM SSR entry alongside the browser entry.
4135
+ * - Keep the SSR entry out of the browser remote graph for Rollup builds by
4136
+ * emitting it as a generated asset.
4137
+ *
4138
+ * In both cases shared packages (react, react-dom, etc.) are marked as external
4139
+ * so Node resolves them through its own module cache, guaranteeing the singleton
4140
+ * is shared with react-dom/server.
4141
+ */
4142
+ function pluginSSRRemoteEntry(options) {
4143
+ const remoteEntrySSRId = getRemoteEntrySSRId(options);
4144
+ const virtualExposesSSRId = getVirtualExposesSSRId(options);
4145
+ let isRolldown = false;
4146
+ let ssrOutputFilename = "";
4147
+ const ssrOnlyExternals = [
4148
+ "@module-federation/runtime",
4149
+ "@module-federation/runtime-core",
4150
+ "@module-federation/sdk",
4151
+ ...options.ssrExternals ?? []
4152
+ ];
4153
+ const ssrOnlyExternalPattern = new RegExp(`^(${ssrOnlyExternals.map((e) => e.replace(/[/\\^$*+?.()|[\]{}]/g, "\\$&")).join("|")})(\\/.*)?$`);
4154
+ const ssrModuleIds = new Set([remoteEntrySSRId, virtualExposesSSRId]);
4155
+ const resolvedAbsToPackage = /* @__PURE__ */ new Map();
4156
+ let isServe = false;
4157
+ let viteConfig;
4158
+ let isNuxtProject = false;
4159
+ const findNuxtExposesChunk = (bundle) => {
4160
+ const exposeKeys = Object.keys(options.exposes);
4161
+ if (exposeKeys.length === 0) return;
4162
+ return Object.values(bundle).find((file) => {
4163
+ if (file.type !== "chunk" || !file.fileName.startsWith("_nuxt/") || !file.fileName.endsWith(".js")) return false;
4164
+ const code = file.code || "";
4165
+ return exposeKeys.every((key) => code.includes(JSON.stringify(key)));
4166
+ })?.fileName;
4167
+ };
4168
+ return [{
4169
+ name: "mf:ssr-remote-entry:pre",
4170
+ enforce: "pre",
4171
+ configResolved(config) {
4172
+ isServe = config.command === "serve";
4173
+ for (const pkg of ssrOnlyExternals) {
4174
+ const aliasEntry = (config.resolve?.alias)?.find((a) => a.find === pkg || a.find instanceof RegExp && a.find.test(pkg));
4175
+ if (aliasEntry?.replacement) resolvedAbsToPackage.set(aliasEntry.replacement, pkg);
4176
+ }
4177
+ },
4178
+ resolveId(id, importer) {
4179
+ if (id === remoteEntrySSRId || id.startsWith(remoteEntrySSRId)) return id;
4180
+ if (id === virtualExposesSSRId || id.startsWith(virtualExposesSSRId)) return id;
4181
+ if (!importer || !ssrModuleIds.has(importer)) return;
4182
+ if (ssrOnlyExternalPattern.test(id)) return {
4183
+ id,
4184
+ external: true
4185
+ };
4186
+ const pkg = resolvedAbsToPackage.get(id);
4187
+ if (pkg) return {
4188
+ id: pkg,
4189
+ external: true
4190
+ };
4191
+ if (id.startsWith(".") || id.startsWith("/") || id.startsWith("file:")) return this.resolve(id, importer, { skipSelf: true }).then((resolved) => {
4192
+ if (resolved) ssrModuleIds.add(resolved.id);
4193
+ return resolved;
4194
+ });
4195
+ }
4196
+ }, {
4197
+ name: "mf:ssr-remote-entry",
4198
+ configResolved(config) {
4199
+ viteConfig = config;
4200
+ isNuxtProject = isNuxtProjectRoot(config.root);
4201
+ },
4202
+ configureServer(server) {
4203
+ const base = "/__mf_ssr__";
4204
+ const basePath = getBasePath(viteConfig?.base);
4205
+ const ssrEntryFileName = getSsrRemoteEntryFileName(options.filename);
4206
+ if (isNuxtProject || isNuxtClientBase(basePath)) server.middlewares.use((req, _res, next) => {
4207
+ if (req.url?.replace(/\?.*/, "") === `${basePath}/${ssrEntryFileName}`) req.url = `${basePath}/__mf_ssr__/${ssrEntryFileName}`;
4208
+ next();
4209
+ });
4210
+ const ssrEnv = server.environments?.ssr;
4211
+ const clientEnv = server.environments?.client;
4212
+ if (typeof (ssrEnv?.fetchModule ?? clientEnv?.fetchModule) === "function") server.middlewares.use("/__mf_runner__", async (req, res) => {
4213
+ res.setHeader("Access-Control-Allow-Origin", "*");
4214
+ if (req.method === "OPTIONS") {
4215
+ res.setHeader("Access-Control-Allow-Methods", "POST");
4216
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
4217
+ res.statusCode = 204;
4218
+ res.end();
4219
+ return;
4220
+ }
4221
+ if (req.method !== "POST") {
4222
+ res.statusCode = 405;
4223
+ res.end("Method not allowed");
4224
+ return;
4225
+ }
4226
+ try {
4227
+ const chunks = [];
4228
+ await new Promise((resolve, reject) => {
4229
+ req.on("data", (chunk) => chunks.push(chunk));
4230
+ req.on("end", resolve);
4231
+ req.on("error", reject);
4232
+ });
4233
+ const body = JSON.parse(Buffer.concat(chunks).toString("utf8"));
4234
+ if (body.name === "getBuiltins") {
4235
+ const builtins = (clientEnv ?? ssrEnv)?.config?.resolve?.builtins ?? [];
4236
+ res.setHeader("Content-Type", "application/json");
4237
+ res.end(JSON.stringify({ result: builtins }));
4238
+ return;
4239
+ }
4240
+ if (body.name !== "fetchModule") {
4241
+ res.statusCode = 400;
4242
+ res.end(JSON.stringify({ error: { message: `Unsupported invoke: ${body.name}` } }));
4243
+ return;
4244
+ }
4245
+ const [id, importer, opts] = body.data;
4246
+ const fetchEnv = ssrEnv ?? clientEnv;
4247
+ const fetchFn = fetchEnv.fetchModule.bind(fetchEnv);
4248
+ let result;
4249
+ try {
4250
+ result = await fetchFn(id, importer, opts);
4251
+ } catch (fetchErr) {
4252
+ const bareId = id.startsWith("/@id/") ? id.slice(5).replace(/^__x00__/, "\0") : id;
4253
+ try {
4254
+ const { createRequire } = await import("module");
4255
+ const resolved = createRequire(new URL(`file://${server.config.root}/package.json`)).resolve(bareId.replace(/^\0/, ""));
4256
+ const { pathToFileURL } = await import("url");
4257
+ result = {
4258
+ externalize: pathToFileURL(resolved).href,
4259
+ type: "module"
4260
+ };
4261
+ } catch {
4262
+ throw fetchErr;
4263
+ }
4264
+ }
4265
+ res.setHeader("Content-Type", "application/json");
4266
+ res.end(JSON.stringify({ result }));
4267
+ } catch (e) {
4268
+ res.setHeader("Content-Type", "application/json");
4269
+ res.end(JSON.stringify({ error: { message: String(e instanceof Error ? e.message : e) } }));
4270
+ }
4271
+ });
4272
+ const ssrPath = `${base}/${ssrEntryFileName}`;
4273
+ server.middlewares.use(ssrPath, (_req, res) => {
4274
+ const exposesUrl = `${base}/${options.filename.replace(/\.[^.]+$/, "")}.exposes.js`;
4275
+ const code = generateRemoteEntrySSR(options).replace(JSON.stringify(virtualExposesSSRId), JSON.stringify(exposesUrl));
4276
+ res.setHeader("Content-Type", "application/javascript");
4277
+ res.setHeader("Access-Control-Allow-Origin", "*");
4278
+ res.end(code);
4279
+ });
4280
+ },
4281
+ resolveId(id) {
4282
+ if (id === remoteEntrySSRId || id.startsWith(remoteEntrySSRId)) return id;
4283
+ if (id === virtualExposesSSRId || id.startsWith(virtualExposesSSRId)) return id;
4284
+ if (id === `/__mf_ssr__/${getSsrRemoteEntryFileName(options.filename)}`) return remoteEntrySSRId;
4285
+ if (id === `/__mf_ssr__/${options.filename.replace(/\.[^.]+$/, "")}.exposes.js`) return virtualExposesSSRId;
4286
+ },
4287
+ load(id) {
4288
+ if (id === remoteEntrySSRId || id.startsWith(remoteEntrySSRId)) return generateRemoteEntrySSR(options);
4289
+ if (id === virtualExposesSSRId || id.startsWith(virtualExposesSSRId)) return generateExposesSSR(options);
4290
+ },
4291
+ buildStart() {
4292
+ if (isServe) return;
4293
+ isRolldown = getIsRolldown(this);
4294
+ ssrOutputFilename = getSsrRemoteEntryFileName(options.filename);
4295
+ const environmentName = this.environment?.name;
4296
+ const hasSsrEnvironment = Boolean(viteConfig?.environments?.ssr);
4297
+ const isLegacySsrBuild = Boolean(this.environment?.config?.build?.ssr);
4298
+ if (hasSsrEnvironment) {
4299
+ if (isNuxtProject) {
4300
+ if (environmentName === "ssr") return;
4301
+ } else if (environmentName !== "ssr") return;
4302
+ } else if (isLegacySsrBuild) {} else if (environmentName && environmentName !== "client") return;
4303
+ if (Object.keys(options.exposes).length === 0) return;
4304
+ if (isRolldown) this.emitFile({
4305
+ type: "chunk",
4306
+ id: remoteEntrySSRId,
4307
+ name: "ssrRemoteEntry",
4308
+ fileName: ssrOutputFilename,
4309
+ preserveSignature: "strict"
4310
+ });
4311
+ else this.emitFile({
4312
+ type: "asset",
4313
+ fileName: ssrOutputFilename,
4314
+ source: generateRemoteEntrySSR(options)
4315
+ });
4316
+ },
4317
+ generateBundle(_options, bundle) {
4318
+ const exposesChunk = findNuxtExposesChunk(bundle);
4319
+ const ssrAsset = bundle[ssrOutputFilename];
4320
+ if (exposesChunk && ssrAsset?.type === "asset" && typeof ssrAsset.source === "string") ssrAsset.source = ssrAsset.source.replace(/import\("virtual:mf-exposes-ssr:[^"]+"\)/g, `import("./${exposesChunk}")`);
4321
+ if (!isRolldown) return;
4322
+ const chunk = bundle[ssrOutputFilename];
4323
+ if (!chunk || chunk.type !== "chunk") return;
4324
+ }
4325
+ }];
4326
+ }
4327
+ //#endregion
4121
4328
  //#region src/plugins/pluginVarRemoteEntry.ts
4122
4329
  const VarRemoteEntry = () => {
4123
4330
  const mfOptions = getNormalizeModuleFederationOptions();
@@ -4380,6 +4587,11 @@ function createEarlyVirtualModulesPlugin(options) {
4380
4587
  const root = config.root || process.cwd();
4381
4588
  setPackageDetectionCwd(root);
4382
4589
  const isVinext = hasPackageDependency("vinext");
4590
+ setSsrRemotes(Object.entries(options.remotes).map(([key, r]) => ({
4591
+ name: key,
4592
+ entry: r.entry,
4593
+ type: r.type ?? "module"
4594
+ })));
4383
4595
  initVirtualModules(_command, getRemoteEntryId(options));
4384
4596
  const isRolldown = getIsRolldown(this);
4385
4597
  if (remotes && Object.keys(remotes).length > 0) {
@@ -4499,9 +4711,46 @@ export default __mfShared.default ?? __mfShared;`
4499
4711
  }
4500
4712
  writeLocalSharedImportMap();
4501
4713
  }
4714
+ },
4715
+ configResolved(config) {
4716
+ if (parseInt(version, 10) < 8) return;
4717
+ if (!(Object.keys(options.exposes).length > 0 || Object.keys(options.remotes).length > 0)) return;
4718
+ if (options.runtimePlugins.some((p) => {
4719
+ return (typeof p === "string" ? p : p[0]) === "@module-federation/vite/ssrEntryLoader";
4720
+ })) return;
4721
+ const pluginRequire = createRequire(import.meta.url);
4722
+ const projectRequire = createRequire(new URL(`file://${config.root}/package.json`));
4723
+ const sharedKeys = Object.keys(options.shared ?? {});
4724
+ const commonSharedPkgs = [
4725
+ "react",
4726
+ "react-dom",
4727
+ "react/jsx-runtime",
4728
+ "react/jsx-dev-runtime",
4729
+ "@module-federation/runtime",
4730
+ "@module-federation/runtime-core",
4731
+ "@module-federation/sdk"
4732
+ ];
4733
+ const resolvedShared = {};
4734
+ for (const pkg of [...commonSharedPkgs, ...sharedKeys]) try {
4735
+ resolvedShared[pkg] = projectRequire.resolve(pkg);
4736
+ } catch {
4737
+ try {
4738
+ resolvedShared[pkg] = pluginRequire.resolve(pkg);
4739
+ } catch {}
4740
+ }
4741
+ const ssrEntryLoaderSpecifier = "@module-federation/vite/ssrEntryLoader";
4742
+ try {
4743
+ pluginRequire.resolve(ssrEntryLoaderSpecifier);
4744
+ options.runtimePlugins.push([ssrEntryLoaderSpecifier, { resolvedShared }]);
4745
+ } catch {}
4502
4746
  }
4503
4747
  };
4504
4748
  }
4749
+ const SSR_ONLY_PLUGINS = new Set(["@module-federation/vite/ssrEntryLoader"]);
4750
+ function loadPluginDts(options) {
4751
+ if (options.dts === false) return [];
4752
+ return [import("./pluginDts-B5pcUmam.mjs").then(({ default: pluginDts }) => pluginDts(options))];
4753
+ }
4505
4754
  function federation(mfUserOptions) {
4506
4755
  if (isTestEnv()) return [];
4507
4756
  const options = normalizeModuleFederationOptions(mfUserOptions);
@@ -4542,7 +4791,7 @@ function federation(mfUserOptions) {
4542
4791
  const environmentName = this.environment?.name;
4543
4792
  if (!environmentName || environmentName === "client") return;
4544
4793
  const target = reactServerEntryMap[id];
4545
- const reactPackageJson = createRequire$1(new URL(`file://${process.cwd()}/package.json`)).resolve("react/package.json");
4794
+ const reactPackageJson = createRequire(new URL(`file://${process.cwd()}/package.json`)).resolve("react/package.json");
4546
4795
  return path.join(path.dirname(reactPackageJson), target.replace(/^react\//, ""));
4547
4796
  }
4548
4797
  }] : [],
@@ -4553,14 +4802,26 @@ function federation(mfUserOptions) {
4553
4802
  command = env.command;
4554
4803
  },
4555
4804
  configResolved() {
4556
- initVirtualModules(command, remoteEntryId);
4805
+ initVirtualModules(command, remoteEntryId, parseInt(version, 10) >= 8);
4557
4806
  }
4558
4807
  },
4559
4808
  aliasToArrayPlugin_default,
4560
4809
  checkAliasConflicts({ shared }),
4561
4810
  normalizeOptimizeDeps_default,
4562
- ...pluginDts(options),
4811
+ ...loadPluginDts(options),
4563
4812
  pluginDevRemoteHmr(options),
4813
+ {
4814
+ name: "mf:normalize-entry-chunks",
4815
+ enforce: "pre",
4816
+ apply: "build",
4817
+ generateBundle(_options, bundle) {
4818
+ for (const chunk of Object.values(bundle)) {
4819
+ if (typeof chunk !== "object" || chunk === null || chunk.type !== "chunk" || !chunk.isEntry) continue;
4820
+ const facadeId = chunk.facadeModuleId ?? "";
4821
+ if (facadeId.includes("__mf__virtual") || facadeId.startsWith("virtual:mf-") || facadeId.startsWith("virtual:mf:") || facadeId.startsWith("\0virtual:mf-") || facadeId.startsWith("\0virtual:mf:")) chunk.isEntry = false;
4822
+ }
4823
+ }
4824
+ },
4564
4825
  ...addEntry({
4565
4826
  entryName: "remoteEntry",
4566
4827
  entryPath: remoteEntryId,
@@ -4570,7 +4831,8 @@ function federation(mfUserOptions) {
4570
4831
  entryName: "hostInit",
4571
4832
  entryPath: () => getHostAutoInitPath(),
4572
4833
  inject: hostInitInjectLocation,
4573
- forceClientInjected: Object.keys(options.exposes).length > 0
4834
+ forceClientInjected: Object.keys(options.exposes).length > 0,
4835
+ skipTransformFor: Object.values(options.exposes).map((expose) => expose.import)
4574
4836
  }),
4575
4837
  ...addEntry({
4576
4838
  entryName: "virtualExposes",
@@ -4715,7 +4977,7 @@ function federation(mfUserOptions) {
4715
4977
  *
4716
4978
  * @see https://rollupjs.org/plugin-development/#synthetic-named-exports
4717
4979
  */
4718
- if (!/\bexport\s+const\s+__moduleExports\b/.test(code)) {
4980
+ if (!/\bexport\s+const\s+__moduleExports\b/.test(code) && !/\bexport\s*\{[^}]*__moduleExports/.test(code)) {
4719
4981
  const nextCode = code.replace("export default exportModule", "export const __moduleExports = exportModule;\nexport default exportModule.__esModule ? exportModule.default : exportModule");
4720
4982
  code = nextCode === code ? `${code}\nexport const __moduleExports = exportModule;\n` : nextCode;
4721
4983
  }
@@ -4788,6 +5050,7 @@ function federation(mfUserOptions) {
4788
5050
  config.optimizeDeps.include.push("@module-federation/runtime");
4789
5051
  options.runtimePlugins.forEach((p) => {
4790
5052
  const pluginPath = typeof p === "string" ? p : p[0];
5053
+ if (SSR_ONLY_PLUGINS.has(pluginPath)) return;
4791
5054
  if (pluginPath && !pluginPath.startsWith(".") && !pluginPath.startsWith("/") && !pluginPath.startsWith("\0") && !pluginPath.startsWith("virtual:")) config.optimizeDeps.include.push(pluginPath);
4792
5055
  });
4793
5056
  if (isRolldown) {
@@ -4803,6 +5066,7 @@ function federation(mfUserOptions) {
4803
5066
  }
4804
5067
  },
4805
5068
  ...Manifest(),
5069
+ ...pluginSSRRemoteEntry(options),
4806
5070
  ...VarRemoteEntry(),
4807
5071
  {
4808
5072
  name: "module-federation-vinext-fix-rsc-preload-as",
@@ -4876,5 +5140,8 @@ function federation(mfUserOptions) {
4876
5140
  })()
4877
5141
  ];
4878
5142
  }
5143
+ function createModuleFederationConfig(options) {
5144
+ return options;
5145
+ }
4879
5146
  //#endregion
4880
- export { federation };
5147
+ export { createModuleFederationConfig, federation };