@multiplatform.one/config 7.6.3 → 7.7.1

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/src/vite.ts CHANGED
@@ -224,6 +224,15 @@ export function createViteConfig(options: CreateViteConfigOptions = {}): UserCon
224
224
  // This re-asserts the package's own browser redirect (-> browser transports that
225
225
  // use RN's global WebSocket/XMLHttpRequest) with NO node_modules patch.
226
226
  registerNativeEngineIoBrowserTransports();
227
+ // A mixed pnpm install (hoisted root dirs + a leftover .pnpm store) puts
228
+ // two byte-identical copies of react, react-native and 62 other packages
229
+ // on disk, and which one an importer reaches depends only on where that
230
+ // importer sits. Two Reacts in one Hermes bundle means the renderer arms
231
+ // the dispatcher on one copy while components read it as null from the
232
+ // other — "Cannot read property 'useContext' of null" on first render.
233
+ // It delegates through `this.resolve`, so it canonicalizes whatever the
234
+ // rest of the chain produces, wherever it sits in the plugin order.
235
+ registerNativeSinglePackageInstances();
227
236
  // preferBuiltWorkspaceEntryPlugin (web-only) hard-resolves every workspace
228
237
  // package to its `dist/esm/index.mjs` (WEB build), and that resolution reaches
229
238
  // the native graph too — pinning the whole @multiplatform.one/* chain to web
@@ -434,6 +443,20 @@ export function createViteConfig(options: CreateViteConfigOptions = {}): UserCon
434
443
  "process.env.VITE_FRAPPE_ENABLED": JSON.stringify(
435
444
  process.env.VITE_FRAPPE_ENABLED === "true" ? "true" : "false",
436
445
  ),
446
+ // The public-config bake. @multiplatform.one/platform's runtimeConfig
447
+ // reads exactly these two static expressions (it carries no import.meta,
448
+ // because Expo DOM components run it as a classic script, MPO-192), and
449
+ // a static `process.env.<KEY>` is what Vite, vxrn/One and Metro all
450
+ // inline. Absent when no public keys were declared, so a build without
451
+ // them leaves the expressions alone and the reader falls back to {}.
452
+ ...(publicConfigKeys.length > 0
453
+ ? {
454
+ "process.env.VITE_MP_CONFIG": JSON.stringify(process.env.VITE_MP_CONFIG ?? ""),
455
+ "process.env.VITE_MP_PUBLIC_CONFIG_KEYS": JSON.stringify(
456
+ process.env.VITE_MP_PUBLIC_CONFIG_KEYS ?? "",
457
+ ),
458
+ }
459
+ : {}),
437
460
  },
438
461
  ssr: ssr || defaultSsr,
