@module-federation/vite 1.15.5 → 1.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.mjs CHANGED
@@ -1,17 +1,13 @@
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, 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";
8
+ import { createHash } from "node:crypto";
10
9
  import { fileURLToPath } from "url";
11
10
  import { init, parse } from "es-module-lexer";
12
- //#region \0rolldown/runtime.js
13
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
14
- //#endregion
15
11
  //#region src/utils/codeRewriter.ts
16
12
  const BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
17
13
  var CodeRewriter = class {
@@ -173,237 +169,6 @@ function injectEntryScript(html, initSrc) {
173
169
  return html.replace("<head>", `<head><script type="module" src=${JSON.stringify(src)}><\/script>`);
174
170
  }
175
171
  //#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
172
  //#region src/utils/normalizeModuleFederationOptions.ts
408
173
  const INTERNAL_NAME_PREFIX = "__mfe_internal__";
409
174
  function toInternalModuleFederationName(name) {
@@ -475,7 +240,7 @@ function normalizeRemoteItem(key, remote) {
475
240
  * @returns {string | undefined}
476
241
  */
477
242
  function searchPackageVersion(sharedName) {
478
- const version = getInstalledPackageJson(sharedName, { packageName: sharedName })?.packageJson.version;
243
+ const version = getInstalledPackageJson(sharedName)?.packageJson.version;
479
244
  return typeof version === "string" ? version : void 0;
480
245
  }
481
246
  function inferVersionFromRequiredVersion(requiredVersion) {
@@ -489,22 +254,8 @@ function getLitExportSubpathShares(sharedName) {
489
254
  return Object.keys(exportsField).filter((key) => key.startsWith("./") && key !== "." && !key.includes("*")).map((key) => `${sharedName}/${key.slice(2)}`);
490
255
  }
491
256
  function normalizeShareItem(key, shareItem) {
492
- let version;
493
257
  const isImportFalse = typeof shareItem === "object" && shareItem.import === false;
494
- try {
495
- try {
496
- version = __require(path$1.join(getPackageName(key), "package.json")).version;
497
- } catch (e1) {
498
- try {
499
- version = __require(path$1.join(process.cwd(), "node_modules", getPackageName(key), "package.json")).version;
500
- } catch (e2) {
501
- version = searchPackageVersion(key);
502
- if (!version && !isImportFalse) mfError(e1);
503
- }
504
- }
505
- } catch (e) {
506
- if (!isImportFalse) mfError(`Unexpected error resolving version for ${key}:`, e);
507
- }
258
+ const version = (typeof shareItem === "object" ? shareItem.version || inferVersionFromRequiredVersion(shareItem.requiredVersion) : void 0) || searchPackageVersion(key);
508
259
  if (typeof shareItem === "string") return {
509
260
  name: shareItem,
510
261
  version,
@@ -519,12 +270,12 @@ function normalizeShareItem(key, shareItem) {
519
270
  return {
520
271
  name: key,
521
272
  from: "",
522
- version: shareItem.version || inferVersionFromRequiredVersion(shareItem.requiredVersion) || version,
273
+ version,
523
274
  scope: shareItem.shareScope || "default",
524
275
  shareConfig: {
525
276
  import: shareItem.import,
526
277
  singleton: shareItem.singleton || false,
527
- requiredVersion: shareItem.requiredVersion || (isImportFalse ? "*" : version ? `^${version}` : "*"),
278
+ requiredVersion: shareItem.requiredVersion || (isImportFalse || shareItem.version ? "*" : version ? `^${version}` : "*"),
528
279
  strictVersion: !!shareItem.strictVersion
529
280
  }
530
281
  };
@@ -552,11 +303,13 @@ function normalizeShared(shared) {
552
303
  const result = {};
553
304
  const sourceEntries = [];
554
305
  if (Array.isArray(shared)) shared.forEach((key) => {
306
+ if (isModuleFederationRuntimePackage(key)) return;
555
307
  result[key] = normalizeShareItem(key, key);
556
308
  explicitSharedKeys.add(key);
557
309
  sourceEntries.push([key, key]);
558
310
  });
559
311
  else if (typeof shared === "object") Object.keys(shared).forEach((key) => {
312
+ if (isModuleFederationRuntimePackage(key)) return;
560
313
  const value = shared[key];
561
314
  result[key] = normalizeShareItem(key, value);
562
315
  explicitSharedKeys.add(key);
@@ -570,11 +323,14 @@ function normalizeShared(shared) {
570
323
  });
571
324
  return result;
572
325
  }
326
+ function isModuleFederationRuntimePackage(key) {
327
+ return key === "@module-federation/runtime" || key === "@module-federation/runtime-core";
328
+ }
573
329
  function shouldAutoShareDependency(key) {
574
330
  const pkg = getInstalledPackageJson(key)?.packageJson;
575
331
  if (!pkg) return false;
576
332
  if (!pkg.browser && !pkg.exports && typeof pkg.module !== "string") return false;
577
- if (key === "@module-federation/vite") return false;
333
+ if (key === "@module-federation/vite" || isModuleFederationRuntimePackage(key)) return false;
578
334
  const entry = [
579
335
  pkg.module,
580
336
  pkg.main,
@@ -598,6 +354,18 @@ function normalizeManifest(manifest) {
598
354
  }
599
355
  let config;
600
356
  let explicitSharedKeys = /* @__PURE__ */ new Set();
357
+ function resolveRuntimeImplementation() {
358
+ const fallback = __require.resolve("@module-federation/runtime");
359
+ try {
360
+ const packageJsonPath = __require.resolve("@module-federation/runtime/package.json");
361
+ const packageJson = JSON.parse(fs$1.readFileSync(packageJsonPath, "utf-8"));
362
+ const importExport = packageJson.exports?.["."];
363
+ const exportImport = typeof importExport === "object" ? typeof importExport.import === "string" ? importExport.import : importExport.import?.default : void 0;
364
+ const esmEntry = packageJson.module || exportImport;
365
+ if (esmEntry) return path$1.join(path$1.dirname(packageJsonPath), esmEntry);
366
+ } catch {}
367
+ return fallback;
368
+ }
601
369
  function getNormalizeModuleFederationOptions() {
602
370
  return config;
603
371
  }
@@ -622,7 +390,7 @@ function normalizeModuleFederationOptions(options) {
622
390
  shareScope: options.shareScope || "default",
623
391
  shared: normalizeShared(options.shared),
624
392
  runtimePlugins: options.runtimePlugins || [],
625
- implementation: options.implementation || __require.resolve("@module-federation/runtime"),
393
+ implementation: options.implementation || resolveRuntimeImplementation(),
626
394
  manifest: normalizeManifest(options.manifest),
627
395
  dev: options.dev,
628
396
  dts: options.dts,
@@ -836,11 +604,24 @@ function getDeferredInitPromiseCode() {
836
604
  initReject = rj;
837
605
  });`;
838
606
  }
839
- function getSsrNoopResolveCode() {
607
+ let _ssrRemotes = [];
608
+ function setSsrRemotes(remotes) {
609
+ _ssrRemotes = remotes;
610
+ }
611
+ function getSsrNoopResolveCode(enableSsrInit) {
612
+ if (!enableSsrInit) return "";
840
613
  return `if (typeof window === 'undefined') {
841
- initResolve({
842
- loadRemote: function() { return Promise.resolve(undefined); },
843
- loadShare: function() { return Promise.resolve(undefined); },
614
+ var _noop = { loadRemote: function() { return Promise.resolve(undefined); }, loadShare: function() { return Promise.resolve(undefined); } };
615
+ import(/* @vite-ignore */ '@module-federation/runtime').then(function(runtimeMod) {
616
+ return import(/* @vite-ignore */ '@module-federation/vite/ssrEntryLoader').then(
617
+ function(loaderMod) { return [runtimeMod, [loaderMod.default()]]; },
618
+ function() { return [runtimeMod, []]; }
619
+ );
620
+ }).then(function(pair) {
621
+ var runtime = pair[0].init({ name: '__mf_ssr_host__', remotes: ${JSON.stringify(_ssrRemotes)}, shared: {}, plugins: pair[1] });
622
+ initResolve(runtime);
623
+ }, function() {
624
+ initResolve(_noop);
844
625
  });
845
626
  }`;
846
627
  }
@@ -855,12 +636,12 @@ if (!${options.stateVar}) {
855
636
  initResolve,
856
637
  initReject,
857
638
  };
858
- ${getSsrNoopResolveCode()}
639
+ ${getSsrNoopResolveCode(options.enableSsrInit)}
859
640
  }
860
641
  const ${options.exposedConst} = ${options.stateVar}.${options.exposedProperty};
861
642
  `;
862
643
  }
863
- function getRuntimeInitBootstrapCode() {
644
+ function getRuntimeInitBootstrapCode(enableSsrInit = false) {
864
645
  return `
865
646
  const globalKey = ${JSON.stringify(getRuntimeInitGlobalKey())};
866
647
  const moduleCacheGlobalKey = ${JSON.stringify(MODULE_CACHE_GLOBAL_KEY)};
@@ -875,7 +656,7 @@ if (!globalThis[globalKey]) {
875
656
  initReject,
876
657
  moduleCache: globalThis[moduleCacheGlobalKey],
877
658
  };
878
- ${getSsrNoopResolveCode()}
659
+ ${getSsrNoopResolveCode(enableSsrInit)}
879
660
  }
880
661
  globalThis[globalKey].moduleCache ||= globalThis[moduleCacheGlobalKey];
881
662
  globalThis[globalKey].moduleCache.share ||= {};
@@ -891,27 +672,29 @@ globalThis[__mfCacheGlobalKey].remote ||= {};
891
672
  const __mfModuleCache = globalThis[__mfCacheGlobalKey];
892
673
  `;
893
674
  }
894
- function getRuntimeInitPromiseBootstrapCode() {
675
+ function getRuntimeInitPromiseBootstrapCode(enableSsrInit = false) {
895
676
  return getRuntimeInitStateBootstrapCode({
896
677
  globalKeyVar: "__mfPromiseGlobalKey",
897
678
  stateVar: "__mfPromiseState",
898
679
  exposedConst: "initPromise",
899
- exposedProperty: "initPromise"
680
+ exposedProperty: "initPromise",
681
+ enableSsrInit
900
682
  });
901
683
  }
902
- function getRuntimeInitResolveBootstrapCode() {
684
+ function getRuntimeInitResolveBootstrapCode(enableSsrInit = false) {
903
685
  return getRuntimeInitStateBootstrapCode({
904
686
  globalKeyVar: "__mfResolveGlobalKey",
905
687
  stateVar: "__mfResolveState",
906
688
  exposedConst: "initResolve",
907
- exposedProperty: "initResolve"
689
+ exposedProperty: "initResolve",
690
+ enableSsrInit
908
691
  });
909
692
  }
910
- function writeRuntimeInitStatus(command) {
693
+ function writeRuntimeInitStatus(command, enableSsrInit = false) {
911
694
  const exportStatement = command === "build" ? `const { initPromise, initResolve, initReject, moduleCache } = globalThis[globalKey];
912
695
  export { initPromise, initResolve, initReject, moduleCache };` : `module.exports = globalThis[globalKey];`;
913
696
  virtualRuntimeInitStatus.writeSync(`
914
- ${getRuntimeInitBootstrapCode()}
697
+ ${getRuntimeInitBootstrapCode(enableSsrInit)}
915
698
  ${exportStatement}
916
699
  `);
917
700
  }
@@ -946,10 +729,10 @@ function isValidEsmExportName(name) {
946
729
  return !!name && name !== "default" && name !== "__esModule" && isValidJsIdentifier(name);
947
730
  }
948
731
  const JS_IDENTIFIER_PATTERN = `[\$_\\p{ID_Start}][\$_\\u200C\\u200D\\p{ID_Continue}]*`;
949
- const localRequire = createRequire$1(import.meta.url);
732
+ const localRequire = createRequire(import.meta.url);
950
733
  function resolvePackageEntryFromProjectRoot(pkg) {
951
734
  try {
952
- return createRequire$1(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`)).resolve(pkg);
735
+ return createRequire(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`)).resolve(pkg);
953
736
  } catch {
954
737
  return;
955
738
  }
@@ -1047,7 +830,7 @@ function getNamedExportsViaRegex(source, filePath, visited) {
1047
830
  }
1048
831
  function getPackageNamedExports(pkg) {
1049
832
  try {
1050
- const mod = createRequire$1(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`))(pkg);
833
+ const mod = createRequire(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`))(pkg);
1051
834
  return Object.keys(mod).filter((k) => isValidEsmExportName(k));
1052
835
  } catch {
1053
836
  return getEsmNamedExports(pkg);
@@ -1055,7 +838,7 @@ function getPackageNamedExports(pkg) {
1055
838
  }
1056
839
  function getLocalProviderImportPath(pkg) {
1057
840
  try {
1058
- const resolved = createRequire$1(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`)).resolve(pkg);
841
+ const resolved = createRequire(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`)).resolve(pkg);
1059
842
  return isWorkspaceFilePath(resolved) ? resolved : void 0;
1060
843
  } catch {
1061
844
  const resolved = getInstalledPackageEntry(pkg, {
@@ -1076,7 +859,7 @@ function getProjectResolvedImportPath(pkg) {
1076
859
  if (esmEntry) return esmEntry;
1077
860
  }
1078
861
  try {
1079
- return createRequire$1(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`)).resolve(pkg);
862
+ return createRequire(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`)).resolve(pkg);
1080
863
  } catch {
1081
864
  return;
1082
865
  }
@@ -1098,7 +881,7 @@ function isWorkspacePackageEntry(pkg, resolved) {
1098
881
  }
1099
882
  function tryResolveImportFromPackageRoot(pkg, root) {
1100
883
  try {
1101
- return createRequire$1(new URL(`file://${path.join(root, "package.json")}`)).resolve(pkg);
884
+ return createRequire(new URL(`file://${path.join(root, "package.json")}`)).resolve(pkg);
1102
885
  } catch {
1103
886
  return;
1104
887
  }
@@ -1451,7 +1234,7 @@ const normalizeRuntimeShareCode = `const __mfNormalizeRuntimeShare = (mod) => {
1451
1234
  let current = mod;
1452
1235
  for (let i = 0; i < 5; i++) {
1453
1236
  const defaultExport = current?.default;
1454
- if (!defaultExport || typeof defaultExport !== "object") break;
1237
+ if (!defaultExport || typeof defaultExport !== "object" || Object.keys(defaultExport).length === 0) break;
1455
1238
  const namedValues = Object.keys(current).filter((key) => key !== "default").map((key) => current[key]);
1456
1239
  if (namedValues.length > 0 && namedValues.some((value) => value !== undefined)) break;
1457
1240
  current = defaultExport;
@@ -1491,6 +1274,9 @@ const REMOTE_ENTRY_ID = "virtual:mf-REMOTE_ENTRY_ID";
1491
1274
  function getRemoteEntryId(options) {
1492
1275
  return `${REMOTE_ENTRY_ID}:${`${options.internalName}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
1493
1276
  }
1277
+ const SSR_ONLY_PLUGIN_SPECIFIERS = new Set(["@module-federation/vite/ssrEntryLoader"]);
1278
+ const isSsrOnlyPlugin = (importStatement) => [...SSR_ONLY_PLUGIN_SPECIFIERS].some((s) => importStatement.includes(s));
1279
+ const getSsrOnlyPluginSpecifier = (importStatement) => [...SSR_ONLY_PLUGIN_SPECIFIERS].find((s) => importStatement.includes(s));
1494
1280
  function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(options), command = "build") {
1495
1281
  const pluginImportNames = options.runtimePlugins.map((p, i) => {
1496
1282
  if (typeof p === "string") return [
@@ -1514,7 +1300,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
1514
1300
  globalThis.__VUE_HMR_RUNTIME__ = { createRecord() {}, rerender() {}, reload() {} };
1515
1301
  }
1516
1302
  import {init as runtimeInit, loadRemote} from "@module-federation/runtime";
1517
- ${pluginImportNames.map((item) => item[1]).join("\n")}
1303
+ ${pluginImportNames.filter((item) => !isSsrOnlyPlugin(item[1])).map((item) => item[1]).join("\n")}
1518
1304
  ${command === "build" ? getRuntimeInitResolveBootstrapCode() : getRuntimeInitBootstrapCode() + "\n const { initResolve } = globalThis[globalKey];"}
1519
1305
  ${getRuntimeModuleCacheBootstrapCode()}
1520
1306
  const initTokens = {}
@@ -1562,11 +1348,19 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
1562
1348
  async function init(shared = {}, initScope = []) {
1563
1349
  const {usedShared, usedRemotes} = await getLocalSharedImportMap()
1564
1350
  ${generateDirectSharedCacheSeedCode(command)}
1351
+ const __browserPlugins = [${pluginImportNames.filter((item) => !isSsrOnlyPlugin(item[1])).map((item) => `${item[0]}(${item[2]})`).join(", ")}];
1352
+ const __ssrPlugins = typeof globalThis.window === 'undefined'
1353
+ ? await Promise.all([${pluginImportNames.filter((item) => isSsrOnlyPlugin(item[1])).map((item) => {
1354
+ const specifier = getSsrOnlyPluginSpecifier(item[1]);
1355
+ const opts = item[2];
1356
+ return `import(${JSON.stringify(specifier)}).then(m => (m.default ?? m)(${opts}))`;
1357
+ }).join(", ")}])
1358
+ : [];
1565
1359
  const initRes = runtimeInit({
1566
1360
  name: mfName,
1567
- remotes: usedRemotes,
1361
+ remotes: ${options.shareStrategy === "loaded-first" ? "[]" : "usedRemotes"},
1568
1362
  shared: usedShared,
1569
- plugins: [${pluginImportNames.map((item) => `${item[0]}(${item[2]})`).join(", ")}],
1363
+ plugins: [...__browserPlugins, ...__ssrPlugins],
1570
1364
  ${options.shareStrategy ? `shareStrategy: '${options.shareStrategy}'` : ""}
1571
1365
  });
1572
1366
  // handling circular init calls
@@ -1617,6 +1411,7 @@ const hostAutoInitModule = new VirtualModule("hostAutoInit", "__H_A_I__");
1617
1411
  let currentHostAutoInitRemoteEntryId = REMOTE_ENTRY_ID;
1618
1412
  let currentHostAutoInitCommand = "build";
1619
1413
  function generateHostAutoInitCode(remoteEntryImport, _command = "build") {
1414
+ const shouldPreloadShares = getNormalizeModuleFederationOptions().shareStrategy !== "loaded-first";
1620
1415
  return `
1621
1416
  ${getRuntimeModuleCacheBootstrapCode()}
1622
1417
  let hostInitPromise;
@@ -1628,6 +1423,7 @@ function generateHostAutoInitCode(remoteEntryImport, _command = "build") {
1628
1423
  const runtime = await remoteEntry.init();
1629
1424
  const usedShared = ${generateUsedSharedPreloadConfig()};
1630
1425
  ${normalizeRuntimeShareCode}
1426
+ ${shouldPreloadShares ? `
1631
1427
  for (const [pkg, share] of Object.entries(usedShared)) {
1632
1428
  if (__mfModuleCache.share[pkg] !== undefined) {
1633
1429
  continue;
@@ -1641,6 +1437,7 @@ function generateHostAutoInitCode(remoteEntryImport, _command = "build") {
1641
1437
  });
1642
1438
  });
1643
1439
  }
1440
+ ` : ""}
1644
1441
  const __mfRemotePreloads = [];
1645
1442
  await Promise.all(__mfRemotePreloads);
1646
1443
  return runtime;
@@ -1672,12 +1469,13 @@ function getHostAutoInitPath() {
1672
1469
  //#region src/virtualModules/virtualRemotes.ts
1673
1470
  const cacheRemoteMap = {};
1674
1471
  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));
1472
+ function getRemoteVirtualModule(remote, command, enableSsrInit = false) {
1473
+ const cacheKey = `${remote}__${command}__${enableSsrInit ? "ssr" : "no-ssr"}`;
1474
+ if (!cacheRemoteMap[cacheKey]) {
1475
+ cacheRemoteMap[cacheKey] = new VirtualModule(remote, LOAD_REMOTE_TAG, ".mjs");
1476
+ cacheRemoteMap[cacheKey].writeSync(generateRemotes(remote, command, enableSsrInit));
1679
1477
  }
1680
- return cacheRemoteMap[remote];
1478
+ return cacheRemoteMap[cacheKey];
1681
1479
  }
1682
1480
  const usedRemotesMap = {};
1683
1481
  function addUsedRemote(remoteKey, remoteModule) {
@@ -1687,40 +1485,93 @@ function addUsedRemote(remoteKey, remoteModule) {
1687
1485
  function getUsedRemotesMap() {
1688
1486
  return usedRemotesMap;
1689
1487
  }
1690
- function generateRemotes(id, command) {
1691
- const useReactProxy = command === "serve" && hasPackageDependency("react");
1488
+ function getRemoteFromId(id, remotes) {
1489
+ const remoteName = Object.keys(remotes).filter((name) => id === name || id.startsWith(name + "/")).sort((a, b) => b.length - a.length)[0];
1490
+ return remoteName ? remotes[remoteName] : void 0;
1491
+ }
1492
+ function generateRemotes(id, command, enableSsrInit = false) {
1493
+ const useReactProxy = hasPackageDependency("react");
1494
+ const options = getNormalizeModuleFederationOptions();
1495
+ const isLoadedFirst = options.shareStrategy === "loaded-first";
1496
+ const remote = getRemoteFromId(id, options.remotes);
1497
+ const registerRemoteCode = isLoadedFirst && remote ? `runtime.registerRemotes([${JSON.stringify({
1498
+ entryGlobalName: remote.entryGlobalName,
1499
+ name: remote.name,
1500
+ type: remote.type,
1501
+ entry: remote.entry,
1502
+ shareScope: remote.shareScope ?? "default"
1503
+ })}]);` : "";
1692
1504
  const reactImportLine = useReactProxy ? `import __mfReactDefault from "react";
1693
1505
  import * as __mfReactNamespace from "react";
1694
1506
  const __mfReact = __mfReactDefault ?? __mfReactNamespace.default ?? __mfReactNamespace;` : "";
1695
1507
  const importLine = command === "build" ? `${getRuntimeModuleCacheBootstrapCode()}
1696
- import { hostInitPromise as __mfHostInitPromise } from ${JSON.stringify(getHostAutoInitPath())};` : `${getRuntimeInitBootstrapCode()}
1697
- const { initPromise, moduleCache: __mfModuleCache } = globalThis[globalKey];`;
1508
+ import { hostInitPromise as __mfHostInitPromise } from ${JSON.stringify(getHostAutoInitPath())};` : `${getRuntimeInitBootstrapCode(enableSsrInit)}
1509
+ const { initPromise, initResolve, initReject, moduleCache: __mfModuleCache } = globalThis[globalKey];
1510
+ if (typeof window !== "undefined") {
1511
+ import(${JSON.stringify(getHostAutoInitPath())})
1512
+ .then((mod) => mod.hostInitPromise)
1513
+ .then(initResolve, initReject);
1514
+ }`;
1698
1515
  const exportLine = command === "serve" ? `if (__mfRemotePending) {
1699
- const mod = await __mfRemotePending;
1700
- if (mod !== undefined) exportModule = mod;
1516
+ __mfRemotePending = __mfRemotePending.then((mod) => {
1517
+ if (mod !== undefined) exportModule = mod;
1518
+ return exportModule;
1519
+ });
1701
1520
  }
1702
- export const __moduleExports = exportModule;
1703
- export const __mf_remote_pending = Promise.resolve(exportModule);
1521
+ export { exportModule as __moduleExports };
1522
+ export const __mf_remote_pending = __mfRemotePending || Promise.resolve(exportModule);
1704
1523
  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;
1524
+ __mfRemotePending = __mfRemotePending.then((mod) => {
1525
+ if (mod !== undefined) exportModule = mod;
1526
+ return exportModule;
1527
+ });
1707
1528
  }
1708
- export const __moduleExports = exportModule;
1709
- export const __mf_remote_pending = Promise.resolve(exportModule);
1529
+ export { exportModule as __moduleExports };
1530
+ export const __mf_remote_pending = __mfRemotePending || Promise.resolve(exportModule);
1710
1531
  export default exportModule?.__mf_is_remote_proxy ? exportModule : exportModule?.__esModule ? exportModule.default : exportModule.default ?? exportModule` : "export default exportModule";
1532
+ const remoteLoadRuntimePromise = command === "build" ? "__mfHostInitPromise" : "initPromise";
1533
+ const remoteLoadFailureHandler = command === "build" ? `.catch((error) => {
1534
+ delete __mfModuleCache.remote[pendingKey];
1535
+ throw error;
1536
+ })` : `.catch(() => {
1537
+ delete __mfModuleCache.remote[pendingKey];
1538
+ })`;
1711
1539
  return `
1712
1540
  ${reactImportLine}
1713
1541
  ${importLine}
1714
- ${command !== "build" ? `
1542
+ ${`
1543
+ function __mfStartRemoteLoad() {
1544
+ ${`
1545
+ const pendingKey = ${JSON.stringify(`__mf_pending__${id}`)};
1546
+ if (!__mfModuleCache.remote[pendingKey]) {
1547
+ __mfModuleCache.remote[pendingKey] = ${remoteLoadRuntimePromise}
1548
+ .then((runtime) => {
1549
+ ${registerRemoteCode}
1550
+ return runtime.loadRemote(${JSON.stringify(id)});
1551
+ })
1552
+ .then((mod) => {
1553
+ __mfModuleCache.remote[${JSON.stringify(id)}] = mod;
1554
+ delete __mfModuleCache.remote[pendingKey];
1555
+ return mod;
1556
+ })
1557
+ ${remoteLoadFailureHandler};
1558
+ }
1559
+ return __mfModuleCache.remote[pendingKey];`}
1560
+ }
1715
1561
  function __mfCreateRemoteProxy(pendingPromise) {
1716
1562
  const listeners = new Set();
1717
- pendingPromise?.finally(() => {
1718
- for (const listener of listeners) listener();
1719
- });
1563
+ const ensurePending = () => {
1564
+ pendingPromise ||= __mfStartRemoteLoad();
1565
+ pendingPromise?.finally(() => {
1566
+ for (const listener of listeners) listener();
1567
+ });
1568
+ return pendingPromise;
1569
+ };
1720
1570
  const getModule = () => __mfModuleCache.remote[${JSON.stringify(id)}];
1721
1571
  const proxyTarget = function (...args) {
1722
1572
  ${useReactProxy ? `const [, setVersion] = __mfReact.useState(0);
1723
1573
  __mfReact.useEffect(() => {
1574
+ ensurePending();
1724
1575
  const listener = () => setVersion((value) => value + 1);
1725
1576
  listeners.add(listener);
1726
1577
  if (getModule()) listener();
@@ -1731,18 +1582,33 @@ export default exportModule?.__mf_is_remote_proxy ? exportModule : exportModule?
1731
1582
  if (fn !== undefined && fn !== null) {
1732
1583
  ${useReactProxy ? `return __mfReact.createElement(fn, args[0]);` : `return fn.apply(this, args);`}
1733
1584
  }
1734
- ${useReactProxy ? `return null;` : `throw pendingPromise;`}
1585
+ ${useReactProxy ? `return null;` : `throw ensurePending();`}
1735
1586
  };
1736
1587
  return new Proxy(proxyTarget, {
1737
1588
  get(_target, prop) {
1738
1589
  if (prop === "__mf_is_remote_proxy") return true;
1739
1590
  if (prop === "__esModule") return true;
1740
1591
  if (prop === "then") return undefined;
1592
+ // Allow React's dev-mode console.warn to stringify the proxy without
1593
+ // throwing "Cannot convert object to primitive value".
1594
+ if (prop === Symbol.toPrimitive || prop === "toString")
1595
+ return () => "[MF remote proxy: pending]";
1741
1596
  const mod = getModule();
1742
1597
  if (mod) {
1743
1598
  return prop in mod ? mod[prop] : mod.default?.[prop];
1744
1599
  }
1745
- ${useReactProxy ? `return undefined;` : `throw pendingPromise;`}
1600
+ // When the module is pending and React.lazy() checks for "default",
1601
+ // return the proxy function itself so React renders it (returns null)
1602
+ // rather than crashing on undefined.
1603
+ ${useReactProxy ? `if (prop === "default") return proxyTarget;
1604
+ return undefined;` : `throw ensurePending();`}
1605
+ },
1606
+ has(_target, prop) {
1607
+ const mod = getModule();
1608
+ if (mod) return prop in mod;
1609
+ // Tell React that "default" exists when module is pending so it
1610
+ // doesn't warn "lazy: Expected the result of a dynamic import()".
1611
+ ${useReactProxy ? `return prop === "default" || prop === "__esModule" || prop === "__mf_is_remote_proxy";` : `return false;`}
1746
1612
  },
1747
1613
  ownKeys() {
1748
1614
  const mod = getModule();
@@ -1770,40 +1636,12 @@ export default exportModule?.__mf_is_remote_proxy ? exportModule : exportModule?
1770
1636
  return target.apply(thisArg, args);
1771
1637
  }
1772
1638
  });
1773
- }` : ""}
1639
+ }`}
1774
1640
  let __mfRemotePending;
1775
1641
  let exportModule = __mfModuleCache.remote[${JSON.stringify(id)}]
1776
1642
  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 = {};`}
1643
+ __mfRemotePending = __mfStartRemoteLoad();
1644
+ exportModule = __mfCreateRemoteProxy(__mfRemotePending);
1807
1645
  }
1808
1646
  ${exportLine}
1809
1647
  `;
@@ -1813,7 +1651,38 @@ export default exportModule?.__mf_is_remote_proxy ? exportModule : exportModule?
1813
1651
  function getFirstHtmlEntryFile(entryFiles) {
1814
1652
  return entryFiles.find((file) => file.endsWith(".html"));
1815
1653
  }
1816
- const addEntry = ({ entryName, entryPath, fileName, inject = "entry", forceClientInjected }) => {
1654
+ function stripQueryAndHash(file) {
1655
+ return file.split(/[?#]/)[0];
1656
+ }
1657
+ function getBuildInput(config) {
1658
+ return config.build?.rollupOptions?.input ?? config.build?.rolldownOptions?.input;
1659
+ }
1660
+ function patchHashEntryFileName(output, entryName, fileName) {
1661
+ const originalEntryFileNames = output.entryFileNames;
1662
+ output.entryFileNames = (chunkInfo, ...args) => {
1663
+ if (chunkInfo?.name === entryName) return fileName;
1664
+ if (typeof originalEntryFileNames === "function") return originalEntryFileNames(chunkInfo, ...args);
1665
+ return originalEntryFileNames || "assets/[name]-[hash].js";
1666
+ };
1667
+ }
1668
+ function patchHashEntryFileNames(config, entryName, fileName) {
1669
+ if (!fileName?.includes?.("[hash")) return;
1670
+ config.build ??= {};
1671
+ config.build.rollupOptions ??= {};
1672
+ config.build.rolldownOptions ??= {};
1673
+ const patchOutput = (output) => patchHashEntryFileName(output, entryName, fileName);
1674
+ const patchBundlerOutput = (bundlerOptions) => {
1675
+ const output = bundlerOptions.output;
1676
+ if (Array.isArray(output)) {
1677
+ output.forEach(patchOutput);
1678
+ return;
1679
+ }
1680
+ patchOutput(bundlerOptions.output ??= {});
1681
+ };
1682
+ patchBundlerOutput(config.build.rollupOptions);
1683
+ patchBundlerOutput(config.build.rolldownOptions);
1684
+ }
1685
+ const addEntry = ({ entryName, entryPath, fileName, inject = "entry", forceClientInjected, skipTransformFor = [] }) => {
1817
1686
  const DEV_HTML_PROXY_PREFIX = "virtual:mf-html-entry-proxy?";
1818
1687
  const ENTRY_BOOTSTRAP_QUERY = "?mf-entry-bootstrap";
1819
1688
  const waitsForInit = entryName === "hostInit";
@@ -1826,6 +1695,7 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry", forceClien
1826
1695
  let viteConfig;
1827
1696
  let clientInjected = forceClientInjected ?? false;
1828
1697
  let emittedFileName;
1698
+ let skipTransformIds = /* @__PURE__ */ new Set();
1829
1699
  function skipSvelteKitSsrBuild() {
1830
1700
  return (_command === "build" || viteConfig?.command === "build") && viteConfig?.build?.ssr && hasPackageDependency("@sveltejs/kit");
1831
1701
  }
@@ -1881,21 +1751,44 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry", forceClien
1881
1751
  return patched;
1882
1752
  }
1883
1753
  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
1754
  const importHelper = useSystemImportFallback ? `const __mfImport = (src) =>
1886
1755
  globalThis.System && typeof globalThis.System.import === 'function'
1887
1756
  ? globalThis.System.import(src)
1888
1757
  : import(src);
1889
1758
  ` : "";
1890
1759
  const importExpression = (src) => useSystemImportFallback ? `__mfImport(${JSON.stringify(src)})` : `import(${JSON.stringify(src)})`;
1891
- return `${getRuntimeModuleCacheBootstrapCode()}
1892
- ${importHelper}(async () => {
1893
- const { initHost } = await ${importExpression(initSrc)};
1760
+ 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(",") : "";
1761
+ const preloadBlock = remotePreloads ? `
1894
1762
  const runtime = await initHost();
1763
+ const __mfPreloadRemote = (remote) => {
1764
+ const pendingKey = "__mf_pending__" + remote;
1765
+ if (!__mfModuleCache.remote[pendingKey]) {
1766
+ __mfModuleCache.remote[pendingKey] = runtime.loadRemote(remote)
1767
+ .then((mod) => {
1768
+ __mfModuleCache.remote[remote] = mod;
1769
+ delete __mfModuleCache.remote[pendingKey];
1770
+ return mod;
1771
+ })
1772
+ .catch((error) => {
1773
+ delete __mfModuleCache.remote[pendingKey];
1774
+ throw error;
1775
+ });
1776
+ }
1777
+ return __mfModuleCache.remote[pendingKey];
1778
+ };
1895
1779
  const __mfRemotePreloads = [${remotePreloads}];
1896
- await Promise.all(__mfRemotePreloads);
1780
+ await Promise.allSettled(__mfRemotePreloads);` : `await initHost();`;
1781
+ const importCode = `
1782
+ (async () => {
1783
+ const { initHost } = await ${importExpression(initSrc)};
1784
+ ${preloadBlock}
1897
1785
  })().then(() => ${importExpression(entrySrc)});
1898
1786
  `;
1787
+ return [
1788
+ getRuntimeModuleCacheBootstrapCode(),
1789
+ importHelper,
1790
+ importCode
1791
+ ].join("\n");
1899
1792
  }
1900
1793
  function getSystemBootstrapSource(initSrc, entrySrc) {
1901
1794
  return getBootstrapSource(initSrc, entrySrc, true);
@@ -1910,6 +1803,29 @@ ${importHelper}(async () => {
1910
1803
  function normalizeDevHtmlProxyId(id) {
1911
1804
  return id.replace(/^\0/, "").replace(/^\/@id\//, "").replace(/^__x00__/, "");
1912
1805
  }
1806
+ function normalizeModuleId(id) {
1807
+ return id.split("?")[0].replace(/\\/g, "/");
1808
+ }
1809
+ function resolveProjectId(id) {
1810
+ if (id.startsWith("\0") || id.startsWith("virtual:")) return normalizeModuleId(id);
1811
+ return normalizeModuleId(path$1.isAbsolute(id) ? id : path$1.resolve(viteConfig.root, id));
1812
+ }
1813
+ function addEntryFile(file) {
1814
+ const normalized = normalizeModuleId(file);
1815
+ if (!entryFiles.includes(normalized)) entryFiles.push(normalized);
1816
+ }
1817
+ function addHtmlScriptEntries(htmlPath) {
1818
+ if (!fs$1.existsSync(htmlPath)) return;
1819
+ const htmlContent = fs$1.readFileSync(htmlPath, "utf-8");
1820
+ const scriptRegex = /<script\b(?=[^>]*\btype=["']module["'])(?=[^>]*\bsrc=["']([^"']+)["'])[^>]*>/gi;
1821
+ let match;
1822
+ while ((match = scriptRegex.exec(htmlContent)) !== null) {
1823
+ const scriptSrc = stripQueryAndHash(match[1]);
1824
+ if (/^(?:[a-z]+:)?\/\//i.test(scriptSrc)) continue;
1825
+ addEntryFile(scriptSrc);
1826
+ addEntryFile(scriptSrc.startsWith("/") ? path$1.resolve(viteConfig.root, scriptSrc.slice(1)) : path$1.resolve(path$1.dirname(htmlPath), scriptSrc));
1827
+ }
1828
+ }
1913
1829
  return [{
1914
1830
  name: "add-entry",
1915
1831
  apply: "serve",
@@ -1926,6 +1842,7 @@ ${importHelper}(async () => {
1926
1842
  const relativePath = normalized.startsWith(root + "/") ? normalized.slice(root.length) : "/" + normalized.replace(/^[A-Za-z]:[\\/]/, "");
1927
1843
  devEntryPath = config.base + relativePath.replace(/^\//, "");
1928
1844
  }
1845
+ skipTransformIds = new Set(skipTransformFor.map(resolveProjectId));
1929
1846
  },
1930
1847
  configureServer(server) {
1931
1848
  server.middlewares.use((req, res, next) => {
@@ -1953,7 +1870,8 @@ ${importHelper}(async () => {
1953
1870
  transformIndexHtml: {
1954
1871
  order: "pre",
1955
1872
  handler(c) {
1956
- if (!injectHtml()) return;
1873
+ const shouldWrapEntryHtml = _command === "serve" && inject === "entry" && waitsForInit;
1874
+ if (!injectHtml() && !shouldWrapEntryHtml) return;
1957
1875
  clientInjected = true;
1958
1876
  const base = viteConfig.base.replace(/\/$/, "");
1959
1877
  const stripBase = (p) => base && p.startsWith(base + "/") ? p.slice(base.length) : p;
@@ -1985,24 +1903,30 @@ ${importHelper}(async () => {
1985
1903
  }, {
1986
1904
  name: "add-entry",
1987
1905
  enforce: "post",
1906
+ applyToEnvironment() {
1907
+ return true;
1908
+ },
1909
+ config(config) {
1910
+ patchHashEntryFileNames(config, entryName, fileName);
1911
+ },
1988
1912
  configResolved(config) {
1989
1913
  viteConfig = config;
1990
- const inputOptions = config.build.rollupOptions.input;
1914
+ skipTransformIds = new Set(skipTransformFor.map(resolveProjectId));
1915
+ const ctx = this;
1916
+ const envName = ctx != null && typeof ctx === "object" ? ctx["environment"] : void 0;
1917
+ if (envName?.name && envName.name !== "client") return;
1918
+ const inputOptions = getBuildInput(config);
1991
1919
  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);
1920
+ else if (typeof inputOptions === "string") entryFiles = [normalizeModuleId(inputOptions)];
1921
+ else if (Array.isArray(inputOptions)) entryFiles = inputOptions.map(normalizeModuleId);
1922
+ else if (typeof inputOptions === "object") entryFiles = Object.values(inputOptions).map((input) => normalizeModuleId(String(input)));
1995
1923
  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
- }
1924
+ if (htmlFilePath) addHtmlScriptEntries(htmlFilePath);
2002
1925
  },
2003
1926
  buildStart() {
2004
1927
  if (_command === "serve") return;
2005
1928
  if (skipSvelteKitSsrBuild()) return;
1929
+ if (this.environment?.name === "ssr") return;
2006
1930
  const hasHash = fileName?.includes?.("[hash");
2007
1931
  const emitFileOptions = {
2008
1932
  name: entryName,
@@ -2012,16 +1936,14 @@ ${importHelper}(async () => {
2012
1936
  };
2013
1937
  if (!hasHash) emitFileOptions.fileName = fileName;
2014
1938
  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
- }
1939
+ if (htmlFilePath) addHtmlScriptEntries(htmlFilePath);
2021
1940
  },
2022
1941
  generateBundle(_options, bundle) {
2023
1942
  if (skipSvelteKitSsrBuild()) return;
2024
1943
  if (!injectHtml()) return;
1944
+ if (!emitFileId) return;
1945
+ const htmlFileNames = Object.keys(bundle).filter((fileName) => fileName.endsWith(".html"));
1946
+ if (htmlFileNames.length === 0) return;
2025
1947
  const file = this.getFileName(emitFileId);
2026
1948
  emittedFileName = file;
2027
1949
  const resolvePath = (htmlFileName) => {
@@ -2043,7 +1965,7 @@ ${importHelper}(async () => {
2043
1965
  return viteConfig.base + file;
2044
1966
  };
2045
1967
  let bootstrapIndex = 0;
2046
- for (const fileName in bundle) if (fileName.endsWith(".html")) {
1968
+ for (const fileName of htmlFileNames) {
2047
1969
  let htmlAsset = bundle[fileName];
2048
1970
  if (htmlAsset.type === "chunk") return;
2049
1971
  let htmlContent = htmlAsset.source.toString() || "";
@@ -2052,11 +1974,13 @@ ${importHelper}(async () => {
2052
1974
  let rewritten = false;
2053
1975
  htmlContent = htmlContent.replace(scriptRegex, (scriptTag, entrySrc) => {
2054
1976
  rewritten = true;
2055
- const bootstrapFileName = `mf-entry-bootstrap-${bootstrapIndex++}.js`;
1977
+ const bootstrapSource = getSystemBootstrapSource(initPath, entrySrc);
1978
+ const bootstrapHash = createHash("sha256").update(bootstrapSource).digest("hex").slice(0, 8);
1979
+ const bootstrapFileName = `mf-entry-bootstrap-${bootstrapIndex++}-${bootstrapHash}.js`;
2056
1980
  const bootstrapRef = this.emitFile({
2057
1981
  type: "asset",
2058
1982
  fileName: bootstrapFileName,
2059
- source: getSystemBootstrapSource(initPath, entrySrc)
1983
+ source: bootstrapSource
2060
1984
  });
2061
1985
  const bootstrapPath = viteConfig.base + this.getFileName(bootstrapRef);
2062
1986
  return scriptTag.replace(entrySrc, bootstrapPath);
@@ -2088,6 +2012,11 @@ ${importHelper}(async () => {
2088
2012
  if (skipSvelteKitSsrBuild()) return;
2089
2013
  if (isSvelteKitServerModule(id)) return;
2090
2014
  if (id.includes(ENTRY_BOOTSTRAP_QUERY)) return;
2015
+ if (normalizeModuleId(id).endsWith(".html")) return;
2016
+ if (skipTransformIds.has(resolveProjectId(id))) return;
2017
+ const transformCtx = this;
2018
+ const transformEnv = transformCtx != null && typeof transformCtx === "object" ? transformCtx["environment"] : void 0;
2019
+ if (transformEnv?.name && transformEnv.name !== "client") return;
2091
2020
  const isVinext = hasPackageDependency("vinext");
2092
2021
  if (isVinext && inject === "html" && id.includes("virtual:vite-rsc/remove-duplicate-server-css")) {
2093
2022
  const namespaceReactImport = `import * as React from 'react';`;
@@ -2104,9 +2033,16 @@ ${importHelper}(async () => {
2104
2033
  clientInjected = true;
2105
2034
  return mapCodeToCodeWithSourcemap(injection + code);
2106
2035
  }
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)) {
2036
+ if (_command === "serve" && inject === "entry" && waitsForInit && !clientInjected && /(?:^|\/)nuxt\/dist\/app\/entry\.js(?:\?|$)/.test(id) && code.includes("vueApp.mount(vueAppRootContainer);")) {
2108
2037
  clientInjected = true;
2109
- if (!waitsForInit) return mapCodeToCodeWithSourcemap(`import ${JSON.stringify(getEntryPath())};\n` + code);
2038
+ const injection = `await import(${JSON.stringify(getEntryPath())}).then(({ initHost }) => initHost());\n `;
2039
+ return mapCodeToCodeWithSourcemap(code.replace("vueApp.mount(vueAppRootContainer);", `${injection}vueApp.mount(vueAppRootContainer);`));
2040
+ }
2041
+ 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);
2042
+ 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();");
2043
+ 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) {
2044
+ clientInjected = true;
2045
+ if (!waitsForInit || _command === "serve" && inject === "entry" && isHydrationEntryFallback) return mapCodeToCodeWithSourcemap(`import ${JSON.stringify(getEntryPath())};\n` + code);
2110
2046
  const entrySrc = id.includes("?") ? `${id}&${ENTRY_BOOTSTRAP_QUERY.slice(1)}` : `${id}${ENTRY_BOOTSTRAP_QUERY}`;
2111
2047
  return mapCodeToCodeWithSourcemap(getBootstrapSource(getEntryPath(), entrySrc));
2112
2048
  }
@@ -2158,11 +2094,7 @@ function checkAliasConflicts(options) {
2158
2094
  };
2159
2095
  }
2160
2096
  //#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;
2097
+ //#region src/plugins/hmr/react.ts
2166
2098
  /**
2167
2099
  * Proxy module served for `/@react-refresh` on MF remote dev servers.
2168
2100
  * Delegates to the host page's RefreshRuntime instance via
@@ -2182,6 +2114,112 @@ const REACT_REFRESH_PROXY_MODULE = [
2182
2114
  `export const __hmr_import = __rt.__hmr_import;`,
2183
2115
  `export default __rt.default || __rt;`
2184
2116
  ].join("\n");
2117
+ const reactAdapter = {
2118
+ name: "react",
2119
+ pluginNames: ["vite:react-refresh", "vite:react-swc:refresh"],
2120
+ remote: { configureServer({ server }) {
2121
+ server.middlewares.use((req, res, next) => {
2122
+ if (req.url?.replace(/\?.*$/, "") !== "/@react-refresh") return next();
2123
+ res.setHeader("Content-Type", "application/javascript; charset=utf-8");
2124
+ res.setHeader("Access-Control-Allow-Origin", "*");
2125
+ res.end(REACT_REFRESH_PROXY_MODULE);
2126
+ });
2127
+ } }
2128
+ };
2129
+ //#endregion
2130
+ //#region src/plugins/hmr/vue.ts
2131
+ /**
2132
+ * In dev mode each Vite dev server serves its own copy of Vue
2133
+ * (`/node_modules/.vite/deps/vue.js`). When a remote module is loaded into the
2134
+ * host page, the remote's Vue copy evaluates and runs:
2135
+ *
2136
+ * globalThis.__VUE_HMR_RUNTIME__ = createHotReloadAPI()
2137
+ *
2138
+ * which silently overwrites the host's runtime. After that, `createRecord` for
2139
+ * host components lives in the orphaned first runtime, but `reload(hmrId, ...)`
2140
+ * goes through the second runtime — the lookup misses and HMR stops working.
2141
+ *
2142
+ * This guard pins `__VUE_HMR_RUNTIME__` to the first writer (the host's Vue)
2143
+ * via a property trap on `globalThis`. Subsequent writes from remote-side Vue
2144
+ * copies are silently dropped. Must execute before any Vue module loads —
2145
+ * injected as a plain (non-module) script at `head-prepend`.
2146
+ *
2147
+ * `singleton: true` in `shared` is not sufficient: in dev mode MF's share-scope
2148
+ * does not actually dedupe Vue across dev servers, so without this guard the
2149
+ * last-loaded copy wins.
2150
+ */
2151
+ const VUE_HMR_RUNTIME_GUARD_SCRIPT = `
2152
+ (function () {
2153
+ var h = null;
2154
+ Object.defineProperty(globalThis, '__VUE_HMR_RUNTIME__', {
2155
+ get: function () { return h; },
2156
+ set: function (v) { if (h === null) h = v; },
2157
+ configurable: true,
2158
+ enumerable: true,
2159
+ });
2160
+ })();`;
2161
+ /**
2162
+ * `@vitejs/plugin-vue` derives an SFC's `__hmrId` from a hash of the file path
2163
+ * relative to Vite's `root`. With module federation, host and remote are
2164
+ * separate Vite projects with independent roots — so an SFC at `src/App.vue`
2165
+ * in both will hash to the same id. Once both copies of Vue collapse onto the
2166
+ * shared `__VUE_HMR_RUNTIME__` (see the guard above), the host's instance and
2167
+ * the remote's instance both register under that single id. A remote-only
2168
+ * file change then calls `rerender(id, newRender)`, which iterates *every*
2169
+ * instance under that id — including the host one — and applies the remote's
2170
+ * render function to the host instance. The host's `setupState` doesn't have
2171
+ * the remote's bindings, so the render throws and Vue falls back to a full
2172
+ * reload required warning.
2173
+ *
2174
+ * Fix: rewrite the remote's emitted `__hmrId` literal to be prefixed with the
2175
+ * federation `name`, so the remote's instances live under a distinct key. The
2176
+ * accept callback emitted by plugin-vue reads `_sfc_main.__hmrId` /
2177
+ * `updated.__hmrId` at runtime, so rewriting the literal once is enough.
2178
+ */
2179
+ const SFC_HMR_ID_LITERAL_RE = /(\.__hmrId\s*=\s*["'`])([^"'`]+)(["'`])/g;
2180
+ function rewriteSfcHmrId(code, federationName) {
2181
+ let matched = false;
2182
+ return {
2183
+ code: code.replace(SFC_HMR_ID_LITERAL_RE, (_match, prefix, id, suffix) => {
2184
+ matched = true;
2185
+ if (id.startsWith(`${federationName}-`)) return `${prefix}${id}${suffix}`;
2186
+ return `${prefix}${federationName}-${id}${suffix}`;
2187
+ }),
2188
+ matched
2189
+ };
2190
+ }
2191
+ let pluginVueRegressionWarned = false;
2192
+ function warnPluginVueRegression() {
2193
+ if (pluginVueRegressionWarned) return;
2194
+ pluginVueRegressionWarned = true;
2195
+ 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.");
2196
+ }
2197
+ const vueAdapter = {
2198
+ name: "vue",
2199
+ pluginNames: ["vite:vue", "vite:vue-jsx"],
2200
+ host: { transformIndexHtml() {
2201
+ return [{
2202
+ tag: "script",
2203
+ children: VUE_HMR_RUNTIME_GUARD_SCRIPT,
2204
+ injectTo: "head-prepend"
2205
+ }];
2206
+ } },
2207
+ remote: { transform(code, _id, ctx) {
2208
+ if (!code.includes("__VUE_HMR_RUNTIME__.createRecord(")) return;
2209
+ const { code: rewritten, matched } = rewriteSfcHmrId(code, ctx.options.name);
2210
+ if (!matched) {
2211
+ warnPluginVueRegression();
2212
+ return;
2213
+ }
2214
+ return rewritten === code ? void 0 : rewritten;
2215
+ } }
2216
+ };
2217
+ //#endregion
2218
+ //#region src/plugins/hmr/fullReload.ts
2219
+ const REMOTE_HMR_ENDPOINT = "__mf_hmr";
2220
+ const REMOTE_HMR_EVENT = "mf:remote-update";
2221
+ const REMOTE_HMR_CONNECT_RETRY_DELAY_MS = 1e3;
2222
+ const REMOTE_HMR_CONNECT_MAX_RETRIES = 10;
2185
2223
  function getBasePath$1(base) {
2186
2224
  if (!base) return "/";
2187
2225
  if (base.startsWith("http://") || base.startsWith("https://")) try {
@@ -2200,9 +2238,6 @@ function getHmrWsPath(base, hmrPath) {
2200
2238
  if (!normalizedPath || normalizedPath === "/") return normalizedBase;
2201
2239
  return `${normalizedBase.endsWith("/") ? normalizedBase.slice(0, -1) : normalizedBase}/${normalizedPath.startsWith("/") ? normalizedPath.slice(1) : normalizedPath}`;
2202
2240
  }
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
2241
  function getRemoteHmrWsUrl(server) {
2207
2242
  const hmr = server.config.server.hmr;
2208
2243
  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 +2276,275 @@ function getStringPreview(value, max = 180) {
2241
2276
  } catch {}
2242
2277
  return rawValue.slice(0, max);
2243
2278
  }
2244
- function isRemoteHmrEnabled(dev) {
2245
- return typeof dev === "object" && dev !== null && !!dev.remoteHmr;
2279
+ /**
2280
+ * Installs the `/__mf_hmr` metadata endpoint on a remote dev server. The host
2281
+ * fetches this to discover the remote's HMR WebSocket URL before opening a
2282
+ * Node-to-Node relay socket. Always installed on remotes when `remoteHmr` is
2283
+ * enabled — under `'native'` strategy the endpoint is unused but harmless;
2284
+ * under `'full-reload'` it's the discovery hop for the host relay.
2285
+ */
2286
+ function setupRemoteMetadataEndpoint(server, options) {
2287
+ const endpointPath = getRemoteHmrPath(server.config.base);
2288
+ const wsUrl = getRemoteHmrWsUrl(server);
2289
+ server.middlewares.use((req, res, next) => {
2290
+ if (req.url?.replace(/\?.*/, "") !== endpointPath) {
2291
+ next();
2292
+ return;
2293
+ }
2294
+ res.setHeader("Content-Type", "application/json");
2295
+ res.setHeader("Access-Control-Allow-Origin", "*");
2296
+ res.end(JSON.stringify({
2297
+ remote: options.name,
2298
+ event: REMOTE_HMR_EVENT,
2299
+ wsUrl
2300
+ }));
2301
+ });
2246
2302
  }
2247
2303
  /**
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).
2304
+ * Installs file-watcher broadcasts on a remote dev server. Every non-ignored
2305
+ * change/add/unlink emits a `mf:remote-update` custom event on the remote's
2306
+ * own WS channel. The host relay (see `setupHostFullReloadRelay`) listens
2307
+ * for these events and triggers a host-side full reload in response.
2251
2308
  *
2252
- * Currently only React is supported via the shared /@react-refresh proxy.
2309
+ * Only called under the `'full-reload'` strategy.
2253
2310
  */
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
- });
2314
- }
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);
2311
+ function setupRemoteBroadcast(server, options) {
2312
+ const broadcast = (file) => {
2313
+ if (shouldIgnoreFile(file, options)) return;
2314
+ server.ws.send({
2315
+ type: "custom",
2316
+ event: REMOTE_HMR_EVENT,
2317
+ data: {
2318
+ remote: options.name,
2319
+ file,
2320
+ ts: Date.now()
2392
2321
  }
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
2322
  });
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
2323
  };
2431
- };
2432
- const buildDtsModuleFederationConfig = (options) => {
2433
- const exposes = {};
2434
- Object.entries(options.exposes).forEach(([key, value]) => {
2435
- if (value.import) exposes[key] = value.import;
2436
- });
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}`;
2324
+ server.watcher.on("change", broadcast);
2325
+ server.watcher.on("add", broadcast);
2326
+ server.watcher.on("unlink", broadcast);
2327
+ server.httpServer?.once("close", () => {
2328
+ server.watcher.off("change", broadcast);
2329
+ server.watcher.off("add", broadcast);
2330
+ server.watcher.off("unlink", broadcast);
2441
2331
  });
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`)
2332
+ }
2333
+ /**
2334
+ * Installs the host-side full-reload relay. For each configured remote:
2335
+ * 1. Fetches the remote's `/__mf_hmr` metadata to get its WS URL.
2336
+ * 2. Opens a Node-to-Node WebSocket to that URL.
2337
+ * 3. On any `mf:remote-update` message, broadcasts `{ type: 'full-reload' }`
2338
+ * to the host's own browser-facing WS.
2339
+ *
2340
+ * Also reloads on local host file changes. Retries failed connections up to
2341
+ * `REMOTE_HMR_CONNECT_MAX_RETRIES` times with a fixed delay.
2342
+ *
2343
+ * Only called under the `'full-reload'` strategy.
2344
+ */
2345
+ function setupHostFullReloadRelay(server, options) {
2346
+ const connections = [];
2347
+ const reconnectTimers = /* @__PURE__ */ new Map();
2348
+ let isTearingDown = false;
2349
+ const clearReconnectTimer = (remoteName) => {
2350
+ const timer = reconnectTimers.get(remoteName);
2351
+ if (!timer) return;
2352
+ clearTimeout(timer);
2353
+ reconnectTimers.delete(remoteName);
2496
2354
  };
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();
2355
+ const scheduleReconnect = (remoteName, remote, attempt, reason) => {
2356
+ if (isTearingDown) return;
2357
+ if (attempt >= REMOTE_HMR_CONNECT_MAX_RETRIES) {
2358
+ mfWarn(`Remote "${remoteName}" full HMR reconnect skipped after ${REMOTE_HMR_CONNECT_MAX_RETRIES} attempts: ${reason}`);
2505
2359
  return;
2506
2360
  }
2507
- const filePath = isZipRequest ? assetPaths.zipFilePath : assetPaths.apiFilePath;
2508
- if (!fs.existsSync(filePath)) {
2509
- res.statusCode = 404;
2510
- res.end();
2361
+ clearReconnectTimer(remoteName);
2362
+ const timer = setTimeout(() => {
2363
+ reconnectTimers.delete(remoteName);
2364
+ connectRemote(remoteName, remote, attempt + 1);
2365
+ }, REMOTE_HMR_CONNECT_RETRY_DELAY_MS);
2366
+ reconnectTimers.set(remoteName, timer);
2367
+ };
2368
+ const connectRemote = async (remoteName, remote, attempt = 0) => {
2369
+ if (isTearingDown) return;
2370
+ const endpoint = getRemoteHmrEndpoint(remote.entry, server);
2371
+ if (!endpoint) {
2372
+ mfWarn(`Failed to build HMR endpoint URL for remote "${remoteName}"`);
2511
2373
  return;
2512
2374
  }
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;
2375
+ try {
2376
+ const metadataResponse = await fetch(endpoint);
2377
+ if (!metadataResponse.ok) {
2378
+ mfWarn(`Failed to fetch remote HMR metadata from "${remoteName}": ${metadataResponse.status}`);
2379
+ scheduleReconnect(remoteName, remote, attempt, `HTTP ${metadataResponse.status}`);
2380
+ return;
2381
+ }
2382
+ const metadata = await metadataResponse.json();
2383
+ if (metadata.event !== "mf:remote-update" || !metadata.wsUrl) {
2384
+ mfWarn(`Remote "${remoteName}" returned unexpected HMR metadata shape`);
2385
+ return;
2386
+ }
2387
+ const ws = new WebSocket(metadata.wsUrl, "vite-hmr");
2388
+ ws.onmessage = (rawEvent) => {
2389
+ const message = parseRemoteHmrMessage(rawEvent.data);
2390
+ if (!message || message.event !== "mf:remote-update") return;
2391
+ server.ws.send({ type: "full-reload" });
2392
+ };
2393
+ ws.onopen = () => clearReconnectTimer(remoteName);
2394
+ ws.onerror = (error) => mfWarn(`Remote HMR socket error for "${remoteName}":`, error);
2395
+ ws.onclose = () => scheduleReconnect(remoteName, remote, attempt, "socket closed");
2396
+ connections.push(ws);
2397
+ } catch (error) {
2398
+ mfWarn(`Failed to connect remote HMR for "${remoteName}" on attempt ${attempt + 1}: ${getStringPreview(error)}`);
2399
+ scheduleReconnect(remoteName, remote, attempt, getStringPreview(error));
2518
2400
  }
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();
2401
+ };
2402
+ const teardown = () => {
2403
+ isTearingDown = true;
2404
+ reconnectTimers.forEach((timer) => clearTimeout(timer));
2405
+ reconnectTimers.clear();
2406
+ connections.forEach((connection) => {
2407
+ if (connection.readyState !== connection.CLOSING && connection.readyState !== connection.CLOSED) connection.close();
2526
2408
  });
2527
- stream.pipe(res);
2409
+ connections.length = 0;
2528
2410
  };
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)
2411
+ for (const [remoteName, remote] of Object.entries(options.remotes)) connectRemote(remoteName, remote);
2412
+ const triggerHostReload = (file) => {
2413
+ if (shouldIgnoreFile(file, options)) return;
2414
+ server.ws.send({ type: "full-reload" });
2415
+ };
2416
+ server.watcher.on("change", triggerHostReload);
2417
+ server.watcher.on("add", triggerHostReload);
2418
+ server.watcher.on("unlink", triggerHostReload);
2419
+ server.httpServer?.once("close", teardown);
2420
+ }
2421
+ //#endregion
2422
+ //#region src/plugins/pluginDevRemoteHmr.ts
2423
+ /**
2424
+ * Clears the federation runtime's `moduleCache` on every Vite `vite:beforeUpdate`
2425
+ * event. Without this, after a remote SFC change Vite would patch the in-memory
2426
+ * component, but `loadRemote()` would still return the cached stale module on
2427
+ * the next call (e.g. after route navigation), making the patched version
2428
+ * effectively unreachable.
2429
+ */
2430
+ const FEDERATION_MODULE_CACHE_CLEAR_SCRIPT = `
2431
+ if (import.meta.hot) {
2432
+ import.meta.hot.on('vite:beforeUpdate', function () {
2433
+ try {
2434
+ var f = globalThis.__FEDERATION__ || globalThis.__VMOK__;
2435
+ if (!f || !f.__INSTANCES__) return;
2436
+ for (var i = 0; i < f.__INSTANCES__.length; i++) {
2437
+ if (f.__INSTANCES__[i] && f.__INSTANCES__[i].moduleCache)
2438
+ f.__INSTANCES__[i].moduleCache.clear();
2439
+ }
2440
+ } catch (e) {}
2441
+ });
2442
+ }`;
2443
+ const HMR_ADAPTERS = [reactAdapter, vueAdapter];
2444
+ function resolveAdapters(plugins) {
2445
+ const pluginNames = new Set(plugins.map((p) => p.name));
2446
+ return HMR_ADAPTERS.filter((adapter) => adapter.pluginNames.some((name) => pluginNames.has(name)));
2447
+ }
2448
+ function hasCrossFederationHmr(plugins) {
2449
+ return resolveAdapters(plugins).length > 0;
2450
+ }
2451
+ function isRemoteHmrEnabled(dev) {
2452
+ return typeof dev === "object" && dev !== null && !!dev.remoteHmr;
2453
+ }
2454
+ /**
2455
+ * `'native'` — a matched framework adapter owns HMR through Vite's native
2456
+ * channel (e.g. React Fast Refresh via the `/@react-refresh` proxy, Vue's
2457
+ * patched `__VUE_HMR_RUNTIME__`). The broadcast/relay path stays idle.
2458
+ *
2459
+ * `'full-reload'` — no adapter matched, or the user explicitly opted in with
2460
+ * `remoteHmr: 'full-reload'` to bypass adapters: the plugin's broadcast/relay
2461
+ * machinery triggers a page reload on every remote file change.
2462
+ */
2463
+ function resolveHmrStrategy(dev, plugins) {
2464
+ if (typeof dev === "object" && dev !== null && dev.remoteHmr === "full-reload") return "full-reload";
2465
+ return hasCrossFederationHmr(plugins) ? "native" : "full-reload";
2466
+ }
2467
+ function shouldIgnoreFile(file, options) {
2468
+ 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");
2469
+ }
2470
+ function collectHostTags(server, options, adapters) {
2471
+ const ctx = {
2472
+ server,
2473
+ options
2474
+ };
2475
+ const tags = [];
2476
+ for (const adapter of adapters) {
2477
+ const adapterTags = adapter.host?.transformIndexHtml?.(ctx);
2478
+ if (adapterTags) tags.push(...adapterTags);
2479
+ }
2480
+ tags.push({
2481
+ tag: "script",
2482
+ attrs: { type: "module" },
2483
+ children: FEDERATION_MODULE_CACHE_CLEAR_SCRIPT,
2484
+ injectTo: "head"
2549
2485
  });
2550
- let resolvedConfig;
2551
- let devWorker;
2552
- let normalizedDevOptions;
2553
- let hasGeneratedBundle = false;
2554
- return [{
2555
- name: "module-federation-dts-dev",
2486
+ return tags;
2487
+ }
2488
+ function pluginDevRemoteHmr(options) {
2489
+ const isHost = Object.keys(options.remotes).length > 0;
2490
+ const isRemote = Object.keys(options.exposes).length > 0;
2491
+ let adapters = [];
2492
+ let strategy = "full-reload";
2493
+ return {
2494
+ name: "module-federation-dev-remote-hmr",
2556
2495
  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
2496
  configResolved(config) {
2567
- resolvedConfig = config;
2497
+ adapters = resolveAdapters(config.plugins);
2498
+ strategy = resolveHmrStrategy(options.dev, config.plugins);
2568
2499
  },
2569
2500
  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);
2501
+ if (!isRemoteHmrEnabled(options.dev)) return;
2502
+ if (isRemote) {
2503
+ for (const adapter of adapters) adapter.remote?.configureServer?.({
2504
+ server,
2505
+ options
2635
2506
  });
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;
2507
+ setupRemoteMetadataEndpoint(server, options);
2508
+ if (strategy === "full-reload") setupRemoteBroadcast(server, options);
2657
2509
  }
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)
2510
+ if (isHost) {
2511
+ for (const adapter of adapters) adapter.host?.configureServer?.({
2512
+ server,
2513
+ options
2667
2514
  });
2668
- } catch (error) {
2669
- logDtsError(error, normalizedDtsOptions);
2670
- return;
2515
+ if (strategy === "full-reload") setupHostFullReloadRelay(server, options);
2671
2516
  }
2672
- if (consumeOptions?.host?.typesOnBuild) try {
2673
- await consumeTypesAPI(consumeOptions);
2674
- } catch (error) {
2675
- logDtsError(error, normalizedDtsOptions);
2676
- }
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;
2517
+ },
2518
+ transform: {
2519
+ order: "post",
2520
+ handler(code, id) {
2521
+ if (!isRemote || !isRemoteHmrEnabled(options.dev)) return;
2522
+ if (!adapters.length) return;
2523
+ let result = code;
2524
+ const adapterCtx = { options };
2525
+ for (const adapter of adapters) {
2526
+ const next = adapter.remote?.transform?.(result, id, adapterCtx);
2527
+ if (typeof next === "string") result = next;
2528
+ }
2529
+ return result === code ? void 0 : result;
2688
2530
  }
2689
- if (!generateOptions) return;
2690
- try {
2691
- await generateTypesAPI({ dtsManagerOptions: generateOptions });
2692
- } catch (error) {
2693
- logDtsError(error, normalizedDtsOptions);
2531
+ },
2532
+ transformIndexHtml: {
2533
+ order: "pre",
2534
+ handler(_html, ctx) {
2535
+ if (!isRemoteHmrEnabled(options.dev)) return;
2536
+ if (!isHost || !ctx.server) return;
2537
+ return collectHostTags(ctx.server, options, adapters);
2694
2538
  }
2695
2539
  }
2696
- }];
2540
+ };
2697
2541
  }
2698
2542
  //#endregion
2699
2543
  //#region src/virtualModules/index.ts
2700
- function initVirtualModules(command, remoteEntryId) {
2544
+ function initVirtualModules(command, remoteEntryId, enableSsrInit = false) {
2701
2545
  writeLocalSharedImportMap();
2702
2546
  writeHostAutoInit(remoteEntryId, command);
2703
- writeRuntimeInitStatus(command);
2547
+ writeRuntimeInitStatus(command, enableSsrInit);
2704
2548
  }
2705
2549
  //#endregion
2706
2550
  //#region src/utils/bundleHelpers.ts
@@ -3050,6 +2894,12 @@ function removeTrailingSlash(value) {
3050
2894
  function ensureTrailingSlash(value) {
3051
2895
  return `${removeTrailingSlash(value)}/`;
3052
2896
  }
2897
+ function getBasePath(base) {
2898
+ return removeTrailingSlash(base || "/");
2899
+ }
2900
+ function isNuxtClientBase(base) {
2901
+ return getBasePath(base).endsWith("/_nuxt");
2902
+ }
3053
2903
  function normalizeNodeModulePath(source) {
3054
2904
  return source.replace(/\\/g, "/").replace(/\?.*$/, "");
3055
2905
  }
@@ -3083,6 +2933,116 @@ function resolvePublicPath(options, viteBase, originalBase) {
3083
2933
  return "auto";
3084
2934
  }
3085
2935
  //#endregion
2936
+ //#region src/virtualModules/virtualExposesSSR.ts
2937
+ /**
2938
+ * Virtual module ID for the SSR exposes map.
2939
+ * Separate from the browser exposes: no CSS injection, no document references,
2940
+ * and shared packages are imported as bare specifiers (externals in the SSR build).
2941
+ */
2942
+ function getVirtualExposesSSRId(options) {
2943
+ return `virtual:mf-exposes-ssr:${`${options.internalName}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
2944
+ }
2945
+ /**
2946
+ * Generates the SSR exposes map module.
2947
+ *
2948
+ * Differences from the browser version (virtualExposes.ts):
2949
+ * - No CSS asset injection (document APIs unavailable on Node)
2950
+ * - Shared packages (react, react-dom, etc.) must be externals in the SSR
2951
+ * build so Node resolves them via its own module cache — this is what
2952
+ * guarantees the React singleton is shared with react-dom/server.
2953
+ */
2954
+ function generateExposesSSR(options) {
2955
+ return `
2956
+ export default {
2957
+ ${Object.keys(options.exposes).map((key) => {
2958
+ return `
2959
+ ${JSON.stringify(key)}: async () => {
2960
+ const importModule = await import(${JSON.stringify(options.exposes[key].import)})
2961
+ const exportModule = {}
2962
+ Object.assign(exportModule, importModule)
2963
+ Object.defineProperty(exportModule, "__esModule", {
2964
+ value: true,
2965
+ enumerable: false
2966
+ })
2967
+ return exportModule
2968
+ }
2969
+ `;
2970
+ }).join(",")}
2971
+ }
2972
+ `;
2973
+ }
2974
+ //#endregion
2975
+ //#region src/virtualModules/virtualRemoteEntrySSR.ts
2976
+ const REMOTE_ENTRY_SSR_ID = "virtual:mf-REMOTE_ENTRY_SSR_ID";
2977
+ function getRemoteEntrySSRId(options) {
2978
+ return `${REMOTE_ENTRY_SSR_ID}:${`${options.internalName}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
2979
+ }
2980
+ function getSsrRemoteEntryFileName(browserFilename) {
2981
+ const ext = browserFilename.match(/\.[^.]+$/)?.[0] || ".js";
2982
+ return `${browserFilename.slice(0, browserFilename.length - ext.length)}.ssr${ext}`;
2983
+ }
2984
+ /**
2985
+ * Generates the SSR remote entry module.
2986
+ *
2987
+ * This is intentionally minimal — no HMR shim, no loadShare virtual modules,
2988
+ * no browser globals. Shared packages (react, react-dom, etc.) are imported
2989
+ * as externals by the SSR build, so Node's require cache provides the singleton.
2990
+ *
2991
+ * The container API (init / get) mirrors the browser entry so the MF runtime
2992
+ * can call it the same way on the server.
2993
+ */
2994
+ function generateRemoteEntrySSR(options) {
2995
+ const virtualExposesSSRId = getVirtualExposesSSRId(options);
2996
+ return `
2997
+ import { init as runtimeInit } from "@module-federation/runtime";
2998
+
2999
+ let exposesMapPromise;
3000
+
3001
+ async function getExposesMap() {
3002
+ exposesMapPromise ??= import(${JSON.stringify(virtualExposesSSRId)}).then((mod) => mod.default ?? mod);
3003
+ return exposesMapPromise;
3004
+ }
3005
+
3006
+ /**
3007
+ * Called by the MF runtime on the host to register this remote's share scope.
3008
+ * On the server the host has already initialised the runtime, so we just need
3009
+ * to set up a minimal runtime instance for the remote container.
3010
+ */
3011
+ async function init(shared = {}, initScope = []) {
3012
+ const initRes = runtimeInit({
3013
+ name: ${JSON.stringify(options.internalName)},
3014
+ remotes: [],
3015
+ shared: {},
3016
+ });
3017
+ const initToken = { from: ${JSON.stringify(options.internalName)} };
3018
+ if (initScope.indexOf(initToken) >= 0) return;
3019
+ initScope.push(initToken);
3020
+ initRes.initShareScopeMap(${JSON.stringify(options.shareScope)}, shared);
3021
+ try {
3022
+ await Promise.all(
3023
+ await initRes.initializeSharing(${JSON.stringify(options.shareScope)}, {
3024
+ strategy: ${JSON.stringify(options.shareStrategy ?? "version-first")},
3025
+ from: 'build',
3026
+ initScope,
3027
+ })
3028
+ );
3029
+ } catch (e) {
3030
+ console.error('[Module Federation SSR]', e);
3031
+ }
3032
+ return initRes;
3033
+ }
3034
+
3035
+ async function getExposes(moduleName) {
3036
+ const exposesMap = await getExposesMap();
3037
+ if (!(moduleName in exposesMap))
3038
+ throw new Error(\`[Module Federation] Module \${moduleName} does not exist in container.\`);
3039
+ return exposesMap[moduleName]().then((res) => () => res);
3040
+ }
3041
+
3042
+ export { init, getExposes as get };
3043
+ `;
3044
+ }
3045
+ //#endregion
3086
3046
  //#region src/plugins/pluginMFManifest.ts
3087
3047
  /**
3088
3048
  * Resolves the build version for the module federation manifest.
@@ -3113,6 +3073,7 @@ const Manifest = () => {
3113
3073
  };
3114
3074
  let root;
3115
3075
  let remoteEntryFile;
3076
+ let ssrRemoteEntryFile;
3116
3077
  let publicPath;
3117
3078
  let _command;
3118
3079
  let _originalConfigBase;
@@ -3149,8 +3110,8 @@ const Manifest = () => {
3149
3110
  type: "module"
3150
3111
  },
3151
3112
  ssrRemoteEntry: {
3152
- name: filename,
3153
- path: "",
3113
+ name: getSsrRemoteEntryFileName(filename),
3114
+ path: "/__mf_ssr__/",
3154
3115
  type: "module"
3155
3116
  },
3156
3117
  varRemoteEntry: varFilename ? {
@@ -3181,6 +3142,7 @@ const Manifest = () => {
3181
3142
  _originalConfigBase = config.base;
3182
3143
  },
3183
3144
  configResolved(config) {
3145
+ viteConfig = config;
3184
3146
  root = config.root;
3185
3147
  let base = config.base;
3186
3148
  if (_command === "serve") base = (config.server.origin || "") + config.base;
@@ -3190,7 +3152,10 @@ const Manifest = () => {
3190
3152
  if (!mfManifestName) return;
3191
3153
  let filesMap = {};
3192
3154
  const foundRemoteEntryFile = findRemoteEntryFile(mfOptions.filename, bundle);
3155
+ const expectedSsrRemoteEntryFile = getSsrRemoteEntryFileName(mfOptions.filename);
3156
+ const foundSsrRemoteEntryFile = Object.values(bundle).find((file) => file.fileName === expectedSsrRemoteEntryFile)?.fileName;
3193
3157
  if (foundRemoteEntryFile) remoteEntryFile = foundRemoteEntryFile;
3158
+ ssrRemoteEntryFile = foundSsrRemoteEntryFile || expectedSsrRemoteEntryFile;
3194
3159
  const allCssAssets = mfOptions.bundleAllCSS && !disableAssetsAnalyze ? collectCssAssets(bundle) : /* @__PURE__ */ new Set();
3195
3160
  if (!disableAssetsAnalyze) {
3196
3161
  const exposesModules = Object.keys(mfOptions.exposes).map((item) => mfOptions.exposes[item].import);
@@ -3232,6 +3197,11 @@ const Manifest = () => {
3232
3197
  path: "",
3233
3198
  type: "module"
3234
3199
  };
3200
+ const ssrRemoteEntry = {
3201
+ name: ssrRemoteEntryFile || getSsrRemoteEntryFileName(filename),
3202
+ path: _command === "serve" ? "/__mf_ssr__/" : "",
3203
+ type: "module"
3204
+ };
3235
3205
  const varRemoteEntry = varFilename ? {
3236
3206
  name: varFilename,
3237
3207
  path: "",
@@ -3295,7 +3265,7 @@ const Manifest = () => {
3295
3265
  buildName: name
3296
3266
  },
3297
3267
  remoteEntry,
3298
- ssrRemoteEntry: remoteEntry,
3268
+ ssrRemoteEntry,
3299
3269
  varRemoteEntry,
3300
3270
  types: {
3301
3271
  path: "",
@@ -3534,13 +3504,14 @@ function appendAlias(config, alias) {
3534
3504
  function pluginProxyRemotes_default(options) {
3535
3505
  let command;
3536
3506
  let root = process.cwd();
3507
+ let enableSsrInit = false;
3537
3508
  const { remotes } = options;
3538
3509
  function resolveRemoteId(source, importer, remoteName) {
3539
3510
  if (source === remoteName) {
3540
3511
  const installedPackageEntry = getInstalledPackageEntry(source, { cwd: root });
3541
3512
  if (installedPackageEntry && (importer === void 0 || isNodeModulesImporter(importer))) return installedPackageEntry;
3542
3513
  }
3543
- const remoteModule = getRemoteVirtualModule(source, command);
3514
+ const remoteModule = getRemoteVirtualModule(source, command, enableSsrInit);
3544
3515
  addUsedRemote(remoteName, source);
3545
3516
  refreshHostAutoInit();
3546
3517
  return remoteModule.getImportId();
@@ -3559,6 +3530,9 @@ function pluginProxyRemotes_default(options) {
3559
3530
  });
3560
3531
  });
3561
3532
  },
3533
+ configResolved() {
3534
+ enableSsrInit = command === "serve" && parseInt(version, 10) >= 8;
3535
+ },
3562
3536
  resolveId(source, importer) {
3563
3537
  if (!filterId(source)) return;
3564
3538
  for (const remote of Object.values(remotes)) {
@@ -3609,7 +3583,7 @@ function tryResolveFromProjectRoot(source) {
3609
3583
  const browserEntry = getInstalledPackageEntry(source, { cwd: getPackageDetectionCwd() });
3610
3584
  if (browserEntry) return browserEntry;
3611
3585
  try {
3612
- return createRequire$1(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`)).resolve(source);
3586
+ return createRequire(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`)).resolve(source);
3613
3587
  } catch {
3614
3588
  return;
3615
3589
  }
@@ -3840,9 +3814,37 @@ function applyRewrites(code, imports, id) {
3840
3814
  const importParts = [];
3841
3815
  if (imp.defaultLocal) importParts.push(`default as ${imp.defaultLocal}`);
3842
3816
  importParts.push(`__moduleExports as ${nsId}`);
3843
- const destructParts = imp.named.map((s) => s.imported === s.local ? s.local : `${s.imported}: ${s.local}`);
3844
3817
  let rewrite = `import { ${importParts.join(", ")} } from ${src};`;
3845
- if (destructParts.length > 0) rewrite += `\nconst { ${destructParts.join(", ")} } = ${nsId};`;
3818
+ if (imp.named.length > 0) {
3819
+ const isProxyId = `__mf_is_proxy_${counter++}`;
3820
+ const namedProxyHelper = `function __mfCreateNamedRemoteProxy(ns, key) {
3821
+ const target = function (...args) {
3822
+ const value = ns[key];
3823
+ return typeof value === "function" ? value.apply(this, args) : value;
3824
+ };
3825
+ return new Proxy(target, {
3826
+ get(_target, prop) {
3827
+ if (prop === "then") return undefined;
3828
+ const value = ns[key];
3829
+ if (prop === Symbol.toPrimitive) return () => value;
3830
+ const item = value == null ? undefined : value[prop];
3831
+ return typeof item === "function" ? item.bind(value) : item;
3832
+ },
3833
+ apply(target, thisArg, args) {
3834
+ return target.apply(thisArg, args);
3835
+ }
3836
+ });
3837
+ }`;
3838
+ const tempNames = imp.named.map((_s) => `__mf_named_${counter++}`);
3839
+ const destructParts = imp.named.map((s, index) => `${s.imported}: ${tempNames[index]}`);
3840
+ const bindingLines = imp.named.map((s, index) => {
3841
+ const temp = tempNames[index];
3842
+ return `const ${s.local} = ${isProxyId} ? __mfCreateNamedRemoteProxy(${nsId}, ${JSON.stringify(s.imported)}) : ${temp};`;
3843
+ });
3844
+ rewrite += `\n${namedProxyHelper}\nconst ${isProxyId} = ${nsId} && ${nsId}.__mf_is_remote_proxy;`;
3845
+ rewrite += `\nconst { ${destructParts.join(", ")} } = ${isProxyId} ? {} : ${nsId};`;
3846
+ rewrite += `\n${bindingLines.join("\n")}`;
3847
+ }
3846
3848
  ms.overwrite(imp.start, imp.end, rewrite);
3847
3849
  }
3848
3850
  changed = true;
@@ -4118,6 +4120,205 @@ function pluginRemoteNamedExports(options) {
4118
4120
  };
4119
4121
  }
4120
4122
  //#endregion
4123
+ //#region src/plugins/pluginSSRRemoteEntry.ts
4124
+ /**
4125
+ * Emits a Node-compatible SSR remote entry alongside the browser entry.
4126
+ *
4127
+ * Format strategy:
4128
+ * - Emit a dedicated ESM SSR entry alongside the browser entry.
4129
+ * - Keep the SSR entry out of the browser remote graph for Rollup builds by
4130
+ * emitting it as a generated asset.
4131
+ *
4132
+ * In both cases shared packages (react, react-dom, etc.) are marked as external
4133
+ * so Node resolves them through its own module cache, guaranteeing the singleton
4134
+ * is shared with react-dom/server.
4135
+ */
4136
+ function pluginSSRRemoteEntry(options) {
4137
+ const remoteEntrySSRId = getRemoteEntrySSRId(options);
4138
+ const virtualExposesSSRId = getVirtualExposesSSRId(options);
4139
+ let isRolldown = false;
4140
+ let ssrOutputFilename = "";
4141
+ const ssrOnlyExternals = [
4142
+ "@module-federation/runtime",
4143
+ "@module-federation/runtime-core",
4144
+ "@module-federation/sdk",
4145
+ ...options.ssrExternals ?? []
4146
+ ];
4147
+ const ssrOnlyExternalPattern = new RegExp(`^(${ssrOnlyExternals.map((e) => e.replace(/[/\\^$*+?.()|[\]{}]/g, "\\$&")).join("|")})(\\/.*)?$`);
4148
+ const ssrModuleIds = new Set([remoteEntrySSRId, virtualExposesSSRId]);
4149
+ const resolvedAbsToPackage = /* @__PURE__ */ new Map();
4150
+ let isServe = false;
4151
+ let viteConfig;
4152
+ let isNuxtProject = false;
4153
+ const findNuxtExposesChunk = (bundle) => {
4154
+ const exposeKeys = Object.keys(options.exposes);
4155
+ if (exposeKeys.length === 0) return;
4156
+ return Object.values(bundle).find((file) => {
4157
+ if (file.type !== "chunk" || !file.fileName.startsWith("_nuxt/") || !file.fileName.endsWith(".js")) return false;
4158
+ const code = file.code || "";
4159
+ return exposeKeys.every((key) => code.includes(JSON.stringify(key)));
4160
+ })?.fileName;
4161
+ };
4162
+ return [{
4163
+ name: "mf:ssr-remote-entry:pre",
4164
+ enforce: "pre",
4165
+ configResolved(config) {
4166
+ isServe = config.command === "serve";
4167
+ for (const pkg of ssrOnlyExternals) {
4168
+ const aliasEntry = (config.resolve?.alias)?.find((a) => a.find === pkg || a.find instanceof RegExp && a.find.test(pkg));
4169
+ if (aliasEntry?.replacement) resolvedAbsToPackage.set(aliasEntry.replacement, pkg);
4170
+ }
4171
+ },
4172
+ resolveId(id, importer) {
4173
+ if (id === remoteEntrySSRId || id.startsWith(remoteEntrySSRId)) return id;
4174
+ if (id === virtualExposesSSRId || id.startsWith(virtualExposesSSRId)) return id;
4175
+ if (!importer || !ssrModuleIds.has(importer)) return;
4176
+ if (ssrOnlyExternalPattern.test(id)) return {
4177
+ id,
4178
+ external: true
4179
+ };
4180
+ const pkg = resolvedAbsToPackage.get(id);
4181
+ if (pkg) return {
4182
+ id: pkg,
4183
+ external: true
4184
+ };
4185
+ if (id.startsWith(".") || id.startsWith("/") || id.startsWith("file:")) return this.resolve(id, importer, { skipSelf: true }).then((resolved) => {
4186
+ if (resolved) ssrModuleIds.add(resolved.id);
4187
+ return resolved;
4188
+ });
4189
+ }
4190
+ }, {
4191
+ name: "mf:ssr-remote-entry",
4192
+ configResolved(config) {
4193
+ viteConfig = config;
4194
+ isNuxtProject = isNuxtProjectRoot(config.root);
4195
+ },
4196
+ configureServer(server) {
4197
+ const base = "/__mf_ssr__";
4198
+ const basePath = getBasePath(viteConfig?.base);
4199
+ const ssrEntryFileName = getSsrRemoteEntryFileName(options.filename);
4200
+ if (isNuxtProject || isNuxtClientBase(basePath)) server.middlewares.use((req, _res, next) => {
4201
+ if (req.url?.replace(/\?.*/, "") === `${basePath}/${ssrEntryFileName}`) req.url = `${basePath}/__mf_ssr__/${ssrEntryFileName}`;
4202
+ next();
4203
+ });
4204
+ const ssrEnv = server.environments?.ssr;
4205
+ const clientEnv = server.environments?.client;
4206
+ if (typeof (ssrEnv?.fetchModule ?? clientEnv?.fetchModule) === "function") server.middlewares.use("/__mf_runner__", async (req, res) => {
4207
+ res.setHeader("Access-Control-Allow-Origin", "*");
4208
+ if (req.method === "OPTIONS") {
4209
+ res.setHeader("Access-Control-Allow-Methods", "POST");
4210
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
4211
+ res.statusCode = 204;
4212
+ res.end();
4213
+ return;
4214
+ }
4215
+ if (req.method !== "POST") {
4216
+ res.statusCode = 405;
4217
+ res.end("Method not allowed");
4218
+ return;
4219
+ }
4220
+ try {
4221
+ const chunks = [];
4222
+ await new Promise((resolve, reject) => {
4223
+ req.on("data", (chunk) => chunks.push(chunk));
4224
+ req.on("end", resolve);
4225
+ req.on("error", reject);
4226
+ });
4227
+ const body = JSON.parse(Buffer.concat(chunks).toString("utf8"));
4228
+ if (body.name === "getBuiltins") {
4229
+ const builtins = (clientEnv ?? ssrEnv)?.config?.resolve?.builtins ?? [];
4230
+ res.setHeader("Content-Type", "application/json");
4231
+ res.end(JSON.stringify({ result: builtins }));
4232
+ return;
4233
+ }
4234
+ if (body.name !== "fetchModule") {
4235
+ res.statusCode = 400;
4236
+ res.end(JSON.stringify({ error: { message: `Unsupported invoke: ${body.name}` } }));
4237
+ return;
4238
+ }
4239
+ const [id, importer, opts] = body.data;
4240
+ const fetchEnv = ssrEnv ?? clientEnv;
4241
+ const fetchFn = fetchEnv.fetchModule.bind(fetchEnv);
4242
+ let result;
4243
+ try {
4244
+ result = await fetchFn(id, importer, opts);
4245
+ } catch (fetchErr) {
4246
+ const bareId = id.startsWith("/@id/") ? id.slice(5).replace(/^__x00__/, "\0") : id;
4247
+ try {
4248
+ const { createRequire } = await import("module");
4249
+ const resolved = createRequire(new URL(`file://${server.config.root}/package.json`)).resolve(bareId.replace(/^\0/, ""));
4250
+ const { pathToFileURL } = await import("url");
4251
+ result = {
4252
+ externalize: pathToFileURL(resolved).href,
4253
+ type: "module"
4254
+ };
4255
+ } catch {
4256
+ throw fetchErr;
4257
+ }
4258
+ }
4259
+ res.setHeader("Content-Type", "application/json");
4260
+ res.end(JSON.stringify({ result }));
4261
+ } catch (e) {
4262
+ res.setHeader("Content-Type", "application/json");
4263
+ res.end(JSON.stringify({ error: { message: String(e instanceof Error ? e.message : e) } }));
4264
+ }
4265
+ });
4266
+ const ssrPath = `${base}/${ssrEntryFileName}`;
4267
+ server.middlewares.use(ssrPath, (_req, res) => {
4268
+ const exposesUrl = `${base}/${options.filename.replace(/\.[^.]+$/, "")}.exposes.js`;
4269
+ const code = generateRemoteEntrySSR(options).replace(JSON.stringify(virtualExposesSSRId), JSON.stringify(exposesUrl));
4270
+ res.setHeader("Content-Type", "application/javascript");
4271
+ res.setHeader("Access-Control-Allow-Origin", "*");
4272
+ res.end(code);
4273
+ });
4274
+ },
4275
+ resolveId(id) {
4276
+ if (id === remoteEntrySSRId || id.startsWith(remoteEntrySSRId)) return id;
4277
+ if (id === virtualExposesSSRId || id.startsWith(virtualExposesSSRId)) return id;
4278
+ if (id === `/__mf_ssr__/${getSsrRemoteEntryFileName(options.filename)}`) return remoteEntrySSRId;
4279
+ if (id === `/__mf_ssr__/${options.filename.replace(/\.[^.]+$/, "")}.exposes.js`) return virtualExposesSSRId;
4280
+ },
4281
+ load(id) {
4282
+ if (id === remoteEntrySSRId || id.startsWith(remoteEntrySSRId)) return generateRemoteEntrySSR(options);
4283
+ if (id === virtualExposesSSRId || id.startsWith(virtualExposesSSRId)) return generateExposesSSR(options);
4284
+ },
4285
+ buildStart() {
4286
+ if (isServe) return;
4287
+ isRolldown = getIsRolldown(this);
4288
+ ssrOutputFilename = getSsrRemoteEntryFileName(options.filename);
4289
+ const environmentName = this.environment?.name;
4290
+ const hasSsrEnvironment = Boolean(viteConfig?.environments?.ssr);
4291
+ const isLegacySsrBuild = Boolean(this.environment?.config?.build?.ssr);
4292
+ if (hasSsrEnvironment) {
4293
+ if (isNuxtProject) {
4294
+ if (environmentName === "ssr") return;
4295
+ } else if (environmentName !== "ssr") return;
4296
+ } else if (isLegacySsrBuild) {} else if (environmentName && environmentName !== "client") return;
4297
+ if (Object.keys(options.exposes).length === 0) return;
4298
+ if (isRolldown) this.emitFile({
4299
+ type: "chunk",
4300
+ id: remoteEntrySSRId,
4301
+ name: "ssrRemoteEntry",
4302
+ fileName: ssrOutputFilename,
4303
+ preserveSignature: "strict"
4304
+ });
4305
+ else this.emitFile({
4306
+ type: "asset",
4307
+ fileName: ssrOutputFilename,
4308
+ source: generateRemoteEntrySSR(options)
4309
+ });
4310
+ },
4311
+ generateBundle(_options, bundle) {
4312
+ const exposesChunk = findNuxtExposesChunk(bundle);
4313
+ const ssrAsset = bundle[ssrOutputFilename];
4314
+ if (exposesChunk && ssrAsset?.type === "asset" && typeof ssrAsset.source === "string") ssrAsset.source = ssrAsset.source.replace(/import\("virtual:mf-exposes-ssr:[^"]+"\)/g, `import("./${exposesChunk}")`);
4315
+ if (!isRolldown) return;
4316
+ const chunk = bundle[ssrOutputFilename];
4317
+ if (!chunk || chunk.type !== "chunk") return;
4318
+ }
4319
+ }];
4320
+ }
4321
+ //#endregion
4121
4322
  //#region src/plugins/pluginVarRemoteEntry.ts
4122
4323
  const VarRemoteEntry = () => {
4123
4324
  const mfOptions = getNormalizeModuleFederationOptions();
@@ -4380,6 +4581,11 @@ function createEarlyVirtualModulesPlugin(options) {
4380
4581
  const root = config.root || process.cwd();
4381
4582
  setPackageDetectionCwd(root);
4382
4583
  const isVinext = hasPackageDependency("vinext");
4584
+ setSsrRemotes(Object.entries(options.remotes).map(([key, r]) => ({
4585
+ name: key,
4586
+ entry: r.entry,
4587
+ type: r.type ?? "module"
4588
+ })));
4383
4589
  initVirtualModules(_command, getRemoteEntryId(options));
4384
4590
  const isRolldown = getIsRolldown(this);
4385
4591
  if (remotes && Object.keys(remotes).length > 0) {
@@ -4499,9 +4705,46 @@ export default __mfShared.default ?? __mfShared;`
4499
4705
  }
4500
4706
  writeLocalSharedImportMap();
4501
4707
  }
4708
+ },
4709
+ configResolved(config) {
4710
+ if (parseInt(version, 10) < 8) return;
4711
+ if (!(Object.keys(options.exposes).length > 0 || Object.keys(options.remotes).length > 0)) return;
4712
+ if (options.runtimePlugins.some((p) => {
4713
+ return (typeof p === "string" ? p : p[0]) === "@module-federation/vite/ssrEntryLoader";
4714
+ })) return;
4715
+ const pluginRequire = createRequire(import.meta.url);
4716
+ const projectRequire = createRequire(new URL(`file://${config.root}/package.json`));
4717
+ const sharedKeys = Object.keys(options.shared ?? {});
4718
+ const commonSharedPkgs = [
4719
+ "react",
4720
+ "react-dom",
4721
+ "react/jsx-runtime",
4722
+ "react/jsx-dev-runtime",
4723
+ "@module-federation/runtime",
4724
+ "@module-federation/runtime-core",
4725
+ "@module-federation/sdk"
4726
+ ];
4727
+ const resolvedShared = {};
4728
+ for (const pkg of [...commonSharedPkgs, ...sharedKeys]) try {
4729
+ resolvedShared[pkg] = projectRequire.resolve(pkg);
4730
+ } catch {
4731
+ try {
4732
+ resolvedShared[pkg] = pluginRequire.resolve(pkg);
4733
+ } catch {}
4734
+ }
4735
+ const ssrEntryLoaderSpecifier = "@module-federation/vite/ssrEntryLoader";
4736
+ try {
4737
+ pluginRequire.resolve(ssrEntryLoaderSpecifier);
4738
+ options.runtimePlugins.push([ssrEntryLoaderSpecifier, { resolvedShared }]);
4739
+ } catch {}
4502
4740
  }
4503
4741
  };
4504
4742
  }
4743
+ const SSR_ONLY_PLUGINS = new Set(["@module-federation/vite/ssrEntryLoader"]);
4744
+ function loadPluginDts(options) {
4745
+ if (options.dts === false) return [];
4746
+ return [import("./pluginDts-B5pcUmam.mjs").then(({ default: pluginDts }) => pluginDts(options))];
4747
+ }
4505
4748
  function federation(mfUserOptions) {
4506
4749
  if (isTestEnv()) return [];
4507
4750
  const options = normalizeModuleFederationOptions(mfUserOptions);
@@ -4542,7 +4785,7 @@ function federation(mfUserOptions) {
4542
4785
  const environmentName = this.environment?.name;
4543
4786
  if (!environmentName || environmentName === "client") return;
4544
4787
  const target = reactServerEntryMap[id];
4545
- const reactPackageJson = createRequire$1(new URL(`file://${process.cwd()}/package.json`)).resolve("react/package.json");
4788
+ const reactPackageJson = createRequire(new URL(`file://${process.cwd()}/package.json`)).resolve("react/package.json");
4546
4789
  return path.join(path.dirname(reactPackageJson), target.replace(/^react\//, ""));
4547
4790
  }
4548
4791
  }] : [],
@@ -4553,14 +4796,26 @@ function federation(mfUserOptions) {
4553
4796
  command = env.command;
4554
4797
  },
4555
4798
  configResolved() {
4556
- initVirtualModules(command, remoteEntryId);
4799
+ initVirtualModules(command, remoteEntryId, parseInt(version, 10) >= 8);
4557
4800
  }
4558
4801
  },
4559
4802
  aliasToArrayPlugin_default,
4560
4803
  checkAliasConflicts({ shared }),
4561
4804
  normalizeOptimizeDeps_default,
4562
- ...pluginDts(options),
4805
+ ...loadPluginDts(options),
4563
4806
  pluginDevRemoteHmr(options),
4807
+ {
4808
+ name: "mf:normalize-entry-chunks",
4809
+ enforce: "pre",
4810
+ apply: "build",
4811
+ generateBundle(_options, bundle) {
4812
+ for (const chunk of Object.values(bundle)) {
4813
+ if (typeof chunk !== "object" || chunk === null || chunk.type !== "chunk" || !chunk.isEntry) continue;
4814
+ const facadeId = chunk.facadeModuleId ?? "";
4815
+ if (facadeId.includes("__mf__virtual") || facadeId.startsWith("virtual:mf-") || facadeId.startsWith("virtual:mf:") || facadeId.startsWith("\0virtual:mf-") || facadeId.startsWith("\0virtual:mf:")) chunk.isEntry = false;
4816
+ }
4817
+ }
4818
+ },
4564
4819
  ...addEntry({
4565
4820
  entryName: "remoteEntry",
4566
4821
  entryPath: remoteEntryId,
@@ -4570,7 +4825,8 @@ function federation(mfUserOptions) {
4570
4825
  entryName: "hostInit",
4571
4826
  entryPath: () => getHostAutoInitPath(),
4572
4827
  inject: hostInitInjectLocation,
4573
- forceClientInjected: Object.keys(options.exposes).length > 0
4828
+ forceClientInjected: Object.keys(options.exposes).length > 0,
4829
+ skipTransformFor: Object.values(options.exposes).map((expose) => expose.import)
4574
4830
  }),
4575
4831
  ...addEntry({
4576
4832
  entryName: "virtualExposes",
@@ -4715,7 +4971,7 @@ function federation(mfUserOptions) {
4715
4971
  *
4716
4972
  * @see https://rollupjs.org/plugin-development/#synthetic-named-exports
4717
4973
  */
4718
- if (!/\bexport\s+const\s+__moduleExports\b/.test(code)) {
4974
+ if (!/\bexport\s+const\s+__moduleExports\b/.test(code) && !/\bexport\s*\{[^}]*__moduleExports/.test(code)) {
4719
4975
  const nextCode = code.replace("export default exportModule", "export const __moduleExports = exportModule;\nexport default exportModule.__esModule ? exportModule.default : exportModule");
4720
4976
  code = nextCode === code ? `${code}\nexport const __moduleExports = exportModule;\n` : nextCode;
4721
4977
  }
@@ -4788,6 +5044,7 @@ function federation(mfUserOptions) {
4788
5044
  config.optimizeDeps.include.push("@module-federation/runtime");
4789
5045
  options.runtimePlugins.forEach((p) => {
4790
5046
  const pluginPath = typeof p === "string" ? p : p[0];
5047
+ if (SSR_ONLY_PLUGINS.has(pluginPath)) return;
4791
5048
  if (pluginPath && !pluginPath.startsWith(".") && !pluginPath.startsWith("/") && !pluginPath.startsWith("\0") && !pluginPath.startsWith("virtual:")) config.optimizeDeps.include.push(pluginPath);
4792
5049
  });
4793
5050
  if (isRolldown) {
@@ -4803,6 +5060,7 @@ function federation(mfUserOptions) {
4803
5060
  }
4804
5061
  },
4805
5062
  ...Manifest(),
5063
+ ...pluginSSRRemoteEntry(options),
4806
5064
  ...VarRemoteEntry(),
4807
5065
  {
4808
5066
  name: "module-federation-vinext-fix-rsc-preload-as",
@@ -4876,5 +5134,8 @@ function federation(mfUserOptions) {
4876
5134
  })()
4877
5135
  ];
4878
5136
  }
5137
+ function createModuleFederationConfig(options) {
5138
+ return options;
5139
+ }
4879
5140
  //#endregion
4880
- export { federation };
5141
+ export { createModuleFederationConfig, federation };