rnwind 0.0.1 → 0.0.2

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 +164 -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 +100 -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 +96 -0
  36. package/lib/cjs/core/parser/length.cjs.map +1 -0
  37. package/lib/cjs/core/parser/length.d.ts +48 -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 +156 -0
  51. package/lib/cjs/core/parser/shorthand.cjs.map +1 -0
  52. package/lib/cjs/core/parser/shorthand.d.ts +61 -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 +414 -0
  57. package/lib/cjs/core/parser/theme-vars.cjs.map +1 -0
  58. package/lib/cjs/core/parser/theme-vars.d.ts +61 -0
  59. package/lib/cjs/core/parser/tokens.cjs +304 -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 +1567 -0
  66. package/lib/cjs/core/parser/tw-parser.cjs.map +1 -0
  67. package/lib/cjs/core/parser/tw-parser.d.ts +194 -0
  68. package/lib/cjs/core/parser/types.d.ts +37 -0
  69. package/lib/cjs/core/parser/typography-dispatcher.cjs +93 -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 +397 -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 +251 -0
  93. package/lib/cjs/metro/state.cjs.map +1 -0
  94. package/lib/cjs/metro/state.d.ts +72 -0
  95. package/lib/cjs/metro/transform-ast.cjs +1255 -0
  96. package/lib/cjs/metro/transform-ast.cjs.map +1 -0
  97. package/lib/cjs/metro/transform-ast.d.ts +73 -0
  98. package/lib/cjs/metro/transformer.cjs +345 -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 +57 -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 +162 -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 +98 -0
  172. package/lib/esm/core/parser/layout-dispatcher.mjs.map +1 -0
  173. package/lib/esm/core/parser/length.d.ts +48 -0
  174. package/lib/esm/core/parser/length.mjs +90 -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 +61 -0
  189. package/lib/esm/core/parser/shorthand.mjs +148 -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 +61 -0
  195. package/lib/esm/core/parser/theme-vars.mjs +409 -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 +298 -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 +194 -0
  204. package/lib/esm/core/parser/tw-parser.mjs +1565 -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 +91 -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 +395 -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 +72 -0
  231. package/lib/esm/metro/state.mjs +243 -0
  232. package/lib/esm/metro/state.mjs.map +1 -0
  233. package/lib/esm/metro/transform-ast.d.ts +73 -0
  234. package/lib/esm/metro/transform-ast.mjs +1234 -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 +322 -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 +57 -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 +79 -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 +157 -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 +92 -0
  291. package/src/core/parser/length.ts +100 -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 +152 -0
  297. package/src/core/parser/text-truncate.ts +79 -0
  298. package/src/core/parser/theme-vars.ts +412 -0
  299. package/src/core/parser/tokens.ts +286 -0
  300. package/src/core/parser/transform.ts +195 -0
  301. package/src/core/parser/tw-parser.ts +1709 -0
  302. package/src/core/parser/types.ts +45 -0
  303. package/src/core/parser/typography-dispatcher.ts +83 -0
  304. package/src/core/parser/typography.ts +83 -0
  305. package/src/core/style-builder/build-style.ts +442 -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 +257 -0
  313. package/src/metro/transform-ast.ts +1498 -0
  314. package/src/metro/transformer.ts +347 -0
  315. package/src/metro/warn-unknown-classes.ts +79 -0
  316. package/src/metro/with-config.ts +229 -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,404 @@
