rnwind 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (330) hide show
  1. package/lib/cjs/core/parser/animation.cjs +427 -0
  2. package/lib/cjs/core/parser/animation.cjs.map +1 -0
  3. package/lib/cjs/core/parser/animation.d.ts +126 -0
  4. package/lib/cjs/core/parser/border-dispatcher.cjs +180 -0
  5. package/lib/cjs/core/parser/border-dispatcher.cjs.map +1 -0
  6. package/lib/cjs/core/parser/border-dispatcher.d.ts +15 -0
  7. package/lib/cjs/core/parser/case-convert.cjs +15 -0
  8. package/lib/cjs/core/parser/case-convert.cjs.map +1 -0
  9. package/lib/cjs/core/parser/case-convert.d.ts +6 -0
  10. package/lib/cjs/core/parser/color-properties-dispatcher.cjs +84 -0
  11. package/lib/cjs/core/parser/color-properties-dispatcher.cjs.map +1 -0
  12. package/lib/cjs/core/parser/color-properties-dispatcher.d.ts +19 -0
  13. package/lib/cjs/core/parser/color.cjs +193 -0
  14. package/lib/cjs/core/parser/color.cjs.map +1 -0
  15. package/lib/cjs/core/parser/color.d.ts +12 -0
  16. package/lib/cjs/core/parser/constants.cjs +21 -0
  17. package/lib/cjs/core/parser/constants.cjs.map +1 -0
  18. package/lib/cjs/core/parser/constants.d.ts +8 -0
  19. package/lib/cjs/core/parser/declaration.cjs +347 -0
  20. package/lib/cjs/core/parser/declaration.cjs.map +1 -0
  21. package/lib/cjs/core/parser/declaration.d.ts +15 -0
  22. package/lib/cjs/core/parser/gradient.cjs +132 -0
  23. package/lib/cjs/core/parser/gradient.cjs.map +1 -0
  24. package/lib/cjs/core/parser/gradient.d.ts +59 -0
  25. package/lib/cjs/core/parser/haptics.cjs +73 -0
  26. package/lib/cjs/core/parser/haptics.cjs.map +1 -0
  27. package/lib/cjs/core/parser/haptics.d.ts +47 -0
  28. package/lib/cjs/core/parser/index.d.ts +8 -0
  29. package/lib/cjs/core/parser/keyframes.cjs +95 -0
  30. package/lib/cjs/core/parser/keyframes.cjs.map +1 -0
  31. package/lib/cjs/core/parser/keyframes.d.ts +26 -0
  32. package/lib/cjs/core/parser/layout-dispatcher.cjs +120 -0
  33. package/lib/cjs/core/parser/layout-dispatcher.cjs.map +1 -0
  34. package/lib/cjs/core/parser/layout-dispatcher.d.ts +14 -0
  35. package/lib/cjs/core/parser/length.cjs +110 -0
  36. package/lib/cjs/core/parser/length.cjs.map +1 -0
  37. package/lib/cjs/core/parser/length.d.ts +51 -0
  38. package/lib/cjs/core/parser/motion-dispatcher.cjs +77 -0
  39. package/lib/cjs/core/parser/motion-dispatcher.cjs.map +1 -0
  40. package/lib/cjs/core/parser/motion-dispatcher.d.ts +11 -0
  41. package/lib/cjs/core/parser/property.cjs +22 -0
  42. package/lib/cjs/core/parser/property.cjs.map +1 -0
  43. package/lib/cjs/core/parser/property.d.ts +8 -0
  44. package/lib/cjs/core/parser/safe-area.cjs +404 -0
  45. package/lib/cjs/core/parser/safe-area.cjs.map +1 -0
  46. package/lib/cjs/core/parser/safe-area.d.ts +39 -0
  47. package/lib/cjs/core/parser/selector.cjs +22 -0
  48. package/lib/cjs/core/parser/selector.cjs.map +1 -0
  49. package/lib/cjs/core/parser/selector.d.ts +11 -0
  50. package/lib/cjs/core/parser/shorthand.cjs +188 -0
  51. package/lib/cjs/core/parser/shorthand.cjs.map +1 -0
  52. package/lib/cjs/core/parser/shorthand.d.ts +67 -0
  53. package/lib/cjs/core/parser/text-truncate.cjs +78 -0
  54. package/lib/cjs/core/parser/text-truncate.cjs.map +1 -0
  55. package/lib/cjs/core/parser/text-truncate.d.ts +44 -0
  56. package/lib/cjs/core/parser/theme-vars.cjs +467 -0
  57. package/lib/cjs/core/parser/theme-vars.cjs.map +1 -0
  58. package/lib/cjs/core/parser/theme-vars.d.ts +82 -0
  59. package/lib/cjs/core/parser/tokens.cjs +486 -0
  60. package/lib/cjs/core/parser/tokens.cjs.map +1 -0
  61. package/lib/cjs/core/parser/tokens.d.ts +45 -0
  62. package/lib/cjs/core/parser/transform.cjs +198 -0
  63. package/lib/cjs/core/parser/transform.cjs.map +1 -0
  64. package/lib/cjs/core/parser/transform.d.ts +36 -0
  65. package/lib/cjs/core/parser/tw-parser.cjs +1680 -0
  66. package/lib/cjs/core/parser/tw-parser.cjs.map +1 -0
  67. package/lib/cjs/core/parser/tw-parser.d.ts +210 -0
  68. package/lib/cjs/core/parser/types.d.ts +37 -0
  69. package/lib/cjs/core/parser/typography-dispatcher.cjs +108 -0
  70. package/lib/cjs/core/parser/typography-dispatcher.cjs.map +1 -0
  71. package/lib/cjs/core/parser/typography-dispatcher.d.ts +11 -0
  72. package/lib/cjs/core/parser/typography.cjs +97 -0
  73. package/lib/cjs/core/parser/typography.cjs.map +1 -0
  74. package/lib/cjs/core/parser/typography.d.ts +43 -0
  75. package/lib/cjs/core/style-builder/build-style.cjs +444 -0
  76. package/lib/cjs/core/style-builder/build-style.cjs.map +1 -0
  77. package/lib/cjs/core/style-builder/build-style.d.ts +54 -0
  78. package/lib/cjs/core/style-builder/index.d.ts +3 -0
  79. package/lib/cjs/core/style-builder/union-builder.cjs +326 -0
  80. package/lib/cjs/core/style-builder/union-builder.cjs.map +1 -0
  81. package/lib/cjs/core/style-builder/union-builder.d.ts +128 -0
  82. package/lib/cjs/core/types.d.ts +14 -0
  83. package/lib/cjs/metro/dts.cjs +127 -0
  84. package/lib/cjs/metro/dts.cjs.map +1 -0
  85. package/lib/cjs/metro/dts.d.ts +16 -0
  86. package/lib/cjs/metro/index.cjs +19 -0
  87. package/lib/cjs/metro/index.cjs.map +1 -0
  88. package/lib/cjs/metro/index.d.ts +9 -0
  89. package/lib/cjs/metro/resolver.cjs +47 -0
  90. package/lib/cjs/metro/resolver.cjs.map +1 -0
  91. package/lib/cjs/metro/resolver.d.ts +22 -0
  92. package/lib/cjs/metro/state.cjs +301 -0
  93. package/lib/cjs/metro/state.cjs.map +1 -0
  94. package/lib/cjs/metro/state.d.ts +88 -0
  95. package/lib/cjs/metro/transform-ast.cjs +1472 -0
  96. package/lib/cjs/metro/transform-ast.cjs.map +1 -0
  97. package/lib/cjs/metro/transform-ast.d.ts +88 -0
  98. package/lib/cjs/metro/transformer.cjs +372 -0
  99. package/lib/cjs/metro/transformer.cjs.map +1 -0
  100. package/lib/cjs/metro/transformer.d.ts +47 -0
  101. package/lib/cjs/metro/warn-unknown-classes.cjs +86 -0
  102. package/lib/cjs/metro/warn-unknown-classes.cjs.map +1 -0
  103. package/lib/cjs/metro/warn-unknown-classes.d.ts +21 -0
  104. package/lib/cjs/metro/with-config.cjs +196 -0
  105. package/lib/cjs/metro/with-config.cjs.map +1 -0
  106. package/lib/cjs/metro/with-config.d.ts +79 -0
  107. package/lib/cjs/runtime/chain-handlers.cjs +37 -0
  108. package/lib/cjs/runtime/chain-handlers.cjs.map +1 -0
  109. package/lib/cjs/runtime/chain-handlers.d.ts +33 -0
  110. package/lib/cjs/runtime/components/rnwind-provider.cjs +98 -0
  111. package/lib/cjs/runtime/components/rnwind-provider.cjs.map +1 -0
  112. package/lib/cjs/runtime/components/rnwind-provider.d.ts +84 -0
  113. package/lib/cjs/runtime/gradient-types.d.ts +58 -0
  114. package/lib/cjs/runtime/haptics.cjs +113 -0
  115. package/lib/cjs/runtime/haptics.cjs.map +1 -0
  116. package/lib/cjs/runtime/haptics.d.ts +48 -0
  117. package/lib/cjs/runtime/hooks/use-css.cjs +21 -0
  118. package/lib/cjs/runtime/hooks/use-css.cjs.map +1 -0
  119. package/lib/cjs/runtime/hooks/use-css.d.ts +11 -0
  120. package/lib/cjs/runtime/hooks/use-interact.cjs +46 -0
  121. package/lib/cjs/runtime/hooks/use-interact.cjs.map +1 -0
  122. package/lib/cjs/runtime/hooks/use-interact.d.ts +42 -0
  123. package/lib/cjs/runtime/hooks/use-scheme.cjs +68 -0
  124. package/lib/cjs/runtime/hooks/use-scheme.cjs.map +1 -0
  125. package/lib/cjs/runtime/hooks/use-scheme.d.ts +34 -0
  126. package/lib/cjs/runtime/index.cjs +45 -0
  127. package/lib/cjs/runtime/index.cjs.map +1 -0
  128. package/lib/cjs/runtime/index.d.ts +27 -0
  129. package/lib/cjs/runtime/interactive-box.cjs +35 -0
  130. package/lib/cjs/runtime/interactive-box.cjs.map +1 -0
  131. package/lib/cjs/runtime/interactive-box.d.ts +40 -0
  132. package/lib/cjs/runtime/lookup-css.cjs +542 -0
  133. package/lib/cjs/runtime/lookup-css.cjs.map +1 -0
  134. package/lib/cjs/runtime/lookup-css.d.ts +164 -0
  135. package/lib/cjs/runtime/types.d.ts +29 -0
  136. package/lib/cjs/testing/index.cjs +367 -0
  137. package/lib/cjs/testing/index.cjs.map +1 -0
  138. package/lib/cjs/testing/index.d.ts +145 -0
  139. package/lib/esm/core/parser/animation.d.ts +126 -0
  140. package/lib/esm/core/parser/animation.mjs +408 -0
  141. package/lib/esm/core/parser/animation.mjs.map +1 -0
  142. package/lib/esm/core/parser/border-dispatcher.d.ts +15 -0
  143. package/lib/esm/core/parser/border-dispatcher.mjs +178 -0
  144. package/lib/esm/core/parser/border-dispatcher.mjs.map +1 -0
  145. package/lib/esm/core/parser/case-convert.d.ts +6 -0
  146. package/lib/esm/core/parser/case-convert.mjs +13 -0
  147. package/lib/esm/core/parser/case-convert.mjs.map +1 -0
  148. package/lib/esm/core/parser/color-properties-dispatcher.d.ts +19 -0
  149. package/lib/esm/core/parser/color-properties-dispatcher.mjs +82 -0
  150. package/lib/esm/core/parser/color-properties-dispatcher.mjs.map +1 -0
  151. package/lib/esm/core/parser/color.d.ts +12 -0
  152. package/lib/esm/core/parser/color.mjs +191 -0
  153. package/lib/esm/core/parser/color.mjs.map +1 -0
  154. package/lib/esm/core/parser/constants.d.ts +8 -0
  155. package/lib/esm/core/parser/constants.mjs +13 -0
  156. package/lib/esm/core/parser/constants.mjs.map +1 -0
  157. package/lib/esm/core/parser/declaration.d.ts +15 -0
  158. package/lib/esm/core/parser/declaration.mjs +345 -0
  159. package/lib/esm/core/parser/declaration.mjs.map +1 -0
  160. package/lib/esm/core/parser/gradient.d.ts +59 -0
  161. package/lib/esm/core/parser/gradient.mjs +130 -0
  162. package/lib/esm/core/parser/gradient.mjs.map +1 -0
  163. package/lib/esm/core/parser/haptics.d.ts +47 -0
  164. package/lib/esm/core/parser/haptics.mjs +71 -0
  165. package/lib/esm/core/parser/haptics.mjs.map +1 -0
  166. package/lib/esm/core/parser/index.d.ts +8 -0
  167. package/lib/esm/core/parser/keyframes.d.ts +26 -0
  168. package/lib/esm/core/parser/keyframes.mjs +91 -0
  169. package/lib/esm/core/parser/keyframes.mjs.map +1 -0
  170. package/lib/esm/core/parser/layout-dispatcher.d.ts +14 -0
  171. package/lib/esm/core/parser/layout-dispatcher.mjs +118 -0
  172. package/lib/esm/core/parser/layout-dispatcher.mjs.map +1 -0
  173. package/lib/esm/core/parser/length.d.ts +51 -0
  174. package/lib/esm/core/parser/length.mjs +104 -0
  175. package/lib/esm/core/parser/length.mjs.map +1 -0
  176. package/lib/esm/core/parser/motion-dispatcher.d.ts +11 -0
  177. package/lib/esm/core/parser/motion-dispatcher.mjs +75 -0
  178. package/lib/esm/core/parser/motion-dispatcher.mjs.map +1 -0
  179. package/lib/esm/core/parser/property.d.ts +8 -0
  180. package/lib/esm/core/parser/property.mjs +20 -0
  181. package/lib/esm/core/parser/property.mjs.map +1 -0
  182. package/lib/esm/core/parser/safe-area.d.ts +39 -0
  183. package/lib/esm/core/parser/safe-area.mjs +402 -0
  184. package/lib/esm/core/parser/safe-area.mjs.map +1 -0
  185. package/lib/esm/core/parser/selector.d.ts +11 -0
  186. package/lib/esm/core/parser/selector.mjs +20 -0
  187. package/lib/esm/core/parser/selector.mjs.map +1 -0
  188. package/lib/esm/core/parser/shorthand.d.ts +67 -0
  189. package/lib/esm/core/parser/shorthand.mjs +180 -0
  190. package/lib/esm/core/parser/shorthand.mjs.map +1 -0
  191. package/lib/esm/core/parser/text-truncate.d.ts +44 -0
  192. package/lib/esm/core/parser/text-truncate.mjs +75 -0
  193. package/lib/esm/core/parser/text-truncate.mjs.map +1 -0
  194. package/lib/esm/core/parser/theme-vars.d.ts +82 -0
  195. package/lib/esm/core/parser/theme-vars.mjs +461 -0
  196. package/lib/esm/core/parser/theme-vars.mjs.map +1 -0
  197. package/lib/esm/core/parser/tokens.d.ts +45 -0
  198. package/lib/esm/core/parser/tokens.mjs +480 -0
  199. package/lib/esm/core/parser/tokens.mjs.map +1 -0
  200. package/lib/esm/core/parser/transform.d.ts +36 -0
  201. package/lib/esm/core/parser/transform.mjs +193 -0
  202. package/lib/esm/core/parser/transform.mjs.map +1 -0
  203. package/lib/esm/core/parser/tw-parser.d.ts +210 -0
  204. package/lib/esm/core/parser/tw-parser.mjs +1678 -0
  205. package/lib/esm/core/parser/tw-parser.mjs.map +1 -0
  206. package/lib/esm/core/parser/types.d.ts +37 -0
  207. package/lib/esm/core/parser/typography-dispatcher.d.ts +11 -0
  208. package/lib/esm/core/parser/typography-dispatcher.mjs +106 -0
  209. package/lib/esm/core/parser/typography-dispatcher.mjs.map +1 -0
  210. package/lib/esm/core/parser/typography.d.ts +43 -0
  211. package/lib/esm/core/parser/typography.mjs +91 -0
  212. package/lib/esm/core/parser/typography.mjs.map +1 -0
  213. package/lib/esm/core/style-builder/build-style.d.ts +54 -0
  214. package/lib/esm/core/style-builder/build-style.mjs +442 -0
  215. package/lib/esm/core/style-builder/build-style.mjs.map +1 -0
  216. package/lib/esm/core/style-builder/index.d.ts +3 -0
  217. package/lib/esm/core/style-builder/union-builder.d.ts +128 -0
  218. package/lib/esm/core/style-builder/union-builder.mjs +324 -0
  219. package/lib/esm/core/style-builder/union-builder.mjs.map +1 -0
  220. package/lib/esm/core/types.d.ts +14 -0
  221. package/lib/esm/metro/dts.d.ts +16 -0
  222. package/lib/esm/metro/dts.mjs +125 -0
  223. package/lib/esm/metro/dts.mjs.map +1 -0
  224. package/lib/esm/metro/index.d.ts +9 -0
  225. package/lib/esm/metro/index.mjs +6 -0
  226. package/lib/esm/metro/index.mjs.map +1 -0
  227. package/lib/esm/metro/resolver.d.ts +22 -0
  228. package/lib/esm/metro/resolver.mjs +43 -0
  229. package/lib/esm/metro/resolver.mjs.map +1 -0
  230. package/lib/esm/metro/state.d.ts +88 -0
  231. package/lib/esm/metro/state.mjs +291 -0
  232. package/lib/esm/metro/state.mjs.map +1 -0
  233. package/lib/esm/metro/transform-ast.d.ts +88 -0
  234. package/lib/esm/metro/transform-ast.mjs +1451 -0
  235. package/lib/esm/metro/transform-ast.mjs.map +1 -0
  236. package/lib/esm/metro/transformer.d.ts +47 -0
  237. package/lib/esm/metro/transformer.mjs +349 -0
  238. package/lib/esm/metro/transformer.mjs.map +1 -0
  239. package/lib/esm/metro/warn-unknown-classes.d.ts +21 -0
  240. package/lib/esm/metro/warn-unknown-classes.mjs +84 -0
  241. package/lib/esm/metro/warn-unknown-classes.mjs.map +1 -0
  242. package/lib/esm/metro/with-config.d.ts +79 -0
  243. package/lib/esm/metro/with-config.mjs +194 -0
  244. package/lib/esm/metro/with-config.mjs.map +1 -0
  245. package/lib/esm/runtime/chain-handlers.d.ts +33 -0
  246. package/lib/esm/runtime/chain-handlers.mjs +34 -0
  247. package/lib/esm/runtime/chain-handlers.mjs.map +1 -0
  248. package/lib/esm/runtime/components/rnwind-provider.d.ts +84 -0
  249. package/lib/esm/runtime/components/rnwind-provider.mjs +94 -0
  250. package/lib/esm/runtime/components/rnwind-provider.mjs.map +1 -0
  251. package/lib/esm/runtime/gradient-types.d.ts +58 -0
  252. package/lib/esm/runtime/haptics.d.ts +48 -0
  253. package/lib/esm/runtime/haptics.mjs +110 -0
  254. package/lib/esm/runtime/haptics.mjs.map +1 -0
  255. package/lib/esm/runtime/hooks/use-css.d.ts +11 -0
  256. package/lib/esm/runtime/hooks/use-css.mjs +19 -0
  257. package/lib/esm/runtime/hooks/use-css.mjs.map +1 -0
  258. package/lib/esm/runtime/hooks/use-interact.d.ts +42 -0
  259. package/lib/esm/runtime/hooks/use-interact.mjs +44 -0
  260. package/lib/esm/runtime/hooks/use-interact.mjs.map +1 -0
  261. package/lib/esm/runtime/hooks/use-scheme.d.ts +34 -0
  262. package/lib/esm/runtime/hooks/use-scheme.mjs +63 -0
  263. package/lib/esm/runtime/hooks/use-scheme.mjs.map +1 -0
  264. package/lib/esm/runtime/index.d.ts +27 -0
  265. package/lib/esm/runtime/index.mjs +18 -0
  266. package/lib/esm/runtime/index.mjs.map +1 -0
  267. package/lib/esm/runtime/interactive-box.d.ts +40 -0
  268. package/lib/esm/runtime/interactive-box.mjs +33 -0
  269. package/lib/esm/runtime/interactive-box.mjs.map +1 -0
  270. package/lib/esm/runtime/lookup-css.d.ts +164 -0
  271. package/lib/esm/runtime/lookup-css.mjs +531 -0
  272. package/lib/esm/runtime/lookup-css.mjs.map +1 -0
  273. package/lib/esm/runtime/types.d.ts +29 -0
  274. package/lib/esm/testing/index.d.ts +145 -0
  275. package/lib/esm/testing/index.mjs +344 -0
  276. package/lib/esm/testing/index.mjs.map +1 -0
  277. package/package.json +80 -13
  278. package/preset.css +1171 -0
  279. package/src/core/parser/animation.ts +404 -0
  280. package/src/core/parser/border-dispatcher.ts +176 -0
  281. package/src/core/parser/case-convert.ts +10 -0
  282. package/src/core/parser/color-properties-dispatcher.ts +78 -0
  283. package/src/core/parser/color.ts +191 -0
  284. package/src/core/parser/constants.ts +11 -0
  285. package/src/core/parser/declaration.ts +340 -0
  286. package/src/core/parser/gradient.ts +148 -0
  287. package/src/core/parser/haptics.ts +88 -0
  288. package/src/core/parser/index.ts +8 -0
  289. package/src/core/parser/keyframes.ts +84 -0
  290. package/src/core/parser/layout-dispatcher.ts +111 -0
  291. package/src/core/parser/length.ts +114 -0
  292. package/src/core/parser/motion-dispatcher.ts +89 -0
  293. package/src/core/parser/property.ts +15 -0
  294. package/src/core/parser/safe-area.ts +404 -0
  295. package/src/core/parser/selector.ts +17 -0
  296. package/src/core/parser/shorthand.ts +182 -0
  297. package/src/core/parser/text-truncate.ts +79 -0
  298. package/src/core/parser/theme-vars.ts +465 -0
  299. package/src/core/parser/tokens.ts +456 -0
  300. package/src/core/parser/transform.ts +195 -0
  301. package/src/core/parser/tw-parser.ts +1828 -0
  302. package/src/core/parser/types.ts +45 -0
  303. package/src/core/parser/typography-dispatcher.ts +97 -0
  304. package/src/core/parser/typography.ts +83 -0
  305. package/src/core/style-builder/build-style.ts +500 -0
  306. package/src/core/style-builder/index.ts +3 -0
  307. package/src/core/style-builder/union-builder.ts +328 -0
  308. package/src/core/types.ts +15 -0
  309. package/src/metro/dts.ts +128 -0
  310. package/src/metro/index.ts +9 -0
  311. package/src/metro/resolver.ts +42 -0
  312. package/src/metro/state.ts +305 -0
  313. package/src/metro/transform-ast.ts +1729 -0
  314. package/src/metro/transformer.ts +372 -0
  315. package/src/metro/warn-unknown-classes.ts +79 -0
  316. package/src/metro/with-config.ts +251 -0
  317. package/src/runtime/chain-handlers.ts +47 -0
  318. package/src/runtime/components/rnwind-provider.tsx +144 -0
  319. package/src/runtime/gradient-types.ts +60 -0
  320. package/src/runtime/haptics.ts +120 -0
  321. package/src/runtime/hooks/use-css.ts +16 -0
  322. package/src/runtime/hooks/use-interact.ts +65 -0
  323. package/src/runtime/hooks/use-scheme.ts +63 -0
  324. package/src/runtime/index.ts +54 -0
  325. package/src/runtime/interactive-box.tsx +57 -0
  326. package/src/runtime/lookup-css.ts +628 -0
  327. package/src/runtime/types.ts +32 -0
  328. package/src/testing/index.ts +507 -0
  329. package/src/types/tailwindcss-node.d.ts +33 -0
  330. package/src/index.ts +0 -1
