@module-federation/vite 1.23.1 → 1.23.3

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
@@ -278,6 +278,21 @@ federation({
278
278
 
279
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.
280
280
 
281
+ `ssrEntryLoader` also accepts network and cache options:
282
+
283
+ ```ts
284
+ federation({
285
+ // ...
286
+ ssrEntryLoader: {
287
+ maxAgeMs: 60_000, // re-check each remote's manifest after 60s; omit to cache until process exit
288
+ fetchTimeoutMs: 5_000, // per-request timeout, default 10s, 0 disables it
289
+ fetchMaxBytes: 5 * 1024 * 1024, // per-response size limit, default 10 MiB, 0 disables it
290
+ },
291
+ });
292
+ ```
293
+
294
+ With `maxAgeMs`, a remote redeployed at the same URL is picked up once its manifest version changes. Only manifest-resolved entries can be revalidated.
295
+
281
296
  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
297
 
283
298
  ## Runtime capability optimization
@@ -402,6 +417,42 @@ federation({
402
417
  `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. A container that also `exposes` (e.g. a host consumed by its own remotes) may provide the runtime too, as long as exactly one container on the page does and it is loaded before any `externalRuntime` remote evaluates (a second provider is ignored with a `Detect multiple module federation runtime!` warning; a remote evaluated before the provider throws `_FEDERATION_RUNTIME_CORE is missing`).
403
418
  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.
404
419
 
420
+ ## Host provides all shared (`experiments.hostProvidesAllShared`)
421
+
422
+ For topologies where **one host provides every shared dependency** and remotes only consume them, shared dependencies can be resolved by a native browser [import map](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) instead of the Module Federation runtime. No `loadShare` / prebuild glue is generated and `eager` is not needed: shared modules are ordinary static ES imports, so code that reads a shared value at module-evaluation time (`class X extends SharedBase`, decorator metadata) just works.
423
+
424
+ **Remote** (a container with `exposes`):
425
+
426
+ ```ts
427
+ federation({
428
+ name: "remote",
429
+ exposes: { "./Button": "./src/Button.tsx" },
430
+ shared: { react: {}, "react-dom": {}, "react/jsx-runtime": {} },
431
+ experiments: { hostProvidesAllShared: true },
432
+ });
433
+ ```
434
+
435
+ The remote build emits one fixed-name entry per expose (`./Button` → `Button.js`), keeps every shared key as a bare external import and writes a standard `mf-manifest.json` next to the entries, with no `remoteEntry` since there is no runtime container. `metaData.publicPath` comes from Vite's `base` (or `publicPath`); `auto` is inferred from the manifest URL by the host.
436
+
437
+ **Host** (no `exposes`):
438
+
439
+ ```ts
440
+ federation({
441
+ name: "host",
442
+ remotes: { remote: "https://cdn.example.com/remote/mf-manifest.json" },
443
+ shared: { react: {}, "react-dom": {}, "react/jsx-runtime": {} },
444
+ experiments: { hostProvidesAllShared: true },
445
+ });
446
+ ```
447
+
448
+ The host emits one re-export chunk per shared key, which the bundler dedupes with the host's own imports, and injects a `<script type="importmap">` into `index.html` mapping each shared key to that chunk and each remote expose (`remote/Button`) to the entry listed in the manifest. A manifest may also be a path relative to the host root (for example `../remote/dist/mf-manifest.json`). A manifest that has a `remoteEntry` belongs to a Module Federation runtime remote and is rejected. The dev server serves the same map, pointing shared keys at virtual modules that resolve to the host's pre-bundled dependencies, so host and remotes share one instance in dev too.
449
+
450
+ A remote that imports an unshared subpath of a shared package (`rxjs/operators` with only `rxjs` shared) bundles a private copy of that package's internals; the remote build warns and names the key to add to `shared`.
451
+
452
+ `dts` keeps working through the manifest's `metaData.types`: a remote emits its types archive next to `mf-manifest.json` and a host referencing the manifest by URL consumes it from there.
453
+
454
+ Not supported in this mode, since the Module Federation runtime is not loaded: version negotiation and fallbacks, share aliasing (`request` / `shareKey`), `import: false`, runtime plugins, SSR, tree-shaking artifacts, `loadRemote` / runtime-registered remotes, and interop with webpack / rspack containers. Options that only the runtime or `remoteEntry.js` pipeline reads (for example `filename`, `shareStrategy`, `runtimePlugins`, `eager`, `requiredVersion`) are ignored; one warning names them. `singleton` needs no setting: every shared key has exactly one instance. The import map is fixed when the host is built, so a remote must keep its expose entry names stable across deploys. The inline import map changes with every host build; a strict CSP needs a nonce or hash for it.
455
+
405
456
  ## ⚠️ `codeSplitting` is managed by the plugin
406
457
 
407
458
  Do not set `build.rollupOptions.output.codeSplitting` or
@@ -1,5 +1,5 @@
1
1
  import { d as getNodeModulesSuffix, r as getSharedRuntimeKey } from "./sharedKeyMatcher-Od_nYxJA.js";
2
- import { r as mfWarn, t as createModuleFederationError } from "./logger--uKHOVjR.js";
2
+ import { r as mfWarn, t as createModuleFederationError } from "./logger-DOwfTlw5.js";
3
3
  import { existsSync, readFileSync, readdirSync, realpathSync } from "fs";
4
4
  import { createRequire } from "module";
5
5
  import * as path$1 from "node:path";
@@ -36,6 +36,7 @@ function getDependencyCacheKey(cwd, dependencyName) {
36
36
  }
37
37
  const installedPackageJsonCache = /* @__PURE__ */ new Map();
38
38
  const dependentsLookupCache = /* @__PURE__ */ new Map();
39
+ const reachableDependencyNames = /* @__PURE__ */ new Map();
39
40
  function setPackageDetectionCwd(cwd) {
40
41
  packageDetectionCwd = cwd;
41
42
  }
@@ -471,10 +472,17 @@ function resolveInstalledPackageJson(pkg, cwd, packageName, opts) {
471
472
  */
472
473
  function findPackageInDependents(packageName, cwd) {
473
474
  const cacheKey = `${cwd}\0${packageName}`;
474
- if (!dependentsLookupCache.has(cacheKey)) dependentsLookupCache.set(cacheKey, walkDependents(packageName, cwd));
475
+ if (!dependentsLookupCache.has(cacheKey)) {
476
+ if (!reachableDependencyNames.has(cwd)) {
477
+ const names = /* @__PURE__ */ new Set();
478
+ walkDependents("", cwd, names);
479
+ reachableDependencyNames.set(cwd, names);
480
+ }
481
+ dependentsLookupCache.set(cacheKey, reachableDependencyNames.get(cwd).has(packageName) ? walkDependents(packageName, cwd) : void 0);
482
+ }
475
483
  return dependentsLookupCache.get(cacheKey);
476
484
  }
477
- function walkDependents(packageName, cwd) {
485
+ function walkDependents(packageName, cwd, reachedNames) {
478
486
  const root = tryReadPackageJson(path$1.join(cwd, "package.json"));
479
487
  if (!root) return void 0;
480
488
  const visited = /* @__PURE__ */ new Set();
@@ -492,6 +500,7 @@ function walkDependents(packageName, cwd) {
492
500
  const dependent = findPackageFrom(name, fromDir);
493
501
  if (!dependent || visited.has(dependent.dir)) continue;
494
502
  visited.add(dependent.dir);
503
+ reachedNames?.add(name);
495
504
  const dependencies = getDependencyNames(dependent.packageJson);
496
505
  if (dependencies.includes(packageName)) {
497
506
  const copy = findPackageFrom(packageName, dependent.dir);
package/lib/index.d.ts CHANGED
@@ -1,263 +1,4 @@
1
- import { moduleFederationPlugin } from "@module-federation/sdk";
2
- import { ShareStrategy } from "@module-federation/runtime/types";
3
- //#region src/utils/normalizeModuleFederationOptions.d.ts
4
- interface RemoteObjectConfig {
5
- type?: string;
6
- name: string;
7
- internalName?: string;
8
- entry: string;
9
- entryGlobalName?: string;
10
- shareScope?: string | string[];
11
- }
12
- interface TreeShakingConfig {
13
- mode: 'server-calc' | 'runtime-infer';
14
- usedExports?: string[];
15
- }
16
- interface PluginManifestOptions {
17
- filePath?: string;
18
- disableAssetsAnalyze?: boolean;
19
- fileName?: string;
20
- additionalData?: (options: {
21
- stats: Record<string, unknown>;
22
- manifest?: Record<string, unknown>;
23
- pluginOptions: Record<string, unknown>;
24
- compiler?: unknown;
25
- compilation?: unknown;
26
- bundler: 'vite';
27
- }) => Promise<Record<string, unknown> | void> | Record<string, unknown> | void;
28
- }
29
- type ModuleFederationOptions = {
30
- exposes?: Record<string, string | {
31
- import: string;
32
- }> | undefined;
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;
40
- library?: any;
41
- name: string;
42
- remotes?: Record<string, string | RemoteObjectConfig> | undefined;
43
- runtime?: any;
44
- shareScope?: string | string[];
45
- /**
46
- * Override the public path used for remote entries
47
- * Defaults to Vite's base config or "auto" if base is empty
48
- */
49
- publicPath?: string;
50
- /**
51
- * Controls whether all CSS assets from the bundle should be added to every exposed module.
52
- * When false (default), the plugin will not process any CSS assets.
53
- * When true, all CSS assets are bundled into every exposed module.
54
- */
55
- bundleAllCSS?: boolean;
56
- /** Directory reserved for deploy-service generated secondary shared artifacts. */
57
- treeShakingDir?: string;
58
- /** Whether inferred usedExports metadata is injected into generated runtime records. */
59
- injectTreeShakingUsedExports?: boolean;
60
- treeShakingSharedPlugins?: string[];
61
- treeShakingSharedExcludePlugins?: string[];
62
- shared?: string[] | Record<string, string | {
63
- name?: string;
64
- version?: string;
65
- shareScope?: string;
66
- request?: moduleFederationPlugin.SharedConfig['request'];
67
- shareKey?: moduleFederationPlugin.SharedConfig['shareKey'];
68
- singleton?: boolean;
69
- eager?: boolean;
70
- requiredVersion?: moduleFederationPlugin.SharedConfig['requiredVersion'];
71
- strictVersion?: boolean;
72
- allowNodeModulesSuffixMatch?: boolean;
73
- /** Suppress the missing local dependency warning for `import: false` shares. */
74
- suppressMissingImportWarning?: boolean;
75
- treeShaking?: TreeShakingConfig;
76
- import?: moduleFederationPlugin.SharedConfig['import'];
77
- }> | undefined;
78
- runtimePlugins?: Array<string | [string, Record<string, unknown>]>;
79
- getPublicPath?: string;
80
- implementation?: string;
81
- manifest?: PluginManifestOptions | boolean;
82
- dev?: boolean | PluginDevOptions;
83
- dts?: boolean | PluginDtsOptions;
84
- shareStrategy?: ShareStrategy;
85
- ignoreOrigin?: boolean;
86
- virtualModuleDir?: string;
87
- hostInitInjectLocation?: HostInitInjectLocationOptions;
88
- /**
89
- * Timeout for parsing modules in seconds.
90
- * Defaults to 10 seconds.
91
- */
92
- moduleParseTimeout?: number;
93
- /**
94
- * Idle timeout for parsing modules in seconds. When set, the timeout resets
95
- * on every parsed module and only fires when there has been no module activity
96
- * for the configured duration. Prefer this over `moduleParseTimeout` for large
97
- * codebases where the total build time may exceed the fixed timeout.
98
- */
99
- moduleParseIdleTimeout?: number;
100
- /**
101
- * Allows generate additional remoteEntry file for "var" host environment
102
- */
103
- varFilename?: string;
104
- /**
105
- * Target environment for the build to enable effective tree-shaking.
106
- *
107
- * @see https://module-federation.io/configure/experiments#target
108
- * @default 'web' (or 'node' if build.ssr is enabled)
109
- */
110
- target?: 'web' | 'node';
111
- /**
112
- * Removes remote-consumption support from the federation runtime.
113
- * Only enable this for builds that never load remotes.
114
- *
115
- * @default false
116
- */
117
- disableRemote?: boolean;
118
- /**
119
- * Removes shared-dependency support from the federation runtime.
120
- * Only enable this when the build has no shared dependencies.
121
- *
122
- * @default false
123
- */
124
- disableShared?: boolean;
125
- /**
126
- * Removes snapshot support, including manifest-based remotes, preload,
127
- * dynamic type hints, HMR, and devtools integration.
128
- *
129
- * @default false (true for Node/SSR builds)
130
- */
131
- disableSnapshot?: boolean;
132
- /**
133
- * Additional packages to mark as external in the SSR remote entry build.
134
- * Shared packages and MF runtime packages are external by default. Use this to
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`.
139
- */
140
- ssrExternals?: string[];
141
- /**
142
- * Options for the auto-injected `@module-federation/vite/ssrEntryLoader`.
143
- * When omitted, the loader uses the `'temp-file'` strategy. Set
144
- * `strategy: 'vm'` to opt into `vm.SourceTextModule` evaluation while keeping
145
- * the computed `resolvedShared` map.
146
- */
147
- ssrEntryLoader?: SsrEntryLoaderConfig;
148
- /**
149
- * Experimental Module Federation capabilities.
150
- *
151
- * @see https://module-federation.io/configure/experiments
152
- */
153
- experiments?: PluginExperimentsOptions;
154
- };
155
- interface PluginExperimentsOptions {
156
- /**
157
- * Treat `@module-federation/runtime-core` as an external that reads
158
- * `globalThis._FEDERATION_RUNTIME_CORE` at runtime. Pair with a host that
159
- * sets `provideExternalRuntime: true`.
160
- */
161
- externalRuntime?: boolean;
162
- /**
163
- * Injects a local runtime plugin that publishes `runtime-core` on
164
- * `globalThis._FEDERATION_RUNTIME_CORE`. Set it on exactly one container
165
- * per page; that container may also `exposes`.
166
- */
167
- provideExternalRuntime?: boolean;
168
- /** Generate the React SSR/hydration island capability for eligible exposes. */
169
- ssrMode?: 'ISLAND';
170
- }
171
- type SsrEntryLoaderStrategy = 'temp-file' | 'vm';
172
- type SsrEntryLoaderConfig = {
173
- /**
174
- * How the auto-injected `@module-federation/vite/ssrEntryLoader` evaluates
175
- * remote SSR entries.
176
- *
177
- * - `'temp-file'` (default when omitted): fetch the ESM graph, rewrite
178
- * specifiers, write temp files and `import()` them.
179
- * - `'vm'`: evaluate the graph with `vm.SourceTextModule`. Requires
180
- * `--experimental-vm-modules`; the loader emits a single warning and
181
- * falls back to `'temp-file'` when that API is unavailable.
182
- */
183
- strategy?: SsrEntryLoaderStrategy;
184
- };
185
- type HostInitInjectLocationOptions = 'entry' | 'html';
186
- interface PluginDevOptions {
187
- disableLiveReload?: boolean;
188
- disableHotTypesReload?: boolean;
189
- disableDynamicRemoteTypeHints?: boolean;
190
- /**
191
- * Controls cross-federation HMR for remote modules.
192
- *
193
- * - `false` / `undefined` — HMR disabled (default).
194
- * - `true` — HMR enabled with auto-detected strategy. When a framework
195
- * plugin with cross-federation HMR support is detected, broadcast/relay
196
- * is suppressed and the framework's native HMR handles updates:
197
- * - React (`@vitejs/plugin-react` / `@vitejs/plugin-react-swc`) — the
198
- * plugin serves a `/@react-refresh` proxy on remotes that delegates
199
- * to the host's `RefreshRuntime`, unifying the component registry.
200
- * - Vue (`@vitejs/plugin-vue` / `@vitejs/plugin-vue-jsx`) — the plugin
201
- * injects a `__VUE_HMR_RUNTIME__` guard into the host page so the
202
- * first-loaded (host) Vue runtime is pinned and remote-loaded Vue
203
- * copies cannot overwrite it.
204
- * For any host, the plugin also injects a script that clears the
205
- * federation `moduleCache` on `vite:beforeUpdate` so subsequent
206
- * `loadRemote()` calls return the freshly patched module.
207
- * Other frameworks fall back to full page reloads.
208
- * - `'full-reload'` — HMR enabled, always use full page reloads even when
209
- * a framework with native cross-federation HMR is detected.
210
- */
211
- remoteHmr?: boolean | 'full-reload';
212
- }
213
- interface RemoteTypeUrl {
214
- alias?: string;
215
- api: string;
216
- zip: string;
217
- }
218
- interface RemoteTypeUrls {
219
- [remoteName: string]: RemoteTypeUrl;
220
- }
221
- interface PluginDtsOptions {
222
- generateTypes?: boolean | DtsRemoteOptions;
223
- consumeTypes?: boolean | DtsHostOptions;
224
- tsConfigPath?: string;
225
- extraOptions?: Record<string, unknown>;
226
- implementation?: string;
227
- cwd?: string;
228
- displayErrorInTerminal?: boolean;
229
- }
230
- interface DtsRemoteOptions {
231
- tsConfigPath?: string;
232
- typesFolder?: string;
233
- compiledTypesFolder?: string;
234
- deleteTypesFolder?: boolean;
235
- additionalFilesToCompile?: string[];
236
- compilerInstance?: 'tsc' | 'vue-tsc' | 'tspc' | string;
237
- compileInChildProcess?: boolean;
238
- generateAPITypes?: boolean;
239
- extractThirdParty?: boolean | {
240
- exclude?: Array<string | RegExp>;
241
- };
242
- extractRemoteTypes?: boolean;
243
- abortOnError?: boolean;
244
- deleteTsConfig?: boolean;
245
- afterGenerate?: (options: moduleFederationPlugin.DtsGenerateTypesHookOptions) => Promise<void> | void;
246
- }
247
- interface DtsHostOptions {
248
- typesFolder?: string;
249
- abortOnError?: boolean;
250
- remoteTypesFolder?: string;
251
- deleteTypesFolder?: boolean;
252
- maxRetries?: number;
253
- consumeAPITypes?: boolean;
254
- runtimePkgs?: string[];
255
- remoteTypeUrls?: (() => Promise<RemoteTypeUrls>) | RemoteTypeUrls;
256
- timeout?: number;
257
- family?: 0 | 4 | 6;
258
- typesOnBuild?: boolean;
259
- }
260
- //#endregion
1
+ import { a as SsrEntryLoaderStrategy, i as SsrEntryLoaderConfig, n as PluginExperimentsOptions, o as TreeShakingConfig, r as PluginManifestOptions, t as ModuleFederationOptions } from "./normalizeModuleFederationOptions-DNUlv-AL.js";
261
2
  //#region src/index.d.ts
262
3
  declare function federation(mfUserOptions: ModuleFederationOptions): any[];
263
4
  declare function createModuleFederationConfig<T extends ModuleFederationOptions>(options: T): T;