439
462
  resolve: {
@@ -562,6 +585,134 @@ function clientBrokenEsmPlugin(): Plugin {
562
585
  * This re-asserts the browser redirect with no node_modules patch. Web is
563
586
  * unaffected (engine.io is SSR-externalized and the web client already maps `browser`).
564
587
  */
588
+ /**
589
+ * Collapse duplicate PHYSICAL copies of the same package@version to one path
590
+ * in the NATIVE graph, so React (and every other stateful singleton) exists
591
+ * exactly once in the Hermes bundle.
592
+ *
593
+ * The workspace's node_modules is a MIXED install. `.npmrc` says
594
+ * `node-linker=hoisted`, so the root `node_modules/<pkg>` entries are real
595
+ * directories; but an earlier isolated install left a `node_modules/.pnpm`
596
+ * virtual store plus per-package symlink farms behind — e.g. each package's
597
+ * own `node_modules/react` still points at
598
+ * `../../../node_modules/.pnpm/react@19.2.5/node_modules/react`.
599
+ * Both trees are fully populated with byte-identical copies, and which one an
600
+ * importer reaches depends only on where that importer sits on disk:
601
+ *
602
+ * apps/one/app -> node_modules/.pnpm/react@19.2.5/node_modules/react
603
+ * public/forms/src -> node_modules/react (no local symlink)
604
+ *
605
+ * Rolldown resolves symlinks, so the two land as two DIFFERENT absolute paths
606
+ * and it bundles both. React 19's `useContext` is
607
+ * `ReactSharedInternals.H.useContext(Context)` — `H` is the dispatcher the
608
+ * renderer installs during render, and it is only ever set on the copy the
609
+ * renderer imported. A component from the other copy therefore reads `H` as
610
+ * null and the app dies with "Cannot read property 'useContext' of null".
611
+ * That is the whole bug; the same split had also duplicated react-native (427
612
+ * modules each), @tamagui/web, @react-navigation/core, one, scheduler and 58
613
+ * more packages.
614
+ *
615
+ * The rule: an id that resolves inside the virtual store is rewritten to the
616
+ * hoisted root copy when the root copy exists, carries the SAME version, and
617
+ * actually has the file. Version equality is the safety guard — genuine
618
+ * multi-version installs (two @babel/runtime, two viem) keep both copies.
619
+ *
620
+ * On a correctly installed tree this is a no-op by construction: `hoisted`
621
+ * leaves no `.pnpm` store to match, and `isolated` makes the root entry a
622
+ * symlink INTO the store, which realpaths back to the same directory (checked
623
+ * explicitly). It only fires on the mixed tree, and it is cheap because only
624
+ * BARE specifiers are inspected — once a package's entry is canonical, its own
625
+ * relative imports resolve inside the canonical copy for free.
626
+ */
627
+ function registerNativeSinglePackageInstances(): void {
628
+ const g = globalThis as unknown as { __vxrnAddNativePlugins?: unknown[] };
629
+ const name = "vxrn-single-package-instances";
630
+ g.__vxrnAddNativePlugins = g.__vxrnAddNativePlugins ?? [];
631
+ if (g.__vxrnAddNativePlugins.some((p) => (p as { name?: string })?.name === name)) return;
632
+
633
+ const rootModules = path.join(findProjectRoot(), "node_modules");
634
+ // <...>/node_modules/.pnpm/<pkg>@<ver>_<peerhash>/node_modules/<name>[/<rest>]
635
+ const storePath =
636
+ /[\\/]node_modules[\\/]\.pnpm[\\/][^\\/]+[\\/]node_modules[\\/]((?:@[^\\/]+[\\/])?[^\\/]+)(?:[\\/](.*))?$/;
637
+
638
+ function versionOf(dir: string): string | null {
639
+ try {
640
+ const raw = fs.readFileSync(path.join(dir, "package.json"), "utf8");
641
+ return (JSON.parse(raw) as { version?: string }).version ?? null;
642
+ } catch {
643
+ return null;
644
+ }
645
+ }
646
+
647
+ // package name -> hoisted root dir that is a safe stand-in, or null
648
+ const hoisted = new Map<string, string | null>();
649
+ function hoistedTwin(pkg: string, storeDir: string): string | null {
650
+ if (!hoisted.has(pkg)) {
651
+ const rootDir = path.join(rootModules, pkg);
652
+ let twin: string | null = null;
653
+ if (fs.existsSync(rootDir)) {
654
+ const rootVersion = versionOf(rootDir);
655
+ const storeVersion = versionOf(storeDir);
656
+ // Same package, same version => identical library code, and the two
657
+ // dirs are genuinely distinct (not one symlinked onto the other).
658
+ if (rootVersion && rootVersion === storeVersion) {
659
+ try {
660
+ if (fs.realpathSync(rootDir) !== fs.realpathSync(storeDir)) twin = rootDir;
661
+ } catch {
662
+ twin = null;
663
+ }
664
+ }
665
+ }
666
+ hoisted.set(pkg, twin);
667
+ }
668
+ return hoisted.get(pkg) ?? null;
669
+ }
670
+
671
+ const seen = new Map<string, string | null>();
672
+
673
+ g.__vxrnAddNativePlugins.push({
674
+ name,
675
+ async resolveId(
676
+ this: {
677
+ resolve: (
678
+ id: string,
679
+ importer: string | undefined,
680
+ opts: { skipSelf: boolean },
681
+ ) => Promise<{ id: string; external?: boolean | string } | null>;
682
+ },
683
+ id: string,
684
+ importer?: string,
685
+ ): Promise<string | null> {
686
+ // Bare specifiers only: a package entry. Relative/absolute ids inside an
687
+ // already-canonical package resolve within that copy on their own.
688
+ if (!id || id.startsWith(".") || id.startsWith("/") || id.startsWith("\0")) return null;
689
+ const key = `${id} ${importer ?? ""}`;
690
+ const cached = seen.get(key);
691
+ if (cached !== undefined) return cached;
692
+
693
+ let out: string | null = null;
694
+ try {
695
+ const resolved = await this.resolve(id, importer, { skipSelf: true });
696
+ const match = resolved?.external ? null : resolved?.id?.match(storePath);
697
+ if (resolved && match) {
698
+ const pkg = match[1].split(path.sep).join("/");
699
+ const rest = match[2] ?? "";
700
+ const storeDir = resolved.id.slice(0, resolved.id.length - (rest ? rest.length + 1 : 0));
701
+ const twin = hoistedTwin(pkg, storeDir);
702
+ if (twin) {
703
+ const candidate = rest ? path.join(twin, rest) : twin;
704
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) out = candidate;
705
+ }
706
+ }
707
+ } catch {
708
+ out = null;
709
+ }
710
+ seen.set(key, out);
711
+ return out;
712
+ },
713
+ });
714
+ }
715
+
565
716
  function registerNativeEngineIoBrowserTransports(): void {
566
717
  const g = globalThis as unknown as { __vxrnAddNativePlugins?: unknown[] };
567
718
  const name = "vxrn-engineio-browser-transports";
@@ -619,8 +770,21 @@ function registerNativeWorkspaceEntries(): void {
619
770
  // (a) bare workspace specifier (e.g. "@multiplatform.one/rich-text")
620
771
  const root = roots.get(id);
621
772
  if (root) {
773
+ // Prefer the built native entry when present (tamagui-build already
774
+ // rewrote intra-package imports to their `.native` siblings).
622
775
  const nativeEntry = path.join(root, "dist/esm/index.native.js");
623
- return fs.existsSync(nativeEntry) ? nativeEntry : null;
776
+ if (fs.existsSync(nativeEntry)) return nativeEntry;
777
+ // Unbuilt workspace package: compile from source instead of letting
778
+ // rolldown leave the bare specifier external — Hermes has no module
779
+ // mode, so a surviving top-level `import` is unparseable. Mirrors
780
+ // preferBuiltWorkspaceEntryPlugin (web) and apps/storybook-expo's
781
+ // Metro resolveWorkspaceNativeMain. vxrn's native resolver already
782
+ // does platform-extension resolution (.native.tsx/.ios.tsx/…).
783
+ const srcTs = path.join(root, "src/index.ts");
784
+ if (fs.existsSync(srcTs)) return srcTs;
785
+ const srcTsx = path.join(root, "src/index.tsx");
786
+ if (fs.existsSync(srcTsx)) return srcTsx;
787
+ return null;
624
788
  }
625
789
  // (b) already-resolved web entry "<...>/dist/esm/index.mjs" -> ".native.js" sibling
626
790
  if (id.endsWith("/dist/esm/index.mjs")) {
@@ -28,7 +28,16 @@
28
28
  "skipLibCheck": true,
29
29
  "strictNullChecks": true,
30
30
  "target": "ES2020",
31
- "types": ["node", "react", "vite/client", "@testing-library/jest-dom/vitest"],
31
+ // An explicit `types` array turns OFF automatic @types inclusion, so this
32
+ // list is the whole set of ambient packages every program in the workspace
33
+ // gets. `chai` is not decoration: @vitest/expect declares
34
+ // interface Assertion<T> extends VitestAssertion<Chai.Assertion, T>, ...
35
+ // and without the global `Chai` namespace that base interface collapses,
36
+ // taking `.not` and the jest-dom matchers with it. That is where MPO-109's
37
+ // ~800 TS2339 came from (public/components 421 -> 26, public/forms
38
+ // 343 -> 4, features 168 -> 138, public/utils 4 -> 0). Do not trim this
39
+ // list without recounting.
40
+ "types": ["node", "react", "vite/client", "@testing-library/jest-dom/vitest", "chai"],
32
41
  "lib": ["DOM", "DOM.Iterable", "ESNext"]
33
42
  },
34
43
  "typeAcquisition": {
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Vite `resolve.alias` entries pinning each context singleton to one directory.
3
+ *
4
+ * Alias keys match the whole specifier or a `key + "/"` prefix, so subpath
5
+ * imports follow the same copy instead of resolving independently. Entries are
6
+ * omitted when the package is not installed, so a consumer that does not use
7
+ * cookies is unaffected.
8
+ *
9
+ * @param root Directory whose `node_modules` the packages resolve from.
10
+ */
11
+ export declare function singletonDepAliases(root?: string): Record<string, string>;
12
+ //# sourceMappingURL=singletonDepAliases.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"singletonDepAliases.d.ts","sourceRoot":"","sources":["../src/singletonDepAliases.ts"],"names":[],"mappings":"AA0DA;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,MAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUxF"}
@@ -1 +1 @@
1
- {"version":3,"file":"storybook.d.ts","sourceRoot":"","sources":["../src/storybook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAU,UAAU,EAAE,MAAM,MAAM,CAAC;AAK/C,MAAM,WAAW,gCAAgC;IAC/C;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC;;OAEG;IACH,OAAO,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;IAEhC;;OAEG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,gCAAqC,GAC7C,UAAU,CAwKZ"}
1
+ {"version":3,"file":"storybook.d.ts","sourceRoot":"","sources":["../src/storybook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAU,UAAU,EAAE,MAAM,MAAM,CAAC;AAM/C,MAAM,WAAW,gCAAgC;IAC/C;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC;;OAEG;IACH,OAAO,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;IAEhC;;OAEG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,gCAAqC,GAC7C,UAAU,CAuLZ"}
@@ -1 +1 @@
1
- {"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAU,UAAU,EAAE,MAAM,MAAM,CAAC;AAI/C,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE5B;;OAEG;IACH,OAAO,CAAC,EACJ,OAAO,GACP;QACE;;;WAGG;QACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QAEtB;;;WAGG;QACH,MAAM,CAAC,EAAE,MAAM,CAAC;QAEhB;;WAEG;QACH,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IAEN;;OAEG;IACH,IAAI,CAAC,EACD,OAAO,GACP;QACE;;;WAGG;QACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QAEjB;;WAEG;QACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;KAC9B,CAAC;IAEN;;;OAGG;IACH,GAAG,CAAC,EACA,OAAO,GACP;QACE,MAAM,CAAC,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC3B,GAAG,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACtD,KAAK,CAAC,EAAE;YAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;QAC/B,MAAM,CAAC,EAAE;YAAE,GAAG,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC1B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAChC,CAAC;IAEN;;OAEG;IACH,GAAG,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;IAExB;;OAEG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;IAE9B;;OAEG;IACH,OAAO,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;IAEhC;;OAEG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;OAEG;IACH,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;IAEF;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAElC;;;OAGG;IACH,WAAW,CAAC,EAAE;QACZ;;;;;;WAMG;QACH,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB;;;;;;;WAOG;QACH,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,uBAA4B,GAAG,UAAU,CAmSlF;AAED,OAAO,EACL,0BAA0B,EAC1B,8BAA8B,GAC/B,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAU,UAAU,EAAE,MAAM,MAAM,CAAC;AAI/C,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE5B;;OAEG;IACH,OAAO,CAAC,EACJ,OAAO,GACP;QACE;;;WAGG;QACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QAEtB;;;WAGG;QACH,MAAM,CAAC,EAAE,MAAM,CAAC;QAEhB;;WAEG;QACH,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IAEN;;OAEG;IACH,IAAI,CAAC,EACD,OAAO,GACP;QACE;;;WAGG;QACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QAEjB;;WAEG;QACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;KAC9B,CAAC;IAEN;;;OAGG;IACH,GAAG,CAAC,EACA,OAAO,GACP;QACE,MAAM,CAAC,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC3B,GAAG,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACtD,KAAK,CAAC,EAAE;YAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;QAC/B,MAAM,CAAC,EAAE;YAAE,GAAG,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC1B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAChC,CAAC;IAEN;;OAEG;IACH,GAAG,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;IAExB;;OAEG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;IAE9B;;OAEG;IACH,OAAO,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;IAEhC;;OAEG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;OAEG;IACH,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;IAEF;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAEtB;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAElC;;;OAGG;IACH,WAAW,CAAC,EAAE;QACZ;;;;;;WAMG;QACH,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB;;;;;;;WAOG;QACH,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,uBAA4B,GAAG,UAAU,CA0TlF;AAED,OAAO,EACL,0BAA0B,EAC1B,8BAA8B,GAC/B,MAAM,8BAA8B,CAAC"}