@module-federation/vite 1.13.1 → 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 +109 -33
- package/lib/index.mjs +109 -33
- package/package.json +6 -5
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);
|
|
@@ -245,7 +284,7 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
|
|
|
245
284
|
if (typeof result === "string") return result;
|
|
246
285
|
if (result && typeof result === "object") {
|
|
247
286
|
if ("runtime" in result) {
|
|
248
|
-
|
|
287
|
+
mfWarn("renderBuiltUrl returned runtime code for HTML injection. Runtime code cannot be used in <script src=\"\">. Falling back to base path.");
|
|
249
288
|
return viteConfig.base + file;
|
|
250
289
|
}
|
|
251
290
|
if (result.relative) return file;
|
|
@@ -310,11 +349,11 @@ function checkAliasConflicts(options) {
|
|
|
310
349
|
});
|
|
311
350
|
}
|
|
312
351
|
if (conflicts.length > 0) {
|
|
313
|
-
|
|
352
|
+
mfWarn("Detected alias conflicts with shared modules:");
|
|
314
353
|
conflicts.forEach(({ sharedModule, alias, target }) => {
|
|
315
|
-
|
|
354
|
+
mfWarn(`Shared module "${sharedModule}" is aliased by "${alias}" to "${target}"`);
|
|
316
355
|
});
|
|
317
|
-
|
|
356
|
+
mfWarn("This may cause runtime errors as the shared module will bypass Module Federation's sharing mechanism.");
|
|
318
357
|
}
|
|
319
358
|
}
|
|
320
359
|
};
|
|
@@ -343,7 +382,7 @@ function PluginDevProxyModuleTopLevelAwait() {
|
|
|
343
382
|
try {
|
|
344
383
|
ast = this.parse(code, { allowReturnOutsideFunction: true });
|
|
345
384
|
} catch (e) {
|
|
346
|
-
throw
|
|
385
|
+
throw createModuleFederationError(`${id}: ${e}`);
|
|
347
386
|
}
|
|
348
387
|
const magicString = new magic_string.default(code);
|
|
349
388
|
const walk = await loadWalk();
|
|
@@ -471,7 +510,7 @@ const normalizeDevDtsOptions = (dts, context) => {
|
|
|
471
510
|
const logDtsError = (error, dtsOptions) => {
|
|
472
511
|
if (dtsOptions === false) return;
|
|
473
512
|
if (typeof dtsOptions === "object" && dtsOptions && dtsOptions.displayErrorInTerminal === false) return;
|
|
474
|
-
|
|
513
|
+
mfError(error);
|
|
475
514
|
};
|
|
476
515
|
function pluginDts(options) {
|
|
477
516
|
if (options.dts === false) return [];
|
|
@@ -499,7 +538,7 @@ function pluginDts(options) {
|
|
|
499
538
|
if (!normalizedDevOptions || !resolvedConfig) return;
|
|
500
539
|
const devOptions = normalizedDevOptions;
|
|
501
540
|
if (devOptions.disableDynamicRemoteTypeHints && devOptions.disableHotTypesReload && devOptions.disableLiveReload) return;
|
|
502
|
-
if (!options.name) throw
|
|
541
|
+
if (!options.name) throw createModuleFederationError("name is required if you want to enable dev server!");
|
|
503
542
|
const outputDir = resolveOutputDir(resolvedConfig);
|
|
504
543
|
const normalizedDtsOptions = normalizeDevDtsOptions(options.dts, resolvedConfig.root);
|
|
505
544
|
if (typeof normalizedDtsOptions !== "object") return;
|
|
@@ -695,11 +734,11 @@ function normalizeShareItem(key, shareItem) {
|
|
|
695
734
|
version = require(localPath).version;
|
|
696
735
|
} catch (e2) {
|
|
697
736
|
version = searchPackageVersion(key);
|
|
698
|
-
if (!version)
|
|
737
|
+
if (!version) mfError(e1);
|
|
699
738
|
}
|
|
700
739
|
}
|
|
701
740
|
} catch (e) {
|
|
702
|
-
|
|
741
|
+
mfError(`Unexpected error resolving version for ${key}:`, e);
|
|
703
742
|
}
|
|
704
743
|
if (typeof shareItem === "string") return {
|
|
705
744
|
name: shareItem,
|
|
@@ -760,7 +799,7 @@ function getNormalizeShareItem(key) {
|
|
|
760
799
|
return options.shared[key] || options.shared[removePathFromNpmPackage(key)] || options.shared[removePathFromNpmPackage(key) + "/"];
|
|
761
800
|
}
|
|
762
801
|
function normalizeModuleFederationOptions(options) {
|
|
763
|
-
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'.`);
|
|
764
803
|
return config = {
|
|
765
804
|
exposes: normalizeExposes(options.exposes),
|
|
766
805
|
filename: options.filename || "remoteEntry-[hash]",
|
|
@@ -895,7 +934,7 @@ const cacheMap = {};
|
|
|
895
934
|
*/
|
|
896
935
|
function assertModuleFound(tag, str = "") {
|
|
897
936
|
const module = VirtualModule.findModule(tag, str);
|
|
898
|
-
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.`);
|
|
899
938
|
return module;
|
|
900
939
|
}
|
|
901
940
|
var VirtualModule = class {
|
|
@@ -1139,8 +1178,12 @@ function getPreBuildLibImportId(pkg) {
|
|
|
1139
1178
|
}
|
|
1140
1179
|
const LOAD_SHARE_TAG = "__loadShare__";
|
|
1141
1180
|
const loadShareCacheMap = {};
|
|
1142
|
-
function
|
|
1181
|
+
function getLoadShareImportId(pkg, isRolldown, command) {
|
|
1143
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);
|
|
1144
1187
|
return loadShareCacheMap[pkg].getPath();
|
|
1145
1188
|
}
|
|
1146
1189
|
function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
|
|
@@ -1156,8 +1199,8 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
|
|
|
1156
1199
|
if (namedExports.length > 0) {
|
|
1157
1200
|
const destructure = `const { ${namedExports.map((name, i) => `${name}: __mf_${i}`).join(", ")} } = exportModule;`;
|
|
1158
1201
|
const namedExportLine = `export { ${namedExports.map((name, i) => `__mf_${i} as ${name}`).join(", ")} };`;
|
|
1159
|
-
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(", ")} });`;
|
|
1160
|
-
} 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";
|
|
1161
1204
|
loadShareCacheMap[pkg].writeSync(`
|
|
1162
1205
|
import ${JSON.stringify(getPreBuildLibImportId(pkg))};
|
|
1163
1206
|
${command !== "build" ? `;() => import(${JSON.stringify(pkg)}).catch(() => {});` : ""}
|
|
@@ -1209,7 +1252,7 @@ function generateLocalSharedImportMap() {
|
|
|
1209
1252
|
const shareItem = getNormalizeShareItem(pkg);
|
|
1210
1253
|
return `
|
|
1211
1254
|
${JSON.stringify(pkg)}: async () => {
|
|
1212
|
-
${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");
|
|
1213
1256
|
return pkg;` : `let pkg = await import("${getPreBuildLibImportId(pkg)}");
|
|
1214
1257
|
return pkg;`}
|
|
1215
1258
|
}
|
|
@@ -1229,7 +1272,7 @@ function generateLocalSharedImportMap() {
|
|
|
1229
1272
|
from: ${JSON.stringify(options.name)},
|
|
1230
1273
|
async get () {
|
|
1231
1274
|
if (${shareItem.shareConfig.import === false}) {
|
|
1232
|
-
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\`);
|
|
1233
1276
|
}
|
|
1234
1277
|
usedShared[${JSON.stringify(key)}].loaded = true
|
|
1235
1278
|
const {${JSON.stringify(key)}: pkgDynamicImport} = importMap
|
|
@@ -1336,14 +1379,14 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
|
|
|
1336
1379
|
initScope
|
|
1337
1380
|
}));
|
|
1338
1381
|
} catch (e) {
|
|
1339
|
-
console.error(e)
|
|
1382
|
+
console.error('[Module Federation]', e)
|
|
1340
1383
|
}
|
|
1341
1384
|
return initRes
|
|
1342
1385
|
}
|
|
1343
1386
|
|
|
1344
1387
|
async function getExposes(moduleName) {
|
|
1345
1388
|
const exposesMap = await getExposesMap()
|
|
1346
|
-
if (!(moduleName in exposesMap)) throw new Error(\`Module \${moduleName} does not exist in container.\`)
|
|
1389
|
+
if (!(moduleName in exposesMap)) throw new Error(\`[Module Federation] Module \${moduleName} does not exist in container.\`)
|
|
1347
1390
|
return (exposesMap[moduleName])().then(res => () => res)
|
|
1348
1391
|
}
|
|
1349
1392
|
export {
|
|
@@ -1760,14 +1803,14 @@ const promise = new Promise((resolve, reject) => {
|
|
|
1760
1803
|
});
|
|
1761
1804
|
function setParseTimeout(timeout) {
|
|
1762
1805
|
if (!_parseTimeout) _parseTimeout = setTimeout(() => {
|
|
1763
|
-
|
|
1806
|
+
mfWarn(`Parse timeout (${timeout}s) - forcing resolve`);
|
|
1764
1807
|
_resolve(1);
|
|
1765
1808
|
}, timeout * 1e3);
|
|
1766
1809
|
}
|
|
1767
1810
|
function resetIdleTimeout(timeout) {
|
|
1768
1811
|
clearTimeout(_parseTimeout);
|
|
1769
1812
|
_parseTimeout = setTimeout(() => {
|
|
1770
|
-
|
|
1813
|
+
mfWarn(`moduleParseIdleTimeout: no module activity for ${timeout}s, forcing resolve. Some shared/remote dependencies may be missing. Consider increasing moduleParseIdleTimeout.`);
|
|
1771
1814
|
_resolve(1);
|
|
1772
1815
|
}, timeout * 1e3);
|
|
1773
1816
|
}
|
|
@@ -1888,7 +1931,7 @@ function pluginProxyRemotes_default(options) {
|
|
|
1888
1931
|
return {
|
|
1889
1932
|
name: "proxyRemotes",
|
|
1890
1933
|
config(config, { command: _command }) {
|
|
1891
|
-
const isRolldown =
|
|
1934
|
+
const isRolldown = getIsRolldown(this);
|
|
1892
1935
|
Object.keys(remotes).forEach((key) => {
|
|
1893
1936
|
const remote = remotes[key];
|
|
1894
1937
|
config.resolve.alias.push({
|
|
@@ -1960,7 +2003,7 @@ function proxySharedModule(options) {
|
|
|
1960
2003
|
config(config, { command }) {
|
|
1961
2004
|
setPackageDetectionCwd(config.root || process.cwd());
|
|
1962
2005
|
isVinext = hasPackageDependency("vinext");
|
|
1963
|
-
const isRolldown =
|
|
2006
|
+
const isRolldown = getIsRolldown(this);
|
|
1964
2007
|
_command = command;
|
|
1965
2008
|
config.resolve.alias.push(...Object.keys(shared).filter((key) => !(isVinext && key === "react")).map((key) => {
|
|
1966
2009
|
const keyBase = key.endsWith("/") ? key.slice(0, -1) : key;
|
|
@@ -2040,7 +2083,6 @@ const VarRemoteEntry = () => {
|
|
|
2040
2083
|
if (req.url?.replace(/\?.*/, "") === (viteConfig.base + varFilename).replace(/^\/?/, "/")) {
|
|
2041
2084
|
res.setHeader("Content-Type", "text/javascript");
|
|
2042
2085
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
2043
|
-
console.log({ filename });
|
|
2044
2086
|
res.end(generateVarRemoteEntry(filename));
|
|
2045
2087
|
} else next();
|
|
2046
2088
|
});
|
|
@@ -2056,9 +2098,9 @@ const VarRemoteEntry = () => {
|
|
|
2056
2098
|
},
|
|
2057
2099
|
async generateBundle(options, bundle) {
|
|
2058
2100
|
if (!varFilename) return;
|
|
2059
|
-
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).`);
|
|
2060
2102
|
const remoteEntryFile = findRemoteEntryFile(mfOptions.filename, bundle);
|
|
2061
|
-
if (!remoteEntryFile) throw
|
|
2103
|
+
if (!remoteEntryFile) throw createModuleFederationError(`Couldn't find a remoteEntry chunk file for ${mfOptions.filename}, can't generate varRemoteEntry file`);
|
|
2062
2104
|
this.emitFile({
|
|
2063
2105
|
type: "asset",
|
|
2064
2106
|
fileName: varFilename,
|
|
@@ -2083,7 +2125,7 @@ const VarRemoteEntry = () => {
|
|
|
2083
2125
|
function getScriptUrl() {
|
|
2084
2126
|
const currentScript = document.currentScript;
|
|
2085
2127
|
if (!currentScript) {
|
|
2086
|
-
console.error("[
|
|
2128
|
+
console.error("[Module Federation] ${varFilename} script should be called from sync <script> tag (document.currentScript is undefined)")
|
|
2087
2129
|
return '/';
|
|
2088
2130
|
}
|
|
2089
2131
|
return document.currentScript.src.replace(/\\/[^/]*$/, '/');
|
|
@@ -2182,6 +2224,25 @@ var normalizeOptimizeDeps_default = {
|
|
|
2182
2224
|
};
|
|
2183
2225
|
//#endregion
|
|
2184
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
|
+
}
|
|
2185
2246
|
/**
|
|
2186
2247
|
* Plugin that runs FIRST to create virtual module files in the config hook.
|
|
2187
2248
|
* This prevents 504 "Outdated Optimize Dep" errors by ensuring files exist
|
|
@@ -2201,7 +2262,7 @@ function createEarlyVirtualModulesPlugin(options) {
|
|
|
2201
2262
|
VirtualModule.ensureVirtualPackageExists();
|
|
2202
2263
|
initVirtualModules(_command, getRemoteEntryId(options));
|
|
2203
2264
|
if (_command !== "serve") return;
|
|
2204
|
-
const isRolldown =
|
|
2265
|
+
const isRolldown = getIsRolldown(this);
|
|
2205
2266
|
if (remotes && Object.keys(remotes).length > 0) for (const key of Object.keys(remotes)) addUsedRemote(key, key);
|
|
2206
2267
|
if (shared && Object.keys(shared).length > 0) {
|
|
2207
2268
|
config.optimizeDeps = config.optimizeDeps || {};
|
|
@@ -2218,6 +2279,7 @@ function createEarlyVirtualModulesPlugin(options) {
|
|
|
2218
2279
|
writeLoadShareModule(key, shareItem, _command, isRolldown);
|
|
2219
2280
|
writePreBuildLibPath(key);
|
|
2220
2281
|
addUsedShares(key);
|
|
2282
|
+
config.optimizeDeps.include.push(getLoadShareImportId(key, isRolldown, _command));
|
|
2221
2283
|
config.optimizeDeps.include.push(getPreBuildLibImportId(key));
|
|
2222
2284
|
}
|
|
2223
2285
|
writeLocalSharedImportMap();
|
|
@@ -2229,7 +2291,7 @@ function federation(mfUserOptions) {
|
|
|
2229
2291
|
const options = normalizeModuleFederationOptions(mfUserOptions);
|
|
2230
2292
|
const isVinext = hasPackageDependency("vinext");
|
|
2231
2293
|
const { name, remotes, shared, filename, hostInitInjectLocation } = options;
|
|
2232
|
-
if (!name) throw
|
|
2294
|
+
if (!name) throw createModuleFederationError("name is required");
|
|
2233
2295
|
const remoteEntryId = getRemoteEntryId(options);
|
|
2234
2296
|
const virtualExposesId = getVirtualExposesId(options);
|
|
2235
2297
|
let command;
|
|
@@ -2315,7 +2377,16 @@ function federation(mfUserOptions) {
|
|
|
2315
2377
|
}
|
|
2316
2378
|
};
|
|
2317
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
|
+
};
|
|
2318
2388
|
const applyManualChunks = (output) => {
|
|
2389
|
+
ensureCodeSplitting(output);
|
|
2319
2390
|
const existingManualChunks = output.manualChunks;
|
|
2320
2391
|
output.manualChunks = function(id) {
|
|
2321
2392
|
if (id.includes(runtimeInitId)) return "runtimeInit";
|
|
@@ -2537,7 +2608,7 @@ function federation(mfUserOptions) {
|
|
|
2537
2608
|
enforce: "post",
|
|
2538
2609
|
_options: options,
|
|
2539
2610
|
config(config, { command: _command }) {
|
|
2540
|
-
const isRolldown =
|
|
2611
|
+
const isRolldown = getIsRolldown(this);
|
|
2541
2612
|
let implementation = options.implementation;
|
|
2542
2613
|
if (isRolldown) implementation = implementation.replace(/\.cjs(\.js)?$/, ".js");
|
|
2543
2614
|
config.resolve.alias.push({
|
|
@@ -2566,7 +2637,7 @@ function federation(mfUserOptions) {
|
|
|
2566
2637
|
const resolvedTarget = options.target ?? (config.build?.ssr ? "node" : "web");
|
|
2567
2638
|
if (!config.define) config.define = {};
|
|
2568
2639
|
if (!("ENV_TARGET" in config.define)) config.define["ENV_TARGET"] = JSON.stringify(resolvedTarget);
|
|
2569
|
-
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.`);
|
|
2570
2641
|
}
|
|
2571
2642
|
},
|
|
2572
2643
|
...Manifest(),
|
|
@@ -2579,13 +2650,18 @@ function federation(mfUserOptions) {
|
|
|
2579
2650
|
for (const chunk of Object.values(bundle)) {
|
|
2580
2651
|
if (chunk.type !== "chunk") continue;
|
|
2581
2652
|
if (!chunk.code.includes("modulepreload")) continue;
|
|
2582
|
-
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}`;
|
|
2583
2657
|
const replaced = chunk.code.replace(/=\(?(\w+)(?:,\w+)?\)?\s*=>\s*["'`][^"'`]*["'`]\s*\+\s*\1/, replacement);
|
|
2584
2658
|
if (replaced !== chunk.code) {
|
|
2585
2659
|
chunk.code = replaced;
|
|
2586
2660
|
continue;
|
|
2587
2661
|
}
|
|
2588
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`);
|
|
2589
2665
|
}
|
|
2590
2666
|
}
|
|
2591
2667
|
}] : []
|
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);
|
|
@@ -223,7 +262,7 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
|
|
|
223
262
|
if (typeof result === "string") return result;
|
|
224
263
|
if (result && typeof result === "object") {
|
|
225
264
|
if ("runtime" in result) {
|
|
226
|
-
|
|
265
|
+
mfWarn("renderBuiltUrl returned runtime code for HTML injection. Runtime code cannot be used in <script src=\"\">. Falling back to base path.");
|
|
227
266
|
return viteConfig.base + file;
|
|
228
267
|
}
|
|
229
268
|
if (result.relative) return file;
|
|
@@ -288,11 +327,11 @@ function checkAliasConflicts(options) {
|
|
|
288
327
|
});
|
|
289
328
|
}
|
|
290
329
|
if (conflicts.length > 0) {
|
|
291
|
-
|
|
330
|
+
mfWarn("Detected alias conflicts with shared modules:");
|
|
292
331
|
conflicts.forEach(({ sharedModule, alias, target }) => {
|
|
293
|
-
|
|
332
|
+
mfWarn(`Shared module "${sharedModule}" is aliased by "${alias}" to "${target}"`);
|
|
294
333
|
});
|
|
295
|
-
|
|
334
|
+
mfWarn("This may cause runtime errors as the shared module will bypass Module Federation's sharing mechanism.");
|
|
296
335
|
}
|
|
297
336
|
}
|
|
298
337
|
};
|
|
@@ -321,7 +360,7 @@ function PluginDevProxyModuleTopLevelAwait() {
|
|
|
321
360
|
try {
|
|
322
361
|
ast = this.parse(code, { allowReturnOutsideFunction: true });
|
|
323
362
|
} catch (e) {
|
|
324
|
-
throw
|
|
363
|
+
throw createModuleFederationError(`${id}: ${e}`);
|
|
325
364
|
}
|
|
326
365
|
const magicString = new MagicString(code);
|
|
327
366
|
const walk = await loadWalk();
|
|
@@ -449,7 +488,7 @@ const normalizeDevDtsOptions = (dts, context) => {
|
|
|
449
488
|
const logDtsError = (error, dtsOptions) => {
|
|
450
489
|
if (dtsOptions === false) return;
|
|
451
490
|
if (typeof dtsOptions === "object" && dtsOptions && dtsOptions.displayErrorInTerminal === false) return;
|
|
452
|
-
|
|
491
|
+
mfError(error);
|
|
453
492
|
};
|
|
454
493
|
function pluginDts(options) {
|
|
455
494
|
if (options.dts === false) return [];
|
|
@@ -477,7 +516,7 @@ function pluginDts(options) {
|
|
|
477
516
|
if (!normalizedDevOptions || !resolvedConfig) return;
|
|
478
517
|
const devOptions = normalizedDevOptions;
|
|
479
518
|
if (devOptions.disableDynamicRemoteTypeHints && devOptions.disableHotTypesReload && devOptions.disableLiveReload) return;
|
|
480
|
-
if (!options.name) throw
|
|
519
|
+
if (!options.name) throw createModuleFederationError("name is required if you want to enable dev server!");
|
|
481
520
|
const outputDir = resolveOutputDir(resolvedConfig);
|
|
482
521
|
const normalizedDtsOptions = normalizeDevDtsOptions(options.dts, resolvedConfig.root);
|
|
483
522
|
if (typeof normalizedDtsOptions !== "object") return;
|
|
@@ -672,11 +711,11 @@ function normalizeShareItem(key, shareItem) {
|
|
|
672
711
|
version = __require(path$1.join(process.cwd(), "node_modules", removePathFromNpmPackage(key), "package.json")).version;
|
|
673
712
|
} catch (e2) {
|
|
674
713
|
version = searchPackageVersion(key);
|
|
675
|
-
if (!version)
|
|
714
|
+
if (!version) mfError(e1);
|
|
676
715
|
}
|
|
677
716
|
}
|
|
678
717
|
} catch (e) {
|
|
679
|
-
|
|
718
|
+
mfError(`Unexpected error resolving version for ${key}:`, e);
|
|
680
719
|
}
|
|
681
720
|
if (typeof shareItem === "string") return {
|
|
682
721
|
name: shareItem,
|
|
@@ -737,7 +776,7 @@ function getNormalizeShareItem(key) {
|
|
|
737
776
|
return options.shared[key] || options.shared[removePathFromNpmPackage(key)] || options.shared[removePathFromNpmPackage(key) + "/"];
|
|
738
777
|
}
|
|
739
778
|
function normalizeModuleFederationOptions(options) {
|
|
740
|
-
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'.`);
|
|
741
780
|
return config = {
|
|
742
781
|
exposes: normalizeExposes(options.exposes),
|
|
743
782
|
filename: options.filename || "remoteEntry-[hash]",
|
|
@@ -872,7 +911,7 @@ const cacheMap = {};
|
|
|
872
911
|
*/
|
|
873
912
|
function assertModuleFound(tag, str = "") {
|
|
874
913
|
const module = VirtualModule.findModule(tag, str);
|
|
875
|
-
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.`);
|
|
876
915
|
return module;
|
|
877
916
|
}
|
|
878
917
|
var VirtualModule = class {
|
|
@@ -1116,8 +1155,12 @@ function getPreBuildLibImportId(pkg) {
|
|
|
1116
1155
|
}
|
|
1117
1156
|
const LOAD_SHARE_TAG = "__loadShare__";
|
|
1118
1157
|
const loadShareCacheMap = {};
|
|
1119
|
-
function
|
|
1158
|
+
function getLoadShareImportId(pkg, isRolldown, command) {
|
|
1120
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);
|
|
1121
1164
|
return loadShareCacheMap[pkg].getPath();
|
|
1122
1165
|
}
|
|
1123
1166
|
function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
|
|
@@ -1133,8 +1176,8 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
|
|
|
1133
1176
|
if (namedExports.length > 0) {
|
|
1134
1177
|
const destructure = `const { ${namedExports.map((name, i) => `${name}: __mf_${i}`).join(", ")} } = exportModule;`;
|
|
1135
1178
|
const namedExportLine = `export { ${namedExports.map((name, i) => `__mf_${i} as ${name}`).join(", ")} };`;
|
|
1136
|
-
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(", ")} });`;
|
|
1137
|
-
} 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";
|
|
1138
1181
|
loadShareCacheMap[pkg].writeSync(`
|
|
1139
1182
|
import ${JSON.stringify(getPreBuildLibImportId(pkg))};
|
|
1140
1183
|
${command !== "build" ? `;() => import(${JSON.stringify(pkg)}).catch(() => {});` : ""}
|
|
@@ -1186,7 +1229,7 @@ function generateLocalSharedImportMap() {
|
|
|
1186
1229
|
const shareItem = getNormalizeShareItem(pkg);
|
|
1187
1230
|
return `
|
|
1188
1231
|
${JSON.stringify(pkg)}: async () => {
|
|
1189
|
-
${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");
|
|
1190
1233
|
return pkg;` : `let pkg = await import("${getPreBuildLibImportId(pkg)}");
|
|
1191
1234
|
return pkg;`}
|
|
1192
1235
|
}
|
|
@@ -1206,7 +1249,7 @@ function generateLocalSharedImportMap() {
|
|
|
1206
1249
|
from: ${JSON.stringify(options.name)},
|
|
1207
1250
|
async get () {
|
|
1208
1251
|
if (${shareItem.shareConfig.import === false}) {
|
|
1209
|
-
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\`);
|
|
1210
1253
|
}
|
|
1211
1254
|
usedShared[${JSON.stringify(key)}].loaded = true
|
|
1212
1255
|
const {${JSON.stringify(key)}: pkgDynamicImport} = importMap
|
|
@@ -1313,14 +1356,14 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
|
|
|
1313
1356
|
initScope
|
|
1314
1357
|
}));
|
|
1315
1358
|
} catch (e) {
|
|
1316
|
-
console.error(e)
|
|
1359
|
+
console.error('[Module Federation]', e)
|
|
1317
1360
|
}
|
|
1318
1361
|
return initRes
|
|
1319
1362
|
}
|
|
1320
1363
|
|
|
1321
1364
|
async function getExposes(moduleName) {
|
|
1322
1365
|
const exposesMap = await getExposesMap()
|
|
1323
|
-
if (!(moduleName in exposesMap)) throw new Error(\`Module \${moduleName} does not exist in container.\`)
|
|
1366
|
+
if (!(moduleName in exposesMap)) throw new Error(\`[Module Federation] Module \${moduleName} does not exist in container.\`)
|
|
1324
1367
|
return (exposesMap[moduleName])().then(res => () => res)
|
|
1325
1368
|
}
|
|
1326
1369
|
export {
|
|
@@ -1737,14 +1780,14 @@ const promise = new Promise((resolve, reject) => {
|
|
|
1737
1780
|
});
|
|
1738
1781
|
function setParseTimeout(timeout) {
|
|
1739
1782
|
if (!_parseTimeout) _parseTimeout = setTimeout(() => {
|
|
1740
|
-
|
|
1783
|
+
mfWarn(`Parse timeout (${timeout}s) - forcing resolve`);
|
|
1741
1784
|
_resolve(1);
|
|
1742
1785
|
}, timeout * 1e3);
|
|
1743
1786
|
}
|
|
1744
1787
|
function resetIdleTimeout(timeout) {
|
|
1745
1788
|
clearTimeout(_parseTimeout);
|
|
1746
1789
|
_parseTimeout = setTimeout(() => {
|
|
1747
|
-
|
|
1790
|
+
mfWarn(`moduleParseIdleTimeout: no module activity for ${timeout}s, forcing resolve. Some shared/remote dependencies may be missing. Consider increasing moduleParseIdleTimeout.`);
|
|
1748
1791
|
_resolve(1);
|
|
1749
1792
|
}, timeout * 1e3);
|
|
1750
1793
|
}
|
|
@@ -1865,7 +1908,7 @@ function pluginProxyRemotes_default(options) {
|
|
|
1865
1908
|
return {
|
|
1866
1909
|
name: "proxyRemotes",
|
|
1867
1910
|
config(config, { command: _command }) {
|
|
1868
|
-
const isRolldown =
|
|
1911
|
+
const isRolldown = getIsRolldown(this);
|
|
1869
1912
|
Object.keys(remotes).forEach((key) => {
|
|
1870
1913
|
const remote = remotes[key];
|
|
1871
1914
|
config.resolve.alias.push({
|
|
@@ -1937,7 +1980,7 @@ function proxySharedModule(options) {
|
|
|
1937
1980
|
config(config, { command }) {
|
|
1938
1981
|
setPackageDetectionCwd(config.root || process.cwd());
|
|
1939
1982
|
isVinext = hasPackageDependency("vinext");
|
|
1940
|
-
const isRolldown =
|
|
1983
|
+
const isRolldown = getIsRolldown(this);
|
|
1941
1984
|
_command = command;
|
|
1942
1985
|
config.resolve.alias.push(...Object.keys(shared).filter((key) => !(isVinext && key === "react")).map((key) => {
|
|
1943
1986
|
const keyBase = key.endsWith("/") ? key.slice(0, -1) : key;
|
|
@@ -2017,7 +2060,6 @@ const VarRemoteEntry = () => {
|
|
|
2017
2060
|
if (req.url?.replace(/\?.*/, "") === (viteConfig.base + varFilename).replace(/^\/?/, "/")) {
|
|
2018
2061
|
res.setHeader("Content-Type", "text/javascript");
|
|
2019
2062
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
2020
|
-
console.log({ filename });
|
|
2021
2063
|
res.end(generateVarRemoteEntry(filename));
|
|
2022
2064
|
} else next();
|
|
2023
2065
|
});
|
|
@@ -2033,9 +2075,9 @@ const VarRemoteEntry = () => {
|
|
|
2033
2075
|
},
|
|
2034
2076
|
async generateBundle(options, bundle) {
|
|
2035
2077
|
if (!varFilename) return;
|
|
2036
|
-
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).`);
|
|
2037
2079
|
const remoteEntryFile = findRemoteEntryFile(mfOptions.filename, bundle);
|
|
2038
|
-
if (!remoteEntryFile) throw
|
|
2080
|
+
if (!remoteEntryFile) throw createModuleFederationError(`Couldn't find a remoteEntry chunk file for ${mfOptions.filename}, can't generate varRemoteEntry file`);
|
|
2039
2081
|
this.emitFile({
|
|
2040
2082
|
type: "asset",
|
|
2041
2083
|
fileName: varFilename,
|
|
@@ -2060,7 +2102,7 @@ const VarRemoteEntry = () => {
|
|
|
2060
2102
|
function getScriptUrl() {
|
|
2061
2103
|
const currentScript = document.currentScript;
|
|
2062
2104
|
if (!currentScript) {
|
|
2063
|
-
console.error("[
|
|
2105
|
+
console.error("[Module Federation] ${varFilename} script should be called from sync <script> tag (document.currentScript is undefined)")
|
|
2064
2106
|
return '/';
|
|
2065
2107
|
}
|
|
2066
2108
|
return document.currentScript.src.replace(/\\/[^/]*$/, '/');
|
|
@@ -2159,6 +2201,25 @@ var normalizeOptimizeDeps_default = {
|
|
|
2159
2201
|
};
|
|
2160
2202
|
//#endregion
|
|
2161
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
|
+
}
|
|
2162
2223
|
/**
|
|
2163
2224
|
* Plugin that runs FIRST to create virtual module files in the config hook.
|
|
2164
2225
|
* This prevents 504 "Outdated Optimize Dep" errors by ensuring files exist
|
|
@@ -2178,7 +2239,7 @@ function createEarlyVirtualModulesPlugin(options) {
|
|
|
2178
2239
|
VirtualModule.ensureVirtualPackageExists();
|
|
2179
2240
|
initVirtualModules(_command, getRemoteEntryId(options));
|
|
2180
2241
|
if (_command !== "serve") return;
|
|
2181
|
-
const isRolldown =
|
|
2242
|
+
const isRolldown = getIsRolldown(this);
|
|
2182
2243
|
if (remotes && Object.keys(remotes).length > 0) for (const key of Object.keys(remotes)) addUsedRemote(key, key);
|
|
2183
2244
|
if (shared && Object.keys(shared).length > 0) {
|
|
2184
2245
|
config.optimizeDeps = config.optimizeDeps || {};
|
|
@@ -2195,6 +2256,7 @@ function createEarlyVirtualModulesPlugin(options) {
|
|
|
2195
2256
|
writeLoadShareModule(key, shareItem, _command, isRolldown);
|
|
2196
2257
|
writePreBuildLibPath(key);
|
|
2197
2258
|
addUsedShares(key);
|
|
2259
|
+
config.optimizeDeps.include.push(getLoadShareImportId(key, isRolldown, _command));
|
|
2198
2260
|
config.optimizeDeps.include.push(getPreBuildLibImportId(key));
|
|
2199
2261
|
}
|
|
2200
2262
|
writeLocalSharedImportMap();
|
|
@@ -2206,7 +2268,7 @@ function federation(mfUserOptions) {
|
|
|
2206
2268
|
const options = normalizeModuleFederationOptions(mfUserOptions);
|
|
2207
2269
|
const isVinext = hasPackageDependency("vinext");
|
|
2208
2270
|
const { name, remotes, shared, filename, hostInitInjectLocation } = options;
|
|
2209
|
-
if (!name) throw
|
|
2271
|
+
if (!name) throw createModuleFederationError("name is required");
|
|
2210
2272
|
const remoteEntryId = getRemoteEntryId(options);
|
|
2211
2273
|
const virtualExposesId = getVirtualExposesId(options);
|
|
2212
2274
|
let command;
|
|
@@ -2292,7 +2354,16 @@ function federation(mfUserOptions) {
|
|
|
2292
2354
|
}
|
|
2293
2355
|
};
|
|
2294
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
|
+
};
|
|
2295
2365
|
const applyManualChunks = (output) => {
|
|
2366
|
+
ensureCodeSplitting(output);
|
|
2296
2367
|
const existingManualChunks = output.manualChunks;
|
|
2297
2368
|
output.manualChunks = function(id) {
|
|
2298
2369
|
if (id.includes(runtimeInitId)) return "runtimeInit";
|
|
@@ -2514,7 +2585,7 @@ function federation(mfUserOptions) {
|
|
|
2514
2585
|
enforce: "post",
|
|
2515
2586
|
_options: options,
|
|
2516
2587
|
config(config, { command: _command }) {
|
|
2517
|
-
const isRolldown =
|
|
2588
|
+
const isRolldown = getIsRolldown(this);
|
|
2518
2589
|
let implementation = options.implementation;
|
|
2519
2590
|
if (isRolldown) implementation = implementation.replace(/\.cjs(\.js)?$/, ".js");
|
|
2520
2591
|
config.resolve.alias.push({
|
|
@@ -2543,7 +2614,7 @@ function federation(mfUserOptions) {
|
|
|
2543
2614
|
const resolvedTarget = options.target ?? (config.build?.ssr ? "node" : "web");
|
|
2544
2615
|
if (!config.define) config.define = {};
|
|
2545
2616
|
if (!("ENV_TARGET" in config.define)) config.define["ENV_TARGET"] = JSON.stringify(resolvedTarget);
|
|
2546
|
-
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.`);
|
|
2547
2618
|
}
|
|
2548
2619
|
},
|
|
2549
2620
|
...Manifest(),
|
|
@@ -2556,13 +2627,18 @@ function federation(mfUserOptions) {
|
|
|
2556
2627
|
for (const chunk of Object.values(bundle)) {
|
|
2557
2628
|
if (chunk.type !== "chunk") continue;
|
|
2558
2629
|
if (!chunk.code.includes("modulepreload")) continue;
|
|
2559
|
-
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}`;
|
|
2560
2634
|
const replaced = chunk.code.replace(/=\(?(\w+)(?:,\w+)?\)?\s*=>\s*["'`][^"'`]*["'`]\s*\+\s*\1/, replacement);
|
|
2561
2635
|
if (replaced !== chunk.code) {
|
|
2562
2636
|
chunk.code = replaced;
|
|
2563
2637
|
continue;
|
|
2564
2638
|
}
|
|
2565
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`);
|
|
2566
2642
|
}
|
|
2567
2643
|
}
|
|
2568
2644
|
}] : []
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@module-federation/vite",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.2",
|
|
4
4
|
"description": "Vite plugin for Module Federation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.cjs",
|
|
@@ -30,8 +30,9 @@
|
|
|
30
30
|
"dev-rv": "pnpm clean && pnpm -filter 'examples-rust-vite*' run dev",
|
|
31
31
|
"preview-rv": "pnpm clean && pnpm -filter 'examples-rust-vite*' run preview",
|
|
32
32
|
"dev-vv": "pnpm clean && pnpm -filter 'examples-vite-vite*' run dev",
|
|
33
|
-
"dev-nv": "pnpm clean && pnpm -filter 'examples-nuxt-vite-host' -filter 'examples-vite-vite-remote' run dev",
|
|
34
33
|
"preview-vv": "pnpm clean && pnpm -filter 'examples-vite-vite*' --parallel run preview",
|
|
34
|
+
"mixed-vv:1": "pnpm clean && pnpm -filter 'examples-vite-vite*' run mixed:1",
|
|
35
|
+
"mixed-vv:2": "pnpm clean && pnpm -filter 'examples-vite-vite*' run mixed:2",
|
|
35
36
|
"multi-example": "pnpm clean && pnpm --filter \"multi-example-*\" --parallel run start",
|
|
36
37
|
"test": "vitest run --dir src",
|
|
37
38
|
"test:integration": "vitest run integration",
|
|
@@ -66,9 +67,9 @@
|
|
|
66
67
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
|
|
67
68
|
},
|
|
68
69
|
"dependencies": {
|
|
69
|
-
"@module-federation/dts-plugin": "2.2.
|
|
70
|
-
"@module-federation/runtime": "2.2.
|
|
71
|
-
"@module-federation/sdk": "2.2.
|
|
70
|
+
"@module-federation/dts-plugin": "2.2.3",
|
|
71
|
+
"@module-federation/runtime": "2.2.3",
|
|
72
|
+
"@module-federation/sdk": "2.2.3",
|
|
72
73
|
"@rollup/pluginutils": "^5.3.0",
|
|
73
74
|
"defu": "^6.1.4",
|
|
74
75
|
"estree-walker": "^3.0.3",
|