@module-federation/vite 1.23.4 → 1.23.6

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 { g as resolveImportPath, u as hasPackageDependency } from "./dtsConstants-DSTzyKSG.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 { resolvePublicPath as _, matchesSharedSource as a, 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, stripQueryAndHash as v, stripViteFsPrefix 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
@@ -146,7 +141,23 @@ async function nodeImport(id) {
146
141
  const isNodeServer = () => {
147
142
  return import.meta.env?.SSR ?? typeof globalThis.process?.versions?.node === "string";
148
143
  };
149
- const runnerCache = /* @__PURE__ */ new Map();
144
+ const SSR_ENTRY_LOADER_STATE_KEY = Symbol.for("module-federation.vite.ssrEntryLoader.state");
145
+ function getLoaderState() {
146
+ const holder = globalThis;
147
+ const state = holder[SSR_ENTRY_LOADER_STATE_KEY] ??= {};
148
+ state.runnerCache ??= /* @__PURE__ */ new Map();
149
+ state.unversionedGenerations ??= /* @__PURE__ */ new Map();
150
+ state.unversionedGlobalGeneration ??= 0;
151
+ state.ssrEntryCache ??= /* @__PURE__ */ new Map();
152
+ state.manifestFetchCache ??= /* @__PURE__ */ new Map();
153
+ state.tempFileCache ??= /* @__PURE__ */ new Map();
154
+ state.tempFilePathCache ??= /* @__PURE__ */ new Map();
155
+ state.tempFileRecords ??= /* @__PURE__ */ new Map();
156
+ state.scheduledTempFileCleanup ??= /* @__PURE__ */ new Set();
157
+ return state;
158
+ }
159
+ const loaderState = getLoaderState();
160
+ const runnerCache = loaderState.runnerCache;
150
161
  function getSortedRecordEntries(record) {
151
162
  return Object.entries(record).sort(([left], [right]) => left.localeCompare(right));
152
163
  }
@@ -214,8 +225,9 @@ async function getOrCreateRunner(remoteOrigin, resolvedShared, fetchTimeoutMs, f
214
225
  headers: { "Content-Type": "application/json" },
215
226
  body: JSON.stringify(payload)
216
227
  }, fetchTimeoutMs), fetchMaxBytes, runnerEndpoint);
