@module-federation/vite 1.13.3 → 1.13.5

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/README.md CHANGED
@@ -23,6 +23,8 @@ This plugin makes Module Federation work together with [Vite](https://vitejs.dev
23
23
 
24
24
  ### [More examples here](https://github.com/module-federation/vite/tree/main/examples)<br>
25
25
 
26
+ Includes a pure runtime host example in [`examples/vite-runtime-register`](./examples/vite-runtime-register).
27
+
26
28
  ## Try this crazy example with all these bundlers together
27
29
 
28
30
  <img src="./docs/multi-example.png"/>
@@ -168,6 +170,16 @@ const RemoteMFE = defineAsyncComponent( 👈
168
170
  </template>
169
171
  ```
170
172
 
173
+ ## ⚠️ `codeSplitting: false` is not supported
174
+
175
+ Do not set `build.rolldownOptions.output.codeSplitting` to `false` with this plugin — it will be **automatically ignored**.
176
+ Module federation requires chunk splitting to isolate shared dependencies and remote entries into separate chunks.
177
+
178
+ ## ⚠️ `manualChunks` is not supported
179
+
180
+ Do not use `build.rollupOptions.output.manualChunks` with this plugin — it will be **automatically ignored**.
181
+ Module federation transforms shared dependency imports with top-level `await`, and grouping these transformed modules into a single chunk creates circular async dependencies that cause the application to silently hang.
182
+
171
183
  ### So far so good 🎉
172
184
 
173
185
  Now you are ready to use Module Federation in Vite!
package/lib/index.cjs CHANGED
@@ -36,6 +36,7 @@ let _module_federation_dts_plugin = require("@module-federation/dts-plugin");
36
36
  let _module_federation_dts_plugin_core = require("@module-federation/dts-plugin/core");
37
37
  let node_module = require("node:module");
38
38
  let url = require("url");
39
+ let es_module_lexer = require("es-module-lexer");
39
40
  //#region src/utils/mapCodeToCodeWithSourcemap.ts
40
41
  async function mapCodeToCodeWithSourcemap(code) {
41
42
  const resolvedCode = await code;
@@ -151,7 +152,7 @@ function removePathFromNpmPackage(packageString) {
151
152
  return match ? match[0] : packageString;
152
153
  }
153
154
  /**
154
- * Detect whether the current bundler is Rolldown (Vite 8+) by checking
155
+ * Detect whether the current runtime is Vite 8+ (with rolldown internally) by checking
155
156
  * for `meta.rolldownVersion` on the plugin hook context.
156
157
  */
157
158
  function getIsRolldown(ctx) {
@@ -380,15 +381,17 @@ function checkAliasConflicts(options) {
380
381
  };
381
382
  }
382
383
  //#endregion
383
- //#region src/plugins/pluginDevProxyModuleTopLevelAwait.ts
384
- /**
385
- * Solve the problem that dev mode dependency prebunding does not support top-level await syntax
386
- */
384
+ //#region src/utils/loadWalk.ts
387
385
  let walkPromise = null;
388
386
  function loadWalk() {
389
387
  walkPromise ||= import("estree-walker").then(({ walk }) => walk);
390
388
  return walkPromise;
391
389
  }
390
+ //#endregion
391
+ //#region src/plugins/pluginDevProxyModuleTopLevelAwait.ts
392
+ /**
393
+ * Solve the problem that dev mode dependency prebunding does not support top-level await syntax
394
+ */
392
395
  function PluginDevProxyModuleTopLevelAwait() {
393
396
  const filterFunction = (0, _rollup_pluginutils.createFilter)();
394
397
  const processedFlag = "/* already-processed-by-dev-proxy-module-top-level-await */";
@@ -746,7 +749,7 @@ function inferVersionFromRequiredVersion(requiredVersion) {
746
749
  }
747
750
  function normalizeShareItem(key, shareItem) {
748
751
  let version;
749
- try {
752
+ if (!(typeof shareItem === "object" && shareItem.import === false)) try {
750
753
  try {
751
754
  version = require(pathe.join(removePathFromNpmPackage(key), "package.json")).version;
752
755
  } catch (e1) {
@@ -1197,7 +1200,7 @@ function generateRemotes(id, command, isRolldown) {
1197
1200
  const importLine = command === "build" ? getRuntimeInitPromiseBootstrapCode() : useESM ? `${getRuntimeInitBootstrapCode()}
1198
1201
  const { initPromise } = globalThis[globalKey];` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
1199
1202
  const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
1200
- const exportLine = command === "serve" && useESM ? "export default exportModule.default ?? exportModule" : useESM ? "export default exportModule" : "module.exports = exportModule";
1203
+ const exportLine = command === "serve" && useESM ? "export const __moduleExports = exportModule;\nexport default exportModule.default ?? exportModule" : useESM ? "export default exportModule" : "module.exports = exportModule";
1201
1204
  return `
1202
1205
  ${importLine}
1203
1206
  const res = initPromise.then(runtime => runtime.loadRemote(${JSON.stringify(id)}))
@@ -1386,6 +1389,28 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1386
1389
  const importLine = command === "build" ? getRuntimeInitPromiseBootstrapCode() : useESM ? `${getRuntimeInitBootstrapCode()}
1387
1390
  const { initPromise } = globalThis[globalKey];` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
1388
1391
  const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
1392
+ if (shareItem.shareConfig.import === false) {
1393
+ const namedExports = useESM ? getPackageNamedExports(pkg) : [];
1394
+ let exportLine;
1395
+ if (useESM && namedExports.length > 0) exportLine = `export default exportModule.default ?? exportModule;\n ${`const { ${namedExports.map((name, i) => `${name}: __mf_${i}`).join(", ")} } = exportModule;`}\n ${`export { ${namedExports.map((name, i) => `__mf_${i} as ${name}`).join(", ")} };`}`;
1396
+ else {
1397
+ if (useESM) mfWarn(`Shared dependency "${pkg}" has import: false but is not installed locally.\n Named imports (e.g. import { ... } from '${pkg}') will not work in production builds.\n Install it as a devDependency to enable named export detection.`);
1398
+ exportLine = useESM ? "export default exportModule.default ?? exportModule" : "module.exports = exportModule";
1399
+ }
1400
+ loadShareCacheMap[pkg].writeSync(`
1401
+ ${importLine}
1402
+ const res = initPromise.then(runtime => runtime.loadShare(${escapeGeneratedStringLiteral(pkg)}, {
1403
+ customShareInfo: {shareConfig:{
1404
+ singleton: ${shareItem.shareConfig.singleton},
1405
+ strictVersion: ${shareItem.shareConfig.strictVersion},
1406
+ requiredVersion: ${JSON.stringify(shareItem.shareConfig.requiredVersion)}
1407
+ }}
1408
+ }))
1409
+ const exportModule = ${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory))
1410
+ ${exportLine}
1411
+ `, true);
1412
+ return;
1413
+ }
1389
1414
  const useSsrProviderFallback = hasPackageDependency("vinext") && command === "build" && pkg === "react";
1390
1415
  const concreteSharedImportSource = getConcreteSharedImportSource(pkg, shareItem);
1391
1416
  const sharedImportSource = concreteSharedImportSource || getPreBuildLibImportId(pkg);
@@ -1533,6 +1558,14 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
1533
1558
  ];
1534
1559
  });
