@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.cjs
CHANGED
|
@@ -81,6 +81,38 @@ function inlineEntryScripts(html, initSrc) {
|
|
|
81
81
|
return html.replace("<head>", `<head><script type="module" src=${JSON.stringify(src)}><\/script>`);
|
|
82
82
|
}
|
|
83
83
|
//#endregion
|
|
84
|
+
//#region src/utils/logger.ts
|
|
85
|
+
const MODULE_FEDERATION_LOG_PREFIX = "[Module Federation]";
|
|
86
|
+
function formatModuleFederationMessage(message) {
|
|
87
|
+
return `${MODULE_FEDERATION_LOG_PREFIX} ${message}`;
|
|
88
|
+
}
|
|
89
|
+
function createModuleFederationError(message) {
|
|
90
|
+
return new Error(formatModuleFederationMessage(message));
|
|
91
|
+
}
|
|
92
|
+
function toConsoleArgs(message, rest = []) {
|
|
93
|
+
if (typeof message === "string") return [formatModuleFederationMessage(message), ...rest];
|
|
94
|
+
if (message === void 0) return [MODULE_FEDERATION_LOG_PREFIX, ...rest];
|
|
95
|
+
return [
|
|
96
|
+
MODULE_FEDERATION_LOG_PREFIX,
|
|
97
|
+
message,
|
|
98
|
+
...rest
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
const moduleFederationConsole = {
|
|
102
|
+
log(message, ...rest) {
|
|
103
|
+
console.log(...toConsoleArgs(message, rest));
|
|
104
|
+
},
|
|
105
|
+
warn(message, ...rest) {
|
|
106
|
+
console.warn(...toConsoleArgs(message, rest));
|
|
107
|
+
},
|
|
108
|
+
error(message, ...rest) {
|
|
109
|
+
console.error(...toConsoleArgs(message, rest));
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
moduleFederationConsole.log;
|
|
113
|
+
const mfWarn = moduleFederationConsole.warn;
|
|
114
|
+
const mfError = moduleFederationConsole.error;
|
|
115
|
+
//#endregion
|
|
84
116
|
//#region src/utils/packageUtils.ts
|
|
85
117
|
const dependencyPresenceCache = /* @__PURE__ */ new Map();
|
|
86
118
|
let packageDetectionCwd;
|
|
@@ -104,7 +136,7 @@ function setPackageDetectionCwd(cwd) {
|
|
|
104
136
|
* @returns {string} - The encoded file name.
|
|
105
137
|
*/
|
|
106
138
|
function packageNameEncode(name) {
|
|
107
|
-
if (typeof name !== "string") throw
|
|
139
|
+
if (typeof name !== "string") throw createModuleFederationError("A string package name is required");
|
|
108
140
|
return name.replace(/@/g, "_mf_0_").replace(/\//g, "_mf_1_").replace(/-/g, "_mf_2_").replace(/\./g, "_mf_3_");
|
|
109
141
|
}
|
|
110
142
|
/**
|
|
@@ -113,7 +145,7 @@ function packageNameEncode(name) {
|
|
|
113
145
|
* @returns {string} - The decoded package name.
|
|
114
146
|
*/
|
|
115
147
|
function packageNameDecode(encoded) {
|
|
116
|
-
if (typeof encoded !== "string") throw
|
|
148
|
+
if (typeof encoded !== "string") throw createModuleFederationError("A string encoded file name is required");
|
|
117
149
|
return encoded.replace(/_mf_0_/g, "@").replace(/_mf_1_/g, "/").replace(/_mf_2_/g, "-").replace(/_mf_3_/g, ".");
|
|
118
150
|
}
|
|
119
151
|
/**
|
|
@@ -125,6 +157,13 @@ function removePathFromNpmPackage(packageString) {
|
|
|
125
157
|
const match = packageString.match(/^(?:@[^/]+\/)?[^/]+/);
|
|
126
158
|
return match ? match[0] : packageString;
|
|
127
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* Detect whether the current bundler is Rolldown (Vite 8+) by checking
|
|
162
|
+
* for `meta.rolldownVersion` on the plugin hook context.
|
|
163
|
+
*/
|
|
164
|
+
function getIsRolldown(ctx) {
|
|
165
|
+
return !!ctx?.meta?.rolldownVersion;
|
|
166
|
+
}
|
|
128
167
|
function hasPackageDependency(dependencyName, cwd = packageDetectionCwd || process.cwd()) {
|
|
129
168
|
const cacheKey = getDependencyCacheKey(cwd, dependencyName);
|
|
130
169
|
const cached = dependencyPresenceCache.get(cacheKey);
|
|
@@ -206,6 +245,12 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
|
|
|
206
245
|
else if (Array.isArray(inputOptions)) entryFiles = inputOptions;
|
|
207
246
|
else if (typeof inputOptions === "object") entryFiles = Object.values(inputOptions);
|
|
208
247
|
if (entryFiles && entryFiles.length > 0) htmlFilePath = getFirstHtmlEntryFile(entryFiles);
|
|
248
|
+
if (_command === "serve" && htmlFilePath && fs.existsSync(htmlFilePath)) {
|
|
249
|
+
const htmlContent = fs.readFileSync(htmlFilePath, "utf-8");
|
|
250
|
+
const scriptRegex = /<script\s+[^>]*src=["']([^"']+)["'][^>]*>/gi;
|
|
251
|
+
let match;
|
|
252
|
+
while ((match = scriptRegex.exec(htmlContent)) !== null) entryFiles.push(match[1]);
|
|
253
|
+
}
|
|
209
254
|
},
|
|
210
255
|
buildStart() {
|
|
211
256
|
if (_command === "serve") return;
|
|
@@ -239,7 +284,7 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
|
|
|
239
284
|
if (typeof result === "string") return result;
|
|
240
285
|
if (result && typeof result === "object") {
|
|
241
286
|
if ("runtime" in result) {
|
|
242
|
-
|
|
287
|
+
mfWarn("renderBuiltUrl returned runtime code for HTML injection. Runtime code cannot be used in <script src=\"\">. Falling back to base path.");
|
|
243
288
|
return viteConfig.base + file;
|
|
244
289
|
}
|
|
245
290
|
if (result.relative) return file;
|
|
@@ -304,11 +349,11 @@ function checkAliasConflicts(options) {
|
|
|
304
349
|
});
|
|
305
350
|
}
|
|
306
351
|
if (conflicts.length > 0) {
|
|
307
|
-
|
|
352
|
+
mfWarn("Detected alias conflicts with shared modules:");
|
|
308
353
|
conflicts.forEach(({ sharedModule, alias, target }) => {
|
|
309
|
-
|
|
354
|
+
mfWarn(`Shared module "${sharedModule}" is aliased by "${alias}" to "${target}"`);
|
|
310
355
|
});
|
|
311
|
-
|
|
356
|
+
mfWarn("This may cause runtime errors as the shared module will bypass Module Federation's sharing mechanism.");
|
|
312
357
|
}
|
|
313
358
|
}
|
|
314
359
|
};
|
|
@@ -337,7 +382,7 @@ function PluginDevProxyModuleTopLevelAwait() {
|
|
|
337
382
|
try {
|
|
338
383
|
ast = this.parse(code, { allowReturnOutsideFunction: true });
|
|
339
384
|
} catch (e) {
|
|
340
|
-
throw
|
|
385
|
+
throw createModuleFederationError(`${id}: ${e}`);
|
|
341
386
|
}
|
|
342
387
|
const magicString = new magic_string.default(code);
|
|
343
388
|
const walk = await loadWalk();
|
|
@@ -465,7 +510,7 @@ const normalizeDevDtsOptions = (dts, context) => {
|
|
|
465
510
|
const logDtsError = (error, dtsOptions) => {
|
|
466
511
|
if (dtsOptions === false) return;
|
|
467
512
|
if (typeof dtsOptions === "object" && dtsOptions && dtsOptions.displayErrorInTerminal === false) return;
|
|
468
|
-
|
|
513
|
+
mfError(error);
|
|
469
514
|
};
|
|
470
515
|
function pluginDts(options) {
|
|
471
516
|
if (options.dts === false) return [];
|
|
@@ -493,7 +538,7 @@ function pluginDts(options) {
|
|
|
493
538
|
if (!normalizedDevOptions || !resolvedConfig) return;
|
|
494
539
|
const devOptions = normalizedDevOptions;
|
|
495
540
|
if (devOptions.disableDynamicRemoteTypeHints && devOptions.disableHotTypesReload && devOptions.disableLiveReload) return;
|
|
496
|
-
if (!options.name) throw
|
|
541
|
+
if (!options.name) throw createModuleFederationError("name is required if you want to enable dev server!");
|
|
497
542
|
const outputDir = resolveOutputDir(resolvedConfig);
|
|
498
543
|
const normalizedDtsOptions = normalizeDevDtsOptions(options.dts, resolvedConfig.root);
|
|
499
544
|
if (typeof normalizedDtsOptions !== "object") return;
|
|
@@ -674,6 +719,10 @@ function searchPackageVersion(sharedName) {
|
|
|
674
719
|
}
|
|
675
720
|
} catch (_) {}
|
|
676
721
|
}
|
|
722
|
+
function inferVersionFromRequiredVersion(requiredVersion) {
|
|
723
|
+
if (!requiredVersion) return void 0;
|
|
724
|
+
return requiredVersion.match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/)?.[0];
|
|
725
|
+
}
|
|
677
726
|
function normalizeShareItem(key, shareItem) {
|
|
678
727
|
let version;
|
|
679
728
|
try {
|
|
@@ -685,11 +734,11 @@ function normalizeShareItem(key, shareItem) {
|
|
|
685
734
|
version = require(localPath).version;
|
|
686
735
|
} catch (e2) {
|
|
687
736
|
version = searchPackageVersion(key);
|
|
688
|
-
if (!version)
|
|
737
|
+
if (!version) mfError(e1);
|
|
689
738
|
}
|
|
690
739
|
}
|
|
691
740
|
} catch (e) {
|
|
692
|
-
|
|
741
|
+
mfError(`Unexpected error resolving version for ${key}:`, e);
|
|
693
742
|
}
|
|
694
743
|
if (typeof shareItem === "string") return {
|
|
695
744
|
name: shareItem,
|
|
@@ -705,7 +754,7 @@ function normalizeShareItem(key, shareItem) {
|
|
|
705
754
|
return {
|
|
706
755
|
name: key,
|
|
707
756
|
from: "",
|
|
708
|
-
version: shareItem.version || version,
|
|
757
|
+
version: shareItem.version || inferVersionFromRequiredVersion(shareItem.requiredVersion) || version,
|
|
709
758
|
scope: shareItem.shareScope || "default",
|
|
710
759
|
shareConfig: {
|
|
711
760
|
import: typeof shareItem === "object" ? shareItem.import : void 0,
|
|
@@ -750,7 +799,7 @@ function getNormalizeShareItem(key) {
|
|
|
750
799
|
return options.shared[key] || options.shared[removePathFromNpmPackage(key)] || options.shared[removePathFromNpmPackage(key) + "/"];
|
|
751
800
|
}
|
|
752
801
|
function normalizeModuleFederationOptions(options) {
|
|
753
|
-
if (options.virtualModuleDir && options.virtualModuleDir.includes("/")) throw
|
|
802
|
+
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'.`);
|
|
754
803
|
return config = {
|
|
755
804
|
exposes: normalizeExposes(options.exposes),
|
|
756
805
|
filename: options.filename || "remoteEntry-[hash]",
|
|
@@ -885,7 +934,7 @@ const cacheMap = {};
|
|
|
885
934
|
*/
|
|
886
935
|
function assertModuleFound(tag, str = "") {
|
|
887
936
|
const module = VirtualModule.findModule(tag, str);
|
|
888
|
-
if (!module) throw
|
|
937
|
+
if (!module) throw createModuleFederationError(`Module Federation shared module '${str}' not found. Please ensure it's installed as a dependency in your package.json.`);
|
|
889
938
|
return module;
|
|
890
939
|
}
|
|
891
940
|
var VirtualModule = class {
|
|
@@ -972,12 +1021,12 @@ function generateExposes(options) {
|
|
|
972
1021
|
//#endregion
|
|
973
1022
|
//#region src/virtualModules/virtualRuntimeInitStatus.ts
|
|
974
1023
|
const virtualRuntimeInitStatus = new VirtualModule("runtimeInit");
|
|
975
|
-
function
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
const globalKey = ${JSON.stringify(
|
|
1024
|
+
function getRuntimeInitGlobalKey() {
|
|
1025
|
+
return `__mf_init__${virtualRuntimeInitStatus.getImportId()}__`;
|
|
1026
|
+
}
|
|
1027
|
+
function getRuntimeInitBootstrapCode() {
|
|
1028
|
+
return `
|
|
1029
|
+
const globalKey = ${JSON.stringify(getRuntimeInitGlobalKey())};
|
|
981
1030
|
if (!globalThis[globalKey]) {
|
|
982
1031
|
let initResolve, initReject;
|
|
983
1032
|
const initPromise = new Promise((re, rj) => {
|
|
@@ -989,8 +1038,6 @@ if (!globalThis[globalKey]) {
|
|
|
989
1038
|
initResolve,
|
|
990
1039
|
initReject,
|
|
991
1040
|
};
|
|
992
|
-
// In SSR (no window), resolve immediately with a stub runtime
|
|
993
|
-
// so modules don't hang waiting for browser-only init
|
|
994
1041
|
if (typeof window === 'undefined') {
|
|
995
1042
|
initResolve({
|
|
996
1043
|
loadRemote: function() { return Promise.resolve(undefined); },
|
|
@@ -998,6 +1045,64 @@ if (!globalThis[globalKey]) {
|
|
|
998
1045
|
});
|
|
999
1046
|
}
|
|
1000
1047
|
}
|
|
1048
|
+
`;
|
|
1049
|
+
}
|
|
1050
|
+
function getRuntimeInitPromiseBootstrapCode() {
|
|
1051
|
+
return `
|
|
1052
|
+
const __mfPromiseGlobalKey = ${JSON.stringify(getRuntimeInitGlobalKey())};
|
|
1053
|
+
let __mfPromiseState = globalThis[__mfPromiseGlobalKey];
|
|
1054
|
+
if (!__mfPromiseState) {
|
|
1055
|
+
let initResolve, initReject;
|
|
1056
|
+
const initPromise = new Promise((re, rj) => {
|
|
1057
|
+
initResolve = re;
|
|
1058
|
+
initReject = rj;
|
|
1059
|
+
});
|
|
1060
|
+
__mfPromiseState = globalThis[__mfPromiseGlobalKey] = {
|
|
1061
|
+
initPromise,
|
|
1062
|
+
initResolve,
|
|
1063
|
+
initReject,
|
|
1064
|
+
};
|
|
1065
|
+
if (typeof window === 'undefined') {
|
|
1066
|
+
initResolve({
|
|
1067
|
+
loadRemote: function() { return Promise.resolve(undefined); },
|
|
1068
|
+
loadShare: function() { return Promise.resolve(undefined); },
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
const initPromise = __mfPromiseState.initPromise;
|
|
1073
|
+
`;
|
|
1074
|
+
}
|
|
1075
|
+
function getRuntimeInitResolveBootstrapCode() {
|
|
1076
|
+
return `
|
|
1077
|
+
const __mfResolveGlobalKey = ${JSON.stringify(getRuntimeInitGlobalKey())};
|
|
1078
|
+
let __mfResolveState = globalThis[__mfResolveGlobalKey];
|
|
1079
|
+
if (!__mfResolveState) {
|
|
1080
|
+
let initResolve, initReject;
|
|
1081
|
+
const initPromise = new Promise((re, rj) => {
|
|
1082
|
+
initResolve = re;
|
|
1083
|
+
initReject = rj;
|
|
1084
|
+
});
|
|
1085
|
+
__mfResolveState = globalThis[__mfResolveGlobalKey] = {
|
|
1086
|
+
initPromise,
|
|
1087
|
+
initResolve,
|
|
1088
|
+
initReject,
|
|
1089
|
+
};
|
|
1090
|
+
if (typeof window === 'undefined') {
|
|
1091
|
+
initResolve({
|
|
1092
|
+
loadRemote: function() { return Promise.resolve(undefined); },
|
|
1093
|
+
loadShare: function() { return Promise.resolve(undefined); },
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
const initResolve = __mfResolveState.initResolve;
|
|
1098
|
+
`;
|
|
1099
|
+
}
|
|
1100
|
+
function writeRuntimeInitStatus(command) {
|
|
1101
|
+
getRuntimeInitGlobalKey();
|
|
1102
|
+
const exportStatement = command === "build" ? `const { initPromise, initResolve, initReject } = globalThis[globalKey];
|
|
1103
|
+
export { initPromise, initResolve, initReject };` : `module.exports = globalThis[globalKey];`;
|
|
1104
|
+
virtualRuntimeInitStatus.writeSync(`
|
|
1105
|
+
${getRuntimeInitBootstrapCode()}
|
|
1001
1106
|
${exportStatement}
|
|
1002
1107
|
`);
|
|
1003
1108
|
}
|
|
@@ -1022,7 +1127,8 @@ function getUsedRemotesMap() {
|
|
|
1022
1127
|
}
|
|
1023
1128
|
function generateRemotes(id, command, isRolldown) {
|
|
1024
1129
|
const useESM = command === "build" || isRolldown;
|
|
1025
|
-
const importLine =
|
|
1130
|
+
const importLine = command === "build" ? getRuntimeInitPromiseBootstrapCode() : useESM ? `${getRuntimeInitBootstrapCode()}
|
|
1131
|
+
const { initPromise } = globalThis[globalKey];` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
|
|
1026
1132
|
const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
|
|
1027
1133
|
const exportLine = command === "serve" && useESM ? "export default exportModule.default ?? exportModule" : useESM ? "export default exportModule" : "module.exports = exportModule";
|
|
1028
1134
|
return `
|
|
@@ -1072,14 +1178,19 @@ function getPreBuildLibImportId(pkg) {
|
|
|
1072
1178
|
}
|
|
1073
1179
|
const LOAD_SHARE_TAG = "__loadShare__";
|
|
1074
1180
|
const loadShareCacheMap = {};
|
|
1075
|
-
function
|
|
1181
|
+
function getLoadShareImportId(pkg, isRolldown, command) {
|
|
1076
1182
|
if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, isRolldown || command === "build" ? ".mjs" : ".js");
|
|
1183
|
+
return loadShareCacheMap[pkg].getImportId();
|
|
1184
|
+
}
|
|
1185
|
+
function getLoadShareModulePath(pkg, isRolldown, command) {
|
|
1186
|
+
if (!loadShareCacheMap[pkg]) getLoadShareImportId(pkg, isRolldown, command);
|
|
1077
1187
|
return loadShareCacheMap[pkg].getPath();
|
|
1078
1188
|
}
|
|
1079
1189
|
function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
|
|
1080
1190
|
if (!loadShareCacheMap[pkg]) loadShareCacheMap[pkg] = new VirtualModule(pkg, LOAD_SHARE_TAG, isRolldown || command === "build" ? ".mjs" : ".js");
|
|
1081
1191
|
const useESM = command === "build" || isRolldown;
|
|
1082
|
-
const importLine =
|
|
1192
|
+
const importLine = command === "build" ? getRuntimeInitPromiseBootstrapCode() : useESM ? `${getRuntimeInitBootstrapCode()}
|
|
1193
|
+
const { initPromise } = globalThis[globalKey];` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
|
|
1083
1194
|
const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
|
|
1084
1195
|
const useSsrProviderFallback = hasPackageDependency("vinext") && command === "build" && pkg === "react";
|
|
1085
1196
|
const providerImportId = getLocalProviderImportPath(pkg) || getPreBuildLibImportId(pkg);
|
|
@@ -1088,8 +1199,8 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
|
|
|
1088
1199
|
if (namedExports.length > 0) {
|
|
1089
1200
|
const destructure = `const { ${namedExports.map((name, i) => `${name}: __mf_${i}`).join(", ")} } = exportModule;`;
|
|
1090
1201
|
const namedExportLine = `export { ${namedExports.map((name, i) => `__mf_${i} as ${name}`).join(", ")} };`;
|
|
1091
|
-
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(", ")} });`;
|
|
1092
|
-
} else exportLine = useESM ? `export default exportModule\n export * from ${JSON.stringify(getPreBuildLibImportId(pkg))}` : "module.exports = exportModule";
|
|
1202
|
+
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(", ")} });`;
|
|
1203
|
+
} else exportLine = useESM ? `export default exportModule.default ?? exportModule\n export * from ${JSON.stringify(getPreBuildLibImportId(pkg))}` : "module.exports = exportModule";
|
|
1093
1204
|
loadShareCacheMap[pkg].writeSync(`
|
|
1094
1205
|
import ${JSON.stringify(getPreBuildLibImportId(pkg))};
|
|
1095
1206
|
${command !== "build" ? `;() => import(${JSON.stringify(pkg)}).catch(() => {});` : ""}
|
|
@@ -1123,12 +1234,12 @@ new VirtualModule("localSharedImportMap");
|
|
|
1123
1234
|
function getLocalSharedImportMapPath() {
|
|
1124
1235
|
return getLocalSharedImportMapPath_temp();
|
|
1125
1236
|
}
|
|
1126
|
-
let
|
|
1237
|
+
let prevLocalSharedImportMapContent;
|
|
1127
1238
|
function writeLocalSharedImportMap() {
|
|
1128
|
-
const
|
|
1129
|
-
if (
|
|
1130
|
-
|
|
1131
|
-
writeLocalSharedImportMap_temp(
|
|
1239
|
+
const nextContent = generateLocalSharedImportMap();
|
|
1240
|
+
if (prevLocalSharedImportMapContent !== nextContent) {
|
|
1241
|
+
prevLocalSharedImportMapContent = nextContent;
|
|
1242
|
+
writeLocalSharedImportMap_temp(nextContent);
|
|
1132
1243
|
}
|
|
1133
1244
|
}
|
|
1134
1245
|
function generateLocalSharedImportMap() {
|
|
@@ -1141,7 +1252,7 @@ function generateLocalSharedImportMap() {
|
|
|
1141
1252
|
const shareItem = getNormalizeShareItem(pkg);
|
|
1142
1253
|
return `
|
|
1143
1254
|
${JSON.stringify(pkg)}: async () => {
|
|
1144
|
-
${shareItem?.shareConfig.import === false ? `throw new Error(\`Shared module '\${${JSON.stringify(pkg)}}' must be provided by host\`);` : isVinext && pkg === "react" ? `let pkg = await import("react");
|
|
1255
|
+
${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");
|
|
1145
1256
|
return pkg;` : `let pkg = await import("${getPreBuildLibImportId(pkg)}");
|
|
1146
1257
|
return pkg;`}
|
|
1147
1258
|
}
|
|
@@ -1161,7 +1272,7 @@ function generateLocalSharedImportMap() {
|
|
|
1161
1272
|
from: ${JSON.stringify(options.name)},
|
|
1162
1273
|
async get () {
|
|
1163
1274
|
if (${shareItem.shareConfig.import === false}) {
|
|
1164
|
-
throw new Error(\`Shared module '\${${JSON.stringify(key)}}' must be provided by host\`);
|
|
1275
|
+
throw new Error(\`[Module Federation] Shared module '\${${JSON.stringify(key)}}' must be provided by host\`);
|
|
1165
1276
|
}
|
|
1166
1277
|
usedShared[${JSON.stringify(key)}].loaded = true
|
|
1167
1278
|
const {${JSON.stringify(key)}: pkgDynamicImport} = importMap
|
|
@@ -1211,7 +1322,7 @@ const REMOTE_ENTRY_ID = "virtual:mf-REMOTE_ENTRY_ID";
|
|
|
1211
1322
|
function getRemoteEntryId(options) {
|
|
1212
1323
|
return `${REMOTE_ENTRY_ID}:${`${options.name}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
|
|
1213
1324
|
}
|
|
1214
|
-
function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(options)) {
|
|
1325
|
+
function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(options), command = "build") {
|
|
1215
1326
|
const pluginImportNames = options.runtimePlugins.map((p, i) => {
|
|
1216
1327
|
if (typeof p === "string") return [
|
|
1217
1328
|
`$runtimePlugin_${i}`,
|
|
@@ -1227,15 +1338,25 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
|
|
|
1227
1338
|
return `
|
|
1228
1339
|
import {init as runtimeInit, loadRemote} from "@module-federation/runtime";
|
|
1229
1340
|
${pluginImportNames.map((item) => item[1]).join("\n")}
|
|
1230
|
-
|
|
1231
|
-
import {usedShared, usedRemotes} from "${getLocalSharedImportMapPath()}"
|
|
1232
|
-
import {
|
|
1233
|
-
initResolve
|
|
1234
|
-
} from "${virtualRuntimeInitStatus.getImportId()}"
|
|
1341
|
+
${command === "build" ? getRuntimeInitResolveBootstrapCode() : getRuntimeInitBootstrapCode() + "\n const { initResolve } = globalThis[globalKey];"}
|
|
1235
1342
|
const initTokens = {}
|
|
1236
1343
|
const shareScopeName = ${JSON.stringify(options.shareScope)}
|
|
1237
1344
|
const mfName = ${JSON.stringify(options.name)}
|
|
1345
|
+
let localSharedImportMapPromise
|
|
1346
|
+
let exposesMapPromise
|
|
1347
|
+
|
|
1348
|
+
async function getLocalSharedImportMap() {
|
|
1349
|
+
localSharedImportMapPromise ??= import("${getLocalSharedImportMapPath()}")
|
|
1350
|
+
return localSharedImportMapPromise
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
async function getExposesMap() {
|
|
1354
|
+
exposesMapPromise ??= import("${virtualExposesId}").then((mod) => mod.default ?? mod)
|
|
1355
|
+
return exposesMapPromise
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1238
1358
|
async function init(shared = {}, initScope = []) {
|
|
1359
|
+
const {usedShared, usedRemotes} = await getLocalSharedImportMap()
|
|
1239
1360
|
const initRes = runtimeInit({
|
|
1240
1361
|
name: mfName,
|
|
1241
1362
|
remotes: usedRemotes,
|
|
@@ -1258,13 +1379,14 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
|
|
|
1258
1379
|
initScope
|
|
1259
1380
|
}));
|
|
1260
1381
|
} catch (e) {
|
|
1261
|
-
console.error(e)
|
|
1382
|
+
console.error('[Module Federation]', e)
|
|
1262
1383
|
}
|
|
1263
1384
|
return initRes
|
|
1264
1385
|
}
|
|
1265
1386
|
|
|
1266
|
-
function getExposes(moduleName) {
|
|
1267
|
-
|
|
1387
|
+
async function getExposes(moduleName) {
|
|
1388
|
+
const exposesMap = await getExposesMap()
|
|
1389
|
+
if (!(moduleName in exposesMap)) throw new Error(\`[Module Federation] Module \${moduleName} does not exist in container.\`)
|
|
1268
1390
|
return (exposesMap[moduleName])().then(res => () => res)
|
|
1269
1391
|
}
|
|
1270
1392
|
export {
|
|
@@ -1276,13 +1398,8 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
|
|
|
1276
1398
|
const hostAutoInitModule = new VirtualModule("hostAutoInit", "__H_A_I__");
|
|
1277
1399
|
function writeHostAutoInit(remoteEntryId = REMOTE_ENTRY_ID) {
|
|
1278
1400
|
hostAutoInitModule.writeSync(`
|
|
1279
|
-
const
|
|
1280
|
-
|
|
1281
|
-
.then(remoteEntry => {
|
|
1282
|
-
return Promise.resolve(remoteEntry.__tla)
|
|
1283
|
-
.then(remoteEntry.init)
|
|
1284
|
-
.catch(remoteEntry.init)
|
|
1285
|
-
})
|
|
1401
|
+
const remoteEntry = await import("${remoteEntryId}");
|
|
1402
|
+
await remoteEntry.init();
|
|
1286
1403
|
`);
|
|
1287
1404
|
}
|
|
1288
1405
|
function getHostAutoInitImportId() {
|
|
@@ -1305,13 +1422,21 @@ function initVirtualModules(command, remoteEntryId) {
|
|
|
1305
1422
|
* If Rollup's deconflict renamed the alias but didn't update references
|
|
1306
1423
|
* in the code body, fall back to proxyLocal so they stay in sync.
|
|
1307
1424
|
*/
|
|
1308
|
-
function resolveProxyAlias(binding, proxyLocal, code, fullImport) {
|
|
1425
|
+
function resolveProxyAlias(binding, proxyLocal, code, fullImport, claimedLocals = /* @__PURE__ */ new Set()) {
|
|
1309
1426
|
const codeWithoutImport = code.replace(fullImport, "");
|
|
1310
1427
|
const escapedLocal = binding.local.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1311
1428
|
const localUsedInCode = new RegExp(`\\b${escapedLocal}\\b`).test(codeWithoutImport);
|
|
1429
|
+
const claimedImportLocals = /* @__PURE__ */ new Set();
|
|
1430
|
+
const importRe = /import\s*\{([^}]+)\}\s*from\s*["'][^"']+["']\s*;?/g;
|
|
1431
|
+
let match;
|
|
1432
|
+
while ((match = importRe.exec(codeWithoutImport)) !== null) for (const spec of match[1].split(",")) {
|
|
1433
|
+
const parts = spec.trim().split(/\s+as\s+/);
|
|
1434
|
+
claimedImportLocals.add((parts[1] || parts[0]).trim());
|
|
1435
|
+
}
|
|
1436
|
+
const local = !localUsedInCode && !claimedLocals.has(proxyLocal) && !claimedImportLocals.has(proxyLocal) ? proxyLocal : binding.local;
|
|
1312
1437
|
return {
|
|
1313
1438
|
imported: binding.imported,
|
|
1314
|
-
local
|
|
1439
|
+
local
|
|
1315
1440
|
};
|
|
1316
1441
|
}
|
|
1317
1442
|
function findRemoteEntryFile(filename, bundle) {
|
|
@@ -1678,14 +1803,14 @@ const promise = new Promise((resolve, reject) => {
|
|
|
1678
1803
|
});
|
|
1679
1804
|
function setParseTimeout(timeout) {
|
|
1680
1805
|
if (!_parseTimeout) _parseTimeout = setTimeout(() => {
|
|
1681
|
-
|
|
1806
|
+
mfWarn(`Parse timeout (${timeout}s) - forcing resolve`);
|
|
1682
1807
|
_resolve(1);
|
|
1683
1808
|
}, timeout * 1e3);
|
|
1684
1809
|
}
|
|
1685
1810
|
function resetIdleTimeout(timeout) {
|
|
1686
1811
|
clearTimeout(_parseTimeout);
|
|
1687
1812
|
_parseTimeout = setTimeout(() => {
|
|
1688
|
-
|
|
1813
|
+
mfWarn(`moduleParseIdleTimeout: no module activity for ${timeout}s, forcing resolve. Some shared/remote dependencies may be missing. Consider increasing moduleParseIdleTimeout.`);
|
|
1689
1814
|
_resolve(1);
|
|
1690
1815
|
}, timeout * 1e3);
|
|
1691
1816
|
}
|
|
@@ -1766,14 +1891,14 @@ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposes
|
|
|
1766
1891
|
}
|
|
1767
1892
|
},
|
|
1768
1893
|
load(id) {
|
|
1769
|
-
if (id === remoteEntryId) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId));
|
|
1894
|
+
if (id === remoteEntryId) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId, _command));
|
|
1770
1895
|
if (id === virtualExposesId) return generateExposes(options);
|
|
1771
1896
|
if (_command === "serve" && id.includes(getHostAutoInitPath())) return id;
|
|
1772
1897
|
},
|
|
1773
1898
|
transform(code, id) {
|
|
1774
1899
|
return mapCodeToCodeWithSourcemap((() => {
|
|
1775
1900
|
if (!filter(id)) return;
|
|
1776
|
-
if (id.includes(remoteEntryId)) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId));
|
|
1901
|
+
if (id.includes(remoteEntryId)) return parsePromise.then((_) => generateRemoteEntry(options, virtualExposesId, _command));
|
|
1777
1902
|
if (id === virtualExposesId) return generateExposes(options);
|
|
1778
1903
|
if (id.includes(getHostAutoInitPath())) {
|
|
1779
1904
|
if (_command === "serve") {
|
|
@@ -1806,7 +1931,7 @@ function pluginProxyRemotes_default(options) {
|
|
|
1806
1931
|
return {
|
|
1807
1932
|
name: "proxyRemotes",
|
|
1808
1933
|
config(config, { command: _command }) {
|
|
1809
|
-
const isRolldown =
|
|
1934
|
+
const isRolldown = getIsRolldown(this);
|
|
1810
1935
|
Object.keys(remotes).forEach((key) => {
|
|
1811
1936
|
const remote = remotes[key];
|
|
1812
1937
|
config.resolve.alias.push({
|
|
@@ -1878,7 +2003,7 @@ function proxySharedModule(options) {
|
|
|
1878
2003
|
config(config, { command }) {
|
|
1879
2004
|
setPackageDetectionCwd(config.root || process.cwd());
|
|
1880
2005
|
isVinext = hasPackageDependency("vinext");
|
|
1881
|
-
const isRolldown =
|
|
2006
|
+
const isRolldown = getIsRolldown(this);
|
|
1882
2007
|
_command = command;
|
|
1883
2008
|
config.resolve.alias.push(...Object.keys(shared).filter((key) => !(isVinext && key === "react")).map((key) => {
|
|
1884
2009
|
const keyBase = key.endsWith("/") ? key.slice(0, -1) : key;
|
|
@@ -1958,7 +2083,6 @@ const VarRemoteEntry = () => {
|
|
|
1958
2083
|
if (req.url?.replace(/\?.*/, "") === (viteConfig.base + varFilename).replace(/^\/?/, "/")) {
|
|
1959
2084
|
res.setHeader("Content-Type", "text/javascript");
|
|
1960
2085
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1961
|
-
console.log({ filename });
|
|
1962
2086
|
res.end(generateVarRemoteEntry(filename));
|
|
1963
2087
|
} else next();
|
|
1964
2088
|
});
|
|
@@ -1974,9 +2098,9 @@ const VarRemoteEntry = () => {
|
|
|
1974
2098
|
},
|
|
1975
2099
|
async generateBundle(options, bundle) {
|
|
1976
2100
|
if (!varFilename) return;
|
|
1977
|
-
if (!isValidVarName(name))
|
|
2101
|
+
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).`);
|
|
1978
2102
|
const remoteEntryFile = findRemoteEntryFile(mfOptions.filename, bundle);
|
|
1979
|
-
if (!remoteEntryFile) throw
|
|
2103
|
+
if (!remoteEntryFile) throw createModuleFederationError(`Couldn't find a remoteEntry chunk file for ${mfOptions.filename}, can't generate varRemoteEntry file`);
|
|
1980
2104
|
this.emitFile({
|
|
1981
2105
|
type: "asset",
|
|
1982
2106
|
fileName: varFilename,
|
|
@@ -2001,7 +2125,7 @@ const VarRemoteEntry = () => {
|
|
|
2001
2125
|
function getScriptUrl() {
|
|
2002
2126
|
const currentScript = document.currentScript;
|
|
2003
2127
|
if (!currentScript) {
|
|
2004
|
-
console.error("[
|
|
2128
|
+
console.error("[Module Federation] ${varFilename} script should be called from sync <script> tag (document.currentScript is undefined)")
|
|
2005
2129
|
return '/';
|
|
2006
2130
|
}
|
|
2007
2131
|
return document.currentScript.src.replace(/\\/[^/]*$/, '/');
|
|
@@ -2032,6 +2156,58 @@ var aliasToArrayPlugin_default = {
|
|
|
2032
2156
|
}
|
|
2033
2157
|
};
|
|
2034
2158
|
//#endregion
|
|
2159
|
+
//#region src/utils/controlChunkSanitizer.ts
|
|
2160
|
+
const FEDERATION_CONTROL_CHUNK_HINTS = [
|
|
2161
|
+
"hostInit",
|
|
2162
|
+
"virtualExposes",
|
|
2163
|
+
"localSharedImportMap"
|
|
2164
|
+
];
|
|
2165
|
+
function stripEmptyPreloadCalls(code) {
|
|
2166
|
+
const helperImportRegex = /import\s*\{\s*_\s*as\s*(\w+)\s*\}\s*from\s*["'][^"']+["']\s*;?/g;
|
|
2167
|
+
const helperAliases = [...code.matchAll(helperImportRegex)].map((match) => match[1]);
|
|
2168
|
+
let nextCode = code;
|
|
2169
|
+
for (const alias of helperAliases) {
|
|
2170
|
+
const marker = `${alias}(()=>`;
|
|
2171
|
+
let start = nextCode.indexOf(marker);
|
|
2172
|
+
while (start !== -1) {
|
|
2173
|
+
const exprStart = start + marker.length;
|
|
2174
|
+
let depth = 0;
|
|
2175
|
+
let cursor = exprStart;
|
|
2176
|
+
let replacementEnd = -1;
|
|
2177
|
+
while (cursor < nextCode.length) {
|
|
2178
|
+
const char = nextCode[cursor];
|
|
2179
|
+
if (char === "(") depth++;
|
|
2180
|
+
else if (char === ")") depth--;
|
|
2181
|
+
else if (depth === 0 && nextCode.startsWith(",[],import.meta.url)", cursor)) {
|
|
2182
|
+
replacementEnd = cursor;
|
|
2183
|
+
break;
|
|
2184
|
+
}
|
|
2185
|
+
cursor++;
|
|
2186
|
+
}
|
|
2187
|
+
if (replacementEnd === -1) break;
|
|
2188
|
+
const expression = nextCode.slice(exprStart, replacementEnd);
|
|
2189
|
+
nextCode = nextCode.slice(0, start) + expression + nextCode.slice(replacementEnd + 20);
|
|
2190
|
+
start = nextCode.indexOf(marker, start + expression.length);
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
nextCode = nextCode.replace(/import\s*["'][^"']*__loadShare__[^"']*["']\s*;?/g, "");
|
|
2194
|
+
nextCode = nextCode.replace(helperImportRegex, (statement, local) => {
|
|
2195
|
+
return new RegExp(`\\b${local}\\s*\\(`).test(nextCode.replace(statement, "")) ? statement : "";
|
|
2196
|
+
});
|
|
2197
|
+
return nextCode;
|
|
2198
|
+
}
|
|
2199
|
+
function isFederationControlChunk(fileName, filename) {
|
|
2200
|
+
return fileName.includes(filename) || FEDERATION_CONTROL_CHUNK_HINTS.some((hint) => fileName.includes(hint));
|
|
2201
|
+
}
|
|
2202
|
+
function sanitizeFederationControlChunk(code, fileName, filename) {
|
|
2203
|
+
let nextCode = stripEmptyPreloadCalls(code);
|
|
2204
|
+
if (fileName.includes("localSharedImportMap")) {
|
|
2205
|
+
const remoteEntryImportRegex = new RegExp(`import\\s*["'][^"']*${filename.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["']\\s*;?`, "g");
|
|
2206
|
+
nextCode = nextCode.replace(remoteEntryImportRegex, "");
|
|
2207
|
+
}
|
|
2208
|
+
return nextCode;
|
|
2209
|
+
}
|
|
2210
|
+
//#endregion
|
|
2035
2211
|
//#region src/utils/normalizeOptimizeDeps.ts
|
|
2036
2212
|
var normalizeOptimizeDeps_default = {
|
|
2037
2213
|
name: "normalizeOptimizeDeps",
|
|
@@ -2048,6 +2224,25 @@ var normalizeOptimizeDeps_default = {
|
|
|
2048
2224
|
};
|
|
2049
2225
|
//#endregion
|
|
2050
2226
|
//#region src/index.ts
|
|
2227
|
+
const UNSAFE_JS_SOURCE_CHAR_MAP = {
|
|
2228
|
+
"<": "\\u003C",
|
|
2229
|
+
">": "\\u003E",
|
|
2230
|
+
"/": "\\u002F",
|
|
2231
|
+
"\\": "\\\\",
|
|
2232
|
+
"\b": "\\b",
|
|
2233
|
+
"\f": "\\f",
|
|
2234
|
+
"\n": "\\n",
|
|
2235
|
+
"\r": "\\r",
|
|
2236
|
+
" ": "\\t",
|
|
2237
|
+
"\0": "\\0",
|
|
2238
|
+
"\u2028": "\\u2028",
|
|
2239
|
+
"\u2029": "\\u2029"
|
|
2240
|
+
};
|
|
2241
|
+
function escapeUnsafeJsSourceChars(str) {
|
|
2242
|
+
return str.replace(/[<>/\\\b\f\n\r\t\0\u2028\u2029]/g, (char) => {
|
|
2243
|
+
return UNSAFE_JS_SOURCE_CHAR_MAP[char] ?? char;
|
|
2244
|
+
});
|
|
2245
|
+
}
|
|
2051
2246
|
/**
|
|
2052
2247
|
* Plugin that runs FIRST to create virtual module files in the config hook.
|
|
2053
2248
|
* This prevents 504 "Outdated Optimize Dep" errors by ensuring files exist
|
|
@@ -2067,7 +2262,7 @@ function createEarlyVirtualModulesPlugin(options) {
|
|
|
2067
2262
|
VirtualModule.ensureVirtualPackageExists();
|
|
2068
2263
|
initVirtualModules(_command, getRemoteEntryId(options));
|
|
2069
2264
|
if (_command !== "serve") return;
|
|
2070
|
-
const isRolldown =
|
|
2265
|
+
const isRolldown = getIsRolldown(this);
|
|
2071
2266
|
if (remotes && Object.keys(remotes).length > 0) for (const key of Object.keys(remotes)) addUsedRemote(key, key);
|
|
2072
2267
|
if (shared && Object.keys(shared).length > 0) {
|
|
2073
2268
|
config.optimizeDeps = config.optimizeDeps || {};
|
|
@@ -2084,6 +2279,7 @@ function createEarlyVirtualModulesPlugin(options) {
|
|
|
2084
2279
|
writeLoadShareModule(key, shareItem, _command, isRolldown);
|
|
2085
2280
|
writePreBuildLibPath(key);
|
|
2086
2281
|
addUsedShares(key);
|
|
2282
|
+
config.optimizeDeps.include.push(getLoadShareImportId(key, isRolldown, _command));
|
|
2087
2283
|
config.optimizeDeps.include.push(getPreBuildLibImportId(key));
|
|
2088
2284
|
}
|
|
2089
2285
|
writeLocalSharedImportMap();
|
|
@@ -2095,7 +2291,7 @@ function federation(mfUserOptions) {
|
|
|
2095
2291
|
const options = normalizeModuleFederationOptions(mfUserOptions);
|
|
2096
2292
|
const isVinext = hasPackageDependency("vinext");
|
|
2097
2293
|
const { name, remotes, shared, filename, hostInitInjectLocation } = options;
|
|
2098
|
-
if (!name) throw
|
|
2294
|
+
if (!name) throw createModuleFederationError("name is required");
|
|
2099
2295
|
const remoteEntryId = getRemoteEntryId(options);
|
|
2100
2296
|
const virtualExposesId = getVirtualExposesId(options);
|
|
2101
2297
|
let command;
|
|
@@ -2169,9 +2365,28 @@ function federation(mfUserOptions) {
|
|
|
2169
2365
|
config(config) {
|
|
2170
2366
|
const runtimeInitId = virtualRuntimeInitStatus.getImportId();
|
|
2171
2367
|
config.build = config.build || {};
|
|
2172
|
-
config.build.
|
|
2173
|
-
|
|
2174
|
-
const
|
|
2368
|
+
if (config.build.modulePreload !== false) {
|
|
2369
|
+
const currentModulePreload = config.build.modulePreload && typeof config.build.modulePreload === "object" ? config.build.modulePreload : {};
|
|
2370
|
+
const existingResolveDependencies = currentModulePreload.resolveDependencies;
|
|
2371
|
+
config.build.modulePreload = {
|
|
2372
|
+
...currentModulePreload,
|
|
2373
|
+
resolveDependencies(filename, deps, context) {
|
|
2374
|
+
const resolvedDeps = existingResolveDependencies ? existingResolveDependencies(filename, deps, context) : deps;
|
|
2375
|
+
const hostFile = pathe.default.basename(context.hostId);
|
|
2376
|
+
return context.hostType === "js" && (hostFile === options.filename || hostFile.includes("hostInit") || hostFile.includes("virtualExposes") || hostFile.includes("localSharedImportMap")) ? [] : resolvedDeps;
|
|
2377
|
+
}
|
|
2378
|
+
};
|
|
2379
|
+
}
|
|
2380
|
+
let warnedAboutCodeSplitting = false;
|
|
2381
|
+
const ensureCodeSplitting = (output) => {
|
|
2382
|
+
if (output?.codeSplitting !== false) return;
|
|
2383
|
+
delete output.codeSplitting;
|
|
2384
|
+
if (warnedAboutCodeSplitting) return;
|
|
2385
|
+
warnedAboutCodeSplitting = true;
|
|
2386
|
+
mfWarn("Ignoring `build.rolldownOptions.output.codeSplitting = false` because module federation requires chunk splitting.");
|
|
2387
|
+
};
|
|
2388
|
+
const applyManualChunks = (output) => {
|
|
2389
|
+
ensureCodeSplitting(output);
|
|
2175
2390
|
const existingManualChunks = output.manualChunks;
|
|
2176
2391
|
output.manualChunks = function(id) {
|
|
2177
2392
|
if (id.includes(runtimeInitId)) return "runtimeInit";
|
|
@@ -2184,7 +2399,12 @@ function federation(mfUserOptions) {
|
|
|
2184
2399
|
for (const [key, ids] of Object.entries(existingManualChunks)) if (Array.isArray(ids) && ids.some((v) => id.includes(v))) return key;
|
|
2185
2400
|
}
|
|
2186
2401
|
};
|
|
2187
|
-
}
|
|
2402
|
+
};
|
|
2403
|
+
config.build.rollupOptions = config.build.rollupOptions || {};
|
|
2404
|
+
if (!Array.isArray(config.build.rollupOptions.output)) applyManualChunks(config.build.rollupOptions.output ||= {});
|
|
2405
|
+
const buildWithRolldown = config.build;
|
|
2406
|
+
buildWithRolldown.rolldownOptions = buildWithRolldown.rolldownOptions || {};
|
|
2407
|
+
if (!Array.isArray(buildWithRolldown.rolldownOptions.output)) applyManualChunks(buildWithRolldown.rolldownOptions.output ||= {});
|
|
2188
2408
|
},
|
|
2189
2409
|
load(id) {
|
|
2190
2410
|
if (id.startsWith("\0")) return;
|
|
@@ -2216,6 +2436,11 @@ function federation(mfUserOptions) {
|
|
|
2216
2436
|
}
|
|
2217
2437
|
},
|
|
2218
2438
|
generateBundle(_, bundle) {
|
|
2439
|
+
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
2440
|
+
if (chunk.type !== "chunk") continue;
|
|
2441
|
+
if (!isFederationControlChunk(fileName, filename)) continue;
|
|
2442
|
+
chunk.code = sanitizeFederationControlChunk(chunk.code, fileName, filename);
|
|
2443
|
+
}
|
|
2219
2444
|
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
2220
2445
|
if (chunk.type !== "chunk") continue;
|
|
2221
2446
|
if (fileName.includes("__loadShare__")) continue;
|
|
@@ -2253,12 +2478,12 @@ function federation(mfUserOptions) {
|
|
|
2253
2478
|
fileName
|
|
2254
2479
|
});
|
|
2255
2480
|
}
|
|
2256
|
-
if (proxyChunks.size
|
|
2257
|
-
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
2481
|
+
if (proxyChunks.size > 0) for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
2258
2482
|
if (chunk.type !== "chunk") continue;
|
|
2259
2483
|
if (fileName.includes("__loadShare__")) continue;
|
|
2260
2484
|
let code = chunk.code;
|
|
2261
2485
|
let modified = false;
|
|
2486
|
+
const claimedLocals = /* @__PURE__ */ new Set();
|
|
2262
2487
|
for (const [proxyFileName, proxyInfo] of proxyChunks) {
|
|
2263
2488
|
const proxyBaseName = proxyFileName.replace(/^.*\//, "").replace(/\.js$/, "").replace(/-[A-Za-z0-9_-]+$/, "");
|
|
2264
2489
|
const importMatch = new RegExp(`import\\s*\\{([^}]+)\\}\\s*from\\s*["']([^"']*${proxyBaseName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[^"']*)["']\\s*;?`).exec(code);
|
|
@@ -2281,9 +2506,12 @@ function federation(mfUserOptions) {
|
|
|
2281
2506
|
}
|
|
2282
2507
|
const inlineable = [];
|
|
2283
2508
|
const nonInlineable = [];
|
|
2509
|
+
const pendingLocals = new Set(bindings.map((binding) => binding.local));
|
|
2284
2510
|
for (const b of bindings) {
|
|
2511
|
+
pendingLocals.delete(b.local);
|
|
2285
2512
|
const proxyLocal = exportMap[b.imported];
|
|
2286
2513
|
if (!proxyLocal) {
|
|
2514
|
+
claimedLocals.add(b.local);
|
|
2287
2515
|
nonInlineable.push(b);
|
|
2288
2516
|
continue;
|
|
2289
2517
|
}
|
|
@@ -2305,7 +2533,14 @@ function federation(mfUserOptions) {
|
|
|
2305
2533
|
local: b.local,
|
|
2306
2534
|
funcBody: renamedFunc
|
|
2307
2535
|
});
|
|
2308
|
-
|
|
2536
|
+
claimedLocals.add(b.local);
|
|
2537
|
+
} else {
|
|
2538
|
+
const unavailableLocals = new Set(claimedLocals);
|
|
2539
|
+
pendingLocals.forEach((local) => unavailableLocals.add(local));
|
|
2540
|
+
const resolvedBinding = resolveProxyAlias(b, proxyLocal, code, fullImport, unavailableLocals);
|
|
2541
|
+
claimedLocals.add(resolvedBinding.local);
|
|
2542
|
+
nonInlineable.push(resolvedBinding);
|
|
2543
|
+
}
|
|
2309
2544
|
}
|
|
2310
2545
|
const hasRenamedAlias = nonInlineable.some((b) => bindings.find((ob) => ob.imported === b.imported)?.local !== b.local);
|
|
2311
2546
|
if (inlineable.length === 0 && !hasRenamedAlias) continue;
|
|
@@ -2319,6 +2554,28 @@ function federation(mfUserOptions) {
|
|
|
2319
2554
|
}
|
|
2320
2555
|
}
|
|
2321
2556
|
},
|
|
2557
|
+
{
|
|
2558
|
+
name: "module-federation-strip-empty-preload-helper",
|
|
2559
|
+
enforce: "post",
|
|
2560
|
+
apply: "build",
|
|
2561
|
+
renderChunk(code, chunk) {
|
|
2562
|
+
if (!isFederationControlChunk(chunk.fileName, filename)) return;
|
|
2563
|
+
const nextCode = sanitizeFederationControlChunk(code, chunk.fileName, filename);
|
|
2564
|
+
return nextCode === code ? null : {
|
|
2565
|
+
code: nextCode,
|
|
2566
|
+
map: null
|
|
2567
|
+
};
|
|
2568
|
+
},
|
|
2569
|
+
writeBundle(outputOptions, bundle) {
|
|
2570
|
+
if (!outputOptions.dir) return;
|
|
2571
|
+
for (const chunk of Object.values(bundle)) {
|
|
2572
|
+
if (chunk.type !== "chunk") continue;
|
|
2573
|
+
if (!isFederationControlChunk(chunk.fileName, filename)) continue;
|
|
2574
|
+
const outputPath = pathe.default.join(outputOptions.dir, chunk.fileName);
|
|
2575
|
+
(0, fs.writeFileSync)(outputPath, sanitizeFederationControlChunk((0, fs.readFileSync)(outputPath, "utf-8"), chunk.fileName, filename));
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
},
|
|
2322
2579
|
{
|
|
2323
2580
|
name: "module-federation-dev-await-shared-init",
|
|
2324
2581
|
apply: "serve",
|
|
@@ -2351,9 +2608,9 @@ function federation(mfUserOptions) {
|
|
|
2351
2608
|
enforce: "post",
|
|
2352
2609
|
_options: options,
|
|
2353
2610
|
config(config, { command: _command }) {
|
|
2354
|
-
const isRolldown =
|
|
2611
|
+
const isRolldown = getIsRolldown(this);
|
|
2355
2612
|
let implementation = options.implementation;
|
|
2356
|
-
if (isRolldown) implementation = implementation.replace(/\.cjs\.
|
|
2613
|
+
if (isRolldown) implementation = implementation.replace(/\.cjs(\.js)?$/, ".js");
|
|
2357
2614
|
config.resolve.alias.push({
|
|
2358
2615
|
find: "@module-federation/runtime",
|
|
2359
2616
|
replacement: implementation
|
|
@@ -2380,7 +2637,7 @@ function federation(mfUserOptions) {
|
|
|
2380
2637
|
const resolvedTarget = options.target ?? (config.build?.ssr ? "node" : "web");
|
|
2381
2638
|
if (!config.define) config.define = {};
|
|
2382
2639
|
if (!("ENV_TARGET" in config.define)) config.define["ENV_TARGET"] = JSON.stringify(resolvedTarget);
|
|
2383
|
-
if (options.target && "ENV_TARGET" in config.define && config.define["ENV_TARGET"] !== JSON.stringify(options.target))
|
|
2640
|
+
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.`);
|
|
2384
2641
|
}
|
|
2385
2642
|
},
|
|
2386
2643
|
...Manifest(),
|
|
@@ -2393,13 +2650,18 @@ function federation(mfUserOptions) {
|
|
|
2393
2650
|
for (const chunk of Object.values(bundle)) {
|
|
2394
2651
|
if (chunk.type !== "chunk") continue;
|
|
2395
2652
|
if (!chunk.code.includes("modulepreload")) continue;
|
|
2396
|
-
const
|
|
2653
|
+
const chunkDir = pathe.default.dirname(chunk.fileName);
|
|
2654
|
+
const prefixToRoot = chunkDir === "." ? "" : `${pathe.default.relative(chunkDir, ".")}/`;
|
|
2655
|
+
const replacementExpr = prefixToRoot ? `${escapeUnsafeJsSourceChars(JSON.stringify(prefixToRoot))}+$1` : "$1";
|
|
2656
|
+
const replacement = `=function($1){return new URL(${replacementExpr},import.meta.url).href}`;
|
|
2397
2657
|
const replaced = chunk.code.replace(/=\(?(\w+)(?:,\w+)?\)?\s*=>\s*["'`][^"'`]*["'`]\s*\+\s*\1/, replacement);
|
|
2398
2658
|
if (replaced !== chunk.code) {
|
|
2399
2659
|
chunk.code = replaced;
|
|
2400
2660
|
continue;
|
|
2401
2661
|
}
|
|
2402
2662
|
chunk.code = chunk.code.replace(/=function\((\w+)(?:,\w+)?\)\{return\s*["'`][^"'`]*["'`]\s*\+\s*\1\s*\}/, replacement);
|
|
2663
|
+
chunk.code = chunk.code.replace(/=function\((\w+)(?:,\w+)?\)\{return new URL\("\.\.\/"\+\1,import\.meta\.url\)\.href\}/, replacement);
|
|
2664
|
+
chunk.code = chunk.code.replace(/new URL\("\.\.\/"\+(\w+),import\.meta\.url\)\.href/g, `new URL(${replacementExpr},import.meta.url).href`);
|
|
2403
2665
|
}
|
|
2404
2666
|
}
|
|
2405
2667
|
}] : []
|