1
+ /**
2
+ * Detect `env(safe-area-inset-*)` usage in a lightningcss TokenOrValue
3
+ * tree and convert the containing declaration into a runtime-resolved
4
+ * {@link SafeAreaMarker}.
5
+ *
6
+ * Three shapes the NativeWind-compatible `*-safe` utilities produce
7
+ * through Tailwind v4's compiler:
8
+ * - Pure `env(safe-area-inset-top)` → `{ __safe: 't' }`
9
+ * - `max(env(safe-area-inset-top), 16px)` → `{ __safe: 't', or: 16 }`
10
+ * - `calc(env(safe-area-inset-top) + 16px)` → `{ __safe: 't', offset: 16 }`
11
+ *
12
+ * The `h-screen-safe` / `min-h-screen-safe` / `max-h-screen-safe` shape
13
+ * — `calc(100vh - (env(top) + env(bottom)))` — reports as
14
+ * `{ __safe: 'screen-minus-y' }`; the runtime resolves against
15
+ * `Dimensions.get('window').height` minus the current top + bottom
16
+ * insets.
17
+ *
18
+ * Unrecognised compound shapes (e.g. `env(safe-area-inset-top, 12px)`
19
+ * with a fallback) return `null` so the caller falls back to the
20
+ * regular token-serialisation path.
21
+ */
22
+
23
+ import type { TokenOrValue } from 'lightningcss'
24
+ import type { SafeAreaMarker } from './types'
25
+
26
+ /** Map UA env names to the compact side tag used in the marker. */
27
+ const SIDE_TAG: Record<string, SafeAreaMarker['__safe']> = {
28
+ 'safe-area-inset-top': 't',
29
+ 'safe-area-inset-right': 'r',
30
+ 'safe-area-inset-bottom': 'b',
31
+ 'safe-area-inset-left': 'l',
32
+ }
33
+
34
+ /** Rem → px factor used across the parser (matches `tokens.ts`). */
35
+ const REM_TO_PX = 16
36
+
37
+ /**
38
+ * Theme-var lookup table we optionally receive. Values are stored as
39
+ * raw CSS value strings (`'0.25rem'`, `'#fff'`, …); the safe-area
40
+ * detector only needs the ones that resolve to lengths.
41
+ */
42
+ type ThemeVars = ReadonlyMap<string, string>
43
+
44
+ /**
45
+ * Try to compile a token list into a {@link SafeAreaMarker}. Returns
46
+ * `null` when the tokens don't match any recognised safe-area pattern
47
+ * — the caller should fall back to the regular unparsed-value path.
48
+ * @param tokens Declaration value token list from lightningcss.
49
+ * @param themeVars
50
+ * @returns Marker object, or `null`.
51
+ */
52
+ function detectSafeAreaMarker(tokens: readonly TokenOrValue[], themeVars?: ThemeVars): SafeAreaMarker | null {
53
+ const nonWs = stripWhitespace(tokens)
54
+ if (nonWs.length === 0) return null
55
+
56
+ // Shape 1: pure env(safe-area-inset-*)
57
+ if (nonWs.length === 1) {
58
+ const side = envSide(nonWs[0]!)
59
+ if (side !== null) return { __safe: side }
60
+ }
61
+
62
+ // Shape 2 / 3: max(env(...), n) or calc(env(...) + n)
63
+ if (nonWs.length === 1 && isFunction(nonWs[0]!)) {
64
+ const inner = functionInner(nonWs[0]!)
65
+ if (inner) {
66
+ const marker = tryFromFunction(inner.name, inner.args, themeVars)
67
+ if (marker) return marker
68
+ }
69
+ }
70
+
71
+ return null
72
+ }
73
+
74
+ /**
75
+ * Short-circuit whether this token is a `function` token.
76
+ * @param token
77
+ */
78
+ function isFunction(token: TokenOrValue): boolean {
79
+ return token.type === 'function'
80
+ }
81
+
82
+ /**
83
+ * Pull `(name, args-without-whitespace)` out of a function token.
84
+ * @param token A function-type TokenOrValue.
85
+ * @returns Inner record, or null when the shape is unexpected.
86
+ */
87
+ function functionInner(token: TokenOrValue): { name: string; args: readonly TokenOrValue[] } | null {
88
+ if (token.type !== 'function') return null
89
+ return { name: token.value.name, args: token.value.arguments }
90
+ }
91
+
92
+ /**
93
+ * Pattern-match `max(env(...), n)` / `calc(env(...) + n)` / nested
94
+ * screen-minus-y calc. Operates on the function's already-whitespace-
95
+ * trimmed arg list so every branch can index by position.
96
+ * @param name Function name (`max` / `calc`).
97
+ * @param args Raw argument tokens (whitespace still present).
98
+ * @param themeVars
99
+ * @returns Marker, or null when not recognised.
100
+ */
101
+ function tryFromFunction(name: string, args: readonly TokenOrValue[], themeVars: ThemeVars | undefined): SafeAreaMarker | null {
102
+ if (name === 'max') return tryMax(args, themeVars)
103
+ if (name === 'calc') return tryCalc(args, themeVars)
104
+ return null
105
+ }
106
+
107
+ /**
108
+ * `max(env(safe-area-inset-*), <length>)` — Tailwind v4's `*-safe-or-n`
109
+ * shape. Optionally `max(env(...), calc(var(--spacing) * n))` — the
110
+ * calc inside has already been resolved to a bare length by Tailwind's
111
+ * compiler step.
112
+ * @param args Whitespace-preserving argument tokens.
113
+ * @param themeVars
114
+ * @returns Marker, or null.
115
+ */
116
+ function tryMax(args: readonly TokenOrValue[], themeVars: ThemeVars | undefined): SafeAreaMarker | null {
117
+ const parts = splitTopLevelComma(args)
118
+ if (parts.length !== 2) return null
119
+ const firstToken = onlyNonWhitespace(parts[0]!)
120
+ if (!firstToken) return null
121
+ const firstSide = envSide(firstToken)
122
+ if (firstSide === null) return null
123
+ const rhs = coerceLengthPx(parts[1]!, themeVars)
124
+ if (rhs === null) return null
125
+ return { __safe: firstSide, or: rhs }
126
+ }
127
+
128
+ /**
129
+ * Recognise:
130
+ * - `calc(env(safe-area-inset-*) + n)` — `*-safe-offset-n`
131
+ * - `calc(100vh - (env(...-top) + env(...-bottom)))` — `h-screen-safe`
132
+ * @param args Whitespace-preserving calc argument tokens.
133
+ * @param themeVars
134
+ * @returns Marker, or null.
135
+ */
136
+ function tryCalc(args: readonly TokenOrValue[], themeVars: ThemeVars | undefined): SafeAreaMarker | null {
137
+ const [signIndex, sign] = findTopLevelPlusOrMinus(args)
138
+ if (signIndex === -1) return reduceNestedCalc(args, themeVars)
139
+ const lhs = stripWhitespace(args.slice(0, signIndex))
140
+ const rhs = stripWhitespace(args.slice(signIndex + 1))
141
+ const offset = matchOffset(lhs, rhs, sign, themeVars)
142
+ if (offset) return offset
143
+ if (sign === '-' && isViewportHeightHundred(lhs) && isParenthesisedTopBottomSum(rhs)) return { __safe: 'screen-minus-y' }
144
+ return null
145
+ }
146
+
147
+ /**
148
+ * Fall-through arm of {@link tryCalc}: when there's no +/− at the calc
149
+ * body's top level, try to interpret the whole body as a nested
150
+ * function (e.g. a bare `calc(max(...))`).
151
+ * @param args Calc arguments.
152
+ * @param themeVars Optional theme-vars table for length resolution.
153
+ * @returns Marker or null.
154
+ */
155
+ function reduceNestedCalc(args: readonly TokenOrValue[], themeVars: ThemeVars | undefined): SafeAreaMarker | null {
156
+ const inner = onlyNonWhitespace(args)
157
+ if (!inner) return null
158
+ return detectSafeAreaMarker([inner], themeVars)
159
+ }
160
+
161
+ /**
162
+ * Try to match `calc(env(side) ± amount)` against the two-side split of
163
+ * the calc body. Returns a marker with positive / negative `offset`
164
+ * based on the sign, or null when the shape doesn't fit.
165
+ * @param lhs Left-hand tokens (whitespace already stripped).
166
+ * @param rhs Right-hand tokens (whitespace already stripped).
167
+ * @param sign The `+` or `-` delim captured between them.
168
+ * @param themeVars Optional theme-vars table.
169
+ * @returns Offset marker, or null.
170
+ */
171
+ function matchOffset(
172
+ lhs: readonly TokenOrValue[],
173
+ rhs: readonly TokenOrValue[],
174
+ sign: '+' | '-' | null,
175
+ themeVars: ThemeVars | undefined,
176
+ ): SafeAreaMarker | null {
177
+ if (!sign || lhs.length !== 1 || rhs.length !== 1) return null
178
+ const side = envSide(lhs[0]!)
179
+ if (side === null) return null
180
+ const amount = coerceLengthPx([rhs[0]!], themeVars)
181
+ if (amount === null) return null
182
+ return { __safe: side, offset: sign === '+' ? amount : -amount }
183
+ }
184
+
185
+ /**
186
+ * Check whether a token is the env() that names one of the four safe-area sides.
187
+ * @param token One TokenOrValue.
188
+ * @returns The compact side tag, or `null` when the token isn't a safe-area env reference.
189
+ */
190
+ function envSide(token: TokenOrValue): SafeAreaMarker['__safe'] | null {
191
+ if (token.type !== 'env') return null
192
+ const { name } = token.value
193
+ if (name.type !== 'ua') return null
194
+ return SIDE_TAG[name.value] ?? null
195
+ }
196
+
197
+ /**
198
+ * Drop whitespace / comment tokens from a list so downstream branches
199
+ * can pattern-match by index without counting spaces.
200
+ * @param tokens Raw token list.
201
+ * @returns Copy with whitespace tokens removed.
202
+ */
203
+ function stripWhitespace(tokens: readonly TokenOrValue[]): readonly TokenOrValue[] {
204
+ const out: TokenOrValue[] = []
205
+ for (const token of tokens) {
206
+ if (isWhitespaceToken(token)) continue
207
+ out.push(token)
208
+ }
209
+ return out
210
+ }
211
+
212
+ /**
213
+ * Like {@link stripWhitespace} but returns the single non-whitespace
214
+ * token (or null when there's zero or more than one).
215
+ * @param tokens Raw token list.
216
+ * @returns The single meaningful token, or null.
217
+ */
218
+ function onlyNonWhitespace(tokens: readonly TokenOrValue[]): TokenOrValue | null {
219
+ const stripped = stripWhitespace(tokens)
220
+ return stripped.length === 1 ? stripped[0]! : null
221
+ }
222
+
223
+ /**
224
+ * Whether the token is pure whitespace / comment — ignorable when
225
+ * matching by position.
226
+ * @param token One TokenOrValue.
227
+ * @returns True when the token carries no semantic value.
228
+ */
229
+ function isWhitespaceToken(token: TokenOrValue): boolean {
230
+ if (token.type !== 'token') return false
231
+ const { value } = token
232
+ return value.type === 'white-space' || value.type === 'comment'
233
+ }
234
+
235
+ /**
236
+ * Split `a, b` top-level argument lists into their slices. Respects
237
+ * nested function/paren groups so commas inside an inner `calc(a, b)`
238
+ * don't cause an outer split.
239
+ * @param args Raw argument token list.
240
+ * @returns Array of slices (one per top-level comma-separated segment).
241
+ */
242
+ function splitTopLevelComma(args: readonly TokenOrValue[]): readonly TokenOrValue[][] {
243
+ // Functions already arrive as atomic `{type:'function', value}` nodes,
244
+ // so commas inside them are never in `args` — a flat scan for the
245
+ // raw `comma` token is enough.
246
+ const parts: TokenOrValue[][] = [[]]
247
+ for (const token of args) {
248
+ if (token.type === 'token' && token.value.type === 'comma') {
249
+ parts.push([])
250
+ continue
251
+ }
252
+ parts.at(-1)!.push(token)
253
+ }
254
+ return parts
255
+ }
256
+
257
+ /**
258
+ * Find the first top-level `+` / `-` delim in a calc body.
259
+ * @param args Raw argument token list.
260
+ * @returns Tuple of `[index, sign]` or `[-1, null]` when not found.
261
+ */
262
+ function findTopLevelPlusOrMinus(args: readonly TokenOrValue[]): [number, '+' | '-' | null] {
263
+ for (const [index, argument] of args.entries()) {
264
+ const token = argument!
265
+ if (token.type !== 'token') continue
266
+ if (token.value.type !== 'delim') continue
267
+ const delim = token.value.value as string
268
+ if (delim === '+' || delim === '-') return [index, delim]
269
+ }
270
+ return [-1, null]
271
+ }
272
+
273
+ /**
274
+ * Coerce a token list that should represent a CSS length (px / rem /
275
+ * integer-number with no unit) into a px value.
276
+ * @param tokens Tokens for the length fragment.
277
+ * @param themeVars
278
+ * @returns Px value, or null when the shape is unrecognised.
279
+ */
280
+ function coerceLengthPx(tokens: readonly TokenOrValue[], themeVars: ThemeVars | undefined): number | null {
281
+ const stripped = stripWhitespace(tokens)
282
+ if (stripped.length !== 1) return null
283
+ return coerceTokenToPx(stripped[0]!, themeVars)
284
+ }
285
+
286
+ /**
287
+ * Convert one token to a px number, handling the lengths Tailwind v4
288
+ * commonly emits after compile: `length` (px/rem), bare numbers,
289
+ * `calc(0.25rem * 4)` nested functions.
290
+ * @param token One TokenOrValue.
291
+ * @param themeVars
292
+ * @returns Px value, or null.
293
+ */
294
+ function coerceTokenToPx(token: TokenOrValue, themeVars: ThemeVars | undefined): number | null {
295
+ if (token.type === 'length') {
296
+ const { unit, value } = token.value
297
+ if (unit === 'px') return value
298
+ if (unit === 'rem' || unit === 'em') return value * REM_TO_PX
299
+ return null
300
+ }
301
+ if (token.type === 'token' && token.value.type === 'number') {
302
+ return token.value.value
303
+ }
304
+ if (token.type === 'function' && token.value.name === 'calc') {
305
+ return evaluateSimpleCalc(token.value.arguments, themeVars)
306
+ }
307
+ if (token.type === 'var') return resolveVariableToPx(token.value.name.ident, themeVars)
308
+ return null
309
+ }
310
+
311
+ /**
312
+ * Evaluate a trivial `calc(A * B)` where A / B are lengths or numbers
313
+ * Tailwind compiles down to — the exact shape `var(--spacing)` resolves
314
+ * to after `theme(inline)` substitution.
315
+ * @param args Raw calc argument token list.
316
+ * @param themeVars
317
+ * @returns Px value, or null.
318
+ */
319
+ function evaluateSimpleCalc(args: readonly TokenOrValue[], themeVars: ThemeVars | undefined): number | null {
320
+ const stripped = stripWhitespace(args)
321
+ if (stripped.length === 3) {
322
+ const mul = stripped[1]!
323
+ if (mul.type === 'token' && mul.value.type === 'delim' && (mul.value.value as string) === '*') {
324
+ const left = coerceTokenToPx(stripped[0]!, themeVars)
325
+ const right = coerceTokenToPx(stripped[2]!, themeVars)
326
+ if (left !== null && right !== null) return left * right
327
+ }
328
+ }
329
+ if (stripped.length === 1) return coerceTokenToPx(stripped[0]!, themeVars)
330
+ return null
331
+ }
332
+
333
+ /**
334
+ * Resolve a `var(--name)` reference to a px value using the supplied
335
+ * theme-var table. Returns null when the name isn't registered or the
336
+ * value isn't a recognisable length.
337
+ * @param name Custom-property name (with leading `--`).
338
+ * @param themeVars Lookup table (or undefined).
339
+ * @returns Px value, or null.
340
+ */
341
+ function resolveVariableToPx(name: string, themeVars: ThemeVars | undefined): number | null {
342
+ if (!themeVars) return null
343
+ const raw = themeVars.get(name)
344
+ if (raw === undefined) return null
345
+ const trimmed = raw.trim()
346
+ if (trimmed.endsWith('rem')) {
347
+ const n = Number(trimmed.slice(0, -3))
348
+ return Number.isFinite(n) ? n * REM_TO_PX : null
349
+ }
350
+ if (trimmed.endsWith('em')) {
351
+ const n = Number(trimmed.slice(0, -2))
352
+ return Number.isFinite(n) ? n * REM_TO_PX : null
353
+ }
354
+ if (trimmed.endsWith('px')) {
355
+ const n = Number(trimmed.slice(0, -2))
356
+ return Number.isFinite(n) ? n : null
357
+ }
358
+ const n = Number(trimmed)
359
+ return Number.isFinite(n) ? n : null
360
+ }
361
+
362
+ /**
363
+ * Whether the token list represents `100vh`.
364
+ * @param tokens Whitespace-stripped token list.
365
+ * @returns True when the only meaningful token is the length `100vh`.
366
+ */
367
+ function isViewportHeightHundred(tokens: readonly TokenOrValue[]): boolean {
368
+ if (tokens.length !== 1) return false
369
+ const token = tokens[0]!
370
+ if (token.type !== 'length') return false
371
+ return token.value.unit === 'vh' && token.value.value === 100
372
+ }
373
+
374
+ /**
375
+ * Whether the rhs of `calc(100vh - <rhs>)` is the nested `(env(top) + env(bottom))`
376
+ * parenthesised sum Tailwind emits for `h-screen-safe` and siblings.
377
+ * @param tokens Whitespace-stripped rhs token list.
378
+ * @returns True on match.
379
+ */
380
+ function isParenthesisedTopBottomSum(tokens: readonly TokenOrValue[]): boolean {
381
+ // lightningcss emits bare `(...)` as a `parenthesis-block` token
382
+ // followed by the inner tokens inline, then a `close-parenthesis`
383
+ // token at the matching depth. Strip whitespace + the grouping tokens
384
+ // and check that what remains is exactly `env(top) + env(bottom)`
385
+ // (either order).
386
+ const meaningful: TokenOrValue[] = []
387
+ for (const token of tokens) {
388
+ if (isWhitespaceToken(token)) continue
389
+ if (token.type === 'token') {
390
+ const t = token.value.type
391
+ if (t === 'parenthesis-block' || t === 'close-parenthesis') continue
392
+ }
393
+ meaningful.push(token)
394
+ }
395
+ if (meaningful.length !== 3) return false
396
+ const plus = meaningful[1]!
397
+ if (plus.type !== 'token' || plus.value.type !== 'delim' || (plus.value.value as string) !== '+') return false
398
+ const first = envSide(meaningful[0]!)
399
+ const last = envSide(meaningful[2]!)
400
+ return (first === 't' && last === 'b') || (first === 'b' && last === 't')
401
+ }
402
+
403
+ export { detectSafeAreaMarker }
404
+
@@ -0,0 +1,17 @@
1
+ import type { SelectorComponent } from 'lightningcss'
2
+
3
+ /**
4
+ * Pull a bare class name out of a lightningcss selector-part array. Accepts
5
+ * only the simple shape — one `{type: 'class', name}` component — since
6
+ * Tailwind utility rules land as flat single-class selectors once variants
7
+ * are folded in. Compound or pseudo selectors return `null` so the caller
8
+ * skips them.
9
+ * @param selector One selector's component array.
10
+ * @returns Bare class name, or `null` when the selector is anything more complex.
11
+ */
12
+ export function classNameFromSelector(selector: readonly SelectorComponent[]): string | null {
13
+ if (selector.length !== 1) return null
14
+ const [head] = selector
15
+ if (head?.type !== 'class') return null
16
+ return head.name
17
+ }
@@ -0,0 +1,152 @@
1
+ import type {
2
+ BorderColor,
3
+ BorderRadius,
4
+ Flex,
5
+ Gap,
6
+ Margin,
7
+ MarginBlock,
8
+ MarginInline,
9
+ Padding,
10
+ PaddingBlock,
11
+ PaddingInline,
12
+ } from 'lightningcss'
13
+ import { cssColorToString } from './color'
14
+ import { dimensionPercentageToNumber, gapValueToValue, lengthPercentageOrAutoToValue } from './length'
15
+ import type { RNEntry } from './types'
16
+
17
+ /**
18
+ * Expand `margin` / `padding` shorthand (`{top, right, bottom, left}`) to
19
+ * RN entries. When all four sides share the same converted value, collapse
20
+ * to the single-key shorthand RN accepts; otherwise emit four longhands.
21
+ * @param property `'padding'` or `'margin'`.
22
+ * @param value Typed shorthand record.
23
+ * @returns RN entries.
24
+ */
25
+ export function expandFourSided(property: 'padding' | 'margin', value: Padding | Margin): readonly RNEntry[] {
26
+ const top = lengthPercentageOrAutoToValue(value.top)
27
+ const right = lengthPercentageOrAutoToValue(value.right)
28
+ const bottom = lengthPercentageOrAutoToValue(value.bottom)
29
+ const left = lengthPercentageOrAutoToValue(value.left)
30
+ if (top === null || right === null || bottom === null || left === null) return []
31
+ if (top === right && right === bottom && bottom === left) return [[property, top]]
32
+ return [
33
+ [`${property}Top`, top],
34
+ [`${property}Right`, right],
35
+ [`${property}Bottom`, bottom],
36
+ [`${property}Left`, left],
37
+ ]
38
+ }
39
+
40
+ /**
41
+ * Expand `padding-inline` / `margin-inline` (logical property) into RN's
42
+ * physical left / right pair. RN has no RTL-aware logical props at the
43
+ * style-object level, so we lower at compile time.
44
+ * @param property `'padding'` or `'margin'`.
45
+ * @param value Typed inline shorthand.
46
+ * @returns RN entries.
47
+ */
48
+ export function expandLogicalInline(property: 'padding' | 'margin', value: PaddingInline | MarginInline): readonly RNEntry[] {
49
+ const start = lengthPercentageOrAutoToValue(value.inlineStart)
50
+ const end = lengthPercentageOrAutoToValue(value.inlineEnd)
51
+ if (start === null || end === null) return []
52
+ return [
53
+ [`${property}Left`, start],
54
+ [`${property}Right`, end],
55
+ ]
56
+ }
57
+
58
+ /**
59
+ * Expand `padding-block` / `margin-block` (logical property) into RN's
60
+ * physical top / bottom pair.
61
+ * @param property `'padding'` or `'margin'`.
62
+ * @param value Typed block shorthand.
63
+ * @returns RN entries.
64
+ */
65
+ export function expandLogicalBlock(property: 'padding' | 'margin', value: PaddingBlock | MarginBlock): readonly RNEntry[] {
66
+ const start = lengthPercentageOrAutoToValue(value.blockStart)
67
+ const end = lengthPercentageOrAutoToValue(value.blockEnd)
68
+ if (start === null || end === null) return []
69
+ return [
70
+ [`${property}Top`, start],
71
+ [`${property}Bottom`, end],
72
+ ]
73
+ }
74
+
75
+ /**
76
+ * Expand a `border-radius` shorthand into RN corner entries. Each corner
77
+ * is a 2-tuple `[x, y]` in lightningcss; RN exposes one radius per corner
78
+ * so we use the x-axis. When all four corners match, collapse to the
79
+ * single `borderRadius` key.
80
+ * @param value Typed `BorderRadius` record.
81
+ * @returns RN entries.
82
+ */
83
+ export function expandBorderRadius(value: BorderRadius): readonly RNEntry[] {
84
+ const corners: Array<[string, number | string | null]> = [
85
+ ['borderTopLeftRadius', dimensionPercentageToNumber(value.topLeft[0])],
86
+ ['borderTopRightRadius', dimensionPercentageToNumber(value.topRight[0])],
87
+ ['borderBottomRightRadius', dimensionPercentageToNumber(value.bottomRight[0])],
88
+ ['borderBottomLeftRadius', dimensionPercentageToNumber(value.bottomLeft[0])],
89
+ ]
90
+ const [first] = corners
91
+ if (first?.[1] == null) return []
92
+ if (corners.every(([, v]) => v === first[1])) return [['borderRadius', first[1]]]
93
+ return corners.filter((entry): entry is [string, number | string] => entry[1] !== null)
94
+ }
95
+
96
+ /**
97
+ * Expand a `border-color` shorthand into RN longhands. When all four
98
+ * sides match, collapse to a single `borderColor`; otherwise emit per-side
99
+ * props.
100
+ * @param value Typed `BorderColor` record.
101
+ * @returns RN entries.
102
+ */
103
+ export function expandBorderColor(value: BorderColor): readonly RNEntry[] {
104
+ const top = cssColorToString(value.top)
105
+ const right = cssColorToString(value.right)
106
+ const bottom = cssColorToString(value.bottom)
107
+ const left = cssColorToString(value.left)
108
+ if (top === right && right === bottom && bottom === left) return [['borderColor', top]]
109
+ return [
110
+ ['borderTopColor', top],
111
+ ['borderRightColor', right],
112
+ ['borderBottomColor', bottom],
113
+ ['borderLeftColor', left],
114
+ ]
115
+ }
116
+
117
+ /**
118
+ * Expand `gap` shorthand (`{row, column}`) into RN entries. When both
119
+ * axes equal the same value collapse to the single `gap` key; otherwise
120
+ * emit `rowGap` + `columnGap`.
121
+ * @param value Typed `Gap` record.
122
+ * @returns RN entries.
123
+ */
124
+ export function expandGap(value: Gap): readonly RNEntry[] {
125
+ const row = gapValueToValue(value.row)
126
+ const column = gapValueToValue(value.column)
127
+ if (row === null || column === null) return []
128
+ if (row === column) return [['gap', row]]
129
+ return [
130
+ ['rowGap', row],
131
+ ['columnGap', column],
132
+ ]
133
+ }
134
+
135
+ /**
136
+ * Convert `Flex` shorthand to RN entries. When the shape matches `flex:
137
+ * 1` (`{grow:1, shrink:1, basis: 0%}`), emit the single `flex` key RN
138
+ * understands. Otherwise expand to the three longhands.
139
+ * @param value Typed `Flex` record.
140
+ * @returns RN entries.
141
+ */
142
+ export function flexToEntries(value: Flex): readonly RNEntry[] {
143
+ const basis = lengthPercentageOrAutoToValue(value.basis)
144
+ if (basis === null) return []
145
+ if (value.grow === 1 && value.shrink === 1 && basis === '0%') return [['flex', 1]]
146
+ const entries: RNEntry[] = [
147
+ ['flexGrow', value.grow],
148
+ ['flexShrink', value.shrink],
149
+ ]
150
+ if (basis !== 'auto') entries.push(['flexBasis', basis])
151
+ return entries
152
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Text-truncate atom detector.
3
+ *
4
+ * React Native's `<Text>` exposes two props that cover every case
5
+ * Tailwind's text-truncation utilities express:
6
+ *
7
+ * numberOfLines — clamp after N lines (1 for single-line ellipsis)
8
+ * ellipsizeMode — 'tail' (ellipsis) or 'clip' at the truncation point
9
+ *
10
+ * rnwind treats the Tailwind classes as **metadata**: the transformer
11
+ * strips the truncate atoms from the JSX site's className and emits
12
+ * `numberOfLines={N}` / `ellipsizeMode="tail"` props on the element.
13
+ *
14
+ * Covered atoms:
15
+ * truncate → { numberOfLines: 1, ellipsizeMode: 'tail' }
16
+ * text-ellipsis → { ellipsizeMode: 'tail' }
17
+ * text-clip → { ellipsizeMode: 'clip' }
18
+ * line-clamp-<N> → { numberOfLines: <N> }
19
+ * line-clamp-none → { numberOfLines: 0 } (reset — overrides prior)
20
+ *
21
+ * Detection is pure name matching — no CSS inspection — because every
22
+ * relevant piece of data lives in the class name itself.
23
+ */
24
+
25
+ /** Text-truncate metadata produced by a single atom. */
26
+ export interface TextTruncateInfo {
27
+ readonly numberOfLines?: number
28
+ readonly ellipsizeMode?: 'tail' | 'clip'
29
+ }
30
+
31
+ /** Regex matching `line-clamp-<N>` with a positive-integer N. */
32
+ const LINE_CLAMP_RE = /^line-clamp-(\d+)$/
33
+ /** Regex matching Tailwind v4's `line-clamp-[<value>]` arbitrary form. */
34
+ const LINE_CLAMP_ARBITRARY_RE = /^line-clamp-\[([^\]]+)\]$/
35
+
36
+ /**
37
+ * Inspect one class-name token and return the text-truncate metadata it
38
+ * contributes, or `null` when the atom isn't a truncate utility.
39
+ * @param atom Single class-name token (no variant prefix).
40
+ * @returns The atom's contribution, or null.
41
+ */
42
+ function detectTextTruncate(atom: string): TextTruncateInfo | null {
43
+ if (atom === 'truncate') return { numberOfLines: 1, ellipsizeMode: 'tail' }
44
+ if (atom === 'text-ellipsis') return { ellipsizeMode: 'tail' }
45
+ if (atom === 'text-clip') return { ellipsizeMode: 'clip' }
46
+ if (atom === 'line-clamp-none') return { numberOfLines: 0 }
47
+ const numeric = LINE_CLAMP_RE.exec(atom)
48
+ if (numeric) return { numberOfLines: Number(numeric[1]) }
49
+ const arbitrary = LINE_CLAMP_ARBITRARY_RE.exec(atom)
50
+ if (arbitrary) {
51
+ const n = Number.parseInt(arbitrary[1]!, 10)
52
+ if (Number.isFinite(n) && n >= 0) return { numberOfLines: n }
53
+ }
54
+ return null
55
+ }
56
+
57
+ /**
58
+ * Fast pre-check — returns true when ANY atom in the list could be a
59
+ * truncate utility, false when none can. Lets callers skip the
60
+ * allocation of the merge pass for the common "no truncate" case.
61
+ * @param atoms Tokenised atom list from a literal className.
62
+ * @returns Whether to run the full per-atom detection.
63
+ */
64
+ function mayContainTextTruncate(atoms: readonly string[]): boolean {
65
+ for (const atom of atoms) {
66
+ if (
67
+ atom === 'truncate' ||
68
+ atom === 'text-ellipsis' ||
69
+ atom === 'text-clip' ||
70
+ atom === 'line-clamp-none' ||
71
+ atom.startsWith('line-clamp-')
72
+ ) {
73
+ return true
74
+ }
75
+ }
76
+ return false
77
+ }
78
+
79
+ export { detectTextTruncate, mayContainTextTruncate }