@kbach/react-native 1.0.0-beta.50 → 1.0.0-beta.51

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/AGENTS.md CHANGED
@@ -345,3 +345,17 @@ Subpath exports: `@kbach/react-native/babel-plugin`,
345
345
  missing. It's required on every target, not just Android/dev-client
346
346
  builds; without it nothing routes `className` through this package's
347
347
  jsx-runtime at all, so RN just silently ignores the unrecognized prop.
348
+ - **`ERROR [Error: Unable to find node on an unmounted component.]` on
349
+ Expo Fast Refresh, especially right after editing `kbach.config.js`** —
350
+ stale `@kbach/react-native` (`< 1.0.0-beta.51`). Root cause: the babel
351
+ plugin (§1) injects `applyKbachConfig(require('kbach.config.js'))` into
352
+ *every* user file, so editing that config forces Metro to re-execute the
353
+ entire app (every file requires it), not just the screen you're on;
354
+ before beta.51, `setTheme` (`theme.ts`) handed every one of those
355
+ redundant re-applications a brand-new theme object even when the config
356
+ content hadn't changed, invalidating every `useColors()` memo and
357
+ reactive-element check across the whole tree simultaneously, right as
358
+ Fast Refresh was already mid-teardown — worsening this normally-rare
359
+ React Native Fast Refresh race. `setTheme` is idempotent by content
360
+ since beta.51 (compares against the theme's own JSON serialization
361
+ before replacing it); update.
package/README.md CHANGED
@@ -15,6 +15,13 @@ Utility-class styling for React Native — the same class names as
15
15
  npm install @kbach/react-native@beta
