@module-federation/vite 1.21.5 → 1.22.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 +25 -4
- package/lib/{buildPaths-BkaQHrd2.js → buildPaths-BoaQTkxt.js} +20 -1
- package/lib/{dtsConstants-BsaLBaaK.js → dtsConstants-BEGrtvcw.js} +21 -5
- package/lib/index.d.ts +25 -3
- package/lib/index.js +477 -213
- package/lib/{pluginDts-BhONN9dR.js → pluginDts-D7Faa4NJ.js} +9 -3
- package/lib/{ssrEntryLoader-CqtaiDUp.js → ssrEntryLoader-CMVCDSsG.js} +48 -13
- package/lib/{ssrVmStrategy-D4KB-y3H.js → ssrVmStrategy-Cx9WlTsx.js} +15 -3
- package/lib/utils/ssrEntryLoader.js +1 -1
- package/package.json +2 -2
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 on Vite 8+ to reduce startup requests.
|
|
372
393
|
|
|
373
394
|
### `codeSplitting.groups` (Vite 8+ / Rolldown)
|
|
374
395
|
|
|
@@ -43,5 +43,24 @@ function isAbsoluteUrl(src) {
|
|
|
43
43
|
if (/^[a-z]:[\\/]/i.test(src)) return false;
|
|
44
44
|
return EXTERNAL_URL_RE.test(src);
|
|
45
45
|
}
|
|
46
|
+
const HASH_PLACEHOLDER_RE = /(?:[._-]?\[hash(?::\d+)?\])/g;
|
|
47
|
+
function hasFileExtension(fileName) {
|
|
48
|
+
return fileName.slice(Math.max(fileName.lastIndexOf("/"), fileName.lastIndexOf("\\")) + 1).lastIndexOf(".") > 0;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Resolve a bundler `filename` template that still contains `[hash]` placeholders
|
|
52
|
+
* into the concrete, stable file name Module Federation serves.
|
|
53
|
+
*
|
|
54
|
+
* The federation entries are emitted by us rather than hashed by the bundler, so
|
|
55
|
+
* the placeholder is dropped instead of being substituted. When stripping it also
|
|
56
|
+
* removes the extension (`mf-[hash:8]` → `mf`), `.js` is appended so the result
|
|
57
|
+
* stays a loadable module. The extension check deliberately looks at the basename
|
|
58
|
+
* only — a dotted directory (`assets/v1.2/entry`) must not be mistaken for one.
|
|
59
|
+
*/
|
|
60
|
+
function resolveHashPlaceholderFileName(fileName) {
|
|
61
|
+
if (!fileName.includes("[hash")) return fileName;
|
|
62
|
+
const normalized = fileName.replace(HASH_PLACEHOLDER_RE, "");
|
|
63
|
+
return hasFileExtension(normalized) ? normalized : `${normalized}.js`;
|
|
64
|
+
}
|
|
46
65
|
//#endregion
|
|
47
|
-
export { normalizePathForImport as n, rebaseImport as r, EXTERNAL_URL_RE as t };
|
|
66
|
+
export { resolveHashPlaceholderFileName as i, normalizePathForImport as n, rebaseImport as r, EXTERNAL_URL_RE as t };
|
|
@@ -2,6 +2,7 @@ import { existsSync, readFileSync, readdirSync } from "fs";
|
|
|
2
2
|
import { createRequire } from "module";
|
|
3
3
|
import * as path$1 from "node:path";
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
5
6
|
//#region src/utils/logger.ts
|
|
6
7
|
const MODULE_FEDERATION_LOG_PREFIX = "[Module Federation]";
|
|
7
8
|
function formatModuleFederationMessage(message) {
|
|
@@ -136,22 +137,37 @@ function getPackageExportsTarget(pkg, packageName, exportsField) {
|
|
|
136
137
|
* - => 3
|
|
137
138
|
* . => 4
|
|
138
139
|
*/
|
|
140
|
+
const MF_HASHED_NAME_THRESHOLD = 90;
|
|
141
|
+
const MF_HASHED_NAME_HASH_LENGTH = 16;
|
|
142
|
+
const MF_HASHED_NAME_PREFIX_LENGTH = MF_HASHED_NAME_THRESHOLD - MF_HASHED_NAME_HASH_LENGTH;
|
|
143
|
+
const mfHashedNameMap = /* @__PURE__ */ new Map();
|
|
139
144
|
/**
|
|
140
|
-
* Encodes a package name
|
|
141
|
-
*
|
|
145
|
+
* Encodes a package name (or shared-module specifier, which may include a
|
|
146
|
+
* deep import subpath) into a valid file name, falling back to a
|
|
147
|
+
* readable-prefix + content-hash id when the plain encoding would be too
|
|
148
|
+
* long for a filesystem path segment.
|
|
149
|
+
* @param {string} name - The package name or specifier, e.g., "@scope/xx-xx.xx" or "@scope/pkg/deep/sub-path".
|
|
142
150
|
* @returns {string} - The encoded file name.
|
|
143
151
|
*/
|
|
144
152
|
function packageNameEncode(name) {
|
|
145
153
|
if (typeof name !== "string") throw createModuleFederationError("A string package name is required");
|
|
146
|
-
|
|
154
|
+
const encoded = name.replace(/@/g, "_mf_0_").replace(/\//g, "_mf_1_").replace(/-/g, "_mf_2_").replace(/\./g, "_mf_3_");
|
|
155
|
+
if (encoded.length <= MF_HASHED_NAME_THRESHOLD) return encoded;
|
|
156
|
+
const hashedName = `${encoded.slice(0, MF_HASHED_NAME_PREFIX_LENGTH)}${createHash("sha256").update(name).digest("hex").slice(0, MF_HASHED_NAME_HASH_LENGTH)}`;
|
|
157
|
+
mfHashedNameMap.set(hashedName, name);
|
|
158
|
+
return hashedName;
|
|
147
159
|
}
|
|
148
160
|
/**
|
|
149
|
-
* Decodes an encoded file name back to the original package name
|
|
161
|
+
* Decodes an encoded file name back to the original package name or
|
|
162
|
+
* shared-module specifier, whether it was plainly substituted or hashed
|
|
163
|
+
* down by `packageNameEncode`.
|
|
150
164
|
* @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.
|
|
165
|
+
* @returns {string} - The decoded package name or specifier.
|
|
152
166
|
*/
|
|
153
167
|
function packageNameDecode(encoded) {
|
|
154
168
|
if (typeof encoded !== "string") throw createModuleFederationError("A string encoded file name is required");
|
|
169
|
+
const original = mfHashedNameMap.get(encoded);
|
|
170
|
+
if (original !== void 0) return original;
|
|
155
171
|
return encoded.replace(/_mf_0_/g, "@").replace(/_mf_1_/g, "/").replace(/_mf_2_/g, "-").replace(/_mf_3_/g, ".");
|
|
156
172
|
}
|
|
157
173
|
/**
|
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 };
|