rnwind 0.0.9 → 0.0.11

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 (47) hide show
  1. package/lib/cjs/core/parser/tw-parser.cjs +126 -3
  2. package/lib/cjs/core/parser/tw-parser.cjs.map +1 -1
  3. package/lib/cjs/core/parser/tw-parser.d.ts +22 -0
  4. package/lib/cjs/core/style-builder/build-style.cjs +12 -3
  5. package/lib/cjs/core/style-builder/build-style.cjs.map +1 -1
  6. package/lib/cjs/core/style-builder/build-style.d.ts +3 -1
  7. package/lib/cjs/core/style-builder/union-builder.cjs +9 -1
  8. package/lib/cjs/core/style-builder/union-builder.cjs.map +1 -1
  9. package/lib/cjs/core/style-builder/union-builder.d.ts +7 -0
  10. package/lib/cjs/runtime/hooks/use-scheme.cjs +8 -5
  11. package/lib/cjs/runtime/hooks/use-scheme.cjs.map +1 -1
  12. package/lib/cjs/runtime/index.cjs +1 -0
  13. package/lib/cjs/runtime/index.cjs.map +1 -1
  14. package/lib/cjs/runtime/index.d.ts +1 -1
  15. package/lib/cjs/runtime/lookup-css.cjs +27 -0
  16. package/lib/cjs/runtime/lookup-css.cjs.map +1 -1
  17. package/lib/cjs/runtime/lookup-css.d.ts +18 -0
  18. package/lib/cjs/testing/index.cjs +1 -1
  19. package/lib/cjs/testing/index.cjs.map +1 -1
  20. package/lib/esm/core/parser/color.mjs +1 -1
  21. package/lib/esm/core/parser/tw-parser.d.ts +22 -0
  22. package/lib/esm/core/parser/tw-parser.mjs +107 -2
  23. package/lib/esm/core/parser/tw-parser.mjs.map +1 -1
  24. package/lib/esm/core/style-builder/build-style.d.ts +3 -1
  25. package/lib/esm/core/style-builder/build-style.mjs +12 -3
  26. package/lib/esm/core/style-builder/build-style.mjs.map +1 -1
  27. package/lib/esm/core/style-builder/union-builder.d.ts +7 -0
  28. package/lib/esm/core/style-builder/union-builder.mjs +9 -1
  29. package/lib/esm/core/style-builder/union-builder.mjs.map +1 -1
  30. package/lib/esm/runtime/hooks/use-scheme.mjs +8 -5
  31. package/lib/esm/runtime/hooks/use-scheme.mjs.map +1 -1
  32. package/lib/esm/runtime/index.d.ts +1 -1
  33. package/lib/esm/runtime/index.mjs +1 -1
  34. package/lib/esm/runtime/index.mjs.map +1 -1
  35. package/lib/esm/runtime/lookup-css.d.ts +18 -0
  36. package/lib/esm/runtime/lookup-css.mjs +26 -1
  37. package/lib/esm/runtime/lookup-css.mjs.map +1 -1
  38. package/lib/esm/testing/index.mjs +2 -2
  39. package/lib/esm/testing/index.mjs.map +1 -1
  40. package/package.json +1 -1
  41. package/src/core/parser/tw-parser.ts +118 -1
  42. package/src/core/style-builder/build-style.ts +12 -1
  43. package/src/core/style-builder/union-builder.ts +10 -1
  44. package/src/runtime/hooks/use-scheme.ts +8 -4
  45. package/src/runtime/index.ts +1 -0
  46. package/src/runtime/lookup-css.ts +28 -0
  47. package/src/testing/index.ts +3 -0
@@ -38,6 +38,8 @@ const cache = {
38
38
  atoms: Object.create(null),
39
39
  breakpoints: Object.create(null),
40
40
  breakpointList: [],
41
+ /** Per-scheme theme token tables (`--color-*`, `--spacing-*`, …) the manifest registers for `useColor` / `useToken` / `useSize`. */
42
+ themeTokens: {},
41
43
  };
