react-email 4.0.7 → 4.0.9

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 (156) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/cli/index.mjs +214 -186
  3. package/dist/preview/.next/BUILD_ID +1 -1
  4. package/dist/preview/.next/app-build-manifest.json +14 -14
  5. package/dist/preview/.next/app-path-routes-manifest.json +1 -1
  6. package/dist/preview/.next/build-manifest.json +2 -2
  7. package/dist/preview/.next/next-minimal-server.js.nft.json +1 -1
  8. package/dist/preview/.next/next-server.js.nft.json +1 -1
  9. package/dist/preview/.next/prerender-manifest.json +3 -3
  10. package/dist/preview/.next/server/app/_not-found/page.js +1 -1
  11. package/dist/preview/.next/server/app/_not-found/page.js.nft.json +1 -1
  12. package/dist/preview/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  13. package/dist/preview/.next/server/app/page.js +1 -1
  14. package/dist/preview/.next/server/app/page.js.nft.json +1 -1
  15. package/dist/preview/.next/server/app/page_client-reference-manifest.js +1 -1
  16. package/dist/preview/.next/server/app/preview/[...slug]/page.js +137 -75
  17. package/dist/preview/.next/server/app/preview/[...slug]/page.js.nft.json +1 -1
  18. package/dist/preview/.next/server/app/preview/[...slug]/page_client-reference-manifest.js +1 -1
  19. package/dist/preview/.next/server/app-paths-manifest.json +1 -1
  20. package/dist/preview/.next/server/chunks/267.js +14 -0
  21. package/dist/preview/.next/server/chunks/346.js +1 -0
  22. package/dist/preview/.next/server/chunks/{886.js → 775.js} +3 -3
  23. package/dist/preview/.next/server/pages/500.html +1 -1
  24. package/dist/preview/.next/server/server-reference-manifest.js +1 -1
  25. package/dist/preview/.next/server/server-reference-manifest.json +1 -1
  26. package/dist/preview/.next/static/chunks/33-ff3f70a80570ecda.js +1 -0
  27. package/dist/preview/.next/static/chunks/416-9c899340cfaa07d4.js +1 -0
  28. package/dist/preview/.next/static/chunks/516-2716d86d2f8b9000.js +1 -0
  29. package/dist/preview/.next/static/chunks/{587-352f8079202a48d0.js → 587-0644242ce9489212.js} +1 -1
  30. package/dist/preview/.next/static/chunks/app/layout-2726a60e293495d3.js +1 -0
  31. package/dist/preview/.next/static/chunks/app/{page-0ee3a37f3a3f6f17.js → page-1d98e2313c60dd77.js} +1 -1
  32. package/dist/preview/.next/static/chunks/app/preview/[...slug]/page-c77ff9f2bb1709b3.js +1 -0
  33. package/dist/preview/.next/static/chunks/f33a14d2-a04f3be0523bd1fa.js +6 -0
  34. package/dist/preview/.next/static/css/6f42d128f111d7fa.css +3 -0
  35. package/dist/preview/.next/trace +27 -27
  36. package/package.json +44 -46
  37. package/src/actions/email-validation/check-compatibility.ts +1 -1
  38. package/src/actions/email-validation/check-images.spec.tsx +1 -1
  39. package/src/actions/email-validation/check-links.spec.tsx +1 -1
  40. package/src/actions/email-validation/quick-fetch.ts +1 -1
  41. package/src/actions/render-email-by-path.tsx +6 -6
  42. package/src/app/preview/[...slug]/preview.tsx +1 -1
  43. package/src/commands/testing/out/magic-links/aws-verify-email.html +164 -0
  44. package/src/commands/testing/out/magic-links/linear-login-code.html +89 -0
  45. package/src/commands/testing/out/magic-links/notion-magic-link.html +75 -0
  46. package/src/commands/testing/out/magic-links/plaid-verify-identity.html +78 -0
  47. package/src/commands/testing/out/magic-links/raycast-magic-link.html +90 -0
  48. package/src/commands/testing/out/magic-links/slack-confirm.html +239 -0
  49. package/src/commands/testing/out/newsletters/codepen-challengers.html +547 -0
  50. package/src/commands/testing/out/newsletters/google-play-policy-update.html +338 -0
  51. package/src/commands/testing/out/newsletters/stack-overflow-tips.html +230 -0
  52. package/src/commands/testing/out/notifications/github-access-token.html +103 -0
  53. package/src/commands/testing/out/notifications/papermark-year-in-review.html +316 -0
  54. package/src/commands/testing/out/notifications/vercel-invite-user.html +170 -0
  55. package/src/commands/testing/out/notifications/yelp-recent-login.html +194 -0
  56. package/src/commands/testing/out/receipts/apple-receipt.html +676 -0
  57. package/src/commands/testing/out/receipts/nike-receipt.html +723 -0
  58. package/src/commands/testing/out/reset-password/dropbox-reset-password.html +97 -0
  59. package/src/commands/testing/out/reset-password/twitch-reset-password.html +219 -0
  60. package/src/commands/testing/out/reviews/airbnb-review.html +205 -0
  61. package/src/commands/testing/out/reviews/amazon-review.html +355 -0
  62. package/src/commands/testing/out/static/airbnb-logo.png +0 -0
  63. package/src/commands/testing/out/static/airbnb-review-user.jpg +0 -0
  64. package/src/commands/testing/out/static/amazon-book.jpg +0 -0
  65. package/src/commands/testing/out/static/amazon-facebook.jpg +0 -0
  66. package/src/commands/testing/out/static/amazon-instagram.jpg +0 -0
  67. package/src/commands/testing/out/static/amazon-logo.png +0 -0
  68. package/src/commands/testing/out/static/amazon-prime-logo.png +0 -0
  69. package/src/commands/testing/out/static/amazon-rating.gif +0 -0
  70. package/src/commands/testing/out/static/amazon-twitter.jpg +0 -0
  71. package/src/commands/testing/out/static/apple-card-icon.png +0 -0
  72. package/src/commands/testing/out/static/apple-hbo-max-icon.jpeg +0 -0
  73. package/src/commands/testing/out/static/apple-logo.png +0 -0
  74. package/src/commands/testing/out/static/apple-wallet.png +0 -0
  75. package/src/commands/testing/out/static/aws-logo.png +0 -0
  76. package/src/commands/testing/out/static/codepen-challengers.png +0 -0
  77. package/src/commands/testing/out/static/codepen-cube.png +0 -0
  78. package/src/commands/testing/out/static/codepen-pro.png +0 -0
  79. package/src/commands/testing/out/static/dropbox-logo.png +0 -0
  80. package/src/commands/testing/out/static/github.png +0 -0
  81. package/src/commands/testing/out/static/google-play-academy.png +0 -0
  82. package/src/commands/testing/out/static/google-play-chat.png +0 -0
  83. package/src/commands/testing/out/static/google-play-footer.png +0 -0
  84. package/src/commands/testing/out/static/google-play-header.png +0 -0
  85. package/src/commands/testing/out/static/google-play-icon.png +0 -0
  86. package/src/commands/testing/out/static/google-play-logo.png +0 -0
  87. package/src/commands/testing/out/static/google-play-pl.png +0 -0
  88. package/src/commands/testing/out/static/google-play.png +0 -0
  89. package/src/commands/testing/out/static/koala-logo.png +0 -0
  90. package/src/commands/testing/out/static/linear-logo.png +0 -0
  91. package/src/commands/testing/out/static/netlify-logo.png +0 -0
  92. package/src/commands/testing/out/static/nike-logo.png +0 -0
  93. package/src/commands/testing/out/static/nike-phone.png +0 -0
  94. package/src/commands/testing/out/static/nike-product.png +0 -0
  95. package/src/commands/testing/out/static/nike-recomendation-1.png +0 -0
  96. package/src/commands/testing/out/static/nike-recomendation-2.png +0 -0
  97. package/src/commands/testing/out/static/nike-recomendation-3.png +0 -0
  98. package/src/commands/testing/out/static/nike-recomendation-4.png +0 -0
  99. package/src/commands/testing/out/static/notion-logo.png +0 -0
  100. package/src/commands/testing/out/static/plaid-logo.png +0 -0
  101. package/src/commands/testing/out/static/raycast-bg.png +0 -0
  102. package/src/commands/testing/out/static/raycast-logo.png +0 -0
  103. package/src/commands/testing/out/static/slack-facebook.png +0 -0
  104. package/src/commands/testing/out/static/slack-linkedin.png +0 -0
  105. package/src/commands/testing/out/static/slack-logo.png +0 -0
  106. package/src/commands/testing/out/static/slack-twitter.png +0 -0
  107. package/src/commands/testing/out/static/stack-overflow-header.png +0 -0
  108. package/src/commands/testing/out/static/stack-overflow-logo-sm.png +0 -0
  109. package/src/commands/testing/out/static/stack-overflow-logo.png +0 -0
  110. package/src/commands/testing/out/static/stripe-logo.png +0 -0
  111. package/src/commands/testing/out/static/twitch-icon-facebook.png +0 -0
  112. package/src/commands/testing/out/static/twitch-icon-twitter.png +0 -0
  113. package/src/commands/testing/out/static/twitch-logo.png +0 -0
  114. package/src/commands/testing/out/static/vercel-arrow.png +0 -0
  115. package/src/commands/testing/out/static/vercel-logo.png +0 -0
  116. package/src/commands/testing/out/static/vercel-team.png +0 -0
  117. package/src/commands/testing/out/static/vercel-user.png +0 -0
  118. package/src/commands/testing/out/static/yelp-footer.png +0 -0
  119. package/src/commands/testing/out/static/yelp-header.png +0 -0
  120. package/src/commands/testing/out/static/yelp-logo.png +0 -0
  121. package/src/commands/testing/out/welcome/koala-welcome.html +89 -0
  122. package/src/commands/testing/out/welcome/netlify-welcome.html +198 -0
  123. package/src/commands/testing/out/welcome/stripe-welcome.html +152 -0
  124. package/src/components/toolbar.tsx +1 -0
  125. package/src/contexts/emails.tsx +1 -3
  126. package/src/contexts/fragment-identifier.tsx +3 -1
  127. package/src/contexts/preview.tsx +1 -3
  128. package/src/hooks/use-email-rendering-result.ts +18 -5
  129. package/src/hooks/use-hot-reload.ts +1 -1
  130. package/src/utils/__snapshots__/get-email-component.spec.ts.snap +1 -1
  131. package/src/utils/caniemail/ast/get-object-variables.ts +1 -1
  132. package/src/utils/caniemail/tailwind/generate-tailwind-rules.ts +1 -1
  133. package/src/utils/caniemail/tailwind/get-tailwind-config.ts +1 -1
  134. package/src/utils/caniemail/tailwind/get-tailwind-metadata.ts +1 -1
  135. package/src/utils/contains-email-template.spec.ts +107 -0
  136. package/src/utils/contains-email-template.ts +33 -0
  137. package/src/utils/get-email-component.ts +16 -1
  138. package/src/utils/get-emails-directory-metadata.ts +24 -13
  139. package/src/utils/index.ts +2 -2
  140. package/src/utils/run-bundled-code.ts +1 -1
  141. package/tailwind.config.ts +2 -1
  142. package/tsconfig.json +1 -1
  143. package/dist/cli/index.d.mts +0 -1
  144. package/dist/cli/index.d.ts +0 -1
  145. package/dist/cli/index.js +0 -1317
  146. package/dist/preview/.next/server/chunks/380.js +0 -1
  147. package/dist/preview/.next/server/chunks/840.js +0 -14
  148. package/dist/preview/.next/static/chunks/246-e7336e2929971f63.js +0 -1
  149. package/dist/preview/.next/static/chunks/539-6e9405ecdc007bb7.js +0 -1
  150. package/dist/preview/.next/static/chunks/853-a01d49f63a859f3d.js +0 -1
  151. package/dist/preview/.next/static/chunks/afa401a5-55858bf5265319eb.js +0 -6
  152. package/dist/preview/.next/static/chunks/app/layout-fa93a7ef0cc5ebdb.js +0 -1
  153. package/dist/preview/.next/static/chunks/app/preview/[...slug]/page-95449af2d870e732.js +0 -1
  154. package/dist/preview/.next/static/css/67e57230289273a9.css +0 -3
  155. /package/dist/preview/.next/static/{Oy7kpIZ6Nbnd7hpoEKBWw → 3apYH6rky7aNn7g4RIJp5}/_buildManifest.js +0 -0
  156. /package/dist/preview/.next/static/{Oy7kpIZ6Nbnd7hpoEKBWw → 3apYH6rky7aNn7g4RIJp5}/_ssgManifest.js +0 -0
