@kbach/react-native 1.0.0-beta.32 → 1.0.0-beta.34

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.
@@ -5,17 +5,20 @@ import { ReactNode } from 'react';
5
5
 
6
6
  /**
7
7
  * A `kbach.config.js`-style theme customization — same shape as
8
- * @kbach/react's `KbachConfig`, minus mode-aware `{ light, dark }` color
9
- * entries in `extend.colors` specifically: aliasing/opacity math here only
10
- * ever operates on plain hex (`brand: 'blue-6'`, `brandSoft: 'brand/30'`),
11
- * matching `jsEngine/resolvers/color.ts`'s `lookupHex`'s own Plain-only
12
- * convention — a reference to an EXISTING color that happens to be
13
- * mode-aware (`theme.colors` can hold those now, for `useColors()`; see
14
- * `theme.ts`'s own `ColorEntry` doc comment) just doesn't resolve as an
15
- * alias, falling through to "literal CSS value" the same way an unknown
16
- * name would. A config object goes in, a resolved `ThemeConfig` comes out,
17
- * via one PURE function (`resolveKbachConfig`) — no plugins, no
18
- * live-update store, nothing to keep "in sync."
8
+ * @kbach/react's `KbachConfig`, minus @kbach/react's own PER-COLOR
9
+ * `{ light, dark }` object form in `extend.colors` specifically (real
10
+ * mode-aware colors here are written via the grouped `dark` key instead —
11
+ * see `KbachConfig.extend.colors`'s own doc comment below): aliasing/
12
+ * opacity math on an individual color value here only ever operates on
13
+ * plain hex (`brand: 'blue-6'`, `brandSoft: 'brand/30'`), matching
14
+ * `jsEngine/resolvers/color.ts`'s `lookupHex`'s own Plain-only convention
15
+ * — a reference to an EXISTING color that happens to be mode-aware
16
+ * (`theme.colors` can hold those, for `useColors()`; see `theme.ts`'s own
17
+ * `ColorEntry` doc comment) just doesn't resolve as an alias, falling
18
+ * through to "literal CSS value" the same way an unknown name would. A
19
+ * config object goes in, a resolved `ThemeConfig` comes out, via one PURE
20
+ * function (`resolveKbachConfig`) — no plugins, no live-update store,
21
+ * nothing to keep "in sync."
19
22
  */