42
44
  /**
43
45
  * Bumps on every {@link registerAtoms} call. {@link HoistCache} entries
@@ -461,6 +463,28 @@ function registerBreakpoints(breakpoints) {
461
463
  function getBreakpoints() {
462
464
  return cache.breakpointList;
463
465
  }
466
+ /**
467
+ * Register the per-scheme theme token tables the manifest module emits at
468
+ * load time — the data source for `useColor` / `useToken` / `useSize`. The
469
+ * build lowers `--color-*` tokens to sRGB before registering them, so these
470
+ * are RN-safe. Replaces the prior tables; bumps `atomVersion` so a theme HMR
471
+ * cycle re-resolves. The build registers tokens here so the hooks work out of
472
+ * the box, without the user manually threading a `tables` prop on the provider.
473
+ * @param tables Scheme name → (token name → value) map.
474
+ */
475
+ function registerThemeTokens(tables) {
476
+ cache.themeTokens = tables;
477
+ atomVersion += 1;
478
+ }
479
+ /**
480
+ * The manifest-registered theme token tables. The provider merges these under
481
+ * any explicit `tables` prop (the prop wins), so `useColor` resolves from the
482
+ * build by default.
483
+ * @returns Registered per-scheme token tables.
484
+ */
485
+ function getThemeTokens() {
486
+ return cache.themeTokens;
487
+ }
464
488
  /**
465
489
  * Sentinel name returned by {@link activeBreakpointFor} ONLY when no
466
490
  * breakpoints are registered at all (bundle without rnwind-transformed
@@ -542,6 +566,7 @@ function __resetLookupCssState() {
542
566
  for (const key of Object.keys(cache.breakpoints))
543
567
  delete cache.breakpoints[key];
544
568
  cache.breakpointList = [];
569
+ cache.themeTokens = {};
545
570
  windowHeightProvider = null;
546
571
  schemeLoader = null;
547
572
  WARNED_MISSING_INSETS = false;
@@ -549,5 +574,5 @@ function __resetLookupCssState() {
549
574
  atomVersion += 1;
550
575
  }
551
576
 
552
- export { BASE_BREAKPOINT, __resetLookupCssState, activeBreakpointFor, breakpointTier, getBreakpoints, getStyleVersion, loadScheme, lookupCss, registerAtoms, registerBreakpoints, registerSchemeLoader, setWindowHeightProvider };
577
+ export { BASE_BREAKPOINT, __resetLookupCssState, activeBreakpointFor, breakpointTier, getBreakpoints, getStyleVersion, getThemeTokens, loadScheme, lookupCss, registerAtoms, registerBreakpoints, registerSchemeLoader, registerThemeTokens, setWindowHeightProvider };
553
578
  //# sourceMappingURL=lookup-css.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"lookup-css.mjs","sources":["../../../../src/runtime/lookup-css.ts"],"sourcesContent":["/**\n * Runtime resolver for rnwind-transformed files.\n *\n * Hot path is ONE WeakMap.get + cached-array return for stable atoms\n * (no `active:`/`focus:`/`*-safe` variance beyond what the cache key\n * captures). First call per (hoist, scheme, stateIndex) walks the\n * atoms once, looks each up as\n *\n * `cache.atoms[scheme]?.[atom] ?? cache.atoms.common?.[atom]`\n *\n * and caches the result. `registerAtoms(scheme, record)` bumps a\n * version counter; the next lookup notices the mismatch and rebuilds.\n *\n * Keyframes are inlined directly into atom values via `animationName`\n * at build time — no separate registry.\n */\n\nimport type { RnwindState } from './components/rnwind-provider'\n\n/** Empty sentinel returned when the input is null / undefined / empty. */\nconst EMPTY_STYLES: readonly unknown[] = []\n\n/** Registry key for the always-loaded fallback scheme. */\nconst COMMON_SCHEME = 'common'\n\n/** Atom name prefix marking a press-state-gated atom. */\nconst ACTIVE_PREFIX = 'active:'\n\n/** Atom name prefix marking a focus-state-gated atom. */\nconst FOCUS_PREFIX = 'focus:'\n\n/** Partial record — missing keys resolve to undefined, which the fallback handles. */\ntype SchemeAtomsRecord = Partial<Record<string, unknown>>\n\n/** 0 = idle, 1 = active, 2 = focus, 3 = both. */\ntype StateIndex = 0 | 1 | 2 | 3\n\n/**\n * One entry in the sorted-by-threshold breakpoints array. The runtime\n * derives an atom's threshold by matching its `<prefix>:` against\n * `name`; the array form is preferred over a Map so the hot-path\n * `tierFor(width)` walk is a tight numeric loop.\n */\ninterface BreakpointEntry {\n readonly name: string\n readonly minWidth: number\n}\n\n/**\n * Process-global style cache. Replaced key-by-key by {@link registerAtoms}.\n * Plain record-of-records: `scheme → atom → style`. Active scheme\n * lookup is `cache.atoms[scheme]?.[atom]` — two property reads with\n * an `?? cache.atoms.common[atom]` fallback. No loops over the\n * registry, ever.\n *\n * `breakpoints` mirrors the build-time table the manifest module\n * registers via {@link registerBreakpoints} — `name → px-threshold` for\n * fast prefix-based atom filtering plus the sorted-by-threshold list\n * for cheap tier-index computation in `lookupCss`.\n */\nconst cache = {\n atoms: Object.create(null) as Partial<Record<string, SchemeAtomsRecord>>,\n breakpoints: Object.create(null) as Partial<Record<string, number>>,\n breakpointList: [] as readonly BreakpointEntry[],\n}\n\n/**\n * Bumps on every {@link registerAtoms} call. {@link HoistCache} entries\n * stamp themselves with the current version; a mismatch on read\n * triggers a rebuild so HMR-reloaded atoms propagate without manual\n * invalidation.\n */\nlet atomVersion = 0\n\n/** Optional window-height provider for the `screen-minus-y` marker. */\ntype WindowHeightProvider = () => number\nlet windowHeightProvider: WindowHeightProvider | null = null\n\n/**\n * Optional scheme loader. Registered by the generated manifest module\n * (`rnwind/__generated/schemes`) once at import time. SchemeProvider\n * calls it synchronously on every render with the active scheme name;\n * first call per scheme pulls the scheme's style module in via an\n * inline `require()` — subsequent calls are a no-op through Metro's\n * module cache.\n */\ntype SchemeLoader = (scheme: string) => void\nlet schemeLoader: SchemeLoader | null = null\n\n/** Module-scope flag so the missing-insets warning fires at most once. */\nlet WARNED_MISSING_INSETS = false\n\n/** Atoms we've already dev-warned about — keeps the noise to ONE line per typo per session. */\nconst WARNED_UNKNOWN_ATOMS = new Set<string>()\n\n/**\n * Compute the state-array index from the live interactState. Bit-or\n * encoding: 0 = idle, 1 = active, 2 = focus, 3 = both.\n * @param interactState Snapshot from `useInteract()` (or undefined).\n * @returns 0 / 1 / 2 / 3.\n */\nfunction stateIndexFor(interactState: InteractState | undefined): StateIndex {\n if (!interactState) return 0\n return (((interactState.active ? 1 : 0) | (interactState.focus ? 2 : 0)) as StateIndex)\n}\n\n/**\n * Fetch the px inset for one side. Falls back to 0 when insets is undefined.\n * @param side Compact side tag (`t` / `r` / `b` / `l`).\n * @param insets Active insets.\n * @returns Px value for that side.\n */\nfunction insetOf(side: string, insets: LookupInsets | undefined): number {\n if (!insets) return 0\n if (side === 't') return insets.top\n if (side === 'r') return insets.right\n if (side === 'b') return insets.bottom\n if (side === 'l') return insets.left\n return 0\n}\n\n/**\n * Collapse one safe-area marker into a concrete px number using the\n * active insets.\n * @param spec Marker spec tuple `[cssKey, sideTag, or, offset]`.\n * @param insets Active insets (or undefined → 0).\n * @returns Resolved px number.\n */\nfunction resolveMarker(spec: SafeMarkerSpec, insets: LookupInsets | undefined): number {\n const [, side, or_, offset] = spec\n if (side === 'screen-minus-y') {\n const h = windowHeightProvider ? windowHeightProvider() : 0\n return Math.max(0, h - insetOf('t', insets) - insetOf('b', insets))\n }\n let base = insetOf(side, insets)\n if (or_ !== undefined) base = Math.max(base, or_)\n if (offset !== undefined) base += offset\n return base\n}\n\n/**\n * Emit a one-shot dev warning when a safe-area atom resolves without\n * real insets in scope.\n * @param insets Insets received by the resolver.\n */\nfunction warnMissingInsetsOnce(insets: LookupInsets | undefined): void {\n if (WARNED_MISSING_INSETS) return\n const isDevelopment = typeof __DEV__ === 'undefined' || __DEV__\n if (!isDevelopment) return\n if (insets && (insets.top !== 0 || insets.right !== 0 || insets.bottom !== 0 || insets.left !== 0)) return\n WARNED_MISSING_INSETS = true\n // eslint-disable-next-line no-console\n console.warn(\n 'rnwind: a `*-safe` utility resolved with zero insets. Wire `insets` on <SchemeProvider> ' +\n '(e.g. `insets={useSafeAreaInsets()}` from react-native-safe-area-context).',\n )\n}\n\n/**\n * Resolve precomputed safe-area marker specs into a fresh RN style\n * object. Cannot be cached — insets vary per render with rotation /\n * keyboard.\n * @param specs Array of spec tuples.\n * @param insets Live safe-area insets.\n * @returns Fresh RN style object with concrete numbers.\n */\nfunction resolveSafe(specs: readonly SafeMarkerSpec[], insets: LookupInsets | undefined): Record<string, number> {\n warnMissingInsetsOnce(insets)\n const out: Record<string, number> = {}\n for (const spec of specs) out[spec[0]] = resolveMarker(spec, insets)\n return out\n}\n\n/**\n * Multiply `fontSize` / `lineHeight` in a resolved atom value by the\n * active font scale. Early-returns the original reference for any atom\n * that doesn't carry either property (most of them) — zero allocation\n * on the hot path for non-text atoms.\n * @param value Atom value as registered in the global table.\n * @param fontScale Multiplier from `useWindowDimensions().fontScale`.\n * @returns Scaled style object, or the original when no scaling applied.\n */\nfunction applyFontScale(value: unknown, fontScale: number): unknown {\n if (fontScale === 1) return value\n if (typeof value !== 'object' || value === null) return value\n const record = value as Record<string, unknown>\n const fs = record.fontSize\n const lh = record.lineHeight\n if (typeof fs !== 'number' && typeof lh !== 'number') return value\n const scaled: Record<string, unknown> = { ...record }\n if (typeof fs === 'number') scaled.fontSize = fs * fontScale\n if (typeof lh === 'number') scaled.lineHeight = lh * fontScale\n return scaled\n}\n\n/**\n * Read the precomputed safe-area marker spec list off an atom value.\n * Build-side `envelopeSafeMarkers` wraps safe atoms in\n * `{__safeStyle: [...]}`; this is a single property access.\n * @param value Atom value as registered in the global table.\n * @returns Spec array when the atom is safe-area, else null.\n */\nfunction readSafeSpecs(value: unknown): readonly SafeMarkerSpec[] | null {\n if (typeof value !== 'object' || value === null) return null\n const safe = (value as { __safeStyle?: readonly SafeMarkerSpec[] }).__safeStyle\n return safe ?? null\n}\n\n/**\n * Per-atom lookup. Two property reads: scheme's own table then the\n * common fallback. Returns `undefined` for unknown atoms — the caller\n * skips them.\n * @param scheme Active scheme.\n * @param atom Atom name.\n * @returns Resolved value, or undefined.\n */\nfunction lookupAtom(scheme: string, atom: string): unknown {\n const schemeTable = cache.atoms[scheme]\n if (schemeTable !== undefined) {\n const own = schemeTable[atom]\n if (own !== undefined) return own\n }\n const common = cache.atoms[COMMON_SCHEME]\n return common === undefined ? undefined : common[atom]\n}\n\n/**\n * Whether an atom should participate in a given interact-state index.\n * - idle (0): no `active:` / `focus:` atoms.\n * - active (1): base + `active:`.\n * - focus (2): base + `focus:`.\n * - both (3): base + `active:` + `focus:`.\n * @param atom Atom name.\n * @param stateIndex Encoded state (0/1/2/3).\n * @returns True when the atom should be emitted for this state.\n */\nfunction atomMatchesState(atom: string, stateIndex: StateIndex): boolean {\n // Cheap prefix check — check the first code point before the full\n // `startsWith` so we skip it for any atom whose first letter isn't\n // `a` / `f`.\n const code = atom.codePointAt(0)\n if (code === 97 /* a */ && atom.startsWith(ACTIVE_PREFIX)) return (stateIndex & 1) !== 0\n if (code === 102 /* f */ && atom.startsWith(FOCUS_PREFIX)) return (stateIndex & 2) !== 0\n return true\n}\n\n/**\n * Whether an atom passes the responsive-breakpoint gate for the\n * current `windowWidth`. Atoms without a registered `<prefix>:` are\n * always-on (the common case — `bg-red-500`, `active:bg-blue-700`).\n * Atoms whose first prefix matches a registered breakpoint name pass\n * only when `windowWidth >= threshold`.\n * @param atom Atom name.\n * @param windowWidth Live `useWindowDimensions().width` snapshot.\n * @returns True when the atom should be emitted for this width.\n */\nfunction atomMatchesBreakpoint(atom: string, windowWidth: number): boolean {\n const colon = atom.indexOf(':')\n if (colon === -1) return true\n const prefix = atom.slice(0, colon)\n const threshold = cache.breakpoints[prefix]\n if (threshold === undefined) return true\n return windowWidth >= threshold\n}\n\n/**\n * Tier index — count of registered breakpoints whose threshold is\n * `<= windowWidth`. Bounded by the breakpoint count, so it's a stable\n * cache-key dimension instead of the unbounded raw width. Crossings\n * happen ~5 times across the device-width spectrum, not per-pixel.\n * @param windowWidth Live width.\n * @returns Tier 0..N where N = `cache.breakpointList.length`.\n */\nfunction tierFor(windowWidth: number): number {\n let tier = 0\n for (const entry of cache.breakpointList) {\n if (windowWidth >= entry.minWidth) tier += 1\n else break\n }\n return tier\n}\n\n/**\n * Public breakpoint-tier for a width — the count of registered breakpoints\n * whose threshold is reached. Used by the runtime resolver as its width\n * cache dimension: the numeric tier is bounded AND exact, unlike the\n * clamped `activeBreakpoint` NAME (which collapses tier-0 into the smallest\n * breakpoint, so widths straddling that threshold would share a cache key\n * and serve a stale style).\n * @param windowWidth Live window width in px.\n * @returns Tier 0..N.\n */\nexport function breakpointTier(windowWidth: number): number {\n return tierFor(windowWidth)\n}\n\n/**\n * Build the style array for a (hoist, scheme, state, width) tuple.\n * Walks the atom list, applies the interact-state and breakpoint\n * filters, resolves each atom via scheme→common fallback, and\n * envelopes safe values via {@link resolveSafe}.\n * @param atoms Atom name list (build-time constant).\n * @param scheme Active scheme.\n * @param stateIndex Encoded active/focus state.\n * @param insets Live safe-area insets.\n * @param fontScale Font scale multiplier.\n * @param windowWidth Live window width — gates `md:*` / `lg:*` atoms.\n * @returns Fresh style array.\n */\nfunction buildStyleArray(\n atoms: readonly string[],\n scheme: string,\n stateIndex: StateIndex,\n insets: LookupInsets | undefined,\n fontScale: number,\n windowWidth: number,\n): readonly unknown[] {\n const out: unknown[] = []\n for (const atom of atoms) {\n if (!atomMatchesState(atom, stateIndex)) continue\n if (!atomMatchesBreakpoint(atom, windowWidth)) continue\n const value = lookupAtom(scheme, atom)\n if (value === undefined) {\n warnUnknownAtomOnce(atom)\n continue\n }\n const safe = readSafeSpecs(value)\n const resolved = safe === null ? value : resolveSafe(safe, insets)\n out.push(applyFontScale(resolved, fontScale))\n }\n return out\n}\n\n/**\n * Emit a one-shot dev warning when an atom name doesn't resolve in the\n * registry. The two real causes are a typo (`bg-red-501`) or a class\n * the build-time scanner never saw because it lives in a string the\n * oxide tokeniser can't see (e.g. computed at runtime). Either way, a\n * silent empty style is the worst possible UX — surface it.\n *\n * Filters cosmetic non-issues: empty strings, build-time `__safeStyle`\n * envelopes that wandered in, etc.\n * @param atom Class name that didn't resolve.\n */\nfunction warnUnknownAtomOnce(atom: string): void {\n if (atom.length === 0) return\n const isDevelopment = typeof __DEV__ === 'undefined' || __DEV__\n if (!isDevelopment) return\n if (WARNED_UNKNOWN_ATOMS.has(atom)) return\n WARNED_UNKNOWN_ATOMS.add(atom)\n // eslint-disable-next-line no-console\n console.warn(\n `rnwind: unknown class \"${atom}\" — typo, or the class is built dynamically and the build-time ` +\n `scanner never saw it. Static literals + ternaries are scanned automatically; runtime-built ` +\n `strings need to appear somewhere as a literal so oxide can pick them up.`,\n )\n}\n\n/**\n * Per-hoist cache entry. `version` stamps `atomVersion` at build time\n * so HMR reloads (which bump the counter) invalidate cleanly on next\n * read. `hasSafe` prevents caching results whose values depend on\n * per-render insets. `byKey` maps `\"${scheme}|${stateIndex}\"` to the\n * cached result.\n */\ninterface HoistCache {\n version: number\n hasSafe: boolean\n byKey: Partial<Record<string, readonly unknown[]>>\n}\n\n/**\n * Per-atom-list cache keyed on the hoist reference. WeakMap so\n * hoists GC with their host module on HMR.\n */\nconst resultCache = new WeakMap<readonly string[], HoistCache>()\n\n/**\n * Walk the atom list once to detect safe-area atoms — results that\n * vary per render with `insets`. When any atom envelopes safe specs\n * we skip the cache and rebuild every call.\n * @param atoms Hoist atom list.\n * @param scheme Active scheme.\n * @returns Whether the hoist resolves a safe atom under this scheme.\n */\nfunction detectHasSafe(atoms: readonly string[], scheme: string): boolean {\n for (const atom of atoms) {\n const value = lookupAtom(scheme, atom)\n if (readSafeSpecs(value) !== null) return true\n }\n return false\n}\n\n/**\n * Cache-keyed resolution for the common static-schema case. Returns a\n * stable array reference across renders until `atomVersion` bumps.\n * For hoists containing safe atoms — which depend on per-render\n * insets — rebuilds every call.\n *\n * The `tier` dimension keeps the cache bounded under responsive\n * variants: instead of keying on raw `windowWidth` (which would explode\n * the cache to one entry per pixel), we key on the count of registered\n * breakpoints whose threshold is reached. That gives at most\n * `breakpointCount + 1` cache rows per (scheme, state, fontScale).\n * @param atoms Hoist atom list.\n * @param scheme Active scheme.\n * @param stateIndex Encoded interact state.\n * @param insets Live safe-area insets.\n * @param fontScale Font scale multiplier.\n * @param windowWidth Live window width.\n * @returns Style array.\n */\nfunction lookupCached(\n atoms: readonly string[],\n scheme: string,\n stateIndex: StateIndex,\n insets: LookupInsets | undefined,\n fontScale: number,\n windowWidth: number,\n): readonly unknown[] {\n let entry = resultCache.get(atoms)\n if (entry?.version !== atomVersion) {\n entry = { version: atomVersion, hasSafe: detectHasSafe(atoms, scheme), byKey: Object.create(null) }\n resultCache.set(atoms, entry)\n }\n if (entry.hasSafe) return buildStyleArray(atoms, scheme, stateIndex, insets, fontScale, windowWidth)\n const tier = tierFor(windowWidth)\n const key = `${scheme}|${stateIndex}|${fontScale}|${tier}`\n const cached = entry.byKey[key]\n if (cached !== undefined) return cached\n const fresh = buildStyleArray(atoms, scheme, stateIndex, insets, fontScale, windowWidth)\n entry.byKey[key] = fresh\n return fresh\n}\n\n/**\n * Per-render snapshot of which interactive states (active, focus) are\n * currently engaged. Forwarded from the `useInteract()` hook the\n * transformer injects.\n */\nexport interface InteractState {\n active?: boolean\n focus?: boolean\n}\n\n/**\n * Safe-area insets bundle the transformer passes to `lookupCss` when a\n * file uses any `*-safe` utility.\n */\nexport interface LookupInsets {\n top: number\n right: number\n bottom: number\n left: number\n}\n\n\n/**\n * Precomputed safe-area marker spec emitted by the build-side\n * `envelopeSafeMarkers`. Tuple form: `[cssKey, sideTag, or, offset]`.\n */\nexport type SafeMarkerSpec = readonly [string, string, number | undefined, number | undefined]\n\n/** Type alias: the atom-list build output the transformer emits. */\nexport type HoistedClassName = readonly string[]\n\n/**\n * Register a window-height provider used by the `screen-minus-y`\n * safe-area variant. When not wired, `h-screen-safe` resolves to `0`.\n * @param provider Callback returning the current window height in px.\n */\nexport function setWindowHeightProvider(provider: WindowHeightProvider | null): void {\n windowHeightProvider = provider\n}\n\n/**\n * Register the scheme-loader function exported by the generated\n * manifest module. Called once at manifest-module evaluation time —\n * subsequent registrations override the previous loader (useful for\n * tests).\n * @param loader Manifest's `ensureSchemeLoaded` function, or null to\n * detach (tests).\n */\nexport function registerSchemeLoader(loader: SchemeLoader | null): void {\n schemeLoader = loader\n}\n\n/**\n * Ensure the given scheme's style module is loaded. Safe to call in\n * render — zero-cost after the first call per scheme thanks to\n * Metro's module cache, and a no-op when no loader is registered\n * (tests, or a bundle without rnwind-transformed sources).\n * @param scheme Active scheme name.\n */\nexport function loadScheme(scheme: string): void {\n if (schemeLoader) schemeLoader(scheme)\n}\n\n/**\n * Register (or re-register) one scheme's atoms in the global registry.\n * Pure property assignment — no iteration, no allocation. The version\n * counter bump invalidates every hoist-level result cache lazily on\n * next read.\n * @param scheme Registry key — `'common'` for the always-loaded\n * fallback, or a variant name (`'dark'`, `'light'`, `'brand'`, ...).\n * @param atoms Plain record keyed by atom name.\n */\nexport function registerAtoms(scheme: string, atoms: Record<string, unknown>): void {\n cache.atoms[scheme] = atoms\n atomVersion += 1\n}\n\n/**\n * Current registry version — bumps on every `registerAtoms` /\n * `registerBreakpoints`. The molecule resolver folds it into its cache\n * key so an HMR atom reload invalidates derived results.\n * @returns Monotonic version counter.\n */\nexport function getStyleVersion(): number {\n return atomVersion\n}\n\n/**\n * Register the responsive-breakpoint table the manifest module emits at\n * load time. Replaces the prior table — calling with `{}` clears it.\n * Bumps `atomVersion` so any cached lookup invalidates on next read,\n * which matters during a theme HMR cycle that adds/removes breakpoints.\n * @param breakpoints Breakpoint name → minimum-width threshold (px).\n */\nexport function registerBreakpoints(breakpoints: Record<string, number>): void {\n const fresh: Partial<Record<string, number>> = Object.create(null)\n const list: BreakpointEntry[] = []\n for (const name of Object.keys(breakpoints)) {\n const minWidth = breakpoints[name]\n if (!Number.isFinite(minWidth) || minWidth <= 0) continue\n fresh[name] = minWidth\n list.push({ name, minWidth })\n }\n list.sort((a, b) => a.minWidth - b.minWidth || a.name.localeCompare(b.name))\n cache.breakpoints = fresh\n cache.breakpointList = list\n atomVersion += 1\n}\n\n/**\n * Snapshot of the registered breakpoints, for callers that want to\n * compute their own derivations (e.g. the provider deriving the active\n * breakpoint name). Returns a fresh array — callers can iterate without\n * worrying about concurrent mutation from a manifest reload.\n * @returns Breakpoints sorted by ascending min-width threshold.\n */\nexport function getBreakpoints(): readonly BreakpointEntry[] {\n return cache.breakpointList\n}\n\n/**\n * Sentinel name returned by {@link activeBreakpointFor} ONLY when no\n * breakpoints are registered at all (bundle without rnwind-transformed\n * sources, fresh test setup). When at least one breakpoint is\n * registered, the function falls back to the smallest registered name\n * instead — so phone-width devices (402 dp on a stock iPhone, well\n * below `sm = 640`) report `activeBreakpoint === 'sm'` rather than the\n * abstract `'base'`. This matches the user expectation that the value\n * is always a real Tailwind breakpoint label they can branch on.\n *\n * Note: this is decoupled from the className filter. `sm:*` atoms\n * still only fire at `windowWidth >= 640` per Tailwind's mobile-first\n * spec — `activeBreakpoint === 'sm'` at 402 means \"I'm in the smallest\n * tier\", not \"`sm:` classes are firing\".\n */\nexport const BASE_BREAKPOINT = 'base'\n\n/**\n * Resolve the currently-active breakpoint name for a width:\n * - `windowWidth >= some threshold` → the highest matching breakpoint name.\n * - below every registered threshold → the smallest registered name.\n * - no breakpoints registered → {@link BASE_BREAKPOINT}.\n * Always returns a string so consumers can branch on it without\n * null-handling.\n * @param windowWidth Live window width in px.\n * @returns Active breakpoint name (never null).\n */\nexport function activeBreakpointFor(windowWidth: number): string {\n const list = cache.breakpointList\n if (list.length === 0) return BASE_BREAKPOINT\n let active: string = list[0]!.name\n for (const entry of list) {\n if (windowWidth >= entry.minWidth) active = entry.name\n else break\n }\n return active\n}\n\n/**\n * Resolve a className input against the active rnwind context. Hot\n * path:\n * - Array input (build hoist): ONE WeakMap.get + ONE record-access\n * + cached array return. No per-render allocation when there's no\n * userStyle and the hoist has no safe atoms.\n * - String input (dynamic `className={expr}`): tokenise + walk.\n * @param input Hoisted atom list or raw className string.\n * @param ctx Rnwind context — `{scheme, fontScale, insets}` (extra\n * fields ignored). Pass the result of `useRnwind()` directly.\n * @param userStyle Optional caller-supplied style appended last.\n * @param interactState Live active/focus flags from `useInteract()`.\n * @returns Style array for React Native's `style` prop.\n */\nexport function lookupCss(\n input: HoistedClassName | string | null | undefined,\n ctx: RnwindState,\n userStyle?: unknown,\n interactState?: InteractState,\n): readonly unknown[] {\n if (input === null || input === undefined) {\n return userStyle === undefined || userStyle === null ? EMPTY_STYLES : [userStyle]\n }\n const { scheme, insets, fontScale, windowWidth } = ctx\n if (typeof input === 'string') {\n const trimmed = input.trim()\n if (trimmed.length === 0) {\n return userStyle === undefined || userStyle === null ? EMPTY_STYLES : [userStyle]\n }\n const atoms = trimmed.split(/\\s+/)\n const out = buildStyleArray(atoms, scheme, stateIndexFor(interactState), insets, fontScale, windowWidth)\n if (userStyle === undefined || userStyle === null) return out\n return [...out, userStyle]\n }\n const base = lookupCached(input, scheme, stateIndexFor(interactState), insets, fontScale, windowWidth)\n if (userStyle === undefined || userStyle === null) return base\n return [...base, userStyle]\n}\n\n/** Test-only — clear the global registry between suites. */\nexport function __resetLookupCssState(): void {\n for (const key of Object.keys(cache.atoms)) delete cache.atoms[key]\n for (const key of Object.keys(cache.breakpoints)) delete cache.breakpoints[key]\n cache.breakpointList = []\n windowHeightProvider = null\n schemeLoader = null\n WARNED_MISSING_INSETS = false\n WARNED_UNKNOWN_ATOMS.clear()\n atomVersion += 1\n}\n\n/**\n * Test-only sugar: accept a single-scheme record and register it as the\n * `common` table.\n * @param record Atom name → value record (registered under `common`).\n */\nexport function __registerAtomsFromRecord(record: Record<string, unknown>): void {\n registerAtoms(COMMON_SCHEME, record)\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lookup-css.mjs","sources":["../../../../src/runtime/lookup-css.ts"],"sourcesContent":["/**\n * Runtime resolver for rnwind-transformed files.\n *\n * Hot path is ONE WeakMap.get + cached-array return for stable atoms\n * (no `active:`/`focus:`/`*-safe` variance beyond what the cache key\n * captures). First call per (hoist, scheme, stateIndex) walks the\n * atoms once, looks each up as\n *\n * `cache.atoms[scheme]?.[atom] ?? cache.atoms.common?.[atom]`\n *\n * and caches the result. `registerAtoms(scheme, record)` bumps a\n * version counter; the next lookup notices the mismatch and rebuilds.\n *\n * Keyframes are inlined directly into atom values via `animationName`\n * at build time — no separate registry.\n */\n\nimport type { RnwindState } from './components/rnwind-provider'\nimport type { ThemeTables } from '../core/types'\n\n/** Empty sentinel returned when the input is null / undefined / empty. */\nconst EMPTY_STYLES: readonly unknown[] = []\n\n/** Registry key for the always-loaded fallback scheme. */\nconst COMMON_SCHEME = 'common'\n\n/** Atom name prefix marking a press-state-gated atom. */\nconst ACTIVE_PREFIX = 'active:'\n\n/** Atom name prefix marking a focus-state-gated atom. */\nconst FOCUS_PREFIX = 'focus:'\n\n/** Partial record — missing keys resolve to undefined, which the fallback handles. */\ntype SchemeAtomsRecord = Partial<Record<string, unknown>>\n\n/** 0 = idle, 1 = active, 2 = focus, 3 = both. */\ntype StateIndex = 0 | 1 | 2 | 3\n\n/**\n * One entry in the sorted-by-threshold breakpoints array. The runtime\n * derives an atom's threshold by matching its `<prefix>:` against\n * `name`; the array form is preferred over a Map so the hot-path\n * `tierFor(width)` walk is a tight numeric loop.\n */\ninterface BreakpointEntry {\n readonly name: string\n readonly minWidth: number\n}\n\n/**\n * Process-global style cache. Replaced key-by-key by {@link registerAtoms}.\n * Plain record-of-records: `scheme → atom → style`. Active scheme\n * lookup is `cache.atoms[scheme]?.[atom]` — two property reads with\n * an `?? cache.atoms.common[atom]` fallback. No loops over the\n * registry, ever.\n *\n * `breakpoints` mirrors the build-time table the manifest module\n * registers via {@link registerBreakpoints} — `name → px-threshold` for\n * fast prefix-based atom filtering plus the sorted-by-threshold list\n * for cheap tier-index computation in `lookupCss`.\n */\nconst cache = {\n atoms: Object.create(null) as Partial<Record<string, SchemeAtomsRecord>>,\n breakpoints: Object.create(null) as Partial<Record<string, number>>,\n breakpointList: [] as readonly BreakpointEntry[],\n /** Per-scheme theme token tables (`--color-*`, `--spacing-*`, …) the manifest registers for `useColor` / `useToken` / `useSize`. */\n themeTokens: {} as ThemeTables,\n}\n\n/**\n * Bumps on every {@link registerAtoms} call. {@link HoistCache} entries\n * stamp themselves with the current version; a mismatch on read\n * triggers a rebuild so HMR-reloaded atoms propagate without manual\n * invalidation.\n */\nlet atomVersion = 0\n\n/** Optional window-height provider for the `screen-minus-y` marker. */\ntype WindowHeightProvider = () => number\nlet windowHeightProvider: WindowHeightProvider | null = null\n\n/**\n * Optional scheme loader. Registered by the generated manifest module\n * (`rnwind/__generated/schemes`) once at import time. SchemeProvider\n * calls it synchronously on every render with the active scheme name;\n * first call per scheme pulls the scheme's style module in via an\n * inline `require()` — subsequent calls are a no-op through Metro's\n * module cache.\n */\ntype SchemeLoader = (scheme: string) => void\nlet schemeLoader: SchemeLoader | null = null\n\n/** Module-scope flag so the missing-insets warning fires at most once. */\nlet WARNED_MISSING_INSETS = false\n\n/** Atoms we've already dev-warned about — keeps the noise to ONE line per typo per session. */\nconst WARNED_UNKNOWN_ATOMS = new Set<string>()\n\n/**\n * Compute the state-array index from the live interactState. Bit-or\n * encoding: 0 = idle, 1 = active, 2 = focus, 3 = both.\n * @param interactState Snapshot from `useInteract()` (or undefined).\n * @returns 0 / 1 / 2 / 3.\n */\nfunction stateIndexFor(interactState: InteractState | undefined): StateIndex {\n if (!interactState) return 0\n return (((interactState.active ? 1 : 0) | (interactState.focus ? 2 : 0)) as StateIndex)\n}\n\n/**\n * Fetch the px inset for one side. Falls back to 0 when insets is undefined.\n * @param side Compact side tag (`t` / `r` / `b` / `l`).\n * @param insets Active insets.\n * @returns Px value for that side.\n */\nfunction insetOf(side: string, insets: LookupInsets | undefined): number {\n if (!insets) return 0\n if (side === 't') return insets.top\n if (side === 'r') return insets.right\n if (side === 'b') return insets.bottom\n if (side === 'l') return insets.left\n return 0\n}\n\n/**\n * Collapse one safe-area marker into a concrete px number using the\n * active insets.\n * @param spec Marker spec tuple `[cssKey, sideTag, or, offset]`.\n * @param insets Active insets (or undefined → 0).\n * @returns Resolved px number.\n */\nfunction resolveMarker(spec: SafeMarkerSpec, insets: LookupInsets | undefined): number {\n const [, side, or_, offset] = spec\n if (side === 'screen-minus-y') {\n const h = windowHeightProvider ? windowHeightProvider() : 0\n return Math.max(0, h - insetOf('t', insets) - insetOf('b', insets))\n }\n let base = insetOf(side, insets)\n if (or_ !== undefined) base = Math.max(base, or_)\n if (offset !== undefined) base += offset\n return base\n}\n\n/**\n * Emit a one-shot dev warning when a safe-area atom resolves without\n * real insets in scope.\n * @param insets Insets received by the resolver.\n */\nfunction warnMissingInsetsOnce(insets: LookupInsets | undefined): void {\n if (WARNED_MISSING_INSETS) return\n const isDevelopment = typeof __DEV__ === 'undefined' || __DEV__\n if (!isDevelopment) return\n if (insets && (insets.top !== 0 || insets.right !== 0 || insets.bottom !== 0 || insets.left !== 0)) return\n WARNED_MISSING_INSETS = true\n // eslint-disable-next-line no-console\n console.warn(\n 'rnwind: a `*-safe` utility resolved with zero insets. Wire `insets` on <SchemeProvider> ' +\n '(e.g. `insets={useSafeAreaInsets()}` from react-native-safe-area-context).',\n )\n}\n\n/**\n * Resolve precomputed safe-area marker specs into a fresh RN style\n * object. Cannot be cached — insets vary per render with rotation /\n * keyboard.\n * @param specs Array of spec tuples.\n * @param insets Live safe-area insets.\n * @returns Fresh RN style object with concrete numbers.\n */\nfunction resolveSafe(specs: readonly SafeMarkerSpec[], insets: LookupInsets | undefined): Record<string, number> {\n warnMissingInsetsOnce(insets)\n const out: Record<string, number> = {}\n for (const spec of specs) out[spec[0]] = resolveMarker(spec, insets)\n return out\n}\n\n/**\n * Multiply `fontSize` / `lineHeight` in a resolved atom value by the\n * active font scale. Early-returns the original reference for any atom\n * that doesn't carry either property (most of them) — zero allocation\n * on the hot path for non-text atoms.\n * @param value Atom value as registered in the global table.\n * @param fontScale Multiplier from `useWindowDimensions().fontScale`.\n * @returns Scaled style object, or the original when no scaling applied.\n */\nfunction applyFontScale(value: unknown, fontScale: number): unknown {\n if (fontScale === 1) return value\n if (typeof value !== 'object' || value === null) return value\n const record = value as Record<string, unknown>\n const fs = record.fontSize\n const lh = record.lineHeight\n if (typeof fs !== 'number' && typeof lh !== 'number') return value\n const scaled: Record<string, unknown> = { ...record }\n if (typeof fs === 'number') scaled.fontSize = fs * fontScale\n if (typeof lh === 'number') scaled.lineHeight = lh * fontScale\n return scaled\n}\n\n/**\n * Read the precomputed safe-area marker spec list off an atom value.\n * Build-side `envelopeSafeMarkers` wraps safe atoms in\n * `{__safeStyle: [...]}`; this is a single property access.\n * @param value Atom value as registered in the global table.\n * @returns Spec array when the atom is safe-area, else null.\n */\nfunction readSafeSpecs(value: unknown): readonly SafeMarkerSpec[] | null {\n if (typeof value !== 'object' || value === null) return null\n const safe = (value as { __safeStyle?: readonly SafeMarkerSpec[] }).__safeStyle\n return safe ?? null\n}\n\n/**\n * Per-atom lookup. Two property reads: scheme's own table then the\n * common fallback. Returns `undefined` for unknown atoms — the caller\n * skips them.\n * @param scheme Active scheme.\n * @param atom Atom name.\n * @returns Resolved value, or undefined.\n */\nfunction lookupAtom(scheme: string, atom: string): unknown {\n const schemeTable = cache.atoms[scheme]\n if (schemeTable !== undefined) {\n const own = schemeTable[atom]\n if (own !== undefined) return own\n }\n const common = cache.atoms[COMMON_SCHEME]\n return common === undefined ? undefined : common[atom]\n}\n\n/**\n * Whether an atom should participate in a given interact-state index.\n * - idle (0): no `active:` / `focus:` atoms.\n * - active (1): base + `active:`.\n * - focus (2): base + `focus:`.\n * - both (3): base + `active:` + `focus:`.\n * @param atom Atom name.\n * @param stateIndex Encoded state (0/1/2/3).\n * @returns True when the atom should be emitted for this state.\n */\nfunction atomMatchesState(atom: string, stateIndex: StateIndex): boolean {\n // Cheap prefix check — check the first code point before the full\n // `startsWith` so we skip it for any atom whose first letter isn't\n // `a` / `f`.\n const code = atom.codePointAt(0)\n if (code === 97 /* a */ && atom.startsWith(ACTIVE_PREFIX)) return (stateIndex & 1) !== 0\n if (code === 102 /* f */ && atom.startsWith(FOCUS_PREFIX)) return (stateIndex & 2) !== 0\n return true\n}\n\n/**\n * Whether an atom passes the responsive-breakpoint gate for the\n * current `windowWidth`. Atoms without a registered `<prefix>:` are\n * always-on (the common case — `bg-red-500`, `active:bg-blue-700`).\n * Atoms whose first prefix matches a registered breakpoint name pass\n * only when `windowWidth >= threshold`.\n * @param atom Atom name.\n * @param windowWidth Live `useWindowDimensions().width` snapshot.\n * @returns True when the atom should be emitted for this width.\n */\nfunction atomMatchesBreakpoint(atom: string, windowWidth: number): boolean {\n const colon = atom.indexOf(':')\n if (colon === -1) return true\n const prefix = atom.slice(0, colon)\n const threshold = cache.breakpoints[prefix]\n if (threshold === undefined) return true\n return windowWidth >= threshold\n}\n\n/**\n * Tier index — count of registered breakpoints whose threshold is\n * `<= windowWidth`. Bounded by the breakpoint count, so it's a stable\n * cache-key dimension instead of the unbounded raw width. Crossings\n * happen ~5 times across the device-width spectrum, not per-pixel.\n * @param windowWidth Live width.\n * @returns Tier 0..N where N = `cache.breakpointList.length`.\n */\nfunction tierFor(windowWidth: number): number {\n let tier = 0\n for (const entry of cache.breakpointList) {\n if (windowWidth >= entry.minWidth) tier += 1\n else break\n }\n return tier\n}\n\n/**\n * Public breakpoint-tier for a width — the count of registered breakpoints\n * whose threshold is reached. Used by the runtime resolver as its width\n * cache dimension: the numeric tier is bounded AND exact, unlike the\n * clamped `activeBreakpoint` NAME (which collapses tier-0 into the smallest\n * breakpoint, so widths straddling that threshold would share a cache key\n * and serve a stale style).\n * @param windowWidth Live window width in px.\n * @returns Tier 0..N.\n */\nexport function breakpointTier(windowWidth: number): number {\n return tierFor(windowWidth)\n}\n\n/**\n * Build the style array for a (hoist, scheme, state, width) tuple.\n * Walks the atom list, applies the interact-state and breakpoint\n * filters, resolves each atom via scheme→common fallback, and\n * envelopes safe values via {@link resolveSafe}.\n * @param atoms Atom name list (build-time constant).\n * @param scheme Active scheme.\n * @param stateIndex Encoded active/focus state.\n * @param insets Live safe-area insets.\n * @param fontScale Font scale multiplier.\n * @param windowWidth Live window width — gates `md:*` / `lg:*` atoms.\n * @returns Fresh style array.\n */\nfunction buildStyleArray(\n atoms: readonly string[],\n scheme: string,\n stateIndex: StateIndex,\n insets: LookupInsets | undefined,\n fontScale: number,\n windowWidth: number,\n): readonly unknown[] {\n const out: unknown[] = []\n for (const atom of atoms) {\n if (!atomMatchesState(atom, stateIndex)) continue\n if (!atomMatchesBreakpoint(atom, windowWidth)) continue\n const value = lookupAtom(scheme, atom)\n if (value === undefined) {\n warnUnknownAtomOnce(atom)\n continue\n }\n const safe = readSafeSpecs(value)\n const resolved = safe === null ? value : resolveSafe(safe, insets)\n out.push(applyFontScale(resolved, fontScale))\n }\n return out\n}\n\n/**\n * Emit a one-shot dev warning when an atom name doesn't resolve in the\n * registry. The two real causes are a typo (`bg-red-501`) or a class\n * the build-time scanner never saw because it lives in a string the\n * oxide tokeniser can't see (e.g. computed at runtime). Either way, a\n * silent empty style is the worst possible UX — surface it.\n *\n * Filters cosmetic non-issues: empty strings, build-time `__safeStyle`\n * envelopes that wandered in, etc.\n * @param atom Class name that didn't resolve.\n */\nfunction warnUnknownAtomOnce(atom: string): void {\n if (atom.length === 0) return\n const isDevelopment = typeof __DEV__ === 'undefined' || __DEV__\n if (!isDevelopment) return\n if (WARNED_UNKNOWN_ATOMS.has(atom)) return\n WARNED_UNKNOWN_ATOMS.add(atom)\n // eslint-disable-next-line no-console\n console.warn(\n `rnwind: unknown class \"${atom}\" — typo, or the class is built dynamically and the build-time ` +\n `scanner never saw it. Static literals + ternaries are scanned automatically; runtime-built ` +\n `strings need to appear somewhere as a literal so oxide can pick them up.`,\n )\n}\n\n/**\n * Per-hoist cache entry. `version` stamps `atomVersion` at build time\n * so HMR reloads (which bump the counter) invalidate cleanly on next\n * read. `hasSafe` prevents caching results whose values depend on\n * per-render insets. `byKey` maps `\"${scheme}|${stateIndex}\"` to the\n * cached result.\n */\ninterface HoistCache {\n version: number\n hasSafe: boolean\n byKey: Partial<Record<string, readonly unknown[]>>\n}\n\n/**\n * Per-atom-list cache keyed on the hoist reference. WeakMap so\n * hoists GC with their host module on HMR.\n */\nconst resultCache = new WeakMap<readonly string[], HoistCache>()\n\n/**\n * Walk the atom list once to detect safe-area atoms — results that\n * vary per render with `insets`. When any atom envelopes safe specs\n * we skip the cache and rebuild every call.\n * @param atoms Hoist atom list.\n * @param scheme Active scheme.\n * @returns Whether the hoist resolves a safe atom under this scheme.\n */\nfunction detectHasSafe(atoms: readonly string[], scheme: string): boolean {\n for (const atom of atoms) {\n const value = lookupAtom(scheme, atom)\n if (readSafeSpecs(value) !== null) return true\n }\n return false\n}\n\n/**\n * Cache-keyed resolution for the common static-schema case. Returns a\n * stable array reference across renders until `atomVersion` bumps.\n * For hoists containing safe atoms — which depend on per-render\n * insets — rebuilds every call.\n *\n * The `tier` dimension keeps the cache bounded under responsive\n * variants: instead of keying on raw `windowWidth` (which would explode\n * the cache to one entry per pixel), we key on the count of registered\n * breakpoints whose threshold is reached. That gives at most\n * `breakpointCount + 1` cache rows per (scheme, state, fontScale).\n * @param atoms Hoist atom list.\n * @param scheme Active scheme.\n * @param stateIndex Encoded interact state.\n * @param insets Live safe-area insets.\n * @param fontScale Font scale multiplier.\n * @param windowWidth Live window width.\n * @returns Style array.\n */\nfunction lookupCached(\n atoms: readonly string[],\n scheme: string,\n stateIndex: StateIndex,\n insets: LookupInsets | undefined,\n fontScale: number,\n windowWidth: number,\n): readonly unknown[] {\n let entry = resultCache.get(atoms)\n if (entry?.version !== atomVersion) {\n entry = { version: atomVersion, hasSafe: detectHasSafe(atoms, scheme), byKey: Object.create(null) }\n resultCache.set(atoms, entry)\n }\n if (entry.hasSafe) return buildStyleArray(atoms, scheme, stateIndex, insets, fontScale, windowWidth)\n const tier = tierFor(windowWidth)\n const key = `${scheme}|${stateIndex}|${fontScale}|${tier}`\n const cached = entry.byKey[key]\n if (cached !== undefined) return cached\n const fresh = buildStyleArray(atoms, scheme, stateIndex, insets, fontScale, windowWidth)\n entry.byKey[key] = fresh\n return fresh\n}\n\n/**\n * Per-render snapshot of which interactive states (active, focus) are\n * currently engaged. Forwarded from the `useInteract()` hook the\n * transformer injects.\n */\nexport interface InteractState {\n active?: boolean\n focus?: boolean\n}\n\n/**\n * Safe-area insets bundle the transformer passes to `lookupCss` when a\n * file uses any `*-safe` utility.\n */\nexport interface LookupInsets {\n top: number\n right: number\n bottom: number\n left: number\n}\n\n\n/**\n * Precomputed safe-area marker spec emitted by the build-side\n * `envelopeSafeMarkers`. Tuple form: `[cssKey, sideTag, or, offset]`.\n */\nexport type SafeMarkerSpec = readonly [string, string, number | undefined, number | undefined]\n\n/** Type alias: the atom-list build output the transformer emits. */\nexport type HoistedClassName = readonly string[]\n\n/**\n * Register a window-height provider used by the `screen-minus-y`\n * safe-area variant. When not wired, `h-screen-safe` resolves to `0`.\n * @param provider Callback returning the current window height in px.\n */\nexport function setWindowHeightProvider(provider: WindowHeightProvider | null): void {\n windowHeightProvider = provider\n}\n\n/**\n * Register the scheme-loader function exported by the generated\n * manifest module. Called once at manifest-module evaluation time —\n * subsequent registrations override the previous loader (useful for\n * tests).\n * @param loader Manifest's `ensureSchemeLoaded` function, or null to\n * detach (tests).\n */\nexport function registerSchemeLoader(loader: SchemeLoader | null): void {\n schemeLoader = loader\n}\n\n/**\n * Ensure the given scheme's style module is loaded. Safe to call in\n * render — zero-cost after the first call per scheme thanks to\n * Metro's module cache, and a no-op when no loader is registered\n * (tests, or a bundle without rnwind-transformed sources).\n * @param scheme Active scheme name.\n */\nexport function loadScheme(scheme: string): void {\n if (schemeLoader) schemeLoader(scheme)\n}\n\n/**\n * Register (or re-register) one scheme's atoms in the global registry.\n * Pure property assignment — no iteration, no allocation. The version\n * counter bump invalidates every hoist-level result cache lazily on\n * next read.\n * @param scheme Registry key — `'common'` for the always-loaded\n * fallback, or a variant name (`'dark'`, `'light'`, `'brand'`, ...).\n * @param atoms Plain record keyed by atom name.\n */\nexport function registerAtoms(scheme: string, atoms: Record<string, unknown>): void {\n cache.atoms[scheme] = atoms\n atomVersion += 1\n}\n\n/**\n * Current registry version — bumps on every `registerAtoms` /\n * `registerBreakpoints`. The molecule resolver folds it into its cache\n * key so an HMR atom reload invalidates derived results.\n * @returns Monotonic version counter.\n */\nexport function getStyleVersion(): number {\n return atomVersion\n}\n\n/**\n * Register the responsive-breakpoint table the manifest module emits at\n * load time. Replaces the prior table — calling with `{}` clears it.\n * Bumps `atomVersion` so any cached lookup invalidates on next read,\n * which matters during a theme HMR cycle that adds/removes breakpoints.\n * @param breakpoints Breakpoint name → minimum-width threshold (px).\n */\nexport function registerBreakpoints(breakpoints: Record<string, number>): void {\n const fresh: Partial<Record<string, number>> = Object.create(null)\n const list: BreakpointEntry[] = []\n for (const name of Object.keys(breakpoints)) {\n const minWidth = breakpoints[name]\n if (!Number.isFinite(minWidth) || minWidth <= 0) continue\n fresh[name] = minWidth\n list.push({ name, minWidth })\n }\n list.sort((a, b) => a.minWidth - b.minWidth || a.name.localeCompare(b.name))\n cache.breakpoints = fresh\n cache.breakpointList = list\n atomVersion += 1\n}\n\n/**\n * Snapshot of the registered breakpoints, for callers that want to\n * compute their own derivations (e.g. the provider deriving the active\n * breakpoint name). Returns a fresh array — callers can iterate without\n * worrying about concurrent mutation from a manifest reload.\n * @returns Breakpoints sorted by ascending min-width threshold.\n */\nexport function getBreakpoints(): readonly BreakpointEntry[] {\n return cache.breakpointList\n}\n\n/**\n * Register the per-scheme theme token tables the manifest module emits at\n * load time — the data source for `useColor` / `useToken` / `useSize`. The\n * build lowers `--color-*` tokens to sRGB before registering them, so these\n * are RN-safe. Replaces the prior tables; bumps `atomVersion` so a theme HMR\n * cycle re-resolves. The build registers tokens here so the hooks work out of\n * the box, without the user manually threading a `tables` prop on the provider.\n * @param tables Scheme name → (token name → value) map.\n */\nexport function registerThemeTokens(tables: ThemeTables): void {\n cache.themeTokens = tables\n atomVersion += 1\n}\n\n/**\n * The manifest-registered theme token tables. The provider merges these under\n * any explicit `tables` prop (the prop wins), so `useColor` resolves from the\n * build by default.\n * @returns Registered per-scheme token tables.\n */\nexport function getThemeTokens(): ThemeTables {\n return cache.themeTokens\n}\n\n/**\n * Sentinel name returned by {@link activeBreakpointFor} ONLY when no\n * breakpoints are registered at all (bundle without rnwind-transformed\n * sources, fresh test setup). When at least one breakpoint is\n * registered, the function falls back to the smallest registered name\n * instead — so phone-width devices (402 dp on a stock iPhone, well\n * below `sm = 640`) report `activeBreakpoint === 'sm'` rather than the\n * abstract `'base'`. This matches the user expectation that the value\n * is always a real Tailwind breakpoint label they can branch on.\n *\n * Note: this is decoupled from the className filter. `sm:*` atoms\n * still only fire at `windowWidth >= 640` per Tailwind's mobile-first\n * spec — `activeBreakpoint === 'sm'` at 402 means \"I'm in the smallest\n * tier\", not \"`sm:` classes are firing\".\n */\nexport const BASE_BREAKPOINT = 'base'\n\n/**\n * Resolve the currently-active breakpoint name for a width:\n * - `windowWidth >= some threshold` → the highest matching breakpoint name.\n * - below every registered threshold → the smallest registered name.\n * - no breakpoints registered → {@link BASE_BREAKPOINT}.\n * Always returns a string so consumers can branch on it without\n * null-handling.\n * @param windowWidth Live window width in px.\n * @returns Active breakpoint name (never null).\n */\nexport function activeBreakpointFor(windowWidth: number): string {\n const list = cache.breakpointList\n if (list.length === 0) return BASE_BREAKPOINT\n let active: string = list[0]!.name\n for (const entry of list) {\n if (windowWidth >= entry.minWidth) active = entry.name\n else break\n }\n return active\n}\n\n/**\n * Resolve a className input against the active rnwind context. Hot\n * path:\n * - Array input (build hoist): ONE WeakMap.get + ONE record-access\n * + cached array return. No per-render allocation when there's no\n * userStyle and the hoist has no safe atoms.\n * - String input (dynamic `className={expr}`): tokenise + walk.\n * @param input Hoisted atom list or raw className string.\n * @param ctx Rnwind context — `{scheme, fontScale, insets}` (extra\n * fields ignored). Pass the result of `useRnwind()` directly.\n * @param userStyle Optional caller-supplied style appended last.\n * @param interactState Live active/focus flags from `useInteract()`.\n * @returns Style array for React Native's `style` prop.\n */\nexport function lookupCss(\n input: HoistedClassName | string | null | undefined,\n ctx: RnwindState,\n userStyle?: unknown,\n interactState?: InteractState,\n): readonly unknown[] {\n if (input === null || input === undefined) {\n return userStyle === undefined || userStyle === null ? EMPTY_STYLES : [userStyle]\n }\n const { scheme, insets, fontScale, windowWidth } = ctx\n if (typeof input === 'string') {\n const trimmed = input.trim()\n if (trimmed.length === 0) {\n return userStyle === undefined || userStyle === null ? EMPTY_STYLES : [userStyle]\n }\n const atoms = trimmed.split(/\\s+/)\n const out = buildStyleArray(atoms, scheme, stateIndexFor(interactState), insets, fontScale, windowWidth)\n if (userStyle === undefined || userStyle === null) return out\n return [...out, userStyle]\n }\n const base = lookupCached(input, scheme, stateIndexFor(interactState), insets, fontScale, windowWidth)\n if (userStyle === undefined || userStyle === null) return base\n return [...base, userStyle]\n}\n\n/** Test-only — clear the global registry between suites. */\nexport function __resetLookupCssState(): void {\n for (const key of Object.keys(cache.atoms)) delete cache.atoms[key]\n for (const key of Object.keys(cache.breakpoints)) delete cache.breakpoints[key]\n cache.breakpointList = []\n cache.themeTokens = {}\n windowHeightProvider = null\n schemeLoader = null\n WARNED_MISSING_INSETS = false\n WARNED_UNKNOWN_ATOMS.clear()\n atomVersion += 1\n}\n\n/**\n * Test-only sugar: accept a single-scheme record and register it as the\n * `common` table.\n * @param record Atom name → value record (registered under `common`).\n */\nexport function __registerAtomsFromRecord(record: Record<string, unknown>): void {\n registerAtoms(COMMON_SCHEME, record)\n}\n"],"names":[],"mappings":"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"}
@@ -5,7 +5,7 @@ import { createRequire } from 'node:module';
5
5
  import { tmpdir } from 'node:os';
6
6
  import path from 'node:path';
7
7
  import * as React from 'react';
8
- import { __resetLookupCssState, registerAtoms, registerBreakpoints, registerSchemeLoader } from '../runtime/lookup-css.mjs';
8
+ import { __resetLookupCssState, registerAtoms, registerBreakpoints, registerThemeTokens, registerSchemeLoader } from '../runtime/lookup-css.mjs';
9
9
  import { __resetResolveState, registerMolecules, registerGradients, registerHaptics } from '../runtime/resolve.mjs';
10
10
  import * as runtime_index from '../runtime/index.mjs';
11
11
  import { RnwindProvider } from '../runtime/components/rnwind-provider.mjs';
@@ -108,7 +108,7 @@ function evaluateGeneratedFile(filePath) {
108
108
  // `require` is neutralised: each variant file is evaluated directly,
109
109
  // so the manifest's lazy `require('./x.style')` loaders are no-ops.
110
110
  // eslint-disable-next-line sonarjs/code-eval
111
- new Function('StyleSheet', 'registerAtoms', 'registerMolecules', 'registerBreakpoints', 'registerGradients', 'registerHaptics', 'registerSchemeLoader', 'require', body)(BUNDLE_STYLE_SHEET, registerAtoms, registerMolecules, registerBreakpoints, registerGradients, registerHaptics, registerSchemeLoader, () => { });
111
+ new Function('StyleSheet', 'registerAtoms', 'registerMolecules', 'registerBreakpoints', 'registerGradients', 'registerHaptics', 'registerThemeTokens', 'registerSchemeLoader', 'require', body)(BUNDLE_STYLE_SHEET, registerAtoms, registerMolecules, registerBreakpoints, registerGradients, registerHaptics, registerThemeTokens, registerSchemeLoader, () => { });
112
112
  }
113
113
  /**
114
114
  * Sort comparator that floats `common.style.js` to the front, rest alpha.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../../../../src/testing/index.ts"],"sourcesContent":["/* eslint-disable sonarjs/no-unused-vars */\nimport type { File as BabelFile } from '@babel/types'\nimport { parse } from '@babel/parser'\nimport generateImport from '@babel/generator'\nimport { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { tmpdir } from 'node:os'\nimport path from 'node:path'\nimport * as React from 'react'\nimport type {\n render as testingLibraryRender,\n renderHook as testingLibraryRenderHook,\n RenderHookOptions,\n} from '@testing-library/react-native'\nimport { configureRnwindState, resetRnwindState, transform as metroTransform } from '../metro'\nimport {\n __resetLookupCssState,\n registerAtoms,\n registerBreakpoints,\n registerSchemeLoader,\n} from '../runtime/lookup-css'\nimport { __resetResolveState, registerGradients, registerHaptics, registerMolecules } from '../runtime/resolve'\nimport * as rnwindRuntime from '../runtime'\nimport type { Insets } from '../runtime/components/rnwind-provider'\nimport { RnwindProvider } from '../runtime/components/rnwind-provider'\nimport type { OnHaptics } from '../core/parser/haptics'\nimport type { Scheme } from '../runtime/types'\n\n// ────────────────────────────────────────────────────────────────────────\n// Private constants\n// ────────────────────────────────────────────────────────────────────────\n\n/**\n * Minimal theme every `renderWithCss` call defaults to. Ships the two\n * conventional schemes so atoms carrying `dark:` / `light:` prefixes\n * resolve without the caller having to craft a theme CSS string.\n */\nconst DEFAULT_THEME_CSS = `@import 'tailwindcss';\n@layer theme {\n :root {\n @variant light { --color-bg: #ffffff; --color-fg: #0a0a0a; }\n @variant dark { --color-bg: #0a0a0a; --color-fg: #ffffff; }\n }\n}\n`\n\n/** `StyleSheet.create` stub the evaluated bundle calls — identity at test time. */\nconst BUNDLE_STYLE_SHEET = { create: <T>(styles: T): T => styles, hairlineWidth: 1 }\n\n// Synthesize a require rooted at the consumer's cwd so optional peer\n// lookups (`@testing-library/react-native`, `esbuild`) resolve from THEIR\n// node_modules, not rnwind's. A workspace-local rnwind install resolves\n// through a different node_modules chain than the test cwd — we want the\n// test's chain.\nconst localRequire: NodeRequire = createRequire(path.join(process.cwd(), 'package.json'))\n\nconst generate: typeof generateImport = (generateImport as { default?: typeof generateImport }).default ?? generateImport\n\n/**\n * Pre-loaded `@testing-library/react-native`. Resolved once at module\n * init (not lazily per-call) because the testing library registers\n * `beforeAll`/`afterEach` cleanup hooks on import — and Bun's test\n * runner refuses to register lifecycle hooks from inside a running\n * test body, which is where the first `renderWithCss` call lands.\n */\nconst TESTING_LIBRARY: {\n render: typeof testingLibraryRender\n renderHook: typeof testingLibraryRenderHook\n} = (() => {\n try {\n return localRequire('@testing-library/react-native') as {\n render: typeof testingLibraryRender\n renderHook: typeof testingLibraryRenderHook\n }\n } catch (error) {\n throw new Error(\n 'rnwind/testing: cannot load `@testing-library/react-native`. Add it to your dev dependencies. Underlying error: ' +\n (error instanceof Error ? error.message : String(error)),\n )\n }\n})()\n\n// ────────────────────────────────────────────────────────────────────────\n// Private helpers\n// ────────────────────────────────────────────────────────────────────────\n\n/**\n * Scheme string typed to match what `<RnwindProvider>` accepts. Users\n * who declare their own scheme names via `rnwind-types.d.ts` get them\n * through `Scheme`; everyone else gets `'light' | 'dark' | string`.\n */\ntype SchemeName = Scheme\n\n/**\n * Default lookup for the test process's `react-native` module. Users\n * who run with a mocked `react-native` (via Bun's `mock.module` or\n * Jest's `jest.mock`) get the mock here automatically.\n * @returns React Native namespace bindings.\n */\nfunction resolveReactNative(): Record<string, unknown> {\n return localRequire('react-native') as Record<string, unknown>\n}\n\n/**\n * Compile JSX + TypeScript source to plain JS. Prefers Bun's built-in\n * transpiler (fast, zero-dep); falls back to `esbuild` if running under\n * Node (Jest users). Throws with an install hint if neither is available.\n * @param source Source text to compile.\n * @returns JavaScript suitable for `new Function(...)` evaluation.\n */\nfunction compileToJs(source: string): string {\n const globalBun = (\n globalThis as { Bun?: { Transpiler: new (options: unknown) => { transformSync: (text: string) => string } } }\n ).Bun\n if (globalBun?.Transpiler) {\n return new globalBun.Transpiler({\n loader: 'tsx',\n tsconfig: JSON.stringify({ compilerOptions: { jsx: 'react', target: 'esnext' } }),\n }).transformSync(source)\n }\n try {\n const esbuild = localRequire('esbuild') as {\n transformSync: (text: string, options: { loader: string; jsx: string; target?: string }) => { code: string }\n }\n return esbuild.transformSync(source, { loader: 'tsx', jsx: 'transform', target: 'esnext' }).code\n } catch {\n throw new Error(\n 'rnwind/testing: cannot compile JSX. Run tests under Bun (which has a built-in transpiler) ' +\n 'or install `esbuild` as a dev dependency for Node / Jest setups.',\n )\n }\n}\n\n/**\n * Evaluate one generated registry file (`common.style.js`,\n * `<variant>.style.js`, or `schemes.js`) so its `registerAtoms` /\n * `registerBreakpoints` / `registerGradients` / `registerHaptics` /\n * `registerSchemeLoader` calls land in the process-global registries the\n * runtime resolver reads. Imports (`'rnwind'`, `'react-native'`, relative\n * `./common.style`), `require(...)` loaders, and `export {...}` are\n * stripped — every scheme file is evaluated directly, so lazy loaders are\n * inert.\n * @param filePath Absolute path to the generated file.\n */\nfunction evaluateGeneratedFile(filePath: string): void {\n if (!existsSync(filePath)) return\n const body = readFileSync(filePath, 'utf8')\n .replaceAll(/import\\s+\\{[^}]*\\}\\s+from\\s+['\"][^'\"]+['\"];?\\s*\\n?/g, '')\n .replaceAll(/import\\s+['\"][^'\"]+['\"];?\\s*\\n?/g, '')\n .replaceAll(/export\\s+\\{[^}]*\\}\\s*;?\\s*\\n?/g, '')\n // Generated body is produced by rnwind itself — not user-controlled.\n // `require` is neutralised: each variant file is evaluated directly,\n // so the manifest's lazy `require('./x.style')` loaders are no-ops.\n // eslint-disable-next-line sonarjs/code-eval\n new Function(\n 'StyleSheet',\n 'registerAtoms',\n 'registerMolecules',\n 'registerBreakpoints',\n 'registerGradients',\n 'registerHaptics',\n 'registerSchemeLoader',\n 'require',\n body,\n )(\n BUNDLE_STYLE_SHEET,\n registerAtoms,\n registerMolecules,\n registerBreakpoints,\n registerGradients,\n registerHaptics,\n registerSchemeLoader,\n () => {},\n )\n}\n\n/**\n * Sort comparator that floats `common.style.js` to the front, rest alpha.\n * @param a\n * @param b\n */\nfunction commonFirst(a: string, b: string): number {\n if (a === 'common.style.js') return -1\n if (b === 'common.style.js') return 1\n return a.localeCompare(b)\n}\n\n/**\n * Evaluate every generated registry the transform wrote (`common` +\n * every variant scheme + the manifest) so the runtime resolver sees the\n * full atom / molecule / gradient / haptic registries — same state a\n * production bundle would hold once all scheme files load.\n * @param cacheDir The rnwind cache dir holding the generated files.\n */\nfunction evaluateGeneratedRegistries(cacheDir: string): void {\n if (!existsSync(cacheDir)) return\n const files = readdirSync(cacheDir).filter((name) => name.endsWith('.style.js'))\n // common first so variants layer their diffs on top of it.\n for (const name of files.toSorted(commonFirst)) {\n evaluateGeneratedFile(path.join(cacheDir, name))\n }\n evaluateGeneratedFile(path.join(cacheDir, 'schemes.js'))\n}\n\n/**\n * Move every `const {…} = __rnwind;` / `const {…} = __reactNative;`\n * binding line to the top of the source, preserving order, so they\n * initialise before the transformer's `const View = _rnwWrap(_rnw0)`\n * wrap declarations reference them. Mirrors ESM import hoisting.\n * @param source Source with imports already converted to const-destructures.\n * @returns Source with binding consts hoisted to the front.\n */\nfunction hoistBindingConsts(source: string): string {\n const hoisted: string[] = []\n const rest: string[] = []\n for (const line of source.split('\\n')) {\n if (/=\\s*__(?:rnwind|reactNative);\\s*$/.test(line)) hoisted.push(line)\n else rest.push(line)\n }\n return [...hoisted, ...rest].join('\\n')\n}\n\n/**\n * Evaluate the transformer's rewritten source as a standalone module:\n * strip synthetic generated imports, forward `import ... from 'rnwind'`\n * (incl. the injected `wrap as _rnwWrap`) and `'react-native'` to local\n * bindings, compile JSX via the compiler, and capture the default export.\n * Aliased named imports (`{ View as _rnw0 }`) are converted to valid\n * destructuring (`{ View: _rnw0 }`). The import-derived `const`s are then\n * hoisted above the body: the transformer injects `const View =\n * _rnwWrap(_rnw0)` ahead of the (real-ESM-hoisted) `import … as _rnw0`,\n * so without re-hoisting the binding the eval would hit a TDZ on `_rnw0`.\n * @param transformedSource Post-transformer source.\n * @param reactNative `react-native` namespace bindings to forward.\n * @returns The default-exported component.\n */\nfunction evaluateRewrittenModule(\n transformedSource: string,\n reactNative: Record<string, unknown>,\n): React.ComponentType<Record<string, unknown>> {\n const prepared = transformedSource\n .replaceAll(/import\\s+[\"']rnwind\\/__generated\\/[^\"']+[\"'];?\\s*\\n?/g, '')\n .replaceAll(/import\\s+\\{([^}]+)\\}\\s+from\\s+[\"']rnwind[\"'];?/g, (_m, spec: string) => `const {${spec.replaceAll(/\\bas\\b/g, ':')}} = __rnwind;`)\n .replaceAll(/import\\s+\\{([^}]+)\\}\\s+from\\s+[\"']react-native[\"'];?/g, (_m, spec: string) => `const {${spec.replaceAll(/\\bas\\b/g, ':')}} = __reactNative;`)\n .replace(/export\\s+default\\s+/, 'module.exports.default = ')\n\n const compiled = compileToJs(hoistBindingConsts(prepared))\n const moduleObject: { exports: { default?: React.ComponentType<Record<string, unknown>> } } = { exports: {} }\n // Compiled source originates from rnwind's own transformer + the JSX compiler.\n // eslint-disable-next-line sonarjs/code-eval\n new Function('React', '__rnwind', '__reactNative', 'module', compiled)(React, rnwindRuntime, reactNative, moduleObject)\n if (!moduleObject.exports.default) {\n throw new Error('rnwind/testing: evaluated module did not export a default component.')\n }\n return moduleObject.exports.default\n}\n\n/**\n * Build the root element `@testing-library/react-native`'s `render`\n * receives. When a scheme is specified, wrap the component in a live\n * `<RnwindProvider>` so hooks like `useScheme()` and `useCss()` return\n * the requested scheme. Otherwise render bare (context falls back to the\n * default `'light'`).\n * @param Component Component to render.\n * @param scheme Optional active scheme override.\n * @param insets\n * @param onHaptics\n * @returns The React element to hand to `render`.\n */\nfunction buildRootElement(\n Component: React.ComponentType<Record<string, unknown>>,\n scheme: string | undefined,\n insets: Partial<Insets> | undefined,\n onHaptics: OnHaptics | undefined,\n): React.ReactElement {\n const child = React.createElement(Component)\n if (scheme === undefined && !insets && !onHaptics) return child\n const providerProps: { scheme: SchemeName; insets?: Partial<Insets>; onHaptics?: OnHaptics } = {\n scheme: (scheme ?? 'light') as SchemeName,\n }\n if (insets) providerProps.insets = insets\n if (onHaptics) providerProps.onHaptics = onHaptics\n return React.createElement(RnwindProvider, providerProps, child)\n}\n\n/**\n * Compose the `wrapper` option `renderHook` accepts. When a scheme is\n * set, wrap the hook's execution context in `<RnwindProvider>` so hooks\n * that read `useScheme()` see the requested scheme. If the user also\n * supplies their own wrapper, nest it inside the provider so both apply.\n * @param scheme Optional active scheme override.\n * @param userWrapper Optional user-supplied wrapper component.\n * @returns A wrapper component suitable for `renderHook`'s `wrapper` option.\n */\nfunction composeHookWrapper(\n scheme: string | undefined,\n userWrapper: React.ComponentType<{ children?: React.ReactNode }> | undefined,\n): React.ComponentType<{ children?: React.ReactNode }> | undefined {\n if (scheme === undefined && !userWrapper) return undefined\n return function RnwindTestWrapper({ children }: { children?: React.ReactNode }): React.ReactElement {\n const inner = userWrapper ? React.createElement(userWrapper, null, children) : (children as React.ReactElement)\n if (scheme === undefined) return inner\n return React.createElement(RnwindProvider, { scheme: scheme as SchemeName }, inner)\n }\n}\n\n/**\n * Spin up an ephemeral rnwind project and register the atoms produced\n * by `source` (when provided) into the runtime. Returns the project\n * state and the post-transform source so callers can render the\n * rewritten module or just rely on the registered atoms.\n *\n * Shared between `renderWithCss` (needs the rewritten component) and\n * `renderHookWithCss` (only needs the atoms in the registry).\n * @param options Options controlling the theme and pre-registered source.\n * @param options.themeCss Theme CSS override.\n * @param options.source Source whose Tailwind candidates should be registered.\n * @returns Paths plus the transformed source and a `cleanup` closure.\n */\nasync function bootstrapRnwindRuntime(options: { themeCss?: string; source?: string }): Promise<{\n projectRoot: string\n cacheDir: string\n transformedSource: string\n cleanup: () => void\n}> {\n const projectRoot = mkdtempSync(path.join(tmpdir(), 'rnwind-test-'))\n const cacheDir = path.join(projectRoot, '.rnwind-cache')\n const cssPath = path.join(projectRoot, 'theme.css')\n writeFileSync(cssPath, options.themeCss ?? DEFAULT_THEME_CSS)\n configureRnwindState(cssPath, cacheDir)\n\n let transformedSource = ''\n if (options.source !== undefined) {\n const filename = path.join(projectRoot, 'App.tsx')\n writeFileSync(filename, options.source)\n const ast: BabelFile = parse(options.source, { sourceType: 'module', plugins: ['typescript', 'jsx'] })\n const result = await metroTransform({ filename, src: options.source, options: { projectRoot }, ast })\n transformedSource = generate(result.ast).code\n evaluateGeneratedRegistries(cacheDir)\n }\n\n const cleanup = (): void => {\n __resetLookupCssState()\n __resetResolveState()\n resetRnwindState()\n if (existsSync(projectRoot)) rmSync(projectRoot, { recursive: true, force: true })\n }\n\n return { projectRoot, cacheDir, transformedSource, cleanup }\n}\n\n/**\n * Synthesize a throwaway source file that mentions each className in a\n * JSX `className=\"...\"` attribute so the Metro transformer's scanner\n * picks them up and records their resolved styles into the union\n * `style.js`. Used by `renderHookWithCss` to pre-register atoms the\n * hook under test will look up.\n * @param classNames Class names to include.\n * @returns Source string suitable for `bootstrapRnwindRuntime`.\n */\nfunction sourceFromClassNames(classNames: readonly string[]): string {\n const joined = classNames.join(' ').replaceAll('\"', String.raw`\\\"`)\n return `const V: any = () => null\\nexport default () => <V className=\"${joined}\" />\\n`\n}\n\n// ────────────────────────────────────────────────────────────────────────\n// Public exports — types\n// ────────────────────────────────────────────────────────────────────────\n\n/** Exact signature of `@testing-library/react-native`'s `render`. */\nexport type Render = typeof testingLibraryRender\n\n/** Exact signature of `@testing-library/react-native`'s `renderHook`. */\nexport type RenderHook = typeof testingLibraryRenderHook\n\n/**\n * Exact return type of `@testing-library/react-native`'s `render` — we\n * re-export the upstream type so tests get every query (`getByTestId`,\n * `queryByText`, `rerender`, `unmount`, `debug`, `UNSAFE_root`, …) with\n * its real signature.\n */\nexport type TestingLibraryRenderAPI = ReturnType<typeof testingLibraryRender>\n\n/** Options accepted by {@link renderWithCss}. */\nexport interface RenderWithCssOptions {\n /** Theme CSS fed to rnwind's parser. Defaults to {@link DEFAULT_THEME_CSS}. */\n themeCss?: string\n /**\n * Active scheme the runtime resolver uses. Defaults to `'light'`.\n * Flip to `'dark'` / `'brand'` / ... to exercise scheme-variant atoms.\n */\n scheme?: string\n /**\n * Safe-area insets to inject. When provided, the component tree is\n * rendered under a `<RnwindProvider insets={...}>` so `*-safe` atoms\n * resolve to real numbers instead of the `0` fallback. Partial is\n * accepted — missing sides default to zero.\n */\n insets?: Partial<Insets>\n /**\n * Haptic dispatcher forwarded onto the implicit `<RnwindProvider>`.\n * Tests pass a spy callback to assert which haptic atoms fired.\n */\n onHaptics?: OnHaptics\n /**\n * `react-native` namespace bindings forwarded into the rewritten\n * module. Defaults to importing the test process's `react-native`\n * (typically a mocked stub). Override to inject custom host elements.\n */\n reactNative?: Record<string, unknown>\n}\n\n/**\n * Result of a {@link renderWithCss} call. Every key from\n * `@testing-library/react-native`'s `render` return value is spread onto\n * this object — `getByTestId`, `queryByText`, `rerender`, `unmount`,\n * `debug`, etc. — so tests use the testing-library API directly.\n *\n * Two rnwind-specific handles are added:\n * - `transformedSource` — the exact code the rnwind Metro transformer\n * emitted for your input. Log it to confirm the rewrite.\n * - `cleanup` — tears down the ephemeral rnwind state + chunk cache dir.\n * Call from `afterEach` so successive tests don't share atoms.\n */\nexport type RenderWithCssResult = TestingLibraryRenderAPI & {\n /** The post-transformer source text. Same code Metro would ship. */\n transformedSource: string\n /** Tear down the ephemeral rnwind state + cache dir. */\n cleanup: () => void\n}\n\n/** Options accepted by {@link renderHookWithCss}. */\nexport interface RenderHookWithCssOptions<Props> extends RenderHookOptions<Props> {\n /** Theme CSS fed to rnwind's parser. Defaults to the built-in minimal theme. */\n themeCss?: string\n /**\n * Active scheme the runtime resolver uses. Defaults to `'light'`. When\n * set, the hook is wrapped in `<RnwindProvider scheme={scheme}>` so\n * `useScheme()` / `useCss()` observe the override.\n */\n scheme?: string\n /**\n * Class names to pre-register into the runtime atom registry before\n * the hook runs. rnwind synthesizes a throwaway source file mentioning\n * these classes, feeds it through the real Metro transformer, and\n * evaluates the generated `style.js` bundle — so the hook sees\n * exactly the atoms the production bundle would register.\n */\n classNames?: readonly string[]\n}\n\n/**\n * Result of a {@link renderHookWithCss} call. Mirrors\n * `@testing-library/react-native`'s `renderHook` return type — `result`,\n * `rerender`, `unmount` — and adds a `cleanup` that tears down the\n * ephemeral rnwind state.\n */\nexport type RenderHookWithCssResult<Result, Props> = ReturnType<typeof testingLibraryRenderHook<Result, Props>> & {\n /** Tear down the ephemeral rnwind state + cache dir. */\n cleanup: () => void\n}\n\n// ────────────────────────────────────────────────────────────────────────\n// Public exports — functions\n// ────────────────────────────────────────────────────────────────────────\n\n/**\n * Feed a source string through rnwind's Metro transformer, evaluate\n * the generated `style.js` bundle, evaluate the rewritten module, and\n * render the default-exported component with\n * `@testing-library/react-native`.\n *\n * The rendered RN element's `style` prop is the EXACT array your\n * production bundle would attach — same transformer, same runtime\n * resolver, same atoms. Assert on `flatten(node.props.style)` to verify\n * the resolved values.\n * @example\n * ```tsx\n * const handle = await renderWithCss(\n * `import { View } from 'react-native'\n * export default () => <View className=\"bg-primary p-4\" testID=\"box\" />`,\n * { themeCss: `@import 'tailwindcss'; @theme { --color-primary: #6366f1; }` },\n * )\n * const flat = flatten(handle.getByTestId('box').props.style)\n * expect(flat.backgroundColor).toBe('#6366f1')\n * ```\n * @param source User source (one file) to transform + render.\n * @param options Optional theme override, scheme, and `react-native` bindings.\n * @returns Render queries + diagnostic handles.\n */\nexport async function renderWithCss(source: string, options: RenderWithCssOptions = {}): Promise<RenderWithCssResult> {\n const reactNative = options.reactNative ?? resolveReactNative()\n const {\n projectRoot: _projectRoot,\n transformedSource,\n cleanup,\n } = await bootstrapRnwindRuntime({\n themeCss: options.themeCss,\n source,\n })\n const Component = evaluateRewrittenModule(transformedSource, reactNative)\n const rendered = TESTING_LIBRARY.render(buildRootElement(Component, options.scheme, options.insets, options.onHaptics))\n return Object.assign({}, rendered, { transformedSource, cleanup })\n}\n\n/**\n * Render-side counterpart to {@link renderWithCss} for testing hooks in\n * isolation. Pre-registers atoms for the supplied `classNames`, wraps\n * the hook in an optional `<RnwindProvider>`, and forwards to\n * `@testing-library/react-native`'s `renderHook`.\n * @example\n * ```ts\n * const { result, cleanup } = await renderHookWithCss(\n * () => useCss('bg-primary'),\n * {\n * themeCss: `@import 'tailwindcss'; @theme { --color-primary: #6366f1; }`,\n * classNames: ['bg-primary'],\n * },\n * )\n * expect(flatten(result.current).backgroundColor).toBe('#6366f1')\n * cleanup()\n * ```\n * @param callback Hook body — same shape as `renderHook(callback)`.\n * @param options Theme, scheme, classNames to pre-register, plus\n * everything `renderHook` itself accepts (`initialProps`, `wrapper`).\n * @returns `renderHook`'s return value plus a `cleanup` function.\n */\nexport async function renderHookWithCss<Result, Props = unknown>(\n callback: (props: Props) => Result,\n options: RenderHookWithCssOptions<Props> = {},\n): Promise<RenderHookWithCssResult<Result, Props>> {\n const { cleanup } = await bootstrapRnwindRuntime({\n themeCss: options.themeCss,\n source: options.classNames && options.classNames.length > 0 ? sourceFromClassNames(options.classNames) : undefined,\n })\n const wrapper = composeHookWrapper(options.scheme, options.wrapper as RenderHookWithCssOptions<Props>['wrapper'])\n const { themeCss: _themeCss, scheme: _scheme, classNames: _classNames, ...hookOptions } = options\n const rendered = TESTING_LIBRARY.renderHook<Result, Props>(callback, {\n ...(hookOptions as RenderHookOptions<Props>),\n wrapper,\n })\n return Object.assign({}, rendered, { cleanup })\n}\n\n/**\n * Flatten a React Native style array (or single style object) into one\n * merged record. RN flattens left-to-right (later wins), so the returned\n * record is what the native view manager actually applies.\n * @param styles Style array, single object, or null/undefined.\n * @returns Flat style record.\n */\nexport function flatten(styles: unknown): Record<string, unknown> {\n if (styles == null) return {}\n if (Array.isArray(styles)) return Object.assign({}, ...styles.map((entry) => flatten(entry)))\n if (typeof styles === 'object') return styles as Record<string, unknown>\n return {}\n}\n"],"names":["generateImport","rnwindRuntime","metroTransform"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","sources":["../../../../src/testing/index.ts"],"sourcesContent":["/* eslint-disable sonarjs/no-unused-vars */\nimport type { File as BabelFile } from '@babel/types'\nimport { parse } from '@babel/parser'\nimport generateImport from '@babel/generator'\nimport { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { tmpdir } from 'node:os'\nimport path from 'node:path'\nimport * as React from 'react'\nimport type {\n render as testingLibraryRender,\n renderHook as testingLibraryRenderHook,\n RenderHookOptions,\n} from '@testing-library/react-native'\nimport { configureRnwindState, resetRnwindState, transform as metroTransform } from '../metro'\nimport {\n __resetLookupCssState,\n registerAtoms,\n registerBreakpoints,\n registerThemeTokens,\n registerSchemeLoader,\n} from '../runtime/lookup-css'\nimport { __resetResolveState, registerGradients, registerHaptics, registerMolecules } from '../runtime/resolve'\nimport * as rnwindRuntime from '../runtime'\nimport type { Insets } from '../runtime/components/rnwind-provider'\nimport { RnwindProvider } from '../runtime/components/rnwind-provider'\nimport type { OnHaptics } from '../core/parser/haptics'\nimport type { Scheme } from '../runtime/types'\n\n// ────────────────────────────────────────────────────────────────────────\n// Private constants\n// ────────────────────────────────────────────────────────────────────────\n\n/**\n * Minimal theme every `renderWithCss` call defaults to. Ships the two\n * conventional schemes so atoms carrying `dark:` / `light:` prefixes\n * resolve without the caller having to craft a theme CSS string.\n */\nconst DEFAULT_THEME_CSS = `@import 'tailwindcss';\n@layer theme {\n :root {\n @variant light { --color-bg: #ffffff; --color-fg: #0a0a0a; }\n @variant dark { --color-bg: #0a0a0a; --color-fg: #ffffff; }\n }\n}\n`\n\n/** `StyleSheet.create` stub the evaluated bundle calls — identity at test time. */\nconst BUNDLE_STYLE_SHEET = { create: <T>(styles: T): T => styles, hairlineWidth: 1 }\n\n// Synthesize a require rooted at the consumer's cwd so optional peer\n// lookups (`@testing-library/react-native`, `esbuild`) resolve from THEIR\n// node_modules, not rnwind's. A workspace-local rnwind install resolves\n// through a different node_modules chain than the test cwd — we want the\n// test's chain.\nconst localRequire: NodeRequire = createRequire(path.join(process.cwd(), 'package.json'))\n\nconst generate: typeof generateImport = (generateImport as { default?: typeof generateImport }).default ?? generateImport\n\n/**\n * Pre-loaded `@testing-library/react-native`. Resolved once at module\n * init (not lazily per-call) because the testing library registers\n * `beforeAll`/`afterEach` cleanup hooks on import — and Bun's test\n * runner refuses to register lifecycle hooks from inside a running\n * test body, which is where the first `renderWithCss` call lands.\n */\nconst TESTING_LIBRARY: {\n render: typeof testingLibraryRender\n renderHook: typeof testingLibraryRenderHook\n} = (() => {\n try {\n return localRequire('@testing-library/react-native') as {\n render: typeof testingLibraryRender\n renderHook: typeof testingLibraryRenderHook\n }\n } catch (error) {\n throw new Error(\n 'rnwind/testing: cannot load `@testing-library/react-native`. Add it to your dev dependencies. Underlying error: ' +\n (error instanceof Error ? error.message : String(error)),\n )\n }\n})()\n\n// ────────────────────────────────────────────────────────────────────────\n// Private helpers\n// ────────────────────────────────────────────────────────────────────────\n\n/**\n * Scheme string typed to match what `<RnwindProvider>` accepts. Users\n * who declare their own scheme names via `rnwind-types.d.ts` get them\n * through `Scheme`; everyone else gets `'light' | 'dark' | string`.\n */\ntype SchemeName = Scheme\n\n/**\n * Default lookup for the test process's `react-native` module. Users\n * who run with a mocked `react-native` (via Bun's `mock.module` or\n * Jest's `jest.mock`) get the mock here automatically.\n * @returns React Native namespace bindings.\n */\nfunction resolveReactNative(): Record<string, unknown> {\n return localRequire('react-native') as Record<string, unknown>\n}\n\n/**\n * Compile JSX + TypeScript source to plain JS. Prefers Bun's built-in\n * transpiler (fast, zero-dep); falls back to `esbuild` if running under\n * Node (Jest users). Throws with an install hint if neither is available.\n * @param source Source text to compile.\n * @returns JavaScript suitable for `new Function(...)` evaluation.\n */\nfunction compileToJs(source: string): string {\n const globalBun = (\n globalThis as { Bun?: { Transpiler: new (options: unknown) => { transformSync: (text: string) => string } } }\n ).Bun\n if (globalBun?.Transpiler) {\n return new globalBun.Transpiler({\n loader: 'tsx',\n tsconfig: JSON.stringify({ compilerOptions: { jsx: 'react', target: 'esnext' } }),\n }).transformSync(source)\n }\n try {\n const esbuild = localRequire('esbuild') as {\n transformSync: (text: string, options: { loader: string; jsx: string; target?: string }) => { code: string }\n }\n return esbuild.transformSync(source, { loader: 'tsx', jsx: 'transform', target: 'esnext' }).code\n } catch {\n throw new Error(\n 'rnwind/testing: cannot compile JSX. Run tests under Bun (which has a built-in transpiler) ' +\n 'or install `esbuild` as a dev dependency for Node / Jest setups.',\n )\n }\n}\n\n/**\n * Evaluate one generated registry file (`common.style.js`,\n * `<variant>.style.js`, or `schemes.js`) so its `registerAtoms` /\n * `registerBreakpoints` / `registerGradients` / `registerHaptics` /\n * `registerSchemeLoader` calls land in the process-global registries the\n * runtime resolver reads. Imports (`'rnwind'`, `'react-native'`, relative\n * `./common.style`), `require(...)` loaders, and `export {...}` are\n * stripped — every scheme file is evaluated directly, so lazy loaders are\n * inert.\n * @param filePath Absolute path to the generated file.\n */\nfunction evaluateGeneratedFile(filePath: string): void {\n if (!existsSync(filePath)) return\n const body = readFileSync(filePath, 'utf8')\n .replaceAll(/import\\s+\\{[^}]*\\}\\s+from\\s+['\"][^'\"]+['\"];?\\s*\\n?/g, '')\n .replaceAll(/import\\s+['\"][^'\"]+['\"];?\\s*\\n?/g, '')\n .replaceAll(/export\\s+\\{[^}]*\\}\\s*;?\\s*\\n?/g, '')\n // Generated body is produced by rnwind itself — not user-controlled.\n // `require` is neutralised: each variant file is evaluated directly,\n // so the manifest's lazy `require('./x.style')` loaders are no-ops.\n // eslint-disable-next-line sonarjs/code-eval\n new Function(\n 'StyleSheet',\n 'registerAtoms',\n 'registerMolecules',\n 'registerBreakpoints',\n 'registerGradients',\n 'registerHaptics',\n 'registerThemeTokens',\n 'registerSchemeLoader',\n 'require',\n body,\n )(\n BUNDLE_STYLE_SHEET,\n registerAtoms,\n registerMolecules,\n registerBreakpoints,\n registerGradients,\n registerHaptics,\n registerThemeTokens,\n registerSchemeLoader,\n () => {},\n )\n}\n\n/**\n * Sort comparator that floats `common.style.js` to the front, rest alpha.\n * @param a\n * @param b\n */\nfunction commonFirst(a: string, b: string): number {\n if (a === 'common.style.js') return -1\n if (b === 'common.style.js') return 1\n return a.localeCompare(b)\n}\n\n/**\n * Evaluate every generated registry the transform wrote (`common` +\n * every variant scheme + the manifest) so the runtime resolver sees the\n * full atom / molecule / gradient / haptic registries — same state a\n * production bundle would hold once all scheme files load.\n * @param cacheDir The rnwind cache dir holding the generated files.\n */\nfunction evaluateGeneratedRegistries(cacheDir: string): void {\n if (!existsSync(cacheDir)) return\n const files = readdirSync(cacheDir).filter((name) => name.endsWith('.style.js'))\n // common first so variants layer their diffs on top of it.\n for (const name of files.toSorted(commonFirst)) {\n evaluateGeneratedFile(path.join(cacheDir, name))\n }\n evaluateGeneratedFile(path.join(cacheDir, 'schemes.js'))\n}\n\n/**\n * Move every `const {…} = __rnwind;` / `const {…} = __reactNative;`\n * binding line to the top of the source, preserving order, so they\n * initialise before the transformer's `const View = _rnwWrap(_rnw0)`\n * wrap declarations reference them. Mirrors ESM import hoisting.\n * @param source Source with imports already converted to const-destructures.\n * @returns Source with binding consts hoisted to the front.\n */\nfunction hoistBindingConsts(source: string): string {\n const hoisted: string[] = []\n const rest: string[] = []\n for (const line of source.split('\\n')) {\n if (/=\\s*__(?:rnwind|reactNative);\\s*$/.test(line)) hoisted.push(line)\n else rest.push(line)\n }\n return [...hoisted, ...rest].join('\\n')\n}\n\n/**\n * Evaluate the transformer's rewritten source as a standalone module:\n * strip synthetic generated imports, forward `import ... from 'rnwind'`\n * (incl. the injected `wrap as _rnwWrap`) and `'react-native'` to local\n * bindings, compile JSX via the compiler, and capture the default export.\n * Aliased named imports (`{ View as _rnw0 }`) are converted to valid\n * destructuring (`{ View: _rnw0 }`). The import-derived `const`s are then\n * hoisted above the body: the transformer injects `const View =\n * _rnwWrap(_rnw0)` ahead of the (real-ESM-hoisted) `import … as _rnw0`,\n * so without re-hoisting the binding the eval would hit a TDZ on `_rnw0`.\n * @param transformedSource Post-transformer source.\n * @param reactNative `react-native` namespace bindings to forward.\n * @returns The default-exported component.\n */\nfunction evaluateRewrittenModule(\n transformedSource: string,\n reactNative: Record<string, unknown>,\n): React.ComponentType<Record<string, unknown>> {\n const prepared = transformedSource\n .replaceAll(/import\\s+[\"']rnwind\\/__generated\\/[^\"']+[\"'];?\\s*\\n?/g, '')\n .replaceAll(/import\\s+\\{([^}]+)\\}\\s+from\\s+[\"']rnwind[\"'];?/g, (_m, spec: string) => `const {${spec.replaceAll(/\\bas\\b/g, ':')}} = __rnwind;`)\n .replaceAll(/import\\s+\\{([^}]+)\\}\\s+from\\s+[\"']react-native[\"'];?/g, (_m, spec: string) => `const {${spec.replaceAll(/\\bas\\b/g, ':')}} = __reactNative;`)\n .replace(/export\\s+default\\s+/, 'module.exports.default = ')\n\n const compiled = compileToJs(hoistBindingConsts(prepared))\n const moduleObject: { exports: { default?: React.ComponentType<Record<string, unknown>> } } = { exports: {} }\n // Compiled source originates from rnwind's own transformer + the JSX compiler.\n // eslint-disable-next-line sonarjs/code-eval\n new Function('React', '__rnwind', '__reactNative', 'module', compiled)(React, rnwindRuntime, reactNative, moduleObject)\n if (!moduleObject.exports.default) {\n throw new Error('rnwind/testing: evaluated module did not export a default component.')\n }\n return moduleObject.exports.default\n}\n\n/**\n * Build the root element `@testing-library/react-native`'s `render`\n * receives. When a scheme is specified, wrap the component in a live\n * `<RnwindProvider>` so hooks like `useScheme()` and `useCss()` return\n * the requested scheme. Otherwise render bare (context falls back to the\n * default `'light'`).\n * @param Component Component to render.\n * @param scheme Optional active scheme override.\n * @param insets\n * @param onHaptics\n * @returns The React element to hand to `render`.\n */\nfunction buildRootElement(\n Component: React.ComponentType<Record<string, unknown>>,\n scheme: string | undefined,\n insets: Partial<Insets> | undefined,\n onHaptics: OnHaptics | undefined,\n): React.ReactElement {\n const child = React.createElement(Component)\n if (scheme === undefined && !insets && !onHaptics) return child\n const providerProps: { scheme: SchemeName; insets?: Partial<Insets>; onHaptics?: OnHaptics } = {\n scheme: (scheme ?? 'light') as SchemeName,\n }\n if (insets) providerProps.insets = insets\n if (onHaptics) providerProps.onHaptics = onHaptics\n return React.createElement(RnwindProvider, providerProps, child)\n}\n\n/**\n * Compose the `wrapper` option `renderHook` accepts. When a scheme is\n * set, wrap the hook's execution context in `<RnwindProvider>` so hooks\n * that read `useScheme()` see the requested scheme. If the user also\n * supplies their own wrapper, nest it inside the provider so both apply.\n * @param scheme Optional active scheme override.\n * @param userWrapper Optional user-supplied wrapper component.\n * @returns A wrapper component suitable for `renderHook`'s `wrapper` option.\n */\nfunction composeHookWrapper(\n scheme: string | undefined,\n userWrapper: React.ComponentType<{ children?: React.ReactNode }> | undefined,\n): React.ComponentType<{ children?: React.ReactNode }> | undefined {\n if (scheme === undefined && !userWrapper) return undefined\n return function RnwindTestWrapper({ children }: { children?: React.ReactNode }): React.ReactElement {\n const inner = userWrapper ? React.createElement(userWrapper, null, children) : (children as React.ReactElement)\n if (scheme === undefined) return inner\n return React.createElement(RnwindProvider, { scheme: scheme as SchemeName }, inner)\n }\n}\n\n/**\n * Spin up an ephemeral rnwind project and register the atoms produced\n * by `source` (when provided) into the runtime. Returns the project\n * state and the post-transform source so callers can render the\n * rewritten module or just rely on the registered atoms.\n *\n * Shared between `renderWithCss` (needs the rewritten component) and\n * `renderHookWithCss` (only needs the atoms in the registry).\n * @param options Options controlling the theme and pre-registered source.\n * @param options.themeCss Theme CSS override.\n * @param options.source Source whose Tailwind candidates should be registered.\n * @returns Paths plus the transformed source and a `cleanup` closure.\n */\nasync function bootstrapRnwindRuntime(options: { themeCss?: string; source?: string }): Promise<{\n projectRoot: string\n cacheDir: string\n transformedSource: string\n cleanup: () => void\n}> {\n const projectRoot = mkdtempSync(path.join(tmpdir(), 'rnwind-test-'))\n const cacheDir = path.join(projectRoot, '.rnwind-cache')\n const cssPath = path.join(projectRoot, 'theme.css')\n writeFileSync(cssPath, options.themeCss ?? DEFAULT_THEME_CSS)\n configureRnwindState(cssPath, cacheDir)\n\n let transformedSource = ''\n if (options.source !== undefined) {\n const filename = path.join(projectRoot, 'App.tsx')\n writeFileSync(filename, options.source)\n const ast: BabelFile = parse(options.source, { sourceType: 'module', plugins: ['typescript', 'jsx'] })\n const result = await metroTransform({ filename, src: options.source, options: { projectRoot }, ast })\n transformedSource = generate(result.ast).code\n evaluateGeneratedRegistries(cacheDir)\n }\n\n const cleanup = (): void => {\n __resetLookupCssState()\n __resetResolveState()\n resetRnwindState()\n if (existsSync(projectRoot)) rmSync(projectRoot, { recursive: true, force: true })\n }\n\n return { projectRoot, cacheDir, transformedSource, cleanup }\n}\n\n/**\n * Synthesize a throwaway source file that mentions each className in a\n * JSX `className=\"...\"` attribute so the Metro transformer's scanner\n * picks them up and records their resolved styles into the union\n * `style.js`. Used by `renderHookWithCss` to pre-register atoms the\n * hook under test will look up.\n * @param classNames Class names to include.\n * @returns Source string suitable for `bootstrapRnwindRuntime`.\n */\nfunction sourceFromClassNames(classNames: readonly string[]): string {\n const joined = classNames.join(' ').replaceAll('\"', String.raw`\\\"`)\n return `const V: any = () => null\\nexport default () => <V className=\"${joined}\" />\\n`\n}\n\n// ────────────────────────────────────────────────────────────────────────\n// Public exports — types\n// ────────────────────────────────────────────────────────────────────────\n\n/** Exact signature of `@testing-library/react-native`'s `render`. */\nexport type Render = typeof testingLibraryRender\n\n/** Exact signature of `@testing-library/react-native`'s `renderHook`. */\nexport type RenderHook = typeof testingLibraryRenderHook\n\n/**\n * Exact return type of `@testing-library/react-native`'s `render` — we\n * re-export the upstream type so tests get every query (`getByTestId`,\n * `queryByText`, `rerender`, `unmount`, `debug`, `UNSAFE_root`, …) with\n * its real signature.\n */\nexport type TestingLibraryRenderAPI = ReturnType<typeof testingLibraryRender>\n\n/** Options accepted by {@link renderWithCss}. */\nexport interface RenderWithCssOptions {\n /** Theme CSS fed to rnwind's parser. Defaults to {@link DEFAULT_THEME_CSS}. */\n themeCss?: string\n /**\n * Active scheme the runtime resolver uses. Defaults to `'light'`.\n * Flip to `'dark'` / `'brand'` / ... to exercise scheme-variant atoms.\n */\n scheme?: string\n /**\n * Safe-area insets to inject. When provided, the component tree is\n * rendered under a `<RnwindProvider insets={...}>` so `*-safe` atoms\n * resolve to real numbers instead of the `0` fallback. Partial is\n * accepted — missing sides default to zero.\n */\n insets?: Partial<Insets>\n /**\n * Haptic dispatcher forwarded onto the implicit `<RnwindProvider>`.\n * Tests pass a spy callback to assert which haptic atoms fired.\n */\n onHaptics?: OnHaptics\n /**\n * `react-native` namespace bindings forwarded into the rewritten\n * module. Defaults to importing the test process's `react-native`\n * (typically a mocked stub). Override to inject custom host elements.\n */\n reactNative?: Record<string, unknown>\n}\n\n/**\n * Result of a {@link renderWithCss} call. Every key from\n * `@testing-library/react-native`'s `render` return value is spread onto\n * this object — `getByTestId`, `queryByText`, `rerender`, `unmount`,\n * `debug`, etc. — so tests use the testing-library API directly.\n *\n * Two rnwind-specific handles are added:\n * - `transformedSource` — the exact code the rnwind Metro transformer\n * emitted for your input. Log it to confirm the rewrite.\n * - `cleanup` — tears down the ephemeral rnwind state + chunk cache dir.\n * Call from `afterEach` so successive tests don't share atoms.\n */\nexport type RenderWithCssResult = TestingLibraryRenderAPI & {\n /** The post-transformer source text. Same code Metro would ship. */\n transformedSource: string\n /** Tear down the ephemeral rnwind state + cache dir. */\n cleanup: () => void\n}\n\n/** Options accepted by {@link renderHookWithCss}. */\nexport interface RenderHookWithCssOptions<Props> extends RenderHookOptions<Props> {\n /** Theme CSS fed to rnwind's parser. Defaults to the built-in minimal theme. */\n themeCss?: string\n /**\n * Active scheme the runtime resolver uses. Defaults to `'light'`. When\n * set, the hook is wrapped in `<RnwindProvider scheme={scheme}>` so\n * `useScheme()` / `useCss()` observe the override.\n */\n scheme?: string\n /**\n * Class names to pre-register into the runtime atom registry before\n * the hook runs. rnwind synthesizes a throwaway source file mentioning\n * these classes, feeds it through the real Metro transformer, and\n * evaluates the generated `style.js` bundle — so the hook sees\n * exactly the atoms the production bundle would register.\n */\n classNames?: readonly string[]\n}\n\n/**\n * Result of a {@link renderHookWithCss} call. Mirrors\n * `@testing-library/react-native`'s `renderHook` return type — `result`,\n * `rerender`, `unmount` — and adds a `cleanup` that tears down the\n * ephemeral rnwind state.\n */\nexport type RenderHookWithCssResult<Result, Props> = ReturnType<typeof testingLibraryRenderHook<Result, Props>> & {\n /** Tear down the ephemeral rnwind state + cache dir. */\n cleanup: () => void\n}\n\n// ────────────────────────────────────────────────────────────────────────\n// Public exports — functions\n// ────────────────────────────────────────────────────────────────────────\n\n/**\n * Feed a source string through rnwind's Metro transformer, evaluate\n * the generated `style.js` bundle, evaluate the rewritten module, and\n * render the default-exported component with\n * `@testing-library/react-native`.\n *\n * The rendered RN element's `style` prop is the EXACT array your\n * production bundle would attach — same transformer, same runtime\n * resolver, same atoms. Assert on `flatten(node.props.style)` to verify\n * the resolved values.\n * @example\n * ```tsx\n * const handle = await renderWithCss(\n * `import { View } from 'react-native'\n * export default () => <View className=\"bg-primary p-4\" testID=\"box\" />`,\n * { themeCss: `@import 'tailwindcss'; @theme { --color-primary: #6366f1; }` },\n * )\n * const flat = flatten(handle.getByTestId('box').props.style)\n * expect(flat.backgroundColor).toBe('#6366f1')\n * ```\n * @param source User source (one file) to transform + render.\n * @param options Optional theme override, scheme, and `react-native` bindings.\n * @returns Render queries + diagnostic handles.\n */\nexport async function renderWithCss(source: string, options: RenderWithCssOptions = {}): Promise<RenderWithCssResult> {\n const reactNative = options.reactNative ?? resolveReactNative()\n const {\n projectRoot: _projectRoot,\n transformedSource,\n cleanup,\n } = await bootstrapRnwindRuntime({\n themeCss: options.themeCss,\n source,\n })\n const Component = evaluateRewrittenModule(transformedSource, reactNative)\n const rendered = TESTING_LIBRARY.render(buildRootElement(Component, options.scheme, options.insets, options.onHaptics))\n return Object.assign({}, rendered, { transformedSource, cleanup })\n}\n\n/**\n * Render-side counterpart to {@link renderWithCss} for testing hooks in\n * isolation. Pre-registers atoms for the supplied `classNames`, wraps\n * the hook in an optional `<RnwindProvider>`, and forwards to\n * `@testing-library/react-native`'s `renderHook`.\n * @example\n * ```ts\n * const { result, cleanup } = await renderHookWithCss(\n * () => useCss('bg-primary'),\n * {\n * themeCss: `@import 'tailwindcss'; @theme { --color-primary: #6366f1; }`,\n * classNames: ['bg-primary'],\n * },\n * )\n * expect(flatten(result.current).backgroundColor).toBe('#6366f1')\n * cleanup()\n * ```\n * @param callback Hook body — same shape as `renderHook(callback)`.\n * @param options Theme, scheme, classNames to pre-register, plus\n * everything `renderHook` itself accepts (`initialProps`, `wrapper`).\n * @returns `renderHook`'s return value plus a `cleanup` function.\n */\nexport async function renderHookWithCss<Result, Props = unknown>(\n callback: (props: Props) => Result,\n options: RenderHookWithCssOptions<Props> = {},\n): Promise<RenderHookWithCssResult<Result, Props>> {\n const { cleanup } = await bootstrapRnwindRuntime({\n themeCss: options.themeCss,\n source: options.classNames && options.classNames.length > 0 ? sourceFromClassNames(options.classNames) : undefined,\n })\n const wrapper = composeHookWrapper(options.scheme, options.wrapper as RenderHookWithCssOptions<Props>['wrapper'])\n const { themeCss: _themeCss, scheme: _scheme, classNames: _classNames, ...hookOptions } = options\n const rendered = TESTING_LIBRARY.renderHook<Result, Props>(callback, {\n ...(hookOptions as RenderHookOptions<Props>),\n wrapper,\n })\n return Object.assign({}, rendered, { cleanup })\n}\n\n/**\n * Flatten a React Native style array (or single style object) into one\n * merged record. RN flattens left-to-right (later wins), so the returned\n * record is what the native view manager actually applies.\n * @param styles Style array, single object, or null/undefined.\n * @returns Flat style record.\n */\nexport function flatten(styles: unknown): Record<string, unknown> {\n if (styles == null) return {}\n if (Array.isArray(styles)) return Object.assign({}, ...styles.map((entry) => flatten(entry)))\n if (typeof styles === 'object') return styles as Record<string, unknown>\n return {}\n}\n"],"names":["generateImport","rnwindRuntime","metroTransform"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rnwind",
3
- "version": "0.0.9",
3
+ "version": "0.0.11",
4
4
  "description": "Tailwind for React Native",
5
5
  "author": "https://github.com/sagltd",
6
6
  "license": "MIT",
@@ -1,4 +1,5 @@
1
1
  import { compile } from '@tailwindcss/node'
2
+ import * as tailwindNode from '@tailwindcss/node'
2
3
  import { Scanner, type SourceEntry } from '@tailwindcss/oxide'
3
4
  import { formatHex as culoriFormatHex } from 'culori'
4
5
  import { Features, transform, type TransformOptions } from 'lightningcss'
@@ -19,6 +20,7 @@ import {
19
20
  import { coerceUnparsedValue, serializeTokens, substituteThemeVars } from './tokens'
20
21
  import { normalizeColorString } from './color'
21
22
  import type { RNStyle } from './types'
23
+ import type { ThemeTable, ThemeTables } from '../types'
22
24
  import type { Declaration as LcDeclaration, TokenOrValue } from 'lightningcss'
23
25
 
24
26
  /**
@@ -153,6 +155,14 @@ export interface ParsedOutput {
153
155
  * provider's `windowWidth`.
154
156
  */
155
157
  breakpoints: ReadonlyMap<string, number>
158
+ /**
159
+ * Per-scheme user theme token tables (`--color-*`, `--spacing-*`, …) with
160
+ * `--color-*` values lowered to sRGB. The style-builder emits these as
161
+ * `registerThemeTokens({...})` in the manifest so `useColor` / `useToken` /
162
+ * `useSize` resolve out of the box, without the user threading a `tables`
163
+ * prop on the provider. Keyed by scheme (`base` + each declared variant).
164
+ */
165
+ themeTokens: ThemeTables
156
166
  }
157
167
 
158
168
  /**
@@ -174,6 +184,8 @@ export interface ParsedOutput {
174
184
  export class TailwindParser {
175
185
  private readonly scanner: Scanner
176
186
  private compiler: TailwindCompiler | undefined
187
+ /** Full resolved base theme (built-in palette + user `@theme`), colors lowered to sRGB. Source for `useColor` / `useToken`. */
188
+ private baseThemeTokens: ThemeTable | null = null
177
189
  private readonly themeSchemes: ThemeSchemeTable
178
190
  private readonly schemeAliases: ReadonlyMap<string, string>
179
191
  /**
@@ -260,6 +272,39 @@ export class TailwindParser {
260
272
  return merged
261
273
  }
262
274
 
275
+ /**
276
+ * Build the per-scheme theme token tables for `registerThemeTokens` —
277
+ * the data source for `useColor` / `useToken` / `useSize`. Emits one table
278
+ * per scheme the user wrote tokens under (`base` + each variant block /
279
+ * `.dark` override). `--color-*` values are lowered to sRGB (matching the
280
+ * className path) using `resolver` so a wide-gamut or `var()`-referencing
281
+ * token resolves to an RN-renderable string; other tokens pass through.
282
+ * @param resolver Full compiled `:root` table, for resolving `var()` refs.
283
+ * @returns Scheme → (token name → value) map.
284
+ */
285
+ private buildThemeTokens(resolver: ReadonlyMap<string, string>): ThemeTables {
286
+ const out: ThemeTables = {}
287
+ // BASE: the full resolved theme (built-in palette + user `@theme`). The
288
+ // runtime merges this under the active scheme, so `useColor('pink-500')`
289
+ // and `useColor('<your-token>')` both resolve in every scheme.
290
+ if (this.baseThemeTokens) out[BASE_SCHEME] = this.baseThemeTokens
291
+ // VARIANTS: only the per-scheme overrides the user wrote (`.dark { … }` /
292
+ // `@variant dark { … }`) — they layer on top of base at runtime.
293
+ for (const scheme of this.themeSchemes.keys()) {
294
+ if (scheme === BASE_SCHEME) continue
295
+ const userTable = this.themeSchemes.get(scheme)
296
+ if (!userTable || userTable.size === 0) continue
297
+ const schemeResolver = new Map(resolver)
298
+ for (const [k, v] of this.effectiveVars(scheme)) schemeResolver.set(k, v)
299
+ const table: ThemeTable = {}
300
+ for (const [name, raw] of userTable) {
301
+ table[name] = name.startsWith('--color-') ? lowerColorToken(raw, schemeResolver) : raw
302
+ }
303
+ out[scheme] = table
304
+ }
305
+ return out
306
+ }
307
+
263
308
  /**
264
309
  * Build the Tailwind compiler on first use and cache it. The theme CSS
265
310
  * gets a `theme(inline)` modifier on its `@import 'tailwindcss'` so
@@ -277,6 +322,13 @@ export class TailwindParser {
277
322
  } catch (error) {
278
323
  throw wrapThemeError(error)
279
324
  }
325
+ // Load the resolved design system ONCE to capture the FULL theme — the
326
+ // built-in palette (`pink-500`, …) plus the user's `@theme` tokens — so
327
+ // `useColor` / `useToken` resolve any theme value, not just the utilities
328
+ // a class happened to use (Tailwind tree-shakes `:root`, so the compiled
329
+ // CSS alone never carries the full palette). Best-effort: a load failure
330
+ // just narrows the hooks to the user's own tokens.
331
+ this.baseThemeTokens = await loadBaseThemeTokens(ready)
280
332
  return this.compiler
281
333
  }
282
334
 
@@ -446,7 +498,71 @@ export class TailwindParser {
446
498
  if (!referencedKeyframes.has(name)) keyframes.delete(name)
447
499
  }
448
500
 
449
- return { atoms, keyframes, propertyDefaults, gradientAtoms, hapticAtoms, candidates: [...candidates], schemes, breakpoints }
501
+ const themeTokens = this.buildThemeTokens(compiledTheme)
502
+ return { atoms, keyframes, propertyDefaults, gradientAtoms, hapticAtoms, candidates: [...candidates], schemes, breakpoints, themeTokens }
503
+ }
504
+ }
505
+
506
+ /**
507
+ * Lower a `--color-*` token value to an RN-renderable sRGB string, matching
508
+ * the className path: resolve any `var()` ref via `resolver`, then lower a
509
+ * wide-gamut form (`oklch(…)`, `lab(…)`, `color(p3 …)`) to sRGB. Hex / rgb /
510
+ * named colors pass through unchanged.
511
+ * @param raw Raw token value.
512
+ * @param resolver Var name → value table for resolving `var()` references.
513
+ * @returns RN-safe color string.
514
+ */
515
+ function lowerColorToken(raw: string, resolver: ReadonlyMap<string, string>): string {
516
+ const substituted = substituteThemeVars(raw, resolver)
517
+ return normalizeColorString(substituted) ?? substituted
518
+ }
519
+
520
+ /** Theme token families excluded from the registered base table — pure Tailwind internals with no `useColor`/`useToken` value. */
521
+ const INTERNAL_TOKEN_PREFIXES: readonly string[] = ['--tw-', '--default-']
522
+
523
+ /** Shape of a resolved design-system theme entry from `@tailwindcss/node`'s unstable loader. */
524
+ interface DesignSystemTheme {
525
+ theme?: { entries?: () => Iterable<[string, { value?: unknown }]> }
526
+ }
527
+
528
+ /**
529
+ * `@tailwindcss/node`'s `__unstable__loadDesignSystem` — exists at runtime but
530
+ * isn't in the package's published types, so it's accessed through a narrowed
531
+ * cast rather than a named import. Returns `undefined` when the (unstable) API
532
+ * isn't present, so {@link loadBaseThemeTokens} degrades gracefully.
533
+ */
534
+ const loadDesignSystem = (tailwindNode as unknown as {
535
+ __unstable__loadDesignSystem?: (css: string, options: { base: string }) => Promise<DesignSystemTheme>
536
+ }).__unstable__loadDesignSystem
537
+
538
+ /**
539
+ * Load the FULL resolved Tailwind theme (built-in palette + the user's
540
+ * `@theme`) via the design-system API and flatten it to an RN-safe token
541
+ * table — `--color-*` values lowered to sRGB, everything else passed through.
542
+ * This is what lets `useColor` / `useToken` resolve ANY theme token, including
543
+ * built-ins a class never used (Tailwind tree-shakes the compiled `:root`, so
544
+ * the compiled CSS alone can't supply the full palette). Internal `--tw-*` /
545
+ * `--default-*` families are dropped. Returns `null` on any failure so the
546
+ * caller degrades to the user's own `@theme` tokens.
547
+ * @param themeCss Compile-ready theme CSS (variants stripped, custom-variants added).
548
+ * @returns Flattened base token table, or null.
549
+ */
550
+ async function loadBaseThemeTokens(themeCss: string): Promise<ThemeTable | null> {
551
+ if (typeof loadDesignSystem !== 'function') return null
552
+ try {
553
+ const design = await loadDesignSystem(themeCss, { base: process.cwd() })
554
+ const entries = design.theme?.entries?.()
555
+ if (!entries) return null
556
+ const table: ThemeTable = {}
557
+ for (const [name, entry] of entries) {
558
+ const raw = entry?.value
559
+ if (typeof raw !== 'string') continue
560
+ if (INTERNAL_TOKEN_PREFIXES.some((prefix) => name.startsWith(prefix))) continue
561
+ table[name] = name.startsWith('--color-') ? (normalizeColorString(raw) ?? raw) : raw
562
+ }
563
+ return table
564
+ } catch {
565
+ return null
450
566
  }
451
567
  }
452
568
 
@@ -497,6 +613,7 @@ function emptyOutput(): ParsedOutput {
497
613
  candidates: [],
498
614
  schemes: [BASE_SCHEME],
499
615
  breakpoints: new Map(),
616
+ themeTokens: {},
500
617
  }
501
618
  }
502
619