@module-federation/vite 1.13.2 → 1.13.3

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.
Files changed (3) hide show
  1. package/lib/index.cjs +322 -52
  2. package/lib/index.mjs +322 -52
  3. package/package.json +2 -1
package/lib/index.cjs CHANGED
@@ -49,35 +49,25 @@ async function mapCodeToCodeWithSourcemap(code) {
49
49
  //#endregion
50
50
  //#region src/utils/htmlEntryUtils.ts
51
51
  function sanitizeDevEntryPath(devEntryPath) {
52
- return devEntryPath.replace(/^[^:]+:([/\\])[/\\]?/, "$1").replace(/\\\\?/g, "/");
52
+ return devEntryPath.replace(/\\\\?/g, "/");
53
53
  }
54
54
  /**
55
- * Inlines the federation init import into existing module script tags to fix
56
- * the race condition (#396) where separate `<script type="module">` tags
57
- * don't guarantee execution order with top-level await.
58
- *
59
- * If no entry scripts are found, falls back to injecting a separate script tag.
60
- *
61
- * @example
62
- * // Before (two separate scripts, race condition):
63
- * // <script type="module" src="/__mf__virtual/hostAutoInit.js"><\/script>
64
- * // <script type="module" src="/src/main.js"><\/script>
65
- * // After (single inline script, sequential execution):
66
- * // <script type="module">await import("/__mf__virtual/hostAutoInit.js");await import("/src/main.js");<\/script>
55
+ * Rewrites entry module script tags to point at an external wrapper module.
56
+ * The wrapper can then sequence federation init before the app entry without
57
+ * relying on CSP-breaking inline `<script type="module">`.
67
58
  */
68
- function inlineEntryScripts(html, initSrc) {
69
- const src = sanitizeDevEntryPath(initSrc);
70
- const scriptTagRegex = /<script\s+([^>]*\btype=["']module["'][^>]*\bsrc=["'][^"']+["'][^>]*)>/gi;
71
- let hasEntry = false;
72
- const result = html.replace(scriptTagRegex, (match, attrs) => {
59
+ function rewriteEntryScripts(html, createProxySrc) {
60
+ return html.replace(/<script\s+([^>]*\btype=["']module["'][^>]*\bsrc=["'][^"']+["'][^>]*)>/gi, (match, attrs) => {
73
61
  const srcMatch = attrs.match(/\bsrc=["']([^"']+)["']/i);
74
62
  if (!srcMatch) return match;
75
63
  const originalSrc = srcMatch[1];
76
64
  if (originalSrc.includes("@vite/client")) return match;
77
- hasEntry = true;
78
- return `<script ${attrs.replace(/\s*\bsrc=["'][^"']+["']/i, "")}>await import(${JSON.stringify(src)});await import(${JSON.stringify(originalSrc)});`;
65
+ const proxySrc = createProxySrc(originalSrc);
66
+ return match.replace(srcMatch[0], `src=${JSON.stringify(proxySrc)}`);
79
67
  });
80
- if (hasEntry) return result;
68
+ }
69
+ function injectEntryScript(html, initSrc) {
70
+ const src = sanitizeDevEntryPath(initSrc);
81
71
  return html.replace("<head>", `<head><script type="module" src=${JSON.stringify(src)}><\/script>`);
82
72
  }
83
73
  //#endregion
@@ -122,6 +112,9 @@ function getDependencyCacheKey(cwd, dependencyName) {
122
112
  function setPackageDetectionCwd(cwd) {
123
113
  packageDetectionCwd = cwd;
124
114
  }
115
+ function getPackageDetectionCwd() {
116
+ return packageDetectionCwd || process.cwd();
117
+ }
125
118
  /**
126
119
  * Escaping rules:
127
120
  * Convert using the format __${mapping}__, where _ and $ are not allowed in npm package names but can be used in variable names.
@@ -189,6 +182,7 @@ function getFirstHtmlEntryFile(entryFiles) {
189
182
  return entryFiles.find((file) => file.endsWith(".html"));
190
183
  }
191
184
  const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
185
+ const DEV_HTML_PROXY_PREFIX = "virtual:mf-html-entry-proxy?";
192
186
  const getEntryPath = () => typeof entryPath === "function" ? entryPath() : entryPath;
193
187
  let devEntryPath = "";
194
188
  let entryFiles = [];
@@ -212,8 +206,13 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
212
206
  configResolved(config) {
213
207
  viteConfig = config;
214
208
  const resolvedEntryPath = getEntryPath();
215
- devEntryPath = resolvedEntryPath.startsWith("virtual:mf") ? "@id/" + resolvedEntryPath : resolvedEntryPath;
216
- devEntryPath = config.base + devEntryPath.replace(/\\\\?/g, "/").replace(/^[^:]+:([/\\])[/\\]?/, "$1").replace(/^\//, "");
209
+ if (resolvedEntryPath.startsWith("virtual:mf")) devEntryPath = config.base + "@id/" + resolvedEntryPath;
210
+ else {
211
+ const normalized = resolvedEntryPath.replace(/\\\\?/g, "/");
212
+ const root = config.root.replace(/\\\\?/g, "/").replace(/\/$/, "");
213
+ const relativePath = normalized.startsWith(root + "/") ? normalized.slice(root.length) : "/" + normalized.replace(/^[A-Za-z]:[\\/]/, "");
214
+ devEntryPath = config.base + relativePath.replace(/^\//, "");
215
+ }
217
216
  },
218
217
  configureServer(server) {
219
218
  server.middlewares.use((req, res, next) => {
@@ -228,7 +227,29 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
228
227
  transformIndexHtml(c) {
229
228
  if (!injectHtml()) return;
230
229
  clientInjected = true;
231
- return inlineEntryScripts(c, devEntryPath);
230
+ const html = rewriteEntryScripts(c, (originalSrc) => {
231
+ const query = new URLSearchParams({
232
+ init: sanitizeDevEntryPath(devEntryPath),
233
+ entry: originalSrc
234
+ }).toString();
235
+ return `${viteConfig.base}@id/${DEV_HTML_PROXY_PREFIX}${query}`;
236
+ });
237
+ return html === c ? injectEntryScript(c, devEntryPath) : html;
238
+ },
239
+ resolveId(id) {
240
+ if (id.startsWith(DEV_HTML_PROXY_PREFIX)) return id;
241
+ },
242
+ load(id) {
243
+ if (!id.startsWith(DEV_HTML_PROXY_PREFIX)) return;
244
+ const params = new URLSearchParams(id.slice(28));
245
+ const initSrc = params.get("init");
246
+ const entrySrc = params.get("entry");
247
+ if (!initSrc || !entrySrc) return;
248
+ return `
249
+ const baseUrl = document.baseURI || window.location.href;
250
+ await import(new URL(${JSON.stringify(initSrc)}, baseUrl).href);
251
+ await import(new URL(${JSON.stringify(entrySrc)}, baseUrl).href);
252
+ `;
232
253
  },
233
254
  transform(code, id) {
234
255
  if (id.includes("node_modules") || inject !== "html" || htmlFilePath) return;
@@ -995,15 +1016,61 @@ var VirtualModule = class {
995
1016
  };
996
1017
  //#endregion
997
1018
  //#region src/virtualModules/virtualExposes.ts
1019
+ const EXPOSES_CSS_MAP_PLACEHOLDER = "__MF_EXPOSES_CSS_MAP__";
1020
+ function getExposesCssMapPlaceholder() {
1021
+ return EXPOSES_CSS_MAP_PLACEHOLDER;
1022
+ }
998
1023
  function getVirtualExposesId(options) {
999
1024
  return `virtual:mf-exposes:${`${options.name}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
1000
1025
  }
1001
1026
  function generateExposes(options) {
1002
1027
  return `
1028
+ const cssAssetMap = ${JSON.stringify(options.bundleAllCSS ? EXPOSES_CSS_MAP_PLACEHOLDER : {})};
1029
+ const injectedCssHrefs = new Set();
1030
+
1031
+ async function injectCssAssets(exposeKey) {
1032
+ if (typeof document === "undefined") {
1033
+ return;
1034
+ }
1035
+
1036
+ // Replaced at build time with expose -> css asset paths.
1037
+ const cssAssets = cssAssetMap[exposeKey] || [];
1038
+
1039
+ await Promise.all(
1040
+ cssAssets.map((cssAsset) => {
1041
+ const href = new URL(cssAsset, import.meta.url).href;
1042
+
1043
+ // Same expose can be resolved multiple times in one page.
1044
+ if (injectedCssHrefs.has(href)) {
1045
+ return Promise.resolve();
1046
+ }
1047
+ injectedCssHrefs.add(href);
1048
+
1049
+ const existingLink = document.querySelector(
1050
+ \`link[rel="stylesheet"][data-mf-href="\${href}"]\`
1051
+ );
1052
+ if (existingLink) {
1053
+ return Promise.resolve();
1054
+ }
1055
+
1056
+ return new Promise((resolve, reject) => {
1057
+ const link = document.createElement("link");
1058
+ link.rel = "stylesheet";
1059
+ link.href = href;
1060
+ link.setAttribute("data-mf-href", href);
1061
+ link.onload = () => resolve();
1062
+ link.onerror = () => reject(new Error(\`[Module Federation] Failed to load CSS asset: \${href}\`));
1063
+ document.head.appendChild(link);
1064
+ });
1065
+ })
1066
+ );
1067
+ }
1068
+
1003
1069
  export default {
1004
1070
  ${Object.keys(options.exposes).map((key) => {
1005
1071
  return `
1006
1072
  ${JSON.stringify(key)}: async () => {
1073
+ await injectCssAssets(${JSON.stringify(key)})
1007
1074
  const importModule = await import(${JSON.stringify(options.exposes[key].import)})
1008
1075
  const exportModule = {}
1009
1076
  Object.assign(exportModule, importModule)
@@ -1150,32 +1217,159 @@ function generateRemotes(id, command, isRolldown) {
1150
1217
  * 1. __prebuild__: export shareModule (pre-built source code of modules such as vue, react, etc.)
1151
1218
  * 2. __loadShare__: load shareModule (mfRuntime.loadShare('vue'))
1152
1219
  */
1220
+ function escapeGeneratedStringLiteral(value) {
1221
+ return JSON.stringify(value).replace(/[<>\u2028\u2029]/g, (char) => {
1222
+ switch (char) {
1223
+ case "<": return "\\u003C";
1224
+ case ">": return "\\u003E";
1225
+ case "\u2028": return "\\u2028";
1226
+ case "\u2029": return "\\u2029";
1227
+ default: return char;
1228
+ }
1229
+ });
1230
+ }
1231
+ const localRequire = (0, module$1.createRequire)(require("url").pathToFileURL(__filename).href);
1232
+ function resolvePackageEntryFromProjectRoot(pkg) {
1233
+ try {
1234
+ return (0, module$1.createRequire)(new URL(`file://${pathe.default.join(getPackageDetectionCwd(), "package.json")}`)).resolve(pkg);
1235
+ } catch {
1236
+ return;
1237
+ }
1238
+ }
1239
+ function getInstalledPackageJsonPath(pkg) {
1240
+ try {
1241
+ const packageName = removePathFromNpmPackage(pkg);
1242
+ const projectRequire = (0, module$1.createRequire)(new URL(`file://${pathe.default.join(getPackageDetectionCwd(), "package.json")}`));
1243
+ let resolvedPath;
1244
+ try {
1245
+ resolvedPath = projectRequire.resolve(pkg);
1246
+ } catch {
1247
+ resolvedPath = projectRequire.resolve(packageName);
1248
+ }
1249
+ let currentDir = pathe.default.dirname(resolvedPath);
1250
+ const rootDir = pathe.default.parse(currentDir).root;
1251
+ while (currentDir !== rootDir) {
1252
+ const packageJsonPath = pathe.default.join(currentDir, "package.json");
1253
+ if ((0, fs.existsSync)(packageJsonPath)) {
1254
+ if (JSON.parse((0, fs.readFileSync)(packageJsonPath, "utf-8")).name === packageName) return packageJsonPath;
1255
+ }
1256
+ currentDir = pathe.default.dirname(currentDir);
1257
+ }
1258
+ const rootPackageJsonPath = pathe.default.join(rootDir, "package.json");
1259
+ if ((0, fs.existsSync)(rootPackageJsonPath)) {
1260
+ if (JSON.parse((0, fs.readFileSync)(rootPackageJsonPath, "utf-8")).name === packageName) return rootPackageJsonPath;
1261
+ }
1262
+ } catch {
1263
+ const packageName = removePathFromNpmPackage(pkg);
1264
+ let currentDir = getPackageDetectionCwd();
1265
+ const rootDir = pathe.default.parse(currentDir).root;
1266
+ while (currentDir !== rootDir) {
1267
+ const packageJsonPath = pathe.default.join(currentDir, "node_modules", packageName, "package.json");
1268
+ if ((0, fs.existsSync)(packageJsonPath)) return packageJsonPath;
1269
+ currentDir = pathe.default.dirname(currentDir);
1270
+ }
1271
+ const rootPackageJsonPath = pathe.default.join(rootDir, "node_modules", packageName, "package.json");
1272
+ return (0, fs.existsSync)(rootPackageJsonPath) ? rootPackageJsonPath : void 0;
1273
+ }
1274
+ }
1275
+ function resolveImportTarget(exportsField) {
1276
+ if (typeof exportsField === "string") return exportsField;
1277
+ if (!exportsField || typeof exportsField !== "object") return void 0;
1278
+ const record = exportsField;
1279
+ for (const condition of [
1280
+ "import",
1281
+ "module",
1282
+ "default"
1283
+ ]) {
1284
+ const target = resolveImportTarget(record[condition]);
1285
+ if (target) return target;
1286
+ }
1287
+ for (const target of Object.values(record)) {
1288
+ const resolved = resolveImportTarget(target);
1289
+ if (resolved) return resolved;
1290
+ }
1291
+ }
1292
+ function getPackageEsmEntryPath(pkg) {
1293
+ try {
1294
+ const resolvedEntryPath = resolvePackageEntryFromProjectRoot(pkg);
1295
+ const packageJsonPath = getInstalledPackageJsonPath(pkg);
1296
+ if (!packageJsonPath) return resolvedEntryPath;
1297
+ const packageName = removePathFromNpmPackage(pkg);
1298
+ const packageJson = JSON.parse((0, fs.readFileSync)(packageJsonPath, "utf-8"));
1299
+ const subpath = pkg === packageName ? "." : `.${pkg.slice(packageName.length)}`;
1300
+ const target = resolveImportTarget(typeof packageJson.exports === "string" ? subpath === "." ? packageJson.exports : void 0 : packageJson.exports?.[subpath] ?? (subpath === "." ? packageJson.exports?.["."] ?? (packageJson.exports && !Object.keys(packageJson.exports).some((key) => key.startsWith(".")) ? packageJson.exports : void 0) : void 0)) || packageJson.module;
1301
+ if (!target) return resolvedEntryPath;
1302
+ return pathe.default.resolve(pathe.default.dirname(packageJsonPath), target);
1303
+ } catch {
1304
+ return resolvePackageEntryFromProjectRoot(pkg);
1305
+ }
1306
+ }
1307
+ function getEsmNamedExports(pkg) {
1308
+ try {
1309
+ const entryPath = getPackageEsmEntryPath(pkg);
1310
+ if (!entryPath) return [];
1311
+ const { initSync, parse } = localRequire("es-module-lexer");
1312
+ initSync();
1313
+ const [, exports] = parse((0, fs.readFileSync)(entryPath, "utf-8"), entryPath);
1314
+ return exports.map((item) => item.n).filter((name) => !!name && name !== "default" && name !== "__esModule" && /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name));
1315
+ } catch {
1316
+ return [];
1317
+ }
1318
+ }
1153
1319
  function getPackageNamedExports(pkg) {
1154
1320
  try {
1155
- const mod = (0, module$1.createRequire)(new URL("file://" + process.cwd() + "/package.json"))(pkg);
1321
+ const mod = (0, module$1.createRequire)(new URL(`file://${pathe.default.join(getPackageDetectionCwd(), "package.json")}`))(pkg);
1156
1322
  return Object.keys(mod).filter((k) => k !== "default" && k !== "__esModule" && /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(k));
1157
1323
  } catch {
1158
- return [];
1324
+ return getEsmNamedExports(pkg);
1159
1325
  }
1160
1326
  }
1161
1327
  function getLocalProviderImportPath(pkg) {
1162
1328
  try {
1163
- const resolved = (0, module$1.createRequire)(new URL("file://" + process.cwd() + "/package.json")).resolve(pkg);
1329
+ const resolved = (0, module$1.createRequire)(new URL(`file://${pathe.default.join(getPackageDetectionCwd(), "package.json")}`)).resolve(pkg);
1164
1330
  return resolved.includes("/node_modules/") || resolved.includes("\\node_modules\\") ? void 0 : resolved;
1165
1331
  } catch {
1166
1332
  return;
1167
1333
  }
1168
1334
  }
1335
+ function tryResolveImportFromPackageRoot(pkg, root) {
1336
+ try {
1337
+ return (0, module$1.createRequire)(new URL(`file://${pathe.default.join(root, "package.json")}`)).resolve(pkg);
1338
+ } catch {
1339
+ return;
1340
+ }
1341
+ }
1342
+ function getConcreteSharedImportSource(pkg, shareItem) {
1343
+ const configuredImport = shareItem?.shareConfig.import;
1344
+ if (typeof configuredImport === "string") return configuredImport;
1345
+ const projectRoot = getPackageDetectionCwd();
1346
+ if (tryResolveImportFromPackageRoot(pkg, projectRoot)) return;
1347
+ let currentDir = pathe.default.dirname(projectRoot);
1348
+ while (currentDir !== pathe.default.dirname(currentDir)) {
1349
+ const resolved = tryResolveImportFromPackageRoot(pkg, currentDir);
1350
+ if (resolved) return resolved;
1351
+ currentDir = pathe.default.dirname(currentDir);
1352
+ }
1353
+ return tryResolveImportFromPackageRoot(pkg, currentDir);
1354
+ }
1169
1355
  const preBuildCacheMap = {};
1356
+ const preBuildShareItemMap = {};
1170
1357
  const PREBUILD_TAG = "__prebuild__";
1171
- function writePreBuildLibPath(pkg) {
1358
+ function writePreBuildLibPath(pkg, shareItem) {
1172
1359
  if (!preBuildCacheMap[pkg]) preBuildCacheMap[pkg] = new VirtualModule(pkg, PREBUILD_TAG);
1173
- preBuildCacheMap[pkg].writeSync("");
1360
+ preBuildShareItemMap[pkg] = shareItem;
1361
+ preBuildCacheMap[pkg].writeSync("", true);
1174
1362
  }
1175
1363
  function getPreBuildLibImportId(pkg) {
1176
1364
  if (!preBuildCacheMap[pkg]) preBuildCacheMap[pkg] = new VirtualModule(pkg, PREBUILD_TAG);
1177
1365
  return preBuildCacheMap[pkg].getImportId();
1178
1366
  }
1367
+ function getPreBuildShareItem(pkg) {
1368
+ return preBuildShareItemMap[pkg];
1369
+ }
1370
+ function getSharedImportSource(pkg, shareItem) {
1371
+ return getConcreteSharedImportSource(pkg, shareItem) || getPreBuildLibImportId(pkg);
1372
+ }
1179
1373
  const LOAD_SHARE_TAG = "__loadShare__";
1180
1374
  const loadShareCacheMap = {};
1181
1375
  function getLoadShareImportId(pkg, isRolldown, command) {
@@ -1193,22 +1387,25 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1193
1387
  const { initPromise } = globalThis[globalKey];` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
1194
1388
  const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
1195
1389
  const useSsrProviderFallback = hasPackageDependency("vinext") && command === "build" && pkg === "react";
1196
- const providerImportId = getLocalProviderImportPath(pkg) || getPreBuildLibImportId(pkg);
1390
+ const concreteSharedImportSource = getConcreteSharedImportSource(pkg, shareItem);
1391
+ const sharedImportSource = concreteSharedImportSource || getPreBuildLibImportId(pkg);
1392
+ const devImportSource = concreteSharedImportSource || pkg;
1393
+ const providerImportId = getLocalProviderImportPath(pkg) || concreteSharedImportSource || sharedImportSource;
1197
1394
  const namedExports = getPackageNamedExports(pkg);
1198
1395
  let exportLine;
1199
1396
  if (namedExports.length > 0) {
1200
1397
  const destructure = `const { ${namedExports.map((name, i) => `${name}: __mf_${i}`).join(", ")} } = exportModule;`;
1201
1398
  const namedExportLine = `export { ${namedExports.map((name, i) => `__mf_${i} as ${name}`).join(", ")} };`;
1202
1399
  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";
1400
+ } else exportLine = useESM ? `export default exportModule.default ?? exportModule\n export * from ${escapeGeneratedStringLiteral(sharedImportSource)}` : "module.exports = exportModule";
1204
1401
  loadShareCacheMap[pkg].writeSync(`
1205
- import ${JSON.stringify(getPreBuildLibImportId(pkg))};
1206
- ${command !== "build" ? `;() => import(${JSON.stringify(pkg)}).catch(() => {});` : ""}
1402
+ import ${escapeGeneratedStringLiteral(sharedImportSource)};
1403
+ ${command !== "build" ? `;() => import(${escapeGeneratedStringLiteral(devImportSource)}).catch(() => {});` : ""}
1207
1404
  ${importLine}
1208
1405
  ${useSsrProviderFallback ? `const providerModulePromise = typeof window === "undefined"
1209
- ? import(${JSON.stringify(providerImportId)})
1406
+ ? import(${escapeGeneratedStringLiteral(providerImportId)})
1210
1407
  : undefined` : ""}
1211
- const res = initPromise.then(runtime => runtime.loadShare(${JSON.stringify(pkg)}, {
1408
+ const res = initPromise.then(runtime => runtime.loadShare(${escapeGeneratedStringLiteral(pkg)}, {
1212
1409
  customShareInfo: {shareConfig:{
1213
1410
  singleton: ${shareItem.shareConfig.singleton},
1214
1411
  strictVersion: ${shareItem.shareConfig.strictVersion},
@@ -1219,7 +1416,7 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1219
1416
  ? ((await providerModulePromise)?.default ?? await providerModulePromise)
1220
1417
  : ${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory)))` : `${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory))`}
1221
1418
  ${exportLine}
1222
- `);
1419
+ `, true);
1223
1420
  }
1224
1421
  //#endregion
1225
1422
  //#region src/virtualModules/virtualRemoteEntry.ts
@@ -1253,7 +1450,7 @@ function generateLocalSharedImportMap() {
1253
1450
  return `
1254
1451
  ${JSON.stringify(pkg)}: async () => {
1255
1452
  ${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");
1256
- return pkg;` : `let pkg = await import("${getPreBuildLibImportId(pkg)}");
1453
+ return pkg;` : `let pkg = await import(${JSON.stringify(getSharedImportSource(pkg, shareItem))});
1257
1454
  return pkg;`}
1258
1455
  }
1259
1456
  `;
@@ -1535,6 +1732,18 @@ const processModuleAssets = (bundle, filesMap, moduleMatcher) => {
1535
1732
  }
1536
1733
  };
1537
1734
  /**
1735
+ * Adds global CSS assets to all module exports
1736
+ * @param filesMap - The preload map to update
1737
+ * @param cssAssets - Set of CSS asset filenames to add
1738
+ */
1739
+ const addCssAssetsToAllExports = (filesMap, cssAssets) => {
1740
+ Object.keys(filesMap).forEach((key) => {
1741
+ cssAssets.forEach((cssAsset) => {
1742
+ trackAsset(filesMap, key, cssAsset, false, "css");
1743
+ });
1744
+ });
1745
+ };
1746
+ /**
1538
1747
  * Deduplicates assets in the files map
1539
1748
  * @param filesMap - The preload map to deduplicate
1540
1749
  * @returns New deduplicated preload map
@@ -1860,12 +2069,13 @@ function pluginModuleParseEnd_default(excludeFn, options) {
1860
2069
  //#region src/plugins/pluginProxyRemoteEntry.ts
1861
2070
  const filter = (0, _rollup_pluginutils.createFilter)();
1862
2071
  function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposesId }) {
1863
- let viteConfig, _command;
2072
+ let viteConfig, _command, root;
1864
2073
  return {
1865
2074
  name: "proxyRemoteEntry",
1866
2075
  enforce: "post",
1867
2076
  configResolved(config) {
1868
2077
  viteConfig = config;
2078
+ root = config.root;
1869
2079
  },
1870
2080
  config(config, { command }) {
1871
2081
  _command = command;
@@ -1920,6 +2130,51 @@ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposes
1920
2130
  return code;
1921
2131
  }
1922
2132
  })());
2133
+ },
2134
+ generateBundle(_, bundle) {
2135
+ if (_command !== "build") return;
2136
+ const filesMap = {};
2137
+ const exposeEntries = Object.entries(options.exposes);
2138
+ const allCssAssets = options.bundleAllCSS ? collectCssAssets(bundle) : /* @__PURE__ */ new Set();
2139
+ processModuleAssets(bundle, filesMap, (modulePath) => {
2140
+ const absoluteModulePath = pathe.resolve(root, modulePath);
2141
+ return exposeEntries.find(([_, exposeOptions]) => {
2142
+ const exposePath = pathe.resolve(root, exposeOptions.import);
2143
+ if (absoluteModulePath === exposePath) return true;
2144
+ const stripKnownJsExt = (filePath) => {
2145
+ const ext = pathe.extname(filePath);
2146
+ return [
2147
+ ".ts",
2148
+ ".tsx",
2149
+ ".jsx",
2150
+ ".mjs",
2151
+ ".cjs"
2152
+ ].includes(ext) ? pathe.join(pathe.dirname(filePath), pathe.basename(filePath, ext)) : filePath;
2153
+ };
2154
+ return stripKnownJsExt(absoluteModulePath) === stripKnownJsExt(exposePath);
2155
+ })?.[1].import;
2156
+ });
2157
+ if (options.bundleAllCSS) addCssAssetsToAllExports(filesMap, allCssAssets);
2158
+ const ensureRelativeImportPath = (fromFile, toFile) => {
2159
+ let relativePath = pathe.relative(pathe.dirname(fromFile), toFile);
2160
+ if (!relativePath.startsWith(".")) relativePath = `./${relativePath}`;
2161
+ return relativePath;
2162
+ };
2163
+ const placeholderValue = getExposesCssMapPlaceholder();
2164
+ const placeholderPatterns = [
2165
+ JSON.stringify(placeholderValue),
2166
+ `'${placeholderValue}'`,
2167
+ `\`${placeholderValue}\``
2168
+ ];
2169
+ for (const file of Object.values(bundle)) {
2170
+ if (file.type !== "chunk" || !file.code.includes(placeholderValue)) continue;
2171
+ const cssAssetMap = exposeEntries.reduce((acc, [exposeKey, expose]) => {
2172
+ const assets = filesMap[expose.import] || createEmptyAssetMap();
2173
+ acc[exposeKey] = [...assets.css.sync, ...assets.css.async].map((cssAsset) => ensureRelativeImportPath(file.fileName, cssAsset));
2174
+ return acc;
2175
+ }, {});
2176
+ for (const placeholderPattern of placeholderPatterns) file.code = file.code.replace(placeholderPattern, JSON.stringify(cssAssetMap));
2177
+ }
1923
2178
  }
1924
2179
  };
1925
2180
  }
@@ -1982,6 +2237,9 @@ var PromiseStore = class {
1982
2237
  };
1983
2238
  //#endregion
1984
2239
  //#region src/plugins/pluginProxySharedModule_preBuild.ts
2240
+ function getPrebuildResolutionSource(pkgName, shareItem) {
2241
+ return getConcreteSharedImportSource(pkgName, shareItem) || pkgName;
2242
+ }
1985
2243
  function proxySharedModule(options) {
1986
2244
  const { shared = {} } = options;
1987
2245
  let _config;
@@ -2020,7 +2278,7 @@ function proxySharedModule(options) {
2020
2278
  if (key.endsWith("/") && source !== key.slice(0, -1)) return;
2021
2279
  const loadSharePath = getLoadShareModulePath(source, isRolldown, command);
2022
2280
  writeLoadShareModule(source, shared[key], command, isRolldown);
2023
- writePreBuildLibPath(source);
2281
+ writePreBuildLibPath(source, shared[key]);
2024
2282
  addUsedShares(source);
2025
2283
  writeLocalSharedImportMap();
2026
2284
  return this.resolve(loadSharePath, importer);
@@ -2031,14 +2289,18 @@ function proxySharedModule(options) {
2031
2289
  return command === "build" ? {
2032
2290
  find: new RegExp(`(.*${PREBUILD_TAG}.*)`),
2033
2291
  replacement: function($1) {
2034
- return assertModuleFound(PREBUILD_TAG, $1).name;
2292
+ const pkgName = assertModuleFound(PREBUILD_TAG, $1).name;
2293
+ return getPrebuildResolutionSource(pkgName, getPreBuildShareItem(pkgName));
2035
2294
  }
2036
2295
  } : {
2037
2296
  find: new RegExp(`(.*${PREBUILD_TAG}.*)`),
2038
2297
  replacement: "$1",
2039
2298
  async customResolver(source, importer) {
2040
2299
  const pkgName = assertModuleFound(PREBUILD_TAG, source).name;
2041
- const result = await this.resolve(pkgName, importer).then((item) => item.id);
2300
+ const importSource = getPrebuildResolutionSource(pkgName, getPreBuildShareItem(pkgName));
2301
+ const resolved = await this.resolve(importSource, importer);
2302
+ if (!resolved?.id) return;
2303
+ const result = resolved.id;
2042
2304
  if (_config && !result.includes(_config.cacheDir)) savePrebuild.set(pkgName, Promise.resolve(result));
2043
2305
  return await this.resolve(await savePrebuild.get(pkgName), importer);
2044
2306
  }
@@ -2055,7 +2317,7 @@ function proxySharedModule(options) {
2055
2317
  return;
2056
2318
  }
2057
2319
  writeLoadShareModule(key, shared[key], _command, isRolldown);
2058
- writePreBuildLibPath(key);
2320
+ writePreBuildLibPath(key, shared[key]);
2059
2321
  addUsedShares(key);
2060
2322
  });
2061
2323
  writeLocalSharedImportMap();
@@ -2177,14 +2439,19 @@ function stripEmptyPreloadCalls(code) {
2177
2439
  while (cursor < nextCode.length) {
2178
2440
  const char = nextCode[cursor];
2179
2441
  if (char === "(") depth++;
2180
- else if (char === ")") depth--;
2181
- else if (depth === 0 && nextCode.startsWith(",[],import.meta.url)", cursor)) {
2442
+ else if (char === ")") {
2443
+ depth--;
2444
+ if (depth < 0) break;
2445
+ } else if (depth === 0 && nextCode.startsWith(",[],import.meta.url)", cursor)) {
2182
2446
  replacementEnd = cursor;
2183
2447
  break;
2184
2448
  }
2185
2449
  cursor++;
2186
2450
  }
2187
- if (replacementEnd === -1) break;
2451
+ if (replacementEnd === -1) {
2452
+ start = nextCode.indexOf(marker, start + marker.length);
2453
+ continue;
2454
+ }
2188
2455
  const expression = nextCode.slice(exprStart, replacementEnd);
2189
2456
  nextCode = nextCode.slice(0, start) + expression + nextCode.slice(replacementEnd + 20);
2190
2457
  start = nextCode.indexOf(marker, start + expression.length);
@@ -2261,13 +2528,14 @@ function createEarlyVirtualModulesPlugin(options) {
2261
2528
  VirtualModule.setRoot(root);
2262
2529
  VirtualModule.ensureVirtualPackageExists();
2263
2530
  initVirtualModules(_command, getRemoteEntryId(options));
2264
- if (_command !== "serve") return;
2265
2531
  const isRolldown = getIsRolldown(this);
2266
2532
  if (remotes && Object.keys(remotes).length > 0) for (const key of Object.keys(remotes)) addUsedRemote(key, key);
2267
2533
  if (shared && Object.keys(shared).length > 0) {
2268
- config.optimizeDeps = config.optimizeDeps || {};
2269
- config.optimizeDeps.include = config.optimizeDeps.include || [];
2270
- config.optimizeDeps.include.push(virtualRuntimeInitStatus.getImportId());
2534
+ if (_command === "serve") {
2535
+ config.optimizeDeps = config.optimizeDeps || {};
2536
+ config.optimizeDeps.include = config.optimizeDeps.include || [];
2537
+ config.optimizeDeps.include.push(virtualRuntimeInitStatus.getImportId());
2538
+ }
2271
2539
  for (const key of Object.keys(shared)) {
2272
2540
  if (key.endsWith("/")) continue;
2273
2541
  const shareItem = shared[key];
@@ -2277,10 +2545,12 @@ function createEarlyVirtualModulesPlugin(options) {
2277
2545
  }
2278
2546
  getLoadShareModulePath(key, isRolldown);
2279
2547
  writeLoadShareModule(key, shareItem, _command, isRolldown);
2280
- writePreBuildLibPath(key);
2548
+ writePreBuildLibPath(key, shareItem);
2281
2549
  addUsedShares(key);
2282
- config.optimizeDeps.include.push(getLoadShareImportId(key, isRolldown, _command));
2283
- config.optimizeDeps.include.push(getPreBuildLibImportId(key));
2550
+ if (_command === "serve") {
2551
+ if (!isRolldown) config.optimizeDeps.include.push(getLoadShareImportId(key, isRolldown, _command));
2552
+ config.optimizeDeps.include.push(getPreBuildLibImportId(key));
2553
+ }
2284
2554
  }
2285
2555
  writeLocalSharedImportMap();
2286
2556
  }
package/lib/index.mjs CHANGED
@@ -27,35 +27,25 @@ async function mapCodeToCodeWithSourcemap(code) {
27
27
  //#endregion
28
28
  //#region src/utils/htmlEntryUtils.ts
29
29
  function sanitizeDevEntryPath(devEntryPath) {
30
- return devEntryPath.replace(/^[^:]+:([/\\])[/\\]?/, "$1").replace(/\\\\?/g, "/");
30
+ return devEntryPath.replace(/\\\\?/g, "/");
31
31
  }
32
32
  /**
33
- * Inlines the federation init import into existing module script tags to fix
34
- * the race condition (#396) where separate `<script type="module">` tags
35
- * don't guarantee execution order with top-level await.
36
- *
37
- * If no entry scripts are found, falls back to injecting a separate script tag.
38
- *
39
- * @example
40
- * // Before (two separate scripts, race condition):
41
- * // <script type="module" src="/__mf__virtual/hostAutoInit.js"><\/script>
42
- * // <script type="module" src="/src/main.js"><\/script>
43
- * // After (single inline script, sequential execution):
44
- * // <script type="module">await import("/__mf__virtual/hostAutoInit.js");await import("/src/main.js");<\/script>
33
+ * Rewrites entry module script tags to point at an external wrapper module.
34
+ * The wrapper can then sequence federation init before the app entry without
35
+ * relying on CSP-breaking inline `<script type="module">`.
45
36
  */
46
- function inlineEntryScripts(html, initSrc) {
47
- const src = sanitizeDevEntryPath(initSrc);
48
- const scriptTagRegex = /<script\s+([^>]*\btype=["']module["'][^>]*\bsrc=["'][^"']+["'][^>]*)>/gi;
49
- let hasEntry = false;
50
- const result = html.replace(scriptTagRegex, (match, attrs) => {
37
+ function rewriteEntryScripts(html, createProxySrc) {
38
+ return html.replace(/<script\s+([^>]*\btype=["']module["'][^>]*\bsrc=["'][^"']+["'][^>]*)>/gi, (match, attrs) => {
51
39
  const srcMatch = attrs.match(/\bsrc=["']([^"']+)["']/i);
52
40
  if (!srcMatch) return match;
53
41
  const originalSrc = srcMatch[1];
54
42
  if (originalSrc.includes("@vite/client")) return match;
55
- hasEntry = true;
56
- return `<script ${attrs.replace(/\s*\bsrc=["'][^"']+["']/i, "")}>await import(${JSON.stringify(src)});await import(${JSON.stringify(originalSrc)});`;
43
+ const proxySrc = createProxySrc(originalSrc);
44
+ return match.replace(srcMatch[0], `src=${JSON.stringify(proxySrc)}`);
57
45
  });
58
- if (hasEntry) return result;
46
+ }
47
+ function injectEntryScript(html, initSrc) {
48
+ const src = sanitizeDevEntryPath(initSrc);
59
49
  return html.replace("<head>", `<head><script type="module" src=${JSON.stringify(src)}><\/script>`);
60
50
  }
61
51
  //#endregion
@@ -100,6 +90,9 @@ function getDependencyCacheKey(cwd, dependencyName) {
100
90
  function setPackageDetectionCwd(cwd) {
101
91
  packageDetectionCwd = cwd;
102
92
  }
93
+ function getPackageDetectionCwd() {
94
+ return packageDetectionCwd || process.cwd();
95
+ }
103
96
  /**
104
97
  * Escaping rules:
105
98
  * Convert using the format __${mapping}__, where _ and $ are not allowed in npm package names but can be used in variable names.
@@ -167,6 +160,7 @@ function getFirstHtmlEntryFile(entryFiles) {
167
160
  return entryFiles.find((file) => file.endsWith(".html"));
168
161
  }
169
162
  const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
163
+ const DEV_HTML_PROXY_PREFIX = "virtual:mf-html-entry-proxy?";
170
164
  const getEntryPath = () => typeof entryPath === "function" ? entryPath() : entryPath;
171
165
  let devEntryPath = "";
172
166
  let entryFiles = [];
@@ -190,8 +184,13 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
190
184
  configResolved(config) {
191
185
  viteConfig = config;
192
186
  const resolvedEntryPath = getEntryPath();
193
- devEntryPath = resolvedEntryPath.startsWith("virtual:mf") ? "@id/" + resolvedEntryPath : resolvedEntryPath;
194
- devEntryPath = config.base + devEntryPath.replace(/\\\\?/g, "/").replace(/^[^:]+:([/\\])[/\\]?/, "$1").replace(/^\//, "");
187
+ if (resolvedEntryPath.startsWith("virtual:mf")) devEntryPath = config.base + "@id/" + resolvedEntryPath;
188
+ else {
189
+ const normalized = resolvedEntryPath.replace(/\\\\?/g, "/");
190
+ const root = config.root.replace(/\\\\?/g, "/").replace(/\/$/, "");
191
+ const relativePath = normalized.startsWith(root + "/") ? normalized.slice(root.length) : "/" + normalized.replace(/^[A-Za-z]:[\\/]/, "");
192
+ devEntryPath = config.base + relativePath.replace(/^\//, "");
193
+ }
195
194
  },
196
195
  configureServer(server) {
197
196
  server.middlewares.use((req, res, next) => {
@@ -206,7 +205,29 @@ const addEntry = ({ entryName, entryPath, fileName, inject = "entry" }) => {
206
205
  transformIndexHtml(c) {
207
206
  if (!injectHtml()) return;
208
207
  clientInjected = true;
209
- return inlineEntryScripts(c, devEntryPath);
208
+ const html = rewriteEntryScripts(c, (originalSrc) => {
209
+ const query = new URLSearchParams({
210
+ init: sanitizeDevEntryPath(devEntryPath),
211
+ entry: originalSrc
212
+ }).toString();
213
+ return `${viteConfig.base}@id/${DEV_HTML_PROXY_PREFIX}${query}`;
214
+ });
215
+ return html === c ? injectEntryScript(c, devEntryPath) : html;
216
+ },
217
+ resolveId(id) {
218
+ if (id.startsWith(DEV_HTML_PROXY_PREFIX)) return id;
219
+ },
220
+ load(id) {
221
+ if (!id.startsWith(DEV_HTML_PROXY_PREFIX)) return;
222
+ const params = new URLSearchParams(id.slice(28));
223
+ const initSrc = params.get("init");
224
+ const entrySrc = params.get("entry");
225
+ if (!initSrc || !entrySrc) return;
226
+ return `
227
+ const baseUrl = document.baseURI || window.location.href;
228
+ await import(new URL(${JSON.stringify(initSrc)}, baseUrl).href);
229
+ await import(new URL(${JSON.stringify(entrySrc)}, baseUrl).href);
230
+ `;
210
231
  },
211
232
  transform(code, id) {
212
233
  if (id.includes("node_modules") || inject !== "html" || htmlFilePath) return;
@@ -972,15 +993,61 @@ var VirtualModule = class {
972
993
  };
973
994
  //#endregion
974
995
  //#region src/virtualModules/virtualExposes.ts
996
+ const EXPOSES_CSS_MAP_PLACEHOLDER = "__MF_EXPOSES_CSS_MAP__";
997
+ function getExposesCssMapPlaceholder() {
998
+ return EXPOSES_CSS_MAP_PLACEHOLDER;
999
+ }
975
1000
  function getVirtualExposesId(options) {
976
1001
  return `virtual:mf-exposes:${`${options.name}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
977
1002
  }
978
1003
  function generateExposes(options) {
979
1004
  return `
1005
+ const cssAssetMap = ${JSON.stringify(options.bundleAllCSS ? EXPOSES_CSS_MAP_PLACEHOLDER : {})};
1006
+ const injectedCssHrefs = new Set();
1007
+
1008
+ async function injectCssAssets(exposeKey) {
1009
+ if (typeof document === "undefined") {
1010
+ return;
1011
+ }
1012
+
1013
+ // Replaced at build time with expose -> css asset paths.
1014
+ const cssAssets = cssAssetMap[exposeKey] || [];
1015
+
1016
+ await Promise.all(
1017
+ cssAssets.map((cssAsset) => {
1018
+ const href = new URL(cssAsset, import.meta.url).href;
1019
+
1020
+ // Same expose can be resolved multiple times in one page.
1021
+ if (injectedCssHrefs.has(href)) {
1022
+ return Promise.resolve();
1023
+ }
1024
+ injectedCssHrefs.add(href);
1025
+
1026
+ const existingLink = document.querySelector(
1027
+ \`link[rel="stylesheet"][data-mf-href="\${href}"]\`
1028
+ );
1029
+ if (existingLink) {
1030
+ return Promise.resolve();
1031
+ }
1032
+
1033
+ return new Promise((resolve, reject) => {
1034
+ const link = document.createElement("link");
1035
+ link.rel = "stylesheet";
1036
+ link.href = href;
1037
+ link.setAttribute("data-mf-href", href);
1038
+ link.onload = () => resolve();
1039
+ link.onerror = () => reject(new Error(\`[Module Federation] Failed to load CSS asset: \${href}\`));
1040
+ document.head.appendChild(link);
1041
+ });
1042
+ })
1043
+ );
1044
+ }
1045
+
980
1046
  export default {
981
1047
  ${Object.keys(options.exposes).map((key) => {
982
1048
  return `
983
1049
  ${JSON.stringify(key)}: async () => {
1050
+ await injectCssAssets(${JSON.stringify(key)})
984
1051
  const importModule = await import(${JSON.stringify(options.exposes[key].import)})
985
1052
  const exportModule = {}
986
1053
  Object.assign(exportModule, importModule)
@@ -1127,32 +1194,159 @@ function generateRemotes(id, command, isRolldown) {
1127
1194
  * 1. __prebuild__: export shareModule (pre-built source code of modules such as vue, react, etc.)
1128
1195
  * 2. __loadShare__: load shareModule (mfRuntime.loadShare('vue'))
1129
1196
  */
1197
+ function escapeGeneratedStringLiteral(value) {
1198
+ return JSON.stringify(value).replace(/[<>\u2028\u2029]/g, (char) => {
1199
+ switch (char) {
1200
+ case "<": return "\\u003C";
1201
+ case ">": return "\\u003E";
1202
+ case "\u2028": return "\\u2028";
1203
+ case "\u2029": return "\\u2029";
1204
+ default: return char;
1205
+ }
1206
+ });
1207
+ }
1208
+ const localRequire = createRequire$1(import.meta.url);
1209
+ function resolvePackageEntryFromProjectRoot(pkg) {
1210
+ try {
1211
+ return createRequire$1(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`)).resolve(pkg);
1212
+ } catch {
1213
+ return;
1214
+ }
1215
+ }
1216
+ function getInstalledPackageJsonPath(pkg) {
1217
+ try {
1218
+ const packageName = removePathFromNpmPackage(pkg);
1219
+ const projectRequire = createRequire$1(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`));
1220
+ let resolvedPath;
1221
+ try {
1222
+ resolvedPath = projectRequire.resolve(pkg);
1223
+ } catch {
1224
+ resolvedPath = projectRequire.resolve(packageName);
1225
+ }
1226
+ let currentDir = path.dirname(resolvedPath);
1227
+ const rootDir = path.parse(currentDir).root;
1228
+ while (currentDir !== rootDir) {
1229
+ const packageJsonPath = path.join(currentDir, "package.json");
1230
+ if (existsSync(packageJsonPath)) {
1231
+ if (JSON.parse(readFileSync(packageJsonPath, "utf-8")).name === packageName) return packageJsonPath;
1232
+ }
1233
+ currentDir = path.dirname(currentDir);
1234
+ }
1235
+ const rootPackageJsonPath = path.join(rootDir, "package.json");
1236
+ if (existsSync(rootPackageJsonPath)) {
1237
+ if (JSON.parse(readFileSync(rootPackageJsonPath, "utf-8")).name === packageName) return rootPackageJsonPath;
1238
+ }
1239
+ } catch {
1240
+ const packageName = removePathFromNpmPackage(pkg);
1241
+ let currentDir = getPackageDetectionCwd();
1242
+ const rootDir = path.parse(currentDir).root;
1243
+ while (currentDir !== rootDir) {
1244
+ const packageJsonPath = path.join(currentDir, "node_modules", packageName, "package.json");
1245
+ if (existsSync(packageJsonPath)) return packageJsonPath;
1246
+ currentDir = path.dirname(currentDir);
1247
+ }
1248
+ const rootPackageJsonPath = path.join(rootDir, "node_modules", packageName, "package.json");
1249
+ return existsSync(rootPackageJsonPath) ? rootPackageJsonPath : void 0;
1250
+ }
1251
+ }
1252
+ function resolveImportTarget(exportsField) {
1253
+ if (typeof exportsField === "string") return exportsField;
1254
+ if (!exportsField || typeof exportsField !== "object") return void 0;
1255
+ const record = exportsField;
1256
+ for (const condition of [
1257
+ "import",
1258
+ "module",
1259
+ "default"
1260
+ ]) {
1261
+ const target = resolveImportTarget(record[condition]);
1262
+ if (target) return target;
1263
+ }
1264
+ for (const target of Object.values(record)) {
1265
+ const resolved = resolveImportTarget(target);
1266
+ if (resolved) return resolved;
1267
+ }
1268
+ }
1269
+ function getPackageEsmEntryPath(pkg) {
1270
+ try {
1271
+ const resolvedEntryPath = resolvePackageEntryFromProjectRoot(pkg);
1272
+ const packageJsonPath = getInstalledPackageJsonPath(pkg);
1273
+ if (!packageJsonPath) return resolvedEntryPath;
1274
+ const packageName = removePathFromNpmPackage(pkg);
1275
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
1276
+ const subpath = pkg === packageName ? "." : `.${pkg.slice(packageName.length)}`;
1277
+ const target = resolveImportTarget(typeof packageJson.exports === "string" ? subpath === "." ? packageJson.exports : void 0 : packageJson.exports?.[subpath] ?? (subpath === "." ? packageJson.exports?.["."] ?? (packageJson.exports && !Object.keys(packageJson.exports).some((key) => key.startsWith(".")) ? packageJson.exports : void 0) : void 0)) || packageJson.module;
1278
+ if (!target) return resolvedEntryPath;
1279
+ return path.resolve(path.dirname(packageJsonPath), target);
1280
+ } catch {
1281
+ return resolvePackageEntryFromProjectRoot(pkg);
1282
+ }
1283
+ }
1284
+ function getEsmNamedExports(pkg) {
1285
+ try {
1286
+ const entryPath = getPackageEsmEntryPath(pkg);
1287
+ if (!entryPath) return [];
1288
+ const { initSync, parse } = localRequire("es-module-lexer");
1289
+ initSync();
1290
+ const [, exports] = parse(readFileSync(entryPath, "utf-8"), entryPath);
1291
+ return exports.map((item) => item.n).filter((name) => !!name && name !== "default" && name !== "__esModule" && /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name));
1292
+ } catch {
1293
+ return [];
1294
+ }
1295
+ }
1130
1296
  function getPackageNamedExports(pkg) {
1131
1297
  try {
1132
- const mod = createRequire$1(new URL("file://" + process.cwd() + "/package.json"))(pkg);
1298
+ const mod = createRequire$1(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`))(pkg);
1133
1299
  return Object.keys(mod).filter((k) => k !== "default" && k !== "__esModule" && /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(k));
1134
1300
  } catch {
1135
- return [];
1301
+ return getEsmNamedExports(pkg);
1136
1302
  }
1137
1303
  }
1138
1304
  function getLocalProviderImportPath(pkg) {
1139
1305
  try {
1140
- const resolved = createRequire$1(new URL("file://" + process.cwd() + "/package.json")).resolve(pkg);
1306
+ const resolved = createRequire$1(new URL(`file://${path.join(getPackageDetectionCwd(), "package.json")}`)).resolve(pkg);
1141
1307
  return resolved.includes("/node_modules/") || resolved.includes("\\node_modules\\") ? void 0 : resolved;
1142
1308
  } catch {
1143
1309
  return;
1144
1310
  }
1145
1311
  }
1312
+ function tryResolveImportFromPackageRoot(pkg, root) {
1313
+ try {
1314
+ return createRequire$1(new URL(`file://${path.join(root, "package.json")}`)).resolve(pkg);
1315
+ } catch {
1316
+ return;
1317
+ }
1318
+ }
1319
+ function getConcreteSharedImportSource(pkg, shareItem) {
1320
+ const configuredImport = shareItem?.shareConfig.import;
1321
+ if (typeof configuredImport === "string") return configuredImport;
1322
+ const projectRoot = getPackageDetectionCwd();
1323
+ if (tryResolveImportFromPackageRoot(pkg, projectRoot)) return;
1324
+ let currentDir = path.dirname(projectRoot);
1325
+ while (currentDir !== path.dirname(currentDir)) {
1326
+ const resolved = tryResolveImportFromPackageRoot(pkg, currentDir);
1327
+ if (resolved) return resolved;
1328
+ currentDir = path.dirname(currentDir);
1329
+ }
1330
+ return tryResolveImportFromPackageRoot(pkg, currentDir);
1331
+ }
1146
1332
  const preBuildCacheMap = {};
1333
+ const preBuildShareItemMap = {};
1147
1334
  const PREBUILD_TAG = "__prebuild__";
1148
- function writePreBuildLibPath(pkg) {
1335
+ function writePreBuildLibPath(pkg, shareItem) {
1149
1336
  if (!preBuildCacheMap[pkg]) preBuildCacheMap[pkg] = new VirtualModule(pkg, PREBUILD_TAG);
1150
- preBuildCacheMap[pkg].writeSync("");
1337
+ preBuildShareItemMap[pkg] = shareItem;
1338
+ preBuildCacheMap[pkg].writeSync("", true);
1151
1339
  }
1152
1340
  function getPreBuildLibImportId(pkg) {
1153
1341
  if (!preBuildCacheMap[pkg]) preBuildCacheMap[pkg] = new VirtualModule(pkg, PREBUILD_TAG);
1154
1342
  return preBuildCacheMap[pkg].getImportId();
1155
1343
  }
1344
+ function getPreBuildShareItem(pkg) {
1345
+ return preBuildShareItemMap[pkg];
1346
+ }
1347
+ function getSharedImportSource(pkg, shareItem) {
1348
+ return getConcreteSharedImportSource(pkg, shareItem) || getPreBuildLibImportId(pkg);
1349
+ }
1156
1350
  const LOAD_SHARE_TAG = "__loadShare__";
1157
1351
  const loadShareCacheMap = {};
1158
1352
  function getLoadShareImportId(pkg, isRolldown, command) {
@@ -1170,22 +1364,25 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1170
1364
  const { initPromise } = globalThis[globalKey];` : `const {initPromise} = require("${virtualRuntimeInitStatus.getImportId()}")`;
1171
1365
  const awaitOrPlaceholder = useESM ? "await " : "/*mf top-level-await placeholder replacement mf*/";
1172
1366
  const useSsrProviderFallback = hasPackageDependency("vinext") && command === "build" && pkg === "react";
1173
- const providerImportId = getLocalProviderImportPath(pkg) || getPreBuildLibImportId(pkg);
1367
+ const concreteSharedImportSource = getConcreteSharedImportSource(pkg, shareItem);
1368
+ const sharedImportSource = concreteSharedImportSource || getPreBuildLibImportId(pkg);
1369
+ const devImportSource = concreteSharedImportSource || pkg;
1370
+ const providerImportId = getLocalProviderImportPath(pkg) || concreteSharedImportSource || sharedImportSource;
1174
1371
  const namedExports = getPackageNamedExports(pkg);
1175
1372
  let exportLine;
1176
1373
  if (namedExports.length > 0) {
1177
1374
  const destructure = `const { ${namedExports.map((name, i) => `${name}: __mf_${i}`).join(", ")} } = exportModule;`;
1178
1375
  const namedExportLine = `export { ${namedExports.map((name, i) => `__mf_${i} as ${name}`).join(", ")} };`;
1179
1376
  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";
1377
+ } else exportLine = useESM ? `export default exportModule.default ?? exportModule\n export * from ${escapeGeneratedStringLiteral(sharedImportSource)}` : "module.exports = exportModule";
1181
1378
  loadShareCacheMap[pkg].writeSync(`
1182
- import ${JSON.stringify(getPreBuildLibImportId(pkg))};
1183
- ${command !== "build" ? `;() => import(${JSON.stringify(pkg)}).catch(() => {});` : ""}
1379
+ import ${escapeGeneratedStringLiteral(sharedImportSource)};
1380
+ ${command !== "build" ? `;() => import(${escapeGeneratedStringLiteral(devImportSource)}).catch(() => {});` : ""}
1184
1381
  ${importLine}
1185
1382
  ${useSsrProviderFallback ? `const providerModulePromise = typeof window === "undefined"
1186
- ? import(${JSON.stringify(providerImportId)})
1383
+ ? import(${escapeGeneratedStringLiteral(providerImportId)})
1187
1384
  : undefined` : ""}
1188
- const res = initPromise.then(runtime => runtime.loadShare(${JSON.stringify(pkg)}, {
1385
+ const res = initPromise.then(runtime => runtime.loadShare(${escapeGeneratedStringLiteral(pkg)}, {
1189
1386
  customShareInfo: {shareConfig:{
1190
1387
  singleton: ${shareItem.shareConfig.singleton},
1191
1388
  strictVersion: ${shareItem.shareConfig.strictVersion},
@@ -1196,7 +1393,7 @@ function writeLoadShareModule(pkg, shareItem, command, isRolldown) {
1196
1393
  ? ((await providerModulePromise)?.default ?? await providerModulePromise)
1197
1394
  : ${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory)))` : `${awaitOrPlaceholder}res.then((factory) => (typeof factory === "function" ? factory() : factory))`}
1198
1395
  ${exportLine}
1199
- `);
1396
+ `, true);
1200
1397
  }
1201
1398
  //#endregion
1202
1399
  //#region src/virtualModules/virtualRemoteEntry.ts
@@ -1230,7 +1427,7 @@ function generateLocalSharedImportMap() {
1230
1427
  return `
1231
1428
  ${JSON.stringify(pkg)}: async () => {
1232
1429
  ${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");
1233
- return pkg;` : `let pkg = await import("${getPreBuildLibImportId(pkg)}");
1430
+ return pkg;` : `let pkg = await import(${JSON.stringify(getSharedImportSource(pkg, shareItem))});
1234
1431
  return pkg;`}
1235
1432
  }
1236
1433
  `;
@@ -1512,6 +1709,18 @@ const processModuleAssets = (bundle, filesMap, moduleMatcher) => {
1512
1709
  }
1513
1710
  };
1514
1711
  /**
1712
+ * Adds global CSS assets to all module exports
1713
+ * @param filesMap - The preload map to update
1714
+ * @param cssAssets - Set of CSS asset filenames to add
1715
+ */
1716
+ const addCssAssetsToAllExports = (filesMap, cssAssets) => {
1717
+ Object.keys(filesMap).forEach((key) => {
1718
+ cssAssets.forEach((cssAsset) => {
1719
+ trackAsset(filesMap, key, cssAsset, false, "css");
1720
+ });
1721
+ });
1722
+ };
1723
+ /**
1515
1724
  * Deduplicates assets in the files map
1516
1725
  * @param filesMap - The preload map to deduplicate
1517
1726
  * @returns New deduplicated preload map
@@ -1837,12 +2046,13 @@ function pluginModuleParseEnd_default(excludeFn, options) {
1837
2046
  //#region src/plugins/pluginProxyRemoteEntry.ts
1838
2047
  const filter = createFilter();
1839
2048
  function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposesId }) {
1840
- let viteConfig, _command;
2049
+ let viteConfig, _command, root;
1841
2050
  return {
1842
2051
  name: "proxyRemoteEntry",
1843
2052
  enforce: "post",
1844
2053
  configResolved(config) {
1845
2054
  viteConfig = config;
2055
+ root = config.root;
1846
2056
  },
1847
2057
  config(config, { command }) {
1848
2058
  _command = command;
@@ -1897,6 +2107,51 @@ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposes
1897
2107
  return code;
1898
2108
  }
1899
2109
  })());
2110
+ },
2111
+ generateBundle(_, bundle) {
2112
+ if (_command !== "build") return;
2113
+ const filesMap = {};
2114
+ const exposeEntries = Object.entries(options.exposes);
2115
+ const allCssAssets = options.bundleAllCSS ? collectCssAssets(bundle) : /* @__PURE__ */ new Set();
2116
+ processModuleAssets(bundle, filesMap, (modulePath) => {
2117
+ const absoluteModulePath = path$1.resolve(root, modulePath);
2118
+ return exposeEntries.find(([_, exposeOptions]) => {
2119
+ const exposePath = path$1.resolve(root, exposeOptions.import);
2120
+ if (absoluteModulePath === exposePath) return true;
2121
+ const stripKnownJsExt = (filePath) => {
2122
+ const ext = path$1.extname(filePath);
2123
+ return [
2124
+ ".ts",
2125
+ ".tsx",
2126
+ ".jsx",
2127
+ ".mjs",
2128
+ ".cjs"
2129
+ ].includes(ext) ? path$1.join(path$1.dirname(filePath), path$1.basename(filePath, ext)) : filePath;
2130
+ };
2131
+ return stripKnownJsExt(absoluteModulePath) === stripKnownJsExt(exposePath);
2132
+ })?.[1].import;
2133
+ });
2134
+ if (options.bundleAllCSS) addCssAssetsToAllExports(filesMap, allCssAssets);
2135
+ const ensureRelativeImportPath = (fromFile, toFile) => {
2136
+ let relativePath = path$1.relative(path$1.dirname(fromFile), toFile);
2137
+ if (!relativePath.startsWith(".")) relativePath = `./${relativePath}`;
2138
+ return relativePath;
2139
+ };
2140
+ const placeholderValue = getExposesCssMapPlaceholder();
2141
+ const placeholderPatterns = [
2142
+ JSON.stringify(placeholderValue),
2143
+ `'${placeholderValue}'`,
2144
+ `\`${placeholderValue}\``
2145
+ ];
2146
+ for (const file of Object.values(bundle)) {
2147
+ if (file.type !== "chunk" || !file.code.includes(placeholderValue)) continue;
2148
+ const cssAssetMap = exposeEntries.reduce((acc, [exposeKey, expose]) => {
2149
+ const assets = filesMap[expose.import] || createEmptyAssetMap();
2150
+ acc[exposeKey] = [...assets.css.sync, ...assets.css.async].map((cssAsset) => ensureRelativeImportPath(file.fileName, cssAsset));
2151
+ return acc;
2152
+ }, {});
2153
+ for (const placeholderPattern of placeholderPatterns) file.code = file.code.replace(placeholderPattern, JSON.stringify(cssAssetMap));
2154
+ }
1900
2155
  }
1901
2156
  };
1902
2157
  }
@@ -1959,6 +2214,9 @@ var PromiseStore = class {
1959
2214
  };
1960
2215
  //#endregion
1961
2216
  //#region src/plugins/pluginProxySharedModule_preBuild.ts
2217
+ function getPrebuildResolutionSource(pkgName, shareItem) {
2218
+ return getConcreteSharedImportSource(pkgName, shareItem) || pkgName;
2219
+ }
1962
2220
  function proxySharedModule(options) {
1963
2221
  const { shared = {} } = options;
1964
2222
  let _config;
@@ -1997,7 +2255,7 @@ function proxySharedModule(options) {
1997
2255
  if (key.endsWith("/") && source !== key.slice(0, -1)) return;
1998
2256
  const loadSharePath = getLoadShareModulePath(source, isRolldown, command);
1999
2257
  writeLoadShareModule(source, shared[key], command, isRolldown);
2000
- writePreBuildLibPath(source);
2258
+ writePreBuildLibPath(source, shared[key]);
2001
2259
  addUsedShares(source);
2002
2260
  writeLocalSharedImportMap();
2003
2261
  return this.resolve(loadSharePath, importer);
@@ -2008,14 +2266,18 @@ function proxySharedModule(options) {
2008
2266
  return command === "build" ? {
2009
2267
  find: new RegExp(`(.*${PREBUILD_TAG}.*)`),
2010
2268
  replacement: function($1) {
2011
- return assertModuleFound(PREBUILD_TAG, $1).name;
2269
+ const pkgName = assertModuleFound(PREBUILD_TAG, $1).name;
2270
+ return getPrebuildResolutionSource(pkgName, getPreBuildShareItem(pkgName));
2012
2271
  }
2013
2272
  } : {
2014
2273
  find: new RegExp(`(.*${PREBUILD_TAG}.*)`),
2015
2274
  replacement: "$1",
2016
2275
  async customResolver(source, importer) {
2017
2276
  const pkgName = assertModuleFound(PREBUILD_TAG, source).name;
2018
- const result = await this.resolve(pkgName, importer).then((item) => item.id);
2277
+ const importSource = getPrebuildResolutionSource(pkgName, getPreBuildShareItem(pkgName));
2278
+ const resolved = await this.resolve(importSource, importer);
2279
+ if (!resolved?.id) return;
2280
+ const result = resolved.id;
2019
2281
  if (_config && !result.includes(_config.cacheDir)) savePrebuild.set(pkgName, Promise.resolve(result));
2020
2282
  return await this.resolve(await savePrebuild.get(pkgName), importer);
2021
2283
  }
@@ -2032,7 +2294,7 @@ function proxySharedModule(options) {
2032
2294
  return;
2033
2295
  }
2034
2296
  writeLoadShareModule(key, shared[key], _command, isRolldown);
2035
- writePreBuildLibPath(key);
2297
+ writePreBuildLibPath(key, shared[key]);
2036
2298
  addUsedShares(key);
2037
2299
  });
2038
2300
  writeLocalSharedImportMap();
@@ -2154,14 +2416,19 @@ function stripEmptyPreloadCalls(code) {
2154
2416
  while (cursor < nextCode.length) {
2155
2417
  const char = nextCode[cursor];
2156
2418
  if (char === "(") depth++;
2157
- else if (char === ")") depth--;
2158
- else if (depth === 0 && nextCode.startsWith(",[],import.meta.url)", cursor)) {
2419
+ else if (char === ")") {
2420
+ depth--;
2421
+ if (depth < 0) break;
2422
+ } else if (depth === 0 && nextCode.startsWith(",[],import.meta.url)", cursor)) {
2159
2423
  replacementEnd = cursor;
2160
2424
  break;
2161
2425
  }
2162
2426
  cursor++;
2163
2427
  }
2164
- if (replacementEnd === -1) break;
2428
+ if (replacementEnd === -1) {
2429
+ start = nextCode.indexOf(marker, start + marker.length);
2430
+ continue;
2431
+ }
2165
2432
  const expression = nextCode.slice(exprStart, replacementEnd);
2166
2433
  nextCode = nextCode.slice(0, start) + expression + nextCode.slice(replacementEnd + 20);
2167
2434
  start = nextCode.indexOf(marker, start + expression.length);
@@ -2238,13 +2505,14 @@ function createEarlyVirtualModulesPlugin(options) {
2238
2505
  VirtualModule.setRoot(root);
2239
2506
  VirtualModule.ensureVirtualPackageExists();
2240
2507
  initVirtualModules(_command, getRemoteEntryId(options));
2241
- if (_command !== "serve") return;
2242
2508
  const isRolldown = getIsRolldown(this);
2243
2509
  if (remotes && Object.keys(remotes).length > 0) for (const key of Object.keys(remotes)) addUsedRemote(key, key);
2244
2510
  if (shared && Object.keys(shared).length > 0) {
2245
- config.optimizeDeps = config.optimizeDeps || {};
2246
- config.optimizeDeps.include = config.optimizeDeps.include || [];
2247
- config.optimizeDeps.include.push(virtualRuntimeInitStatus.getImportId());
2511
+ if (_command === "serve") {
2512
+ config.optimizeDeps = config.optimizeDeps || {};
2513
+ config.optimizeDeps.include = config.optimizeDeps.include || [];
2514
+ config.optimizeDeps.include.push(virtualRuntimeInitStatus.getImportId());
2515
+ }
2248
2516
  for (const key of Object.keys(shared)) {
2249
2517
  if (key.endsWith("/")) continue;
2250
2518
  const shareItem = shared[key];
@@ -2254,10 +2522,12 @@ function createEarlyVirtualModulesPlugin(options) {
2254
2522
  }
2255
2523
  getLoadShareModulePath(key, isRolldown);
2256
2524
  writeLoadShareModule(key, shareItem, _command, isRolldown);
2257
- writePreBuildLibPath(key);
2525
+ writePreBuildLibPath(key, shareItem);
2258
2526
  addUsedShares(key);
2259
- config.optimizeDeps.include.push(getLoadShareImportId(key, isRolldown, _command));
2260
- config.optimizeDeps.include.push(getPreBuildLibImportId(key));
2527
+ if (_command === "serve") {
2528
+ if (!isRolldown) config.optimizeDeps.include.push(getLoadShareImportId(key, isRolldown, _command));
2529
+ config.optimizeDeps.include.push(getPreBuildLibImportId(key));
2530
+ }
2261
2531
  }
2262
2532
  writeLocalSharedImportMap();
2263
2533
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/vite",
3
- "version": "1.13.2",
3
+ "version": "1.13.3",
4
4
  "description": "Vite plugin for Module Federation",
5
5
  "type": "module",
6
6
  "main": "./lib/index.cjs",
@@ -72,6 +72,7 @@
72
72
  "@module-federation/sdk": "2.2.3",
73
73
  "@rollup/pluginutils": "^5.3.0",
74
74
  "defu": "^6.1.4",
75
+ "es-module-lexer": "^1.7.0",
75
76
  "estree-walker": "^3.0.3",
76
77
  "magic-string": "^0.30.21",
77
78
  "pathe": "^2.0.3"