@module-federation/vite 1.21.5 → 1.22.0

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.
@@ -1,5 +1,5 @@
1
- import { n as normalizePathForImport } from "./buildPaths-BkaQHrd2.js";
2
- import { _ as mfError, g as createModuleFederationError, l as hasPackageDependency, p as resolveImportPath } from "./dtsConstants-BsaLBaaK.js";
1
+ import { n as normalizePathForImport } from "./buildPaths-BoaQTkxt.js";
2
+ import { _ as mfError, g as createModuleFederationError, l as hasPackageDependency, p as resolveImportPath } from "./dtsConstants-BEGrtvcw.js";
3
3
  import fs from "fs";
4
4
  import * as path$1 from "node:path";
5
5
  import os from "os";
@@ -34,6 +34,7 @@ const getIPv4 = () => {
34
34
  return (getIpv4Interfaces()[0] || { address: localIpv4 }).address;
35
35
  };
36
36
  const DEV_TYPES_FOLDER = ".dev-server";
37
+ const UPDATE_DEBOUNCE_MS = 300;
37
38
  const forkDevWorkerPath = (() => {
38
39
  return resolveImportPath("@module-federation/dts-plugin/dist/fork-dev-worker.js");
39
40
  })();
@@ -256,11 +257,16 @@ function pluginDts(options) {
256
257
  disableLiveReload: devOptions.disableLiveReload,
257
258
  disableHotTypesReload: devOptions.disableHotTypesReload
258
259
  });
259
- const update = () => devWorker?.update();
260
+ let updateTimer;
261
+ const update = () => {
262
+ clearTimeout(updateTimer);
263
+ updateTimer = setTimeout(() => devWorker?.update(), UPDATE_DEBOUNCE_MS);
264
+ };
260
265
  server.watcher.on("change", update);
261
266
  server.watcher.on("add", update);
262
267
  server.watcher.on("unlink", update);
