useblocks 1.1.85-dev.2 → 1.1.85-dev.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.
Files changed (43) hide show
  1. package/iframe/.vite/manifest.json +4 -4
  2. package/iframe/js/{discussHandlers.BtCUuj58.js → discussHandlers.BxlCsZ-4.js} +2 -2
  3. package/iframe/js/iframe-eblock.body.js +1 -1
  4. package/iframe/js/iframe-eblock.js +1 -1
  5. package/iframe/js/iframe.body.js +2 -2
  6. package/iframe/js/iframe.js +1 -1
  7. package/node_modules/useblocks-ui-kit/package.json +1 -1
  8. package/package.json +2 -2
  9. package/runtime/react/index.js +1 -1
  10. package/static/core/{ef3477dd.js → 14a79b09.js} +1 -1
  11. package/static/core/{6c7df338.js → 199dc589.js} +1 -1
  12. package/static/core/{ca873214.js → 230f23ec.js} +1 -1
  13. package/static/core/{181088a9.js → 30f16e98.js} +1 -1
  14. package/static/core/{6fdfa46d.js → 34730be9.js} +1 -1
  15. package/static/core/{9b7f52a5.js → 364dcebf.js} +1 -1
  16. package/static/core/{43affb86.js → 4916b3a1.js} +1 -1
  17. package/static/core/{c789db17.js → 4f790fdc.js} +1 -1
  18. package/static/core/{d74b0853.js → 520ec797.js} +3 -3
  19. package/static/core/{2639f789.js → 57c114f0.js} +1 -1
  20. package/static/core/{cab426fa.js → 5bf994b4.js} +1 -1
  21. package/static/core/{13563d2c.js → 6541580a.js} +1 -1
  22. package/static/core/{a72f1f53.js → 79431a89.js} +1 -1
  23. package/static/core/{b502ca96.js → 8fb0fe48.js} +1 -1
  24. package/static/core/{fad0a613.js → 9a70cc1e.js} +1 -1
  25. package/static/core/{6589e3b8.js → a15ab67d.js} +1 -1
  26. package/static/core/{576452f9.js → adf6e008.js} +1 -1
  27. package/static/core/{f0f90842.js → b743a76d.js} +1 -1
  28. package/static/core/{c6d342f2.js → b97bd382.js} +6 -6
  29. package/static/core/{92e10a15.js → ddb5a35d.js} +1 -1
  30. package/static/core/{70b2c337.js → dfddbd68.js} +1 -1
  31. package/static/core/{6eeae572.js → f3eda1c7.js} +1 -1
  32. package/static/core/{5f0c0e3a.js → ffb36e93.js} +1 -1
  33. package/static/umd-runtime/runtime.js +1 -1
  34. package/{useblocks-core.4865197d.js → useblocks-core.168aeeb1.js} +8 -8
  35. package/useblocks-esm.js +6 -6
  36. package/{useblocks-po.4865197d.js → useblocks-po.168aeeb1.js} +1 -1
  37. package/useblocks.js +6 -6
  38. package/vite/index.cjs +99 -3
  39. package/vite/index.js +99 -3
  40. package/webpack/index.cjs +93 -0
  41. package/webpack/index.js +93 -0
  42. /package/iframe/{iframe-eblock.4865197d.html → iframe-eblock.168aeeb1.html} +0 -0
  43. /package/iframe/{iframe.4865197d.html → iframe.168aeeb1.html} +0 -0
package/vite/index.cjs CHANGED
@@ -749,6 +749,81 @@ function checkHostReactVersion(projectRoot) {
749
749
  };
750
750
  }
751
751
  //#endregion