1535
1560
  return `
1561
+ // Shim Vue HMR runtime for dev-compiled components loaded by a non-Vite host.
1562
+ // When a remote is served by a Vite dev server, Vue's SFC compiler injects HMR
1563
+ // hooks that reference __VUE_HMR_RUNTIME__. This global only exists on pages
1564
+ // served by Vite's client runtime. When a production host loads the remote,
1565
+ // the HMR calls would throw. This no-op shim prevents that.
1566
+ if (typeof __VUE_HMR_RUNTIME__ === 'undefined') {
1567
+ globalThis.__VUE_HMR_RUNTIME__ = { createRecord() {}, rerender() {}, reload() {} };
1568
+ }
1536
1569
  import {init as runtimeInit, loadRemote} from "@module-federation/runtime";
1537
1570
  ${pluginImportNames.map((item) => item[1]).join("\n")}
1538
1571
  ${command === "build" ? getRuntimeInitResolveBootstrapCode() : getRuntimeInitBootstrapCode() + "\n const { initResolve } = globalThis[globalKey];"}
@@ -2203,6 +2236,265 @@ function pluginProxyRemotes_default(options) {
2203
2236
  };
2204
2237
  }
2205
2238
  //#endregion
2239
+ //#region src/plugins/pluginRemoteNamedExports.ts
2240
+ /**
2241
+ * Transforms consumer-side imports of remote modules so that named exports
2242
+ * are accessible even when the bundler does not support syntheticNamedExports
2243
+ * (Rolldown / Vite 8+).
2244
+ *
2245
+ * The remote proxy module exports:
2246
+ * export const __moduleExports = exportModule; // full namespace
2247
+ * export default exportModule.default ?? exportModule; // unwrapped default
2248
+ *
2249
+ * This plugin rewrites consumer code:
2250
+ * import { foo } from "remote/xxx"
2251
+ * → import { __moduleExports as __mf_ns_0 } from "remote/xxx"; const { foo } = __mf_ns_0;
2252
+ *
2253
+ * import("remote/xxx")
2254
+ * → import("remote/xxx").then(…) // spreads __moduleExports into namespace
2255
+ *
2256
+ * NOTE: `export * from "remote/xxx"` is not supported — Rolldown cannot
2257
+ * statically resolve the set of exported names from a federated remote at
2258
+ * build time. Use explicit named re-exports instead.
2259
+ */
2260
+ const JS_EXTENSIONS_RE = /\.(?:[mc]?[jt]sx?|vue|svelte)(?:\?|$)/;
2261
+ function wrapDynamicImport(original) {
2262
+ return `${original}.then(function(__mf_m__) {\n if (!__mf_m__ || !__mf_m__.__moduleExports) return __mf_m__;\n var __mf_ns__ = Object.create(null);\n Object.defineProperty(__mf_ns__, Symbol.toStringTag, { value: "Module" });\n var __mf_e__ = __mf_m__.__moduleExports;\n Object.keys(__mf_e__).forEach(function(k) { if (k !== "__esModule") __mf_ns__[k] = __mf_e__[k] });\n if ("default" in __mf_m__) __mf_ns__.default = __mf_m__.default;\n return __mf_ns__;\n})`;
2263
+ }
2264
+ function applyRewrites(code, imports, id) {
2265
+ if (imports.length === 0) return;
2266
+ const ms = new magic_string.default(code);
2267
+ let changed = false;
2268
+ let counter = 0;
2269
+ for (const imp of imports) switch (imp.kind) {
2270
+ case "static": {
2271
+ const src = JSON.stringify(imp.source);
2272
+ if (imp.namespaceLocal && !imp.defaultLocal && imp.named.length === 0) ms.overwrite(imp.start, imp.end, `import { __moduleExports as ${imp.namespaceLocal} } from ${src};`);
2273
+ else {
2274
+ const nsId = `__mf_ns_${counter++}`;
2275
+ const importParts = [];
2276
+ if (imp.defaultLocal) importParts.push(`default as ${imp.defaultLocal}`);
2277
+ importParts.push(`__moduleExports as ${nsId}`);
2278
+ const destructParts = imp.named.map((s) => s.imported === s.local ? s.local : `${s.imported}: ${s.local}`);
2279
+ let rewrite = `import { ${importParts.join(", ")} } from ${src};`;
2280
+ if (destructParts.length > 0) rewrite += `\nconst { ${destructParts.join(", ")} } = ${nsId};`;
2281
+ ms.overwrite(imp.start, imp.end, rewrite);
2282
+ }
2283
+ changed = true;
2284
+ break;
2285
+ }
2286
+ case "reexport": {
2287
+ const src = JSON.stringify(imp.source);
2288
+ const nsId = `__mf_ns_${counter++}`;
2289
+ const vars = imp.specifiers.map((s) => {
2290
+ const tmp = `__mf_re_${counter++}`;
2291
+ return {
2292
+ ...s,
2293
+ tmp
2294
+ };
2295
+ });
2296
+ const importLine = `import { __moduleExports as ${nsId} } from ${src};`;
2297
+ const varLines = vars.map((v) => `const ${v.tmp} = ${nsId}[${JSON.stringify(v.local)}];`).join("\n");
2298
+ const exportLine = `export { ${vars.map((v) => `${v.tmp} as ${v.exported}`).join(", ")} };`;
2299
+ ms.overwrite(imp.start, imp.end, `${importLine}\n${varLines}\n${exportLine}`);
2300
+ changed = true;
2301
+ break;
2302
+ }
2303
+ case "export-all":
2304
+ console.warn(`[module-federation] "export * from '${imp.source}'" is not supported with Rolldown — use explicit named re-exports instead. (${id})`);
2305
+ break;
2306
+ case "dynamic":
2307
+ ms.overwrite(imp.start, imp.end, wrapDynamicImport(imp.originalText));
2308
+ changed = true;
2309
+ break;
2310
+ }
2311
+ if (!changed) return;
2312
+ return {
2313
+ code: ms.toString(),
2314
+ map: ms.generateMap({ hires: true })
2315
+ };
2316
+ }
2317
+ async function collectFromAST(ast, code, isRemoteImport) {
2318
+ const walk = await loadWalk();
2319
+ const result = [];
2320
+ walk(ast, { enter(node) {
2321
+ if (node.type === "ImportDeclaration" && node.source?.value) {
2322
+ if (!isRemoteImport(node.source.value)) return;
2323
+ const specifiers = node.specifiers || [];
2324
+ const named = specifiers.filter((s) => s.type === "ImportSpecifier" && s.importKind !== "type").map((s) => ({
2325
+ imported: s.imported.name ?? s.imported.value,
2326
+ local: s.local.name
2327
+ }));
2328
+ const defaultSpec = specifiers.find((s) => s.type === "ImportDefaultSpecifier");
2329
+ const nsSpec = specifiers.find((s) => s.type === "ImportNamespaceSpecifier");
2330
+ if (named.length === 0 && !nsSpec) return;
2331
+ result.push({
2332
+ kind: "static",
2333
+ source: node.source.value,
2334
+ start: node.start,
2335
+ end: node.end,
2336
+ named,
2337
+ defaultLocal: defaultSpec?.local.name,
2338
+ namespaceLocal: nsSpec?.local.name
2339
+ });
2340
+ }
2341
+ if (node.type === "ExportNamedDeclaration" && node.source?.value && isRemoteImport(node.source.value)) {
2342
+ const specifiers = (node.specifiers || []).filter((s) => s.exportKind !== "type").map((s) => ({
2343
+ local: s.local.name ?? s.local.value,
2344
+ exported: s.exported.name ?? s.exported.value
2345
+ }));
2346
+ if (specifiers.length === 0) return;
2347
+ result.push({
2348
+ kind: "reexport",
2349
+ source: node.source.value,
2350
+ start: node.start,
2351
+ end: node.end,
2352
+ specifiers
2353
+ });
2354
+ }
2355
+ if (node.type === "ExportAllDeclaration" && node.source?.value && isRemoteImport(node.source.value)) {
2356
+ this.skip();
2357
+ result.push({
2358
+ kind: "export-all",
2359
+ source: node.source.value,
2360
+ start: node.start,
2361
+ end: node.end
2362
+ });
2363
+ }
2364
+ if (node.type === "ImportExpression") {
2365
+ const source = node.source;
2366
+ if (source.type !== "Literal" && source.type !== "StringLiteral" && source.type !== "TemplateLiteral") return;
2367
+ const value = source.type === "TemplateLiteral" ? source.quasis?.length === 1 ? source.quasis[0].value?.cooked : void 0 : source.value;
2368
+ if (!value || !isRemoteImport(value)) return;
2369
+ result.push({
2370
+ kind: "dynamic",
2371
+ start: node.start,
2372
+ end: node.end,
2373
+ originalText: code.slice(node.start, node.end)
2374
+ });
2375
+ }
2376
+ } });
2377
+ return result;
2378
+ }
2379
+ async function collectFromEsLexer(code, isRemoteImport) {
2380
+ await es_module_lexer.init;
2381
+ let imports;
2382
+ try {
2383
+ [imports] = (0, es_module_lexer.parse)(code);
2384
+ } catch {
2385
+ return;
2386
+ }
2387
+ const result = [];
2388
+ for (const imp of imports) {
2389
+ if (imp.d === -2) continue;
2390
+ if (!imp.n || !isRemoteImport(imp.n)) continue;
2391
+ const stmtText = code.slice(imp.ss, imp.se);
2392
+ if (imp.d >= 0) {
2393
+ result.push({
2394
+ kind: "dynamic",
2395
+ start: imp.ss,
2396
+ end: imp.se,
2397
+ originalText: stmtText
2398
+ });
2399
+ continue;
2400
+ }
2401
+ if (/^\s*export\s*\*\s/.test(stmtText)) {
2402
+ result.push({
2403
+ kind: "export-all",
2404
+ source: imp.n,
2405
+ start: imp.ss,
2406
+ end: imp.se
2407
+ });
2408
+ continue;
2409
+ }
2410
+ if (/^\s*export\s/.test(stmtText)) {
2411
+ const braceMatch = stmtText.match(/\{([^}]*)\}/);
2412
+ if (!braceMatch) continue;
2413
+ const specs = braceMatch[1].split(",").map((s) => s.trim()).filter((s) => s.length > 0);
2414
+ if (specs.length === 0) continue;
2415
+ const specifiers = specs.map((s) => {
2416
+ const asMatch = s.match(/^(\w+)\s+as\s+(\w+)$/);
2417
+ return {
2418
+ local: asMatch ? asMatch[1] : s,
2419
+ exported: asMatch ? asMatch[2] : s
2420
+ };
2421
+ });
2422
+ result.push({
2423
+ kind: "reexport",
2424
+ source: imp.n,
2425
+ start: imp.ss,
2426
+ end: imp.se,
2427
+ specifiers
2428
+ });
2429
+ continue;
2430
+ }
2431
+ const importMatch = stmtText.match(/^import\s+([\s\S]*?)\s+from\s/);
2432
+ if (!importMatch) continue;
2433
+ const specifiersPart = importMatch[1].trim();
2434
+ if (/^type\s/.test(specifiersPart)) continue;
2435
+ const nsMatch = specifiersPart.match(/^\*\s+as\s+(\w+)$/);
2436
+ if (nsMatch) {
2437
+ result.push({
2438
+ kind: "static",
2439
+ source: imp.n,
2440
+ start: imp.ss,
2441
+ end: imp.se,
2442
+ named: [],
2443
+ namespaceLocal: nsMatch[1]
2444
+ });
2445
+ continue;
2446
+ }
2447
+ const braceMatch = specifiersPart.match(/\{([^}]*)\}/);
2448
+ if (!braceMatch) continue;
2449
+ const namedSpecifiers = braceMatch[1].split(",").map((s) => s.trim()).filter((s) => s.length > 0 && !s.startsWith("type "));
2450
+ if (namedSpecifiers.length === 0) continue;
2451
+ const defaultMatch = specifiersPart.match(/^(\w+)\s*,/);
2452
+ const named = namedSpecifiers.map((s) => {
2453
+ const asMatch = s.match(/^(\w+)\s+as\s+(\w+)$/);
2454
+ return {
2455
+ imported: asMatch ? asMatch[1] : s,
2456
+ local: asMatch ? asMatch[2] : s
2457
+ };
2458
+ });
2459
+ result.push({
2460
+ kind: "static",
2461
+ source: imp.n,
2462
+ start: imp.ss,
2463
+ end: imp.se,
2464
+ named,
2465
+ defaultLocal: defaultMatch?.[1]
2466
+ });
2467
+ }
2468
+ return result;
2469
+ }
2470
+ function pluginRemoteNamedExports(options) {
2471
+ const remoteNames = Object.keys(options.remotes);
2472
+ let rolldown;
2473
+ function isRemoteImport(source) {
2474
+ return remoteNames.some((name) => source === name || source.startsWith(name + "/"));
2475
+ }
2476
+ return {
2477
+ name: "module-federation-remote-named-exports",
2478
+ enforce: "pre",
2479
+ async transform(code, id) {
2480
+ rolldown ??= getIsRolldown(this);
2481
+ if (!rolldown) return;
2482
+ if (remoteNames.length === 0) return;
2483
+ if (id.includes("__loadRemote__") || id.includes("__loadShare__")) return;
2484
+ if (!JS_EXTENSIONS_RE.test(id)) return;
2485
+ if (!remoteNames.some((name) => code.includes(name))) return;
2486
+ let imports;
2487
+ try {
2488
+ imports = await collectFromAST(this.parse(code), code, isRemoteImport);
2489
+ } catch {
2490
+ imports = await collectFromEsLexer(code, isRemoteImport);
2491
+ }
2492
+ if (!imports) return;
2493
+ return applyRewrites(code, imports, id);
2494
+ }
2495
+ };
2496
+ }
2497
+ //#endregion
2206
2498
  //#region src/utils/PromiseStore.ts
2207
2499
  /**
2208
2500
  * example:
@@ -2278,7 +2570,7 @@ function proxySharedModule(options) {
2278
2570
  if (key.endsWith("/") && source !== key.slice(0, -1)) return;
2279
2571
  const loadSharePath = getLoadShareModulePath(source, isRolldown, command);
2280
2572
  writeLoadShareModule(source, shared[key], command, isRolldown);
2281
- writePreBuildLibPath(source, shared[key]);
2573
+ if (shared[key].shareConfig.import !== false) writePreBuildLibPath(source, shared[key]);
2282
2574
  addUsedShares(source);
2283
2575
  writeLocalSharedImportMap();
2284
2576
  return this.resolve(loadSharePath, importer);
@@ -2309,7 +2601,7 @@ function proxySharedModule(options) {
2309
2601
  },
2310
2602
  configResolved(config) {
2311
2603
  _config = config;
2312
- const isRolldown = !!config.experimental?.rolldownDev;
2604
+ const isRolldown = getIsRolldown(this);
2313
2605
  Object.keys(shared).forEach((key) => {
2314
2606
  if (key.endsWith("/")) return;
2315
2607
  if (isVinext && key === "react") {
@@ -2317,7 +2609,7 @@ function proxySharedModule(options) {
2317
2609
  return;
2318
2610
  }
2319
2611
  writeLoadShareModule(key, shared[key], _command, isRolldown);
2320
- writePreBuildLibPath(key, shared[key]);
2612
+ if (shared[key].shareConfig.import !== false) writePreBuildLibPath(key, shared[key]);
2321
2613
  addUsedShares(key);
2322
2614
  });
2323
2615
  writeLocalSharedImportMap();
@@ -2545,9 +2837,9 @@ function createEarlyVirtualModulesPlugin(options) {
2545
2837
  }
2546
2838
  getLoadShareModulePath(key, isRolldown);
2547
2839
  writeLoadShareModule(key, shareItem, _command, isRolldown);
2548
- writePreBuildLibPath(key, shareItem);
2840
+ if (shareItem.shareConfig?.import !== false) writePreBuildLibPath(key, shareItem);
2549
2841
  addUsedShares(key);
2550
- if (_command === "serve") {
2842
+ if (_command === "serve" && shareItem.shareConfig?.import !== false) {
2551
2843
  if (!isRolldown) config.optimizeDeps.include.push(getLoadShareImportId(key, isRolldown, _command));
2552
2844
  config.optimizeDeps.include.push(getPreBuildLibImportId(key));
2553
2845
  }
@@ -2620,6 +2912,7 @@ function federation(mfUserOptions) {
2620
2912
  virtualExposesId
2621
2913
  }),
2622
2914
  pluginProxyRemotes_default(options),
2915
+ pluginRemoteNamedExports(options),
2623
2916
  ...pluginModuleParseEnd_default((id) => {
2624
2917
  return id.includes(getHostAutoInitImportId()) || id.includes(remoteEntryId) || id.includes(virtualExposesId) || id.includes(getLocalSharedImportMapPath());
2625
2918
  }, {
@@ -2655,19 +2948,19 @@ function federation(mfUserOptions) {
2655
2948
  warnedAboutCodeSplitting = true;
2656
2949
  mfWarn("Ignoring `build.rolldownOptions.output.codeSplitting = false` because module federation requires chunk splitting.");
2657
2950
  };
2951
+ let warnedAboutManualChunks = false;
2658
2952
  const applyManualChunks = (output) => {
2659
2953
  ensureCodeSplitting(output);
2660
- const existingManualChunks = output.manualChunks;
2954
+ if (output.manualChunks && !warnedAboutManualChunks) {
2955
+ warnedAboutManualChunks = true;
2956
+ mfWarn("Ignoring `build.rollupOptions.output.manualChunks` because it conflicts with module federation. Module federation transforms shared dependency imports with top-level await, and grouping these transformed modules into a single chunk creates circular async dependencies that cause the application to silently hang.");
2957
+ }
2661
2958
  output.manualChunks = function(id) {
2662
2959
  if (id.includes(runtimeInitId)) return "runtimeInit";
2663
2960
  if (id.includes("__loadShare__")) {
2664
2961
  const match = id.match(/([^/\\]+__loadShare__[^/\\]+)/);
2665
2962
  return match ? match[1] : "loadShare";
2666
2963
  }
2667
- if (typeof existingManualChunks === "function") return existingManualChunks.apply(this, arguments);
2668
- if (existingManualChunks && typeof existingManualChunks === "object") {
2669
- for (const [key, ids] of Object.entries(existingManualChunks)) if (Array.isArray(ids) && ids.some((v) => id.includes(v))) return key;
2670
- }
2671
2964
  };
2672
2965
  };
2673
2966
  config.build.rollupOptions = config.build.rollupOptions || {};
@@ -2699,6 +2992,7 @@ function federation(mfUserOptions) {
2699
2992
  * @see https://rollupjs.org/plugin-development/#synthetic-named-exports
2700
2993
  */
2701
2994
  code = code.replace("export default exportModule", "export const __moduleExports = exportModule;\nexport default exportModule.__esModule ? exportModule.default : exportModule");
2995
+ if (getIsRolldown(this)) return { code };
2702
2996
  return {
2703
2997
  code,
2704
2998
  syntheticNamedExports: "__moduleExports"
package/lib/index.mjs CHANGED
@@ -11,6 +11,7 @@ import { normalizeOptions } from "@module-federation/sdk";
11
11
  import { consumeTypesAPI, generateTypesAPI, isTSProject, normalizeConsumeTypesOptions, normalizeDtsOptions, normalizeGenerateTypesOptions } from "@module-federation/dts-plugin";
12
12
  import { rpc } from "@module-federation/dts-plugin/core";
13
13
  import { fileURLToPath } from "url";
14
+ import { init, parse as parse$1 } from "es-module-lexer";
14
15
  //#region \0rolldown/runtime.js
15
16
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
16
17
  //#endregion
@@ -129,7 +130,7 @@ function removePathFromNpmPackage(packageString) {
129
130
  return match ? match[0] : packageString;
130
131
  }
131
132
  /**
132
- * Detect whether the current bundler is Rolldown (Vite 8+) by checking
133
+ * Detect whether the current runtime is Vite 8+ (with rolldown internally) by checking
133
134
  * for `meta.rolldownVersion` on the plugin hook context.
134
135
  */
135
136
  function getIsRolldown(ctx) {
@@ -358,15 +359,17 @@ function checkAliasConflicts(options) {
358
359
  };
359
360
  }
360
361
  //#endregion
361
- //#region src/plugins/pluginDevProxyModuleTopLevelAwait.ts
362
- /**
363
- * Solve the problem that dev mode dependency prebunding does not support top-level await syntax
364
- */
362
+ //#region src/utils/loadWalk.ts
365
363
  let walkPromise = null;
366
364
  function loadWalk() {
367
365
  walkPromise ||= import("estree-walker").then(({ walk }) => walk);
368
366
  return walkPromise;
369
367
  }
368
+ //#endregion
369
+ //#region src/plugins/pluginDevProxyModuleTopLevelAwait.ts
370
+ /**
371
+ * Solve the problem that dev mode dependency prebunding does not support top-level await syntax
372
+ */
370
373
  function PluginDevProxyModuleTopLevelAwait() {
371
374
  const filterFunction = createFilter();
372
375
  const processedFlag = "/* already-processed-by-dev-proxy-module-top-level-await */";
@@ -724,7 +727,7 @@ function inferVersionFromRequiredVersion(requiredVersion) {
724
727
  }
725
728
  function normalizeShareItem(key, shareItem) {
726
729
  let version;
727
- try {
730
+ if (!(typeof shareItem === "object" && shareItem.import === false)) try {
728
731
  try {
729
732
  version = __require(path$1.join(removePathFromNpmPackage(key), "package.json")).version;
730
733
  } catch (e1) {
@@ -1174,7 +1177,7 @@ function generateRemotes(id, command, isRolldown) {
1174
1177
  const importLine = command === "build" ? getRuntimeInitPromiseBootstrapCode() : useESM ? `${getRuntimeInitBootstrapCode()}
