@module-federation/vite 1.12.3 → 1.13.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/lib/index.cjs +179 -53
- package/lib/index.d.cts +1 -1
- package/lib/index.d.mts +1 -1
- package/lib/index.mjs +178 -53
- package/package.json +3 -3
package/lib/index.cjs
CHANGED
|
@@ -25,6 +25,7 @@ let defu = require("defu");
|
|
|
25
25
|
defu = __toESM(defu);
|
|
26
26
|
let fs = require("fs");
|
|
27
27
|
fs = __toESM(fs);
|
|
28
|
+
let module$1 = require("module");
|
|
28
29
|
let pathe = require("pathe");
|
|
29
30
|
pathe = __toESM(pathe);
|
|
30
31
|
let magic_string = require("magic-string");
|
|
@@ -33,7 +34,7 @@ let _rollup_pluginutils = require("@rollup/pluginutils");
|
|
|
33
34
|
let _module_federation_sdk = require("@module-federation/sdk");
|
|
34
35
|
let _module_federation_dts_plugin = require("@module-federation/dts-plugin");
|
|
35
36
|
let _module_federation_dts_plugin_core = require("@module-federation/dts-plugin/core");
|
|
36
|
-
let
|
|
37
|
+
let node_module = require("node:module");
|
|
37
38
|
let url = require("url");
|
|
38
39
|
//#region src/utils/mapCodeToCodeWithSourcemap.ts
|
|
39
40
|
async function mapCodeToCodeWithSourcemap(code) {
|
|
@@ -46,6 +47,104 @@ async function mapCodeToCodeWithSourcemap(code) {
|
|
|
46
47
|
};
|
|
47
48
|
}
|
|
48
49
|
//#endregion
|
|
50
|
+
//#region src/utils/htmlEntryUtils.ts
|
|
51
|
+
function sanitizeDevEntryPath(devEntryPath) {
|
|
52
|
+
return devEntryPath.replace(/^[^:]+:([/\\])[/\\]?/, "$1").replace(/\\\\?/g, "/");
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Inlines the federation init import into existing module script tags to fix
|
|
56
|
+
* the race condition (#396) where separate `<script type="module">` tags
|
|
57
|
+
* don't guarantee execution order with top-level await.
|
|
58
|
+
*
|
|
59
|
+
* If no entry scripts are found, falls back to injecting a separate script tag.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* // Before (two separate scripts, race condition):
|
|
63
|
+
* // <script type="module" src="/__mf__virtual/hostAutoInit.js"><\/script>
|
|
64
|
+
* // <script type="module" src="/src/main.js"><\/script>
|
|
65
|
+
* // After (single inline script, sequential execution):
|
|
66
|
+
* // <script type="module">await import("/__mf__virtual/hostAutoInit.js");await import("/src/main.js");<\/script>
|
|
67
|
+
*/
|
|
68
|
+
function inlineEntryScripts(html, initSrc) {
|
|
69
|
+
const src = sanitizeDevEntryPath(initSrc);
|
|
70
|
+
const scriptTagRegex = /<script\s+([^>]*\btype=["']module["'][^>]*\bsrc=["'][^"']+["'][^>]*)>/gi;
|
|
71
|
+
let hasEntry = false;
|
|
72
|
+
const result = html.replace(scriptTagRegex, (match, attrs) => {
|
|
73
|
+
const srcMatch = attrs.match(/\bsrc=["']([^"']+)["']/i);
|
|
74
|
+
if (!srcMatch) return match;
|
|
75
|
+
const originalSrc = srcMatch[1];
|
|
76
|
+
if (originalSrc.includes("@vite/client")) return match;
|
|
77
|
+
hasEntry = true;
|
|
78
|
+
return `<script ${attrs.replace(/\s*\bsrc=["'][^"']+["']/i, "")}>await import(${JSON.stringify(src)});await import(${JSON.stringify(originalSrc)});`;
|
|
79
|
+
});
|
|
80
|
+
if (hasEntry) return result;
|
|
81
|
+
return html.replace("<head>", `<head><script type="module" src=${JSON.stringify(src)}><\/script>`);
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/utils/packageUtils.ts
|
|
85
|
+
const dependencyPresenceCache = /* @__PURE__ */ new Map();
|
|
86
|
+
let packageDetectionCwd;
|
|
87
|
+
function getDependencyCacheKey(cwd, dependencyName) {
|
|
88
|
+
return `${cwd}:${dependencyName}`;
|
|
89
|
+
}
|
|
90
|
+
function setPackageDetectionCwd(cwd) {
|
|
91
|
+
packageDetectionCwd = cwd;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Escaping rules:
|
|
95
|
+
* Convert using the format __${mapping}__, where _ and $ are not allowed in npm package names but can be used in variable names.
|
|
96
|
+
* @ => 1
|
|
97
|
+
* / => 2
|
|
98
|
+
* - => 3
|
|
99
|
+
* . => 4
|
|
100
|
+
*/
|
|
101
|
+
/**
|
|
102
|
+
* Encodes a package name into a valid file name.
|
|
103
|
+
* @param {string} name - The package name, e.g., "@scope/xx-xx.xx".
|
|
104
|
+
* @returns {string} - The encoded file name.
|
|
105
|
+
*/
|
|
106
|
+
function packageNameEncode(name) {
|
|
107
|
+
if (typeof name !== "string") throw new Error("A string package name is required");
|
|
108
|
+
return name.replace(/@/g, "_mf_0_").replace(/\//g, "_mf_1_").replace(/-/g, "_mf_2_").replace(/\./g, "_mf_3_");
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Decodes an encoded file name back to the original package name.
|
|
112
|
+
* @param {string} encoded - The encoded file name, e.g., "_mf_0_scope_mf_1_xx_mf_2_xx_mf_3_xx".
|
|
113
|
+
* @returns {string} - The decoded package name.
|
|
114
|
+
*/
|
|
115
|
+
function packageNameDecode(encoded) {
|
|
116
|
+
if (typeof encoded !== "string") throw new Error("A string encoded file name is required");
|
|
117
|
+
return encoded.replace(/_mf_0_/g, "@").replace(/_mf_1_/g, "/").replace(/_mf_2_/g, "-").replace(/_mf_3_/g, ".");
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Removes any subpath from an npm package specifier and returns the package name only.
|
|
121
|
+
* @param {string} packageString - The package specifier, e.g., "@scope/pkg/runtime" or "react/jsx-runtime".
|
|
122
|
+
* @returns {string} - The base npm package name.
|
|
123
|
+
*/
|
|
124
|
+
function removePathFromNpmPackage(packageString) {
|
|
125
|
+
const match = packageString.match(/^(?:@[^/]+\/)?[^/]+/);
|
|
126
|
+
return match ? match[0] : packageString;
|
|
127
|
+
}
|
|
128
|
+
function hasPackageDependency(dependencyName, cwd = packageDetectionCwd || process.cwd()) {
|
|
129
|
+
const cacheKey = getDependencyCacheKey(cwd, dependencyName);
|
|
130
|
+
const cached = dependencyPresenceCache.get(cacheKey);
|
|
131
|
+
if (cached !== void 0) return cached;
|
|
132
|
+
try {
|
|
133
|
+
const packageJson = JSON.parse((0, fs.readFileSync)(pathe.default.join(cwd, "package.json"), "utf8"));
|
|
134
|
+
const hasDependency = [
|
|
135
|
+
packageJson.dependencies,
|
|
136
|
+
packageJson.devDependencies,
|
|
137
|
+
packageJson.peerDependencies,
|
|
138
|
+
packageJson.optionalDependencies
|
|
139
|
+
].some((deps) => !!deps?.[dependencyName]);
|
|
140
|
+
dependencyPresenceCache.set(cacheKey, hasDependency);
|
|
141
|
+
return hasDependency;
|
|
142
|
+
} catch {
|
|
143
|
+
dependencyPresenceCache.set(cacheKey, false);
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
//#endregion
|
|
49
148
|
//#region src/plugins/pluginAddEntry.ts
|
|
50
149
|
function getFirstHtmlEntryFile(entryFiles) {
|
|
51
150
|
return entryFiles.find((file) => file.endsWith(".html"));
|
|
@@ -75,7 +174,7 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
|
|
|
75
174
|
viteConfig = config;
|
|
76
175
|
const resolvedEntryPath = getEntryPath();
|
|
77
176
|
devEntryPath = resolvedEntryPath.startsWith("virtual:mf") ? "@id/" + resolvedEntryPath : resolvedEntryPath;
|
|
78
|
-
devEntryPath = config.base + devEntryPath.replace(/\\\\?/g, "/").replace(
|
|
177
|
+
devEntryPath = config.base + devEntryPath.replace(/\\\\?/g, "/").replace(/^[^:]+:([/\\])[/\\]?/, "$1").replace(/^\//, "");
|
|
79
178
|
},
|
|
80
179
|
configureServer(server) {
|
|
81
180
|
server.middlewares.use((req, res, next) => {
|
|
@@ -90,14 +189,11 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
|
|
|
90
189
|
transformIndexHtml(c) {
|
|
91
190
|
if (!injectHtml()) return;
|
|
92
191
|
clientInjected = true;
|
|
93
|
-
return c
|
|
192
|
+
return inlineEntryScripts(c, devEntryPath);
|
|
94
193
|
},
|
|
95
194
|
transform(code, id) {
|
|
96
195
|
if (id.includes("node_modules") || inject !== "html" || htmlFilePath) return;
|
|
97
|
-
if (id.includes(".svelte-kit") && id.includes("internal.js"))
|
|
98
|
-
const src = devEntryPath.replace(/.+?\:([/\\])[/\\]?/, "$1").replace(/\\\\?/g, "/");
|
|
99
|
-
return code.replace(/<head>/g, "<head><script type=\\\"module\\\" src=\\\"" + src + "\\\"><\/script>");
|
|
100
|
-
}
|
|
196
|
+
if (id.includes(".svelte-kit") && id.includes("internal.js")) return code.replace(/<head>/g, "<head><script type=\\\"module\\\" src=\\\"" + sanitizeDevEntryPath(devEntryPath) + "\\\"><\/script>");
|
|
101
197
|
}
|
|
102
198
|
}, {
|
|
103
199
|
name: "add-entry",
|
|
@@ -162,6 +258,15 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
|
|
|
162
258
|
}
|
|
163
259
|
},
|
|
164
260
|
transform(code, id) {
|
|
261
|
+
if (hasPackageDependency("vinext") && inject === "html" && (id.includes("virtual:vite-rsc/entry-browser") || id.includes("virtual:vinext-app-browser-entry"))) {
|
|
262
|
+
const injection = `import ${JSON.stringify(getEntryPath())};\n`;
|
|
263
|
+
if (code.includes(injection.trim())) {
|
|
264
|
+
clientInjected = true;
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
clientInjected = true;
|
|
268
|
+
return mapCodeToCodeWithSourcemap(injection + code);
|
|
269
|
+
}
|
|
165
270
|
if (injectEntry() && entryFiles.some((file) => id.endsWith(file)) || _command === "serve" && inject === "html" && !clientInjected && !id.includes("node_modules") && /\.(js|ts|mjs|vue|jsx|tsx)(\?|$)/.test(id)) {
|
|
166
271
|
clientInjected = true;
|
|
167
272
|
return mapCodeToCodeWithSourcemap(`import ${JSON.stringify(getEntryPath())};\n` + code);
|
|
@@ -235,7 +340,9 @@ function PluginDevProxyModuleTopLevelAwait() {
|
|
|
235
340
|
throw new Error(`${id}: ${e}`);
|
|
236
341
|
}
|
|
237
342
|
const magicString = new magic_string.default(code);
|
|
238
|
-
|
|
343
|
+
const walk = await loadWalk();
|
|
344
|
+
const defaultExportExpression = hasPackageDependency("vinext") ? "(__mfproxy__awaitdefault?.default ?? __mfproxy__awaitdefault)" : "__mfproxy__awaitdefault";
|
|
345
|
+
walk(ast, { enter(node) {
|
|
239
346
|
if (node.type === "ExportNamedDeclaration" && node.specifiers) {
|
|
240
347
|
const exportSpecifiers = node.specifiers.map((specifier) => specifier.exported.name);
|
|
241
348
|
const proxyStatements = exportSpecifiers.map((name) => `
|
|
@@ -256,15 +363,15 @@ function PluginDevProxyModuleTopLevelAwait() {
|
|
|
256
363
|
let exportStatement = "default";
|
|
257
364
|
if (declaration.type === "Identifier") proxyStatement = `
|
|
258
365
|
const __mfproxy__awaitdefault = await ${declaration.name}();
|
|
259
|
-
const __mfproxy__default =
|
|
366
|
+
const __mfproxy__default = ${defaultExportExpression};
|
|
260
367
|
`;
|
|
261
368
|
else if (declaration.type === "CallExpression" || declaration.type === "FunctionDeclaration") proxyStatement = `
|
|
262
369
|
const __mfproxy__awaitdefault = await (${code.slice(declaration.start, declaration.end)});
|
|
263
|
-
const __mfproxy__default =
|
|
370
|
+
const __mfproxy__default = ${defaultExportExpression};
|
|
264
371
|
`;
|
|
265
372
|
else proxyStatement = `
|
|
266
373
|
const __mfproxy__awaitdefault = await (${code.slice(declaration.start, declaration.end)});
|
|
267
|
-
const __mfproxy__default =
|
|
374
|
+
const __mfproxy__default = ${defaultExportExpression};
|
|
268
375
|
`;
|
|
269
376
|
const replacement = `${proxyStatement}\nexport { __mfproxy__default as ${exportStatement} };`;
|
|
270
377
|
magicString.overwrite(start, end, replacement);
|
|
@@ -546,10 +653,6 @@ function normalizeRemoteItem(key, remote) {
|
|
|
546
653
|
entryGlobalName: key
|
|
547
654
|
}, remote);
|
|
548
655
|
}
|
|
549
|
-
function removePathFromNpmPackage(packageString) {
|
|
550
|
-
const match = packageString.match(/^(?:@[^/]+\/)?[^/]+/);
|
|
551
|
-
return match ? match[0] : packageString;
|
|
552
|
-
}
|
|
553
656
|
/**
|
|
554
657
|
* Tries to find the package.json's version of a shared package
|
|
555
658
|
* if `package.json` is not declared in `exports`
|
|
@@ -558,13 +661,13 @@ function removePathFromNpmPackage(packageString) {
|
|
|
558
661
|
*/
|
|
559
662
|
function searchPackageVersion(sharedName) {
|
|
560
663
|
try {
|
|
561
|
-
const sharedPath =
|
|
664
|
+
const sharedPath = (0, node_module.createRequire)(process.cwd()).resolve(sharedName);
|
|
562
665
|
let potentialPackageJsonDir = pathe.dirname(sharedPath);
|
|
563
666
|
const rootDir = pathe.parse(potentialPackageJsonDir).root;
|
|
564
667
|
while (pathe.parse(potentialPackageJsonDir).base !== "node_modules" && potentialPackageJsonDir !== rootDir) {
|
|
565
668
|
const potentialPackageJsonPath = pathe.join(potentialPackageJsonDir, "package.json");
|
|
566
669
|
if (fs.existsSync(potentialPackageJsonPath)) {
|
|
567
|
-
const potentialPackageJson =
|
|
670
|
+
const potentialPackageJson = JSON.parse(fs.readFileSync(potentialPackageJsonPath, "utf-8"));
|
|
568
671
|
if (typeof potentialPackageJson == "object" && potentialPackageJson !== null && typeof potentialPackageJson.version === "string" && potentialPackageJson.name === sharedName) return potentialPackageJson.version;
|
|
569
672
|
}
|
|
570
673
|
potentialPackageJsonDir = pathe.dirname(potentialPackageJsonDir);
|
|
@@ -676,34 +779,6 @@ function normalizeModuleFederationOptions(options) {
|
|
|
676
779
|
};
|
|
677
780
|
}
|
|
678
781
|
//#endregion
|
|
679
|
-
//#region src/utils/packageNameUtils.ts
|
|
680
|
-
/**
|
|
681
|
-
* Escaping rules:
|
|
682
|
-
* Convert using the format __${mapping}__, where _ and $ are not allowed in npm package names but can be used in variable names.
|
|
683
|
-
* @ => 1
|
|
684
|
-
* / => 2
|
|
685
|
-
* - => 3
|
|
686
|
-
* . => 4
|
|
687
|
-
*/
|
|
688
|
-
/**
|
|
689
|
-
* Encodes a package name into a valid file name.
|
|
690
|
-
* @param {string} name - The package name, e.g., "@scope/xx-xx.xx".
|
|
691
|
-
* @returns {string} - The encoded file name.
|
|
692
|
-
*/
|
|
693
|
-
function packageNameEncode(name) {
|
|
694
|
-
if (typeof name !== "string") throw new Error("A string package name is required");
|
|
695
|
-
return name.replace(/@/g, "_mf_0_").replace(/\//g, "_mf_1_").replace(/-/g, "_mf_2_").replace(/\./g, "_mf_3_");
|
|
696
|
-
}
|
|
697
|
-
/**
|
|
698
|
-
* Decodes an encoded file name back to the original package name.
|
|
699
|
-
* @param {string} encoded - The encoded file name, e.g., "_mf_0_scope_mf_1_xx_mf_2_xx_mf_3_xx".
|
|
700
|
-
* @returns {string} - The decoded package name.
|
|
701
|
-
*/
|
|
702
|
-
function packageNameDecode(encoded) {
|
|
703
|
-
if (typeof encoded !== "string") throw new Error("A string encoded file name is required");
|
|
704
|
-
return encoded.replace(/_mf_0_/g, "@").replace(/_mf_1_/g, "/").replace(/_mf_2_/g, "-").replace(/_mf_3_/g, ".");
|
|
705
|
-
}
|
|
706
|
-
//#endregion
|
|
707
782
|
//#region src/utils/localSharedImportMap_temp.ts
|
|
708
783
|
/**
|
|
709
784
|
* https://github.com/module-federation/vite/issues/68
|
|
@@ -977,6 +1052,14 @@ function getPackageNamedExports(pkg) {
|
|
|
977
1052
|
return [];
|
|
978
1053
|
}
|
|
979
1054
|
}
|
|
1055
|
+
function getLocalProviderImportPath(pkg) {
|
|
1056
|
+
try {
|
|
1057
|
+
const resolved = (0, module$1.createRequire)(new URL("file://" + process.cwd() + "/package.json")).resolve(pkg);
|
|
1058
|
+
return resolved.includes("/node_modules/") || resolved.includes("\\node_modules\\") ? void 0 : resolved;
|
|
1059
|
+
} catch {
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
980
1063
|
const preBuildCacheMap = {};
|
|
981
1064
|
const PREBUILD_TAG = "__prebuild__";
|
|
982
1065
|
function writePreBuildLibPath(pkg) {
|
|
@@ -998,6 +1081,8 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
|
|
|
998
1081
|
const useESM = command === "build" || isRolldown;
|
|
999
1082
|
const importLine = useESM ? `import { initPromise } from "${virtualRuntimeInitStatus.getImportId()}"` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
|
|
1000
1083
|
const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
|
|
1084
|
+
const useSsrProviderFallback = hasPackageDependency("vinext") && command === "build" && pkg === "react";
|
|
1085
|
+
const providerImportId = getLocalProviderImportPath(pkg) || getPreBuildLibImportId(pkg);
|
|
1001
1086
|
const namedExports = getPackageNamedExports(pkg);
|
|
1002
1087
|
let exportLine;
|
|
1003
1088
|
if (namedExports.length > 0) {
|
|
@@ -1009,6 +1094,9 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
|
|
|
1009
1094
|
import ${JSON.stringify(getPreBuildLibImportId(pkg))};
|
|
1010
1095
|
${command !== "build" ? `;() => import(${JSON.stringify(pkg)}).catch(() => {});` : ""}
|
|
1011
1096
|
${importLine}
|
|
1097
|
+
${useSsrProviderFallback ? `const providerModulePromise = typeof window === "undefined"
|
|
1098
|
+
? import(${JSON.stringify(providerImportId)})
|
|
1099
|
+
: undefined` : ""}
|
|
1012
1100
|
const res = initPromise.then(runtime => runtime.loadShare(${JSON.stringify(pkg)}, {
|
|
1013
1101
|
customShareInfo: {shareConfig:{
|
|
1014
1102
|
singleton: ${shareItem.shareConfig.singleton},
|
|
@@ -1016,7 +1104,9 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
|
|
|
1016
1104
|
requiredVersion: ${JSON.stringify(shareItem.shareConfig.requiredVersion)}
|
|
1017
1105
|
}}
|
|
1018
1106
|
}))
|
|
1019
|
-
const exportModule = ${
|
|
1107
|
+
const exportModule = ${useSsrProviderFallback ? `(typeof window === "undefined"
|
|
1108
|
+
? ((await providerModulePromise)?.default ?? await providerModulePromise)
|
|
1109
|
+
: ${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory)))` : `${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory))`}
|
|
1020
1110
|
${exportLine}
|
|
1021
1111
|
`);
|
|
1022
1112
|
}
|
|
@@ -1042,6 +1132,7 @@ function writeLocalSharedImportMap() {
|
|
|
1042
1132
|
}
|
|
1043
1133
|
}
|
|
1044
1134
|
function generateLocalSharedImportMap() {
|
|
1135
|
+
const isVinext = hasPackageDependency("vinext");
|
|
1045
1136
|
const options = getNormalizeModuleFederationOptions();
|
|
1046
1137
|
return `
|
|
1047
1138
|
import {loadShare} from "@module-federation/runtime";
|
|
@@ -1050,7 +1141,8 @@ function generateLocalSharedImportMap() {
|
|
|
1050
1141
|
const shareItem = getNormalizeShareItem(pkg);
|
|
1051
1142
|
return `
|
|
1052
1143
|
${JSON.stringify(pkg)}: async () => {
|
|
1053
|
-
${shareItem?.shareConfig.import === false ? `throw new Error(\`Shared module '\${${JSON.stringify(pkg)}}' must be provided by host\`);` : `let pkg = await import("
|
|
1144
|
+
${shareItem?.shareConfig.import === false ? `throw new Error(\`Shared module '\${${JSON.stringify(pkg)}}' must be provided by host\`);` : isVinext && pkg === "react" ? `let pkg = await import("react");
|
|
1145
|
+
return pkg;` : `let pkg = await import("${getPreBuildLibImportId(pkg)}");
|
|
1054
1146
|
return pkg;`}
|
|
1055
1147
|
}
|
|
1056
1148
|
`;
|
|
@@ -1074,7 +1166,9 @@ function generateLocalSharedImportMap() {
|
|
|
1074
1166
|
usedShared[${JSON.stringify(key)}].loaded = true
|
|
1075
1167
|
const {${JSON.stringify(key)}: pkgDynamicImport} = importMap
|
|
1076
1168
|
const res = await pkgDynamicImport()
|
|
1077
|
-
const exportModule = {
|
|
1169
|
+
const exportModule = ${JSON.stringify(isVinext)} && ${JSON.stringify(key)} === "react"
|
|
1170
|
+
? (res?.default ?? res)
|
|
1171
|
+
: {...res}
|
|
1078
1172
|
// All npm packages pre-built by vite will be converted to esm
|
|
1079
1173
|
Object.defineProperty(exportModule, "__esModule", {
|
|
1080
1174
|
value: true,
|
|
@@ -1156,6 +1250,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
|
|
|
1156
1250
|
if (initScope.indexOf(initToken) >= 0) return;
|
|
1157
1251
|
initScope.push(initToken);
|
|
1158
1252
|
initRes.initShareScopeMap('${options.shareScope}', shared);
|
|
1253
|
+
initResolve(initRes)
|
|
1159
1254
|
try {
|
|
1160
1255
|
await Promise.all(await initRes.initializeSharing('${options.shareScope}', {
|
|
1161
1256
|
strategy: '${options.shareStrategy}',
|
|
@@ -1165,7 +1260,6 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
|
|
|
1165
1260
|
} catch (e) {
|
|
1166
1261
|
console.error(e)
|
|
1167
1262
|
}
|
|
1168
|
-
initResolve(initRes)
|
|
1169
1263
|
return initRes
|
|
1170
1264
|
}
|
|
1171
1265
|
|
|
@@ -1183,11 +1277,11 @@ const hostAutoInitModule = new VirtualModule("hostAutoInit", "__H_A_I__");
|
|
|
1183
1277
|
function writeHostAutoInit(remoteEntryId = REMOTE_ENTRY_ID) {
|
|
1184
1278
|
hostAutoInitModule.writeSync(`
|
|
1185
1279
|
const remoteEntryPromise = import("${remoteEntryId}")
|
|
1186
|
-
// __tla only serves as a hack for vite-plugin-top-level-await.
|
|
1187
1280
|
Promise.resolve(remoteEntryPromise)
|
|
1188
1281
|
.then(remoteEntry => {
|
|
1189
1282
|
return Promise.resolve(remoteEntry.__tla)
|
|
1190
|
-
.then(remoteEntry.init)
|
|
1283
|
+
.then(remoteEntry.init)
|
|
1284
|
+
.catch(remoteEntry.init)
|
|
1191
1285
|
})
|
|
1192
1286
|
`);
|
|
1193
1287
|
}
|
|
@@ -1767,6 +1861,7 @@ function proxySharedModule(options) {
|
|
|
1767
1861
|
const { shared = {} } = options;
|
|
1768
1862
|
let _config;
|
|
1769
1863
|
let _command = "serve";
|
|
1864
|
+
let isVinext = false;
|
|
1770
1865
|
const savePrebuild = new PromiseStore();
|
|
1771
1866
|
return [{
|
|
1772
1867
|
name: "generateLocalSharedImportMap",
|
|
@@ -1781,9 +1876,11 @@ function proxySharedModule(options) {
|
|
|
1781
1876
|
name: "proxyPreBuildShared",
|
|
1782
1877
|
enforce: "post",
|
|
1783
1878
|
config(config, { command }) {
|
|
1879
|
+
setPackageDetectionCwd(config.root || process.cwd());
|
|
1880
|
+
isVinext = hasPackageDependency("vinext");
|
|
1784
1881
|
const isRolldown = !!this?.meta?.rolldownVersion;
|
|
1785
1882
|
_command = command;
|
|
1786
|
-
config.resolve.alias.push(...Object.keys(shared).map((key) => {
|
|
1883
|
+
config.resolve.alias.push(...Object.keys(shared).filter((key) => !(isVinext && key === "react")).map((key) => {
|
|
1787
1884
|
const keyBase = key.endsWith("/") ? key.slice(0, -1) : key;
|
|
1788
1885
|
const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1789
1886
|
const escapedKeyBase = escapeRegex(keyBase);
|
|
@@ -1793,6 +1890,7 @@ function proxySharedModule(options) {
|
|
|
1793
1890
|
replacement: "$1",
|
|
1794
1891
|
customResolver(source, importer) {
|
|
1795
1892
|
if (/\.css$/.test(source)) return;
|
|
1893
|
+
if (isVinext && source === "react") return;
|
|
1796
1894
|
if (importer && importer.includes("localSharedImportMap")) return;
|
|
1797
1895
|
if (key.endsWith("/") && source !== key.slice(0, -1)) return;
|
|
1798
1896
|
const loadSharePath = getLoadShareModulePath(source, isRolldown, command);
|
|
@@ -1804,7 +1902,7 @@ function proxySharedModule(options) {
|
|
|
1804
1902
|
}
|
|
1805
1903
|
};
|
|
1806
1904
|
}));
|
|
1807
|
-
config.resolve.alias.push(...Object.keys(shared).map((key) => {
|
|
1905
|
+
config.resolve.alias.push(...Object.keys(shared).filter((key) => !(isVinext && key === "react")).map((key) => {
|
|
1808
1906
|
return command === "build" ? {
|
|
1809
1907
|
find: new RegExp(`(.*${PREBUILD_TAG}.*)`),
|
|
1810
1908
|
replacement: function($1) {
|
|
@@ -1827,6 +1925,10 @@ function proxySharedModule(options) {
|
|
|
1827
1925
|
const isRolldown = !!config.experimental?.rolldownDev;
|
|
1828
1926
|
Object.keys(shared).forEach((key) => {
|
|
1829
1927
|
if (key.endsWith("/")) return;
|
|
1928
|
+
if (isVinext && key === "react") {
|
|
1929
|
+
addUsedShares(key);
|
|
1930
|
+
return;
|
|
1931
|
+
}
|
|
1830
1932
|
writeLoadShareModule(key, shared[key], _command, isRolldown);
|
|
1831
1933
|
writePreBuildLibPath(key);
|
|
1832
1934
|
addUsedShares(key);
|
|
@@ -1958,6 +2060,8 @@ function createEarlyVirtualModulesPlugin(options) {
|
|
|
1958
2060
|
enforce: "pre",
|
|
1959
2061
|
config(config, { command: _command }) {
|
|
1960
2062
|
const root = config.root || process.cwd();
|
|
2063
|
+
setPackageDetectionCwd(root);
|
|
2064
|
+
const isVinext = hasPackageDependency("vinext");
|
|
1961
2065
|
initVirtualModuleInfrastructure(root, virtualModuleDir);
|
|
1962
2066
|
VirtualModule.setRoot(root);
|
|
1963
2067
|
VirtualModule.ensureVirtualPackageExists();
|
|
@@ -1972,6 +2076,10 @@ function createEarlyVirtualModulesPlugin(options) {
|
|
|
1972
2076
|
for (const key of Object.keys(shared)) {
|
|
1973
2077
|
if (key.endsWith("/")) continue;
|
|
1974
2078
|
const shareItem = shared[key];
|
|
2079
|
+
if (isVinext && key === "react") {
|
|
2080
|
+
addUsedShares(key);
|
|
2081
|
+
continue;
|
|
2082
|
+
}
|
|
1975
2083
|
getLoadShareModulePath(key, isRolldown);
|
|
1976
2084
|
writeLoadShareModule(key, shareItem, _command, isRolldown);
|
|
1977
2085
|
writePreBuildLibPath(key);
|
|
@@ -1985,6 +2093,7 @@ function createEarlyVirtualModulesPlugin(options) {
|
|
|
1985
2093
|
}
|
|
1986
2094
|
function federation(mfUserOptions) {
|
|
1987
2095
|
const options = normalizeModuleFederationOptions(mfUserOptions);
|
|
2096
|
+
const isVinext = hasPackageDependency("vinext");
|
|
1988
2097
|
const { name, remotes, shared, filename, hostInitInjectLocation } = options;
|
|
1989
2098
|
if (!name) throw new Error("name is required");
|
|
1990
2099
|
const remoteEntryId = getRemoteEntryId(options);
|
|
@@ -1992,6 +2101,23 @@ function federation(mfUserOptions) {
|
|
|
1992
2101
|
let command;
|
|
1993
2102
|
return [
|
|
1994
2103
|
createEarlyVirtualModulesPlugin(options),
|
|
2104
|
+
...isVinext ? [{
|
|
2105
|
+
name: "module-federation-vinext-react-server-build-alias",
|
|
2106
|
+
apply: "build",
|
|
2107
|
+
enforce: "pre",
|
|
2108
|
+
resolveId(id) {
|
|
2109
|
+
const reactServerEntryMap = {
|
|
2110
|
+
"react/jsx-runtime": "react/cjs/react-jsx-runtime.production.js",
|
|
2111
|
+
"react/jsx-dev-runtime": "react/cjs/react-jsx-dev-runtime.production.js"
|
|
2112
|
+
};
|
|
2113
|
+
if (!(id in reactServerEntryMap)) return;
|
|
2114
|
+
const environmentName = this?.environment?.name;
|
|
2115
|
+
if (!environmentName || environmentName === "client") return;
|
|
2116
|
+
const target = reactServerEntryMap[id];
|
|
2117
|
+
const reactPackageJson = (0, module$1.createRequire)(new URL(`file://${process.cwd()}/package.json`)).resolve("react/package.json");
|
|
2118
|
+
return pathe.default.join(pathe.default.dirname(reactPackageJson), target.replace(/^react\//, ""));
|
|
2119
|
+
}
|
|
2120
|
+
}] : [],
|
|
1995
2121
|
{
|
|
1996
2122
|
name: "vite:module-federation-config",
|
|
1997
2123
|
enforce: "pre",
|
package/lib/index.d.cts
CHANGED
package/lib/index.d.mts
CHANGED
package/lib/index.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { createRequire } from "node:module";
|
|
|
2
2
|
import defu from "defu";
|
|
3
3
|
import * as fs from "fs";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, writeFile, writeFileSync } from "fs";
|
|
5
|
+
import { createRequire as createRequire$1 } from "module";
|
|
5
6
|
import * as path$1 from "pathe";
|
|
6
7
|
import path, { basename, dirname, join, parse, resolve } from "pathe";
|
|
7
8
|
import MagicString from "magic-string";
|
|
@@ -9,7 +10,6 @@ import { createFilter } from "@rollup/pluginutils";
|
|
|
9
10
|
import { normalizeOptions } from "@module-federation/sdk";
|
|
10
11
|
import { consumeTypesAPI, generateTypesAPI, isTSProject, normalizeConsumeTypesOptions, normalizeDtsOptions, normalizeGenerateTypesOptions } from "@module-federation/dts-plugin";
|
|
11
12
|
import { rpc } from "@module-federation/dts-plugin/core";
|
|
12
|
-
import { createRequire as createRequire$1 } from "module";
|
|
13
13
|
import { fileURLToPath } from "url";
|
|
14
14
|
//#region \0rolldown/runtime.js
|
|
15
15
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
@@ -25,6 +25,104 @@ async function mapCodeToCodeWithSourcemap(code) {
|
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
//#endregion
|
|
28
|
+
//#region src/utils/htmlEntryUtils.ts
|
|
29
|
+
function sanitizeDevEntryPath(devEntryPath) {
|
|
30
|
+
return devEntryPath.replace(/^[^:]+:([/\\])[/\\]?/, "$1").replace(/\\\\?/g, "/");
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Inlines the federation init import into existing module script tags to fix
|
|
34
|
+
* the race condition (#396) where separate `<script type="module">` tags
|
|
35
|
+
* don't guarantee execution order with top-level await.
|
|
36
|
+
*
|
|
37
|
+
* If no entry scripts are found, falls back to injecting a separate script tag.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* // Before (two separate scripts, race condition):
|
|
41
|
+
* // <script type="module" src="/__mf__virtual/hostAutoInit.js"><\/script>
|
|
42
|
+
* // <script type="module" src="/src/main.js"><\/script>
|
|
43
|
+
* // After (single inline script, sequential execution):
|
|
44
|
+
* // <script type="module">await import("/__mf__virtual/hostAutoInit.js");await import("/src/main.js");<\/script>
|
|
45
|
+
*/
|
|
46
|
+
function inlineEntryScripts(html, initSrc) {
|
|
47
|
+
const src = sanitizeDevEntryPath(initSrc);
|
|
48
|
+
const scriptTagRegex = /<script\s+([^>]*\btype=["']module["'][^>]*\bsrc=["'][^"']+["'][^>]*)>/gi;
|
|
49
|
+
let hasEntry = false;
|
|
50
|
+
const result = html.replace(scriptTagRegex, (match, attrs) => {
|
|
51
|
+
const srcMatch = attrs.match(/\bsrc=["']([^"']+)["']/i);
|
|
52
|
+
if (!srcMatch) return match;
|
|
53
|
+
const originalSrc = srcMatch[1];
|
|
54
|
+
if (originalSrc.includes("@vite/client")) return match;
|
|
55
|
+
hasEntry = true;
|
|
56
|
+
return `<script ${attrs.replace(/\s*\bsrc=["'][^"']+["']/i, "")}>await import(${JSON.stringify(src)});await import(${JSON.stringify(originalSrc)});`;
|
|
57
|
+
});
|
|
58
|
+
if (hasEntry) return result;
|
|
59
|
+
return html.replace("<head>", `<head><script type="module" src=${JSON.stringify(src)}><\/script>`);
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/utils/packageUtils.ts
|
|
63
|
+
const dependencyPresenceCache = /* @__PURE__ */ new Map();
|
|
64
|
+
let packageDetectionCwd;
|
|
65
|
+
function getDependencyCacheKey(cwd, dependencyName) {
|
|
66
|
+
return `${cwd}:${dependencyName}`;
|
|
67
|
+
}
|
|
68
|
+
function setPackageDetectionCwd(cwd) {
|
|
69
|
+
packageDetectionCwd = cwd;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Escaping rules:
|
|
73
|
+
* Convert using the format __${mapping}__, where _ and $ are not allowed in npm package names but can be used in variable names.
|
|
74
|
+
* @ => 1
|
|
75
|
+
* / => 2
|
|
76
|
+
* - => 3
|
|
77
|
+
* . => 4
|
|
78
|
+
*/
|
|
79
|
+
/**
|
|
80
|
+
* Encodes a package name into a valid file name.
|
|
81
|
+
* @param {string} name - The package name, e.g., "@scope/xx-xx.xx".
|
|
82
|
+
* @returns {string} - The encoded file name.
|
|
83
|
+
*/
|
|
84
|
+
function packageNameEncode(name) {
|
|
85
|
+
if (typeof name !== "string") throw new Error("A string package name is required");
|
|
86
|
+
return name.replace(/@/g, "_mf_0_").replace(/\//g, "_mf_1_").replace(/-/g, "_mf_2_").replace(/\./g, "_mf_3_");
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Decodes an encoded file name back to the original package name.
|
|
90
|
+
* @param {string} encoded - The encoded file name, e.g., "_mf_0_scope_mf_1_xx_mf_2_xx_mf_3_xx".
|
|
91
|
+
* @returns {string} - The decoded package name.
|
|
92
|
+
*/
|
|
93
|
+
function packageNameDecode(encoded) {
|
|
94
|
+
if (typeof encoded !== "string") throw new Error("A string encoded file name is required");
|
|
95
|
+
return encoded.replace(/_mf_0_/g, "@").replace(/_mf_1_/g, "/").replace(/_mf_2_/g, "-").replace(/_mf_3_/g, ".");
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Removes any subpath from an npm package specifier and returns the package name only.
|
|
99
|
+
* @param {string} packageString - The package specifier, e.g., "@scope/pkg/runtime" or "react/jsx-runtime".
|
|
100
|
+
* @returns {string} - The base npm package name.
|
|
101
|
+
*/
|
|
102
|
+
function removePathFromNpmPackage(packageString) {
|
|
103
|
+
const match = packageString.match(/^(?:@[^/]+\/)?[^/]+/);
|
|
104
|
+
return match ? match[0] : packageString;
|
|
105
|
+
}
|
|
106
|
+
function hasPackageDependency(dependencyName, cwd = packageDetectionCwd || process.cwd()) {
|
|
107
|
+
const cacheKey = getDependencyCacheKey(cwd, dependencyName);
|
|
108
|
+
const cached = dependencyPresenceCache.get(cacheKey);
|
|
109
|
+
if (cached !== void 0) return cached;
|
|
110
|
+
try {
|
|
111
|
+
const packageJson = JSON.parse(readFileSync(path.join(cwd, "package.json"), "utf8"));
|
|
112
|
+
const hasDependency = [
|
|
113
|
+
packageJson.dependencies,
|
|
114
|
+
packageJson.devDependencies,
|
|
115
|
+
packageJson.peerDependencies,
|
|
116
|
+
packageJson.optionalDependencies
|
|
117
|
+
].some((deps) => !!deps?.[dependencyName]);
|
|
118
|
+
dependencyPresenceCache.set(cacheKey, hasDependency);
|
|
119
|
+
return hasDependency;
|
|
120
|
+
} catch {
|
|
121
|
+
dependencyPresenceCache.set(cacheKey, false);
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
//#endregion
|
|
28
126
|
//#region src/plugins/pluginAddEntry.ts
|
|
29
127
|
function getFirstHtmlEntryFile(entryFiles) {
|
|
30
128
|
return entryFiles.find((file) => file.endsWith(".html"));
|
|
@@ -54,7 +152,7 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
|
|
|
54
152
|
viteConfig = config;
|
|
55
153
|
const resolvedEntryPath = getEntryPath();
|
|
56
154
|
devEntryPath = resolvedEntryPath.startsWith("virtual:mf") ? "@id/" + resolvedEntryPath : resolvedEntryPath;
|
|
57
|
-
devEntryPath = config.base + devEntryPath.replace(/\\\\?/g, "/").replace(
|
|
155
|
+
devEntryPath = config.base + devEntryPath.replace(/\\\\?/g, "/").replace(/^[^:]+:([/\\])[/\\]?/, "$1").replace(/^\//, "");
|
|
58
156
|
},
|
|
59
157
|
configureServer(server) {
|
|
60
158
|
server.middlewares.use((req, res, next) => {
|
|
@@ -69,14 +167,11 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
|
|
|
69
167
|
transformIndexHtml(c) {
|
|
70
168
|
if (!injectHtml()) return;
|
|
71
169
|
clientInjected = true;
|
|
72
|
-
return c
|
|
170
|
+
return inlineEntryScripts(c, devEntryPath);
|
|
73
171
|
},
|
|
74
172
|
transform(code, id) {
|
|
75
173
|
if (id.includes("node_modules") || inject !== "html" || htmlFilePath) return;
|
|
76
|
-
if (id.includes(".svelte-kit") && id.includes("internal.js"))
|
|
77
|
-
const src = devEntryPath.replace(/.+?\:([/\\])[/\\]?/, "$1").replace(/\\\\?/g, "/");
|
|
78
|
-
return code.replace(/<head>/g, "<head><script type=\\\"module\\\" src=\\\"" + src + "\\\"><\/script>");
|
|
79
|
-
}
|
|
174
|
+
if (id.includes(".svelte-kit") && id.includes("internal.js")) return code.replace(/<head>/g, "<head><script type=\\\"module\\\" src=\\\"" + sanitizeDevEntryPath(devEntryPath) + "\\\"><\/script>");
|
|
80
175
|
}
|
|
81
176
|
}, {
|
|
82
177
|
name: "add-entry",
|
|
@@ -141,6 +236,15 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
|
|
|
141
236
|
}
|
|
142
237
|
},
|
|
143
238
|
transform(code, id) {
|
|
239
|
+
if (hasPackageDependency("vinext") && inject === "html" && (id.includes("virtual:vite-rsc/entry-browser") || id.includes("virtual:vinext-app-browser-entry"))) {
|
|
240
|
+
const injection = `import ${JSON.stringify(getEntryPath())};\n`;
|
|
241
|
+
if (code.includes(injection.trim())) {
|
|
242
|
+
clientInjected = true;
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
clientInjected = true;
|
|
246
|
+
return mapCodeToCodeWithSourcemap(injection + code);
|
|
247
|
+
}
|
|
144
248
|
if (injectEntry() && entryFiles.some((file) => id.endsWith(file)) || _command === "serve" && inject === "html" && !clientInjected && !id.includes("node_modules") && /\.(js|ts|mjs|vue|jsx|tsx)(\?|$)/.test(id)) {
|
|
145
249
|
clientInjected = true;
|
|
146
250
|
return mapCodeToCodeWithSourcemap(`import ${JSON.stringify(getEntryPath())};\n` + code);
|
|
@@ -214,7 +318,9 @@ function PluginDevProxyModuleTopLevelAwait() {
|
|
|
214
318
|
throw new Error(`${id}: ${e}`);
|
|
215
319
|
}
|
|
216
320
|
const magicString = new MagicString(code);
|
|
217
|
-
|
|
321
|
+
const walk = await loadWalk();
|
|
322
|
+
const defaultExportExpression = hasPackageDependency("vinext") ? "(__mfproxy__awaitdefault?.default ?? __mfproxy__awaitdefault)" : "__mfproxy__awaitdefault";
|
|
323
|
+
walk(ast, { enter(node) {
|
|
218
324
|
if (node.type === "ExportNamedDeclaration" && node.specifiers) {
|
|
219
325
|
const exportSpecifiers = node.specifiers.map((specifier) => specifier.exported.name);
|
|
220
326
|
const proxyStatements = exportSpecifiers.map((name) => `
|
|
@@ -235,15 +341,15 @@ function PluginDevProxyModuleTopLevelAwait() {
|
|
|
235
341
|
let exportStatement = "default";
|
|
236
342
|
if (declaration.type === "Identifier") proxyStatement = `
|
|
237
343
|
const __mfproxy__awaitdefault = await ${declaration.name}();
|
|
238
|
-
const __mfproxy__default =
|
|
344
|
+
const __mfproxy__default = ${defaultExportExpression};
|
|
239
345
|
`;
|
|
240
346
|
else if (declaration.type === "CallExpression" || declaration.type === "FunctionDeclaration") proxyStatement = `
|
|
241
347
|
const __mfproxy__awaitdefault = await (${code.slice(declaration.start, declaration.end)});
|
|
242
|
-
const __mfproxy__default =
|
|
348
|
+
const __mfproxy__default = ${defaultExportExpression};
|
|
243
349
|
`;
|
|
244
350
|
else proxyStatement = `
|
|
245
351
|
const __mfproxy__awaitdefault = await (${code.slice(declaration.start, declaration.end)});
|
|
246
|
-
const __mfproxy__default =
|
|
352
|
+
const __mfproxy__default = ${defaultExportExpression};
|
|
247
353
|
`;
|
|
248
354
|
const replacement = `${proxyStatement}\nexport { __mfproxy__default as ${exportStatement} };`;
|
|
249
355
|
magicString.overwrite(start, end, replacement);
|
|
@@ -525,10 +631,6 @@ function normalizeRemoteItem(key, remote) {
|
|
|
525
631
|
entryGlobalName: key
|
|
526
632
|
}, remote);
|
|
527
633
|
}
|
|
528
|
-
function removePathFromNpmPackage(packageString) {
|
|
529
|
-
const match = packageString.match(/^(?:@[^/]+\/)?[^/]+/);
|
|
530
|
-
return match ? match[0] : packageString;
|
|
531
|
-
}
|
|
532
634
|
/**
|
|
533
635
|
* Tries to find the package.json's version of a shared package
|
|
534
636
|
* if `package.json` is not declared in `exports`
|
|
@@ -537,13 +639,13 @@ function removePathFromNpmPackage(packageString) {
|
|
|
537
639
|
*/
|
|
538
640
|
function searchPackageVersion(sharedName) {
|
|
539
641
|
try {
|
|
540
|
-
const sharedPath =
|
|
642
|
+
const sharedPath = createRequire(process.cwd()).resolve(sharedName);
|
|
541
643
|
let potentialPackageJsonDir = path$1.dirname(sharedPath);
|
|
542
644
|
const rootDir = path$1.parse(potentialPackageJsonDir).root;
|
|
543
645
|
while (path$1.parse(potentialPackageJsonDir).base !== "node_modules" && potentialPackageJsonDir !== rootDir) {
|
|
544
646
|
const potentialPackageJsonPath = path$1.join(potentialPackageJsonDir, "package.json");
|
|
545
647
|
if (fs.existsSync(potentialPackageJsonPath)) {
|
|
546
|
-
const potentialPackageJson =
|
|
648
|
+
const potentialPackageJson = JSON.parse(fs.readFileSync(potentialPackageJsonPath, "utf-8"));
|
|
547
649
|
if (typeof potentialPackageJson == "object" && potentialPackageJson !== null && typeof potentialPackageJson.version === "string" && potentialPackageJson.name === sharedName) return potentialPackageJson.version;
|
|
548
650
|
}
|
|
549
651
|
potentialPackageJsonDir = path$1.dirname(potentialPackageJsonDir);
|
|
@@ -654,34 +756,6 @@ function normalizeModuleFederationOptions(options) {
|
|
|
654
756
|
};
|
|
655
757
|
}
|
|
656
758
|
//#endregion
|
|
657
|
-
//#region src/utils/packageNameUtils.ts
|
|
658
|
-
/**
|
|
659
|
-
* Escaping rules:
|
|
660
|
-
* Convert using the format __${mapping}__, where _ and $ are not allowed in npm package names but can be used in variable names.
|
|
661
|
-
* @ => 1
|
|
662
|
-
* / => 2
|
|
663
|
-
* - => 3
|
|
664
|
-
* . => 4
|
|
665
|
-
*/
|
|
666
|
-
/**
|
|
667
|
-
* Encodes a package name into a valid file name.
|
|
668
|
-
* @param {string} name - The package name, e.g., "@scope/xx-xx.xx".
|
|
669
|
-
* @returns {string} - The encoded file name.
|
|
670
|
-
*/
|
|
671
|
-
function packageNameEncode(name) {
|
|
672
|
-
if (typeof name !== "string") throw new Error("A string package name is required");
|
|
673
|
-
return name.replace(/@/g, "_mf_0_").replace(/\//g, "_mf_1_").replace(/-/g, "_mf_2_").replace(/\./g, "_mf_3_");
|
|
674
|
-
}
|
|
675
|
-
/**
|
|
676
|
-
* Decodes an encoded file name back to the original package name.
|
|
677
|
-
* @param {string} encoded - The encoded file name, e.g., "_mf_0_scope_mf_1_xx_mf_2_xx_mf_3_xx".
|
|
678
|
-
* @returns {string} - The decoded package name.
|
|
679
|
-
*/
|
|
680
|
-
function packageNameDecode(encoded) {
|
|
681
|
-
if (typeof encoded !== "string") throw new Error("A string encoded file name is required");
|
|
682
|
-
return encoded.replace(/_mf_0_/g, "@").replace(/_mf_1_/g, "/").replace(/_mf_2_/g, "-").replace(/_mf_3_/g, ".");
|
|
683
|
-
}
|
|
684
|
-
//#endregion
|
|
685
759
|
//#region src/utils/localSharedImportMap_temp.ts
|
|
686
760
|
/**
|
|
687
761
|
* https://github.com/module-federation/vite/issues/68
|
|
@@ -955,6 +1029,14 @@ function getPackageNamedExports(pkg) {
|
|
|
955
1029
|
return [];
|
|
956
1030
|
}
|
|
957
1031
|
}
|
|
1032
|
+
function getLocalProviderImportPath(pkg) {
|
|
1033
|
+
try {
|
|
1034
|
+
const resolved = createRequire$1(new URL("file://" + process.cwd() + "/package.json")).resolve(pkg);
|
|
1035
|
+
return resolved.includes("/node_modules/") || resolved.includes("\\node_modules\\") ? void 0 : resolved;
|
|
1036
|
+
} catch {
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
958
1040
|
const preBuildCacheMap = {};
|
|
959
1041
|
const PREBUILD_TAG = "__prebuild__";
|
|
960
1042
|
function writePreBuildLibPath(pkg) {
|
|
@@ -976,6 +1058,8 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
|
|
|
976
1058
|
const useESM = command === "build" || isRolldown;
|
|
977
1059
|
const importLine = useESM ? `import { initPromise } from "${virtualRuntimeInitStatus.getImportId()}"` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
|
|
978
1060
|
const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
|
|
1061
|
+
const useSsrProviderFallback = hasPackageDependency("vinext") && command === "build" && pkg === "react";
|
|
1062
|
+
const providerImportId = getLocalProviderImportPath(pkg) || getPreBuildLibImportId(pkg);
|
|
979
1063
|
const namedExports = getPackageNamedExports(pkg);
|
|
980
1064
|
let exportLine;
|
|
981
1065
|
if (namedExports.length > 0) {
|
|
@@ -987,6 +1071,9 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
|
|
|
987
1071
|
import ${JSON.stringify(getPreBuildLibImportId(pkg))};
|
|
988
1072
|
${command !== "build" ? `;() => import(${JSON.stringify(pkg)}).catch(() => {});` : ""}
|
|
989
1073
|
${importLine}
|
|
1074
|
+
${useSsrProviderFallback ? `const providerModulePromise = typeof window === "undefined"
|
|
1075
|
+
? import(${JSON.stringify(providerImportId)})
|
|
1076
|
+
: undefined` : ""}
|
|
990
1077
|
const res = initPromise.then(runtime => runtime.loadShare(${JSON.stringify(pkg)}, {
|
|
991
1078
|
customShareInfo: {shareConfig:{
|
|
992
1079
|
singleton: ${shareItem.shareConfig.singleton},
|
|
@@ -994,7 +1081,9 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
|
|
|
994
1081
|
requiredVersion: ${JSON.stringify(shareItem.shareConfig.requiredVersion)}
|
|
995
1082
|
}}
|
|
996
1083
|
}))
|
|
997
|
-
const exportModule = ${
|
|
1084
|
+
const exportModule = ${useSsrProviderFallback ? `(typeof window === "undefined"
|
|
1085
|
+
? ((await providerModulePromise)?.default ?? await providerModulePromise)
|
|
1086
|
+
: ${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory)))` : `${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory))`}
|
|
998
1087
|
${exportLine}
|
|
999
1088
|
`);
|
|
1000
1089
|
}
|
|
@@ -1020,6 +1109,7 @@ function writeLocalSharedImportMap() {
|
|
|
1020
1109
|
}
|
|
1021
1110
|
}
|
|
1022
1111
|
function generateLocalSharedImportMap() {
|
|
1112
|
+
const isVinext = hasPackageDependency("vinext");
|
|
1023
1113
|
const options = getNormalizeModuleFederationOptions();
|
|
1024
1114
|
return `
|
|
1025
1115
|
import {loadShare} from "@module-federation/runtime";
|
|
@@ -1028,7 +1118,8 @@ function generateLocalSharedImportMap() {
|
|
|
1028
1118
|
const shareItem = getNormalizeShareItem(pkg);
|
|
1029
1119
|
return `
|
|
1030
1120
|
${JSON.stringify(pkg)}: async () => {
|
|
1031
|
-
${shareItem?.shareConfig.import === false ? `throw new Error(\`Shared module '\${${JSON.stringify(pkg)}}' must be provided by host\`);` : `let pkg = await import("
|
|
1121
|
+
${shareItem?.shareConfig.import === false ? `throw new Error(\`Shared module '\${${JSON.stringify(pkg)}}' must be provided by host\`);` : isVinext && pkg === "react" ? `let pkg = await import("react");
|
|
1122
|
+
return pkg;` : `let pkg = await import("${getPreBuildLibImportId(pkg)}");
|
|
1032
1123
|
return pkg;`}
|
|
1033
1124
|
}
|
|
1034
1125
|
`;
|
|
@@ -1052,7 +1143,9 @@ function generateLocalSharedImportMap() {
|
|
|
1052
1143
|
usedShared[${JSON.stringify(key)}].loaded = true
|
|
1053
1144
|
const {${JSON.stringify(key)}: pkgDynamicImport} = importMap
|
|
1054
1145
|
const res = await pkgDynamicImport()
|
|
1055
|
-
const exportModule = {
|
|
1146
|
+
const exportModule = ${JSON.stringify(isVinext)} && ${JSON.stringify(key)} === "react"
|
|
1147
|
+
? (res?.default ?? res)
|
|
1148
|
+
: {...res}
|
|
1056
1149
|
// All npm packages pre-built by vite will be converted to esm
|
|
1057
1150
|
Object.defineProperty(exportModule, "__esModule", {
|
|
1058
1151
|
value: true,
|
|
@@ -1134,6 +1227,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
|
|
|
1134
1227
|
if (initScope.indexOf(initToken) >= 0) return;
|
|
1135
1228
|
initScope.push(initToken);
|
|
1136
1229
|
initRes.initShareScopeMap('${options.shareScope}', shared);
|
|
1230
|
+
initResolve(initRes)
|
|
1137
1231
|
try {
|
|
1138
1232
|
await Promise.all(await initRes.initializeSharing('${options.shareScope}', {
|
|
1139
1233
|
strategy: '${options.shareStrategy}',
|
|
@@ -1143,7 +1237,6 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
|
|
|
1143
1237
|
} catch (e) {
|
|
1144
1238
|
console.error(e)
|
|
1145
1239
|
}
|
|
1146
|
-
initResolve(initRes)
|
|
1147
1240
|
return initRes
|
|
1148
1241
|
}
|
|
1149
1242
|
|
|
@@ -1161,11 +1254,11 @@ const hostAutoInitModule = new VirtualModule("hostAutoInit", "__H_A_I__");
|
|
|
1161
1254
|
function writeHostAutoInit(remoteEntryId = REMOTE_ENTRY_ID) {
|
|
1162
1255
|
hostAutoInitModule.writeSync(`
|
|
1163
1256
|
const remoteEntryPromise = import("${remoteEntryId}")
|
|
1164
|
-
// __tla only serves as a hack for vite-plugin-top-level-await.
|
|
1165
1257
|
Promise.resolve(remoteEntryPromise)
|
|
1166
1258
|
.then(remoteEntry => {
|
|
1167
1259
|
return Promise.resolve(remoteEntry.__tla)
|
|
1168
|
-
.then(remoteEntry.init)
|
|
1260
|
+
.then(remoteEntry.init)
|
|
1261
|
+
.catch(remoteEntry.init)
|
|
1169
1262
|
})
|
|
1170
1263
|
`);
|
|
1171
1264
|
}
|
|
@@ -1745,6 +1838,7 @@ function proxySharedModule(options) {
|
|
|
1745
1838
|
const { shared = {} } = options;
|
|
1746
1839
|
let _config;
|
|
1747
1840
|
let _command = "serve";
|
|
1841
|
+
let isVinext = false;
|
|
1748
1842
|
const savePrebuild = new PromiseStore();
|
|
1749
1843
|
return [{
|
|
1750
1844
|
name: "generateLocalSharedImportMap",
|
|
@@ -1759,9 +1853,11 @@ function proxySharedModule(options) {
|
|
|
1759
1853
|
name: "proxyPreBuildShared",
|
|
1760
1854
|
enforce: "post",
|
|
1761
1855
|
config(config, { command }) {
|
|
1856
|
+
setPackageDetectionCwd(config.root || process.cwd());
|
|
1857
|
+
isVinext = hasPackageDependency("vinext");
|
|
1762
1858
|
const isRolldown = !!this?.meta?.rolldownVersion;
|
|
1763
1859
|
_command = command;
|
|
1764
|
-
config.resolve.alias.push(...Object.keys(shared).map((key) => {
|
|
1860
|
+
config.resolve.alias.push(...Object.keys(shared).filter((key) => !(isVinext && key === "react")).map((key) => {
|
|
1765
1861
|
const keyBase = key.endsWith("/") ? key.slice(0, -1) : key;
|
|
1766
1862
|
const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1767
1863
|
const escapedKeyBase = escapeRegex(keyBase);
|
|
@@ -1771,6 +1867,7 @@ function proxySharedModule(options) {
|
|
|
1771
1867
|
replacement: "$1",
|
|
1772
1868
|
customResolver(source, importer) {
|
|
1773
1869
|
if (/\.css$/.test(source)) return;
|
|
1870
|
+
if (isVinext && source === "react") return;
|
|
1774
1871
|
if (importer && importer.includes("localSharedImportMap")) return;
|
|
1775
1872
|
if (key.endsWith("/") && source !== key.slice(0, -1)) return;
|
|
1776
1873
|
const loadSharePath = getLoadShareModulePath(source, isRolldown, command);
|
|
@@ -1782,7 +1879,7 @@ function proxySharedModule(options) {
|
|
|
1782
1879
|
}
|
|
1783
1880
|
};
|
|
1784
1881
|
}));
|
|
1785
|
-
config.resolve.alias.push(...Object.keys(shared).map((key) => {
|
|
1882
|
+
config.resolve.alias.push(...Object.keys(shared).filter((key) => !(isVinext && key === "react")).map((key) => {
|
|
1786
1883
|
return command === "build" ? {
|
|
1787
1884
|
find: new RegExp(`(.*${PREBUILD_TAG}.*)`),
|
|
1788
1885
|
replacement: function($1) {
|
|
@@ -1805,6 +1902,10 @@ function proxySharedModule(options) {
|
|
|
1805
1902
|
const isRolldown = !!config.experimental?.rolldownDev;
|
|
1806
1903
|
Object.keys(shared).forEach((key) => {
|
|
1807
1904
|
if (key.endsWith("/")) return;
|
|
1905
|
+
if (isVinext && key === "react") {
|
|
1906
|
+
addUsedShares(key);
|
|
1907
|
+
return;
|
|
1908
|
+
}
|
|
1808
1909
|
writeLoadShareModule(key, shared[key], _command, isRolldown);
|
|
1809
1910
|
writePreBuildLibPath(key);
|
|
1810
1911
|
addUsedShares(key);
|
|
@@ -1936,6 +2037,8 @@ function createEarlyVirtualModulesPlugin(options) {
|
|
|
1936
2037
|
enforce: "pre",
|
|
1937
2038
|
config(config, { command: _command }) {
|
|
1938
2039
|
const root = config.root || process.cwd();
|
|
2040
|
+
setPackageDetectionCwd(root);
|
|
2041
|
+
const isVinext = hasPackageDependency("vinext");
|
|
1939
2042
|
initVirtualModuleInfrastructure(root, virtualModuleDir);
|
|
1940
2043
|
VirtualModule.setRoot(root);
|
|
1941
2044
|
VirtualModule.ensureVirtualPackageExists();
|
|
@@ -1950,6 +2053,10 @@ function createEarlyVirtualModulesPlugin(options) {
|
|
|
1950
2053
|
for (const key of Object.keys(shared)) {
|
|
1951
2054
|
if (key.endsWith("/")) continue;
|
|
1952
2055
|
const shareItem = shared[key];
|
|
2056
|
+
if (isVinext && key === "react") {
|
|
2057
|
+
addUsedShares(key);
|
|
2058
|
+
continue;
|
|
2059
|
+
}
|
|
1953
2060
|
getLoadShareModulePath(key, isRolldown);
|
|
1954
2061
|
writeLoadShareModule(key, shareItem, _command, isRolldown);
|
|
1955
2062
|
writePreBuildLibPath(key);
|
|
@@ -1963,6 +2070,7 @@ function createEarlyVirtualModulesPlugin(options) {
|
|
|
1963
2070
|
}
|
|
1964
2071
|
function federation(mfUserOptions) {
|
|
1965
2072
|
const options = normalizeModuleFederationOptions(mfUserOptions);
|
|
2073
|
+
const isVinext = hasPackageDependency("vinext");
|
|
1966
2074
|
const { name, remotes, shared, filename, hostInitInjectLocation } = options;
|
|
1967
2075
|
if (!name) throw new Error("name is required");
|
|
1968
2076
|
const remoteEntryId = getRemoteEntryId(options);
|
|
@@ -1970,6 +2078,23 @@ function federation(mfUserOptions) {
|
|
|
1970
2078
|
let command;
|
|
1971
2079
|
return [
|
|
1972
2080
|
createEarlyVirtualModulesPlugin(options),
|
|
2081
|
+
...isVinext ? [{
|
|
2082
|
+
name: "module-federation-vinext-react-server-build-alias",
|
|
2083
|
+
apply: "build",
|
|
2084
|
+
enforce: "pre",
|
|
2085
|
+
resolveId(id) {
|
|
2086
|
+
const reactServerEntryMap = {
|
|
2087
|
+
"react/jsx-runtime": "react/cjs/react-jsx-runtime.production.js",
|
|
2088
|
+
"react/jsx-dev-runtime": "react/cjs/react-jsx-dev-runtime.production.js"
|
|
2089
|
+
};
|
|
2090
|
+
if (!(id in reactServerEntryMap)) return;
|
|
2091
|
+
const environmentName = this?.environment?.name;
|
|
2092
|
+
if (!environmentName || environmentName === "client") return;
|
|
2093
|
+
const target = reactServerEntryMap[id];
|
|
2094
|
+
const reactPackageJson = createRequire$1(new URL(`file://${process.cwd()}/package.json`)).resolve("react/package.json");
|
|
2095
|
+
return path.join(path.dirname(reactPackageJson), target.replace(/^react\//, ""));
|
|
2096
|
+
}
|
|
2097
|
+
}] : [],
|
|
1973
2098
|
{
|
|
1974
2099
|
name: "vite:module-federation-config",
|
|
1975
2100
|
enforce: "pre",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@module-federation/vite",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "Vite plugin for Module Federation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.cjs",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@module-federation/dts-plugin": "2.0
|
|
69
|
+
"@module-federation/dts-plugin": "2.2.0",
|
|
70
70
|
"@module-federation/runtime": "2.0.1",
|
|
71
71
|
"@module-federation/sdk": "2.0.1",
|
|
72
72
|
"@rollup/pluginutils": "^5.3.0",
|
|
@@ -88,4 +88,4 @@
|
|
|
88
88
|
"vite": "^7.3.1",
|
|
89
89
|
"vitest": "^4.0.18"
|
|
90
90
|
}
|
|
91
|
-
}
|
|
91
|
+
}
|