20
23
  interface KbachConfig {
21
24
  darkMode?: ThemeConfig['darkMode'];
@@ -37,8 +40,26 @@ interface KbachConfig {
37
40
  * '#ff6b35', brandSoft: 'brand/30' }` — `brandSoft` resolves to
38
41
  * `brand`'s own value at 30% opacity). A string that doesn't match any
39
42
  * known color name is treated as a literal CSS value instead.
43
+ *
44
+ * `dark` is a reserved key, not a color name: a parallel map of
45
+ * dark-mode overrides for any of the OTHER names in this same object —
46
+ * `{ surface: 'gray-2', card: 'white', dark: { surface: 'gray-11',
47
+ * card: 'gray-9' } }` makes `surface`/`card` mode-aware
48
+ * (`{ light, dark }` entries in the resolved theme), while `brand`
49
+ * (never mentioned under `dark`) stays a plain, mode-independent
50
+ * color. A name under `dark` with no matching top-level (light) entry
51
+ * is skipped — mode-aware colors need both sides written out, and
52
+ * inventing a "light" value out of nowhere would be worse than just
53
+ * not creating the entry at all. References inside `dark` resolve
54
+ * against the built-in palette and OTHER entries within `dark` itself
55
+ * (not against the light-side custom colors) — the same
56
+ * "self-contained, ordering-independent-of-the-other-side" resolution
57
+ * the light side already gets, just mirrored.
40
58
  */
41
- colors?: Record<string, string>;
59
+ colors?: {
60
+ [colorName: string]: string | Record<string, string> | undefined;
61
+ dark?: Record<string, string>;
62
+ };
42
63
  spacing?: Record<string, number>;
43
64
  screens?: Record<string, number>;
44
65
  /**
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { ColorEntry, ContainerConfig, ThemeConfig, defaultTheme, getTheme, setTheme } from './theme';
2
- export { C as ClassArray, a as ClassDictionary, b as ClassValue, c as ColorScale, d as ColorsAPI, K as KbachConfig, T as ThemeProvider, e as ThemeProviderProps, f as ThemeState, g as ThemeStorageAdapter, h as applyKbachConfig, i as clsx, r as resolveKbachConfig, u as useColors, j as useDynamicToken, k as useTheme } from './clsx-BCXALPGh.js';
2
+ export { C as ClassArray, a as ClassDictionary, b as ClassValue, c as ColorScale, d as ColorsAPI, K as KbachConfig, T as ThemeProvider, e as ThemeProviderProps, f as ThemeState, g as ThemeStorageAdapter, h as applyKbachConfig, i as clsx, r as resolveKbachConfig, u as useColors, j as useDynamicToken, k as useTheme } from './clsx-CxjWSCcU.js';
3
3
  export { ThemeMode, getGlobalDarkMode, getGlobalThemeMode, setGlobalThemeMode, subscribeGlobalDarkMode, toggleGlobalDarkMode } from './darkModeStore';
4
4
  export { deleteDynamicToken, getDynamicToken, setDynamicToken, subscribeDynamicTokens } from './dynamicTokens';
5
5
  import 'react';
package/dist/index.js CHANGED
@@ -1843,15 +1843,33 @@ function resolveColorEntry(value, lookup) {
1843
1843
  return opacity === void 0 ? base : applyOpacityToHex(base, opacity);
1844
1844
  }
1845
1845
  function resolveColors(extend, existing) {
1846
- const resolved = {};
1847
- const lookup = (name) => {
1848
- const found = resolved[name] ?? existing[name];
1846
+ const { dark: darkExtend, ...lightExtend } = extend;
1847
+ const resolvedLight = {};
1848
+ const lookupLight = (name) => {
1849
+ const found = resolvedLight[name] ?? existing[name];
1849
1850
  return typeof found === "string" ? found : void 0;
1850
1851
  };
1851
- for (const [name, value] of Object.entries(extend)) {
1852
- resolved[name] = resolveColorEntry(value, lookup);
1852
+ for (const [name, value] of Object.entries(lightExtend)) {
1853
+ if (typeof value !== "string") continue;
1854
+ resolvedLight[name] = resolveColorEntry(value, lookupLight);
1855
+ }
1856
+ const result = { ...resolvedLight };
1857
+ if (darkExtend) {
1858
+ const resolvedDark = {};
1859
+ const lookupDark = (name) => {
1860
+ const found = resolvedDark[name] ?? existing[name];
1861
+ return typeof found === "string" ? found : void 0;
1862
+ };
1863
+ for (const [name, value] of Object.entries(darkExtend)) {
1864
+ resolvedDark[name] = resolveColorEntry(value, lookupDark);
1865
+ }
1866
+ for (const [name, darkValue] of Object.entries(resolvedDark)) {
1867
+ const lightValue = resolvedLight[name];
1868
+ if (lightValue === void 0) continue;
1869
+ result[name] = { light: lightValue, dark: darkValue };
1870
+ }
1853
1871
  }
1854
- return resolved;
1872
+ return result;
1855
1873
  }
1856
1874
  function resolveKbachConfig(config) {
1857
1875
  let theme = { ...import_theme2.defaultTheme, ...config.theme };
@@ -1,5 +1,5 @@
1
1
  export { ColorEntry, ContainerConfig, ThemeConfig, defaultTheme, getTheme, setTheme } from './theme';
2
- export { C as ClassArray, a as ClassDictionary, b as ClassValue, c as ColorScale, d as ColorsAPI, K as KbachConfig, T as ThemeProvider, e as ThemeProviderProps, f as ThemeState, h as applyKbachConfig, i as clsx, r as resolveKbachConfig, u as useColors, j as useDynamicToken, k as useTheme } from './clsx-BCXALPGh.js';
2
+ export { C as ClassArray, a as ClassDictionary, b as ClassValue, c as ColorScale, d as ColorsAPI, K as KbachConfig, T as ThemeProvider, e as ThemeProviderProps, f as ThemeState, h as applyKbachConfig, i as clsx, r as resolveKbachConfig, u as useColors, j as useDynamicToken, k as useTheme } from './clsx-CxjWSCcU.js';
3
3
  export { ThemeMode, getGlobalDarkMode, getGlobalThemeMode, setGlobalThemeMode, subscribeGlobalDarkMode, toggleGlobalDarkMode } from './darkModeStore';
4
4
  export { deleteDynamicToken, getDynamicToken, setDynamicToken, subscribeDynamicTokens } from './dynamicTokens';
5
5
  import 'react';
package/dist/index.web.js CHANGED
@@ -271,15 +271,33 @@ function resolveColorEntry(value, lookup) {
271
271
  return opacity === void 0 ? base : applyOpacityToHex(base, opacity);
272
272
  }
273
273
  function resolveColors(extend, existing) {
274
- const resolved = {};
275
- const lookup = (name) => {
276
- const found = resolved[name] ?? existing[name];
274
+ const { dark: darkExtend, ...lightExtend } = extend;
275
+ const resolvedLight = {};
276
+ const lookupLight = (name) => {
277
+ const found = resolvedLight[name] ?? existing[name];
277
278
  return typeof found === "string" ? found : void 0;
278
279
  };
279
- for (const [name, value] of Object.entries(extend)) {
280
- resolved[name] = resolveColorEntry(value, lookup);
280
+ for (const [name, value] of Object.entries(lightExtend)) {
281
+ if (typeof value !== "string") continue;
282
+ resolvedLight[name] = resolveColorEntry(value, lookupLight);
281
283
  }
282
- return resolved;
284
+ const result = { ...resolvedLight };
285
+ if (darkExtend) {
286
+ const resolvedDark = {};
287
+ const lookupDark = (name) => {
288
+ const found = resolvedDark[name] ?? existing[name];
289
+ return typeof found === "string" ? found : void 0;
290
+ };
291
+ for (const [name, value] of Object.entries(darkExtend)) {
292
+ resolvedDark[name] = resolveColorEntry(value, lookupDark);
293
+ }
294
+ for (const [name, darkValue] of Object.entries(resolvedDark)) {
295
+ const lightValue = resolvedLight[name];
296
+ if (lightValue === void 0) continue;
297
+ result[name] = { light: lightValue, dark: darkValue };
298
+ }
299
+ }
300
+ return result;
283
301
  }
284
302
  function resolveKbachConfig(config) {
285
303
  let theme = { ...import_theme2.defaultTheme, ...config.theme };
@@ -43,15 +43,33 @@ function resolveColorEntry(value, lookup) {
43
43
  return opacity === void 0 ? base : applyOpacityToHex(base, opacity);
44
44
  }
45
45
  function resolveColors(extend, existing) {
46
- const resolved = {};
47
- const lookup = (name) => {
48
- const found = resolved[name] ?? existing[name];
46
+ const { dark: darkExtend, ...lightExtend } = extend;
47
+ const resolvedLight = {};
48
+ const lookupLight = (name) => {
49
+ const found = resolvedLight[name] ?? existing[name];
49
50
  return typeof found === "string" ? found : void 0;
50
51
  };
51
- for (const [name, value] of Object.entries(extend)) {
52
- resolved[name] = resolveColorEntry(value, lookup);
52
+ for (const [name, value] of Object.entries(lightExtend)) {
53
+ if (typeof value !== "string") continue;
54
+ resolvedLight[name] = resolveColorEntry(value, lookupLight);
53
55
  }
54
- return resolved;
56
+ const result = { ...resolvedLight };
57
+ if (darkExtend) {
58
+ const resolvedDark = {};
59
+ const lookupDark = (name) => {
60
+ const found = resolvedDark[name] ?? existing[name];
61
+ return typeof found === "string" ? found : void 0;
62
+ };
63
+ for (const [name, value] of Object.entries(darkExtend)) {
64
+ resolvedDark[name] = resolveColorEntry(value, lookupDark);
65
+ }
66
+ for (const [name, darkValue] of Object.entries(resolvedDark)) {
67
+ const lightValue = resolvedLight[name];
68
+ if (lightValue === void 0) continue;
69
+ result[name] = { light: lightValue, dark: darkValue };
70
+ }
71
+ }
72
+ return result;
55
73
  }
56
74
  function resolveKbachConfig(config) {
57
75
  let theme = { ...defaultTheme, ...config.theme };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kbach/react-native",
3
- "version": "1.0.0-beta.32",
3
+ "version": "1.0.0-beta.34",
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",
@@ -124,6 +124,36 @@ module.exports = function kbachReactNativeBabelPlugin(_babel, options) {
124
124
  const filename = file.opts && file.opts.filename;
125
125
  if (filename && filename.indexOf('node_modules') !== -1) return;
126
126
 
127
+ // Metro/Expo's own internal polyfills (the Node.js-external-require
128
+ // shim, the assets-registry shim, node:/weak-ref virtualizations, ...)
129
+ // are synthetic "virtual modules" with no real file on disk. Expo's
130
+ // own Metro config marks every one of these with an embedded NUL
131
+ // control character in its module ID (Rollup's/Metro's own
132
+ // convention for "this isn't a real file"). These never sit inside
133
+ // node_modules, so the check above doesn't catch them — and Metro
134
+ // places them directly in the bundle PRELUDE, entirely outside any
135
+ // wrapped module, where `require` isn't defined as a real function
136
+ // at all. Confirmed as a real, reproducible runtime crash (not a
137
+ // hypothetical): injecting this plugin's applyKbachConfig() require()
138
+ // call into that exact polyfill produced Expo's own "[runtime not
139
+ // ready]: ReferenceError: Property 'require' doesn't exist" at app
140
+ // startup, verified by exporting a real dev bundle and finding the
141
+ // offending, un-instrumented require() call sitting inside
142
+ // withMetroMultiPlatform.js's own external-require polyfill — a file
143
+ // this plugin was never meant to touch at all.
144
+ //
145
+ // The control character is NOT at the start of `filename`, so
146
+ // `startsWith`/`charCodeAt(0)` (what an earlier version of this
147
+ // check used) never matches — confirmed by actually logging Babel's
148
+ // real `filename` for this file, not assumed: Metro joins the
149
+ // project root onto the raw virtual module ID first, so the control
150
+ // character ends up in the MIDDLE of the full string, right after
151
+ // the trailing path separator, not at index 0. A search anywhere in
152
+ // the string (rather than just its start) is what actually catches
153
+ // it — see this file's own test suite for the exact filename shape
154
+ // this was verified against.
155
+ if (filename && filename.indexOf(String.fromCharCode(0)) !== -1) return;
156
+
127
157
  const comments = file.ast.comments || (file.ast.comments = []);
128
158
  const alreadySet = comments.some((c) => /@jsxImportSource|@jsxRuntime/.test(c.value));
129
159
  if (!alreadySet) {