@depup/react-email 5.2.10-depup.0 → 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
@@ -0,0 +1,60 @@
1
+ import * as React from 'react';
2
+ import { markAsElement } from '../element-marker.js';
3
+
4
+ type RootProps = React.ComponentPropsWithoutRef<'code'> &
5
+ React.ComponentPropsWithoutRef<'span'>;
6
+
7
+ export type CodeInlineProps = Readonly<RootProps>;
8
+
9
+ /**
10
+ * If you are sending emails for users that have the Orange.fr email client,
11
+ * beware that this component will only work when you have a head containing meta tags.
12
+ */
13
+ export const CodeInline = React.forwardRef<HTMLSpanElement, CodeInlineProps>(
14
+ ({ children, ...props }, ref) => {
15
+ return (
16
+ <>
17
+ {/*
18
+ This style tag is targeted at fixing an issue for the Orange.fr email client
19
+ See:
20
+ - https://www.caniemail.com/features/html-code/
21
+ - https://www.howtotarget.email/#2019-03-26-freenet-2
22
+
23
+ On that email client, the head and html elements are removed, making the meta tag a sibling of them
24
+ allowing us to use a selector on them. Also <style> tags are supported on it.
25
+ */}
26
+ <style>{`
27
+ meta ~ .cino {
28
+ display: none !important;
29
+ opacity: 0 !important;
30
+ }
31
+
32
+ meta ~ .cio {
33
+ display: block !important;
34
+ }
35
+ `}</style>
36
+
37
+ {/* Does not render on Orange.fr */}
38
+ <code
39
+ {...props}
40
+ className={`${props.className ? props.className : ''} cino`}
41
+ >
42
+ {children}
43
+ </code>
44
+
45
+ {/* Renders only on Orange.fr */}
46
+ <span
47
+ {...props}
48
+ className={`${props.className ? props.className : ''} cio`}
49
+ ref={ref}
50
+ style={{ display: 'none', ...props.style }}
51
+ >
52
+ {children}
53
+ </span>
54
+ </>
55
+ );
56
+ },
57
+ );
58
+
59
+ CodeInline.displayName = 'CodeInline';
60
+ markAsElement(CodeInline);
@@ -0,0 +1 @@
1
+ export * from './code-inline.js';
@@ -0,0 +1,28 @@
1
+ import { render } from '@react-email/render';
2
+ import { Column } from './index.js';
3
+
4
+ describe('<Column> component', () => {
5
+ it('renders children correctly', async () => {
6
+ const testMessage = 'Test message';
7
+ const html = await render(<Column>{testMessage}</Column>);
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
+ <Column data-testid="column-test" style={style}>
15
+ Test
16
+ </Column>,
17
+ );
18
+ expect(html).toContain('style="background-color:red"');
19
+ expect(html).toContain('data-testid="column-test"');
20
+ });
21
+
22
+ it('renders correctly', async () => {
23
+ const actualOutput = await render(<Column>Lorem ipsum</Column>);
24
+ expect(actualOutput).toMatchInlineSnapshot(
25
+ `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><td data-id="__react-email-column">Lorem ipsum</td><!--/$-->"`,
26
+ );
27
+ });
28
+ });
@@ -0,0 +1,17 @@
1
+ import * as React from 'react';
2
+ import { markAsElement } from '../element-marker.js';
3
+
4
+ export type ColumnProps = Readonly<React.ComponentPropsWithoutRef<'td'>>;
5
+
6
+ export const Column = React.forwardRef<HTMLTableCellElement, ColumnProps>(
7
+ ({ children, style, ...props }, ref) => {
8
+ return (
9
+ <td {...props} data-id="__react-email-column" ref={ref} style={style}>
10
+ {children}
11
+ </td>
12
+ );
13
+ },
14
+ );
15
+
16
+ Column.displayName = 'Column';
17
+ markAsElement(Column);
@@ -0,0 +1 @@
1
+ export * from './column.js';
@@ -0,0 +1,33 @@
1
+ import { render } from '@react-email/render';
2
+ import { Container } from './index.js';
3
+
4
+ describe('<Container> component', () => {
5
+ it('renders children correctly', async () => {
6
+ const testMessage = 'Test message';
7
+ const html = await render(<Container>{testMessage}</Container>);
8
+ expect(html).toContain(testMessage);
9
+ });
10
+
11
+ it('passes style and other props correctly', async () => {
12
+ const style = { maxWidth: 300, backgroundColor: 'red' };
13
+ const html = await render(
14
+ <Container data-testid="container-test" style={style}>
15
+ Test
16
+ </Container>,
17
+ );
18
+ expect(html).toContain('style="max-width:300px;background-color:red"');
19
+ expect(html).toContain('data-testid="container-test"');
20
+ });
21
+
22
+ it('renders correctly', async () => {
23
+ const container = await render(
24
+ <Container style={{ maxWidth: '300px' }}>
25
+ <button type="button">Hi</button>
26
+ </Container>,
27
+ );
28
+
29
+ expect(container).toMatchInlineSnapshot(
30
+ `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><table align="center" width="100%" border="0" cellPadding="0" cellSpacing="0" role="presentation" style="max-width:300px"><tbody><tr style="width:100%"><td><button type="button">Hi</button></td></tr></tbody></table><!--/$-->"`,
31
+ );
32
+ });
33
+ });
@@ -0,0 +1,59 @@
1
+ import * as React from 'react';
2
+ import { markAsElement } from '../element-marker.js';
3
+
4
+ export type ContainerProps = Readonly<React.ComponentPropsWithoutRef<'table'>>;
5
+
6
+ export const Container = React.forwardRef<HTMLTableElement, ContainerProps>(
7
+ ({ children, style = {}, ...props }, ref) => {
8
+ // Split padding styles to improve compatibility with Klaviyo and Outlook,
9
+ // while preserving user-provided style property order without allocating
10
+ // entry arrays on each render.
11
+ const tdStyle: React.CSSProperties = {};
12
+ const tableStyle: React.CSSProperties = {};
13
+
14
+ const styleRecord = style as Record<string, unknown>;
15
+
16
+ for (const key in styleRecord) {
17
+ if (!Object.hasOwn(styleRecord, key)) {
18
+ continue;
19
+ }
20
+
21
+ const value = styleRecord[key];
22
+
23
+ if (
24
+ key === 'padding' ||
25
+ key === 'paddingTop' ||
26
+ key === 'paddingRight' ||
27
+ key === 'paddingBottom' ||
28
+ key === 'paddingLeft'
29
+ ) {
30
+ (tdStyle as Record<string, unknown>)[key] = value;
31
+ } else {
32
+ (tableStyle as Record<string, unknown>)[key] = value;
33
+ }
34
+ }
35
+
36
+ return (
37
+ <table
38
+ align="center"
39
+ width="100%"
40
+ {...props}
41
+ border={0}
42
+ cellPadding="0"
43
+ cellSpacing="0"
44
+ ref={ref}
45
+ role="presentation"
46
+ style={{ maxWidth: '37.5em', ...tableStyle }}
47
+ >
48
+ <tbody>
49
+ <tr style={{ width: '100%' }}>
50
+ <td style={tdStyle}>{children}</td>
51
+ </tr>
52
+ </tbody>
53
+ </table>
54
+ );
55
+ },
56
+ );
57
+
58
+ Container.displayName = 'Container';
59
+ markAsElement(Container);
@@ -0,0 +1 @@
1
+ export * from './container.js';
@@ -0,0 +1,5 @@
1
+ export const elementMarker = Symbol.for('react-email.element');
2
+
3
+ export const markAsElement = (component: unknown) => {
4
+ (component as Record<symbol, boolean>)[elementMarker] = true;
5
+ };
@@ -0,0 +1,63 @@
1
+ import { render } from '@react-email/render';
2
+ import { Font } from './index.js';
3
+
4
+ describe('<Font> component', () => {
5
+ it('renders with default props', async () => {
6
+ const html = await render(
7
+ <Font fallbackFontFamily="Helvetica" fontFamily="Arial" />,
8
+ );
9
+
10
+ expect(html).toContain('font-style: normal;');
11
+ expect(html).toContain('font-weight: 400;');
12
+ expect(html).toContain("font-family: 'Arial';");
13
+ });
14
+
15
+ it('renders with webFont prop', async () => {
16
+ const webFont = {
17
+ url: 'example.com/font.woff',
18
+ format: 'woff',
19
+ } as const;
20
+
21
+ const html = await render(
22
+ <Font
23
+ fallbackFontFamily="Helvetica"
24
+ fontFamily="Example"
25
+ webFont={webFont}
26
+ />,
27
+ );
28
+
29
+ expect(html).toContain("font-family: 'Example';");
30
+ expect(html).toContain(
31
+ `src: url(${webFont.url}) format('${webFont.format}');`,
32
+ );
33
+ });
34
+
35
+ it('renders with multiple fallback fonts', async () => {
36
+ const html = await render(
37
+ <Font fallbackFontFamily={['Helvetica', 'Verdana']} fontFamily="Arial" />,
38
+ );
39
+
40
+ expect(html).toContain("font-family: 'Arial', Helvetica, Verdana;");
41
+ });
42
+
43
+ it('renders correctly', async () => {
44
+ const actualOutput = await render(
45
+ <Font fallbackFontFamily="Verdana" fontFamily="Roboto" />,
46
+ );
47
+ expect(actualOutput).toMatchInlineSnapshot(`
48
+ "<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><style>
49
+ @font-face {
50
+ font-family: 'Roboto';
51
+ font-style: normal;
52
+ font-weight: 400;
53
+ mso-font-alt: 'Verdana';
54
+
55
+ }
56
+
57
+ * {
58
+ font-family: 'Roboto', Verdana;
59
+ }
60
+ </style><!--/$-->"
61
+ `);
62
+ });
63
+ });
@@ -0,0 +1,76 @@
1
+ import type * as React from 'react';
2
+
3
+ type FallbackFont =
4
+ | 'Arial'
5
+ | 'Helvetica'
6
+ | 'Verdana'
7
+ | 'Georgia'
8
+ | 'Times New Roman'
9
+ | 'serif'
10
+ | 'sans-serif'
11
+ | 'monospace'
12
+ | 'cursive'
13
+ | 'fantasy';
14
+
15
+ type FontFormat =
16
+ | 'woff'
17
+ | 'woff2'
18
+ | 'truetype'
19
+ | 'opentype'
20
+ | 'embedded-opentype'
21
+ | 'svg';
22
+
23
+ type FontWeight = React.CSSProperties['fontWeight'];
24
+ type FontStyle = React.CSSProperties['fontStyle'];
25
+
26
+ export interface FontProps {
27
+ /** The font you want to use. NOTE: Do not insert multiple fonts here, use fallbackFontFamily for that */
28
+ fontFamily: string;
29
+ /** An array is possible, but the order of the array is the priority order */
30
+ fallbackFontFamily: FallbackFont | FallbackFont[];
31
+ /** Not all clients support web fonts. For support check: https://www.caniemail.com/features/css-at-font-face/ */
32
+ webFont?: {
33
+ url: string;
34
+ format: FontFormat;
35
+ };
36
+ /** Default: 'normal' */
37
+ fontStyle?: FontStyle;
38
+ /** Default: 400 */
39
+ fontWeight?: FontWeight;
40
+ }
41
+
42
+ /** The component MUST be place inside the <head> tag */
43
+ export const Font: React.FC<Readonly<FontProps>> = ({
44
+ fontFamily,
45
+ fallbackFontFamily,
46
+ webFont,
47
+ fontStyle = 'normal',
48
+ fontWeight = 400,
49
+ }) => {
50
+ const src = webFont
51
+ ? `src: url(${webFont.url}) format('${webFont.format}');`
52
+ : '';
53
+
54
+ const style = `
55
+ @font-face {
56
+ font-family: '${fontFamily}';
57
+ font-style: ${fontStyle};
58
+ font-weight: ${fontWeight};
59
+ mso-font-alt: '${
60
+ Array.isArray(fallbackFontFamily)
61
+ ? fallbackFontFamily[0]
62
+ : fallbackFontFamily
63
+ }';
64
+ ${src}
65
+ }
66
+
67
+ * {
68
+ font-family: '${fontFamily}', ${
69
+ Array.isArray(fallbackFontFamily)
70
+ ? fallbackFontFamily.join(', ')
71
+ : fallbackFontFamily
72
+ };
73
+ }
74
+ `;
75
+ return <style dangerouslySetInnerHTML={{ __html: style }} />;
76
+ };
@@ -0,0 +1 @@
1
+ export * from './font.js';
@@ -0,0 +1,34 @@
1
+ import { render } from '@react-email/render';
2
+ import { Head } from './index.js';
3
+
4
+ describe('<Head> component', () => {
5
+ it('renders children correctly', async () => {
6
+ const testMessage = 'Test message';
7
+ const html = await render(<Head>{testMessage}</Head>);
8
+ expect(html).toContain(testMessage);
9
+ });
10
+
11
+ it('renders correctly', async () => {
12
+ const actualOutput = await render(<Head />);
13
+ expect(actualOutput).toMatchInlineSnapshot(
14
+ `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><head><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/><meta name="x-apple-disable-message-reformatting"/></head><!--$--><!--head--><!--/$-->"`,
15
+ );
16
+ });
17
+
18
+ it('renders style tags', async () => {
19
+ const actualOutput = await render(
20
+ <Head>
21
+ <style>
22
+ {`body{
23
+ color: red;
24
+ }`}
25
+ </style>
26
+ </Head>,
27
+ );
28
+ expect(actualOutput).toMatchInlineSnapshot(`
29
+ "<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><head><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/><meta name="x-apple-disable-message-reformatting"/><style>body{
30
+ color: red;
31
+ }</style></head><!--$--><!--head--><!--/$-->"
32
+ `);
33
+ });
34
+ });
@@ -0,0 +1,15 @@
1
+ import * as React from 'react';
2
+
3
+ export type HeadProps = Readonly<React.ComponentPropsWithoutRef<'head'>>;
4
+
5
+ export const Head = React.forwardRef<HTMLHeadElement, HeadProps>(
6
+ ({ children, ...props }, ref) => (
7
+ <head {...props} ref={ref}>
8
+ <meta content="text/html; charset=UTF-8" httpEquiv="Content-Type" />
9
+ <meta name="x-apple-disable-message-reformatting" />
10
+ {children}
11
+ </head>
12
+ ),
13
+ );
14
+
15
+ Head.displayName = 'Head';
@@ -0,0 +1 @@
1
+ export * from './head.js';
@@ -0,0 +1,32 @@
1
+ import { render } from '@react-email/render';
2
+ import { Heading } from './index.js';
3
+
4
+ describe('render', () => {
5
+ it('renders children correctly', async () => {
6
+ const testMessage = 'Test message';
7
+ const html = await render(<Heading>{testMessage}</Heading>);
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
+ <Heading data-testid="heading-test" style={style}>
15
+ Test
16
+ </Heading>,
17
+ );
18
+ expect(html).toContain('background-color:red');
19
+ expect(html).toContain('data-testid="heading-test"');
20
+ });
21
+
22
+ it('renders the <Heading> component', async () => {
23
+ const actualOutput = await render(
24
+ <Heading as="h2" mx={4}>
25
+ Lorem ipsum
26
+ </Heading>,
27
+ );
28
+ expect(actualOutput).toMatchInlineSnapshot(
29
+ `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><h2 style="margin-left:4px;margin-right:4px">Lorem ipsum</h2><!--/$-->"`,
30
+ );
31
+ });
32
+ });
@@ -0,0 +1,31 @@
1
+ import * as React from 'react';
2
+ import { markAsElement } from '../element-marker.js';
3
+ import type { As } from './utils/as.js';
4
+ import type { Margin } from './utils/spaces.js';
5
+ import { withMargin } from './utils/spaces.js';
6
+
7
+ export type HeadingAs = As<'h1', 'h2', 'h3', 'h4', 'h5', 'h6'>;
8
+ export type HeadingProps = HeadingAs & Margin;
9
+
10
+ export const Heading = React.forwardRef<
11
+ HTMLHeadingElement,
12
+ Readonly<HeadingProps>
13
+ >(
14
+ (
15
+ { as: Tag = 'h1', children, style, m, mx, my, mt, mr, mb, ml, ...props },
16
+ ref,
17
+ ) => {
18
+ return (
19
+ <Tag
20
+ {...props}
21
+ ref={ref}
22
+ style={{ ...withMargin({ m, mx, my, mt, mr, mb, ml }), ...style }}
23
+ >
24
+ {children}
25
+ </Tag>
26
+ );
27
+ },
28
+ );
29
+
30
+ Heading.displayName = 'Heading';
31
+ markAsElement(Heading);
@@ -0,0 +1 @@
1
+ export * from './heading.js';
@@ -0,0 +1,26 @@
1
+ export type As<
2
+ DefaultTag extends React.ElementType,
3
+ T1 extends React.ElementType,
4
+ T2 extends React.ElementType = T1,
5
+ T3 extends React.ElementType = T1,
6
+ T4 extends React.ElementType = T1,
7
+ T5 extends React.ElementType = T1,
8
+ > =
9
+ | (React.ComponentPropsWithRef<DefaultTag> & {
10
+ as?: DefaultTag;
11
+ })
12
+ | (React.ComponentPropsWithRef<T1> & {
13
+ as: T1;
14
+ })
15
+ | (React.ComponentPropsWithRef<T2> & {
16
+ as: T2;
17
+ })
18
+ | (React.ComponentPropsWithRef<T3> & {
19
+ as: T3;
20
+ })
21
+ | (React.ComponentPropsWithRef<T4> & {
22
+ as: T4;
23
+ })
24
+ | (React.ComponentPropsWithRef<T5> & {
25
+ as: T5;
26
+ });
@@ -0,0 +1,64 @@
1
+ import type React from 'react';
2
+
3
+ type MarginCSSProperty =
4
+ | 'margin'
5
+ | 'marginLeft'
6
+ | 'marginRight'
7
+ | 'marginTop'
8
+ | 'marginBottom';
9
+
10
+ type MarginStyles = Partial<Pick<React.CSSProperties, MarginCSSProperty>>;
11
+
12
+ export interface Margin {
13
+ m?: number | string;
14
+ mx?: number | string;
15
+ my?: number | string;
16
+ mt?: number | string;
17
+ mr?: number | string;
18
+ mb?: number | string;
19
+ ml?: number | string;
20
+ }
21
+
22
+ export const withMargin = (props: Margin): MarginStyles => {
23
+ const candidates = [
24
+ withSpace(props.m, ['margin']),
25
+ withSpace(props.mx, ['marginLeft', 'marginRight']),
26
+ withSpace(props.my, ['marginTop', 'marginBottom']),
27
+ withSpace(props.mt, ['marginTop']),
28
+ withSpace(props.mr, ['marginRight']),
29
+ withSpace(props.mb, ['marginBottom']),
30
+ withSpace(props.ml, ['marginLeft']),
31
+ ];
32
+
33
+ const mergedStyles: MarginStyles = {};
34
+
35
+ for (const style of candidates) {
36
+ if (Object.keys(style).length > 0) {
37
+ Object.assign(mergedStyles, style);
38
+ }
39
+ }
40
+
41
+ return mergedStyles;
42
+ };
43
+
44
+ export const withSpace = (
45
+ value: number | string | undefined,
46
+ properties: MarginCSSProperty[],
47
+ ) => {
48
+ const styles: MarginStyles = {};
49
+
50
+ if (value === undefined) {
51
+ return styles;
52
+ }
53
+
54
+ // Check to ensure string value is a valid number
55
+ if (Number.isNaN(Number.parseFloat(String(value)))) {
56
+ return styles;
57
+ }
58
+
59
+ for (const property of properties) {
60
+ styles[property] = `${value}px` as React.CSSProperties[MarginCSSProperty];
61
+ }
62
+
63
+ return styles;
64
+ };
@@ -0,0 +1,70 @@
1
+ import type { Margin } from './spaces.js';
2
+ import { withMargin, withSpace } from './spaces.js';
3
+
4
+ describe('withMargin', () => {
5
+ it('returns an empty object for empty input', () => {
6
+ const marginProps: Margin = {};
7
+ const result = withMargin(marginProps);
8
+ expect(result).toEqual({});
9
+ });
10
+
11
+ it('applies margin to the top', () => {
12
+ const marginProps: Margin = { mt: '10' };
13
+ const result = withMargin(marginProps);
14
+ expect(result).toEqual({ marginTop: '10px' });
15
+ });
16
+
17
+ it('applies margin to the left and right', () => {
18
+ const marginProps: Margin = { mx: '20' };
19
+ const result = withMargin(marginProps);
20
+ expect(result).toEqual({ marginLeft: '20px', marginRight: '20px' });
21
+ });
22
+
23
+ it('applies margin to the top and bottom', () => {
24
+ const marginProps: Margin = { my: '15' };
25
+ const result = withMargin(marginProps);
26
+ expect(result).toEqual({
27
+ marginBottom: '15px',
28
+ marginTop: '15px',
29
+ });
30
+ });
31
+
32
+ it('applies margin to all sides', () => {
33
+ const marginProps: Margin = { m: '25' };
34
+ const result = withMargin(marginProps);
35
+ expect(result).toEqual({
36
+ margin: '25px',
37
+ });
38
+ });
39
+
40
+ it('applies margin to specified sides when provided', () => {
41
+ const marginProps: Margin = { mt: '5', mr: '10', mb: '15', ml: '20' };
42
+ const result = withMargin(marginProps);
43
+ expect(result).toEqual({
44
+ marginBottom: '15px',
45
+ marginLeft: '20px',
46
+ marginRight: '10px',
47
+ marginTop: '5px',
48
+ });
49
+ });
50
+
51
+ it('ignores invalid margin values', () => {
52
+ const marginProps: Margin = { m: 'invalid', mt: '5', mx: 'valid' };
53
+ const result = withMargin(marginProps);
54
+ expect(result).toEqual({
55
+ marginTop: '5px',
56
+ });
57
+ });
58
+ });
59
+
60
+ describe('withSpace', () => {
61
+ it('returns an empty object for undefined value', () => {
62
+ const result = withSpace(undefined, ['margin']);
63
+ expect(result).toEqual({});
64
+ });
65
+
66
+ it('applies space to the specified properties', () => {
67
+ const result = withSpace(15, ['marginTop', 'marginLeft']);
68
+ expect(result).toEqual({ marginTop: '15px', marginLeft: '15px' });
69
+ });
70
+ });
@@ -0,0 +1,22 @@
1
+ import { render } from '@react-email/render';
2
+ import { Hr } from './index.js';
3
+
4
+ describe('<Hr> component', () => {
5
+ it('passes styles and other props correctly', async () => {
6
+ const style = {
7
+ width: '50%',
8
+ borderColor: 'black',
9
+ };
10
+ const html = await render(<Hr data-testid="hr-test" style={style} />);
11
+ expect(html).toContain('width:50%');
12
+ expect(html).toContain('border-color:black');
13
+ expect(html).toContain('data-testid="hr-test"');
14
+ });
15
+
16
+ it('renders correctly', async () => {
17
+ const actualOutput = await render(<Hr />);
18
+ expect(actualOutput).toMatchInlineSnapshot(
19
+ `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><hr style="width:100%;border:none;border-color:transparent;border-top:1px solid #eaeaea"/><!--/$-->"`,
20
+ );
21
+ });
22
+ });