@multiplatform.one/config 7.8.0 → 7.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { t as createViteConfig } from "./vite-DCmObTtC.js";
2
- import { t as createStorybookViteConfig } from "./storybook-B3oEWyc-.js";
1
+ import { t as createViteConfig } from "./vite-D6vMjPwv.js";
2
+ import { t as createStorybookViteConfig } from "./storybook-CGNSgSBS.js";
3
3
  import { n as tamaguiWorkspacePathsFile, t as ensureTamaguiWorkspacePaths } from "./tamaguiWorkspacePaths-ZIcqybtn.js";
4
4
  import { createVitestConfig } from "./vitest.js";
5
5
 
@@ -1,4 +1,4 @@
1
- import { i as withSingletonDepAliases, r as resolvePackageMainSource, t as discoverPublicPackageRoots } from "./workspacePublicPackages-DdJxvtWd.js";
1
+ import { i as withSingletonDepAliases, r as resolvePackageMainSource, t as discoverPublicPackageRoots } from "./workspacePublicPackages-B4FGgRYG.js";
2
2
  import { t as ensureTamaguiWorkspacePaths } from "./tamaguiWorkspacePaths-ZIcqybtn.js";
3
3
  import { createRequire } from "node:module";
4
4
  import fs from "node:fs";
package/lib/storybook.js CHANGED
@@ -1,3 +1,3 @@
1
- import { t as createStorybookViteConfig } from "./storybook-B3oEWyc-.js";
1
+ import { t as createStorybookViteConfig } from "./storybook-CGNSgSBS.js";
2
2
 
3
3
  export { createStorybookViteConfig };
@@ -1,4 +1,4 @@
1
- import { i as withSingletonDepAliases, t as discoverPublicPackageRoots } from "./workspacePublicPackages-DdJxvtWd.js";
1
+ import { i as withSingletonDepAliases, t as discoverPublicPackageRoots } from "./workspacePublicPackages-B4FGgRYG.js";
2
2
  import { t as ensureTamaguiWorkspacePaths } from "./tamaguiWorkspacePaths-ZIcqybtn.js";
3
3
  import { createRequire } from "node:module";
4
4
  import fs from "node:fs";
package/lib/vite.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as createViteConfig } from "./vite-DCmObTtC.js";
2
- import { n as publicPackageViteSourceAliases, t as discoverPublicPackageRoots } from "./workspacePublicPackages-DdJxvtWd.js";
1
+ import { t as createViteConfig } from "./vite-D6vMjPwv.js";
2
+ import { n as publicPackageViteSourceAliases, t as discoverPublicPackageRoots } from "./workspacePublicPackages-B4FGgRYG.js";
3
3
 
4
4
  export { createViteConfig, discoverPublicPackageRoots, publicPackageViteSourceAliases };
@@ -64,13 +64,28 @@ import path from "node:path";
64
64
  * mounting a data-bound panel threw `Cannot read properties of null (reading
65
65
  * 'useSyncExternalStore')` on every load, and so did every data-bound page
66
66
  * after it.
67
+ *
68
+ * MPO-288. THE CRITERION, so the next addition is a decision against a rule
69
+ * rather than a guess. A package belongs here when a second module record
70
+ * would break it: it holds state at module scope, it subscribes an external
71
+ * store, or it provides a React context consumers compare by identity.
72
+ * Grepping the installed package for `useSyncExternalStore` is a cheap test
73
+ * for the second clause, and `singletonDepAliases.spec.ts` re-runs it against
74
+ * the tree so a tanstack upgrade is checked rather than assumed. It is one
75
+ * clause and not the whole rule: `@tanstack/db` references the hook nowhere
76
+ * and is pinned anyway, for the collection registry it holds at module scope.
77
+ *
78
+ * `@tanstack/react-query` references the hook in 25 shipped files and is not
79
+ * listed. That is recorded rather than settled — no split has been observed
80
+ * there, and pinning it reaches further than this measurement does.
67
81
  */
