react-email 4.0.16 → 4.1.0-canary.10

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 (470) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/dev/index.js +40 -0
  3. package/dev/package.json +13 -0
  4. package/dist/index.js +1 -1
  5. package/package.json +13 -46
  6. package/readme.md +16 -0
  7. package/src/commands/build.ts +306 -0
  8. package/src/commands/dev.ts +27 -0
  9. package/src/commands/export.ts +204 -0
  10. package/src/commands/start.ts +38 -0
  11. package/src/index.ts +55 -0
  12. package/src/utils/__snapshots__/tree.spec.ts.snap +27 -0
  13. package/src/utils/esbuild/renderring-utilities-exporter.ts +1 -1
  14. package/src/utils/get-emails-directory-metadata.spec.ts +1 -1
  15. package/src/utils/get-preview-server-location.ts +51 -0
  16. package/src/utils/index.ts +2 -6
  17. package/src/utils/packageJson.ts +4 -0
  18. package/src/utils/preview/get-env-variables-for-preview-app.ts +14 -0
  19. package/src/utils/preview/hot-reloading/create-dependency-graph.spec.ts +284 -0
  20. package/src/utils/preview/hot-reloading/create-dependency-graph.ts +321 -0
  21. package/src/utils/preview/hot-reloading/get-imported-modules.spec.ts +151 -0
  22. package/src/utils/preview/hot-reloading/get-imported-modules.ts +49 -0
  23. package/src/utils/preview/hot-reloading/resolve-path-aliases.spec.ts +11 -0
  24. package/src/utils/preview/hot-reloading/resolve-path-aliases.ts +32 -0
  25. package/src/utils/preview/hot-reloading/setup-hot-reloading.ts +121 -0
  26. package/src/utils/preview/hot-reloading/test/tsconfig.json +8 -0
  27. package/src/utils/preview/index.ts +2 -0
  28. package/src/utils/preview/serve-static-file.ts +51 -0
  29. package/src/utils/preview/start-dev-server.ts +234 -0
  30. package/src/utils/tree.spec.ts +5 -0
  31. package/src/utils/tree.ts +76 -0
  32. package/src/utils/types/hot-reload-change.ts +1 -1
  33. package/src/utils/types/hot-reload-event.ts +1 -1
  34. package/tsconfig.json +4 -10
  35. package/dist/cli/index.d.mts +0 -1
  36. package/dist/cli/index.d.ts +0 -1
  37. package/dist/cli/index.js +0 -2785
  38. package/dist/cli/index.mjs +0 -1361
  39. package/dist/index.d.mts +0 -20
  40. package/dist/index.d.ts +0 -20
  41. package/dist/index.mjs +0 -21
  42. package/dist/package/index.d.mts +0 -33
  43. package/dist/package/index.d.ts +0 -33
  44. package/dist/package/index.js +0 -62
  45. package/dist/package/index.mjs +0 -7
  46. package/dist/preview/.next/BUILD_ID +0 -1
  47. package/dist/preview/.next/app-build-manifest.json +0 -44
  48. package/dist/preview/.next/app-path-routes-manifest.json +0 -6
  49. package/dist/preview/.next/build-manifest.json +0 -33
  50. package/dist/preview/.next/diagnostics/build-diagnostics.json +0 -6
  51. package/dist/preview/.next/diagnostics/framework.json +0 -1
  52. package/dist/preview/.next/export-marker.json +0 -6
  53. package/dist/preview/.next/images-manifest.json +0 -57
  54. package/dist/preview/.next/next-minimal-server.js.nft.json +0 -1
  55. package/dist/preview/.next/next-server.js.nft.json +0 -1
  56. package/dist/preview/.next/package.json +0 -1
  57. package/dist/preview/.next/prerender-manifest.json +0 -41
  58. package/dist/preview/.next/react-loadable-manifest.json +0 -1
  59. package/dist/preview/.next/required-server-files.json +0 -311
  60. package/dist/preview/.next/routes-manifest.json +0 -64
  61. package/dist/preview/.next/server/app/_not-found/page.js +0 -1
  62. package/dist/preview/.next/server/app/_not-found/page.js.nft.json +0 -1
  63. package/dist/preview/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
  64. package/dist/preview/.next/server/app/favicon.ico/route.js +0 -1
  65. package/dist/preview/.next/server/app/favicon.ico/route.js.nft.json +0 -1
  66. package/dist/preview/.next/server/app/favicon.ico.body +0 -0
  67. package/dist/preview/.next/server/app/favicon.ico.meta +0 -1
  68. package/dist/preview/.next/server/app/page.js +0 -1
  69. package/dist/preview/.next/server/app/page.js.nft.json +0 -1
  70. package/dist/preview/.next/server/app/page_client-reference-manifest.js +0 -1
  71. package/dist/preview/.next/server/app/preview/[...slug]/page.js +0 -321
  72. package/dist/preview/.next/server/app/preview/[...slug]/page.js.nft.json +0 -1
  73. package/dist/preview/.next/server/app/preview/[...slug]/page_client-reference-manifest.js +0 -1
  74. package/dist/preview/.next/server/app-paths-manifest.json +0 -6
  75. package/dist/preview/.next/server/chunks/134.js +0 -6
  76. package/dist/preview/.next/server/chunks/235.js +0 -15
  77. package/dist/preview/.next/server/chunks/275.js +0 -1
  78. package/dist/preview/.next/server/chunks/343.js +0 -20
  79. package/dist/preview/.next/server/chunks/428.js +0 -14
  80. package/dist/preview/.next/server/chunks/963.js +0 -1
  81. package/dist/preview/.next/server/functions-config-manifest.json +0 -4
  82. package/dist/preview/.next/server/interception-route-rewrite-manifest.js +0 -1
  83. package/dist/preview/.next/server/middleware-build-manifest.js +0 -1
  84. package/dist/preview/.next/server/middleware-manifest.json +0 -6
  85. package/dist/preview/.next/server/middleware-react-loadable-manifest.js +0 -1
  86. package/dist/preview/.next/server/next-font-manifest.js +0 -1
  87. package/dist/preview/.next/server/next-font-manifest.json +0 -1
  88. package/dist/preview/.next/server/pages/500.html +0 -1
  89. package/dist/preview/.next/server/pages/_app.js +0 -1
  90. package/dist/preview/.next/server/pages/_app.js.nft.json +0 -1
  91. package/dist/preview/.next/server/pages/_document.js +0 -1
  92. package/dist/preview/.next/server/pages/_document.js.nft.json +0 -1
  93. package/dist/preview/.next/server/pages/_error.js +0 -1
  94. package/dist/preview/.next/server/pages/_error.js.nft.json +0 -1
  95. package/dist/preview/.next/server/pages-manifest.json +0 -5
  96. package/dist/preview/.next/server/server-reference-manifest.js +0 -1
  97. package/dist/preview/.next/server/server-reference-manifest.json +0 -1
  98. package/dist/preview/.next/server/webpack-runtime.js +0 -1
  99. package/dist/preview/.next/static/chunks/107-3043079e7cb8bcae.js +0 -1
  100. package/dist/preview/.next/static/chunks/293-297b1eb2241f9a70.js +0 -1
  101. package/dist/preview/.next/static/chunks/3bd82e28-cda2c00a924937c5.js +0 -1
  102. package/dist/preview/.next/static/chunks/45-1021fac82f766268.js +0 -1
  103. package/dist/preview/.next/static/chunks/484-e38a627386aae911.js +0 -1
  104. package/dist/preview/.next/static/chunks/589-817d8691661d370e.js +0 -1
  105. package/dist/preview/.next/static/chunks/902-c34acb56733e0ce1.js +0 -1
  106. package/dist/preview/.next/static/chunks/app/_not-found/page-4cbc7dce3ad33336.js +0 -1
  107. package/dist/preview/.next/static/chunks/app/layout-269b5cbd8f4cd2e3.js +0 -1
  108. package/dist/preview/.next/static/chunks/app/page-2dbfb5b2dc4b1191.js +0 -1
  109. package/dist/preview/.next/static/chunks/app/preview/[...slug]/page-79e0c9b528a62f8b.js +0 -1
  110. package/dist/preview/.next/static/chunks/f33a14d2-ec7c5f0b91818561.js +0 -6
  111. package/dist/preview/.next/static/chunks/framework-b887e9fc751a9906.js +0 -1
  112. package/dist/preview/.next/static/chunks/main-9a03e7ba8acb1900.js +0 -1
  113. package/dist/preview/.next/static/chunks/main-app-976577a424e11c75.js +0 -1
  114. package/dist/preview/.next/static/chunks/pages/_app-542a93a5a214e1c0.js +0 -1
  115. package/dist/preview/.next/static/chunks/pages/_error-d5fe1b1612642f76.js +0 -1
  116. package/dist/preview/.next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  117. package/dist/preview/.next/static/chunks/webpack-31c45daa2bd82a7b.js +0 -1
  118. package/dist/preview/.next/static/css/ac3decd5d6736fbe.css +0 -3
  119. package/dist/preview/.next/static/media/05613964ce6c782e-s.p.otf +0 -0
  120. package/dist/preview/.next/static/media/11c6126b9369e85e-s.p.otf +0 -0
  121. package/dist/preview/.next/static/media/26a46d62cd723877-s.woff2 +0 -0
  122. package/dist/preview/.next/static/media/26cb97734d8cb717-s.p.otf +0 -0
  123. package/dist/preview/.next/static/media/55c55f0601d81cf3-s.woff2 +0 -0
  124. package/dist/preview/.next/static/media/581909926a08bbc8-s.woff2 +0 -0
  125. package/dist/preview/.next/static/media/8e9860b6e62d6359-s.woff2 +0 -0
  126. package/dist/preview/.next/static/media/97e0cb1ae144a2a9-s.woff2 +0 -0
  127. package/dist/preview/.next/static/media/bb6462617151f6b7-s.p.otf +0 -0
  128. package/dist/preview/.next/static/media/cf6daef822ab0142-s.p.otf +0 -0
  129. package/dist/preview/.next/static/media/df0a9ae256c0569c-s.woff2 +0 -0
  130. package/dist/preview/.next/static/media/e4051546b3043204-s.p.otf +0 -0
  131. package/dist/preview/.next/static/media/e4af272ccee01ff0-s.p.woff2 +0 -0
  132. package/dist/preview/.next/static/media/logo.2ce2a759.png +0 -0
  133. package/dist/preview/.next/static/yxjkw7Y7HRLmRWkl3S43z/_buildManifest.js +0 -1
  134. package/dist/preview/.next/static/yxjkw7Y7HRLmRWkl3S43z/_ssgManifest.js +0 -1
  135. package/dist/preview/.next/trace +0 -27
  136. package/dist/preview/.next/types/app/layout.ts +0 -84
  137. package/dist/preview/.next/types/app/page.ts +0 -84
  138. package/dist/preview/.next/types/app/preview/[...slug]/page.ts +0 -84
  139. package/dist/preview/.next/types/cache-life.d.ts +0 -141
  140. package/dist/preview/.next/types/package.json +0 -1
  141. package/module-punycode.d.ts +0 -3
  142. package/next-env.d.ts +0 -5
  143. package/next.config.js +0 -22
  144. package/postcss.config.js +0 -8
  145. package/scripts/build-preview-server.mjs +0 -33
  146. package/scripts/fill-caniemail-data.mjs +0 -36
  147. package/src/actions/email-validation/caniemail-data.ts +0 -85993
  148. package/src/actions/email-validation/check-compatibility.ts +0 -333
  149. package/src/actions/email-validation/check-images.spec.tsx +0 -100
  150. package/src/actions/email-validation/check-images.ts +0 -160
  151. package/src/actions/email-validation/check-links.spec.tsx +0 -113
  152. package/src/actions/email-validation/check-links.ts +0 -113
  153. package/src/actions/email-validation/get-code-location-from-ast-element.ts +0 -18
  154. package/src/actions/email-validation/quick-fetch.ts +0 -14
  155. package/src/actions/get-email-path-from-slug.ts +0 -32
  156. package/src/actions/get-emails-directory-metadata-action.ts +0 -19
  157. package/src/actions/render-email-by-path.tsx +0 -121
  158. package/src/animated-icons-data/help.json +0 -1082
  159. package/src/animated-icons-data/link.json +0 -1309
  160. package/src/animated-icons-data/load.json +0 -443
  161. package/src/animated-icons-data/mail.json +0 -1320
  162. package/src/app/env.ts +0 -15
  163. package/src/app/favicon.ico +0 -0
  164. package/src/app/fonts/SFMono/SFMonoBold.otf +0 -0
  165. package/src/app/fonts/SFMono/SFMonoBoldItalic.otf +0 -0
  166. package/src/app/fonts/SFMono/SFMonoHeavy.otf +0 -0
  167. package/src/app/fonts/SFMono/SFMonoHeavyItalic.otf +0 -0
  168. package/src/app/fonts/SFMono/SFMonoLight.otf +0 -0
  169. package/src/app/fonts/SFMono/SFMonoLightItalic.otf +0 -0
  170. package/src/app/fonts/SFMono/SFMonoMedium.otf +0 -0
  171. package/src/app/fonts/SFMono/SFMonoMediumItalic.otf +0 -0
  172. package/src/app/fonts/SFMono/SFMonoRegular.otf +0 -0
  173. package/src/app/fonts/SFMono/SFMonoRegularItalic.otf +0 -0
  174. package/src/app/fonts/SFMono/SFMonoSemibold.otf +0 -0
  175. package/src/app/fonts/SFMono/SFMonoSemiboldItalic.otf +0 -0
  176. package/src/app/fonts.ts +0 -39
  177. package/src/app/globals.css +0 -15
  178. package/src/app/layout.tsx +0 -43
  179. package/src/app/logo.png +0 -0
  180. package/src/app/page.tsx +0 -46
  181. package/src/app/preview/[...slug]/page.tsx +0 -157
  182. package/src/app/preview/[...slug]/preview.tsx +0 -216
  183. package/src/app/preview/[...slug]/rendering-error.tsx +0 -40
  184. package/src/commands/testing/out/magic-links/aws-verify-email.html +0 -165
  185. package/src/commands/testing/out/magic-links/linear-login-code.html +0 -90
  186. package/src/commands/testing/out/magic-links/notion-magic-link.html +0 -76
  187. package/src/commands/testing/out/magic-links/plaid-verify-identity.html +0 -78
  188. package/src/commands/testing/out/magic-links/raycast-magic-link.html +0 -91
  189. package/src/commands/testing/out/magic-links/slack-confirm.html +0 -240
  190. package/src/commands/testing/out/newsletters/codepen-challengers.html +0 -548
  191. package/src/commands/testing/out/newsletters/google-play-policy-update.html +0 -339
  192. package/src/commands/testing/out/newsletters/stack-overflow-tips.html +0 -231
  193. package/src/commands/testing/out/notifications/github-access-token.html +0 -104
  194. package/src/commands/testing/out/notifications/papermark-year-in-review.html +0 -317
  195. package/src/commands/testing/out/notifications/vercel-invite-user.html +0 -171
  196. package/src/commands/testing/out/notifications/yelp-recent-login.html +0 -195
  197. package/src/commands/testing/out/receipts/apple-receipt.html +0 -677
  198. package/src/commands/testing/out/receipts/nike-receipt.html +0 -724
  199. package/src/commands/testing/out/reset-password/dropbox-reset-password.html +0 -98
  200. package/src/commands/testing/out/reset-password/twitch-reset-password.html +0 -220
  201. package/src/commands/testing/out/reviews/airbnb-review.html +0 -206
  202. package/src/commands/testing/out/reviews/amazon-review.html +0 -356
  203. package/src/commands/testing/out/static/airbnb-logo.png +0 -0
  204. package/src/commands/testing/out/static/airbnb-review-user.jpg +0 -0
  205. package/src/commands/testing/out/static/amazon-book.jpg +0 -0
  206. package/src/commands/testing/out/static/amazon-facebook.jpg +0 -0
  207. package/src/commands/testing/out/static/amazon-instagram.jpg +0 -0
  208. package/src/commands/testing/out/static/amazon-logo.png +0 -0
  209. package/src/commands/testing/out/static/amazon-prime-logo.png +0 -0
  210. package/src/commands/testing/out/static/amazon-rating.gif +0 -0
  211. package/src/commands/testing/out/static/amazon-twitter.jpg +0 -0
  212. package/src/commands/testing/out/static/apple-card-icon.png +0 -0
  213. package/src/commands/testing/out/static/apple-hbo-max-icon.jpeg +0 -0
  214. package/src/commands/testing/out/static/apple-logo.png +0 -0
  215. package/src/commands/testing/out/static/apple-wallet.png +0 -0
  216. package/src/commands/testing/out/static/aws-logo.png +0 -0
  217. package/src/commands/testing/out/static/codepen-challengers.png +0 -0
  218. package/src/commands/testing/out/static/codepen-cube.png +0 -0
  219. package/src/commands/testing/out/static/codepen-pro.png +0 -0
  220. package/src/commands/testing/out/static/dropbox-logo.png +0 -0
  221. package/src/commands/testing/out/static/github.png +0 -0
  222. package/src/commands/testing/out/static/google-play-academy.png +0 -0
  223. package/src/commands/testing/out/static/google-play-chat.png +0 -0
  224. package/src/commands/testing/out/static/google-play-footer.png +0 -0
  225. package/src/commands/testing/out/static/google-play-header.png +0 -0
  226. package/src/commands/testing/out/static/google-play-icon.png +0 -0
  227. package/src/commands/testing/out/static/google-play-logo.png +0 -0
  228. package/src/commands/testing/out/static/google-play-pl.png +0 -0
  229. package/src/commands/testing/out/static/google-play.png +0 -0
  230. package/src/commands/testing/out/static/koala-logo.png +0 -0
  231. package/src/commands/testing/out/static/linear-logo.png +0 -0
  232. package/src/commands/testing/out/static/netlify-logo.png +0 -0
  233. package/src/commands/testing/out/static/nike-logo.png +0 -0
  234. package/src/commands/testing/out/static/nike-phone.png +0 -0
  235. package/src/commands/testing/out/static/nike-product.png +0 -0
  236. package/src/commands/testing/out/static/nike-recomendation-1.png +0 -0
  237. package/src/commands/testing/out/static/nike-recomendation-2.png +0 -0
  238. package/src/commands/testing/out/static/nike-recomendation-3.png +0 -0
  239. package/src/commands/testing/out/static/nike-recomendation-4.png +0 -0
  240. package/src/commands/testing/out/static/notion-logo.png +0 -0
  241. package/src/commands/testing/out/static/plaid-logo.png +0 -0
  242. package/src/commands/testing/out/static/raycast-bg.png +0 -0
  243. package/src/commands/testing/out/static/raycast-logo.png +0 -0
  244. package/src/commands/testing/out/static/slack-facebook.png +0 -0
  245. package/src/commands/testing/out/static/slack-linkedin.png +0 -0
  246. package/src/commands/testing/out/static/slack-logo.png +0 -0
  247. package/src/commands/testing/out/static/slack-twitter.png +0 -0
  248. package/src/commands/testing/out/static/stack-overflow-header.png +0 -0
  249. package/src/commands/testing/out/static/stack-overflow-logo-sm.png +0 -0
  250. package/src/commands/testing/out/static/stack-overflow-logo.png +0 -0
  251. package/src/commands/testing/out/static/stripe-logo.png +0 -0
  252. package/src/commands/testing/out/static/twitch-icon-facebook.png +0 -0
  253. package/src/commands/testing/out/static/twitch-icon-twitter.png +0 -0
  254. package/src/commands/testing/out/static/twitch-logo.png +0 -0
  255. package/src/commands/testing/out/static/vercel-arrow.png +0 -0
  256. package/src/commands/testing/out/static/vercel-logo.png +0 -0
  257. package/src/commands/testing/out/static/vercel-team.png +0 -0
  258. package/src/commands/testing/out/static/vercel-user.png +0 -0
  259. package/src/commands/testing/out/static/yelp-footer.png +0 -0
  260. package/src/commands/testing/out/static/yelp-header.png +0 -0
  261. package/src/commands/testing/out/static/yelp-logo.png +0 -0
  262. package/src/commands/testing/out/welcome/koala-welcome.html +0 -90
  263. package/src/commands/testing/out/welcome/netlify-welcome.html +0 -199
  264. package/src/commands/testing/out/welcome/stripe-welcome.html +0 -153
  265. package/src/components/button.tsx +0 -101
  266. package/src/components/code-container.tsx +0 -164
  267. package/src/components/code-snippet.tsx +0 -9
  268. package/src/components/code.tsx +0 -184
  269. package/src/components/heading.tsx +0 -113
  270. package/src/components/icons/icon-arrow-down.tsx +0 -16
  271. package/src/components/icons/icon-base.tsx +0 -26
  272. package/src/components/icons/icon-bug.tsx +0 -19
  273. package/src/components/icons/icon-button.tsx +0 -23
  274. package/src/components/icons/icon-check.tsx +0 -19
  275. package/src/components/icons/icon-clipboard.tsx +0 -40
  276. package/src/components/icons/icon-download.tsx +0 -19
  277. package/src/components/icons/icon-email.tsx +0 -18
  278. package/src/components/icons/icon-file.tsx +0 -19
  279. package/src/components/icons/icon-folder-open.tsx +0 -19
  280. package/src/components/icons/icon-folder.tsx +0 -18
  281. package/src/components/icons/icon-hide-sidebar.tsx +0 -23
  282. package/src/components/icons/icon-image.tsx +0 -19
  283. package/src/components/icons/icon-info.tsx +0 -18
  284. package/src/components/icons/icon-link.tsx +0 -14
  285. package/src/components/icons/icon-monitor.tsx +0 -19
  286. package/src/components/icons/icon-phone.tsx +0 -26
  287. package/src/components/icons/icon-reload.tsx +0 -18
  288. package/src/components/icons/icon-source.tsx +0 -19
  289. package/src/components/icons/icon-stamp.tsx +0 -14
  290. package/src/components/icons/icon-warning.tsx +0 -31
  291. package/src/components/index.ts +0 -7
  292. package/src/components/logo.tsx +0 -63
  293. package/src/components/resizable-wrapper.tsx +0 -173
  294. package/src/components/send.tsx +0 -134
  295. package/src/components/shell.tsx +0 -95
  296. package/src/components/sidebar/file-tree-directory-children.tsx +0 -134
  297. package/src/components/sidebar/file-tree-directory.tsx +0 -92
  298. package/src/components/sidebar/file-tree.tsx +0 -31
  299. package/src/components/sidebar/index.ts +0 -1
  300. package/src/components/sidebar/sidebar.tsx +0 -43
  301. package/src/components/text.tsx +0 -99
  302. package/src/components/toolbar/checking-results.tsx +0 -150
  303. package/src/components/toolbar/code-preview-line-link.tsx +0 -40
  304. package/src/components/toolbar/compatibility.tsx +0 -113
  305. package/src/components/toolbar/linter.tsx +0 -278
  306. package/src/components/toolbar/results.tsx +0 -51
  307. package/src/components/toolbar/spam-assassin.tsx +0 -155
  308. package/src/components/toolbar/toolbar-button.tsx +0 -52
  309. package/src/components/toolbar/use-cached-state.ts +0 -33
  310. package/src/components/toolbar.tsx +0 -349
  311. package/src/components/tooltip-content.tsx +0 -31
  312. package/src/components/tooltip.tsx +0 -19
  313. package/src/components/topbar/active-view-toggle-group.tsx +0 -86
  314. package/src/components/topbar/view-size-controls.tsx +0 -247
  315. package/src/components/topbar.tsx +0 -59
  316. package/src/contexts/emails.tsx +0 -59
  317. package/src/contexts/fragment-identifier.tsx +0 -48
  318. package/src/contexts/preview.tsx +0 -79
  319. package/src/hooks/use-clamped-state.ts +0 -24
  320. package/src/hooks/use-email-rendering-result.ts +0 -58
  321. package/src/hooks/use-fragment-identifier.ts +0 -14
  322. package/src/hooks/use-hot-reload.ts +0 -31
  323. package/src/hooks/use-icon-animation.ts +0 -41
  324. package/src/hooks/use-rendering-metadata.ts +0 -36
  325. package/src/package/body/dist/index.d.mts +0 -6
  326. package/src/package/body/dist/index.d.ts +0 -6
  327. package/src/package/body/dist/index.js +0 -79
  328. package/src/package/body/dist/index.mjs +0 -45
  329. package/src/package/button/dist/index.d.mts +0 -6
  330. package/src/package/button/dist/index.d.ts +0 -6
  331. package/src/package/button/dist/index.js +0 -252
  332. package/src/package/button/dist/index.mjs +0 -218
  333. package/src/package/code-block/dist/index.d.mts +0 -4906
  334. package/src/package/code-block/dist/index.d.ts +0 -4906
  335. package/src/package/code-block/dist/index.js +0 -18205
  336. package/src/package/code-block/dist/index.mjs +0 -18133
  337. package/src/package/code-inline/dist/index.d.mts +0 -11
  338. package/src/package/code-inline/dist/index.d.ts +0 -11
  339. package/src/package/code-inline/dist/index.js +0 -106
  340. package/src/package/code-inline/dist/index.mjs +0 -72
  341. package/src/package/column/dist/index.d.mts +0 -6
  342. package/src/package/column/dist/index.d.ts +0 -6
  343. package/src/package/column/dist/index.js +0 -79
  344. package/src/package/column/dist/index.mjs +0 -45
  345. package/src/package/components/dist/index.d.mts +0 -20
  346. package/src/package/components/dist/index.d.ts +0 -20
  347. package/src/package/components/dist/index.js +0 -62
  348. package/src/package/components/dist/index.mjs +0 -21
  349. package/src/package/container/dist/index.d.mts +0 -6
  350. package/src/package/container/dist/index.d.ts +0 -6
  351. package/src/package/container/dist/index.js +0 -93
  352. package/src/package/container/dist/index.mjs +0 -59
  353. package/src/package/font/dist/index.d.mts +0 -25
  354. package/src/package/font/dist/index.d.ts +0 -25
  355. package/src/package/font/dist/index.js +0 -55
  356. package/src/package/font/dist/index.mjs +0 -28
  357. package/src/package/head/dist/index.d.mts +0 -6
  358. package/src/package/head/dist/index.d.ts +0 -6
  359. package/src/package/head/dist/index.js +0 -83
  360. package/src/package/head/dist/index.mjs +0 -49
  361. package/src/package/heading/dist/index.d.mts +0 -43
  362. package/src/package/heading/dist/index.d.ts +0 -43
  363. package/src/package/heading/dist/index.js +0 -113
  364. package/src/package/heading/dist/index.mjs +0 -79
  365. package/src/package/hr/dist/index.d.mts +0 -6
  366. package/src/package/hr/dist/index.d.ts +0 -6
  367. package/src/package/hr/dist/index.js +0 -89
  368. package/src/package/hr/dist/index.mjs +0 -55
  369. package/src/package/html/dist/index.d.mts +0 -6
  370. package/src/package/html/dist/index.d.ts +0 -6
  371. package/src/package/html/dist/index.js +0 -79
  372. package/src/package/html/dist/index.mjs +0 -45
  373. package/src/package/img/dist/index.d.mts +0 -6
  374. package/src/package/img/dist/index.d.ts +0 -6
  375. package/src/package/img/dist/index.js +0 -94
  376. package/src/package/img/dist/index.mjs +0 -60
  377. package/src/package/link/dist/index.d.mts +0 -6
  378. package/src/package/link/dist/index.d.ts +0 -6
  379. package/src/package/link/dist/index.js +0 -90
  380. package/src/package/link/dist/index.mjs +0 -56
  381. package/src/package/markdown/dist/index.d.mts +0 -15
  382. package/src/package/markdown/dist/index.d.ts +0 -15
  383. package/src/package/markdown/dist/index.js +0 -92
  384. package/src/package/markdown/dist/index.mjs +0 -58
  385. package/src/package/preview/dist/index.d.mts +0 -12
  386. package/src/package/preview/dist/index.d.ts +0 -12
  387. package/src/package/preview/dist/index.js +0 -108
  388. package/src/package/preview/dist/index.mjs +0 -73
  389. package/src/package/render/dist/browser/index.d.mts +0 -24
  390. package/src/package/render/dist/browser/index.d.ts +0 -24
  391. package/src/package/render/dist/browser/index.js +0 -250
  392. package/src/package/render/dist/browser/index.mjs +0 -214
  393. package/src/package/render/dist/index.d.mts +0 -23
  394. package/src/package/render/dist/index.d.ts +0 -23
  395. package/src/package/render/dist/index.js +0 -768
  396. package/src/package/render/dist/index.mjs +0 -733
  397. package/src/package/render/dist/node/index.d.mts +0 -27
  398. package/src/package/render/dist/node/index.d.ts +0 -27
  399. package/src/package/render/dist/node/index.js +0 -212
  400. package/src/package/render/dist/node/index.mjs +0 -176
  401. package/src/package/row/dist/index.d.mts +0 -10
  402. package/src/package/row/dist/index.d.ts +0 -10
  403. package/src/package/row/dist/index.js +0 -93
  404. package/src/package/row/dist/index.mjs +0 -59
  405. package/src/package/section/dist/index.d.mts +0 -6
  406. package/src/package/section/dist/index.d.ts +0 -6
  407. package/src/package/section/dist/index.js +0 -93
  408. package/src/package/section/dist/index.mjs +0 -59
  409. package/src/package/tailwind/dist/index.d.ts +0 -19
  410. package/src/package/tailwind/dist/index.js +0 -48
  411. package/src/package/tailwind/dist/index.mjs +0 -17167
  412. package/src/package/tailwind/dist/tailwindcss/config.d.ts +0 -376
  413. package/src/package/tailwind/dist/tailwindcss/generated/.gitkeep +0 -0
  414. package/src/package/tailwind/dist/tailwindcss/generated/colors.d.ts +0 -298
  415. package/src/package/tailwind/dist/tailwindcss/generated/corePluginList.d.ts +0 -1
  416. package/src/package/tailwind/dist/tailwindcss/generated/default-theme.d.ts +0 -397
  417. package/src/package/tailwind/dist/tailwindcss/index.d.ts +0 -11
  418. package/src/package/text/dist/index.d.mts +0 -6
  419. package/src/package/text/dist/index.d.ts +0 -6
  420. package/src/package/text/dist/index.js +0 -89
  421. package/src/package/text/dist/index.mjs +0 -55
  422. package/src/utils/__snapshots__/get-email-component.spec.ts.snap +0 -3
  423. package/src/utils/caniemail/all-css-properties.ts +0 -358
  424. package/src/utils/caniemail/ast/__snapshots__/get-object-variables.spec.ts.snap +0 -74
  425. package/src/utils/caniemail/ast/__snapshots__/get-used-style-properties.spec.ts.snap +0 -24
  426. package/src/utils/caniemail/ast/get-object-variables.spec.ts +0 -19
  427. package/src/utils/caniemail/ast/get-object-variables.ts +0 -61
  428. package/src/utils/caniemail/ast/get-used-style-properties.spec.ts +0 -23
  429. package/src/utils/caniemail/ast/get-used-style-properties.ts +0 -91
  430. package/src/utils/caniemail/get-compatibility-stats-for-entry.ts +0 -118
  431. package/src/utils/caniemail/get-css-functions.ts +0 -25
  432. package/src/utils/caniemail/get-css-property-names.ts +0 -32
  433. package/src/utils/caniemail/get-css-property-with-value.ts +0 -14
  434. package/src/utils/caniemail/get-css-unit.ts +0 -3
  435. package/src/utils/caniemail/get-element-attributes.ts +0 -7
  436. package/src/utils/caniemail/get-element-names.ts +0 -20
  437. package/src/utils/caniemail/tailwind/generate-tailwind-rules.ts +0 -30
  438. package/src/utils/caniemail/tailwind/get-tailwind-config.ts +0 -187
  439. package/src/utils/caniemail/tailwind/get-tailwind-metadata.spec.ts +0 -25
  440. package/src/utils/caniemail/tailwind/get-tailwind-metadata.ts +0 -45
  441. package/src/utils/caniemail/tailwind/setup-tailwind-context.ts +0 -15
  442. package/src/utils/cn.ts +0 -6
  443. package/src/utils/constants.ts +0 -6
  444. package/src/utils/contains-email-template.spec.ts +0 -124
  445. package/src/utils/contains-email-template.ts +0 -33
  446. package/src/utils/copy-text-to-clipboard.ts +0 -7
  447. package/src/utils/get-email-component.spec.ts +0 -41
  448. package/src/utils/get-email-component.ts +0 -134
  449. package/src/utils/get-line-and-column-from-offset.spec.ts +0 -11
  450. package/src/utils/get-line-and-column-from-offset.ts +0 -11
  451. package/src/utils/improve-error-with-sourcemap.ts +0 -86
  452. package/src/utils/js-email-detection.spec.ts +0 -24
  453. package/src/utils/language-map.ts +0 -7
  454. package/src/utils/linting.ts +0 -60
  455. package/src/utils/load-stream.ts +0 -15
  456. package/src/utils/result.ts +0 -49
  457. package/src/utils/run-bundled-code.ts +0 -64
  458. package/src/utils/sanitize.ts +0 -6
  459. package/src/utils/static-node-modules-for-vm.ts +0 -93
  460. package/src/utils/testing/js-email-export-default.js +0 -17
  461. package/src/utils/testing/js-email-test.js +0 -18
  462. package/src/utils/testing/mdx-email-test.js +0 -128
  463. package/src/utils/testing/request-response-email.tsx +0 -9
  464. package/src/utils/types/as.ts +0 -26
  465. package/src/utils/types/email-template.ts +0 -8
  466. package/src/utils/types/error-object.ts +0 -11
  467. package/src/utils/unreachable.ts +0 -8
  468. package/tailwind-internals.d.ts +0 -133
  469. package/tailwind.config.ts +0 -99
  470. /package/src/{components/toolbar/results-table.tsx → utils/preview/hot-reloading/test/some-file.ts} +0 -0
