@module-federation/vite 1.21.2 → 1.21.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.
package/README.md CHANGED
@@ -304,6 +304,18 @@ federation({
304
304
 
305
305
  `runtime-infer` is useful for local development and falls back to the full dependency when the required exports are not available. `server-calc` is recommended for deployments because it can use aggregated export metadata from all consumers.
306
306
 
307
+ Use `eager: true` when a shared dependency must provide exports during module evaluation. For example, icon libraries may call `jsx()` at module scope before deferred singleton initialization completes. Configure the remote's React share as follows; discovered React subpaths such as `react/jsx-runtime` inherit this setting:
308
+
309
+ ```ts
310
+ federation({
311
+ shared: {
312
+ react: { singleton: true, eager: true },
313
+ },
314
+ });
315
+ ```
316
+
317
+ This bundles the local fallback into the initial entry, so enable it only when synchronous evaluation requires it.
318
+
307
319
  Do not combine `eager: true` with `treeShaking`; eager shared dependencies are bundled into the initial entry and cannot use the on-demand tree-shaking path. Choose eager loading for small dependencies, or tree shaking for larger dependencies such as component libraries.
308
320
 
309
321
  With `server-calc`, the Vite build records the exports used by each application in its generated Module Federation metadata. A deployment service must then collect that metadata for all applications that share the same dependency and version, merge their `usedExports` lists, and use the resulting union to create one optimized secondary shared artifact. For example, if one application uses `Button` and another uses `Input`, the secondary artifact must contain both exports.
@@ -388,17 +388,22 @@ function resolveInstalledPackageJson(pkg, cwd, packageName, opts) {
388
388
  }
389
389
  let currentDir = path$1.dirname(resolvedPath);
390
390
  const rootDir = path$1.parse(currentDir).root;
391
+ let matchingPackage;
391
392
  while (true) {
392
393
  const packageJsonPath = path$1.join(currentDir, "package.json");
393
394
  if (existsSync(packageJsonPath)) {
394
395
  const packageJsonContent = readFileSync(packageJsonPath, "utf-8");
395
396
  try {
396
397
  const packageJson = JSON.parse(packageJsonContent);
397
- if (packageJson.name === packageName) return {
398
- path: packageJsonPath,
399
- dir: currentDir,
400
- packageJson
401
- };
398
+ if (packageJson.name === packageName) {
399
+ const packageInfo = {
400
+ path: packageJsonPath,
401
+ dir: currentDir,
402
+ packageJson
403
+ };
404
+ if (currentDir.endsWith(path$1.join("node_modules", packageName))) return packageInfo;
405
+ matchingPackage ??= packageInfo;
406
+ }
402
407
  } catch (error) {
403
408
  if (!(error instanceof SyntaxError)) throw error;
404
409
  }
@@ -406,6 +411,7 @@ function resolveInstalledPackageJson(pkg, cwd, packageName, opts) {
406
411
  if (currentDir === rootDir) break;
407
412
  currentDir = path$1.dirname(currentDir);
408
413
  }
414
+ return matchingPackage;
409
415
  } catch {
410
416
  let currentDir = cwd;
411
417
  const rootDir = path$1.parse(currentDir).root;
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as normalizePathForImport, r as rebaseImport } from "./buildPaths-BkaQHrd2.js";
2
- import { a as getPackageDetectionCwd, c as getSharedCacheDescriptor, d as packageNameDecode, f as packageNameEncode, g as createModuleFederationError, h as sharedCacheHelperCode, i as getIsRolldown, l as hasPackageDependency, m as setPackageDetectionCwd, n as getInstalledPackageEntry, o as getPackageName, p as resolveImportPath, r as getInstalledPackageJson, s as getPackageNameFromNodeModulePath, u as isNuxtProjectRoot, v as mfWarn } from "./dtsConstants-DyJrx8ah.js";
2
+ import { a as getPackageDetectionCwd, c as getSharedCacheDescriptor, d as packageNameDecode, f as packageNameEncode, g as createModuleFederationError, h as sharedCacheHelperCode, i as getIsRolldown, l as hasPackageDependency, m as setPackageDetectionCwd, n as getInstalledPackageEntry, o as getPackageName, p as resolveImportPath, r as getInstalledPackageJson, s as getPackageNameFromNodeModulePath, u as isNuxtProjectRoot, v as mfWarn } from "./dtsConstants-CScOzmdO.js";
3
3
  import { a as getCommonSharedSubpaths, c as isNodeModulePath, d as normalizeNodeModulePath, f as resolvePublicPath, i as getCommonSharedSubpathFromNodeModulePath, l as isNuxtClientBase, n as filterId, o as getMatchingNodeModuleSubpath, r as getBasePath$1, s as isAssetLikeImport, t as ensureTrailingSlash, u as isViteOptimizableEntry } from "./pathNormalization-CHct3UwV.js";
4
4
  import { createRequire } from "node:module";
5
5
  import * as fs$2 from "fs";
@@ -502,68 +502,119 @@ function createCodePositionMap(code) {
502
502
  }
503
503
  //#endregion
504
504
  //#region src/utils/htmlEntryUtils.ts
505
- function isTypeOnlyClause(clause) {
506
- const normalized = clause.trim();
507
- if (/^type\b/.test(normalized)) return true;
508
- const namedSpecifiers = normalized.match(/^\{([\s\S]*)\}$/)?.[1];
509
- if (!namedSpecifiers) return false;
505
+ const IDENTIFIER = String.raw`[$_\p{ID_Start}][$_\u200C\u200D\p{ID_Continue}]*`;
506
+ const NAMED_SPECIFIERS = String.raw`\{[^{}]*\}`;
507
+ const NAMESPACE_SPECIFIER = String.raw`\*\s*as\s+${IDENTIFIER}`;
508
+ const IMPORT_CLAUSE = String.raw`(?:(?<importType>type)\s+)?(?<importClause>${NAMESPACE_SPECIFIER}|${NAMED_SPECIFIERS}|${IDENTIFIER}(?:\s*,\s*(?:${NAMESPACE_SPECIFIER}|${NAMED_SPECIFIERS}))?)`;
509
+ const EXPORT_CLAUSE = String.raw`(?:(?<exportType>type)\s+)?(?<exportClause>\*(?:\s*as\s+(?:${IDENTIFIER}|"[^"]*"|'[^']*'))?|${NAMED_SPECIFIERS})`;
510
+ const SPECIFIER = String.raw`(?<quote>["'])(?<source>[^"'\r\n]*)\k<quote>`;
511
+ const KEYWORD_BOUNDARY = String.raw`(?<![.$\w])`;
512
+ const KEYWORD_GAP = String.raw`(?:\s+|(?=[{*]))`;
513
+ const STATIC_PATTERN = new RegExp(String.raw`${KEYWORD_BOUNDARY}(?:import${KEYWORD_GAP}${IMPORT_CLAUSE}|export${KEYWORD_GAP}${EXPORT_CLAUSE})\s*from\s*${SPECIFIER}`, "gud");
514
+ const DYNAMIC_PATTERN = new RegExp(String.raw`${KEYWORD_BOUNDARY}import\s*\(\s*${SPECIFIER}`, "gud");
515
+ const REQUIRE_PATTERN = new RegExp(String.raw`${KEYWORD_BOUNDARY}require\s*\(\s*${SPECIFIER}\s*\)`, "gud");
516
+ const SIDE_EFFECT_PATTERN = new RegExp(String.raw`${KEYWORD_BOUNDARY}import\s*${SPECIFIER}`, "gud");
517
+ /**
518
+ * Returns `code` with every non-code region blanked out to spaces so that
519
+ * regexes can run against real syntax only. String literals keep their
520
+ * delimiters (with a blank interior) so import specifiers stay locatable;
521
+ * comments, template literals, and regular expressions vanish entirely.
522
+ * The result has the same length as `code`, so match indices map back 1:1.
523
+ */
524
+ function blankNonCode(code) {
525
+ const codePositions = createCodePositionMap(code);
526
+ const chars = code.split("");
527
+ let index = 0;
528
+ while (index < code.length) {
529
+ if (codePositions[index]) {
530
+ index++;
531
+ continue;
532
+ }
533
+ const start = index;
534
+ while (index < code.length && !codePositions[index]) index++;
535
+ const quote = code[start];
536
+ const isString = (quote === "\"" || quote === "'") && index - start >= 2 && code[index - 1] === quote;
537
+ for (let position = start; position < index; position++) chars[position] = isString && (position === start || position === index - 1) ? quote : code[position] === "\n" ? "\n" : " ";
538
+ }
539
+ return chars.join("");
540
+ }
541
+ function isTypeOnlyNamedClause(clause) {
542
+ const namedSpecifiers = clause.trim().match(/^\{([\s\S]*)\}$/)?.[1];
543
+ if (namedSpecifiers === void 0) return false;
510
544
  const specifiers = namedSpecifiers.split(",").map((specifier) => specifier.trim()).filter(Boolean);
511
- return specifiers.length > 0 && specifiers.every((specifier) => /^type\s+\S/.test(specifier));
545
+ return specifiers.length > 0 && specifiers.every((specifier) => /^type\s+(?!as(?:\s|$))\S/.test(specifier));
546
+ }
547
+ function readSource(code, match) {
548
+ const range = match.indices?.groups?.source;
549
+ if (!range) return void 0;
550
+ const source = code.slice(range[0], range[1]);
551
+ return source.length > 0 ? source : void 0;
512
552
  }
513
553
  /**
514
554
  * Finds module imports while ignoring comments, strings, and regular expressions.
515
555
  * The descriptor keeps enough information for callers to distinguish runtime
516
556
  * static imports from type-only imports without introducing a parser dependency.
557
+ *
558
+ * Matching runs against a blanked copy of the code (see `blankNonCode`), so an
559
+ * `import` inside a comment or string can never match, comments inside a
560
+ * statement never change its classification, and a clause can never span
561
+ * multiple statements.
517
562
  */
518
563
  function findModuleImportDescriptors(code) {
519
- const codePositions = createCodePositionMap(code);
564
+ const blanked = blankNonCode(code);
520
565
  const descriptors = [];
521
- const staticFromPattern = /\b(?:import|export)\s+([\s\S]*?)\s+from\s*(["'])([^"']+)\2/g;
522
- const dynamicPattern = /\bimport\s*\(\s*(?:\/\*[\s\S]*?\*\/\s*)?(["'])([^"']+)\1\s*\)/g;
523
- const requirePattern = /\brequire\s*\(\s*(["'])([^"']+)\1\s*\)/g;
524
- const sideEffectPattern = /\bimport\s*(["'])([^"']+)\1/g;
525
- let match;
526
- while (match = staticFromPattern.exec(code)) {
527
- if (!codePositions[match.index]) {
528
- staticFromPattern.lastIndex = match.index + 1;
529
- continue;
530
- }
566
+ for (const match of blanked.matchAll(STATIC_PATTERN)) {
567
+ const source = readSource(code, match);
568
+ if (!source) continue;
569
+ const groups = match.groups;
570
+ const typeOnly = groups.importType !== void 0 || groups.exportType !== void 0 || isTypeOnlyNamedClause(groups.importClause ?? groups.exportClause ?? "");
531
571
  descriptors.push({
532
572
  kind: "static",
533
573
  syntax: "import",
534
- source: match[3],
535
- typeOnly: isTypeOnlyClause(match[1])
574
+ source,
575
+ typeOnly
536
576
  });
537
577
  }
538
- for (const match of code.matchAll(dynamicPattern)) {
539
- if (!codePositions[match.index]) continue;
540
- descriptors.push({
578
+ for (const match of blanked.matchAll(DYNAMIC_PATTERN)) {
579
+ const source = readSource(code, match);
580
+ if (source) descriptors.push({
541
581
  kind: "dynamic",
542
582
  syntax: "import",
543
- source: match[2],
583
+ source,
544
584
  typeOnly: false
545
585
  });
546
586
  }
547
- for (const match of code.matchAll(requirePattern)) {
548
- if (!codePositions[match.index]) continue;
549
- descriptors.push({
587
+ for (const match of blanked.matchAll(REQUIRE_PATTERN)) {
588
+ const source = readSource(code, match);
589
+ if (source) descriptors.push({
550
590
  kind: "dynamic",
551
591
  syntax: "require",
552
- source: match[2],
592
+ source,
553
593
  typeOnly: false
554
594
  });
555
595
  }
556
- for (const match of code.matchAll(sideEffectPattern)) {
557
- if (!codePositions[match.index]) continue;
558
- descriptors.push({
596
+ for (const match of blanked.matchAll(SIDE_EFFECT_PATTERN)) {
597
+ const source = readSource(code, match);
598
+ if (source) descriptors.push({
559
599
  kind: "static",
560
600
  syntax: "import",
561
- source: match[2],
601
+ source,
562
602
  typeOnly: false
563
603
  });
564
604
  }
565
605
  return descriptors;
566
606
  }
607
+ /**
608
+ * Returns the JavaScript/TypeScript portion of a module for import scanning.
609
+ * Vue and Svelte single-file components only contribute their `<script>`
610
+ * blocks so template markup and styles are never misread as code.
611
+ */
612
+ function getScannableModuleSource(id, code) {
613
+ if (!/\.(?:vue|svelte)(?:\?|$)/.test(id)) return code;
614
+ const blocks = [];
615
+ for (const match of code.matchAll(/<script\b[^>]*>([\s\S]*?)<\/script(?:\s[^>]*)?>/gi)) blocks.push(match[1]);
616
+ return blocks.join("\n");
617
+ }
567
618
  function findModuleImportSources(code) {
568
619
  return Array.from(new Set(findModuleImportDescriptors(code).filter(({ syntax, typeOnly }) => syntax === "import" && !typeOnly).map(({ source }) => source)));
569
620
  }
@@ -673,7 +724,30 @@ function searchPackageVersion(sharedName) {
673
724
  }
674
725
  function inferVersionFromRequiredVersion(requiredVersion) {
675
726
  if (typeof requiredVersion !== "string") return void 0;
676
- return requiredVersion.match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/)?.[0];
727
+ const isDigit = (char) => char !== void 0 && char >= "0" && char <= "9";
728
+ const isSuffixChar = (char) => char !== void 0 && (char >= "0" && char <= "9" || char >= "A" && char <= "Z" || char >= "a" && char <= "z" || char === "." || char === "-");
729
+ let index = 0;
730
+ while (index < requiredVersion.length) {
731
+ if (!isDigit(requiredVersion[index])) {
732
+ index += 1;
733
+ continue;
734
+ }
735
+ const start = index;
736
+ while (isDigit(requiredVersion[index])) index += 1;
737
+ if (requiredVersion[index] !== ".") continue;
738
+ index += 1;
739
+ if (!isDigit(requiredVersion[index])) continue;
740
+ while (isDigit(requiredVersion[index])) index += 1;
741
+ if (requiredVersion[index] !== ".") continue;
742
+ index += 1;
743
+ if (!isDigit(requiredVersion[index])) continue;
744
+ while (isDigit(requiredVersion[index])) index += 1;
745
+ if ((requiredVersion[index] === "-" || requiredVersion[index] === "+") && isSuffixChar(requiredVersion[index + 1])) {
746
+ index += 1;
747
+ while (isSuffixChar(requiredVersion[index])) index += 1;
748
+ }
749
+ return requiredVersion.slice(start, index);
750
+ }
677
751
  }
678
752
  /** URI-style package specifiers are not semver ranges for runtime satisfy(). */
679
753
  const PACKAGE_SPECIFIER_PROTOCOL_RE = /^[a-z][a-z\d+.-]*:/i;
@@ -1283,6 +1357,49 @@ function loadReactIslandConsumerModule(id) {
1283
1357
  function getVirtualModuleScopeKey(options) {
1284
1358
  return `${options.internalName}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_");
1285
1359
  }
1360
+ function compareConfigValues(a, b) {
1361
+ const serializedA = JSON.stringify(a);
1362
+ const serializedB = JSON.stringify(b);
1363
+ return serializedA < serializedB ? -1 : serializedA > serializedB ? 1 : 0;
1364
+ }
1365
+ function stableConfigValue(value, ancestors = /* @__PURE__ */ new WeakSet()) {
1366
+ if (typeof value === "function") return value.toString();
1367
+ if (!value || typeof value !== "object") return value;
1368
+ if (value instanceof Date) return {
1369
+ type: "Date",
1370
+ value: value.toISOString()
1371
+ };
1372
+ if (value instanceof RegExp) return {
1373
+ type: "RegExp",
1374
+ source: value.source,
1375
+ flags: value.flags
1376
+ };
1377
+ if (ancestors.has(value)) return "__circular__";
1378
+ ancestors.add(value);
1379
+ let result;
1380
+ if (value instanceof Map) {
1381
+ const entries = [...value.entries()].map(([key, item]) => [stableConfigValue(key, ancestors), stableConfigValue(item, ancestors)]);
1382
+ entries.sort(compareConfigValues);
1383
+ result = {
1384
+ type: "Map",
1385
+ entries
1386
+ };
1387
+ } else if (value instanceof Set) {
1388
+ const values = [...value].map((item) => stableConfigValue(item, ancestors));
1389
+ values.sort(compareConfigValues);
1390
+ result = {
1391
+ type: "Set",
1392
+ values
1393
+ };
1394
+ } else result = Array.isArray(value) ? value.map((item) => stableConfigValue(item, ancestors)) : Object.fromEntries(Object.entries(value).filter(([key]) => key !== "implementation").sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, item]) => [key, stableConfigValue(item, ancestors)]));
1395
+ ancestors.delete(value);
1396
+ return result;
1397
+ }
1398
+ function getFederationScopeKey(options) {
1399
+ const identity = JSON.stringify(stableConfigValue(options));
1400
+ const ownerId = BigInt(`0x${createHash("sha256").update(identity).digest("hex").slice(0, 12)}`);
1401
+ return `${options.internalName}${MF_OWNER_INFIX}${ownerId}`;
1402
+ }
1286
1403
  //#endregion
1287
1404
  //#region src/virtualModules/virtualExposes.ts
1288
1405
  const EXPOSES_CSS_MAP_PLACEHOLDER = "__MF_EXPOSES_CSS_MAP__";
@@ -1432,28 +1549,17 @@ function isReactServerConditions(conditions) {
1432
1549
  //#region src/virtualModules/virtualRuntimeInitStatus.ts
1433
1550
  const virtualRuntimeInitStatus = new VirtualModule("runtimeInit");
1434
1551
  const runtimeInitModules = /* @__PURE__ */ new WeakMap();
1435
- const runtimeInitOwnerIds = /* @__PURE__ */ new WeakMap();
1436
- let nextRuntimeInitOwnerId = 1;
1437
1552
  const MODULE_CACHE_GLOBAL_KEY = "__mf_module_cache__";
1438
1553
  const REACT_SERVER_MODULE_CACHE_GLOBAL_KEY = "__mf_module_cache_react_server__";
1439
1554
  const MODULE_CACHE_SHARE_SCOPE_KEY = "module-federation.vite-module-cache";
1440
1555
  function getModuleCacheGlobalKey(exportConditions) {
1441
1556
  return isReactServerConditions(exportConditions) ? REACT_SERVER_MODULE_CACHE_GLOBAL_KEY : MODULE_CACHE_GLOBAL_KEY;
1442
1557
  }
1443
- function getRuntimeInitOwnerId(options) {
1444
- let ownerId = runtimeInitOwnerIds.get(options);
1445
- if (!ownerId) {
1446
- ownerId = nextRuntimeInitOwnerId++;
1447
- runtimeInitOwnerIds.set(options, ownerId);
1448
- }
1449
- return ownerId;
1450
- }
1451
1558
  function getRuntimeInitModule(options) {
1452
1559
  if (!options) return virtualRuntimeInitStatus;
1453
1560
  let runtimeInitModule = runtimeInitModules.get(options);
1454
1561
  if (!runtimeInitModule) {
1455
- const ownerId = getRuntimeInitOwnerId(options);
1456
- runtimeInitModule = new VirtualModule("runtimeInit", "__mf_v__", "", `${options.internalName}${MF_OWNER_INFIX}${ownerId}`);
1562
+ runtimeInitModule = new VirtualModule("runtimeInit", "__mf_v__", "", getFederationScopeKey(options));
1457
1563
  runtimeInitModules.set(options, runtimeInitModule);
1458
1564
  }
1459
1565
  return runtimeInitModule;
@@ -1466,7 +1572,7 @@ function getRuntimeRemoteCachePrefix(options) {
1466
1572
  }
1467
1573
  function getRuntimeRemoteAlias(alias, options) {
1468
1574
  if (!options) return alias;
1469
- return `${options.internalName}${MF_OWNER_INFIX}${getRuntimeInitOwnerId(options)}__${alias}`;
1575
+ return `${getFederationScopeKey(options)}__${alias}`;
1470
1576
  }
1471
1577
  function getRuntimeInitGlobalKey(ownerImportId) {
1472
1578
  return `__mf_init__${ownerImportId ?? virtualRuntimeInitStatus.getImportId()}__`;
@@ -1556,6 +1662,16 @@ globalThis[__mfCacheGlobalKey] ||= { share: {}, remote: {} };
1556
1662
  globalThis[__mfCacheGlobalKey].share ||= {};
1557
1663
  globalThis[__mfCacheGlobalKey].remote ||= {};
1558
1664
  const __mfModuleCache = globalThis[__mfCacheGlobalKey];
1665
+ const __mfTrackPendingShareLoad = (promise) => {
1666
+ const pendingShareLoads = (__mfModuleCache.pendingShareLoads ||= []);
1667
+ pendingShareLoads.push(promise);
1668
+ const cleanup = () => {
1669
+ const index = pendingShareLoads.indexOf(promise);
1670
+ if (index !== -1) pendingShareLoads.splice(index, 1);
1671
+ };
1672
+ void promise.then(cleanup, cleanup);
1673
+ return promise;
1674
+ };
1559
1675
  for (const __mfShareKey of Object.keys(__mfModuleCache.share)) {
1560
1676
  if (__mfShareKey.startsWith("default:")) {
1561
1677
  const __mfLegacyShareKey = __mfShareKey.slice("default:".length);
@@ -2236,6 +2352,14 @@ function getAdditionalTopLevelDeclaratorNames(source, start, codePositions) {
2236
2352
  canStartRegex = false;
2237
2353
  continue;
2238
2354
  }
2355
+ if (char === "<" && source[index + 1] === "/") {
2356
+ const jsxClosingTag = source.slice(index).match(/^<\/\s*(?:[$_\p{ID_Start}][$_\u200C\u200D\p{ID_Continue}.:-]*\s*)?>/u);
2357
+ if (jsxClosingTag) {
2358
+ index += jsxClosingTag[0].length - 1;
2359
+ canStartRegex = false;
2360
+ continue;
2361
+ }
2362
+ }
2239
2363
  if (char === "/" && source[index + 1] === "/") {
2240
2364
  index = source.indexOf("\n", index + 2);
2241
2365
  if (index === -1) return names;
@@ -2472,6 +2596,9 @@ function getNamedExportsViaRegex(source, filePath, visited, scanState = { comple
2472
2596
  let previousCodeIndex = match.index - 1;
2473
2597
  while (previousCodeIndex >= 0 && (/\s/.test(source[previousCodeIndex]) || !codePositions[previousCodeIndex])) previousCodeIndex--;
2474
2598
  if (source[previousCodeIndex] === ".") continue;
2599
+ let nextCodeIndex = match.index + match[0].length;
2600
+ while (/\s/.test(source[nextCodeIndex] || "")) nextCodeIndex++;
2601
+ if (source[nextCodeIndex] === "(") continue;
2475
2602
  scanState.complete = false;
2476
2603
  break;
2477
2604
  }
@@ -2710,7 +2837,6 @@ const legacySharedVirtualModuleState = {
2710
2837
  warnedMissingImportFalse: /* @__PURE__ */ new Set()
2711
2838
  };
2712
2839
  const sharedVirtualModuleStates = /* @__PURE__ */ new WeakMap();
2713
- let nextSharedVirtualModuleOwnerId = 1;
2714
2840
  function getSharedVirtualModuleState(options) {
2715
2841
  if (!options) try {
2716
2842
  const currentOptions = getNormalizeModuleFederationOptions();
@@ -2727,7 +2853,7 @@ function getSharedVirtualModuleState(options) {
2727
2853
  materializedTreeShakingProviders: /* @__PURE__ */ new Set(),
2728
2854
  loadShareCacheMap: {},
2729
2855
  warnedMissingImportFalse: /* @__PURE__ */ new Set(),
2730
- ownerKey: `${options.internalName}${MF_OWNER_INFIX}${nextSharedVirtualModuleOwnerId++}`
2856
+ ownerKey: getFederationScopeKey(options)
2731
2857
  };
2732
2858
  sharedVirtualModuleStates.set(options, state);
2733
2859
  }
@@ -3026,7 +3152,7 @@ function generateLazyWorkspaceSingletonExports(namedExports, importSource, cache
3026
3152
  if (import.meta.env.SSR${serveLocalFallback ? " || (import.meta.env.DEV && typeof __mfLocalShare !== 'undefined')" : ""}) {
3027
3153
  ${applyLocalFallback}
3028
3154
  } else {
3029
- (__mfModuleCache.pendingShareLoads ||= []).push(initPromise.then(() => {
3155
+ __mfTrackPendingShareLoad(initPromise.then(() => {
3030
3156
  exportModule = ${getSharedCacheReadExpression(cacheDescriptor, treeShakingConsumer)};
3031
3157
  if (exportModule !== undefined) {
3032
3158
  __mfApplyLazyShareExports(exportModule);
@@ -3071,7 +3197,7 @@ function generateDeferredHostProvidedExports(namedExports, pkg, cacheDescriptor,
3071
3197
  };
3072
3198
  let exportModule = ${getSharedCacheReadExpression(cacheDescriptor, treeShakingConsumer)};
3073
3199
  if (exportModule === undefined) {
3074
- (__mfModuleCache.pendingShareLoads ||= []).push(initPromise.then(() => {
3200
+ __mfTrackPendingShareLoad(initPromise.then(() => {
3075
3201
  exportModule = ${getSharedCacheReadExpression(cacheDescriptor, treeShakingConsumer)};
3076
3202
  if (exportModule === undefined) {
3077
3203
  throw new Error("[Module Federation] Shared module ${pkg} was imported before federation bootstrap finished.");
@@ -3158,10 +3284,10 @@ function writeLoadShareModule(pkg, shareItem, command, _isRolldown, options, exp
3158
3284
  const hasCompleteExportCoverage = detectedNamedExports !== void 0;
3159
3285
  const isWorkspaceSingleton = isWorkspacePackage && shareItem.shareConfig.singleton === true;
3160
3286
  const isDefaultShareScope = shareItem.scope === void 0 || shareItem.scope === "default" || Array.isArray(shareItem.scope) && shareItem.scope[0] === "default";
3161
- const usesDeferredSingletonFallback = hasCompleteExportCoverage && (isWorkspacePackage || command !== "build" && isRemoteOnlyContainer(resolvedOptions) && shareItem.shareConfig.singleton === true || command === "build" && isRemoteOnlyContainer(resolvedOptions) && (shareItem.shareConfig.singleton === true ? !isDefaultShareScope : isDefaultShareScope));
3287
+ const usesDeferredSingletonFallback = hasCompleteExportCoverage && shareItem.shareConfig.eager !== true && (isWorkspacePackage || command !== "build" && isRemoteOnlyContainer(resolvedOptions) && shareItem.shareConfig.singleton === true || command === "build" && isRemoteOnlyContainer(resolvedOptions) && (shareItem.shareConfig.singleton === true || isDefaultShareScope));
3162
3288
  const servesRemoteSingletonFallback = command !== "build" && isRemoteOnlyContainer(resolvedOptions) && shareItem.shareConfig.singleton === true;
3163
3289
  const isConsumedByPeerSingleton = isSharedSingletonConsumedByPeer(pkg, resolvedOptions);
3164
- const usesEntryInjectedRemoteFallback = hasCompleteExportCoverage && command !== "build" && !isWorkspaceSingleton && isRemoteOnlyContainer(resolvedOptions) && shareItem.shareConfig.singleton === true && resolvedOptions.hostInitInjectLocation === "entry" && isConsumedByPeerSingleton;
3290
+ const usesEntryInjectedRemoteFallback = hasCompleteExportCoverage && !isWorkspaceSingleton && isRemoteOnlyContainer(resolvedOptions) && shareItem.shareConfig.singleton === true && resolvedOptions.hostInitInjectLocation === "entry" && (command === "build" || isConsumedByPeerSingleton);
3165
3291
  const usesEagerWorkspaceFallback = hasCompleteExportCoverage && isWorkspaceSingleton && !servesRemoteSingletonFallback && (isConsumedByPeerSingleton || shareItem.shareConfig.eager === true);
3166
3292
  const usesDeferredTreeShakingFallback = hasCompleteExportCoverage && Boolean(treeShakingConsumer);
3167
3293
  const reactMixedModeGuard = pkg === "react" ? createReactMixedModeRuntimeGuard() : "";
@@ -3655,8 +3781,16 @@ const sharedProviderSelectionHelperCode = `const __mfOriginalProviderKey = Symbo
3655
3781
  strategy
3656
3782
  ) => {
3657
3783
  if (!versions || !share) return undefined;
3784
+ // import:false stubs provide nothing, so they are never a selectable
3785
+ // provider and must not be satisfy-checked. Skip the runtime entirely
3786
+ // when nothing remains: it treats an empty map as version "0" and
3787
+ // warns that it fails even a "*" requirement.
3788
+ const candidates = Object.fromEntries(
3789
+ Object.entries(versions).filter(([, provider]) => provider?.shareConfig?.import !== false)
3790
+ );
3791
+ if (Object.keys(candidates).length === 0) return undefined;
3658
3792
  const scopes = Array.isArray(share.scope) ? share.scope : [share.scope || "default"];
3659
- const selectionVersions = __mfCreateProviderSelectionVersions(versions, strategy);
3793
+ const selectionVersions = __mfCreateProviderSelectionVersions(candidates, strategy);
3660
3794
  const shareScopeMap = {};
3661
3795
  for (const scope of scopes) {
3662
3796
  shareScopeMap[scope || "default"] = { [pkg]: selectionVersions };
@@ -4225,7 +4359,21 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
4225
4359
  }
4226
4360
  return runtimeScope;
4227
4361
  };
4362
+ // runtimeInit() lets runtime plugins register providers (public
4363
+ // registerShared) before initShareScopeMap() replaces the runtime's own
4364
+ // scope map. Carry those registrations over; the runtime's own copies of
4365
+ // usedShared keep registering lazily through loadShare().
4366
+ const __mfKeepRegisteredShares = (scopeName, scope) => {
4367
+ for (const [pkg, versions] of Object.entries(initRes.shareScopeMap?.[scopeName] || {})) {
4368
+ for (const [version, provider] of Object.entries(versions || {})) {
4369
+ if (!provider || provider.get === usedShared[pkg]?.get) continue;
4370
+ const target = scope[pkg] ||= {};
4371
+ if (target[version] === undefined) target[version] = provider;
4372
+ }
4373
+ }
4374
+ };
4228
4375
  const __mfGetRuntimeShareScope = (scopeName, hostScope) => {
4376
+ __mfKeepRegisteredShares(scopeName, hostScope);
4229
4377
  const isWebpackScope = !scopeRoot && Object.values(hostScope || {}).some((versions) =>
4230
4378
  Object.values(versions || {}).some(isWebpackProvider)
4231
4379
  );
@@ -4621,6 +4769,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
4621
4769
  );
4622
4770
  const providerEntry = __mfFindSharedProviderEntry(versionMap, provider);
4623
4771
  if (!providerEntry) return;
4772
+ if (usedShare.shareConfig?.import === false && __mfMatchesSharedProvider(provider, usedShare)) return;
4624
4773
  const { version } = providerEntry;
4625
4774
  if (!singleton && version !== usedShare.version) return;
4626
4775
  if (
@@ -4734,6 +4883,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
4734
4883
  scopeRootProvider: undefined
4735
4884
  };
4736
4885
  if (usedShare.canLiveRebind === false) return;
4886
+ if (usedShare.shareConfig?.import === false && __mfMatchesSharedProvider(provider, usedShare)) return;
4737
4887
  // Preserve a singleton already selected by another container. The bridge may
4738
4888
  // only replace the provisional local fallback seeded by this container.
4739
4889
  if (cachedShare !== undefined && cachedShareOwner !== mfName) return;
@@ -4900,6 +5050,11 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
4900
5050
  ) || share;
4901
5051
  const providerEntry = __mfFindSharedProviderEntry(versionMap, provider);
4902
5052
  if (!providerEntry) return;
5053
+ // A provider registered on this instance through the public registerShared
5054
+ // API carries this container's own name, so tell the own stub apart by its
5055
+ // import:false config rather than by provenance.
5056
+ const __mfIsOwnStub = (candidate) => candidate === share || candidate?.shareConfig?.import === false;
5057
+ if (__mfIsOwnStub(provider)) return;
4903
5058
  const { version } = providerEntry;
4904
5059
  const currentProvider = versionMap?.[version];
4905
5060
  const loadedShare = await __mfLoadPinnedRuntimeShare(
@@ -4909,13 +5064,13 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
4909
5064
  version,
4910
5065
  currentProvider,
4911
5066
  provider,
4912
- providerEntry.registered && !__mfMatchesSharedProvider(provider, share)
5067
+ providerEntry.registered
4913
5068
  );
4914
5069
  const providerSelection = loadedShare?.selection;
4915
5070
  const actualProvider = loadedShare?.provider;
4916
5071
  const resolved = loadedShare?.resolved;
4917
5072
  if (!providerSelection) return;
4918
- if (__mfMatchesSharedProvider(actualProvider, share)) return;
5073
+ if (__mfIsOwnStub(actualProvider)) return;
4919
5074
  if (resolved === undefined) return;
4920
5075
  const latestCachedShare = share.treeShaking
4921
5076
  ? __mfReadTreeShakingSharedSelection(__mfModuleCache.share, cacheDescriptor, mfName)
@@ -5056,6 +5211,16 @@ function generateHostAutoInitCode(remoteEntryImport, _command = "build", options
5056
5211
  __mfReadSharedCache(__mfModuleCache.share, cacheDescriptor) !== undefined &&
5057
5212
  __mfReadSharedCacheOwner(__mfModuleCache.share, cacheDescriptor) ${_command === "serve" ? "!== undefined" : `=== ${cacheOwner}`}
5058
5213
  ) return;
5214
+ // An import:false share has nothing to load until a foreign provider
5215
+ // registers: its own stub getter throws by construction.
5216
+ if (
5217
+ share.shareConfig?.import === false &&
5218
+ !(Array.isArray(share.scope) ? share.scope : [share.scope || 'default']).some((scopeName) =>
5219
+ Object.values(runtime.shareScopeMap?.[scopeName]?.[pkg] || {}).some(
5220
+ (provider) => provider?.shareConfig?.import !== false
5221
+ )
5222
+ )
5223
+ ) return;
5059
5224
  await runtime.loadShare(pkg, {
5060
5225
  customShareInfo: { shareConfig: share.shareConfig }
5061
5226
  }).then(async (factory) => {
@@ -5966,6 +6131,10 @@ for (const __mfRemoteEntryPrefetchUrl of __mfRemoteEntryPrefetchUrls) {
5966
6131
  else if (Array.isArray(inputOptions)) entryFiles = inputOptions.filter((input) => !isReactRouterClientRouteInput(String(input))).map(resolveProjectId);
5967
6132
  else if (typeof inputOptions === "object") entryFiles = Object.values(inputOptions).filter((input) => !isReactRouterClientRouteInput(String(input))).map((input) => resolveProjectId(String(input)));
5968
6133
  if (entryFiles.length > 0) htmlFilePath = getFirstHtmlEntryFile(entryFiles);
6134
+ if (config.command === "serve" && !htmlFilePath) {
6135
+ const rootIndexHtml = path$1.resolve(config.root, "index.html");
6136
+ if (fs$2.existsSync(rootIndexHtml)) htmlFilePath = rootIndexHtml;
6137
+ }
5969
6138
  if (htmlFilePath) addHtmlScriptEntries(htmlFilePath);
5970
6139
  },
5971
6140
  buildStart() {
@@ -7854,12 +8023,10 @@ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposes
7854
8023
  }
7855
8024
  function collectImportSources(code) {
7856
8025
  const sources = /* @__PURE__ */ new Map();
7857
- for (const match of code.matchAll(/(?:^|[;\n\r])\s*import\s+(?:[\s\S]*?\s+from\s+)?["']([^"']+)["']|import\(\s*["']([^"']+)["']\s*\)/g)) {
7858
- const source = match[1] || match[2];
7859
- if (source) {
7860
- const dynamic = !match[1];
7861
- sources.set(source, (sources.get(source) ?? true) && dynamic);
7862
- }
8026
+ for (const { source, kind, syntax, typeOnly } of findModuleImportDescriptors(code)) {
8027
+ if (syntax !== "import" || typeOnly) continue;
8028
+ const dynamic = kind === "dynamic";
8029
+ sources.set(source, (sources.get(source) ?? true) && dynamic);
7863
8030
  }
7864
8031
  return Array.from(sources, ([source, dynamic]) => ({
7865
8032
  source,
@@ -7876,7 +8043,7 @@ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposes
7876
8043
  seen.add(id);
7877
8044
  let code;
7878
8045
  try {
7879
- code = readFileSync$1(id, "utf8");
8046
+ code = getScannableModuleSource(id, readFileSync$1(id, "utf8"));
7880
8047
  } catch {
7881
8048
  return [];
7882
8049
  }
@@ -8271,6 +8438,35 @@ function excludeSharedSubDependencies(shared) {
8271
8438
  }
8272
8439
  }
8273
8440
  }
8441
+ const sharedDependencyCache = /* @__PURE__ */ new Map();
8442
+ /** Whether `dependency` is reachable through the shared package's manifest dependencies. */
8443
+ function isSharedPackageDependency(sharedKey, dependency) {
8444
+ const sharedPackage = getPackageName(sharedKey);
8445
+ let reachable = sharedDependencyCache.get(sharedPackage);
8446
+ if (!reachable) {
8447
+ reachable = /* @__PURE__ */ new Set();
8448
+ const visited = /* @__PURE__ */ new Set();
8449
+ const queue = [getInstalledPackageJson(sharedPackage, { packageName: sharedPackage })];
8450
+ for (let installed = queue.shift(); installed; installed = queue.shift()) {
8451
+ if (visited.has(installed.dir)) continue;
8452
+ visited.add(installed.dir);
8453
+ const manifest = installed.packageJson;
8454
+ for (const dep of Object.keys({
8455
+ ...manifest.dependencies,
8456
+ ...manifest.peerDependencies,
8457
+ ...manifest.optionalDependencies
8458
+ })) {
8459
+ reachable.add(dep);
8460
+ queue.push(getInstalledPackageJson(dep, {
8461
+ cwd: installed.dir,
8462
+ packageName: dep
8463
+ }));
8464
+ }
8465
+ }
8466
+ sharedDependencyCache.set(sharedPackage, reachable);
8467
+ }
8468
+ return reachable.has(dependency);
8469
+ }
8274
8470
  function proxySharedModule(options) {
8275
8471
  const { shared = {}, federationOptions, getParsePromise = () => Promise.resolve() } = options;
8276
8472
  let _config;
@@ -8352,6 +8548,7 @@ function proxySharedModule(options) {
8352
8548
  setTreeShakingBuildMode(command === "build", federationOptions);
8353
8549
  resetTreeShakingExports(federationOptions);
8354
8550
  emittedTreeShakingProviders.clear();
8551
+ sharedDependencyCache.clear();
8355
8552
  const isVinext = hasPackageDependency("vinext");
8356
8553
  const isAstro = hasPackageDependency("astro");
8357
8554
  const isRolldown = getIsRolldown(this);
@@ -8440,6 +8637,9 @@ function proxySharedModule(options) {
8440
8637
  }
8441
8638
  const key = findSharedKeyForSource(source, shared);
8442
8639
  if (!key) return;
8640
+ const importerPackage = importer ? getPackageNameFromNodeModulePath(importer) : void 0;
8641
+ if (importerPackage === getPackageName(key)) return;
8642
+ if (importerPackage && isSharedPackageDependency(key, importerPackage)) return;
8443
8643
  if (useDirectReactImport && key === "react") return;
8444
8644
  if (isAssetLikeImport(source)) return;
8445
8645
  if (isBuildConfigImporter(importer)) return;
@@ -9628,7 +9828,7 @@ function registerEntryImports(options, projectRoot, recordShared = true, entryFi
9628
9828
  const { file, preloadRemotes } = pending.pop();
9629
9829
  if (visited.get(file) || visited.has(file) && !preloadRemotes) continue;
9630
9830
  visited.set(file, preloadRemotes);
9631
- const code = readFileSync(file, "utf8");
9831
+ const code = getScannableModuleSource(file, readFileSync(file, "utf8"));
9632
9832
  for (const { source: request, kind, typeOnly } of findModuleImportDescriptors(code)) {
9633
9833
  const isStatic = kind === "static" && !typeOnly;
9634
9834
  const remoteKey = preloadRemotes && isStatic && request ? Object.keys(options.remotes).find((name) => request === name || request.startsWith(`${name}/`)) : void 0;
@@ -9883,7 +10083,7 @@ function applyBuildTimeRuntimeDefines(define, options, { target, isAstro, defaul
9883
10083
  }
9884
10084
  function loadPluginDts(options) {
9885
10085
  if (options.dts === false) return [];
9886
- return [import("./pluginDts-sJeW2nss.js").then(({ default: pluginDts }) => pluginDts(options))];
10086
+ return [import("./pluginDts-C2bUY8h9.js").then(({ default: pluginDts }) => pluginDts(options))];
9887
10087
  }
9888
10088
  const INJECT_EXTERNAL_RUNTIME_CORE_PLUGIN = "@module-federation/vite/injectExternalRuntimeCorePlugin";
9889
10089
  function isInjectExternalRuntimeCorePlugin(specifier) {
@@ -1,5 +1,5 @@
1
1
  import { n as normalizePathForImport } from "./buildPaths-BkaQHrd2.js";
2
- import { _ as mfError, g as createModuleFederationError, l as hasPackageDependency, p as resolveImportPath } from "./dtsConstants-DyJrx8ah.js";
2
+ import { _ as mfError, g as createModuleFederationError, l as hasPackageDependency, p as resolveImportPath } from "./dtsConstants-CScOzmdO.js";
3
3
  import fs from "fs";
4
4
  import * as path$1 from "node:path";
5
5
  import os from "os";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/vite",
3
- "version": "1.21.2",
3
+ "version": "1.21.3",
4
4
  "description": "Vite plugin for Module Federation",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -28,7 +28,7 @@
28
28
  "lib/**/*"
29
29
  ],
30
30
  "scripts": {
31
- "prepare": "husky install",
31
+ "prepare": "husky",
32
32
  "fmt": "oxfmt src",
33
33
  "fmt.check": "oxfmt --check src",
34
34
  "dev": "tsdown --watch",