rnwind 0.0.8 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/core/parser/color.cjs +33 -1
- package/lib/cjs/core/parser/color.cjs.map +1 -1
- package/lib/cjs/core/parser/color.d.ts +10 -0
- package/lib/cjs/core/parser/declaration.cjs +121 -9
- package/lib/cjs/core/parser/declaration.cjs.map +1 -1
- package/lib/cjs/core/parser/gradient.cjs +46 -12
- package/lib/cjs/core/parser/gradient.cjs.map +1 -1
- package/lib/cjs/core/parser/gradient.d.ts +2 -1
- package/lib/cjs/core/parser/keyframes.cjs +27 -12
- package/lib/cjs/core/parser/keyframes.cjs.map +1 -1
- package/lib/cjs/core/parser/keyframes.d.ts +11 -0
- package/lib/cjs/core/parser/layout-dispatcher.cjs +33 -10
- package/lib/cjs/core/parser/layout-dispatcher.cjs.map +1 -1
- package/lib/cjs/core/parser/length.cjs +17 -1
- package/lib/cjs/core/parser/length.cjs.map +1 -1
- package/lib/cjs/core/parser/safe-area.cjs +24 -3
- package/lib/cjs/core/parser/safe-area.cjs.map +1 -1
- package/lib/cjs/core/parser/theme-vars.cjs +58 -8
- package/lib/cjs/core/parser/theme-vars.cjs.map +1 -1
- package/lib/cjs/core/parser/tokens.cjs +77 -9
- package/lib/cjs/core/parser/tokens.cjs.map +1 -1
- package/lib/cjs/core/parser/tokens.d.ts +9 -0
- package/lib/cjs/core/parser/transform.cjs +18 -9
- package/lib/cjs/core/parser/transform.cjs.map +1 -1
- package/lib/cjs/core/parser/tw-parser.cjs +136 -34
- package/lib/cjs/core/parser/tw-parser.cjs.map +1 -1
- package/lib/cjs/core/parser/tw-parser.d.ts +20 -0
- package/lib/cjs/core/parser/typography-dispatcher.cjs +19 -1
- package/lib/cjs/core/parser/typography-dispatcher.cjs.map +1 -1
- package/lib/cjs/core/parser/typography.cjs +15 -18
- package/lib/cjs/core/parser/typography.cjs.map +1 -1
- package/lib/cjs/core/parser/typography.d.ts +5 -5
- package/lib/cjs/core/style-builder/build-style.cjs +12 -3
- package/lib/cjs/core/style-builder/build-style.cjs.map +1 -1
- package/lib/cjs/core/style-builder/build-style.d.ts +3 -1
- package/lib/cjs/core/style-builder/union-builder.cjs +9 -11
- package/lib/cjs/core/style-builder/union-builder.cjs.map +1 -1
- package/lib/cjs/core/style-builder/union-builder.d.ts +7 -8
- package/lib/cjs/metro/dts.cjs +6 -1
- package/lib/cjs/metro/dts.cjs.map +1 -1
- package/lib/cjs/metro/transformer.cjs +42 -77
- package/lib/cjs/metro/transformer.cjs.map +1 -1
- package/lib/cjs/metro/with-config.cjs +9 -29
- package/lib/cjs/metro/with-config.cjs.map +1 -1
- package/lib/cjs/runtime/hooks/use-scheme.cjs +17 -11
- package/lib/cjs/runtime/hooks/use-scheme.cjs.map +1 -1
- package/lib/cjs/runtime/hooks/use-scheme.d.ts +7 -4
- package/lib/cjs/runtime/index.cjs +2 -1
- package/lib/cjs/runtime/index.cjs.map +1 -1
- package/lib/cjs/runtime/index.d.ts +2 -2
- package/lib/cjs/runtime/lookup-css.cjs +41 -0
- package/lib/cjs/runtime/lookup-css.cjs.map +1 -1
- package/lib/cjs/runtime/lookup-css.d.ts +29 -0
- package/lib/cjs/runtime/resolve.cjs +8 -6
- package/lib/cjs/runtime/resolve.cjs.map +1 -1
- package/lib/cjs/runtime/wrap.cjs +50 -57
- package/lib/cjs/runtime/wrap.cjs.map +1 -1
- package/lib/cjs/runtime/wrap.d.ts +10 -4
- package/lib/cjs/testing/index.cjs +1 -1
- package/lib/cjs/testing/index.cjs.map +1 -1
- package/lib/esm/core/parser/color.d.ts +10 -0
- package/lib/esm/core/parser/color.mjs +34 -3
- package/lib/esm/core/parser/color.mjs.map +1 -1
- package/lib/esm/core/parser/declaration.mjs +122 -10
- package/lib/esm/core/parser/declaration.mjs.map +1 -1
- package/lib/esm/core/parser/gradient.d.ts +2 -1
- package/lib/esm/core/parser/gradient.mjs +45 -11
- package/lib/esm/core/parser/gradient.mjs.map +1 -1
- package/lib/esm/core/parser/keyframes.d.ts +11 -0
- package/lib/esm/core/parser/keyframes.mjs +27 -12
- package/lib/esm/core/parser/keyframes.mjs.map +1 -1
- package/lib/esm/core/parser/layout-dispatcher.mjs +33 -10
- package/lib/esm/core/parser/layout-dispatcher.mjs.map +1 -1
- package/lib/esm/core/parser/length.mjs +17 -1
- package/lib/esm/core/parser/length.mjs.map +1 -1
- package/lib/esm/core/parser/safe-area.mjs +24 -3
- package/lib/esm/core/parser/safe-area.mjs.map +1 -1
- package/lib/esm/core/parser/theme-vars.mjs +58 -8
- package/lib/esm/core/parser/theme-vars.mjs.map +1 -1
- package/lib/esm/core/parser/tokens.d.ts +9 -0
- package/lib/esm/core/parser/tokens.mjs +77 -10
- package/lib/esm/core/parser/tokens.mjs.map +1 -1
- package/lib/esm/core/parser/transform.mjs +18 -9
- package/lib/esm/core/parser/transform.mjs.map +1 -1
- package/lib/esm/core/parser/tw-parser.d.ts +20 -0
- package/lib/esm/core/parser/tw-parser.mjs +138 -36
- package/lib/esm/core/parser/tw-parser.mjs.map +1 -1
- package/lib/esm/core/parser/typography-dispatcher.mjs +19 -1
- package/lib/esm/core/parser/typography-dispatcher.mjs.map +1 -1
- package/lib/esm/core/parser/typography.d.ts +5 -5
- package/lib/esm/core/parser/typography.mjs +15 -18
- package/lib/esm/core/parser/typography.mjs.map +1 -1
- package/lib/esm/core/style-builder/build-style.d.ts +3 -1
- package/lib/esm/core/style-builder/build-style.mjs +12 -3
- package/lib/esm/core/style-builder/build-style.mjs.map +1 -1
- package/lib/esm/core/style-builder/union-builder.d.ts +7 -8
- package/lib/esm/core/style-builder/union-builder.mjs +9 -11
- package/lib/esm/core/style-builder/union-builder.mjs.map +1 -1
- package/lib/esm/metro/dts.mjs +6 -1
- package/lib/esm/metro/dts.mjs.map +1 -1
- package/lib/esm/metro/transformer.mjs +42 -77
- package/lib/esm/metro/transformer.mjs.map +1 -1
- package/lib/esm/metro/with-config.mjs +10 -30
- package/lib/esm/metro/with-config.mjs.map +1 -1
- package/lib/esm/runtime/hooks/use-scheme.d.ts +7 -4
- package/lib/esm/runtime/hooks/use-scheme.mjs +17 -11
- package/lib/esm/runtime/hooks/use-scheme.mjs.map +1 -1
- package/lib/esm/runtime/index.d.ts +2 -2
- package/lib/esm/runtime/index.mjs +2 -2
- package/lib/esm/runtime/index.mjs.map +1 -1
- package/lib/esm/runtime/lookup-css.d.ts +29 -0
- package/lib/esm/runtime/lookup-css.mjs +39 -1
- package/lib/esm/runtime/lookup-css.mjs.map +1 -1
- package/lib/esm/runtime/resolve.mjs +9 -7
- package/lib/esm/runtime/resolve.mjs.map +1 -1
- package/lib/esm/runtime/wrap.d.ts +10 -4
- package/lib/esm/runtime/wrap.mjs +50 -57
- package/lib/esm/runtime/wrap.mjs.map +1 -1
- package/lib/esm/testing/index.mjs +2 -2
- package/lib/esm/testing/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/core/parser/color.ts +32 -1
- package/src/core/parser/declaration.ts +119 -10
- package/src/core/parser/gradient.ts +48 -11
- package/src/core/parser/keyframes.ts +31 -3
- package/src/core/parser/layout-dispatcher.ts +32 -9
- package/src/core/parser/length.ts +18 -1
- package/src/core/parser/safe-area.ts +23 -2
- package/src/core/parser/theme-vars.ts +75 -8
- package/src/core/parser/tokens.ts +76 -9
- package/src/core/parser/transform.ts +19 -8
- package/src/core/parser/tw-parser.ts +148 -31
- package/src/core/parser/typography-dispatcher.ts +20 -1
- package/src/core/parser/typography.ts +15 -15
- package/src/core/style-builder/build-style.ts +12 -1
- package/src/core/style-builder/union-builder.ts +10 -12
- package/src/metro/dts.ts +6 -1
- package/src/metro/transformer.ts +42 -78
- package/src/metro/with-config.ts +10 -29
- package/src/runtime/hooks/use-scheme.ts +17 -10
- package/src/runtime/index.ts +2 -1
- package/src/runtime/lookup-css.ts +42 -0
- package/src/runtime/resolve.ts +9 -7
- package/src/runtime/wrap.tsx +57 -61
- package/src/testing/index.ts +3 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.cjs","sources":["../../../../src/runtime/resolve.ts"],"sourcesContent":["import { getStyleVersion, lookupCss, type InteractState } from './lookup-css'\nimport type { RnwindState } from './components/rnwind-provider'\nimport { normalizeClassName } from '../core/normalize-classname'\nimport type { GradientAtomInfo, GradientDirection } from '../core/parser/gradient'\nimport type { HapticRequest, HapticTrigger } from '../core/parser/haptics'\n\n/**\n * Rich className resolver — the runtime heart of the wrap / `useCss`.\n *\n * Resolution order, per className string:\n * 1. **Molecule** — a build-time PRE-MERGED single style object for the\n * whole literal className (per scheme). One map lookup returns it by\n * reference: no array, no merge, no per-atom loop. The common case.\n * 2. **Atom fallback** — for a className the scanner never saw (a\n * runtime-built string like `` `${className} px-2` ``) OR one that\n * carries context-dependent atoms (`pt-safe`, `text-base`, `md:*`),\n * fall back to per-atom resolution via `lookupCss`, which folds in\n * insets / fontScale / breakpoint / scheme.\n *\n * Results are cached by `(normalized className, scheme, insets, fontScale,\n * breakpoint)` so repeated renders return the SAME reference until the\n * reactive context changes. Atoms / molecules / features all live in\n * build-time registries the generated `.rnwind/*.js` modules populate.\n */\n\n/** Always-loaded fallback scheme key. */\nconst COMMON_SCHEME = 'common'\n\n/** Empty style sentinel. */\nconst EMPTY: readonly unknown[] = []\n\n/** scheme → normalized className → pre-merged style object. */\nlet molecules: Record<string, Record<string, unknown>> = Object.create(null)\n/** atom name → gradient role + resolved colour. */\nlet gradients: Record<string, GradientAtomInfo> = Object.create(null)\n/** atom name (incl. `active:`/`focus:` prefix) → haptic request. */\nlet haptics: Record<string, HapticRequest> = Object.create(null)\n/** Bumps on any molecule/gradient/haptic registration. */\nlet registryVersion = 0\n\n/** Per-(className·state) resolved cache — strong references between context changes. */\nconst resolvedCache = new Map<string, ResolvedCss>()\n/** Version the cache was last valid for (`getStyleVersion()` + {@link registryVersion}). */\nlet cachedFor = -1\n\n/**\n * Hard ceiling on the resolved cache. The cache is pure memoisation, so\n * eviction only costs a re-resolve (sub-µs) — never correctness. Build\n * molecules are NOT in here; they live permanently in `molecules`.\n */\nconst MAX_RESOLVED_CACHE = 2048\n\n/**\n * Store a resolved result, bulk-evicting the OLDEST half when the cache\n * hits {@link MAX_RESOLVED_CACHE}. `Map` preserves insertion order, so the\n * first keys are the oldest. Bulk eviction keeps the hot (cache-hit) path\n * free of per-access LRU bookkeeping at the cost of an occasional small\n * recompute burst under sustained pressure (web / long sessions).\n * @param key Cache key.\n * @param value Resolved result to store.\n */\nfunction cacheResolved(key: string, value: ResolvedCss): void {\n if (resolvedCache.size >= MAX_RESOLVED_CACHE) {\n let drop = resolvedCache.size >> 1\n for (const oldKey of resolvedCache.keys()) {\n resolvedCache.delete(oldKey)\n if (--drop <= 0) break\n }\n }\n resolvedCache.set(key, value)\n}\n\n/** A unit-square gradient endpoint. */\ninterface GradientPoint {\n readonly x: number\n readonly y: number\n}\n\n/** Rich resolution: the RN `style` plus any className-derived props. */\nexport interface ResolvedCss {\n /** RN `style` value — a single molecule object (by ref) or an atom array. */\n readonly style: unknown\n /** Gradient stop colours (when the className is a complete gradient). */\n readonly colors?: readonly string[]\n /** Gradient start point. */\n readonly start?: GradientPoint\n /** Gradient end point. */\n readonly end?: GradientPoint\n /** Text truncation line count. */\n readonly numberOfLines?: number\n /** Text ellipsize mode. */\n readonly ellipsizeMode?: 'tail' | 'clip'\n /** Haptic requests present on the className, for the wrap to dispatch. */\n readonly haptics?: readonly { readonly request: HapticRequest; readonly trigger: HapticTrigger }[]\n}\n\n/** `GradientDirection` → expo-linear-gradient start/end points. */\nconst DIRECTION_POINTS: Record<GradientDirection, { start: GradientPoint; end: GradientPoint }> = {\n 'to-t': { start: { x: 0.5, y: 1 }, end: { x: 0.5, y: 0 } },\n 'to-b': { start: { x: 0.5, y: 0 }, end: { x: 0.5, y: 1 } },\n 'to-l': { start: { x: 1, y: 0.5 }, end: { x: 0, y: 0.5 } },\n 'to-r': { start: { x: 0, y: 0.5 }, end: { x: 1, y: 0.5 } },\n 'to-tl': { start: { x: 1, y: 1 }, end: { x: 0, y: 0 } },\n 'to-tr': { start: { x: 0, y: 1 }, end: { x: 1, y: 0 } },\n 'to-bl': { start: { x: 1, y: 0 }, end: { x: 0, y: 1 } },\n 'to-br': { start: { x: 0, y: 0 }, end: { x: 1, y: 1 } },\n unknown: { start: { x: 0, y: 0.5 }, end: { x: 1, y: 0.5 } },\n}\n\n/**\n * Register one scheme's pre-merged molecules (atom-merged literal\n * classNames). Merges onto any existing entries for the scheme.\n * @param scheme Scheme name (or `'common'`).\n * @param entries Normalized className → merged style object.\n */\nexport function registerMolecules(scheme: string, entries: Record<string, unknown>): void {\n molecules[scheme] = { ...molecules[scheme], ...entries }\n registryVersion += 1\n}\n\n/**\n * Register the gradient atom map (atom name → role + resolved colour).\n * @param map Atom name → gradient info.\n */\nexport function registerGradients(map: Record<string, GradientAtomInfo>): void {\n gradients = map\n registryVersion += 1\n}\n\n/**\n * Register the haptic atom map (atom name → request).\n * @param map Atom name → haptic request.\n */\nexport function registerHaptics(map: Record<string, HapticRequest>): void {\n haptics = map\n registryVersion += 1\n}\n\n\n\n/**\n * Per-state-object signature memo. `RnwindState` is created fresh (via the\n * provider's `useMemo`) whenever any field changes, so its identity is a\n * sound key — a new object means a new signature. Keyed weakly so states\n * GC with their provider.\n */\nconst stateSignatureCache = new WeakMap<RnwindState, string>()\n\n/**\n * Cache key dimension for the reactive context — everything that can\n * change a resolved style. Uses the breakpoint TIER (`activeBreakpoint`),\n * NOT the raw `windowWidth`: two widths in the same tier gate `md:*` atoms\n * identically, so they resolve the same. This collapses the window axis\n * from \"every pixel\" to ~6 values, bounding the cache on resizable\n * surfaces (web / desktop) without changing any result.\n * @param state Rnwind context.\n * @returns Compact signature string.\n */\nfunction stateSignature(state: RnwindState): string {\n const { insets } = state\n return `${state.scheme}|${insets.top},${insets.right},${insets.bottom},${insets.left}|${state.fontScale}|${state.activeBreakpoint}`\n}\n\n/**\n * Memoised {@link stateSignature} — one `WeakMap.get` on the hot path\n * instead of rebuilding the template string every resolve.\n * @param state Rnwind context.\n * @returns Cached compact signature.\n */\nfunction stateSignatureCached(state: RnwindState): string {\n let signature = stateSignatureCache.get(state)\n if (signature === undefined) {\n signature = stateSignature(state)\n stateSignatureCache.set(state, signature)\n }\n return signature\n}\n\n/**\n * Compact signature of the live interactive state for the cache key.\n * @param interactState Active/focus flags, or undefined for the plain path.\n * @returns Two-bit signature (`''` when no interactive state).\n */\nfunction interactSignature(interactState?: InteractState): string {\n if (!interactState) return ''\n const active = interactState.active ? 1 : 0\n const focus = interactState.focus ? 1 : 0\n return `${active}${focus}`\n}\n\n/**\n * Whether a token is a feature-ONLY utility (gradient stop/direction,\n * haptic request, or text-truncate) that contributes NO RN `style`. These\n * are folded in via {@link attachFeatures}, so they must be kept OUT of\n * the `lookupCss` input — otherwise the atom resolver treats them as\n * unknown style atoms and emits a spurious \"unknown class\" dev warning\n * (e.g. for `active:haptic-rigid`).\n * @param token Atom name.\n * @returns True when the token carries no style.\n */\nfunction isFeatureOnlyToken(token: string): boolean {\n return Boolean(gradients[token]) || Boolean(haptics[token]) || truncateForToken(token) !== null\n}\n\n/**\n * Lifecycle trigger for a haptic atom from its variant prefix.\n * @param token Atom name (maybe `active:`/`focus:`/`hover:` prefixed).\n * @returns The trigger.\n */\nfunction hapticTriggerForToken(token: string): HapticTrigger {\n const colon = token.indexOf(':')\n if (colon === -1) return 'mount'\n const prefix = token.slice(0, colon)\n if (prefix === 'active') return 'pressIn'\n if (prefix === 'focus') return 'focus'\n if (prefix === 'hover') return 'hover'\n return 'mount'\n}\n\n/**\n * Syntactic text-truncate directive for one atom.\n * @param token Atom name.\n * @returns Partial truncate props, or null.\n */\nfunction truncateForToken(token: string): { numberOfLines?: number; ellipsizeMode?: 'tail' | 'clip' } | null {\n if (token === 'truncate') return { numberOfLines: 1, ellipsizeMode: 'tail' }\n if (token === 'text-ellipsis') return { ellipsizeMode: 'tail' }\n if (token === 'text-clip') return { ellipsizeMode: 'clip' }\n if (token === 'line-clamp-none') return { numberOfLines: 0 }\n if (token.startsWith('line-clamp-')) {\n const count = Number(token.slice('line-clamp-'.length))\n if (Number.isInteger(count) && count >= 0) return { numberOfLines: count }\n }\n return null\n}\n\n/**\n * Assemble gradient props from gradient roles present in the atom list.\n * @param tokens Atom names.\n * @returns `{colors, start, end}` or null when not a complete gradient.\n */\nfunction assembleGradient(tokens: readonly string[]): { colors: string[]; start: GradientPoint; end: GradientPoint } | null {\n let from: string | undefined\n let via: string | undefined\n let to: string | undefined\n let dir: GradientDirection | undefined\n for (const token of tokens) {\n const info = gradients[token]\n if (!info) continue\n switch (info.role) {\n case 'from': {\n from = info.color\n break\n }\n case 'via': {\n via = info.color\n break\n }\n case 'to': {\n to = info.color\n break\n }\n default: {\n ;({ dir } = info)\n }\n }\n }\n if (dir === undefined) return null\n const colors = [from, via, to].filter((color): color is string => color !== undefined)\n if (colors.length < 2) return null\n const points = DIRECTION_POINTS[dir]\n return { colors, start: points.start, end: points.end }\n}\n\n/**\n * Fold every truncate directive across the atom list into one result —\n * last token wins per prop (matches Tailwind last-wins).\n * @param tokens Atom names.\n * @returns Merged truncate props (empty when none apply).\n */\nfunction collectTruncate(tokens: readonly string[]): { numberOfLines?: number; ellipsizeMode?: 'tail' | 'clip' } {\n const out: { numberOfLines?: number; ellipsizeMode?: 'tail' | 'clip' } = {}\n for (const token of tokens) {\n const truncate = truncateForToken(token)\n if (!truncate) continue\n if (truncate.numberOfLines !== undefined) out.numberOfLines = truncate.numberOfLines\n if (truncate.ellipsizeMode !== undefined) out.ellipsizeMode = truncate.ellipsizeMode\n }\n return out\n}\n\n/**\n * Collect every haptic request present in the atom list, tagged with the\n * lifecycle trigger its variant prefix implies.\n * @param tokens Atom names.\n * @returns Haptic request list, or undefined when none apply.\n */\nfunction collectHaptics(tokens: readonly string[]): { request: HapticRequest; trigger: HapticTrigger }[] | undefined {\n let collected: { request: HapticRequest; trigger: HapticTrigger }[] | undefined\n for (const token of tokens) {\n const request = haptics[token]\n if (!request) continue\n collected ??= []\n collected.push({ request, trigger: hapticTriggerForToken(token) })\n }\n return collected\n}\n\n/**\n * Scan tokens for the className-derived feature props (gradient,\n * truncate, haptics) and fold them onto the base result.\n * @param base Result carrying the resolved `style`.\n * @param tokens Atom names.\n * @returns The result with any feature props attached.\n */\nfunction attachFeatures(base: ResolvedCss, tokens: readonly string[]): ResolvedCss {\n const { numberOfLines, ellipsizeMode } = collectTruncate(tokens)\n const collected = collectHaptics(tokens)\n const gradient = assembleGradient(tokens)\n const result: Mutable<ResolvedCss> = { style: base.style }\n if (gradient) {\n result.colors = gradient.colors\n result.start = gradient.start\n result.end = gradient.end\n }\n // `numberOfLines: 0` is kept (RN reads it as \"unlimited\"): `line-clamp-none`\n // must be able to explicitly reset an earlier `line-clamp-N` on the same\n // element — dropping the 0 would silently leave the prior limit in place.\n if (numberOfLines !== undefined) {\n result.numberOfLines = numberOfLines\n if (ellipsizeMode !== undefined) result.ellipsizeMode = ellipsizeMode\n }\n if (collected) result.haptics = collected\n return result\n}\n\n/**\n * Flatten the per-atom style array into ONE object — a *runtime molecule*.\n * RN flattens a style array left-to-right (later wins), which is exactly\n * `Object.assign` semantics, so the merged object renders identically\n * while giving dynamic (cva / clsx) classNames the same single-object\n * shape a build-time molecule has. The caller caches the result per\n * `(className · state)`, so the merge runs once per unique context.\n * @param array Per-atom style array from `lookupCss`.\n * @returns Single merged style object.\n */\nfunction mergeStyleArray(array: readonly unknown[]): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const entry of array) if (entry && typeof entry === 'object') Object.assign(out, entry)\n return out\n}\n\n/**\n * Compose a resolved style with a caller-supplied inline style (user wins).\n * @param style\n * @param userStyle\n */\nfunction withUserStyle(style: unknown, userStyle: unknown): unknown {\n return Array.isArray(style) ? [...style, userStyle] : [style, userStyle]\n}\n\n/**\n * Resolve a className against the reactive context into a style plus any\n * className-derived props. Molecule-first (one lookup, by reference),\n * atom-fallback for unseen / context-dependent strings, cached per\n * `(className, state)`.\n * @param className Raw className string.\n * @param state Rnwind context from `useRnwind()`.\n * @param userStyle Optional inline style appended last (wins).\n * @param interactState Live active/focus flags (for `active:`/`focus:` atoms).\n * @returns The resolved style + feature props.\n */\nexport function resolve(\n className: string | null | undefined,\n state: RnwindState,\n userStyle?: unknown,\n interactState?: InteractState,\n): ResolvedCss {\n const version = getStyleVersion() + registryVersion\n if (version !== cachedFor) {\n resolvedCache.clear()\n cachedFor = version\n }\n if (className == null) {\n return { style: userStyle === undefined || userStyle === null ? EMPTY : [userStyle] }\n }\n // Key on the RAW className so the hot (cache-hit) path skips normalize\n // entirely — normalization only runs on a miss. The state signature is\n // memoised per state object, so the hit path is one WeakMap.get + one\n // string concat + one Map.get.\n const key = `${className}@${stateSignatureCached(state)}@${interactSignature(interactState)}`\n const cached = resolvedCache.get(key)\n if (cached !== undefined) {\n return userStyle === undefined || userStyle === null ? cached : { ...cached, style: withUserStyle(cached.style, userStyle) }\n }\n const normalized = normalizeClassName(className)\n if (normalized.length === 0) {\n const empty: ResolvedCss = { style: EMPTY }\n cacheResolved(key, empty)\n return userStyle === undefined || userStyle === null ? empty : { style: [userStyle] }\n }\n // Molecules are static pre-merges; anything carrying `active:`/`focus:`\n // is never registered as one, so the atom path handles interactive state.\n const tokens = normalized.split(' ')\n const molecule = interactState ? undefined : molecules[state.scheme]?.[normalized] ?? molecules[COMMON_SCHEME]?.[normalized]\n // Feature-only tokens (gradient / haptic / truncate) carry no style — keep\n // them out of the atom lookup so they don't warn as \"unknown class\". The\n // atom array is merged into ONE object (a runtime molecule) so dynamic\n // (cva / clsx) classNames get the same single-object shape as a build\n // molecule; the cache below pins the context, so the merge is correct.\n const style =\n molecule === undefined\n ? mergeStyleArray(lookupCss(tokens.filter((token) => !isFeatureOnlyToken(token)).join(' '), state, undefined, interactState))\n : molecule\n const base = attachFeatures({ style }, tokens)\n cacheResolved(key, base)\n return userStyle === undefined || userStyle === null ? base : { ...base, style: withUserStyle(base.style, userStyle) }\n}\n\n/** Local mutable view for building the frozen-shaped result. */\ntype Mutable<T> = { -readonly [K in keyof T]: T[K] }\n\n/** Test-only — clear the molecule / gradient / haptic registries + cache. */\nexport function __resetResolveState(): void {\n molecules = Object.create(null)\n gradients = Object.create(null)\n haptics = Object.create(null)\n resolvedCache.clear()\n registryVersion += 1\n cachedFor = -1\n}\n\n/** Test-only — current resolved-cache entry count + its hard ceiling. */\nexport function __resolveCacheStats(): { size: number; max: number } {\n return { size: resolvedCache.size, max: MAX_RESOLVED_CACHE }\n}\n\nexport {normalizeClassName} from '../core/normalize-classname'"],"names":["getStyleVersion","normalizeClassName","lookupCss"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"resolve.cjs","sources":["../../../../src/runtime/resolve.ts"],"sourcesContent":["import { breakpointTier, getStyleVersion, lookupCss, type InteractState } from './lookup-css'\nimport type { RnwindState } from './components/rnwind-provider'\nimport { normalizeClassName } from '../core/normalize-classname'\nimport type { GradientAtomInfo, GradientDirection } from '../core/parser/gradient'\nimport type { HapticRequest, HapticTrigger } from '../core/parser/haptics'\n\n/**\n * Rich className resolver — the runtime heart of the wrap / `useCss`.\n *\n * Resolution order, per className string:\n * 1. **Molecule** — a build-time PRE-MERGED single style object for the\n * whole literal className (per scheme). One map lookup returns it by\n * reference: no array, no merge, no per-atom loop. The common case.\n * 2. **Atom fallback** — for a className the scanner never saw (a\n * runtime-built string like `` `${className} px-2` ``) OR one that\n * carries context-dependent atoms (`pt-safe`, `text-base`, `md:*`),\n * fall back to per-atom resolution via `lookupCss`, which folds in\n * insets / fontScale / breakpoint / scheme.\n *\n * Results are cached by `(normalized className, scheme, insets, fontScale,\n * breakpoint)` so repeated renders return the SAME reference until the\n * reactive context changes. Atoms / molecules / features all live in\n * build-time registries the generated `.rnwind/*.js` modules populate.\n */\n\n/** Always-loaded fallback scheme key. */\nconst COMMON_SCHEME = 'common'\n\n/** Empty style sentinel. */\nconst EMPTY: readonly unknown[] = []\n\n/** scheme → normalized className → pre-merged style object. */\nlet molecules: Record<string, Record<string, unknown>> = Object.create(null)\n/** atom name → gradient role + resolved colour. */\nlet gradients: Record<string, GradientAtomInfo> = Object.create(null)\n/** atom name (incl. `active:`/`focus:` prefix) → haptic request. */\nlet haptics: Record<string, HapticRequest> = Object.create(null)\n/** Bumps on any molecule/gradient/haptic registration. */\nlet registryVersion = 0\n\n/** Per-(className·state) resolved cache — strong references between context changes. */\nconst resolvedCache = new Map<string, ResolvedCss>()\n/** Version the cache was last valid for (`getStyleVersion()` + {@link registryVersion}). */\nlet cachedFor = -1\n\n/**\n * Hard ceiling on the resolved cache. The cache is pure memoisation, so\n * eviction only costs a re-resolve (sub-µs) — never correctness. Build\n * molecules are NOT in here; they live permanently in `molecules`.\n */\nconst MAX_RESOLVED_CACHE = 2048\n\n/**\n * Store a resolved result, bulk-evicting the OLDEST half when the cache\n * hits {@link MAX_RESOLVED_CACHE}. `Map` preserves insertion order, so the\n * first keys are the oldest. Bulk eviction keeps the hot (cache-hit) path\n * free of per-access LRU bookkeeping at the cost of an occasional small\n * recompute burst under sustained pressure (web / long sessions).\n * @param key Cache key.\n * @param value Resolved result to store.\n */\nfunction cacheResolved(key: string, value: ResolvedCss): void {\n if (resolvedCache.size >= MAX_RESOLVED_CACHE) {\n let drop = resolvedCache.size >> 1\n for (const oldKey of resolvedCache.keys()) {\n resolvedCache.delete(oldKey)\n if (--drop <= 0) break\n }\n }\n resolvedCache.set(key, value)\n}\n\n/** A unit-square gradient endpoint. */\ninterface GradientPoint {\n readonly x: number\n readonly y: number\n}\n\n/** Rich resolution: the RN `style` plus any className-derived props. */\nexport interface ResolvedCss {\n /** RN `style` value — a single molecule object (by ref) or an atom array. */\n readonly style: unknown\n /** Gradient stop colours (when the className is a complete gradient). */\n readonly colors?: readonly string[]\n /** Gradient start point. */\n readonly start?: GradientPoint\n /** Gradient end point. */\n readonly end?: GradientPoint\n /** Text truncation line count. */\n readonly numberOfLines?: number\n /** Text ellipsize mode. */\n readonly ellipsizeMode?: 'tail' | 'clip'\n /** Haptic requests present on the className, for the wrap to dispatch. */\n readonly haptics?: readonly { readonly request: HapticRequest; readonly trigger: HapticTrigger }[]\n}\n\n/** `GradientDirection` → expo-linear-gradient start/end points. */\nconst DIRECTION_POINTS: Record<GradientDirection, { start: GradientPoint; end: GradientPoint }> = {\n 'to-t': { start: { x: 0.5, y: 1 }, end: { x: 0.5, y: 0 } },\n 'to-b': { start: { x: 0.5, y: 0 }, end: { x: 0.5, y: 1 } },\n 'to-l': { start: { x: 1, y: 0.5 }, end: { x: 0, y: 0.5 } },\n 'to-r': { start: { x: 0, y: 0.5 }, end: { x: 1, y: 0.5 } },\n 'to-tl': { start: { x: 1, y: 1 }, end: { x: 0, y: 0 } },\n 'to-tr': { start: { x: 0, y: 1 }, end: { x: 1, y: 0 } },\n 'to-bl': { start: { x: 1, y: 0 }, end: { x: 0, y: 1 } },\n 'to-br': { start: { x: 0, y: 0 }, end: { x: 1, y: 1 } },\n unknown: { start: { x: 0, y: 0.5 }, end: { x: 1, y: 0.5 } },\n}\n\n/**\n * Register one scheme's pre-merged molecules (atom-merged literal\n * classNames). Merges onto any existing entries for the scheme.\n * @param scheme Scheme name (or `'common'`).\n * @param entries Normalized className → merged style object.\n */\nexport function registerMolecules(scheme: string, entries: Record<string, unknown>): void {\n molecules[scheme] = { ...molecules[scheme], ...entries }\n registryVersion += 1\n}\n\n/**\n * Register the gradient atom map (atom name → role + resolved colour).\n * @param map Atom name → gradient info.\n */\nexport function registerGradients(map: Record<string, GradientAtomInfo>): void {\n gradients = map\n registryVersion += 1\n}\n\n/**\n * Register the haptic atom map (atom name → request).\n * @param map Atom name → haptic request.\n */\nexport function registerHaptics(map: Record<string, HapticRequest>): void {\n haptics = map\n registryVersion += 1\n}\n\n\n\n/**\n * Per-state-object signature memo. `RnwindState` is created fresh (via the\n * provider's `useMemo`) whenever any field changes, so its identity is a\n * sound key — a new object means a new signature. Keyed weakly so states\n * GC with their provider.\n */\nconst stateSignatureCache = new WeakMap<RnwindState, string>()\n\n/**\n * Cache key dimension for the reactive context — everything that can\n * change a resolved style. Uses the numeric breakpoint TIER (count of\n * thresholds reached) from `breakpointTier(windowWidth)`, NOT the\n * `activeBreakpoint` NAME: the name clamps tier-0 into the smallest\n * breakpoint, so widths straddling that threshold (e.g. 320 vs 700 with\n * `sm=640`) would collide on one cache key and serve a stale style. The\n * tier is exact AND bounded — two widths in the same tier gate every\n * `sm:`/`md:`/… atom identically, so they resolve the same.\n * @param state Rnwind context.\n * @returns Compact signature string.\n */\nfunction stateSignature(state: RnwindState): string {\n const { insets } = state\n return `${state.scheme}|${insets.top},${insets.right},${insets.bottom},${insets.left}|${state.fontScale}|${breakpointTier(state.windowWidth)}`\n}\n\n/**\n * Memoised {@link stateSignature} — one `WeakMap.get` on the hot path\n * instead of rebuilding the template string every resolve.\n * @param state Rnwind context.\n * @returns Cached compact signature.\n */\nfunction stateSignatureCached(state: RnwindState): string {\n let signature = stateSignatureCache.get(state)\n if (signature === undefined) {\n signature = stateSignature(state)\n stateSignatureCache.set(state, signature)\n }\n return signature\n}\n\n/**\n * Compact signature of the live interactive state for the cache key.\n * @param interactState Active/focus flags, or undefined for the plain path.\n * @returns Two-bit signature (`''` when no interactive state).\n */\nfunction interactSignature(interactState?: InteractState): string {\n if (!interactState) return ''\n const active = interactState.active ? 1 : 0\n const focus = interactState.focus ? 1 : 0\n return `${active}${focus}`\n}\n\n/**\n * Whether a token is a feature-ONLY utility (gradient stop/direction,\n * haptic request, or text-truncate) that contributes NO RN `style`. These\n * are folded in via {@link attachFeatures}, so they must be kept OUT of\n * the `lookupCss` input — otherwise the atom resolver treats them as\n * unknown style atoms and emits a spurious \"unknown class\" dev warning\n * (e.g. for `active:haptic-rigid`).\n * @param token Atom name.\n * @returns True when the token carries no style.\n */\nfunction isFeatureOnlyToken(token: string): boolean {\n return Boolean(gradients[token]) || Boolean(haptics[token]) || truncateForToken(token) !== null\n}\n\n/**\n * Lifecycle trigger for a haptic atom from its variant prefix.\n * @param token Atom name (maybe `active:`/`focus:`/`hover:` prefixed).\n * @returns The trigger.\n */\nfunction hapticTriggerForToken(token: string): HapticTrigger {\n const colon = token.indexOf(':')\n if (colon === -1) return 'mount'\n const prefix = token.slice(0, colon)\n if (prefix === 'active') return 'pressIn'\n if (prefix === 'focus') return 'focus'\n if (prefix === 'hover') return 'hover'\n return 'mount'\n}\n\n/**\n * Syntactic text-truncate directive for one atom.\n * @param token Atom name.\n * @returns Partial truncate props, or null.\n */\nfunction truncateForToken(token: string): { numberOfLines?: number; ellipsizeMode?: 'tail' | 'clip' } | null {\n if (token === 'truncate') return { numberOfLines: 1, ellipsizeMode: 'tail' }\n if (token === 'text-ellipsis') return { ellipsizeMode: 'tail' }\n if (token === 'text-clip') return { ellipsizeMode: 'clip' }\n if (token === 'line-clamp-none') return { numberOfLines: 0 }\n if (token.startsWith('line-clamp-')) {\n const count = Number(token.slice('line-clamp-'.length))\n if (Number.isInteger(count) && count >= 0) return { numberOfLines: count }\n }\n return null\n}\n\n/**\n * Assemble gradient props from gradient roles present in the atom list.\n * @param tokens Atom names.\n * @returns `{colors, start, end}` or null when not a complete gradient.\n */\nfunction assembleGradient(tokens: readonly string[]): { colors: string[]; start: GradientPoint; end: GradientPoint } | null {\n let from: string | undefined\n let via: string | undefined\n let to: string | undefined\n let dir: GradientDirection | undefined\n for (const token of tokens) {\n const info = gradients[token]\n if (!info) continue\n switch (info.role) {\n case 'from': {\n from = info.color\n break\n }\n case 'via': {\n via = info.color\n break\n }\n case 'to': {\n to = info.color\n break\n }\n default: {\n ;({ dir } = info)\n }\n }\n }\n if (dir === undefined) return null\n const colors = [from, via, to].filter((color): color is string => color !== undefined)\n if (colors.length < 2) return null\n const points = DIRECTION_POINTS[dir]\n return { colors, start: points.start, end: points.end }\n}\n\n/**\n * Fold every truncate directive across the atom list into one result —\n * last token wins per prop (matches Tailwind last-wins).\n * @param tokens Atom names.\n * @returns Merged truncate props (empty when none apply).\n */\nfunction collectTruncate(tokens: readonly string[]): { numberOfLines?: number; ellipsizeMode?: 'tail' | 'clip' } {\n const out: { numberOfLines?: number; ellipsizeMode?: 'tail' | 'clip' } = {}\n for (const token of tokens) {\n const truncate = truncateForToken(token)\n if (!truncate) continue\n if (truncate.numberOfLines !== undefined) out.numberOfLines = truncate.numberOfLines\n if (truncate.ellipsizeMode !== undefined) out.ellipsizeMode = truncate.ellipsizeMode\n }\n return out\n}\n\n/**\n * Collect every haptic request present in the atom list, tagged with the\n * lifecycle trigger its variant prefix implies.\n * @param tokens Atom names.\n * @returns Haptic request list, or undefined when none apply.\n */\nfunction collectHaptics(tokens: readonly string[]): { request: HapticRequest; trigger: HapticTrigger }[] | undefined {\n let collected: { request: HapticRequest; trigger: HapticTrigger }[] | undefined\n for (const token of tokens) {\n const request = haptics[token]\n if (!request) continue\n collected ??= []\n collected.push({ request, trigger: hapticTriggerForToken(token) })\n }\n return collected\n}\n\n/**\n * Scan tokens for the className-derived feature props (gradient,\n * truncate, haptics) and fold them onto the base result.\n * @param base Result carrying the resolved `style`.\n * @param tokens Atom names.\n * @returns The result with any feature props attached.\n */\nfunction attachFeatures(base: ResolvedCss, tokens: readonly string[]): ResolvedCss {\n const { numberOfLines, ellipsizeMode } = collectTruncate(tokens)\n const collected = collectHaptics(tokens)\n const gradient = assembleGradient(tokens)\n const result: Mutable<ResolvedCss> = { style: base.style }\n if (gradient) {\n result.colors = gradient.colors\n result.start = gradient.start\n result.end = gradient.end\n }\n // `numberOfLines: 0` is kept (RN reads it as \"unlimited\"): `line-clamp-none`\n // must be able to explicitly reset an earlier `line-clamp-N` on the same\n // element — dropping the 0 would silently leave the prior limit in place.\n if (numberOfLines !== undefined) {\n result.numberOfLines = numberOfLines\n if (ellipsizeMode !== undefined) result.ellipsizeMode = ellipsizeMode\n }\n if (collected) result.haptics = collected\n return result\n}\n\n/**\n * Flatten the per-atom style array into ONE object — a *runtime molecule*.\n * RN flattens a style array left-to-right (later wins), which is exactly\n * `Object.assign` semantics, so the merged object renders identically\n * while giving dynamic (cva / clsx) classNames the same single-object\n * shape a build-time molecule has. The caller caches the result per\n * `(className · state)`, so the merge runs once per unique context.\n * @param array Per-atom style array from `lookupCss`.\n * @returns Single merged style object.\n */\nfunction mergeStyleArray(array: readonly unknown[]): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const entry of array) if (entry && typeof entry === 'object') Object.assign(out, entry)\n return out\n}\n\n/**\n * Compose a resolved style with a caller-supplied inline style (user wins).\n * @param style\n * @param userStyle\n */\nfunction withUserStyle(style: unknown, userStyle: unknown): unknown {\n return Array.isArray(style) ? [...style, userStyle] : [style, userStyle]\n}\n\n/**\n * Resolve a className against the reactive context into a style plus any\n * className-derived props. Molecule-first (one lookup, by reference),\n * atom-fallback for unseen / context-dependent strings, cached per\n * `(className, state)`.\n * @param className Raw className string.\n * @param state Rnwind context from `useRnwind()`.\n * @param userStyle Optional inline style appended last (wins).\n * @param interactState Live active/focus flags (for `active:`/`focus:` atoms).\n * @returns The resolved style + feature props.\n */\nexport function resolve(\n className: string | null | undefined,\n state: RnwindState,\n userStyle?: unknown,\n interactState?: InteractState,\n): ResolvedCss {\n const version = getStyleVersion() + registryVersion\n if (version !== cachedFor) {\n resolvedCache.clear()\n cachedFor = version\n }\n if (className == null) {\n return { style: userStyle === undefined || userStyle === null ? EMPTY : [userStyle] }\n }\n // Key on the RAW className so the hot (cache-hit) path skips normalize\n // entirely — normalization only runs on a miss. The state signature is\n // memoised per state object, so the hit path is one WeakMap.get + one\n // string concat + one Map.get.\n const key = `${className}@${stateSignatureCached(state)}@${interactSignature(interactState)}`\n const cached = resolvedCache.get(key)\n if (cached !== undefined) {\n return userStyle === undefined || userStyle === null ? cached : { ...cached, style: withUserStyle(cached.style, userStyle) }\n }\n const normalized = normalizeClassName(className)\n if (normalized.length === 0) {\n const empty: ResolvedCss = { style: EMPTY }\n cacheResolved(key, empty)\n return userStyle === undefined || userStyle === null ? empty : { style: [userStyle] }\n }\n // Molecules are static pre-merges; anything carrying `active:`/`focus:`\n // is never registered as one, so the atom path handles interactive state.\n const tokens = normalized.split(' ')\n const molecule = interactState ? undefined : molecules[state.scheme]?.[normalized] ?? molecules[COMMON_SCHEME]?.[normalized]\n // Feature-only tokens (gradient / haptic / truncate) carry no style — keep\n // them out of the atom lookup so they don't warn as \"unknown class\". The\n // atom array is merged into ONE object (a runtime molecule) so dynamic\n // (cva / clsx) classNames get the same single-object shape as a build\n // molecule; the cache below pins the context, so the merge is correct.\n const style =\n molecule === undefined\n ? mergeStyleArray(lookupCss(tokens.filter((token) => !isFeatureOnlyToken(token)).join(' '), state, undefined, interactState))\n : molecule\n const base = attachFeatures({ style }, tokens)\n cacheResolved(key, base)\n return userStyle === undefined || userStyle === null ? base : { ...base, style: withUserStyle(base.style, userStyle) }\n}\n\n/** Local mutable view for building the frozen-shaped result. */\ntype Mutable<T> = { -readonly [K in keyof T]: T[K] }\n\n/** Test-only — clear the molecule / gradient / haptic registries + cache. */\nexport function __resetResolveState(): void {\n molecules = Object.create(null)\n gradients = Object.create(null)\n haptics = Object.create(null)\n resolvedCache.clear()\n registryVersion += 1\n cachedFor = -1\n}\n\n/** Test-only — current resolved-cache entry count + its hard ceiling. */\nexport function __resolveCacheStats(): { size: number; max: number } {\n return { size: resolvedCache.size, max: MAX_RESOLVED_CACHE }\n}\n\nexport {normalizeClassName} from '../core/normalize-classname'"],"names":["breakpointTier","getStyleVersion","normalizeClassName","lookupCss"],"mappings":"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"}
|
package/lib/cjs/runtime/wrap.cjs
CHANGED
|
@@ -75,8 +75,10 @@ const CLASSNAME_SUFFIX = 'ClassName';
|
|
|
75
75
|
* handled separately by the leaf and never reaches here.
|
|
76
76
|
* @param props Mutable prop object being assembled for the host.
|
|
77
77
|
* @param state Rnwind context for resolution.
|
|
78
|
+
* @param interactState Live press/focus state so `active:`/`focus:` variants
|
|
79
|
+
* on a secondary class prop resolve too (undefined for non-interactive).
|
|
78
80
|
*/
|
|
79
|
-
function applyContainerClassNames(props, state) {
|
|
81
|
+
function applyContainerClassNames(props, state, interactState) {
|
|
80
82
|
for (const key of Object.keys(props)) {
|
|
81
83
|
if (!key.endsWith(CLASSNAME_SUFFIX))
|
|
82
84
|
continue;
|
|
@@ -84,7 +86,7 @@ function applyContainerClassNames(props, state) {
|
|
|
84
86
|
if (typeof value !== 'string')
|
|
85
87
|
continue;
|
|
86
88
|
const styleKey = `${key.slice(0, -CLASSNAME_SUFFIX.length)}Style`;
|
|
87
|
-
props[styleKey] = resolve.resolve(value, state, props[styleKey]).style;
|
|
89
|
+
props[styleKey] = resolve.resolve(value, state, props[styleKey], interactState).style;
|
|
88
90
|
delete props[key];
|
|
89
91
|
}
|
|
90
92
|
}
|
|
@@ -99,12 +101,13 @@ function applyContainerClassNames(props, state) {
|
|
|
99
101
|
* @param state Rnwind context — used to resolve secondary class props.
|
|
100
102
|
* @param onHaptics Dispatcher from context.
|
|
101
103
|
* @param userOnPressIn Caller-supplied onPressIn to chain after the haptic.
|
|
104
|
+
* @param interactState Live press/focus state for secondary class props.
|
|
102
105
|
* @returns The merged prop object for `createElement`.
|
|
103
106
|
*/
|
|
104
|
-
function buildProps(rest, resolved, state, onHaptics, userOnPressIn) {
|
|
107
|
+
function buildProps(rest, resolved, state, onHaptics, userOnPressIn, interactState) {
|
|
105
108
|
warnIfHapticsUnwired(onHaptics, resolved.haptics);
|
|
106
109
|
const props = { ...rest, style: resolved.style };
|
|
107
|
-
applyContainerClassNames(props, state);
|
|
110
|
+
applyContainerClassNames(props, state, interactState);
|
|
108
111
|
if (resolved.colors) {
|
|
109
112
|
props.colors = resolved.colors;
|
|
110
113
|
props.start = resolved.start;
|
|
@@ -129,56 +132,20 @@ function buildProps(rest, resolved, state, onHaptics, userOnPressIn) {
|
|
|
129
132
|
}
|
|
130
133
|
return props;
|
|
131
134
|
}
|
|
132
|
-
/**
|
|
133
|
-
* Non-interactive leaf: resolve className → style (+ features) and
|
|
134
|
-
* forward. One context read, one molecule/atom resolve.
|
|
135
|
-
* @param props Leaf props.
|
|
136
|
-
* @param props.as
|
|
137
|
-
* @param props.className
|
|
138
|
-
* @param props.style
|
|
139
|
-
* @param props.onPressIn
|
|
140
|
-
* @returns The rendered `as` element.
|
|
141
|
-
*/
|
|
142
|
-
function PlainLeaf({ as: As, className, style, onPressIn, ...rest }) {
|
|
143
|
-
const state = rnwindProvider.useRnwind();
|
|
144
|
-
const resolved = resolve.resolve(className, state, style);
|
|
145
|
-
useMountHaptics(resolved, state.onHaptics);
|
|
146
|
-
return React.createElement(As, buildProps(rest, resolved, state, state.onHaptics, onPressIn));
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Interactive leaf: tracks press/focus via `useInteract()`, feeds it into
|
|
150
|
-
* `resolve` so `active:`/`focus:` atoms apply, and chains the
|
|
151
|
-
* press/focus handlers.
|
|
152
|
-
* @param props Leaf props.
|
|
153
|
-
* @param props.as
|
|
154
|
-
* @param props.className
|
|
155
|
-
* @param props.style
|
|
156
|
-
* @param props.onPressIn
|
|
157
|
-
* @param props.onPressOut
|
|
158
|
-
* @param props.onFocus
|
|
159
|
-
* @param props.onBlur
|
|
160
|
-
* @returns The rendered `as` element with interactive wiring.
|
|
161
|
-
*/
|
|
162
|
-
function InteractiveLeaf({ as: As, className, style, onPressIn, onPressOut, onFocus, onBlur, ...rest }) {
|
|
163
|
-
const state = rnwindProvider.useRnwind();
|
|
164
|
-
const interact = useInteract.useInteract();
|
|
165
|
-
const resolved = resolve.resolve(className, state, style, interact.state);
|
|
166
|
-
useMountHaptics(resolved, state.onHaptics);
|
|
167
|
-
const props = buildProps(rest, resolved, state, state.onHaptics, onPressIn);
|
|
168
|
-
props.onPressIn = chainHandlers.chainPress(props.onPressIn, interact.onPressIn);
|
|
169
|
-
props.onPressOut = chainHandlers.chainPress(onPressOut, interact.onPressOut);
|
|
170
|
-
props.onFocus = chainHandlers.chainFocus(onFocus, interact.onFocus);
|
|
171
|
-
props.onBlur = chainHandlers.chainFocus(onBlur, interact.onBlur);
|
|
172
|
-
return React.createElement(As, props);
|
|
173
|
-
}
|
|
174
135
|
/**
|
|
175
136
|
* Wrap a component so its `className` prop resolves to RN `style` (plus
|
|
176
137
|
* gradient / truncate props and haptic dispatch) at render — no matter
|
|
177
138
|
* how className arrived: written directly, spread through `{...rest}`, or
|
|
178
|
-
* forwarded down custom wrappers.
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
139
|
+
* forwarded down custom wrappers. `ref` (a normal prop in React 19) and all
|
|
140
|
+
* other props forward untouched.
|
|
141
|
+
*
|
|
142
|
+
* A SINGLE stable component does the work and always calls `useInteract()`,
|
|
143
|
+
* so its identity never changes when `className` flips between interactive
|
|
144
|
+
* (`active:`/`focus:`) and not — swapping the rendered component type would
|
|
145
|
+
* unmount + remount the whole host subtree (lost state, effect re-runs,
|
|
146
|
+
* visual flash). `useInteract` is cheap and shares one idle-state ref, so the
|
|
147
|
+
* always-on cost is a couple of `useState` cells; the press/focus handlers
|
|
148
|
+
* and live state only wire in when the className actually needs them.
|
|
182
149
|
* @example
|
|
183
150
|
* ```tsx
|
|
184
151
|
* const Pressable = wrap(RNPressable)
|
|
@@ -190,16 +157,42 @@ function InteractiveLeaf({ as: As, className, style, onPressIn, onPressOut, onFo
|
|
|
190
157
|
function wrap(Component) {
|
|
191
158
|
const as = Component;
|
|
192
159
|
/**
|
|
193
|
-
* The wrapped component
|
|
160
|
+
* The wrapped component. Stable identity + unconditional hooks; branches
|
|
161
|
+
* internally on whether the className carries an interactive variant.
|
|
194
162
|
* @param props Forwarded props with `className` intercepted.
|
|
195
|
-
* @param props.className
|
|
196
|
-
* @
|
|
163
|
+
* @param props.className Raw className string.
|
|
164
|
+
* @param props.style Caller-supplied style, merged under the resolved style.
|
|
165
|
+
* @param props.onPressIn Caller onPressIn — chained after haptics / interact.
|
|
166
|
+
* @param props.onPressOut Caller onPressOut — chained with interact when active.
|
|
167
|
+
* @param props.onFocus Caller onFocus — chained with interact when active.
|
|
168
|
+
* @param props.onBlur Caller onBlur — chained with interact when active.
|
|
169
|
+
* @returns The rendered `as` element.
|
|
197
170
|
*/
|
|
198
|
-
function RnwindWrapped({ className, ...rest }) {
|
|
199
|
-
|
|
200
|
-
|
|
171
|
+
function RnwindWrapped({ className, style, onPressIn, onPressOut, onFocus, onBlur, ...rest }) {
|
|
172
|
+
const state = rnwindProvider.useRnwind();
|
|
173
|
+
const interact = useInteract.useInteract();
|
|
174
|
+
const isInteractive = className !== undefined && hasInteractiveVariant(className);
|
|
175
|
+
const interactState = isInteractive ? interact.state : undefined;
|
|
176
|
+
const resolved = resolve.resolve(className, state, style, interactState);
|
|
177
|
+
useMountHaptics(resolved, state.onHaptics);
|
|
178
|
+
const props = buildProps(rest, resolved, state, state.onHaptics, onPressIn, interactState);
|
|
179
|
+
if (isInteractive) {
|
|
180
|
+
props.onPressIn = chainHandlers.chainPress(props.onPressIn, interact.onPressIn);
|
|
181
|
+
props.onPressOut = chainHandlers.chainPress(onPressOut, interact.onPressOut);
|
|
182
|
+
props.onFocus = chainHandlers.chainFocus(onFocus, interact.onFocus);
|
|
183
|
+
props.onBlur = chainHandlers.chainFocus(onBlur, interact.onBlur);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
// Forward the caller's press/focus handlers untouched (onPressIn is
|
|
187
|
+
// already set by buildProps, possibly haptic-chained).
|
|
188
|
+
if (onPressOut !== undefined)
|
|
189
|
+
props.onPressOut = onPressOut;
|
|
190
|
+
if (onFocus !== undefined)
|
|
191
|
+
props.onFocus = onFocus;
|
|
192
|
+
if (onBlur !== undefined)
|
|
193
|
+
props.onBlur = onBlur;
|
|
201
194
|
}
|
|
202
|
-
return React.createElement(
|
|
195
|
+
return React.createElement(as, props);
|
|
203
196
|
}
|
|
204
197
|
RnwindWrapped.displayName = `wrap(${displayNameOf(Component)})`;
|
|
205
198
|
return RnwindWrapped;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrap.cjs","sources":["../../../../src/runtime/wrap.tsx"],"sourcesContent":["import { createElement, useEffect, useRef, type ComponentType, type ReactElement } from 'react'\nimport { chainFocus, chainPress } from './chain-handlers'\nimport { useInteract } from './hooks/use-interact'\nimport { useRnwind } from './components/rnwind-provider'\nimport type { RnwindState } from './components/rnwind-provider'\nimport { resolve, type ResolvedCss } from './resolve'\nimport type { OnHaptics } from '../core/parser/haptics'\n\n/** Matches a leading `active:` / `focus:` variant token (`\\b` excludes `inactive:`). */\nconst INTERACTIVE_VARIANT = /\\b(?:active|focus):/\n\n/** One-shot guard so the missing-`onHaptics` warning logs once per session. */\nlet warnedMissingOnHaptics = false\n\n/**\n * Dev-only warning when a className carries a haptic utility but no\n * `onHaptics` dispatcher is wired on the nearest `<RnwindProvider>` — the\n * haptic would silently drop otherwise. Fires once per session.\n * @param onHaptics The dispatcher from context (or undefined).\n * @param haptics The resolved haptic requests (or undefined).\n */\nfunction warnIfHapticsUnwired(onHaptics: OnHaptics | undefined, haptics: ResolvedCss['haptics']): void {\n if (onHaptics || !haptics || haptics.length === 0) return\n const isDevelopment = typeof __DEV__ === 'undefined' || __DEV__\n if (!isDevelopment || warnedMissingOnHaptics) return\n warnedMissingOnHaptics = true\n // eslint-disable-next-line no-console\n console.warn(\n 'rnwind: a `haptic-*` utility resolved but no `onHaptics` callback is wired on <RnwindProvider>. ' +\n 'Pass `onHaptics` on the provider to forward the request to expo-haptics (or any library).',\n )\n}\n\n/**\n * Whether a className needs press/focus state tracking.\n * @param className Raw className string.\n * @returns True when an `active:` / `focus:` variant is present.\n */\nfunction hasInteractiveVariant(className: string): boolean {\n return INTERACTIVE_VARIANT.test(className)\n}\n\n/**\n * Best-effort display name for the wrapped component.\n * @param component Component being wrapped.\n * @returns Its `displayName`, `name`, or `'Component'`.\n */\nfunction displayNameOf(component: unknown): string {\n const named = component as { displayName?: string; name?: string }\n return named.displayName ?? named.name ?? 'Component'\n}\n\n/**\n * Fire the `mount`-trigger haptics once, after the element mounts. Snaps\n * the resolved requests + dispatcher at mount via a `useRef` initializer\n * (evaluated only on the first render), so an unstable inline `onHaptics`\n * doesn't re-fire them and no ref is written during render.\n * @param resolved The resolved className (carries any haptic requests).\n * @param onHaptics The dispatcher from context (or undefined).\n */\nfunction useMountHaptics(resolved: ResolvedCss, onHaptics: OnHaptics | undefined): void {\n const mount = useRef({ resolved, onHaptics })\n useEffect(() => {\n const { resolved: current, onHaptics: dispatch } = mount.current\n if (!dispatch || !current.haptics) return\n for (const entry of current.haptics) if (entry.trigger === 'mount') dispatch(entry.request, 'mount')\n }, [])\n}\n\n/** Suffix marking a secondary class-prop (`contentContainerClassName`, …). */\nconst CLASSNAME_SUFFIX = 'ClassName'\n\n/**\n * Resolve every secondary `<prefix>ClassName` prop (e.g.\n * `contentContainerClassName` on a ScrollView / FlatList) into its\n * matching `<prefix>Style`, in place. Any existing `<prefix>Style` is\n * appended last (caller wins). The original `*ClassName` prop is deleted\n * so RN never sees an unknown attribute. The primary `className` is\n * handled separately by the leaf and never reaches here.\n * @param props Mutable prop object being assembled for the host.\n * @param state Rnwind context for resolution.\n */\nfunction applyContainerClassNames(props: Record<string, unknown>, state: RnwindState): void {\n for (const key of Object.keys(props)) {\n if (!key.endsWith(CLASSNAME_SUFFIX)) continue\n const value = props[key]\n if (typeof value !== 'string') continue\n const styleKey = `${key.slice(0, -CLASSNAME_SUFFIX.length)}Style`\n props[styleKey] = resolve(value, state, props[styleKey]).style\n delete props[key]\n }\n}\n\n/**\n * Build the props for the wrapped host: resolved `style`, gradient\n * (`colors`/`start`/`end`), truncate (`numberOfLines`/`ellipsizeMode`),\n * secondary `<prefix>ClassName` → `<prefix>Style`, and a chained\n * `onPressIn` that fires press-trigger haptics. Unknown props on a host\n * are simply ignored by RN, so this stays generic.\n * @param rest Forwarded props (incl. `ref`).\n * @param resolved Resolved className result.\n * @param state Rnwind context — used to resolve secondary class props.\n * @param onHaptics Dispatcher from context.\n * @param userOnPressIn Caller-supplied onPressIn to chain after the haptic.\n * @returns The merged prop object for `createElement`.\n */\nfunction buildProps(\n rest: Record<string, unknown>,\n resolved: ResolvedCss,\n state: RnwindState,\n onHaptics: OnHaptics | undefined,\n userOnPressIn?: unknown,\n): Record<string, unknown> {\n warnIfHapticsUnwired(onHaptics, resolved.haptics)\n const props: Record<string, unknown> = { ...rest, style: resolved.style }\n applyContainerClassNames(props, state)\n if (resolved.colors) {\n props.colors = resolved.colors\n props.start = resolved.start\n props.end = resolved.end\n }\n if (resolved.numberOfLines !== undefined) {\n props.numberOfLines = resolved.numberOfLines\n if (resolved.ellipsizeMode !== undefined) props.ellipsizeMode = resolved.ellipsizeMode\n }\n const pressHaptics = onHaptics && resolved.haptics?.filter((entry) => entry.trigger === 'pressIn')\n if (pressHaptics && pressHaptics.length > 0) {\n const previous = userOnPressIn as ((event: unknown) => void) | undefined\n props.onPressIn = (event: unknown): void => {\n for (const entry of pressHaptics) onHaptics(entry.request, 'pressIn')\n previous?.(event)\n }\n } else if (userOnPressIn !== undefined) {\n props.onPressIn = userOnPressIn\n }\n return props\n}\n\n/** Props a leaf receives — the wrapped `as` tag plus forwarded props. */\ninterface LeafProps {\n readonly as: ComponentType<Record<string, unknown>>\n readonly className?: string\n readonly style?: unknown\n readonly [key: string]: unknown\n}\n\n/**\n * Non-interactive leaf: resolve className → style (+ features) and\n * forward. One context read, one molecule/atom resolve.\n * @param props Leaf props.\n * @param props.as\n * @param props.className\n * @param props.style\n * @param props.onPressIn\n * @returns The rendered `as` element.\n */\nfunction PlainLeaf({ as: As, className, style, onPressIn, ...rest }: LeafProps): ReactElement {\n const state = useRnwind()\n const resolved = resolve(className, state, style)\n useMountHaptics(resolved, state.onHaptics)\n return createElement(As, buildProps(rest, resolved, state, state.onHaptics, onPressIn))\n}\n\n/**\n * Interactive leaf: tracks press/focus via `useInteract()`, feeds it into\n * `resolve` so `active:`/`focus:` atoms apply, and chains the\n * press/focus handlers.\n * @param props Leaf props.\n * @param props.as\n * @param props.className\n * @param props.style\n * @param props.onPressIn\n * @param props.onPressOut\n * @param props.onFocus\n * @param props.onBlur\n * @returns The rendered `as` element with interactive wiring.\n */\nfunction InteractiveLeaf({ as: As, className, style, onPressIn, onPressOut, onFocus, onBlur, ...rest }: LeafProps): ReactElement {\n const state = useRnwind()\n const interact = useInteract()\n const resolved = resolve(className, state, style, interact.state)\n useMountHaptics(resolved, state.onHaptics)\n const props = buildProps(rest, resolved, state, state.onHaptics, onPressIn)\n props.onPressIn = chainPress(props.onPressIn as Parameters<typeof chainPress>[0], interact.onPressIn)\n props.onPressOut = chainPress(onPressOut as Parameters<typeof chainPress>[0], interact.onPressOut)\n props.onFocus = chainFocus(onFocus as Parameters<typeof chainFocus>[0], interact.onFocus)\n props.onBlur = chainFocus(onBlur as Parameters<typeof chainFocus>[0], interact.onBlur)\n return createElement(As, props)\n}\n\n/**\n * Wrap a component so its `className` prop resolves to RN `style` (plus\n * gradient / truncate props and haptic dispatch) at render — no matter\n * how className arrived: written directly, spread through `{...rest}`, or\n * forwarded down custom wrappers. The returned component is hook-free; it\n * dispatches to a plain or interactive leaf so non-interactive elements\n * never pay for press/focus state. `ref` (a normal prop in React 19) and\n * all other props forward untouched.\n * @example\n * ```tsx\n * const Pressable = wrap(RNPressable)\n * <Pressable className=\"active:bg-sky-700 px-4 haptic-light\" onPress={fn} />\n * ```\n * @param Component Any component accepting a `style` prop.\n * @returns A component accepting `className`.\n */\nexport function wrap<P>(Component: ComponentType<P>): ComponentType<P & { className?: string }> {\n const as = Component as unknown as ComponentType<Record<string, unknown>>\n /**\n * The wrapped component — hook-free dispatcher to a leaf.\n * @param props Forwarded props with `className` intercepted.\n * @param props.className\n * @returns The rendered leaf.\n */\n function RnwindWrapped({ className, ...rest }: { className?: string; [key: string]: unknown }): ReactElement {\n if (className !== undefined && hasInteractiveVariant(className)) {\n return createElement(InteractiveLeaf, { as, className, ...rest })\n }\n return createElement(PlainLeaf, { as, className, ...rest })\n }\n RnwindWrapped.displayName = `wrap(${displayNameOf(Component)})`\n return RnwindWrapped as unknown as ComponentType<P & { className?: string }>\n}\n\n/**\n * Whether a namespace member name denotes a component to wrap —\n * PascalCase and not a React context (`*Context`). Lowercase utilities /\n * hooks (`createAnimatedComponent`, `spring`) pass through untouched.\n * @param name Member key.\n * @returns True when the member should be `wrap()`-ed.\n */\nfunction isComponentMember(name: string): boolean {\n return /^[A-Z]/.test(name) && !name.endsWith('Context')\n}\n\n/**\n * Wrap a component NAMESPACE (a default/namespace import like reanimated's\n * `Animated`) so member access — `Animated.View`, `Animated.ScrollView` —\n * returns a `wrap()`-ed component whose `className` resolves at render.\n * Returns a Proxy: component members are wrapped lazily and memoised so\n * each access yields the SAME wrapped component (stable identity — React\n * would remount otherwise). Non-component members (`createAnimatedComponent`,\n * config objects) pass straight through.\n * @example\n * ```tsx\n * const Animated = wrapNamespace(RNReanimated)\n * <Animated.View className=\"enter-fade\" />\n * ```\n * @param namespace The imported namespace object.\n * @returns A Proxy that wraps component members on access.\n */\nexport function wrapNamespace<T extends object>(namespace: T): T {\n const cache = new Map<string, unknown>()\n return new Proxy(namespace, {\n get(target, key, receiver): unknown {\n const value = Reflect.get(target, key, receiver)\n if (typeof key !== 'string' || !isComponentMember(key)) return value\n if (!value || (typeof value !== 'function' && typeof value !== 'object')) return value\n let wrapped = cache.get(key)\n if (wrapped === undefined) {\n wrapped = wrap(value as ComponentType<unknown>)\n cache.set(key, wrapped)\n }\n return wrapped\n },\n })\n}\n"],"names":["useRef","useEffect","resolve","useRnwind","createElement","useInteract","chainPress","chainFocus"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"wrap.cjs","sources":["../../../../src/runtime/wrap.tsx"],"sourcesContent":["import { createElement, useEffect, useRef, type ComponentType, type ReactElement } from 'react'\nimport { chainFocus, chainPress } from './chain-handlers'\nimport { useInteract } from './hooks/use-interact'\nimport { useRnwind } from './components/rnwind-provider'\nimport type { RnwindState } from './components/rnwind-provider'\nimport type { InteractState } from './lookup-css'\nimport { resolve, type ResolvedCss } from './resolve'\nimport type { OnHaptics } from '../core/parser/haptics'\n\n/** Matches a leading `active:` / `focus:` variant token (`\\b` excludes `inactive:`). */\nconst INTERACTIVE_VARIANT = /\\b(?:active|focus):/\n\n/** One-shot guard so the missing-`onHaptics` warning logs once per session. */\nlet warnedMissingOnHaptics = false\n\n/**\n * Dev-only warning when a className carries a haptic utility but no\n * `onHaptics` dispatcher is wired on the nearest `<RnwindProvider>` — the\n * haptic would silently drop otherwise. Fires once per session.\n * @param onHaptics The dispatcher from context (or undefined).\n * @param haptics The resolved haptic requests (or undefined).\n */\nfunction warnIfHapticsUnwired(onHaptics: OnHaptics | undefined, haptics: ResolvedCss['haptics']): void {\n if (onHaptics || !haptics || haptics.length === 0) return\n const isDevelopment = typeof __DEV__ === 'undefined' || __DEV__\n if (!isDevelopment || warnedMissingOnHaptics) return\n warnedMissingOnHaptics = true\n // eslint-disable-next-line no-console\n console.warn(\n 'rnwind: a `haptic-*` utility resolved but no `onHaptics` callback is wired on <RnwindProvider>. ' +\n 'Pass `onHaptics` on the provider to forward the request to expo-haptics (or any library).',\n )\n}\n\n/**\n * Whether a className needs press/focus state tracking.\n * @param className Raw className string.\n * @returns True when an `active:` / `focus:` variant is present.\n */\nfunction hasInteractiveVariant(className: string): boolean {\n return INTERACTIVE_VARIANT.test(className)\n}\n\n/**\n * Best-effort display name for the wrapped component.\n * @param component Component being wrapped.\n * @returns Its `displayName`, `name`, or `'Component'`.\n */\nfunction displayNameOf(component: unknown): string {\n const named = component as { displayName?: string; name?: string }\n return named.displayName ?? named.name ?? 'Component'\n}\n\n/**\n * Fire the `mount`-trigger haptics once, after the element mounts. Snaps\n * the resolved requests + dispatcher at mount via a `useRef` initializer\n * (evaluated only on the first render), so an unstable inline `onHaptics`\n * doesn't re-fire them and no ref is written during render.\n * @param resolved The resolved className (carries any haptic requests).\n * @param onHaptics The dispatcher from context (or undefined).\n */\nfunction useMountHaptics(resolved: ResolvedCss, onHaptics: OnHaptics | undefined): void {\n const mount = useRef({ resolved, onHaptics })\n useEffect(() => {\n const { resolved: current, onHaptics: dispatch } = mount.current\n if (!dispatch || !current.haptics) return\n for (const entry of current.haptics) if (entry.trigger === 'mount') dispatch(entry.request, 'mount')\n }, [])\n}\n\n/** Suffix marking a secondary class-prop (`contentContainerClassName`, …). */\nconst CLASSNAME_SUFFIX = 'ClassName'\n\n/**\n * Resolve every secondary `<prefix>ClassName` prop (e.g.\n * `contentContainerClassName` on a ScrollView / FlatList) into its\n * matching `<prefix>Style`, in place. Any existing `<prefix>Style` is\n * appended last (caller wins). The original `*ClassName` prop is deleted\n * so RN never sees an unknown attribute. The primary `className` is\n * handled separately by the leaf and never reaches here.\n * @param props Mutable prop object being assembled for the host.\n * @param state Rnwind context for resolution.\n * @param interactState Live press/focus state so `active:`/`focus:` variants\n * on a secondary class prop resolve too (undefined for non-interactive).\n */\nfunction applyContainerClassNames(\n props: Record<string, unknown>,\n state: RnwindState,\n interactState?: InteractState,\n): void {\n for (const key of Object.keys(props)) {\n if (!key.endsWith(CLASSNAME_SUFFIX)) continue\n const value = props[key]\n if (typeof value !== 'string') continue\n const styleKey = `${key.slice(0, -CLASSNAME_SUFFIX.length)}Style`\n props[styleKey] = resolve(value, state, props[styleKey], interactState).style\n delete props[key]\n }\n}\n\n/**\n * Build the props for the wrapped host: resolved `style`, gradient\n * (`colors`/`start`/`end`), truncate (`numberOfLines`/`ellipsizeMode`),\n * secondary `<prefix>ClassName` → `<prefix>Style`, and a chained\n * `onPressIn` that fires press-trigger haptics. Unknown props on a host\n * are simply ignored by RN, so this stays generic.\n * @param rest Forwarded props (incl. `ref`).\n * @param resolved Resolved className result.\n * @param state Rnwind context — used to resolve secondary class props.\n * @param onHaptics Dispatcher from context.\n * @param userOnPressIn Caller-supplied onPressIn to chain after the haptic.\n * @param interactState Live press/focus state for secondary class props.\n * @returns The merged prop object for `createElement`.\n */\nfunction buildProps(\n rest: Record<string, unknown>,\n resolved: ResolvedCss,\n state: RnwindState,\n onHaptics: OnHaptics | undefined,\n userOnPressIn?: unknown,\n interactState?: InteractState,\n): Record<string, unknown> {\n warnIfHapticsUnwired(onHaptics, resolved.haptics)\n const props: Record<string, unknown> = { ...rest, style: resolved.style }\n applyContainerClassNames(props, state, interactState)\n if (resolved.colors) {\n props.colors = resolved.colors\n props.start = resolved.start\n props.end = resolved.end\n }\n if (resolved.numberOfLines !== undefined) {\n props.numberOfLines = resolved.numberOfLines\n if (resolved.ellipsizeMode !== undefined) props.ellipsizeMode = resolved.ellipsizeMode\n }\n const pressHaptics = onHaptics && resolved.haptics?.filter((entry) => entry.trigger === 'pressIn')\n if (pressHaptics && pressHaptics.length > 0) {\n const previous = userOnPressIn as ((event: unknown) => void) | undefined\n props.onPressIn = (event: unknown): void => {\n for (const entry of pressHaptics) onHaptics(entry.request, 'pressIn')\n previous?.(event)\n }\n } else if (userOnPressIn !== undefined) {\n props.onPressIn = userOnPressIn\n }\n return props\n}\n\n/** Props the wrapped renderer accepts — `className` plus forwarded props. */\ninterface WrappedProps {\n readonly className?: string\n readonly style?: unknown\n readonly onPressIn?: unknown\n readonly onPressOut?: unknown\n readonly onFocus?: unknown\n readonly onBlur?: unknown\n readonly [key: string]: unknown\n}\n\n/**\n * Wrap a component so its `className` prop resolves to RN `style` (plus\n * gradient / truncate props and haptic dispatch) at render — no matter\n * how className arrived: written directly, spread through `{...rest}`, or\n * forwarded down custom wrappers. `ref` (a normal prop in React 19) and all\n * other props forward untouched.\n *\n * A SINGLE stable component does the work and always calls `useInteract()`,\n * so its identity never changes when `className` flips between interactive\n * (`active:`/`focus:`) and not — swapping the rendered component type would\n * unmount + remount the whole host subtree (lost state, effect re-runs,\n * visual flash). `useInteract` is cheap and shares one idle-state ref, so the\n * always-on cost is a couple of `useState` cells; the press/focus handlers\n * and live state only wire in when the className actually needs them.\n * @example\n * ```tsx\n * const Pressable = wrap(RNPressable)\n * <Pressable className=\"active:bg-sky-700 px-4 haptic-light\" onPress={fn} />\n * ```\n * @param Component Any component accepting a `style` prop.\n * @returns A component accepting `className`.\n */\nexport function wrap<P>(Component: ComponentType<P>): ComponentType<P & { className?: string }> {\n const as = Component as unknown as ComponentType<Record<string, unknown>>\n /**\n * The wrapped component. Stable identity + unconditional hooks; branches\n * internally on whether the className carries an interactive variant.\n * @param props Forwarded props with `className` intercepted.\n * @param props.className Raw className string.\n * @param props.style Caller-supplied style, merged under the resolved style.\n * @param props.onPressIn Caller onPressIn — chained after haptics / interact.\n * @param props.onPressOut Caller onPressOut — chained with interact when active.\n * @param props.onFocus Caller onFocus — chained with interact when active.\n * @param props.onBlur Caller onBlur — chained with interact when active.\n * @returns The rendered `as` element.\n */\n function RnwindWrapped({ className, style, onPressIn, onPressOut, onFocus, onBlur, ...rest }: WrappedProps): ReactElement {\n const state = useRnwind()\n const interact = useInteract()\n const isInteractive = className !== undefined && hasInteractiveVariant(className)\n const interactState = isInteractive ? interact.state : undefined\n const resolved = resolve(className, state, style, interactState)\n useMountHaptics(resolved, state.onHaptics)\n const props = buildProps(rest, resolved, state, state.onHaptics, onPressIn, interactState)\n if (isInteractive) {\n props.onPressIn = chainPress(props.onPressIn as Parameters<typeof chainPress>[0], interact.onPressIn)\n props.onPressOut = chainPress(onPressOut as Parameters<typeof chainPress>[0], interact.onPressOut)\n props.onFocus = chainFocus(onFocus as Parameters<typeof chainFocus>[0], interact.onFocus)\n props.onBlur = chainFocus(onBlur as Parameters<typeof chainFocus>[0], interact.onBlur)\n } else {\n // Forward the caller's press/focus handlers untouched (onPressIn is\n // already set by buildProps, possibly haptic-chained).\n if (onPressOut !== undefined) props.onPressOut = onPressOut\n if (onFocus !== undefined) props.onFocus = onFocus\n if (onBlur !== undefined) props.onBlur = onBlur\n }\n return createElement(as, props)\n }\n RnwindWrapped.displayName = `wrap(${displayNameOf(Component)})`\n return RnwindWrapped as unknown as ComponentType<P & { className?: string }>\n}\n\n/**\n * Whether a namespace member name denotes a component to wrap —\n * PascalCase and not a React context (`*Context`). Lowercase utilities /\n * hooks (`createAnimatedComponent`, `spring`) pass through untouched.\n * @param name Member key.\n * @returns True when the member should be `wrap()`-ed.\n */\nfunction isComponentMember(name: string): boolean {\n return /^[A-Z]/.test(name) && !name.endsWith('Context')\n}\n\n/**\n * Wrap a component NAMESPACE (a default/namespace import like reanimated's\n * `Animated`) so member access — `Animated.View`, `Animated.ScrollView` —\n * returns a `wrap()`-ed component whose `className` resolves at render.\n * Returns a Proxy: component members are wrapped lazily and memoised so\n * each access yields the SAME wrapped component (stable identity — React\n * would remount otherwise). Non-component members (`createAnimatedComponent`,\n * config objects) pass straight through.\n * @example\n * ```tsx\n * const Animated = wrapNamespace(RNReanimated)\n * <Animated.View className=\"enter-fade\" />\n * ```\n * @param namespace The imported namespace object.\n * @returns A Proxy that wraps component members on access.\n */\nexport function wrapNamespace<T extends object>(namespace: T): T {\n const cache = new Map<string, unknown>()\n return new Proxy(namespace, {\n get(target, key, receiver): unknown {\n const value = Reflect.get(target, key, receiver)\n if (typeof key !== 'string' || !isComponentMember(key)) return value\n if (!value || (typeof value !== 'function' && typeof value !== 'object')) return value\n let wrapped = cache.get(key)\n if (wrapped === undefined) {\n wrapped = wrap(value as ComponentType<unknown>)\n cache.set(key, wrapped)\n }\n return wrapped\n },\n })\n}\n"],"names":["useRef","useEffect","resolve","useRnwind","useInteract","chainPress","chainFocus","createElement"],"mappings":"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"}
|
|
@@ -3,10 +3,16 @@ import { type ComponentType } from 'react';
|
|
|
3
3
|
* Wrap a component so its `className` prop resolves to RN `style` (plus
|
|
4
4
|
* gradient / truncate props and haptic dispatch) at render — no matter
|
|
5
5
|
* how className arrived: written directly, spread through `{...rest}`, or
|
|
6
|
-
* forwarded down custom wrappers.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* forwarded down custom wrappers. `ref` (a normal prop in React 19) and all
|
|
7
|
+
* other props forward untouched.
|
|
8
|
+
*
|
|
9
|
+
* A SINGLE stable component does the work and always calls `useInteract()`,
|
|
10
|
+
* so its identity never changes when `className` flips between interactive
|
|
11
|
+
* (`active:`/`focus:`) and not — swapping the rendered component type would
|
|
12
|
+
* unmount + remount the whole host subtree (lost state, effect re-runs,
|
|
13
|
+
* visual flash). `useInteract` is cheap and shares one idle-state ref, so the
|
|
14
|
+
* always-on cost is a couple of `useState` cells; the press/focus handlers
|
|
15
|
+
* and live state only wire in when the className actually needs them.
|
|
10
16
|
* @example
|
|
11
17
|
* ```tsx
|
|
12
18
|
* const Pressable = wrap(RNPressable)
|
|
@@ -129,7 +129,7 @@ function evaluateGeneratedFile(filePath) {
|
|
|
129
129
|
// `require` is neutralised: each variant file is evaluated directly,
|
|
130
130
|
// so the manifest's lazy `require('./x.style')` loaders are no-ops.
|
|
131
131
|
// eslint-disable-next-line sonarjs/code-eval
|
|
132
|
-
new Function('StyleSheet', 'registerAtoms', 'registerMolecules', 'registerBreakpoints', 'registerGradients', 'registerHaptics', 'registerSchemeLoader', 'require', body)(BUNDLE_STYLE_SHEET, lookupCss.registerAtoms, resolve.registerMolecules, lookupCss.registerBreakpoints, resolve.registerGradients, resolve.registerHaptics, lookupCss.registerSchemeLoader, () => { });
|
|
132
|
+
new Function('StyleSheet', 'registerAtoms', 'registerMolecules', 'registerBreakpoints', 'registerGradients', 'registerHaptics', 'registerThemeTokens', 'registerSchemeLoader', 'require', body)(BUNDLE_STYLE_SHEET, lookupCss.registerAtoms, resolve.registerMolecules, lookupCss.registerBreakpoints, resolve.registerGradients, resolve.registerHaptics, lookupCss.registerThemeTokens, lookupCss.registerSchemeLoader, () => { });
|
|
133
133
|
}
|
|
134
134
|
/**
|
|
135
135
|
* Sort comparator that floats `common.style.js` to the front, rest alpha.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","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":["createRequire","generateImport","existsSync","readFileSync","registerAtoms","registerMolecules","registerBreakpoints","registerGradients","registerHaptics","registerSchemeLoader","readdirSync","React","rnwindRuntime","RnwindProvider","mkdtempSync","tmpdir","writeFileSync","configureRnwindState","parse","metroTransform","__resetLookupCssState","__resetResolveState","resetRnwindState","rmSync"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","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":["createRequire","generateImport","existsSync","readFileSync","registerAtoms","registerMolecules","registerBreakpoints","registerGradients","registerHaptics","registerThemeTokens","registerSchemeLoader","readdirSync","React","rnwindRuntime","RnwindProvider","mkdtempSync","tmpdir","writeFileSync","configureRnwindState","parse","metroTransform","__resetLookupCssState","__resetResolveState","resetRnwindState","rmSync"],"mappings":"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"}
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import type { CssColor } from 'lightningcss';
|
|
2
|
+
/**
|
|
3
|
+
* Lower a wide-gamut / modern CSS color STRING (`oklch(…)`, `lab(…)`,
|
|
4
|
+
* `color(display-p3 …)`, …) to an sRGB hex/rgba string RN can paint. Returns
|
|
5
|
+
* `null` for anything RN already understands (hex, rgb, hsl, named) so the
|
|
6
|
+
* caller keeps the original text — only the unrepresentable forms convert.
|
|
7
|
+
* Mirrors {@link cssColorToString}'s culori lowering for the string path.
|
|
8
|
+
* @param text Resolved CSS color text (post theme-var substitution).
|
|
9
|
+
* @returns sRGB color string, or `null` when no conversion is needed/possible.
|
|
10
|
+
*/
|
|
11
|
+
export declare function normalizeColorString(text: string): string | null;
|
|
2
12
|
/**
|
|
3
13
|
* Convert a lightningcss `CssColor` to an RN-safe color string. RGB
|
|
4
14
|
* passes through unchanged. LAB / LCH / OKLAB / OKLCH / `color(xyz-…)`
|