263
268
  server.httpServer?.once("close", () => {
269
+ clearTimeout(updateTimer);
264
270
  devWorker?.exit();
265
271
  server.watcher.off("change", update);
266
272
  server.watcher.off("add", update);
@@ -1,4 +1,4 @@
1
- import { t as EXTERNAL_URL_RE } from "./buildPaths-BkaQHrd2.js";
1
+ import { t as EXTERNAL_URL_RE } from "./buildPaths-BoaQTkxt.js";
2
2
  //#region src/utils/fetchWithTimeout.ts
3
3
  const DEFAULT_SSR_FETCH_TIMEOUT_MS = 1e4;
4
4
  const DEFAULT_SSR_FETCH_MAX_BYTES = 10 * 1024 * 1024;
@@ -188,7 +188,8 @@ async function resolveSharedExternal(id, resolvedShared) {
188
188
  }
189
189
  async function getOrCreateRunner(remoteOrigin, resolvedShared, fetchTimeoutMs, fetchMaxBytes) {
190
190
  const cacheKey = getRunnerCacheKey(remoteOrigin, resolvedShared, fetchTimeoutMs, fetchMaxBytes);
191
- if (runnerCache.has(cacheKey)) return runnerCache.get(cacheKey);
191
+ const cached = runnerCache.get(cacheKey);
192
+ if (cached) return cached.promise;
192
193
  const promise = (async () => {
193
194
  const viteRunner = await getModuleRunnerModule();
194
195
  if (!viteRunner) return null;
@@ -215,7 +216,10 @@ async function getOrCreateRunner(remoteOrigin, resolvedShared, fetchTimeoutMs, f
215
216
  return null;
216
217
  }
217
218
  })();
218
- runnerCache.set(cacheKey, promise);
219
+ runnerCache.set(cacheKey, {
220
+ remoteOrigin,
221
+ promise
222
+ });
219
223
  return promise;
220
224
  }
221
225
  const _path = () => nodeImport("path");
@@ -252,10 +256,19 @@ function parseRunnerInvokeResult(data) {
252
256
  * Version key for a resolved SSR entry. Derived from the remote's manifest
253
257
  * content so a redeploy at the same URL produces a different key, which in
254
258
  * turn produces different temp-file names — busting both our caches and
255
- * Node's ESM module cache. Convention-resolved entries (no manifest) get a
256
- * stable placeholder key and cannot be revalidated automatically.
259
+ * Node's ESM module cache. Convention-resolved entries (no manifest) use a
260
+ * stable placeholder key for ordinary loads; explicit `revalidate()` calls
261
+ * advance a process-local generation so the next import is fresh.
257
262
  */
258
263
  const UNVERSIONED = "unversioned";
264
+ const unversionedGenerations = /* @__PURE__ */ new Map();
265
+ let unversionedGlobalGeneration = 0;
266
+ function getUnversionedVersionKey(remoteEntryUrl) {
267
+ return `${UNVERSIONED}-${unversionedGlobalGeneration}-${unversionedGenerations.get(remoteEntryUrl) ?? 0}`;
268
+ }
269
+ function bumpUnversionedGeneration(remoteEntryUrl) {
270
+ unversionedGenerations.set(remoteEntryUrl, (unversionedGenerations.get(remoteEntryUrl) ?? 0) + 1);
271
+ }
259
272
  function hashString(value) {
260
273
  let hash = 2166136261;
261
274
  for (let i = 0; i < value.length; i++) {
@@ -392,16 +405,16 @@ function buildSsrEntryCandidates(ctx, options = {}) {
392
405
  if (!options.skipServerBuild) candidates.push({
393
406
  url: `${remoteOrigin}/__mf_server__/${filename}.ssr.js`,
394
407
  type: "module",
395
- versionKey: UNVERSIONED
408
+ versionKey: getUnversionedVersionKey(ctx.entryUrl)
396
409
  });
397
410
  candidates.push({
398
411
  url: `${base}.ssr.js`,
399
412
  type: "module",
400
- versionKey: UNVERSIONED
413
+ versionKey: getUnversionedVersionKey(ctx.entryUrl)
401
414
  }, {
402
415
  url: `${remoteOrigin}/__mf_ssr__/${filename}.ssr.js`,
403
416
  type: "module",
404
- versionKey: UNVERSIONED
417
+ versionKey: getUnversionedVersionKey(ctx.entryUrl)
405
418
  });
406
419
  return candidates;
407
420
  }
@@ -416,14 +429,14 @@ async function resolveSSREntryImpl(remoteEntryUrl, fetchTimeoutMs, fetchMaxBytes
416
429
  if (isSsrEntry(remoteEntryUrl)) return {
417
430
  url: remoteEntryUrl,
418
431
  type: "module",
419
- versionKey: UNVERSIONED
432
+ versionKey: getUnversionedVersionKey(remoteEntryUrl)
420
433
  };
421
434
  if (!isManifestEntry(remoteEntryUrl)) {
422
435
  const filename = getEntryFilename(remoteEntryUrl);
423
436
  const fromServerBuild = await headCheckSsrEntry({
424
437
  url: `${remoteEntryUrl.replace(/\/[^/]+$/, "")}/__mf_server__/${filename}.ssr.js`,
425
438
  type: "module",
426
- versionKey: UNVERSIONED
439
+ versionKey: getUnversionedVersionKey(remoteEntryUrl)
427
440
  }, fetchTimeoutMs);
428
441
  if (fromServerBuild) return fromServerBuild;
429
442
  }
@@ -457,7 +470,10 @@ async function getSSREntry(remoteEntryUrl, maxAgeMs, fetchTimeoutMs, fetchMaxByt
457
470
  manifestFetchCache.delete(makeUrlCacheKey(getManifestUrl(remoteEntryUrl), fetchTimeoutMs, fetchMaxBytes));
458
471
  const record = setSsrEntryCache(remoteEntryUrl, fetchTimeoutMs, fetchMaxBytes);
459
472
  const next = await record.promise.catch(() => null);
460
- if (previous && next && previous.versionKey !== next.versionKey) dropRemoteCaches(remoteEntryUrl);
473
+ if (previous && next && previous.versionKey !== next.versionKey) {
474
+ dropRemoteCaches(remoteEntryUrl);
475
+ await clearRunnerCaches(remoteEntryUrl);
476
+ }
461
477
  return record.promise;
462
478
  }
463
479
  /**
@@ -477,6 +493,19 @@ function dropRemoteCaches(remoteEntryUrl) {
477
493
  tempFilePathCache.delete(key);
478
494
  }
479
495
  }
496
+ async function clearRunnerCaches(remoteEntryUrl) {
497
+ let remoteOrigin;
498
+ if (remoteEntryUrl) try {
499
+ remoteOrigin = new URL(remoteEntryUrl).origin;
500
+ } catch {
501
+ return;
502
+ }
503
+ await Promise.all([...runnerCache.values()].filter((cached) => !remoteOrigin || cached.remoteOrigin === remoteOrigin).map(async (cached) => {
504
+ try {
505
+ (await cached.promise)?.clearCache?.();
506
+ } catch {}
507
+ }));
508
+ }
480
509
  /**
481
510
  * Drop the loader's caches so the next `loadEntry` re-resolves and re-fetches
482
511
  * remote SSR entries. Pass a remote entry URL to scope the invalidation to one
@@ -489,16 +518,19 @@ function dropRemoteCaches(remoteEntryUrl) {
489
518
  */
490
519
  function revalidate(remoteEntryUrl) {
491
520
  if (remoteEntryUrl) {
521
+ bumpUnversionedGeneration(remoteEntryUrl);
492
522
  for (const key of ssrEntryCache.keys()) if (key.endsWith(`::${remoteEntryUrl}`)) ssrEntryCache.delete(key);
493
523
  const manifestUrl = getManifestUrl(remoteEntryUrl);
494
524
  for (const key of manifestFetchCache.keys()) if (key.endsWith(`::${manifestUrl}`)) manifestFetchCache.delete(key);
495
525
  dropRemoteCaches(remoteEntryUrl);
496
526
  } else {
527
+ unversionedGlobalGeneration += 1;
497
528
  ssrEntryCache.clear();
498
529
  manifestFetchCache.clear();
499
530
  tempFileCache.clear();
500
531
  tempFilePathCache.clear();
501
532
  }
533
+ clearRunnerCaches(remoteEntryUrl);
502
534
  const federation = globalThis.__FEDERATION__;
503
535
  for (const instance of federation?.__INSTANCES__ ?? []) try {
504
536
  instance?.moduleCache?.clear?.();
@@ -555,6 +587,9 @@ function transformSsrCode(code, base, sharedPkgMap) {
555
587
  function isVitePreloadHelperSpecifier(specifier) {
556
588
  return specifier.includes("preload-helper");
557
589
  }
590
+ function getTempFileImportUrl(filePath, versionKey) {
591
+ return `file://${filePath}?v=${encodeURIComponent(versionKey)}`;
592
+ }
558
593
  /**
559
594
  * Fetch an HTTP ESM module, transform it, write it to a temp .js file and
560
595
  * return the file path. Recursively does the same for HTTP transitive imports
@@ -601,7 +636,7 @@ async function fetchEsmToTempFile(url, tmpDir, visited, pending, sharedPkgMap, v
601
636
  const subMap = /* @__PURE__ */ new Map();
602
637
  await Promise.all([...new Set(relImports)].filter((u) => u.startsWith("http://") || u.startsWith("https://")).map(async (u) => {
603
638
  const tmpPath = await fetchEsmToTempFile(u, tmpDir, visited, pending, sharedPkgMap, versionKey, fetchTimeoutMs, contextKey, fetchMaxBytes);
604
- subMap.set(u, `file://${tmpPath}`);
639
+ subMap.set(u, getTempFileImportUrl(tmpPath, versionKey));
605
640
  }));
606
641
  code = transformSsrCode(code, base, sharedPkgMap);
607
642
  for (const [httpUrl, fileUrl] of subMap) code = code.split(httpUrl).join(fileUrl);
@@ -631,7 +666,7 @@ async function importTempModule(filePath, versionKey) {
631
666
  }
632
667
  let warnedVmUnavailable = false;
633
668
  async function tryVmStrategy(ssrEntry, options) {
634
- const { loadViaVmStrategy, isVmStrategyAvailable } = await import("./ssrVmStrategy-D4KB-y3H.js");
669
+ const { loadViaVmStrategy, isVmStrategyAvailable } = await import("./ssrVmStrategy-Cx9WlTsx.js");
635
670
  if (!await isVmStrategyAvailable()) {
636
671
  if (!warnedVmUnavailable) {
637
672
  warnedVmUnavailable = true;
@@ -1,5 +1,5 @@
1
1
  import { t as findSharedKey } from "./sharedKeyMatcher-DiUzRVH1.js";
2
- import { c as readResponseTextBounded, n as neutralizeBrowserPreloadHelpers, s as fetchWithTimeout, t as SsrEntryHttpError } from "./ssrEntryLoader-CqtaiDUp.js";
2
+ import { c as readResponseTextBounded, n as neutralizeBrowserPreloadHelpers, s as fetchWithTimeout, t as SsrEntryHttpError } from "./ssrEntryLoader-CMVCDSsG.js";
3
3
  //#region src/utils/ssrVmStrategy.ts
4
4
  /**
5
5
  * vm.SourceTextModule strategy for loading remote SSR entries.
@@ -90,6 +90,7 @@ function createSyntheticModule(vm, specifier, namespace) {
90
90
  }
91
91
  const httpModuleCache = /* @__PURE__ */ new Map();
92
92
  const namespaceCache = /* @__PURE__ */ new Map();
93
+ const linkQueues = /* @__PURE__ */ new WeakMap();
93
94
  const contextIds = /* @__PURE__ */ new WeakMap();
94
95
  let nextContextId = 1;
95
96
  function getContextId(context) {
@@ -153,10 +154,21 @@ async function linkModule(vm, specifier, referencingModule, options) {
153
154
  if (url) return getHttpModule(vm, url, options);
154
155
  return createSyntheticModule(vm, specifier, await loadBareModule(specifier, options));
155
156
  }
157
+ async function linkModuleGraph(module, linker, cacheContext) {
158
+ const current = (linkQueues.get(cacheContext) ?? Promise.resolve()).catch(() => {}).then(async () => {
159
+ if (module.status === "unlinked") await module.link(linker);
160
+ });
161
+ linkQueues.set(cacheContext, current);
162
+ try {
163
+ await current;
164
+ } finally {
165
+ if (linkQueues.get(cacheContext) === current) linkQueues.delete(cacheContext);
166
+ }
167
+ }
156
168
  async function importDynamically(vm, specifier, referencingModule, options) {
157
169
  const linker = (spec, referencer) => linkModule(vm, spec, referencer, options);
158
170
  const module = await linker(specifier, referencingModule);
159
- if (module.status === "unlinked") await module.link(linker);
171
+ await linkModuleGraph(module, linker, options.cacheContext);
160
172
  if (module.status === "linked") await module.evaluate();
161
173
  return module;
162
174
  }
@@ -172,7 +184,7 @@ async function loadViaVmStrategy(entryUrl, options) {
172
184
  if (!namespaceCache.has(cacheKey)) namespaceCache.set(cacheKey, (async () => {
173
185
  const entryModule = await getHttpModule(vm, entryUrl, options);
174
186
  const linker = (specifier, referencingModule) => linkModule(vm, specifier, referencingModule, options);
175
- if (entryModule.status === "unlinked") await entryModule.link(linker);
187
+ await linkModuleGraph(entryModule, linker, options.cacheContext);
176
188
  if (entryModule.status === "linked") await entryModule.evaluate();
177
189
  return entryModule.namespace;
178
190
  })().catch((error) => {
@@ -1,2 +1,2 @@
1
- import { i as ssrEntryLoaderPlugin, n as neutralizeBrowserPreloadHelpers, r as revalidate, t as SsrEntryHttpError } from "../ssrEntryLoader-CqtaiDUp.js";
1
+ import { i as ssrEntryLoaderPlugin, n as neutralizeBrowserPreloadHelpers, r as revalidate, t as SsrEntryHttpError } from "../ssrEntryLoader-CMVCDSsG.js";
2
2
  export { 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.21.5",
3
+ "version": "1.22.0",
4
4
  "description": "Vite plugin for Module Federation",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -91,6 +91,6 @@
91
91
  "tsdown": "0.22.14",
92
92
  "typescript": "7.0.2",
93
93
  "vite": "8.2.0",
94
- "vitest": "4.1.10"
94
+ "vitest": "4.1.11"
95
95
  }
96
96
  }