@depup/react-email 5.2.10-depup.1 → 6.9.0-depup.1

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 (404) hide show
  1. package/CHANGELOG.md +313 -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 +7801 -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/downlevel-for-email-clients.cjs +126 -0
  162. package/dist/components/tailwind/utils/css/downlevel-for-email-clients.mjs +125 -0
  163. package/dist/components/tailwind/utils/css/extract-rules-per-class.cjs +45 -0
  164. package/dist/components/tailwind/utils/css/extract-rules-per-class.mjs +44 -0
  165. package/dist/components/tailwind/utils/css/get-custom-properties.cjs +35 -0
  166. package/dist/components/tailwind/utils/css/get-custom-properties.mjs +34 -0
  167. package/dist/components/tailwind/utils/css/is-part-inlinable.cjs +10 -0
  168. package/dist/components/tailwind/utils/css/is-part-inlinable.mjs +9 -0
  169. package/dist/components/tailwind/utils/css/is-rule-inlinable.cjs +10 -0
  170. package/dist/components/tailwind/utils/css/is-rule-inlinable.mjs +9 -0
  171. package/dist/components/tailwind/utils/css/make-inline-styles-for.cjs +52 -0
  172. package/dist/components/tailwind/utils/css/make-inline-styles-for.mjs +51 -0
  173. package/dist/components/tailwind/utils/css/resolve-all-css-variables.cjs +89 -0
  174. package/dist/components/tailwind/utils/css/resolve-all-css-variables.mjs +88 -0
  175. package/dist/components/tailwind/utils/css/resolve-calc-expressions.cjs +92 -0
  176. package/dist/components/tailwind/utils/css/resolve-calc-expressions.mjs +91 -0
  177. package/dist/components/tailwind/utils/css/sanitize-declarations.cjs +310 -0
  178. package/dist/components/tailwind/utils/css/sanitize-declarations.mjs +309 -0
  179. package/dist/components/tailwind/utils/css/sanitize-non-inlinable-rules.cjs +46 -0
  180. package/dist/components/tailwind/utils/css/sanitize-non-inlinable-rules.mjs +45 -0
  181. package/dist/components/tailwind/utils/css/split-mixed-rule.cjs +41 -0
  182. package/dist/components/tailwind/utils/css/split-mixed-rule.mjs +40 -0
  183. package/dist/components/tailwind/utils/css/strip-empty-tailwind-vars.cjs +52 -0
  184. package/dist/components/tailwind/utils/css/strip-empty-tailwind-vars.mjs +51 -0
  185. package/dist/components/tailwind/utils/css/unwrap-value.cjs +7 -0
  186. package/dist/components/tailwind/utils/css/unwrap-value.mjs +7 -0
  187. package/dist/components/tailwind/utils/react/is-component.cjs +7 -0
  188. package/dist/components/tailwind/utils/react/is-component.mjs +7 -0
  189. package/dist/components/tailwind/utils/react/map-react-tree.cjs +32 -0
  190. package/dist/components/tailwind/utils/react/map-react-tree.mjs +30 -0
  191. package/dist/components/tailwind/utils/tailwindcss/clone-element-with-inlined-styles.cjs +37 -0
  192. package/dist/components/tailwind/utils/tailwindcss/clone-element-with-inlined-styles.mjs +35 -0
  193. package/dist/components/tailwind/utils/tailwindcss/setup-tailwind.cjs +77 -0
  194. package/dist/components/tailwind/utils/tailwindcss/setup-tailwind.d.cts +19 -0
  195. package/dist/components/tailwind/utils/tailwindcss/setup-tailwind.d.mts +19 -0
  196. package/dist/components/tailwind/utils/tailwindcss/setup-tailwind.mjs +76 -0
  197. package/dist/components/tailwind/utils/tailwindcss/tailwind-stylesheets/index.cjs +901 -0
  198. package/dist/components/tailwind/utils/tailwindcss/tailwind-stylesheets/index.mjs +901 -0
  199. package/dist/components/tailwind/utils/tailwindcss/tailwind-stylesheets/preflight.cjs +398 -0
  200. package/dist/components/tailwind/utils/tailwindcss/tailwind-stylesheets/preflight.mjs +398 -0
  201. package/dist/components/tailwind/utils/tailwindcss/tailwind-stylesheets/theme.cjs +467 -0
  202. package/dist/components/tailwind/utils/tailwindcss/tailwind-stylesheets/theme.mjs +467 -0
  203. package/dist/components/tailwind/utils/tailwindcss/tailwind-stylesheets/utilities.cjs +6 -0
  204. package/dist/components/tailwind/utils/tailwindcss/tailwind-stylesheets/utilities.mjs +6 -0
  205. package/dist/components/tailwind/utils/text/from-dash-case-to-camel-case.cjs +6 -0
  206. package/dist/components/tailwind/utils/text/from-dash-case-to-camel-case.mjs +6 -0
  207. package/dist/components/text/index.d.cts +1 -0
  208. package/dist/components/text/index.d.mts +1 -0
  209. package/dist/components/text/text.cjs +43 -0
  210. package/dist/components/text/text.d.cts +7 -0
  211. package/dist/components/text/text.d.mts +7 -0
  212. package/dist/components/text/text.mjs +41 -0
  213. package/dist/components/text/utils/compute-margins.cjs +62 -0
  214. package/dist/components/text/utils/compute-margins.mjs +62 -0
  215. package/dist/index.cjs +95 -0
  216. package/dist/index.d.cts +26 -0
  217. package/dist/index.d.mts +26 -0
  218. package/dist/index.mjs +25 -7326
  219. package/package.json +64 -32
  220. package/src/components/body/__snapshots__/body.spec.tsx.snap +23 -0
  221. package/src/components/body/body.spec.tsx +78 -0
  222. package/src/components/body/body.tsx +62 -0
  223. package/src/components/body/index.ts +1 -0
  224. package/src/components/body/margin-properties.ts +27 -0
  225. package/src/components/button/button.spec.tsx +53 -0
  226. package/src/components/button/button.tsx +114 -0
  227. package/src/components/button/index.ts +1 -0
  228. package/src/components/button/utils/parse-padding.ts +137 -0
  229. package/src/components/button/utils/px-to-pt.ts +4 -0
  230. package/src/components/button/utils/utils.spec.ts +153 -0
  231. package/src/components/code-block/code-block.tsx +136 -0
  232. package/src/components/code-block/index.ts +3 -0
  233. package/src/components/code-block/languages-available.ts +402 -0
  234. package/src/components/code-block/prism.ts +15618 -0
  235. package/src/components/code-block/themes.ts +4929 -0
  236. package/src/components/code-inline/code-inline.tsx +60 -0
  237. package/src/components/code-inline/index.ts +1 -0
  238. package/src/components/column/column.spec.tsx +28 -0
  239. package/src/components/column/column.tsx +17 -0
  240. package/src/components/column/index.ts +1 -0
  241. package/src/components/container/container.spec.tsx +33 -0
  242. package/src/components/container/container.tsx +59 -0
  243. package/src/components/container/index.ts +1 -0
  244. package/src/components/element-marker.ts +5 -0
  245. package/src/components/font/font.spec.tsx +63 -0
  246. package/src/components/font/font.tsx +76 -0
  247. package/src/components/font/index.ts +1 -0
  248. package/src/components/head/head.spec.tsx +34 -0
  249. package/src/components/head/head.tsx +15 -0
  250. package/src/components/head/index.ts +1 -0
  251. package/src/components/heading/heading.spec.tsx +32 -0
  252. package/src/components/heading/heading.tsx +31 -0
  253. package/src/components/heading/index.ts +1 -0
  254. package/src/components/heading/utils/as.ts +26 -0
  255. package/src/components/heading/utils/spaces.ts +64 -0
  256. package/src/components/heading/utils/utils.spec.ts +70 -0
  257. package/src/components/hr/hr.spec.tsx +22 -0
  258. package/src/components/hr/hr.tsx +23 -0
  259. package/src/components/hr/index.ts +1 -0
  260. package/src/components/html/html.spec.tsx +26 -0
  261. package/src/components/html/html.tsx +13 -0
  262. package/src/components/html/index.ts +1 -0
  263. package/src/components/img/img.spec.tsx +30 -0
  264. package/src/components/img/img.tsx +27 -0
  265. package/src/components/img/index.ts +1 -0
  266. package/src/components/index.ts +19 -0
  267. package/src/components/link/index.ts +1 -0
  268. package/src/components/link/link.spec.tsx +37 -0
  269. package/src/components/link/link.tsx +24 -0
  270. package/src/components/markdown/index.ts +1 -0
  271. package/src/components/markdown/markdown.spec.tsx +221 -0
  272. package/src/components/markdown/markdown.tsx +239 -0
  273. package/src/components/markdown/styles.ts +130 -0
  274. package/src/components/markdown/utils/parse-css-in-js-to-inline-css.spec.ts +42 -0
  275. package/src/components/markdown/utils/parse-css-in-js-to-inline-css.ts +73 -0
  276. package/src/components/preview/index.ts +1 -0
  277. package/src/components/preview/preview.spec.tsx +49 -0
  278. package/src/components/preview/preview.tsx +56 -0
  279. package/src/components/row/index.ts +1 -0
  280. package/src/components/row/row.spec.tsx +28 -0
  281. package/src/components/row/row.tsx +33 -0
  282. package/src/components/section/index.ts +1 -0
  283. package/src/components/section/section.spec.tsx +58 -0
  284. package/src/components/section/section.tsx +59 -0
  285. package/src/components/tailwind/e2e/integrations.spec.ts +44 -0
  286. package/src/components/tailwind/e2e/nextjs/README.md +9 -0
  287. package/src/components/tailwind/e2e/nextjs/emails/vercel-invite-user.tsx +151 -0
  288. package/src/components/tailwind/e2e/nextjs/next.config.mjs +6 -0
  289. package/src/components/tailwind/e2e/nextjs/package.json +24 -0
  290. package/src/components/tailwind/e2e/nextjs/src/app/favicon.ico +0 -0
  291. package/src/components/tailwind/e2e/nextjs/src/app/layout.tsx +23 -0
  292. package/src/components/tailwind/e2e/nextjs/src/app/page.tsx +7 -0
  293. package/src/components/tailwind/e2e/nextjs/tsconfig.json +27 -0
  294. package/src/components/tailwind/e2e/vite/README.md +9 -0
  295. package/src/components/tailwind/e2e/vite/emails/vercel-invite-user.tsx +151 -0
  296. package/src/components/tailwind/e2e/vite/index.html +13 -0
  297. package/src/components/tailwind/e2e/vite/package.json +25 -0
  298. package/src/components/tailwind/e2e/vite/public/vite.svg +1 -0
  299. package/src/components/tailwind/e2e/vite/src/App.tsx +10 -0
  300. package/src/components/tailwind/e2e/vite/src/main.tsx +9 -0
  301. package/src/components/tailwind/e2e/vite/src/vite-env.d.ts +1 -0
  302. package/src/components/tailwind/e2e/vite/tsconfig.json +25 -0
  303. package/src/components/tailwind/e2e/vite/tsconfig.node.json +11 -0
  304. package/src/components/tailwind/e2e/vite/vite.config.ts +7 -0
  305. package/src/components/tailwind/hooks/use-suspended-promise.ts +34 -0
  306. package/src/components/tailwind/hooks/use-suspensed-promise.spec.ts +263 -0
  307. package/src/components/tailwind/index.ts +4 -0
  308. package/src/components/tailwind/inline-styles.spec.ts +49 -0
  309. package/src/components/tailwind/inline-styles.ts +21 -0
  310. package/src/components/tailwind/sanitize-stylesheet.ts +10 -0
  311. package/src/components/tailwind/tailwind.spec.tsx +1390 -0
  312. package/src/components/tailwind/tailwind.tsx +178 -0
  313. package/src/components/tailwind/utils/__snapshots__/quick-safe-render-to-string.spec.tsx.snap +3 -0
  314. package/src/components/tailwind/utils/compatibility/escape-class-name.spec.ts +17 -0
  315. package/src/components/tailwind/utils/compatibility/escape-class-name.ts +18 -0
  316. package/src/components/tailwind/utils/compatibility/get-react-property.ts +15 -0
  317. package/src/components/tailwind/utils/compatibility/sanitize-class-name.spec.ts +7 -0
  318. package/src/components/tailwind/utils/compatibility/sanitize-class-name.ts +36 -0
  319. package/src/components/tailwind/utils/compatibility/unescape-class.ts +3 -0
  320. package/src/components/tailwind/utils/css/__snapshots__/extract-rules-matching-classes.spec.ts.snap +23 -0
  321. package/src/components/tailwind/utils/css/__snapshots__/remove-rule-duplicates-from-root.spec.ts.snap +3 -0
  322. package/src/components/tailwind/utils/css/__snapshots__/sanitize-non-inlinable-classes.spec.ts.snap +7 -0
  323. package/src/components/tailwind/utils/css/downlevel-for-email-clients.spec.ts +119 -0
  324. package/src/components/tailwind/utils/css/downlevel-for-email-clients.ts +244 -0
  325. package/src/components/tailwind/utils/css/extract-rules-per-class.spec.ts +232 -0
  326. package/src/components/tailwind/utils/css/extract-rules-per-class.ts +76 -0
  327. package/src/components/tailwind/utils/css/get-custom-properties.ts +49 -0
  328. package/src/components/tailwind/utils/css/is-part-inlinable.ts +15 -0
  329. package/src/components/tailwind/utils/css/is-rule-inlinable.ts +15 -0
  330. package/src/components/tailwind/utils/css/make-inline-styles-for.spec.ts +124 -0
  331. package/src/components/tailwind/utils/css/make-inline-styles-for.ts +73 -0
  332. package/src/components/tailwind/utils/css/resolve-all-css-variables.spec.ts +296 -0
  333. package/src/components/tailwind/utils/css/resolve-all-css-variables.ts +236 -0
  334. package/src/components/tailwind/utils/css/resolve-calc-expressions.spec.ts +36 -0
  335. package/src/components/tailwind/utils/css/resolve-calc-expressions.ts +147 -0
  336. package/src/components/tailwind/utils/css/sanitize-declarations.spec.ts +491 -0
  337. package/src/components/tailwind/utils/css/sanitize-declarations.ts +443 -0
  338. package/src/components/tailwind/utils/css/sanitize-non-inlinable-rules.spec.ts +72 -0
  339. package/src/components/tailwind/utils/css/sanitize-non-inlinable-rules.ts +48 -0
  340. package/src/components/tailwind/utils/css/split-mixed-rule.ts +68 -0
  341. package/src/components/tailwind/utils/css/strip-empty-tailwind-vars.spec.ts +128 -0
  342. package/src/components/tailwind/utils/css/strip-empty-tailwind-vars.ts +70 -0
  343. package/src/components/tailwind/utils/css/unwrap-value.ts +9 -0
  344. package/src/components/tailwind/utils/react/is-component.ts +12 -0
  345. package/src/components/tailwind/utils/react/map-react-tree.spec.tsx +58 -0
  346. package/src/components/tailwind/utils/react/map-react-tree.ts +58 -0
  347. package/src/components/tailwind/utils/tailwindcss/clone-element-with-inlined-styles.ts +61 -0
  348. package/src/components/tailwind/utils/tailwindcss/setup-tailwind.spec.ts +25 -0
  349. package/src/components/tailwind/utils/tailwindcss/setup-tailwind.ts +121 -0
  350. package/src/components/tailwind/utils/tailwindcss/tailwind-stylesheets/index.ts +900 -0
  351. package/src/components/tailwind/utils/tailwindcss/tailwind-stylesheets/preflight.ts +397 -0
  352. package/src/components/tailwind/utils/tailwindcss/tailwind-stylesheets/theme.ts +466 -0
  353. package/src/components/tailwind/utils/tailwindcss/tailwind-stylesheets/utilities.ts +5 -0
  354. package/src/components/tailwind/utils/text/from-dash-case-to-camel-case.ts +3 -0
  355. package/src/components/text/index.ts +1 -0
  356. package/src/components/text/text.spec.tsx +35 -0
  357. package/src/components/text/text.tsx +50 -0
  358. package/src/components/text/utils/compute-margins.spec.ts +67 -0
  359. package/src/components/text/utils/compute-margins.ts +102 -0
  360. package/src/index.ts +2 -110
  361. package/tsconfig.json +9 -4
  362. package/tsdown.config.ts +51 -6
  363. package/vitest.config.ts +1 -0
  364. package/vitest.e2e.config.ts +17 -0
  365. package/src/commands/build.ts +0 -269
  366. package/src/commands/dev.ts +0 -27
  367. package/src/commands/export.ts +0 -204
  368. package/src/commands/resend/reset.ts +0 -8
  369. package/src/commands/resend/setup.ts +0 -29
  370. package/src/commands/start.ts +0 -38
  371. package/src/utils/conf.ts +0 -9
  372. package/src/utils/esbuild/escape-string-for-regex.ts +0 -3
  373. package/src/utils/esbuild/renderring-utilities-exporter.ts +0 -63
  374. package/src/utils/get-emails-directory-metadata.spec.ts +0 -82
  375. package/src/utils/get-emails-directory-metadata.ts +0 -140
  376. package/src/utils/get-preview-server-location.ts +0 -50
  377. package/src/utils/index.ts +0 -2
  378. package/src/utils/packageJson.ts +0 -4
  379. package/src/utils/preview/get-env-variables-for-preview-app.ts +0 -20
  380. package/src/utils/preview/hot-reloading/create-dependency-graph.spec.ts +0 -226
  381. package/src/utils/preview/hot-reloading/create-dependency-graph.ts +0 -343
  382. package/src/utils/preview/hot-reloading/get-imported-modules.spec.ts +0 -151
  383. package/src/utils/preview/hot-reloading/get-imported-modules.ts +0 -49
  384. package/src/utils/preview/hot-reloading/resolve-path-aliases.spec.ts +0 -11
  385. package/src/utils/preview/hot-reloading/resolve-path-aliases.ts +0 -32
  386. package/src/utils/preview/hot-reloading/setup-hot-reloading.ts +0 -121
  387. package/src/utils/preview/hot-reloading/test/dependency-graph/inner/data-to-import.json +0 -1
  388. package/src/utils/preview/hot-reloading/test/dependency-graph/inner/file-a.ts +0 -5
  389. package/src/utils/preview/hot-reloading/test/dependency-graph/inner/file-b.ts +0 -5
  390. package/src/utils/preview/hot-reloading/test/dependency-graph/inner/general-importing-file.ts +0 -9
  391. package/src/utils/preview/hot-reloading/test/dependency-graph/inner/outer-dependency.ts +0 -3
  392. package/src/utils/preview/hot-reloading/test/dependency-graph/inner/path-aliases.ts +0 -1
  393. package/src/utils/preview/hot-reloading/test/dependency-graph/outer.ts +0 -5
  394. package/src/utils/preview/hot-reloading/test/some-file.ts +0 -0
  395. package/src/utils/preview/hot-reloading/test/tsconfig.json +0 -8
  396. package/src/utils/preview/index.ts +0 -2
  397. package/src/utils/preview/serve-static-file.ts +0 -51
  398. package/src/utils/preview/start-dev-server.ts +0 -252
  399. package/src/utils/register-spinner-autostopping.ts +0 -28
  400. package/src/utils/style-text.ts +0 -11
  401. package/src/utils/tree.spec.ts +0 -29
  402. package/src/utils/tree.ts +0 -76
  403. package/src/utils/types/hot-reload-change.ts +0 -6
  404. package/src/utils/types/hot-reload-event.ts +0 -3
