@module-federation/vite 1.21.1 → 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
@@ -167,7 +167,7 @@ export default defineConfig({
167
167
  name: "host",
168
168
  remotes: {
169
169
  remote: {
170
- type: "module", // type "var" (default) for vite remote is supported with remote's `varFilename` option
170
+ type: "module", // omitted object type defaults to "var" with a warning
171
171
  name: "remote",
172
172
  entry: "https://[...]/remoteEntry.js",
173
173
  entryGlobalName: "remote",
@@ -237,6 +237,7 @@ export default defineConfig({
237
237
  ```
238
238
 
239
239
  The host app configuration specifies its name, the filename of its exposed remote entry remoteEntry.js, and importantly, the configuration of the remote application to load.
240
+ Object remotes that omit `type` retain the legacy `"var"` default and emit a warning. Use `type: "module"` for Vite ESM remotes. Use `type: "var"` explicitly only for global-format remotes, such as Webpack/Rspack remotes or a Vite remote's `varFilename` output.
240
241
  You can specify the place the host initialization file is injected with the **hostInitInjectLocation** option, which is described in the example code above.
241
242
  The **moduleParseTimeout** option allows you to configure the maximum time to wait for module parsing during the build process.
242
243
  The **moduleParseIdleTimeout** option is an alternative that resets the timer on every parsed module. It only fires when there has been no module activity for the configured duration, making it suitable for large codebases where the total build time exceeds the fixed timeout.
@@ -303,6 +304,18 @@ federation({
303
304
 
304
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.
305
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
+
306
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.
307
320
 
308
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,6 +1,6 @@
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";
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-DvgU8LIp.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
+ 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";
6
6
  import { existsSync, readFileSync, realpathSync, statSync, writeFileSync } from "fs";
@@ -502,64 +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
- for (const match of code.matchAll(staticFromPattern)) {
526
- if (!codePositions[match.index]) continue;
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 ?? "");
527
571
  descriptors.push({
528
572
  kind: "static",
529
573
  syntax: "import",
530
- source: match[3],
531
- typeOnly: isTypeOnlyClause(match[1])
574
+ source,
575
+ typeOnly
532
576
  });
533
577
  }
534
- for (const match of code.matchAll(dynamicPattern)) {
535
- if (!codePositions[match.index]) continue;
536
- descriptors.push({
578
+ for (const match of blanked.matchAll(DYNAMIC_PATTERN)) {
579
+ const source = readSource(code, match);
580
+ if (source) descriptors.push({
537
581
  kind: "dynamic",
538
582
  syntax: "import",
539
- source: match[2],
583
+ source,
540
584
  typeOnly: false
541
585
  });
542
586
  }
543
- for (const match of code.matchAll(requirePattern)) {
544
- if (!codePositions[match.index]) continue;
545
- descriptors.push({
587
+ for (const match of blanked.matchAll(REQUIRE_PATTERN)) {
588
+ const source = readSource(code, match);
589
+ if (source) descriptors.push({
546
590
  kind: "dynamic",
547
591
  syntax: "require",
548
- source: match[2],
592
+ source,
549
593
  typeOnly: false
550
594
  });
551
595
  }
552
- for (const match of code.matchAll(sideEffectPattern)) {
553
- if (!codePositions[match.index]) continue;
554
- descriptors.push({
596
+ for (const match of blanked.matchAll(SIDE_EFFECT_PATTERN)) {
597
+ const source = readSource(code, match);
598
+ if (source) descriptors.push({
555
599
  kind: "static",
556
600
  syntax: "import",
557
- source: match[2],
601
+ source,
558
602
  typeOnly: false
559
603
  });
560
604
  }
561
605
  return descriptors;
562
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
+ }
563
618
  function findModuleImportSources(code) {
564
619
  return Array.from(new Set(findModuleImportDescriptors(code).filter(({ syntax, typeOnly }) => syntax === "import" && !typeOnly).map(({ source }) => source)));
565
620
  }
@@ -573,6 +628,7 @@ function sanitizeDevEntryPath(devEntryPath) {
573
628
  */
574
629
  function rewriteEntryScripts(html, createProxySrc) {
575
630
  return html.replace(/<script\b(?=[^>]*\btype=["']module["'])(?=[^>]*\bsrc=["'][^"']+["'])([^>]*)>/gi, (match, attrs) => {
631
+ if (/\svite-ignore(?:\s|=|\/|$)/i.test(attrs)) return match;
576
632
  const srcMatch = attrs.match(/\bsrc=["']([^"']+)["']/i);
577
633
  if (!srcMatch) return match;
578
634
  const originalSrc = srcMatch[1];
@@ -617,6 +673,9 @@ function normalizeRemotes(remotes) {
617
673
  });
618
674
  return result;
619
675
  }
676
+ function warnOmittedObjectRemoteType(remoteKey) {
677
+ mfWarn(`Remote "${remoteKey}" omits type and defaults to 'var'. Set type: 'module' for Vite ESM remotes, or type: 'var' explicitly to silence this warning.`);
678
+ }
620
679
  function normalizeRemoteItem(key, remote) {
621
680
  warnOnReservedInternalNamePrefix(key, "remoteAlias");
622
681
  if (typeof remote === "string") {
@@ -639,6 +698,8 @@ function normalizeRemoteItem(key, remote) {
639
698
  shareScope: "default"
640
699
  };
641
700
  }
701
+ const typeOmitted = remote.type === void 0 || remote.type === null || remote.type === "";
702
+ if (typeOmitted) warnOmittedObjectRemoteType(key);
642
703
  return Object.assign({
643
704
  type: "var",
644
705
  name: key,
@@ -647,6 +708,7 @@ function normalizeRemoteItem(key, remote) {
647
708
  entryGlobalName: key
648
709
  }, {
649
710
  ...remote,
711
+ type: typeOmitted ? "var" : remote.type,
650
712
  internalName: toInternalModuleFederationName(remote.name || key)
651
713
  });
652
714
  }
@@ -662,7 +724,35 @@ function searchPackageVersion(sharedName) {
662
724
  }
663
725
  function inferVersionFromRequiredVersion(requiredVersion) {
664
726
  if (typeof requiredVersion !== "string") return void 0;
665
- 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
+ }
751
+ }
752
+ /** URI-style package specifiers are not semver ranges for runtime satisfy(). */
753
+ const PACKAGE_SPECIFIER_PROTOCOL_RE = /^[a-z][a-z\d+.-]*:/i;
754
+ function isProtocolRequiredVersion(requiredVersion) {
755
+ return PACKAGE_SPECIFIER_PROTOCOL_RE.test(requiredVersion.trim());
666
756
  }
667
757
  function getLitExportSubpathShares(sharedName) {
668
758
  if (sharedName !== "lit") return [];
@@ -691,6 +781,8 @@ function normalizeShareItem(key, shareItem) {
691
781
  requiredVersion: version ? `^${version}` : "*"
692
782
  }
693
783
  };
784
+ const userRequiredVersion = shareItem.requiredVersion;
785
+ const keepUserRequiredVersion = userRequiredVersion === false || typeof userRequiredVersion === "string" && userRequiredVersion.trim() !== "" && !isProtocolRequiredVersion(userRequiredVersion);
694
786
  return {
695
787
  name: key,
696
788
  from: "",
@@ -700,7 +792,7 @@ function normalizeShareItem(key, shareItem) {
700
792
  import: shareItem.import,
701
793
  singleton: shareItem.singleton || false,
702
794
  eager: shareItem.eager || false,
703
- requiredVersion: shareItem.requiredVersion !== void 0 ? shareItem.requiredVersion : isImportFalse || shareItem.version ? "*" : version ? `^${version}` : "*",
795
+ requiredVersion: keepUserRequiredVersion ? userRequiredVersion : isImportFalse || shareItem.version ? "*" : version ? `^${version}` : "*",
704
796
  strictVersion: !!shareItem.strictVersion,
705
797
  ...shareItem.suppressMissingImportWarning ? { suppressMissingImportWarning: true } : {},
706
798
  ...treeShaking ? { treeShaking: { ...treeShaking } } : {}
@@ -719,9 +811,9 @@ function normalizeShareItem(key, shareItem) {
719
811
  * a prefix; concrete subpaths materialize on import via the generic matcher.
720
812
  *
721
813
  * `react-dom/` must keep collapsing. A browser-wide `react-dom/` prefix would
722
- * also capture `react-dom/server*`, which is unsafe without environment-aware
723
- * filtering. Browser-safe entries such as `react-dom/client` stay explicit via
724
- * COMMON_SHARED_SUBPATHS (local provider) or an exact shared key.
814
+ * also capture `react-dom/server*`. Browser-safe entries (`react-dom/client`,
815
+ * `react-dom/profiling`) stay via COMMON_SHARED_SUBPATHS (local provider) or
816
+ * an exact shared key; SSR server* entries need an explicit shared key.
725
817
  */
726
818
  function normalizeSharedKey(key) {
727
819
  if (!key.endsWith("/")) return key;
@@ -1265,6 +1357,49 @@ function loadReactIslandConsumerModule(id) {
1265
1357
  function getVirtualModuleScopeKey(options) {
1266
1358
  return `${options.internalName}__${options.filename}`.replace(/[^a-zA-Z0-9_-]/g, "_");
1267
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
+ }
1268
1403
  //#endregion
1269
1404
  //#region src/virtualModules/virtualExposes.ts
1270
1405
  const EXPOSES_CSS_MAP_PLACEHOLDER = "__MF_EXPOSES_CSS_MAP__";
@@ -1414,28 +1549,17 @@ function isReactServerConditions(conditions) {
1414
1549
  //#region src/virtualModules/virtualRuntimeInitStatus.ts
1415
1550
  const virtualRuntimeInitStatus = new VirtualModule("runtimeInit");
1416
1551
  const runtimeInitModules = /* @__PURE__ */ new WeakMap();
1417
- const runtimeInitOwnerIds = /* @__PURE__ */ new WeakMap();
1418
- let nextRuntimeInitOwnerId = 1;
1419
1552
  const MODULE_CACHE_GLOBAL_KEY = "__mf_module_cache__";
1420
1553
  const REACT_SERVER_MODULE_CACHE_GLOBAL_KEY = "__mf_module_cache_react_server__";
1421
1554
  const MODULE_CACHE_SHARE_SCOPE_KEY = "module-federation.vite-module-cache";
1422
1555
  function getModuleCacheGlobalKey(exportConditions) {
1423
1556
  return isReactServerConditions(exportConditions) ? REACT_SERVER_MODULE_CACHE_GLOBAL_KEY : MODULE_CACHE_GLOBAL_KEY;
1424
1557
  }
1425
- function getRuntimeInitOwnerId(options) {
1426
- let ownerId = runtimeInitOwnerIds.get(options);
1427
- if (!ownerId) {
1428
- ownerId = nextRuntimeInitOwnerId++;
1429
- runtimeInitOwnerIds.set(options, ownerId);
1430
- }
1431
- return ownerId;
1432
- }
1433
1558
  function getRuntimeInitModule(options) {
1434
1559
  if (!options) return virtualRuntimeInitStatus;
1435
1560
  let runtimeInitModule = runtimeInitModules.get(options);
1436
1561
  if (!runtimeInitModule) {
1437
- const ownerId = getRuntimeInitOwnerId(options);
1438
- runtimeInitModule = new VirtualModule("runtimeInit", "__mf_v__", "", `${options.internalName}${MF_OWNER_INFIX}${ownerId}`);
1562
+ runtimeInitModule = new VirtualModule("runtimeInit", "__mf_v__", "", getFederationScopeKey(options));
1439
1563
  runtimeInitModules.set(options, runtimeInitModule);
1440
1564
  }
1441
1565
  return runtimeInitModule;
@@ -1448,7 +1572,7 @@ function getRuntimeRemoteCachePrefix(options) {
1448
1572
  }
1449
1573
  function getRuntimeRemoteAlias(alias, options) {
1450
1574
  if (!options) return alias;
1451
- return `${options.internalName}${MF_OWNER_INFIX}${getRuntimeInitOwnerId(options)}__${alias}`;
1575
+ return `${getFederationScopeKey(options)}__${alias}`;
1452
1576
  }
1453
1577
  function getRuntimeInitGlobalKey(ownerImportId) {
1454
1578
  return `__mf_init__${ownerImportId ?? virtualRuntimeInitStatus.getImportId()}__`;
@@ -1538,6 +1662,16 @@ globalThis[__mfCacheGlobalKey] ||= { share: {}, remote: {} };
1538
1662
  globalThis[__mfCacheGlobalKey].share ||= {};
1539
1663
  globalThis[__mfCacheGlobalKey].remote ||= {};
1540
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
+ };
1541
1675
  for (const __mfShareKey of Object.keys(__mfModuleCache.share)) {
1542
1676
  if (__mfShareKey.startsWith("default:")) {
1543
1677
  const __mfLegacyShareKey = __mfShareKey.slice("default:".length);
@@ -2218,6 +2352,14 @@ function getAdditionalTopLevelDeclaratorNames(source, start, codePositions) {
2218
2352
  canStartRegex = false;
2219
2353
  continue;
2220
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
+ }
2221
2363
  if (char === "/" && source[index + 1] === "/") {
2222
2364
  index = source.indexOf("\n", index + 2);
2223
2365
  if (index === -1) return names;
@@ -2454,6 +2596,9 @@ function getNamedExportsViaRegex(source, filePath, visited, scanState = { comple
2454
2596
  let previousCodeIndex = match.index - 1;
2455
2597
  while (previousCodeIndex >= 0 && (/\s/.test(source[previousCodeIndex]) || !codePositions[previousCodeIndex])) previousCodeIndex--;
2456
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;
2457
2602
  scanState.complete = false;
2458
2603
  break;
2459
2604
  }
@@ -2692,7 +2837,6 @@ const legacySharedVirtualModuleState = {
2692
2837
  warnedMissingImportFalse: /* @__PURE__ */ new Set()
2693
2838
  };
2694
2839
  const sharedVirtualModuleStates = /* @__PURE__ */ new WeakMap();
2695
- let nextSharedVirtualModuleOwnerId = 1;
2696
2840
  function getSharedVirtualModuleState(options) {
2697
2841
  if (!options) try {
2698
2842
  const currentOptions = getNormalizeModuleFederationOptions();
@@ -2709,7 +2853,7 @@ function getSharedVirtualModuleState(options) {
2709
2853
  materializedTreeShakingProviders: /* @__PURE__ */ new Set(),
2710
2854
  loadShareCacheMap: {},
2711
2855
  warnedMissingImportFalse: /* @__PURE__ */ new Set(),
2712
- ownerKey: `${options.internalName}${MF_OWNER_INFIX}${nextSharedVirtualModuleOwnerId++}`
2856
+ ownerKey: getFederationScopeKey(options)
2713
2857
  };
2714
2858
  sharedVirtualModuleStates.set(options, state);
2715
2859
  }
@@ -3008,7 +3152,7 @@ function generateLazyWorkspaceSingletonExports(namedExports, importSource, cache
3008
3152
  if (import.meta.env.SSR${serveLocalFallback ? " || (import.meta.env.DEV && typeof __mfLocalShare !== 'undefined')" : ""}) {
3009
3153
  ${applyLocalFallback}
3010
3154
  } else {
3011
- (__mfModuleCache.pendingShareLoads ||= []).push(initPromise.then(() => {
3155
+ __mfTrackPendingShareLoad(initPromise.then(() => {
3012
3156
  exportModule = ${getSharedCacheReadExpression(cacheDescriptor, treeShakingConsumer)};
3013
3157
  if (exportModule !== undefined) {
3014
3158
  __mfApplyLazyShareExports(exportModule);
@@ -3053,7 +3197,7 @@ function generateDeferredHostProvidedExports(namedExports, pkg, cacheDescriptor,
3053
3197
  };
3054
3198
  let exportModule = ${getSharedCacheReadExpression(cacheDescriptor, treeShakingConsumer)};
3055
3199
  if (exportModule === undefined) {
3056
- (__mfModuleCache.pendingShareLoads ||= []).push(initPromise.then(() => {
3200
+ __mfTrackPendingShareLoad(initPromise.then(() => {
3057
3201
  exportModule = ${getSharedCacheReadExpression(cacheDescriptor, treeShakingConsumer)};
3058
3202
  if (exportModule === undefined) {
3059
3203
  throw new Error("[Module Federation] Shared module ${pkg} was imported before federation bootstrap finished.");
@@ -3140,10 +3284,10 @@ function writeLoadShareModule(pkg, shareItem, command, _isRolldown, options, exp
3140
3284
  const hasCompleteExportCoverage = detectedNamedExports !== void 0;
3141
3285
  const isWorkspaceSingleton = isWorkspacePackage && shareItem.shareConfig.singleton === true;
3142
3286
  const isDefaultShareScope = shareItem.scope === void 0 || shareItem.scope === "default" || Array.isArray(shareItem.scope) && shareItem.scope[0] === "default";
3143
- 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));
3144
3288
  const servesRemoteSingletonFallback = command !== "build" && isRemoteOnlyContainer(resolvedOptions) && shareItem.shareConfig.singleton === true;
3145
3289
  const isConsumedByPeerSingleton = isSharedSingletonConsumedByPeer(pkg, resolvedOptions);
3146
- 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);
3147
3291
  const usesEagerWorkspaceFallback = hasCompleteExportCoverage && isWorkspaceSingleton && !servesRemoteSingletonFallback && (isConsumedByPeerSingleton || shareItem.shareConfig.eager === true);
3148
3292
  const usesDeferredTreeShakingFallback = hasCompleteExportCoverage && Boolean(treeShakingConsumer);
3149
3293
  const reactMixedModeGuard = pkg === "react" ? createReactMixedModeRuntimeGuard() : "";
@@ -3432,12 +3576,24 @@ function generateLocalSharedImportMap(options) {
3432
3576
  }
3433
3577
  `;
3434
3578
  }
3579
+ /** Expand `pkg/` → package root + matching usedShares; never returns the prefix string. */
3580
+ function expandSharedPrefixKey(prefixKey, used) {
3581
+ const base = prefixKey.slice(0, -1);
3582
+ const expanded = /* @__PURE__ */ new Set([base]);
3583
+ for (const pkg of used) {
3584
+ if (pkg.endsWith("/")) continue;
3585
+ if (pkg === base || pkg.startsWith(`${base}/`)) expanded.add(pkg);
3586
+ }
3587
+ return [...expanded];
3588
+ }
3435
3589
  function getOrderedUsedShares(options) {
3436
3590
  const resolvedOptions = options ?? getNormalizeModuleFederationOptions();
3437
- const shares = new Set(getUsedShares(options));
3591
+ const used = getUsedShares(options);
3592
+ const shares = new Set(used);
3438
3593
  Object.keys(resolvedOptions.shared ?? {}).forEach((pkg) => {
3439
3594
  if (!pkg.endsWith("/")) shares.add(pkg);
3440
3595
  });
3596
+ for (const [pkg, share] of Object.entries(resolvedOptions.shared ?? {})) if (pkg.endsWith("/") && share.shareConfig?.eager) for (const concrete of expandSharedPrefixKey(pkg, used)) shares.add(concrete);
3441
3597
  return orderSharedDependenciesFirst(Array.from(shares).sort((a, b) => {
3442
3598
  const priority = (pkg) => pkg === "react" ? 0 : pkg === "react-dom" ? 1 : pkg.startsWith("react/") ? 2 : 3;
3443
3599
  return priority(a) - priority(b) || a.localeCompare(b);
@@ -3447,7 +3603,12 @@ function getMaterializedShares(options) {
3447
3603
  const resolvedOptions = options ?? getNormalizeModuleFederationOptions();
3448
3604
  const scopedRegistrations = options ? usedSharesByOptions.get(options) : void 0;
3449
3605
  const shares = new Set(options && scopedRegistrations?.size ? materializedSharesByOptions.get(options) ?? [] : usedShares);
3450
- for (const [pkg, share] of Object.entries(resolvedOptions.shared ?? {})) if (!pkg.endsWith("/") && share.shareConfig?.eager) shares.add(pkg);
3606
+ const usedForEager = options ? usedSharesByOptions.get(options) ?? [] : usedShares;
3607
+ for (const [pkg, share] of Object.entries(resolvedOptions.shared ?? {})) {
3608
+ if (!share.shareConfig?.eager) continue;
3609
+ if (pkg.endsWith("/")) for (const concrete of expandSharedPrefixKey(pkg, usedForEager)) shares.add(concrete);
3610
+ else shares.add(pkg);
3611
+ }
3451
3612
  const configured = /* @__PURE__ */ new Map();
3452
3613
  for (const pkg of getOrderedUsedShares(options)) {
3453
3614
  const packageName = getPackageName(pkg);
@@ -3620,8 +3781,16 @@ const sharedProviderSelectionHelperCode = `const __mfOriginalProviderKey = Symbo
3620
3781
  strategy
3621
3782
  ) => {
3622
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;
3623
3792
  const scopes = Array.isArray(share.scope) ? share.scope : [share.scope || "default"];
3624
- const selectionVersions = __mfCreateProviderSelectionVersions(versions, strategy);
3793
+ const selectionVersions = __mfCreateProviderSelectionVersions(candidates, strategy);
3625
3794
  const shareScopeMap = {};
3626
3795
  for (const scope of scopes) {
3627
3796
  shareScopeMap[scope || "default"] = { [pkg]: selectionVersions };
@@ -4190,7 +4359,21 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
4190
4359
  }
4191
4360
  return runtimeScope;
4192
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
+ };
4193
4375
  const __mfGetRuntimeShareScope = (scopeName, hostScope) => {
4376
+ __mfKeepRegisteredShares(scopeName, hostScope);
4194
4377
  const isWebpackScope = !scopeRoot && Object.values(hostScope || {}).some((versions) =>
4195
4378
  Object.values(versions || {}).some(isWebpackProvider)
4196
4379
  );
@@ -4586,6 +4769,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
4586
4769
  );
4587
4770
  const providerEntry = __mfFindSharedProviderEntry(versionMap, provider);
4588
4771
  if (!providerEntry) return;
4772
+ if (usedShare.shareConfig?.import === false && __mfMatchesSharedProvider(provider, usedShare)) return;
4589
4773
  const { version } = providerEntry;
4590
4774
  if (!singleton && version !== usedShare.version) return;
4591
4775
  if (
@@ -4699,6 +4883,7 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
4699
4883
  scopeRootProvider: undefined
4700
4884
  };
4701
4885
  if (usedShare.canLiveRebind === false) return;
4886
+ if (usedShare.shareConfig?.import === false && __mfMatchesSharedProvider(provider, usedShare)) return;
4702
4887
  // Preserve a singleton already selected by another container. The bridge may
4703
4888
  // only replace the provisional local fallback seeded by this container.
4704
4889
  if (cachedShare !== undefined && cachedShareOwner !== mfName) return;
@@ -4865,6 +5050,11 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
4865
5050
  ) || share;
4866
5051
  const providerEntry = __mfFindSharedProviderEntry(versionMap, provider);
4867
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;
4868
5058
  const { version } = providerEntry;
4869
5059
  const currentProvider = versionMap?.[version];
4870
5060
  const loadedShare = await __mfLoadPinnedRuntimeShare(
@@ -4874,13 +5064,13 @@ function generateRemoteEntry(options, virtualExposesId = getVirtualExposesId(opt
4874
5064
  version,
4875
5065
  currentProvider,
4876
5066
  provider,
4877
- providerEntry.registered && !__mfMatchesSharedProvider(provider, share)
5067
+ providerEntry.registered
4878
5068
  );
4879
5069
  const providerSelection = loadedShare?.selection;
4880
5070
  const actualProvider = loadedShare?.provider;
4881
5071
  const resolved = loadedShare?.resolved;
4882
5072
  if (!providerSelection) return;
4883
- if (__mfMatchesSharedProvider(actualProvider, share)) return;
5073
+ if (__mfIsOwnStub(actualProvider)) return;
4884
5074
  if (resolved === undefined) return;
4885
5075
  const latestCachedShare = share.treeShaking
4886
5076
  ? __mfReadTreeShakingSharedSelection(__mfModuleCache.share, cacheDescriptor, mfName)
@@ -5021,6 +5211,16 @@ function generateHostAutoInitCode(remoteEntryImport, _command = "build", options
5021
5211
  __mfReadSharedCache(__mfModuleCache.share, cacheDescriptor) !== undefined &&
5022
5212
  __mfReadSharedCacheOwner(__mfModuleCache.share, cacheDescriptor) ${_command === "serve" ? "!== undefined" : `=== ${cacheOwner}`}
5023
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;
5024
5224
  await runtime.loadShare(pkg, {
5025
5225
  customShareInfo: { shareConfig: share.shareConfig }
5026
5226
  }).then(async (factory) => {
@@ -5122,6 +5322,7 @@ const usedRemotesMap = {};
5122
5322
  const usedRemotesByOptions = /* @__PURE__ */ new WeakMap();
5123
5323
  const dynamicRemotesByOptions = /* @__PURE__ */ new WeakMap();
5124
5324
  const staticRemotesByOptions = /* @__PURE__ */ new WeakMap();
5325
+ const preloadRemotesByOptions = /* @__PURE__ */ new WeakMap();
5125
5326
  const EMPTY_STATIC_REMOTES = /* @__PURE__ */ new Set();
5126
5327
  function getScopedUsedRemotesMap(options) {
5127
5328
  let scoped = usedRemotesByOptions.get(options);
@@ -5159,8 +5360,16 @@ function markStaticRemote(remote, options) {
5159
5360
  }
5160
5361
  remotes.add(remote);
5161
5362
  }
5162
- function getStaticRemotes(options) {
5163
- return staticRemotesByOptions.get(options) ?? EMPTY_STATIC_REMOTES;
5363
+ function markPreloadRemote(remote, options) {
5364
+ let remotes = preloadRemotesByOptions.get(options);
5365
+ if (!remotes) {
5366
+ remotes = /* @__PURE__ */ new Set();
5367
+ preloadRemotesByOptions.set(options, remotes);
5368
+ }
5369
+ remotes.add(remote);
5370
+ }
5371
+ function getPreloadRemotes(options) {
5372
+ return preloadRemotesByOptions.get(options) ?? EMPTY_STATIC_REMOTES;
5164
5373
  }
5165
5374
  function isDynamicOnlyRemote(remote, options) {
5166
5375
  return (dynamicRemotesByOptions.get(options)?.has(remote) ?? false) && !(staticRemotesByOptions.get(options)?.has(remote) ?? false);
@@ -5660,7 +5869,7 @@ const __mfCurrentScript = document.currentScript;
5660
5869
  const normalizedOptions = federationOptions ?? getNormalizeModuleFederationOptions();
5661
5870
  const isLoadedFirstClientBuild = (_command === "build" || viteConfig?.command === "build") && waitsForInit && !viteConfig?.build?.ssr && normalizedOptions.shareStrategy === "loaded-first";
5662
5871
  if (normalizedOptions.shareStrategy === "loaded-first" && !isLoadedFirstClientBuild) return [];
5663
- const remoteSources = isLoadedFirstClientBuild ? Array.from(getStaticRemotes(normalizedOptions)) : Object.entries(getUsedRemotesMap(federationOptions)).flatMap(([, remotes]) => Array.from(remotes)).filter((remote) => !federationOptions || !isDynamicOnlyRemote(remote, federationOptions));
5872
+ const remoteSources = isLoadedFirstClientBuild ? Array.from(getPreloadRemotes(normalizedOptions)) : Object.entries(getUsedRemotesMap(federationOptions)).flatMap(([, remotes]) => Array.from(remotes)).filter((remote) => !federationOptions || !isDynamicOnlyRemote(remote, federationOptions));
5664
5873
  return Array.from(new Set(remoteSources.flatMap((remote) => {
5665
5874
  const registration = getRemoteRegistration(remote, normalizedOptions.remotes, federationOptions);
5666
5875
  return registration && (registration.type === "module" || registration.type === "esm") && /^(?:https?:)?\/\//.test(registration.entry) ? [registration.entry] : [];
@@ -5680,7 +5889,7 @@ const __mfCurrentScript = document.currentScript;
5680
5889
  const normalizedOptions = federationOptions ?? getNormalizeModuleFederationOptions();
5681
5890
  const isLoadedFirstClientBuild = (_command === "build" || viteConfig?.command === "build") && waitsForInit && !viteConfig?.build?.ssr && normalizedOptions.shareStrategy === "loaded-first";
5682
5891
  const shouldPreloadRemotes = !options?.skipRemotePreload && (normalizedOptions.shareStrategy !== "loaded-first" || isLoadedFirstClientBuild);
5683
- const remoteSources = isLoadedFirstClientBuild ? Array.from(getStaticRemotes(normalizedOptions)) : Object.entries(getUsedRemotesMap(federationOptions)).flatMap(([, remotes]) => Array.from(remotes)).filter((remote) => !federationOptions || !isDynamicOnlyRemote(remote, federationOptions));
5892
+ const remoteSources = isLoadedFirstClientBuild ? Array.from(getPreloadRemotes(normalizedOptions)) : Object.entries(getUsedRemotesMap(federationOptions)).flatMap(([, remotes]) => Array.from(remotes)).filter((remote) => !federationOptions || !isDynamicOnlyRemote(remote, federationOptions));
5684
5893
  const remotePreloads = shouldPreloadRemotes ? remoteSources.sort().map((remote) => {
5685
5894
  const registration = isLoadedFirstClientBuild ? getRemoteRegistration(remote, normalizedOptions.remotes, federationOptions) : void 0;
5686
5895
  return `__mfPreloadRemote(${JSON.stringify(getRuntimeRemoteId(remote, normalizedOptions.remotes, federationOptions))}, ${JSON.stringify(remote)}${registration ? `, ${JSON.stringify(registration)}` : ""})`;
@@ -5922,6 +6131,10 @@ for (const __mfRemoteEntryPrefetchUrl of __mfRemoteEntryPrefetchUrls) {
5922
6131
  else if (Array.isArray(inputOptions)) entryFiles = inputOptions.filter((input) => !isReactRouterClientRouteInput(String(input))).map(resolveProjectId);
5923
6132
  else if (typeof inputOptions === "object") entryFiles = Object.values(inputOptions).filter((input) => !isReactRouterClientRouteInput(String(input))).map((input) => resolveProjectId(String(input)));
5924
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
+ }
5925
6138
  if (htmlFilePath) addHtmlScriptEntries(htmlFilePath);
5926
6139
  },
5927
6140
  buildStart() {
@@ -7028,8 +7241,30 @@ function getRemoteEntrySSRId(options) {
7028
7241
  return `${REMOTE_ENTRY_SSR_ID}:${getVirtualModuleScopeKey(options)}`;
7029
7242
  }
7030
7243
  function getSsrRemoteEntryFileName(browserFilename) {
7031
- const ext = browserFilename.match(/\.[^.]+$/)?.[0] || ".js";
7032
- return `${browserFilename.slice(0, browserFilename.length - ext.length)}.ssr${ext}`;
7244
+ let filename = browserFilename;
7245
+ if (filename.includes("[hash")) {
7246
+ filename = filename.replace(/(?:[._-]?\[hash(?::\d+)?\])/g, "");
7247
+ if (!/\.[^.]+$/.test(filename)) filename = `${filename}.js`;
7248
+ }
7249
+ const ext = filename.match(/\.[^.]+$/)?.[0] || ".js";
7250
+ return `${filename.slice(0, filename.length - ext.length)}.ssr${ext}`;
7251
+ }
7252
+ /** Singleton map for SSR loadShare: expand `pkg/` via usedShares; never serialize the prefix. */
7253
+ function getSsrSharedSingletons(options) {
7254
+ const used = getUsedShares(options);
7255
+ const result = {};
7256
+ for (const [pkg, share] of Object.entries(options.shared)) {
7257
+ if (!share.shareConfig.singleton) continue;
7258
+ if (pkg.endsWith("/")) {
7259
+ for (const concrete of expandSharedPrefixKey(pkg, used)) result[concrete] = {
7260
+ ...share,
7261
+ name: concrete
7262
+ };
7263
+ continue;
7264
+ }
7265
+ result[pkg] = share;
7266
+ }
7267
+ return result;
7033
7268
  }
7034
7269
  /**
7035
7270
  * Generates the SSR remote entry module.
@@ -7043,7 +7278,7 @@ function getSsrRemoteEntryFileName(browserFilename) {
7043
7278
  */
7044
7279
  function generateRemoteEntrySSR(options) {
7045
7280
  const virtualExposesSSRId = getVirtualExposesSSRId(options);
7046
- const sharedSingletons = Object.fromEntries(Object.entries(options.shared).filter(([, share]) => share.shareConfig.singleton));
7281
+ const sharedSingletons = getSsrSharedSingletons(options);
7047
7282
  return `
7048
7283
  import { init as runtimeInit } from "@module-federation/runtime";
7049
7284
 
@@ -7398,7 +7633,7 @@ const Manifest = (providedOptions) => {
7398
7633
  if (this.environment?.name === "ssr") return;
7399
7634
  let filesMap = {};
7400
7635
  const foundRemoteEntryFile = findRemoteEntryFile(mfOptions.filename, bundle);
7401
- const expectedSsrRemoteEntryFile = getSsrRemoteEntryFileName(foundRemoteEntryFile || mfOptions.filename);
7636
+ const expectedSsrRemoteEntryFile = getSsrRemoteEntryFileName(mfOptions.filename);
7402
7637
  const foundSsrRemoteEntryFile = Object.values(bundle).find((file) => file.fileName === expectedSsrRemoteEntryFile)?.fileName;
7403
7638
  if (foundRemoteEntryFile) remoteEntryFile = foundRemoteEntryFile;
7404
7639
  ssrRemoteEntryFile = foundSsrRemoteEntryFile || (_command === "serve" ? getSsrRemoteEntryFileName(resolveDevRemoteEntryFileName(mfOptions.filename)) : expectedSsrRemoteEntryFile);
@@ -7788,12 +8023,10 @@ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposes
7788
8023
  }
7789
8024
  function collectImportSources(code) {
7790
8025
  const sources = /* @__PURE__ */ new Map();
7791
- for (const match of code.matchAll(/(?:^|[;\n\r])\s*import\s+(?:[\s\S]*?\s+from\s+)?["']([^"']+)["']|import\(\s*["']([^"']+)["']\s*\)/g)) {
7792
- const source = match[1] || match[2];
7793
- if (source) {
7794
- const dynamic = !match[1];
7795
- sources.set(source, (sources.get(source) ?? true) && dynamic);
7796
- }
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);
7797
8030
  }
7798
8031
  return Array.from(sources, ([source, dynamic]) => ({
7799
8032
  source,
@@ -7810,7 +8043,7 @@ function pluginProxyRemoteEntry_default({ options, remoteEntryId, virtualExposes
7810
8043
  seen.add(id);
7811
8044
  let code;
7812
8045
  try {
7813
- code = readFileSync$1(id, "utf8");
8046
+ code = getScannableModuleSource(id, readFileSync$1(id, "utf8"));
7814
8047
  } catch {
7815
8048
  return [];
7816
8049
  }
@@ -8205,6 +8438,35 @@ function excludeSharedSubDependencies(shared) {
8205
8438
  }
8206
8439
  }
8207
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
+ }
8208
8470
  function proxySharedModule(options) {
8209
8471
  const { shared = {}, federationOptions, getParsePromise = () => Promise.resolve() } = options;
8210
8472
  let _config;
@@ -8286,6 +8548,7 @@ function proxySharedModule(options) {
8286
8548
  setTreeShakingBuildMode(command === "build", federationOptions);
8287
8549
  resetTreeShakingExports(federationOptions);
8288
8550
  emittedTreeShakingProviders.clear();
8551
+ sharedDependencyCache.clear();
8289
8552
  const isVinext = hasPackageDependency("vinext");
8290
8553
  const isAstro = hasPackageDependency("astro");
8291
8554
  const isRolldown = getIsRolldown(this);
@@ -8374,6 +8637,9 @@ function proxySharedModule(options) {
8374
8637
  }
8375
8638
  const key = findSharedKeyForSource(source, shared);
8376
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;
8377
8643
  if (useDirectReactImport && key === "react") return;
8378
8644
  if (isAssetLikeImport(source)) return;
8379
8645
  if (isBuildConfigImporter(importer)) return;
@@ -9562,7 +9828,7 @@ function registerEntryImports(options, projectRoot, recordShared = true, entryFi
9562
9828
  const { file, preloadRemotes } = pending.pop();
9563
9829
  if (visited.get(file) || visited.has(file) && !preloadRemotes) continue;
9564
9830
  visited.set(file, preloadRemotes);
9565
- const code = readFileSync(file, "utf8");
9831
+ const code = getScannableModuleSource(file, readFileSync(file, "utf8"));
9566
9832
  for (const { source: request, kind, typeOnly } of findModuleImportDescriptors(code)) {
9567
9833
  const isStatic = kind === "static" && !typeOnly;
9568
9834
  const remoteKey = preloadRemotes && isStatic && request ? Object.keys(options.remotes).find((name) => request === name || request.startsWith(`${name}/`)) : void 0;
@@ -9570,6 +9836,7 @@ function registerEntryImports(options, projectRoot, recordShared = true, entryFi
9570
9836
  if (remoteKey) {
9571
9837
  addUsedRemote(remoteKey, request, options);
9572
9838
  markStaticRemote(request, options);
9839
+ markPreloadRemote(request, options);
9573
9840
  } else if (sharedKey && recordShared) addUsedShares(request, options);
9574
9841
  else if (request && !typeOnly) enqueue(request, file, preloadRemotes && isStatic);
9575
9842
  }
@@ -9743,7 +10010,11 @@ export default __mfShared.default ?? __mfShared;`
9743
10010
  else optimizeDeps.include.push(key);
9744
10011
  for (const subpath of getCommonSharedSubpaths(key)) {
9745
10012
  const canResolveSubpath = canResolveSharedSubpath(subpath, root);
9746
- if (["react/compiler-runtime", "react-dom/client"].includes(subpath) && !canResolveSubpath) {
10013
+ if ([
10014
+ "react/compiler-runtime",
10015
+ "react-dom/client",
10016
+ "react-dom/profiling"
10017
+ ].includes(subpath) && !canResolveSubpath) {
9747
10018
  optimizeDeps.exclude.push(subpath);
9748
10019
  continue;
9749
10020
  }
@@ -9812,7 +10083,7 @@ function applyBuildTimeRuntimeDefines(define, options, { target, isAstro, defaul
9812
10083
  }
9813
10084
  function loadPluginDts(options) {
9814
10085
  if (options.dts === false) return [];
9815
- return [import("./pluginDts-sJeW2nss.js").then(({ default: pluginDts }) => pluginDts(options))];
10086
+ return [import("./pluginDts-C2bUY8h9.js").then(({ default: pluginDts }) => pluginDts(options))];
9816
10087
  }
9817
10088
  const INJECT_EXTERNAL_RUNTIME_CORE_PLUGIN = "@module-federation/vite/injectExternalRuntimeCorePlugin";
9818
10089
  function isInjectExternalRuntimeCorePlugin(specifier) {
@@ -5,11 +5,7 @@ const COMMON_SHARED_SUBPATHS = {
5
5
  "react/jsx-dev-runtime",
6
6
  "react/compiler-runtime"
7
7
  ],
8
- "react-dom": [
9
- "react-dom/client",
10
- "react-dom/server",
11
- "react-dom/server.browser"
12
- ],
8
+ "react-dom": ["react-dom/client", "react-dom/profiling"],
13
9
  "solid-js": [
14
10
  "solid-js/web",
15
11
  "solid-js/store",
@@ -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";
@@ -606,7 +606,7 @@ async function importTempModule(filePath, versionKey) {
606
606
  }
607
607
  let warnedVmUnavailable = false;
608
608
  async function tryVmStrategy(ssrEntry, options) {
609
- const { loadViaVmStrategy, isVmStrategyAvailable } = await import("./ssrVmStrategy-B0fCaHs5.js");
609
+ const { loadViaVmStrategy, isVmStrategyAvailable } = await import("./ssrVmStrategy-tpI6we9Q.js");
610
610
  if (!await isVmStrategyAvailable()) {
611
611
  if (!warnedVmUnavailable) {
612
612
  warnedVmUnavailable = true;
@@ -1,5 +1,5 @@
1
- import { a as getCommonSharedSubpaths } from "./pathNormalization-DvgU8LIp.js";
2
- import { c as readResponseTextBounded, n as neutralizeBrowserPreloadHelpers, s as fetchWithTimeout, t as SsrEntryHttpError } from "./ssrEntryLoader-0NnTjWR1.js";
1
+ import { a as getCommonSharedSubpaths } from "./pathNormalization-CHct3UwV.js";
2
+ import { c as readResponseTextBounded, n as neutralizeBrowserPreloadHelpers, s as fetchWithTimeout, t as SsrEntryHttpError } from "./ssrEntryLoader-BMtjS_Vl.js";
3
3
  //#region src/utils/ssrVmStrategy.ts
4
4
  /**
5
5
  * vm.SourceTextModule strategy for loading remote SSR entries.
@@ -1,2 +1,2 @@
1
- import { i as ssrEntryLoaderPlugin, n as neutralizeBrowserPreloadHelpers, r as revalidate, t as SsrEntryHttpError } from "../ssrEntryLoader-0NnTjWR1.js";
1
+ import { i as ssrEntryLoaderPlugin, n as neutralizeBrowserPreloadHelpers, r as revalidate, t as SsrEntryHttpError } from "../ssrEntryLoader-BMtjS_Vl.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.1",
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",
@@ -93,4 +93,4 @@
93
93
  "vite": "8.2.0",
94
94
  "vitest": "4.1.10"
95
95
  }
96
- }
96
+ }