752
+ //#region src/vite/pinVendoredReact.ts
753
+ /**
754
+ * Pinned React: inside the package `react` always means the copy the package ships.
755
+ *
756
+ * The product never imports `react` by its bare name, but the vendored tree does — antd and its
757
+ * relatives, and the `runtime/react` subpath itself, which is a re-export of `import … from 'react'`.
758
+ * Those bare names are exactly what a host build is free to redirect, and four mechanisms do it:
759
+ * `resolve.dedupe`, Module Federation `shared: { react: { singleton: true } }`, webpack
760
+ * `resolve.alias` and `resolve.modules`. None of them is sabotage — they are the recommended cure for
761
+ * duplicate React — but they silently hand the editor whichever React the host happens to have. At
762
+ * campaigns that was React 18 against an editor built for 19.
763
+ *
764
+ * So imports are pinned by their importer: a request coming from a file inside the package resolves
765
+ * to the vendored copy, and nothing else is touched. Aliases run before any plugin, so the order of
766
+ * the host's own configuration does not matter. `sharedReact` is the one way to opt out — then the
767
+ * editor runs on the host's React on purpose.
768
+ */
769
+ /** Bare names the vendored tree imports directly; the same set `sharedReact` sends to the host. */
770
+ var PINNED_BARE_NAMES = [
771
+ "react",
772
+ "react-dom",
773
+ "react-dom/client",
774
+ "react/jsx-runtime",
775
+ "react/jsx-dev-runtime"
776
+ ];
777
+ function normalize(value) {
778
+ return value.replace(/\\/g, "/");
779
+ }
780
+ /**
781
+ * Real path, with symlinks resolved. With pnpm the package is a link into `.pnpm/…`, and the
782
+ * importer arrives already resolved: comparing the visible path against it never matches, and the
783
+ * pinning would quietly do nothing. The measurement notes in DELIVERY_BOUNDARIES.md warn about the
784
+ * same trap from the other side.
785
+ */
786
+ function realPath(value) {
787
+ try {
788
+ return fs.default.realpathSync(value);
789
+ } catch {
790
+ return value;
791
+ }
792
+ }
793
+ /** Is the importing file part of the published package (its own files or its vendored tree). */
794
+ function isInsidePackage(importer, packageRoot) {
795
+ const root = normalize(realPath(packageRoot)).replace(/\/$/, "");
796
+ return normalize(realPath(importer)).startsWith(`${root}/`);
797
+ }
798
+ /**
799
+ * Where the package's own copy of that module lives. Resolved from the package manifest, so pnpm's
800
+ * `.pnpm/…` layout and a plain `node_modules` tree both land on the vendored files.
801
+ */
802
+ function resolveVendoredModule(packageRoot, specifier) {
803
+ try {
804
+ return (0, module$1.createRequire)(path.default.join(packageRoot, "package.json")).resolve(specifier);
805
+ } catch {
806
+ return;
807
+ }
808
+ }
809
+ /**
810
+ * Aliases that pin React for the package's own graph. Empty when the package ships no copy: then
811
+ * there is nothing to pin to, and redirecting would break the build instead of protecting it.
812
+ */
813
+ function buildPinnedReactAliases(packageRoot, onPin) {
814
+ if (resolveVendoredModule(packageRoot, "react") === void 0) return [];
815
+ return PINNED_BARE_NAMES.map((name) => ({
816
+ find: new RegExp(`^${name.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&")}$`, "u"),
817
+ replacement: name,
818
+ customResolver: (source, importer) => {
819
+ if (!importer || !isInsidePackage(importer, packageRoot)) return null;
820
+ const vendored = resolveVendoredModule(packageRoot, source);
821
+ if (vendored && onPin) onPin(source);
822
+ return vendored ?? null;
823
+ }
824
+ }));
825
+ }
826
+ //#endregion
752
827
  //#region src/shared/reactDuplicateDetector.ts