16
16
  ```
17
17
 
18
+ Or skip straight to a working setup: `npx @kbach/cli init` detects Expo
19
+ or React Native CLI, installs this package, and patches
20
+ `babel.config.js` for you (on React Native CLI, it'll even offer to run
21
+ the native rebuild) — see [`@kbach/cli`](https://www.npmjs.com/package/@kbach/cli).
22
+ The steps below are what it automates, and still the fallback it prints
23
+ if your `babel.config.js`'s shape is too unusual for it to edit safely.
24
+
18
25
  ## Setup
19
26
 
20
27
  The babel plugin (below) is **required on every target, including Expo
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveClassName
3
- } from "./chunk-3KLOVONL.mjs";
3
+ } from "./chunk-IJAXJVB4.mjs";
4
4
 
5
5
  // src/jsxRuntimeCoreWeb.ts
6
6
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
@@ -349,8 +349,10 @@ var defaultTheme = {
349
349
  var activeTheme = defaultTheme;
350
350
  var activeThemeJson = JSON.stringify(defaultTheme);
351
351
  function setTheme(theme) {
352
+ const json = JSON.stringify(theme);
353
+ if (json === activeThemeJson) return;
352
354
  activeTheme = theme;
353
- activeThemeJson = JSON.stringify(theme);
355
+ activeThemeJson = json;
354
356
  }
355
357
  function getTheme() {
356
358
  return activeTheme;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getTheme
3
- } from "./chunk-3KLOVONL.mjs";
3
+ } from "./chunk-IJAXJVB4.mjs";
4
4
 
5
5
  // src/darkModeStore.ts
6
6
  import { Appearance } from "react-native";
@@ -10,13 +10,13 @@ import {
10
10
  subscribeDynamicTokens,
11
11
  subscribeGlobalDarkMode,
12
12
  toggleGlobalDarkMode
13
- } from "./chunk-XOH25F3E.mjs";
13
+ } from "./chunk-KHQ2BWYH.mjs";
14
14
  import {
15
15
  defaultTheme,
16
16
  getTheme,
17
17
  resolveClassName,
18
18
  setTheme
19
- } from "./chunk-3KLOVONL.mjs";
19
+ } from "./chunk-IJAXJVB4.mjs";
20
20
 
21
21
  // src/config.ts
22
22
  function parseHexRgb(hex) {
@@ -1,10 +1,10 @@
1
- import "./chunk-XOH25F3E.mjs";
1
+ import "./chunk-KHQ2BWYH.mjs";
2
2
  import {
3
3
  Fragment,
4
4
  jsx,
5
5
  jsxs
6
- } from "./chunk-SC3L5I7H.mjs";
7
- import "./chunk-3KLOVONL.mjs";
6
+ } from "./chunk-CD5WFFIQ.mjs";
7
+ import "./chunk-IJAXJVB4.mjs";
8
8
 
9
9
  // src/jsxRuntimeCore.ts
10
10
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
@@ -2,8 +2,8 @@ import {
2
2
  Fragment,
3
3
  jsx,
4
4
  jsxs
5
- } from "./chunk-SC3L5I7H.mjs";
6
- import "./chunk-3KLOVONL.mjs";
5
+ } from "./chunk-CD5WFFIQ.mjs";
6
+ import "./chunk-IJAXJVB4.mjs";
7
7
  export {
8
8
  Fragment,
9
9
  jsx,
package/dist/theme.d.ts CHANGED
@@ -46,6 +46,31 @@ interface ThemeConfig {
46
46
  container?: ContainerConfig;
47
47
  }
48
48
  declare const defaultTheme: ThemeConfig;
49
+ /**
50
+ * Idempotent by CONTENT, not just a plain assignment — deliberately, not a
51
+ * micro-optimization. The babel plugin (`babel-plugin.js`) injects
52
+ * `applyKbachConfig(require('kbach.config.js'))` at the top of EVERY user
53
+ * file, not just one entry point, so it re-runs on every single file
54
+ * Metro (re-)executes. Editing `kbach.config.js` itself under Expo Fast
55
+ * Refresh forces Metro to invalidate and re-execute every file that
56
+ * requires it — which, because of that injection, is the entire app, not
57
+ * just the file you're looking at. A plain unconditional reassignment here
58
+ * would hand every one of those re-executions a BRAND NEW theme object
59
+ * (`resolveKbachConfig` always builds one fresh) even when the config's
60
+ * actual content hasn't changed, which in turn invalidates every
61
+ * `useColors()` call's `useMemo` (keyed on this object's identity) and
62
+ * re-evaluates `usesModeAwareColor()` for every reactive element
63
+ * (jsxRuntimeCore.ts) across the whole tree, all at once, right as Fast
64
+ * Refresh is already mid-teardown/rebuild for that same app-wide
65
+ * invalidation — reported live as "Unable to find node on an unmounted
66
+ * component," a known-flaky class of React Native Fast Refresh race that
67
+ * this amount of simultaneous churn measurably worsens. Comparing against
68
+ * the JSON serialization this function already computes (previously only
69
+ * for the JNI boundary, see `getThemeJson`) costs nothing new — it's the
70
+ * same computation the old code did unconditionally — it just skips the
71
+ * assignment (and therefore the reference change every downstream
72
+ * consumer sees) when the content is unchanged.
73
+ */
49
74
  declare function setTheme(theme: ThemeConfig): void;
50
75
  declare function getTheme(): ThemeConfig;
51
76
  /** Memoized JSON serialization of the active theme — passed across the JNI boundary. */
package/dist/theme.js CHANGED
@@ -378,8 +378,10 @@ var defaultTheme = {
378
378
  var activeTheme = defaultTheme;
379
379
  var activeThemeJson = JSON.stringify(defaultTheme);
380
380
  function setTheme(theme) {
381
+ const json = JSON.stringify(theme);
382
+ if (json === activeThemeJson) return;
381
383
  activeTheme = theme;
382
- activeThemeJson = JSON.stringify(theme);
384
+ activeThemeJson = json;
383
385
  }
384
386
  function getTheme() {
385
387
  return activeTheme;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kbach/react-native",
3
- "version": "1.0.0-beta.50",
3
+ "version": "1.0.0-beta.51",
4
4
  "description": "Kbach's utility-class styling engine for React Native — the same class names as @kbach/react, resolved to a StyleSheet-ready style object via a Kotlin/JNI TurboModule (native), the same WASM engine @kbach/react uses (Expo Web / react-native-web), or a pure-JS fallback engine (Expo Go). Ships its own native Android module — one npm install, no separate native package. Beta. Native is Android-only (no iOS bridge yet); works with the React Native CLI, an Expo dev client, AND Expo Go (a reduced-coverage pure-JS fallback, since Expo Go can't load any custom native module).",
5
5
  "keywords": [
6
6
  "css",