@module-federation/vite 1.13.0 → 1.13.2
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 +337 -75
- package/lib/index.mjs +337 -75
- package/package.json +7 -6
package/lib/index.mjs
CHANGED
|
@@ -59,6 +59,38 @@ function inlineEntryScripts(html, initSrc) {
|
|
|
59
59
|
return html.replace("<head>", `<head><script type="module" src=${JSON.stringify(src)}><\/script>`);
|
|
60
60
|
}
|
|
61
61
|
//#endregion
|
|
62
|
+
//#region src/utils/logger.ts
|
|
63
|
+
const MODULE_FEDERATION_LOG_PREFIX = "[Module Federation]";
|
|
64
|
+
function formatModuleFederationMessage(message) {
|
|
65
|
+
return `${MODULE_FEDERATION_LOG_PREFIX} ${message}`;
|
|
66
|
+
}
|
|
67
|
+
function createModuleFederationError(message) {
|
|
68
|
+
return new Error(formatModuleFederationMessage(message));
|
|
69
|
+
}
|
|
70
|
+
function toConsoleArgs(message, rest = []) {
|
|
71
|
+
if (typeof message === "string") return [formatModuleFederationMessage(message), ...rest];
|
|
72
|
+
if (message === void 0) return [MODULE_FEDERATION_LOG_PREFIX, ...rest];
|
|
73
|
+
return [
|
|
74
|
+
MODULE_FEDERATION_LOG_PREFIX,
|
|
75
|
+
message,
|
|
76
|
+
...rest
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
const moduleFederationConsole = {
|
|
80
|
+
log(message, ...rest) {
|
|
81
|
+
console.log(...toConsoleArgs(message, rest));
|
|
82
|
+
},
|
|
83
|
+
warn(message, ...rest) {
|
|
84
|
+
console.warn(...toConsoleArgs(message, rest));
|
|
85
|
+
},
|
|
86
|
+
error(message, ...rest) {
|
|
87
|
+
console.error(...toConsoleArgs(message, rest));
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
moduleFederationConsole.log;
|
|
91
|
+
const mfWarn = moduleFederationConsole.warn;
|
|
92
|
+
const mfError = moduleFederationConsole.error;
|
|
93
|
+
//#endregion
|
|
62
94
|
//#region src/utils/packageUtils.ts
|
|
63
95
|
const dependencyPresenceCache = /* @__PURE__ */ new Map();
|
|
64
96
|
let packageDetectionCwd;
|
|
@@ -82,7 +114,7 @@ function setPackageDetectionCwd(cwd) {
|
|
|
82
114
|
* @returns {string} - The encoded file name.
|
|
83
115
|
*/
|
|
84
116
|
function packageNameEncode(name) {
|
|
85
|
-
if (typeof name !== "string") throw
|
|
117
|
+
if (typeof name !== "string") throw createModuleFederationError("A string package name is required");
|
|
86
118
|
return name.replace(/@/g, "_mf_0_").replace(/\//g, "_mf_1_").replace(/-/g, "_mf_2_").replace(/\./g, "_mf_3_");
|
|
87
119
|
}
|
|
88
120
|
/**
|
|
@@ -91,7 +123,7 @@ function packageNameEncode(name) {
|
|
|
91
123
|
* @returns {string} - The decoded package name.
|
|
92
124
|
*/
|
|
93
125
|
function packageNameDecode(encoded) {
|
|
94
|
-
if (typeof encoded !== "string") throw
|
|
126
|
+
if (typeof encoded !== "string") throw createModuleFederationError("A string encoded file name is required");
|
|
95
127
|
return encoded.replace(/_mf_0_/g, "@").replace(/_mf_1_/g, "/").replace(/_mf_2_/g, "-").replace(/_mf_3_/g, ".");
|
|
96
128
|
}
|
|
97
129
|
/**
|
|
@@ -103,6 +135,13 @@ function removePathFromNpmPackage(packageString) {
|
|
|
103
135
|
const match = packageString.match(/^(?:@[^/]+\/)?[^/]+/);
|
|
104
136
|
return match ? match[0] : packageString;
|
|
105
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Detect whether the current bundler is Rolldown (Vite 8+) by checking
|
|
140
|
+
* for `meta.rolldownVersion` on the plugin hook context.
|
|
141
|
+
*/
|
|
142
|
+
function getIsRolldown(ctx) {
|
|
143
|
+
return !!ctx?.meta?.rolldownVersion;
|
|
144
|
+
}
|
|
106
145
|
function hasPackageDependency(dependencyName, cwd = packageDetectionCwd || process.cwd()) {
|
|
107
146
|
const cacheKey = getDependencyCacheKey(cwd, dependencyName);
|
|
108
147
|
const cached = dependencyPresenceCache.get(cacheKey);
|
|
@@ -184,6 +223,12 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
|
|
|
184
223
|
else if (Array.isArray(inputOptions)) entryFiles = inputOptions;
|
|
185
224
|
else if (typeof inputOptions === "object") entryFiles = Object.values(inputOptions);
|
|
186
225
|
if (entryFiles && entryFiles.length > 0) htmlFilePath = getFirstHtmlEntryFile(entryFiles);
|
|
226
|
+
if (_command === "serve" && htmlFilePath && fs.existsSync(htmlFilePath)) {
|
|
227
|
+
const htmlContent = fs.readFileSync(htmlFilePath, "utf-8");
|
|
228
|
+
const scriptRegex = /<script\s+[^>]*src=["']([^"']+)["'][^>]*>/gi;
|
|
229
|
+
let match;
|
|
230
|
+
while ((match = scriptRegex.exec(htmlContent)) !== null) entryFiles.push(match[1]);
|
|
231
|
+
}
|
|
187
232
|
},
|
|
188
233
|
buildStart() {
|
|
189
234
|
if (_command === "serve") return;
|
|
@@ -217,7 +262,7 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
|
|
|
217
262
|
if (typeof result === "string") return result;
|
|
218
263
|
if (result && typeof result === "object") {
|
|
219
264
|
if ("runtime" in result) {
|
|
220
|
-
|
|
265
|
+
mfWarn("renderBuiltUrl returned runtime code for HTML injection. Runtime code cannot be used in <script src=\"\">. Falling back to base path.");
|
|
221
266
|
return viteConfig.base + file;
|
|
222
267
|
}
|
|
223
268
|
if (result.relative) return file;
|
|
@@ -282,11 +327,11 @@ function checkAliasConflicts(options) {
|
|
|
282
327
|
});
|
|
283
328
|
}
|
|
284
329
|
if (conflicts.length > 0) {
|
|
285
|
-
|
|
330
|
+
mfWarn("Detected alias conflicts with shared modules:");
|
|
286
331
|
conflicts.forEach(({ sharedModule, alias, target }) => {
|
|
287
|
-
|
|
332
|
+
mfWarn(`Shared module "${sharedModule}" is aliased by "${alias}" to "${target}"`);
|
|
288
333
|
});
|
|
289
|
-
|
|
334
|
+
mfWarn("This may cause runtime errors as the shared module will bypass Module Federation's sharing mechanism.");
|
|
290
335
|
}
|
|
291
336
|
}
|
|
292
337
|
};
|
|
@@ -315,7 +360,7 @@ function PluginDevProxyModuleTopLevelAwait() {
|
|
|
315
360
|
try {
|
|
316
361
|
ast = this.parse(code, { allowReturnOutsideFunction: true });
|
|
317
362
|
} catch (e) {
|
|
318
|
-
throw
|
|
363
|
+
throw createModuleFederationError(`${id}: ${e}`);
|
|
319
364
|
}
|
|
320
365
|
const magicString = new MagicString(code);
|
|
321
366
|
const walk = await loadWalk();
|
|
@@ -443,7 +488,7 @@ const normalizeDevDtsOptions = (dts, context) => {
|
|
|
443
488
|
const logDtsError = (error, dtsOptions) => {
|
|
444
489
|
if (dtsOptions === false) return;
|
|
445
490
|
if (typeof dtsOptions === "object" && dtsOptions && dtsOptions.displayErrorInTerminal === false) return;
|
|
446
|
-
|
|
491
|
+
mfError(error);
|
|
447
492
|
};
|
|
448
493
|
function pluginDts(options) {
|
|
449
494
|
if (options.dts === false) return [];
|
|
@@ -471,7 +516,7 @@ function pluginDts(options) {
|
|
|
471
516
|
if (!normalizedDevOptions || !resolvedConfig) return;
|
|
472
517
|
const devOptions = normalizedDevOptions;
|
|
473
518
|
if (devOptions.disableDynamicRemoteTypeHints && devOptions.disableHotTypesReload && devOptions.disableLiveReload) return;
|
|
474
|
-
if (!options.name) throw
|
|
519
|
+
if (!options.name) throw createModuleFederationError("name is required if you want to enable dev server!");
|
|
475
520
|
const outputDir = resolveOutputDir(resolvedConfig);
|
|
476
521
|
const normalizedDtsOptions = normalizeDevDtsOptions(options.dts, resolvedConfig.root);
|
|
477
522
|
if (typeof normalizedDtsOptions !== "object") return;
|
|
@@ -652,6 +697,10 @@ function searchPackageVersion(sharedName) {
|
|
|
652
697
|
}
|
|
653
698
|
} catch (_) {}
|
|
654
699
|
}
|
|
700
|
+
function inferVersionFromRequiredVersion(requiredVersion) {
|
|
701
|
+
if (!requiredVersion) return void 0;
|
|
702
|
+
return requiredVersion.match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/)?.[0];
|
|
703
|
+
}
|
|
655
704
|
function normalizeShareItem(key, shareItem) {
|
|
656
705
|
let version;
|
|
657
706
|
try {
|
|
@@ -662,11 +711,11 @@ function normalizeShareItem(key, shareItem) {
|
|
|
662
711
|
version = __require(path$1.join(process.cwd(), "node_modules", removePathFromNpmPackage(key), "package.json")).version;
|
|
663
712
|
} catch (e2) {
|
|
664
713
|
version = searchPackageVersion(key);
|
|
665
|
-
if (!version)
|
|
714
|
+
if (!version) mfError(e1);
|
|
666
715
|
}
|
|
667
716
|
}
|
|
668
717
|
} catch (e) {
|
|
669
|
-
|
|
718
|
+
mfError(`Unexpected error resolving version for ${key}:`, e);
|
|
670
719
|
}
|
|
671
720
|
if (typeof shareItem === "string") return {
|
|
672
721
|
name: shareItem,
|
|
@@ -682,7 +731,7 @@ function normalizeShareItem(key, shareItem) {
|
|
|
682
731
|
return {
|
|
683
732
|
name: key,
|
|
684
733
|
from: "",
|
|
685
|
-
version: shareItem.version || version,
|
|
734
|
+
version: shareItem.version || inferVersionFromRequiredVersion(shareItem.requiredVersion) || version,
|
|
686
735
|
scope: shareItem.shareScope || "default",
|
|
687
736
|
shareConfig: {
|
|
688
737
|
import: typeof shareItem === "object" ? shareItem.import : void 0,
|
|
@@ -727,7 +776,7 @@ function getNormalizeShareItem(key) {
|
|
|
727
776
|
return options.shared[key] || options.shared[removePathFromNpmPackage(key)] || options.shared[removePathFromNpmPackage(key) + "/"];
|
|
728
777
|
}
|
|
729
778
|
function normalizeModuleFederationOptions(options) {
|
|
730
|
-
if (options.virtualModuleDir && options.virtualModuleDir.includes("/")) throw
|
|
779
|
+
if (options.virtualModuleDir && options.virtualModuleDir.includes("/")) throw createModuleFederationError(`Invalid virtualModuleDir: "${options.virtualModuleDir}". The virtualModuleDir option cannot contain slashes (/). Please use a single directory name like '__mf__virtual__your_app_name'.`);
|
|
731
780
|
return config = {
|
|
732
781
|
exposes: normalizeExposes(options.exposes),
|
|
733
782
|
filename: options.filename || "remoteEntry-[hash]",
|
|
@@ -862,7 +911,7 @@ const cacheMap = {};
|
|
|
862
911
|
*/
|
|
863
912
|
function assertModuleFound(tag, str = "") {
|
|
864
913
|
const module = VirtualModule.findModule(tag, str);
|
|
865
|
-
if (!module) throw
|
|
914
|
+
if (!module) throw createModuleFederationError(`Module Federation shared module '${str}' not found. Please ensure it's installed as a dependency in your package.json.`);
|
|
866
915
|
return module;
|
|
867
916
|
}
|
|
868
917
|
var VirtualModule = class {
|
|
@@ -949,12 +998,12 @@ function generateExposes(options) {
|
|
|
949
998
|
//#endregion
|
|
950
999
|
//#region src/virtualModules/virtualRuntimeInitStatus.ts
|
|
951
1000
|
const virtualRuntimeInitStatus = new VirtualModule("runtimeInit");
|
|
952
|
-
function
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
const globalKey = ${JSON.stringify(
|
|
1001
|
+
function getRuntimeInitGlobalKey() {
|
|
1002
|
+
return `__mf_init__${virtualRuntimeInitStatus.getImportId()}__`;
|
|
1003
|
+
}
|
|
1004
|
+
function getRuntimeInitBootstrapCode() {
|
|
1005
|
+
return `
|
|
1006
|
+
const globalKey = ${JSON.stringify(getRuntimeInitGlobalKey())};
|
|
958
1007
|
if (!globalThis[globalKey]) {
|
|
959
1008
|
let initResolve, initReject;
|
|
960
1009
|
const initPromise = new Promise((re, rj) => {
|
|
@@ -966,8 +1015,6 @@ if (!globalThis[globalKey]) {
|
|
|
966
1015
|
initResolve,
|
|
967
1016
|
initReject,
|
|
968
1017
|
};
|
|
969
|
-
// In SSR (no window), resolve immediately with a stub runtime
|
|
970
|
-
// so modules don't hang waiting for browser-only init
|
|
971
1018
|
if (typeof window === 'undefined') {
|
|
972
1019
|
initResolve({
|
|
973
1020
|
loadRemote: function() { return Promise.resolve(undefined); },
|
|
@@ -975,6 +1022,64 @@ if (!globalThis[globalKey]) {
|
|
|
975
1022
|
});
|
|
976
1023
|
}
|
|
977
1024
|
}
|
|
1025
|
+
`;
|
|
1026
|
+
}
|
|
1027
|
+
function getRuntimeInitPromiseBootstrapCode() {
|
|
1028
|
+
return `
|
|
1029
|
+
const __mfPromiseGlobalKey = ${JSON.stringify(getRuntimeInitGlobalKey())};
|
|
1030
|
+
let __mfPromiseState = globalThis[__mfPromiseGlobalKey];
|
|
1031
|
+
if (!__mfPromiseState) {
|
|
1032
|
+
let initResolve, initReject;
|
|
1033
|
+
const initPromise = new Promise((re, rj) => {
|
|
1034
|
+
initResolve = re;
|
|
1035
|
+
initReject = rj;
|
|
1036
|
+
});
|
|
1037
|
+
__mfPromiseState = globalThis[__mfPromiseGlobalKey] = {
|
|
1038
|
+
initPromise,
|
|
1039
|
+
initResolve,
|
|
1040
|
+
initReject,
|
|
1041
|
+
};
|
|
1042
|
+
if (typeof window === 'undefined') {
|
|
1043
|
+
initResolve({
|
|
1044
|
+
loadRemote: function() { return Promise.resolve(undefined); },
|
|
1045
|
+
loadShare: function() { return Promise.resolve(undefined); },
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
const initPromise = __mfPromiseState.initPromise;
|
|
1050
|
+
`;
|
|
1051
|
+
}
|
|
1052
|
+
function getRuntimeInitResolveBootstrapCode() {
|
|
1053
|
+
return `
|
|
1054
|
+
const __mfResolveGlobalKey = ${JSON.stringify(getRuntimeInitGlobalKey())};
|
|
1055
|
+
let __mfResolveState = globalThis[__mfResolveGlobalKey];
|
|
1056
|
+
if (!__mfResolveState) {
|
|
1057
|
+
let initResolve, initReject;
|
|
1058
|
+
const initPromise = new Promise((re, rj) => {
|
|
1059
|
+
initResolve = re;
|
|
1060
|
+
initReject = rj;
|
|
1061
|
+
});
|
|
1062
|
+
__mfResolveState = globalThis[__mfResolveGlobalKey] = {
|
|
1063
|
+
initPromise,
|
|
1064
|
+
initResolve,
|
|
1065
|
+
initReject,
|
|
1066
|
+
};
|
|
1067
|
+
if (typeof window === 'undefined') {
|
|
1068
|
+
initResolve({
|
|
1069
|
+
loadRemote: function() { return Promise.resolve(undefined); },
|
|
1070
|
+
loadShare: function() { return Promise.resolve(undefined); },
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
const initResolve = __mfResolveState.initResolve;
|
|
1075
|
+
`;
|
|
1076
|
+
}
|
|
1077
|
+
function writeRuntimeInitStatus(command) {
|
|
1078
|
+
getRuntimeInitGlobalKey();
|
|
1079
|
+
const exportStatement = command === "build" ? `const { initPromise, initResolve, initReject } = globalThis[globalKey];
|
|
1080
|
+
export { initPromise, initResolve, initReject };` : `module.exports = globalThis[globalKey];`;
|
|
1081
|
+
virtualRuntimeInitStatus.writeSync(`
|
|
1082
|
+
${getRuntimeInitBootstrapCode()}
|
|
978
1083
|
${exportStatement}
|
|
979
1084
|
`);
|
|
980
1085
|
}
|
|
@@ -999,7 +1104,8 @@ function getUsedRemotesMap() {
|
|
|
999
1104
|
}
|
|
1000
1105
|
function generateRemotes(id, command, isRolldown) {
|
|
1001
1106
|
const useESM = command === "build" || isRolldown;
|
|
1002
|
-
const importLine =
|
|
1107
|
+
const importLine = command === "build" ? getRuntimeInitPromiseBootstrapCode() : useESM ? `${getRuntimeInitBootstrapCode()}
|
|
1108
|
+
const { initPromise } = globalThis[globalKey];` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
|
|
1003
1109
|
const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
|
|
1004
1110
|
const exportLine = command === "serve" && useESM ? "export default exportModule.default ?? exportModule" : useESM ? "export default exportModule" : "module.exports = exportModule";
|
|
1005
1111
|
return `
|
|
@@ -1049,14 +1155,19 @@ function getPreBuildLibImportId(pkg) {
|
|
|
1049
1155
|
}
|
|
1050
1156
|
const LOAD_SHARE_TAG = "__loadShare__";
|
|
1051
1157
|
const loadShareCacheMap = {};
|
|
1052
|
-
function
|
|
1158
|
+
function getLoadShareImportId(pkg, isRolldown, command) {
|
|
1053
1159
|
if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, isRolldown || command === "build" ? ".mjs" : ".js");
|
|
1160
|
+
return loadShareCacheMap[pkg].getImportId();
|
|
1161
|
+
}
|
|
1162
|
+
function getLoadShareModulePath(pkg, isRolldown, command) {
|
|
1163
|
+
if (!loadShareCacheMap[pkg]) getLoadShareImportId(pkg, isRolldown, command);
|
|
1054
1164
|
return loadShareCacheMap[pkg].getPath();
|
|
1055
1165
|
}
|
|
1056
1166
|
function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
|
|
1057
1167
|
if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, isRolldown || command === "build" ? ".mjs" : ".js");
|
|
1058
1168
|
const useESM = command === "build" || isRolldown;
|
|
1059
|
-
const importLine =
|
|
1169
|
+
const importLine = command === "build" ? getRuntimeInitPromiseBootstrapCode() : useESM ? `${getRuntimeInitBootstrapCode()}
|
|
1170
|
+
const { initPromise } = globalThis[globalKey];` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
|
|
1060
1171
|
const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
|
|
1061
1172
|
const useSsrProviderFallback = hasPackageDependency("vinext") && command === "build" && pkg === "react";
|
|
1062
1173
|
const providerImportId = getLocalProviderImportPath(pkg) || getPreBuildLibImportId(pkg);
|
|
@@ -1065,8 +1176,8 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
|
|
|
1065
1176
|
if (namedExports.length > 0) {
|
|
1066
1177
|
const destructure = `const { ${namedExports.map((name, i) => `${name}: __mf_${i}`).join(", ")} } = exportModule;`;
|
|
1067
1178
|
const namedExportLine = `export { ${namedExports.map((name, i) => `__mf_${i} as ${name}`).join(", ")} };`;
|
|
1068
|
-
exportLine = useESM ? `export default exportModule;\n ${destructure}\n ${namedExportLine}` : `module.exports = exportModule;\n ${destructure}\n Object.assign(module.exports, { ${namedExports.map((name, i) => `"${name}": __mf_${i}`).join(", ")} });`;
|
|
1069
|
-
} else exportLine = useESM ? `export default exportModule\n export * from ${JSON.stringify(getPreBuildLibImportId(pkg))}` : "module.exports = exportModule";
|
|
1179
|
+
exportLine = useESM ? `export default exportModule.default ?? exportModule;\n ${destructure}\n ${namedExportLine}` : `module.exports = exportModule;\n ${destructure}\n Object.assign(module.exports, { ${namedExports.map((name, i) => `"${name}": __mf_${i}`).join(", ")} });`;
|
|
1180
|
+
} else exportLine = useESM ? `export default exportModule.default ?? exportModule\n export * from ${JSON.stringify(getPreBuildLibImportId(pkg))}` : "module.exports = exportModule";
|
|
1070
1181
|
loadShareCacheMap[pkg].writeSync(`
|
|
1071
1182
|
import ${JSON.stringify(getPreBuildLibImportId(pkg))};
|
|
1072
1183
|
${command !== "build" ? `;() => import(${JSON.stringify(pkg)}).catch(() => {});` : ""}
|
|
@@ -1100,12 +1211,12 @@ new VirtualModule("localSharedImportMap");
|
|
|
1100
1211
|
function getLocalSharedImportMapPath() {
|
|
1101
1212
|
return getLocalSharedImportMapPath_temp();
|
|
1102
1213
|
}
|
|
1103
|
-
let
|
|
1214
|
+
let prevLocalSharedImportMapContent;
|
|
1104
1215
|
function writeLocalSharedImportMap() {
|
|
1105
|
-
const
|
|
1106
|
-
if (
|
|
1107
|
-
|
|
1108
|
-
writeLocalSharedImportMap_temp(
|
|
1216
|
+
const nextContent = generateLocalSharedImportMap();
|
|
1217
|
+
if (prevLocalSharedImportMapContent !== nextContent) {
|
|
1218
|
+
prevLocalSharedImportMapContent = nextContent;
|
|
1219
|
+
writeLocalSharedImportMap_temp(nextContent);
|
|
1109
1220
|
}
|
|
1110
1221
|
}
|
|
1111
1222
|
function generateLocalSharedImportMap() {
|
|
@@ -1118,7 +1229,7 @@ function generateLocalSharedImportMap() {
|
|
|
1118
1229
|
const shareItem = getNormalizeShareItem(pkg);
|
|
1119
1230
|
return `
|
|
1120
1231
|
${JSON.stringify(pkg)}: async () => {
|
|
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");
|
|
1232
|
+
${shareItem?.shareConfig.import === false ? `throw new Error(\`[Module Federation] Shared module '\${${JSON.stringify(pkg)}}' must be provided by host\`);` : isVinext && pkg === "react" ? `let pkg = await import("react");
|
|
1122
1233
|
return pkg;` : `let pkg = await import("${getPreBuildLibImportId(pkg)}");
|
|
1123
1234
|
return pkg;`}
|
|
1124
1235
|
}
|
|
@@ -1138,7 +1249,7 @@ function generateLocalSharedImportMap() {
|
|
|
1138
1249
|
from: ${JSON.stringify(options.name)},
|
|
1139
1250
|
async get () {
|
|
1140
1251
|
if (${shareItem.shareConfig.import === false}) {
|
|
1141
|
-
throw new Error(\`Shared module '\${${JSON.stringify(key)}}' must be provided by host\`);
|
|
1252
|
+
throw new Error(\`[Module Federation] Shared module '\${${JSON.stringify(key)}}' must be provided by host\`);
|
|
1142
1253
|
}
|
|
1143
1254
|
usedShared[${JSON.stringify(key)}].loaded = true
|
|
1144
1255
|
const {${JSON.stringify(key)}: pkgDynamicImport} = importMap
|
|
@@ -1188,7 +1299,7 @@ const REMOTE_ENTRY_ID = "virtual:mf-REMOTE_ENTRY_ID";
|
|
|
1188
1299
|
function getRemoteEntryId(options) {
|
|
1189
1300
|
return `${REMOTE_ENTRY_ID}:${`${options.name}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
|
|
1190
1301
|
}
|
|
1191
|
-
function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(options)) {
|
|
1302
|
+
function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(options), command = "build") {
|
|
1192
1303
|
const pluginImportNames = options.runtimePlugins.map((p, i) => {
|
|
1193
1304
|
if (typeof p === "string") return [
|
|
1194
1305
|
`$runtimePlugin_${i}`,
|
|
@@ -1204,15 +1315,25 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
|
|
|
1204
1315
|
return `
|
|
1205
1316
|
import {init as runtimeInit, loadRemote} from "@module-federation/runtime";
|
|
1206
1317
|
${pluginImportNames.map((item) => item[1]).join("\n")}
|
|
1207
|
-
|
|
1208
|
-
import {usedShared, usedRemotes} from "${getLocalSharedImportMapPath()}"
|
|
1209
|
-
import {
|
|
1210
|
-
initResolve
|
|
1211
|
-
} from "${virtualRuntimeInitStatus.getImportId()}"
|
|
1318
|
+
${command === "build" ? getRuntimeInitResolveBootstrapCode() : getRuntimeInitBootstrapCode() + "\n const { initResolve } = globalThis[globalKey];"}
|
|
1212
1319
|
const initTokens = {}
|
|
1213
1320
|
const shareScopeName = ${JSON.stringify(options.shareScope)}
|
|
1214
1321
|
const mfName = ${JSON.stringify(options.name)}
|
|
1322
|
+
let localSharedImportMapPromise
|
|
1323
|
+
let exposesMapPromise
|
|
1324
|
+
|
|
1325
|
+
async function getLocalSharedImportMap() {
|
|
1326
|
+
localSharedImportMapPromise ??= import("${getLocalSharedImportMapPath()}")
|
|
1327
|
+
return localSharedImportMapPromise
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
async function getExposesMap() {
|
|
1331
|
+
exposesMapPromise ??= import("${virtualExposesId}").then((mod) => mod.default ?? mod)
|
|
1332
|
+
return exposesMapPromise
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1215
1335
|
async function init(shared = {}, initScope = []) {
|
|
1336
|
+
const {usedShared, usedRemotes} = await getLocalSharedImportMap()
|
|
1216
1337
|
const initRes = runtimeInit({
|
|
1217
1338
|
name: mfName,
|
|
1218
1339
|
remotes: usedRemotes,
|
|
@@ -1235,13 +1356,14 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
|
|
|
1235
1356
|
initScope
|
|
1236
1357
|
}));
|
|
1237
1358
|
} catch (e) {
|
|
1238
|
-
console.error(e)
|
|
1359
|
+
console.error('[Module Federation]', e)
|
|
1239
1360
|
}
|
|
1240
1361
|
return initRes
|
|
1241
1362
|
}
|
|
1242
1363
|
|
|
1243
|
-
function getExposes(moduleName) {
|
|
1244
|
-
|
|
1364
|
+
async function getExposes(moduleName) {
|
|
1365
|
+
const exposesMap = await getExposesMap()
|
|
1366
|
+
if (!(moduleName in exposesMap)) throw new Error(\`[Module Federation] Module \${moduleName} does not exist in container.\`)
|
|
1245
1367
|
return (exposesMap[moduleName])().then(res => () => res)
|
|
1246
1368
|
}
|
|
1247
1369
|
export {
|
|
@@ -1253,13 +1375,8 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
|
|
|
1253
1375
|
const hostAutoInitModule = new VirtualModule("hostAutoInit", "__H_A_I__");
|
|
1254
1376
|
function writeHostAutoInit(remoteEntryId = REMOTE_ENTRY_ID) {
|
|
1255
1377
|
hostAutoInitModule.writeSync(`
|
|
1256
|
-
const
|
|
1257
|
-
|
|
1258
|
-
.then(remoteEntry => {
|
|
1259
|
-
return Promise.resolve(remoteEntry.__tla)
|
|
1260
|
-
.then(remoteEntry.init)
|
|
1261
|
-
.catch(remoteEntry.init)
|
|
1262
|
-
})
|
|
1378
|
+
const remoteEntry = await import("${remoteEntryId}");
|
|
1379
|
+
await remoteEntry.init();
|
|
1263
1380
|
`);
|
|
1264
1381
|
}
|
|
1265
1382
|
function getHostAutoInitImportId() {
|
|
@@ -1282,13 +1399,21 @@ function initVirtualModules(command, remoteEntryId) {
|
|
|
1282
1399
|
* If Rollup's deconflict renamed the alias but didn't update references
|
|
1283
1400
|
* in the code body, fall back to proxyLocal so they stay in sync.
|
|
1284
1401
|
*/
|
|
1285
|
-
function resolveProxyAlias(binding, proxyLocal, code, fullImport) {
|
|
1402
|
+
function resolveProxyAlias(binding, proxyLocal, code, fullImport, claimedLocals = /* @__PURE__ */ new Set()) {
|
|
1286
1403
|
const codeWithoutImport = code.replace(fullImport, "");
|
|
1287
1404
|
const escapedLocal = binding.local.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1288
1405
|
const localUsedInCode = new RegExp(`\\b${escapedLocal}\\b`).test(codeWithoutImport);
|
|
1406
|
+
const claimedImportLocals = /* @__PURE__ */ new Set();
|
|
1407
|
+
const importRe = /import\s*\{([^}]+)\}\s*from\s*["'][^"']+["']\s*;?/g;
|
|
1408
|
+
let match;
|
|
1409
|
+
while ((match = importRe.exec(codeWithoutImport)) !== null) for (const spec of match[1].split(",")) {
|
|
1410
|
+
const parts = spec.trim().split(/\s+as\s+/);
|
|
1411
|
+
claimedImportLocals.add((parts[1] || parts[0]).trim());
|
|
1412
|
+
}
|
|
1413
|
+
const local = !localUsedInCode && !claimedLocals.has(proxyLocal) && !claimedImportLocals.has(proxyLocal) ? proxyLocal : binding.local;
|
|
1289
1414
|
return {
|
|
1290
1415
|
imported: binding.imported,
|
|
1291
|
-
local
|
|
1416
|
+
local
|
|
1292
1417
|
};
|
|
1293
1418
|
}
|
|
1294
1419
|
function findRemoteEntryFile(filename, bundle) {
|
|
@@ -1655,14 +1780,14 @@ const promise = new Promise((resolve, reject) => {
|
|
|
1655
1780
|
});
|
|
1656
1781
|
function setParseTimeout(timeout) {
|
|
1657
1782
|
if (!_parseTimeout) _parseTimeout = setTimeout(() => {
|
|
1658
|
-
|
|
1783
|
+
mfWarn(`Parse timeout (${timeout}s) - forcing resolve`);
|
|
1659
1784
|
_resolve(1);
|
|
1660
1785
|
}, timeout * 1e3);
|
|
1661
1786
|
}
|
|
1662
1787
|
function resetIdleTimeout(timeout) {
|
|
1663
1788
|
clearTimeout(_parseTimeout);
|
|
1664
1789
|
_parseTimeout = setTimeout(() => {
|
|
1665
|
-
|
|
1790
|
+
mfWarn(`moduleParseIdleTimeout: no module activity for ${timeout}s, forcing resolve. Some shared/remote dependencies may be missing. Consider increasing moduleParseIdleTimeout.`);
|
|
1666
1791
|
_resolve(1);
|
|
1667
1792
|
}, timeout * 1e3);
|
|
1668
1793
|
}
|
|
@@ -1743,14 +1868,14 @@ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposes
|
|
|
1743
1868
|
}
|
|
1744
1869
|
},
|
|
1745
1870
|
load(id) {
|
|
1746
|
-
if (id === remoteEntryId) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId));
|
|
1871
|
+
if (id === remoteEntryId) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId, _command));
|
|
1747
1872
|
if (id === virtualExposesId) return generateExposes(options);
|
|
1748
1873
|
if (_command === "serve" && id.includes(getHostAutoInitPath())) return id;
|
|
1749
1874
|
},
|
|
1750
1875
|
transform(code, id) {
|
|
1751
1876
|
return mapCodeToCodeWithSourcemap((() => {
|
|
1752
1877
|
if (!filter(id)) return;
|
|
1753
|
-
if (id.includes(remoteEntryId)) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId));
|
|
1878
|
+
if (id.includes(remoteEntryId)) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId, _command));
|
|
1754
1879
|
if (id === virtualExposesId) return generateExposes(options);
|
|
1755
1880
|
if (id.includes(getHostAutoInitPath())) {
|
|
1756
1881
|
if (_command === "serve") {
|
|
@@ -1783,7 +1908,7 @@ function pluginProxyRemotes_default(options) {
|
|
|
1783
1908
|
return {
|
|
1784
1909
|
name: "proxyRemotes",
|
|
1785
1910
|
config(config, { command: _command }) {
|
|
1786
|
-
const isRolldown =
|
|
1911
|
+
const isRolldown = getIsRolldown(this);
|
|
1787
1912
|
Object.keys(remotes).forEach((key) => {
|
|
1788
1913
|
const remote = remotes[key];
|
|
1789
1914
|
config.resolve.alias.push({
|
|
@@ -1855,7 +1980,7 @@ function proxySharedModule(options) {
|
|
|
1855
1980
|
config(config, { command }) {
|
|
1856
1981
|
setPackageDetectionCwd(config.root || process.cwd());
|
|
1857
1982
|
isVinext = hasPackageDependency("vinext");
|
|
1858
|
-
const isRolldown =
|
|
1983
|
+
const isRolldown = getIsRolldown(this);
|
|
1859
1984
|
_command = command;
|
|
1860
1985
|
config.resolve.alias.push(...Object.keys(shared).filter((key) => !(isVinext && key === "react")).map((key) => {
|
|
1861
1986
|
const keyBase = key.endsWith("/") ? key.slice(0, -1) : key;
|
|
@@ -1935,7 +2060,6 @@ const VarRemoteEntry = () => {
|
|
|
1935
2060
|
if (req.url?.replace(/\?.*/, "") === (viteConfig.base + varFilename).replace(/^\/?/, "/")) {
|
|
1936
2061
|
res.setHeader("Content-Type", "text/javascript");
|
|
1937
2062
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1938
|
-
console.log({ filename });
|
|
1939
2063
|
res.end(generateVarRemoteEntry(filename));
|
|
1940
2064
|
} else next();
|
|
1941
2065
|
});
|
|
@@ -1951,9 +2075,9 @@ const VarRemoteEntry = () => {
|
|
|
1951
2075
|
},
|
|
1952
2076
|
async generateBundle(options, bundle) {
|
|
1953
2077
|
if (!varFilename) return;
|
|
1954
|
-
if (!isValidVarName(name))
|
|
2078
|
+
if (!isValidVarName(name)) mfWarn(`Provided remote name "${name}" is not valid for "var" remoteEntry type, thus it's placed in globalThis['${name}'].\nIt may cause problems, so you would better want to use valid var name (see https://www.w3schools.com/js/js_variables.asp).`);
|
|
1955
2079
|
const remoteEntryFile = findRemoteEntryFile(mfOptions.filename, bundle);
|
|
1956
|
-
if (!remoteEntryFile) throw
|
|
2080
|
+
if (!remoteEntryFile) throw createModuleFederationError(`Couldn't find a remoteEntry chunk file for ${mfOptions.filename}, can't generate varRemoteEntry file`);
|
|
1957
2081
|
this.emitFile({
|
|
1958
2082
|
type: "asset",
|
|
1959
2083
|
fileName: varFilename,
|
|
@@ -1978,7 +2102,7 @@ const VarRemoteEntry = () => {
|
|
|
1978
2102
|
function getScriptUrl() {
|
|
1979
2103
|
const currentScript = document.currentScript;
|
|
1980
2104
|
if (!currentScript) {
|
|
1981
|
-
console.error("[
|
|
2105
|
+
console.error("[Module Federation] ${varFilename} script should be called from sync <script> tag (document.currentScript is undefined)")
|
|
1982
2106
|
return '/';
|
|
1983
2107
|
}
|
|
1984
2108
|
return document.currentScript.src.replace(/\\/[^/]*$/, '/');
|
|
@@ -2009,6 +2133,58 @@ var aliasToArrayPlugin_default = {
|
|
|
2009
2133
|
}
|
|
2010
2134
|
};
|
|
2011
2135
|
//#endregion
|
|
2136
|
+
//#region src/utils/controlChunkSanitizer.ts
|
|
2137
|
+
const FEDERATION_CONTROL_CHUNK_HINTS = [
|
|
2138
|
+
"hostInit",
|
|
2139
|
+
"virtualExposes",
|
|
2140
|
+
"localSharedImportMap"
|
|
2141
|
+
];
|
|
2142
|
+
function stripEmptyPreloadCalls(code) {
|
|
2143
|
+
const helperImportRegex = /import\s*\{\s*_\s*as\s*(\w+)\s*\}\s*from\s*["'][^"']+["']\s*;?/g;
|
|
2144
|
+
const helperAliases = [...code.matchAll(helperImportRegex)].map((match) => match[1]);
|
|
2145
|
+
let nextCode = code;
|
|
2146
|
+
for (const alias of helperAliases) {
|
|
2147
|
+
const marker = `${alias}(()=>`;
|
|
2148
|
+
let start = nextCode.indexOf(marker);
|
|
2149
|
+
while (start !== -1) {
|
|
2150
|
+
const exprStart = start + marker.length;
|
|
2151
|
+
let depth = 0;
|
|
2152
|
+
let cursor = exprStart;
|
|
2153
|
+
let replacementEnd = -1;
|
|
2154
|
+
while (cursor < nextCode.length) {
|
|
2155
|
+
const char = nextCode[cursor];
|
|
2156
|
+
if (char === "(") depth++;
|
|
2157
|
+
else if (char === ")") depth--;
|
|
2158
|
+
else if (depth === 0 && nextCode.startsWith(",[],import.meta.url)", cursor)) {
|
|
2159
|
+
replacementEnd = cursor;
|
|
2160
|
+
break;
|
|
2161
|
+
}
|
|
2162
|
+
cursor++;
|
|
2163
|
+
}
|
|
2164
|
+
if (replacementEnd === -1) break;
|
|
2165
|
+
const expression = nextCode.slice(exprStart, replacementEnd);
|
|
2166
|
+
nextCode = nextCode.slice(0, start) + expression + nextCode.slice(replacementEnd + 20);
|
|
2167
|
+
start = nextCode.indexOf(marker, start + expression.length);
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
nextCode = nextCode.replace(/import\s*["'][^"']*__loadShare__[^"']*["']\s*;?/g, "");
|
|
2171
|
+
nextCode = nextCode.replace(helperImportRegex, (statement, local) => {
|
|
2172
|
+
return new RegExp(`\\b${local}\\s*\\(`).test(nextCode.replace(statement, "")) ? statement : "";
|
|
2173
|
+
});
|
|
2174
|
+
return nextCode;
|
|
2175
|
+
}
|
|
2176
|
+
function isFederationControlChunk(fileName, filename) {
|
|
2177
|
+
return fileName.includes(filename) || FEDERATION_CONTROL_CHUNK_HINTS.some((hint) => fileName.includes(hint));
|
|
2178
|
+
}
|
|
2179
|
+
function sanitizeFederationControlChunk(code, fileName, filename) {
|
|
2180
|
+
let nextCode = stripEmptyPreloadCalls(code);
|
|
2181
|
+
if (fileName.includes("localSharedImportMap")) {
|
|
2182
|
+
const remoteEntryImportRegex = new RegExp(`import\\s*["'][^"']*${filename.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["']\\s*;?`, "g");
|
|
2183
|
+
nextCode = nextCode.replace(remoteEntryImportRegex, "");
|
|
2184
|
+
}
|
|
2185
|
+
return nextCode;
|
|
2186
|
+
}
|
|
2187
|
+
//#endregion
|
|
2012
2188
|
//#region src/utils/normalizeOptimizeDeps.ts
|
|
2013
2189
|
var normalizeOptimizeDeps_default = {
|
|
2014
2190
|
name: "normalizeOptimizeDeps",
|
|
@@ -2025,6 +2201,25 @@ var normalizeOptimizeDeps_default = {
|
|
|
2025
2201
|
};
|
|
2026
2202
|
//#endregion
|
|
2027
2203
|
//#region src/index.ts
|
|
2204
|
+
const UNSAFE_JS_SOURCE_CHAR_MAP = {
|
|
2205
|
+
"<": "\\u003C",
|
|
2206
|
+
">": "\\u003E",
|
|
2207
|
+
"/": "\\u002F",
|
|
2208
|
+
"\\": "\\\\",
|
|
2209
|
+
"\b": "\\b",
|
|
2210
|
+
"\f": "\\f",
|
|
2211
|
+
"\n": "\\n",
|
|
2212
|
+
"\r": "\\r",
|
|
2213
|
+
" ": "\\t",
|
|
2214
|
+
"\0": "\\0",
|
|
2215
|
+
"\u2028": "\\u2028",
|
|
2216
|
+
"\u2029": "\\u2029"
|
|
2217
|
+
};
|
|
2218
|
+
function escapeUnsafeJsSourceChars(str) {
|
|
2219
|
+
return str.replace(/[<>/\\\b\f\n\r\t\0\u2028\u2029]/g, (char) => {
|
|
2220
|
+
return UNSAFE_JS_SOURCE_CHAR_MAP[char] ?? char;
|
|
2221
|
+
});
|
|
2222
|
+
}
|
|
2028
2223
|
/**
|
|
2029
2224
|
* Plugin that runs FIRST to create virtual module files in the config hook.
|
|
2030
2225
|
* This prevents 504 "Outdated Optimize Dep" errors by ensuring files exist
|
|
@@ -2044,7 +2239,7 @@ function createEarlyVirtualModulesPlugin(options) {
|
|
|
2044
2239
|
VirtualModule.ensureVirtualPackageExists();
|
|
2045
2240
|
initVirtualModules(_command, getRemoteEntryId(options));
|
|
2046
2241
|
if (_command !== "serve") return;
|
|
2047
|
-
const isRolldown =
|
|
2242
|
+
const isRolldown = getIsRolldown(this);
|
|
2048
2243
|
if (remotes && Object.keys(remotes).length > 0) for (const key of Object.keys(remotes)) addUsedRemote(key, key);
|
|
2049
2244
|
if (shared && Object.keys(shared).length > 0) {
|
|
2050
2245
|
config.optimizeDeps = config.optimizeDeps || {};
|
|
@@ -2061,6 +2256,7 @@ function createEarlyVirtualModulesPlugin(options) {
|
|
|
2061
2256
|
writeLoadShareModule(key, shareItem, _command, isRolldown);
|
|
2062
2257
|
writePreBuildLibPath(key);
|
|
2063
2258
|
addUsedShares(key);
|
|
2259
|
+
config.optimizeDeps.include.push(getLoadShareImportId(key, isRolldown, _command));
|
|
2064
2260
|
config.optimizeDeps.include.push(getPreBuildLibImportId(key));
|
|
2065
2261
|
}
|
|
2066
2262
|
writeLocalSharedImportMap();
|
|
@@ -2072,7 +2268,7 @@ function federation(mfUserOptions) {
|
|
|
2072
2268
|
const options = normalizeModuleFederationOptions(mfUserOptions);
|
|
2073
2269
|
const isVinext = hasPackageDependency("vinext");
|
|
2074
2270
|
const { name, remotes, shared, filename, hostInitInjectLocation } = options;
|
|
2075
|
-
if (!name) throw
|
|
2271
|
+
if (!name) throw createModuleFederationError("name is required");
|
|
2076
2272
|
const remoteEntryId = getRemoteEntryId(options);
|
|
2077
2273
|
const virtualExposesId = getVirtualExposesId(options);
|
|
2078
2274
|
let command;
|
|
@@ -2146,9 +2342,28 @@ function federation(mfUserOptions) {
|
|
|
2146
2342
|
config(config) {
|
|
2147
2343
|
const runtimeInitId = virtualRuntimeInitStatus.getImportId();
|
|
2148
2344
|
config.build = config.build || {};
|
|
2149
|
-
config.build.
|
|
2150
|
-
|
|
2151
|
-
const
|
|
2345
|
+
if (config.build.modulePreload !== false) {
|
|
2346
|
+
const currentModulePreload = config.build.modulePreload && typeof config.build.modulePreload === "object" ? config.build.modulePreload : {};
|
|
2347
|
+
const existingResolveDependencies = currentModulePreload.resolveDependencies;
|
|
2348
|
+
config.build.modulePreload = {
|
|
2349
|
+
...currentModulePreload,
|
|
2350
|
+
resolveDependencies(filename, deps, context) {
|
|
2351
|
+
const resolvedDeps = existingResolveDependencies ? existingResolveDependencies(filename, deps, context) : deps;
|
|
2352
|
+
const hostFile = path.basename(context.hostId);
|
|
2353
|
+
return context.hostType === "js" && (hostFile === options.filename || hostFile.includes("hostInit") || hostFile.includes("virtualExposes") || hostFile.includes("localSharedImportMap")) ? [] : resolvedDeps;
|
|
2354
|
+
}
|
|
2355
|
+
};
|
|
2356
|
+
}
|
|
2357
|
+
let warnedAboutCodeSplitting = false;
|
|
2358
|
+
const ensureCodeSplitting = (output) => {
|
|
2359
|
+
if (output?.codeSplitting !== false) return;
|
|
2360
|
+
delete output.codeSplitting;
|
|
2361
|
+
if (warnedAboutCodeSplitting) return;
|
|
2362
|
+
warnedAboutCodeSplitting = true;
|
|
2363
|
+
mfWarn("Ignoring `build.rolldownOptions.output.codeSplitting = false` because module federation requires chunk splitting.");
|
|
2364
|
+
};
|
|
2365
|
+
const applyManualChunks = (output) => {
|
|
2366
|
+
ensureCodeSplitting(output);
|
|
2152
2367
|
const existingManualChunks = output.manualChunks;
|
|
2153
2368
|
output.manualChunks = function(id) {
|
|
2154
2369
|
if (id.includes(runtimeInitId)) return "runtimeInit";
|
|
@@ -2161,7 +2376,12 @@ function federation(mfUserOptions) {
|
|
|
2161
2376
|
for (const [key, ids] of Object.entries(existingManualChunks)) if (Array.isArray(ids) && ids.some((v) => id.includes(v))) return key;
|
|
2162
2377
|
}
|
|
2163
2378
|
};
|
|
2164
|
-
}
|
|
2379
|
+
};
|
|
2380
|
+
config.build.rollupOptions = config.build.rollupOptions || {};
|
|
2381
|
+
if (!Array.isArray(config.build.rollupOptions.output)) applyManualChunks(config.build.rollupOptions.output ||= {});
|
|
2382
|
+
const buildWithRolldown = config.build;
|
|
2383
|
+
buildWithRolldown.rolldownOptions = buildWithRolldown.rolldownOptions || {};
|
|
2384
|
+
if (!Array.isArray(buildWithRolldown.rolldownOptions.output)) applyManualChunks(buildWithRolldown.rolldownOptions.output ||= {});
|
|
2165
2385
|
},
|
|
2166
2386
|
load(id) {
|
|
2167
2387
|
if (id.startsWith("\0")) return;
|
|
@@ -2193,6 +2413,11 @@ function federation(mfUserOptions) {
|
|
|
2193
2413
|
}
|
|
2194
2414
|
},
|
|
2195
2415
|
generateBundle(_, bundle) {
|
|
2416
|
+
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
2417
|
+
if (chunk.type !== "chunk") continue;
|
|
2418
|
+
if (!isFederationControlChunk(fileName, filename)) continue;
|
|
2419
|
+
chunk.code = sanitizeFederationControlChunk(chunk.code, fileName, filename);
|
|
2420
|
+
}
|
|
2196
2421
|
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
2197
2422
|
if (chunk.type !== "chunk") continue;
|
|
2198
2423
|
if (fileName.includes("__loadShare__")) continue;
|
|
@@ -2230,12 +2455,12 @@ function federation(mfUserOptions) {
|
|
|
2230
2455
|
fileName
|
|
2231
2456
|
});
|
|
2232
2457
|
}
|
|
2233
|
-
if (proxyChunks.size
|
|
2234
|
-
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
2458
|
+
if (proxyChunks.size > 0) for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
2235
2459
|
if (chunk.type !== "chunk") continue;
|
|
2236
2460
|
if (fileName.includes("__loadShare__")) continue;
|
|
2237
2461
|
let code = chunk.code;
|
|
2238
2462
|
let modified = false;
|
|
2463
|
+
const claimedLocals = /* @__PURE__ */ new Set();
|
|
2239
2464
|
for (const [proxyFileName, proxyInfo] of proxyChunks) {
|
|
2240
2465
|
const proxyBaseName = proxyFileName.replace(/^.*\//, "").replace(/\.js$/, "").replace(/-[A-Za-z0-9_-]+$/, "");
|
|
2241
2466
|
const importMatch = new RegExp(`import\\s*\\{([^}]+)\\}\\s*from\\s*["']([^"']*${proxyBaseName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[^"']*)["']\\s*;?`).exec(code);
|
|
@@ -2258,9 +2483,12 @@ function federation(mfUserOptions) {
|
|
|
2258
2483
|
}
|
|
2259
2484
|
const inlineable = [];
|
|
2260
2485
|
const nonInlineable = [];
|
|
2486
|
+
const pendingLocals = new Set(bindings.map((binding) => binding.local));
|
|
2261
2487
|
for (const b of bindings) {
|
|
2488
|
+
pendingLocals.delete(b.local);
|
|
2262
2489
|
const proxyLocal = exportMap[b.imported];
|
|
2263
2490
|
if (!proxyLocal) {
|
|
2491
|
+
claimedLocals.add(b.local);
|
|
2264
2492
|
nonInlineable.push(b);
|
|
2265
2493
|
continue;
|
|
2266
2494
|
}
|
|
@@ -2282,7 +2510,14 @@ function federation(mfUserOptions) {
|
|
|
2282
2510
|
local: b.local,
|
|
2283
2511
|
funcBody: renamedFunc
|
|
2284
2512
|
});
|
|
2285
|
-
|
|
2513
|
+
claimedLocals.add(b.local);
|
|
2514
|
+
} else {
|
|
2515
|
+
const unavailableLocals = new Set(claimedLocals);
|
|
2516
|
+
pendingLocals.forEach((local) => unavailableLocals.add(local));
|
|
2517
|
+
const resolvedBinding = resolveProxyAlias(b, proxyLocal, code, fullImport, unavailableLocals);
|
|
2518
|
+
claimedLocals.add(resolvedBinding.local);
|
|
2519
|
+
nonInlineable.push(resolvedBinding);
|
|
2520
|
+
}
|
|
2286
2521
|
}
|
|
2287
2522
|
const hasRenamedAlias = nonInlineable.some((b) => bindings.find((ob) => ob.imported === b.imported)?.local !== b.local);
|
|
2288
2523
|
if (inlineable.length === 0 && !hasRenamedAlias) continue;
|
|
@@ -2296,6 +2531,28 @@ function federation(mfUserOptions) {
|
|
|
2296
2531
|
}
|
|
2297
2532
|
}
|
|
2298
2533
|
},
|
|
2534
|
+
{
|
|
2535
|
+
name: "module-federation-strip-empty-preload-helper",
|
|
2536
|
+
enforce: "post",
|
|
2537
|
+
apply: "build",
|
|
2538
|
+
renderChunk(code, chunk) {
|
|
2539
|
+
if (!isFederationControlChunk(chunk.fileName, filename)) return;
|
|
2540
|
+
const nextCode = sanitizeFederationControlChunk(code, chunk.fileName, filename);
|
|
2541
|
+
return nextCode === code ? null : {
|
|
2542
|
+
code: nextCode,
|
|
2543
|
+
map: null
|
|
2544
|
+
};
|
|
2545
|
+
},
|
|
2546
|
+
writeBundle(outputOptions, bundle) {
|
|
2547
|
+
if (!outputOptions.dir) return;
|
|
2548
|
+
for (const chunk of Object.values(bundle)) {
|
|
2549
|
+
if (chunk.type !== "chunk") continue;
|
|
2550
|
+
if (!isFederationControlChunk(chunk.fileName, filename)) continue;
|
|
2551
|
+
const outputPath = path.join(outputOptions.dir, chunk.fileName);
|
|
2552
|
+
writeFileSync(outputPath, sanitizeFederationControlChunk(readFileSync(outputPath, "utf-8"), chunk.fileName, filename));
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
},
|
|
2299
2556
|
{
|
|
2300
2557
|
name: "module-federation-dev-await-shared-init",
|
|
2301
2558
|
apply: "serve",
|
|
@@ -2328,9 +2585,9 @@ function federation(mfUserOptions) {
|
|
|
2328
2585
|
enforce: "post",
|
|
2329
2586
|
_options: options,
|
|
2330
2587
|
config(config, { command: _command }) {
|
|
2331
|
-
const isRolldown =
|
|
2588
|
+
const isRolldown = getIsRolldown(this);
|
|
2332
2589
|
let implementation = options.implementation;
|
|
2333
|
-
if (isRolldown) implementation = implementation.replace(/\.cjs\.
|
|
2590
|
+
if (isRolldown) implementation = implementation.replace(/\.cjs(\.js)?$/, ".js");
|
|
2334
2591
|
config.resolve.alias.push({
|
|
2335
2592
|
find: "@module-federation/runtime",
|
|
2336
2593
|
replacement: implementation
|
|
@@ -2357,7 +2614,7 @@ function federation(mfUserOptions) {
|
|
|
2357
2614
|
const resolvedTarget = options.target ?? (config.build?.ssr ? "node" : "web");
|
|
2358
2615
|
if (!config.define) config.define = {};
|
|
2359
2616
|
if (!("ENV_TARGET" in config.define)) config.define["ENV_TARGET"] = JSON.stringify(resolvedTarget);
|
|
2360
|
-
if (options.target && "ENV_TARGET" in config.define && config.define["ENV_TARGET"] !== JSON.stringify(options.target))
|
|
2617
|
+
if (options.target && "ENV_TARGET" in config.define && config.define["ENV_TARGET"] !== JSON.stringify(options.target)) mfWarn(`ENV_TARGET define (${config.define["ENV_TARGET"]}) differs from target option ("${options.target}"). ENV_TARGET will not be overridden.`);
|
|
2361
2618
|
}
|
|
2362
2619
|
},
|
|
2363
2620
|
...Manifest(),
|
|
@@ -2370,13 +2627,18 @@ function federation(mfUserOptions) {
|
|
|
2370
2627
|
for (const chunk of Object.values(bundle)) {
|
|
2371
2628
|
if (chunk.type !== "chunk") continue;
|
|
2372
2629
|
if (!chunk.code.includes("modulepreload")) continue;
|
|
2373
|
-
const
|
|
2630
|
+
const chunkDir = path.dirname(chunk.fileName);
|
|
2631
|
+
const prefixToRoot = chunkDir === "." ? "" : `${path.relative(chunkDir, ".")}/`;
|
|
2632
|
+
const replacementExpr = prefixToRoot ? `${escapeUnsafeJsSourceChars(JSON.stringify(prefixToRoot))}+$1` : "$1";
|
|
2633
|
+
const replacement = `=function($1){return new URL(${replacementExpr},import.meta.url).href}`;
|
|
2374
2634
|
const replaced = chunk.code.replace(/=\(?(\w+)(?:,\w+)?\)?\s*=>\s*["'`][^"'`]*["'`]\s*\+\s*\1/, replacement);
|
|
2375
2635
|
if (replaced !== chunk.code) {
|
|
2376
2636
|
chunk.code = replaced;
|
|
2377
2637
|
continue;
|
|
2378
2638
|
}
|
|
2379
2639
|
chunk.code = chunk.code.replace(/=function\((\w+)(?:,\w+)?\)\{return\s*["'`][^"'`]*["'`]\s*\+\s*\1\s*\}/, replacement);
|
|
2640
|
+
chunk.code = chunk.code.replace(/=function\((\w+)(?:,\w+)?\)\{return new URL\("\.\.\/"\+\1,import\.meta\.url\)\.href\}/, replacement);
|
|
2641
|
+
chunk.code = chunk.code.replace(/new URL\("\.\.\/"\+(\w+),import\.meta\.url\)\.href/g, `new URL(${replacementExpr},import.meta.url).href`);
|
|
2380
2642
|
}
|
|
2381
2643
|
}
|
|
2382
2644
|
}] : []
|