@module-federation/vite 1.13.0 → 1.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.cjs +228 -42
- package/lib/index.mjs +228 -42
- package/package.json +5 -5
package/lib/index.cjs
CHANGED
|
@@ -206,6 +206,12 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
|
|
|
206
206
|
else if (Array.isArray(inputOptions)) entryFiles = inputOptions;
|
|
207
207
|
else if (typeof inputOptions === "object") entryFiles = Object.values(inputOptions);
|
|
208
208
|
if (entryFiles && entryFiles.length > 0) htmlFilePath = getFirstHtmlEntryFile(entryFiles);
|
|
209
|
+
if (_command === "serve" && htmlFilePath && fs.existsSync(htmlFilePath)) {
|
|
210
|
+
const htmlContent = fs.readFileSync(htmlFilePath, "utf-8");
|
|
211
|
+
const scriptRegex = /<script\s+[^>]*src=["']([^"']+)["'][^>]*>/gi;
|
|
212
|
+
let match;
|
|
213
|
+
while ((match = scriptRegex.exec(htmlContent)) !== null) entryFiles.push(match[1]);
|
|
214
|
+
}
|
|
209
215
|
},
|
|
210
216
|
buildStart() {
|
|
211
217
|
if (_command === "serve") return;
|
|
@@ -674,6 +680,10 @@ function searchPackageVersion(sharedName) {
|
|
|
674
680
|
}
|
|
675
681
|
} catch (_) {}
|
|
676
682
|
}
|
|
683
|
+
function inferVersionFromRequiredVersion(requiredVersion) {
|
|
684
|
+
if (!requiredVersion) return void 0;
|
|
685
|
+
return requiredVersion.match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/)?.[0];
|
|
686
|
+
}
|
|
677
687
|
function normalizeShareItem(key, shareItem) {
|
|
678
688
|
let version;
|
|
679
689
|
try {
|
|
@@ -705,7 +715,7 @@ function normalizeShareItem(key, shareItem) {
|
|
|
705
715
|
return {
|
|
706
716
|
name: key,
|
|
707
717
|
from: "",
|
|
708
|
-
version: shareItem.version || version,
|
|
718
|
+
version: shareItem.version || inferVersionFromRequiredVersion(shareItem.requiredVersion) || version,
|
|
709
719
|
scope: shareItem.shareScope || "default",
|
|
710
720
|
shareConfig: {
|
|
711
721
|
import: typeof shareItem === "object" ? shareItem.import : void 0,
|
|
@@ -972,12 +982,12 @@ function generateExposes(options) {
|
|
|
972
982
|
//#endregion
|
|
973
983
|
//#region src/virtualModules/virtualRuntimeInitStatus.ts
|
|
974
984
|
const virtualRuntimeInitStatus = new VirtualModule("runtimeInit");
|
|
975
|
-
function
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
const globalKey = ${JSON.stringify(
|
|
985
|
+
function getRuntimeInitGlobalKey() {
|
|
986
|
+
return `__mf_init__${virtualRuntimeInitStatus.getImportId()}__`;
|
|
987
|
+
}
|
|
988
|
+
function getRuntimeInitBootstrapCode() {
|
|
989
|
+
return `
|
|
990
|
+
const globalKey = ${JSON.stringify(getRuntimeInitGlobalKey())};
|
|
981
991
|
if (!globalThis[globalKey]) {
|
|
982
992
|
let initResolve, initReject;
|
|
983
993
|
const initPromise = new Promise((re, rj) => {
|
|
@@ -989,8 +999,6 @@ if (!globalThis[globalKey]) {
|
|
|
989
999
|
initResolve,
|
|
990
1000
|
initReject,
|
|
991
1001
|
};
|
|
992
|
-
// In SSR (no window), resolve immediately with a stub runtime
|
|
993
|
-
// so modules don't hang waiting for browser-only init
|
|
994
1002
|
if (typeof window === 'undefined') {
|
|
995
1003
|
initResolve({
|
|
996
1004
|
loadRemote: function() { return Promise.resolve(undefined); },
|
|
@@ -998,6 +1006,64 @@ if (!globalThis[globalKey]) {
|
|
|
998
1006
|
});
|
|
999
1007
|
}
|
|
1000
1008
|
}
|
|
1009
|
+
`;
|
|
1010
|
+
}
|
|
1011
|
+
function getRuntimeInitPromiseBootstrapCode() {
|
|
1012
|
+
return `
|
|
1013
|
+
const __mfPromiseGlobalKey = ${JSON.stringify(getRuntimeInitGlobalKey())};
|
|
1014
|
+
let __mfPromiseState = globalThis[__mfPromiseGlobalKey];
|
|
1015
|
+
if (!__mfPromiseState) {
|
|
1016
|
+
let initResolve, initReject;
|
|
1017
|
+
const initPromise = new Promise((re, rj) => {
|
|
1018
|
+
initResolve = re;
|
|
1019
|
+
initReject = rj;
|
|
1020
|
+
});
|
|
1021
|
+
__mfPromiseState = globalThis[__mfPromiseGlobalKey] = {
|
|
1022
|
+
initPromise,
|
|
1023
|
+
initResolve,
|
|
1024
|
+
initReject,
|
|
1025
|
+
};
|
|
1026
|
+
if (typeof window === 'undefined') {
|
|
1027
|
+
initResolve({
|
|
1028
|
+
loadRemote: function() { return Promise.resolve(undefined); },
|
|
1029
|
+
loadShare: function() { return Promise.resolve(undefined); },
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
const initPromise = __mfPromiseState.initPromise;
|
|
1034
|
+
`;
|
|
1035
|
+
}
|
|
1036
|
+
function getRuntimeInitResolveBootstrapCode() {
|
|
1037
|
+
return `
|
|
1038
|
+
const __mfResolveGlobalKey = ${JSON.stringify(getRuntimeInitGlobalKey())};
|
|
1039
|
+
let __mfResolveState = globalThis[__mfResolveGlobalKey];
|
|
1040
|
+
if (!__mfResolveState) {
|
|
1041
|
+
let initResolve, initReject;
|
|
1042
|
+
const initPromise = new Promise((re, rj) => {
|
|
1043
|
+
initResolve = re;
|
|
1044
|
+
initReject = rj;
|
|
1045
|
+
});
|
|
1046
|
+
__mfResolveState = globalThis[__mfResolveGlobalKey] = {
|
|
1047
|
+
initPromise,
|
|
1048
|
+
initResolve,
|
|
1049
|
+
initReject,
|
|
1050
|
+
};
|
|
1051
|
+
if (typeof window === 'undefined') {
|
|
1052
|
+
initResolve({
|
|
1053
|
+
loadRemote: function() { return Promise.resolve(undefined); },
|
|
1054
|
+
loadShare: function() { return Promise.resolve(undefined); },
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
const initResolve = __mfResolveState.initResolve;
|
|
1059
|
+
`;
|
|
1060
|
+
}
|
|
1061
|
+
function writeRuntimeInitStatus(command) {
|
|
1062
|
+
getRuntimeInitGlobalKey();
|
|
1063
|
+
const exportStatement = command === "build" ? `const { initPromise, initResolve, initReject } = globalThis[globalKey];
|
|
1064
|
+
export { initPromise, initResolve, initReject };` : `module.exports = globalThis[globalKey];`;
|
|
1065
|
+
virtualRuntimeInitStatus.writeSync(`
|
|
1066
|
+
${getRuntimeInitBootstrapCode()}
|
|
1001
1067
|
${exportStatement}
|
|
1002
1068
|
`);
|
|
1003
1069
|
}
|
|
@@ -1022,7 +1088,8 @@ function getUsedRemotesMap() {
|
|
|
1022
1088
|
}
|
|
1023
1089
|
function generateRemotes(id, command, isRolldown) {
|
|
1024
1090
|
const useESM = command === "build" || isRolldown;
|
|
1025
|
-
const importLine =
|
|
1091
|
+
const importLine = command === "build" ? getRuntimeInitPromiseBootstrapCode() : useESM ? `${getRuntimeInitBootstrapCode()}
|
|
1092
|
+
const { initPromise } = globalThis[globalKey];` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
|
|
1026
1093
|
const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
|
|
1027
1094
|
const exportLine = command === "serve" && useESM ? "export default exportModule.default ?? exportModule" : useESM ? "export default exportModule" : "module.exports = exportModule";
|
|
1028
1095
|
return `
|
|
@@ -1079,7 +1146,8 @@ function getLoadShareModulePath(pkg, isRolldown, command) {
|
|
|
1079
1146
|
function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
|
|
1080
1147
|
if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, isRolldown || command === "build" ? ".mjs" : ".js");
|
|
1081
1148
|
const useESM = command === "build" || isRolldown;
|
|
1082
|
-
const importLine =
|
|
1149
|
+
const importLine = command === "build" ? getRuntimeInitPromiseBootstrapCode() : useESM ? `${getRuntimeInitBootstrapCode()}
|
|
1150
|
+
const { initPromise } = globalThis[globalKey];` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
|
|
1083
1151
|
const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
|
|
1084
1152
|
const useSsrProviderFallback = hasPackageDependency("vinext") && command === "build" && pkg === "react";
|
|
1085
1153
|
const providerImportId = getLocalProviderImportPath(pkg) || getPreBuildLibImportId(pkg);
|
|
@@ -1123,12 +1191,12 @@ new VirtualModule("localSharedImportMap");
|
|
|
1123
1191
|
function getLocalSharedImportMapPath() {
|
|
1124
1192
|
return getLocalSharedImportMapPath_temp();
|
|
1125
1193
|
}
|
|
1126
|
-
let
|
|
1194
|
+
let prevLocalSharedImportMapContent;
|
|
1127
1195
|
function writeLocalSharedImportMap() {
|
|
1128
|
-
const
|
|
1129
|
-
if (
|
|
1130
|
-
|
|
1131
|
-
writeLocalSharedImportMap_temp(
|
|
1196
|
+
const nextContent = generateLocalSharedImportMap();
|
|
1197
|
+
if (prevLocalSharedImportMapContent !== nextContent) {
|
|
1198
|
+
prevLocalSharedImportMapContent = nextContent;
|
|
1199
|
+
writeLocalSharedImportMap_temp(nextContent);
|
|
1132
1200
|
}
|
|
1133
1201
|
}
|
|
1134
1202
|
function generateLocalSharedImportMap() {
|
|
@@ -1211,7 +1279,7 @@ const REMOTE_ENTRY_ID = "virtual:mf-REMOTE_ENTRY_ID";
|
|
|
1211
1279
|
function getRemoteEntryId(options) {
|
|
1212
1280
|
return `${REMOTE_ENTRY_ID}:${`${options.name}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
|
|
1213
1281
|
}
|
|
1214
|
-
function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(options)) {
|
|
1282
|
+
function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(options), command = "build") {
|
|
1215
1283
|
const pluginImportNames = options.runtimePlugins.map((p, i) => {
|
|
1216
1284
|
if (typeof p === "string") return [
|
|
1217
1285
|
`$runtimePlugin_${i}`,
|
|
@@ -1227,15 +1295,25 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
|
|
|
1227
1295
|
return `
|
|
1228
1296
|
import {init as runtimeInit, loadRemote} from "@module-federation/runtime";
|
|
1229
1297
|
${pluginImportNames.map((item) => item[1]).join("\n")}
|
|
1230
|
-
|
|
1231
|
-
import {usedShared, usedRemotes} from "${getLocalSharedImportMapPath()}"
|
|
1232
|
-
import {
|
|
1233
|
-
initResolve
|
|
1234
|
-
} from "${virtualRuntimeInitStatus.getImportId()}"
|
|
1298
|
+
${command === "build" ? getRuntimeInitResolveBootstrapCode() : getRuntimeInitBootstrapCode() + "\n const { initResolve } = globalThis[globalKey];"}
|
|
1235
1299
|
const initTokens = {}
|
|
1236
1300
|
const shareScopeName = ${JSON.stringify(options.shareScope)}
|
|
1237
1301
|
const mfName = ${JSON.stringify(options.name)}
|
|
1302
|
+
let localSharedImportMapPromise
|
|
1303
|
+
let exposesMapPromise
|
|
1304
|
+
|
|
1305
|
+
async function getLocalSharedImportMap() {
|
|
1306
|
+
localSharedImportMapPromise ??= import("${getLocalSharedImportMapPath()}")
|
|
1307
|
+
return localSharedImportMapPromise
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
async function getExposesMap() {
|
|
1311
|
+
exposesMapPromise ??= import("${virtualExposesId}").then((mod) => mod.default ?? mod)
|
|
1312
|
+
return exposesMapPromise
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1238
1315
|
async function init(shared = {}, initScope = []) {
|
|
1316
|
+
const {usedShared, usedRemotes} = await getLocalSharedImportMap()
|
|
1239
1317
|
const initRes = runtimeInit({
|
|
1240
1318
|
name: mfName,
|
|
1241
1319
|
remotes: usedRemotes,
|
|
@@ -1263,7 +1341,8 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
|
|
|
1263
1341
|
return initRes
|
|
1264
1342
|
}
|
|
1265
1343
|
|
|
1266
|
-
function getExposes(moduleName) {
|
|
1344
|
+
async function getExposes(moduleName) {
|
|
1345
|
+
const exposesMap = await getExposesMap()
|
|
1267
1346
|
if (!(moduleName in exposesMap)) throw new Error(\`Module \${moduleName} does not exist in container.\`)
|
|
1268
1347
|
return (exposesMap[moduleName])().then(res => () => res)
|
|
1269
1348
|
}
|
|
@@ -1276,13 +1355,8 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
|
|
|
1276
1355
|
const hostAutoInitModule = new VirtualModule("hostAutoInit", "__H_A_I__");
|
|
1277
1356
|
function writeHostAutoInit(remoteEntryId = REMOTE_ENTRY_ID) {
|
|
1278
1357
|
hostAutoInitModule.writeSync(`
|
|
1279
|
-
const
|
|
1280
|
-
|
|
1281
|
-
.then(remoteEntry => {
|
|
1282
|
-
return Promise.resolve(remoteEntry.__tla)
|
|
1283
|
-
.then(remoteEntry.init)
|
|
1284
|
-
.catch(remoteEntry.init)
|
|
1285
|
-
})
|
|
1358
|
+
const remoteEntry = await import("${remoteEntryId}");
|
|
1359
|
+
await remoteEntry.init();
|
|
1286
1360
|
`);
|
|
1287
1361
|
}
|
|
1288
1362
|
function getHostAutoInitImportId() {
|
|
@@ -1305,13 +1379,21 @@ function initVirtualModules(command, remoteEntryId) {
|
|
|
1305
1379
|
* If Rollup's deconflict renamed the alias but didn't update references
|
|
1306
1380
|
* in the code body, fall back to proxyLocal so they stay in sync.
|
|
1307
1381
|
*/
|
|
1308
|
-
function resolveProxyAlias(binding, proxyLocal, code, fullImport) {
|
|
1382
|
+
function resolveProxyAlias(binding, proxyLocal, code, fullImport, claimedLocals = /* @__PURE__ */ new Set()) {
|
|
1309
1383
|
const codeWithoutImport = code.replace(fullImport, "");
|
|
1310
1384
|
const escapedLocal = binding.local.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1311
1385
|
const localUsedInCode = new RegExp(`\\b${escapedLocal}\\b`).test(codeWithoutImport);
|
|
1386
|
+
const claimedImportLocals = /* @__PURE__ */ new Set();
|
|
1387
|
+
const importRe = /import\s*\{([^}]+)\}\s*from\s*["'][^"']+["']\s*;?/g;
|
|
1388
|
+
let match;
|
|
1389
|
+
while ((match = importRe.exec(codeWithoutImport)) !== null) for (const spec of match[1].split(",")) {
|
|
1390
|
+
const parts = spec.trim().split(/\s+as\s+/);
|
|
1391
|
+
claimedImportLocals.add((parts[1] || parts[0]).trim());
|
|
1392
|
+
}
|
|
1393
|
+
const local = !localUsedInCode && !claimedLocals.has(proxyLocal) && !claimedImportLocals.has(proxyLocal) ? proxyLocal : binding.local;
|
|
1312
1394
|
return {
|
|
1313
1395
|
imported: binding.imported,
|
|
1314
|
-
local
|
|
1396
|
+
local
|
|
1315
1397
|
};
|
|
1316
1398
|
}
|
|
1317
1399
|
function findRemoteEntryFile(filename, bundle) {
|
|
@@ -1766,14 +1848,14 @@ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposes
|
|
|
1766
1848
|
}
|
|
1767
1849
|
},
|
|
1768
1850
|
load(id) {
|
|
1769
|
-
if (id === remoteEntryId) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId));
|
|
1851
|
+
if (id === remoteEntryId) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId, _command));
|
|
1770
1852
|
if (id === virtualExposesId) return generateExposes(options);
|
|
1771
1853
|
if (_command === "serve" && id.includes(getHostAutoInitPath())) return id;
|
|
1772
1854
|
},
|
|
1773
1855
|
transform(code, id) {
|
|
1774
1856
|
return mapCodeToCodeWithSourcemap((() => {
|
|
1775
1857
|
if (!filter(id)) return;
|
|
1776
|
-
if (id.includes(remoteEntryId)) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId));
|
|
1858
|
+
if (id.includes(remoteEntryId)) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId, _command));
|
|
1777
1859
|
if (id === virtualExposesId) return generateExposes(options);
|
|
1778
1860
|
if (id.includes(getHostAutoInitPath())) {
|
|
1779
1861
|
if (_command === "serve") {
|
|
@@ -2032,6 +2114,58 @@ var aliasToArrayPlugin_default = {
|
|
|
2032
2114
|
}
|
|
2033
2115
|
};
|
|
2034
2116
|
//#endregion
|
|
2117
|
+
//#region src/utils/controlChunkSanitizer.ts
|
|
2118
|
+
const FEDERATION_CONTROL_CHUNK_HINTS = [
|
|
2119
|
+
"hostInit",
|
|
2120
|
+
"virtualExposes",
|
|
2121
|
+
"localSharedImportMap"
|
|
2122
|
+
];
|
|
2123
|
+
function stripEmptyPreloadCalls(code) {
|
|
2124
|
+
const helperImportRegex = /import\s*\{\s*_\s*as\s*(\w+)\s*\}\s*from\s*["'][^"']+["']\s*;?/g;
|
|
2125
|
+
const helperAliases = [...code.matchAll(helperImportRegex)].map((match) => match[1]);
|
|
2126
|
+
let nextCode = code;
|
|
2127
|
+
for (const alias of helperAliases) {
|
|
2128
|
+
const marker = `${alias}(()=>`;
|
|
2129
|
+
let start = nextCode.indexOf(marker);
|
|
2130
|
+
while (start !== -1) {
|
|
2131
|
+
const exprStart = start + marker.length;
|
|
2132
|
+
let depth = 0;
|
|
2133
|
+
let cursor = exprStart;
|
|
2134
|
+
let replacementEnd = -1;
|
|
2135
|
+
while (cursor < nextCode.length) {
|
|
2136
|
+
const char = nextCode[cursor];
|
|
2137
|
+
if (char === "(") depth++;
|
|
2138
|
+
else if (char === ")") depth--;
|
|
2139
|
+
else if (depth === 0 && nextCode.startsWith(",[],import.meta.url)", cursor)) {
|
|
2140
|
+
replacementEnd = cursor;
|
|
2141
|
+
break;
|
|
2142
|
+
}
|
|
2143
|
+
cursor++;
|
|
2144
|
+
}
|
|
2145
|
+
if (replacementEnd === -1) break;
|
|
2146
|
+
const expression = nextCode.slice(exprStart, replacementEnd);
|
|
2147
|
+
nextCode = nextCode.slice(0, start) + expression + nextCode.slice(replacementEnd + 20);
|
|
2148
|
+
start = nextCode.indexOf(marker, start + expression.length);
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
nextCode = nextCode.replace(/import\s*["'][^"']*__loadShare__[^"']*["']\s*;?/g, "");
|
|
2152
|
+
nextCode = nextCode.replace(helperImportRegex, (statement, local) => {
|
|
2153
|
+
return new RegExp(`\\b${local}\\s*\\(`).test(nextCode.replace(statement, "")) ? statement : "";
|
|
2154
|
+
});
|
|
2155
|
+
return nextCode;
|
|
2156
|
+
}
|
|
2157
|
+
function isFederationControlChunk(fileName, filename) {
|
|
2158
|
+
return fileName.includes(filename) || FEDERATION_CONTROL_CHUNK_HINTS.some((hint) => fileName.includes(hint));
|
|
2159
|
+
}
|
|
2160
|
+
function sanitizeFederationControlChunk(code, fileName, filename) {
|
|
2161
|
+
let nextCode = stripEmptyPreloadCalls(code);
|
|
2162
|
+
if (fileName.includes("localSharedImportMap")) {
|
|
2163
|
+
const remoteEntryImportRegex = new RegExp(`import\\s*["'][^"']*${filename.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["']\\s*;?`, "g");
|
|
2164
|
+
nextCode = nextCode.replace(remoteEntryImportRegex, "");
|
|
2165
|
+
}
|
|
2166
|
+
return nextCode;
|
|
2167
|
+
}
|
|
2168
|
+
//#endregion
|
|
2035
2169
|
//#region src/utils/normalizeOptimizeDeps.ts
|
|
2036
2170
|
var normalizeOptimizeDeps_default = {
|
|
2037
2171
|
name: "normalizeOptimizeDeps",
|
|
@@ -2169,9 +2303,19 @@ function federation(mfUserOptions) {
|
|
|
2169
2303
|
config(config) {
|
|
2170
2304
|
const runtimeInitId = virtualRuntimeInitStatus.getImportId();
|
|
2171
2305
|
config.build = config.build || {};
|
|
2172
|
-
config.build.
|
|
2173
|
-
|
|
2174
|
-
const
|
|
2306
|
+
if (config.build.modulePreload !== false) {
|
|
2307
|
+
const currentModulePreload = config.build.modulePreload && typeof config.build.modulePreload === "object" ? config.build.modulePreload : {};
|
|
2308
|
+
const existingResolveDependencies = currentModulePreload.resolveDependencies;
|
|
2309
|
+
config.build.modulePreload = {
|
|
2310
|
+
...currentModulePreload,
|
|
2311
|
+
resolveDependencies(filename, deps, context) {
|
|
2312
|
+
const resolvedDeps = existingResolveDependencies ? existingResolveDependencies(filename, deps, context) : deps;
|
|
2313
|
+
const hostFile = pathe.default.basename(context.hostId);
|
|
2314
|
+
return context.hostType === "js" && (hostFile === options.filename || hostFile.includes("hostInit") || hostFile.includes("virtualExposes") || hostFile.includes("localSharedImportMap")) ? [] : resolvedDeps;
|
|
2315
|
+
}
|
|
2316
|
+
};
|
|
2317
|
+
}
|
|
2318
|
+
const applyManualChunks = (output) => {
|
|
2175
2319
|
const existingManualChunks = output.manualChunks;
|
|
2176
2320
|
output.manualChunks = function(id) {
|
|
2177
2321
|
if (id.includes(runtimeInitId)) return "runtimeInit";
|
|
@@ -2184,7 +2328,12 @@ function federation(mfUserOptions) {
|
|
|
2184
2328
|
for (const [key, ids] of Object.entries(existingManualChunks)) if (Array.isArray(ids) && ids.some((v) => id.includes(v))) return key;
|
|
2185
2329
|
}
|
|
2186
2330
|
};
|
|
2187
|
-
}
|
|
2331
|
+
};
|
|
2332
|
+
config.build.rollupOptions = config.build.rollupOptions || {};
|
|
2333
|
+
if (!Array.isArray(config.build.rollupOptions.output)) applyManualChunks(config.build.rollupOptions.output ||= {});
|
|
2334
|
+
const buildWithRolldown = config.build;
|
|
2335
|
+
buildWithRolldown.rolldownOptions = buildWithRolldown.rolldownOptions || {};
|
|
2336
|
+
if (!Array.isArray(buildWithRolldown.rolldownOptions.output)) applyManualChunks(buildWithRolldown.rolldownOptions.output ||= {});
|
|
2188
2337
|
},
|
|
2189
2338
|
load(id) {
|
|
2190
2339
|
if (id.startsWith("\0")) return;
|
|
@@ -2216,6 +2365,11 @@ function federation(mfUserOptions) {
|
|
|
2216
2365
|
}
|
|
2217
2366
|
},
|
|
2218
2367
|
generateBundle(_, bundle) {
|
|
2368
|
+
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
2369
|
+
if (chunk.type !== "chunk") continue;
|
|
2370
|
+
if (!isFederationControlChunk(fileName, filename)) continue;
|
|
2371
|
+
chunk.code = sanitizeFederationControlChunk(chunk.code, fileName, filename);
|
|
2372
|
+
}
|
|
2219
2373
|
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
2220
2374
|
if (chunk.type !== "chunk") continue;
|
|
2221
2375
|
if (fileName.includes("__loadShare__")) continue;
|
|
@@ -2253,12 +2407,12 @@ function federation(mfUserOptions) {
|
|
|
2253
2407
|
fileName
|
|
2254
2408
|
});
|
|
2255
2409
|
}
|
|
2256
|
-
if (proxyChunks.size
|
|
2257
|
-
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
2410
|
+
if (proxyChunks.size > 0) for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
2258
2411
|
if (chunk.type !== "chunk") continue;
|
|
2259
2412
|
if (fileName.includes("__loadShare__")) continue;
|
|
2260
2413
|
let code = chunk.code;
|
|
2261
2414
|
let modified = false;
|
|
2415
|
+
const claimedLocals = /* @__PURE__ */ new Set();
|
|
2262
2416
|
for (const [proxyFileName, proxyInfo] of proxyChunks) {
|
|
2263
2417
|
const proxyBaseName = proxyFileName.replace(/^.*\//, "").replace(/\.js$/, "").replace(/-[A-Za-z0-9_-]+$/, "");
|
|
2264
2418
|
const importMatch = new RegExp(`import\\s*\\{([^}]+)\\}\\s*from\\s*["']([^"']*${proxyBaseName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[^"']*)["']\\s*;?`).exec(code);
|
|
@@ -2281,9 +2435,12 @@ function federation(mfUserOptions) {
|
|
|
2281
2435
|
}
|
|
2282
2436
|
const inlineable = [];
|
|
2283
2437
|
const nonInlineable = [];
|
|
2438
|
+
const pendingLocals = new Set(bindings.map((binding) => binding.local));
|
|
2284
2439
|
for (const b of bindings) {
|
|
2440
|
+
pendingLocals.delete(b.local);
|
|
2285
2441
|
const proxyLocal = exportMap[b.imported];
|
|
2286
2442
|
if (!proxyLocal) {
|
|
2443
|
+
claimedLocals.add(b.local);
|
|
2287
2444
|
nonInlineable.push(b);
|
|
2288
2445
|
continue;
|
|
2289
2446
|
}
|
|
@@ -2305,7 +2462,14 @@ function federation(mfUserOptions) {
|
|
|
2305
2462
|
local: b.local,
|
|
2306
2463
|
funcBody: renamedFunc
|
|
2307
2464
|
});
|
|
2308
|
-
|
|
2465
|
+
claimedLocals.add(b.local);
|
|
2466
|
+
} else {
|
|
2467
|
+
const unavailableLocals = new Set(claimedLocals);
|
|
2468
|
+
pendingLocals.forEach((local) => unavailableLocals.add(local));
|
|
2469
|
+
const resolvedBinding = resolveProxyAlias(b, proxyLocal, code, fullImport, unavailableLocals);
|
|
2470
|
+
claimedLocals.add(resolvedBinding.local);
|
|
2471
|
+
nonInlineable.push(resolvedBinding);
|
|
2472
|
+
}
|
|
2309
2473
|
}
|
|
2310
2474
|
const hasRenamedAlias = nonInlineable.some((b) => bindings.find((ob) => ob.imported === b.imported)?.local !== b.local);
|
|
2311
2475
|
if (inlineable.length === 0 && !hasRenamedAlias) continue;
|
|
@@ -2319,6 +2483,28 @@ function federation(mfUserOptions) {
|
|
|
2319
2483
|
}
|
|
2320
2484
|
}
|
|
2321
2485
|
},
|
|
2486
|
+
{
|
|
2487
|
+
name: "module-federation-strip-empty-preload-helper",
|
|
2488
|
+
enforce: "post",
|
|
2489
|
+
apply: "build",
|
|
2490
|
+
renderChunk(code, chunk) {
|
|
2491
|
+
if (!isFederationControlChunk(chunk.fileName, filename)) return;
|
|
2492
|
+
const nextCode = sanitizeFederationControlChunk(code, chunk.fileName, filename);
|
|
2493
|
+
return nextCode === code ? null : {
|
|
2494
|
+
code: nextCode,
|
|
2495
|
+
map: null
|
|
2496
|
+
};
|
|
2497
|
+
},
|
|
2498
|
+
writeBundle(outputOptions, bundle) {
|
|
2499
|
+
if (!outputOptions.dir) return;
|
|
2500
|
+
for (const chunk of Object.values(bundle)) {
|
|
2501
|
+
if (chunk.type !== "chunk") continue;
|
|
2502
|
+
if (!isFederationControlChunk(chunk.fileName, filename)) continue;
|
|
2503
|
+
const outputPath = pathe.default.join(outputOptions.dir, chunk.fileName);
|
|
2504
|
+
(0, fs.writeFileSync)(outputPath, sanitizeFederationControlChunk((0, fs.readFileSync)(outputPath, "utf-8"), chunk.fileName, filename));
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
},
|
|
2322
2508
|
{
|
|
2323
2509
|
name: "module-federation-dev-await-shared-init",
|
|
2324
2510
|
apply: "serve",
|
|
@@ -2353,7 +2539,7 @@ function federation(mfUserOptions) {
|
|
|
2353
2539
|
config(config, { command: _command }) {
|
|
2354
2540
|
const isRolldown = !!this?.meta?.rolldownVersion;
|
|
2355
2541
|
let implementation = options.implementation;
|
|
2356
|
-
if (isRolldown) implementation = implementation.replace(/\.cjs\.
|
|
2542
|
+
if (isRolldown) implementation = implementation.replace(/\.cjs(\.js)?$/, ".js");
|
|
2357
2543
|
config.resolve.alias.push({
|
|
2358
2544
|
find: "@module-federation/runtime",
|
|
2359
2545
|
replacement: implementation
|
package/lib/index.mjs
CHANGED
|
@@ -184,6 +184,12 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
|
|
|
184
184
|
else if (Array.isArray(inputOptions)) entryFiles = inputOptions;
|
|
185
185
|
else if (typeof inputOptions === "object") entryFiles = Object.values(inputOptions);
|
|
186
186
|
if (entryFiles && entryFiles.length > 0) htmlFilePath = getFirstHtmlEntryFile(entryFiles);
|
|
187
|
+
if (_command === "serve" && htmlFilePath && fs.existsSync(htmlFilePath)) {
|
|
188
|
+
const htmlContent = fs.readFileSync(htmlFilePath, "utf-8");
|
|
189
|
+
const scriptRegex = /<script\s+[^>]*src=["']([^"']+)["'][^>]*>/gi;
|
|
190
|
+
let match;
|
|
191
|
+
while ((match = scriptRegex.exec(htmlContent)) !== null) entryFiles.push(match[1]);
|
|
192
|
+
}
|
|
187
193
|
},
|
|
188
194
|
buildStart() {
|
|
189
195
|
if (_command === "serve") return;
|
|
@@ -652,6 +658,10 @@ function searchPackageVersion(sharedName) {
|
|
|
652
658
|
}
|
|
653
659
|
} catch (_) {}
|
|
654
660
|
}
|
|
661
|
+
function inferVersionFromRequiredVersion(requiredVersion) {
|
|
662
|
+
if (!requiredVersion) return void 0;
|
|
663
|
+
return requiredVersion.match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/)?.[0];
|
|
664
|
+
}
|
|
655
665
|
function normalizeShareItem(key, shareItem) {
|
|
656
666
|
let version;
|
|
657
667
|
try {
|
|
@@ -682,7 +692,7 @@ function normalizeShareItem(key, shareItem) {
|
|
|
682
692
|
return {
|
|
683
693
|
name: key,
|
|
684
694
|
from: "",
|
|
685
|
-
version: shareItem.version || version,
|
|
695
|
+
version: shareItem.version || inferVersionFromRequiredVersion(shareItem.requiredVersion) || version,
|
|
686
696
|
scope: shareItem.shareScope || "default",
|
|
687
697
|
shareConfig: {
|
|
688
698
|
import: typeof shareItem === "object" ? shareItem.import : void 0,
|
|
@@ -949,12 +959,12 @@ function generateExposes(options) {
|
|
|
949
959
|
//#endregion
|
|
950
960
|
//#region src/virtualModules/virtualRuntimeInitStatus.ts
|
|
951
961
|
const virtualRuntimeInitStatus = new VirtualModule("runtimeInit");
|
|
952
|
-
function
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
const globalKey = ${JSON.stringify(
|
|
962
|
+
function getRuntimeInitGlobalKey() {
|
|
963
|
+
return `__mf_init__${virtualRuntimeInitStatus.getImportId()}__`;
|
|
964
|
+
}
|
|
965
|
+
function getRuntimeInitBootstrapCode() {
|
|
966
|
+
return `
|
|
967
|
+
const globalKey = ${JSON.stringify(getRuntimeInitGlobalKey())};
|
|
958
968
|
if (!globalThis[globalKey]) {
|
|
959
969
|
let initResolve, initReject;
|
|
960
970
|
const initPromise = new Promise((re, rj) => {
|
|
@@ -966,8 +976,6 @@ if (!globalThis[globalKey]) {
|
|
|
966
976
|
initResolve,
|
|
967
977
|
initReject,
|
|
968
978
|
};
|
|
969
|
-
// In SSR (no window), resolve immediately with a stub runtime
|
|
970
|
-
// so modules don't hang waiting for browser-only init
|
|
971
979
|
if (typeof window === 'undefined') {
|
|
972
980
|
initResolve({
|
|
973
981
|
loadRemote: function() { return Promise.resolve(undefined); },
|
|
@@ -975,6 +983,64 @@ if (!globalThis[globalKey]) {
|
|
|
975
983
|
});
|
|
976
984
|
}
|
|
977
985
|
}
|
|
986
|
+
`;
|
|
987
|
+
}
|
|
988
|
+
function getRuntimeInitPromiseBootstrapCode() {
|
|
989
|
+
return `
|
|
990
|
+
const __mfPromiseGlobalKey = ${JSON.stringify(getRuntimeInitGlobalKey())};
|
|
991
|
+
let __mfPromiseState = globalThis[__mfPromiseGlobalKey];
|
|
992
|
+
if (!__mfPromiseState) {
|
|
993
|
+
let initResolve, initReject;
|
|
994
|
+
const initPromise = new Promise((re, rj) => {
|
|
995
|
+
initResolve = re;
|
|
996
|
+
initReject = rj;
|
|
997
|
+
});
|
|
998
|
+
__mfPromiseState = globalThis[__mfPromiseGlobalKey] = {
|
|
999
|
+
initPromise,
|
|
1000
|
+
initResolve,
|
|
1001
|
+
initReject,
|
|
1002
|
+
};
|
|
1003
|
+
if (typeof window === 'undefined') {
|
|
1004
|
+
initResolve({
|
|
1005
|
+
loadRemote: function() { return Promise.resolve(undefined); },
|
|
1006
|
+
loadShare: function() { return Promise.resolve(undefined); },
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
const initPromise = __mfPromiseState.initPromise;
|
|
1011
|
+
`;
|
|
1012
|
+
}
|
|
1013
|
+
function getRuntimeInitResolveBootstrapCode() {
|
|
1014
|
+
return `
|
|
1015
|
+
const __mfResolveGlobalKey = ${JSON.stringify(getRuntimeInitGlobalKey())};
|
|
1016
|
+
let __mfResolveState = globalThis[__mfResolveGlobalKey];
|
|
1017
|
+
if (!__mfResolveState) {
|
|
1018
|
+
let initResolve, initReject;
|
|
1019
|
+
const initPromise = new Promise((re, rj) => {
|
|
1020
|
+
initResolve = re;
|
|
1021
|
+
initReject = rj;
|
|
1022
|
+
});
|
|
1023
|
+
__mfResolveState = globalThis[__mfResolveGlobalKey] = {
|
|
1024
|
+
initPromise,
|
|
1025
|
+
initResolve,
|
|
1026
|
+
initReject,
|
|
1027
|
+
};
|
|
1028
|
+
if (typeof window === 'undefined') {
|
|
1029
|
+
initResolve({
|
|
1030
|
+
loadRemote: function() { return Promise.resolve(undefined); },
|
|
1031
|
+
loadShare: function() { return Promise.resolve(undefined); },
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
const initResolve = __mfResolveState.initResolve;
|
|
1036
|
+
`;
|
|
1037
|
+
}
|
|
1038
|
+
function writeRuntimeInitStatus(command) {
|
|
1039
|
+
getRuntimeInitGlobalKey();
|
|
1040
|
+
const exportStatement = command === "build" ? `const { initPromise, initResolve, initReject } = globalThis[globalKey];
|
|
1041
|
+
export { initPromise, initResolve, initReject };` : `module.exports = globalThis[globalKey];`;
|
|
1042
|
+
virtualRuntimeInitStatus.writeSync(`
|
|
1043
|
+
${getRuntimeInitBootstrapCode()}
|
|
978
1044
|
${exportStatement}
|
|
979
1045
|
`);
|
|
980
1046
|
}
|
|
@@ -999,7 +1065,8 @@ function getUsedRemotesMap() {
|
|
|
999
1065
|
}
|
|
1000
1066
|
function generateRemotes(id, command, isRolldown) {
|
|
1001
1067
|
const useESM = command === "build" || isRolldown;
|
|
1002
|
-
const importLine =
|
|
1068
|
+
const importLine = command === "build" ? getRuntimeInitPromiseBootstrapCode() : useESM ? `${getRuntimeInitBootstrapCode()}
|
|
1069
|
+
const { initPromise } = globalThis[globalKey];` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
|
|
1003
1070
|
const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
|
|
1004
1071
|
const exportLine = command === "serve" && useESM ? "export default exportModule.default ?? exportModule" : useESM ? "export default exportModule" : "module.exports = exportModule";
|
|
1005
1072
|
return `
|
|
@@ -1056,7 +1123,8 @@ function getLoadShareModulePath(pkg, isRolldown, command) {
|
|
|
1056
1123
|
function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
|
|
1057
1124
|
if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, isRolldown || command === "build" ? ".mjs" : ".js");
|
|
1058
1125
|
const useESM = command === "build" || isRolldown;
|
|
1059
|
-
const importLine =
|
|
1126
|
+
const importLine = command === "build" ? getRuntimeInitPromiseBootstrapCode() : useESM ? `${getRuntimeInitBootstrapCode()}
|
|
1127
|
+
const { initPromise } = globalThis[globalKey];` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
|
|
1060
1128
|
const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
|
|
1061
1129
|
const useSsrProviderFallback = hasPackageDependency("vinext") && command === "build" && pkg === "react";
|
|
1062
1130
|
const providerImportId = getLocalProviderImportPath(pkg) || getPreBuildLibImportId(pkg);
|
|
@@ -1100,12 +1168,12 @@ new VirtualModule("localSharedImportMap");
|
|
|
1100
1168
|
function getLocalSharedImportMapPath() {
|
|
1101
1169
|
return getLocalSharedImportMapPath_temp();
|
|
1102
1170
|
}
|
|
1103
|
-
let
|
|
1171
|
+
let prevLocalSharedImportMapContent;
|
|
1104
1172
|
function writeLocalSharedImportMap() {
|
|
1105
|
-
const
|
|
1106
|
-
if (
|
|
1107
|
-
|
|
1108
|
-
writeLocalSharedImportMap_temp(
|
|
1173
|
+
const nextContent = generateLocalSharedImportMap();
|
|
1174
|
+
if (prevLocalSharedImportMapContent !== nextContent) {
|
|
1175
|
+
prevLocalSharedImportMapContent = nextContent;
|
|
1176
|
+
writeLocalSharedImportMap_temp(nextContent);
|
|
1109
1177
|
}
|
|
1110
1178
|
}
|
|
1111
1179
|
function generateLocalSharedImportMap() {
|
|
@@ -1188,7 +1256,7 @@ const REMOTE_ENTRY_ID = "virtual:mf-REMOTE_ENTRY_ID";
|
|
|
1188
1256
|
function getRemoteEntryId(options) {
|
|
1189
1257
|
return `${REMOTE_ENTRY_ID}:${`${options.name}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
|
|
1190
1258
|
}
|
|
1191
|
-
function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(options)) {
|
|
1259
|
+
function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(options), command = "build") {
|
|
1192
1260
|
const pluginImportNames = options.runtimePlugins.map((p, i) => {
|
|
1193
1261
|
if (typeof p === "string") return [
|
|
1194
1262
|
`$runtimePlugin_${i}`,
|
|
@@ -1204,15 +1272,25 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
|
|
|
1204
1272
|
return `
|
|
1205
1273
|
import {init as runtimeInit, loadRemote} from "@module-federation/runtime";
|
|
1206
1274
|
${pluginImportNames.map((item) => item[1]).join("\n")}
|
|
1207
|
-
|
|
1208
|
-
import {usedShared, usedRemotes} from "${getLocalSharedImportMapPath()}"
|
|
1209
|
-
import {
|
|
1210
|
-
initResolve
|
|
1211
|
-
} from "${virtualRuntimeInitStatus.getImportId()}"
|
|
1275
|
+
${command === "build" ? getRuntimeInitResolveBootstrapCode() : getRuntimeInitBootstrapCode() + "\n const { initResolve } = globalThis[globalKey];"}
|
|
1212
1276
|
const initTokens = {}
|
|
1213
1277
|
const shareScopeName = ${JSON.stringify(options.shareScope)}
|
|
1214
1278
|
const mfName = ${JSON.stringify(options.name)}
|
|
1279
|
+
let localSharedImportMapPromise
|
|
1280
|
+
let exposesMapPromise
|
|
1281
|
+
|
|
1282
|
+
async function getLocalSharedImportMap() {
|
|
1283
|
+
localSharedImportMapPromise ??= import("${getLocalSharedImportMapPath()}")
|
|
1284
|
+
return localSharedImportMapPromise
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
async function getExposesMap() {
|
|
1288
|
+
exposesMapPromise ??= import("${virtualExposesId}").then((mod) => mod.default ?? mod)
|
|
1289
|
+
return exposesMapPromise
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1215
1292
|
async function init(shared = {}, initScope = []) {
|
|
1293
|
+
const {usedShared, usedRemotes} = await getLocalSharedImportMap()
|
|
1216
1294
|
const initRes = runtimeInit({
|
|
1217
1295
|
name: mfName,
|
|
1218
1296
|
remotes: usedRemotes,
|
|
@@ -1240,7 +1318,8 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
|
|
|
1240
1318
|
return initRes
|
|
1241
1319
|
}
|
|
1242
1320
|
|
|
1243
|
-
function getExposes(moduleName) {
|
|
1321
|
+
async function getExposes(moduleName) {
|
|
1322
|
+
const exposesMap = await getExposesMap()
|
|
1244
1323
|
if (!(moduleName in exposesMap)) throw new Error(\`Module \${moduleName} does not exist in container.\`)
|
|
1245
1324
|
return (exposesMap[moduleName])().then(res => () => res)
|
|
1246
1325
|
}
|
|
@@ -1253,13 +1332,8 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
|
|
|
1253
1332
|
const hostAutoInitModule = new VirtualModule("hostAutoInit", "__H_A_I__");
|
|
1254
1333
|
function writeHostAutoInit(remoteEntryId = REMOTE_ENTRY_ID) {
|
|
1255
1334
|
hostAutoInitModule.writeSync(`
|
|
1256
|
-
const
|
|
1257
|
-
|
|
1258
|
-
.then(remoteEntry => {
|
|
1259
|
-
return Promise.resolve(remoteEntry.__tla)
|
|
1260
|
-
.then(remoteEntry.init)
|
|
1261
|
-
.catch(remoteEntry.init)
|
|
1262
|
-
})
|
|
1335
|
+
const remoteEntry = await import("${remoteEntryId}");
|
|
1336
|
+
await remoteEntry.init();
|
|
1263
1337
|
`);
|
|
1264
1338
|
}
|
|
1265
1339
|
function getHostAutoInitImportId() {
|
|
@@ -1282,13 +1356,21 @@ function initVirtualModules(command, remoteEntryId) {
|
|
|
1282
1356
|
* If Rollup's deconflict renamed the alias but didn't update references
|
|
1283
1357
|
* in the code body, fall back to proxyLocal so they stay in sync.
|
|
1284
1358
|
*/
|
|
1285
|
-
function resolveProxyAlias(binding, proxyLocal, code, fullImport) {
|
|
1359
|
+
function resolveProxyAlias(binding, proxyLocal, code, fullImport, claimedLocals = /* @__PURE__ */ new Set()) {
|
|
1286
1360
|
const codeWithoutImport = code.replace(fullImport, "");
|
|
1287
1361
|
const escapedLocal = binding.local.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1288
1362
|
const localUsedInCode = new RegExp(`\\b${escapedLocal}\\b`).test(codeWithoutImport);
|
|
1363
|
+
const claimedImportLocals = /* @__PURE__ */ new Set();
|
|
1364
|
+
const importRe = /import\s*\{([^}]+)\}\s*from\s*["'][^"']+["']\s*;?/g;
|
|
1365
|
+
let match;
|
|
1366
|
+
while ((match = importRe.exec(codeWithoutImport)) !== null) for (const spec of match[1].split(",")) {
|
|
1367
|
+
const parts = spec.trim().split(/\s+as\s+/);
|
|
1368
|
+
claimedImportLocals.add((parts[1] || parts[0]).trim());
|
|
1369
|
+
}
|
|
1370
|
+
const local = !localUsedInCode && !claimedLocals.has(proxyLocal) && !claimedImportLocals.has(proxyLocal) ? proxyLocal : binding.local;
|
|
1289
1371
|
return {
|
|
1290
1372
|
imported: binding.imported,
|
|
1291
|
-
local
|
|
1373
|
+
local
|
|
1292
1374
|
};
|
|
1293
1375
|
}
|
|
1294
1376
|
function findRemoteEntryFile(filename, bundle) {
|
|
@@ -1743,14 +1825,14 @@ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposes
|
|
|
1743
1825
|
}
|
|
1744
1826
|
},
|
|
1745
1827
|
load(id) {
|
|
1746
|
-
if (id === remoteEntryId) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId));
|
|
1828
|
+
if (id === remoteEntryId) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId, _command));
|
|
1747
1829
|
if (id === virtualExposesId) return generateExposes(options);
|
|
1748
1830
|
if (_command === "serve" && id.includes(getHostAutoInitPath())) return id;
|
|
1749
1831
|
},
|
|
1750
1832
|
transform(code, id) {
|
|
1751
1833
|
return mapCodeToCodeWithSourcemap((() => {
|
|
1752
1834
|
if (!filter(id)) return;
|
|
1753
|
-
if (id.includes(remoteEntryId)) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId));
|
|
1835
|
+
if (id.includes(remoteEntryId)) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId, _command));
|
|
1754
1836
|
if (id === virtualExposesId) return generateExposes(options);
|
|
1755
1837
|
if (id.includes(getHostAutoInitPath())) {
|
|
1756
1838
|
if (_command === "serve") {
|
|
@@ -2009,6 +2091,58 @@ var aliasToArrayPlugin_default = {
|
|
|
2009
2091
|
}
|
|
2010
2092
|
};
|
|
2011
2093
|
//#endregion
|
|
2094
|
+
//#region src/utils/controlChunkSanitizer.ts
|
|
2095
|
+
const FEDERATION_CONTROL_CHUNK_HINTS = [
|
|
2096
|
+
"hostInit",
|
|
2097
|
+
"virtualExposes",
|
|
2098
|
+
"localSharedImportMap"
|
|
2099
|
+
];
|
|
2100
|
+
function stripEmptyPreloadCalls(code) {
|
|
2101
|
+
const helperImportRegex = /import\s*\{\s*_\s*as\s*(\w+)\s*\}\s*from\s*["'][^"']+["']\s*;?/g;
|
|
2102
|
+
const helperAliases = [...code.matchAll(helperImportRegex)].map((match) => match[1]);
|
|
2103
|
+
let nextCode = code;
|
|
2104
|
+
for (const alias of helperAliases) {
|
|
2105
|
+
const marker = `${alias}(()=>`;
|
|
2106
|
+
let start = nextCode.indexOf(marker);
|
|
2107
|
+
while (start !== -1) {
|
|
2108
|
+
const exprStart = start + marker.length;
|
|
2109
|
+
let depth = 0;
|
|
2110
|
+
let cursor = exprStart;
|
|
2111
|
+
let replacementEnd = -1;
|
|
2112
|
+
while (cursor < nextCode.length) {
|
|
2113
|
+
const char = nextCode[cursor];
|
|
2114
|
+
if (char === "(") depth++;
|
|
2115
|
+
else if (char === ")") depth--;
|
|
2116
|
+
else if (depth === 0 && nextCode.startsWith(",[],import.meta.url)", cursor)) {
|
|
2117
|
+
replacementEnd = cursor;
|
|
2118
|
+
break;
|
|
2119
|
+
}
|
|
2120
|
+
cursor++;
|
|
2121
|
+
}
|
|
2122
|
+
if (replacementEnd === -1) break;
|
|
2123
|
+
const expression = nextCode.slice(exprStart, replacementEnd);
|
|
2124
|
+
nextCode = nextCode.slice(0, start) + expression + nextCode.slice(replacementEnd + 20);
|
|
2125
|
+
start = nextCode.indexOf(marker, start + expression.length);
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
nextCode = nextCode.replace(/import\s*["'][^"']*__loadShare__[^"']*["']\s*;?/g, "");
|
|
2129
|
+
nextCode = nextCode.replace(helperImportRegex, (statement, local) => {
|
|
2130
|
+
return new RegExp(`\\b${local}\\s*\\(`).test(nextCode.replace(statement, "")) ? statement : "";
|
|
2131
|
+
});
|
|
2132
|
+
return nextCode;
|
|
2133
|
+
}
|
|
2134
|
+
function isFederationControlChunk(fileName, filename) {
|
|
2135
|
+
return fileName.includes(filename) || FEDERATION_CONTROL_CHUNK_HINTS.some((hint) => fileName.includes(hint));
|
|
2136
|
+
}
|
|
2137
|
+
function sanitizeFederationControlChunk(code, fileName, filename) {
|
|
2138
|
+
let nextCode = stripEmptyPreloadCalls(code);
|
|
2139
|
+
if (fileName.includes("localSharedImportMap")) {
|
|
2140
|
+
const remoteEntryImportRegex = new RegExp(`import\\s*["'][^"']*${filename.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["']\\s*;?`, "g");
|
|
2141
|
+
nextCode = nextCode.replace(remoteEntryImportRegex, "");
|
|
2142
|
+
}
|
|
2143
|
+
return nextCode;
|
|
2144
|
+
}
|
|
2145
|
+
//#endregion
|
|
2012
2146
|
//#region src/utils/normalizeOptimizeDeps.ts
|
|
2013
2147
|
var normalizeOptimizeDeps_default = {
|
|
2014
2148
|
name: "normalizeOptimizeDeps",
|
|
@@ -2146,9 +2280,19 @@ function federation(mfUserOptions) {
|
|
|
2146
2280
|
config(config) {
|
|
2147
2281
|
const runtimeInitId = virtualRuntimeInitStatus.getImportId();
|
|
2148
2282
|
config.build = config.build || {};
|
|
2149
|
-
config.build.
|
|
2150
|
-
|
|
2151
|
-
const
|
|
2283
|
+
if (config.build.modulePreload !== false) {
|
|
2284
|
+
const currentModulePreload = config.build.modulePreload && typeof config.build.modulePreload === "object" ? config.build.modulePreload : {};
|
|
2285
|
+
const existingResolveDependencies = currentModulePreload.resolveDependencies;
|
|
2286
|
+
config.build.modulePreload = {
|
|
2287
|
+
...currentModulePreload,
|
|
2288
|
+
resolveDependencies(filename, deps, context) {
|
|
2289
|
+
const resolvedDeps = existingResolveDependencies ? existingResolveDependencies(filename, deps, context) : deps;
|
|
2290
|
+
const hostFile = path.basename(context.hostId);
|
|
2291
|
+
return context.hostType === "js" && (hostFile === options.filename || hostFile.includes("hostInit") || hostFile.includes("virtualExposes") || hostFile.includes("localSharedImportMap")) ? [] : resolvedDeps;
|
|
2292
|
+
}
|
|
2293
|
+
};
|
|
2294
|
+
}
|
|
2295
|
+
const applyManualChunks = (output) => {
|
|
2152
2296
|
const existingManualChunks = output.manualChunks;
|
|
2153
2297
|
output.manualChunks = function(id) {
|
|
2154
2298
|
if (id.includes(runtimeInitId)) return "runtimeInit";
|
|
@@ -2161,7 +2305,12 @@ function federation(mfUserOptions) {
|
|
|
2161
2305
|
for (const [key, ids] of Object.entries(existingManualChunks)) if (Array.isArray(ids) && ids.some((v) => id.includes(v))) return key;
|
|
2162
2306
|
}
|
|
2163
2307
|
};
|
|
2164
|
-
}
|
|
2308
|
+
};
|
|
2309
|
+
config.build.rollupOptions = config.build.rollupOptions || {};
|
|
2310
|
+
if (!Array.isArray(config.build.rollupOptions.output)) applyManualChunks(config.build.rollupOptions.output ||= {});
|
|
2311
|
+
const buildWithRolldown = config.build;
|
|
2312
|
+
buildWithRolldown.rolldownOptions = buildWithRolldown.rolldownOptions || {};
|
|
2313
|
+
if (!Array.isArray(buildWithRolldown.rolldownOptions.output)) applyManualChunks(buildWithRolldown.rolldownOptions.output ||= {});
|
|
2165
2314
|
},
|
|
2166
2315
|
load(id) {
|
|
2167
2316
|
if (id.startsWith("\0")) return;
|
|
@@ -2193,6 +2342,11 @@ function federation(mfUserOptions) {
|
|
|
2193
2342
|
}
|
|
2194
2343
|
},
|
|
2195
2344
|
generateBundle(_, bundle) {
|
|
2345
|
+
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
2346
|
+
if (chunk.type !== "chunk") continue;
|
|
2347
|
+
if (!isFederationControlChunk(fileName, filename)) continue;
|
|
2348
|
+
chunk.code = sanitizeFederationControlChunk(chunk.code, fileName, filename);
|
|
2349
|
+
}
|
|
2196
2350
|
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
2197
2351
|
if (chunk.type !== "chunk") continue;
|
|
2198
2352
|
if (fileName.includes("__loadShare__")) continue;
|
|
@@ -2230,12 +2384,12 @@ function federation(mfUserOptions) {
|
|
|
2230
2384
|
fileName
|
|
2231
2385
|
});
|
|
2232
2386
|
}
|
|
2233
|
-
if (proxyChunks.size
|
|
2234
|
-
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
2387
|
+
if (proxyChunks.size > 0) for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
2235
2388
|
if (chunk.type !== "chunk") continue;
|
|
2236
2389
|
if (fileName.includes("__loadShare__")) continue;
|
|
2237
2390
|
let code = chunk.code;
|
|
2238
2391
|
let modified = false;
|
|
2392
|
+
const claimedLocals = /* @__PURE__ */ new Set();
|
|
2239
2393
|
for (const [proxyFileName, proxyInfo] of proxyChunks) {
|
|
2240
2394
|
const proxyBaseName = proxyFileName.replace(/^.*\//, "").replace(/\.js$/, "").replace(/-[A-Za-z0-9_-]+$/, "");
|
|
2241
2395
|
const importMatch = new RegExp(`import\\s*\\{([^}]+)\\}\\s*from\\s*["']([^"']*${proxyBaseName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[^"']*)["']\\s*;?`).exec(code);
|
|
@@ -2258,9 +2412,12 @@ function federation(mfUserOptions) {
|
|
|
2258
2412
|
}
|
|
2259
2413
|
const inlineable = [];
|
|
2260
2414
|
const nonInlineable = [];
|
|
2415
|
+
const pendingLocals = new Set(bindings.map((binding) => binding.local));
|
|
2261
2416
|
for (const b of bindings) {
|
|
2417
|
+
pendingLocals.delete(b.local);
|
|
2262
2418
|
const proxyLocal = exportMap[b.imported];
|
|
2263
2419
|
if (!proxyLocal) {
|
|
2420
|
+
claimedLocals.add(b.local);
|
|
2264
2421
|
nonInlineable.push(b);
|
|
2265
2422
|
continue;
|
|
2266
2423
|
}
|
|
@@ -2282,7 +2439,14 @@ function federation(mfUserOptions) {
|
|
|
2282
2439
|
local: b.local,
|
|
2283
2440
|
funcBody: renamedFunc
|
|
2284
2441
|
});
|
|
2285
|
-
|
|
2442
|
+
claimedLocals.add(b.local);
|
|
2443
|
+
} else {
|
|
2444
|
+
const unavailableLocals = new Set(claimedLocals);
|
|
2445
|
+
pendingLocals.forEach((local) => unavailableLocals.add(local));
|
|
2446
|
+
const resolvedBinding = resolveProxyAlias(b, proxyLocal, code, fullImport, unavailableLocals);
|
|
2447
|
+
claimedLocals.add(resolvedBinding.local);
|
|
2448
|
+
nonInlineable.push(resolvedBinding);
|
|
2449
|
+
}
|
|
2286
2450
|
}
|
|
2287
2451
|
const hasRenamedAlias = nonInlineable.some((b) => bindings.find((ob) => ob.imported === b.imported)?.local !== b.local);
|
|
2288
2452
|
if (inlineable.length === 0 && !hasRenamedAlias) continue;
|
|
@@ -2296,6 +2460,28 @@ function federation(mfUserOptions) {
|
|
|
2296
2460
|
}
|
|
2297
2461
|
}
|
|
2298
2462
|
},
|
|
2463
|
+
{
|
|
2464
|
+
name: "module-federation-strip-empty-preload-helper",
|
|
2465
|
+
enforce: "post",
|
|
2466
|
+
apply: "build",
|
|
2467
|
+
renderChunk(code, chunk) {
|
|
2468
|
+
if (!isFederationControlChunk(chunk.fileName, filename)) return;
|
|
2469
|
+
const nextCode = sanitizeFederationControlChunk(code, chunk.fileName, filename);
|
|
2470
|
+
return nextCode === code ? null : {
|
|
2471
|
+
code: nextCode,
|
|
2472
|
+
map: null
|
|
2473
|
+
};
|
|
2474
|
+
},
|
|
2475
|
+
writeBundle(outputOptions, bundle) {
|
|
2476
|
+
if (!outputOptions.dir) return;
|
|
2477
|
+
for (const chunk of Object.values(bundle)) {
|
|
2478
|
+
if (chunk.type !== "chunk") continue;
|
|
2479
|
+
if (!isFederationControlChunk(chunk.fileName, filename)) continue;
|
|
2480
|
+
const outputPath = path.join(outputOptions.dir, chunk.fileName);
|
|
2481
|
+
writeFileSync(outputPath, sanitizeFederationControlChunk(readFileSync(outputPath, "utf-8"), chunk.fileName, filename));
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2484
|
+
},
|
|
2299
2485
|
{
|
|
2300
2486
|
name: "module-federation-dev-await-shared-init",
|
|
2301
2487
|
apply: "serve",
|
|
@@ -2330,7 +2516,7 @@ function federation(mfUserOptions) {
|
|
|
2330
2516
|
config(config, { command: _command }) {
|
|
2331
2517
|
const isRolldown = !!this?.meta?.rolldownVersion;
|
|
2332
2518
|
let implementation = options.implementation;
|
|
2333
|
-
if (isRolldown) implementation = implementation.replace(/\.cjs\.
|
|
2519
|
+
if (isRolldown) implementation = implementation.replace(/\.cjs(\.js)?$/, ".js");
|
|
2334
2520
|
config.resolve.alias.push({
|
|
2335
2521
|
find: "@module-federation/runtime",
|
|
2336
2522
|
replacement: implementation
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@module-federation/vite",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.1",
|
|
4
4
|
"description": "Vite plugin for Module Federation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.cjs",
|
|
@@ -66,9 +66,9 @@
|
|
|
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.2.
|
|
70
|
-
"@module-federation/runtime": "2.
|
|
71
|
-
"@module-federation/sdk": "2.
|
|
69
|
+
"@module-federation/dts-plugin": "2.2.2",
|
|
70
|
+
"@module-federation/runtime": "2.2.2",
|
|
71
|
+
"@module-federation/sdk": "2.2.2",
|
|
72
72
|
"@rollup/pluginutils": "^5.3.0",
|
|
73
73
|
"defu": "^6.1.4",
|
|
74
74
|
"estree-walker": "^3.0.3",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"oxfmt": "^0.36.0",
|
|
86
86
|
"rollup": "^4.47.1",
|
|
87
87
|
"tsdown": "^0.21.0",
|
|
88
|
-
"vite": "^
|
|
88
|
+
"vite": "^8.0.0",
|
|
89
89
|
"vitest": "^4.0.18"
|
|
90
90
|
}
|
|
91
91
|
}
|