1175
1178
  const { initPromise } = globalThis[globalKey];` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
1176
1179
  const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
1177
- const exportLine = command === "serve" && useESM ? "export default exportModule.default ?? exportModule" : useESM ? "export default exportModule" : "module.exports = exportModule";
1180
+ const exportLine = command === "serve" && useESM ? "export const __moduleExports = exportModule;\nexport default exportModule.default ?? exportModule" : useESM ? "export default exportModule" : "module.exports = exportModule";
1178
1181
  return `
1179
1182
  ${importLine}
1180
1183
  const res = initPromise.then(runtime => runtime.loadRemote(${JSON.stringify(id)}))
@@ -1363,6 +1366,28 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1363
1366
  const importLine = command === "build" ? getRuntimeInitPromiseBootstrapCode() : useESM ? `${getRuntimeInitBootstrapCode()}
1364
1367
  const { initPromise } = globalThis[globalKey];` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
1365
1368
  const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
1369
+ if (shareItem.shareConfig.import === false) {
1370
+ const namedExports = useESM ? getPackageNamedExports(pkg) : [];
1371
+ let exportLine;
1372
+ if (useESM && namedExports.length > 0) exportLine = `export default exportModule.default ?? exportModule;\n ${`const { ${namedExports.map((name, i) => `${name}: __mf_${i}`).join(", ")} } = exportModule;`}\n ${`export { ${namedExports.map((name, i) => `__mf_${i} as ${name}`).join(", ")} };`}`;
1373
+ else {
1374
+ if (useESM) mfWarn(`Shared dependency "${pkg}" has import: false but is not installed locally.\n Named imports (e.g. import { ... } from '${pkg}') will not work in production builds.\n Install it as a devDependency to enable named export detection.`);
1375
+ exportLine = useESM ? "export default exportModule.default ?? exportModule" : "module.exports = exportModule";
1376
+ }
1377
+ loadShareCacheMap[pkg].writeSync(`
1378
+ ${importLine}
1379
+ const res = initPromise.then(runtime => runtime.loadShare(${escapeGeneratedStringLiteral(pkg)}, {
1380
+ customShareInfo: {shareConfig:{
1381
+ singleton: ${shareItem.shareConfig.singleton},
1382
+ strictVersion: ${shareItem.shareConfig.strictVersion},
1383
+ requiredVersion: ${JSON.stringify(shareItem.shareConfig.requiredVersion)}
1384
+ }}
1385
+ }))
1386
+ const exportModule = ${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory))
1387
+ ${exportLine}
1388
+ `, true);
1389
+ return;
1390
+ }
1366
1391
  const useSsrProviderFallback = hasPackageDependency("vinext") && command === "build" && pkg === "react";