@@ -0,0 +1,198 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html dir="ltr" lang="en">
3
+ <head>
4
+ <link rel="preload" as="image" href="/static/netlify-logo.png" />
5
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
6
+ <meta name="x-apple-disable-message-reformatting" />
7
+ <!--$-->
8
+ </head>
9
+ <div
10
+ style="display:none;overflow:hidden;line-height:1px;opacity:0;max-height:0;max-width:0">
11
+ Netlify Welcome
12
+ <div>
13
+  ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏
14
+ </div>
15
+ </div>
16
+ <body
17
+ style='background-color:rgb(250,251,251);font-family:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";font-size:1rem;line-height:1.5rem'>
18
+ <img
19
+ alt="Netlify"
20
+ height="75"
21
+ src="/static/netlify-logo.png"
22
+ style="margin-left:auto;margin-right:auto;margin-top:20px;margin-bottom:20px;display:block;outline:none;border:none;text-decoration:none"
23
+ width="184" />
24
+ <table
25
+ align="center"
26
+ width="100%"
27
+ border="0"
28
+ cellpadding="0"
29
+ cellspacing="0"
30
+ role="presentation"
31
+ style="background-color:rgb(255,255,255);padding:45px;max-width:37.5em">
32
+ <tbody>
33
+ <tr style="width:100%">
34
+ <td>
35
+ <h1
36
+ style="margin-top:0px;margin-bottom:0px;text-align:center;line-height:2rem">
37
+ Welcome to Netlify
38
+ </h1>
39
+ <table
40
+ align="center"
41
+ width="100%"
42
+ border="0"
43
+ cellpadding="0"
44
+ cellspacing="0"
45
+ role="presentation">
46
+ <tbody>
47
+ <tr>
48
+ <td>
49
+ <table
50
+ align="center"
51
+ width="100%"
52
+ border="0"
53
+ cellpadding="0"
54
+ cellspacing="0"
55
+ role="presentation">
56
+ <tbody style="width:100%">
57
+ <tr style="width:100%">
58
+ <p
59
+ style="font-size:1rem;line-height:1.5rem;margin-top:16px;margin-bottom:16px">
60
+ Congratulations! You&#x27;re joining over 3 million
61
+ developers around the world who use Netlify to build
62
+ and ship sites, stores, and apps.
63
+ </p>
64
+ <p
65
+ style="font-size:1rem;line-height:1.5rem;margin-top:16px;margin-bottom:16px">
66
+ Here&#x27;s how to get started:
67
+ </p>
68
+ </tr>
69
+ </tbody>
70
+ </table>
71
+ </td>
72
+ </tr>
73
+ </tbody>
74
+ </table>
75
+ <ul></ul>
76
+ <table
77
+ align="center"
78
+ width="100%"
79
+ border="0"
80
+ cellpadding="0"
81
+ cellspacing="0"
82
+ role="presentation"
83
+ style="text-align:center">
84
+ <tbody>
85
+ <tr>
86
+ <td>
87
+ <a
88
+ style="border-radius:0.5rem;background-color:rgb(34,80,244);padding-left:18px;padding-right:18px;padding-top:0.75rem;padding-bottom:0.75rem;color:rgb(255,255,255);line-height:100%;text-decoration:none;display:inline-block;max-width:100%;mso-padding-alt:0px;padding:12px 18px 12px 18px"
89
+ target="_blank"
90
+ ><span
91
+ ><!--[if mso]><i style="mso-font-width:450%;mso-text-raise:18" hidden>&#8202;&#8202;</i><![endif]--></span
92
+ ><span
93
+ style="max-width:100%;display:inline-block;line-height:120%;mso-padding-alt:0px;mso-text-raise:9px"
94
+ >Go to your dashboard</span
95
+ ><span
96
+ ><!--[if mso]><i style="mso-font-width:450%" hidden>&#8202;&#8202;&#8203;</i><![endif]--></span
97
+ ></a
98
+ >
99
+ </td>
100
+ </tr>
101
+ </tbody>
102
+ </table>
103
+ <table
104
+ align="center"
105
+ width="100%"
106
+ border="0"
107
+ cellpadding="0"
108
+ cellspacing="0"
109
+ role="presentation"
110
+ style="margin-top:45px">
111
+ <tbody>
112
+ <tr>
113
+ <td>
114
+ <table
115
+ align="center"
116
+ width="100%"
117
+ border="0"
118
+ cellpadding="0"
119
+ cellspacing="0"
120
+ role="presentation">
121
+ <tbody style="width:100%">
122
+ <tr style="width:100%"></tr>
123
+ </tbody>
124
+ </table>
125
+ </td>
126
+ </tr>
127
+ </tbody>
128
+ </table>
129
+ </td>
130
+ </tr>
131
+ </tbody>
132
+ </table>
133
+ <table
134
+ align="center"
135
+ width="100%"
136
+ border="0"
137
+ cellpadding="0"
138
+ cellspacing="0"
139
+ role="presentation"
140
+ style="margin-top:20px;max-width:37.5em">
141
+ <tbody>
142
+ <tr style="width:100%">
143
+ <td>
144
+ <table
145
+ align="center"
146
+ width="100%"
147
+ border="0"
148
+ cellpadding="0"
149
+ cellspacing="0"
150
+ role="presentation">
151
+ <tbody>
152
+ <tr>
153
+ <td>
154
+ <table
155
+ align="center"
156
+ width="100%"
157
+ border="0"
158
+ cellpadding="0"
159
+ cellspacing="0"
160
+ role="presentation">
161
+ <tbody style="width:100%">
162
+ <tr style="width:100%">
163
+ <td
164
+ data-id="__react-email-column"
165
+ style="padding-left:20px;padding-right:20px;text-align:right">
166
+ <a
167
+ style="color:#067df7;text-decoration-line:none"
168
+ target="_blank"
169
+ >Unsubscribe</a
170
+ >
171
+ </td>
172
+ <td
173
+ data-id="__react-email-column"
174
+ style="text-align:left">
175
+ <a
176
+ style="color:#067df7;text-decoration-line:none"
177
+ target="_blank"
178
+ >Manage Preferences</a
179
+ >
180
+ </td>
181
+ </tr>
182
+ </tbody>
183
+ </table>
184
+ </td>
185
+ </tr>
186
+ </tbody>
187
+ </table>
188
+ <p
189
+ style="margin-bottom:45px;text-align:center;color:rgb(156,163,175);font-size:14px;line-height:24px;margin-top:16px">
190
+ Netlify, 44 Montgomery Street, Suite 300 San Francisco, CA
191
+ </p>
192
+ </td>
193
+ </tr>
194
+ </tbody>
195
+ </table>
196
+ <!--/$-->
197
+ </body>
198
+ </html>
@@ -0,0 +1,152 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html dir="ltr" lang="en">
3
+ <head>
4
+ <link rel="preload" as="image" href="/static/stripe-logo.png" />
5
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
6
+ <meta name="x-apple-disable-message-reformatting" />
7
+ <!--$-->
8
+ </head>
9
+ <body
10
+ style='background-color:#f6f9fc;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif'>
11
+ <div
12
+ style="display:none;overflow:hidden;line-height:1px;opacity:0;max-height:0;max-width:0">
13
+ You&#x27;re now ready to make live transactions with Stripe!
14
+ <div>
15
+  ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏
16
+ </div>
17
+ </div>
18
+ <table
19
+ align="center"
20
+ width="100%"
21
+ border="0"
22
+ cellpadding="0"
23
+ cellspacing="0"
24
+ role="presentation"
25
+ style="max-width:37.5em;background-color:#ffffff;margin:0 auto;padding:20px 0 48px;margin-bottom:64px">
26
+ <tbody>
27
+ <tr style="width:100%">
28
+ <td>
29
+ <table
30
+ align="center"
31
+ width="100%"
32
+ border="0"
33
+ cellpadding="0"
34
+ cellspacing="0"
35
+ role="presentation"
36
+ style="padding:0 48px">
37
+ <tbody>
38
+ <tr>
39
+ <td>
40
+ <img
41
+ alt="Stripe"
42
+ height="21"
43
+ src="/static/stripe-logo.png"
44
+ style="display:block;outline:none;border:none;text-decoration:none"
45
+ width="49" />
46
+ <hr
47
+ style="width:100%;border:none;border-top:1px solid #eaeaea;border-color:#e6ebf1;margin:20px 0" />
48
+ <p
49
+ style="font-size:16px;line-height:24px;color:#525f7f;text-align:left;margin-top:16px;margin-bottom:16px">
50
+ Thanks for submitting your account information.
51
+ You&#x27;re now ready to make live transactions with
52
+ Stripe!
53
+ </p>
54
+ <p
55
+ style="font-size:16px;line-height:24px;color:#525f7f;text-align:left;margin-top:16px;margin-bottom:16px">
56
+ You can view your payments and a variety of other
57
+ information about your account right from your dashboard.
58
+ </p>
59
+ <a
60
+ href="https://dashboard.stripe.com/login"
61
+ style="line-height:100%;text-decoration:none;display:block;max-width:100%;mso-padding-alt:0px;background-color:#656ee8;border-radius:5px;color:#fff;font-size:16px;font-weight:bold;text-align:center;padding:10px 10px 10px 10px"
62
+ target="_blank"
63
+ ><span
64
+ ><!--[if mso]><i style="mso-font-width:500%;mso-text-raise:15" hidden>&#8202;</i><![endif]--></span
65
+ ><span
66
+ style="max-width:100%;display:inline-block;line-height:120%;mso-padding-alt:0px;mso-text-raise:7.5px"
67
+ >View your Stripe Dashboard</span
68
+ ><span
69
+ ><!--[if mso]><i style="mso-font-width:500%" hidden>&#8202;&#8203;</i><![endif]--></span
70
+ ></a
71
+ >
72
+ <hr
73
+ style="width:100%;border:none;border-top:1px solid #eaeaea;border-color:#e6ebf1;margin:20px 0" />
74
+ <p
75
+ style="font-size:16px;line-height:24px;color:#525f7f;text-align:left;margin-top:16px;margin-bottom:16px">
76
+ If you haven&#x27;t finished your integration, you might
77
+ find our<!-- -->
78
+ <a
79
+ href="https://docs.stripe.com/dashboard/basics"
80
+ style="color:#556cd6;text-decoration-line:none"
81
+ target="_blank"
82
+ >docs</a
83
+ >
84
+ <!-- -->handy.
85
+ </p>
86
+ <p
87
+ style="font-size:16px;line-height:24px;color:#525f7f;text-align:left;margin-top:16px;margin-bottom:16px">
88
+ Once you&#x27;re ready to start accepting payments,
89
+ you&#x27;ll just need to use your live<!-- -->
90
+ <a
91
+ href="https://dashboard.stripe.com/login?redirect=%2Fapikeys"
92
+ style="color:#556cd6;text-decoration-line:none"
93
+ target="_blank"
94
+ >API keys</a
95
+ >
96
+ <!-- -->instead of your test API keys. Your account can
97
+ simultaneously be used for both test and live requests, so
98
+ you can continue testing while accepting live payments.
99
+ Check out our<!-- -->
100
+ <a
101
+ href="https://docs.stripe.com/dashboard/basics"
102
+ style="color:#556cd6;text-decoration-line:none"
103
+ target="_blank"
104
+ >tutorial about account basics</a
105
+ >.
106
+ </p>
107
+ <p
108
+ style="font-size:16px;line-height:24px;color:#525f7f;text-align:left;margin-top:16px;margin-bottom:16px">
109
+ Finally, we&#x27;ve put together a<!-- -->
110
+ <a
111
+ href="https://docs.stripe.com/get-started/checklist/website"
112
+ style="color:#556cd6;text-decoration-line:none"
113
+ target="_blank"
114
+ >quick checklist</a
115
+ >
116
+ <!-- -->to ensure your website conforms to card network
117
+ standards.
118
+ </p>
119
+ <p
120
+ style="font-size:16px;line-height:24px;color:#525f7f;text-align:left;margin-top:16px;margin-bottom:16px">
121
+ We&#x27;ll be here to help you with any step along the
122
+ way. You can find answers to most questions and get in
123
+ touch with us on our<!-- -->
124
+ <a
125
+ href="https://support.stripe.com"
126
+ style="color:#556cd6;text-decoration-line:none"
127
+ target="_blank"
128
+ >support site</a
129
+ >.
130
+ </p>
131
+ <p
132
+ style="font-size:16px;line-height:24px;color:#525f7f;text-align:left;margin-top:16px;margin-bottom:16px">
133
+ — The Stripe team
134
+ </p>
135
+ <hr
136
+ style="width:100%;border:none;border-top:1px solid #eaeaea;border-color:#e6ebf1;margin:20px 0" />
137
+ <p
138
+ style="font-size:12px;line-height:16px;color:#8898aa;margin-top:16px;margin-bottom:16px">
139
+ Stripe, 354 Oyster Point Blvd, South San Francisco, CA
140
+ 94080
141
+ </p>
142
+ </td>
143
+ </tr>
144
+ </tbody>
145
+ </table>
146
+ </td>
147
+ </tr>
148
+ </tbody>
149
+ </table>
150
+ <!--/$-->
151
+ </body>
152
+ </html>
@@ -104,6 +104,7 @@ const ToolbarInner = ({
104
104
  });