package/package.json CHANGED
@@ -1,12 +1,24 @@
1
1
  {
2
2
  "name": "@depup/react-email",
3
- "version": "5.2.10-depup.1",
3
+ "version": "6.9.0-depup.1",
4
4
  "description": "A live preview of your emails right in your browser. (with updated dependencies)",
5
5
  "bin": {
6
- "email": "./dist/index.mjs"
6
+ "email": "./dist/cli/index.mjs"
7
7
  },
8
8
  "type": "module",
9
+ "sideEffects": false,
10
+ "types": "./dist/index.d.mts",
9
11
  "license": "MIT",
12
+ "exports": {
13
+ "import": {
14
+ "types": "./dist/index.d.mts",
15
+ "default": "./dist/index.mjs"
16
+ },
17
+ "require": {
18
+ "types": "./dist/index.d.cts",
19
+ "default": "./dist/index.cjs"
20
+ }
21
+ },
10
22
  "repository": {
11
23
  "type": "git",
12
24
  "url": "https://github.com/resend/react-email.git",
@@ -25,54 +37,70 @@
25
37
  "engines": {
26
38
  "node": ">=20.0.0"
27
39
  },
40
+ "peerDependencies": {
41
+ "react": "^18.0 || ^19.0 || ^19.0.0-rc",
42
+ "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc"
43
+ },
28
44
  "dependencies": {
29
- "@babel/parser": "^7.29.2",
30
- "@babel/traverse": "^7.29.0",
45
+ "@babel/parser": "^8.0.4",
46
+ "@babel/traverse": "^8.0.4",
47
+ "@react-email/render": ">=2.1.0",
31
48
  "chokidar": "^5.0.0",
32
- "commander": "^14.0.3",
49
+ "commander": "^15.0.0",
33
50
  "conf": "^15.1.0",
51
+ "css-tree": "3.2.1",
34
52
  "debounce": "^3.0.0",
35
- "esbuild": "^0.28.0",
53
+ "esbuild": "^0.28.1",
36
54
  "glob": "^13.0.6",
37
- "jiti": "^2.6.1",
55
+ "jiti": "^2.7.0",
38
56
  "log-symbols": "^7.0.1",
57
+ "marked": "^18.0.7",
39
58
  "mime-types": "^3.0.2",
40
59
  "normalize-path": "^3.0.0",
41
- "nypm": "^0.6.5",
42
- "ora": "^9.3.0",
60
+ "nypm": "^0.6.8",
61
+ "picospinner": "^3.0.0",
62
+ "prismjs": "^1.30.0",
43
63
  "prompts": "2.4.2",
44
64
  "socket.io": "^4.8.3",
65
+ "tailwindcss": "^4.3.3",
45
66
  "tsconfig-paths": "4.2.0"
46
67
  },
47
68
  "devDependencies": {
69
+ "@responsive-email/react-email": "0.0.4",
48
70
  "@types/babel__core": "7.20.5",
49
71
  "@types/babel__traverse": "7.20.7",
72
+ "@types/css-tree": "2.3.11",
50
73
  "@types/mime-types": "2.1.4",
51
74
  "@types/prompts": "2.4.9",
52
- "next": "16.1.7",
53
- "react": "19.0.0",
54
- "react-dom": "19.0.0",
75
+ "@types/shelljs": "0.10.0",
76
+ "next": "16.2.6",
77
+ "react": "19.2.4",
78
+ "react-dom": "19.2.4",
79
+ "shelljs": "0.10.0",
55
80
  "shlex": "3.0.0",
56
81
  "tsx": "4.21.0",
57
82
  "typescript": "5.9.3",
58
- "@react-email/components": "1.0.10"
83
+ "yalc": "1.0.0-pre.53",
84
+ "@react-email/render": "2.1.0"
59
85
  },
60
86
  "scripts": {
61
87
  "build": "tsdown",
62
88
  "build:watch": "tsdown --watch src",
63
89
  "clean": "rm -rf dist",
64
90
  "test": "vitest run",
65
- "test:watch": "vitest"
91
+ "test:e2e": "vitest run --config vitest.e2e.config.ts",
92
+ "test:watch": "vitest",
93
+ "typecheck": "tsc --noEmit"
66
94
  },
67
95
  "depup": {
68
96
  "changes": {
69
97
  "@babel/parser": {
70
- "from": "7.27.0",
71
- "to": "^7.29.2"
98
+ "from": "7.29.2",
99
+ "to": "^8.0.4"
72
100
  },
73
101
  "@babel/traverse": {
74
- "from": "7.27.0",
75
- "to": "^7.29.0"
102
+ "from": "7.29.0",
103
+ "to": "^8.0.4"
76
104
  },
77
105
  "chokidar": {
78
106
  "from": "^4.0.3",
@@ -80,7 +108,7 @@
80
108
  },
81
109
  "commander": {
82
110
  "from": "^13.0.0",
83
- "to": "^14.0.3"
111
+ "to": "^15.0.0"
84
112
  },
85
113
  "conf": {
86
114
  "from": "^15.0.2",
@@ -91,38 +119,42 @@
91
119
  "to": "^3.0.0"
92
120
  },
93
121
  "esbuild": {
94
- "from": "0.27.3",
95
- "to": "^0.28.0"
122
+ "from": "^0.28.0",
123
+ "to": "^0.28.1"
96
124
  },
97
125
  "jiti": {
98
- "from": "2.4.2",
99
- "to": "^2.6.1"
126
+ "from": "2.6.1",
127
+ "to": "^2.7.0"
100
128
  },
101
129
  "log-symbols": {
102
130
  "from": "^7.0.0",
103
131
  "to": "^7.0.1"
104
132
  },
133
+ "marked": {
134
+ "from": "^15.0.12",
135
+ "to": "^18.0.7"
136
+ },
105
137
  "mime-types": {
106
138
  "from": "^3.0.0",
107
139
  "to": "^3.0.2"
108
140
  },
109
141
  "nypm": {
110
- "from": "0.6.2",
111
- "to": "^0.6.5"
112
- },
113
- "ora": {
114
- "from": "^8.0.0",
115
- "to": "^9.3.0"
142
+ "from": "0.6.6",
143
+ "to": "^0.6.8"
116
144
  },
117
145
  "socket.io": {
118
146
  "from": "^4.8.1",
119
147
  "to": "^4.8.3"
148
+ },
149
+ "tailwindcss": {
150
+ "from": "^4.1.18",
151
+ "to": "^4.3.3"
120
152
  }
121
153
  },
122
- "depsUpdated": 13,
154
+ "depsUpdated": 14,
123
155
  "originalPackage": "react-email",
124
- "originalVersion": "5.2.10",
125
- "processedAt": "2026-04-03T00:45:49.488Z",
156
+ "originalVersion": "6.9.0",
157
+ "processedAt": "2026-07-21T17:30:39.869Z",
126
158
  "smokeTest": "failed"
127
159
  }
128
160
  }
@@ -0,0 +1,23 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`<Body> component > margin resetting behavior > should reset the margin property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><body dir="ltr" lang="en" style="margin:0"><!--$--><!--body--><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td dir="ltr" lang="en" style="margin:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
4
+
5
+ exports[`<Body> component > margin resetting behavior > should reset the marginBlock property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><body dir="ltr" lang="en" style="margin-block:0"><!--$--><!--body--><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td dir="ltr" lang="en" style="margin-block:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
6
+
7
+ exports[`<Body> component > margin resetting behavior > should reset the marginBlockEnd property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><body dir="ltr" lang="en" style="margin-block-end:0"><!--$--><!--body--><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td dir="ltr" lang="en" style="margin-block-end:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
8
+
9
+ exports[`<Body> component > margin resetting behavior > should reset the marginBlockStart property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><body dir="ltr" lang="en" style="margin-block-start:0"><!--$--><!--body--><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td dir="ltr" lang="en" style="margin-block-start:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
10
+
11
+ exports[`<Body> component > margin resetting behavior > should reset the marginBottom property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><body dir="ltr" lang="en" style="margin-bottom:0"><!--$--><!--body--><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td dir="ltr" lang="en" style="margin-bottom:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
12
+
13
+ exports[`<Body> component > margin resetting behavior > should reset the marginInline property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><body dir="ltr" lang="en" style="margin-inline:0"><!--$--><!--body--><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td dir="ltr" lang="en" style="margin-inline:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
14
+
15
+ exports[`<Body> component > margin resetting behavior > should reset the marginInlineEnd property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><body dir="ltr" lang="en" style="margin-inline-end:0"><!--$--><!--body--><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td dir="ltr" lang="en" style="margin-inline-end:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
16
+
17
+ exports[`<Body> component > margin resetting behavior > should reset the marginInlineStart property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><body dir="ltr" lang="en" style="margin-inline-start:0"><!--$--><!--body--><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td dir="ltr" lang="en" style="margin-inline-start:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
18
+
19
+ exports[`<Body> component > margin resetting behavior > should reset the marginLeft property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><body dir="ltr" lang="en" style="margin-left:0"><!--$--><!--body--><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td dir="ltr" lang="en" style="margin-left:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
20
+
21
+ exports[`<Body> component > margin resetting behavior > should reset the marginRight property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><body dir="ltr" lang="en" style="margin-right:0"><!--$--><!--body--><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td dir="ltr" lang="en" style="margin-right:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
22
+
23
+ exports[`<Body> component > margin resetting behavior > should reset the marginTop property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><body dir="ltr" lang="en" style="margin-top:0"><!--$--><!--body--><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td dir="ltr" lang="en" style="margin-top:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
@@ -0,0 +1,78 @@
1
+ import { render } from '@react-email/render';
2
+ import { Body } from './index.js';
3
+ import { marginProperties, paddingProperties } from './margin-properties.js';
4
+
5
+ describe('<Body> component', () => {
6
+ it('renders children correctly', async () => {
7
+ const testMessage = 'Test message';
8
+ const html = await render(<Body>{testMessage}</Body>);
9
+ expect(html).toContain(testMessage);
10
+ });
11
+
12
+ it('passes style and other props correctly', async () => {
13
+ const style = { backgroundColor: 'red' };
14
+ const html = await render(
15
+ <Body data-testid="body-test" style={style}>
16
+ Test
17
+ </Body>,
18
+ );
19
+ expect(html).toContain('style="background-color:red"');
20
+ expect(html).toContain('data-testid="body-test"');
21
+ });
22
+
23
+ it('renders correctly', async () => {
24
+ const actualOutput = await render(<Body>Lorem ipsum</Body>);
25
+ expect(actualOutput).toMatchInlineSnapshot(
26
+ `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><body dir="ltr" lang="en"><!--$--><!--body--><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td dir="ltr" lang="en">Lorem ipsum</td></tr></tbody></table><!--/$--></body>"`,
27
+ );
28
+ });
29
+
30
+ describe('margin resetting behavior', () => {
31
+ for (const property of marginProperties) {
32
+ it(`should reset the ${property} property when it comes from props`, async () => {
33
+ const actualOutput = await render(
34
+ <Body style={{ [property]: 10 }}>Random text</Body>,
35
+ );
36
+ expect(actualOutput).toMatchSnapshot();
37
+ });
38
+ }
39
+ });
40
+
41
+ it('resets body padding to override client default', async () => {
42
+ const actualOutput = await render(
43
+ <Body style={{ padding: '20px', backgroundColor: 'pink' }}>
44
+ Random text
45
+ </Body>,
46
+ );
47
+
48
+ const bodyStyle =
49
+ actualOutput.match(/<body[^>]*style="([^"]*)"/)?.[1] ?? '';
50
+ const tdStyle = actualOutput.match(/<td[^>]*style="([^"]*)"/)?.[1] ?? '';
51
+
52
+ expect(bodyStyle).toContain('padding:0');
53
+ expect(bodyStyle).toContain('background-color:pink');
54
+ expect(bodyStyle).not.toContain('padding:20px');
55
+ expect(tdStyle).toContain('padding:20px');
56
+ });
57
+
58
+ describe('padding resetting behavior', () => {
59
+ for (const property of paddingProperties) {
60
+ it(`resets the ${property} property on body when it comes from props`, async () => {
61
+ const actualOutput = await render(
62
+ <Body style={{ [property]: '10px' }}>Random text</Body>,
63
+ );
64
+ const kebabProperty = property.replace(
65
+ /[A-Z]/g,
66
+ (match) => `-${match.toLowerCase()}`,
67
+ );
68
+ const bodyStyle =
69
+ actualOutput.match(/<body[^>]*style="([^"]*)"/)?.[1] ?? '';
70
+ const tdStyle =
71
+ actualOutput.match(/<td[^>]*style="([^"]*)"/)?.[1] ?? '';
72
+
73
+ expect(bodyStyle).toContain(`${kebabProperty}:0`);
74
+ expect(tdStyle).toContain(`${kebabProperty}:10px`);
75
+ });
76
+ }
77
+ });
78
+ });
@@ -0,0 +1,62 @@
1
+ import * as React from 'react';
2
+ import { markAsElement } from '../element-marker.js';
3
+ import { marginProperties, paddingProperties } from './margin-properties.js';
4
+
5
+ export type BodyProps = Readonly<React.HtmlHTMLAttributes<HTMLBodyElement>>;
6
+
7
+ export const Body = React.forwardRef<HTMLBodyElement, BodyProps>(
8
+ ({ children, style, ...props }, ref) => {
9
+ const bodyStyle: Record<string, string | number | undefined> = {
10
+ background: style?.background,
11
+ backgroundColor: style?.backgroundColor,
12
+ };
13
+ if (style) {
14
+ for (const property of [...marginProperties, ...paddingProperties]) {
15
+ // We reset the margin if the user sets it, this mimics the
16
+ // same behavior that would happen if this was only using the body.
17
+ // This avoids the incoming margin summing up with the margin
18
+ // defined by the email client on the body, or by the browser itself
19
+ bodyStyle[property] = style[property] !== undefined ? 0 : undefined;
20
+ }
21
+ }
22
+ return (
23
+ <body
24
+ {...props}
25
+ dir={props.dir ?? 'ltr'}
26
+ lang={props.lang ?? 'en'}
27
+ style={bodyStyle}
28
+ ref={ref}
29
+ >
30
+ <table
31
+ border={0}
32
+ width="100%"
33
+ cellPadding="0"
34
+ cellSpacing="0"
35
+ role="presentation"
36
+ align="center"
37
+ >
38
+ <tbody>
39
+ <tr>
40
+ {/*
41
+ Yahoo and AOL remove all styles of the body element while converting it to a div,
42
+ so we need to apply them to to an inner cell.
43
+
44
+ See https://github.com/resend/react-email/issues/662.
45
+ */}
46
+ <td
47
+ dir={props.dir ?? 'ltr'}
48
+ lang={props.lang ?? 'en'}
49
+ style={style}
50
+ >
51
+ {children}
52
+ </td>
53
+ </tr>
54
+ </tbody>
55
+ </table>
56
+ </body>
57
+ );
58
+ },
59
+ );
60
+
61
+ Body.displayName = 'Body';
62
+ markAsElement(Body);
@@ -0,0 +1 @@
1
+ export * from './body.js';
@@ -0,0 +1,27 @@
1
+ export const marginProperties: (keyof React.CSSProperties)[] = [
2
+ 'margin',
3
+ 'marginTop',
4
+ 'marginBottom',
5
+ 'marginRight',
6
+ 'marginLeft',
7
+ 'marginInline',
8
+ 'marginBlock',
9
+ 'marginBlockStart',
10
+ 'marginBlockEnd',
11
+ 'marginInlineStart',
12
+ 'marginInlineEnd',
13
+ ];
14
+
15
+ export const paddingProperties: (keyof React.CSSProperties)[] = [
16
+ 'padding',
17
+ 'paddingTop',
18
+ 'paddingBottom',
19
+ 'paddingRight',
20
+ 'paddingLeft',
21
+ 'paddingInline',
22
+ 'paddingBlock',
23
+ 'paddingBlockStart',
24
+ 'paddingBlockEnd',
25
+ 'paddingInlineStart',
26
+ 'paddingInlineEnd',
27
+ ];
@@ -0,0 +1,53 @@
1
+ import { render } from '@react-email/render';
2
+ import { Button } from './index.js';
3
+
4
+ describe('<Button> component', () => {
5
+ it('renders children correctly', async () => {
6
+ const testMessage = 'Test message';
7
+ const html = await render(<Button>{testMessage}</Button>);
8
+ expect(html).toContain(testMessage);
9
+ });
10
+
11
+ it('passes style and other props correctly', async () => {
12
+ const style = { backgroundColor: 'red' };
13
+ const html = await render(
14
+ <Button data-testid="button-test" style={style}>
15
+ Test
16
+ </Button>,
17
+ );
18
+ expect(html).toContain('background-color:red');
19
+ expect(html).toContain('data-testid="button-test"');
20
+ });
21
+
22
+ it('renders correctly with padding values from style prop', async () => {
23
+ const actualOutput = await render(
24
+ <Button href="https://example.com" style={{ padding: '12px 20px' }} />,
25
+ );
26
+ expect(actualOutput).toMatchInlineSnapshot(
27
+ `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><a href="https://example.com" style="line-height:100%;text-decoration:none;display:inline-block;max-width:100%;mso-padding-alt:0px;padding:12px 20px;padding-top:12px;padding-right:20px;padding-bottom:12px;padding-left:20px" target="_blank"><span><!--[if mso]><i style="mso-font-width:500%;mso-text-raise:18px" hidden>&#8202;&#8202;</i><![endif]--></span><span style="max-width:100%;display:inline-block;line-height:120%;mso-padding-alt:0px;mso-text-raise:9px"></span><span><!--[if mso]><i style="mso-font-width:500%" hidden>&#8202;&#8202;&#8203;</i><![endif]--></span></a><!--/$-->"`,
28
+ );
29
+ });
30
+
31
+ it('renders the <Button> component with no padding value', async () => {
32
+ const actualOutput = await render(<Button href="https://example.com" />);
33
+ expect(actualOutput).toMatchInlineSnapshot(
34
+ `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><a href="https://example.com" style="line-height:100%;text-decoration:none;display:inline-block;max-width:100%;mso-padding-alt:0px" target="_blank"><span><!--[if mso]><i style="mso-font-width:0%;mso-text-raise:0px" hidden></i><![endif]--></span><span style="max-width:100%;display:inline-block;line-height:120%;mso-padding-alt:0px"></span><span><!--[if mso]><i style="mso-font-width:0%" hidden>&#8203;</i><![endif]--></span></a><!--/$-->"`,
35
+ );
36
+ });
37
+
38
+ it('allows users to overwrite style props', async () => {
39
+ const actualOutput = await render(
40
+ <Button
41
+ style={{
42
+ lineHeight: '150%',
43
+ display: 'block',
44
+ textDecoration: 'underline red',
45
+ maxWidth: '50%',
46
+ }}
47
+ />,
48
+ );
49
+ expect(actualOutput).toMatchInlineSnapshot(
50
+ `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><a style="line-height:150%;text-decoration:underline red;display:block;max-width:50%;mso-padding-alt:0px" target="_blank"><span><!--[if mso]><i style="mso-font-width:0%;mso-text-raise:0px" hidden></i><![endif]--></span><span style="max-width:100%;display:inline-block;line-height:120%;mso-padding-alt:0px"></span><span><!--[if mso]><i style="mso-font-width:0%" hidden>&#8203;</i><![endif]--></span></a><!--/$-->"`,
51
+ );
52
+ });
53
+ });
@@ -0,0 +1,114 @@
1
+ import * as React from 'react';
2
+ import { markAsElement } from '../element-marker.js';
3
+ import { parsePadding } from './utils/parse-padding.js';
4
+ import { pxToPt } from './utils/px-to-pt.js';
5
+
6
+ export type ButtonProps = Readonly<React.ComponentPropsWithoutRef<'a'>>;
7
+
8
+ const maxFontWidth = 5;
9
+
10
+ /**
11
+ * Computes a msoFontWidth \<= 5 and a count of space characters that,
12
+ * when applied, end up being as close to `expectedWidth` as possible.
13
+ */
14
+ function computeFontWidthAndSpaceCount(expectedWidth: number) {
15
+ if (expectedWidth === 0) return [0, 0] as const;
16
+
17
+ let smallestSpaceCount = 0;
18
+
19
+ const computeRequiredFontWidth = () => {
20
+ if (smallestSpaceCount > 0) {
21
+ return expectedWidth / smallestSpaceCount / 2;
22
+ }
23
+
24
+ return Number.POSITIVE_INFINITY;
25
+ };
26
+
27
+ while (computeRequiredFontWidth() > maxFontWidth) {
28
+ smallestSpaceCount++;
29
+ }
30
+
31
+ return [computeRequiredFontWidth(), smallestSpaceCount] as const;
32
+ }
33
+
34
+ declare module 'react' {
35
+ interface CSSProperties {
36
+ msoPaddingAlt?: string | number | undefined;
37
+ msoTextRaise?: string | number | undefined;
38
+ }
39
+ }
40
+
41
+ export const Button = React.forwardRef<HTMLAnchorElement, ButtonProps>(
42
+ ({ children, style, target = '_blank', ...props }, ref) => {
43
+ const { paddingTop, paddingRight, paddingBottom, paddingLeft } =
44
+ parsePadding(style ?? {});
45
+
46
+ const y = (paddingTop ?? 0) + (paddingBottom ?? 0);
47
+ const textRaise = pxToPt(y);
48
+
49
+ const [plFontWidth, plSpaceCount] = computeFontWidthAndSpaceCount(
50
+ paddingLeft ?? 0,
51
+ );
52
+ const [prFontWidth, prSpaceCount] = computeFontWidthAndSpaceCount(
53
+ paddingRight ?? 0,
54
+ );
55
+
56
+ return (
57
+ <a
58
+ {...props}
59
+ ref={ref}
60
+ style={{
61
+ lineHeight: '100%',
62
+ textDecoration: 'none',
63
+ display: 'inline-block',
64
+ maxWidth: '100%',
65
+ msoPaddingAlt: '0px',
66
+ ...style,
67
+ paddingTop,
68
+ paddingRight,
69
+ paddingBottom,
70
+ paddingLeft,
71
+ }}
72
+ target={target}
73
+ >
74
+ <span
75
+ dangerouslySetInnerHTML={{
76
+ // The `&#8202;` is as close to `1px` of an empty character as we can get, then, we use the `mso-font-width`
77
+ // to scale it according to what padding the developer wants. `mso-font-width` also does not allow for percentages
78
+ // >= 500% so we need to add extra spaces accordingly.
79
+ //
80
+ // See https://github.com/resend/react-email/issues/1512 for why we do not use letter-spacing instead.
81
+ __html: `<!--[if mso]><i style="mso-font-width:${
82
+ plFontWidth * 100
83
+ }%;mso-text-raise:${textRaise}px" hidden>${'&#8202;'.repeat(
84
+ plSpaceCount,
85
+ )}</i><![endif]-->`,
86
+ }}
87
+ />
88
+ <span
89
+ style={{
90
+ maxWidth: '100%',
91
+ display: 'inline-block',
92
+ lineHeight: '120%',
93
+ msoPaddingAlt: '0px',
94
+ msoTextRaise: pxToPt(paddingBottom),
95
+ }}
96
+ >
97
+ {children}
98
+ </span>
99
+ <span
100
+ dangerouslySetInnerHTML={{
101
+ __html: `<!--[if mso]><i style="mso-font-width:${
102
+ prFontWidth * 100
103
+ }%" hidden>${'&#8202;'.repeat(
104
+ prSpaceCount,
105
+ )}&#8203;</i><![endif]-->`,
106
+ }}
107
+ />
108
+ </a>
109
+ );
110
+ },
111
+ );
112
+
113
+ Button.displayName = 'Button';
114
+ markAsElement(Button);
@@ -0,0 +1 @@
1
+ export * from './button.js';