@module-federation/vite 1.23.5 → 1.23.7

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.
@@ -39,6 +39,9 @@ function normalizePathForImport(path) {
39
39
  return path.replace(/\\/g, "/");
40
40
  }
41
41
  const EXTERNAL_URL_RE = /^(?:[a-z]+:|\/\/)/i;
42
+ function isHttpUrl(value) {
43
+ return value.startsWith("http://") || value.startsWith("https://");
44
+ }
42
45
  function isAbsoluteUrl(src) {
43
46
  if (/^[a-z]:[\\/]/i.test(src)) return false;
44
47
  return EXTERNAL_URL_RE.test(src);
@@ -205,28 +208,91 @@ const REGEX_PREFIX_KEYWORDS = /* @__PURE__ */ new Set([
205
208
  "void",
206
209
  "yield"
207
210
  ]);
211
+ const CONTROL_PAREN_KEYWORDS = /* @__PURE__ */ new Set([
212
+ "if",
213
+ "while",
214
+ "for",
215
+ "with",
216
+ "switch",
217
+ "catch"
218
+ ]);
219
+ function isWhitespace(char) {
220
+ if (char === void 0) return false;
221
+ const code = char.charCodeAt(0);
222
+ if (code < 128) return code === 32 || code >= 9 && code <= 13;
223
+ return /\s/.test(char);
224
+ }
225
+ function isAsciiLetter(code) {
226
+ return code >= 97 && code <= 122 || code >= 65 && code <= 90;
227
+ }
228
+ function isAsciiDigit(code) {
229
+ return code >= 48 && code <= 57;
230
+ }
231
+ function isIdentifierStart(char) {
232
+ const code = char.charCodeAt(0);
233
+ if (code < 128) return isAsciiLetter(code) || code === 36 || code === 95;
234
+ return /[$_\p{ID_Start}]/u.test(char);
235
+ }
236
+ function isIdentifierPart(char) {
237
+ if (char === void 0) return false;
238
+ const code = char.charCodeAt(0);
239
+ if (code < 128) return isAsciiLetter(code) || isAsciiDigit(code) || code === 36 || code === 95;
240
+ return /[$_\u200C\u200D\p{ID_Continue}]/u.test(char);
241
+ }
242
+ function isRegexFlag(char) {
243
+ if (char === void 0) return false;
244
+ const code = char.charCodeAt(0);
245
+ if (code < 128) return isAsciiLetter(code) || isAsciiDigit(code) || code === 36 || code === 95;
246
+ return /[$_\p{ID_Continue}]/u.test(char);
247
+ }
248
+ function isNumberPart(char) {
249
+ if (char === void 0) return false;
250
+ const code = char.charCodeAt(0);
251
+ return isAsciiLetter(code) || isAsciiDigit(code) || code === 95 || code === 46;
252
+ }
208
253
  function isJsxClosingTagSlash(code, slashIndex) {
209
254
  if (code[slashIndex - 1] !== "<") return false;
210
255
  let cursor = slashIndex + 1;
211
- while (/\s/.test(code[cursor] || "")) cursor++;
256
+ while (isWhitespace(code[cursor])) cursor++;
212
257
  if (code[cursor] === ">") return true;
213
258
  const tagStart = cursor;
214
259
  while (/[-:.$_\u200C\u200D\p{ID_Continue}]/u.test(code[cursor] || "")) cursor++;
215
260
  if (cursor === tagStart) return false;
216
- while (/\s/.test(code[cursor] || "")) cursor++;
261
+ while (isWhitespace(code[cursor])) cursor++;
217
262
  return code[cursor] === ">";
218
263
  }
219
264
  /** Mark comments, string/template literals, and regular expressions as non-code. */
220
265
  function createCodePositionMap(code, onComment) {
221
- const positions = Array(code.length).fill(true);
266
+ const positions = new Uint8Array(code.length).fill(1);
222
267
  const mask = (start, end) => {
223
- for (let index = start; index < end; index++) positions[index] = false;
268
+ positions.fill(0, start, end);
224
269
  };
225
270
  let canStartRegex = true;
271
+ let pendingControlParen;
272
+ const controlParens = [];
273
+ let templateStart;
274
+ const templateExpressions = [];
226
275
  for (let index = 0; index < code.length;) {
227
276
  const char = code[index];
228
277
  const next = code[index + 1];
229
- if (/\s/.test(char)) {
278
+ if (templateStart !== void 0) {
279
+ if (char === "\\") index += 2;
280
+ else if (char === "`") {
281
+ mask(templateStart, ++index);
282
+ templateStart = void 0;
283
+ canStartRegex = false;
284
+ } else if (char === "$" && next === "{") {
285
+ templateExpressions.push({
286
+ start: templateStart,
287
+ braceDepth: 0
288
+ });
289
+ templateStart = void 0;
290
+ canStartRegex = true;
291
+ index += 2;
292
+ } else index++;
293
+ continue;
294
+ }
295
+ if (isWhitespace(char)) {
230
296
  index++;
231
297
  continue;
232
298
  }
@@ -247,7 +313,13 @@ function createCodePositionMap(code, onComment) {
247
313
  onComment?.(start, index);
248
314
  continue;
249
315
  }
250
- if (char === "\"" || char === "'" || char === "`") {
316
+ const controlKeyword = pendingControlParen;
317
+ pendingControlParen = void 0;
318
+ if (char === "`") {
319
+ templateStart = index++;
320
+ continue;
321
+ }
322
+ if (char === "\"" || char === "'") {
251
323
  const quote = char;
252
324
  const start = index++;
253
325
  while (index < code.length) {
@@ -265,8 +337,7 @@ function createCodePositionMap(code, onComment) {
265
337
  canStartRegex = false;
266
338
  continue;
267
339
  }
268
- const closesJsxTag = isJsxClosingTagSlash(code, index);
269
- if (char === "/" && canStartRegex && !closesJsxTag) {
340
+ if (char === "/" && canStartRegex && !isJsxClosingTagSlash(code, index)) {
270
341
  const start = index;
271
342
  let cursor = index + 1;
272
343
  let escaped = false;
@@ -293,7 +364,7 @@ function createCodePositionMap(code, onComment) {
293
364
  }
294
365
  if (regexChar === "/" && !inCharacterClass) {
295
366
  cursor++;
296
- while (/[$_\p{ID_Continue}]/u.test(code[cursor] || "")) cursor++;
367
+ while (isRegexFlag(code[cursor])) cursor++;
297
368
  closed = true;
298
369
  break;
299
370
  }
@@ -305,15 +376,21 @@ function createCodePositionMap(code, onComment) {
305
376
  continue;
306
377
  }
307
378
  }
308
- if (/[$_\p{ID_Start}]/u.test(char)) {
379
+ if (isIdentifierStart(char)) {
309
380
  const start = index++;
310
- while (/[$_\u200C\u200D\p{ID_Continue}]/u.test(code[index] || "")) index++;
311
- canStartRegex = REGEX_PREFIX_KEYWORDS.has(code.slice(start, index));
381
+ while (isIdentifierPart(code[index])) index++;
382
+ const word = code.slice(start, index);
383
+ if (CONTROL_PAREN_KEYWORDS.has(word)) {
384
+ let previous = start - 1;
385
+ while (previous >= 0 && (isWhitespace(code[previous]) || !positions[previous])) previous--;
386
+ if (code[previous] !== ".") pendingControlParen = word;
387
+ } else if (word === "await" && controlKeyword === "for") pendingControlParen = controlKeyword;
388
+ canStartRegex = REGEX_PREFIX_KEYWORDS.has(word);
312
389
  continue;
313
390
  }
314
- if (/\d/.test(char)) {
391
+ if (isAsciiDigit(char.charCodeAt(0))) {
315
392
  index++;
316
- while (/[\w.]/.test(code[index] || "")) index++;
393
+ while (isNumberPart(code[index])) index++;
317
394
  canStartRegex = false;
318
395
  continue;
319
396
  }
@@ -325,10 +402,27 @@ function createCodePositionMap(code, onComment) {
325
402
  index++;
326
403
  continue;
327
404
  }
328
- if (char === ")" || char === "]" || char === "}") canStartRegex = false;
405
+ const expression = templateExpressions.at(-1);
406
+ if (expression && char === "{") expression.braceDepth++;
407
+ else if (expression && char === "}") {
408
+ if (expression.braceDepth === 0) {
409
+ templateStart = expression.start;
410
+ templateExpressions.pop();
411
+ index++;
412
+ continue;
413
+ }
414
+ expression.braceDepth--;
415
+ }
416
+ if (char === "(") {
417
+ controlParens.push(controlKeyword !== void 0);
418
+ canStartRegex = true;
419
+ } else if (char === ")") canStartRegex = controlParens.pop() === true;
420
+ else if (char === "]" || char === "}") canStartRegex = false;
329
421
  else if (char !== ".") canStartRegex = true;
330
422
  index++;
331
423
  }
424
+ const unfinishedTemplateStart = templateExpressions[0]?.start ?? templateStart;
425
+ if (unfinishedTemplateStart !== void 0) mask(unfinishedTemplateStart, code.length);
332
426
  return positions;
333
427
  }
334
428
  //#endregion
@@ -363,4 +457,4 @@ const moduleFederationConsole = {
363
457
  const mfWarn = moduleFederationConsole.warn;
364
458
  const mfError = moduleFederationConsole.error;
365
459
  //#endregion
366
- export { CodeRewriter as a, isAbsoluteUrl as c, resolveHashPlaceholderFileName as d, createCodePositionMap as i, normalizePathForImport as l, mfError as n, createSourceMap as o, mfWarn as r, EXTERNAL_URL_RE as s, createModuleFederationError as t, rebaseImport as u };
460
+ export { CodeRewriter as a, isAbsoluteUrl as c, rebaseImport as d, resolveHashPlaceholderFileName as f, createCodePositionMap as i, isHttpUrl as l, mfError as n, createSourceMap as o, mfWarn as r, EXTERNAL_URL_RE as s, createModuleFederationError as t, normalizePathForImport as u };
@@ -1,5 +1,5 @@
1
- import { l as normalizePathForImport, n as mfError, t as createModuleFederationError } from "./logger-DOwfTlw5.js";
2
- import { _ as resolveImportPath, d as hasPackageDependency } from "./dtsConstants-BQdFpgKb.js";
1
+ import { l as isHttpUrl, n as mfError, t as createModuleFederationError, u as normalizePathForImport } from "./logger-D67aJYOe.js";
2
+ import { p as hasPackageDependency, r as watchFileEvents, y as resolveImportPath } from "./dtsConstants-CDuC8Bb7.js";
3
3
  import fs from "fs";
4
4
  import * as path$1 from "node:path";
5
5
  import { normalizeOptions } from "@module-federation/sdk";
@@ -113,7 +113,7 @@ const resolveDtsPluginOptions = (dts, options, context) => {
113
113
  };
114
114
  };
115
115
  const getBasePath = (base) => {
116
- if (base.startsWith("http://") || base.startsWith("https://")) return new URL(base).pathname.replace(/\/$/, "") || "/";
116
+ if (isHttpUrl(base)) return new URL(base).pathname.replace(/\/$/, "") || "/";
117
117
  return base.replace(/\/$/, "") || "/";
118
118
  };
119
119
  const joinBaseAndAsset = (base, assetFileName) => {
@@ -262,15 +262,11 @@ function pluginDts(options) {
262
262
  clearTimeout(updateTimer);
263
263
  updateTimer = setTimeout(() => devWorker?.update(), UPDATE_DEBOUNCE_MS);
264
264
  };
265
- server.watcher.on("change", update);
266
- server.watcher.on("add", update);
267
- server.watcher.on("unlink", update);
265
+ const stopWatching = watchFileEvents(server.watcher, update);
268
266
  server.httpServer?.once("close", () => {
269
267
  clearTimeout(updateTimer);
270
268
  devWorker?.exit();
271
- server.watcher.off("change", update);
272
- server.watcher.off("add", update);
273
- server.watcher.off("unlink", update);
269
+ stopWatching();
274
270
  });
275
271
  };
276
272
  startDevWorker().catch((error) => {
@@ -1,3 +1,5 @@
1
+ import { u as normalizePathForImport } from "./logger-D67aJYOe.js";
2
+ import * as path$1 from "node:path";
1
3
  //#region src/utils/pathNormalization.ts
2
4
  const COMMON_SHARED_SUBPATHS = {
3
5
  react: [
@@ -127,6 +129,11 @@ function resolvePublicPath(options, viteBase, originalBase) {
127
129
  }
128
130
  return "auto";
129
131
  }
132
+ /** Relative `./`-prefixed import specifier from `fromFile` to `targetFile`. */
133
+ function toRelativeImport(fromFile, targetFile) {
134
+ const relative = normalizePathForImport(path$1.relative(path$1.dirname(fromFile), targetFile));
135
+ return relative.startsWith(".") ? relative : `./${relative}`;
136
+ }
130
137
  //#endregion
131
138
  //#region src/utils/sharedKeyMatcher.ts
132
139
  function getSharedRequest(key, shareItem) {
@@ -218,4 +225,4 @@ function getSharedKeyMatcher(shared) {
218
225
  return matcher;
219
226
  }
220
227
  //#endregion
221
- export { removeTrailingSlash as _, matchesSharedSource as a, stripViteFsPrefix as b, filterId as c, getNodeModulesSuffix as d, isAssetLikeImport as f, normalizeNodeModulePath as g, isViteOptimizableEntry as h, invalidateSharedKeyMatcher as i, getBasePath as l, isNuxtClientBase as m, getSharedRequest as n, VITE_FS_PREFIX as o, isNodeModulePath as p, getSharedRuntimeKey as r, ensureTrailingSlash as s, findSharedKey as t, getCommonSharedSubpaths as u, resolvePublicPath as v, stripQueryAndHash as y };
228
+ export { removeTrailingSlash as _, matchesSharedSource as a, stripViteFsPrefix as b, filterId as c, getNodeModulesSuffix as d, isAssetLikeImport as f, normalizeNodeModulePath as g, isViteOptimizableEntry as h, invalidateSharedKeyMatcher as i, getBasePath as l, isNuxtClientBase as m, getSharedRequest as n, VITE_FS_PREFIX as o, isNodeModulePath as p, getSharedRuntimeKey as r, ensureTrailingSlash as s, findSharedKey as t, getCommonSharedSubpaths as u, resolvePublicPath as v, toRelativeImport as x, stripQueryAndHash as y };
@@ -1,4 +1,4 @@
1
- import { a as CodeRewriter, i as createCodePositionMap, r as mfWarn, s as EXTERNAL_URL_RE } from "./logger-DOwfTlw5.js";
1
+ import { a as CodeRewriter, i as createCodePositionMap, l as isHttpUrl, r as mfWarn, s as EXTERNAL_URL_RE } from "./logger-D67aJYOe.js";
2
2
  import { simpleJoinRemoteEntry } from "@module-federation/sdk";
3
3
  //#region src/utils/fetchWithTimeout.ts
4
4
  const DEFAULT_SSR_FETCH_TIMEOUT_MS = 1e4;
@@ -57,6 +57,10 @@ var SsrFetchBodyTooLargeError = class extends Error {
57
57
  function isSsrFetchBodyTooLargeError(error) {
58
58
  return error instanceof SsrFetchBodyTooLargeError;
59
59
  }
60
+ /** A short single-line excerpt of a response body for error messages. */
61
+ function getBodyPreview(body) {
62
+ return body.slice(0, 240).replace(/\s+/g, " ").trim();
63
+ }
60
64
  /**
61
65
  * Read a response body as text, rejecting when it exceeds `maxBytes`.
62
66
  * Set `maxBytes` to 0 (or a non-finite value) to disable the limit.
@@ -99,15 +103,6 @@ async function readResponseTextBounded(res, maxBytes = DEFAULT_SSR_FETCH_MAX_BYT
99
103
  return new TextDecoder().decode(merged);
100
104
  }
101
105
  //#endregion
102
- //#region src/utils/url.ts
103
- function getUrlOrigin(value) {
104
- try {
105
- return new URL(value).origin;
106
- } catch {
107
- return;
108
- }
109
- }
110
- //#endregion
111
106
  //#region src/utils/ssrEntryLoader.ts
112
107
  /**
113
108
  * MF runtime plugin that intercepts the `loadEntry` lifecycle hook on the
@@ -198,9 +193,62 @@ function getRunnerCacheKey(remoteOrigin, resolvedShared, fetchTimeoutMs, fetchMa
198
193
  getSortedRecordEntries(resolvedShared)
199
194
  ]);
200
195
  }
201
- async function resolveSharedExternal(id, resolvedShared) {
196
+ function findResolvedSharedKey(id, resolvedShared) {
197
+ if (resolvedShared[id]) return id;
198
+ let matchedKey;
199
+ for (const key of Object.keys(resolvedShared)) {
200
+ if (!key.endsWith("/")) continue;
201
+ const base = key.slice(0, -1);
202
+ if (id !== base && !id.startsWith(`${base}/`)) continue;
203
+ if (!matchedKey || key.length > matchedKey.length) matchedKey = key;
204
+ }
205
+ return matchedKey;
206
+ }
207
+ function getEsmExportTarget(target) {
208
+ if (typeof target === "string") return target;
209
+ if (!target || typeof target !== "object" || Array.isArray(target)) return void 0;
210
+ for (const [condition, value] of Object.entries(target)) {
211
+ if (condition !== "node" && condition !== "import" && condition !== "default") continue;
212
+ const resolved = getEsmExportTarget(value);
213
+ if (resolved) return resolved;
214
+ }
215
+ }
216
+ async function resolveEsmSubpath(id, base) {
217
+ const { dirname, join } = await _path();
218
+ const { existsSync, readFileSync } = await _fs();
219
+ const segments = id.split("/");
220
+ const nameLength = id.startsWith("@") ? 2 : 1;
221
+ const name = segments.slice(0, nameLength).join("/");
222
+ const subpath = [".", ...segments.slice(nameLength)].join("/");
223
+ for (let dir = dirname(base); dirname(dir) !== dir; dir = dirname(dir)) {
224
+ const packageJson = join(dir, "node_modules", name, "package.json");
225
+ if (!existsSync(packageJson)) continue;
226
+ const { exports } = JSON.parse(readFileSync(packageJson, "utf8"));
227
+ const target = getEsmExportTarget(exports?.[subpath]);
228
+ return target && join(dirname(packageJson), target);
229
+ }
230
+ }
231
+ /**
232
+ * Resolve a shared request from the exact or wildcard build-time map.
233
+ * Wildcard entries point at the package entry, so concrete subpaths need a
234
+ * second Node resolution step to preserve package exports and conditions.
235
+ */
236
+ async function resolveSharedPath(id, resolvedShared) {
202
237
  if (typeof id !== "string") return null;
203
- const resolved = resolvedShared[id];
238
+ const matchedKey = findResolvedSharedKey(id, resolvedShared);
239
+ if (!matchedKey) return null;
240
+ const resolved = resolvedShared[matchedKey];
241
+ if (!resolved) return null;
242
+ if (matchedKey === id || EXTERNAL_URL_RE.test(resolved)) return resolved;
243
+ const { createRequire } = await _module();
244
+ const { join } = await _path();
245
+ for (const base of [resolved, join(process.cwd(), "noop.js")]) try {
246
+ return await resolveEsmSubpath(id, base).catch(() => void 0) ?? createRequire(base).resolve(id);
247
+ } catch {}
248
+ return null;
249
+ }
250
+ async function resolveSharedExternal(id, resolvedShared) {
251
+ const resolved = await resolveSharedPath(id, resolvedShared);
204
252
  if (!resolved) return null;
205
253
  if (EXTERNAL_URL_RE.test(resolved)) return {
206
254
  externalize: resolved,
@@ -230,8 +278,9 @@ async function getOrCreateRunner(remoteOrigin, resolvedShared, fetchTimeoutMs, f
230
278
  headers: { "Content-Type": "application/json" },
231
279
  body: JSON.stringify(payload)
232
280
  }, fetchTimeoutMs), fetchMaxBytes, runnerEndpoint);
233
- const result = parseRunnerInvokeResult(JSON.parse(text));
234
- if ("error" in result && payload.data.name === "fetchModule") {
281
+ const data = JSON.parse(text);
282
+ const result = parseRunnerInvokeResult(data);
283
+ if (("error" in result || isPlainObject(data) && data.shared === true) && payload.data.name === "fetchModule") {
235
284
  const sharedExternal = await resolveSharedExternal(payload.data.data[0], resolvedShared);
236
285
  if (sharedExternal) return { result: sharedExternal };
237
286
  }
@@ -326,9 +375,6 @@ var SsrEntryHttpError = class extends Error {
326
375
  this.name = "SsrEntryHttpError";
327
376
  }
328
377
  };
329
- function getBodyPreview(body) {
330
- return body.slice(0, 240).replace(/\s+/g, " ").trim();
331
- }
332
378
  function isSsrEntryHttpError(error) {
333
379
  return error instanceof SsrEntryHttpError;
334
380
  }
@@ -540,9 +586,10 @@ function scheduleVmCacheCleanup(remoteEntryUrl) {
540
586
  */
541
587
  async function clearRunnerCaches(remoteEntryUrl) {
542
588
  let remoteOrigin;
543
- if (remoteEntryUrl) {
544
- remoteOrigin = getUrlOrigin(remoteEntryUrl);
545
- if (!remoteOrigin) return;
589
+ if (remoteEntryUrl) try {
590
+ remoteOrigin = new URL(remoteEntryUrl).origin;
591
+ } catch {
592
+ return;
546
593
  }
547
594
  await Promise.all([...runnerCache.values()].filter((cached) => !remoteOrigin || cached.remoteOrigin === remoteOrigin).map(async (cached) => {
548
595
  try {
@@ -751,13 +798,22 @@ function transformSsrCode(code, base, specifiers, sharedPkgMap, requireShimUrl,
751
798
  }
752
799
  const resolvedShared = sharedPkgMap?.get(value);
753
800
  const relativeUrl = isRelativeSpecifier(value) ? new URL(value, base).href : void 0;
754
- const absoluteHttpUrl = value.startsWith("http://") || value.startsWith("https://") ? value : void 0;
801
+ const absoluteHttpUrl = isHttpUrl(value) ? value : void 0;
755
802
  const httpUrl = relativeUrl ?? absoluteHttpUrl;
756
803
  const replacement = (httpUrl ? tempFileUrlMap?.get(httpUrl) : void 0) ?? relativeUrl ?? (resolvedShared && `file://${resolvedShared}`);
757
804
  if (replacement) rewriter.overwrite(start, end, `"${replacement}"`);
758
805
  }
759
806
  return neutralizeBrowserPreloadHelpers(rewriter.toString());
760
807
  }
808
+ async function materializeSharedSubpaths(specifiers, sharedPkgMap) {
809
+ const uniqueSpecifiers = new Set(specifiers.map(({ value }) => value));
810
+ const resolvedShared = Object.fromEntries(sharedPkgMap);
811
+ for (const specifier of uniqueSpecifiers) {
812
+ if (sharedPkgMap.has(specifier)) continue;
813
+ const resolved = await resolveSharedPath(specifier, resolvedShared);
814
+ if (resolved) sharedPkgMap.set(specifier, resolved);
815
+ }
816
+ }
761
817
  function isVitePreloadHelperSpecifier(specifier) {
762
818
  return specifier.includes("preload-helper");
763
819
  }
@@ -773,7 +829,8 @@ function getTempFileImportUrl(filePath, versionKey) {
773
829
  * a remote redeploy (new manifest → new key) produces new files and bypasses
774
830
  * Node's ESM module cache instead of serving the stale build.
775
831
  */
776
- async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, versionKey = UNVERSIONED, fetchTimeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS, contextKey = "default", fetchMaxBytes = DEFAULT_SSR_FETCH_MAX_BYTES, extension = ".mjs", owner) {
832
+ async function fetchEsmToTempFile(url, graph) {
833
+ const { tmpDir, visited, pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey, fetchMaxBytes, extension, owner } = graph;
777
834
  const cacheKey = JSON.stringify([
778
835
  fetchTimeoutMs,
779
836
  fetchMaxBytes,
@@ -795,7 +852,8 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
795
852
  const tmpFilePromise = (async () => {
796
853
  const { createHash } = await _crypto();
797
854
  const { join } = await _path();
798
- return join(tmpDir, `${createHash("sha1").update(cacheKey).digest("hex").slice(0, 12)}${extension}`);
855
+ const hash = createHash("sha1").update(cacheKey).digest("hex").slice(0, 12);
856
+ return join(tmpDir, `${hash}${extension}`);
799
857
  })();
800
858
  tempFilePathCache.set(cacheKey, tmpFilePromise);
801
859
  const promise = (async () => {
@@ -808,10 +866,11 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
808
866
  const specifiers = findSsrModuleSpecifiers(code);
809
867
  const relImports = specifiers.map(({ value }) => value).filter((value) => isRelativeSpecifier(value) && !isVitePreloadHelperSpecifier(value)).map((value) => new URL(value, base).href);
810
868
  const subMap = /* @__PURE__ */ new Map();
811
- await Promise.all([...new Set(relImports)].filter((u) => u.startsWith("http://") || u.startsWith("https://")).map(async (u) => {
812
- const tmpPath = await fetchEsmToTempFile(u, tmpDir, visited, pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey, fetchMaxBytes, extension, owner);
869
+ await Promise.all([...new Set(relImports)].filter(isHttpUrl).map(async (u) => {
870
+ const tmpPath = await fetchEsmToTempFile(u, graph);
813
871
  subMap.set(u, getTempFileImportUrl(tmpPath, versionKey));
814
872
  }));
873
+ if (sharedPkgMap) await materializeSharedSubpaths(specifiers, sharedPkgMap);
815
874
  const requireShimUrl = sharedPkgMap?.size && specifiers.some(({ value }) => isNodeModuleSpecifier(value)) ? await getRequireShimUrl(tmpDir, sharedPkgMap, contextKey) : void 0;
816
875
  code = transformSsrCode(code, base, specifiers, sharedPkgMap, requireShimUrl, subMap);
817
876
  const { writeFileSync } = await _fs();
@@ -834,9 +893,18 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
834
893
  });
835
894
  return promise;
836
895
  }
837
- async function fetchEsmGraphToTempFile(url, tmpDir, sharedPkgMap, versionKey = UNVERSIONED, fetchTimeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS, contextKey = "default", fetchMaxBytes = DEFAULT_SSR_FETCH_MAX_BYTES, extension = ".mjs", owner) {
896
+ async function fetchEsmGraphToTempFile(url, { versionKey = UNVERSIONED, fetchTimeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS, contextKey = "default", fetchMaxBytes = DEFAULT_SSR_FETCH_MAX_BYTES, extension = ".mjs", ...rest }) {
838
897
  const pending = /* @__PURE__ */ new Set();
839
- const rootFile = await fetchEsmToTempFile(url, tmpDir, /* @__PURE__ */ new Map(), pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey, fetchMaxBytes, extension, owner);
898
+ const rootFile = await fetchEsmToTempFile(url, {
899
+ ...rest,
900
+ versionKey,
901
+ fetchTimeoutMs,
902
+ contextKey,
903
+ fetchMaxBytes,
904
+ extension,
905
+ visited: /* @__PURE__ */ new Map(),
906
+ pending
907
+ });
840
908
  await Promise.all(pending);
841
909
  return rootFile;
842
910
  }
@@ -848,7 +916,7 @@ async function importTempModule(filePath, versionKey) {
848
916
  }
849
917
  let warnedVmUnavailable = false;
850
918
  function getVmStrategyModule() {
851
- if (!loaderState.vmStrategyModulePromise) loaderState.vmStrategyModulePromise = import("./ssrVmStrategy-BkyvcG4s.js");
919
+ if (!loaderState.vmStrategyModulePromise) loaderState.vmStrategyModulePromise = import("./ssrVmStrategy-D1cSKUV-.js");
852
920
  return loaderState.vmStrategyModulePromise;
853
921
  }
854
922
  /**
@@ -889,7 +957,7 @@ async function loadSSRRemoteEntry(ssrEntry, options, rootEntryUrl) {
889
957
  return req(url);
890
958
  } catch {}
891
959
  }
892
- if (url.startsWith("http://") || url.startsWith("https://")) {
960
+ if (isHttpUrl(url)) {
893
961
  const urlObj = new URL(url);
894
962
  if (urlObj.pathname.includes("/__mf_ssr__/")) {
895
963
  const remoteOrigin = urlObj.origin;
@@ -919,7 +987,16 @@ async function loadSSRRemoteEntry(ssrEntry, options, rootEntryUrl) {
919
987
  const isCommonJs = type === "commonjs-module" || type === "commonjs";
920
988
  const extension = isCommonJs ? ".cjs" : ".mjs";
921
989
  try {
922
- const tmpFile = await fetchEsmGraphToTempFile(url, cacheDir, sharedPkgMap, versionKey, options.fetchTimeoutMs, getSsrTransformContextKey(resolvedShared, options.shareScopeName), options.fetchMaxBytes, extension, rootEntryUrl);
990
+ const tmpFile = await fetchEsmGraphToTempFile(url, {
991
+ tmpDir: cacheDir,
992
+ sharedPkgMap,
993
+ versionKey,
994
+ fetchTimeoutMs: options.fetchTimeoutMs,
995
+ contextKey: getSsrTransformContextKey(resolvedShared, options.shareScopeName),
996
+ fetchMaxBytes: options.fetchMaxBytes,
997
+ extension,
998
+ owner: rootEntryUrl
999
+ });
923
1000
  if (isCommonJs) return (await _module()).createRequire(import.meta.url)(tmpFile);
924
1001
  return await importTempModule(tmpFile, versionKey);
925
1002
  } catch (error) {
@@ -937,6 +1014,43 @@ async function loadSSRRemoteEntry(ssrEntry, options, rootEntryUrl) {
937
1014
  return null;
938
1015
  }
939
1016
  }
1017
+ /**
1018
+ * Build-time `resolvedShared` paths point into the build machine's
1019
+ * node_modules. A traced or relocated server output imports shared packages
1020
+ * from its own copy, so pinning remotes to the build paths loads a second
1021
+ * instance (two Reacts: invalid hook call) or, once the build directory is
1022
+ * gone, nothing at all (ERR_MODULE_NOT_FOUND).
1023
+ *
1024
+ * Each specifier takes the first hit of a fixed order, independent of import
1025
+ * order or timing:
1026
+ * 1. resolved from the real path of the server entry (`process.argv[1]`),
1027
+ * which is where Node resolves the host's own bare imports from, unless
1028
+ * it is a CLI inside node_modules (e.g. `vite preview`);
1029
+ * 2. resolved from `process.cwd()`, for launchers whose `argv[1]` is not the
1030
+ * server entry or is missing;
1031
+ * 3. the build-time path.
1032
+ */
1033
+ async function rebaseResolvedShared(resolvedShared) {
1034
+ const { createRequire } = await _module();
1035
+ const { realpathSync } = await _fs();
1036
+ const { join } = await _path();
1037
+ const requires = [];
1038
+ try {
1039
+ const entry = process.argv[1] && realpathSync(process.argv[1]);
1040
+ if (entry && !/[\\/]node_modules[\\/]/.test(entry)) requires.push(createRequire(entry));
1041
+ } catch {}
1042
+ requires.push(createRequire(join(process.cwd(), "noop.js")));
1043
+ const rebased = {};
1044
+ for (const [specifier, buildPath] of Object.entries(resolvedShared)) {
1045
+ rebased[specifier] = buildPath;
1046
+ const resolutionSpecifier = specifier.endsWith("/") ? specifier.slice(0, -1) : specifier;
1047
+ for (const require of requires) try {
1048
+ rebased[specifier] = require.resolve(resolutionSpecifier);
1049
+ break;
1050
+ } catch {}
1051
+ }
1052
+ return rebased;
1053
+ }
940
1054
  function ssrEntryLoaderPlugin(options = {}) {
941
1055
  const resolved = {
942
1056
  resolvedShared: options.resolvedShared ?? {},
@@ -947,10 +1061,17 @@ function ssrEntryLoaderPlugin(options = {}) {
947
1061
  fetchMaxBytes: options.fetchMaxBytes ?? 10485760,
948
1062
  cacheContext: {}
949
1063
  };
1064
+ let rebased;
950
1065
  return {
951
1066
  name: "mf-vite:ssr-entry-loader",
952
1067
  async loadEntry({ remoteInfo, origin }) {
953
1068
  if (!isNodeServer()) return;
1069
+ if (options.rebaseSharedAtRuntime) {
1070
+ rebased ??= rebaseResolvedShared(resolved.resolvedShared).then((map) => {
1071
+ resolved.resolvedShared = map;
1072
+ }, () => {});
1073
+ await rebased;
1074
+ }
954
1075
  const loadOptions = origin ? {
955
1076
  ...resolved,
956
1077
  cacheContext: origin,
@@ -968,4 +1089,4 @@ function ssrEntryLoaderPlugin(options = {}) {
968
1089
  };
969
1090
  }
970
1091
  //#endregion
971
- export { ssrEntryLoaderPlugin as a, fetchWithTimeout as c, revalidate as i, readResponseTextBounded as l, SsrEntryHttpError as n, DEFAULT_SSR_FETCH_MAX_BYTES as o, neutralizeBrowserPreloadHelpers as r, DEFAULT_SSR_FETCH_TIMEOUT_MS as s, SSR_ENTRY_LOADER_STATE_KEY as t };
1092
+ export { revalidate as a, DEFAULT_SSR_FETCH_TIMEOUT_MS as c, readResponseTextBounded as d, resolveSharedPath as i, fetchWithTimeout as l, SsrEntryHttpError as n, ssrEntryLoaderPlugin as o, neutralizeBrowserPreloadHelpers as r, DEFAULT_SSR_FETCH_MAX_BYTES as s, SSR_ENTRY_LOADER_STATE_KEY as t, getBodyPreview as u };
@@ -1,5 +1,6 @@
1
- import { t as findSharedKey } from "./sharedKeyMatcher-CHp4QRNq.js";
2
- import { c as fetchWithTimeout, l as readResponseTextBounded, n as SsrEntryHttpError, r as neutralizeBrowserPreloadHelpers } from "./ssrEntryLoader-CHdWk8Yg.js";
1
+ import { l as isHttpUrl, s as EXTERNAL_URL_RE } from "./logger-D67aJYOe.js";
2
+ import { t as findSharedKey } from "./sharedKeyMatcher-C04bVN1q.js";
3
+ import { d as readResponseTextBounded, i as resolveSharedPath, l as fetchWithTimeout, n as SsrEntryHttpError, r as neutralizeBrowserPreloadHelpers, u as getBodyPreview } from "./ssrEntryLoader-DKpOrW8D.js";
3
4
  //#region src/utils/ssrVmStrategy.ts
4
5
  /**
5
6
  * vm.SourceTextModule strategy for loading remote SSR entries.
@@ -68,11 +69,8 @@ async function loadBareModule(specifier, options) {
68
69
  }
69
70
  } catch {}
70
71
  }
71
- const resolvedPath = options.resolvedShared[specifier];
72
- if (resolvedPath) return import(
73
- /* @vite-ignore */
74
- `file://${resolvedPath}`
75
- );
72
+ const resolvedPath = await resolveSharedPath(specifier, options.resolvedShared);
73
+ if (resolvedPath) return EXTERNAL_URL_RE.test(resolvedPath) ? import(resolvedPath) : import(`file://${resolvedPath}`);
76
74
  const namespace = await import(
77
75
  /* @vite-ignore */
78
76
  specifier
@@ -163,18 +161,12 @@ function clearVmStrategyCaches(remoteEntryUrl) {
163
161
  clearOwnedVmCache(httpModuleCache, httpModuleCacheOwners, remoteEntryUrl);
164
162
  clearOwnedVmCache(namespaceCache, namespaceCacheOwners, remoteEntryUrl);
165
163
  }
166
- function getBodyPreview(body) {
167
- return body.slice(0, 240).replace(/\s+/g, " ").trim();
168
- }
169
164
  async function fetchModuleSource(url, fetchTimeoutMs, fetchMaxBytes) {
170
165
  const res = await fetchWithTimeout(url, {}, fetchTimeoutMs);
171
166
  const text = await readResponseTextBounded(res, fetchMaxBytes ?? 10485760, url);
172
167
  if (!res.ok) throw new SsrEntryHttpError(url, res.status, res.statusText, getBodyPreview(text));
173
168
  return neutralizeBrowserPreloadHelpers(text);
174
169
  }
175
- function isHttpUrl(value) {
176
- return value.startsWith("http://") || value.startsWith("https://");
177
- }
178
170
  /** Resolve a specifier against the referencing module's URL; null for bare specifiers. */
179
171
  function resolveSpecifierUrl(specifier, referencerUrl) {
180
172
  if (isHttpUrl(specifier)) return specifier;
@@ -1,6 +1,12 @@
1
1
  import { i as SsrEntryLoaderConfig } from "../normalizeModuleFederationOptions-DNUlv-AL.js";
2
2
  //#region src/utils/ssrEntryLoader.d.ts
3
3
  declare const SSR_ENTRY_LOADER_STATE_KEY: unique symbol;
4
+ /**
5
+ * Resolve a shared request from the exact or wildcard build-time map.
6
+ * Wildcard entries point at the package entry, so concrete subpaths need a
7
+ * second Node resolution step to preserve package exports and conditions.
8
+ */
9
+ declare function resolveSharedPath(id: unknown, resolvedShared: Record<string, string>): Promise<string | null>;
4
10
  interface RemoteInfo {
5
11
  name: string;
6
12
  entry: string;
@@ -65,4 +71,4 @@ declare function ssrEntryLoaderPlugin(options?: SsrEntryLoaderOptions): {
65
71
  } | undefined>;
66
72
  };
67
73
  //#endregion
68
- export { SSR_ENTRY_LOADER_STATE_KEY, SsrEntryHttpError, ssrEntryLoaderPlugin as default, neutralizeBrowserPreloadHelpers, revalidate };
74
+ export { SSR_ENTRY_LOADER_STATE_KEY, SsrEntryHttpError, ssrEntryLoaderPlugin as default, neutralizeBrowserPreloadHelpers, resolveSharedPath, revalidate };
@@ -1,2 +1,2 @@
1
- import { a as ssrEntryLoaderPlugin, i as revalidate, n as SsrEntryHttpError, r as neutralizeBrowserPreloadHelpers, t as SSR_ENTRY_LOADER_STATE_KEY } from "../ssrEntryLoader-CHdWk8Yg.js";
2
- export { SSR_ENTRY_LOADER_STATE_KEY, SsrEntryHttpError, ssrEntryLoaderPlugin as default, neutralizeBrowserPreloadHelpers, revalidate };
1
+ import { a as revalidate, i as resolveSharedPath, n as SsrEntryHttpError, o as ssrEntryLoaderPlugin, r as neutralizeBrowserPreloadHelpers, t as SSR_ENTRY_LOADER_STATE_KEY } from "../ssrEntryLoader-DKpOrW8D.js";
2
+ export { SSR_ENTRY_LOADER_STATE_KEY, SsrEntryHttpError, ssrEntryLoaderPlugin as default, neutralizeBrowserPreloadHelpers, resolveSharedPath, revalidate };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/vite",
3
- "version": "1.23.5",
3
+ "version": "1.23.7",
4
4
  "description": "Vite plugin for Module Federation",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -74,7 +74,7 @@
74
74
  "homepage": "https://github.com/module-federation/vite#readme",
75
75
  "packageManager": "pnpm@10.28.2",
76
76
  "peerDependencies": {
77
- "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
77
+ "vite": "^5.1.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
78
78
  },
79
79
  "dependencies": {
80
80
  "@module-federation/dts-plugin": "2.9.2",
@@ -90,7 +90,7 @@
90
90
  "rollup": "4.62.3",
91
91
  "tsdown": "0.22.14",
92
92
  "typescript": "7.0.2",
93
- "vite": "8.2.0",
94
- "vitest": "4.1.11"
93
+ "vite": "8.2.4",
94
+ "vitest": "4.1.12"
95
95
  }
96
96
  }