@module-federation/vite 1.12.2 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.cjs CHANGED
@@ -25,6 +25,7 @@ let defu = require("defu");
25
25
  defu = __toESM(defu);
26
26
  let fs = require("fs");
27
27
  fs = __toESM(fs);
28
+ let module$1 = require("module");
28
29
  let pathe = require("pathe");
29
30
  pathe = __toESM(pathe);
30
31
  let magic_string = require("magic-string");
@@ -33,7 +34,7 @@ let _rollup_pluginutils = require("@rollup/pluginutils");
33
34
  let _module_federation_sdk = require("@module-federation/sdk");
34
35
  let _module_federation_dts_plugin = require("@module-federation/dts-plugin");
35
36
  let _module_federation_dts_plugin_core = require("@module-federation/dts-plugin/core");
36
- let module$1 = require("module");
37
+ let node_module = require("node:module");
37
38
  let url = require("url");
38
39
  //#region src/utils/mapCodeToCodeWithSourcemap.ts
39
40
  async function mapCodeToCodeWithSourcemap(code) {
@@ -46,6 +47,104 @@ async function mapCodeToCodeWithSourcemap(code) {
46
47
  };
47
48
  }
48
49
  //#endregion
50
+ //#region src/utils/htmlEntryUtils.ts
51
+ function sanitizeDevEntryPath(devEntryPath) {
52
+ return devEntryPath.replace(/^[^:]+:([/\\])[/\\]?/, "$1").replace(/\\\\?/g, "/");
53
+ }
54
+ /**
55
+ * Inlines the federation init import into existing module script tags to fix
56
+ * the race condition (#396) where separate `<script type="module">` tags
57
+ * don't guarantee execution order with top-level await.
58
+ *
59
+ * If no entry scripts are found, falls back to injecting a separate script tag.
60
+ *
61
+ * @example
62
+ * // Before (two separate scripts, race condition):
63
+ * // <script type="module" src="/__mf__virtual/hostAutoInit.js"><\/script>
64
+ * // <script type="module" src="/src/main.js"><\/script>
65
+ * // After (single inline script, sequential execution):
66
+ * // <script type="module">await import("/__mf__virtual/hostAutoInit.js");await import("/src/main.js");<\/script>
67
+ */
68
+ function inlineEntryScripts(html, initSrc) {
69
+ const src = sanitizeDevEntryPath(initSrc);
70
+ const scriptTagRegex = /<script\s+([^>]*\btype=["']module["'][^>]*\bsrc=["'][^"']+["'][^>]*)>/gi;
71
+ let hasEntry = false;
72
+ const result = html.replace(scriptTagRegex, (match, attrs) => {
73
+ const srcMatch = attrs.match(/\bsrc=["']([^"']+)["']/i);
74
+ if (!srcMatch) return match;
75
+ const originalSrc = srcMatch[1];
76
+ if (originalSrc.includes("@vite/client")) return match;
77
+ hasEntry = true;
78
+ return `<script ${attrs.replace(/\s*\bsrc=["'][^"']+["']/i, "")}>await import(${JSON.stringify(src)});await import(${JSON.stringify(originalSrc)});`;
79
+ });
80
+ if (hasEntry) return result;
81
+ return html.replace("<head>", `<head><script type="module" src=${JSON.stringify(src)}><\/script>`);
82
+ }
83
+ //#endregion
84
+ //#region src/utils/packageUtils.ts
85
+ const dependencyPresenceCache = /* @__PURE__ */ new Map();
86
+ let packageDetectionCwd;
87
+ function getDependencyCacheKey(cwd, dependencyName) {
88
+ return `${cwd}:${dependencyName}`;
89
+ }
90
+ function setPackageDetectionCwd(cwd) {
91
+ packageDetectionCwd = cwd;
92
+ }
93
+ /**
94
+ * Escaping rules:
95
+ * Convert using the format __${mapping}__, where _ and $ are not allowed in npm package names but can be used in variable names.
96
+ * @ => 1
97
+ * / => 2
98
+ * - => 3
99
+ * . => 4
100
+ */
101
+ /**
102
+ * Encodes a package name into a valid file name.
103
+ * @param {string} name - The package name, e.g., "@scope/xx-xx.xx".
104
+ * @returns {string} - The encoded file name.
105
+ */
106
+ function packageNameEncode(name) {
107
+ if (typeof name !== "string") throw new Error("A string package name is required");
108
+ return name.replace(/@/g, "_mf_0_").replace(/\//g, "_mf_1_").replace(/-/g, "_mf_2_").replace(/\./g, "_mf_3_");
109
+ }
110
+ /**
111
+ * Decodes an encoded file name back to the original package name.
112
+ * @param {string} encoded - The encoded file name, e.g., "_mf_0_scope_mf_1_xx_mf_2_xx_mf_3_xx".
113
+ * @returns {string} - The decoded package name.
114
+ */
115
+ function packageNameDecode(encoded) {
116
+ if (typeof encoded !== "string") throw new Error("A string encoded file name is required");
117
+ return encoded.replace(/_mf_0_/g, "@").replace(/_mf_1_/g, "/").replace(/_mf_2_/g, "-").replace(/_mf_3_/g, ".");
118
+ }
119
+ /**
120
+ * Removes any subpath from an npm package specifier and returns the package name only.
121
+ * @param {string} packageString - The package specifier, e.g., "@scope/pkg/runtime" or "react/jsx-runtime".
122
+ * @returns {string} - The base npm package name.
123
+ */
124
+ function removePathFromNpmPackage(packageString) {
125
+ const match = packageString.match(/^(?:@[^/]+\/)?[^/]+/);
126
+ return match ? match[0] : packageString;
127
+ }
128
+ function hasPackageDependency(dependencyName, cwd = packageDetectionCwd || process.cwd()) {
129
+ const cacheKey = getDependencyCacheKey(cwd, dependencyName);
130
+ const cached = dependencyPresenceCache.get(cacheKey);
131
+ if (cached !== void 0) return cached;
132
+ try {
133
+ const packageJson = JSON.parse((0, fs.readFileSync)(pathe.default.join(cwd, "package.json"), "utf8"));
134
+ const hasDependency = [
135
+ packageJson.dependencies,
136
+ packageJson.devDependencies,
137
+ packageJson.peerDependencies,
138
+ packageJson.optionalDependencies
139
+ ].some((deps) => !!deps?.[dependencyName]);
140
+ dependencyPresenceCache.set(cacheKey, hasDependency);
141
+ return hasDependency;
142
+ } catch {
143
+ dependencyPresenceCache.set(cacheKey, false);
144
+ return false;
145
+ }
146
+ }
147
+ //#endregion
49
148
  //#region src/plugins/pluginAddEntry.ts
50
149
  function getFirstHtmlEntryFile(entryFiles) {
51
150
  return entryFiles.find((file) => file.endsWith(".html"));
@@ -75,7 +174,7 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
75
174
  viteConfig = config;
76
175
  const resolvedEntryPath = getEntryPath();
77
176
  devEntryPath = resolvedEntryPath.startsWith("virtual:mf") ? "@id/" + resolvedEntryPath : resolvedEntryPath;
78
- devEntryPath = config.base + devEntryPath.replace(/\\\\?/g, "/").replace(/.+?\:([/\\])[/\\]?/, "$1").replace(/^\//, "");
177
+ devEntryPath = config.base + devEntryPath.replace(/\\\\?/g, "/").replace(/^[^:]+:([/\\])[/\\]?/, "$1").replace(/^\//, "");
79
178
  },
80
179
  configureServer(server) {
81
180
  server.middlewares.use((req, res, next) => {
@@ -90,14 +189,11 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
90
189
  transformIndexHtml(c) {
91
190
  if (!injectHtml()) return;
92
191
  clientInjected = true;
93
- return c.replace("<head>", `<head><script type="module" src=${JSON.stringify(devEntryPath.replace(/.+?\:([/\\])[/\\]?/, "$1").replace(/\\\\?/g, "/"))}><\/script>`);
192
+ return inlineEntryScripts(c, devEntryPath);
94
193
  },
95
194
  transform(code, id) {
96
195
  if (id.includes("node_modules") || inject !== "html" || htmlFilePath) return;
97
- if (id.includes(".svelte-kit") && id.includes("internal.js")) {
98
- const src = devEntryPath.replace(/.+?\:([/\\])[/\\]?/, "$1").replace(/\\\\?/g, "/");
99
- return code.replace(/<head>/g, "<head><script type=\\\"module\\\" src=\\\"" + src + "\\\"><\/script>");
100
- }
196
+ if (id.includes(".svelte-kit") && id.includes("internal.js")) return code.replace(/<head>/g, "<head><script type=\\\"module\\\" src=\\\"" + sanitizeDevEntryPath(devEntryPath) + "\\\"><\/script>");
101
197
  }
102
198
  }, {
103
199
  name: "add-entry",
@@ -162,6 +258,15 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
162
258
  }
163
259
  },
164
260
  transform(code, id) {
261
+ if (hasPackageDependency("vinext") && inject === "html" && (id.includes("virtual:vite-rsc/entry-browser") || id.includes("virtual:vinext-app-browser-entry"))) {
262
+ const injection = `import ${JSON.stringify(getEntryPath())};\n`;
263
+ if (code.includes(injection.trim())) {
264
+ clientInjected = true;
265
+ return;
266
+ }
267
+ clientInjected = true;
268
+ return mapCodeToCodeWithSourcemap(injection + code);
269
+ }
165
270
  if (injectEntry() && entryFiles.some((file) => id.endsWith(file)) || _command === "serve" && inject === "html" && !clientInjected && !id.includes("node_modules") && /\.(js|ts|mjs|vue|jsx|tsx)(\?|$)/.test(id)) {
166
271
  clientInjected = true;
167
272
  return mapCodeToCodeWithSourcemap(`import ${JSON.stringify(getEntryPath())};\n` + code);
@@ -235,7 +340,9 @@ function PluginDevProxyModuleTopLevelAwait() {
235
340
  throw new Error(`${id}: ${e}`);
236
341
  }
237
342
  const magicString = new magic_string.default(code);
238
- (await loadWalk())(ast, { enter(node) {
343
+ const walk = await loadWalk();
344
+ const defaultExportExpression = hasPackageDependency("vinext") ? "(__mfproxy__awaitdefault?.default ?? __mfproxy__awaitdefault)" : "__mfproxy__awaitdefault";
345
+ walk(ast, { enter(node) {
239
346
  if (node.type === "ExportNamedDeclaration" && node.specifiers) {
240
347
  const exportSpecifiers = node.specifiers.map((specifier) => specifier.exported.name);
241
348
  const proxyStatements = exportSpecifiers.map((name) => `
@@ -256,15 +363,15 @@ function PluginDevProxyModuleTopLevelAwait() {
256
363
  let exportStatement = "default";
257
364
  if (declaration.type === "Identifier") proxyStatement = `
258
365
  const __mfproxy__awaitdefault = await ${declaration.name}();
259
- const __mfproxy__default = __mfproxy__awaitdefault;
366
+ const __mfproxy__default = ${defaultExportExpression};
260
367
  `;
261
368
  else if (declaration.type === "CallExpression" || declaration.type === "FunctionDeclaration") proxyStatement = `
262
369
  const __mfproxy__awaitdefault = await (${code.slice(declaration.start, declaration.end)});
263
- const __mfproxy__default = __mfproxy__awaitdefault;
370
+ const __mfproxy__default = ${defaultExportExpression};
264
371
  `;
265
372
  else proxyStatement = `
266
373
  const __mfproxy__awaitdefault = await (${code.slice(declaration.start, declaration.end)});
267
- const __mfproxy__default = __mfproxy__awaitdefault;
374
+ const __mfproxy__default = ${defaultExportExpression};
268
375
  `;
269
376
  const replacement = `${proxyStatement}\nexport { __mfproxy__default as ${exportStatement} };`;
270
377
  magicString.overwrite(start, end, replacement);
@@ -546,10 +653,6 @@ function normalizeRemoteItem(key, remote) {
546
653
  entryGlobalName: key
547
654
  }, remote);
548
655
  }
549
- function removePathFromNpmPackage(packageString) {
550
- const match = packageString.match(/^(?:@[^/]+\/)?[^/]+/);
551
- return match ? match[0] : packageString;
552
- }
553
656
  /**
554
657
  * Tries to find the package.json's version of a shared package
555
658
  * if `package.json` is not declared in `exports`
@@ -558,13 +661,13 @@ function removePathFromNpmPackage(packageString) {
558
661
  */
559
662
  function searchPackageVersion(sharedName) {
560
663
  try {
561
- const sharedPath = require.resolve(sharedName);
664
+ const sharedPath = (0, node_module.createRequire)(process.cwd()).resolve(sharedName);
562
665
  let potentialPackageJsonDir = pathe.dirname(sharedPath);
563
666
  const rootDir = pathe.parse(potentialPackageJsonDir).root;
564
667
  while (pathe.parse(potentialPackageJsonDir).base !== "node_modules" && potentialPackageJsonDir !== rootDir) {
565
668
  const potentialPackageJsonPath = pathe.join(potentialPackageJsonDir, "package.json");
566
669
  if (fs.existsSync(potentialPackageJsonPath)) {
567
- const potentialPackageJson = require(potentialPackageJsonPath);
670
+ const potentialPackageJson = JSON.parse(fs.readFileSync(potentialPackageJsonPath, "utf-8"));
568
671
  if (typeof potentialPackageJson == "object" && potentialPackageJson !== null && typeof potentialPackageJson.version === "string" && potentialPackageJson.name === sharedName) return potentialPackageJson.version;
569
672
  }
570
673
  potentialPackageJsonDir = pathe.dirname(potentialPackageJsonDir);
@@ -676,34 +779,6 @@ function normalizeModuleFederationOptions(options) {
676
779
  };
677
780
  }
678
781
  //#endregion
679
- //#region src/utils/packageNameUtils.ts
680
- /**
681
- * Escaping rules:
682
- * Convert using the format __${mapping}__, where _ and $ are not allowed in npm package names but can be used in variable names.
683
- * @ => 1
684
- * / => 2
685
- * - => 3
686
- * . => 4
687
- */
688
- /**
689
- * Encodes a package name into a valid file name.
690
- * @param {string} name - The package name, e.g., "@scope/xx-xx.xx".
691
- * @returns {string} - The encoded file name.
692
- */
693
- function packageNameEncode(name) {
694
- if (typeof name !== "string") throw new Error("A string package name is required");
695
- return name.replace(/@/g, "_mf_0_").replace(/\//g, "_mf_1_").replace(/-/g, "_mf_2_").replace(/\./g, "_mf_3_");
696
- }
697
- /**
698
- * Decodes an encoded file name back to the original package name.
699
- * @param {string} encoded - The encoded file name, e.g., "_mf_0_scope_mf_1_xx_mf_2_xx_mf_3_xx".
700
- * @returns {string} - The decoded package name.
701
- */
702
- function packageNameDecode(encoded) {
703
- if (typeof encoded !== "string") throw new Error("A string encoded file name is required");
704
- return encoded.replace(/_mf_0_/g, "@").replace(/_mf_1_/g, "/").replace(/_mf_2_/g, "-").replace(/_mf_3_/g, ".");
705
- }
706
- //#endregion
707
782
  //#region src/utils/localSharedImportMap_temp.ts
708
783
  /**
709
784
  * https://github.com/module-federation/vite/issues/68
@@ -977,6 +1052,14 @@ function getPackageNamedExports(pkg) {
977
1052
  return [];
978
1053
  }
979
1054
  }
1055
+ function getLocalProviderImportPath(pkg) {
1056
+ try {
1057
+ const resolved = (0, module$1.createRequire)(new URL("file://" + process.cwd() + "/package.json")).resolve(pkg);
1058
+ return resolved.includes("/node_modules/") || resolved.includes("\\node_modules\\") ? void 0 : resolved;
1059
+ } catch {
1060
+ return;
1061
+ }
1062
+ }
980
1063
  const preBuildCacheMap = {};
981
1064
  const PREBUILD_TAG = "__prebuild__";
982
1065
  function writePreBuildLibPath(pkg) {
@@ -998,6 +1081,8 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
998
1081
  const useESM = command === "build" || isRolldown;
999
1082
  const importLine = useESM ? `import { initPromise } from "${virtualRuntimeInitStatus.getImportId()}"` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
1000
1083
  const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
1084
+ const useSsrProviderFallback = hasPackageDependency("vinext") && command === "build" && pkg === "react";
1085
+ const providerImportId = getLocalProviderImportPath(pkg) || getPreBuildLibImportId(pkg);
1001
1086
  const namedExports = getPackageNamedExports(pkg);
1002
1087
  let exportLine;
1003
1088
  if (namedExports.length > 0) {
@@ -1009,6 +1094,9 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1009
1094
  import ${JSON.stringify(getPreBuildLibImportId(pkg))};
1010
1095
  ${command !== "build" ? `;() => import(${JSON.stringify(pkg)}).catch(() => {});` : ""}
1011
1096
  ${importLine}
1097
+ ${useSsrProviderFallback ? `const providerModulePromise = typeof window === "undefined"
1098
+ ? import(${JSON.stringify(providerImportId)})
1099
+ : undefined` : ""}
1012
1100
  const res = initPromise.then(runtime => runtime.loadShare(${JSON.stringify(pkg)}, {
1013
1101
  customShareInfo: {shareConfig:{
1014
1102
  singleton: ${shareItem.shareConfig.singleton},
@@ -1016,7 +1104,9 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1016
1104
  requiredVersion: ${JSON.stringify(shareItem.shareConfig.requiredVersion)}
1017
1105
  }}
1018
1106
  }))
1019
- const exportModule = ${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory))
1107
+ const exportModule = ${useSsrProviderFallback ? `(typeof window === "undefined"
1108
+ ? ((await providerModulePromise)?.default ?? await providerModulePromise)
1109
+ : ${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory)))` : `${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory))`}
1020
1110
  ${exportLine}
1021
1111
  `);
1022
1112
  }
@@ -1042,6 +1132,7 @@ function writeLocalSharedImportMap() {
1042
1132
  }
1043
1133
  }
1044
1134
  function generateLocalSharedImportMap() {
1135
+ const isVinext = hasPackageDependency("vinext");
1045
1136
  const options = getNormalizeModuleFederationOptions();
1046
1137
  return `
1047
1138
  import {loadShare} from "@module-federation/runtime";
@@ -1050,7 +1141,8 @@ function generateLocalSharedImportMap() {
1050
1141
  const shareItem = getNormalizeShareItem(pkg);
1051
1142
  return `
1052
1143
  ${JSON.stringify(pkg)}: async () => {
1053
- ${shareItem?.shareConfig.import === false ? `throw new Error(\`Shared module '\${${JSON.stringify(pkg)}}' must be provided by host\`);` : `let pkg = await import("${getPreBuildLibImportId(pkg)}");
1144
+ ${shareItem?.shareConfig.import === false ? `throw new Error(\`Shared module '\${${JSON.stringify(pkg)}}' must be provided by host\`);` : isVinext && pkg === "react" ? `let pkg = await import("react");
1145
+ return pkg;` : `let pkg = await import("${getPreBuildLibImportId(pkg)}");
1054
1146
  return pkg;`}
1055
1147
  }
1056
1148
  `;
@@ -1074,7 +1166,9 @@ function generateLocalSharedImportMap() {
1074
1166
  usedShared[${JSON.stringify(key)}].loaded = true
1075
1167
  const {${JSON.stringify(key)}: pkgDynamicImport} = importMap
1076
1168
  const res = await pkgDynamicImport()
1077
- const exportModule = {...res}
1169
+ const exportModule = ${JSON.stringify(isVinext)} && ${JSON.stringify(key)} === "react"
1170
+ ? (res?.default ?? res)
1171
+ : {...res}
1078
1172
  // All npm packages pre-built by vite will be converted to esm
1079
1173
  Object.defineProperty(exportModule, "__esModule", {
1080
1174
  value: true,
@@ -1156,6 +1250,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
1156
1250
  if (initScope.indexOf(initToken) >= 0) return;
1157
1251
  initScope.push(initToken);
1158
1252
  initRes.initShareScopeMap('${options.shareScope}', shared);
1253
+ initResolve(initRes)
1159
1254
  try {
1160
1255
  await Promise.all(await initRes.initializeSharing('${options.shareScope}', {
1161
1256
  strategy: '${options.shareStrategy}',
@@ -1165,7 +1260,6 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
1165
1260
  } catch (e) {
1166
1261
  console.error(e)
1167
1262
  }
1168
- initResolve(initRes)
1169
1263
  return initRes
1170
1264
  }
1171
1265
 
@@ -1183,11 +1277,11 @@ const hostAutoInitModule = new VirtualModule("hostAutoInit", "__H_A_I__");
1183
1277
  function writeHostAutoInit(remoteEntryId = REMOTE_ENTRY_ID) {
1184
1278
  hostAutoInitModule.writeSync(`
1185
1279
  const remoteEntryPromise = import("${remoteEntryId}")
1186
- // __tla only serves as a hack for vite-plugin-top-level-await.
1187
1280
  Promise.resolve(remoteEntryPromise)
1188
1281
  .then(remoteEntry => {
1189
1282
  return Promise.resolve(remoteEntry.__tla)
1190
- .then(remoteEntry.init).catch(remoteEntry.init)
1283
+ .then(remoteEntry.init)
1284
+ .catch(remoteEntry.init)
1191
1285
  })
1192
1286
  `);
1193
1287
  }
@@ -1206,6 +1300,20 @@ function initVirtualModules(command, remoteEntryId) {
1206
1300
  }
1207
1301
  //#endregion
1208
1302
  //#region src/utils/bundleHelpers.ts
1303
+ /**
1304
+ * Resolve the local alias for a non-inlineable proxy binding.
1305
+ * If Rollup's deconflict renamed the alias but didn't update references
1306
+ * in the code body, fall back to proxyLocal so they stay in sync.
1307
+ */
1308
+ function resolveProxyAlias(binding, proxyLocal, code, fullImport) {
1309
+ const codeWithoutImport = code.replace(fullImport, "");
1310
+ const escapedLocal = binding.local.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1311
+ const localUsedInCode = new RegExp(`\\b${escapedLocal}\\b`).test(codeWithoutImport);
1312
+ return {
1313
+ imported: binding.imported,
1314
+ local: localUsedInCode ? binding.local : proxyLocal
1315
+ };
1316
+ }
1209
1317
  function findRemoteEntryFile(filename, bundle) {
1210
1318
  for (const [_, fileData] of Object.entries(bundle)) if (filename.replace(/[\[\]]/g, "_").replace(/\.[^/.]+$/, "") === fileData.name || fileData.name === "remoteEntry") return fileData.fileName;
1211
1319
  }
@@ -1753,6 +1861,7 @@ function proxySharedModule(options) {
1753
1861
  const { shared = {} } = options;
1754
1862
  let _config;
1755
1863
  let _command = "serve";
1864
+ let isVinext = false;
1756
1865
  const savePrebuild = new PromiseStore();
1757
1866
  return [{
1758
1867
  name: "generateLocalSharedImportMap",
@@ -1767,9 +1876,11 @@ function proxySharedModule(options) {
1767
1876
  name: "proxyPreBuildShared",
1768
1877
  enforce: "post",
1769
1878
  config(config, { command }) {
1879
+ setPackageDetectionCwd(config.root || process.cwd());
1880
+ isVinext = hasPackageDependency("vinext");
1770
1881
  const isRolldown = !!this?.meta?.rolldownVersion;
1771
1882
  _command = command;
1772
- config.resolve.alias.push(...Object.keys(shared).map((key) => {
1883
+ config.resolve.alias.push(...Object.keys(shared).filter((key) => !(isVinext && key === "react")).map((key) => {
1773
1884
  const keyBase = key.endsWith("/") ? key.slice(0, -1) : key;
1774
1885
  const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1775
1886
  const escapedKeyBase = escapeRegex(keyBase);
@@ -1779,6 +1890,7 @@ function proxySharedModule(options) {
1779
1890
  replacement: "$1",
1780
1891
  customResolver(source, importer) {
1781
1892
  if (/\.css$/.test(source)) return;
1893
+ if (isVinext && source === "react") return;
1782
1894
  if (importer && importer.includes("localSharedImportMap")) return;
1783
1895
  if (key.endsWith("/") && source !== key.slice(0, -1)) return;
1784
1896
  const loadSharePath = getLoadShareModulePath(source, isRolldown, command);
@@ -1790,7 +1902,7 @@ function proxySharedModule(options) {
1790
1902
  }
1791
1903
  };
1792
1904
  }));
1793
- config.resolve.alias.push(...Object.keys(shared).map((key) => {
1905
+ config.resolve.alias.push(...Object.keys(shared).filter((key) => !(isVinext && key === "react")).map((key) => {
1794
1906
  return command === "build" ? {
1795
1907
  find: new RegExp(`(.*${PREBUILD_TAG}.*)`),
1796
1908
  replacement: function($1) {
@@ -1813,6 +1925,10 @@ function proxySharedModule(options) {
1813
1925
  const isRolldown = !!config.experimental?.rolldownDev;
1814
1926
  Object.keys(shared).forEach((key) => {
1815
1927
  if (key.endsWith("/")) return;
1928
+ if (isVinext && key === "react") {
1929
+ addUsedShares(key);
1930
+ return;
1931
+ }
1816
1932
  writeLoadShareModule(key, shared[key], _command, isRolldown);
1817
1933
  writePreBuildLibPath(key);
1818
1934
  addUsedShares(key);
@@ -1944,6 +2060,8 @@ function createEarlyVirtualModulesPlugin(options) {
1944
2060
  enforce: "pre",
1945
2061
  config(config, { command: _command }) {
1946
2062
  const root = config.root || process.cwd();
2063
+ setPackageDetectionCwd(root);
2064
+ const isVinext = hasPackageDependency("vinext");
1947
2065
  initVirtualModuleInfrastructure(root, virtualModuleDir);
1948
2066
  VirtualModule.setRoot(root);
1949
2067
  VirtualModule.ensureVirtualPackageExists();
@@ -1958,6 +2076,10 @@ function createEarlyVirtualModulesPlugin(options) {
1958
2076
  for (const key of Object.keys(shared)) {
1959
2077
  if (key.endsWith("/")) continue;
1960
2078
  const shareItem = shared[key];
2079
+ if (isVinext && key === "react") {
2080
+ addUsedShares(key);
2081
+ continue;
2082
+ }
1961
2083
  getLoadShareModulePath(key, isRolldown);
1962
2084
  writeLoadShareModule(key, shareItem, _command, isRolldown);
1963
2085
  writePreBuildLibPath(key);
@@ -1971,6 +2093,7 @@ function createEarlyVirtualModulesPlugin(options) {
1971
2093
  }
1972
2094
  function federation(mfUserOptions) {
1973
2095
  const options = normalizeModuleFederationOptions(mfUserOptions);
2096
+ const isVinext = hasPackageDependency("vinext");
1974
2097
  const { name, remotes, shared, filename, hostInitInjectLocation } = options;
1975
2098
  if (!name) throw new Error("name is required");
1976
2099
  const remoteEntryId = getRemoteEntryId(options);
@@ -1978,6 +2101,23 @@ function federation(mfUserOptions) {
1978
2101
  let command;
1979
2102
  return [
1980
2103
  createEarlyVirtualModulesPlugin(options),
2104
+ ...isVinext ? [{
2105
+ name: "module-federation-vinext-react-server-build-alias",
2106
+ apply: "build",
2107
+ enforce: "pre",
2108
+ resolveId(id) {
2109
+ const reactServerEntryMap = {
2110
+ "react/jsx-runtime": "react/cjs/react-jsx-runtime.production.js",
2111
+ "react/jsx-dev-runtime": "react/cjs/react-jsx-dev-runtime.production.js"
2112
+ };
2113
+ if (!(id in reactServerEntryMap)) return;
2114
+ const environmentName = this?.environment?.name;
2115
+ if (!environmentName || environmentName === "client") return;
2116
+ const target = reactServerEntryMap[id];
2117
+ const reactPackageJson = (0, module$1.createRequire)(new URL(`file://${process.cwd()}/package.json`)).resolve("react/package.json");
2118
+ return pathe.default.join(pathe.default.dirname(reactPackageJson), target.replace(/^react\//, ""));
2119
+ }
2120
+ }] : [],
1981
2121
  {
1982
2122
  name: "vite:module-federation-config",
1983
2123
  enforce: "pre",
@@ -2165,13 +2305,14 @@ function federation(mfUserOptions) {
2165
2305
  local: b.local,
2166
2306
  funcBody: renamedFunc
2167
2307
  });
2168
- } else nonInlineable.push(b);
2308
+ } else nonInlineable.push(resolveProxyAlias(b, proxyLocal, code, fullImport));
2169
2309
  }
2170
- if (inlineable.length === 0) continue;
2310
+ const hasRenamedAlias = nonInlineable.some((b) => bindings.find((ob) => ob.imported === b.imported)?.local !== b.local);
2311
+ if (inlineable.length === 0 && !hasRenamedAlias) continue;
2171
2312
  let replacement = "";
2172
2313
  if (nonInlineable.length > 0) replacement = `import{${nonInlineable.map((b) => b.imported === b.local ? b.imported : `${b.imported} as ${b.local}`).join(",")}}from"${importMatch[2]}";`;
2173
2314
  replacement += inlineable.map((f) => f.funcBody).join("");
2174
- code = code.replace(fullImport, replacement);
2315
+ code = code.replace(fullImport, () => replacement);
2175
2316
  modified = true;
2176
2317
  }
2177
2318
  if (modified) chunk.code = code;
package/lib/index.d.cts CHANGED
@@ -135,4 +135,4 @@ interface DtsHostOptions {
135
135
  //#region src/index.d.ts
136
136
  declare function federation(mfUserOptions: ModuleFederationOptions): Plugin[];
137
137
  //#endregion
138
- export { federation };
138
+ export { type ModuleFederationOptions, federation };
package/lib/index.d.mts CHANGED
@@ -135,4 +135,4 @@ interface DtsHostOptions {
135
135
  //#region src/index.d.ts
136
136
  declare function federation(mfUserOptions: ModuleFederationOptions): Plugin[];
137
137
  //#endregion
138
- export { federation };
138
+ export { type ModuleFederationOptions, federation };
package/lib/index.mjs CHANGED
@@ -2,6 +2,7 @@ import { createRequire } from "node:module";
2
2
  import defu from "defu";
3
3
  import * as fs from "fs";
4
4
  import { existsSync, mkdirSync, readFileSync, writeFile, writeFileSync } from "fs";
5
+ import { createRequire as createRequire$1 } from "module";
5
6
  import * as path$1 from "pathe";
6
7
  import path, { basename, dirname, join, parse, resolve } from "pathe";
7
8
  import MagicString from "magic-string";
@@ -9,7 +10,6 @@ import { createFilter } from "@rollup/pluginutils";
9
10
  import { normalizeOptions } from "@module-federation/sdk";
10
11
  import { consumeTypesAPI, generateTypesAPI, isTSProject, normalizeConsumeTypesOptions, normalizeDtsOptions, normalizeGenerateTypesOptions } from "@module-federation/dts-plugin";
11
12
  import { rpc } from "@module-federation/dts-plugin/core";
12
- import { createRequire as createRequire$1 } from "module";
13
13
  import { fileURLToPath } from "url";
14
14
  //#region \0rolldown/runtime.js
15
15
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
@@ -25,6 +25,104 @@ async function mapCodeToCodeWithSourcemap(code) {
25
25
  };
26
26
  }
27
27
  //#endregion
28
+ //#region src/utils/htmlEntryUtils.ts
29
+ function sanitizeDevEntryPath(devEntryPath) {
30
+ return devEntryPath.replace(/^[^:]+:([/\\])[/\\]?/, "$1").replace(/\\\\?/g, "/");
31
+ }
32
+ /**
33
+ * Inlines the federation init import into existing module script tags to fix
34
+ * the race condition (#396) where separate `<script type="module">` tags
35
+ * don't guarantee execution order with top-level await.
36
+ *
37
+ * If no entry scripts are found, falls back to injecting a separate script tag.
38
+ *
39
+ * @example
40
+ * // Before (two separate scripts, race condition):
41
+ * // <script type="module" src="/__mf__virtual/hostAutoInit.js"><\/script>
42
+ * // <script type="module" src="/src/main.js"><\/script>
43
+ * // After (single inline script, sequential execution):
44
+ * // <script type="module">await import("/__mf__virtual/hostAutoInit.js");await import("/src/main.js");<\/script>
45
+ */
46
+ function inlineEntryScripts(html, initSrc) {
47
+ const src = sanitizeDevEntryPath(initSrc);
48
+ const scriptTagRegex = /<script\s+([^>]*\btype=["']module["'][^>]*\bsrc=["'][^"']+["'][^>]*)>/gi;
49
+ let hasEntry = false;
50
+ const result = html.replace(scriptTagRegex, (match, attrs) => {
51
+ const srcMatch = attrs.match(/\bsrc=["']([^"']+)["']/i);
52
+ if (!srcMatch) return match;
53
+ const originalSrc = srcMatch[1];
54
+ if (originalSrc.includes("@vite/client")) return match;
55
+ hasEntry = true;
56
+ return `<script ${attrs.replace(/\s*\bsrc=["'][^"']+["']/i, "")}>await import(${JSON.stringify(src)});await import(${JSON.stringify(originalSrc)});`;
57
+ });
58
+ if (hasEntry) return result;
59
+ return html.replace("<head>", `<head><script type="module" src=${JSON.stringify(src)}><\/script>`);
60
+ }
61
+ //#endregion
62
+ //#region src/utils/packageUtils.ts
63
+ const dependencyPresenceCache = /* @__PURE__ */ new Map();
64
+ let packageDetectionCwd;
65
+ function getDependencyCacheKey(cwd, dependencyName) {
66
+ return `${cwd}:${dependencyName}`;
67
+ }
68
+ function setPackageDetectionCwd(cwd) {
69
+ packageDetectionCwd = cwd;
70
+ }
71
+ /**
72
+ * Escaping rules:
73
+ * Convert using the format __${mapping}__, where _ and $ are not allowed in npm package names but can be used in variable names.
74
+ * @ => 1
75
+ * / => 2
76
+ * - => 3
77
+ * . => 4
78
+ */
79
+ /**
80
+ * Encodes a package name into a valid file name.
81
+ * @param {string} name - The package name, e.g., "@scope/xx-xx.xx".
82
+ * @returns {string} - The encoded file name.
83
+ */
84
+ function packageNameEncode(name) {
85
+ if (typeof name !== "string") throw new Error("A string package name is required");
86
+ return name.replace(/@/g, "_mf_0_").replace(/\//g, "_mf_1_").replace(/-/g, "_mf_2_").replace(/\./g, "_mf_3_");
87
+ }
88
+ /**
89
+ * Decodes an encoded file name back to the original package name.
90
+ * @param {string} encoded - The encoded file name, e.g., "_mf_0_scope_mf_1_xx_mf_2_xx_mf_3_xx".
91
+ * @returns {string} - The decoded package name.
92
+ */
93
+ function packageNameDecode(encoded) {
94
+ if (typeof encoded !== "string") throw new Error("A string encoded file name is required");
95
+ return encoded.replace(/_mf_0_/g, "@").replace(/_mf_1_/g, "/").replace(/_mf_2_/g, "-").replace(/_mf_3_/g, ".");
96
+ }
97
+ /**
98
+ * Removes any subpath from an npm package specifier and returns the package name only.
99
+ * @param {string} packageString - The package specifier, e.g., "@scope/pkg/runtime" or "react/jsx-runtime".
100
+ * @returns {string} - The base npm package name.
101
+ */
102
+ function removePathFromNpmPackage(packageString) {
103
+ const match = packageString.match(/^(?:@[^/]+\/)?[^/]+/);
104
+ return match ? match[0] : packageString;
105
+ }
106
+ function hasPackageDependency(dependencyName, cwd = packageDetectionCwd || process.cwd()) {
107
+ const cacheKey = getDependencyCacheKey(cwd, dependencyName);
108
+ const cached = dependencyPresenceCache.get(cacheKey);
109
+ if (cached !== void 0) return cached;
110
+ try {
111
+ const packageJson = JSON.parse(readFileSync(path.join(cwd, "package.json"), "utf8"));
112
+ const hasDependency = [
113
+ packageJson.dependencies,
114
+ packageJson.devDependencies,
115
+ packageJson.peerDependencies,
116
+ packageJson.optionalDependencies
117
+ ].some((deps) => !!deps?.[dependencyName]);
118
+ dependencyPresenceCache.set(cacheKey, hasDependency);
119
+ return hasDependency;
120
+ } catch {
121
+ dependencyPresenceCache.set(cacheKey, false);
122
+ return false;
123
+ }
124
+ }
125
+ //#endregion
28
126
  //#region src/plugins/pluginAddEntry.ts
29
127
  function getFirstHtmlEntryFile(entryFiles) {
30
128
  return entryFiles.find((file) => file.endsWith(".html"));
@@ -54,7 +152,7 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
54
152
  viteConfig = config;
55
153
  const resolvedEntryPath = getEntryPath();
56
154
  devEntryPath = resolvedEntryPath.startsWith("virtual:mf") ? "@id/" + resolvedEntryPath : resolvedEntryPath;
57
- devEntryPath = config.base + devEntryPath.replace(/\\\\?/g, "/").replace(/.+?\:([/\\])[/\\]?/, "$1").replace(/^\//, "");
155
+ devEntryPath = config.base + devEntryPath.replace(/\\\\?/g, "/").replace(/^[^:]+:([/\\])[/\\]?/, "$1").replace(/^\//, "");
58
156
  },
59
157
  configureServer(server) {
60
158
  server.middlewares.use((req, res, next) => {
@@ -69,14 +167,11 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
69
167
  transformIndexHtml(c) {
70
168
  if (!injectHtml()) return;
71
169
  clientInjected = true;
72
- return c.replace("<head>", `<head><script type="module" src=${JSON.stringify(devEntryPath.replace(/.+?\:([/\\])[/\\]?/, "$1").replace(/\\\\?/g, "/"))}><\/script>`);
170
+ return inlineEntryScripts(c, devEntryPath);
73
171
  },
74
172
  transform(code, id) {
75
173
  if (id.includes("node_modules") || inject !== "html" || htmlFilePath) return;
76
- if (id.includes(".svelte-kit") && id.includes("internal.js")) {
77
- const src = devEntryPath.replace(/.+?\:([/\\])[/\\]?/, "$1").replace(/\\\\?/g, "/");
78
- return code.replace(/<head>/g, "<head><script type=\\\"module\\\" src=\\\"" + src + "\\\"><\/script>");
79
- }
174
+ if (id.includes(".svelte-kit") && id.includes("internal.js")) return code.replace(/<head>/g, "<head><script type=\\\"module\\\" src=\\\"" + sanitizeDevEntryPath(devEntryPath) + "\\\"><\/script>");
80
175
  }
81
176
  }, {
82
177
  name: "add-entry",
@@ -141,6 +236,15 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
141
236
  }
142
237
  },
143
238
  transform(code, id) {
239
+ if (hasPackageDependency("vinext") && inject === "html" && (id.includes("virtual:vite-rsc/entry-browser") || id.includes("virtual:vinext-app-browser-entry"))) {
240
+ const injection = `import ${JSON.stringify(getEntryPath())};\n`;
241
+ if (code.includes(injection.trim())) {
242
+ clientInjected = true;
243
+ return;
244
+ }
245
+ clientInjected = true;
246
+ return mapCodeToCodeWithSourcemap(injection + code);
247
+ }
144
248
  if (injectEntry() && entryFiles.some((file) => id.endsWith(file)) || _command === "serve" && inject === "html" && !clientInjected && !id.includes("node_modules") && /\.(js|ts|mjs|vue|jsx|tsx)(\?|$)/.test(id)) {
145
249
  clientInjected = true;
146
250
  return mapCodeToCodeWithSourcemap(`import ${JSON.stringify(getEntryPath())};\n` + code);
@@ -214,7 +318,9 @@ function PluginDevProxyModuleTopLevelAwait() {
214
318
  throw new Error(`${id}: ${e}`);
215
319
  }
216
320
  const magicString = new MagicString(code);
217
- (await loadWalk())(ast, { enter(node) {
321
+ const walk = await loadWalk();
322
+ const defaultExportExpression = hasPackageDependency("vinext") ? "(__mfproxy__awaitdefault?.default ?? __mfproxy__awaitdefault)" : "__mfproxy__awaitdefault";
323
+ walk(ast, { enter(node) {
218
324
  if (node.type === "ExportNamedDeclaration" && node.specifiers) {
219
325
  const exportSpecifiers = node.specifiers.map((specifier) => specifier.exported.name);
220
326
  const proxyStatements = exportSpecifiers.map((name) => `
@@ -235,15 +341,15 @@ function PluginDevProxyModuleTopLevelAwait() {
235
341
  let exportStatement = "default";
236
342
  if (declaration.type === "Identifier") proxyStatement = `
237
343
  const __mfproxy__awaitdefault = await ${declaration.name}();
238
- const __mfproxy__default = __mfproxy__awaitdefault;
344
+ const __mfproxy__default = ${defaultExportExpression};
239
345
  `;
240
346
  else if (declaration.type === "CallExpression" || declaration.type === "FunctionDeclaration") proxyStatement = `
241
347
  const __mfproxy__awaitdefault = await (${code.slice(declaration.start, declaration.end)});
242
- const __mfproxy__default = __mfproxy__awaitdefault;
348
+ const __mfproxy__default = ${defaultExportExpression};
243
349
  `;
244
350
  else proxyStatement = `
245
351
  const __mfproxy__awaitdefault = await (${code.slice(declaration.start, declaration.end)});
246
- const __mfproxy__default = __mfproxy__awaitdefault;
352
+ const __mfproxy__default = ${defaultExportExpression};
247
353
  `;
248
354
  const replacement = `${proxyStatement}\nexport { __mfproxy__default as ${exportStatement} };`;
249
355
  magicString.overwrite(start, end, replacement);
@@ -525,10 +631,6 @@ function normalizeRemoteItem(key, remote) {
525
631
  entryGlobalName: key
526
632
  }, remote);
527
633
  }
528
- function removePathFromNpmPackage(packageString) {
529
- const match = packageString.match(/^(?:@[^/]+\/)?[^/]+/);
530
- return match ? match[0] : packageString;
531
- }
532
634
  /**
533
635
  * Tries to find the package.json's version of a shared package
534
636
  * if `package.json` is not declared in `exports`
@@ -537,13 +639,13 @@ function removePathFromNpmPackage(packageString) {
537
639
  */
538
640
  function searchPackageVersion(sharedName) {
539
641
  try {
540
- const sharedPath = __require.resolve(sharedName);
642
+ const sharedPath = createRequire(process.cwd()).resolve(sharedName);
541
643
  let potentialPackageJsonDir = path$1.dirname(sharedPath);
542
644
  const rootDir = path$1.parse(potentialPackageJsonDir).root;
543
645
  while (path$1.parse(potentialPackageJsonDir).base !== "node_modules" && potentialPackageJsonDir !== rootDir) {
544
646
  const potentialPackageJsonPath = path$1.join(potentialPackageJsonDir, "package.json");
545
647
  if (fs.existsSync(potentialPackageJsonPath)) {
546
- const potentialPackageJson = __require(potentialPackageJsonPath);
648
+ const potentialPackageJson = JSON.parse(fs.readFileSync(potentialPackageJsonPath, "utf-8"));
547
649
  if (typeof potentialPackageJson == "object" && potentialPackageJson !== null && typeof potentialPackageJson.version === "string" && potentialPackageJson.name === sharedName) return potentialPackageJson.version;
548
650
  }
549
651
  potentialPackageJsonDir = path$1.dirname(potentialPackageJsonDir);
@@ -654,34 +756,6 @@ function normalizeModuleFederationOptions(options) {
654
756
  };
655
757
  }
656
758
  //#endregion
657
- //#region src/utils/packageNameUtils.ts
658
- /**
659
- * Escaping rules:
660
- * Convert using the format __${mapping}__, where _ and $ are not allowed in npm package names but can be used in variable names.
661
- * @ => 1
662
- * / => 2
663
- * - => 3
664
- * . => 4
665
- */
666
- /**
667
- * Encodes a package name into a valid file name.
668
- * @param {string} name - The package name, e.g., "@scope/xx-xx.xx".
669
- * @returns {string} - The encoded file name.
670
- */
671
- function packageNameEncode(name) {
672
- if (typeof name !== "string") throw new Error("A string package name is required");
673
- return name.replace(/@/g, "_mf_0_").replace(/\//g, "_mf_1_").replace(/-/g, "_mf_2_").replace(/\./g, "_mf_3_");
674
- }
675
- /**
676
- * Decodes an encoded file name back to the original package name.
677
- * @param {string} encoded - The encoded file name, e.g., "_mf_0_scope_mf_1_xx_mf_2_xx_mf_3_xx".
678
- * @returns {string} - The decoded package name.
679
- */
680
- function packageNameDecode(encoded) {
681
- if (typeof encoded !== "string") throw new Error("A string encoded file name is required");
682
- return encoded.replace(/_mf_0_/g, "@").replace(/_mf_1_/g, "/").replace(/_mf_2_/g, "-").replace(/_mf_3_/g, ".");
683
- }
684
- //#endregion
685
759
  //#region src/utils/localSharedImportMap_temp.ts
686
760
  /**
687
761
  * https://github.com/module-federation/vite/issues/68
@@ -955,6 +1029,14 @@ function getPackageNamedExports(pkg) {
955
1029
  return [];
956
1030
  }
957
1031
  }
1032
+ function getLocalProviderImportPath(pkg) {
1033
+ try {
1034
+ const resolved = createRequire$1(new URL("file://" + process.cwd() + "/package.json")).resolve(pkg);
1035
+ return resolved.includes("/node_modules/") || resolved.includes("\\node_modules\\") ? void 0 : resolved;
1036
+ } catch {
1037
+ return;
1038
+ }
1039
+ }
958
1040
  const preBuildCacheMap = {};
959
1041
  const PREBUILD_TAG = "__prebuild__";
960
1042
  function writePreBuildLibPath(pkg) {
@@ -976,6 +1058,8 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
976
1058
  const useESM = command === "build" || isRolldown;
977
1059
  const importLine = useESM ? `import { initPromise } from "${virtualRuntimeInitStatus.getImportId()}"` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
978
1060
  const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
1061
+ const useSsrProviderFallback = hasPackageDependency("vinext") && command === "build" && pkg === "react";
1062
+ const providerImportId = getLocalProviderImportPath(pkg) || getPreBuildLibImportId(pkg);
979
1063
  const namedExports = getPackageNamedExports(pkg);
980
1064
  let exportLine;
981
1065
  if (namedExports.length > 0) {
@@ -987,6 +1071,9 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
987
1071
  import ${JSON.stringify(getPreBuildLibImportId(pkg))};
988
1072
  ${command !== "build" ? `;() => import(${JSON.stringify(pkg)}).catch(() => {});` : ""}
989
1073
  ${importLine}
1074
+ ${useSsrProviderFallback ? `const providerModulePromise = typeof window === "undefined"
1075
+ ? import(${JSON.stringify(providerImportId)})
1076
+ : undefined` : ""}
990
1077
  const res = initPromise.then(runtime => runtime.loadShare(${JSON.stringify(pkg)}, {
991
1078
  customShareInfo: {shareConfig:{
992
1079
  singleton: ${shareItem.shareConfig.singleton},
@@ -994,7 +1081,9 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
994
1081
  requiredVersion: ${JSON.stringify(shareItem.shareConfig.requiredVersion)}
995
1082
  }}
996
1083
  }))
997
- const exportModule = ${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory))
1084
+ const exportModule = ${useSsrProviderFallback ? `(typeof window === "undefined"
1085
+ ? ((await providerModulePromise)?.default ?? await providerModulePromise)
1086
+ : ${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory)))` : `${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory))`}
998
1087
  ${exportLine}
999
1088
  `);
1000
1089
  }
@@ -1020,6 +1109,7 @@ function writeLocalSharedImportMap() {
1020
1109
  }
1021
1110
  }
1022
1111
  function generateLocalSharedImportMap() {
1112
+ const isVinext = hasPackageDependency("vinext");
1023
1113
  const options = getNormalizeModuleFederationOptions();
1024
1114
  return `
1025
1115
  import {loadShare} from "@module-federation/runtime";
@@ -1028,7 +1118,8 @@ function generateLocalSharedImportMap() {
1028
1118
  const shareItem = getNormalizeShareItem(pkg);
1029
1119
  return `
1030
1120
  ${JSON.stringify(pkg)}: async () => {
1031
- ${shareItem?.shareConfig.import === false ? `throw new Error(\`Shared module '\${${JSON.stringify(pkg)}}' must be provided by host\`);` : `let pkg = await import("${getPreBuildLibImportId(pkg)}");
1121
+ ${shareItem?.shareConfig.import === false ? `throw new Error(\`Shared module '\${${JSON.stringify(pkg)}}' must be provided by host\`);` : isVinext && pkg === "react" ? `let pkg = await import("react");
1122
+ return pkg;` : `let pkg = await import("${getPreBuildLibImportId(pkg)}");
1032
1123
  return pkg;`}
1033
1124
  }
1034
1125
  `;
@@ -1052,7 +1143,9 @@ function generateLocalSharedImportMap() {
1052
1143
  usedShared[${JSON.stringify(key)}].loaded = true
1053
1144
  const {${JSON.stringify(key)}: pkgDynamicImport} = importMap
1054
1145
  const res = await pkgDynamicImport()
1055
- const exportModule = {...res}
1146
+ const exportModule = ${JSON.stringify(isVinext)} && ${JSON.stringify(key)} === "react"
1147
+ ? (res?.default ?? res)
1148
+ : {...res}
1056
1149
  // All npm packages pre-built by vite will be converted to esm
1057
1150
  Object.defineProperty(exportModule, "__esModule", {
1058
1151
  value: true,
@@ -1134,6 +1227,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
1134
1227
  if (initScope.indexOf(initToken) >= 0) return;
1135
1228
  initScope.push(initToken);
1136
1229
  initRes.initShareScopeMap('${options.shareScope}', shared);
1230
+ initResolve(initRes)
1137
1231
  try {
1138
1232
  await Promise.all(await initRes.initializeSharing('${options.shareScope}', {
1139
1233
  strategy: '${options.shareStrategy}',
@@ -1143,7 +1237,6 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
1143
1237
  } catch (e) {
1144
1238
  console.error(e)
1145
1239
  }
1146
- initResolve(initRes)
1147
1240
  return initRes
1148
1241
  }
1149
1242
 
@@ -1161,11 +1254,11 @@ const hostAutoInitModule = new VirtualModule("hostAutoInit", "__H_A_I__");
1161
1254
  function writeHostAutoInit(remoteEntryId = REMOTE_ENTRY_ID) {
1162
1255
  hostAutoInitModule.writeSync(`
1163
1256
  const remoteEntryPromise = import("${remoteEntryId}")
1164
- // __tla only serves as a hack for vite-plugin-top-level-await.
1165
1257
  Promise.resolve(remoteEntryPromise)
1166
1258
  .then(remoteEntry => {
1167
1259
  return Promise.resolve(remoteEntry.__tla)
1168
- .then(remoteEntry.init).catch(remoteEntry.init)
1260
+ .then(remoteEntry.init)
1261
+ .catch(remoteEntry.init)
1169
1262
  })
1170
1263
  `);
1171
1264
  }
@@ -1184,6 +1277,20 @@ function initVirtualModules(command, remoteEntryId) {
1184
1277
  }
1185
1278
  //#endregion
1186
1279
  //#region src/utils/bundleHelpers.ts
1280
+ /**
1281
+ * Resolve the local alias for a non-inlineable proxy binding.
1282
+ * If Rollup's deconflict renamed the alias but didn't update references
1283
+ * in the code body, fall back to proxyLocal so they stay in sync.
1284
+ */
1285
+ function resolveProxyAlias(binding, proxyLocal, code, fullImport) {
1286
+ const codeWithoutImport = code.replace(fullImport, "");
1287
+ const escapedLocal = binding.local.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1288
+ const localUsedInCode = new RegExp(`\\b${escapedLocal}\\b`).test(codeWithoutImport);
1289
+ return {
1290
+ imported: binding.imported,
1291
+ local: localUsedInCode ? binding.local : proxyLocal
1292
+ };
1293
+ }
1187
1294
  function findRemoteEntryFile(filename, bundle) {
1188
1295
  for (const [_, fileData] of Object.entries(bundle)) if (filename.replace(/[\[\]]/g, "_").replace(/\.[^/.]+$/, "") === fileData.name || fileData.name === "remoteEntry") return fileData.fileName;
1189
1296
  }
@@ -1731,6 +1838,7 @@ function proxySharedModule(options) {
1731
1838
  const { shared = {} } = options;
1732
1839
  let _config;
1733
1840
  let _command = "serve";
1841
+ let isVinext = false;
1734
1842
  const savePrebuild = new PromiseStore();
1735
1843
  return [{
1736
1844
  name: "generateLocalSharedImportMap",
@@ -1745,9 +1853,11 @@ function proxySharedModule(options) {
1745
1853
  name: "proxyPreBuildShared",
1746
1854
  enforce: "post",
1747
1855
  config(config, { command }) {
1856
+ setPackageDetectionCwd(config.root || process.cwd());
1857
+ isVinext = hasPackageDependency("vinext");
1748
1858
  const isRolldown = !!this?.meta?.rolldownVersion;
1749
1859
  _command = command;
1750
- config.resolve.alias.push(...Object.keys(shared).map((key) => {
1860
+ config.resolve.alias.push(...Object.keys(shared).filter((key) => !(isVinext && key === "react")).map((key) => {
1751
1861
  const keyBase = key.endsWith("/") ? key.slice(0, -1) : key;
1752
1862
  const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1753
1863
  const escapedKeyBase = escapeRegex(keyBase);
@@ -1757,6 +1867,7 @@ function proxySharedModule(options) {
1757
1867
  replacement: "$1",
1758
1868
  customResolver(source, importer) {
1759
1869
  if (/\.css$/.test(source)) return;
1870
+ if (isVinext && source === "react") return;
1760
1871
  if (importer && importer.includes("localSharedImportMap")) return;
1761
1872
  if (key.endsWith("/") && source !== key.slice(0, -1)) return;
1762
1873
  const loadSharePath = getLoadShareModulePath(source, isRolldown, command);
@@ -1768,7 +1879,7 @@ function proxySharedModule(options) {
1768
1879
  }
1769
1880
  };
1770
1881
  }));
1771
- config.resolve.alias.push(...Object.keys(shared).map((key) => {
1882
+ config.resolve.alias.push(...Object.keys(shared).filter((key) => !(isVinext && key === "react")).map((key) => {
1772
1883
  return command === "build" ? {
1773
1884
  find: new RegExp(`(.*${PREBUILD_TAG}.*)`),
1774
1885
  replacement: function($1) {
@@ -1791,6 +1902,10 @@ function proxySharedModule(options) {
1791
1902
  const isRolldown = !!config.experimental?.rolldownDev;
1792
1903
  Object.keys(shared).forEach((key) => {
1793
1904
  if (key.endsWith("/")) return;
1905
+ if (isVinext && key === "react") {
1906
+ addUsedShares(key);
1907
+ return;
1908
+ }
1794
1909
  writeLoadShareModule(key, shared[key], _command, isRolldown);
1795
1910
  writePreBuildLibPath(key);
1796
1911
  addUsedShares(key);
@@ -1922,6 +2037,8 @@ function createEarlyVirtualModulesPlugin(options) {
1922
2037
  enforce: "pre",
1923
2038
  config(config, { command: _command }) {
1924
2039
  const root = config.root || process.cwd();
2040
+ setPackageDetectionCwd(root);
2041
+ const isVinext = hasPackageDependency("vinext");
1925
2042
  initVirtualModuleInfrastructure(root, virtualModuleDir);
1926
2043
  VirtualModule.setRoot(root);
1927
2044
  VirtualModule.ensureVirtualPackageExists();
@@ -1936,6 +2053,10 @@ function createEarlyVirtualModulesPlugin(options) {
1936
2053
  for (const key of Object.keys(shared)) {
1937
2054
  if (key.endsWith("/")) continue;
1938
2055
  const shareItem = shared[key];
2056
+ if (isVinext && key === "react") {
2057
+ addUsedShares(key);
2058
+ continue;
2059
+ }
1939
2060
  getLoadShareModulePath(key, isRolldown);
1940
2061
  writeLoadShareModule(key, shareItem, _command, isRolldown);
1941
2062
  writePreBuildLibPath(key);
@@ -1949,6 +2070,7 @@ function createEarlyVirtualModulesPlugin(options) {
1949
2070
  }
1950
2071
  function federation(mfUserOptions) {
1951
2072
  const options = normalizeModuleFederationOptions(mfUserOptions);
2073
+ const isVinext = hasPackageDependency("vinext");
1952
2074
  const { name, remotes, shared, filename, hostInitInjectLocation } = options;
1953
2075
  if (!name) throw new Error("name is required");
1954
2076
  const remoteEntryId = getRemoteEntryId(options);
@@ -1956,6 +2078,23 @@ function federation(mfUserOptions) {
1956
2078
  let command;
1957
2079
  return [
1958
2080
  createEarlyVirtualModulesPlugin(options),
2081
+ ...isVinext ? [{
2082
+ name: "module-federation-vinext-react-server-build-alias",
2083
+ apply: "build",
2084
+ enforce: "pre",
2085
+ resolveId(id) {
2086
+ const reactServerEntryMap = {
2087
+ "react/jsx-runtime": "react/cjs/react-jsx-runtime.production.js",
2088
+ "react/jsx-dev-runtime": "react/cjs/react-jsx-dev-runtime.production.js"
2089
+ };
2090
+ if (!(id in reactServerEntryMap)) return;
2091
+ const environmentName = this?.environment?.name;
2092
+ if (!environmentName || environmentName === "client") return;
2093
+ const target = reactServerEntryMap[id];
2094
+ const reactPackageJson = createRequire$1(new URL(`file://${process.cwd()}/package.json`)).resolve("react/package.json");
2095
+ return path.join(path.dirname(reactPackageJson), target.replace(/^react\//, ""));
2096
+ }
2097
+ }] : [],
1959
2098
  {
1960
2099
  name: "vite:module-federation-config",
1961
2100
  enforce: "pre",
@@ -2143,13 +2282,14 @@ function federation(mfUserOptions) {
2143
2282
  local: b.local,
2144
2283
  funcBody: renamedFunc
2145
2284
  });
2146
- } else nonInlineable.push(b);
2285
+ } else nonInlineable.push(resolveProxyAlias(b, proxyLocal, code, fullImport));
2147
2286
  }
2148
- if (inlineable.length === 0) continue;
2287
+ const hasRenamedAlias = nonInlineable.some((b) => bindings.find((ob) => ob.imported === b.imported)?.local !== b.local);
2288
+ if (inlineable.length === 0 && !hasRenamedAlias) continue;
2149
2289
  let replacement = "";
2150
2290
  if (nonInlineable.length > 0) replacement = `import{${nonInlineable.map((b) => b.imported === b.local ? b.imported : `${b.imported} as ${b.local}`).join(",")}}from"${importMatch[2]}";`;
2151
2291
  replacement += inlineable.map((f) => f.funcBody).join("");
2152
- code = code.replace(fullImport, replacement);
2292
+ code = code.replace(fullImport, () => replacement);
2153
2293
  modified = true;
2154
2294
  }
2155
2295
  if (modified) chunk.code = code;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/vite",
3
- "version": "1.12.2",
3
+ "version": "1.13.0",
4
4
  "description": "Vite plugin for Module Federation",
5
5
  "type": "module",
6
6
  "main": "./lib/index.cjs",
@@ -14,7 +14,8 @@
14
14
  },
15
15
  "import": "./lib/index.mjs",
16
16
  "require": "./lib/index.cjs"
17
- }
17
+ },
18
+ "./package.json": "./package.json"
18
19
  },
19
20
  "files": [
20
21
  "lib/**/*"
@@ -65,9 +66,9 @@
65
66
  "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
66
67
  },
67
68
  "dependencies": {
68
- "@module-federation/dts-plugin": "^2.0.1",
69
- "@module-federation/runtime": "^2.0.1",
70
- "@module-federation/sdk": "^2.0.1",
69
+ "@module-federation/dts-plugin": "2.2.0",
70
+ "@module-federation/runtime": "2.0.1",
71
+ "@module-federation/sdk": "2.0.1",
71
72
  "@rollup/pluginutils": "^5.3.0",
72
73
  "defu": "^6.1.4",
73
74
  "estree-walker": "^3.0.3",
@@ -87,4 +88,4 @@
87
88
  "vite": "^7.3.1",
88
89
  "vitest": "^4.0.18"
89
90
  }
90
- }
91
+ }