@@ -0,0 +1,151 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import { getImportedModules } from './get-imported-modules.js';
3
+
4
+ vi.mock('@babel/traverse', async () => {
5
+ const traverse = await vi.importActual('@babel/traverse');
6
+ return { default: traverse };
7
+ });
8
+
9
+ describe('getImportedModules()', () => {
10
+ it('should work with this test file', async () => {
11
+ const contents = await fs.readFile(import.meta.filename, 'utf8');
12
+
13
+ expect(getImportedModules(contents)).toEqual([
14
+ 'node:fs',
15
+ './get-imported-modules.js',
16
+ ]);
17
+ });
18
+
19
+ it('should work with direct exports', () => {
20
+ const contents = `export * from './component-a';
21
+ export { ComponentB } from './component-b';
22
+
23
+ import { ComponentC } from './component-c';
24
+ export { ComponentC }`;
25
+ expect(getImportedModules(contents)).toEqual([
26
+ './component-a',
27
+ './component-b',
28
+ './component-c',
29
+ ]);
30
+ });
31
+
32
+ it('should work with regular imports and double quotes', () => {
33
+ const contents = `import {
34
+ Body,
35
+ Button,
36
+ Container,
37
+ Column,
38
+ Head,
39
+ Heading,
40
+ Hr,
41
+ Html,
42
+ Img,
43
+ Link,
44
+ Preview,
45
+ Row,
46
+ Section,
47
+ Text,
48
+ } from "@react-email/components";
49
+ import { Tailwind } from "@react-email/tailwind";
50
+ import { Component } from '../../my-component';
51
+
52
+ import * as React from "react";
53
+ `;
54
+ expect(getImportedModules(contents)).toEqual([
55
+ '@react-email/components',
56
+ '@react-email/tailwind',
57
+ '../../my-component',
58
+ 'react',
59
+ ]);
60
+ });
61
+
62
+ it('should work with regular imports and single quotes', () => {
63
+ const contents = `import {
64
+ Body,
65
+ Button,
66
+ Container,
67
+ Column,
68
+ Head,
69
+ Heading,
70
+ Hr,
71
+ Html,
72
+ Img,
73
+ Link,
74
+ Preview,
75
+ Row,
76
+ Section,
77
+ Text,
78
+ } from '@react-email/components';
79
+ import { Tailwind } from '@react-email/tailwind';
80
+ import { Component } from '../../my-component';
81
+
82
+ import * as React from 'react';
83
+ `;
84
+ expect(getImportedModules(contents)).toEqual([
85
+ '@react-email/components',
86
+ '@react-email/tailwind',
87
+ '../../my-component',
88
+ 'react',
89
+ ]);
90
+ });
91
+
92
+ it('should work with commonjs require with double quotes', () => {
93
+ const contents = `const {
94
+ Body,
95
+ Button,
96
+ Container,
97
+ Column,
98
+ Head,
99
+ Heading,
100
+ Hr,
101
+ Html,
102
+ Img,
103
+ Link,
104
+ Preview,
105
+ Row,
106
+ Section,
107
+ Text,
108
+ } = require("@react-email/components");
109
+ const { Tailwind } = require("@react-email/tailwind");
110
+ const { Component } = require("../../my-component");
111
+
112
+ const React = require("react");
113
+ `;
114
+ expect(getImportedModules(contents)).toEqual([
115
+ '@react-email/components',
116
+ '@react-email/tailwind',
117
+ '../../my-component',
118
+ 'react',
119
+ ]);
120
+ });
121
+
122
+ it('should work with commonjs require with single quotes', () => {
123
+ const contents = `const {
124
+ Body,
125
+ Button,
126
+ Container,
127
+ Column,
128
+ Head,
129
+ Heading,
130
+ Hr,
131
+ Html,
132
+ Img,
133
+ Link,
134
+ Preview,
135
+ Row,
136
+ Section,
137
+ Text,
138
+ } = require('@react-email/components');
139
+ const { Tailwind } = require('@react-email/tailwind');
140
+ const { Component } = require('../../my-component');
141
+
142
+ const React = require('react');
143
+ `;
144
+ expect(getImportedModules(contents)).toEqual([
145
+ '@react-email/components',
146
+ '@react-email/tailwind',
147
+ '../../my-component',
148
+ 'react',
149
+ ]);
150
+ });
151
+ });
@@ -0,0 +1,49 @@
1
+ import { parse } from '@babel/parser';
2
+
3
+ import traverseModule from '@babel/traverse';
4
+
5
+ const traverse =
6
+ // we keep this check here so that this still works with the dev:preview
7
+ // script's use of tsx
8
+ typeof traverseModule === 'function'
9
+ ? traverseModule
10
+ : traverseModule.default;
11
+
12
+ export const getImportedModules = (contents: string) => {
13
+ const importedPaths: string[] = [];
14
+ const parsedContents = parse(contents, {
15
+ sourceType: 'unambiguous',
16
+ strictMode: false,
17
+ errorRecovery: true,
18
+ plugins: ['jsx', 'typescript', 'decorators'],
19
+ });
20
+
21
+ traverse(parsedContents, {
22
+ ImportDeclaration({ node }) {
23
+ importedPaths.push(node.source.value);
24
+ },
25
+ ExportAllDeclaration({ node }) {
26
+ importedPaths.push(node.source.value);
27
+ },
28
+ ExportNamedDeclaration({ node }) {
29
+ if (node.source) {
30
+ importedPaths.push(node.source.value);
31
+ }
32
+ },
33
+ TSExternalModuleReference({ node }) {
34
+ importedPaths.push(node.expression.value);
35
+ },
36
+ CallExpression({ node }) {
37
+ if ('name' in node.callee && node.callee.name === 'require') {
38
+ if (node.arguments.length === 1) {
39
+ const importPathNode = node.arguments[0]!;
40
+ if (importPathNode!.type === 'StringLiteral') {
41
+ importedPaths.push(importPathNode.value);
42
+ }
43
+ }
44
+ }
45
+ },
46
+ });
47
+
48
+ return importedPaths;
49
+ };
@@ -0,0 +1,11 @@
1
+ import path from 'node:path';
2
+ import { resolvePathAliases } from './resolve-path-aliases.js';
3
+
4
+ test('resolveImports()', async () => {
5
+ expect(
6
+ resolvePathAliases(
7
+ ['@/some-file'],
8
+ path.resolve(import.meta.dirname, './test'),
9
+ ),
10
+ ).toEqual(['./some-file']);
11
+ });
@@ -0,0 +1,32 @@
1
+ import path from 'node:path';
2
+ import { createMatchPath, loadConfig } from 'tsconfig-paths';
3
+
4
+ export const resolvePathAliases = (
5
+ importPaths: string[],
6
+ projectPath: string,
7
+ ) => {
8
+ const configLoadResult = loadConfig(projectPath);
9
+
10
+ if (configLoadResult.resultType === 'success') {
11
+ const matchPath = createMatchPath(
12
+ configLoadResult.absoluteBaseUrl,
13
+ configLoadResult.paths,
14
+ );
15
+ return importPaths.map((importedPath) => {
16
+ const unaliasedPath = matchPath(importedPath, undefined, undefined, [
17
+ '.tsx',
18
+ '.ts',
19
+ '.js',
20
+ '.jsx',
21
+ '.cjs',
22
+ '.mjs',
23
+ ]);
24
+ if (unaliasedPath) {
25
+ return `./${path.relative(projectPath, unaliasedPath)}`;
26
+ }
27
+ return importedPath;
28
+ });
29
+ }
30
+
31
+ return importPaths;
32
+ };
@@ -0,0 +1,121 @@
1
+ import type http from 'node:http';
2
+ import path from 'node:path';
3
+ import { watch } from 'chokidar';
4
+ import debounce from 'debounce';
5
+ import { type Socket, Server as SocketServer } from 'socket.io';
6
+ import type { HotReloadChange } from '../../types/hot-reload-change.js';
7
+ import { createDependencyGraph } from './create-dependency-graph.js';
8
+
9
+ export const setupHotreloading = async (
10
+ devServer: http.Server,
11
+ emailDirRelativePath: string,
12
+ ) => {
13
+ let clients: Socket[] = [];
14
+ const io = new SocketServer(devServer);
15
+
16
+ io.on('connection', (client) => {
17
+ clients.push(client);
18
+
19
+ client.on('disconnect', () => {
20
+ clients = clients.filter((item) => item !== client);
21
+ });
22
+ });
23
+
24
+ // used to keep track of all changes
25
+ // and send them at once to the preview app through the web socket
26
+ let changes = [] as HotReloadChange[];
27
+
28
+ const reload = debounce(() => {
29
+ // we detect these using the useHotreload hook on the Next app
30
+ clients.forEach((client) => {
31
+ client.emit(
32
+ 'reload',
33
+ changes.filter((change) =>
34
+ // Ensures only changes inside the emails directory are emitted
35
+ path
36
+ .resolve(absolutePathToEmailsDirectory, change.filename)
37
+ .startsWith(absolutePathToEmailsDirectory),
38
+ ),
39
+ );
40
+ });
41
+
42
+ changes = [];
43
+ }, 150);
44
+
45
+ const absolutePathToEmailsDirectory = path.resolve(
46
+ process.cwd(),
47
+ emailDirRelativePath,
48
+ );
49
+
50
+ const [dependencyGraph, updateDependencyGraph, { resolveDependentsOf }] =
51
+ await createDependencyGraph(absolutePathToEmailsDirectory);
52
+
53
+ const watcher = watch('', {
54
+ ignoreInitial: true,
55
+ cwd: absolutePathToEmailsDirectory,
56
+ });
57
+
58
+ const getFilesOutsideEmailsDirectory = () =>
59
+ Object.keys(dependencyGraph).filter((p) =>
60
+ path.relative(absolutePathToEmailsDirectory, p).startsWith('..'),
61
+ );
62
+ let filesOutsideEmailsDirectory = getFilesOutsideEmailsDirectory();
63
+ // adds in to be watched separately all of the files that are outside of
64
+ // the user's emails directory
65
+ for (const p of filesOutsideEmailsDirectory) {
66
+ watcher.add(p);
67
+ }
68
+
69
+ const exit = async () => {
70
+ await watcher.close();
71
+ };
72
+ process.on('SIGINT', exit);
73
+ process.on('uncaughtException', exit);
74
+
75
+ watcher.on('all', async (event, relativePathToChangeTarget) => {
76
+ const file = relativePathToChangeTarget.split(path.sep);
77
+ if (file.length === 0) {
78
+ return;
79
+ }
80
+ const pathToChangeTarget = path.resolve(
81
+ absolutePathToEmailsDirectory,
82
+ relativePathToChangeTarget,
83
+ );
84
+
85
+ await updateDependencyGraph(event, pathToChangeTarget);
86
+
87
+ const newFilesOutsideEmailsDirectory = getFilesOutsideEmailsDirectory();
88
+ // updates the files outside of the user's emails directory by unwatching
89
+ // the inexistent ones and watching the new ones
90
+ //
91
+ // this is necessary to avoid the issue mentioned here https://github.com/resend/react-email/issues/1433#issuecomment-2177515290
92
+ for (const p of filesOutsideEmailsDirectory) {
93
+ if (!newFilesOutsideEmailsDirectory.includes(p)) {
94
+ watcher.unwatch(p);
95
+ }
96
+ }
97
+ for (const p of newFilesOutsideEmailsDirectory) {
98
+ if (!filesOutsideEmailsDirectory.includes(p)) {
99
+ watcher.add(p);
100
+ }
101
+ }
102
+ filesOutsideEmailsDirectory = newFilesOutsideEmailsDirectory;
103
+
104
+ changes.push({
105
+ event,
106
+ filename: relativePathToChangeTarget,
107
+ });
108
+
109
+ // These dependents are dependents resolved recursively, so even dependents of dependents
110
+ // will be notified of this change so that we ensure that things are updated in the preview.
111
+ for (const dependentPath of resolveDependentsOf(pathToChangeTarget)) {
112
+ changes.push({
113
+ event: 'change' as const,
114
+ filename: path.relative(absolutePathToEmailsDirectory, dependentPath),
115
+ });
116
+ }
117
+ reload();
118
+ });
119
+
120
+ return watcher;
121
+ };
@@ -0,0 +1,8 @@
1
+ {
2
+ "compilerOptions": {
3
+ "paths": {
4
+ "@/*": ["./*"]
5
+ }
6
+ },
7
+ "include": ["**/*.ts", "**/*.tsx"]
8
+ }
@@ -0,0 +1,2 @@
1
+ export * from './hot-reloading/setup-hot-reloading.js';
2
+ export * from './start-dev-server.js';
@@ -0,0 +1,51 @@
1
+ import { existsSync, promises as fs } from 'node:fs';
2
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
3
+ import type http from 'node:http';
4
+ import path from 'node:path';
5
+ import type url from 'node:url';
6
+ import { lookup } from 'mime-types';
7
+
8
+ export const serveStaticFile = async (
9
+ res: http.ServerResponse,
10
+ parsedUrl: url.UrlWithParsedQuery,
11
+ staticDirRelativePath: string,
12
+ ) => {
13
+ const pathname = parsedUrl.pathname!.replace('/static', './static');
14
+ const ext = path.parse(pathname).ext;
15
+
16
+ const staticBaseDir = path.resolve(process.cwd(), staticDirRelativePath);
17
+ const fileAbsolutePath = path.resolve(staticBaseDir, pathname);
18
+ if (!fileAbsolutePath.startsWith(staticBaseDir)) {
19
+ res.statusCode = 403;
20
+ res.end();
21
+ return;
22
+ }
23
+
24
+ try {
25
+ const fileHandle = await fs.open(fileAbsolutePath, 'r');
26
+
27
+ const fileData = await fs.readFile(fileHandle);
28
+
29
+ // if the file is found, set Content-type and send data
30
+ res.setHeader('Content-type', lookup(ext) || 'text/plain');
31
+ res.end(fileData);
32
+
33
+ fileHandle.close();
34
+ } catch (exception) {
35
+ if (!existsSync(fileAbsolutePath)) {
36
+ res.statusCode = 404;
37
+ res.end();
38
+ } else {
39
+ const sanitizedFilePath = fileAbsolutePath.replace(/\n|\r/g, '');
40
+ console.error(
41
+ `Could not read file at ${sanitizedFilePath} to be served, here's the exception:`,
42
+ exception,
43
+ );
44
+
45
+ res.statusCode = 500;
46
+ res.end(
47
+ 'Could not read file to be served! Check your terminal for more information.',
48
+ );
49
+ }
50
+ }
51
+ };
@@ -0,0 +1,234 @@
1
+ import http from 'node:http';
2
+ import path from 'node:path';
3
+ import url from 'node:url';
4
+ import chalk from 'chalk';
5
+ import { createJiti } from 'jiti';
6
+ import logSymbols from 'log-symbols';
7
+ import ora from 'ora';
8
+ import { registerSpinnerAutostopping } from '../../utils/register-spinner-autostopping.js';
9
+ import { getPreviewServerLocation } from '../get-preview-server-location.js';
10
+ import { packageJson } from '../packageJson.js';
11
+ import { getEnvVariablesForPreviewApp } from './get-env-variables-for-preview-app.js';
12
+ import { serveStaticFile } from './serve-static-file.js';
13
+
14
+ let devServer: http.Server | undefined;
15
+
16
+ const safeAsyncServerListen = (server: http.Server, port: number) => {
17
+ return new Promise<{ portAlreadyInUse: boolean }>((resolve) => {
18
+ server.listen(port, () => {
19
+ resolve({ portAlreadyInUse: false });
20
+ });
21
+
22
+ server.on('error', (e: NodeJS.ErrnoException) => {
23
+ if (e.code === 'EADDRINUSE') {
24
+ resolve({ portAlreadyInUse: true });
25
+ }
26
+ });
27
+ });
28
+ };
29
+
30
+ const filename = url.fileURLToPath(import.meta.url);
31
+ const dirname = path.dirname(filename);
32
+
33
+ export const isDev = !dirname.includes('dist');
34
+
35
+ export const startDevServer = async (
36
+ emailsDirRelativePath: string,
37
+ staticBaseDirRelativePath: string,
38
+ port: number,
39
+ ): Promise<http.Server> => {
40
+ const [majorNodeVersion] = process.versions.node.split('.');
41
+ if (majorNodeVersion && Number.parseInt(majorNodeVersion) < 18) {
42
+ console.error(
43
+ ` ${logSymbols.error} Node ${majorNodeVersion} is not supported. Please upgrade to Node 18 or higher.`,
44
+ );
45
+ process.exit(1);
46
+ }
47
+
48
+ const previewServerLocation = await getPreviewServerLocation();
49
+ const previewServer = createJiti(previewServerLocation);
50
+
51
+ const { default: next } =
52
+ await previewServer.import<typeof import('next')>('next');
53
+
54
+ devServer = http.createServer((req, res) => {
55
+ if (!req.url) {
56
+ res.end(404);
57
+ return;
58
+ }
59
+
60
+ const parsedUrl = url.parse(req.url, true);
61
+
62
+ // Never cache anything to avoid
63
+ res.setHeader(
64
+ 'Cache-Control',
65
+ 'no-cache, max-age=0, must-revalidate, no-store',
66
+ );
67
+ res.setHeader('Pragma', 'no-cache');
68
+ res.setHeader('Expires', '-1');
69
+
70
+ try {
71
+ if (
72
+ parsedUrl.path?.includes('static/') &&
73
+ !parsedUrl.path.includes('_next/static/')
74
+ ) {
75
+ void serveStaticFile(res, parsedUrl, staticBaseDirRelativePath);
76
+ } else if (!isNextReady) {
77
+ void nextReadyPromise.then(() =>
78
+ nextHandleRequest?.(req, res, parsedUrl),
79
+ );
80
+ } else {
81
+ void nextHandleRequest?.(req, res, parsedUrl);
82
+ }
83
+ } catch (e) {
84
+ console.error('caught error', e);
85
+
86
+ res.writeHead(500);
87
+ res.end();
88
+ }
89
+ });
90
+
91
+ const { portAlreadyInUse } = await safeAsyncServerListen(devServer, port);
92
+
93
+ if (!portAlreadyInUse) {
94
+ // this errors when linting but doesn't on the editor so ignore the warning on this
95
+ /* eslint-disable-next-line @typescript-eslint/no-unsafe-member-access */
96
+ console.log(chalk.greenBright(` React Email ${packageJson.version}`));
97
+ console.log(` Running preview at: http://localhost:${port}\n`);
98
+ } else {
99
+ const nextPortToTry = port + 1;
100
+ console.warn(
101
+ ` ${logSymbols.warning} Port ${port} is already in use, trying ${nextPortToTry}`,
102
+ );
103
+ return startDevServer(
104
+ emailsDirRelativePath,
105
+ staticBaseDirRelativePath,
106
+ nextPortToTry,
107
+ );
108
+ }
109
+
110
+ devServer.on('close', async () => {
111
+ await app.close();
112
+ });
113
+
114
+ devServer.on('error', (e: NodeJS.ErrnoException) => {
115
+ spinner.stopAndPersist({
116
+ symbol: logSymbols.error,
117
+ text: `Preview Server had an error: ${e}`,
118
+ });
119
+ process.exit(1);
120
+ });
121
+
122
+ const spinner = ora({
123
+ text: 'Getting react-email preview server ready...\n',
124
+ prefixText: ' ',
125
+ }).start();
126
+
127
+ registerSpinnerAutostopping(spinner);
128
+ const timeBeforeNextReady = performance.now();
129
+
130
+ // these environment variables are used on the next app
131
+ // this is the most reliable way of communicating these paths through
132
+ process.env = {
133
+ NODE_ENV: 'development',
134
+ ...(process.env as Omit<NodeJS.ProcessEnv, 'NODE_ENV'> & {
135
+ NODE_ENV?: NodeJS.ProcessEnv['NODE_ENV'];
136
+ }),
137
+ ...getEnvVariablesForPreviewApp(
138
+ // If we don't do normalization here, stuff like https://github.com/resend/react-email/issues/1354 happens.
139
+ path.normalize(emailsDirRelativePath),
140
+ process.cwd(),
141
+ ),
142
+ };
143
+
144
+ const app = next({
145
+ // passing in env here does not get the environment variables there
146
+ dev: isDev,
147
+ conf: {
148
+ images: {
149
+ // This is to avoid the warning with sharp
150
+ unoptimized: true,
151
+ },
152
+ },
153
+ hostname: 'localhost',
154
+ port,
155
+ dir: previewServerLocation,
156
+ });
157
+
158
+ let isNextReady = false;
159
+ const nextReadyPromise = app.prepare();
160
+ try {
161
+ await nextReadyPromise;
162
+ } catch (exception) {
163
+ spinner.stopAndPersist({
164
+ symbol: logSymbols.error,
165
+ text: ` Preview Server had an error: ${exception}`,
166
+ });
167
+ process.exit(1);
168
+ }
169
+ isNextReady = true;
170
+
171
+ const nextHandleRequest:
172
+ | ReturnType<typeof app.getRequestHandler>
173
+ | undefined = app.getRequestHandler();
174
+
175
+ const secondsToNextReady = (
176
+ (performance.now() - timeBeforeNextReady) /
177
+ 1000
178
+ ).toFixed(1);
179
+
180
+ spinner.stopAndPersist({
181
+ text: `Ready in ${secondsToNextReady}s\n`,
182
+ symbol: logSymbols.success,
183
+ });
184
+
185
+ return devServer;
186
+ };
187
+
188
+ // based on https://stackoverflow.com/a/14032965
189
+ const makeExitHandler =
190
+ (
191
+ options?:
192
+ | { shouldKillProcess: false }
193
+ | { shouldKillProcess: true; killWithErrorCode: boolean },
194
+ ) =>
195
+ (codeSignalOrError: number | NodeJS.Signals | Error) => {
196
+ if (typeof devServer !== 'undefined') {
197
+ console.log('\nshutting down dev server');
198
+ devServer.close();
199
+ devServer = undefined;
200
+ }
201
+
202
+ if (codeSignalOrError instanceof Error) {
203
+ console.error(codeSignalOrError);
204
+ }
205
+
206
+ if (options?.shouldKillProcess) {
207
+ process.exit(options.killWithErrorCode ? 1 : 0);
208
+ }
209
+ };
210
+
211
+ // do something when app is closing
212
+ process.on('exit', makeExitHandler());
213
+
214
+ // catches ctrl+c event
215
+ process.on(
216
+ 'SIGINT',
217
+ makeExitHandler({ shouldKillProcess: true, killWithErrorCode: false }),
218
+ );
219
+
220
+ // catches "kill pid" (for example: nodemon restart)
221
+ process.on(
222
+ 'SIGUSR1',
223
+ makeExitHandler({ shouldKillProcess: true, killWithErrorCode: false }),
224
+ );
225
+ process.on(
226
+ 'SIGUSR2',
227
+ makeExitHandler({ shouldKillProcess: true, killWithErrorCode: false }),
228
+ );
229
+
230
+ // catches uncaught exceptions
231
+ process.on(
232
+ 'uncaughtException',
233
+ makeExitHandler({ shouldKillProcess: true, killWithErrorCode: true }),
234
+ );
@@ -0,0 +1,5 @@
1
+ import { tree } from './tree.js';
2
+
3
+ test('tree(__dirname, 2)', async () => {
4
+ expect(await tree(__dirname, 2)).toMatchSnapshot();
5
+ });