68
82
  const CONTEXT_SINGLETONS = [
69
83
  "react-cookie",
70
84
  "@tamagui/core",
71
85
  "@tamagui/web",
72
86
  "@tanstack/db",
73
- "@tanstack/react-db"
87
+ "@tanstack/react-db",
88
+ "@tanstack/react-store"
74
89
  ];
75
90
  /**
76
91
  * Vite `resolve.alias` entries pinning each context singleton to one directory.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multiplatform.one/config",
3
- "version": "7.8.0",
3
+ "version": "7.10.0",
4
4
  "description": "Shared build and test configuration presets for multiplatform.one",
5
5
  "keywords": [
6
6
  "config",
@@ -102,7 +102,7 @@
102
102
  "vite-plugin-external": "^6.2.2",
103
103
  "vite-plugin-i18next-loader": "^3.1.3",
104
104
  "vitest": "^4.1.5",
105
- "@multiplatform.one/utils": "7.8.0"
105
+ "@multiplatform.one/utils": "7.10.0"
106
106
  },
107
107
  "devDependencies": {
108
108
  "tsdown": "^0.21.10",
@@ -67,6 +67,18 @@ describe("singletonDepAliases", () => {
67
67
  expect(fs.existsSync(path.join(aliases["@tanstack/react-db"], "package.json"))).toBe(true);
68
68
  });
69
69
 
70
+ it("pins the react half of the store pair, which is the one that calls the hook (MPO-288)", () => {
71
+ // The same split as react-db, measured the same way: @tanstack/store has
72
+ // zero references to useSyncExternalStore and @tanstack/react-store has
73
+ // three, all of them useSelector, which is what useStore calls.
74
+ // public/store and public/forms both reach React through it, so a second
75
+ // record subscribes a store nothing armed.
76
+ const aliases = singletonDepAliases(workspaceRoot);
77
+ expect(aliases["@tanstack/react-store"]).toBeTruthy();
78
+ expect(path.isAbsolute(aliases["@tanstack/react-store"])).toBe(true);
79
+ expect(fs.existsSync(path.join(aliases["@tanstack/react-store"], "package.json"))).toBe(true);
80
+ });
81
+
70
82
  it("omits packages that are not installed", () => {
71
83
  // Resolution runs against the given root, so a consumer without the
72
84
  // package gets no alias rather than a broken one.
@@ -141,3 +153,55 @@ describe("createViteConfig context singletons (MPO-225)", () => {
141
153
  expect(alias.tamagui).toBeUndefined();
142
154
  });
143
155
  });
156
+
157
+ describe("CONTEXT_SINGLETONS membership, measured rather than recited (MPO-288)", () => {
158
+ const aliases = singletonDepAliases(workspaceRoot);
159
+
160
+ it("pins every tanstack half that subscribes an external store", () => {
161
+ for (const pkg of ["@tanstack/react-store", "@tanstack/react-db"]) {
162
+ expect(subscribesExternalStore(pkg), `${pkg} must reference the hook`).toBe(true);
163
+ expect(aliases[pkg], `${pkg} subscribes and must be pinned`).toBeTruthy();
164
+ }
165
+ });
166
+
167
+ it("leaves the pacer pair off, which the same measurement confirms", () => {
168
+ for (const pkg of ["@tanstack/pacer", "@tanstack/react-pacer"]) {
169
+ expect(subscribesExternalStore(pkg), `${pkg} must not reference the hook`).toBe(false);
170
+ expect(aliases[pkg], `${pkg} has no measured reason to be pinned`).toBeUndefined();
171
+ }
172
+ });
173
+
174
+ it("does not treat the grep as the whole rule", () => {
175
+ // @tanstack/db is pinned and references the hook nowhere: it holds the
176
+ // collection registry at module scope, which is a different clause of the
177
+ // criterion and one no grep can see. @tanstack/store carries no such
178
+ // registry, so silence there means what it says.
179
+ expect(subscribesExternalStore("@tanstack/db")).toBe(false);
180
+ expect(aliases["@tanstack/db"]).toBeTruthy();
181
+ expect(subscribesExternalStore("@tanstack/store")).toBe(false);
182
+ });
183
+ });
184
+
185
+ /**
186
+ * Whether an installed package references `useSyncExternalStore` in anything
187
+ * it ships. The cheap half of the membership rule, re-run against the tree so
188
+ * the next tanstack upgrade is checked rather than assumed.
189
+ */
190
+ function subscribesExternalStore(pkgName: string): boolean {
191
+ const dir = path.join(workspaceRoot, "node_modules", ...pkgName.split("/"));
192
+ return fs.existsSync(dir) && referencesHook(dir);
193
+ }
194
+
195
+ function referencesHook(dir: string): boolean {
196
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
197
+ if (entry.name === "node_modules") continue;
198
+ const full = path.join(dir, entry.name);
199
+ if (fs.statSync(full).isDirectory()) {
200
+ if (referencesHook(full)) return true;
201
+ continue;
202
+ }
203
+ if (!/\.(?:js|cjs|mjs|ts|tsx)$/.test(entry.name)) continue;
204
+ if (fs.readFileSync(full, "utf8").includes("useSyncExternalStore")) return true;
205
+ }
206
+ return false;
207
+ }
@@ -63,6 +63,20 @@ import path from "node:path";
63
63
  * mounting a data-bound panel threw `Cannot read properties of null (reading
64
64
  * 'useSyncExternalStore')` on every load, and so did every data-bound page
