@module-federation/vite 1.22.1 → 1.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -119,6 +119,9 @@ export default defineConfig({
119
119
  // optional: additional "var" remoteEntry file
120
120
  // needed only for legacy hosts with "var" usage (remote.type = 'var')
121
121
  varFilename: "varRemoteEntry.js",
122
+ // optional: exact name of the SSR remote entry
123
+ // default: "<filename base>.ssr.js" (e.g. remoteEntry.ssr.js)
124
+ ssrFilename: "remoteEntry.ssr.js",
122
125
  exposes: {
123
126
  "./remote-app": "./src/App.vue",
124
127
  },
@@ -151,6 +154,18 @@ shared: {
151
154
 
152
155
  `suppressMissingImportWarning` defaults to `false`. Enable it only when the host is guaranteed to provide the dependency and local named-export detection is unnecessary.
153
156
 
157
+ In a production build, a host-only dependency that the exposed modules reach only through a dynamic `import()` is not fetched while the remote initializes. It is loaded from the host's provider when that `import()` runs, before the imported chunk evaluates. A dependency imported statically by an exposed module, or configured with `eager: true`, is still loaded during initialization.
158
+
159
+ ### Shared dependencies installed in several places
160
+
161
+ An absolute import that resolves inside a different `node_modules` directory than the one the share itself resolves to, for example a workspace package with its own nested copy, is not treated as the shared module by default. Set `allowNodeModulesSuffixMatch: true` on the share to match it by the path after `node_modules/`, as webpack and rspack do with the same option:
162
+
163
+ ```ts
164
+ shared: {
165
+ react: { singleton: true, allowNodeModulesSuffixMatch: true },
166
+ },
167
+ ```
168
+
154
169
  ## The Host Application configuration
155
170
 
156
171
  file **host/vite.config.ts**
@@ -263,6 +278,8 @@ federation({
263
278
 
264
279
  The `"vm"` strategy requires Node.js to run with `--experimental-vm-modules`. When unavailable, the loader warns once and falls back to `"temp-file"`. Vite 8 development uses `ModuleRunner`; this option primarily affects build and preview SSR entry loading.
265
280
 
281
+ The SSR remote entry is emitted as `<filename base>.ssr.js` next to the browser entry, or under the exact name given by `ssrFilename`, and is advertised as `metaData.ssrRemoteEntry` in `mf-manifest.json`. It keeps `@module-federation/runtime`, `@module-federation/runtime-core` and `@module-federation/sdk` external by default so Node resolves them from the host. Hosts that cannot resolve those packages, such as a plain `@module-federation/enhanced` host using the SDK's reference Node loader, need a self-contained entry: opt the packages back in with Vite's `ssr.noExternal` (for example `ssr: { noExternal: true }`) on the remote.
282
+
266
283
  ## Runtime capability optimization
267
284
 
268
285
  Runtime features that a build never uses can be removed at build time:
@@ -391,6 +408,8 @@ Do not set `build.rollupOptions.output.codeSplitting` or
391
408
  `build.rolldownOptions.output.codeSplitting` to `false` — it will be **ignored** (with a warning).
392
409
  Module Federation requires chunk splitting so `runtimeInitStatus` and deferred `loadShare` wrappers stay isolated for correct bootstrap order. Eager `loadShare` wrappers are coalesced into one `loadShare-eager` chunk to reduce startup requests, on both Rolldown (Vite 8+) and Rollup (Vite 5–7).
393
410
 
411
+ Non-eager `loadShare` wrappers that reach their local `__prebuild__` fallback only through `import()` are merged into one `__loadShare__shared` chunk per `federation()` instance, with the shared-cache helpers included once. A wrapper that statically imports its local payload keeps its own chunk, since merging it could close a top-level-await cycle. `__prebuild__` fallbacks stay in separate lazily imported chunks, so a consumer never downloads its local copy when a peer already provides the share. The plugin warns at build time if a fallback ends up inside the merged chunk.
412
+
394
413
  ### `codeSplitting.groups` (Vite 8+ / Rolldown)
395
414
 
396
415
  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.
@@ -1,5 +1,5 @@
1
- import { l as getNodeModulesSuffix } from "./sharedKeyMatcher-BRzBCbuR.js";
2
- import { existsSync, readFileSync, readdirSync } from "fs";
1
+ import { d as getNodeModulesSuffix, r as getSharedRuntimeKey } from "./sharedKeyMatcher-Od_nYxJA.js";
2
+ import { existsSync, readFileSync, readdirSync, realpathSync } from "fs";
3
3
  import { createRequire } from "module";
4
4
  import * as path$1 from "node:path";
5
5
  import { fileURLToPath, pathToFileURL } from "url";
@@ -35,6 +35,29 @@ const moduleFederationConsole = {
35
35
  const mfWarn = moduleFederationConsole.warn;
36
36
  const mfError = moduleFederationConsole.error;
37
37
  //#endregion
38
+ //#region src/utils/reactShares.ts
39
+ /** Property under which React 19 exposes its internals object. */
40
+ const REACT_CLIENT_INTERNALS_KEY = "__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE";
41
+ /**
42
+ * Every property React has used for its internals object, newest first. Two
43
+ * module objects that expose the same internals are the same React instance.
44
+ */
45
+ const REACT_INTERNALS_KEYS = [
46
+ REACT_CLIENT_INTERNALS_KEY,
47
+ "__TEST_INTERNALS",
48
+ "__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED"
49
+ ];
50
+ /**
51
+ * Importing this share's local fallback registers a DOM renderer as a side
52
+ * effect. An entry-injected leaf must not evaluate its own copy while the host
53
+ * renderer already owns the root, so its wrapper waits for the cache or init.
54
+ */
55
+ const REACT_DOM_CLIENT_SHARE = "react-dom/client";
56
+ /** React and its subpath exports must bind synchronously for CJS consumers. */
57
+ function isReactCoreShare(pkg) {
58
+ return pkg === "react" || pkg.startsWith("react/");
59
+ }
60
+ //#endregion
38
61
  //#region src/utils/packageUtils.ts
39
62
  const dependencyPresenceCache = /* @__PURE__ */ new Map();
40
63
  let packageDetectionCwd;
@@ -42,6 +65,7 @@ function getDependencyCacheKey(cwd, dependencyName) {
42
65
  return `${cwd}:${dependencyName}`;
43
66
  }
44
67
  const installedPackageJsonCache = /* @__PURE__ */ new Map();
68
+ const dependentsLookupCache = /* @__PURE__ */ new Map();
45
69
  function setPackageDetectionCwd(cwd) {
46
70
  packageDetectionCwd = cwd;
47
71
  }
@@ -213,7 +237,7 @@ function getSharedCacheKeyParts(input) {
213
237
  }
214
238
  function getSharedCacheDescriptor(pkg, shareItem) {
215
239
  const parts = getSharedCacheKeyParts({
216
- pkg,
240
+ pkg: shareItem.shareConfig.request !== void 0 || shareItem.shareConfig.shareKey !== void 0 ? getSharedRuntimeKey(pkg, shareItem) : pkg,
217
241
  singleton: shareItem.shareConfig.singleton,
218
242
  version: shareItem.version,
219
243
  scope: shareItem.scope
@@ -279,7 +303,47 @@ const sharedCacheHelperCode = `const __mfGetSharedCacheDescriptor = (pkg, single
279
303
  };
280
304
  const __mfReadSharedCacheOwner = (cache, descriptor) =>
281
305
  cache[__mfSharedCacheOwnersKey]?.[descriptor.canonical];
306
+ const __mfGetSharedModuleIdentity = (value) => {
307
+ const candidates = [value, value?.default];
308
+ for (const candidate of candidates) {
309
+ if (!candidate || typeof candidate !== "object") continue;
310
+ for (const key of ${JSON.stringify(REACT_INTERNALS_KEYS)}) {
311
+ const internals = candidate[key];
312
+ if (internals && typeof internals === "object") {
313
+ return { internals };
314
+ }
315
+ }
316
+ if (typeof candidate.useState === "function") return { hook: candidate.useState };
317
+ }
318
+ return undefined;
319
+ };
320
+ const __mfIsSameSharedModule = (current, next) => {
321
+ if (current === next) return true;
322
+ const currentIdentity = __mfGetSharedModuleIdentity(current);
323
+ const nextIdentity = __mfGetSharedModuleIdentity(next);
324
+ if (!currentIdentity || !nextIdentity) return false;
325
+ const currentInternals = currentIdentity.internals;
326
+ const nextInternals = nextIdentity.internals;
327
+ // Internals identity is authoritative when either side exposes it.
328
+ // Never treat absent fields as equal (undefined === undefined).
329
+ if (currentInternals !== undefined || nextInternals !== undefined) {
330
+ return (
331
+ currentInternals !== undefined &&
332
+ currentInternals === nextInternals
333
+ );
334
+ }
335
+ // Hook identity is only a fallback when neither side exposes internals.
336
+ return (
337
+ typeof currentIdentity.hook === "function" &&
338
+ currentIdentity.hook === nextIdentity.hook
339
+ );
340
+ };
282
341
  const __mfWriteSharedCache = (cache, descriptor, value, owner) => {
342
+ // Rewriting the same instance is a no-op on purpose: the first
343
+ // provider keeps ownership and subscribers are not re-notified, so
344
+ // an already-bound renderer never sees its React rebound.
345
+ const current = __mfReadSharedCache(cache, descriptor);
346
+ if (current !== undefined && __mfIsSameSharedModule(current, value)) return value;
283
347
  cache[descriptor.canonical] = value;
284
348
  const aliases = descriptor.aliases || [];
285
349
  for (const alias of aliases) {
@@ -378,85 +442,181 @@ function getInstalledPackageJson(pkg, opts) {
378
442
  installedPackageJsonCache.set(cacheKey, result);
379
443
  return result;
380
444
  }
445
+ function tryReadPackageJson(packageJsonPath, expectedName) {
446
+ if (!existsSync(packageJsonPath)) return void 0;
447
+ try {
448
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
449
+ if (expectedName !== void 0 && packageJson.name !== expectedName) return void 0;
450
+ return {
451
+ path: packageJsonPath,
452
+ dir: path$1.dirname(packageJsonPath),
453
+ packageJson
454
+ };
455
+ } catch {
456
+ return;
457
+ }
458
+ }
381
459
  function resolveInstalledPackageJson(pkg, cwd, packageName, opts) {
382
- const tryReadPackageJson = (packageJsonPath) => {
383
- if (!existsSync(packageJsonPath)) return void 0;
384
- try {
385
- return {
386
- path: packageJsonPath,
387
- dir: path$1.dirname(packageJsonPath),
388
- packageJson: JSON.parse(readFileSync(packageJsonPath, "utf-8"))
389
- };
390
- } catch {
391
- return;
392
- }
393
- };
394
460
  const findPackageInPnpmStore = (startDir) => {
395
- let currentDir = startDir;
396
- while (true) {
397
- const pnpmStoreDir = path$1.join(currentDir, "node_modules", ".pnpm");
398
- if (existsSync(pnpmStoreDir)) try {
461
+ for (const dir of ancestorDirs(startDir)) {
462
+ const pnpmStoreDir = path$1.join(dir, "node_modules", ".pnpm");
463
+ if (!existsSync(pnpmStoreDir)) continue;
464
+ try {
399
465
  for (const entry of readdirSync(pnpmStoreDir, { withFileTypes: true })) {
400
466
  if (!entry.isDirectory()) continue;
401
467
  const candidate = tryReadPackageJson(path$1.join(pnpmStoreDir, entry.name, "node_modules", packageName, "package.json"));
402
468
  if (candidate?.packageJson.name === packageName) return candidate;
403
469
  }
404
470
  } catch {}
405
- const parentDir = path$1.dirname(currentDir);
406
- if (parentDir === currentDir) break;
407
- currentDir = parentDir;
408
471
  }
409
472
  };
410
473
  let resolvedPath;
411
474
  try {
412
- const projectRequire = createRequire(pathToFileURL(path$1.join(cwd, "package.json")));
475
+ const rootPackageJson = path$1.join(cwd, "package.json");
413
476
  if (opts?.fromResolvedEntry) resolvedPath = opts.fromResolvedEntry;
414
477
  else try {
415
- resolvedPath = projectRequire.resolve(pkg);
478
+ resolvedPath = resolveModulePath(pkg, rootPackageJson);
416
479
  } catch {
417
- resolvedPath = projectRequire.resolve(packageName);
480
+ resolvedPath = resolveModulePath(packageName, rootPackageJson);
418
481
  }
419
482
  } catch {
420
483
  resolvedPath = void 0;
421
484
  }
422
485
  if (resolvedPath !== void 0 && !path$1.isAbsolute(resolvedPath)) resolvedPath = void 0;
423
- if (resolvedPath !== void 0) try {
424
- let currentDir = path$1.dirname(resolvedPath);
425
- let matchingPackage;
426
- while (true) {
427
- const packageJsonPath = path$1.join(currentDir, "package.json");
428
- if (existsSync(packageJsonPath)) {
429
- const packageJsonContent = readFileSync(packageJsonPath, "utf-8");
430
- try {
431
- const packageJson = JSON.parse(packageJsonContent);
432
- if (packageJson.name === packageName) {
433
- const packageInfo = {
434
- path: packageJsonPath,
435
- dir: currentDir,
436
- packageJson
437
- };
438
- if (currentDir.endsWith(path$1.join("node_modules", packageName))) return packageInfo;
439
- matchingPackage ??= packageInfo;
440
- }
441
- } catch (error) {
442
- if (!(error instanceof SyntaxError)) throw error;
486
+ if (resolvedPath !== void 0) {
487
+ const owner = findOwningPackageJson(path$1.dirname(resolvedPath), packageName);
488
+ if (owner) return owner;
489
+ }
490
+ for (const dir of ancestorDirs(cwd)) {
491
+ const directCandidate = tryReadPackageJson(path$1.join(dir, "node_modules", packageName, "package.json"), packageName);
492
+ if (directCandidate) return directCandidate;
493
+ }
494
+ return findPackageInDependents(packageName, cwd) ?? findPackageInPnpmStore(cwd);
495
+ }
496
+ /**
497
+ * A package installed only under a dependent is invisible to the walks above.
498
+ * Resolving it from each dependent in turn follows the same links that dependent
499
+ * does, so the copy found is the version it is installed against rather than
500
+ * whichever one the pnpm store happens to list first.
501
+ */
502
+ function findPackageInDependents(packageName, cwd) {
503
+ const cacheKey = `${cwd}\0${packageName}`;
504
+ if (!dependentsLookupCache.has(cacheKey)) dependentsLookupCache.set(cacheKey, walkDependents(packageName, cwd));
505
+ return dependentsLookupCache.get(cacheKey);
506
+ }
507
+ function walkDependents(packageName, cwd) {
508
+ const root = tryReadPackageJson(path$1.join(cwd, "package.json"));
509
+ if (!root) return void 0;
510
+ const visited = /* @__PURE__ */ new Set();
511
+ const rootDependencyGroups = [getDependencyNames(root.packageJson), getDependencyNames(root.packageJson, ["devDependencies"])];
512
+ for (const rootDependencies of rootDependencyGroups) {
513
+ let level = rootDependencies.map((name) => ({
514
+ name,
515
+ fromDir: cwd
516
+ }));
517
+ while (level.length) {
518
+ const candidates = [];
519
+ const nextLevel = [];
520
+ for (const { name, fromDir } of level) {
521
+ if (name === packageName) continue;
522
+ const dependent = findPackageFrom(name, fromDir);
523
+ if (!dependent || visited.has(dependent.dir)) continue;
524
+ visited.add(dependent.dir);
525
+ const dependencies = getDependencyNames(dependent.packageJson);
526
+ if (dependencies.includes(packageName)) {
527
+ const copy = findPackageFrom(packageName, dependent.dir);
528
+ if (copy) candidates.push({
529
+ copy,
530
+ via: name
531
+ });
443
532
  }
533
+ for (const dependency of dependencies) nextLevel.push({
534
+ name: dependency,
535
+ fromDir: dependent.dir
536
+ });
444
537
  }
445
- const parentDir = path$1.dirname(currentDir);
446
- if (parentDir === currentDir) break;
447
- currentDir = parentDir;
538
+ if (candidates.length) {
539
+ warnOnAmbiguousDependents(packageName, candidates);
540
+ return candidates[0].copy;
541
+ }
542
+ level = nextLevel;
448
543
  }
449
- return matchingPackage;
450
- } catch {}
451
- let currentDir = cwd;
544
+ }
545
+ }
546
+ function warnOnAmbiguousDependents(packageName, candidates) {
547
+ const viaByVersion = /* @__PURE__ */ new Map();
548
+ for (const { copy, via } of candidates) if (!viaByVersion.has(copy.packageJson.version)) viaByVersion.set(copy.packageJson.version, via);
549
+ if (viaByVersion.size < 2) return;
550
+ const versions = [...viaByVersion].map(([version, via]) => `${version} (via ${via})`);
551
+ mfWarn(`Shared package "${packageName}" is not a dependency of the project, and its closest dependents install different versions: ${versions.join(", ")}. The fallback uses ${versions[0]}. Add "${packageName}" to the project's dependencies or set its shared "import" to choose the version.`);
552
+ }
553
+ /** The package Node's `node_modules` lookup finds from `fromDir`, read at its real path. */
554
+ function findPackageFrom(packageName, fromDir) {
555
+ const entry = findNodeModulesEntry(packageName, fromDir);
556
+ if (!entry) return void 0;
557
+ try {
558
+ return tryReadPackageJson(path$1.join(realpathSync(entry), "package.json"), packageName);
559
+ } catch {
560
+ return;
561
+ }
562
+ }
563
+ /**
564
+ * Walks up from `startDir` to the package.json named `packageName`, preferring the
565
+ * one at `node_modules/<packageName>` so a nested `package.json` (for example a
566
+ * subpath's `type: module` marker) does not shadow the package root.
567
+ */
568
+ function findOwningPackageJson(startDir, packageName) {
569
+ let matchingPackage;
570
+ for (const dir of ancestorDirs(startDir)) {
571
+ const candidate = tryReadPackageJson(path$1.join(dir, "package.json"), packageName);
572
+ if (!candidate) continue;
573
+ if (dir.endsWith(path$1.join("node_modules", packageName))) return candidate;
574
+ matchingPackage ??= candidate;
575
+ }
576
+ return matchingPackage;
577
+ }
578
+ function* ancestorDirs(startDir) {
579
+ let dir = startDir;
452
580
  while (true) {
453
- const directCandidate = tryReadPackageJson(path$1.join(currentDir, "node_modules", packageName, "package.json"));
454
- if (directCandidate?.packageJson.name === packageName) return directCandidate;
455
- const parentDir = path$1.dirname(currentDir);
456
- if (parentDir === currentDir) break;
457
- currentDir = parentDir;
581
+ yield dir;
582
+ const parentDir = path$1.dirname(dir);
583
+ if (parentDir === dir) return;
584
+ dir = parentDir;
458
585
  }
459
- return findPackageInPnpmStore(cwd);
586
+ }
587
+ /** The first `node_modules/<packageName>` that Node's lookup reaches from `fromDir`. */
588
+ function findNodeModulesEntry(packageName, fromDir) {
589
+ for (const dir of ancestorDirs(fromDir)) {
590
+ const entry = path$1.join(dir, "node_modules", packageName);
591
+ if (existsSync(entry)) return entry;
592
+ }
593
+ }
594
+ /**
595
+ * `require.resolve(specifier)` from the file `from`, limited to the `node_modules` lookup
596
+ * bundlers perform. Node also searches NODE_PATH, which pnpm's bin shims point at the
597
+ * store's hoisted `node_modules`, so it can find packages the bundler cannot.
598
+ */
599
+ function resolveModulePath(specifier, from) {
600
+ const resolved = createRequire(pathToFileURL(from)).resolve(specifier);
601
+ if (!path$1.isAbsolute(resolved) || specifier.startsWith(".") || path$1.isAbsolute(specifier) || process.versions.pnp) return resolved;
602
+ if (findNodeModulesEntry(getPackageName(specifier), path$1.dirname(from))) return resolved;
603
+ const error = /* @__PURE__ */ new Error(`Cannot find module '${specifier}' from '${from}'`);
604
+ error.code = "MODULE_NOT_FOUND";
605
+ throw error;
606
+ }
607
+ function getDependencyNames(packageJson, fields = [
608
+ "dependencies",
609
+ "peerDependencies",
610
+ "optionalDependencies"
611
+ ]) {
612
+ if (!packageJson) return [];
613
+ const names = /* @__PURE__ */ new Set();
614
+ for (const field of fields) {
615
+ const deps = packageJson[field];
616
+ if (!deps || typeof deps !== "object") continue;
617
+ for (const dep of Object.keys(deps)) names.add(dep);
618
+ }
619
+ return [...names];
460
620
  }
461
621
  function getInstalledPackageEntry(pkg, opts) {
462
622
  const installed = getInstalledPackageJson(pkg, opts);
@@ -465,7 +625,7 @@ function getInstalledPackageEntry(pkg, opts) {
465
625
  const packageName = opts?.packageName || getPackageName(pkg);
466
626
  const packageJson = installed.packageJson;
467
627
  if (pkg !== packageName && (opts?.resolveSubpathWithRequire !== false || packageJson.exports === void 0)) try {
468
- return createRequire(pathToFileURL(path$1.join(cwd, "package.json"))).resolve(pkg);
628
+ return resolveModulePath(pkg, path$1.join(cwd, "package.json"));
469
629
  } catch {}
470
630
  const explicitEntry = resolveExportsEntry(getPackageExportsTarget(pkg, packageName, packageJson.exports), opts?.conditions) || (typeof packageJson.module === "string" ? packageJson.module : void 0) || (typeof packageJson.main === "string" ? packageJson.main : void 0) || "index.js";
471
631
  return path$1.join(installed.dir, explicitEntry);
@@ -479,6 +639,18 @@ function getIsRolldown(ctx) {
479
639
  const viteMajor = Number(String(viteVersion ?? "").split(".")[0]);
480
640
  return Number.isFinite(viteMajor) && viteMajor >= 8 || !!ctx?.meta?.rolldownVersion;
481
641
  }
642
+ /**
643
+ * Rolldown 1.2.10 added `codeSplitting.groups[].debugName` and warns when a group's
644
+ * `name` is a function without one. Older versions reject it as an unknown option.
645
+ */
646
+ function supportsCodeSplittingGroupDebugName(ctx) {
647
+ const rolldownVersion = ctx?.meta?.rolldownVersion;
648
+ if (typeof rolldownVersion !== "string") return false;
649
+ const [major = 0, minor = 0, patch = 0] = rolldownVersion.split(/[.+-]/, 3).map((part) => parseInt(part, 10) || 0);
650
+ if (major !== 1) return major > 1;
651
+ if (minor !== 2) return minor > 2;
652
+ return patch >= 10;
653
+ }
482
654
  /** Walk up from Vite `config.root` (Nuxt may point at `.nuxt` cache dirs). */
483
655
  function isNuxtProjectRoot(root) {
484
656
  let dir = root;
@@ -513,4 +685,4 @@ function hasPackageDependency(dependencyName, cwd = packageDetectionCwd || proce
513
685
  //#region src/utils/dtsConstants.ts
514
686
  const DEFAULT_PUBLIC_TYPES_FOLDER = "@mf-types";
515
687
  //#endregion
516
- export { sharedCacheHelperCode as _, getPackageDetectionCwd as a, mfWarn as b, getSharedCacheDescriptor as c, isPackageExportAvailable as d, isPackageInstalled as f, setPackageDetectionCwd as g, resolveImportPath as h, getIsRolldown as i, hasPackageDependency as l, packageNameEncode as m, getInstalledPackageEntry as n, getPackageName as o, packageNameDecode as p, getInstalledPackageJson as r, getPackageNameFromNodeModulePath as s, DEFAULT_PUBLIC_TYPES_FOLDER as t, isNuxtProjectRoot as u, createModuleFederationError as v, mfError as y };
688
+ export { isReactCoreShare as C, mfWarn as E, REACT_DOM_CLIENT_SHARE as S, mfError as T, resolveModulePath as _, getIsRolldown as a, supportsCodeSplittingGroupDebugName as b, getPackageNameFromNodeModulePath as c, isNuxtProjectRoot as d, isPackageExportAvailable as f, resolveImportPath as g, packageNameEncode as h, getInstalledPackageJson as i, getSharedCacheDescriptor as l, packageNameDecode as m, getDependencyNames as n, getPackageDetectionCwd as o, isPackageInstalled as p, getInstalledPackageEntry as r, getPackageName as s, DEFAULT_PUBLIC_TYPES_FOLDER as t, hasPackageDependency as u, setPackageDetectionCwd as v, createModuleFederationError as w, REACT_CLIENT_INTERNALS_KEY as x, sharedCacheHelperCode as y };
package/lib/index.d.ts CHANGED
@@ -31,6 +31,12 @@ type ModuleFederationOptions = {
31
31
  import: string;
32
32
  }> | undefined;
33
33
  filename?: string;
34
+ /**
35
+ * Exact file name of the SSR remote entry emitted next to `filename`.
36
+ * Defaults to `<filename base>.ssr<ext>` so it never clobbers the browser
37
+ * entry when both builds share an output directory. `[hash]` is not supported.
38
+ */
39
+ ssrFilename?: string;
34
40
  library?: any;
35
41
  name: string;
36
42
  remotes?: Record<string, string | RemoteObjectConfig> | undefined;
@@ -57,10 +63,13 @@ type ModuleFederationOptions = {
57
63
  name?: string;
58
64
  version?: string;
59
65
  shareScope?: string;
66
+ request?: moduleFederationPlugin.SharedConfig['request'];
67
+ shareKey?: moduleFederationPlugin.SharedConfig['shareKey'];
60
68
  singleton?: boolean;
61
69
  eager?: boolean;
62
70
  requiredVersion?: moduleFederationPlugin.SharedConfig['requiredVersion'];
63
71
  strictVersion?: boolean;
72
+ allowNodeModulesSuffixMatch?: boolean;
64
73
  /** Suppress the missing local dependency warning for `import: false` shares. */
65
74
  suppressMissingImportWarning?: boolean;
66
75
  treeShaking?: TreeShakingConfig;
@@ -122,8 +131,11 @@ type ModuleFederationOptions = {
122
131
  disableSnapshot?: boolean;
123
132
  /**
124
133
  * Additional packages to mark as external in the SSR remote entry build.
125
- * Shared packages and MF runtime packages are always external. Use this to
134
+ * Shared packages and MF runtime packages are external by default. Use this to
126
135
  * add any other Node-only packages that should not be bundled into the SSR entry.
136
+ * To emit a self-contained SSR entry (loadable by hosts that cannot resolve the
137
+ * MF runtime from Node, such as the `@module-federation/sdk` reference loader),
138
+ * opt the MF runtime packages back in through Vite's `ssr.noExternal`.
127
139
  */
128
140
  ssrExternals?: string[];
129
141
  /**
@@ -230,6 +242,7 @@ interface DtsRemoteOptions {
230
242
  extractRemoteTypes?: boolean;
231
243
  abortOnError?: boolean;
232
244
  deleteTsConfig?: boolean;
245
+ afterGenerate?: (options: moduleFederationPlugin.DtsGenerateTypesHookOptions) => Promise<void> | void;
233
246
  }
234
247
  interface DtsHostOptions {
235
248
  typesFolder?: string;