@module-federation/vite 1.12.3 → 1.13.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
@@ -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",
@@ -89,6 +184,12 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
89
184
  else if (Array.isArray(inputOptions)) entryFiles = inputOptions;
90
185
  else if (typeof inputOptions === "object") entryFiles = Object.values(inputOptions);
91
186
  if (entryFiles && entryFiles.length > 0) htmlFilePath = getFirstHtmlEntryFile(entryFiles);
187
+ if (_command === "serve" && htmlFilePath && fs.existsSync(htmlFilePath)) {
188
+ const htmlContent = fs.readFileSync(htmlFilePath, "utf-8");
189
+ const scriptRegex = /<script\s+[^>]*src=["']([^"']+)["'][^>]*>/gi;
190
+ let match;
191
+ while ((match = scriptRegex.exec(htmlContent)) !== null) entryFiles.push(match[1]);
192
+ }
92
193
  },
93
194
  buildStart() {
94
195
  if (_command === "serve") return;
@@ -141,6 +242,15 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
141
242
  }
142
243
  },
143
244
  transform(code, id) {
245
+ if (hasPackageDependency("vinext") && inject === "html" && (id.includes("virtual:vite-rsc/entry-browser") || id.includes("virtual:vinext-app-browser-entry"))) {
246
+ const injection = `import ${JSON.stringify(getEntryPath())};\n`;
247
+ if (code.includes(injection.trim())) {
248
+ clientInjected = true;
249
+ return;
250
+ }
251
+ clientInjected = true;
252
+ return mapCodeToCodeWithSourcemap(injection + code);
253
+ }
144
254
  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
255
  clientInjected = true;
146
256
  return mapCodeToCodeWithSourcemap(`import ${JSON.stringify(getEntryPath())};\n` + code);
@@ -214,7 +324,9 @@ function PluginDevProxyModuleTopLevelAwait() {
214
324
  throw new Error(`${id}: ${e}`);
215
325
  }
216
326
  const magicString = new MagicString(code);
217
- (await loadWalk())(ast, { enter(node) {
327
+ const walk = await loadWalk();
328
+ const defaultExportExpression = hasPackageDependency("vinext") ? "(__mfproxy__awaitdefault?.default ?? __mfproxy__awaitdefault)" : "__mfproxy__awaitdefault";
329
+ walk(ast, { enter(node) {
218
330
  if (node.type === "ExportNamedDeclaration" && node.specifiers) {
219
331
  const exportSpecifiers = node.specifiers.map((specifier) => specifier.exported.name);
220
332
  const proxyStatements = exportSpecifiers.map((name) => `
@@ -235,15 +347,15 @@ function PluginDevProxyModuleTopLevelAwait() {
235
347
  let exportStatement = "default";
236
348
  if (declaration.type === "Identifier") proxyStatement = `
237
349
  const __mfproxy__awaitdefault = await ${declaration.name}();
238
- const __mfproxy__default = __mfproxy__awaitdefault;
350
+ const __mfproxy__default = ${defaultExportExpression};
239
351
  `;
240
352
  else if (declaration.type === "CallExpression" || declaration.type === "FunctionDeclaration") proxyStatement = `
241
353
  const __mfproxy__awaitdefault = await (${code.slice(declaration.start, declaration.end)});
242
- const __mfproxy__default = __mfproxy__awaitdefault;
354
+ const __mfproxy__default = ${defaultExportExpression};
243
355
  `;
244
356
  else proxyStatement = `
245
357
  const __mfproxy__awaitdefault = await (${code.slice(declaration.start, declaration.end)});
246
- const __mfproxy__default = __mfproxy__awaitdefault;
358
+ const __mfproxy__default = ${defaultExportExpression};
247
359
  `;
248
360
  const replacement = `${proxyStatement}\nexport { __mfproxy__default as ${exportStatement} };`;
249
361
  magicString.overwrite(start, end, replacement);
@@ -525,10 +637,6 @@ function normalizeRemoteItem(key, remote) {
525
637
  entryGlobalName: key
526
638
  }, remote);
527
639
  }
528
- function removePathFromNpmPackage(packageString) {
529
- const match = packageString.match(/^(?:@[^/]+\/)?[^/]+/);
530
- return match ? match[0] : packageString;
531
- }
532
640
  /**
533
641
  * Tries to find the package.json's version of a shared package
534
642
  * if `package.json` is not declared in `exports`
@@ -537,19 +645,23 @@ function removePathFromNpmPackage(packageString) {
537
645
  */
538
646
  function searchPackageVersion(sharedName) {
539
647
  try {
540
- const sharedPath = __require.resolve(sharedName);
648
+ const sharedPath = createRequire(process.cwd()).resolve(sharedName);
541
649
  let potentialPackageJsonDir = path$1.dirname(sharedPath);
542
650
  const rootDir = path$1.parse(potentialPackageJsonDir).root;
543
651
  while (path$1.parse(potentialPackageJsonDir).base !== "node_modules" && potentialPackageJsonDir !== rootDir) {
544
652
  const potentialPackageJsonPath = path$1.join(potentialPackageJsonDir, "package.json");
545
653
  if (fs.existsSync(potentialPackageJsonPath)) {
546
- const potentialPackageJson = __require(potentialPackageJsonPath);
654
+ const potentialPackageJson = JSON.parse(fs.readFileSync(potentialPackageJsonPath, "utf-8"));
547
655
  if (typeof potentialPackageJson == "object" && potentialPackageJson !== null && typeof potentialPackageJson.version === "string" && potentialPackageJson.name === sharedName) return potentialPackageJson.version;
548
656
  }
549
657
  potentialPackageJsonDir = path$1.dirname(potentialPackageJsonDir);
550
658
  }
551
659
  } catch (_) {}
552
660
  }
661
+ function inferVersionFromRequiredVersion(requiredVersion) {
662
+ if (!requiredVersion) return void 0;
663
+ return requiredVersion.match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/)?.[0];
664
+ }
553
665
  function normalizeShareItem(key, shareItem) {
554
666
  let version;
555
667
  try {
@@ -580,7 +692,7 @@ function normalizeShareItem(key, shareItem) {
580
692
  return {
581
693
  name: key,
582
694
  from: "",
583
- version: shareItem.version || version,
695
+ version: shareItem.version || inferVersionFromRequiredVersion(shareItem.requiredVersion) || version,
584
696
  scope: shareItem.shareScope || "default",
585
697
  shareConfig: {
586
698
  import: typeof shareItem === "object" ? shareItem.import : void 0,
@@ -654,34 +766,6 @@ function normalizeModuleFederationOptions(options) {
654
766
  };
655
767
  }
656
768
  //#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
769
  //#region src/utils/localSharedImportMap_temp.ts
686
770
  /**
687
771
  * https://github.com/module-federation/vite/issues/68
@@ -875,12 +959,12 @@ function generateExposes(options) {
875
959
  //#endregion
876
960
  //#region src/virtualModules/virtualRuntimeInitStatus.ts
877
961
  const virtualRuntimeInitStatus = new VirtualModule("runtimeInit");
878
- function writeRuntimeInitStatus(command) {
879
- const globalKey = `__mf_init__${virtualRuntimeInitStatus.getImportId()}__`;
880
- const exportStatement = command === "build" ? `const { initPromise, initResolve, initReject } = globalThis[globalKey];
881
- export { initPromise, initResolve, initReject };` : `module.exports = globalThis[globalKey];`;
882
- virtualRuntimeInitStatus.writeSync(`
883
- const globalKey = ${JSON.stringify(globalKey)};
962
+ function getRuntimeInitGlobalKey() {
963
+ return `__mf_init__${virtualRuntimeInitStatus.getImportId()}__`;
964
+ }
965
+ function getRuntimeInitBootstrapCode() {
966
+ return `
967
+ const globalKey = ${JSON.stringify(getRuntimeInitGlobalKey())};
884
968
  if (!globalThis[globalKey]) {
885
969
  let initResolve, initReject;
886
970
  const initPromise = new Promise((re, rj) => {
@@ -892,8 +976,6 @@ if (!globalThis[globalKey]) {
892
976
  initResolve,
893
977
  initReject,
894
978
  };
895
- // In SSR (no window), resolve immediately with a stub runtime
896
- // so modules don't hang waiting for browser-only init
897
979
  if (typeof window === 'undefined') {
898
980
  initResolve({
899
981
  loadRemote: function() { return Promise.resolve(undefined); },
@@ -901,6 +983,64 @@ if (!globalThis[globalKey]) {
901
983
  });
902
984
  }
903
985
  }
986
+ `;
987
+ }
988
+ function getRuntimeInitPromiseBootstrapCode() {
989
+ return `
990
+ const __mfPromiseGlobalKey = ${JSON.stringify(getRuntimeInitGlobalKey())};
991
+ let __mfPromiseState = globalThis[__mfPromiseGlobalKey];
992
+ if (!__mfPromiseState) {
993
+ let initResolve, initReject;
994
+ const initPromise = new Promise((re, rj) => {
995
+ initResolve = re;
996
+ initReject = rj;
997
+ });
998
+ __mfPromiseState = globalThis[__mfPromiseGlobalKey] = {
999
+ initPromise,
1000
+ initResolve,
1001
+ initReject,
1002
+ };
1003
+ if (typeof window === 'undefined') {
1004
+ initResolve({
1005
+ loadRemote: function() { return Promise.resolve(undefined); },
1006
+ loadShare: function() { return Promise.resolve(undefined); },
1007
+ });
1008
+ }
1009
+ }
1010
+ const initPromise = __mfPromiseState.initPromise;
1011
+ `;
1012
+ }
1013
+ function getRuntimeInitResolveBootstrapCode() {
1014
+ return `
1015
+ const __mfResolveGlobalKey = ${JSON.stringify(getRuntimeInitGlobalKey())};
1016
+ let __mfResolveState = globalThis[__mfResolveGlobalKey];
1017
+ if (!__mfResolveState) {
1018
+ let initResolve, initReject;
1019
+ const initPromise = new Promise((re, rj) => {
1020
+ initResolve = re;
1021
+ initReject = rj;
1022
+ });
1023
+ __mfResolveState = globalThis[__mfResolveGlobalKey] = {
1024
+ initPromise,
1025
+ initResolve,
1026
+ initReject,
1027
+ };
1028
+ if (typeof window === 'undefined') {
1029
+ initResolve({
1030
+ loadRemote: function() { return Promise.resolve(undefined); },
1031
+ loadShare: function() { return Promise.resolve(undefined); },
1032
+ });
1033
+ }
1034
+ }
1035
+ const initResolve = __mfResolveState.initResolve;
1036
+ `;
1037
+ }
1038
+ function writeRuntimeInitStatus(command) {
1039
+ getRuntimeInitGlobalKey();
1040
+ const exportStatement = command === "build" ? `const { initPromise, initResolve, initReject } = globalThis[globalKey];
1041
+ export { initPromise, initResolve, initReject };` : `module.exports = globalThis[globalKey];`;
1042
+ virtualRuntimeInitStatus.writeSync(`
1043
+ ${getRuntimeInitBootstrapCode()}
904
1044
  ${exportStatement}
905
1045
  `);
906
1046
  }
@@ -925,7 +1065,8 @@ function getUsedRemotesMap() {
925
1065
  }
926
1066
  function generateRemotes(id, command, isRolldown) {
927
1067
  const useESM = command === "build" || isRolldown;
928
- const importLine = useESM ? `import { initPromise } from "${virtualRuntimeInitStatus.getImportId()}"` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
1068
+ const importLine = command === "build" ? getRuntimeInitPromiseBootstrapCode() : useESM ? `${getRuntimeInitBootstrapCode()}
1069
+ const { initPromise } = globalThis[globalKey];` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
929
1070
  const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
930
1071
  const exportLine = command === "serve" && useESM ? "export default exportModule.default ?? exportModule" : useESM ? "export default exportModule" : "module.exports = exportModule";
931
1072
  return `
@@ -955,6 +1096,14 @@ function getPackageNamedExports(pkg) {
955
1096
  return [];
956
1097
  }
957
1098
  }
1099
+ function getLocalProviderImportPath(pkg) {
1100
+ try {
1101
+ const resolved = createRequire$1(new URL("file://" + process.cwd() + "/package.json")).resolve(pkg);
1102
+ return resolved.includes("/node_modules/") || resolved.includes("\\node_modules\\") ? void 0 : resolved;
1103
+ } catch {
1104
+ return;
1105
+ }
1106
+ }
958
1107
  const preBuildCacheMap = {};
959
1108
  const PREBUILD_TAG = "__prebuild__";
960
1109
  function writePreBuildLibPath(pkg) {
@@ -974,8 +1123,11 @@ function getLoadShareModulePath(pkg, isRolldown, command) {
974
1123
  function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
975
1124
  if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, isRolldown || command === "build" ? ".mjs" : ".js");
976
1125
  const useESM = command === "build" || isRolldown;
977
- const importLine = useESM ? `import { initPromise } from "${virtualRuntimeInitStatus.getImportId()}"` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
1126
+ const importLine = command === "build" ? getRuntimeInitPromiseBootstrapCode() : useESM ? `${getRuntimeInitBootstrapCode()}
1127
+ const { initPromise } = globalThis[globalKey];` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
978
1128
  const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
1129
+ const useSsrProviderFallback = hasPackageDependency("vinext") && command === "build" && pkg === "react";
1130
+ const providerImportId = getLocalProviderImportPath(pkg) || getPreBuildLibImportId(pkg);
979
1131
  const namedExports = getPackageNamedExports(pkg);
980
1132
  let exportLine;
981
1133
  if (namedExports.length > 0) {
@@ -987,6 +1139,9 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
987
1139
  import ${JSON.stringify(getPreBuildLibImportId(pkg))};
988
1140
  ${command !== "build" ? `;() => import(${JSON.stringify(pkg)}).catch(() => {});` : ""}
989
1141
  ${importLine}
1142
+ ${useSsrProviderFallback ? `const providerModulePromise = typeof window === "undefined"
1143
+ ? import(${JSON.stringify(providerImportId)})
1144
+ : undefined` : ""}
990
1145
  const res = initPromise.then(runtime => runtime.loadShare(${JSON.stringify(pkg)}, {
991
1146
  customShareInfo: {shareConfig:{
992
1147
  singleton: ${shareItem.shareConfig.singleton},
@@ -994,7 +1149,9 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
994
1149
  requiredVersion: ${JSON.stringify(shareItem.shareConfig.requiredVersion)}
995
1150
  }}
996
1151
  }))
997
- const exportModule = ${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory))
1152
+ const exportModule = ${useSsrProviderFallback ? `(typeof window === "undefined"
1153
+ ? ((await providerModulePromise)?.default ?? await providerModulePromise)
1154
+ : ${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory)))` : `${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory))`}
998
1155
  ${exportLine}
999
1156
  `);
1000
1157
  }
@@ -1011,15 +1168,16 @@ new VirtualModule("localSharedImportMap");
1011
1168
  function getLocalSharedImportMapPath() {
1012
1169
  return getLocalSharedImportMapPath_temp();
1013
1170
  }
1014
- let prevSharedCount;
1171
+ let prevLocalSharedImportMapContent;
1015
1172
  function writeLocalSharedImportMap() {
1016
- const sharedCount = getUsedShares().size;
1017
- if (prevSharedCount !== sharedCount) {
1018
- prevSharedCount = sharedCount;
1019
- writeLocalSharedImportMap_temp(generateLocalSharedImportMap());
1173
+ const nextContent = generateLocalSharedImportMap();
1174
+ if (prevLocalSharedImportMapContent !== nextContent) {
1175
+ prevLocalSharedImportMapContent = nextContent;
1176
+ writeLocalSharedImportMap_temp(nextContent);
1020
1177
  }
1021
1178
  }
1022
1179
  function generateLocalSharedImportMap() {
1180
+ const isVinext = hasPackageDependency("vinext");
1023
1181
  const options = getNormalizeModuleFederationOptions();
1024
1182
  return `
1025
1183
  import {loadShare} from "@module-federation/runtime";
@@ -1028,7 +1186,8 @@ function generateLocalSharedImportMap() {
1028
1186
  const shareItem = getNormalizeShareItem(pkg);
1029
1187
  return `
1030
1188
  ${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)}");
1189
+ ${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");
1190
+ return pkg;` : `let pkg = await import("${getPreBuildLibImportId(pkg)}");
1032
1191
  return pkg;`}
1033
1192
  }
1034
1193
  `;
@@ -1052,7 +1211,9 @@ function generateLocalSharedImportMap() {
1052
1211
  usedShared[${JSON.stringify(key)}].loaded = true
1053
1212
  const {${JSON.stringify(key)}: pkgDynamicImport} = importMap
1054
1213
  const res = await pkgDynamicImport()
1055
- const exportModule = {...res}
1214
+ const exportModule = ${JSON.stringify(isVinext)} && ${JSON.stringify(key)} === "react"
1215
+ ? (res?.default ?? res)
1216
+ : {...res}
1056
1217
  // All npm packages pre-built by vite will be converted to esm
1057
1218
  Object.defineProperty(exportModule, "__esModule", {
1058
1219
  value: true,
@@ -1095,7 +1256,7 @@ const REMOTE_ENTRY_ID = "virtual:mf-REMOTE_ENTRY_ID";
1095
1256
  function getRemoteEntryId(options) {
1096
1257
  return `${REMOTE_ENTRY_ID}:${`${options.name}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
1097
1258
  }
1098
- function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(options)) {
1259
+ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(options), command = "build") {
1099
1260
  const pluginImportNames = options.runtimePlugins.map((p, i) => {
1100
1261
  if (typeof p === "string") return [
1101
1262
  `$runtimePlugin_${i}`,
@@ -1111,15 +1272,25 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
1111
1272
  return `
1112
1273
  import {init as runtimeInit, loadRemote} from "@module-federation/runtime";
1113
1274
  ${pluginImportNames.map((item) => item[1]).join("\n")}
1114
- import exposesMap from "${virtualExposesId}"
1115
- import {usedShared, usedRemotes} from "${getLocalSharedImportMapPath()}"
1116
- import {
1117
- initResolve
1118
- } from "${virtualRuntimeInitStatus.getImportId()}"
1275
+ ${command === "build" ? getRuntimeInitResolveBootstrapCode() : getRuntimeInitBootstrapCode() + "\n const { initResolve } = globalThis[globalKey];"}
1119
1276
  const initTokens = {}
1120
1277
  const shareScopeName = ${JSON.stringify(options.shareScope)}
1121
1278
  const mfName = ${JSON.stringify(options.name)}
1279
+ let localSharedImportMapPromise
1280
+ let exposesMapPromise
1281
+
1282
+ async function getLocalSharedImportMap() {
1283
+ localSharedImportMapPromise ??= import("${getLocalSharedImportMapPath()}")
1284
+ return localSharedImportMapPromise
1285
+ }
1286
+
1287
+ async function getExposesMap() {
1288
+ exposesMapPromise ??= import("${virtualExposesId}").then((mod) => mod.default ?? mod)
1289
+ return exposesMapPromise
1290
+ }
1291
+
1122
1292
  async function init(shared = {}, initScope = []) {
1293
+ const {usedShared, usedRemotes} = await getLocalSharedImportMap()
1123
1294
  const initRes = runtimeInit({
1124
1295
  name: mfName,
1125
1296
  remotes: usedRemotes,
@@ -1134,6 +1305,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
1134
1305
  if (initScope.indexOf(initToken) >= 0) return;
1135
1306
  initScope.push(initToken);
1136
1307
  initRes.initShareScopeMap('${options.shareScope}', shared);
1308
+ initResolve(initRes)
1137
1309
  try {
1138
1310
  await Promise.all(await initRes.initializeSharing('${options.shareScope}', {
1139
1311
  strategy: '${options.shareStrategy}',
@@ -1143,11 +1315,11 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
1143
1315
  } catch (e) {
1144
1316
  console.error(e)
1145
1317
  }
1146
- initResolve(initRes)
1147
1318
  return initRes
1148
1319
  }
1149
1320
 
1150
- function getExposes(moduleName) {
1321
+ async function getExposes(moduleName) {
1322
+ const exposesMap = await getExposesMap()
1151
1323
  if (!(moduleName in exposesMap)) throw new Error(\`Module \${moduleName} does not exist in container.\`)
1152
1324
  return (exposesMap[moduleName])().then(res => () => res)
1153
1325
  }
@@ -1160,13 +1332,8 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
1160
1332
  const hostAutoInitModule = new VirtualModule("hostAutoInit", "__H_A_I__");
1161
1333
  function writeHostAutoInit(remoteEntryId = REMOTE_ENTRY_ID) {
1162
1334
  hostAutoInitModule.writeSync(`
1163
- const remoteEntryPromise = import("${remoteEntryId}")
1164
- // __tla only serves as a hack for vite-plugin-top-level-await.
1165
- Promise.resolve(remoteEntryPromise)
1166
- .then(remoteEntry => {
1167
- return Promise.resolve(remoteEntry.__tla)
1168
- .then(remoteEntry.init).catch(remoteEntry.init)
1169
- })
1335
+ const remoteEntry = await import("${remoteEntryId}");
1336
+ await remoteEntry.init();
1170
1337
  `);
1171
1338
  }
1172
1339
  function getHostAutoInitImportId() {
@@ -1189,13 +1356,21 @@ function initVirtualModules(command, remoteEntryId) {
1189
1356
  * If Rollup's deconflict renamed the alias but didn't update references
1190
1357
  * in the code body, fall back to proxyLocal so they stay in sync.
1191
1358
  */
1192
- function resolveProxyAlias(binding, proxyLocal, code, fullImport) {
1359
+ function resolveProxyAlias(binding, proxyLocal, code, fullImport, claimedLocals = /* @__PURE__ */ new Set()) {
1193
1360
  const codeWithoutImport = code.replace(fullImport, "");
1194
1361
  const escapedLocal = binding.local.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1195
1362
  const localUsedInCode = new RegExp(`\\b${escapedLocal}\\b`).test(codeWithoutImport);
1363
+ const claimedImportLocals = /* @__PURE__ */ new Set();
1364
+ const importRe = /import\s*\{([^}]+)\}\s*from\s*["'][^"']+["']\s*;?/g;
1365
+ let match;
1366
+ while ((match = importRe.exec(codeWithoutImport)) !== null) for (const spec of match[1].split(",")) {
1367
+ const parts = spec.trim().split(/\s+as\s+/);
1368
+ claimedImportLocals.add((parts[1] || parts[0]).trim());
1369
+ }
1370
+ const local = !localUsedInCode && !claimedLocals.has(proxyLocal) && !claimedImportLocals.has(proxyLocal) ? proxyLocal : binding.local;
1196
1371
  return {
1197
1372
  imported: binding.imported,
1198
- local: localUsedInCode ? binding.local : proxyLocal
1373
+ local
1199
1374
  };
1200
1375
  }
1201
1376
  function findRemoteEntryFile(filename, bundle) {
@@ -1650,14 +1825,14 @@ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposes
1650
1825
  }
1651
1826
  },
1652
1827
  load(id) {
1653
- if (id === remoteEntryId) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId));
1828
+ if (id === remoteEntryId) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId, _command));
1654
1829
  if (id === virtualExposesId) return generateExposes(options);
1655
1830
  if (_command === "serve" && id.includes(getHostAutoInitPath())) return id;
1656
1831
  },
1657
1832
  transform(code, id) {
1658
1833
  return mapCodeToCodeWithSourcemap((() => {
1659
1834
  if (!filter(id)) return;
1660
- if (id.includes(remoteEntryId)) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId));
1835
+ if (id.includes(remoteEntryId)) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId, _command));
1661
1836
  if (id === virtualExposesId) return generateExposes(options);
1662
1837
  if (id.includes(getHostAutoInitPath())) {
1663
1838
  if (_command === "serve") {
@@ -1745,6 +1920,7 @@ function proxySharedModule(options) {
1745
1920
  const { shared = {} } = options;
1746
1921
  let _config;
1747
1922
  let _command = "serve";
1923
+ let isVinext = false;
1748
1924
  const savePrebuild = new PromiseStore();
1749
1925
  return [{
1750
1926
  name: "generateLocalSharedImportMap",
@@ -1759,9 +1935,11 @@ function proxySharedModule(options) {
1759
1935
  name: "proxyPreBuildShared",
1760
1936
  enforce: "post",
1761
1937
  config(config, { command }) {
1938
+ setPackageDetectionCwd(config.root || process.cwd());
1939
+ isVinext = hasPackageDependency("vinext");
1762
1940
  const isRolldown = !!this?.meta?.rolldownVersion;
1763
1941
  _command = command;
1764
- config.resolve.alias.push(...Object.keys(shared).map((key) => {
1942
+ config.resolve.alias.push(...Object.keys(shared).filter((key) => !(isVinext && key === "react")).map((key) => {
1765
1943
  const keyBase = key.endsWith("/") ? key.slice(0, -1) : key;
1766
1944
  const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1767
1945
  const escapedKeyBase = escapeRegex(keyBase);
@@ -1771,6 +1949,7 @@ function proxySharedModule(options) {
1771
1949
  replacement: "$1",
1772
1950
  customResolver(source, importer) {
1773
1951
  if (/\.css$/.test(source)) return;
1952
+ if (isVinext && source === "react") return;
1774
1953
  if (importer && importer.includes("localSharedImportMap")) return;
1775
1954
  if (key.endsWith("/") && source !== key.slice(0, -1)) return;
1776
1955
  const loadSharePath = getLoadShareModulePath(source, isRolldown, command);
@@ -1782,7 +1961,7 @@ function proxySharedModule(options) {
1782
1961
  }
1783
1962
  };
1784
1963
  }));
1785
- config.resolve.alias.push(...Object.keys(shared).map((key) => {
1964
+ config.resolve.alias.push(...Object.keys(shared).filter((key) => !(isVinext && key === "react")).map((key) => {
1786
1965
  return command === "build" ? {
1787
1966
  find: new RegExp(`(.*${PREBUILD_TAG}.*)`),
1788
1967
  replacement: function($1) {
@@ -1805,6 +1984,10 @@ function proxySharedModule(options) {
1805
1984
  const isRolldown = !!config.experimental?.rolldownDev;
1806
1985
  Object.keys(shared).forEach((key) => {
1807
1986
  if (key.endsWith("/")) return;
1987
+ if (isVinext && key === "react") {
1988
+ addUsedShares(key);
1989
+ return;
1990
+ }
1808
1991
  writeLoadShareModule(key, shared[key], _command, isRolldown);
1809
1992
  writePreBuildLibPath(key);
1810
1993
  addUsedShares(key);
@@ -1908,6 +2091,58 @@ var aliasToArrayPlugin_default = {
1908
2091
  }
1909
2092
  };
1910
2093
  //#endregion
2094
+ //#region src/utils/controlChunkSanitizer.ts
2095
+ const FEDERATION_CONTROL_CHUNK_HINTS = [
2096
+ "hostInit",
2097
+ "virtualExposes",
2098
+ "localSharedImportMap"
2099
+ ];
2100
+ function stripEmptyPreloadCalls(code) {
2101
+ const helperImportRegex = /import\s*\{\s*_\s*as\s*(\w+)\s*\}\s*from\s*["'][^"']+["']\s*;?/g;
2102
+ const helperAliases = [...code.matchAll(helperImportRegex)].map((match) => match[1]);
2103
+ let nextCode = code;
2104
+ for (const alias of helperAliases) {
2105
+ const marker = `${alias}(()=>`;
2106
+ let start = nextCode.indexOf(marker);
2107
+ while (start !== -1) {
2108
+ const exprStart = start + marker.length;
2109
+ let depth = 0;
2110
+ let cursor = exprStart;
2111
+ let replacementEnd = -1;
2112
+ while (cursor < nextCode.length) {
2113
+ const char = nextCode[cursor];
2114
+ if (char === "(") depth++;
2115
+ else if (char === ")") depth--;
2116
+ else if (depth === 0 && nextCode.startsWith(",[],import.meta.url)", cursor)) {
2117
+ replacementEnd = cursor;
2118
+ break;
2119
+ }
2120
+ cursor++;
2121
+ }
2122
+ if (replacementEnd === -1) break;
2123
+ const expression = nextCode.slice(exprStart, replacementEnd);
2124
+ nextCode = nextCode.slice(0, start) + expression + nextCode.slice(replacementEnd + 20);
2125
+ start = nextCode.indexOf(marker, start + expression.length);
2126
+ }
2127
+ }
2128
+ nextCode = nextCode.replace(/import\s*["'][^"']*__loadShare__[^"']*["']\s*;?/g, "");
2129
+ nextCode = nextCode.replace(helperImportRegex, (statement, local) => {
2130
+ return new RegExp(`\\b${local}\\s*\\(`).test(nextCode.replace(statement, "")) ? statement : "";
2131
+ });
2132
+ return nextCode;
2133
+ }
2134
+ function isFederationControlChunk(fileName, filename) {
2135
+ return fileName.includes(filename) || FEDERATION_CONTROL_CHUNK_HINTS.some((hint) => fileName.includes(hint));
2136
+ }
2137
+ function sanitizeFederationControlChunk(code, fileName, filename) {
2138
+ let nextCode = stripEmptyPreloadCalls(code);
2139
+ if (fileName.includes("localSharedImportMap")) {
2140
+ const remoteEntryImportRegex = new RegExp(`import\\s*["'][^"']*${filename.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["']\\s*;?`, "g");
2141
+ nextCode = nextCode.replace(remoteEntryImportRegex, "");
2142
+ }
2143
+ return nextCode;
2144
+ }
2145
+ //#endregion
1911
2146
  //#region src/utils/normalizeOptimizeDeps.ts
1912
2147
  var normalizeOptimizeDeps_default = {
1913
2148
  name: "normalizeOptimizeDeps",
@@ -1936,6 +2171,8 @@ function createEarlyVirtualModulesPlugin(options) {
1936
2171
  enforce: "pre",
1937
2172
  config(config, { command: _command }) {
1938
2173
  const root = config.root || process.cwd();
2174
+ setPackageDetectionCwd(root);
2175
+ const isVinext = hasPackageDependency("vinext");
1939
2176
  initVirtualModuleInfrastructure(root, virtualModuleDir);
1940
2177
  VirtualModule.setRoot(root);
1941
2178
  VirtualModule.ensureVirtualPackageExists();
@@ -1950,6 +2187,10 @@ function createEarlyVirtualModulesPlugin(options) {
1950
2187
  for (const key of Object.keys(shared)) {
1951
2188
  if (key.endsWith("/")) continue;
1952
2189
  const shareItem = shared[key];
2190
+ if (isVinext && key === "react") {
2191
+ addUsedShares(key);
2192
+ continue;
2193
+ }
1953
2194
  getLoadShareModulePath(key, isRolldown);
1954
2195
  writeLoadShareModule(key, shareItem, _command, isRolldown);
1955
2196
  writePreBuildLibPath(key);
@@ -1963,6 +2204,7 @@ function createEarlyVirtualModulesPlugin(options) {
1963
2204
  }
1964
2205
  function federation(mfUserOptions) {
1965
2206
  const options = normalizeModuleFederationOptions(mfUserOptions);
2207
+ const isVinext = hasPackageDependency("vinext");
1966
2208
  const { name, remotes, shared, filename, hostInitInjectLocation } = options;
1967
2209
  if (!name) throw new Error("name is required");
1968
2210
  const remoteEntryId = getRemoteEntryId(options);
@@ -1970,6 +2212,23 @@ function federation(mfUserOptions) {
1970
2212
  let command;
1971
2213
  return [
1972
2214
  createEarlyVirtualModulesPlugin(options),
2215
+ ...isVinext ? [{
2216
+ name: "module-federation-vinext-react-server-build-alias",
2217
+ apply: "build",
2218
+ enforce: "pre",
2219
+ resolveId(id) {
2220
+ const reactServerEntryMap = {
2221
+ "react/jsx-runtime": "react/cjs/react-jsx-runtime.production.js",
2222
+ "react/jsx-dev-runtime": "react/cjs/react-jsx-dev-runtime.production.js"
2223
+ };
2224
+ if (!(id in reactServerEntryMap)) return;
2225
+ const environmentName = this?.environment?.name;
2226
+ if (!environmentName || environmentName === "client") return;
2227
+ const target = reactServerEntryMap[id];
2228
+ const reactPackageJson = createRequire$1(new URL(`file://${process.cwd()}/package.json`)).resolve("react/package.json");
2229
+ return path.join(path.dirname(reactPackageJson), target.replace(/^react\//, ""));
2230
+ }
2231
+ }] : [],
1973
2232
  {
1974
2233
  name: "vite:module-federation-config",
1975
2234
  enforce: "pre",
@@ -2021,9 +2280,19 @@ function federation(mfUserOptions) {
2021
2280
  config(config) {
2022
2281
  const runtimeInitId = virtualRuntimeInitStatus.getImportId();
2023
2282
  config.build = config.build || {};
2024
- config.build.rollupOptions = config.build.rollupOptions || {};
2025
- if (!Array.isArray(config.build.rollupOptions.output)) {
2026
- const output = config.build.rollupOptions.output ||= {};
2283
+ if (config.build.modulePreload !== false) {
2284
+ const currentModulePreload = config.build.modulePreload && typeof config.build.modulePreload === "object" ? config.build.modulePreload : {};
2285
+ const existingResolveDependencies = currentModulePreload.resolveDependencies;
2286
+ config.build.modulePreload = {
2287
+ ...currentModulePreload,
2288
+ resolveDependencies(filename, deps, context) {
2289
+ const resolvedDeps = existingResolveDependencies ? existingResolveDependencies(filename, deps, context) : deps;
2290
+ const hostFile = path.basename(context.hostId);
2291
+ return context.hostType === "js" && (hostFile === options.filename || hostFile.includes("hostInit") || hostFile.includes("virtualExposes") || hostFile.includes("localSharedImportMap")) ? [] : resolvedDeps;
2292
+ }
2293
+ };
2294
+ }
2295
+ const applyManualChunks = (output) => {
2027
2296
  const existingManualChunks = output.manualChunks;
2028
2297
  output.manualChunks = function(id) {
2029
2298
  if (id.includes(runtimeInitId)) return "runtimeInit";
@@ -2036,7 +2305,12 @@ function federation(mfUserOptions) {
2036
2305
  for (const [key, ids] of Object.entries(existingManualChunks)) if (Array.isArray(ids) && ids.some((v) => id.includes(v))) return key;
2037
2306
  }
2038
2307
  };
2039
- }
2308
+ };
2309
+ config.build.rollupOptions = config.build.rollupOptions || {};
2310
+ if (!Array.isArray(config.build.rollupOptions.output)) applyManualChunks(config.build.rollupOptions.output ||= {});
2311
+ const buildWithRolldown = config.build;
2312
+ buildWithRolldown.rolldownOptions = buildWithRolldown.rolldownOptions || {};
2313
+ if (!Array.isArray(buildWithRolldown.rolldownOptions.output)) applyManualChunks(buildWithRolldown.rolldownOptions.output ||= {});
2040
2314
  },
2041
2315
  load(id) {
2042
2316
  if (id.startsWith("\0")) return;
@@ -2068,6 +2342,11 @@ function federation(mfUserOptions) {
2068
2342
  }
2069
2343
  },
2070
2344
  generateBundle(_, bundle) {
2345
+ for (const [fileName, chunk] of Object.entries(bundle)) {
2346
+ if (chunk.type !== "chunk") continue;
2347
+ if (!isFederationControlChunk(fileName, filename)) continue;
2348
+ chunk.code = sanitizeFederationControlChunk(chunk.code, fileName, filename);
2349
+ }
2071
2350
  for (const [fileName, chunk] of Object.entries(bundle)) {
2072
2351
  if (chunk.type !== "chunk") continue;
2073
2352
  if (fileName.includes("__loadShare__")) continue;
@@ -2105,12 +2384,12 @@ function federation(mfUserOptions) {
2105
2384
  fileName
2106
2385
  });
2107
2386
  }
2108
- if (proxyChunks.size === 0) return;
2109
- for (const [fileName, chunk] of Object.entries(bundle)) {
2387
+ if (proxyChunks.size > 0) for (const [fileName, chunk] of Object.entries(bundle)) {
2110
2388
  if (chunk.type !== "chunk") continue;
2111
2389
  if (fileName.includes("__loadShare__")) continue;
2112
2390
  let code = chunk.code;
2113
2391
  let modified = false;
2392
+ const claimedLocals = /* @__PURE__ */ new Set();
2114
2393
  for (const [proxyFileName, proxyInfo] of proxyChunks) {
2115
2394
  const proxyBaseName = proxyFileName.replace(/^.*\//, "").replace(/\.js$/, "").replace(/-[A-Za-z0-9_-]+$/, "");
2116
2395
  const importMatch = new RegExp(`import\\s*\\{([^}]+)\\}\\s*from\\s*["']([^"']*${proxyBaseName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[^"']*)["']\\s*;?`).exec(code);
@@ -2133,9 +2412,12 @@ function federation(mfUserOptions) {
2133
2412
  }
2134
2413
  const inlineable = [];
2135
2414
  const nonInlineable = [];
2415
+ const pendingLocals = new Set(bindings.map((binding) => binding.local));
2136
2416
  for (const b of bindings) {
2417
+ pendingLocals.delete(b.local);
2137
2418
  const proxyLocal = exportMap[b.imported];
2138
2419
  if (!proxyLocal) {
2420
+ claimedLocals.add(b.local);
2139
2421
  nonInlineable.push(b);
2140
2422
  continue;
2141
2423
  }
@@ -2157,7 +2439,14 @@ function federation(mfUserOptions) {
2157
2439
  local: b.local,
2158
2440
  funcBody: renamedFunc
2159
2441
  });
2160
- } else nonInlineable.push(resolveProxyAlias(b, proxyLocal, code, fullImport));
2442
+ claimedLocals.add(b.local);
2443
+ } else {
2444
+ const unavailableLocals = new Set(claimedLocals);
2445
+ pendingLocals.forEach((local) => unavailableLocals.add(local));
2446
+ const resolvedBinding = resolveProxyAlias(b, proxyLocal, code, fullImport, unavailableLocals);
2447
+ claimedLocals.add(resolvedBinding.local);
2448
+ nonInlineable.push(resolvedBinding);
2449
+ }
2161
2450
  }
2162
2451
  const hasRenamedAlias = nonInlineable.some((b) => bindings.find((ob) => ob.imported === b.imported)?.local !== b.local);
2163
2452
  if (inlineable.length === 0 && !hasRenamedAlias) continue;
@@ -2171,6 +2460,28 @@ function federation(mfUserOptions) {
2171
2460
  }
2172
2461
  }
2173
2462
  },
2463
+ {
2464
+ name: "module-federation-strip-empty-preload-helper",
2465
+ enforce: "post",
2466
+ apply: "build",
2467
+ renderChunk(code, chunk) {
2468
+ if (!isFederationControlChunk(chunk.fileName, filename)) return;
2469
+ const nextCode = sanitizeFederationControlChunk(code, chunk.fileName, filename);
2470
+ return nextCode === code ? null : {
2471
+ code: nextCode,
2472
+ map: null
2473
+ };
2474
+ },
2475
+ writeBundle(outputOptions, bundle) {
2476
+ if (!outputOptions.dir) return;
2477
+ for (const chunk of Object.values(bundle)) {
2478
+ if (chunk.type !== "chunk") continue;
2479
+ if (!isFederationControlChunk(chunk.fileName, filename)) continue;
2480
+ const outputPath = path.join(outputOptions.dir, chunk.fileName);
2481
+ writeFileSync(outputPath, sanitizeFederationControlChunk(readFileSync(outputPath, "utf-8"), chunk.fileName, filename));
2482
+ }
2483
+ }
2484
+ },
2174
2485
  {
2175
2486
  name: "module-federation-dev-await-shared-init",
2176
2487
  apply: "serve",
@@ -2205,7 +2516,7 @@ function federation(mfUserOptions) {
2205
2516
  config(config, { command: _command }) {
2206
2517
  const isRolldown = !!this?.meta?.rolldownVersion;
2207
2518
  let implementation = options.implementation;
2208
- if (isRolldown) implementation = implementation.replace(/\.cjs\.cjs$/, ".esm.js");
2519
+ if (isRolldown) implementation = implementation.replace(/\.cjs(\.js)?$/, ".js");
2209
2520
  config.resolve.alias.push({
2210
2521
  find: "@module-federation/runtime",
2211
2522
  replacement: implementation