@module-federation/vite 1.16.4 → 1.16.6

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.
@@ -1,311 +0,0 @@
1
- const require_packageUtils = require("./packageUtils-se-UDhCa.cjs");
2
- let fs = require("fs");
3
- fs = require_packageUtils.__toESM(fs);
4
- let pathe = require("pathe");
5
- pathe = require_packageUtils.__toESM(pathe);
6
- let _module_federation_sdk = require("@module-federation/sdk");
7
- let _module_federation_dts_plugin = require("@module-federation/dts-plugin");
8
- let _module_federation_dts_plugin_core = require("@module-federation/dts-plugin/core");
9
- //#region src/plugins/pluginDts.ts
10
- const DEFAULT_DEV_OPTIONS = {
11
- disableLiveReload: true,
12
- disableHotTypesReload: false,
13
- disableDynamicRemoteTypeHints: false
14
- };
15
- const DYNAMIC_HINTS_PLUGIN = "@module-federation/dts-plugin/dynamic-remote-type-hints-plugin";
16
- const getIPv4 = () => process.env["FEDERATION_IPV4"] || "127.0.0.1";
17
- const DEV_TYPES_FOLDER = ".dev-server";
18
- const DEFAULT_PUBLIC_TYPES_FOLDER = "@mf-types";
19
- const forkDevWorkerPath = require.resolve("@module-federation/dts-plugin/dist/fork-dev-worker.js");
20
- var DevWorker = class {
21
- worker = _module_federation_dts_plugin_core.rpc.createRpcWorker(forkDevWorkerPath, {}, void 0, false);
22
- constructor(options) {
23
- this.worker.connect(options);
24
- }
25
- update() {
26
- this.worker.process?.send?.({
27
- type: _module_federation_dts_plugin_core.rpc.RpcGMCallTypes.CALL,
28
- id: this.worker.id,
29
- args: [void 0, "update"]
30
- });
31
- }
32
- exit() {
33
- this.worker.terminate();
34
- }
35
- };
36
- const normalizeDevOptions = (dev) => {
37
- if (dev === false) return false;
38
- if (dev === true || typeof dev === "undefined") return { ...DEFAULT_DEV_OPTIONS };
39
- return {
40
- ...DEFAULT_DEV_OPTIONS,
41
- ...dev
42
- };
43
- };
44
- const buildDtsModuleFederationConfig = (options) => {
45
- const exposes = {};
46
- Object.entries(options.exposes).forEach(([key, value]) => {
47
- if (value.import) exposes[key] = value.import;
48
- });
49
- const remotes = {};
50
- Object.entries(options.remotes).forEach(([key, remote]) => {
51
- if (!remote.entry) return;
52
- remotes[key] = `${remote.entryGlobalName?.startsWith("http") || remote.entryGlobalName?.includes(".json") ? remote.name || key : remote.entryGlobalName || remote.name || key}@${remote.entry}`;
53
- });
54
- return {
55
- ...options,
56
- exposes,
57
- remotes
58
- };
59
- };
60
- const resolveOutputDir = (config) => {
61
- const { outDir } = config.build;
62
- if (pathe.isAbsolute(outDir)) return pathe.relative(config.root, outDir);
63
- return outDir;
64
- };
65
- const ensureRuntimePlugin = (options, pluginId) => {
66
- if (!options.runtimePlugins.some((plugin) => {
67
- if (typeof plugin === "string") return plugin === pluginId;
68
- return plugin[0] === pluginId;
69
- })) options.runtimePlugins.push(pluginId);
70
- };
71
- const getExposeImportPaths = (options) => {
72
- return Object.values(options.exposes).map((value) => {
73
- return value.import;
74
- }).filter((value) => Boolean(value));
75
- };
76
- const usesVueSfcExposes = (options) => {
77
- return getExposeImportPaths(options).some((value) => value.endsWith(".vue"));
78
- };
79
- const resolveDtsPluginOptions = (dts, options, context) => {
80
- if (dts === false) return false;
81
- const inferredGenerateTypesDefaults = { generateAPITypes: true };
82
- if (usesVueSfcExposes(options) && require_packageUtils.hasPackageDependency("vue-tsc", context)) inferredGenerateTypesDefaults.compilerInstance = "vue-tsc";
83
- if (dts === true || typeof dts === "undefined") return { generateTypes: inferredGenerateTypesDefaults };
84
- const generateTypes = dts.generateTypes;
85
- return {
86
- ...dts,
87
- generateTypes: generateTypes === false ? false : {
88
- ...inferredGenerateTypesDefaults,
89
- ...generateTypes === true || typeof generateTypes === "undefined" ? {} : generateTypes
90
- }
91
- };
92
- };
93
- const getBasePath = (base) => {
94
- if (base.startsWith("http://") || base.startsWith("https://")) return new URL(base).pathname.replace(/\/$/, "") || "/";
95
- return base.replace(/\/$/, "") || "/";
96
- };
97
- const joinBaseAndAsset = (base, assetFileName) => {
98
- const basePath = getBasePath(base);
99
- return `${basePath === "/" ? "" : basePath}/${assetFileName}`.replace(/\/{2,}/g, "/");
100
- };
101
- const getDevDtsAssetPaths = (options) => {
102
- const { outputDir, publicTypesFolder, root, base } = options;
103
- return {
104
- apiFilePath: pathe.resolve(root, outputDir, `${DEV_TYPES_FOLDER}.d.ts`),
105
- apiRequestPath: joinBaseAndAsset(base, `${publicTypesFolder}.d.ts`),
106
- zipFilePath: pathe.resolve(root, outputDir, `${DEV_TYPES_FOLDER}.zip`),
107
- zipRequestPath: joinBaseAndAsset(base, `${publicTypesFolder}.zip`)
108
- };
109
- };
110
- const createDevDtsAssetMiddleware = (assetPaths) => {
111
- return (req, res, next) => {
112
- const requestPath = req.url?.split("?")[0];
113
- const isZipRequest = requestPath === assetPaths.zipRequestPath;
114
- const isApiRequest = requestPath === assetPaths.apiRequestPath;
115
- if (!isZipRequest && !isApiRequest) {
116
- next();
117
- return;
118
- }
119
- const filePath = isZipRequest ? assetPaths.zipFilePath : assetPaths.apiFilePath;
120
- if (!fs.default.existsSync(filePath)) {
121
- res.statusCode = 404;
122
- res.end();
123
- return;
124
- }
125
- res.statusCode = 200;
126
- res.setHeader("Content-Type", isZipRequest ? "application/x-gzip" : "application/typescript");
127
- if (req.method === "HEAD") {
128
- res.end();
129
- return;
130
- }
131
- const stream = fs.default.createReadStream(filePath);
132
- stream.on("error", () => {
133
- if (!res.headersSent) res.statusCode = 500;
134
- res.end();
135
- });
136
- res.on("close", () => {
137
- stream.destroy();
138
- });
139
- stream.pipe(res);
140
- };
141
- };
142
- const normalizeDevDtsOptions = (dts, context) => {
143
- return (0, _module_federation_sdk.normalizeOptions)((0, _module_federation_dts_plugin.isTSProject)(dts, context), {
144
- generateTypes: { compileInChildProcess: true },
145
- consumeTypes: { consumeAPITypes: true },
146
- extraOptions: {},
147
- displayErrorInTerminal: typeof dts === "object" && dts ? dts.displayErrorInTerminal : void 0
148
- }, "mfOptions.dts")(dts);
149
- };
150
- const logDtsError = (error, dtsOptions) => {
151
- if (dtsOptions === false) return;
152
- if (typeof dtsOptions === "object" && dtsOptions && dtsOptions.displayErrorInTerminal === false) return;
153
- require_packageUtils.mfError(error);
154
- };
155
- function pluginDts(options) {
156
- if (options.dts === false) return [];
157
- const baseDtsModuleFederationConfig = buildDtsModuleFederationConfig(options);
158
- const getDtsModuleFederationConfig = (context) => ({
159
- ...baseDtsModuleFederationConfig,
160
- dts: resolveDtsPluginOptions(options.dts, options, context)
161
- });
162
- let resolvedConfig;
163
- let devWorker;
164
- let normalizedDevOptions;
165
- let hasGeneratedBundle = false;
166
- return [{
167
- name: "module-federation-dts-dev",
168
- apply: "serve",
169
- config(config) {
170
- normalizedDevOptions = normalizeDevOptions(options.dev);
171
- if (!normalizedDevOptions) return;
172
- if (normalizedDevOptions.disableDynamicRemoteTypeHints) return;
173
- ensureRuntimePlugin(options, DYNAMIC_HINTS_PLUGIN);
174
- const define = config.define ? { ...config.define } : {};
175
- if (!("FEDERATION_IPV4" in define)) define.FEDERATION_IPV4 = JSON.stringify(getIPv4());
176
- config.define = define;
177
- },
178
- configResolved(config) {
179
- resolvedConfig = config;
180
- },
181
- configureServer(server) {
182
- if (!normalizedDevOptions || !resolvedConfig) return;
183
- const devOptions = normalizedDevOptions;
184
- if (devOptions.disableDynamicRemoteTypeHints && devOptions.disableHotTypesReload && devOptions.disableLiveReload) return;
185
- if (!options.name) throw require_packageUtils.createModuleFederationError("name is required if you want to enable dev server!");
186
- const outputDir = resolveOutputDir(resolvedConfig);
187
- const dtsModuleFederationConfig = getDtsModuleFederationConfig(resolvedConfig.root);
188
- const normalizedDtsOptions = normalizeDevDtsOptions(dtsModuleFederationConfig.dts, resolvedConfig.root);
189
- if (typeof normalizedDtsOptions !== "object") return;
190
- const normalizedGenerateTypes = (0, _module_federation_sdk.normalizeOptions)(Boolean(normalizedDtsOptions), { compileInChildProcess: true }, "mfOptions.dts.generateTypes")(normalizedDtsOptions.generateTypes);
191
- const remote = normalizedGenerateTypes === false ? void 0 : {
192
- implementation: normalizedDtsOptions.implementation,
193
- context: resolvedConfig.root,
194
- outputDir,
195
- moduleFederationConfig: { ...dtsModuleFederationConfig },
196
- hostRemoteTypesFolder: normalizedGenerateTypes.typesFolder || DEFAULT_PUBLIC_TYPES_FOLDER,
197
- ...normalizedGenerateTypes,
198
- typesFolder: DEV_TYPES_FOLDER
199
- };
200
- if (remote) server.middlewares.use(createDevDtsAssetMiddleware(getDevDtsAssetPaths({
201
- outputDir,
202
- publicTypesFolder: remote.hostRemoteTypesFolder || DEFAULT_PUBLIC_TYPES_FOLDER,
203
- root: resolvedConfig.root,
204
- base: resolvedConfig.base
205
- })));
206
- if (remote && !remote.tsConfigPath && normalizedDtsOptions.tsConfigPath) remote.tsConfigPath = normalizedDtsOptions.tsConfigPath;
207
- const normalizedConsumeTypes = (0, _module_federation_sdk.normalizeOptions)(Boolean(normalizedDtsOptions), { consumeAPITypes: true }, "mfOptions.dts.consumeTypes")(normalizedDtsOptions.consumeTypes);
208
- const host = normalizedConsumeTypes === false ? void 0 : {
209
- implementation: normalizedDtsOptions.implementation,
210
- context: resolvedConfig.root,
211
- moduleFederationConfig: dtsModuleFederationConfig,
212
- typesFolder: normalizedConsumeTypes.typesFolder || "@mf-types",
213
- abortOnError: false,
214
- ...normalizedConsumeTypes
215
- };
216
- const extraOptions = normalizedDtsOptions.extraOptions || {};
217
- if (!remote && !host && devOptions.disableLiveReload) return;
218
- const startDevWorker = async () => {
219
- let remoteTypeUrls;
220
- if (host) remoteTypeUrls = await new Promise((resolve) => {
221
- (0, _module_federation_dts_plugin.consumeTypesAPI)({
222
- host,
223
- extraOptions,
224
- displayErrorInTerminal: normalizedDtsOptions.displayErrorInTerminal
225
- }, resolve);
226
- });
227
- devWorker = new DevWorker({
228
- name: options.name,
229
- remote,
230
- host: host ? {
231
- ...host,
232
- remoteTypeUrls
233
- } : void 0,
234
- extraOptions,
235
- disableLiveReload: devOptions.disableLiveReload,
236
- disableHotTypesReload: devOptions.disableHotTypesReload
237
- });
238
- const update = () => devWorker?.update();
239
- server.watcher.on("change", update);
240
- server.watcher.on("add", update);
241
- server.watcher.on("unlink", update);
242
- server.httpServer?.once("close", () => {
243
- devWorker?.exit();
244
- server.watcher.off("change", update);
245
- server.watcher.off("add", update);
246
- server.watcher.off("unlink", update);
247
- });
248
- };
249
- startDevWorker().catch((error) => {
250
- logDtsError(error, normalizedDtsOptions);
251
- });
252
- }
253
- }, {
254
- name: "module-federation-dts-build",
255
- apply: "build",
256
- configResolved(config) {
257
- resolvedConfig = config;
258
- },
259
- async generateBundle() {
260
- if (hasGeneratedBundle) return;
261
- hasGeneratedBundle = true;
262
- if (!resolvedConfig) return;
263
- let normalizedDtsOptions;
264
- try {
265
- normalizedDtsOptions = (0, _module_federation_dts_plugin.normalizeDtsOptions)(getDtsModuleFederationConfig(resolvedConfig.root), resolvedConfig.root);
266
- } catch (error) {
267
- logDtsError(error, options.dts);
268
- return;
269
- }
270
- if (typeof normalizedDtsOptions !== "object") return;
271
- const context = resolvedConfig.root;
272
- const outputDir = resolveOutputDir(resolvedConfig);
273
- let consumeOptions;
274
- try {
275
- consumeOptions = (0, _module_federation_dts_plugin.normalizeConsumeTypesOptions)({
276
- context,
277
- dtsOptions: normalizedDtsOptions,
278
- pluginOptions: getDtsModuleFederationConfig(resolvedConfig.root)
279
- });
280
- } catch (error) {
281
- logDtsError(error, normalizedDtsOptions);
282
- return;
283
- }
284
- if (consumeOptions?.host?.typesOnBuild) try {
285
- await (0, _module_federation_dts_plugin.consumeTypesAPI)(consumeOptions);
286
- } catch (error) {
287
- logDtsError(error, normalizedDtsOptions);
288
- }
289
- let generateOptions;
290
- try {
291
- generateOptions = (0, _module_federation_dts_plugin.normalizeGenerateTypesOptions)({
292
- context,
293
- outputDir,
294
- dtsOptions: normalizedDtsOptions,
295
- pluginOptions: getDtsModuleFederationConfig(resolvedConfig.root)
296
- });
297
- } catch (error) {
298
- logDtsError(error, normalizedDtsOptions);
299
- return;
300
- }
301
- if (!generateOptions) return;
302
- try {
303
- await (0, _module_federation_dts_plugin.generateTypesAPI)({ dtsManagerOptions: generateOptions });
304
- } catch (error) {
305
- logDtsError(error, normalizedDtsOptions);
306
- }
307
- }
308
- }];
309
- }
310
- //#endregion
311
- exports.default = pluginDts;
@@ -1,301 +0,0 @@
1
- //#region src/utils/ssrEntryLoader.ts
2
- /**
3
- * MF runtime plugin that intercepts the `loadEntry` lifecycle hook on the
4
- * server and loads the SSR-compatible remote entry instead of the browser one.
5
- *
6
- * This completely replaces the need for any `@module-federation/sdk` patches.
7
- * The `loadEntry` hook is emitted by `runtime-core` before it falls through to
8
- * `loadScriptNode` — if the hook returns a value, the runtime uses it directly.
9
- *
10
- * Strategy:
11
- * - In Node (typeof window === 'undefined'), fetch the remote's mf-manifest.json
12
- * to discover the ssrRemoteEntry URL and its type.
13
- * - ESM entry: use a dynamic `import()` — the SSR entry has no browser
14
- * globals and all shared packages are external.
15
- * - Dev mode (Vite 8+ only): use `ModuleRunner` with an HTTP transport backed
16
- * by the remote's `/__mf_runner__` endpoint. This fetches fully-transformed
17
- * module source through Vite's plugin pipeline, avoiding serialisation which
18
- * cannot faithfully represent React components or closures.
19
- *
20
- * Dev mode on Vite < 8 is NOT supported — `ModuleRunner` and
21
- * `FetchableDevEnvironment` are Vite 8+ APIs. If you need dev-mode SSR on
22
- * an older Vite version, implement an alternative loader in `loadSSRRemoteEntry`
23
- * for the `isDevSsrEntry` branch and expose a corresponding server endpoint
24
- * from `pluginSSRRemoteEntry.configureServer`.
25
- *
26
- * Exported as a plain factory function so it can be serialised into the
27
- * generated runtimePlugins list in virtualRemotes.ts.
28
- */
29
- const importCache = /* @__PURE__ */ new Map();
30
- async function nodeImport(id) {
31
- if (!importCache.has(id)) importCache.set(id, import(
32
- /* @vite-ignore */
33
- id
34
- ));
35
- return importCache.get(id);
36
- }
37
- const runnerCache = /* @__PURE__ */ new Map();
38
- /**
39
- * Import `vite/module-runner` dynamically. Returns null on Vite < 8 where the
40
- * subpath doesn't exist. Uses a plain dynamic import (not nodeImport) so that
41
- * Vitest can intercept it with vi.mock in tests.
42
- */
43
- async function getModuleRunnerModule() {
44
- try {
45
- return await import("vite/module-runner");
46
- } catch {
47
- return null;
48
- }
49
- }
50
- /**
51
- * Create a ModuleRunner that fetches modules from a remote Vite dev server's
52
- * `/__mf_runner__` endpoint. Each HTTP POST carries a `fetchModule` invoke
53
- * payload; the remote responds with the transformed module source as JSON.
54
- *
55
- * This is Vite 8+ only — older versions don't expose `vite/module-runner` or
56
- * the `/__mf_runner__` proxy endpoint.
57
- */
58
- async function getOrCreateRunner(remoteOrigin) {
59
- if (runnerCache.has(remoteOrigin)) return runnerCache.get(remoteOrigin);
60
- const promise = (async () => {
61
- const viteRunner = await getModuleRunnerModule();
62
- if (!viteRunner) return null;
63
- const { ModuleRunner, ESModulesEvaluator } = viteRunner;
64
- const runnerEndpoint = `${remoteOrigin}/__mf_runner__`;
65
- try {
66
- return new ModuleRunner({
67
- hmr: false,
68
- transport: { async invoke(payload) {
69
- return await (await fetch(runnerEndpoint, {
70
- method: "POST",
71
- headers: { "Content-Type": "application/json" },
72
- body: JSON.stringify({
73
- name: payload.data.name,
74
- data: payload.data.data
75
- })
76
- })).json();
77
- } }
78
- }, new ESModulesEvaluator());
79
- } catch {
80
- return null;
81
- }
82
- })();
83
- runnerCache.set(remoteOrigin, promise);
84
- return promise;
85
- }
86
- const _path = () => nodeImport("path");
87
- const _fs = () => nodeImport("fs");
88
- const _crypto = () => nodeImport("crypto");
89
- const _module = () => nodeImport("module");
90
- const manifestCache = /* @__PURE__ */ new Map();
91
- async function fetchManifest(manifestUrl) {
92
- try {
93
- const res = await fetch(manifestUrl);
94
- if (!res.ok) return null;
95
- return await res.json();
96
- } catch {
97
- return null;
98
- }
99
- }
100
- function getManifestUrl(remoteEntryUrl) {
101
- return remoteEntryUrl.replace(/\/[^/]+$/, "/mf-manifest.json");
102
- }
103
- function resolveSSREntryUrl(manifest, manifestUrl) {
104
- const meta = manifest?.metaData;
105
- if (!meta?.ssrRemoteEntry?.name) return null;
106
- const base = manifestUrl.replace(/\/[^/]+$/, "/");
107
- const entryPath = (meta.ssrRemoteEntry.path || "") + meta.ssrRemoteEntry.name;
108
- return {
109
- url: new URL(entryPath, base).href,
110
- type: meta.ssrRemoteEntry.type || "module"
111
- };
112
- }
113
- /**
114
- * Derive the SSR entry URL by convention when no manifest is available.
115
- * remoteEntry.js → remoteEntry.ssr.js
116
- * remoteEntry.js → /__mf_ssr__/remoteEntry.ssr.js (dev middleware)
117
- * Returns the first URL that responds with a 200.
118
- */
119
- async function headCheckSsrEntry(candidate) {
120
- try {
121
- const res = await fetch(candidate.url, { method: "HEAD" });
122
- const ct = res.headers.get("content-type") ?? "";
123
- if (res.ok && !ct.includes("text/html")) return candidate;
124
- } catch {}
125
- return null;
126
- }
127
- async function getSSREntryByConvention(remoteEntryUrl, options = {}) {
128
- const base = remoteEntryUrl.replace(/\.[^.]+$/, "");
129
- const remoteOrigin = remoteEntryUrl.replace(/\/[^/]+$/, "");
130
- const filename = remoteEntryUrl.split("/").pop()?.replace(/\.[^.]+$/, "") ?? "remoteEntry";
131
- const candidates = [
132
- ...options.skipServerBuild ? [] : [{
133
- url: `${remoteOrigin}/__mf_server__/${filename}.ssr.js`,
134
- type: "module"
135
- }],
136
- {
137
- url: `${base}.ssr.js`,
138
- type: "module"
139
- },
140
- {
141
- url: `${remoteOrigin}/__mf_ssr__/${filename}.ssr.js`,
142
- type: "module"
143
- }
144
- ];
145
- for (const candidate of candidates) {
146
- const hit = await headCheckSsrEntry(candidate);
147
- if (hit) return hit;
148
- }
149
- return null;
150
- }
151
- async function getSSREntry(remoteEntryUrl) {
152
- const remoteOrigin = remoteEntryUrl.replace(/\/[^/]+$/, "");
153
- const filename = remoteEntryUrl.split("/").pop()?.replace(/\.[^.]+$/, "") ?? "remoteEntry";
154
- const manifestUrl = getManifestUrl(remoteEntryUrl);
155
- if (!manifestCache.has(manifestUrl)) manifestCache.set(manifestUrl, (async () => {
156
- const fromServerBuild = await headCheckSsrEntry({
157
- url: `${remoteOrigin}/__mf_server__/${filename}.ssr.js`,
158
- type: "module"
159
- });
160
- if (fromServerBuild) return fromServerBuild;
161
- const manifest = await fetchManifest(manifestUrl);
162
- if (manifest) {
163
- const fromManifest = resolveSSREntryUrl(manifest, manifestUrl);
164
- if (fromManifest) return fromManifest;
165
- }
166
- return getSSREntryByConvention(remoteEntryUrl, { skipServerBuild: true });
167
- })());
168
- return manifestCache.get(manifestUrl);
169
- }
170
- const tempFileCache = /* @__PURE__ */ new Map();
171
- let ssrCacheDirPromise;
172
- async function getSSRCacheDir() {
173
- if (!ssrCacheDirPromise) ssrCacheDirPromise = (async () => {
174
- const { join } = await _path();
175
- const { rmSync } = await _fs();
176
- const dir = join(process.cwd(), "node_modules", ".ssr-cache");
177
- process.once("exit", () => {
178
- try {
179
- rmSync(dir, {
180
- recursive: true,
181
- force: true
182
- });
183
- } catch {}
184
- });
185
- return dir;
186
- })();
187
- return ssrCacheDirPromise;
188
- }
189
- function transformSsrCode(code, base, sharedPkgMap) {
190
- code = code.replace(/((?:from|export\s*\*\s*from)\s*)(["'`])(\.\.?\/[^"'`\s][^"'`]*)["'`]/g, (_m, prefix, _q, specifier) => `${prefix}"${new URL(specifier, base).href}"`);
191
- code = code.replace(/(import\s*)(["'`])(\.\.?\/[^"'`\s][^"'`]*)["'`]/g, (_m, prefix, _q, specifier) => `${prefix}"${new URL(specifier, base).href}"`);
192
- code = code.replace(/(import\s*\(\s*)(["'`])(\.\.?\/[^"'`\s][^"'`]*)["'`](\s*\))/g, (_m, prefix, _q, specifier, suffix) => `${prefix}"${new URL(specifier, base).href}"${suffix}`);
193
- if (sharedPkgMap && sharedPkgMap.size > 0) code = code.replace(/(?:from|import\s*\()\s*(["'`])([^"'`./][^"'`]*)["'`]/g, (m, _q, specifier) => {
194
- const resolved = sharedPkgMap.get(specifier);
195
- return resolved ? m.replace(specifier, `file://${resolved}`) : m;
196
- });
197
- code = code.replace(/import\s*\{([^}]*)\}\s*from\s*["'][^"']*preload-helper[^"']*["'];?/g, (_m, bindings) => {
198
- return bindings.split(",").map((b) => {
199
- const parts = b.trim().split(/\s+as\s+/);
200
- return (parts[1] ?? parts[0]).trim();
201
- }).filter(Boolean).map((l) => `const ${l} = (fn) => fn();`).join("\n");
202
- });
203
- code = code.replace(/__vite__mapDeps\([^)]+\)/g, "[]");
204
- code = code.replace(/\b([A-Za-z_$][\w$]*)\s*\(\s*\(\s*\)\s*=>\s*import\(([^)]*)\)\s*,\s*\[\]\s*\)/g, "import($2)");
205
- return code;
206
- }
207
- /**
208
- * Fetch an HTTP ESM module, transform it, write it to a temp .mjs file and
209
- * return the file path. Recursively does the same for HTTP transitive imports
210
- * so that `import('file:///...temp.mjs')` can resolve them.
211
- */
212
- async function fetchEsmToTempFile(url, tmpDir, visited, sharedPkgMap) {
213
- if (visited.has(url)) return visited.get(url);
214
- if (tempFileCache.has(url)) return tempFileCache.get(url);
215
- const promise = (async () => {
216
- let code = await (await fetch(url)).text();
217
- const base = url.replace(/\/[^/]*$/, "/");
218
- const relImports = [];
219
- const relRegex = /(?:from|export\s*\*\s*from|import\s*(?:\(|\s))\s*["'`]([^"'`\s]+)["'`]/g;
220
- let m;
221
- while ((m = relRegex.exec(code)) !== null) if (m[1].startsWith("./") || m[1].startsWith("../")) relImports.push(new URL(m[1], base).href);
222
- const subMap = /* @__PURE__ */ new Map();
223
- await Promise.all([...new Set(relImports)].filter((u) => u.startsWith("http://") || u.startsWith("https://")).map(async (u) => {
224
- const tmpPath = await fetchEsmToTempFile(u, tmpDir, visited, sharedPkgMap);
225
- subMap.set(u, `file://${tmpPath}`);
226
- }));
227
- code = transformSsrCode(code, base, sharedPkgMap);
228
- for (const [httpUrl, fileUrl] of subMap) code = code.split(httpUrl).join(fileUrl);
229
- const { createHash } = await _crypto();
230
- const { join } = await _path();
231
- const { writeFileSync } = await _fs();
232
- const tmpFile = join(tmpDir, `${createHash("sha1").update(url).digest("hex").slice(0, 12)}.mjs`);
233
- writeFileSync(tmpFile, code, "utf8");
234
- visited.set(url, tmpFile);
235
- return tmpFile;
236
- })();
237
- tempFileCache.set(url, promise);
238
- return promise;
239
- }
240
- async function importTempModule(filePath) {
241
- return await import(
242
- /* @vite-ignore */
243
- filePath
244
- );
245
- }
246
- async function loadSSRRemoteEntry(ssrEntry, resolvedShared = {}) {
247
- const { url, type } = ssrEntry;
248
- if (type === "commonjs-module" || type === "commonjs") {
249
- const { createRequire } = await _module();
250
- const req = createRequire(require("url").pathToFileURL(__filename).href);
251
- try {
252
- return req(url);
253
- } catch {}
254
- }
255
- if (url.startsWith("http://") || url.startsWith("https://")) {
256
- const urlObj = new URL(url);
257
- if (urlObj.pathname.includes("/__mf_ssr__/")) {
258
- const remoteOrigin = urlObj.origin;
259
- const runner = await getOrCreateRunner(remoteOrigin);
260
- if (!runner) return null;
261
- try {
262
- return await runner.import(urlObj.pathname);
263
- } catch {
264
- return null;
265
- }
266
- }
267
- const { mkdirSync } = await _fs();
268
- const cacheDir = await getSSRCacheDir();
269
- mkdirSync(cacheDir, { recursive: true });
270
- const sharedPkgMap = new Map(Object.entries(resolvedShared));
271
- try {
272
- return await importTempModule(await fetchEsmToTempFile(url, cacheDir, /* @__PURE__ */ new Map(), sharedPkgMap));
273
- } catch {
274
- return null;
275
- }
276
- }
277
- try {
278
- return await import(
279
- /* @vite-ignore */
280
- url
281
- );
282
- } catch {
283
- return null;
284
- }
285
- }
286
- function ssrEntryLoaderPlugin(options = {}) {
287
- const resolvedShared = options.resolvedShared ?? {};
288
- return {
289
- name: "mf-vite:ssr-entry-loader",
290
- async loadEntry({ remoteInfo }) {
291
- if (typeof globalThis.window !== "undefined") return;
292
- const ssrEntry = await getSSREntry(remoteInfo.entry);
293
- if (!ssrEntry) return;
294
- const mod = await loadSSRRemoteEntry(ssrEntry, resolvedShared);
295
- if (!mod) return;
296
- return mod;
297
- }
298
- };
299
- }
300
- //#endregion
301
- module.exports = ssrEntryLoaderPlugin;
@@ -1,65 +0,0 @@
1
- //#region src/utils/ssrEntryLoader.d.ts
2
- /**
3
- * MF runtime plugin that intercepts the `loadEntry` lifecycle hook on the
4
- * server and loads the SSR-compatible remote entry instead of the browser one.
5
- *
6
- * This completely replaces the need for any `@module-federation/sdk` patches.
7
- * The `loadEntry` hook is emitted by `runtime-core` before it falls through to
8
- * `loadScriptNode` — if the hook returns a value, the runtime uses it directly.
9
- *
10
- * Strategy:
11
- * - In Node (typeof window === 'undefined'), fetch the remote's mf-manifest.json
12
- * to discover the ssrRemoteEntry URL and its type.
13
- * - ESM entry: use a dynamic `import()` — the SSR entry has no browser
14
- * globals and all shared packages are external.
15
- * - Dev mode (Vite 8+ only): use `ModuleRunner` with an HTTP transport backed
16
- * by the remote's `/__mf_runner__` endpoint. This fetches fully-transformed
17
- * module source through Vite's plugin pipeline, avoiding serialisation which
18
- * cannot faithfully represent React components or closures.
19
- *
20
- * Dev mode on Vite < 8 is NOT supported — `ModuleRunner` and
21
- * `FetchableDevEnvironment` are Vite 8+ APIs. If you need dev-mode SSR on
22
- * an older Vite version, implement an alternative loader in `loadSSRRemoteEntry`
23
- * for the `isDevSsrEntry` branch and expose a corresponding server endpoint
24
- * from `pluginSSRRemoteEntry.configureServer`.
25
- *
26
- * Exported as a plain factory function so it can be serialised into the
27
- * generated runtimePlugins list in virtualRemotes.ts.
28
- */
29
- interface RemoteInfo {
30
- name: string;
31
- entry: string;
32
- type?: string;
33
- entryGlobalName?: string;
34
- }
35
- /**
36
- * MF runtime plugin factory.
37
- *
38
- * Usage in runtimePlugins:
39
- * import { ssrEntryLoaderPlugin } from '@module-federation/vite/ssrEntryLoader'
40
- * federation({ runtimePlugins: [ssrEntryLoaderPlugin] })
41
- *
42
- * The plugin is also injected automatically for SSR contexts by the vite plugin.
43
- */
44
- interface SsrEntryLoaderOptions {
45
- /**
46
- * Pre-resolved absolute file paths for common shared packages, keyed by
47
- * bare specifier. Populated at build time by the Vite plugin from the MF
48
- * plugin's own installed location so the resolution is package-manager-
49
- * agnostic. ssrEntryLoader uses these directly when rewriting bare specifiers
50
- * in remote SSR entry temp files — no runtime createRequire walk-up needed.
51
- */
52
- resolvedShared?: Record<string, string>;
53
- }
54
- declare function ssrEntryLoaderPlugin(options?: SsrEntryLoaderOptions): {
55
- name: string;
56
- loadEntry({
57
- remoteInfo
58
- }: {
59
- remoteInfo: RemoteInfo;
60
- }): Promise<{
61
- init: unknown;
62
- get: unknown;
63
- } | undefined>;
64
- };
65
- export = ssrEntryLoaderPlugin;