217
- const result = parseRunnerInvokeResult(JSON.parse(text));
218
- if ("error" in result && payload.data.name === "fetchModule") {
228
+ const data = JSON.parse(text);
229
+ const result = parseRunnerInvokeResult(data);
230
+ if (("error" in result || isPlainObject(data) && data.shared === true) && payload.data.name === "fetchModule") {
219
231
  const sharedExternal = await resolveSharedExternal(payload.data.data[0], resolvedShared);
220
232
  if (sharedExternal) return { result: sharedExternal };
221
233
  }
@@ -271,10 +283,9 @@ function parseRunnerInvokeResult(data) {
271
283
  * advance a process-local generation so the next import is fresh.
272
284
  */
273
285
  const UNVERSIONED = "unversioned";
274
- const unversionedGenerations = /* @__PURE__ */ new Map();
275
- let unversionedGlobalGeneration = 0;
286
+ const unversionedGenerations = loaderState.unversionedGenerations;
276
287
  function getUnversionedVersionKey(remoteEntryUrl) {
277
- return `${UNVERSIONED}-${unversionedGlobalGeneration}-${unversionedGenerations.get(remoteEntryUrl) ?? 0}`;
288
+ return `${UNVERSIONED}-${loaderState.unversionedGlobalGeneration}-${unversionedGenerations.get(remoteEntryUrl) ?? 0}`;
278
289
  }
279
290
  function bumpUnversionedGeneration(remoteEntryUrl) {
280
291
  unversionedGenerations.set(remoteEntryUrl, (unversionedGenerations.get(remoteEntryUrl) ?? 0) + 1);
@@ -292,8 +303,8 @@ function computeManifestVersionKey(manifest) {
292
303
  const contentHash = hashString(JSON.stringify(manifest));
293
304
  return buildVersion ? `${buildVersion}-${contentHash}` : contentHash;
294
305
  }
295
- const ssrEntryCache = /* @__PURE__ */ new Map();
296
- const manifestFetchCache = /* @__PURE__ */ new Map();
306
+ const ssrEntryCache = loaderState.ssrEntryCache;
307
+ const manifestFetchCache = loaderState.manifestFetchCache;
297
308
  function makeUrlCacheKey(url, fetchTimeoutMs, fetchMaxBytes) {
298
309
  return `${fetchTimeoutMs}::${fetchMaxBytes}::${url}`;
299
310
  }
@@ -311,9 +322,6 @@ var SsrEntryHttpError = class extends Error {
311
322
  this.name = "SsrEntryHttpError";
312
323
  }
313
324
  };
314
- function getBodyPreview(body) {
315
- return body.slice(0, 240).replace(/\s+/g, " ").trim();
316
- }
317
325
  function isSsrEntryHttpError(error) {
318
326
  return error instanceof SsrEntryHttpError;
319
327
  }
@@ -514,8 +522,8 @@ function invalidateRemoteCaches(remoteEntryUrl) {
514
522
  * static import cycle between the two SSR strategies.
515
523
  */
516
524
  function scheduleVmCacheCleanup(remoteEntryUrl) {
517
- if (!vmStrategyModulePromise) return;
518
- vmStrategyModulePromise.then(({ clearVmStrategyCaches }) => clearVmStrategyCaches(remoteEntryUrl)).catch(() => {});
525
+ if (!loaderState.vmStrategyModulePromise) return;
526
+ loaderState.vmStrategyModulePromise.then(({ clearVmStrategyCaches }) => clearVmStrategyCaches(remoteEntryUrl)).catch(() => {});
519
527
  }
520
528
  /**
521
529
  * A dev-mode ModuleRunner is created per origin (one Vite dev server) and is
@@ -525,9 +533,10 @@ function scheduleVmCacheCleanup(remoteEntryUrl) {
525
533
  */
526
534
  async function clearRunnerCaches(remoteEntryUrl) {
527
535
  let remoteOrigin;
528
- if (remoteEntryUrl) {
529
- remoteOrigin = getUrlOrigin(remoteEntryUrl);
530
- if (!remoteOrigin) return;
536
+ if (remoteEntryUrl) try {
537
+ remoteOrigin = new URL(remoteEntryUrl).origin;
538
+ } catch {
539
+ return;
531
540
  }
532
541
  await Promise.all([...runnerCache.values()].filter((cached) => !remoteOrigin || cached.remoteOrigin === remoteOrigin).map(async (cached) => {
533
542
  try {
@@ -575,7 +584,7 @@ function revalidate(remoteEntryUrl) {
575
584
  for (const key of manifestFetchCache.keys()) if (key.endsWith(`::${manifestUrl}`)) manifestFetchCache.delete(key);
576
585
  invalidateRemoteCaches(remoteEntryUrl);
577
586
  } else {
578
- unversionedGlobalGeneration += 1;
587
+ loaderState.unversionedGlobalGeneration += 1;
579
588
  ssrEntryCache.clear();
580
589
  manifestFetchCache.clear();
581
590
  invalidateAllRemoteCaches();
@@ -585,11 +594,11 @@ function revalidate(remoteEntryUrl) {
585
594
  instance?.moduleCache?.clear?.();
586
595
  } catch {}
587
596
  }
588
- const tempFileCache = /* @__PURE__ */ new Map();
589
- const tempFilePathCache = /* @__PURE__ */ new Map();
597
+ const tempFileCache = loaderState.tempFileCache;
598
+ const tempFilePathCache = loaderState.tempFilePathCache;
590
599
  const TEMP_FILE_CLEANUP_DELAY_MS = 3e4;
591
- const tempFileRecords = /* @__PURE__ */ new Map();
592
- const scheduledTempFileCleanup = /* @__PURE__ */ new Set();
600
+ const tempFileRecords = loaderState.tempFileRecords;
601
+ const scheduledTempFileCleanup = loaderState.scheduledTempFileCleanup;
593
602
  function scheduleTempFileCleanup(records) {
594
603
  for (const record of records) {
595
604
  if (scheduledTempFileCleanup.has(record.cacheKey)) continue;
@@ -736,7 +745,7 @@ function transformSsrCode(code, base, specifiers, sharedPkgMap, requireShimUrl,
736
745
  }
737
746
  const resolvedShared = sharedPkgMap?.get(value);
738
747
  const relativeUrl = isRelativeSpecifier(value) ? new URL(value, base).href : void 0;
739
- const absoluteHttpUrl = value.startsWith("http://") || value.startsWith("https://") ? value : void 0;
748
+ const absoluteHttpUrl = isHttpUrl(value) ? value : void 0;
740
749
  const httpUrl = relativeUrl ?? absoluteHttpUrl;
741
750
  const replacement = (httpUrl ? tempFileUrlMap?.get(httpUrl) : void 0) ?? relativeUrl ?? (resolvedShared && `file://${resolvedShared}`);
742
751
  if (replacement) rewriter.overwrite(start, end, `"${replacement}"`);
@@ -758,7 +767,8 @@ function getTempFileImportUrl(filePath, versionKey) {
758
767
  * a remote redeploy (new manifest → new key) produces new files and bypasses
759
768
  * Node's ESM module cache instead of serving the stale build.
760
769
  */
761
- 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) {
770
+ async function fetchEsmToTempFile(url, graph) {
771
+ const { tmpDir, visited, pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey, fetchMaxBytes, extension, owner } = graph;
762
772
  const cacheKey = JSON.stringify([
763
773
  fetchTimeoutMs,
764
774
  fetchMaxBytes,
@@ -780,7 +790,8 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
780
790
  const tmpFilePromise = (async () => {
781
791
  const { createHash } = await _crypto();
782
792
  const { join } = await _path();
783
- return join(tmpDir, `${createHash("sha1").update(cacheKey).digest("hex").slice(0, 12)}${extension}`);
793
+ const hash = createHash("sha1").update(cacheKey).digest("hex").slice(0, 12);
794
+ return join(tmpDir, `${hash}${extension}`);
784
795
  })();
785
796
  tempFilePathCache.set(cacheKey, tmpFilePromise);
786
797
  const promise = (async () => {
@@ -793,8 +804,8 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
793
804
  const specifiers = findSsrModuleSpecifiers(code);
794
805
  const relImports = specifiers.map(({ value }) => value).filter((value) => isRelativeSpecifier(value) && !isVitePreloadHelperSpecifier(value)).map((value) => new URL(value, base).href);
795
806
  const subMap = /* @__PURE__ */ new Map();
796
- await Promise.all([...new Set(relImports)].filter((u) => u.startsWith("http://") || u.startsWith("https://")).map(async (u) => {
797
- const tmpPath = await fetchEsmToTempFile(u, tmpDir, visited, pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey, fetchMaxBytes, extension, owner);
807
+ await Promise.all([...new Set(relImports)].filter(isHttpUrl).map(async (u) => {
808
+ const tmpPath = await fetchEsmToTempFile(u, graph);
798
809
  subMap.set(u, getTempFileImportUrl(tmpPath, versionKey));
799
810
  }));
800
811
  const requireShimUrl = sharedPkgMap?.size && specifiers.some(({ value }) => isNodeModuleSpecifier(value)) ? await getRequireShimUrl(tmpDir, sharedPkgMap, contextKey) : void 0;
@@ -819,9 +830,18 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
819
830
  });
820
831
  return promise;
821
832
  }
822
- 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) {
833
+ async function fetchEsmGraphToTempFile(url, { versionKey = UNVERSIONED, fetchTimeoutMs = DEFAULT_SSR_FETCH_TIMEOUT_MS, contextKey = "default", fetchMaxBytes = DEFAULT_SSR_FETCH_MAX_BYTES, extension = ".mjs", ...rest }) {
823
834
  const pending = /* @__PURE__ */ new Set();
824
- const rootFile = await fetchEsmToTempFile(url, tmpDir, /* @__PURE__ */ new Map(), pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey, fetchMaxBytes, extension, owner);
835
+ const rootFile = await fetchEsmToTempFile(url, {
836
+ ...rest,
837
+ versionKey,
838
+ fetchTimeoutMs,
839
+ contextKey,
840
+ fetchMaxBytes,
841
+ extension,
842
+ visited: /* @__PURE__ */ new Map(),
843
+ pending
844
+ });
825
845
  await Promise.all(pending);
826
846
  return rootFile;
827
847
  }
@@ -832,10 +852,9 @@ async function importTempModule(filePath, versionKey) {
832
852
  );
833
853
  }
834
854
  let warnedVmUnavailable = false;
835
- let vmStrategyModulePromise;
836
855
  function getVmStrategyModule() {
837
- if (!vmStrategyModulePromise) vmStrategyModulePromise = import("./ssrVmStrategy-Dk5xXVvR.js");
838
- return vmStrategyModulePromise;
856
+ if (!loaderState.vmStrategyModulePromise) loaderState.vmStrategyModulePromise = import("./ssrVmStrategy-BCaiSEbA.js");
857
+ return loaderState.vmStrategyModulePromise;
839
858
  }
840
859
  /**
841
860
  * Non-HTTP failures keep falling back (to temp-file after vm, to the federation
@@ -875,7 +894,7 @@ async function loadSSRRemoteEntry(ssrEntry, options, rootEntryUrl) {
875
894
  return req(url);
876
895
  } catch {}
877
896
  }
878
- if (url.startsWith("http://") || url.startsWith("https://")) {
897
+ if (isHttpUrl(url)) {
879
898
  const urlObj = new URL(url);
880
899
  if (urlObj.pathname.includes("/__mf_ssr__/")) {
881
900
  const remoteOrigin = urlObj.origin;
@@ -905,7 +924,16 @@ async function loadSSRRemoteEntry(ssrEntry, options, rootEntryUrl) {
905
924
  const isCommonJs = type === "commonjs-module" || type === "commonjs";
906
925
  const extension = isCommonJs ? ".cjs" : ".mjs";
907
926
  try {
908
- const tmpFile = await fetchEsmGraphToTempFile(url, cacheDir, sharedPkgMap, versionKey, options.fetchTimeoutMs, getSsrTransformContextKey(resolvedShared, options.shareScopeName), options.fetchMaxBytes, extension, rootEntryUrl);
927
+ const tmpFile = await fetchEsmGraphToTempFile(url, {
928
+ tmpDir: cacheDir,
929
+ sharedPkgMap,
930
+ versionKey,
931
+ fetchTimeoutMs: options.fetchTimeoutMs,
932
+ contextKey: getSsrTransformContextKey(resolvedShared, options.shareScopeName),
933
+ fetchMaxBytes: options.fetchMaxBytes,
934
+ extension,
935
+ owner: rootEntryUrl
936
+ });
909
937
  if (isCommonJs) return (await _module()).createRequire(import.meta.url)(tmpFile);
910
938
  return await importTempModule(tmpFile, versionKey);
911
939
  } catch (error) {
@@ -923,6 +951,42 @@ async function loadSSRRemoteEntry(ssrEntry, options, rootEntryUrl) {
923
951
  return null;
924
952
  }
925
953
  }
954
+ /**
955
+ * Build-time `resolvedShared` paths point into the build machine's
956
+ * node_modules. A traced or relocated server output imports shared packages
957
+ * from its own copy, so pinning remotes to the build paths loads a second
958
+ * instance (two Reacts: invalid hook call) or, once the build directory is
959
+ * gone, nothing at all (ERR_MODULE_NOT_FOUND).
960
+ *
961
+ * Each specifier takes the first hit of a fixed order, independent of import
962
+ * order or timing:
963
+ * 1. resolved from the real path of the server entry (`process.argv[1]`),
964
+ * which is where Node resolves the host's own bare imports from, unless
965
+ * it is a CLI inside node_modules (e.g. `vite preview`);
966
+ * 2. resolved from `process.cwd()`, for launchers whose `argv[1]` is not the
967
+ * server entry or is missing;
968
+ * 3. the build-time path.
969
+ */
970
+ async function rebaseResolvedShared(resolvedShared) {
971
+ const { createRequire } = await _module();
972
+ const { realpathSync } = await _fs();
973
+ const { join } = await _path();
974
+ const requires = [];
975
+ try {
976
+ const entry = process.argv[1] && realpathSync(process.argv[1]);
977
+ if (entry && !/[\\/]node_modules[\\/]/.test(entry)) requires.push(createRequire(entry));
978
+ } catch {}
979
+ requires.push(createRequire(join(process.cwd(), "noop.js")));
980
+ const rebased = {};
981
+ for (const [specifier, buildPath] of Object.entries(resolvedShared)) {
982
+ rebased[specifier] = buildPath;
983
+ for (const require of requires) try {
984
+ rebased[specifier] = require.resolve(specifier);
985
+ break;
986
+ } catch {}
987
+ }
988
+ return rebased;
989
+ }
926
990
  function ssrEntryLoaderPlugin(options = {}) {
927
991
  const resolved = {
928
992
  resolvedShared: options.resolvedShared ?? {},
@@ -933,10 +997,17 @@ function ssrEntryLoaderPlugin(options = {}) {
933
997
  fetchMaxBytes: options.fetchMaxBytes ?? 10485760,
934
998
  cacheContext: {}
935
999
  };
1000
+ let rebased;
936
1001
  return {
937
1002
  name: "mf-vite:ssr-entry-loader",
938
1003
  async loadEntry({ remoteInfo, origin }) {
939
1004
  if (!isNodeServer()) return;
1005
+ if (options.rebaseSharedAtRuntime) {
1006
+ rebased ??= rebaseResolvedShared(resolved.resolvedShared).then((map) => {
1007
+ resolved.resolvedShared = map;
1008
+ }, () => {});
1009
+ await rebased;
1010
+ }
940
1011
  const loadOptions = origin ? {
941
1012
  ...resolved,
942
1013
  cacheContext: origin,
@@ -954,4 +1025,4 @@ function ssrEntryLoaderPlugin(options = {}) {
954
1025
  };
955
1026
  }
956
1027
  //#endregion
957
- export { DEFAULT_SSR_FETCH_MAX_BYTES as a, readResponseTextBounded as c, ssrEntryLoaderPlugin as i, neutralizeBrowserPreloadHelpers as n, DEFAULT_SSR_FETCH_TIMEOUT_MS as o, revalidate as r, fetchWithTimeout as s, SsrEntryHttpError as t };
1028
+ export { ssrEntryLoaderPlugin as a, fetchWithTimeout as c, revalidate as i, getBodyPreview 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, readResponseTextBounded as u };
@@ -1,5 +1,6 @@
1
- import { t as findSharedKey } from "./sharedKeyMatcher-Od_nYxJA.js";
2
- import { c as readResponseTextBounded, n as neutralizeBrowserPreloadHelpers, s as fetchWithTimeout, t as SsrEntryHttpError } from "./ssrEntryLoader-DjZj-5Lw.js";
1
+ import { l as isHttpUrl } from "./logger-D67aJYOe.js";
2
+ import { t as findSharedKey } from "./sharedKeyMatcher-C04bVN1q.js";
3
+ import { c as fetchWithTimeout, l as getBodyPreview, n as SsrEntryHttpError, r as neutralizeBrowserPreloadHelpers, u as readResponseTextBounded } from "./ssrEntryLoader-Bh2Tq1cZ.js";
3
4
  //#region src/utils/ssrVmStrategy.ts
4
5
  /**
5
6
  * vm.SourceTextModule strategy for loading remote SSR entries.
@@ -163,18 +164,12 @@ function clearVmStrategyCaches(remoteEntryUrl) {
163
164
  clearOwnedVmCache(httpModuleCache, httpModuleCacheOwners, remoteEntryUrl);
164
165
  clearOwnedVmCache(namespaceCache, namespaceCacheOwners, remoteEntryUrl);
165
166
  }
166
- function getBodyPreview(body) {
167
- return body.slice(0, 240).replace(/\s+/g, " ").trim();
168
- }
169
167
  async function fetchModuleSource(url, fetchTimeoutMs, fetchMaxBytes) {
170
168
  const res = await fetchWithTimeout(url, {}, fetchTimeoutMs);
171
169
  const text = await readResponseTextBounded(res, fetchMaxBytes ?? 10485760, url);
172
170
  if (!res.ok) throw new SsrEntryHttpError(url, res.status, res.statusText, getBodyPreview(text));
173
171
  return neutralizeBrowserPreloadHelpers(text);
174
172
  }
175
- function isHttpUrl(value) {
176
- return value.startsWith("http://") || value.startsWith("https://");
177
- }
178
173
  /** Resolve a specifier against the referencing module's URL; null for bare specifiers. */
179
174
  function resolveSpecifierUrl(specifier, referencerUrl) {
180
175
  if (isHttpUrl(specifier)) return specifier;
@@ -1,5 +1,6 @@
1
1
  import { i as SsrEntryLoaderConfig } from "../normalizeModuleFederationOptions-DNUlv-AL.js";
2
2
  //#region src/utils/ssrEntryLoader.d.ts
3
+ declare const SSR_ENTRY_LOADER_STATE_KEY: unique symbol;
3
4
  interface RemoteInfo {
4
5
  name: string;
5
6
  entry: string;
@@ -64,4 +65,4 @@ declare function ssrEntryLoaderPlugin(options?: SsrEntryLoaderOptions): {
64
65
  } | undefined>;
65
66
  };
66
67
  //#endregion
67
- export { SsrEntryHttpError, ssrEntryLoaderPlugin as default, neutralizeBrowserPreloadHelpers, revalidate };
68
+ export { SSR_ENTRY_LOADER_STATE_KEY, SsrEntryHttpError, ssrEntryLoaderPlugin as default, neutralizeBrowserPreloadHelpers, revalidate };
@@ -1,2 +1,2 @@
1
- import { i as ssrEntryLoaderPlugin, n as neutralizeBrowserPreloadHelpers, r as revalidate, t as SsrEntryHttpError } from "../ssrEntryLoader-DjZj-5Lw.js";
2
- export { SsrEntryHttpError, ssrEntryLoaderPlugin as default, neutralizeBrowserPreloadHelpers, revalidate };
1
+ import { a as ssrEntryLoaderPlugin, i as revalidate, n as SsrEntryHttpError, r as neutralizeBrowserPreloadHelpers, t as SSR_ENTRY_LOADER_STATE_KEY } from "../ssrEntryLoader-Bh2Tq1cZ.js";
2
+ export { SSR_ENTRY_LOADER_STATE_KEY, SsrEntryHttpError, ssrEntryLoaderPlugin as default, neutralizeBrowserPreloadHelpers, revalidate };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/vite",
3
- "version": "1.23.4",
3
+ "version": "1.23.6",
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
  }