105
105
 
106
106
  if (!isBuilding) {
107
+ // biome-ignore lint/correctness/useHookAtTopLevel: This is fine since isBuilding does not change at runtime
107
108
  useEffect(() => {
108
109
  (async () => {
109
110
  const lintingRows = await loadLinting();
@@ -32,9 +32,7 @@ export const EmailsProvider = (props: {
32
32
  useState<EmailsDirectory>(props.initialEmailsDirectoryMetadata);
33
33
 
34
34
  if (!isBuilding) {
35
- // this will not change on runtime so it doesn't violate
36
- // the rules of hooks
37
- // eslint-disable-next-line react-hooks/rules-of-hooks
35
+ // biome-ignore lint/correctness/useHookAtTopLevel: this will not change on runtime so it doesn't violate the rules of hooks
38
36
  useHotreload(async () => {
39
37
  const metadata = await getEmailsDirectoryMetadataAction(
40
38
  props.initialEmailsDirectoryMetadata.absolutePath,
@@ -18,7 +18,9 @@ export const useFragmentIdentifier = () => {
18
18
 
19
19
  export const FragmentIdentifierProvider = ({
20
20
  children,
21
- }: { children: React.ReactNode }) => {
21
+ }: {
22
+ children: React.ReactNode;
23
+ }) => {
22
24
  const [fragmentIdentifier, setFragmentIdentifier] = useState<string>();
23
25
  const pathname = usePathname();
24
26
  const searchParams = useSearchParams();
@@ -50,9 +50,7 @@ export const PreviewProvider = ({
50
50
  );
51
51
 
52
52
  if (!isBuilding) {
53
- // this will not change on runtime so it doesn't violate
54
- // the rules of hooks
55
- // eslint-disable-next-line react-hooks/rules-of-hooks
53
+ // biome-ignore lint/correctness/useHookAtTopLevel: this will not change on runtime so it doesn't violate the rules of hooks
56
54
  useHotreload((changes) => {
57
55
  const changeForThisEmail = changes.find((change) =>
58
56
  change.filename.includes(emailSlug),
@@ -5,6 +5,8 @@ import {
5
5
  renderEmailByPath,
6
6
  } from '../actions/render-email-by-path';
7
7
  import { isBuilding } from '../app/env';
8
+ import { useEmails } from '../contexts/emails';
9
+ import { containsEmailTemplate } from '../utils/contains-email-template';
8
10
  import { useHotreload } from './use-hot-reload';
9
11
 
10
12
  export const useEmailRenderingResult = (
@@ -15,20 +17,31 @@ export const useEmailRenderingResult = (
15
17
  serverEmailRenderedResult,
16
18
  );
17
19
 
20
+ const { emailsDirectoryMetadata } = useEmails();
21
+
18
22
  if (!isBuilding) {
19
- // eslint-disable-next-line react-hooks/rules-of-hooks
23
+ // biome-ignore lint/correctness/useHookAtTopLevel: This is fine since isBuilding does not change at runtime
20
24
  useHotreload(async (changes) => {
21
25
  for await (const change of changes) {
22
- const slugForChangedEmail =
26
+ const relativePathForChangedFile =
23
27
  // ex: apple-receipt.tsx
24
28
  // it will be the path relative to the emails directory, so it is already
25
29
  // going to be equivalent to the slug
26
30
  change.filename;
27
31
 
28
- const pathForChangedEmail =
29
- await getEmailPathFromSlug(slugForChangedEmail);
32
+ if (
33
+ !containsEmailTemplate(
34
+ relativePathForChangedFile,
35
+ emailsDirectoryMetadata,
36
+ )
37
+ ) {
38
+ continue;
39
+ }
40
+
41
+ const pathForChangedEmail = await getEmailPathFromSlug(
42
+ relativePathForChangedFile,
43
+ );
30
44
 
31
- // We always render the email template here so that we can allow
32
45
  const newRenderingResult = await renderEmailByPath(
33
46
  pathForChangedEmail,
34
47
  true,
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { useEffect, useRef } from 'react';
3
- import { type Socket, io } from 'socket.io-client';
3
+ import { io, type Socket } from 'socket.io-client';
4
4
  import type { HotReloadChange } from '../utils/types/hot-reload-change';
5
5
 
6
6
  /**
@@ -1,3 +1,3 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
- exports[`getEmailComponent() > with a demo email template 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html dir="ltr" lang="en"><head><link rel="preload" as="image" href="/static/vercel-logo.png"/><link rel="preload" as="image" href="/static/vercel-user.png"/><link rel="preload" as="image" href="/static/vercel-arrow.png"/><link rel="preload" as="image" href="/static/vercel-team.png"/><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/><meta name="x-apple-disable-message-reformatting"/><!--$--></head><body style="background-color:rgb(255,255,255);margin-top:auto;margin-bottom:auto;margin-left:auto;margin-right:auto;font-family:ui-sans-serif, system-ui, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;, &quot;Segoe UI Symbol&quot;, &quot;Noto Color Emoji&quot;;padding-left:0.5rem;padding-right:0.5rem"><div style="display:none;overflow:hidden;line-height:1px;opacity:0;max-height:0;max-width:0">Join Alan on Vercel<div> ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏</div></div><table align="center" width="100%" border="0" cellPadding="0" cellSpacing="0" role="presentation" style="border-width:1px;border-style:solid;border-color:rgb(234,234,234);border-radius:0.25rem;margin-top:40px;margin-bottom:40px;margin-left:auto;margin-right:auto;padding:20px;max-width:465px"><tbody><tr style="width:100%"><td><table align="center" width="100%" border="0" cellPadding="0" cellSpacing="0" role="presentation" style="margin-top:32px"><tbody><tr><td><img alt="Vercel Logo" height="37" src="/static/vercel-logo.png" style="margin-top:0px;margin-bottom:0px;margin-left:auto;margin-right:auto;display:block;outline:none;border:none;text-decoration:none" width="40"/></td></tr></tbody></table><h1 style="color:rgb(0,0,0);font-size:24px;font-weight:400;text-align:center;padding:0px;margin-top:30px;margin-bottom:30px;margin-left:0px;margin-right:0px">Join <strong>Enigma</strong> on <strong>Vercel</strong></h1><p style="color:rgb(0,0,0);font-size:14px;line-height:24px;margin-bottom:16px;margin-top:16px">Hello <!-- -->alanturing<!-- -->,</p><p style="color:rgb(0,0,0);font-size:14px;line-height:24px;margin-bottom:16px;margin-top:16px"><strong>Alan</strong> (<a href="mailto:alan.turing@example.com" style="color:rgb(37,99,235);text-decoration-line:none" target="_blank">alan.turing@example.com</a>) has invited you to the <strong>Enigma</strong> team on<!-- --> <strong>Vercel</strong>.</p><table align="center" width="100%" border="0" cellPadding="0" cellSpacing="0" role="presentation"><tbody><tr><td><table align="center" width="100%" border="0" cellPadding="0" cellSpacing="0" role="presentation"><tbody style="width:100%"><tr style="width:100%"><td align="right" data-id="__react-email-column"><img alt="alanturing&#x27;s profile picture" height="64" src="/static/vercel-user.png" style="border-radius:9999px;display:block;outline:none;border:none;text-decoration:none" width="64"/></td><td align="center" data-id="__react-email-column"><img alt="Arrow indicating invitation" height="9" src="/static/vercel-arrow.png" style="display:block;outline:none;border:none;text-decoration:none" width="12"/></td><td align="left" data-id="__react-email-column"><img alt="Enigma team logo" height="64" src="/static/vercel-team.png" style="border-radius:9999px;display:block;outline:none;border:none;text-decoration:none" width="64"/></td></tr></tbody></table></td></tr></tbody></table><table align="center" width="100%" border="0" cellPadding="0" cellSpacing="0" role="presentation" style="text-align:center;margin-top:32px;margin-bottom:32px"><tbody><tr><td><a href="https://vercel.com" style="background-color:rgb(0,0,0);border-radius:0.25rem;color:rgb(255,255,255);font-size:12px;font-weight:600;text-decoration-line:none;text-align:center;padding-left:1.25rem;padding-right:1.25rem;padding-top:0.75rem;padding-bottom:0.75rem;line-height:100%;text-decoration:none;display:inline-block;max-width:100%;mso-padding-alt:0px;padding:12px 20px 12px 20px" target="_blank"><span><!--[if mso]><i style="mso-font-width:500%;mso-text-raise:18" hidden>&#8202;&#8202;</i><![endif]--></span><span style="max-width:100%;display:inline-block;line-height:120%;mso-padding-alt:0px;mso-text-raise:9px">Join the team</span><span><!--[if mso]><i style="mso-font-width:500%" hidden>&#8202;&#8202;&#8203;</i><![endif]--></span></a></td></tr></tbody></table><p style="color:rgb(0,0,0);font-size:14px;line-height:24px;margin-bottom:16px;margin-top:16px">or copy and paste this URL into your browser:<!-- --> <a href="https://vercel.com" style="color:rgb(37,99,235);text-decoration-line:none" target="_blank">https://vercel.com</a></p><hr style="border-width:1px;border-style:solid;border-color:rgb(234,234,234);margin-top:26px;margin-bottom:26px;margin-left:0px;margin-right:0px;width:100%;border:none;border-top:1px solid #eaeaea"/><p style="color:rgb(102,102,102);font-size:12px;line-height:24px;margin-bottom:16px;margin-top:16px">This invitation was intended for<!-- --> <span style="color:rgb(0,0,0)">alanturing</span>. This invite was sent from <span style="color:rgb(0,0,0)">204.13.186.218</span> <!-- -->located in<!-- --> <span style="color:rgb(0,0,0)">São Paulo, Brazil</span>. If you were not expecting this invitation, you can ignore this email. If you are concerned about your account&#x27;s safety, please reply to this email to get in touch with us.</p></td></tr></tbody></table><!--/$--></body></html>"`;
3
+ exports[`getEmailComponent() > with a demo email template 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html dir="ltr" lang="en"><head><link rel="preload" as="image" href="/static/vercel-logo.png"/><link rel="preload" as="image" href="/static/vercel-user.png"/><link rel="preload" as="image" href="/static/vercel-arrow.png"/><link rel="preload" as="image" href="/static/vercel-team.png"/><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/><meta name="x-apple-disable-message-reformatting"/><!--$--></head><body style="margin-left:auto;margin-right:auto;margin-top:auto;margin-bottom:auto;background-color:rgb(255,255,255);padding-left:0.5rem;padding-right:0.5rem;font-family:ui-sans-serif, system-ui, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;, &quot;Segoe UI Symbol&quot;, &quot;Noto Color Emoji&quot;"><div style="display:none;overflow:hidden;line-height:1px;opacity:0;max-height:0;max-width:0">Join Alan on Vercel<div> ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏</div></div><table align="center" width="100%" border="0" cellPadding="0" cellSpacing="0" role="presentation" style="margin-left:auto;margin-right:auto;margin-top:40px;margin-bottom:40px;max-width:465px;border-radius:0.25rem;border-width:1px;border-color:rgb(234,234,234);border-style:solid;padding:20px"><tbody><tr style="width:100%"><td><table align="center" width="100%" border="0" cellPadding="0" cellSpacing="0" role="presentation" style="margin-top:32px"><tbody><tr><td><img alt="Vercel Logo" height="37" src="/static/vercel-logo.png" style="margin-left:auto;margin-right:auto;margin-top:0px;margin-bottom:0px;display:block;outline:none;border:none;text-decoration:none" width="40"/></td></tr></tbody></table><h1 style="margin-left:0px;margin-right:0px;margin-top:30px;margin-bottom:30px;padding:0px;text-align:center;font-weight:400;font-size:24px;color:rgb(0,0,0)">Join <strong>Enigma</strong> on <strong>Vercel</strong></h1><p style="font-size:14px;color:rgb(0,0,0);line-height:24px;margin-top:16px;margin-bottom:16px">Hello <!-- -->alanturing<!-- -->,</p><p style="font-size:14px;color:rgb(0,0,0);line-height:24px;margin-top:16px;margin-bottom:16px"><strong>Alan</strong> (<a href="mailto:alan.turing@example.com" style="color:rgb(37,99,235);text-decoration-line:none" target="_blank">alan.turing@example.com</a>) has invited you to the <strong>Enigma</strong> team on<!-- --> <strong>Vercel</strong>.</p><table align="center" width="100%" border="0" cellPadding="0" cellSpacing="0" role="presentation"><tbody><tr><td><table align="center" width="100%" border="0" cellPadding="0" cellSpacing="0" role="presentation"><tbody style="width:100%"><tr style="width:100%"><td align="right" data-id="__react-email-column"><img alt="alanturing&#x27;s profile picture" height="64" src="/static/vercel-user.png" style="border-radius:9999px;display:block;outline:none;border:none;text-decoration:none" width="64"/></td><td align="center" data-id="__react-email-column"><img alt="Arrow indicating invitation" height="9" src="/static/vercel-arrow.png" style="display:block;outline:none;border:none;text-decoration:none" width="12"/></td><td align="left" data-id="__react-email-column"><img alt="Enigma team logo" height="64" src="/static/vercel-team.png" style="border-radius:9999px;display:block;outline:none;border:none;text-decoration:none" width="64"/></td></tr></tbody></table></td></tr></tbody></table><table align="center" width="100%" border="0" cellPadding="0" cellSpacing="0" role="presentation" style="margin-top:32px;margin-bottom:32px;text-align:center"><tbody><tr><td><a href="https://vercel.com" style="border-radius:0.25rem;background-color:rgb(0,0,0);padding-left:1.25rem;padding-right:1.25rem;padding-top:0.75rem;padding-bottom:0.75rem;text-align:center;font-weight:600;font-size:12px;color:rgb(255,255,255);text-decoration-line:none;line-height:100%;text-decoration:none;display:inline-block;max-width:100%;mso-padding-alt:0px;padding:12px 20px 12px 20px" target="_blank"><span><!--[if mso]><i style="mso-font-width:500%;mso-text-raise:18" hidden>&#8202;&#8202;</i><![endif]--></span><span style="max-width:100%;display:inline-block;line-height:120%;mso-padding-alt:0px;mso-text-raise:9px">Join the team</span><span><!--[if mso]><i style="mso-font-width:500%" hidden>&#8202;&#8202;&#8203;</i><![endif]--></span></a></td></tr></tbody></table><p style="font-size:14px;color:rgb(0,0,0);line-height:24px;margin-top:16px;margin-bottom:16px">or copy and paste this URL into your browser:<!-- --> <a href="https://vercel.com" style="color:rgb(37,99,235);text-decoration-line:none" target="_blank">https://vercel.com</a></p><hr style="margin-left:0px;margin-right:0px;margin-top:26px;margin-bottom:26px;width:100%;border-width:1px;border-color:rgb(234,234,234);border-style:solid;border:none;border-top:1px solid #eaeaea"/><p style="color:rgb(102,102,102);font-size:12px;line-height:24px;margin-top:16px;margin-bottom:16px">This invitation was intended for<!-- --> <span style="color:rgb(0,0,0)">alanturing</span>. This invite was sent from <span style="color:rgb(0,0,0)">204.13.186.218</span> <!-- -->located in<!-- --> <span style="color:rgb(0,0,0)">São Paulo, Brazil</span>. If you were not expecting this invitation, you can ignore this email. If you are concerned about your account&#x27;s safety, please reply to this email to get in touch with us.</p></td></tr></tbody></table><!--/$--></body></html>"`;
@@ -1,5 +1,5 @@
1
- import type { Node } from '@babel/traverse';
2
1
  import traverse from '@babel/traverse';
2
+ import type { Node } from '@babel/traverse';
3
3
  import type { AST } from '../../../actions/email-validation/check-compatibility';
4
4
 
5
5
  export interface Position {
@@ -1,5 +1,5 @@
1
- import type { Root, Rule } from 'postcss';
2
1
  import postcss from 'postcss';
2
+ import type { Root, Rule } from 'postcss';
3
3
  import evaluateTailwindFunctions from 'tailwindcss/lib/lib/evaluateTailwindFunctions';
4
4
  import { generateRules as rawGenerateRules } from 'tailwindcss/lib/lib/generateRules';
5
5
  import type { JitContext } from 'tailwindcss/lib/lib/setupContextUtils';
@@ -1,7 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import type { Node } from '@babel/traverse';
4
3
  import traverse from '@babel/traverse';
4
+ import type { Node } from '@babel/traverse';
5
5
  import * as esbuild from 'esbuild';
6
6
  import type { Config as TailwindOriginalConfig } from 'tailwindcss';
7
7
  import type { AST } from '../../../actions/email-validation/check-compatibility';
@@ -1,7 +1,7 @@
1
1
  import traverse from '@babel/traverse';
2
2
  import type { JitContext } from 'tailwindcss/lib/lib/setupContextUtils';
3
3
  import type { AST } from '../../../actions/email-validation/check-compatibility';
4
- import { type TailwindConfig, getTailwindConfig } from './get-tailwind-config';
4
+ import { getTailwindConfig, type TailwindConfig } from './get-tailwind-config';
5
5
  import { setupTailwindContext } from './setup-tailwind-context';
6
6
 
7
7
  export const getTailwindMetadata = async (
@@ -0,0 +1,107 @@
1
+ import path from 'node:path';
2
+ import {
3
+ containsEmailTemplate,
4
+ removeFilenameExtension,
5
+ } from './contains-email-template';
6
+
7
+ describe('removeFilenameExtension()', async () => {
8
+ it('should work with a single .', () => {
9
+ expect(removeFilenameExtension('email-template.tsx')).toBe(
10
+ 'email-template',
11
+ );
12
+ });
13
+
14
+ it('should work with an example test file', () => {
15
+ expect(removeFilenameExtension('email-template.spec.tsx')).toBe(
16
+ 'email-template.spec',
17
+ );
18
+ });
19
+
20
+ it('should do nothing when there is no extension', () => {
21
+ expect(removeFilenameExtension('email-template')).toBe('email-template');
22
+ });
23
+ });
24
+
25
+ test('containsEmailTemplate()', async () => {
26
+ const emailsDirectoryPath = path.resolve(
27
+ __dirname,
28
+ '../../../../apps/demo/emails',
29
+ );
30
+ const directory = {
31
+ absolutePath: emailsDirectoryPath,
32
+ directoryName: 'emails',
33
+ relativePath: '',
34
+ emailFilenames: [],
35
+ subDirectories: [
36
+ {
37
+ absolutePath: `${emailsDirectoryPath}/magic-links`,
38
+ directoryName: 'magic-links',
39
+ relativePath: 'magic-links',
40
+ emailFilenames: [
41
+ 'aws-verify-email',
42
+ 'linear-login-code',
43
+ 'notion-magic-link',
44
+ 'plaid-verify-identity',
45
+ 'raycast-magic-link',
46
+ 'slack-confirm',
47
+ ],
48
+ subDirectories: [],
49
+ },
50
+ {
51
+ absolutePath: `${emailsDirectoryPath}/newsletters`,
52
+ directoryName: 'newsletters',
53
+ relativePath: 'newsletters',
54
+ emailFilenames: [
55
+ 'codepen-challengers',
56
+ 'google-play-policy-update',
57
+ 'stack-overflow-tips',
58
+ ],
59
+ subDirectories: [],
60
+ },
61
+ {
62
+ absolutePath: `${emailsDirectoryPath}/notifications`,
63
+ directoryName: 'notifications',
64
+ relativePath: 'notifications',
65
+ emailFilenames: [
66
+ 'github-access-token',
67
+ 'papermark-year-in-review',
68
+ 'vercel-invite-user',
69
+ 'yelp-recent-login',
70
+ ],
71
+ subDirectories: [],
72
+ },
73
+ {
74
+ absolutePath: `${emailsDirectoryPath}/receipts`,
75
+ directoryName: 'receipts',
76
+ relativePath: 'receipts',
77
+ emailFilenames: ['apple-receipt', 'nike-receipt'],
78
+ subDirectories: [],
79
+ },
80
+ {
81
+ absolutePath: `${emailsDirectoryPath}/reset-password`,
82
+ directoryName: 'reset-password',
83
+ relativePath: 'reset-password',
84
+ emailFilenames: ['dropbox-reset-password', 'twitch-reset-password'],
85
+ subDirectories: [],
86
+ },
87
+ {
88
+ absolutePath: `${emailsDirectoryPath}/reviews`,
89
+ directoryName: 'reviews',
90
+ relativePath: 'reviews',
91
+ emailFilenames: ['airbnb-review', 'amazon-review'],
92
+ subDirectories: [],
93
+ },
94
+ {
95
+ absolutePath: `${emailsDirectoryPath}/welcome`,
96
+ directoryName: 'welcome',
97
+ relativePath: 'welcome',
98
+ emailFilenames: ['koala-welcome', 'netlify-welcome', 'stripe-welcome'],
99
+ subDirectories: [],
100
+ },
101
+ ],
102
+ };
103
+ expect(containsEmailTemplate('welcome/koala-welcome', directory)).toBe(true);
104
+ expect(containsEmailTemplate('welcome/missing-template', directory)).toBe(
105
+ false,
106
+ );
107
+ });