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,33 @@
1
+ import type { EmailsDirectory } from './get-emails-directory-metadata';
2
+
3
+ export const removeFilenameExtension = (filename: string): string => {
4
+ const parts = filename.split('.');
5
+
6
+ if (parts.length > 1) {
7
+ return parts.slice(0, -1).join('.');
8
+ }
9
+
10
+ return filename;
11
+ };
12
+
13
+ export const containsEmailTemplate = (
14
+ relativeEmailPath: string,
15
+ directory: EmailsDirectory,
16
+ ) => {
17
+ const remainingSegments = relativeEmailPath
18
+ .replace(directory.relativePath, '')
19
+ .split('/')
20
+ .filter(Boolean);
21
+ if (remainingSegments.length === 1) {
22
+ const emailFilename = removeFilenameExtension(remainingSegments[0]!);
23
+ return directory.emailFilenames.includes(emailFilename);
24
+ }
25
+ const subDirectory = directory.subDirectories.find(
26
+ (sub) => sub.relativePath === remainingSegments[0],
27
+ );
28
+ if (subDirectory === undefined) {
29
+ return false;
30
+ }
31
+
32
+ return containsEmailTemplate(relativeEmailPath, subDirectory);
33
+ };
@@ -1,6 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import type { render } from '@react-email/components';
3
- import { type BuildFailure, type OutputFile, build } from 'esbuild';
3
+ import { type BuildFailure, build, type OutputFile } from 'esbuild';
4
4
  import type React from 'react';
5
5
  import type { RawSourceMap } from 'source-map-js';
6
6
  import { z } from 'zod';
@@ -106,6 +106,21 @@ export const getEmailComponent = async (
106
106
  };
107
107
  }
108
108
 
109
+ if (typeof parseResult.data.default !== 'function') {
110
+ return {
111
+ error: improveErrorWithSourceMap(
112
+ new Error(
113
+ `The email component at ${emailPath} does not contain a default exported function`,
114
+ {
115
+ cause: parseResult.error,
116
+ },
117
+ ),
118
+ emailPath,
119
+ sourceMapToEmail,
120
+ ),
121
+ };
122
+ }
123
+
109
124
  const { data: componentModule } = parseResult;
110
125
 
111
126
  return {
@@ -2,25 +2,33 @@
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
 
5
- const isFileAnEmail = (fullPath: string): boolean => {
6
- const stat = fs.statSync(fullPath);
5
+ const isFileAnEmail = async (fullPath: string): Promise<boolean> => {
6
+ let fileHandle: fs.promises.FileHandle;
7
+ try {
8
+ fileHandle = await fs.promises.open(fullPath, 'r');
9
+ } catch (exception) {
10
+ console.warn(exception);
11
+ return false;
12
+ }
13
+ const stat = await fileHandle.stat();
7
14
 
8
- if (stat.isDirectory()) return false;
15
+ if (stat.isDirectory()) {
16
+ await fileHandle.close();
17
+ return false;
18
+ }
9
19
 
10
20
  const { ext } = path.parse(fullPath);
11
21
 
12
- if (!['.js', '.tsx', '.jsx'].includes(ext)) return false;
13
-
14
- // This is to avoid a possible race condition where the file doesn't exist anymore
15
- // once we are checking if it is an actual email, this couuld cause issues that
16
- // would be very hard to debug and find out the why of it happening.
17
- if (!fs.existsSync(fullPath)) {
22
+ if (!['.js', '.tsx', '.jsx'].includes(ext)) {
23
+ await fileHandle.close();
18
24
  return false;
19
25
  }
20
26
 
21
27
  // check with a heuristic to see if the file has at least
22
28
  // a default export (ES6) or module.exports (CommonJS) or named exports (MDX)
23
- const fileContents = fs.readFileSync(fullPath, 'utf8');
29
+ const fileContents = await fileHandle.readFile('utf8');
30
+
31
+ await fileHandle.close();
24
32
 
25
33
  // Check for ES6 export default syntax
26
34
  const hasES6DefaultExport = /\bexport\s+default\b/gm.test(fileContents);
@@ -80,10 +88,13 @@ export const getEmailsDirectoryMetadata = async (
80
88
  withFileTypes: true,
81
89
  });
82
90
 
83
- const emailFilenames = dirents
84
- .filter((dirent) =>
91
+ const isEmailPredicates = await Promise.all(
92
+ dirents.map((dirent) =>
85
93
  isFileAnEmail(path.join(absolutePathToEmailsDirectory, dirent.name)),
86
- )
94
+ ),
95
+ );
96
+ const emailFilenames = dirents
97
+ .filter((_, i) => isEmailPredicates[i])
87
98
  .map((dirent) =>
88
99
  keepFileExtensions
89
100
  ? dirent.name
@@ -1,6 +1,6 @@
1
- export * from './types/as';
2
1
  export * from './cn';
3
2
  export * from './copy-text-to-clipboard';
4
3
  export * from './language-map';
5
- export * from './unreachable';
6
4
  export * from './sanitize';
5
+ export * from './types/as';
6
+ export * from './unreachable';
@@ -1,6 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import vm from 'node:vm';
3
- import { type Result, err, ok } from './result';
3
+ import { err, ok, type Result } from './result';
4
4
  import { staticNodeModulesForVM } from './static-node-modules-for-vm';
5
5
 
6
6
  export const runBundledCode = (
@@ -1,5 +1,6 @@
1
+ import * as colors from '@radix-ui/colors';
1
2
  import type { Config } from 'tailwindcss';
2
- import colors = require('@radix-ui/colors');
3
+
3
4
  import { fontFamily } from 'tailwindcss/defaultTheme';
4
5
  import plugin from 'tailwindcss/plugin';
5
6
 
package/tsconfig.json CHANGED
@@ -28,7 +28,7 @@
28
28
  "noEmit": true,
29
29
  "strict": false,
30
30
  "target": "ESNext",
31
- "module": "CommonJS",
31
+ "module": "ESNext",
32
32
  "noUncheckedIndexedAccess": true,
33
33
  "resolveJsonModule": true,
34
34
  "types": ["vitest/globals"],
@@ -1 +0,0 @@
1
- #!/usr/bin/env node
@@ -1 +0,0 @@
1
- #!/usr/bin/env node