753
828
  /**
754
829
  * Детектор дублированных runtime-копий React/antd в Vite-prebundle integrator'а.
@@ -883,6 +958,25 @@ var APP_CORE_OPTIMIZED_DEP_RE = createAppCoreOptimizedDepRe(BUNDLER_DEFAULT_APP_
883
958
  var DEFAULT_OUTPUT_FOLDER = `/${BUNDLER_DEFAULT_APP_NAME}`;
884
959
  var DEFAULT_MODULES_PATH = "node_modules";
885
960
  var DEFAULT_STARTUP_PREFETCH_MODE = "idle";
961
+ /**
962
+ * Как разрешать React в графе партнёра.
963
+ *
964
+ * По умолчанию — пришиваем свою копию: импорт из файла внутри пакета уходит в неё, чужие импорты не
965
+ * трогаются. `sharedReact` — обратный случай: интегратор просит редактор жить на его React, и тогда
966
+ * наши подпути уводятся к хосту.
967
+ */
968
+ function reactResolveConfig(root, options) {
969
+ if (options.sharedReact) return { resolve: { alias: buildSharedReactAliases(BUNDLER_DEFAULT_PACKAGE_NAME) } };
970
+ const packageRoot = resolvePublishedPackageRoot(BUNDLER_DEFAULT_PACKAGE_NAME, options.modulesPath || DEFAULT_MODULES_PATH, root ? path.default.resolve(root) : process.cwd());
971
+ const pinned = /* @__PURE__ */ new Set();
972
+ const alias = buildPinnedReactAliases(packageRoot, (source) => {
973
+ if (!pinned.has(source)) {
974
+ pinned.add(source);
975
+ console.log(`[${PLUGIN_NAME}] pinnedReact: "${source}" внутри пакета → своя копия`);
976
+ }
977
+ });
978
+ return alias.length > 0 ? { resolve: { alias } } : void 0;
979
+ }
886
980
  function outputFolderFsSegment(folder, appName) {
887
981
  return (folder != null && folder !== "" ? String(folder) : `/${appName}`).replace(/^\/+/, "") || appName;
888
982
  }