@@ -0,0 +1,1472 @@
1
+ 'use strict';
2
+
3
+ var t = require('@babel/types');
4
+ var traverseImport = require('@babel/traverse');
5
+ var node_crypto = require('node:crypto');
6
+ var textTruncate = require('../core/parser/text-truncate.cjs');
7
+
8
+ function _interopNamespaceDefault(e) {
9
+ var n = Object.create(null);
10
+ if (e) {
11
+ Object.keys(e).forEach(function (k) {
12
+ if (k !== 'default') {
13
+ var d = Object.getOwnPropertyDescriptor(e, k);
14
+ Object.defineProperty(n, k, d.get ? d : {
15
+ enumerable: true,
16
+ get: function () { return e[k]; }
17
+ });
18
+ }
19
+ });
20
+ }
21
+ n.default = e;
22
+ return Object.freeze(n);
23
+ }
24
+
25
+ var t__namespace = /*#__PURE__*/_interopNamespaceDefault(t);
26
+
27
+ const traverse = traverseImport.default ?? traverseImport;
28
+ /**
29
+ * Name of the internal rnwind context hook the transformer injects at
30
+ * each component top. Must start with `use` + uppercase letter so
31
+ * react-refresh's babel plugin folds it into each component's
32
+ * fast-refresh signature — otherwise stale signatures preserve fiber
33
+ * state across transformer changes and the rendered hook list shifts,
34
+ * which React surfaces as "change in the order of Hooks" runtime errors.
35
+ */
36
+ const USE_RNWIND_INTERNAL = 'useR_';
37
+ /** Internal alias of `useMountHaptic` — emit `_hm` instead of the public name to dodge shadowing. */
38
+ const USE_MOUNT_HAPTIC = '_hm';
39
+ /** Internal alias of `triggerHaptic` — `_ht` so a user-defined `triggerHaptic` won't shadow it. */
40
+ const TRIGGER_HAPTIC = '_ht';
41
+ /** Internal alias of `lookupCss` — `_l` so user can't accidentally shadow at the JSX call sites. */
42
+ const LOOKUP_CSS = '_l';
43
+ /** Package specifier rnwind runtime primitives import from. */
44
+ const RUNTIME_MODULE = 'rnwind';
45
+ /** Local binding name for the rnwind context hook result inside components. */
46
+ const CONTEXT_BINDING = '_t';
47
+ /**
48
+ * Name of the per-instance wrapper component rnwind substitutes for any
49
+ * JSX site that uses active: / focus: variants. One `useInteract()` lives
50
+ * INSIDE each InteractiveBox so siblings never share state — a previous
51
+ * design that called `useInteract()` once per enclosing component caused
52
+ * "press one button, all buttons glow" because every site read the same
53
+ * `_i.state` reference.
54
+ */
55
+ const INTERACTIVE_BOX = '_ib';
56
+ /** Leading variant prefixes that trigger interact wiring. */
57
+ const INTERACTIVE_PREFIXES = ['active:', 'focus:'];
58
+ /**
59
+ * Regex matching the atom-name shape of every `*-safe` utility the
60
+ * rnwind preset ships. Used by the transformer to decide whether a
61
+ * file needs the `const _i = useInsets()` binding injected. Matches
62
+ * three families:
63
+ * - `*-safe` exactly (e.g. `pt-safe`, `inset-safe`).
64
+ * - `*-safe-or-<N>` / `*-safe-offset-<N>` (e.g. `mt-safe-or-4`).
65
+ * - `*-screen-safe` (e.g. `h-screen-safe`, `min-h-screen-safe`).
66
+ *
67
+ * Dynamic className expressions always inject `_i` — we can't inspect
68
+ * the string at build time, and the runtime slow path reads `SAFE_ATOMS`
69
+ * to pick up any user-defined safe utility the preset doesn't cover.
70
+ */
71
+ const SAFE_ATOM_PATTERN = /(?:(?:^|-)safe(?:-or-|-offset-|$))|(?:-screen-safe$)/;
72
+ /**
73
+ * RN host components that NEVER emit press / focus events — wrapping
74
+ * them in `<InteractiveBox>` buys nothing and costs `useInteract()`'s
75
+ * hooks per mount. When the JSX tag is in this set, we skip the
76
+ * wrapper even for dynamic `className={…}` expressions. On a list of
77
+ * 1000 `<View className={rowCls}>` rows this drops 6 × 1000 hook
78
+ * initialisations per mount.
79
+ *
80
+ * The set is intentionally conservative — anything not listed keeps
81
+ * the wrapper. Custom components, `Animated.View`, and
82
+ * `Pressable`/`TextInput` all still get interactive variants on
83
+ * dynamic className.
84
+ */
85
+ const NON_INTERACTIVE_HOST_TAGS = new Set([
86
+ 'View',
87
+ 'Text',
88
+ 'ScrollView',
89
+ 'SafeAreaView',
90
+ 'Image',
91
+ 'ImageBackground',
92
+ 'FlatList',
93
+ 'SectionList',
94
+ 'VirtualizedList',
95
+ 'KeyboardAvoidingView',
96
+ 'ActivityIndicator',
97
+ 'RefreshControl',
98
+ 'Fragment',
99
+ ]);
100
+ /**
101
+ * Built-in prefix that's always active — covers the React Native
102
+ * ecosystem's `contentContainerStyle` pattern on ScrollView / FlatList /
103
+ * SectionList. Users who pass `classNamePrefixes` get their list merged
104
+ * on top, never replacing this.
105
+ */
106
+ const DEFAULT_CLASSNAME_PREFIXES = ['contentContainer'];
107
+ /**
108
+ * Module specifiers whose JSX exports are "host-like" — they consume
109
+ * `style` directly (and own no opaque component logic that depends on
110
+ * receiving the raw `className` string). For tags imported from these
111
+ * sources the transformer rewrites `className="…"` → `style={lookupCss(…)}`
112
+ * at build time, so the runtime cost is zero.
113
+ *
114
+ * For tags from ANY other source the transformer leaves `className`
115
+ * alone — the importing component receives the raw string and decides
116
+ * what to do with it (forward to an inner host, reshape, route a slice
117
+ * to `contentContainerStyle`, …). This is what makes patterns like
118
+ * `<MyButton className="px-4 bg-primary" />` work without rnwind
119
+ * stealing the prop before the component sees it.
120
+ *
121
+ * Users extend the list via `withRnwindConfig`'s `hostSources` option.
122
+ */
123
+ const DEFAULT_HOST_SOURCES = [
124
+ 'react-native',
125
+ 'react-native-reanimated',
126
+ 'react-native-svg',
127
+ 'react-native-gesture-handler',
128
+ 'react-native-safe-area-context',
129
+ 'expo-linear-gradient',
130
+ 'expo-image',
131
+ 'expo-blur',
132
+ 'expo-symbols',
133
+ '@shopify/flash-list',
134
+ '@shopify/react-native-skia',
135
+ 'lottie-react-native',
136
+ ];
137
+ /**
138
+ * Whether a JSX tag name is lowercase. Lowercase tags don't appear in
139
+ * native React Native userland — but if one shows up (web target via
140
+ * `react-native-web`, mdx, etc.) treat it as a host so the rewrite
141
+ * engages instead of silently dropping the className.
142
+ * @param name JSX tag identifier text.
143
+ * @returns True for ASCII-lowercase first character.
144
+ */
145
+ function isLowercaseTag(name) {
146
+ const code = name.codePointAt(0);
147
+ return code !== undefined && code >= 97 && code <= 122;
148
+ }
149
+ /**
150
+ * Walk a JSX opening element's tag name node into a dotted string
151
+ * (`Animated.View`, `Foo.Bar.Baz`). Returns `null` for namespaced names
152
+ * (`<svg:rect>` — invalid in RN; we skip them).
153
+ * @param name JSXOpeningElement name node.
154
+ * @returns Dotted tag text, or null.
155
+ */
156
+ function jsxTagText(name) {
157
+ if (t__namespace.isJSXIdentifier(name))
158
+ return name.name;
159
+ if (t__namespace.isJSXMemberExpression(name)) {
160
+ const left = jsxTagText(name.object);
161
+ return left ? `${left}.${name.property.name}` : null;
162
+ }
163
+ return null;
164
+ }
165
+ /**
166
+ * Leftmost identifier of a (possibly dotted) tag — used to look up its import source.
167
+ * @param tagText
168
+ */
169
+ function leftmostIdentifier(tagText) {
170
+ const dot = tagText.indexOf('.');
171
+ return dot === -1 ? tagText : tagText.slice(0, dot);
172
+ }
173
+ /**
174
+ * Build the per-file host lookup. Walks every `import` declaration once
175
+ * to map every locally-bound name to its source module. A JSX tag is a
176
+ * host when:
177
+ * 1. its full text matches an entry in `extraHostComponents` (verbatim),
178
+ * 2. its leftmost identifier was imported from a `hostSources` module,
179
+ * 3. it's a lowercase tag (web targets, defensive).
180
+ *
181
+ * Anything else is custom and the transformer leaves its className alone.
182
+ * @param ast File AST.
183
+ * @param extraHostSources User-supplied additional host module specifiers.
184
+ * @param extraHostComponents User-supplied additional host component names.
185
+ * @returns Lookup callback.
186
+ */
187
+ function buildHostLookup(ast, extraHostSources, extraHostComponents) {
188
+ const importSourceByLocal = new Map();
189
+ for (const node of ast.program.body) {
190
+ if (!t__namespace.isImportDeclaration(node))
191
+ continue;
192
+ const source = node.source.value;
193
+ for (const spec of node.specifiers) {
194
+ if (t__namespace.isImportDefaultSpecifier(spec) || t__namespace.isImportSpecifier(spec) || t__namespace.isImportNamespaceSpecifier(spec)) {
195
+ importSourceByLocal.set(spec.local.name, source);
196
+ }
197
+ }
198
+ }
199
+ // Recognise module-local host aliases — common pattern in React Native:
200
+ // const AnimatedTextInput = Animated.createAnimatedComponent(TextInput)
201
+ // const Animated = createAnimatedComponent(View)
202
+ // The local binding wraps a host underneath so its className must still
203
+ // be rewritten. Without this every `<AnimatedTextInput className="…" />`
204
+ // site looked custom and the className silently dropped.
205
+ const localHostAliases = collectCreateAnimatedComponentAliases(ast);
206
+ const hostSources = new Set([...DEFAULT_HOST_SOURCES, ...(extraHostSources ?? [])]);
207
+ const hostComponents = new Set(extraHostComponents);
208
+ return (tagText) => {
209
+ if (isLowercaseTag(tagText))
210
+ return true;
211
+ if (hostComponents.has(tagText))
212
+ return true;
213
+ const left = leftmostIdentifier(tagText);
214
+ if (localHostAliases.has(left))
215
+ return true;
216
+ const source = importSourceByLocal.get(left);
217
+ return source !== undefined && hostSources.has(source);
218
+ };
219
+ }
220
+ /**
221
+ * Walk top-level `const X = createAnimatedComponent(Y)` /
222
+ * `Animated.createAnimatedComponent(Y)` declarations and return the set
223
+ * of local names so the host-lookup recognises them. Reanimated +
224
+ * RN-core `Animated.createAnimatedComponent` are the only creators in
225
+ * common use; matching by callee-name covers both shapes without
226
+ * needing import-source resolution.
227
+ * @param ast File AST.
228
+ * @returns Set of locally-bound names that wrap a host component.
229
+ */
230
+ function collectCreateAnimatedComponentAliases(ast) {
231
+ const aliases = new Set();
232
+ for (const node of ast.program.body) {
233
+ const declaration = t__namespace.isExportNamedDeclaration(node) ? node.declaration : node;
234
+ if (!t__namespace.isVariableDeclaration(declaration))
235
+ continue;
236
+ for (const decl of declaration.declarations) {
237
+ if (!t__namespace.isIdentifier(decl.id) || !decl.init)
238
+ continue;
239
+ if (!isCreateAnimatedComponentCall(decl.init))
240
+ continue;
241
+ aliases.add(decl.id.name);
242
+ }
243
+ }
244
+ return aliases;
245
+ }
246
+ /**
247
+ * True for `createAnimatedComponent(...)` and `<x>.createAnimatedComponent(...)` calls.
248
+ * @param expr
249
+ */
250
+ function isCreateAnimatedComponentCall(expr) {
251
+ if (!t__namespace.isCallExpression(expr))
252
+ return false;
253
+ const { callee } = expr;
254
+ if (t__namespace.isIdentifier(callee) && callee.name === 'createAnimatedComponent')
255
+ return true;
256
+ if (t__namespace.isMemberExpression(callee) && t__namespace.isIdentifier(callee.property) && callee.property.name === 'createAnimatedComponent')
257
+ return true;
258
+ return false;
259
+ }
260
+ /**
261
+ * Mutate an already-parsed Babel AST in place:
262
+ * - Rewrite every JSX `className="…"` / `className={expr}` attribute to
263
+ * `style={lookupCss(<ref|expr>, _s, <existingStyle>)}`. Static string
264
+ * literals get a module-scope `const _c_<hash> = Object.freeze(['a',
265
+ * 'b'])` hoist so React sees an identity-stable array across renders.
266
+ * - Preserve any adjacent `style={…}` prop — it becomes the third
267
+ * argument so user inline styles keep working (and trump atoms).
268
+ * - Inject `const _s = useScheme()` at the top of the enclosing
269
+ * function component (idempotent — one injection per component).
270
+ * - Prepend `import { lookupCss, useScheme } from 'rnwind'`.
271
+ * - Prepend a side-effect `import 'rnwind/__generated/style'` so the
272
+ * union registry is loaded before any hoist runs.
273
+ * @param ast Babel File AST (usually handed to us by Metro).
274
+ * @param options Extra inputs — side-effect import specifiers + parser metadata.
275
+ * @returns Transform outcome flags + the hoist table.
276
+ */
277
+ function transformAst(ast, options) {
278
+ const hoister = createHoister();
279
+ const gradientHoister = createGradientHoister();
280
+ const literals = [];
281
+ const prefixSet = buildPrefixSet(options.classNamePrefixes);
282
+ const hapticHoister = createHapticHoister();
283
+ const isHostTag = buildHostLookup(ast, options.hostSources, options.hostComponents);
284
+ const rewriteCtx = {
285
+ needsInsets: false,
286
+ gradientAtoms: options.gradientAtoms ?? EMPTY_GRADIENT_ATOMS,
287
+ gradientHoister,
288
+ hapticAtoms: options.hapticAtoms ?? EMPTY_HAPTIC_ATOMS,
289
+ hapticHoister,
290
+ mountByComponent: new Map(),
291
+ needsHapticsHook: false,
292
+ };
293
+ let touched = false;
294
+ let usedLookupCss = false;
295
+ let usedInteractiveBox = false;
296
+ // Per-element host classification, captured the first time we see each
297
+ // JSXOpeningElement. Necessary because the InteractiveBox wrap mutates
298
+ // `parent.name` in-place from the original tag → `_ib`; sibling
299
+ // attributes processed AFTER the swap would otherwise re-classify off
300
+ // the now-meaningless `_ib` name and skip rewrites they should do
301
+ // (e.g. `contentContainerClassName` next to an `active:` className on
302
+ // the same `<ScrollView>`).
303
+ const customElements = new WeakSet();
304
+ const classifiedElements = new WeakSet();
305
+ traverse(ast, {
306
+ JSXAttribute(attributePath) {
307
+ const { node } = attributePath;
308
+ if (!t__namespace.isJSXIdentifier(node.name))
309
+ return;
310
+ const target = classifyAttributeName(node.name.name, prefixSet);
311
+ if (!target)
312
+ return;
313
+ // Skip className rewrite when the parent JSX tag is a custom
314
+ // component (not imported from a known host source). Custom
315
+ // components own their `className` prop — the transformer would
316
+ // steal the string from under them otherwise. The literal still
317
+ // appears in source text, so oxide still discovers its atoms via
318
+ // the project scan; the inner host that ultimately consumes the
319
+ // forwarded className gets rewritten by ITS file's transform.
320
+ const { parent } = attributePath;
321
+ if (t__namespace.isJSXOpeningElement(parent)) {
322
+ if (!classifiedElements.has(parent)) {
323
+ classifiedElements.add(parent);
324
+ const tagText = jsxTagText(parent.name);
325
+ if (tagText !== null && !isHostTag(tagText))
326
+ customElements.add(parent);
327
+ }
328
+ if (customElements.has(parent))
329
+ return;
330
+ }
331
+ const rewritten = rewriteClassNameAttribute(attributePath, hoister, literals, rewriteCtx, target);
332
+ if (!rewritten)
333
+ return;
334
+ touched = true;
335
+ if (rewritten.injectedInteract)
336
+ usedInteractiveBox = true;
337
+ else
338
+ usedLookupCss = true;
339
+ },
340
+ });
341
+ if (!touched && options.styleSpecifiers.length === 0)
342
+ return { touched: false, hoistedArrays: hoister.entries, literals };
343
+ // Inject `useMountHaptic(<hoisted>)` per component that had bare
344
+ // haptic atoms. Done post-traversal so we know every aggregated
345
+ // request up front and can hoist one frozen array per component.
346
+ injectMountHapticCalls(rewriteCtx);
347
+ const usedMountHaptic = rewriteCtx.mountByComponent.size > 0;
348
+ prependRuntimeImports(ast, {
349
+ usedLookupCss,
350
+ usedInteractiveBox,
351
+ usedMountHaptic,
352
+ usedTriggerHaptic: rewriteCtx.needsHapticsHook,
353
+ touched,
354
+ }, options.styleSpecifiers);
355
+ if (hoister.entries.size > 0)
356
+ injectHoistedConsts(ast, hoister.entries);
357
+ if (gradientHoister.entries.size > 0)
358
+ injectGradientConsts(ast, gradientHoister.entries);
359
+ if (hapticHoister.entries.size > 0)
360
+ injectHapticConsts(ast, hapticHoister.entries);
361
+ return { touched, hoistedArrays: hoister.entries, literals };
362
+ }
363
+ /** Default empty gradient-atoms map used when callers don't supply one. */
364
+ const EMPTY_GRADIENT_ATOMS = new Map();
365
+ /** Default empty haptic-atoms map used when callers don't supply one. */
366
+ const EMPTY_HAPTIC_ATOMS = new Map();
367
+ /**
368
+ * Merge the built-in default prefix with the caller-supplied list. The
369
+ * default (`contentContainer`) is always present; user entries are
370
+ * additive. Returned as a Set so the hot-path visitor classifies one
371
+ * attribute in O(1).
372
+ * @param userPrefixes Extra prefixes the caller wants active.
373
+ * @returns Sorted effective prefix set.
374
+ */
375
+ function buildPrefixSet(userPrefixes) {
376
+ const out = new Set(DEFAULT_CLASSNAME_PREFIXES);
377
+ if (userPrefixes)
378
+ for (const prefix of userPrefixes)
379
+ out.add(prefix);
380
+ return out;
381
+ }
382
+ /**
383
+ * Decide whether a JSX attribute name is one the transformer should
384
+ * rewrite, and derive the replacement prop name when it is.
385
+ *
386
+ * `className` is the classic path. `<prefix>ClassName` where `prefix`
387
+ * is in the active set becomes `<prefix>Style`. Everything else returns
388
+ * `null` and the visitor moves on.
389
+ * @param name JSXAttribute's identifier text.
390
+ * @param prefixes Effective prefix set for this transform.
391
+ * @returns Rewrite target record, or `null` when the attribute is not ours.
392
+ */
393
+ function classifyAttributeName(name, prefixes) {
394
+ if (name === 'className')
395
+ return { kind: 'className', styleProp: 'style' };
396
+ if (!name.endsWith('ClassName'))
397
+ return null;
398
+ const prefix = name.slice(0, -'ClassName'.length);
399
+ if (prefix.length === 0 || !prefixes.has(prefix))
400
+ return null;
401
+ return { kind: 'prefix', styleProp: `${prefix}Style` };
402
+ }
403
+ /**
404
+ * Rewrite one `className` JSXAttribute node.
405
+ *
406
+ * Two paths:
407
+ * - **Non-interactive** (literal with no `active:` / `focus:` tokens):
408
+ * emit `style={lookupCss(<ref|expr>, _s [, userStyle])}` inline on
409
+ * the existing tag. The JSX site keeps its original component.
410
+ * - **Interactive** (literal with an interactive token OR any dynamic
411
+ * expression): replace the JSXElement's tag with `<InteractiveBox>`,
412
+ * move the original tag into a `_rw.as` spec prop, and forward all
413
+ * other attributes untouched. Each InteractiveBox instance calls
414
+ * `useInteract()` internally so sibling elements don't share state.
415
+ *
416
+ * If the element has a sibling `style={…}` attribute it's removed and
417
+ * its expression threads through as the user-style merge source.
418
+ * @param attributePath The JSXAttribute path.
419
+ * @param hoister Per-file hoist table.
420
+ * @param literals Output array — each static literal gets pushed so the
421
+ * caller can feed them into the parser / atom ledger.
422
+ * @param rewriteCtx
423
+ * @param target
424
+ * @returns Outcome flags, or `null` when the attribute was unrewritable.
425
+ */
426
+ function rewriteClassNameAttribute(attributePath, hoister, literals, rewriteCtx, target) {
427
+ const { node } = attributePath;
428
+ const { value } = node;
429
+ if (!value)
430
+ return null;
431
+ const { parent } = attributePath;
432
+ if (!t__namespace.isJSXOpeningElement(parent))
433
+ return null;
434
+ // The rewrite emits references to `_t` (the `useR_()` binding). That
435
+ // binding can only live in a component body — so if this JSX site has
436
+ // no enclosing component (e.g. a top-level `const renderItem = (...) =>
437
+ // <View className=.../>` helper), bail and leave className untouched
438
+ // rather than emit a dangling `_t`. Checked BEFORE any mutation
439
+ // (hoist, sibling-style drop) so a bail leaves the AST pristine.
440
+ if (!hasComponentBody(attributePath))
441
+ return null;
442
+ const buildResult = buildFirstArgument(value, hoister, literals, rewriteCtx);
443
+ if (!buildResult)
444
+ return null;
445
+ const userStyleExpr = extractAndDropSiblingStyle(parent, target.styleProp);
446
+ // Single context binding `_t = _r()` — carries scheme, fontScale,
447
+ // insets together so React tracks all three as render deps via one
448
+ // useContext read.
449
+ const ctxBinding = injectContextHook(attributePath);
450
+ applyDerivedJsxAttributes(attributePath, parent, buildResult, target, rewriteCtx);
451
+ // Prefixed rewrites (`<prefix>ClassName`) target a passive sub-surface
452
+ // that can't receive press / focus — skip the InteractiveBox wrapper
453
+ // even for dynamic expressions. Only the plain `className` path is
454
+ // eligible for InteractiveBox routing.
455
+ if (target.kind === 'className' && buildResult.mayBeInteractive && isTagInteractive(parent.name)) {
456
+ rewriteAsInteractiveBox(attributePath, parent, buildResult.expression, ctxBinding, userStyleExpr);
457
+ return { injectedInteract: true };
458
+ }
459
+ const args = [buildResult.expression, t__namespace.identifier(ctxBinding)];
460
+ // 3rd arg = userStyle (sibling style={…}). 4th arg = interactState
461
+ // (always undefined in the non-interactive branch).
462
+ if (userStyleExpr)
463
+ args.push(userStyleExpr);
464
+ const call = t__namespace.callExpression(t__namespace.identifier(LOOKUP_CSS), args);
465
+ attributePath.replaceWith(t__namespace.jsxAttribute(t__namespace.jsxIdentifier(target.styleProp), t__namespace.jsxExpressionContainer(call)));
466
+ return { injectedInteract: false };
467
+ }
468
+ /**
469
+ * Apply every JSX attribute + side-effect derived from a parsed
470
+ * className literal: gradient props, truncate props, mount-haptic
471
+ * aggregation, and event-haptic handler chaining. Collected in one
472
+ * helper so {@link rewriteClassNameAttribute} stays under the
473
+ * complexity cap.
474
+ * @param attributePath Path of the className attribute being rewritten.
475
+ * @param parent Opening element to mutate.
476
+ * @param result Per-literal derived state.
477
+ * @param target Rewrite target (only `className`-kind gets derived attrs).
478
+ * @param rewriteCtx Rewrite-wide state.
479
+ */
480
+ function applyDerivedJsxAttributes(attributePath, parent, result, target, rewriteCtx) {
481
+ if (target.kind !== 'className')
482
+ return;
483
+ if (result.gradientAttrs)
484
+ appendGradientAttributes(parent, result.gradientAttrs);
485
+ if (result.truncateAttrs)
486
+ appendGradientAttributes(parent, result.truncateAttrs);
487
+ if (result.mountHaptics)
488
+ recordMountHaptics(attributePath, result.mountHaptics, rewriteCtx);
489
+ if (result.eventHaptics)
490
+ injectEventHapticHandlers(attributePath, parent, result.eventHaptics, rewriteCtx);
491
+ }
492
+ /**
493
+ * Splice class-derived JSX attributes (`colors={…}` / `start={…}` /
494
+ * `end={…}` for gradients; `numberOfLines=` / `ellipsizeMode=` for
495
+ * truncate) into a JSXOpeningElement's attribute list — but only when
496
+ * the developer hasn't already written that attribute themselves.
497
+ *
498
+ * **User attrs always win.** If a hand-written `colors={USER}` is
499
+ * present, the class-derived hoist is dropped on the floor for that
500
+ * specific attribute. Same rule for every derived prop, applied
501
+ * per-attribute so the user can override one slot (e.g. `start={…}`)
502
+ * and let rnwind fill in the others. Documented in
503
+ * `docs/architecture.md`.
504
+ * @param opening JSXOpeningElement to mutate.
505
+ * @param gradientAttributes Freshly built JSX attributes.
506
+ */
507
+ function appendGradientAttributes(opening, gradientAttributes) {
508
+ const userAttributeNames = new Set();
509
+ for (const attribute of opening.attributes) {
510
+ if (!t__namespace.isJSXAttribute(attribute))
511
+ continue;
512
+ if (!t__namespace.isJSXIdentifier(attribute.name))
513
+ continue;
514
+ userAttributeNames.add(attribute.name.name);
515
+ }
516
+ for (const derived of gradientAttributes) {
517
+ if (!t__namespace.isJSXIdentifier(derived.name))
518
+ continue;
519
+ if (userAttributeNames.has(derived.name.name))
520
+ continue;
521
+ opening.attributes.push(derived);
522
+ }
523
+ }
524
+ /**
525
+ * Whether a JSX tag can fire press / focus events. Pure host-tag check
526
+ * against {@link NON_INTERACTIVE_HOST_TAGS}: anything in the set is
527
+ * definitely non-interactive; anything else (custom component,
528
+ * `Animated.View`, etc.) is treated as potentially interactive so the
529
+ * InteractiveBox wrapper is still applied.
530
+ * @param name JSXOpeningElement name node.
531
+ * @returns `true` when the tag might emit press / focus events.
532
+ */
533
+ function isTagInteractive(name) {
534
+ if (t__namespace.isJSXIdentifier(name))
535
+ return !NON_INTERACTIVE_HOST_TAGS.has(name.name);
536
+ // Member expressions (`Animated.View`, `Foo.Bar`): conservatively
537
+ // treat as interactive since the outer object's semantics are opaque.
538
+ return true;
539
+ }
540
+ /**
541
+ * Replace the JSXElement's tag with `<InteractiveBox>`, packing the
542
+ * original tag, the className ref / expression, the scheme binding, and
543
+ * any user style into a single `_rw` spec prop. All other attributes
544
+ * forward through unchanged.
545
+ *
546
+ * The replacement keeps the element's children — only the opening /
547
+ * closing tag name changes, plus the className attribute is replaced by
548
+ * `_rw` (and a preceding `style` attribute was already spliced out).
549
+ * @param attributePath JSXAttribute path for the className being rewritten.
550
+ * @param opening JSXOpeningElement the attribute lives on.
551
+ * @param classNameExpr The first-arg expression (hoisted ref or dynamic).
552
+ * @param schemeBinding Name of the `_s = useScheme()` binding.
553
+ * @param ctxBinding
554
+ * @param userStyleExpr Optional user style spliced from a sibling `style={…}`.
555
+ * @param insetsBinding `_i = useInsets()` binding name when the rewrite needs insets, else null.
556
+ * @param fontScaleBinding `_fs = useFontScale()` binding name — always present since every rewrite injects it.
557
+ */
558
+ function rewriteAsInteractiveBox(attributePath, opening, classNameExpr, ctxBinding, userStyleExpr) {
559
+ const originalTagExpr = jsxNameToExpression(opening.name);
560
+ const rwProperties = [
561
+ t__namespace.objectProperty(t__namespace.identifier('as'), originalTagExpr),
562
+ t__namespace.objectProperty(t__namespace.identifier('cn'), classNameExpr),
563
+ t__namespace.objectProperty(t__namespace.identifier('t'), t__namespace.identifier(ctxBinding)),
564
+ ];
565
+ if (userStyleExpr)
566
+ rwProperties.push(t__namespace.objectProperty(t__namespace.identifier('us'), userStyleExpr));
567
+ const rwAttribute = t__namespace.jsxAttribute(t__namespace.jsxIdentifier('_rw'), t__namespace.jsxExpressionContainer(t__namespace.objectExpression(rwProperties)));
568
+ // Swap the className attribute out for `_rw`, keeping it at the
569
+ // attribute's original position so any surrounding spread attrs stay
570
+ // honouring the user's intended order.
571
+ attributePath.replaceWith(rwAttribute);
572
+ opening.name = t__namespace.jsxIdentifier(INTERACTIVE_BOX);
573
+ const jsxElement = findParentJsxElement(attributePath);
574
+ if (jsxElement?.closingElement)
575
+ jsxElement.closingElement.name = t__namespace.jsxIdentifier(INTERACTIVE_BOX);
576
+ }
577
+ /**
578
+ * Walk from a JSXAttribute path up to its JSXElement ancestor.
579
+ * @param attributePath JSXAttribute path.
580
+ * @returns The enclosing JSXElement, or `null` when the shape is unexpected.
581
+ */
582
+ function findParentJsxElement(attributePath) {
583
+ const openingPath = attributePath.parentPath;
584
+ if (!openingPath)
585
+ return null;
586
+ const elementPath = openingPath.parentPath;
587
+ if (!elementPath)
588
+ return null;
589
+ const { node } = elementPath;
590
+ return t__namespace.isJSXElement(node) ? node : null;
591
+ }
592
+ /**
593
+ * Convert a JSX opening-element name (identifier or member expression)
594
+ * into a regular JS expression we can splice into the `_rw.as` object
595
+ * property. `<Animated.View>` → `Animated.View`, `<Pressable>` →
596
+ * `Pressable`.
597
+ * @param name JSXOpeningElement name node.
598
+ * @returns Equivalent identifier / member-expression node.
599
+ */
600
+ function jsxNameToExpression(name) {
601
+ if (t__namespace.isJSXIdentifier(name))
602
+ return t__namespace.identifier(name.name);
603
+ if (t__namespace.isJSXMemberExpression(name)) {
604
+ return t__namespace.memberExpression(jsxNameToExpression(name.object), t__namespace.identifier(name.property.name));
605
+ }
606
+ throw new Error(`rnwind: unsupported JSX tag shape "${name.type ?? 'unknown'}" for interactive className`);
607
+ }
608
+ /**
609
+ * Decide what the first arg of the rewritten `lookupCss(...)` call
610
+ * should be:
611
+ * - Static string literal (`"…"` or `{"…"}` or static template): tokenize,
612
+ * push literal text for the ledger, return a hoisted const reference.
613
+ * - Dynamic expression: forward the expression unchanged; runtime
614
+ * tokenizes the string result at render time.
615
+ * @param value Attribute's value node (StringLiteral or JSXExpressionContainer).
616
+ * @param hoister Hoist table.
617
+ * @param literals Output array for static literals.
618
+ * @param rewriteCtx
619
+ * @returns The first-arg expression + interact-eligibility flag, or `null`.
620
+ */
621
+ function buildFirstArgument(value, hoister, literals, rewriteCtx) {
622
+ if (t__namespace.isStringLiteral(value))
623
+ return literalResult(value.value, hoister, literals, rewriteCtx);
624
+ if (!t__namespace.isJSXExpressionContainer(value))
625
+ return null;
626
+ const { expression } = value;
627
+ if (t__namespace.isJSXEmptyExpression(expression))
628
+ return null;
629
+ if (t__namespace.isStringLiteral(expression))
630
+ return literalResult(expression.value, hoister, literals, rewriteCtx);
631
+ if (t__namespace.isTemplateLiteral(expression) && expression.expressions.length === 0 && expression.quasis[0]) {
632
+ const text = expression.quasis[0].value.cooked ?? expression.quasis[0].value.raw;
633
+ return literalResult(text, hoister, literals, rewriteCtx);
634
+ }
635
+ // Dynamic expression — can't inspect atoms at build time. Assume safe
636
+ // is possible and pull in `_i` so the runtime can resolve any
637
+ // `*-safe` class a consumer composes at runtime. The runtime fast
638
+ // path is still taken when the dynamic string resolves to a plain
639
+ // non-safe atom list (SAFE_ATOMS index check gates the slow path).
640
+ rewriteCtx.needsInsets = true;
641
+ return { expression: expression, mayBeInteractive: true, needsInsets: true };
642
+ }
643
+ /**
644
+ * Package a literal classname into a hoisted atom-array ref and scan it
645
+ * for interactive prefixes (`active:`, `focus:`) + safe-area patterns.
646
+ * Pre-scanned literals without any interactive tokens skip the (small
647
+ * but measurable) cost of injecting a `useInteract()` hook; literals
648
+ * without any `*-safe` token skip the insets arg, keeping the runtime
649
+ * fast path engaged.
650
+ * @param text Raw classname string.
651
+ * @param hoister Hoist table.
652
+ * @param literals Output array for literal-text sink.
653
+ * @param rewriteCtx Rewrite-wide state; updated when any atom needs insets.
654
+ * @returns The expression + per-rewrite flags.
655
+ */
656
+ function literalResult(text, hoister, literals, rewriteCtx) {
657
+ literals.push(text);
658
+ const atoms = tokenize(text);
659
+ const { gradientAttrs, remaining: afterGradient } = extractGradientSpec(atoms, rewriteCtx);
660
+ const { truncateAttrs, remaining: afterTruncate } = extractTextTruncateSpec(afterGradient);
661
+ const { mountHaptics, eventHaptics, remaining } = extractHapticSpec(afterTruncate, rewriteCtx);
662
+ const mayBeInteractive = remaining.some((atom) => INTERACTIVE_PREFIXES.some((prefix) => atom.startsWith(prefix)));
663
+ const needsInsets = remaining.some((atom) => SAFE_ATOM_PATTERN.test(atom));
664
+ if (needsInsets)
665
+ rewriteCtx.needsInsets = true;
666
+ return {
667
+ expression: hoister.refFor(remaining),
668
+ mayBeInteractive,
669
+ needsInsets,
670
+ gradientAttrs: gradientAttrs.length > 0 ? gradientAttrs : undefined,
671
+ truncateAttrs: truncateAttrs.length > 0 ? truncateAttrs : undefined,
672
+ mountHaptics: mountHaptics.length > 0 ? mountHaptics : undefined,
673
+ eventHaptics: eventHaptics.length > 0 ? eventHaptics : undefined,
674
+ };
675
+ }
676
+ /**
677
+ * Scan the atom list for gradient roles (direction + from/via/to
678
+ * colours), strip those atoms out, and produce the JSX attributes the
679
+ * rewrite will splice onto the opening element:
680
+ * colors={_g_<hash>} start={_gs_<hash>} end={_ge_<hash>}
681
+ * When the atom list doesn't contain a complete gradient (no direction
682
+ * OR no colour stops), the gradient atoms pass through untouched —
683
+ * they'd resolve to `{}` in the runtime anyway. This keeps the
684
+ * transform conservative.
685
+ * @param atoms Tokenised atom list from the literal.
686
+ * @param rewriteCtx Rewrite-wide state (for the hoister).
687
+ * @returns The gradient JSX attrs (possibly empty) and the non-gradient remainder.
688
+ */
689
+ function extractGradientSpec(atoms, rewriteCtx) {
690
+ const { gradientAtoms } = rewriteCtx;
691
+ if (gradientAtoms.size === 0)
692
+ return { gradientAttrs: [], remaining: atoms };
693
+ let direction = null;
694
+ let from = null;
695
+ let via = null;
696
+ let to = null;
697
+ const remaining = [];
698
+ for (const atom of atoms) {
699
+ const info = gradientAtoms.get(atom);
700
+ if (!info) {
701
+ remaining.push(atom);
702
+ continue;
703
+ }
704
+ switch (info.role) {
705
+ case 'direction': {
706
+ direction = info.dir;
707
+ break;
708
+ }
709
+ case 'from': {
710
+ from = info.color;
711
+ break;
712
+ }
713
+ case 'via': {
714
+ via = info.color;
715
+ break;
716
+ }
717
+ case 'to': {
718
+ {
719
+ to = info.color;
720
+ // No default
721
+ }
722
+ break;
723
+ }
724
+ }
725
+ // Gradient atoms deliberately drop from `remaining` — they're
726
+ // consumed at build time and don't need a runtime style slot.
727
+ }
728
+ if (direction === null || direction === 'unknown' || (from === null && to === null)) {
729
+ // No recognisable gradient — put atoms back so they at least
730
+ // attempt to resolve through lookupCss.
731
+ return { gradientAttrs: [], remaining: atoms };
732
+ }
733
+ const colors = gradientColors(from, via, to);
734
+ const points = directionToPoints(direction);
735
+ const colorsRef = rewriteCtx.gradientHoister.refForColors(colors);
736
+ const startRef = rewriteCtx.gradientHoister.refForPoint(points.start, 'start');
737
+ const endRef = rewriteCtx.gradientHoister.refForPoint(points.end, 'end');
738
+ const attributes = [
739
+ t__namespace.jsxAttribute(t__namespace.jsxIdentifier('colors'), t__namespace.jsxExpressionContainer(colorsRef)),
740
+ t__namespace.jsxAttribute(t__namespace.jsxIdentifier('start'), t__namespace.jsxExpressionContainer(startRef)),
741
+ t__namespace.jsxAttribute(t__namespace.jsxIdentifier('end'), t__namespace.jsxExpressionContainer(endRef)),
742
+ ];
743
+ return { gradientAttrs: attributes, remaining };
744
+ }
745
+ /**
746
+ * Scan the atom list for text-truncate utilities (`truncate`,
747
+ * `line-clamp-<N>`, `line-clamp-none`, `text-ellipsis`, `text-clip`),
748
+ * strip them out, and produce the JSX attributes the rewrite will
749
+ * splice onto the opening element: `numberOfLines={N}` and/or
750
+ * `ellipsizeMode="tail"|"clip"`.
751
+ *
752
+ * Merge rule mirrors Tailwind's cascade — later atoms override earlier
753
+ * ones. `numberOfLines: 0` (the `line-clamp-none` reset) suppresses
754
+ * emission entirely; a standalone `text-ellipsis` / `text-clip` with no
755
+ * companion line count also emits nothing because `ellipsizeMode`
756
+ * alone has no effect on RN `<Text>`.
757
+ * @param atoms Tokenised atom list.
758
+ * @returns The truncate JSX attrs (possibly empty) and the non-truncate remainder.
759
+ */
760
+ function extractTextTruncateSpec(atoms) {
761
+ if (!textTruncate.mayContainTextTruncate(atoms))
762
+ return { truncateAttrs: [], remaining: atoms };
763
+ let numberOfLines;
764
+ let ellipsizeMode;
765
+ const remaining = [];
766
+ for (const atom of atoms) {
767
+ const info = textTruncate.detectTextTruncate(atom);
768
+ if (!info) {
769
+ remaining.push(atom);
770
+ continue;
771
+ }
772
+ const { numberOfLines: infoLines, ellipsizeMode: infoMode } = info;
773
+ if (infoLines !== undefined)
774
+ numberOfLines = infoLines;
775
+ if (infoMode !== undefined)
776
+ ellipsizeMode = infoMode;
777
+ }
778
+ const attributes = buildTruncateAttributes(numberOfLines, ellipsizeMode);
779
+ return { truncateAttrs: attributes, remaining };
780
+ }
781
+ /**
782
+ * Assemble JSXAttribute nodes for the resolved truncate props. Drops
783
+ * `numberOfLines` when zero (reset) and drops `ellipsizeMode` when not
784
+ * paired with a positive line count — matching RN's behaviour where
785
+ * `ellipsizeMode` needs `numberOfLines` to do anything.
786
+ * @param numberOfLines Resolved clamp count, or undefined.
787
+ * @param ellipsizeMode Resolved ellipsize mode, or undefined.
788
+ * @returns Zero, one, or two JSX attributes.
789
+ */
790
+ function buildTruncateAttributes(numberOfLines, ellipsizeMode) {
791
+ const attributes = [];
792
+ if (numberOfLines !== undefined && numberOfLines > 0) {
793
+ attributes.push(t__namespace.jsxAttribute(t__namespace.jsxIdentifier('numberOfLines'), t__namespace.jsxExpressionContainer(t__namespace.numericLiteral(numberOfLines))));
794
+ if (ellipsizeMode !== undefined) {
795
+ attributes.push(t__namespace.jsxAttribute(t__namespace.jsxIdentifier('ellipsizeMode'), t__namespace.stringLiteral(ellipsizeMode)));
796
+ }
797
+ }
798
+ return attributes;
799
+ }
800
+ /**
801
+ * Map of variant-prefix → trigger. Bare atoms (no colon) resolve to
802
+ * `'mount'` through {@link extractHapticSpec}; these entries cover the
803
+ * explicit `active:` / `focus:` / `hover:` cases.
804
+ */
805
+ const HAPTIC_VARIANT_TRIGGER = {
806
+ active: 'pressIn',
807
+ focus: 'focus',
808
+ hover: 'hover',
809
+ };
810
+ /** Map a non-mount haptic trigger to the JSX event prop it chains onto. */
811
+ const HAPTIC_EVENT_PROP = {
812
+ pressIn: 'onPressIn',
813
+ pressOut: 'onPressOut',
814
+ focus: 'onFocus',
815
+ hover: 'onMouseEnter',
816
+ };
817
+ /**
818
+ * Scan atom list for haptic utilities. Bare `haptic-*` → mount trigger;
819
+ * `active:haptic-*` / `focus:haptic-*` / `hover:haptic-*` → the matching
820
+ * event trigger. Matched atoms are stripped from the remainder so the
821
+ * runtime style resolver never tries to look up `--rnwind-haptic`.
822
+ * @param atoms Post-gradient, post-truncate atom list.
823
+ * @param rewriteCtx Rewrite-wide state (for the haptic-atom map).
824
+ * @returns Mount + event haptic entries, plus the non-haptic remainder.
825
+ */
826
+ function extractHapticSpec(atoms, rewriteCtx) {
827
+ const { hapticAtoms } = rewriteCtx;
828
+ if (hapticAtoms.size === 0)
829
+ return { mountHaptics: [], eventHaptics: [], remaining: atoms };
830
+ const mountHaptics = [];
831
+ const eventHaptics = [];
832
+ const remaining = [];
833
+ for (const atom of atoms) {
834
+ const resolved = resolveHapticAtom(atom, hapticAtoms);
835
+ if (!resolved) {
836
+ remaining.push(atom);
837
+ continue;
838
+ }
839
+ if (resolved.trigger === 'mount')
840
+ mountHaptics.push(resolved.request);
841
+ else
842
+ eventHaptics.push({ request: resolved.request, trigger: resolved.trigger });
843
+ }
844
+ return { mountHaptics, eventHaptics, remaining };
845
+ }
846
+ /**
847
+ * Classify one atom against the parser's haptic map. A colon-free atom
848
+ * maps to `'mount'`; `active:` / `focus:` / `hover:` prefixes map to
849
+ * the matching event trigger. Other prefixes return `null` so they
850
+ * fall through to the regular style path.
851
+ * @param atom Atom name, possibly variant-prefixed.
852
+ * @param hapticAtoms Parser-surfaced haptic metadata.
853
+ * @returns `{request, trigger}` on match, null otherwise.
854
+ */
855
+ function resolveHapticAtom(atom, hapticAtoms) {
856
+ // Direct lookup first — Tailwind v4 registers the variant-prefixed
857
+ // class (e.g. `active:haptic-medium`) as its own rule, and the
858
+ // parser's nested-rule walk surfaces the marker under that key.
859
+ const direct = hapticAtoms.get(atom);
860
+ if (direct) {
861
+ const colon = atom.indexOf(':');
862
+ if (colon === -1)
863
+ return { request: direct, trigger: 'mount' };
864
+ const trigger = HAPTIC_VARIANT_TRIGGER[atom.slice(0, colon)];
865
+ if (trigger)
866
+ return { request: direct, trigger };
867
+ return null;
868
+ }
869
+ // Fallback — try stripping a known variant prefix and looking up
870
+ // the bare class. Handles cases where the parser only registered
871
+ // the base utility (the variant rule may be missing if only the
872
+ // bare class is otherwise used in the theme).
873
+ const colon = atom.indexOf(':');
874
+ if (colon === -1)
875
+ return null;
876
+ const prefix = atom.slice(0, colon);
877
+ const trigger = HAPTIC_VARIANT_TRIGGER[prefix];
878
+ if (!trigger)
879
+ return null;
880
+ const bare = hapticAtoms.get(atom.slice(colon + 1));
881
+ if (!bare)
882
+ return null;
883
+ return { request: bare, trigger };
884
+ }
885
+ /**
886
+ * Append mount-haptic requests to the aggregate keyed by the JSX site's
887
+ * enclosing component body. Post-traversal the transformer injects one
888
+ * `useMountHaptic(<hoisted>)` call per component.
889
+ * @param attributePath The JSXAttribute path the haptic came from.
890
+ * @param requests Mount requests gathered from this literal.
891
+ * @param rewriteCtx Rewrite-wide state.
892
+ */
893
+ function recordMountHaptics(attributePath, requests, rewriteCtx) {
894
+ const body = findComponentBody(attributePath);
895
+ if (!body)
896
+ return;
897
+ const bucket = rewriteCtx.mountByComponent.get(body.node);
898
+ if (bucket) {
899
+ bucket.push(...requests);
900
+ return;
901
+ }
902
+ rewriteCtx.mountByComponent.set(body.node, [...requests]);
903
+ }
904
+ /**
905
+ * Splice one chained event handler per event-haptic entry onto the
906
+ * JSXOpeningElement. Each handler calls `triggerHaptic(_h, <request>,
907
+ * '<trigger>')` and then forwards to any pre-existing user handler.
908
+ * @param attributePath Path of the className attribute being rewritten.
909
+ * @param opening Opening element to mutate.
910
+ * @param entries Event-haptic entries.
911
+ * @param rewriteCtx Rewrite-wide state (for the hoister).
912
+ */
913
+ function injectEventHapticHandlers(attributePath, opening, entries, rewriteCtx) {
914
+ // Make sure `_t = _r()` is in scope — haptic dispatcher reads `_t.onHaptics`.
915
+ injectContextHook(attributePath);
916
+ rewriteCtx.needsHapticsHook = true;
917
+ const byTrigger = new Map();
918
+ for (const { request, trigger } of entries) {
919
+ const list = byTrigger.get(trigger);
920
+ if (list)
921
+ list.push(request);
922
+ else
923
+ byTrigger.set(trigger, [request]);
924
+ }
925
+ for (const [trigger, requests] of byTrigger) {
926
+ const eventProperty = HAPTIC_EVENT_PROP[trigger];
927
+ const existing = extractAndDropSiblingStyle(opening, eventProperty);
928
+ const handler = buildChainedHapticHandler(rewriteCtx, requests, trigger, existing);
929
+ opening.attributes.push(t__namespace.jsxAttribute(t__namespace.jsxIdentifier(eventProperty), t__namespace.jsxExpressionContainer(handler)));
930
+ }
931
+ }
932
+ /**
933
+ * Build the inline arrow body for one chained handler — dispatch every
934
+ * request in `requests` via `triggerHaptic`, then forward the event to
935
+ * the user-supplied handler (if any) via `existing?.(event)`.
936
+ * @param rewriteCtx Rewrite-wide state.
937
+ * @param requests Requests that share this trigger.
938
+ * @param trigger Lifecycle trigger this handler fires on.
939
+ * @param existing User-supplied event handler expression, or null.
940
+ * @returns ArrowFunctionExpression ready to splice into a JSXAttribute.
941
+ */
942
+ function buildChainedHapticHandler(rewriteCtx, requests, trigger, existing) {
943
+ const eventId = t__namespace.identifier('_e');
944
+ const body = [];
945
+ for (const request of requests) {
946
+ const ref = rewriteCtx.hapticHoister.refForRequest(request);
947
+ body.push(t__namespace.expressionStatement(t__namespace.callExpression(t__namespace.identifier(TRIGGER_HAPTIC), [
948
+ t__namespace.memberExpression(t__namespace.identifier(CONTEXT_BINDING), t__namespace.identifier('onHaptics')),
949
+ ref,
950
+ t__namespace.stringLiteral(trigger),
951
+ ])));
952
+ }
953
+ if (existing) {
954
+ body.push(t__namespace.expressionStatement(t__namespace.optionalCallExpression(existing, [eventId], true)));
955
+ }
956
+ return t__namespace.arrowFunctionExpression([eventId], t__namespace.blockStatement(body));
957
+ }
958
+ /**
959
+ * Walk the aggregated `mountByComponent` map and inject a single
960
+ * `useMountHaptic(<hoisted requests>)` call per component body.
961
+ * The hoist yields a stable `const _hm_<hash>` referencing a frozen
962
+ * array of request objects.
963
+ * @param rewriteCtx Rewrite-wide state.
964
+ */
965
+ function injectMountHapticCalls(rewriteCtx) {
966
+ for (const [body, requests] of rewriteCtx.mountByComponent) {
967
+ const ref = rewriteCtx.hapticHoister.refForRequestList(requests);
968
+ const declaration = t__namespace.expressionStatement(t__namespace.callExpression(t__namespace.identifier(USE_MOUNT_HAPTIC), [ref]));
969
+ body.body.unshift(declaration);
970
+ }
971
+ }
972
+ /**
973
+ * Derive a stable cache key for one {@link HapticRequest}. Keys are
974
+ * used both for hoister interning and mount-list digesting.
975
+ * @param request Haptic request.
976
+ * @returns Canonical key text.
977
+ */
978
+ function keyForHapticRequest(request) {
979
+ if (request.kind === 'impact')
980
+ return `impact:${request.style}`;
981
+ if (request.kind === 'notification')
982
+ return `notification:${request.type}`;
983
+ return 'selection';
984
+ }
985
+ /**
986
+ * Build the haptic hoist table. Single requests and mount-request
987
+ * lists each get their own module-scope frozen const so component
988
+ * bodies only reference stable identifiers — no per-render allocation.
989
+ * @returns HapticHoister API.
990
+ */
991
+ function createHapticHoister() {
992
+ const pool = createInternPool();
993
+ const refForRequest = (request) => pool.intern('_hr', `req:${keyForHapticRequest(request)}`, { kind: 'request', request });
994
+ const refForRequestList = (requests) => pool.intern('_hm', `list:${requests.map((request) => keyForHapticRequest(request)).join('|')}`, {
995
+ kind: 'list',
996
+ requests,
997
+ });
998
+ return { refForRequest, refForRequestList, entries: pool.entries };
999
+ }
1000
+ /**
1001
+ * Generic intern-pool: one shared cache for module-scope consts keyed
1002
+ * by an arbitrary string. Returns a stable `t.Identifier` per key and
1003
+ * records `{name → entry}` for the emitter pass.
1004
+ * @returns Intern API.
1005
+ */
1006
+ function createInternPool() {
1007
+ const byKey = new Map();
1008
+ const entries = new Map();
1009
+ const intern = (prefix, key, entry) => {
1010
+ const existing = byKey.get(key);
1011
+ if (existing)
1012
+ return existing;
1013
+ const hash = node_crypto.createHash('sha256').update(key).digest('hex').slice(0, 12);
1014
+ const name = `${prefix}_${hash}`;
1015
+ const ident = t__namespace.identifier(name);
1016
+ byKey.set(key, ident);
1017
+ entries.set(name, entry);
1018
+ return ident;
1019
+ };
1020
+ return { intern, entries };
1021
+ }
1022
+ /**
1023
+ * Emit `const _hr_<hash> = Object.freeze({...})` and `const _hm_<hash>
1024
+ * = Object.freeze([{...}, ...])` statements at module scope — one per
1025
+ * hoister entry.
1026
+ * @param ast Babel File AST.
1027
+ * @param entries Hoister entries.
1028
+ */
1029
+ function injectHapticConsts(ast, entries) {
1030
+ const declarations = [];
1031
+ for (const [name, entry] of entries) {
1032
+ if (entry.kind === 'request') {
1033
+ declarations.push(t__namespace.variableDeclaration('const', [
1034
+ t__namespace.variableDeclarator(t__namespace.identifier(name), freezeExpression(requestLiteral(entry.request))),
1035
+ ]));
1036
+ }
1037
+ else {
1038
+ declarations.push(t__namespace.variableDeclaration('const', [
1039
+ t__namespace.variableDeclarator(t__namespace.identifier(name), freezeExpression(t__namespace.arrayExpression(entry.requests.map((request) => freezeExpression(requestLiteral(request)))))),
1040
+ ]));
1041
+ }
1042
+ }
1043
+ ast.program.body.unshift(...declarations);
1044
+ }
1045
+ /**
1046
+ * Build an `Object.freeze(...)` call around the given expression.
1047
+ * @param value Expression to freeze.
1048
+ * @returns CallExpression node.
1049
+ */
1050
+ function freezeExpression(value) {
1051
+ return t__namespace.callExpression(t__namespace.memberExpression(t__namespace.identifier('Object'), t__namespace.identifier('freeze')), [value]);
1052
+ }
1053
+ /**
1054
+ * Build an object-literal representation of a {@link HapticRequest} —
1055
+ * `{kind: 'impact', style: 'Light'}` etc.
1056
+ * @param request Haptic request.
1057
+ * @returns ObjectExpression node.
1058
+ */
1059
+ function requestLiteral(request) {
1060
+ const properties = [
1061
+ t__namespace.objectProperty(t__namespace.identifier('kind'), t__namespace.stringLiteral(request.kind)),
1062
+ ];
1063
+ if (request.kind === 'impact')
1064
+ properties.push(t__namespace.objectProperty(t__namespace.identifier('style'), t__namespace.stringLiteral(request.style)));
1065
+ else if (request.kind === 'notification')
1066
+ properties.push(t__namespace.objectProperty(t__namespace.identifier('type'), t__namespace.stringLiteral(request.type)));
1067
+ return t__namespace.objectExpression(properties);
1068
+ }
1069
+ /**
1070
+ * Normalise the `from/via/to` triple into the array
1071
+ * `<LinearGradient colors={…}>` expects: drop `null` entries while
1072
+ * keeping the source order.
1073
+ * @param from Hex colour for `from-*`, or null.
1074
+ * @param via Hex colour for `via-*`, or null.
1075
+ * @param to Hex colour for `to-*`, or null.
1076
+ * @returns Colour array (at least one entry guaranteed by the caller).
1077
+ */
1078
+ function gradientColors(from, via, to) {
1079
+ const out = [];
1080
+ if (from !== null)
1081
+ out.push(from);
1082
+ if (via !== null)
1083
+ out.push(via);
1084
+ if (to !== null)
1085
+ out.push(to);
1086
+ return out;
1087
+ }
1088
+ /**
1089
+ * Map Tailwind's stock direction tag to the `(start, end)` pair of
1090
+ * unit-square points expo-linear-gradient expects. Pure constants —
1091
+ * the same as NativeWind and the wider RN-gradient community.
1092
+ * @param dir Compact direction tag from the parser.
1093
+ * @returns Start + end point records.
1094
+ */
1095
+ function directionToPoints(dir) {
1096
+ switch (dir) {
1097
+ case 'to-r': {
1098
+ return { start: { x: 0, y: 0.5 }, end: { x: 1, y: 0.5 } };
1099
+ }
1100
+ case 'to-l': {
1101
+ return { start: { x: 1, y: 0.5 }, end: { x: 0, y: 0.5 } };
1102
+ }
1103
+ case 'to-t': {
1104
+ return { start: { x: 0.5, y: 1 }, end: { x: 0.5, y: 0 } };
1105
+ }
1106
+ case 'to-b': {
1107
+ return { start: { x: 0.5, y: 0 }, end: { x: 0.5, y: 1 } };
1108
+ }
1109
+ case 'to-tr': {
1110
+ return { start: { x: 0, y: 1 }, end: { x: 1, y: 0 } };
1111
+ }
1112
+ case 'to-tl': {
1113
+ return { start: { x: 1, y: 1 }, end: { x: 0, y: 0 } };
1114
+ }
1115
+ case 'to-br': {
1116
+ return { start: { x: 0, y: 0 }, end: { x: 1, y: 1 } };
1117
+ }
1118
+ case 'to-bl': {
1119
+ return { start: { x: 1, y: 0 }, end: { x: 0, y: 1 } };
1120
+ }
1121
+ default: {
1122
+ return { start: { x: 0, y: 0.5 }, end: { x: 1, y: 0.5 } };
1123
+ }
1124
+ }
1125
+ }
1126
+ /**
1127
+ * Look for a sibling style attribute on the same JSXOpeningElement, drop
1128
+ * it, and return its expression for the caller to pass as the `lookupCss`
1129
+ * third arg. The attribute name is parameterised so the prefix path can
1130
+ * pull `contentContainerStyle` (et al.) instead of plain `style`.
1131
+ * @param parent JSXOpeningElement containing the className we're rewriting.
1132
+ * @param styleProp The exact sibling attribute name to look for.
1133
+ * @param styleProperty
1134
+ * @returns Expression from the dropped attribute, or `null`.
1135
+ */
1136
+ function extractAndDropSiblingStyle(parent, styleProperty) {
1137
+ const { attributes } = parent;
1138
+ for (let index = 0; index < attributes.length; index += 1) {
1139
+ const attribute = attributes[index];
1140
+ if (!t__namespace.isJSXAttribute(attribute))
1141
+ continue;
1142
+ if (!t__namespace.isJSXIdentifier(attribute.name) || attribute.name.name !== styleProperty)
1143
+ continue;
1144
+ const { value } = attribute;
1145
+ if (!value || !t__namespace.isJSXExpressionContainer(value))
1146
+ return null;
1147
+ const { expression } = value;
1148
+ if (t__namespace.isJSXEmptyExpression(expression))
1149
+ return null;
1150
+ attributes.splice(index, 1);
1151
+ return expression;
1152
+ }
1153
+ return null;
1154
+ }
1155
+ const INJECTED = new WeakSet();
1156
+ /**
1157
+ * Walk up from the rewrite site to the nearest enclosing function
1158
+ * component and inject `const _t = _r()` at the top of its body. This
1159
+ * is the SINGLE rnwind context binding — `_t` carries scheme,
1160
+ * fontScale, insets, etc. Idempotent per component.
1161
+ * @param path Path of any node inside the component's JSX.
1162
+ * @returns The binding name (`_t`).
1163
+ */
1164
+ function injectContextHook(path) {
1165
+ const componentBody = findComponentBody(path);
1166
+ if (!componentBody)
1167
+ return CONTEXT_BINDING;
1168
+ if (INJECTED.has(componentBody.node))
1169
+ return CONTEXT_BINDING;
1170
+ INJECTED.add(componentBody.node);
1171
+ const declaration = t__namespace.variableDeclaration('const', [
1172
+ t__namespace.variableDeclarator(t__namespace.identifier(CONTEXT_BINDING), t__namespace.callExpression(t__namespace.identifier(USE_RNWIND_INTERNAL), [])),
1173
+ ]);
1174
+ componentBody.unshiftContainer('body', declaration);
1175
+ return CONTEXT_BINDING;
1176
+ }
1177
+ /**
1178
+ * Whether `path` sits inside a recognised function component — i.e.
1179
+ * {@link injectContextHook} would find a body to host `const _t =
1180
+ * useR_()`. Pure lookup that mirrors {@link findComponentBody}'s walk
1181
+ * but performs NO body promotion, so a caller can bail before mutating
1182
+ * when the answer is no.
1183
+ * @param path Rewrite-site path.
1184
+ * @returns True when an enclosing component function exists.
1185
+ */
1186
+ function hasComponentBody(path) {
1187
+ let current = path;
1188
+ while (current) {
1189
+ const fn = current.findParent((parent) => parent.isFunction());
1190
+ if (!fn)
1191
+ return false;
1192
+ if (isComponentFunction(fn))
1193
+ return true;
1194
+ current = fn;
1195
+ }
1196
+ return false;
1197
+ }
1198
+ /**
1199
+ * Walk up from `path` to the nearest recognised function component.
1200
+ * Accepts:
1201
+ * - `function Capital() {}` declarations.
1202
+ * - `const Capital = () => …` / `const Capital = function () {}` bindings.
1203
+ * - `forwardRef(…)` / `memo(…)` argument callbacks.
1204
+ * - `export default function () {}`.
1205
+ *
1206
+ * Arrow components with expression bodies get promoted to block bodies
1207
+ * so the hook can be `unshift`ed.
1208
+ * @param path Starting path.
1209
+ * @returns BlockStatement path of the component's body, or `null`.
1210
+ */
1211
+ function findComponentBody(path) {
1212
+ let current = path;
1213
+ while (current) {
1214
+ const fn = current.findParent((parent) => parent.isFunction());
1215
+ if (!fn)
1216
+ return null;
1217
+ if (isComponentFunction(fn))
1218
+ return ensureBlockBody(fn);
1219
+ current = fn;
1220
+ }
1221
+ return null;
1222
+ }
1223
+ /**
1224
+ * Classify a function path as a React component per the three accepted
1225
+ * shapes (PascalCase decl, PascalCase var assignment, forwardRef/memo
1226
+ * argument, default export).
1227
+ * @param fn Function-like path.
1228
+ * @returns Whether the path is a React function component.
1229
+ */
1230
+ function isComponentFunction(fn) {
1231
+ if (fn.isFunctionDeclaration()) {
1232
+ const { id } = fn.node;
1233
+ if (!id)
1234
+ return isExportDefaultValue(fn);
1235
+ return isPascalCase(id.name);
1236
+ }
1237
+ if (fn.isArrowFunctionExpression() || fn.isFunctionExpression()) {
1238
+ return isAssignedToPascalCase(fn) || isHocArgument(fn) || isExportDefaultValue(fn);
1239
+ }
1240
+ return false;
1241
+ }
1242
+ /**
1243
+ * Whether this function is the value of an `export default`.
1244
+ * @param fn Babel path pointing at the function node.
1245
+ * @returns True when the node is directly the default export value.
1246
+ */
1247
+ function isExportDefaultValue(fn) {
1248
+ const { parent } = fn;
1249
+ if (t__namespace.isExportDefaultDeclaration(parent))
1250
+ return parent.declaration === fn.node;
1251
+ return false;
1252
+ }
1253
+ /**
1254
+ * Whether this arrow/function-expression is the init of `const Capital = …`.
1255
+ * @param fn Babel path pointing at the function node.
1256
+ * @returns True when the enclosing declarator's id starts with an uppercase letter.
1257
+ */
1258
+ function isAssignedToPascalCase(fn) {
1259
+ const { parent } = fn;
1260
+ if (!t__namespace.isVariableDeclarator(parent))
1261
+ return false;
1262
+ if (!t__namespace.isIdentifier(parent.id))
1263
+ return false;
1264
+ return isPascalCase(parent.id.name);
1265
+ }
1266
+ /**
1267
+ * Whether this fn is the first argument to `forwardRef(...)` / `memo(...)`.
1268
+ * @param fn Babel path pointing at the function node.
1269
+ * @returns True when wrapped by a recognized React HOC call.
1270
+ */
1271
+ function isHocArgument(fn) {
1272
+ const { parent } = fn;
1273
+ if (!t__namespace.isCallExpression(parent))
1274
+ return false;
1275
+ if (parent.arguments[0] !== fn.node)
1276
+ return false;
1277
+ const { callee } = parent;
1278
+ if (!t__namespace.isIdentifier(callee))
1279
+ return false;
1280
+ return callee.name === 'forwardRef' || callee.name === 'memo';
1281
+ }
1282
+ /**
1283
+ * Identifier-starts-with-uppercase — Conventional React component marker.
1284
+ * @param name Identifier text.
1285
+ * @returns True when the first character is `A`–`Z`.
1286
+ */
1287
+ function isPascalCase(name) {
1288
+ const first = name.charAt(0);
1289
+ return first >= 'A' && first <= 'Z';
1290
+ }
1291
+ /**
1292
+ * Promote an expression-bodied arrow to a block so we can unshift statements in.
1293
+ * @param fn Babel path at the function / arrow whose body should be a block.
1294
+ * @returns The path, mutated in place when the body was an expression.
1295
+ */
1296
+ function ensureBlockBody(fn) {
1297
+ const bodyPath = fn.get('body');
1298
+ if (Array.isArray(bodyPath))
1299
+ throw new Error('rnwind: unexpected multi-body function node');
1300
+ if (bodyPath.isBlockStatement())
1301
+ return bodyPath;
1302
+ const node = bodyPath.node;
1303
+ bodyPath.replaceWith(t__namespace.blockStatement([t__namespace.returnStatement(node)]));
1304
+ return bodyPath;
1305
+ }
1306
+ /**
1307
+ * Build a per-file hoist table. Every unique source-order atom list gets
1308
+ * one module-scope `const _c_<hash> = Object.freeze(['a', 'b'])`. Order
1309
+ * is part of the hash key — `className="a b"` and `className="b a"`
1310
+ * intentionally produce different hoisted consts because RN's style
1311
+ * flatten is order-dependent (later atoms override earlier ones for
1312
+ * conflicting props). Canonicalizing by sort would collapse
1313
+ * `opacity-100 opacity-0` and `opacity-0 opacity-100` to the same atom
1314
+ * list and silently break the user's intended last-wins override.
1315
+ * @returns Hoister API.
1316
+ */
1317
+ function createHoister() {
1318
+ const byKey = new Map();
1319
+ const entries = new Map();
1320
+ const refFor = (atoms) => {
1321
+ const ordered = [...atoms];
1322
+ const canonical = ordered.join('\0');
1323
+ const hit = byKey.get(canonical);
1324
+ if (hit)
1325
+ return t__namespace.identifier(hit.name);
1326
+ const hash = node_crypto.createHash('sha256').update(canonical).digest('hex').slice(0, 12);
1327
+ const name = `_c_${hash}`;
1328
+ byKey.set(canonical, { name, atoms: ordered });
1329
+ entries.set(name, ordered);
1330
+ return t__namespace.identifier(name);
1331
+ };
1332
+ return { refFor, entries };
1333
+ }
1334
+ /**
1335
+ * Build the gradient hoist table. Colour arrays and `(x,y)` point
1336
+ * records each get their own module-scope `const _g_<hash>` so the
1337
+ * JSX site references a stable identity — `<LinearGradient
1338
+ * colors={_g_hash}>`'s prop never changes across renders, which lets
1339
+ * React's prop-diff short-circuit and keeps native-side gradient
1340
+ * rebuilds off the hot path.
1341
+ * @returns GradientHoister API.
1342
+ */
1343
+ function createGradientHoister() {
1344
+ const pool = createInternPool();
1345
+ const refForColors = (colors) => pool.intern('_g', `colors:${colors.join('|')}`, { kind: 'colors', colors });
1346
+ const refForPoint = (point, role) => {
1347
+ const prefix = role === 'start' ? '_gs' : '_ge';
1348
+ return pool.intern(prefix, `point:${point.x},${point.y}`, { kind: 'point', point });
1349
+ };
1350
+ return { refForColors, refForPoint, entries: pool.entries };
1351
+ }
1352
+ /**
1353
+ * Prepend the runtime + style imports to the file's program body.
1354
+ * Only the specifiers actually used by the rewritten code are added
1355
+ * — a file with only interactive rewrites skips `lookupCss` entirely
1356
+ * (it lives inside InteractiveBox) and vice versa.
1357
+ * @param ast File AST.
1358
+ * @param flags Which runtime symbols the rewritten code references.
1359
+ * @param styleSpecifiers Side-effect import specifiers (style.js + keyframes.js).
1360
+ */
1361
+ function prependRuntimeImports(ast, flags, styleSpecifiers) {
1362
+ const heads = [];
1363
+ for (const specifier of styleSpecifiers) {
1364
+ heads.push(t__namespace.importDeclaration([], t__namespace.stringLiteral(specifier)));
1365
+ }
1366
+ if (flags.touched) {
1367
+ heads.push(t__namespace.importDeclaration(buildRuntimeSpecifiers(flags), t__namespace.stringLiteral(RUNTIME_MODULE)));
1368
+ }
1369
+ if (heads.length > 0)
1370
+ ast.program.body.unshift(...heads);
1371
+ }
1372
+ /**
1373
+ * Build the import specifiers for the `rnwind` runtime module — only
1374
+ * symbols the rewritten code actually references. Extracted from
1375
+ * {@link prependRuntimeImports} to keep cognitive complexity low.
1376
+ * @param flags Per-file usage flags.
1377
+ * @returns The import specifiers to splice into the runtime import.
1378
+ */
1379
+ function buildRuntimeSpecifiers(flags) {
1380
+ const specifiers = [];
1381
+ const named = (name) => {
1382
+ specifiers.push(t__namespace.importSpecifier(t__namespace.identifier(name), t__namespace.identifier(name)));
1383
+ };
1384
+ if (flags.usedLookupCss)
1385
+ named(LOOKUP_CSS);
1386
+ named(USE_RNWIND_INTERNAL);
1387
+ if (flags.usedMountHaptic)
1388
+ named(USE_MOUNT_HAPTIC);
1389
+ if (flags.usedTriggerHaptic)
1390
+ named(TRIGGER_HAPTIC);
1391
+ if (flags.usedInteractiveBox)
1392
+ named(INTERACTIVE_BOX);
1393
+ return specifiers;
1394
+ }
1395
+ /**
1396
+ * Splice hoisted `const _c_<hash> = ['flex-1', 'bg-primary', ...]`
1397
+ * atom-list declarations into the file right after the imports so
1398
+ * every JSX rewrite site sees them in scope.
1399
+ *
1400
+ * The JSX site references the const as `lookupCss(_c0, _s, userStyle,
1401
+ * …)`. The runtime caches its resolved style array per
1402
+ * (hoist, scheme, stateIndex) against a global version counter, so
1403
+ * subsequent renders return the SAME array reference — zero
1404
+ * allocation on the hot path.
1405
+ * @param ast File AST.
1406
+ * @param entries Hoist table (const name → atom names).
1407
+ */
1408
+ function injectHoistedConsts(ast, entries) {
1409
+ const decls = [];
1410
+ for (const [name, atoms] of entries) {
1411
+ const array = t__namespace.arrayExpression(atoms.map((atom) => t__namespace.stringLiteral(atom)));
1412
+ decls.push(t__namespace.variableDeclaration('const', [t__namespace.variableDeclarator(t__namespace.identifier(name), array)]));
1413
+ }
1414
+ spliceAfterImports(ast, decls);
1415
+ }
1416
+ /**
1417
+ * Splice gradient const declarations after the imports. Each entry is
1418
+ * either `colors` (frozen string array) or a `point` ({x, y} object
1419
+ * literal) so `<LinearGradient>` gets a stable ref for every gradient
1420
+ * shape.
1421
+ * @param ast File AST to mutate.
1422
+ * @param entries Gradient-hoister entries.
1423
+ */
1424
+ function injectGradientConsts(ast, entries) {
1425
+ const decls = [];
1426
+ for (const [name, entry] of entries) {
1427
+ const init = entry.kind === 'colors'
1428
+ ? t__namespace.callExpression(t__namespace.memberExpression(t__namespace.identifier('Object'), t__namespace.identifier('freeze')), [
1429
+ t__namespace.arrayExpression(entry.colors.map((c) => t__namespace.stringLiteral(c))),
1430
+ ])
1431
+ : t__namespace.callExpression(t__namespace.memberExpression(t__namespace.identifier('Object'), t__namespace.identifier('freeze')), [
1432
+ t__namespace.objectExpression([
1433
+ t__namespace.objectProperty(t__namespace.identifier('x'), t__namespace.numericLiteral(entry.point.x)),
1434
+ t__namespace.objectProperty(t__namespace.identifier('y'), t__namespace.numericLiteral(entry.point.y)),
1435
+ ]),
1436
+ ]);
1437
+ decls.push(t__namespace.variableDeclaration('const', [t__namespace.variableDeclarator(t__namespace.identifier(name), init)]));
1438
+ }
1439
+ spliceAfterImports(ast, decls);
1440
+ }
1441
+ /**
1442
+ * Insert a block of declarations right after the last import in the
1443
+ * program body. Shared helper for atom-hoist and gradient-hoist.
1444
+ * @param ast File AST.
1445
+ * @param decls Declarations to splice in (already-built statements).
1446
+ */
1447
+ function spliceAfterImports(ast, decls) {
1448
+ if (decls.length === 0)
1449
+ return;
1450
+ const { body } = ast.program;
1451
+ let index = 0;
1452
+ while (index < body.length && t__namespace.isImportDeclaration(body[index]))
1453
+ index += 1;
1454
+ body.splice(index, 0, ...decls);
1455
+ }
1456
+ /**
1457
+ * Tokenize a classname literal — split on whitespace, drop empties.
1458
+ * Mirrors what Tailwind + the runtime tokenizer expect.
1459
+ * @param literal Raw classname text.
1460
+ * @returns Atom names in document order.
1461
+ */
1462
+ function tokenize(literal) {
1463
+ const out = [];
1464
+ for (const piece of literal.split(/\s+/)) {
1465
+ if (piece.length > 0)
1466
+ out.push(piece);
1467
+ }
1468
+ return out;
1469
+ }
1470
+
1471
+ exports.transformAst = transformAst;
1472
+ //# sourceMappingURL=transform-ast.cjs.map