@depup/react-email 5.2.10-depup.1 → 6.9.1-depup.0

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 (407) hide show
  1. package/CHANGELOG.md +320 -0
  2. package/README.md +11 -10
  3. package/changes.json +21 -17
  4. package/dev/CHANGELOG.md +2 -0
  5. package/dev/index.js +1 -1
  6. package/dev/package.json +1 -1
  7. package/dist/_virtual/_rolldown/runtime.cjs +23 -0
  8. package/dist/cli/index.mjs +7810 -0
  9. package/dist/components/body/body.cjs +39 -0
  10. package/dist/components/body/body.d.cts +7 -0
  11. package/dist/components/body/body.d.mts +7 -0
  12. package/dist/components/body/body.mjs +37 -0
  13. package/dist/components/body/index.d.cts +1 -0
  14. package/dist/components/body/index.d.mts +1 -0
  15. package/dist/components/body/margin-properties.cjs +30 -0
  16. package/dist/components/body/margin-properties.mjs +29 -0
  17. package/dist/components/button/button.cjs +64 -0
  18. package/dist/components/button/button.d.cts +13 -0
  19. package/dist/components/button/button.d.mts +13 -0
  20. package/dist/components/button/button.mjs +62 -0
  21. package/dist/components/button/index.d.cts +1 -0
  22. package/dist/components/button/index.d.mts +1 -0
  23. package/dist/components/button/utils/parse-padding.cjs +90 -0
  24. package/dist/components/button/utils/parse-padding.mjs +90 -0
  25. package/dist/components/button/utils/px-to-pt.cjs +4 -0
  26. package/dist/components/button/utils/px-to-pt.mjs +4 -0
  27. package/dist/components/code-block/code-block.cjs +79 -0
  28. package/dist/components/code-block/code-block.d.cts +20 -0
  29. package/dist/components/code-block/code-block.d.mts +20 -0
  30. package/dist/components/code-block/code-block.mjs +77 -0
  31. package/dist/components/code-block/index.d.cts +3 -0
  32. package/dist/components/code-block/index.d.mts +3 -0
  33. package/dist/components/code-block/languages-available.d.cts +4 -0
  34. package/dist/components/code-block/languages-available.d.mts +4 -0
  35. package/dist/components/code-block/prism.cjs +14564 -0
  36. package/dist/components/code-block/prism.mjs +14562 -0
  37. package/dist/components/code-block/themes.cjs +2578 -0
  38. package/dist/components/code-block/themes.d.cts +4877 -0
  39. package/dist/components/code-block/themes.d.mts +4877 -0
  40. package/dist/components/code-block/themes.mjs +2541 -0
  41. package/dist/components/code-inline/code-inline.cjs +43 -0
  42. package/dist/components/code-inline/code-inline.d.cts +12 -0
  43. package/dist/components/code-inline/code-inline.d.mts +12 -0
  44. package/dist/components/code-inline/code-inline.mjs +41 -0
  45. package/dist/components/code-inline/index.d.cts +1 -0
  46. package/dist/components/code-inline/index.d.mts +1 -0
  47. package/dist/components/column/column.cjs +19 -0
  48. package/dist/components/column/column.d.cts +7 -0
  49. package/dist/components/column/column.d.mts +7 -0
  50. package/dist/components/column/column.mjs +17 -0
  51. package/dist/components/column/index.d.cts +1 -0
  52. package/dist/components/column/index.d.mts +1 -0
  53. package/dist/components/container/container.cjs +42 -0
  54. package/dist/components/container/container.d.cts +7 -0
  55. package/dist/components/container/container.d.mts +7 -0
  56. package/dist/components/container/container.mjs +40 -0
  57. package/dist/components/container/index.d.cts +1 -0
  58. package/dist/components/container/index.d.mts +1 -0
  59. package/dist/components/element-marker.cjs +8 -0
  60. package/dist/components/element-marker.mjs +7 -0
  61. package/dist/components/font/font.cjs +22 -0
  62. package/dist/components/font/font.d.cts +26 -0
  63. package/dist/components/font/font.d.mts +26 -0
  64. package/dist/components/font/font.mjs +21 -0
  65. package/dist/components/font/index.d.cts +1 -0
  66. package/dist/components/font/index.d.mts +1 -0
  67. package/dist/components/head/head.cjs +20 -0
  68. package/dist/components/head/head.d.cts +7 -0
  69. package/dist/components/head/head.d.mts +7 -0
  70. package/dist/components/head/head.mjs +18 -0
  71. package/dist/components/head/index.d.cts +1 -0
  72. package/dist/components/head/index.d.mts +1 -0
  73. package/dist/components/heading/heading.cjs +30 -0
  74. package/dist/components/heading/heading.d.cts +22 -0
  75. package/dist/components/heading/heading.d.mts +22 -0
  76. package/dist/components/heading/heading.mjs +28 -0
  77. package/dist/components/heading/index.d.cts +1 -0
  78. package/dist/components/heading/index.d.mts +1 -0
  79. package/dist/components/heading/utils/as.d.cts +16 -0
  80. package/dist/components/heading/utils/as.d.mts +16 -0
  81. package/dist/components/heading/utils/spaces.cjs +24 -0
  82. package/dist/components/heading/utils/spaces.d.cts +14 -0
  83. package/dist/components/heading/utils/spaces.d.mts +14 -0
  84. package/dist/components/heading/utils/spaces.mjs +24 -0
  85. package/dist/components/hr/hr.cjs +21 -0
  86. package/dist/components/hr/hr.d.cts +7 -0
  87. package/dist/components/hr/hr.d.mts +7 -0
  88. package/dist/components/hr/hr.mjs +19 -0
  89. package/dist/components/hr/index.d.cts +1 -0
  90. package/dist/components/hr/index.d.mts +1 -0
  91. package/dist/components/html/html.cjs +15 -0
  92. package/dist/components/html/html.d.cts +7 -0
  93. package/dist/components/html/html.d.mts +7 -0
  94. package/dist/components/html/html.mjs +13 -0
  95. package/dist/components/html/index.d.cts +1 -0
  96. package/dist/components/html/index.d.mts +1 -0
  97. package/dist/components/img/img.cjs +25 -0
  98. package/dist/components/img/img.d.cts +7 -0
  99. package/dist/components/img/img.d.mts +7 -0
  100. package/dist/components/img/img.mjs +23 -0
  101. package/dist/components/img/index.d.cts +1 -0
  102. package/dist/components/img/index.d.mts +1 -0
  103. package/dist/components/index.d.cts +24 -0
  104. package/dist/components/index.d.mts +24 -0
  105. package/dist/components/link/index.d.cts +1 -0
  106. package/dist/components/link/index.d.mts +1 -0
  107. package/dist/components/link/link.cjs +21 -0
  108. package/dist/components/link/link.d.cts +7 -0
  109. package/dist/components/link/link.d.mts +7 -0
  110. package/dist/components/link/link.mjs +19 -0
  111. package/dist/components/markdown/index.d.cts +1 -0
  112. package/dist/components/markdown/index.d.mts +1 -0
  113. package/dist/components/markdown/markdown.cjs +101 -0
  114. package/dist/components/markdown/markdown.d.cts +16 -0
  115. package/dist/components/markdown/markdown.d.mts +16 -0
  116. package/dist/components/markdown/markdown.mjs +99 -0
  117. package/dist/components/markdown/styles.cjs +92 -0
  118. package/dist/components/markdown/styles.d.cts +31 -0
  119. package/dist/components/markdown/styles.d.mts +31 -0
  120. package/dist/components/markdown/styles.mjs +92 -0
  121. package/dist/components/markdown/utils/parse-css-in-js-to-inline-css.cjs +60 -0
  122. package/dist/components/markdown/utils/parse-css-in-js-to-inline-css.mjs +60 -0
  123. package/dist/components/preview/index.d.cts +1 -0
  124. package/dist/components/preview/index.d.mts +1 -0
  125. package/dist/components/preview/preview.cjs +34 -0
  126. package/dist/components/preview/preview.d.cts +21 -0
  127. package/dist/components/preview/preview.d.mts +21 -0
  128. package/dist/components/preview/preview.mjs +31 -0
  129. package/dist/components/row/index.d.cts +1 -0
  130. package/dist/components/row/index.d.mts +1 -0
  131. package/dist/components/row/row.cjs +30 -0
  132. package/dist/components/row/row.d.cts +11 -0
  133. package/dist/components/row/row.d.mts +11 -0
  134. package/dist/components/row/row.mjs +28 -0
  135. package/dist/components/section/index.d.cts +1 -0
  136. package/dist/components/section/index.d.mts +1 -0
  137. package/dist/components/section/section.cjs +36 -0
  138. package/dist/components/section/section.d.cts +7 -0
  139. package/dist/components/section/section.d.mts +7 -0
  140. package/dist/components/section/section.mjs +34 -0
  141. package/dist/components/tailwind/hooks/use-suspended-promise.cjs +15 -0
  142. package/dist/components/tailwind/hooks/use-suspended-promise.mjs +15 -0
  143. package/dist/components/tailwind/index.d.cts +4 -0
  144. package/dist/components/tailwind/index.d.mts +4 -0
  145. package/dist/components/tailwind/inline-styles.cjs +11 -0
  146. package/dist/components/tailwind/inline-styles.d.cts +6 -0
  147. package/dist/components/tailwind/inline-styles.d.mts +6 -0
  148. package/dist/components/tailwind/inline-styles.mjs +11 -0
  149. package/dist/components/tailwind/sanitize-stylesheet.cjs +11 -0
  150. package/dist/components/tailwind/sanitize-stylesheet.d.cts +6 -0
  151. package/dist/components/tailwind/sanitize-stylesheet.d.mts +6 -0
  152. package/dist/components/tailwind/sanitize-stylesheet.mjs +11 -0
  153. package/dist/components/tailwind/tailwind.cjs +120 -0
  154. package/dist/components/tailwind/tailwind.d.cts +25 -0
  155. package/dist/components/tailwind/tailwind.d.mts +25 -0
  156. package/dist/components/tailwind/tailwind.mjs +117 -0
  157. package/dist/components/tailwind/utils/compatibility/get-react-property.cjs +10 -0
  158. package/dist/components/tailwind/utils/compatibility/get-react-property.mjs +10 -0
  159. package/dist/components/tailwind/utils/compatibility/sanitize-class-name.cjs +26 -0
  160. package/dist/components/tailwind/utils/compatibility/sanitize-class-name.mjs +26 -0
  161. package/dist/components/tailwind/utils/css/constants.cjs +4 -0
  162. package/dist/components/tailwind/utils/css/constants.mjs +4 -0
  163. package/dist/components/tailwind/utils/css/downlevel-for-email-clients.cjs +127 -0
  164. package/dist/components/tailwind/utils/css/downlevel-for-email-clients.mjs +126 -0
  165. package/dist/components/tailwind/utils/css/extract-rules-per-class.cjs +88 -0
  166. package/dist/components/tailwind/utils/css/extract-rules-per-class.mjs +87 -0
  167. package/dist/components/tailwind/utils/css/get-custom-properties.cjs +35 -0
  168. package/dist/components/tailwind/utils/css/get-custom-properties.mjs +34 -0
  169. package/dist/components/tailwind/utils/css/is-part-inlinable.cjs +10 -0
  170. package/dist/components/tailwind/utils/css/is-part-inlinable.mjs +9 -0
  171. package/dist/components/tailwind/utils/css/is-rule-inlinable.cjs +10 -0
  172. package/dist/components/tailwind/utils/css/is-rule-inlinable.mjs +9 -0
  173. package/dist/components/tailwind/utils/css/make-inline-styles-for.cjs +52 -0
  174. package/dist/components/tailwind/utils/css/make-inline-styles-for.mjs +51 -0
  175. package/dist/components/tailwind/utils/css/resolve-all-css-variables.cjs +89 -0
  176. package/dist/components/tailwind/utils/css/resolve-all-css-variables.mjs +88 -0
  177. package/dist/components/tailwind/utils/css/resolve-calc-expressions.cjs +92 -0
  178. package/dist/components/tailwind/utils/css/resolve-calc-expressions.mjs +91 -0
  179. package/dist/components/tailwind/utils/css/sanitize-declarations.cjs +310 -0
  180. package/dist/components/tailwind/utils/css/sanitize-declarations.mjs +309 -0
  181. package/dist/components/tailwind/utils/css/sanitize-non-inlinable-rules.cjs +46 -0
  182. package/dist/components/tailwind/utils/css/sanitize-non-inlinable-rules.mjs +45 -0
  183. package/dist/components/tailwind/utils/css/split-mixed-rule.cjs +41 -0
  184. package/dist/components/tailwind/utils/css/split-mixed-rule.mjs +40 -0
  185. package/dist/components/tailwind/utils/css/strip-empty-tailwind-vars.cjs +52 -0
  186. package/dist/components/tailwind/utils/css/strip-empty-tailwind-vars.mjs +51 -0
  187. package/dist/components/tailwind/utils/css/unwrap-value.cjs +7 -0
  188. package/dist/components/tailwind/utils/css/unwrap-value.mjs +7 -0
  189. package/dist/components/tailwind/utils/react/is-component.cjs +7 -0
  190. package/dist/components/tailwind/utils/react/is-component.mjs +7 -0
  191. package/dist/components/tailwind/utils/react/map-react-tree.cjs +32 -0
  192. package/dist/components/tailwind/utils/react/map-react-tree.mjs +30 -0
  193. package/dist/components/tailwind/utils/tailwindcss/clone-element-with-inlined-styles.cjs +37 -0
  194. package/dist/components/tailwind/utils/tailwindcss/clone-element-with-inlined-styles.mjs +35 -0
  195. package/dist/components/tailwind/utils/tailwindcss/setup-tailwind.cjs +77 -0
  196. package/dist/components/tailwind/utils/tailwindcss/setup-tailwind.d.cts +19 -0
  197. package/dist/components/tailwind/utils/tailwindcss/setup-tailwind.d.mts +19 -0
  198. package/dist/components/tailwind/utils/tailwindcss/setup-tailwind.mjs +76 -0
  199. package/dist/components/tailwind/utils/tailwindcss/tailwind-stylesheets/index.cjs +901 -0
  200. package/dist/components/tailwind/utils/tailwindcss/tailwind-stylesheets/index.mjs +901 -0
  201. package/dist/components/tailwind/utils/tailwindcss/tailwind-stylesheets/preflight.cjs +398 -0
  202. package/dist/components/tailwind/utils/tailwindcss/tailwind-stylesheets/preflight.mjs +398 -0
  203. package/dist/components/tailwind/utils/tailwindcss/tailwind-stylesheets/theme.cjs +467 -0
  204. package/dist/components/tailwind/utils/tailwindcss/tailwind-stylesheets/theme.mjs +467 -0
  205. package/dist/components/tailwind/utils/tailwindcss/tailwind-stylesheets/utilities.cjs +6 -0
  206. package/dist/components/tailwind/utils/tailwindcss/tailwind-stylesheets/utilities.mjs +6 -0
  207. package/dist/components/tailwind/utils/text/from-dash-case-to-camel-case.cjs +6 -0
  208. package/dist/components/tailwind/utils/text/from-dash-case-to-camel-case.mjs +6 -0
  209. package/dist/components/text/index.d.cts +1 -0
  210. package/dist/components/text/index.d.mts +1 -0
  211. package/dist/components/text/text.cjs +43 -0
  212. package/dist/components/text/text.d.cts +7 -0
  213. package/dist/components/text/text.d.mts +7 -0
  214. package/dist/components/text/text.mjs +41 -0
  215. package/dist/components/text/utils/compute-margins.cjs +62 -0
  216. package/dist/components/text/utils/compute-margins.mjs +62 -0
  217. package/dist/index.cjs +95 -0
  218. package/dist/index.d.cts +26 -0
  219. package/dist/index.d.mts +26 -0
  220. package/dist/index.mjs +25 -7326
  221. package/package.json +64 -32
  222. package/src/components/body/__snapshots__/body.spec.tsx.snap +23 -0
  223. package/src/components/body/body.spec.tsx +78 -0
  224. package/src/components/body/body.tsx +62 -0
  225. package/src/components/body/index.ts +1 -0
  226. package/src/components/body/margin-properties.ts +27 -0
  227. package/src/components/button/button.spec.tsx +53 -0
  228. package/src/components/button/button.tsx +114 -0
  229. package/src/components/button/index.ts +1 -0
  230. package/src/components/button/utils/parse-padding.ts +137 -0
  231. package/src/components/button/utils/px-to-pt.ts +4 -0
  232. package/src/components/button/utils/utils.spec.ts +153 -0
  233. package/src/components/code-block/code-block.tsx +136 -0
  234. package/src/components/code-block/index.ts +3 -0
  235. package/src/components/code-block/languages-available.ts +402 -0
  236. package/src/components/code-block/prism.ts +15618 -0
  237. package/src/components/code-block/themes.ts +4929 -0
  238. package/src/components/code-inline/code-inline.tsx +60 -0
  239. package/src/components/code-inline/index.ts +1 -0
  240. package/src/components/column/column.spec.tsx +28 -0
  241. package/src/components/column/column.tsx +17 -0
  242. package/src/components/column/index.ts +1 -0
  243. package/src/components/container/container.spec.tsx +33 -0
  244. package/src/components/container/container.tsx +59 -0
  245. package/src/components/container/index.ts +1 -0
  246. package/src/components/element-marker.ts +5 -0
  247. package/src/components/font/font.spec.tsx +63 -0
  248. package/src/components/font/font.tsx +76 -0
  249. package/src/components/font/index.ts +1 -0
  250. package/src/components/head/head.spec.tsx +34 -0
  251. package/src/components/head/head.tsx +15 -0
  252. package/src/components/head/index.ts +1 -0
  253. package/src/components/heading/heading.spec.tsx +32 -0
  254. package/src/components/heading/heading.tsx +31 -0
  255. package/src/components/heading/index.ts +1 -0
  256. package/src/components/heading/utils/as.ts +26 -0
  257. package/src/components/heading/utils/spaces.ts +64 -0
  258. package/src/components/heading/utils/utils.spec.ts +70 -0
  259. package/src/components/hr/hr.spec.tsx +22 -0
  260. package/src/components/hr/hr.tsx +23 -0
  261. package/src/components/hr/index.ts +1 -0
  262. package/src/components/html/html.spec.tsx +26 -0
  263. package/src/components/html/html.tsx +13 -0
  264. package/src/components/html/index.ts +1 -0
  265. package/src/components/img/img.spec.tsx +30 -0
  266. package/src/components/img/img.tsx +27 -0
  267. package/src/components/img/index.ts +1 -0
  268. package/src/components/index.ts +19 -0
  269. package/src/components/link/index.ts +1 -0
  270. package/src/components/link/link.spec.tsx +37 -0
  271. package/src/components/link/link.tsx +24 -0
  272. package/src/components/markdown/index.ts +1 -0
  273. package/src/components/markdown/markdown.spec.tsx +221 -0
  274. package/src/components/markdown/markdown.tsx +239 -0
  275. package/src/components/markdown/styles.ts +130 -0
  276. package/src/components/markdown/utils/parse-css-in-js-to-inline-css.spec.ts +42 -0
  277. package/src/components/markdown/utils/parse-css-in-js-to-inline-css.ts +73 -0
  278. package/src/components/preview/index.ts +1 -0
  279. package/src/components/preview/preview.spec.tsx +49 -0
  280. package/src/components/preview/preview.tsx +56 -0
  281. package/src/components/row/index.ts +1 -0
  282. package/src/components/row/row.spec.tsx +28 -0
  283. package/src/components/row/row.tsx +33 -0
  284. package/src/components/section/index.ts +1 -0
  285. package/src/components/section/section.spec.tsx +58 -0
  286. package/src/components/section/section.tsx +59 -0
  287. package/src/components/tailwind/e2e/integrations.spec.ts +44 -0
  288. package/src/components/tailwind/e2e/nextjs/README.md +9 -0
  289. package/src/components/tailwind/e2e/nextjs/emails/vercel-invite-user.tsx +151 -0
  290. package/src/components/tailwind/e2e/nextjs/next.config.mjs +6 -0
  291. package/src/components/tailwind/e2e/nextjs/package.json +24 -0
  292. package/src/components/tailwind/e2e/nextjs/src/app/favicon.ico +0 -0
  293. package/src/components/tailwind/e2e/nextjs/src/app/layout.tsx +23 -0
  294. package/src/components/tailwind/e2e/nextjs/src/app/page.tsx +7 -0
  295. package/src/components/tailwind/e2e/nextjs/tsconfig.json +27 -0
  296. package/src/components/tailwind/e2e/vite/README.md +9 -0
  297. package/src/components/tailwind/e2e/vite/emails/vercel-invite-user.tsx +151 -0
  298. package/src/components/tailwind/e2e/vite/index.html +13 -0
  299. package/src/components/tailwind/e2e/vite/package.json +25 -0
  300. package/src/components/tailwind/e2e/vite/public/vite.svg +1 -0
  301. package/src/components/tailwind/e2e/vite/src/App.tsx +10 -0
  302. package/src/components/tailwind/e2e/vite/src/main.tsx +9 -0
  303. package/src/components/tailwind/e2e/vite/src/vite-env.d.ts +1 -0
  304. package/src/components/tailwind/e2e/vite/tsconfig.json +25 -0
  305. package/src/components/tailwind/e2e/vite/tsconfig.node.json +11 -0
  306. package/src/components/tailwind/e2e/vite/vite.config.ts +7 -0
  307. package/src/components/tailwind/hooks/use-suspended-promise.ts +34 -0
  308. package/src/components/tailwind/hooks/use-suspensed-promise.spec.ts +263 -0
  309. package/src/components/tailwind/index.ts +4 -0
  310. package/src/components/tailwind/inline-styles.spec.ts +49 -0
  311. package/src/components/tailwind/inline-styles.ts +21 -0
  312. package/src/components/tailwind/sanitize-stylesheet.ts +10 -0
  313. package/src/components/tailwind/tailwind.spec.tsx +1390 -0
  314. package/src/components/tailwind/tailwind.tsx +178 -0
  315. package/src/components/tailwind/utils/__snapshots__/quick-safe-render-to-string.spec.tsx.snap +3 -0
  316. package/src/components/tailwind/utils/compatibility/escape-class-name.spec.ts +17 -0
  317. package/src/components/tailwind/utils/compatibility/escape-class-name.ts +18 -0
  318. package/src/components/tailwind/utils/compatibility/get-react-property.ts +15 -0
  319. package/src/components/tailwind/utils/compatibility/sanitize-class-name.spec.ts +7 -0
  320. package/src/components/tailwind/utils/compatibility/sanitize-class-name.ts +36 -0
  321. package/src/components/tailwind/utils/compatibility/unescape-class.ts +3 -0
  322. package/src/components/tailwind/utils/css/__snapshots__/extract-rules-matching-classes.spec.ts.snap +23 -0
  323. package/src/components/tailwind/utils/css/__snapshots__/remove-rule-duplicates-from-root.spec.ts.snap +3 -0
  324. package/src/components/tailwind/utils/css/__snapshots__/sanitize-non-inlinable-classes.spec.ts.snap +7 -0
  325. package/src/components/tailwind/utils/css/constants.ts +5 -0
  326. package/src/components/tailwind/utils/css/downlevel-for-email-clients.spec.ts +119 -0
  327. package/src/components/tailwind/utils/css/downlevel-for-email-clients.ts +245 -0
  328. package/src/components/tailwind/utils/css/extract-rules-per-class.spec.ts +324 -0
  329. package/src/components/tailwind/utils/css/extract-rules-per-class.ts +152 -0
  330. package/src/components/tailwind/utils/css/get-custom-properties.ts +49 -0
  331. package/src/components/tailwind/utils/css/is-part-inlinable.ts +15 -0
  332. package/src/components/tailwind/utils/css/is-rule-inlinable.ts +15 -0
  333. package/src/components/tailwind/utils/css/make-inline-styles-for.spec.ts +124 -0
  334. package/src/components/tailwind/utils/css/make-inline-styles-for.ts +73 -0
  335. package/src/components/tailwind/utils/css/resolve-all-css-variables.spec.ts +296 -0
  336. package/src/components/tailwind/utils/css/resolve-all-css-variables.ts +236 -0
  337. package/src/components/tailwind/utils/css/resolve-calc-expressions.spec.ts +36 -0
  338. package/src/components/tailwind/utils/css/resolve-calc-expressions.ts +147 -0
  339. package/src/components/tailwind/utils/css/sanitize-declarations.spec.ts +491 -0
  340. package/src/components/tailwind/utils/css/sanitize-declarations.ts +443 -0
  341. package/src/components/tailwind/utils/css/sanitize-non-inlinable-rules.spec.ts +72 -0
  342. package/src/components/tailwind/utils/css/sanitize-non-inlinable-rules.ts +48 -0
  343. package/src/components/tailwind/utils/css/split-mixed-rule.ts +68 -0
  344. package/src/components/tailwind/utils/css/strip-empty-tailwind-vars.spec.ts +128 -0
  345. package/src/components/tailwind/utils/css/strip-empty-tailwind-vars.ts +70 -0
  346. package/src/components/tailwind/utils/css/unwrap-value.ts +9 -0
  347. package/src/components/tailwind/utils/react/is-component.ts +12 -0
  348. package/src/components/tailwind/utils/react/map-react-tree.spec.tsx +58 -0
  349. package/src/components/tailwind/utils/react/map-react-tree.ts +58 -0
  350. package/src/components/tailwind/utils/tailwindcss/clone-element-with-inlined-styles.ts +61 -0
  351. package/src/components/tailwind/utils/tailwindcss/setup-tailwind.spec.ts +25 -0
  352. package/src/components/tailwind/utils/tailwindcss/setup-tailwind.ts +121 -0
  353. package/src/components/tailwind/utils/tailwindcss/tailwind-stylesheets/index.ts +900 -0
  354. package/src/components/tailwind/utils/tailwindcss/tailwind-stylesheets/preflight.ts +397 -0
  355. package/src/components/tailwind/utils/tailwindcss/tailwind-stylesheets/theme.ts +466 -0
  356. package/src/components/tailwind/utils/tailwindcss/tailwind-stylesheets/utilities.ts +5 -0
  357. package/src/components/tailwind/utils/text/from-dash-case-to-camel-case.ts +3 -0
  358. package/src/components/text/index.ts +1 -0
  359. package/src/components/text/text.spec.tsx +35 -0
  360. package/src/components/text/text.tsx +50 -0
  361. package/src/components/text/utils/compute-margins.spec.ts +67 -0
  362. package/src/components/text/utils/compute-margins.ts +102 -0
  363. package/src/index.ts +2 -110
  364. package/tsconfig.json +9 -4
  365. package/tsdown.config.ts +51 -6
  366. package/vitest.config.ts +1 -0
  367. package/vitest.e2e.config.ts +17 -0
  368. package/src/commands/build.ts +0 -269
  369. package/src/commands/dev.ts +0 -27
  370. package/src/commands/export.ts +0 -204
  371. package/src/commands/resend/reset.ts +0 -8
  372. package/src/commands/resend/setup.ts +0 -29
  373. package/src/commands/start.ts +0 -38
  374. package/src/utils/conf.ts +0 -9
  375. package/src/utils/esbuild/escape-string-for-regex.ts +0 -3
  376. package/src/utils/esbuild/renderring-utilities-exporter.ts +0 -63
  377. package/src/utils/get-emails-directory-metadata.spec.ts +0 -82
  378. package/src/utils/get-emails-directory-metadata.ts +0 -140
  379. package/src/utils/get-preview-server-location.ts +0 -50
  380. package/src/utils/index.ts +0 -2
  381. package/src/utils/packageJson.ts +0 -4
  382. package/src/utils/preview/get-env-variables-for-preview-app.ts +0 -20
  383. package/src/utils/preview/hot-reloading/create-dependency-graph.spec.ts +0 -226
  384. package/src/utils/preview/hot-reloading/create-dependency-graph.ts +0 -343
  385. package/src/utils/preview/hot-reloading/get-imported-modules.spec.ts +0 -151
  386. package/src/utils/preview/hot-reloading/get-imported-modules.ts +0 -49
  387. package/src/utils/preview/hot-reloading/resolve-path-aliases.spec.ts +0 -11
  388. package/src/utils/preview/hot-reloading/resolve-path-aliases.ts +0 -32
  389. package/src/utils/preview/hot-reloading/setup-hot-reloading.ts +0 -121
  390. package/src/utils/preview/hot-reloading/test/dependency-graph/inner/data-to-import.json +0 -1
  391. package/src/utils/preview/hot-reloading/test/dependency-graph/inner/file-a.ts +0 -5
  392. package/src/utils/preview/hot-reloading/test/dependency-graph/inner/file-b.ts +0 -5
  393. package/src/utils/preview/hot-reloading/test/dependency-graph/inner/general-importing-file.ts +0 -9
  394. package/src/utils/preview/hot-reloading/test/dependency-graph/inner/outer-dependency.ts +0 -3
  395. package/src/utils/preview/hot-reloading/test/dependency-graph/inner/path-aliases.ts +0 -1
  396. package/src/utils/preview/hot-reloading/test/dependency-graph/outer.ts +0 -5
  397. package/src/utils/preview/hot-reloading/test/some-file.ts +0 -0
  398. package/src/utils/preview/hot-reloading/test/tsconfig.json +0 -8
  399. package/src/utils/preview/index.ts +0 -2
  400. package/src/utils/preview/serve-static-file.ts +0 -51
  401. package/src/utils/preview/start-dev-server.ts +0 -252
  402. package/src/utils/register-spinner-autostopping.ts +0 -28
  403. package/src/utils/style-text.ts +0 -11
  404. package/src/utils/tree.spec.ts +0 -29
  405. package/src/utils/tree.ts +0 -76
  406. package/src/utils/types/hot-reload-change.ts +0 -6
  407. package/src/utils/types/hot-reload-event.ts +0 -3
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Downlevels modern CSS features that email clients don't support,
3
+ * operating on a css-tree StyleSheet AST.
4
+ *
5
+ * 1. CSS Nesting: unnests @media rules from inside selectors
6
+ * `.sm_p-4{@media (min-width:40rem){padding:1rem!important}}`
7
+ * → `@media (min-width:40rem){.sm_p-4{padding:1rem!important}}`
8
+ *
9
+ * 2. Media Queries Level 4 range syntax → legacy min-width/max-width
10
+ * `(width>=40rem)` → `(min-width:40rem)`
11
+ *
12
+ * Gmail, Outlook, Yahoo, and most email clients don't support either feature.
13
+ * See: https://www.caniemail.com/features/css-at-media/
14
+ * https://www.caniemail.com/features/css-nesting/
15
+ */
16
+
17
+ import {
18
+ type Atrule,
19
+ type CssNode,
20
+ clone,
21
+ type Feature,
22
+ type FeatureRange,
23
+ List,
24
+ type ListItem,
25
+ type Rule,
26
+ type StyleSheet,
27
+ walk,
28
+ } from 'css-tree';
29
+ import { NON_INLINABLE_ATRULES } from './constants.js';
30
+
31
+ /**
32
+ * css-tree 3.x introduced new AST node types for query-related at-rules that
33
+ * `@types/css-tree` (still on 2.x at the time of writing) doesn't expose.
34
+ * Augmenting the module here lets the rest of this file work with strong
35
+ * types instead of scattering `as` casts everywhere.
36
+ *
37
+ * - `FeatureRange` is what the parser emits for Media Queries Level 4 range
38
+ * syntax: `(width >= 40rem)`.
39
+ * - `Feature` is the legacy form (`(min-width: 40rem)`) we construct as the
40
+ * downleveled output.
41
+ *
42
+ * Note: we cannot extend the `CssNode` union itself (it's a `type` alias, not
43
+ * an interface), so two narrow casts remain in this file:
44
+ * 1. Widening `node` inside `walk()` so we can narrow against
45
+ * `FeatureRange.type` (`CssNode` doesn't list `'FeatureRange'`).
46
+ * 2. Assigning a constructed `Feature` back to `ListItem<CssNode>.data`.
47
+ *
48
+ * Both are flagged inline and reference back to this block.
49
+ *
50
+ * See:
51
+ * https://github.com/csstree/csstree/blob/master/lib/syntax/node/FeatureRange.js
52
+ * https://github.com/csstree/csstree/blob/master/lib/syntax/node/Feature.js
53
+ */
54
+ declare module 'css-tree' {
55
+ interface FeatureRange extends CssNodeCommon {
56
+ type: 'FeatureRange';
57
+ kind: string;
58
+ left: CssNode;
59
+ leftComparison: string;
60
+ middle: CssNode;
61
+ rightComparison: string | null;
62
+ right: CssNode | null;
63
+ }
64
+
65
+ interface Feature extends CssNodeCommon {
66
+ type: 'Feature';
67
+ kind: string;
68
+ name: string;
69
+ value: CssNode | null;
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Unnest @media at-rules from inside regular rules, and downlevel
75
+ * range media query syntax to legacy min-width/max-width.
76
+ *
77
+ * Mutates the stylesheet in place.
78
+ */
79
+ export function downlevelForEmailClients(styleSheet: StyleSheet): void {
80
+ unnestMediaQueries(styleSheet);
81
+ downlevelRangeMediaQueries(styleSheet);
82
+ }
83
+
84
+ // ---------------------------------------------------------------------------
85
+ // Unnesting
86
+ // ---------------------------------------------------------------------------
87
+
88
+ interface UnnestTransform {
89
+ parentRule: Rule;
90
+ parentItem: ListItem<CssNode>;
91
+ parentList: List<CssNode>;
92
+ nestedAtrules: Atrule[];
93
+ remainingChildren: CssNode[];
94
+ }
95
+
96
+ /**
97
+ * Walk the stylesheet and unnest any @media/@supports rules that are nested
98
+ * inside regular rules. For each, the parent Rule's selector wraps the
99
+ * at-rule's body.
100
+ *
101
+ * Before: `.sm_p-4 { @media (...) { padding: 1rem } }`
102
+ * After: `@media (...) { .sm_p-4 { padding: 1rem } }`
103
+ */
104
+ function unnestMediaQueries(styleSheet: StyleSheet): void {
105
+ const transforms: UnnestTransform[] = [];
106
+
107
+ walk(styleSheet, {
108
+ visit: 'Rule',
109
+ enter(rule, item, list) {
110
+ if (!rule.block || !item) return;
111
+
112
+ const nestedAtrules: Atrule[] = [];
113
+ const remainingChildren: CssNode[] = [];
114
+
115
+ rule.block.children.forEach((child) => {
116
+ if (
117
+ child.type === 'Atrule' &&
118
+ NON_INLINABLE_ATRULES.has(child.name.toLowerCase())
119
+ ) {
120
+ nestedAtrules.push(child);
121
+ } else {
122
+ remainingChildren.push(child);
123
+ }
124
+ });
125
+
126
+ if (nestedAtrules.length > 0) {
127
+ transforms.push({
128
+ parentRule: rule,
129
+ parentItem: item,
130
+ parentList: list,
131
+ nestedAtrules,
132
+ remainingChildren,
133
+ });
134
+ }
135
+ },
136
+ });
137
+
138
+ // Apply in reverse so list positions stay valid
139
+ for (let i = transforms.length - 1; i >= 0; i--) {
140
+ const {
141
+ parentRule,
142
+ parentItem,
143
+ parentList,
144
+ nestedAtrules,
145
+ remainingChildren,
146
+ } = transforms[i]!;
147
+
148
+ // Build replacement list: [modified parent rule (if any), unnested @media rules...]
149
+ const replacements = new List<CssNode>();
150
+
151
+ if (remainingChildren.length > 0) {
152
+ parentRule.block.children = new List<CssNode>().fromArray(
153
+ remainingChildren,
154
+ );
155
+ replacements.appendData(parentRule);
156
+ }
157
+
158
+ for (const atrule of nestedAtrules) {
159
+ const wrappedRule: Rule = {
160
+ type: 'Rule',
161
+ prelude: clone(parentRule.prelude) as Rule['prelude'],
162
+ block: {
163
+ type: 'Block',
164
+ children: atrule.block ? atrule.block.children : new List<CssNode>(),
165
+ },
166
+ };
167
+
168
+ const newAtrule: Atrule = {
169
+ type: 'Atrule',
170
+ name: atrule.name,
171
+ prelude: atrule.prelude,
172
+ block: {
173
+ type: 'Block',
174
+ children: new List<CssNode>().fromArray([wrappedRule]),
175
+ },
176
+ };
177
+
178
+ replacements.appendData(newAtrule);
179
+ }
180
+
181
+ // Replace the original rule with the entire list of new nodes
182
+ parentList.replace(parentItem, replacements);
183
+ }
184
+ }
185
+
186
+ // ---------------------------------------------------------------------------
187
+ // Range media query downleveling
188
+ // ---------------------------------------------------------------------------
189
+
190
+ /**
191
+ * Walk all nodes and downlevel range syntax (`FeatureRange`) inside @media
192
+ * preludes to legacy `Feature` nodes (`min-width` / `max-width`).
193
+ */
194
+ function downlevelRangeMediaQueries(styleSheet: StyleSheet): void {
195
+ const replacements: Array<{
196
+ item: ListItem<CssNode>;
197
+ replacement: Feature;
198
+ }> = [];
199
+
200
+ walk(styleSheet, {
201
+ enter(originalNode, item) {
202
+ // See module augmentation above: `CssNode` (from @types/css-tree 2.x)
203
+ // doesn't include `FeatureRange`, so widen here to enable narrowing.
204
+ const node = originalNode as CssNode | FeatureRange;
205
+ if (item && node.type === 'FeatureRange') {
206
+ const replacement = downlevelFeatureRange(node);
207
+ if (replacement) {
208
+ replacements.push({ item, replacement });
209
+ }
210
+ }
211
+ },
212
+ });
213
+
214
+ for (const { item, replacement } of replacements) {
215
+ // See module augmentation above: `Feature` is not part of the `CssNode`
216
+ // union, so a single cast is required when handing it back to the AST.
217
+ item.data = replacement as unknown as CssNode;
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Convert a `FeatureRange` node to a `Feature` node (legacy min-/max- syntax).
223
+ *
224
+ * For `width >= 40rem`: left=Identifier("width"), leftComparison=">=", middle=Dimension("40","rem")
225
+ * Result: { type: "Feature", name: "min-width", value: Dimension("40","rem") }
226
+ */
227
+ function downlevelFeatureRange(range: FeatureRange): Feature | null {
228
+ if (range.left.type !== 'Identifier') return null;
229
+
230
+ let prefix: string;
231
+ if (range.leftComparison === '>=' || range.leftComparison === '>') {
232
+ prefix = 'min-';
233
+ } else if (range.leftComparison === '<=' || range.leftComparison === '<') {
234
+ prefix = 'max-';
235
+ } else {
236
+ return null;
237
+ }
238
+
239
+ return {
240
+ type: 'Feature',
241
+ kind: 'media',
242
+ name: `${prefix}${range.left.name}`,
243
+ value: range.middle,
244
+ };
245
+ }
@@ -0,0 +1,324 @@
1
+ import { generate, parse, type Rule, type StyleSheet } from 'css-tree';
2
+ import { setupTailwind } from '../tailwindcss/setup-tailwind.js';
3
+ import { extractRulesPerClass } from './extract-rules-per-class.js';
4
+
5
+ describe('extractRulesPerClass()', async () => {
6
+ function convertToComparable(
7
+ map: Map<string, Rule[]>,
8
+ ): Record<string, string[]> {
9
+ return Object.fromEntries(
10
+ map
11
+ .entries()
12
+ .map(([k, rules]) => [k, rules.map((rule) => generate(rule))]),
13
+ );
14
+ }
15
+
16
+ it('works with just inlinable utilities', async () => {
17
+ const tailwind = await setupTailwind({});
18
+ const classes = ['text-center', 'bg-red-500'];
19
+ tailwind.addUtilities(classes);
20
+
21
+ const stylesheet = tailwind.getStyleSheet();
22
+
23
+ const { inlinable, nonInlinable } = extractRulesPerClass(
24
+ stylesheet,
25
+ classes,
26
+ );
27
+
28
+ expect(convertToComparable(inlinable)).toMatchInlineSnapshot(`
29
+ {
30
+ "bg-red-500": [
31
+ ".bg-red-500{background-color:var(--color-red-500)}",
32
+ ],
33
+ "text-center": [
34
+ ".text-center{text-align:center}",
35
+ ],
36
+ }
37
+ `);
38
+ expect(convertToComparable(nonInlinable)).toMatchInlineSnapshot('{}');
39
+ });
40
+
41
+ it('keeps every rule when a class is defined more than once', async () => {
42
+ const tailwind = await setupTailwind({
43
+ config: {
44
+ plugins: [
45
+ {
46
+ handler: (api) => {
47
+ api.addComponents({
48
+ '.box': { '@apply rounded-lg bg-white p-4': {} },
49
+ });
50
+ api.addComponents({
51
+ '.box': { '@apply bg-red-500': {} },
52
+ });
53
+ },
54
+ },
55
+ ],
56
+ },
57
+ });
58
+ const classes = ['box'];
59
+ tailwind.addUtilities(classes);
60
+
61
+ const stylesheet = tailwind.getStyleSheet();
62
+ const { inlinable } = extractRulesPerClass(stylesheet, classes);
63
+
64
+ expect(convertToComparable(inlinable)).toMatchInlineSnapshot(`
65
+ {
66
+ "box": [
67
+ ".box{border-radius:var(--radius-lg);background-color:var(--color-white);padding:calc(var(--spacing)*4)}",
68
+ ".box{background-color:var(--color-red-500)}",
69
+ ],
70
+ }
71
+ `);
72
+ });
73
+
74
+ it('handles non-inlinable utilities', async () => {
75
+ const tailwind = await setupTailwind({});
76
+ const classes = ['lg:w-1/2'];
77
+ tailwind.addUtilities(classes);
78
+
79
+ const stylesheet = tailwind.getStyleSheet();
80
+
81
+ const { inlinable, nonInlinable } = extractRulesPerClass(
82
+ stylesheet,
83
+ classes,
84
+ );
85
+
86
+ expect(convertToComparable(inlinable)).toMatchInlineSnapshot('{}');
87
+ expect(convertToComparable(nonInlinable)).toMatchInlineSnapshot(`
88
+ {
89
+ "lg:w-1/2": [
90
+ ".lg\\:w-1\\/2{@media (width>=64rem){width:calc(1/2*100%)}}",
91
+ ],
92
+ }
93
+ `);
94
+ });
95
+
96
+ it('handles a mix of inlinable and non-inlinable utilities', async () => {
97
+ const tailwind = await setupTailwind({});
98
+ const classes = [
99
+ 'text-center',
100
+ 'bg-red-500',
101
+ 'some-other-class', // should be ignored
102
+ 'w-full',
103
+ 'lg:w-1/2',
104
+ ];
105
+ tailwind.addUtilities(classes);
106
+
107
+ const stylesheet = tailwind.getStyleSheet();
108
+ const { inlinable, nonInlinable } = extractRulesPerClass(
109
+ stylesheet,
110
+ classes,
111
+ );
112
+ expect(convertToComparable(inlinable)).toMatchInlineSnapshot(`
113
+ {
114
+ "bg-red-500": [
115
+ ".bg-red-500{background-color:var(--color-red-500)}",
116
+ ],
117
+ "text-center": [
118
+ ".text-center{text-align:center}",
119
+ ],
120
+ "w-full": [
121
+ ".w-full{width:100%}",
122
+ ],
123
+ }
124
+ `);
125
+ expect(convertToComparable(nonInlinable)).toMatchInlineSnapshot(`
126
+ {
127
+ "lg:w-1/2": [
128
+ ".lg\\:w-1\\/2{@media (width>=64rem){width:calc(1/2*100%)}}",
129
+ ],
130
+ }
131
+ `);
132
+ });
133
+
134
+ it('splits mixed rules (base + media) into inlinable base and non-inlinable media', async () => {
135
+ const tailwind = await setupTailwind({
136
+ config: {
137
+ plugins: [
138
+ {
139
+ handler: (api) => {
140
+ api.addUtilities({
141
+ '.text-body': {
142
+ '@apply text-[green] sm:text-[darkgreen]': {},
143
+ },
144
+ });
145
+ },
146
+ },
147
+ ],
148
+ },
149
+ });
150
+ const classes = ['text-body'];
151
+ tailwind.addUtilities(classes);
152
+
153
+ const stylesheet = tailwind.getStyleSheet();
154
+ const { inlinable, nonInlinable } = extractRulesPerClass(
155
+ stylesheet,
156
+ classes,
157
+ );
158
+
159
+ expect(convertToComparable(inlinable)).toMatchInlineSnapshot(`
160
+ {
161
+ "text-body": [
162
+ ".text-body{color:green}",
163
+ ],
164
+ }
165
+ `);
166
+ expect(convertToComparable(nonInlinable)).toMatchInlineSnapshot(`
167
+ {
168
+ "text-body": [
169
+ ".text-body{@media (width>=40rem){color:darkgreen}}",
170
+ ],
171
+ }
172
+ `);
173
+ });
174
+
175
+ it('treats rules with pseudo-selectors as fully non-inlinable', async () => {
176
+ const tailwind = await setupTailwind({
177
+ config: {
178
+ plugins: [
179
+ {
180
+ handler: (api) => {
181
+ api.addUtilities({
182
+ '.btn:hover': {
183
+ color: 'red',
184
+ },
185
+ });
186
+ },
187
+ },
188
+ ],
189
+ },
190
+ });
191
+ const classes = ['btn'];
192
+ tailwind.addUtilities(classes);
193
+
194
+ const stylesheet = tailwind.getStyleSheet();
195
+ const { inlinable, nonInlinable } = extractRulesPerClass(
196
+ stylesheet,
197
+ classes,
198
+ );
199
+
200
+ expect(convertToComparable(inlinable)).toMatchInlineSnapshot('{}');
201
+ expect(convertToComparable(nonInlinable)).toMatchInlineSnapshot(`
202
+ {
203
+ "btn": [
204
+ ".btn{&:hover{color:red}}",
205
+ ],
206
+ }
207
+ `);
208
+ });
209
+
210
+ // Wrapped shape emitted by Tailwind >=4.3.3.
211
+ // See https://github.com/resend/react-email/issues/3662
212
+ it('treats @media-wrapped rules (Tailwind >=4.3.3) as non-inlinable', () => {
213
+ const classes = [
214
+ 'bg-white',
215
+ 'text-black',
216
+ 'dark:bg-black',
217
+ 'dark:text-white',
218
+ ];
219
+ const stylesheet = parse(`
220
+ @layer utilities {
221
+ .bg-white { background-color: rgb(255, 255, 255); }
222
+ .text-black { color: rgb(0, 0, 0); }
223
+ @media (prefers-color-scheme: dark) {
224
+ .dark\\:bg-black { background-color: rgb(0, 0, 0); }
225
+ .dark\\:text-white { color: rgb(255, 255, 255); }
226
+ }
227
+ }
228
+ `) as StyleSheet;
229
+
230
+ const { inlinable, nonInlinable } = extractRulesPerClass(
231
+ stylesheet,
232
+ classes,
233
+ );
234
+
235
+ expect(convertToComparable(inlinable)).toMatchInlineSnapshot(`
236
+ {
237
+ "bg-white": [
238
+ ".bg-white{background-color:rgb(255,255,255)}",
239
+ ],
240
+ "text-black": [
241
+ ".text-black{color:rgb(0,0,0)}",
242
+ ],
243
+ }
244
+ `);
245
+ expect(convertToComparable(nonInlinable)).toMatchInlineSnapshot(`
246
+ {
247
+ "dark:bg-black": [
248
+ ".dark\\:bg-black{@media (prefers-color-scheme:dark){background-color:rgb(0,0,0)}}",
249
+ ],
250
+ "dark:text-white": [
251
+ ".dark\\:text-white{@media (prefers-color-scheme:dark){color:rgb(255,255,255)}}",
252
+ ],
253
+ }
254
+ `);
255
+ });
256
+
257
+ // Nested shape emitted by Tailwind <=4.3.2; must classify identically.
258
+ // See https://github.com/resend/react-email/issues/3662
259
+ it('treats @media-nested rules (Tailwind <=4.3.2) as non-inlinable', () => {
260
+ const classes = [
261
+ 'bg-white',
262
+ 'text-black',
263
+ 'dark:bg-black',
264
+ 'dark:text-white',
265
+ ];
266
+ const stylesheet = parse(`
267
+ @layer utilities {
268
+ .bg-white { background-color: rgb(255, 255, 255); }
269
+ .text-black { color: rgb(0, 0, 0); }
270
+ .dark\\:bg-black { @media (prefers-color-scheme: dark) { background-color: rgb(0, 0, 0); } }
271
+ .dark\\:text-white { @media (prefers-color-scheme: dark) { color: rgb(255, 255, 255); } }
272
+ }
273
+ `) as StyleSheet;
274
+
275
+ const { inlinable, nonInlinable } = extractRulesPerClass(
276
+ stylesheet,
277
+ classes,
278
+ );
279
+
280
+ expect(convertToComparable(inlinable)).toMatchInlineSnapshot(`
281
+ {
282
+ "bg-white": [
283
+ ".bg-white{background-color:rgb(255,255,255)}",
284
+ ],
285
+ "text-black": [
286
+ ".text-black{color:rgb(0,0,0)}",
287
+ ],
288
+ }
289
+ `);
290
+ expect(convertToComparable(nonInlinable)).toMatchInlineSnapshot(`
291
+ {
292
+ "dark:bg-black": [
293
+ ".dark\\:bg-black{@media (prefers-color-scheme:dark){background-color:rgb(0,0,0)}}",
294
+ ],
295
+ "dark:text-white": [
296
+ ".dark\\:text-white{@media (prefers-color-scheme:dark){color:rgb(255,255,255)}}",
297
+ ],
298
+ }
299
+ `);
300
+ });
301
+
302
+ it('does not emit a bare nested rule for group/peer marker classes', async () => {
303
+ const tailwind = await setupTailwind({});
304
+ const classes = ['group', 'group-hover:underline'];
305
+ tailwind.addUtilities(classes);
306
+
307
+ const stylesheet = tailwind.getStyleSheet();
308
+ const { inlinable, nonInlinable } = extractRulesPerClass(
309
+ stylesheet,
310
+ classes,
311
+ );
312
+
313
+ // Only the real utility is keyed; the `group` marker must not produce a
314
+ // duplicate, parentless `&:is(...)` rule.
315
+ expect(Object.keys(convertToComparable(inlinable))).toEqual([]);
316
+ expect(convertToComparable(nonInlinable)).toMatchInlineSnapshot(`
317
+ {
318
+ "group-hover:underline": [
319
+ ".group-hover\\:underline{&:is(:where(.group):hover *){@media (hover:hover){text-decoration-line:underline}}}",
320
+ ],
321
+ }
322
+ `);
323
+ });
324
+ });