@@ -991,6 +1085,9 @@ var ViteServePlugin = (options) => {
991
1085
  name: `${PLUGIN_NAME}-serve`,
992
1086
  enforce: "pre",
993
1087
  apply: "serve",
1088
+ config(config) {
1089
+ return reactResolveConfig(config.root, options);
1090
+ },
994
1091
  configResolved(config) {
995
1092
  projectRoot = config.root;
996
1093
  destPath = path.default.normalize(path.default.join(projectRoot, mountSeg));
@@ -1144,9 +1241,8 @@ var ViteBuildPlugin = (options) => {
1144
1241
  name: `${PLUGIN_NAME}-build`,
1145
1242
  enforce: "pre",
1146
1243
  apply: "build",
1147
- config() {
1148
- if (!options.sharedReact) return;
1149
- return { resolve: { alias: buildSharedReactAliases(BUNDLER_DEFAULT_PACKAGE_NAME) } };
1244
+ config(config) {
1245
+ return reactResolveConfig(config.root, options);
1150
1246
  },
1151
1247
  resolveId(source, importer) {
1152
1248
  if (!options.sharedReact || !importer || !resolvedConfig || !isSharedReactSpecifier(source)) return null;
package/vite/index.js CHANGED
@@ -720,6 +720,81 @@ function checkHostReactVersion(projectRoot) {
720
720
  };
721
721
  }
722
722
  //#endregion
723
+ //#region src/vite/pinVendoredReact.ts
724
+ /**
725
+ * Pinned React: inside the package `react` always means the copy the package ships.
726
+ *
727
+ * The product never imports `react` by its bare name, but the vendored tree does — antd and its
728
+ * relatives, and the `runtime/react` subpath itself, which is a re-export of `import … from 'react'`.
729
+ * Those bare names are exactly what a host build is free to redirect, and four mechanisms do it:
730
+ * `resolve.dedupe`, Module Federation `shared: { react: { singleton: true } }`, webpack
731
+ * `resolve.alias` and `resolve.modules`. None of them is sabotage — they are the recommended cure for
732
+ * duplicate React — but they silently hand the editor whichever React the host happens to have. At
733
+ * campaigns that was React 18 against an editor built for 19.
734
+ *
735
+ * So imports are pinned by their importer: a request coming from a file inside the package resolves
736
+ * to the vendored copy, and nothing else is touched. Aliases run before any plugin, so the order of
737
+ * the host's own configuration does not matter. `sharedReact` is the one way to opt out — then the
738
+ * editor runs on the host's React on purpose.
739
+ */
740
+ /** Bare names the vendored tree imports directly; the same set `sharedReact` sends to the host. */
741
+ var PINNED_BARE_NAMES = [
742
+ "react",
743
+ "react-dom",
744
+ "react-dom/client",
745
+ "react/jsx-runtime",
746
+ "react/jsx-dev-runtime"
747
+ ];
748
+ function normalize(value) {
749
+ return value.replace(/\\/g, "/");
750
+ }
751
+ /**
752
+ * Real path, with symlinks resolved. With pnpm the package is a link into `.pnpm/…`, and the
753
+ * importer arrives already resolved: comparing the visible path against it never matches, and the
754
+ * pinning would quietly do nothing. The measurement notes in DELIVERY_BOUNDARIES.md warn about the
755
+ * same trap from the other side.
756
+ */
757
+ function realPath(value) {
758
+ try {
759
+ return fs.realpathSync(value);
760
+ } catch {
761
+ return value;
762
+ }
763
+ }
764
+ /** Is the importing file part of the published package (its own files or its vendored tree). */
765
+ function isInsidePackage(importer, packageRoot) {
766
+ const root = normalize(realPath(packageRoot)).replace(/\/$/, "");
767
+ return normalize(realPath(importer)).startsWith(`${root}/`);
768
+ }
769
+ /**
770
+ * Where the package's own copy of that module lives. Resolved from the package manifest, so pnpm's
771
+ * `.pnpm/…` layout and a plain `node_modules` tree both land on the vendored files.
772
+ */
773
+ function resolveVendoredModule(packageRoot, specifier) {
774
+ try {
775
+ return createRequire(path.join(packageRoot, "package.json")).resolve(specifier);
776
+ } catch {
777
+ return;
778
+ }
779
+ }
780
+ /**
781
+ * Aliases that pin React for the package's own graph. Empty when the package ships no copy: then
782
+ * there is nothing to pin to, and redirecting would break the build instead of protecting it.
783
+ */
784
+ function buildPinnedReactAliases(packageRoot, onPin) {
785
+ if (resolveVendoredModule(packageRoot, "react") === void 0) return [];
786
+ return PINNED_BARE_NAMES.map((name) => ({
787
+ find: new RegExp(`^${name.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&")}$`, "u"),
788
+ replacement: name,
789
+ customResolver: (source, importer) => {
790
+ if (!importer || !isInsidePackage(importer, packageRoot)) return null;
791
+ const vendored = resolveVendoredModule(packageRoot, source);
792
+ if (vendored && onPin) onPin(source);
793
+ return vendored ?? null;
794
+ }
795
+ }));
796
+ }
797
+ //#endregion
723
798
  //#region src/shared/reactDuplicateDetector.ts
724
799
  /**
725
800
  * Детектор дублированных runtime-копий React/antd в Vite-prebundle integrator'а.
@@ -854,6 +929,25 @@ var APP_CORE_OPTIMIZED_DEP_RE = createAppCoreOptimizedDepRe(BUNDLER_DEFAULT_APP_
854
929
  var DEFAULT_OUTPUT_FOLDER = `/${BUNDLER_DEFAULT_APP_NAME}`;
855
930
  var DEFAULT_MODULES_PATH = "node_modules";
856
931
  var DEFAULT_STARTUP_PREFETCH_MODE = "idle";
932
+ /**
933
+ * Как разрешать React в графе партнёра.
934
+ *
935
+ * По умолчанию — пришиваем свою копию: импорт из файла внутри пакета уходит в неё, чужие импорты не
936
+ * трогаются. `sharedReact` — обратный случай: интегратор просит редактор жить на его React, и тогда
937
+ * наши подпути уводятся к хосту.
938
+ */
939
+ function reactResolveConfig(root, options) {
940
+ if (options.sharedReact) return { resolve: { alias: buildSharedReactAliases(BUNDLER_DEFAULT_PACKAGE_NAME) } };
941
+ const packageRoot = resolvePublishedPackageRoot(BUNDLER_DEFAULT_PACKAGE_NAME, options.modulesPath || DEFAULT_MODULES_PATH, root ? path.resolve(root) : process.cwd());
942
+ const pinned = /* @__PURE__ */ new Set();
943
+ const alias = buildPinnedReactAliases(packageRoot, (source) => {
944
+ if (!pinned.has(source)) {
945
+ pinned.add(source);
946
+ console.log(`[${PLUGIN_NAME}] pinnedReact: "${source}" внутри пакета → своя копия`);
947
+ }
948
+ });
949
+ return alias.length > 0 ? { resolve: { alias } } : void 0;
950
+ }
857
951
  function outputFolderFsSegment(folder, appName) {
858
952
  return (folder != null && folder !== "" ? String(folder) : `/${appName}`).replace(/^\/+/, "") || appName;
859
953
  }
@@ -962,6 +1056,9 @@ var ViteServePlugin = (options) => {
962
1056
  name: `${PLUGIN_NAME}-serve`,
963
1057
  enforce: "pre",
964
1058
  apply: "serve",
1059
+ config(config) {
1060
+ return reactResolveConfig(config.root, options);
1061
+ },
965
1062
  configResolved(config) {
966
1063
  projectRoot = config.root;
967
1064
  destPath = path.normalize(path.join(projectRoot, mountSeg));
@@ -1115,9 +1212,8 @@ var ViteBuildPlugin = (options) => {
1115
1212
  name: `${PLUGIN_NAME}-build`,
1116
1213
  enforce: "pre",
1117
1214
  apply: "build",
1118
- config() {
1119
- if (!options.sharedReact) return;
1120
- return { resolve: { alias: buildSharedReactAliases(BUNDLER_DEFAULT_PACKAGE_NAME) } };
1215
+ config(config) {
1216
+ return reactResolveConfig(config.root, options);
1121
1217
  },
1122
1218
  resolveId(source, importer) {
1123
1219
  if (!options.sharedReact || !importer || !resolvedConfig || !isSharedReactSpecifier(source)) return null;
package/webpack/index.cjs CHANGED
@@ -549,6 +549,81 @@ function applySharedReactAliases(compiler, mainPackage, logger) {
549
549
  logger.info(`sharedReact: ${mapped} module(s) mapped onto the host React${version.version ? ` ${version.version}` : ""}`);
550
550
  }
551
551
  //#endregion
552
+ //#region src/vite/pinVendoredReact.ts
553
+ /**
554
+ * Pinned React: inside the package `react` always means the copy the package ships.
555
+ *
556
+ * The product never imports `react` by its bare name, but the vendored tree does — antd and its
557
+ * relatives, and the `runtime/react` subpath itself, which is a re-export of `import … from 'react'`.
558
+ * Those bare names are exactly what a host build is free to redirect, and four mechanisms do it:
559
+ * `resolve.dedupe`, Module Federation `shared: { react: { singleton: true } }`, webpack
560
+ * `resolve.alias` and `resolve.modules`. None of them is sabotage — they are the recommended cure for
561
+ * duplicate React — but they silently hand the editor whichever React the host happens to have. At
562
+ * campaigns that was React 18 against an editor built for 19.
563
+ *
564
+ * So imports are pinned by their importer: a request coming from a file inside the package resolves
565
+ * to the vendored copy, and nothing else is touched. Aliases run before any plugin, so the order of
566
+ * the host's own configuration does not matter. `sharedReact` is the one way to opt out — then the
567
+ * editor runs on the host's React on purpose.
568
+ */
569
+ /** Bare names the vendored tree imports directly; the same set `sharedReact` sends to the host. */
570
+ var PINNED_BARE_NAMES = [
571
+ "react",
572
+ "react-dom",
573
+ "react-dom/client",
574
+ "react/jsx-runtime",
575
+ "react/jsx-dev-runtime"
576
+ ];
577
+ function normalize(value) {
578
+ return value.replace(/\\/g, "/");
579
+ }
580
+ /**
581
+ * Real path, with symlinks resolved. With pnpm the package is a link into `.pnpm/…`, and the
582
+ * importer arrives already resolved: comparing the visible path against it never matches, and the
583
+ * pinning would quietly do nothing. The measurement notes in DELIVERY_BOUNDARIES.md warn about the
584
+ * same trap from the other side.
585
+ */
586
+ function realPath(value) {
587
+ try {
588
+ return fs.default.realpathSync(value);
589
+ } catch {
590
+ return value;
591
+ }
592
+ }
593
+ /** Is the importing file part of the published package (its own files or its vendored tree). */
594
+ function isInsidePackage(importer, packageRoot) {
595
+ const root = normalize(realPath(packageRoot)).replace(/\/$/, "");
596
+ return normalize(realPath(importer)).startsWith(`${root}/`);
597
+ }
598
+ /**
599
+ * Where the package's own copy of that module lives. Resolved from the package manifest, so pnpm's
600
+ * `.pnpm/…` layout and a plain `node_modules` tree both land on the vendored files.
601
+ */
602
+ function resolveVendoredModule(packageRoot, specifier) {
603
+ try {
604
+ return (0, module$1.createRequire)(path.default.join(packageRoot, "package.json")).resolve(specifier);
605
+ } catch {
606
+ return;
607
+ }
608
+ }
609
+ /**
610
+ * Aliases that pin React for the package's own graph. Empty when the package ships no copy: then
611
+ * there is nothing to pin to, and redirecting would break the build instead of protecting it.
612
+ */
613
+ function buildPinnedReactAliases(packageRoot, onPin) {
614
+ if (resolveVendoredModule(packageRoot, "react") === void 0) return [];
615
+ return PINNED_BARE_NAMES.map((name) => ({
616
+ find: new RegExp(`^${name.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&")}$`, "u"),
617
+ replacement: name,
618
+ customResolver: (source, importer) => {
619
+ if (!importer || !isInsidePackage(importer, packageRoot)) return null;
620
+ const vendored = resolveVendoredModule(packageRoot, source);
621
+ if (vendored && onPin) onPin(source);
622
+ return vendored ?? null;
623
+ }
624
+ }));
625
+ }
626
+ //#endregion
552
627
  //#region src/shared/resolvePublishedPackage.ts
553
628
  /**
554
629
  * Resolve the on-disk root of the published main package (handles pnpm hoisting / symlinks).
@@ -587,6 +662,23 @@ function assertCoreChunkInCompilationAssets(assetNames, appName, outputFolder) {
587
662
  })) throw new Error(`[host-plugin] No "${appName}-core.*.js" asset was emitted under "${folderPrefix || "(output)"}" after copy. The published package may be incomplete or IGNORED_PATTERNS filtered the core chunk.`);
588
663
  }
589
664
  //#endregion
665
+ //#region src/webpack/pinVendoredReact.ts
666
+ function applyPinnedReact(compiler, mainPackage, modulesPath, logger) {
667
+ const packageRoot = resolvePublishedPackageRoot(mainPackage, modulesPath, compiler.context);
668
+ if (buildPinnedReactAliases(packageRoot).length === 0) return;
669
+ const { NormalModuleReplacementPlugin } = compiler.webpack ?? {};
670
+ if (!NormalModuleReplacementPlugin) {
671
+ logger.warn("pinnedReact: webpack.NormalModuleReplacementPlugin is unavailable — React stays on the host resolve");
672
+ return;
673
+ }
674
+ new NormalModuleReplacementPlugin(/^react(-dom)?(\/.*)?$/u, (resource) => {
675
+ if (!resource.context || !isInsidePackage(resource.context, packageRoot)) return;
676
+ const vendored = resolveVendoredModule(packageRoot, resource.request);
677
+ if (vendored) resource.request = vendored;
678
+ }).apply(compiler);
679
+ logger.info("pinnedReact: the editor runs on the React shipped inside the package");
680
+ }
681
+ //#endregion
590
682
  //#region src/shared/branding.ts
591
683
  /**
592
684
  * Чтение `process.env.REACT_APP_*` — как в основном приложении.
@@ -738,6 +830,7 @@ var WebpackPlugin = class {
738
830
  logger.info(`Host plugin active (package=${BUNDLER_DEFAULT_PACKAGE_NAME}, namespace=${ENVIRONMENT_VARIABLE}, publicPath=${normalizedPublicPath}, strict=${this._strict})`);
739
831
  if (isPackageExternalized(compiler.options.externals, "useblocks")) logger.warn(`${BUNDLER_DEFAULT_PACKAGE_NAME} is listed in webpack externals — the product facade may be absent from the bundle and host bootstrap cannot be injected.`);
740
832
  if (this._sharedReact) applySharedReactAliases(compiler, BUNDLER_DEFAULT_PACKAGE_NAME, logger);
833
+ else applyPinnedReact(compiler, BUNDLER_DEFAULT_PACKAGE_NAME, this._modulesPath, logger);
741
834
  compiler.hooks.afterCompile?.tap(PLUGIN_NAME, (compilation) => {
742
835
  if (!compilation.modules || !compilation.moduleGraph) return;
743
836
  const edges = [];
package/webpack/index.js CHANGED
@@ -525,6 +525,81 @@ function applySharedReactAliases(compiler, mainPackage, logger) {
525
525
  logger.info(`sharedReact: ${mapped} module(s) mapped onto the host React${version.version ? ` ${version.version}` : ""}`);
526
526
  }
527
527
  //#endregion
528
+ //#region src/vite/pinVendoredReact.ts
529
+ /**
530
+ * Pinned React: inside the package `react` always means the copy the package ships.
531
+ *
532
+ * The product never imports `react` by its bare name, but the vendored tree does — antd and its
533
+ * relatives, and the `runtime/react` subpath itself, which is a re-export of `import … from 'react'`.
534
+ * Those bare names are exactly what a host build is free to redirect, and four mechanisms do it:
535
+ * `resolve.dedupe`, Module Federation `shared: { react: { singleton: true } }`, webpack
536
+ * `resolve.alias` and `resolve.modules`. None of them is sabotage — they are the recommended cure for
537
+ * duplicate React — but they silently hand the editor whichever React the host happens to have. At
538
+ * campaigns that was React 18 against an editor built for 19.
539
+ *
540
+ * So imports are pinned by their importer: a request coming from a file inside the package resolves
541
+ * to the vendored copy, and nothing else is touched. Aliases run before any plugin, so the order of
542
+ * the host's own configuration does not matter. `sharedReact` is the one way to opt out — then the
543
+ * editor runs on the host's React on purpose.
544
+ */
545
+ /** Bare names the vendored tree imports directly; the same set `sharedReact` sends to the host. */
546
+ var PINNED_BARE_NAMES = [
547
+ "react",
548
+ "react-dom",
549
+ "react-dom/client",
550
+ "react/jsx-runtime",
551
+ "react/jsx-dev-runtime"
552
+ ];
553
+ function normalize(value) {
554
+ return value.replace(/\\/g, "/");
555
+ }
556
+ /**
557
+ * Real path, with symlinks resolved. With pnpm the package is a link into `.pnpm/…`, and the
558
+ * importer arrives already resolved: comparing the visible path against it never matches, and the
559
+ * pinning would quietly do nothing. The measurement notes in DELIVERY_BOUNDARIES.md warn about the
560
+ * same trap from the other side.
561
+ */
562
+ function realPath(value) {
563
+ try {
564
+ return fs.realpathSync(value);
565
+ } catch {
566
+ return value;
567
+ }
568
+ }
569
+ /** Is the importing file part of the published package (its own files or its vendored tree). */
570
+ function isInsidePackage(importer, packageRoot) {
571
+ const root = normalize(realPath(packageRoot)).replace(/\/$/, "");
572
+ return normalize(realPath(importer)).startsWith(`${root}/`);
573
+ }
574
+ /**
575
+ * Where the package's own copy of that module lives. Resolved from the package manifest, so pnpm's
576
+ * `.pnpm/…` layout and a plain `node_modules` tree both land on the vendored files.
577
+ */
578
+ function resolveVendoredModule(packageRoot, specifier) {
579
+ try {
580
+ return createRequire(path.join(packageRoot, "package.json")).resolve(specifier);
581
+ } catch {
582
+ return;
583
+ }
584
+ }
585
+ /**
586
+ * Aliases that pin React for the package's own graph. Empty when the package ships no copy: then
587
+ * there is nothing to pin to, and redirecting would break the build instead of protecting it.
588
+ */
589
+ function buildPinnedReactAliases(packageRoot, onPin) {
590
+ if (resolveVendoredModule(packageRoot, "react") === void 0) return [];
591
+ return PINNED_BARE_NAMES.map((name) => ({
592
+ find: new RegExp(`^${name.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&")}$`, "u"),
593
+ replacement: name,
594
+ customResolver: (source, importer) => {
595
+ if (!importer || !isInsidePackage(importer, packageRoot)) return null;
596
+ const vendored = resolveVendoredModule(packageRoot, source);
597
+ if (vendored && onPin) onPin(source);
598
+ return vendored ?? null;
599
+ }
600
+ }));
601
+ }
602
+ //#endregion
528
603
  //#region src/shared/resolvePublishedPackage.ts
529
604
  /**
530
605
  * Resolve the on-disk root of the published main package (handles pnpm hoisting / symlinks).
@@ -563,6 +638,23 @@ function assertCoreChunkInCompilationAssets(assetNames, appName, outputFolder) {
563
638
  })) throw new Error(`[host-plugin] No "${appName}-core.*.js" asset was emitted under "${folderPrefix || "(output)"}" after copy. The published package may be incomplete or IGNORED_PATTERNS filtered the core chunk.`);
564
639
  }
565
640
  //#endregion
641
+ //#region src/webpack/pinVendoredReact.ts
642
+ function applyPinnedReact(compiler, mainPackage, modulesPath, logger) {
643
+ const packageRoot = resolvePublishedPackageRoot(mainPackage, modulesPath, compiler.context);
644
+ if (buildPinnedReactAliases(packageRoot).length === 0) return;
645
+ const { NormalModuleReplacementPlugin } = compiler.webpack ?? {};
646
+ if (!NormalModuleReplacementPlugin) {
647
+ logger.warn("pinnedReact: webpack.NormalModuleReplacementPlugin is unavailable — React stays on the host resolve");
648
+ return;
649
+ }
650
+ new NormalModuleReplacementPlugin(/^react(-dom)?(\/.*)?$/u, (resource) => {
651
+ if (!resource.context || !isInsidePackage(resource.context, packageRoot)) return;
652
+ const vendored = resolveVendoredModule(packageRoot, resource.request);
653
+ if (vendored) resource.request = vendored;
654
+ }).apply(compiler);
655
+ logger.info("pinnedReact: the editor runs on the React shipped inside the package");
656
+ }
657
+ //#endregion
566
658
  //#region src/shared/branding.ts
567
659
  /**
568
660
  * Чтение `process.env.REACT_APP_*` — как в основном приложении.
@@ -714,6 +806,7 @@ var WebpackPlugin = class {
714
806
  logger.info(`Host plugin active (package=${BUNDLER_DEFAULT_PACKAGE_NAME}, namespace=${ENVIRONMENT_VARIABLE}, publicPath=${normalizedPublicPath}, strict=${this._strict})`);
715
807
  if (isPackageExternalized(compiler.options.externals, "useblocks")) logger.warn(`${BUNDLER_DEFAULT_PACKAGE_NAME} is listed in webpack externals — the product facade may be absent from the bundle and host bootstrap cannot be injected.`);
716
808
  if (this._sharedReact) applySharedReactAliases(compiler, BUNDLER_DEFAULT_PACKAGE_NAME, logger);
809
+ else applyPinnedReact(compiler, BUNDLER_DEFAULT_PACKAGE_NAME, this._modulesPath, logger);
717
810
  compiler.hooks.afterCompile?.tap(PLUGIN_NAME, (compilation) => {
718
811
  if (!compilation.modules || !compilation.moduleGraph) return;
719
812
  const edges = [];