@module-federation/vite 1.14.3 → 1.14.4

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.
Files changed (3) hide show
  1. package/lib/index.cjs +148 -73
  2. package/lib/index.mjs +148 -73
  3. package/package.json +4 -4
package/lib/index.cjs CHANGED
@@ -152,6 +152,56 @@ function removePathFromNpmPackage(packageString) {
152
152
  const match = packageString.match(/^(?:@[^/]+\/)?[^/]+/);
153
153
  return match ? match[0] : packageString;
154
154
  }
155
+ function getInstalledPackageJson(pkg, opts) {
156
+ const cwd = opts?.cwd || getPackageDetectionCwd();
157
+ const packageName = opts?.packageName || removePathFromNpmPackage(pkg);
158
+ try {
159
+ const projectRequire = (0, module$1.createRequire)(new URL(`file://${pathe.default.join(cwd, "package.json")}`));
160
+ let resolvedPath;
161
+ try {
162
+ resolvedPath = projectRequire.resolve(pkg);
163
+ } catch {
164
+ resolvedPath = projectRequire.resolve(packageName);
165
+ }
166
+ let currentDir = pathe.default.dirname(resolvedPath);
167
+ const rootDir = pathe.default.parse(currentDir).root;
168
+ while (true) {
169
+ const packageJsonPath = pathe.default.join(currentDir, "package.json");
170
+ if ((0, fs.existsSync)(packageJsonPath)) {
171
+ const packageJsonContent = (0, fs.readFileSync)(packageJsonPath, "utf-8");
172
+ try {
173
+ const packageJson = JSON.parse(packageJsonContent);
174
+ if (packageJson.name === packageName) return {
175
+ path: packageJsonPath,
176
+ dir: currentDir,
177
+ packageJson
178
+ };
179
+ } catch (error) {
180
+ if (!(error instanceof SyntaxError)) throw error;
181
+ }
182
+ }
183
+ if (currentDir === rootDir) break;
184
+ currentDir = pathe.default.dirname(currentDir);
185
+ }
186
+ } catch {
187
+ let currentDir = cwd;
188
+ const rootDir = pathe.default.parse(currentDir).root;
189
+ while (true) {
190
+ const packageJsonPath = pathe.default.join(currentDir, "node_modules", packageName, "package.json");
191
+ if ((0, fs.existsSync)(packageJsonPath)) try {
192
+ return {
193
+ path: packageJsonPath,
194
+ dir: pathe.default.dirname(packageJsonPath),
195
+ packageJson: JSON.parse((0, fs.readFileSync)(packageJsonPath, "utf-8"))
196
+ };
197
+ } catch {
198
+ return;
199
+ }
200
+ if (currentDir === rootDir) break;
201
+ currentDir = pathe.default.dirname(currentDir);
202
+ }
203
+ }
204
+ }
155
205
  /**
156
206
  * Detect whether the current runtime is Vite 8+ by checking for a Vite version flag
157
207
  * on the plugin hook context, with Rolldown metadata kept as a compatibility fallback.
@@ -956,6 +1006,12 @@ function inferVersionFromRequiredVersion(requiredVersion) {
956
1006
  if (!requiredVersion) return void 0;
957
1007
  return requiredVersion.match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/)?.[0];
958
1008
  }
1009
+ function getLitExportSubpathShares(sharedName) {
1010
+ if (sharedName !== "lit") return [];
1011
+ const exportsField = getInstalledPackageJson(sharedName, { packageName: sharedName })?.packageJson.exports;
1012
+ if (!exportsField || typeof exportsField === "string") return [];
1013
+ return Object.keys(exportsField).filter((key) => key.startsWith("./") && key !== "." && !key.includes("*")).map((key) => `${sharedName}/${key.slice(2)}`);
1014
+ }
959
1015
  function normalizeShareItem(key, shareItem) {
960
1016
  let version;
961
1017
  if (!(typeof shareItem === "object" && shareItem.import === false)) try {
@@ -1000,14 +1056,21 @@ function normalizeShareItem(key, shareItem) {
1000
1056
  function normalizeShared(shared) {
1001
1057
  if (!shared) return {};
1002
1058
  const result = {};
1003
- if (Array.isArray(shared)) {
1004
- shared.forEach((key) => {
1005
- result[key] = normalizeShareItem(key, key);
1006
- });
1007
- return result;
1008
- }
1009
- if (typeof shared === "object") Object.keys(shared).forEach((key) => {
1010
- result[key] = normalizeShareItem(key, shared[key]);
1059
+ const sourceEntries = [];
1060
+ if (Array.isArray(shared)) shared.forEach((key) => {
1061
+ result[key] = normalizeShareItem(key, key);
1062
+ sourceEntries.push([key, key]);
1063
+ });
1064
+ else if (typeof shared === "object") Object.keys(shared).forEach((key) => {
1065
+ const value = shared[key];
1066
+ result[key] = normalizeShareItem(key, value);
1067
+ sourceEntries.push([key, value]);
1068
+ });
1069
+ sourceEntries.forEach(([key, value]) => {
1070
+ for (const subpathShare of getLitExportSubpathShares(key)) {
1071
+ if (result[subpathShare]) continue;
1072
+ result[subpathShare] = normalizeShareItem(subpathShare, value);
1073
+ }
1011
1074
  });
1012
1075
  return result;
1013
1076
  }
@@ -1467,52 +1530,6 @@ function resolvePackageEntryFromProjectRoot(pkg) {
1467
1530
  return;
1468
1531
  }
1469
1532
  }
1470
- function getInstalledPackageJsonPath(pkg) {
1471
- try {
1472
- const packageName = removePathFromNpmPackage(pkg);
1473
- const projectRequire = (0, module$1.createRequire)(new URL(`file://${pathe.default.join(getPackageDetectionCwd(), "package.json")}`));
1474
- let resolvedPath;
1475
- try {
1476
- resolvedPath = projectRequire.resolve(pkg);
1477
- } catch {
1478
- resolvedPath = projectRequire.resolve(packageName);
1479
- }
1480
- let currentDir = pathe.default.dirname(resolvedPath);
1481
- const rootDir = pathe.default.parse(currentDir).root;
1482
- while (currentDir !== rootDir) {
1483
- const packageJsonPath = pathe.default.join(currentDir, "package.json");
1484
- if ((0, fs.existsSync)(packageJsonPath)) {
1485
- const packageJsonContent = (0, fs.readFileSync)(packageJsonPath, "utf-8");
1486
- try {
1487
- if (JSON.parse(packageJsonContent).name === packageName) return packageJsonPath;
1488
- } catch (error) {
1489
- if (!(error instanceof SyntaxError)) throw error;
1490
- }
1491
- }
1492
- currentDir = pathe.default.dirname(currentDir);
1493
- }
1494
- const rootPackageJsonPath = pathe.default.join(rootDir, "package.json");
1495
- if ((0, fs.existsSync)(rootPackageJsonPath)) {
1496
- const rootPackageJsonContent = (0, fs.readFileSync)(rootPackageJsonPath, "utf-8");
1497
- try {
1498
- if (JSON.parse(rootPackageJsonContent).name === packageName) return rootPackageJsonPath;
1499
- } catch (error) {
1500
- if (!(error instanceof SyntaxError)) throw error;
1501
- }
1502
- }
1503
- } catch {
1504
- const packageName = removePathFromNpmPackage(pkg);
1505
- let currentDir = getPackageDetectionCwd();
1506
- const rootDir = pathe.default.parse(currentDir).root;
1507
- while (currentDir !== rootDir) {
1508
- const packageJsonPath = pathe.default.join(currentDir, "node_modules", packageName, "package.json");
1509
- if ((0, fs.existsSync)(packageJsonPath)) return packageJsonPath;
1510
- currentDir = pathe.default.dirname(currentDir);
1511
- }
1512
- const rootPackageJsonPath = pathe.default.join(rootDir, "node_modules", packageName, "package.json");
1513
- return (0, fs.existsSync)(rootPackageJsonPath) ? rootPackageJsonPath : void 0;
1514
- }
1515
- }
1516
1533
  function resolveImportTarget(exportsField) {
1517
1534
  if (typeof exportsField === "string") return exportsField;
1518
1535
  if (!exportsField || typeof exportsField !== "object") return void 0;
@@ -1533,14 +1550,14 @@ function resolveImportTarget(exportsField) {
1533
1550
  function getPackageEsmEntryPath(pkg) {
1534
1551
  try {
1535
1552
  const resolvedEntryPath = resolvePackageEntryFromProjectRoot(pkg);
1536
- const packageJsonPath = getInstalledPackageJsonPath(pkg);
1537
- if (!packageJsonPath) return resolvedEntryPath;
1553
+ const installedPackageJson = getInstalledPackageJson(pkg);
1554
+ if (!installedPackageJson) return resolvedEntryPath;
1538
1555
  const packageName = removePathFromNpmPackage(pkg);
1539
- const packageJson = JSON.parse((0, fs.readFileSync)(packageJsonPath, "utf-8"));
1556
+ const packageJson = installedPackageJson.packageJson;
1540
1557
  const subpath = pkg === packageName ? "." : `.${pkg.slice(packageName.length)}`;
1541
1558
  const target = resolveImportTarget(typeof packageJson.exports === "string" ? subpath === "." ? packageJson.exports : void 0 : packageJson.exports?.[subpath] ?? (subpath === "." ? packageJson.exports?.["."] ?? (packageJson.exports && !Object.keys(packageJson.exports).some((key) => key.startsWith(".")) ? packageJson.exports : void 0) : void 0)) || packageJson.module;
1542
1559
  if (!target) return resolvedEntryPath;
1543
- return pathe.default.resolve(pathe.default.dirname(packageJsonPath), target);
1560
+ return pathe.default.resolve(installedPackageJson.dir, target);
1544
1561
  } catch {
1545
1562
  return resolvePackageEntryFromProjectRoot(pkg);
1546
1563
  }
@@ -1684,8 +1701,11 @@ function getSharedImportSource(pkg, shareItem) {
1684
1701
  }
1685
1702
  const LOAD_SHARE_TAG = "__loadShare__";
1686
1703
  const loadShareCacheMap = {};
1704
+ function shouldUseEsmLoadShare(pkg, command, isRolldown) {
1705
+ return command === "build" || !!isRolldown || pkg === "lit" || pkg.startsWith("lit/");
1706
+ }
1687
1707
  function getLoadShareImportId(pkg, isRolldown, command) {
1688
- if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, isRolldown || command === "build" ? ".mjs" : ".js");
1708
+ if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, shouldUseEsmLoadShare(pkg, command, isRolldown) ? ".mjs" : ".js");
1689
1709
  return loadShareCacheMap[pkg].getImportId();
1690
1710
  }
1691
1711
  function getLoadShareModulePath(pkg, isRolldown, command) {
@@ -1693,8 +1713,8 @@ function getLoadShareModulePath(pkg, isRolldown, command) {
1693
1713
  return loadShareCacheMap[pkg].getPath();
1694
1714
  }
1695
1715
  function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1696
- if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, isRolldown || command === "build" ? ".mjs" : ".js");
1697
- const useESM = command === "build" || isRolldown;
1716
+ if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, shouldUseEsmLoadShare(pkg, command, isRolldown) ? ".mjs" : ".js");
1717
+ const useESM = shouldUseEsmLoadShare(pkg, command, isRolldown);
1698
1718
  const importLine = command === "build" ? getRuntimeInitPromiseBootstrapCode() : useESM ? `${getRuntimeInitBootstrapCode()}
1699
1719
  const { initPromise } = globalThis[globalKey];` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
1700
1720
  const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
@@ -1728,6 +1748,7 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1728
1748
  const devImportSource = concreteSharedImportSource || pkg;
1729
1749
  const localProviderPath = getLocalProviderImportPath(pkg);
1730
1750
  const isWorkspacePackage = isWorkspaceFilePath(localProviderPath) || isWorkspaceFilePath(concreteSharedImportSource);
1751
+ const skipServePrebuildWarmup = command !== "build" && (pkg === "lit" || pkg.startsWith("lit/"));
1731
1752
  const providerImportId = localProviderPath || concreteSharedImportSource || sharedImportSource;
1732
1753
  const namedExports = getPackageNamedExports(pkg);
1733
1754
  let exportLine;
@@ -1736,8 +1757,8 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1736
1757
  const namedExportLine = `export { ${namedExports.map((name, i) => `__mf_${i} as ${name}`).join(", ")} };`;
1737
1758
  exportLine = useESM ? `export default exportModule.default ?? exportModule;\n ${destructure}\n ${namedExportLine}` : `module.exports = exportModule;\n ${destructure}\n Object.assign(module.exports, { ${namedExports.map((name, i) => `"${name}": __mf_${i}`).join(", ")} });`;
1738
1759
  } else exportLine = useESM ? `export default exportModule.default ?? exportModule\n export * from ${escapeGeneratedStringLiteral(sharedImportSource)}` : "module.exports = exportModule";
1739
- const prebuildImportLine = isWorkspacePackage && command !== "build" ? "" : `import ${escapeGeneratedStringLiteral(sharedImportSource)};`;
1740
- const devDynamicImportLine = isWorkspacePackage ? "" : command !== "build" ? `;() => import(${escapeGeneratedStringLiteral(devImportSource)}).catch(() => {});` : "";
1760
+ const prebuildImportLine = isWorkspacePackage && command !== "build" || skipServePrebuildWarmup ? "" : `import ${escapeGeneratedStringLiteral(sharedImportSource)};`;
1761
+ const devDynamicImportLine = isWorkspacePackage ? "" : command !== "build" && !skipServePrebuildWarmup ? `;() => import(${escapeGeneratedStringLiteral(devImportSource)}).catch(() => {});` : "";
1741
1762
  loadShareCacheMap[pkg].writeSync(`
1742
1763
  ${prebuildImportLine}
1743
1764
  ${devDynamicImportLine}
@@ -3072,6 +3093,7 @@ function pluginRemoteNamedExports(options) {
3072
3093
  name: "module-federation-remote-named-exports",
3073
3094
  enforce: "post",
3074
3095
  async transform(code, id) {
3096
+ if (!getIsRolldown(this)) return;
3075
3097
  if (remoteNames.length === 0) return;
3076
3098
  if (id.includes("__loadRemote__") || id.includes("__loadShare__")) return;
3077
3099
  if (!JS_EXTENSIONS_RE.test(id)) return;
@@ -3330,6 +3352,7 @@ function insertAfterLastTopLevelImport(code, snippet) {
3330
3352
  */
