@module-federation/vite 1.21.6 → 1.22.1
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 +25 -4
- package/lib/{dtsConstants-BsaLBaaK.js → dtsConstants-dDsHZFC1.js} +40 -10
- package/lib/index.d.ts +25 -3
- package/lib/index.js +1130 -684
- package/lib/{pluginDts-4sHIZPIi.js → pluginDts-CQ088qGF.js} +1 -1
- package/lib/{sharedKeyMatcher-DiUzRVH1.js → sharedKeyMatcher-BRzBCbuR.js} +19 -25
- package/lib/{ssrEntryLoader-BqzV2t-n.js → ssrEntryLoader-BUXUQi0P.js} +11 -7
- package/lib/{ssrVmStrategy-CkmYR5_u.js → ssrVmStrategy-Ud06y3Xf.js} +2 -2
- package/lib/utils/ssrEntryLoader.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -242,6 +242,27 @@ You can specify the place the host initialization file is injected with the **ho
|
|
|
242
242
|
The **moduleParseTimeout** option allows you to configure the maximum time to wait for module parsing during the build process.
|
|
243
243
|
The **moduleParseIdleTimeout** option is an alternative that resets the timer on every parsed module. It only fires when there has been no module activity for the configured duration, making it suitable for large codebases where the total build time exceeds the fixed timeout.
|
|
244
244
|
|
|
245
|
+
## SSR entry loading strategy
|
|
246
|
+
|
|
247
|
+
SSR hosts can choose how HTTP ESM remote entries are evaluated during build and preview:
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
federation({
|
|
251
|
+
name: "host",
|
|
252
|
+
remotes: {
|
|
253
|
+
// ...
|
|
254
|
+
},
|
|
255
|
+
ssrEntryLoader: {
|
|
256
|
+
strategy: "vm",
|
|
257
|
+
},
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
- `"temp-file"` (default) fetches the remote graph, rewrites imports to host-resolved shared packages, writes temporary files, and loads them with `import()`.
|
|
262
|
+
- `"vm"` evaluates the graph in memory with `vm.SourceTextModule` and resolves shared packages through the Module Federation share scope.
|
|
263
|
+
|
|
264
|
+
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
|
+
|
|
245
266
|
## Runtime capability optimization
|
|
246
267
|
|
|
247
268
|
Runtime features that a build never uses can be removed at build time:
|
|
@@ -326,9 +347,9 @@ This deployment step is separate from the local Vite build; the plugin only emit
|
|
|
326
347
|
|
|
327
348
|
## External runtime (`experiments`)
|
|
328
349
|
|
|
329
|
-
Share one `@module-federation/runtime-core` instance from
|
|
350
|
+
Share one `@module-federation/runtime-core` instance from the host so remotes do not bundle their own copy. Pair the flags — remotes with `externalRuntime` require a host that provides the global.
|
|
330
351
|
|
|
331
|
-
**Host
|
|
352
|
+
**Host:**
|
|
332
353
|
|
|
333
354
|
```ts
|
|
334
355
|
federation({
|
|
@@ -361,14 +382,14 @@ federation({
|
|
|
361
382
|
});
|
|
362
383
|
```
|
|
363
384
|
|
|
364
|
-
`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.
|
|
385
|
+
`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`).
|
|
365
386
|
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.
|
|
366
387
|
|
|
367
388
|
## ⚠️ `codeSplitting` is managed by the plugin
|
|
368
389
|
|
|
369
390
|
Do not set `build.rollupOptions.output.codeSplitting` or
|
|
370
391
|
`build.rolldownOptions.output.codeSplitting` to `false` — it will be **ignored** (with a warning).
|
|
371
|
-
Module Federation requires chunk splitting so `
|
|
392
|
+
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).
|
|
372
393
|
|
|
373
394
|
### `codeSplitting.groups` (Vite 8+ / Rolldown)
|
|
374
395
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { l as getNodeModulesSuffix } from "./sharedKeyMatcher-BRzBCbuR.js";
|
|
1
2
|
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
2
3
|
import { createRequire } from "module";
|
|
3
4
|
import * as path$1 from "node:path";
|
|
4
5
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
5
7
|
//#region src/utils/logger.ts
|
|
6
8
|
const MODULE_FEDERATION_LOG_PREFIX = "[Module Federation]";
|
|
7
9
|
function formatModuleFederationMessage(message) {
|
|
@@ -128,6 +130,20 @@ function getPackageExportsTarget(pkg, packageName, exportsField) {
|
|
|
128
130
|
if (subpath !== ".") return matchExportsSubpath(record, subpath);
|
|
129
131
|
return record["."] ?? (!Object.keys(record).some((key) => key.startsWith(".")) ? record : void 0);
|
|
130
132
|
}
|
|
133
|
+
/** Whether `pkg` can be located from `opts.cwd` (or the detection cwd). Aliased or non-hoisted packages may still resolve through Vite. */
|
|
134
|
+
function isPackageInstalled(pkg, opts = {}) {
|
|
135
|
+
return getInstalledPackageJson(getPackageName(pkg), opts) !== void 0;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Whether the installed package's `exports` field permits `pkg`. Lenient when the package cannot
|
|
139
|
+
* be found or has no `exports`: callers that need a hard "is installed" gate use `isPackageInstalled`.
|
|
140
|
+
*/
|
|
141
|
+
function isPackageExportAvailable(pkg, opts = {}) {
|
|
142
|
+
const packageName = getPackageName(pkg);
|
|
143
|
+
const installed = getInstalledPackageJson(packageName, opts);
|
|
144
|
+
if (installed?.packageJson.exports == null) return true;
|
|
145
|
+
return resolveExportsEntry(getPackageExportsTarget(pkg, packageName, installed.packageJson.exports), opts.conditions) !== void 0;
|
|
146
|
+
}
|
|
131
147
|
/**
|
|
132
148
|
* Escaping rules:
|
|
133
149
|
* Convert using the format __${mapping}__, where _ and $ are not allowed in npm package names but can be used in variable names.
|
|
@@ -136,22 +152,37 @@ function getPackageExportsTarget(pkg, packageName, exportsField) {
|
|
|
136
152
|
* - => 3
|
|
137
153
|
* . => 4
|
|
138
154
|
*/
|
|
155
|
+
const MF_HASHED_NAME_THRESHOLD = 90;
|
|
156
|
+
const MF_HASHED_NAME_HASH_LENGTH = 16;
|
|
157
|
+
const MF_HASHED_NAME_PREFIX_LENGTH = MF_HASHED_NAME_THRESHOLD - MF_HASHED_NAME_HASH_LENGTH;
|
|
158
|
+
const mfHashedNameMap = /* @__PURE__ */ new Map();
|
|
139
159
|
/**
|
|
140
|
-
* Encodes a package name
|
|
141
|
-
*
|
|
160
|
+
* Encodes a package name (or shared-module specifier, which may include a
|
|
161
|
+
* deep import subpath) into a valid file name, falling back to a
|
|
162
|
+
* readable-prefix + content-hash id when the plain encoding would be too
|
|
163
|
+
* long for a filesystem path segment.
|
|
164
|
+
* @param {string} name - The package name or specifier, e.g., "@scope/xx-xx.xx" or "@scope/pkg/deep/sub-path".
|
|
142
165
|
* @returns {string} - The encoded file name.
|
|
143
166
|
*/
|
|
144
167
|
function packageNameEncode(name) {
|
|
145
168
|
if (typeof name !== "string") throw createModuleFederationError("A string package name is required");
|
|
146
|
-
|
|
169
|
+
const encoded = name.replace(/@/g, "_mf_0_").replace(/\//g, "_mf_1_").replace(/-/g, "_mf_2_").replace(/\./g, "_mf_3_");
|
|
170
|
+
if (encoded.length <= MF_HASHED_NAME_THRESHOLD) return encoded;
|
|
171
|
+
const hashedName = `${encoded.slice(0, MF_HASHED_NAME_PREFIX_LENGTH)}${createHash("sha256").update(name).digest("hex").slice(0, MF_HASHED_NAME_HASH_LENGTH)}`;
|
|
172
|
+
mfHashedNameMap.set(hashedName, name);
|
|
173
|
+
return hashedName;
|
|
147
174
|
}
|
|
148
175
|
/**
|
|
149
|
-
* Decodes an encoded file name back to the original package name
|
|
176
|
+
* Decodes an encoded file name back to the original package name or
|
|
177
|
+
* shared-module specifier, whether it was plainly substituted or hashed
|
|
178
|
+
* down by `packageNameEncode`.
|
|
150
179
|
* @param {string} encoded - The encoded file name, e.g., "_mf_0_scope_mf_1_xx_mf_2_xx_mf_3_xx".
|
|
151
|
-
* @returns {string} - The decoded package name.
|
|
180
|
+
* @returns {string} - The decoded package name or specifier.
|
|
152
181
|
*/
|
|
153
182
|
function packageNameDecode(encoded) {
|
|
154
183
|
if (typeof encoded !== "string") throw createModuleFederationError("A string encoded file name is required");
|
|
184
|
+
const original = mfHashedNameMap.get(encoded);
|
|
185
|
+
if (original !== void 0) return original;
|
|
155
186
|
return encoded.replace(/_mf_0_/g, "@").replace(/_mf_1_/g, "/").replace(/_mf_2_/g, "-").replace(/_mf_3_/g, ".");
|
|
156
187
|
}
|
|
157
188
|
/**
|
|
@@ -164,10 +195,9 @@ function getPackageName(packageString) {
|
|
|
164
195
|
return match ? match[0] : packageString;
|
|
165
196
|
}
|
|
166
197
|
function getPackageNameFromNodeModulePath(source) {
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const parts = normalized.slice(nodeModulesIndex + 14).split("/");
|
|
198
|
+
const suffix = getNodeModulesSuffix(source);
|
|
199
|
+
if (!suffix) return;
|
|
200
|
+
const parts = suffix.split("/");
|
|
171
201
|
if (!parts[0]) return;
|
|
172
202
|
if (parts[0].startsWith("@")) return parts[1] ? `${parts[0]}/${parts[1]}` : void 0;
|
|
173
203
|
return parts[0];
|
|
@@ -483,4 +513,4 @@ function hasPackageDependency(dependencyName, cwd = packageDetectionCwd || proce
|
|
|
483
513
|
//#region src/utils/dtsConstants.ts
|
|
484
514
|
const DEFAULT_PUBLIC_TYPES_FOLDER = "@mf-types";
|
|
485
515
|
//#endregion
|
|
486
|
-
export {
|
|
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 };
|
package/lib/index.d.ts
CHANGED
|
@@ -126,6 +126,13 @@ type ModuleFederationOptions = {
|
|
|
126
126
|
* add any other Node-only packages that should not be bundled into the SSR entry.
|
|
127
127
|
*/
|
|
128
128
|
ssrExternals?: string[];
|
|
129
|
+
/**
|
|
130
|
+
* Options for the auto-injected `@module-federation/vite/ssrEntryLoader`.
|
|
131
|
+
* When omitted, the loader uses the `'temp-file'` strategy. Set
|
|
132
|
+
* `strategy: 'vm'` to opt into `vm.SourceTextModule` evaluation while keeping
|
|
133
|
+
* the computed `resolvedShared` map.
|
|
134
|
+
*/
|
|
135
|
+
ssrEntryLoader?: SsrEntryLoaderConfig;
|
|
129
136
|
/**
|
|
130
137
|
* Experimental Module Federation capabilities.
|
|
131
138
|
*
|
|
@@ -141,13 +148,28 @@ interface PluginExperimentsOptions {
|
|
|
141
148
|
*/
|
|
142
149
|
externalRuntime?: boolean;
|
|
143
150
|
/**
|
|
144
|
-
*
|
|
145
|
-
*
|
|
151
|
+
* Injects a local runtime plugin that publishes `runtime-core` on
|
|
152
|
+
* `globalThis._FEDERATION_RUNTIME_CORE`. Set it on exactly one container
|
|
153
|
+
* per page; that container may also `exposes`.
|
|
146
154
|
*/
|
|
147
155
|
provideExternalRuntime?: boolean;
|
|
148
156
|
/** Generate the React SSR/hydration island capability for eligible exposes. */
|
|
149
157
|
ssrMode?: 'ISLAND';
|
|
150
158
|
}
|
|
159
|
+
type SsrEntryLoaderStrategy = 'temp-file' | 'vm';
|
|
160
|
+
type SsrEntryLoaderConfig = {
|
|
161
|
+
/**
|
|
162
|
+
* How the auto-injected `@module-federation/vite/ssrEntryLoader` evaluates
|
|
163
|
+
* remote SSR entries.
|
|
164
|
+
*
|
|
165
|
+
* - `'temp-file'` (default when omitted): fetch the ESM graph, rewrite
|
|
166
|
+
* specifiers, write temp files and `import()` them.
|
|
167
|
+
* - `'vm'`: evaluate the graph with `vm.SourceTextModule`. Requires
|
|
168
|
+
* `--experimental-vm-modules`; the loader emits a single warning and
|
|
169
|
+
* falls back to `'temp-file'` when that API is unavailable.
|
|
170
|
+
*/
|
|
171
|
+
strategy?: SsrEntryLoaderStrategy;
|
|
172
|
+
};
|
|
151
173
|
type HostInitInjectLocationOptions = 'entry' | 'html';
|
|
152
174
|
interface PluginDevOptions {
|
|
153
175
|
disableLiveReload?: boolean;
|
|
@@ -227,4 +249,4 @@ interface DtsHostOptions {
|
|
|
227
249
|
declare function federation(mfUserOptions: ModuleFederationOptions): any[];
|
|
228
250
|
declare function createModuleFederationConfig<T extends ModuleFederationOptions>(options: T): T;
|
|
229
251
|
//#endregion
|
|
230
|
-
export { type ModuleFederationOptions, type PluginExperimentsOptions, type PluginManifestOptions, type TreeShakingConfig, createModuleFederationConfig, federation };
|
|
252
|
+
export { type ModuleFederationOptions, type PluginExperimentsOptions, type PluginManifestOptions, type SsrEntryLoaderConfig, type SsrEntryLoaderStrategy, type TreeShakingConfig, createModuleFederationConfig, federation };
|