65
65
  * after it.
66
+ *
67
+ * MPO-288. THE CRITERION, so the next addition is a decision against a rule
68
+ * rather than a guess. A package belongs here when a second module record
69
+ * would break it: it holds state at module scope, it subscribes an external
70
+ * store, or it provides a React context consumers compare by identity.
71
+ * Grepping the installed package for `useSyncExternalStore` is a cheap test
72
+ * for the second clause, and `singletonDepAliases.spec.ts` re-runs it against
73
+ * the tree so a tanstack upgrade is checked rather than assumed. It is one
74
+ * clause and not the whole rule: `@tanstack/db` references the hook nowhere
75
+ * and is pinned anyway, for the collection registry it holds at module scope.
76
+ *
77
+ * `@tanstack/react-query` references the hook in 25 shipped files and is not
78
+ * listed. That is recorded rather than settled — no split has been observed
79
+ * there, and pinning it reaches further than this measurement does.
66
80
  */
67
81
  const CONTEXT_SINGLETONS: readonly string[] = [
68
82
  "react-cookie",
@@ -78,6 +92,12 @@ const CONTEXT_SINGLETONS: readonly string[] = [
78
92
  // exactly where it was. MPO-225 named @tanstack/db; the measurement says
79
93
  // the pair has to travel together.
80
94
  "@tanstack/react-db",
95
+ // Measured at 0.11.0: @tanstack/store references useSyncExternalStore in
96
+ // nothing it ships and @tanstack/react-store in three, all useSelector,
97
+ // which is the call behind useStore. public/store and public/forms both
98
+ // reach React through it, so a second record would subscribe a store
99
+ // nothing armed — the null read MPO-225 reported, one package family over.
100
+ "@tanstack/react-store",
81
101
  ];
82
102
 
83
103
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"singletonDepAliases.d.ts","sourceRoot":"","sources":["../src/singletonDepAliases.ts"],"names":[],"mappings":"AAkFA;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,MAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUxF;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,IAAI,GAAE,MAAsB,EAC5B,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACrC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOxB"}
1
+ {"version":3,"file":"singletonDepAliases.d.ts","sourceRoot":"","sources":["../src/singletonDepAliases.ts"],"names":[],"mappings":"AAsGA;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,MAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUxF;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,IAAI,GAAE,MAAsB,EAC5B,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACrC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOxB"}