3331
3353
  function createEarlyVirtualModulesPlugin(options) {
3332
3354
  const { shared, remotes, virtualModuleDir } = options;
3355
+ const isLitShare = (pkg) => pkg === "lit" || pkg.startsWith("lit/");
3333
3356
  return {
3334
3357
  name: "vite:module-federation-early-init",
3335
3358
  enforce: "pre",
@@ -3365,8 +3388,13 @@ function createEarlyVirtualModulesPlugin(options) {
3365
3388
  if (shareItem.shareConfig?.import !== false) writePreBuildLibPath(key, shareItem);
3366
3389
  addUsedShares(key);
3367
3390
  if (_command === "serve" && shareItem.shareConfig?.import !== false) {
3368
- if (!isRolldown) config.optimizeDeps.include.push(getLoadShareImportId(key, isRolldown, _command));
3369
- config.optimizeDeps.include.push(getPreBuildLibImportId(key));
3391
+ const optimizeDeps = config.optimizeDeps ??= {};
3392
+ optimizeDeps.include ??= [];
3393
+ optimizeDeps.exclude ??= [];
3394
+ const shouldBypassOptimizeDep = isLitShare(key);
3395
+ if (shouldBypassOptimizeDep) optimizeDeps.exclude.push(key);
3396
+ if (!isRolldown && !shouldBypassOptimizeDep) optimizeDeps.include.push(getLoadShareImportId(key, isRolldown, _command));
3397
+ optimizeDeps.include.push(getPreBuildLibImportId(key));
3370
3398
  }
3371
3399
  }
3372
3400
  writeLocalSharedImportMap();
@@ -3384,6 +3412,7 @@ function federation(mfUserOptions) {
3384
3412
  const virtualExposesId = getVirtualExposesId(options);
3385
3413
  let command;
3386
3414
  let depsDir = "/node_modules/.vite/deps/";
3415
+ let desiredRolldownOutput;
3387
3416
  return [
3388
3417
  createEarlyVirtualModulesPlugin(options),
3389
3418
  ...isVinext ? [{
@@ -3474,12 +3503,22 @@ function federation(mfUserOptions) {
3474
3503
  };
3475
3504
  }
3476
3505
  let warnedAboutCodeSplitting = false;
3506
+ let warnedAboutCodeSplittingGroups = false;
3477
3507
  const ensureCodeSplitting = (output) => {
3478
- if (output?.codeSplitting !== false) return;
3479
- delete output.codeSplitting;
3480
- if (warnedAboutCodeSplitting) return;
3481
- warnedAboutCodeSplitting = true;
3482
- mfWarn("Ignoring `build.rolldownOptions.output.codeSplitting = false` because module federation requires chunk splitting.");
3508
+ if (output?.codeSplitting === false) {
3509
+ delete output.codeSplitting;
3510
+ if (warnedAboutCodeSplitting) return;
3511
+ warnedAboutCodeSplitting = true;
3512
+ mfWarn("Ignoring `output.codeSplitting = false` because module federation requires chunk splitting.");
3513
+ return;
3514
+ }
3515
+ if (!output?.codeSplitting || typeof output.codeSplitting !== "object") return;
3516
+ if (!("groups" in output.codeSplitting)) return;
3517
+ delete output.codeSplitting.groups;
3518
+ if (Object.keys(output.codeSplitting).length === 0) delete output.codeSplitting;
3519
+ if (warnedAboutCodeSplittingGroups) return;
3520
+ warnedAboutCodeSplittingGroups = true;
3521
+ mfWarn("Ignoring `output.codeSplitting.groups` because it conflicts with module federation. Grouping shared dependency init wrappers with their dependent modules can break runtime init order and cause standalone remotes to fail before mount.");
3483
3522
  };
3484
3523
  let warnedAboutManualChunks = false;
3485
3524
  const applyManualChunks = (output) => {
@@ -3487,7 +3526,7 @@ function federation(mfUserOptions) {
3487
3526
  const isPatchedByPlugin = !!(output.manualChunks && patchedManualChunks.has(output.manualChunks));
3488
3527
  if (output.manualChunks && !isPatchedByPlugin && !warnedAboutManualChunks) {
3489
3528
  warnedAboutManualChunks = true;
3490
- 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.");
3529
+ mfWarn("Ignoring `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.");
3491
3530
  }
3492
3531
  const mfManualChunks = function(id) {
3493
3532
  if (id.includes(runtimeInitId)) return "runtimeInit";
@@ -3500,10 +3539,46 @@ function federation(mfUserOptions) {
3500
3539
  output.manualChunks = mfManualChunks;
3501
3540
  };
3502
3541
  config.build.rollupOptions = config.build.rollupOptions || {};
3503
- if (!Array.isArray(config.build.rollupOptions.output)) applyManualChunks(config.build.rollupOptions.output ||= {});
3542
+ const rollupOutput = config.build.rollupOptions.output;
3543
+ if (Array.isArray(rollupOutput)) rollupOutput.forEach((output) => applyManualChunks(output));
3544
+ else applyManualChunks(config.build.rollupOptions.output ||= {});
3504
3545
  const buildWithRolldown = config.build;
3505
3546
  buildWithRolldown.rolldownOptions = buildWithRolldown.rolldownOptions || {};
3506
- if (!Array.isArray(buildWithRolldown.rolldownOptions.output)) applyManualChunks(buildWithRolldown.rolldownOptions.output ||= {});
3547
+ const rolldownOutput = buildWithRolldown.rolldownOptions.output;
3548
+ const snapshotRolldownOutput = (output) => ({
3549
+ entryFileNames: output.entryFileNames,
3550
+ chunkFileNames: output.chunkFileNames,
3551
+ assetFileNames: output.assetFileNames
3552
+ });
3553
+ if (Array.isArray(rolldownOutput)) {
3554
+ rolldownOutput.forEach((output) => applyManualChunks(output));
3555
+ desiredRolldownOutput = rolldownOutput.map((output) => snapshotRolldownOutput(output));
3556
+ } else {
3557
+ applyManualChunks(buildWithRolldown.rolldownOptions.output ||= {});
3558
+ desiredRolldownOutput = [snapshotRolldownOutput(buildWithRolldown.rolldownOptions.output)];
3559
+ }
3560
+ },
3561
+ async buildApp(builder) {
3562
+ if (!desiredRolldownOutput) return;
3563
+ const applyRolldownOutput = (output, restoredOutput) => {
3564
+ if (!output || !restoredOutput) return;
3565
+ if (restoredOutput.entryFileNames !== void 0) output.entryFileNames = restoredOutput.entryFileNames;
3566
+ if (restoredOutput.chunkFileNames !== void 0) output.chunkFileNames = restoredOutput.chunkFileNames;
3567
+ if (restoredOutput.assetFileNames !== void 0) output.assetFileNames = restoredOutput.assetFileNames;
3568
+ };
3569
+ for (const environment of Object.values(builder.environments)) {
3570
+ const getRolldownOptions = environment?.getRolldownOptions;
3571
+ if (typeof getRolldownOptions !== "function") continue;
3572
+ environment.getRolldownOptions = async () => {
3573
+ const rolldownOptions = await getRolldownOptions.call(environment);
3574
+ if (Array.isArray(rolldownOptions.output)) rolldownOptions.output.forEach((output, index) => applyRolldownOutput(output, desiredRolldownOutput?.[index]));
3575
+ else {
3576
+ rolldownOptions.output ||= {};
3577
+ applyRolldownOutput(rolldownOptions.output, desiredRolldownOutput[0]);
3578
+ }
3579
+ return rolldownOptions;
3580
+ };
3581
+ }
3507
3582
  },
3508
3583
  load(id) {
3509
3584
  if (id.startsWith("\0")) return;
package/lib/index.mjs CHANGED
@@ -130,6 +130,56 @@ function removePathFromNpmPackage(packageString) {
130
130
  const match = packageString.match(/^(?:@[^/]+\/)?[^/]+/);
131
131
  return match ? match[0] : packageString;
132
132
  }
133
+ function getInstalledPackageJson(pkg, opts) {
134
+ const cwd = opts?.cwd || getPackageDetectionCwd();
135
+ const packageName = opts?.packageName || removePathFromNpmPackage(pkg);
136
+ try {
137
+ const projectRequire = createRequire$1(new URL(`file://${path.join(cwd, "package.json")}`));
138
+ let resolvedPath;
139
+ try {
140
+ resolvedPath = projectRequire.resolve(pkg);
141
+ } catch {
142
+ resolvedPath = projectRequire.resolve(packageName);
143
+ }
144
+ let currentDir = path.dirname(resolvedPath);
145
+ const rootDir = path.parse(currentDir).root;
146
+ while (true) {
147
+ const packageJsonPath = path.join(currentDir, "package.json");
148
+ if (existsSync(packageJsonPath)) {
149
+ const packageJsonContent = readFileSync(packageJsonPath, "utf-8");
150
+ try {
151
+ const packageJson = JSON.parse(packageJsonContent);
152
+ if (packageJson.name === packageName) return {
153
+ path: packageJsonPath,
154
+ dir: currentDir,
155
+ packageJson
156
+ };
157
+ } catch (error) {
158
+ if (!(error instanceof SyntaxError)) throw error;
159
+ }
160
+ }
161
+ if (currentDir === rootDir) break;
162
+ currentDir = path.dirname(currentDir);
163
+ }
164
+ } catch {
165
+ let currentDir = cwd;
166
+ const rootDir = path.parse(currentDir).root;
167
+ while (true) {
168
+ const packageJsonPath = path.join(currentDir, "node_modules", packageName, "package.json");
169
+ if (existsSync(packageJsonPath)) try {
170
+ return {
171
+ path: packageJsonPath,
172
+ dir: path.dirname(packageJsonPath),
173
+ packageJson: JSON.parse(readFileSync(packageJsonPath, "utf-8"))
174
+ };
175
+ } catch {
176
+ return;
177
+ }
178
+ if (currentDir === rootDir) break;
179
+ currentDir = path.dirname(currentDir);
180
+ }
181
+ }
182
+ }
133
183
  /**
134
184
  * Detect whether the current runtime is Vite 8+ by checking for a Vite version flag
135
185
  * on the plugin hook context, with Rolldown metadata kept as a compatibility fallback.
@@ -934,6 +984,12 @@ function inferVersionFromRequiredVersion(requiredVersion) {
934
984
  if (!requiredVersion) return void 0;
935
985
  return requiredVersion.match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/)?.[0];
936
986
  }
987
+ function getLitExportSubpathShares(sharedName) {
988
+ if (sharedName !== "lit") return [];
989
+ const exportsField = getInstalledPackageJson(sharedName, { packageName: sharedName })?.packageJson.exports;
990
+ if (!exportsField || typeof exportsField === "string") return [];
991
+ return Object.keys(exportsField).filter((key) => key.startsWith("./") && key !== "." && !key.includes("*")).map((key) => `${sharedName}/${key.slice(2)}`);
992
+ }
937
993
  function normalizeShareItem(key, shareItem) {
938
994
  let version;
939
995
  if (!(typeof shareItem === "object" && shareItem.import === false)) try {
@@ -977,14 +1033,21 @@ function normalizeShareItem(key, shareItem) {
977
1033
  function normalizeShared(shared) {
978
1034
  if (!shared) return {};
979
1035
  const result = {};
980
- if (Array.isArray(shared)) {
981
- shared.forEach((key) => {
982
- result[key] = normalizeShareItem(key, key);
983
- });
984
- return result;
985
- }
986
- if (typeof shared === "object") Object.keys(shared).forEach((key) => {
987
- result[key] = normalizeShareItem(key, shared[key]);
1036
+ const sourceEntries = [];
1037
+ if (Array.isArray(shared)) shared.forEach((key) => {
1038
+ result[key] = normalizeShareItem(key, key);
1039
+ sourceEntries.push([key, key]);
1040
+ });
1041
+ else if (typeof shared === "object") Object.keys(shared).forEach((key) => {
1042
+ const value = shared[key];
1043
+ result[key] = normalizeShareItem(key, value);
1044
+ sourceEntries.push([key, value]);
1045
+ });
1046
+ sourceEntries.forEach(([key, value]) => {
1047
+ for (const subpathShare of getLitExportSubpathShares(key)) {
1048
+ if (result[subpathShare]) continue;
1049
+ result[subpathShare] = normalizeShareItem(subpathShare, value);
1050
+ }
988
1051
  });
989
1052
  return result;
990
1053
  }
@@ -1444,52 +1507,6 @@ function resolvePackageEntryFromProjectRoot(pkg) {
1444
1507
  return;
1445
1508
  }
1446
1509
  }
1447
- function getInstalledPackageJsonPath(pkg) {
1448
- try {
1449
- const packageName = removePathFromNpmPackage(pkg);
1450
- const projectRequire = createRequire$1(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`));
1451
- let resolvedPath;
1452
- try {
1453
- resolvedPath = projectRequire.resolve(pkg);
1454
- } catch {
1455
- resolvedPath = projectRequire.resolve(packageName);
1456
- }
1457
- let currentDir = path.dirname(resolvedPath);
1458
- const rootDir = path.parse(currentDir).root;
1459
- while (currentDir !== rootDir) {
1460
- const packageJsonPath = path.join(currentDir, "package.json");
1461
- if (existsSync(packageJsonPath)) {
1462
- const packageJsonContent = readFileSync(packageJsonPath, "utf-8");
1463
- try {
1464
- if (JSON.parse(packageJsonContent).name === packageName) return packageJsonPath;
1465
- } catch (error) {
1466
- if (!(error instanceof SyntaxError)) throw error;
1467
- }
1468
- }
1469
- currentDir = path.dirname(currentDir);
1470
- }
1471
- const rootPackageJsonPath = path.join(rootDir, "package.json");
1472
- if (existsSync(rootPackageJsonPath)) {
1473
- const rootPackageJsonContent = readFileSync(rootPackageJsonPath, "utf-8");
1474
- try {
1475
- if (JSON.parse(rootPackageJsonContent).name === packageName) return rootPackageJsonPath;
1476
- } catch (error) {
1477
- if (!(error instanceof SyntaxError)) throw error;
1478
- }
1479
- }
1480
- } catch {
1481
- const packageName = removePathFromNpmPackage(pkg);
1482
- let currentDir = getPackageDetectionCwd();
1483
- const rootDir = path.parse(currentDir).root;
1484
- while (currentDir !== rootDir) {
1485
- const packageJsonPath = path.join(currentDir, "node_modules", packageName, "package.json");
1486
- if (existsSync(packageJsonPath)) return packageJsonPath;
1487
- currentDir = path.dirname(currentDir);
1488
- }
1489
- const rootPackageJsonPath = path.join(rootDir, "node_modules", packageName, "package.json");
1490
- return existsSync(rootPackageJsonPath) ? rootPackageJsonPath : void 0;
1491
- }
1492
- }
1493
1510
  function resolveImportTarget(exportsField) {
1494
1511
  if (typeof exportsField === "string") return exportsField;
1495
1512
  if (!exportsField || typeof exportsField !== "object") return void 0;
@@ -1510,14 +1527,14 @@ function resolveImportTarget(exportsField) {
1510
1527
  function getPackageEsmEntryPath(pkg) {
1511
1528
  try {
1512
1529
  const resolvedEntryPath = resolvePackageEntryFromProjectRoot(pkg);
1513
- const packageJsonPath = getInstalledPackageJsonPath(pkg);
1514
- if (!packageJsonPath) return resolvedEntryPath;
1530
+ const installedPackageJson = getInstalledPackageJson(pkg);
1531
+ if (!installedPackageJson) return resolvedEntryPath;
1515
1532
  const packageName = removePathFromNpmPackage(pkg);
1516
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
1533
+ const packageJson = installedPackageJson.packageJson;
1517
1534
  const subpath = pkg === packageName ? "." : `.${pkg.slice(packageName.length)}`;
1518
1535
  const target = resolveImportTarget(typeof packageJson.exports === "string" ? subpath === "." ? packageJson.exports : void 0 : packageJson.exports?.[subpath] ?? (subpath === "." ? packageJson.exports?.["."] ?? (packageJson.exports && !Object.keys(packageJson.exports).some((key) => key.startsWith(".")) ? packageJson.exports : void 0) : void 0)) || packageJson.module;
1519
1536
  if (!target) return resolvedEntryPath;
1520
- return path.resolve(path.dirname(packageJsonPath), target);
1537
+ return path.resolve(installedPackageJson.dir, target);
1521
1538
  } catch {
1522
1539
  return resolvePackageEntryFromProjectRoot(pkg);
1523
1540
  }
@@ -1661,8 +1678,11 @@ function getSharedImportSource(pkg, shareItem) {
1661
1678
  }
1662
1679
  const LOAD_SHARE_TAG = "__loadShare__";
1663
1680
  const loadShareCacheMap = {};
1681
+ function shouldUseEsmLoadShare(pkg, command, isRolldown) {
1682
+ return command === "build" || !!isRolldown || pkg === "lit" || pkg.startsWith("lit/");
1683
+ }
1664
1684
  function getLoadShareImportId(pkg, isRolldown, command) {
1665
- if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, isRolldown || command === "build" ? ".mjs" : ".js");
1685
+ if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, shouldUseEsmLoadShare(pkg, command, isRolldown) ? ".mjs" : ".js");
1666
1686
  return loadShareCacheMap[pkg].getImportId();
1667
1687
  }
1668
1688
  function getLoadShareModulePath(pkg, isRolldown, command) {
@@ -1670,8 +1690,8 @@ function getLoadShareModulePath(pkg, isRolldown, command) {
1670
1690
  return loadShareCacheMap[pkg].getPath();
1671
1691
  }
1672
1692
  function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1673
- if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, isRolldown || command === "build" ? ".mjs" : ".js");
1674
- const useESM = command === "build" || isRolldown;
1693
+ if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, shouldUseEsmLoadShare(pkg, command, isRolldown) ? ".mjs" : ".js");
1694
+ const useESM = shouldUseEsmLoadShare(pkg, command, isRolldown);
1675
1695
  const importLine = command === "build" ? getRuntimeInitPromiseBootstrapCode() : useESM ? `${getRuntimeInitBootstrapCode()}
1676
1696
  const { initPromise } = globalThis[globalKey];` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
1677
1697
  const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
@@ -1705,6 +1725,7 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1705
1725
  const devImportSource = concreteSharedImportSource || pkg;
1706
1726
  const localProviderPath = getLocalProviderImportPath(pkg);
1707
1727
  const isWorkspacePackage = isWorkspaceFilePath(localProviderPath) || isWorkspaceFilePath(concreteSharedImportSource);
1728
+ const skipServePrebuildWarmup = command !== "build" && (pkg === "lit" || pkg.startsWith("lit/"));
1708
1729
  const providerImportId = localProviderPath || concreteSharedImportSource || sharedImportSource;
1709
1730
  const namedExports = getPackageNamedExports(pkg);
1710
1731
  let exportLine;
@@ -1713,8 +1734,8 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1713
1734
  const namedExportLine = `export { ${namedExports.map((name, i) => `__mf_${i} as ${name}`).join(", ")} };`;
1714
1735
  exportLine = useESM ? `export default exportModule.default ?? exportModule;\n ${destructure}\n ${namedExportLine}` : `module.exports = exportModule;\n ${destructure}\n Object.assign(module.exports, { ${namedExports.map((name, i) => `"${name}": __mf_${i}`).join(", ")} });`;
1715
1736
  } else exportLine = useESM ? `export default exportModule.default ?? exportModule\n export * from ${escapeGeneratedStringLiteral(sharedImportSource)}` : "module.exports = exportModule";
1716
- const prebuildImportLine = isWorkspacePackage && command !== "build" ? "" : `import ${escapeGeneratedStringLiteral(sharedImportSource)};`;
1717
- const devDynamicImportLine = isWorkspacePackage ? "" : command !== "build" ? `;() => import(${escapeGeneratedStringLiteral(devImportSource)}).catch(() => {});` : "";
1737
+ const prebuildImportLine = isWorkspacePackage && command !== "build" || skipServePrebuildWarmup ? "" : `import ${escapeGeneratedStringLiteral(sharedImportSource)};`;
1738
+ const devDynamicImportLine = isWorkspacePackage ? "" : command !== "build" && !skipServePrebuildWarmup ? `;() => import(${escapeGeneratedStringLiteral(devImportSource)}).catch(() => {});` : "";
1718
1739
  loadShareCacheMap[pkg].writeSync(`
1719
1740
  ${prebuildImportLine}
1720
1741
  ${devDynamicImportLine}
@@ -3049,6 +3070,7 @@ function pluginRemoteNamedExports(options) {
3049
3070
  name: "module-federation-remote-named-exports",
3050
3071
  enforce: "post",
3051
3072
  async transform(code, id) {
3073
+ if (!getIsRolldown(this)) return;
3052
3074
  if (remoteNames.length === 0) return;
3053
3075
  if (id.includes("__loadRemote__") || id.includes("__loadShare__")) return;
3054
3076
  if (!JS_EXTENSIONS_RE.test(id)) return;
@@ -3307,6 +3329,7 @@ function insertAfterLastTopLevelImport(code, snippet) {
3307
3329
  */
3308
3330
  function createEarlyVirtualModulesPlugin(options) {
3309
3331
  const { shared, remotes, virtualModuleDir } = options;
3332
+ const isLitShare = (pkg) => pkg === "lit" || pkg.startsWith("lit/");
3310
3333
  return {
3311
3334
  name: "vite:module-federation-early-init",
3312
3335
  enforce: "pre",
@@ -3342,8 +3365,13 @@ function createEarlyVirtualModulesPlugin(options) {
3342
3365
  if (shareItem.shareConfig?.import !== false) writePreBuildLibPath(key, shareItem);
3343
3366
  addUsedShares(key);
3344
3367
  if (_command === "serve" && shareItem.shareConfig?.import !== false) {
3345
- if (!isRolldown) config.optimizeDeps.include.push(getLoadShareImportId(key, isRolldown, _command));
3346
- config.optimizeDeps.include.push(getPreBuildLibImportId(key));
3368
+ const optimizeDeps = config.optimizeDeps ??= {};
3369
+ optimizeDeps.include ??= [];
3370
+ optimizeDeps.exclude ??= [];
3371
+ const shouldBypassOptimizeDep = isLitShare(key);
3372
+ if (shouldBypassOptimizeDep) optimizeDeps.exclude.push(key);
3373
+ if (!isRolldown && !shouldBypassOptimizeDep) optimizeDeps.include.push(getLoadShareImportId(key, isRolldown, _command));
3374
+ optimizeDeps.include.push(getPreBuildLibImportId(key));
3347
3375
  }
3348
3376
  }
3349
3377
  writeLocalSharedImportMap();
@@ -3361,6 +3389,7 @@ function federation(mfUserOptions) {
3361
3389
  const virtualExposesId = getVirtualExposesId(options);
3362
3390
  let command;
3363
3391
  let depsDir = "/node_modules/.vite/deps/";
3392
+ let desiredRolldownOutput;
3364
3393
  return [
3365
3394
  createEarlyVirtualModulesPlugin(options),
3366
3395
  ...isVinext ? [{
@@ -3451,12 +3480,22 @@ function federation(mfUserOptions) {
3451
3480
  };
3452
3481
  }
3453
3482
  let warnedAboutCodeSplitting = false;
3483
+ let warnedAboutCodeSplittingGroups = false;
3454
3484
  const ensureCodeSplitting = (output) => {
3455
- if (output?.codeSplitting !== false) return;
3456
- delete output.codeSplitting;
3457
- if (warnedAboutCodeSplitting) return;
3458
- warnedAboutCodeSplitting = true;
3459
- mfWarn("Ignoring `build.rolldownOptions.output.codeSplitting = false` because module federation requires chunk splitting.");
3485
+ if (output?.codeSplitting === false) {
3486
+ delete output.codeSplitting;
3487
+ if (warnedAboutCodeSplitting) return;
3488
+ warnedAboutCodeSplitting = true;
3489
+ mfWarn("Ignoring `output.codeSplitting = false` because module federation requires chunk splitting.");
3490
+ return;
3491
+ }
3492
+ if (!output?.codeSplitting || typeof output.codeSplitting !== "object") return;
3493
+ if (!("groups" in output.codeSplitting)) return;
3494
+ delete output.codeSplitting.groups;
3495
+ if (Object.keys(output.codeSplitting).length === 0) delete output.codeSplitting;
3496
+ if (warnedAboutCodeSplittingGroups) return;
3497
+ warnedAboutCodeSplittingGroups = true;
3498
+ mfWarn("Ignoring `output.codeSplitting.groups` because it conflicts with module federation. Grouping shared dependency init wrappers with their dependent modules can break runtime init order and cause standalone remotes to fail before mount.");
3460
3499
  };
3461
3500
  let warnedAboutManualChunks = false;
3462
3501
  const applyManualChunks = (output) => {
@@ -3464,7 +3503,7 @@ function federation(mfUserOptions) {
3464
3503
  const isPatchedByPlugin = !!(output.manualChunks && patchedManualChunks.has(output.manualChunks));
3465
3504
  if (output.manualChunks && !isPatchedByPlugin && !warnedAboutManualChunks) {
3466
3505
  warnedAboutManualChunks = true;
3467
- 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.");
3506
+ mfWarn("Ignoring `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.");
3468
3507
  }
3469
3508
  const mfManualChunks = function(id) {
3470
3509
  if (id.includes(runtimeInitId)) return "runtimeInit";
@@ -3477,10 +3516,46 @@ function federation(mfUserOptions) {
3477
3516
  output.manualChunks = mfManualChunks;
3478
3517
  };
3479
3518
  config.build.rollupOptions = config.build.rollupOptions || {};
3480
- if (!Array.isArray(config.build.rollupOptions.output)) applyManualChunks(config.build.rollupOptions.output ||= {});
3519
+ const rollupOutput = config.build.rollupOptions.output;
3520
+ if (Array.isArray(rollupOutput)) rollupOutput.forEach((output) => applyManualChunks(output));
3521
+ else applyManualChunks(config.build.rollupOptions.output ||= {});
3481
3522
  const buildWithRolldown = config.build;
3482
3523
  buildWithRolldown.rolldownOptions = buildWithRolldown.rolldownOptions || {};
3483
- if (!Array.isArray(buildWithRolldown.rolldownOptions.output)) applyManualChunks(buildWithRolldown.rolldownOptions.output ||= {});
3524
+ const rolldownOutput = buildWithRolldown.rolldownOptions.output;
3525
+ const snapshotRolldownOutput = (output) => ({
3526
+ entryFileNames: output.entryFileNames,
3527
+ chunkFileNames: output.chunkFileNames,
3528
+ assetFileNames: output.assetFileNames
3529
+ });
3530
+ if (Array.isArray(rolldownOutput)) {
3531
+ rolldownOutput.forEach((output) => applyManualChunks(output));
3532
+ desiredRolldownOutput = rolldownOutput.map((output) => snapshotRolldownOutput(output));
3533
+ } else {
3534
+ applyManualChunks(buildWithRolldown.rolldownOptions.output ||= {});
3535
+ desiredRolldownOutput = [snapshotRolldownOutput(buildWithRolldown.rolldownOptions.output)];
3536
+ }
3537
+ },
3538
+ async buildApp(builder) {
3539
+ if (!desiredRolldownOutput) return;
3540
+ const applyRolldownOutput = (output, restoredOutput) => {
3541
+ if (!output || !restoredOutput) return;
3542
+ if (restoredOutput.entryFileNames !== void 0) output.entryFileNames = restoredOutput.entryFileNames;
3543
+ if (restoredOutput.chunkFileNames !== void 0) output.chunkFileNames = restoredOutput.chunkFileNames;
3544
+ if (restoredOutput.assetFileNames !== void 0) output.assetFileNames = restoredOutput.assetFileNames;
3545
+ };
3546
+ for (const environment of Object.values(builder.environments)) {
3547
+ const getRolldownOptions = environment?.getRolldownOptions;
3548
+ if (typeof getRolldownOptions !== "function") continue;
3549
+ environment.getRolldownOptions = async () => {
3550
+ const rolldownOptions = await getRolldownOptions.call(environment);
3551
+ if (Array.isArray(rolldownOptions.output)) rolldownOptions.output.forEach((output, index) => applyRolldownOutput(output, desiredRolldownOutput?.[index]));
3552
+ else {
3553
+ rolldownOptions.output ||= {};
3554
+ applyRolldownOutput(rolldownOptions.output, desiredRolldownOutput[0]);
3555
+ }
3556
+ return rolldownOptions;
3557
+ };
3558
+ }
3484
3559
  },
3485
3560
  load(id) {
3486
3561
  if (id.startsWith("\0")) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/vite",
3
- "version": "1.14.3",
3
+ "version": "1.14.4",
4
4
  "description": "Vite plugin for Module Federation",
5
5
  "type": "module",
6
6
  "main": "./lib/index.cjs",
@@ -70,9 +70,9 @@
70
70
  "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
71
71
  },
72
72
  "dependencies": {
73
- "@module-federation/dts-plugin": "2.3.2",
74
- "@module-federation/runtime": "2.3.2",
75
- "@module-federation/sdk": "2.3.2",
73
+ "@module-federation/dts-plugin": "2.3.3",
74
+ "@module-federation/runtime": "2.3.3",
75
+ "@module-federation/sdk": "2.3.3",
76
76
  "@rollup/pluginutils": "^5.3.0",
77
77
  "defu": "^6.1.4",
78
78
  "es-module-lexer": "^2.0.0",