@module-federation/vite 1.20.8 → 1.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +19 -10
  2. package/lib/index.js +108 -45
  3. package/package.json +5 -5
package/README.md CHANGED
@@ -351,20 +351,29 @@ federation({
351
351
  `provideExternalRuntime` injects a local runtime plugin that publishes `runtime-core` on `globalThis._FEDERATION_RUNTIME_CORE`. `externalRuntime` rewrites imports of `@module-federation/runtime-core` to read that global. Using `provideExternalRuntime` together with `exposes` throws — only pure consumers may provide the runtime.
352
352
  The `externalRuntime` rewrite applies to the browser remote graph; SSR remote entries continue to resolve `@module-federation/runtime-core` from Node so they do not depend on the browser global.
353
353
 
354
- ## ⚠️ `codeSplitting` settings are controlled by the plugin
354
+ ## ⚠️ `codeSplitting` is managed by the plugin
355
355
 
356
- Do not set either `build.rollupOptions.output.codeSplitting` or
357
- `build.rolldownOptions.output.codeSplitting` to `false` with this plugin — it will be **automatically ignored**.
356
+ Do not set `build.rollupOptions.output.codeSplitting` or
357
+ `build.rolldownOptions.output.codeSplitting` to `false` — it will be **ignored** (with a warning).
358
+ Module Federation requires chunk splitting so `loadShare` and `runtimeInitStatus` stay isolated for correct bootstrap order.
358
359
 
359
- `codeSplitting.groups` is also ignored because grouping shared-runtime chunks can break MF init order.
360
- Module Federation needs `loadShare` and `runtimeInitStatus` isolated into separate chunks for correct bootstrap behavior.
360
+ ### `codeSplitting.groups` (Vite 8+ / Rolldown)
361
361
 
362
- ## ⚠️ `manualChunks` is not supported
362
+ User groups are now **preserved**. The plugin installs its own federation groups at the highest priority and appends your groups below them, so your groups can only claim modules the federation groups didn't.
363
363
 
364
- Do not use `build.rollupOptions.output.manualChunks` or
365
- `build.rolldownOptions.output.manualChunks` with this plugin it will be **automatically ignored**.
366
- The plugin manages the runtime chunk graph itself, and forcing custom chunk grouping can break Module Federation bootstrap order.
367
- The plugin injects the splits it needs so `runtimeInitStatus` and `loadShare` stay isolated.
364
+ - No warning is emitted just for keeping your groups.
365
+ - If one of your existing groups sets a `priority` high enough to outrank the federation groups, it is **clamped** below them and the plugin warns once. This prevents those groups from capturing a `runtimeInit`/`loadShare` wrapper or the preload helper.
366
+
367
+ ## ⚠️ `manualChunks` behavior depends on your Vite version
368
+
369
+ | Setting | Vite 5–7 (Rollup) | Vite 8+ (Rolldown) |
370
+ | --- | --- | --- |
371
+ | `manualChunks` (function) | **Composed as a fallback** — federation modules are claimed first, everything else falls through to your function | **Ignored** (warns) — move grouping to `codeSplitting.groups` |
372
+ | `manualChunks` (object) | **Ignored** (warns) — use the function form to compose | **Ignored** (warns) — move grouping to `codeSplitting.groups` |
373
+
374
+ On Vite 5–7, Rollup doesn't support `codeSplitting`, so the plugin isolates `runtimeInitStatus`, `loadShare`, and the preload helper via `manualChunks`. A user-provided **function** is called for any module the plugin doesn't claim; the **object** form isn't composed by the plugin and is ignored.
375
+
376
+ On Vite 8+, chunking is managed through `codeSplitting.groups` (see above), so `manualChunks` is removed — express your grouping as `codeSplitting.groups` instead, where user groups are preserved below the federation groups.
368
377
 
369
378
  ### So far so good 🎉
370
379
 
package/lib/index.js CHANGED
@@ -5378,7 +5378,7 @@ function generateRemotes(id, command, enableSsrInit = false, consumer = "unified
5378
5378
  }`;
5379
5379
  const realRemoteInit = `__mfRemotePending = __mfStartRemoteLoad().then(__mfAssignRemoteModule);`;
5380
5380
  const deferredClientInit = `exportModule = __mfCreateDeferredRemoteProxy();`;
5381
- const eagerLoadClientRemote = shouldEagerLoadClientRemoteInDev(command, enableSsrInit);
5381
+ const eagerLoadClientRemote = id === remoteRegistration?.alias || shouldEagerLoadClientRemoteInDev(command, enableSsrInit);
5382
5382
  const eagerClientInit = eagerLoadClientRemote ? getEagerDeferredClientInit() : deferredClientInit;
5383
5383
  const loadedFirstClientInit = eagerLoadClientRemote ? getEagerDeferredClientInit() : deferredClientInit;
5384
5384
  const environmentSplitInit = (clientInit, serverInit) => consumer === "client" ? clientInit : consumer === "server" ? serverInit : `if (${SERVER_ENV_GUARD}) {
@@ -5515,20 +5515,25 @@ function resolveDevHashEntryFileName$1(fileName) {
5515
5515
  function getBuildInput(config) {
5516
5516
  return config.build?.rollupOptions?.input ?? config.build?.rolldownOptions?.input;
5517
5517
  }
5518
- function patchHashEntryFileName(output, entryName, fileName) {
5519
- const originalEntryFileNames = output.entryFileNames;
5520
- output.entryFileNames = (chunkInfo, ...args) => {
5521
- if (chunkInfo?.name === entryName) return fileName;
5522
- if (typeof originalEntryFileNames === "function") return originalEntryFileNames(chunkInfo, ...args);
5523
- return originalEntryFileNames || "assets/[name]-[hash].js";
5524
- };
5518
+ function patchHashEntryFileName(output, entryName, fileName, defaultFileNames) {
5519
+ for (const option of ["entryFileNames", "chunkFileNames"]) {
5520
+ const originalFileNames = output[option];
5521
+ output[option] = (chunkInfo, ...args) => {
5522
+ if (chunkInfo?.name === entryName) return fileName;
5523
+ if (typeof originalFileNames === "function") return originalFileNames(chunkInfo, ...args);
5524
+ return originalFileNames || defaultFileNames;
5525
+ };
5526
+ }
5525
5527
  }
5526
5528
  function patchHashEntryFileNames(config, entryName, fileName) {
5527
5529
  if (!fileName?.includes?.("[hash")) return;
5530
+ fileName = fileName.replace(/(\[hash(?::\d+)?\])$/, "$1.js");
5528
5531
  config.build ??= {};
5529
5532
  config.build.rollupOptions ??= {};
5530
5533
  config.build.rolldownOptions ??= {};
5531
- const patchOutput = (output) => patchHashEntryFileName(output, entryName, fileName);
5534
+ const assetsDir = config.build.assetsDir ?? "assets";
5535
+ const defaultFileNames = `${assetsDir ? `${assetsDir}/` : ""}[name]-[hash].js`;
5536
+ const patchOutput = (output) => patchHashEntryFileName(output, entryName, fileName, defaultFileNames);
5532
5537
  const patchBundlerOutput = (bundlerOptions) => {
5533
5538
  const output = bundlerOptions.output;
5534
5539
  if (Array.isArray(output)) {
@@ -5539,6 +5544,7 @@ function patchHashEntryFileNames(config, entryName, fileName) {
5539
5544
  };
5540
5545
  patchBundlerOutput(config.build.rollupOptions);
5541
5546
  patchBundlerOutput(config.build.rolldownOptions);
5547
+ Object.values(config.environments ?? {}).forEach((environment) => patchHashEntryFileNames(environment, entryName, fileName));
5542
5548
  }
5543
5549
  const addEntry = ({ entryName, entryPath, fileName, inject = "entry", forceClientInjected, skipTransformFor = [], federationOptions }) => {
5544
5550
  const DEV_HTML_PROXY_PREFIX = "virtual:mf-html-entry-proxy?";
@@ -6099,6 +6105,7 @@ const REACT_REFRESH_PROXY_MODULE = [
6099
6105
  `const __rt = await import(__target);`,
6100
6106
  `export const injectIntoGlobalHook = __rt.injectIntoGlobalHook;`,
6101
6107
  `export const register = __rt.register;`,
6108
+ `export const getRefreshReg = __rt.getRefreshReg;`,
6102
6109
  `export const createSignatureFunctionForTransform = __rt.createSignatureFunctionForTransform;`,
6103
6110
  `export const registerExportsForReactRefresh = __rt.registerExportsForReactRefresh;`,
6104
6111
  `export const validateRefreshBoundaryAndEnqueueUpdate = __rt.validateRefreshBoundaryAndEnqueueUpdate;`,
@@ -7742,12 +7749,18 @@ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposes
7742
7749
  return Object.keys(options.remotes).some((name) => source === name || source.startsWith(name + "/"));
7743
7750
  }
7744
7751
  function collectImportSources(code) {
7745
- const sources = /* @__PURE__ */ new Set();
7752
+ const sources = /* @__PURE__ */ new Map();
7746
7753
  for (const match of code.matchAll(/(?:^|[;\n\r])\s*import\s+(?:[\s\S]*?\s+from\s+)?["']([^"']+)["']|import\(\s*["']([^"']+)["']\s*\)/g)) {
7747
7754
  const source = match[1] || match[2];
7748
- if (source) sources.add(source);
7755
+ if (source) {
7756
+ const dynamic = !match[1];
7757
+ sources.set(source, (sources.get(source) ?? true) && dynamic);
7758
+ }
7749
7759
  }
7750
- return Array.from(sources).sort();
7760
+ return Array.from(sources, ([source, dynamic]) => ({
7761
+ source,
7762
+ dynamic
7763
+ })).sort((a, b) => a.source.localeCompare(b.source));
7751
7764
  }
7752
7765
  function shouldScanResolvedImport(id) {
7753
7766
  if (!id || id.includes("\0")) return false;
@@ -7764,9 +7777,9 @@ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposes
7764
7777
  return [];
7765
7778
  }
7766
7779
  const dependencies = /* @__PURE__ */ new Set();
7767
- for (const source of collectImportSources(code)) {
7780
+ for (const { source, dynamic } of collectImportSources(code)) {
7768
7781
  if (isRemoteImport(source)) {
7769
- dependencies.add(source);
7782
+ if (!dynamic) dependencies.add(source);
7770
7783
  continue;
7771
7784
  }
7772
7785
  const resolved = await ctx.resolve(source, id);
@@ -8430,8 +8443,10 @@ function applyRewrites(code, imports, id) {
8430
8443
  importParts.push(`__mf_remote_pending as ${pendingId}`);
8431
8444
  let rewrite = `import { ${importParts.join(", ")} } from ${src};`;
8432
8445
  if (imp.named.length > 0) {
8433
- const destructParts = imp.named.map((s) => `${s.imported}: ${s.local}`);
8434
- rewrite += `\nconst { ${destructParts.join(", ")} } = ${nsId};`;
8446
+ const declarations = imp.named.map((s) => `let ${s.local};`).join("\n");
8447
+ const initializers = imp.named.map((s) => `if (${JSON.stringify(s.imported)} in ${nsId}) {\n${s.local} = ${nsId}[${JSON.stringify(s.imported)}];\n}`).join("\n");
8448
+ const assignments = imp.named.map((s) => `${s.local} = ${nsId}[${JSON.stringify(s.imported)}];`).join("\n");
8449
+ rewrite += `\n${declarations}\n${initializers}\n${pendingId}.then(() => {\n${assignments}\n});`;
8435
8450
  }
8436
8451
  ms.overwrite(imp.start, imp.end, rewrite);
8437
8452
  }
@@ -8451,10 +8466,11 @@ function applyRewrites(code, imports, id) {
8451
8466
  };
8452
8467
  });
8453
8468
  const importLine = `import { __moduleExports as ${nsId}, __mf_remote_pending as ${pendingId} } from ${src};`;
8454
- const varLines = vars.map((v) => `let ${v.tmp} = ${nsId}[${JSON.stringify(v.local)}];`).join("\n");
8469
+ const varLines = vars.map((v) => `let ${v.tmp};`).join("\n");
8470
+ const initializers = vars.map((v) => `if (${JSON.stringify(v.local)} in ${nsId}) {\n${v.tmp} = ${nsId}[${JSON.stringify(v.local)}];\n}`).join("\n");
8455
8471
  const syncLine = `${pendingId}.then(() => {\n${vars.map((v) => `${v.tmp} = ${nsId}[${JSON.stringify(v.local)}];`).join("\n")}\n});`;
8456
8472
  const exportLine = `export { ${vars.map((v) => `${v.tmp} as ${v.exported}`).join(", ")} };`;
8457
- ms.overwrite(imp.start, imp.end, `${importLine}\n${varLines}\n${syncLine}\n${exportLine}`);
8473
+ ms.overwrite(imp.start, imp.end, `${importLine}\n${varLines}\n${initializers}\n${syncLine}\n${exportLine}`);
8458
8474
  changed = true;
8459
8475
  break;
8460
8476
  }
@@ -9264,8 +9280,11 @@ function getRuntimeCapabilityConfigurationWarnings(options) {
9264
9280
  //#endregion
9265
9281
  //#region src/index.ts
9266
9282
  const patchedManualChunks = /* @__PURE__ */ new WeakSet();
9283
+ const federationGroups = /* @__PURE__ */ new WeakSet();
9267
9284
  const PRELOAD_HELPER_CHUNK = "vite-preload-helper";
9268
9285
  const PRELOAD_HELPER_TEST = /\0?vite\/preload-helper/;
9286
+ const MF_GROUP_PRIORITY = 1e6;
9287
+ const USER_GROUP_MAX_PRIORITY = MF_GROUP_PRIORITY - 1;
9269
9288
  function normalizeVinextRscPreloadHints(code) {
9270
9289
  return code.replace(/(:HL\[[^\]\n]*?,)"stylesheet"/g, "$1\"style\"").replace(/(:HL\[[^\]\n]*?,)\\"stylesheet\\"/g, "$1\\\"style\\\"");
9271
9290
  }
@@ -9422,6 +9441,14 @@ function includeLinkedSharedEntries(optimizeDeps, shared, projectRoot, exposes,
9422
9441
  ]);
9423
9442
  for (const [packageName, share] of Object.entries(shared ?? {})) {
9424
9443
  if (share?.shareConfig?.import === false) continue;
9444
+ const configuredImport = share?.shareConfig?.import;
9445
+ if (typeof configuredImport === "string") {
9446
+ const entry = path$1.isAbsolute(configuredImport) ? configuredImport : path$1.resolve(projectRoot, configuredImport);
9447
+ if (existsSync(entry) && !entry.replaceAll("\\", "/").includes("/node_modules/")) {
9448
+ additions.add(entry);
9449
+ continue;
9450
+ }
9451
+ }
9425
9452
  const installed = getInstalledPackageJson(packageName, { cwd: projectRoot });
9426
9453
  if (!installed || installed.dir.replaceAll("\\", "/").includes("/node_modules/")) continue;
9427
9454
  const entry = getInstalledPackageEntry(packageName, { cwd: projectRoot });
@@ -9604,9 +9631,20 @@ function createEarlyVirtualModulesPlugin(options) {
9604
9631
  if (args.kind === "entry-point") return;
9605
9632
  if (!args.importer || args.namespace === "mf-shared") return;
9606
9633
  if (isSharedResolverInternalImporter(args.importer)) return;
9607
- if (!findSharedKey(args.path, shared) || isAssetLikeImport(args.path)) return;
9634
+ const key = findSharedKey(args.path, shared);
9635
+ if (!key || isAssetLikeImport(args.path)) return;
9608
9636
  if (getPackageNameFromNodeModulePath(args.importer) === getPackageName(args.path) && !isReactDomSelfReference(args.path, args.importer)) return;
9609
9637
  addUsedShares(args.path, options);
9638
+ if (args.kind === "import-statement" || args.kind === "dynamic-import") {
9639
+ const shareItem = shared[key];
9640
+ const loadSharePath = getLoadShareModulePath(args.path, isRolldown, options);
9641
+ writeLoadShareModule(args.path, shareItem, _command, isRolldown, options);
9642
+ if (shareItem.shareConfig?.import !== false) writePreBuildLibPath(args.path, shareItem, options);
9643
+ return {
9644
+ path: loadSharePath,
9645
+ external: true
9646
+ };
9647
+ }
9610
9648
  return {
9611
9649
  path: args.path,
9612
9650
  namespace: "mf-shared"
@@ -9663,7 +9701,7 @@ export default __mfShared.default ?? __mfShared;`
9663
9701
  optimizeDeps.exclude ??= [];
9664
9702
  const shouldBypassOptimizeDep = isLitShare(key) || !canResolveSharedSubpath(key, root);
9665
9703
  if (optimizeDeps.include.includes(key)) optimizeDeps.exclude = optimizeDeps.exclude.filter((dep) => dep !== key);
9666
- else if (shouldBypassOptimizeDep) optimizeDeps.exclude.push(key);
9704
+ else if (shouldBypassOptimizeDep || optimizeDeps.exclude.includes(key)) optimizeDeps.exclude.push(key);
9667
9705
  else optimizeDeps.include.push(key);
9668
9706
  for (const subpath of getCommonSharedSubpaths(key)) {
9669
9707
  const canResolveSubpath = canResolveSharedSubpath(subpath, root);
@@ -9990,33 +10028,33 @@ function federation(mfUserOptions) {
9990
10028
  };
9991
10029
  }
9992
10030
  let warnedAboutCodeSplitting = false;
9993
- let warnedAboutCodeSplittingGroups = false;
9994
10031
  const ensureCodeSplitting = (output) => {
9995
- if (output?.codeSplitting === false) {
9996
- delete output.codeSplitting;
9997
- if (warnedAboutCodeSplitting) return;
9998
- warnedAboutCodeSplitting = true;
9999
- mfWarn("Ignoring `output.codeSplitting = false` because module federation requires chunk splitting.");
10000
- return;
10032
+ if (output?.codeSplitting !== false) return;
10033
+ delete output.codeSplitting;
10034
+ if (warnedAboutCodeSplitting) return;
10035
+ warnedAboutCodeSplitting = true;
10036
+ mfWarn("Ignoring `output.codeSplitting = false` because module federation requires chunk splitting.");
10037
+ };
10038
+ const isFederationGroup = (group) => typeof group === "object" && group !== null && federationGroups.has(group);
10039
+ let warnedAboutGroupPriority = false;
10040
+ const clampUserGroup = (group) => {
10041
+ const candidate = group;
10042
+ if (typeof candidate?.priority !== "number") return group;
10043
+ if (candidate.priority <= USER_GROUP_MAX_PRIORITY) return group;
10044
+ if (!warnedAboutGroupPriority) {
10045
+ warnedAboutGroupPriority = true;
10046
+ mfWarn(`Clamping \`output.codeSplitting.groups\` priority to ${USER_GROUP_MAX_PRIORITY} — module federation groups must keep the highest priority so shared dependency init wrappers stay isolated in their own chunks.`);
10001
10047
  }
10002
- if (!output?.codeSplitting || typeof output.codeSplitting !== "object") return;
10003
- if (!("groups" in output.codeSplitting)) return;
10004
- const groups = output.codeSplitting.groups;
10005
- if (Array.isArray(groups) && groups.some((group) => typeof group?.name === "function" && patchedManualChunks.has(group.name))) return;
10006
- delete output.codeSplitting.groups;
10007
- if (Object.keys(output.codeSplitting).length === 0) delete output.codeSplitting;
10008
- if (warnedAboutCodeSplittingGroups) return;
10009
- warnedAboutCodeSplittingGroups = true;
10010
- 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.");
10048
+ return {
10049
+ ...candidate,
10050
+ priority: USER_GROUP_MAX_PRIORITY
10051
+ };
10011
10052
  };
10012
10053
  let warnedAboutManualChunks = false;
10054
+ let warnedAboutObjectManualChunks = false;
10013
10055
  const applyManualChunks = (output, useCodeSplitting) => {
10014
10056
  ensureCodeSplitting(output);
10015
10057
  const isPatchedByPlugin = typeof output.manualChunks === "function" && patchedManualChunks.has(output.manualChunks);
10016
- if (output.manualChunks && !isPatchedByPlugin && !warnedAboutManualChunks) {
10017
- warnedAboutManualChunks = true;
10018
- mfWarn("Ignoring `output.manualChunks` because it conflicts with module federation. Module federation transforms shared dependency imports with async init wrappers, and grouping these transformed modules into a single chunk creates circular async dependencies that cause the application to silently hang.");
10019
- }
10020
10058
  const mfChunkName = function(id) {
10021
10059
  if (id.includes(runtimeInitId) || id.includes("__mf_v__runtimeInit__mf_v__")) return "runtimeInit";
10022
10060
  if (id.includes("__loadShare__")) {
@@ -10027,19 +10065,44 @@ function federation(mfUserOptions) {
10027
10065
  };
10028
10066
  patchedManualChunks.add(mfChunkName);
10029
10067
  if (!useCodeSplitting) {
10030
- const mfManualChunks = function(id) {
10068
+ if (isPatchedByPlugin) return;
10069
+ const userManualChunks = output.manualChunks;
10070
+ if (userManualChunks && typeof userManualChunks !== "function" && !warnedAboutObjectManualChunks) {
10071
+ warnedAboutObjectManualChunks = true;
10072
+ mfWarn("Ignoring the object form of `output.manualChunks` because module federation cannot safely compose with it. Use the function form instead: federation modules are claimed first and your function runs for everything else.");
10073
+ }
10074
+ const mfManualChunks = function(id, ...rest) {
10031
10075
  if (PRELOAD_HELPER_TEST.test(id)) return PRELOAD_HELPER_CHUNK;
10032
- return mfChunkName(id) ?? void 0;
10076
+ const mfChunk = mfChunkName(id);
10077
+ if (mfChunk) return mfChunk;
10078
+ if (typeof userManualChunks === "function") return userManualChunks(id, ...rest) ?? void 0;
10033
10079
  };
10034
10080
  patchedManualChunks.add(mfManualChunks);
10035
10081
  output.manualChunks = mfManualChunks;
10036
10082
  return;
10037
10083
  }
10038
- const groups = [{
10084
+ if (output.manualChunks && !isPatchedByPlugin && !warnedAboutManualChunks) {
10085
+ warnedAboutManualChunks = true;
10086
+ mfWarn("Ignoring `output.manualChunks` for the Rolldown build because module federation manages chunking with `output.codeSplitting.groups`. Move your grouping there — user groups are kept below the federation groups.");
10087
+ }
10088
+ const existingGroups = output.codeSplitting && typeof output.codeSplitting === "object" ? output.codeSplitting.groups : void 0;
10089
+ const userGroups = Array.isArray(existingGroups) ? existingGroups.filter((group) => !isFederationGroup(group)).map(clampUserGroup) : [];
10090
+ const mfPreloadGroup = {
10039
10091
  name: PRELOAD_HELPER_CHUNK,
10040
10092
  test: PRELOAD_HELPER_TEST,
10041
- priority: 100
10042
- }, { name: mfChunkName }];
10093
+ priority: 1000001
10094
+ };
10095
+ const mfNameGroup = {
10096
+ name: mfChunkName,
10097
+ priority: MF_GROUP_PRIORITY
10098
+ };
10099
+ federationGroups.add(mfPreloadGroup);
10100
+ federationGroups.add(mfNameGroup);
10101
+ const groups = [
10102
+ mfPreloadGroup,
10103
+ mfNameGroup,
10104
+ ...userGroups
10105
+ ];
10043
10106
  output.codeSplitting = {
10044
10107
  ...output.codeSplitting || {},
10045
10108
  groups
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/vite",
3
- "version": "1.20.8",
3
+ "version": "1.21.0",
4
4
  "description": "Vite plugin for Module Federation",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -77,9 +77,9 @@
77
77
  "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
78
78
  },
79
79
  "dependencies": {
80
- "@module-federation/dts-plugin": "2.8.2",
81
- "@module-federation/runtime": "2.8.2",
82
- "@module-federation/sdk": "2.8.2"
80
+ "@module-federation/dts-plugin": "2.9.0",
81
+ "@module-federation/runtime": "2.9.0",
82
+ "@module-federation/sdk": "2.9.0"
83
83
  },
84
84
  "devDependencies": {
85
85
  "@playwright/test": "1.62.0",
@@ -93,4 +93,4 @@
93
93
  "vite": "8.2.0",
94
94
  "vitest": "4.1.10"
95
95
  }
96
- }
96
+ }