1367
1392
  const concreteSharedImportSource = getConcreteSharedImportSource(pkg, shareItem);
1368
1393
  const sharedImportSource = concreteSharedImportSource || getPreBuildLibImportId(pkg);
@@ -1510,6 +1535,14 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
1510
1535
  ];
1511
1536
  });
1512
1537
  return `
1538
+ // Shim Vue HMR runtime for dev-compiled components loaded by a non-Vite host.
1539
+ // When a remote is served by a Vite dev server, Vue's SFC compiler injects HMR
1540
+ // hooks that reference __VUE_HMR_RUNTIME__. This global only exists on pages
1541
+ // served by Vite's client runtime. When a production host loads the remote,
1542
+ // the HMR calls would throw. This no-op shim prevents that.
1543
+ if (typeof __VUE_HMR_RUNTIME__ === 'undefined') {
1544
+ globalThis.__VUE_HMR_RUNTIME__ = { createRecord() {}, rerender() {}, reload() {} };
1545
+ }
1513
1546
  import {init as runtimeInit, loadRemote} from "@module-federation/runtime";
1514
1547
  ${pluginImportNames.map((item) => item[1]).join("\n")}
1515
1548
  ${command === "build" ? getRuntimeInitResolveBootstrapCode() : getRuntimeInitBootstrapCode() + "\n const { initResolve } = globalThis[globalKey];"}
@@ -2180,6 +2213,265 @@ function pluginProxyRemotes_default(options) {
2180
2213
  };
2181
2214
  }
2182
2215
  //#endregion
2216
+ //#region src/plugins/pluginRemoteNamedExports.ts
2217
+ /**
2218
+ * Transforms consumer-side imports of remote modules so that named exports
2219
+ * are accessible even when the bundler does not support syntheticNamedExports
2220
+ * (Rolldown / Vite 8+).
2221
+ *
2222
+ * The remote proxy module exports:
2223
+ * export const __moduleExports = exportModule; // full namespace
2224
+ * export default exportModule.default ?? exportModule; // unwrapped default
2225
+ *
2226
+ * This plugin rewrites consumer code:
2227
+ * import { foo } from "remote/xxx"
2228
+ * → import { __moduleExports as __mf_ns_0 } from "remote/xxx"; const { foo } = __mf_ns_0;
2229
+ *
2230
+ * import("remote/xxx")
2231
+ * → import("remote/xxx").then(…) // spreads __moduleExports into namespace
2232
+ *
2233
+ * NOTE: `export * from "remote/xxx"` is not supported — Rolldown cannot
2234
+ * statically resolve the set of exported names from a federated remote at
2235
+ * build time. Use explicit named re-exports instead.
2236
+ */
2237
+ const JS_EXTENSIONS_RE = /\.(?:[mc]?[jt]sx?|vue|svelte)(?:\?|$)/;
2238
+ function wrapDynamicImport(original) {
2239
+ return `${original}.then(function(__mf_m__) {\n if (!__mf_m__ || !__mf_m__.__moduleExports) return __mf_m__;\n var __mf_ns__ = Object.create(null);\n Object.defineProperty(__mf_ns__, Symbol.toStringTag, { value: "Module" });\n var __mf_e__ = __mf_m__.__moduleExports;\n Object.keys(__mf_e__).forEach(function(k) { if (k !== "__esModule") __mf_ns__[k] = __mf_e__[k] });\n if ("default" in __mf_m__) __mf_ns__.default = __mf_m__.default;\n return __mf_ns__;\n})`;
2240
+ }
2241
+ function applyRewrites(code, imports, id) {
2242
+ if (imports.length === 0) return;
2243
+ const ms = new MagicString(code);
2244
+ let changed = false;
2245
+ let counter = 0;
2246
+ for (const imp of imports) switch (imp.kind) {
2247
+ case "static": {
2248
+ const src = JSON.stringify(imp.source);
2249
+ if (imp.namespaceLocal && !imp.defaultLocal && imp.named.length === 0) ms.overwrite(imp.start, imp.end, `import { __moduleExports as ${imp.namespaceLocal} } from ${src};`);
2250
+ else {
2251
+ const nsId = `__mf_ns_${counter++}`;
2252
+ const importParts = [];
2253
+ if (imp.defaultLocal) importParts.push(`default as ${imp.defaultLocal}`);
2254
+ importParts.push(`__moduleExports as ${nsId}`);
2255
+ const destructParts = imp.named.map((s) => s.imported === s.local ? s.local : `${s.imported}: ${s.local}`);
2256
+ let rewrite = `import { ${importParts.join(", ")} } from ${src};`;
2257
+ if (destructParts.length > 0) rewrite += `\nconst { ${destructParts.join(", ")} } = ${nsId};`;
2258
+ ms.overwrite(imp.start, imp.end, rewrite);
2259
+ }
2260
+ changed = true;
2261
+ break;
2262
+ }
2263
+ case "reexport": {
2264
+ const src = JSON.stringify(imp.source);
2265
+ const nsId = `__mf_ns_${counter++}`;
2266
+ const vars = imp.specifiers.map((s) => {
2267
+ const tmp = `__mf_re_${counter++}`;
2268
+ return {
2269
+ ...s,
2270
+ tmp
2271
+ };
2272
+ });
2273
+ const importLine = `import { __moduleExports as ${nsId} } from ${src};`;
2274
+ const varLines = vars.map((v) => `const ${v.tmp} = ${nsId}[${JSON.stringify(v.local)}];`).join("\n");
2275
+ const exportLine = `export { ${vars.map((v) => `${v.tmp} as ${v.exported}`).join(", ")} };`;
2276
+ ms.overwrite(imp.start, imp.end, `${importLine}\n${varLines}\n${exportLine}`);
2277
+ changed = true;
2278
+ break;
2279
+ }
2280
+ case "export-all":
2281
+ console.warn(`[module-federation] "export * from '${imp.source}'" is not supported with Rolldown — use explicit named re-exports instead. (${id})`);
2282
+ break;
2283
+ case "dynamic":
2284
+ ms.overwrite(imp.start, imp.end, wrapDynamicImport(imp.originalText));
2285
+ changed = true;
2286
+ break;
2287
+ }
2288
+ if (!changed) return;
2289
+ return {
2290
+ code: ms.toString(),
2291
+ map: ms.generateMap({ hires: true })
2292
+ };
2293
+ }
2294
+ async function collectFromAST(ast, code, isRemoteImport) {
2295
+ const walk = await loadWalk();
2296
+ const result = [];
2297
+ walk(ast, { enter(node) {
2298
+ if (node.type === "ImportDeclaration" && node.source?.value) {
2299
+ if (!isRemoteImport(node.source.value)) return;
2300
+ const specifiers = node.specifiers || [];
2301
+ const named = specifiers.filter((s) => s.type === "ImportSpecifier" && s.importKind !== "type").map((s) => ({
2302
+ imported: s.imported.name ?? s.imported.value,
2303
+ local: s.local.name
2304
+ }));
2305
+ const defaultSpec = specifiers.find((s) => s.type === "ImportDefaultSpecifier");
2306
+ const nsSpec = specifiers.find((s) => s.type === "ImportNamespaceSpecifier");
2307
+ if (named.length === 0 && !nsSpec) return;
2308
+ result.push({
2309
+ kind: "static",
2310
+ source: node.source.value,
2311
+ start: node.start,
2312
+ end: node.end,
2313
+ named,
2314
+ defaultLocal: defaultSpec?.local.name,
2315
+ namespaceLocal: nsSpec?.local.name
2316
+ });
2317
+ }
2318
+ if (node.type === "ExportNamedDeclaration" && node.source?.value && isRemoteImport(node.source.value)) {
2319
+ const specifiers = (node.specifiers || []).filter((s) => s.exportKind !== "type").map((s) => ({
2320
+ local: s.local.name ?? s.local.value,
2321
+ exported: s.exported.name ?? s.exported.value
2322
+ }));
2323
+ if (specifiers.length === 0) return;
2324
+ result.push({
2325
+ kind: "reexport",
2326
+ source: node.source.value,
2327
+ start: node.start,
2328
+ end: node.end,
2329
+ specifiers
2330
+ });
2331
+ }
2332
+ if (node.type === "ExportAllDeclaration" && node.source?.value && isRemoteImport(node.source.value)) {
2333
+ this.skip();
2334
+ result.push({
2335
+ kind: "export-all",
2336
+ source: node.source.value,
2337
+ start: node.start,
2338
+ end: node.end
2339
+ });
2340
+ }
2341
+ if (node.type === "ImportExpression") {
2342
+ const source = node.source;
2343
+ if (source.type !== "Literal" && source.type !== "StringLiteral" && source.type !== "TemplateLiteral") return;
2344
+ const value = source.type === "TemplateLiteral" ? source.quasis?.length === 1 ? source.quasis[0].value?.cooked : void 0 : source.value;
2345
+ if (!value || !isRemoteImport(value)) return;
2346
+ result.push({
2347
+ kind: "dynamic",
2348
+ start: node.start,
2349
+ end: node.end,
2350
+ originalText: code.slice(node.start, node.end)
2351
+ });
2352
+ }
2353
+ } });
2354
+ return result;
2355
+ }
2356
+ async function collectFromEsLexer(code, isRemoteImport) {
2357
+ await init;
2358
+ let imports;
2359
+ try {
2360
+ [imports] = parse$1(code);
2361
+ } catch {
2362
+ return;
2363
+ }
2364
+ const result = [];
2365
+ for (const imp of imports) {
2366
+ if (imp.d === -2) continue;
2367
+ if (!imp.n || !isRemoteImport(imp.n)) continue;
2368
+ const stmtText = code.slice(imp.ss, imp.se);
2369
+ if (imp.d >= 0) {
2370
+ result.push({
2371
+ kind: "dynamic",
2372
+ start: imp.ss,
2373
+ end: imp.se,
2374
+ originalText: stmtText
2375
+ });
2376
+ continue;
2377
+ }
2378
+ if (/^\s*export\s*\*\s/.test(stmtText)) {
2379
+ result.push({
2380
+ kind: "export-all",
2381
+ source: imp.n,
2382
+ start: imp.ss,
2383
+ end: imp.se
2384
+ });
2385
+ continue;
2386
+ }
2387
+ if (/^\s*export\s/.test(stmtText)) {
2388
+ const braceMatch = stmtText.match(/\{([^}]*)\}/);
2389
+ if (!braceMatch) continue;
2390
+ const specs = braceMatch[1].split(",").map((s) => s.trim()).filter((s) => s.length > 0);
2391
+ if (specs.length === 0) continue;
2392
+ const specifiers = specs.map((s) => {
2393
+ const asMatch = s.match(/^(\w+)\s+as\s+(\w+)$/);
2394
+ return {
2395
+ local: asMatch ? asMatch[1] : s,
2396
+ exported: asMatch ? asMatch[2] : s
2397
+ };
2398
+ });
2399
+ result.push({
2400
+ kind: "reexport",
2401
+ source: imp.n,
2402
+ start: imp.ss,
2403
+ end: imp.se,
2404
+ specifiers
2405
+ });
2406
+ continue;
2407
+ }
2408
+ const importMatch = stmtText.match(/^import\s+([\s\S]*?)\s+from\s/);
2409
+ if (!importMatch) continue;
2410
+ const specifiersPart = importMatch[1].trim();
2411
+ if (/^type\s/.test(specifiersPart)) continue;
2412
+ const nsMatch = specifiersPart.match(/^\*\s+as\s+(\w+)$/);
2413
+ if (nsMatch) {
2414
+ result.push({
2415
+ kind: "static",
2416
+ source: imp.n,
2417
+ start: imp.ss,
2418
+ end: imp.se,
2419
+ named: [],
2420
+ namespaceLocal: nsMatch[1]
2421
+ });
2422
+ continue;
2423
+ }
2424
+ const braceMatch = specifiersPart.match(/\{([^}]*)\}/);
2425
+ if (!braceMatch) continue;
2426
+ const namedSpecifiers = braceMatch[1].split(",").map((s) => s.trim()).filter((s) => s.length > 0 && !s.startsWith("type "));
2427
+ if (namedSpecifiers.length === 0) continue;
2428
+ const defaultMatch = specifiersPart.match(/^(\w+)\s*,/);
2429
+ const named = namedSpecifiers.map((s) => {
2430
+ const asMatch = s.match(/^(\w+)\s+as\s+(\w+)$/);
2431
+ return {
2432
+ imported: asMatch ? asMatch[1] : s,
2433
+ local: asMatch ? asMatch[2] : s
2434
+ };
2435
+ });
2436
+ result.push({
2437
+ kind: "static",
2438
+ source: imp.n,
2439
+ start: imp.ss,
2440
+ end: imp.se,
2441
+ named,
2442
+ defaultLocal: defaultMatch?.[1]
2443
+ });
2444
+ }
2445
+ return result;
2446
+ }
2447
+ function pluginRemoteNamedExports(options) {
2448
+ const remoteNames = Object.keys(options.remotes);
2449
+ let rolldown;
2450
+ function isRemoteImport(source) {
2451
+ return remoteNames.some((name) => source === name || source.startsWith(name + "/"));
2452
+ }
2453
+ return {
2454
+ name: "module-federation-remote-named-exports",
2455
+ enforce: "pre",
2456
+ async transform(code, id) {
2457
+ rolldown ??= getIsRolldown(this);
2458
+ if (!rolldown) return;
2459
+ if (remoteNames.length === 0) return;
2460
+ if (id.includes("__loadRemote__") || id.includes("__loadShare__")) return;
2461
+ if (!JS_EXTENSIONS_RE.test(id)) return;
2462
+ if (!remoteNames.some((name) => code.includes(name))) return;
2463
+ let imports;
2464
+ try {
2465
+ imports = await collectFromAST(this.parse(code), code, isRemoteImport);
2466
+ } catch {
2467
+ imports = await collectFromEsLexer(code, isRemoteImport);
2468
+ }
2469
+ if (!imports) return;
2470
+ return applyRewrites(code, imports, id);
2471
+ }
2472
+ };
2473
+ }
2474
+ //#endregion
2183
2475
  //#region src/utils/PromiseStore.ts
2184
2476
  /**
2185
2477
  * example:
@@ -2255,7 +2547,7 @@ function proxySharedModule(options) {
2255
2547
  if (key.endsWith("/") && source !== key.slice(0, -1)) return;
2256
2548
  const loadSharePath = getLoadShareModulePath(source, isRolldown, command);
2257
2549
  writeLoadShareModule(source, shared[key], command, isRolldown);
2258
- writePreBuildLibPath(source, shared[key]);
2550
+ if (shared[key].shareConfig.import !== false) writePreBuildLibPath(source, shared[key]);
2259
2551
  addUsedShares(source);
2260
2552
  writeLocalSharedImportMap();
2261
2553
  return this.resolve(loadSharePath, importer);
@@ -2286,7 +2578,7 @@ function proxySharedModule(options) {
2286
2578
  },
2287
2579
  configResolved(config) {
2288
2580
  _config = config;
2289
- const isRolldown = !!config.experimental?.rolldownDev;
2581
+ const isRolldown = getIsRolldown(this);
2290
2582
  Object.keys(shared).forEach((key) => {
2291
2583
  if (key.endsWith("/")) return;
2292
2584
  if (isVinext && key === "react") {
@@ -2294,7 +2586,7 @@ function proxySharedModule(options) {
2294
2586
  return;
2295
2587
  }
2296
2588
  writeLoadShareModule(key, shared[key], _command, isRolldown);
2297
- writePreBuildLibPath(key, shared[key]);
2589
+ if (shared[key].shareConfig.import !== false) writePreBuildLibPath(key, shared[key]);
2298
2590
  addUsedShares(key);
2299
2591
  });
2300
2592
  writeLocalSharedImportMap();
@@ -2522,9 +2814,9 @@ function createEarlyVirtualModulesPlugin(options) {
2522
2814
  }
2523
2815
  getLoadShareModulePath(key, isRolldown);
2524
2816
  writeLoadShareModule(key, shareItem, _command, isRolldown);
2525
- writePreBuildLibPath(key, shareItem);
2817
+ if (shareItem.shareConfig?.import !== false) writePreBuildLibPath(key, shareItem);
2526
2818
  addUsedShares(key);
2527
- if (_command === "serve") {
2819
+ if (_command === "serve" && shareItem.shareConfig?.import !== false) {
2528
2820
  if (!isRolldown) config.optimizeDeps.include.push(getLoadShareImportId(key, isRolldown, _command));
2529
2821
  config.optimizeDeps.include.push(getPreBuildLibImportId(key));
2530
2822
  }
@@ -2597,6 +2889,7 @@ function federation(mfUserOptions) {
2597
2889
  virtualExposesId
2598
2890
  }),
2599
2891
  pluginProxyRemotes_default(options),
2892
+ pluginRemoteNamedExports(options),
2600
2893
  ...pluginModuleParseEnd_default((id) => {
2601
2894
  return id.includes(getHostAutoInitImportId()) || id.includes(remoteEntryId) || id.includes(virtualExposesId) || id.includes(getLocalSharedImportMapPath());
2602
2895
  }, {
@@ -2632,19 +2925,19 @@ function federation(mfUserOptions) {
2632
2925
  warnedAboutCodeSplitting = true;
2633
2926
  mfWarn("Ignoring `build.rolldownOptions.output.codeSplitting = false` because module federation requires chunk splitting.");
2634
2927
  };
2928
+ let warnedAboutManualChunks = false;
2635
2929
  const applyManualChunks = (output) => {
2636
2930
  ensureCodeSplitting(output);
2637
- const existingManualChunks = output.manualChunks;
2931
+ if (output.manualChunks && !warnedAboutManualChunks) {
2932
+ warnedAboutManualChunks = true;
2933
+ mfWarn("Ignoring `build.rollupOptions.output.manualChunks` because it conflicts with module federation. Module federation transforms shared dependency imports with top-level await, and grouping these transformed modules into a single chunk creates circular async dependencies that cause the application to silently hang.");
2934
+ }
2638
2935
  output.manualChunks = function(id) {
2639
2936
  if (id.includes(runtimeInitId)) return "runtimeInit";
2640
2937
  if (id.includes("__loadShare__")) {
2641
2938
  const match = id.match(/([^/\\]+__loadShare__[^/\\]+)/);
2642
2939
  return match ? match[1] : "loadShare";
2643
2940
  }
2644
- if (typeof existingManualChunks === "function") return existingManualChunks.apply(this, arguments);
2645
- if (existingManualChunks && typeof existingManualChunks === "object") {
2646
- for (const [key, ids] of Object.entries(existingManualChunks)) if (Array.isArray(ids) && ids.some((v) => id.includes(v))) return key;
2647
- }
2648
2941
  };
2649
2942
  };
2650
2943
  config.build.rollupOptions = config.build.rollupOptions || {};
@@ -2676,6 +2969,7 @@ function federation(mfUserOptions) {
2676
2969
  * @see https://rollupjs.org/plugin-development/#synthetic-named-exports
2677
2970
  */
2678
2971
  code = code.replace("export default exportModule", "export const __moduleExports = exportModule;\nexport default exportModule.__esModule ? exportModule.default : exportModule");
2972
+ if (getIsRolldown(this)) return { code };
2679
2973
  return {
2680
2974
  code,
2681
2975
  syntheticNamedExports: "__moduleExports"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/vite",
3
- "version": "1.13.3",
3
+ "version": "1.13.5",
4
4
  "description": "Vite plugin for Module Federation",
5
5
  "type": "module",
6
6
  "main": "./lib/index.cjs",
@@ -30,10 +30,13 @@
30
30
  "dev-rv": "pnpm clean && pnpm -filter 'examples-rust-vite*' run dev",
31
31
  "preview-rv": "pnpm clean && pnpm -filter 'examples-rust-vite*' run preview",
32
32
  "dev-vv": "pnpm clean && pnpm -filter 'examples-vite-vite*' run dev",
33
- "preview-vv": "pnpm clean && pnpm -filter 'examples-vite-vite*' --parallel run preview",
33
+ "preview-vv": "pnpm clean && pnpm run build:shared-lib && pnpm -filter 'examples-vite-vite*' --parallel run preview",
34
+ "preview-vv:ci": "pnpm run build:shared-lib && pnpm -filter 'examples-vite-vite*' --parallel run preview",
35
+ "build:shared-lib": "pnpm --filter @vite-vite/shared-lib run build",
36
+ "multi-example:ci": "pnpm -filter 'multi-example-*' --parallel run start",
34
37
  "mixed-vv:1": "pnpm clean && pnpm -filter 'examples-vite-vite*' run mixed:1",
35
38
  "mixed-vv:2": "pnpm clean && pnpm -filter 'examples-vite-vite*' run mixed:2",
36
- "multi-example": "pnpm clean && pnpm --filter \"multi-example-*\" --parallel run start",
39
+ "multi-example": "pnpm clean && pnpm --filter 'multi-example-*' --parallel run start",
37
40
  "test": "vitest run --dir src",
38
41
  "test:integration": "vitest run integration",
39
42
  "e2e": "playwright test",
@@ -72,7 +75,7 @@
72
75
  "@module-federation/sdk": "2.2.3",
73
76
  "@rollup/pluginutils": "^5.3.0",
74
77
  "defu": "^6.1.4",
75
- "es-module-lexer": "^1.7.0",
78
+ "es-module-lexer": "^2.0.0",
76
79
  "estree-walker": "^3.0.3",
77
80
  "magic-string": "^0.30.21",
78
81
  "pathe": "^2.0.3"