react-email 4.0.8 → 4.0.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 (78) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/cli/index.mjs +210 -181
  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/348.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/pages-manifest.json +1 -1
  25. package/dist/preview/.next/server/server-reference-manifest.js +1 -1
  26. package/dist/preview/.next/server/server-reference-manifest.json +1 -1
  27. package/dist/preview/.next/static/chunks/33-ff3f70a80570ecda.js +1 -0
  28. package/dist/preview/.next/static/chunks/416-9c899340cfaa07d4.js +1 -0
  29. package/dist/preview/.next/static/chunks/516-2716d86d2f8b9000.js +1 -0
  30. package/dist/preview/.next/static/chunks/587-311bc718c0ec09c4.js +1 -0
  31. package/dist/preview/.next/static/chunks/app/layout-298855388b1fb3f8.js +1 -0
  32. package/dist/preview/.next/static/chunks/app/{page-0ee3a37f3a3f6f17.js → page-1d98e2313c60dd77.js} +1 -1
  33. package/dist/preview/.next/static/chunks/app/preview/[...slug]/page-aa098d9c0dacb2db.js +1 -0
  34. package/dist/preview/.next/static/chunks/f33a14d2-a04f3be0523bd1fa.js +6 -0
  35. package/dist/preview/.next/static/css/6f42d128f111d7fa.css +3 -0
  36. package/dist/preview/.next/trace +27 -27
  37. package/package.json +44 -46
  38. package/src/actions/email-validation/check-compatibility.ts +1 -1
  39. package/src/actions/email-validation/check-images.spec.tsx +1 -1
  40. package/src/actions/email-validation/check-links.spec.tsx +1 -1
  41. package/src/actions/email-validation/quick-fetch.ts +1 -1
  42. package/src/actions/render-email-by-path.tsx +6 -6
  43. package/src/app/preview/[...slug]/preview.tsx +1 -1
  44. package/src/components/toolbar.tsx +1 -0
  45. package/src/contexts/emails.tsx +1 -3
  46. package/src/contexts/fragment-identifier.tsx +3 -1
  47. package/src/contexts/preview.tsx +1 -3
  48. package/src/hooks/use-email-rendering-result.ts +18 -5
  49. package/src/hooks/use-hot-reload.ts +1 -1
  50. package/src/utils/__snapshots__/get-email-component.spec.ts.snap +1 -1
  51. package/src/utils/caniemail/ast/get-object-variables.ts +1 -1
  52. package/src/utils/caniemail/tailwind/generate-tailwind-rules.ts +1 -1
  53. package/src/utils/caniemail/tailwind/get-tailwind-config.ts +1 -1
  54. package/src/utils/caniemail/tailwind/get-tailwind-metadata.ts +1 -1
  55. package/src/utils/contains-email-template.spec.ts +107 -0
  56. package/src/utils/contains-email-template.ts +33 -0
  57. package/src/utils/get-email-component.ts +16 -1
  58. package/src/utils/get-emails-directory-metadata.ts +24 -13
  59. package/src/utils/index.ts +2 -2
  60. package/src/utils/run-bundled-code.ts +1 -1
  61. package/tailwind.config.ts +2 -1
  62. package/tsconfig.json +1 -1
  63. package/dist/cli/index.d.mts +0 -1
  64. package/dist/cli/index.d.ts +0 -1
  65. package/dist/cli/index.js +0 -1320
  66. package/dist/index.js +0 -1234
  67. package/dist/preview/.next/server/chunks/265.js +0 -1
  68. package/dist/preview/.next/server/chunks/840.js +0 -14
  69. package/dist/preview/.next/static/chunks/246-e7336e2929971f63.js +0 -1
  70. package/dist/preview/.next/static/chunks/539-6e9405ecdc007bb7.js +0 -1
  71. package/dist/preview/.next/static/chunks/587-2b8de61789f0fd1b.js +0 -1
  72. package/dist/preview/.next/static/chunks/853-a01d49f63a859f3d.js +0 -1
  73. package/dist/preview/.next/static/chunks/afa401a5-55858bf5265319eb.js +0 -6
  74. package/dist/preview/.next/static/chunks/app/layout-a3d4e7b4de277118.js +0 -1
  75. package/dist/preview/.next/static/chunks/app/preview/[...slug]/page-cab7e5e69e3b91e2.js +0 -1
  76. package/dist/preview/.next/static/css/67e57230289273a9.css +0 -3
  77. /package/dist/preview/.next/static/{t22IN7aANTezJAJOfFnv- → JVasH_4pOTw7OpjH4h-g_}/_buildManifest.js +0 -0
  78. /package/dist/preview/.next/static/{t22IN7aANTezJAJOfFnv- → JVasH_4pOTw7OpjH4h-g_}/_ssgManifest.js +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # react-email
2
2
 
3
+ ## 4.0.10
4
+
5
+ ### Patch Changes
6
+
7
+ - 5ef9fe8: fix support for `import ... = require(...)` syntax
8
+ - 4c7f597: fix `email dev` not working with `traversal` error
9
+
10
+ ## 4.0.9
11
+
12
+ ### Patch Changes
13
+
14
+ - 643d841: Add .json import support for hot reloading
15
+ - f21a983: fix Node 18 support
16
+ - cd02449: Ensure dependencies outside emails directory are completely resolved
17
+ - 73a31ed: Fix dependent of dependents not causing hot reloads
18
+ - bdffd8c: fix backwards compatibility with `render` versions
19
+ - e7fa043: Fix access to files outside `static` directory
20
+ - 9aa033c: Use range of versions for dependencies
21
+ - ab70556: Fix non-email files being rendered during hot reloading
22
+ - 9c9aa5d: Add error message for when an email template does not have a default export
23
+
3
24
  ## 4.0.8
4
25
 
5
26
  ### Patch Changes
@@ -2,8 +2,7 @@
2
2
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
3
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
4
  }) : x)(function(x) {
5
- if (typeof require !== "undefined")
6
- return require.apply(this, arguments);
5
+ if (typeof require !== "undefined") return require.apply(this, arguments);
7
6
  throw Error('Dynamic require of "' + x + '" is not supported');
8
7
  });
9
8
 
@@ -13,10 +12,10 @@ import { program } from "commander";
13
12
  // package.json
14
13
  var package_default = {
15
14
  name: "react-email",
16
- version: "4.0.8",
15
+ version: "4.0.10",
17
16
  description: "A live preview of your emails right in your browser.",
18
17
  bin: {
19
- email: "./dist/cli/index.js"
18
+ email: "./dist/cli/index.mjs"
20
19
  },
21
20
  scripts: {
22
21
  build: "tsup-node && node ./scripts/build-preview-server.mjs && pnpm install --frozen-lockfile",
@@ -33,75 +32,76 @@ var package_default = {
33
32
  url: "https://github.com/resend/react-email.git",
34
33
  directory: "packages/react-email"
35
34
  },
36
- keywords: ["react", "email"],
35
+ keywords: [
36
+ "react",
37
+ "email"
38
+ ],
37
39
  engines: {
38
40
  node: ">=18.0.0"
39
41
  },
40
42
  dependencies: {
41
- "@babel/parser": "7.24.5",
42
- "@babel/traverse": "7.25.6",
43
- chalk: "4.1.2",
44
- chokidar: "4.0.3",
45
- commander: "11.1.0",
46
- debounce: "2.0.0",
47
- esbuild: "0.25.0",
48
- glob: "10.3.4",
49
- "log-symbols": "4.1.0",
50
- "mime-types": "2.1.35",
51
- next: "15.2.4",
52
- "normalize-path": "3.0.0",
53
- ora: "5.4.1",
54
- "socket.io": "4.8.1"
43
+ "@babel/parser": "^7.27.0",
44
+ "@babel/traverse": "^7.27.0",
45
+ chalk: "^5.0.0",
46
+ chokidar: "^4.0.3",
47
+ commander: "^13.0.0",
48
+ debounce: "^2.0.0",
49
+ esbuild: "^0.25.0",
50
+ glob: "^11.0.0",
51
+ "log-symbols": "^7.0.0",
52
+ "mime-types": "^3.0.0",
53
+ next: "^15.2.4",
54
+ "normalize-path": "^3.0.0",
55
+ ora: "^8.0.0",
56
+ "socket.io": "^4.8.1"
55
57
  },
56
58
  devDependencies: {
57
59
  "@babel/core": "7.26.10",
58
- "@lottiefiles/dotlottie-react": "0.12.3",
59
- "@radix-ui/colors": "1.0.1",
60
- "@radix-ui/react-collapsible": "1.1.0",
61
- "@radix-ui/react-dropdown-menu": "2.1.4",
62
- "@radix-ui/react-popover": "1.1.1",
63
- "@radix-ui/react-slot": "1.1.0",
64
- "@radix-ui/react-tabs": "1.1.1",
65
- "@radix-ui/react-toggle-group": "1.1.0",
66
- "@radix-ui/react-tooltip": "1.1.2",
60
+ "@lottiefiles/dotlottie-react": "0.13.3",
61
+ "@radix-ui/colors": "3.0.0",
62
+ "@radix-ui/react-collapsible": "1.1.7",
63
+ "@radix-ui/react-dropdown-menu": "2.1.10",
64
+ "@radix-ui/react-popover": "1.1.10",
65
+ "@radix-ui/react-slot": "1.2.0",
66
+ "@radix-ui/react-tabs": "1.1.7",
67
+ "@radix-ui/react-toggle-group": "1.1.6",
68
+ "@radix-ui/react-tooltip": "1.2.3",
67
69
  "@react-email/components": "workspace:*",
68
- "@swc/core": "1.4.15",
70
+ "@swc/core": "1.11.21",
69
71
  "@types/babel__core": "7.20.5",
70
- "@types/babel__traverse": "*",
72
+ "@types/babel__traverse": "7.20.7",
71
73
  "@types/fs-extra": "11.0.1",
72
74
  "@types/mime-types": "2.1.4",
73
- "@types/node": "22.10.2",
75
+ "@types/node": "22.14.1",
74
76
  "@types/normalize-path": "3.0.2",
75
77
  "@types/react": "19.0.10",
76
78
  "@types/react-dom": "19.0.4",
77
79
  "@types/webpack": "5.28.5",
78
- "@vercel/style-guide": "5.1.0",
79
- autoprefixer: "10.4.20",
80
+ autoprefixer: "10.4.21",
80
81
  clsx: "2.1.1",
81
- "framer-motion": "12.0.0-alpha.2",
82
+ "framer-motion": "12.7.5",
82
83
  jiti: "2.4.2",
83
84
  json5: "2.2.3",
84
85
  "module-punycode": "npm:punycode@2.3.1",
85
- "node-html-parser": "6.1.13",
86
- postcss: "8.4.40",
87
- "prettier-plugin-tailwindcss": "0.6.6",
86
+ "node-html-parser": "7.0.1",
87
+ postcss: "8.5.3",
88
88
  "pretty-bytes": "6.1.1",
89
- "prism-react-renderer": "2.1.0",
89
+ "prism-react-renderer": "2.4.1",
90
90
  react: "19.0.0",
91
91
  "react-dom": "19.0.0",
92
- sharp: "0.33.3",
93
- "socket.io-client": "4.8.0",
94
- sonner: "1.7.1",
95
- "source-map-js": "1.0.2",
92
+ sharp: "0.34.1",
93
+ "socket.io-client": "4.8.1",
94
+ sonner: "2.0.3",
95
+ "source-map-js": "1.2.1",
96
96
  spamc: "0.0.5",
97
- "stacktrace-parser": "0.1.10",
98
- "tailwind-merge": "2.2.0",
97
+ "stacktrace-parser": "0.1.11",
98
+ "tailwind-merge": "3.2.0",
99
99
  tailwindcss: "3.4.0",
100
- tsup: "7.2.0",
101
- tsx: "4.9.0",
102
- typescript: "5.8.2",
100
+ tsup: "8.4.0",
101
+ tsx: "4.19.3",
102
+ typescript: "5.8.3",
103
103
  "use-debounce": "10.0.4",
104
- zod: "3.24.2"
104
+ zod: "3.24.3"
105
105
  }
106
106
  };
107
107
 
@@ -115,17 +115,26 @@ import ora2 from "ora";
115
115
  // src/utils/get-emails-directory-metadata.ts
116
116
  import fs from "node:fs";
117
117
  import path from "node:path";
118
- var isFileAnEmail = (fullPath) => {
119
- const stat = fs.statSync(fullPath);
120
- if (stat.isDirectory())
118
+ var isFileAnEmail = async (fullPath) => {
119
+ let fileHandle;
120
+ try {
121
+ fileHandle = await fs.promises.open(fullPath, "r");
122
+ } catch (exception) {
123
+ console.warn(exception);
121
124
  return false;
122
- const { ext } = path.parse(fullPath);
123
- if (![".js", ".tsx", ".jsx"].includes(ext))
125
+ }
126
+ const stat = await fileHandle.stat();
127
+ if (stat.isDirectory()) {
128
+ await fileHandle.close();
124
129
  return false;
125
- if (!fs.existsSync(fullPath)) {
130
+ }
131
+ const { ext } = path.parse(fullPath);
132
+ if (![".js", ".tsx", ".jsx"].includes(ext)) {
133
+ await fileHandle.close();
126
134
  return false;
127
135
  }
128
- const fileContents = fs.readFileSync(fullPath, "utf8");
136
+ const fileContents = await fileHandle.readFile("utf8");
137
+ await fileHandle.close();
129
138
  const hasES6DefaultExport = /\bexport\s+default\b/gm.test(fileContents);
130
139
  const hasCommonJSExport = /\bmodule\.exports\s*=/gm.test(fileContents);
131
140
  const hasNamedExport = /\bexport\s+\{[^}]*\bdefault\b[^}]*\}/gm.test(
@@ -148,14 +157,16 @@ var mergeDirectoriesWithSubDirectories = (emailsDirectoryMetadata) => {
148
157
  return currentResultingMergedDirectory;
149
158
  };
150
159
  var getEmailsDirectoryMetadata = async (absolutePathToEmailsDirectory, keepFileExtensions = false, isSubDirectory = false, baseDirectoryPath = absolutePathToEmailsDirectory) => {
151
- if (!fs.existsSync(absolutePathToEmailsDirectory))
152
- return;
160
+ if (!fs.existsSync(absolutePathToEmailsDirectory)) return;
153
161
  const dirents = await fs.promises.readdir(absolutePathToEmailsDirectory, {
154
162
  withFileTypes: true
155
163
  });
156
- const emailFilenames = dirents.filter(
157
- (dirent) => isFileAnEmail(path.join(absolutePathToEmailsDirectory, dirent.name))
158
- ).map(
164
+ const isEmailPredicates = await Promise.all(
165
+ dirents.map(
166
+ (dirent) => isFileAnEmail(path.join(absolutePathToEmailsDirectory, dirent.name))
167
+ )
168
+ );
169
+ const emailFilenames = dirents.filter((_, i) => isEmailPredicates[i]).map(
159
170
  (dirent) => keepFileExtensions ? dirent.name : dirent.name.replace(path.extname(dirent.name), "")
160
171
  );
161
172
  const subDirectories = await Promise.all(
@@ -212,76 +223,19 @@ var registerSpinnerAutostopping = (spinner) => {
212
223
  spinners.add(spinner);
213
224
  };
214
225
 
215
- // src/cli/utils/tree.ts
216
- import { promises as fs2 } from "node:fs";
217
- import os from "node:os";
218
- import path2 from "node:path";
219
- var SYMBOLS = {
220
- BRANCH: "\u251C\u2500\u2500 ",
221
- EMPTY: "",
222
- INDENT: " ",
223
- LAST_BRANCH: "\u2514\u2500\u2500 ",
224
- VERTICAL: "\u2502 "
225
- };
226
- var getTreeLines = async (dirPath, depth, currentDepth = 0) => {
227
- const base = process.cwd();
228
- const dirFullpath = path2.resolve(base, dirPath);
229
- const dirname = path2.basename(dirFullpath);
230
- let lines = [dirname];
231
- const dirStat = await fs2.stat(dirFullpath);
232
- if (dirStat.isDirectory() && currentDepth < depth) {
233
- const childDirents = await fs2.readdir(dirFullpath, { withFileTypes: true });
234
- childDirents.sort((a, b) => {
235
- if (a.isDirectory() && b.isFile()) {
236
- return -1;
237
- }
238
- if (a.isFile() && b.isDirectory()) {
239
- return 1;
240
- }
241
- return b.name > a.name ? -1 : 1;
242
- });
243
- for (let i = 0; i < childDirents.length; i++) {
244
- const dirent = childDirents[i];
245
- const isLast = i === childDirents.length - 1;
246
- const branchingSymbol = isLast ? SYMBOLS.LAST_BRANCH : SYMBOLS.BRANCH;
247
- const verticalSymbol = isLast ? SYMBOLS.INDENT : SYMBOLS.VERTICAL;
248
- if (dirent.isFile()) {
249
- lines.push(`${branchingSymbol}${dirent.name}`);
250
- } else {
251
- const pathToDirectory = path2.join(dirFullpath, dirent.name);
252
- const treeLinesForSubDirectory = await getTreeLines(
253
- pathToDirectory,
254
- depth,
255
- currentDepth + 1
256
- );
257
- lines = lines.concat(
258
- treeLinesForSubDirectory.map(
259
- (line, index) => index === 0 ? `${branchingSymbol}${line}` : `${verticalSymbol}${line}`
260
- )
261
- );
262
- }
263
- }
264
- }
265
- return lines;
266
- };
267
- var tree = async (dirPath, depth) => {
268
- const lines = await getTreeLines(dirPath, depth);
269
- return lines.join(os.EOL);
270
- };
271
-
272
226
  // src/cli/utils/preview/hot-reloading/setup-hot-reloading.ts
273
- import path7 from "node:path";
227
+ import path6 from "node:path";
274
228
  import { watch } from "chokidar";
275
229
  import debounce from "debounce";
276
230
  import { Server as SocketServer } from "socket.io";
277
231
 
278
232
  // src/cli/utils/preview/hot-reloading/create-dependency-graph.ts
279
- import { promises as fs4, existsSync as existsSync2, statSync } from "node:fs";
280
- import path6 from "node:path";
233
+ import { existsSync as existsSync2, promises as fs3, statSync } from "node:fs";
234
+ import path5 from "node:path";
281
235
 
282
236
  // src/cli/utils/preview/start-dev-server.ts
283
237
  import http from "node:http";
284
- import path5 from "node:path";
238
+ import path4 from "node:path";
285
239
  import url from "node:url";
286
240
  import chalk from "chalk";
287
241
  import logSymbols2 from "log-symbols";
@@ -289,28 +243,33 @@ import next from "next";
289
243
  import ora from "ora";
290
244
 
291
245
  // src/cli/utils/preview/get-env-variables-for-preview-app.ts
292
- import path3 from "node:path";
246
+ import path2 from "node:path";
293
247
  var getEnvVariablesForPreviewApp = (relativePathToEmailsDirectory, cwd) => {
294
248
  return {
295
249
  EMAILS_DIR_RELATIVE_PATH: relativePathToEmailsDirectory,
296
- EMAILS_DIR_ABSOLUTE_PATH: path3.resolve(cwd, relativePathToEmailsDirectory),
250
+ EMAILS_DIR_ABSOLUTE_PATH: path2.resolve(cwd, relativePathToEmailsDirectory),
297
251
  USER_PROJECT_LOCATION: cwd,
298
252
  NEXT_PUBLIC_IS_PREVIEW_DEVELOPMENT: isDev ? "true" : "false"
299
253
  };
300
254
  };
301
255
 
302
256
  // src/cli/utils/preview/serve-static-file.ts
303
- import { promises as fs3, existsSync } from "node:fs";
304
- import path4 from "node:path";
257
+ import { existsSync, promises as fs2 } from "node:fs";
258
+ import path3 from "node:path";
305
259
  import { lookup } from "mime-types";
306
260
  var serveStaticFile = async (res, parsedUrl, staticDirRelativePath) => {
307
- const staticBaseDir = path4.join(process.cwd(), staticDirRelativePath);
308
- const pathname = parsedUrl.pathname;
309
- const ext = path4.parse(pathname).ext;
310
- const fileAbsolutePath = path4.join(staticBaseDir, pathname);
261
+ const pathname = parsedUrl.pathname.replace("/static", "./static");
262
+ const ext = path3.parse(pathname).ext;
263
+ const staticBaseDir = path3.resolve(process.cwd(), staticDirRelativePath);
264
+ const fileAbsolutePath = path3.resolve(staticBaseDir, pathname);
265
+ if (!fileAbsolutePath.startsWith(staticBaseDir)) {
266
+ res.statusCode = 403;
267
+ res.end();
268
+ return;
269
+ }
311
270
  try {
312
- const fileHandle = await fs3.open(fileAbsolutePath, "r");
313
- const fileData = await fs3.readFile(fileHandle);
271
+ const fileHandle = await fs2.open(fileAbsolutePath, "r");
272
+ const fileData = await fs2.readFile(fileHandle);
314
273
  res.setHeader("Content-type", lookup(ext) || "text/plain");
315
274
  res.end(fileData);
316
275
  fileHandle.close();
@@ -346,9 +305,11 @@ var safeAsyncServerListen = (server, port) => {
346
305
  });
347
306
  });
348
307
  };
349
- var isDev = !__filename.endsWith(path5.join("cli", "index.js"));
350
- var cliPackageLocation = isDev ? path5.resolve(__dirname, "../../../..") : path5.resolve(__dirname, "../..");
351
- var previewServerLocation = isDev ? path5.resolve(__dirname, "../../../..") : path5.resolve(__dirname, "../preview");
308
+ var filename = url.fileURLToPath(import.meta.url);
309
+ var dirname = path4.dirname(filename);
310
+ var isDev = !filename.endsWith(path4.join("cli", "index.mjs"));
311
+ var cliPackageLocation = isDev ? path4.resolve(dirname, "../../../..") : path4.resolve(dirname, "../..");
312
+ var previewServerLocation = isDev ? path4.resolve(dirname, "../../../..") : path4.resolve(dirname, "../preview");
352
313
  var startDevServer = async (emailsDirRelativePath, staticBaseDirRelativePath, port) => {
353
314
  devServer = http.createServer((req, res) => {
354
315
  if (!req.url) {
@@ -415,7 +376,7 @@ var startDevServer = async (emailsDirRelativePath, staticBaseDirRelativePath, po
415
376
  ...process.env,
416
377
  ...getEnvVariablesForPreviewApp(
417
378
  // If we don't do normalization here, stuff like https://github.com/resend/react-email/issues/1354 happens.
418
- path5.normalize(emailsDirRelativePath),
379
+ path4.normalize(emailsDirRelativePath),
419
380
  process.cwd()
420
381
  )
421
382
  };
@@ -475,7 +436,8 @@ process.on(
475
436
 
476
437
  // src/cli/utils/preview/hot-reloading/get-imported-modules.ts
477
438
  import { parse } from "@babel/parser";
478
- import traverse from "@babel/traverse";
439
+ import traverseModule from "@babel/traverse";
440
+ var traverse = traverseModule.default;
479
441
  var getImportedModules = (contents) => {
480
442
  const importedPaths = [];
481
443
  const parsedContents = parse(contents, {
@@ -496,6 +458,9 @@ var getImportedModules = (contents) => {
496
458
  importedPaths.push(node.source.value);
497
459
  }
498
460
  },
461
+ TSExternalModuleReference({ node }) {
462
+ importedPaths.push(node.expression.value);
463
+ },
499
464
  CallExpression({ node }) {
500
465
  if ("name" in node.callee && node.callee.name === "require") {
501
466
  if (node.arguments.length === 1) {
@@ -513,9 +478,9 @@ var getImportedModules = (contents) => {
513
478
  // src/cli/utils/preview/hot-reloading/create-dependency-graph.ts
514
479
  var readAllFilesInsideDirectory = async (directory) => {
515
480
  let allFilePaths = [];
516
- const topLevelDirents = await fs4.readdir(directory, { withFileTypes: true });
481
+ const topLevelDirents = await fs3.readdir(directory, { withFileTypes: true });
517
482
  for await (const dirent of topLevelDirents) {
518
- const pathToDirent = path6.join(directory, dirent.name);
483
+ const pathToDirent = path5.join(directory, dirent.name);
519
484
  if (dirent.isDirectory()) {
520
485
  allFilePaths = allFilePaths.concat(
521
486
  await readAllFilesInsideDirectory(pathToDirent)
@@ -527,7 +492,7 @@ var readAllFilesInsideDirectory = async (directory) => {
527
492
  return allFilePaths;
528
493
  };
529
494
  var isJavascriptModule = (filePath) => {
530
- const extensionName = path6.extname(filePath);
495
+ const extensionName = path5.extname(filePath);
531
496
  return [".js", ".ts", ".jsx", ".tsx", ".mjs", ".cjs"].includes(extensionName);
532
497
  };
533
498
  var checkFileExtensionsUntilItExists = (pathWithoutExtension) => {
@@ -565,23 +530,23 @@ var createDependencyGraph = async (directory) => {
565
530
  ])
566
531
  );
567
532
  const getDependencyPaths = async (filePath) => {
568
- const contents = await fs4.readFile(filePath, "utf8");
569
- const importedPaths = getImportedModules(contents);
533
+ const contents = await fs3.readFile(filePath, "utf8");
534
+ const importedPaths = isJavascriptModule(filePath) ? getImportedModules(contents) : [];
570
535
  const importedPathsRelativeToDirectory = importedPaths.map(
571
536
  (dependencyPath) => {
572
537
  const isModulePath = !dependencyPath.startsWith(".");
573
- if (isModulePath || path6.isAbsolute(dependencyPath)) {
538
+ if (isModulePath || path5.isAbsolute(dependencyPath)) {
574
539
  return dependencyPath;
575
540
  }
576
- let pathToDependencyFromDirectory = path6.resolve(
541
+ let pathToDependencyFromDirectory = path5.resolve(
577
542
  /*
578
543
  path.resolve resolves paths differently from what imports on javascript do.
579
544
 
580
- So if we wouldn't do this, for an email at "/path/to/email.tsx" with a dependecy path of "./other-email"
545
+ So if we wouldn't do this, for an email at "/path/to/email.tsx" with a dependency path of "./other-email"
581
546
  would end up going into /path/to/email.tsx/other-email instead of /path/to/other-email which is the
582
547
  one the import is meant to go to
583
548
  */
584
- path6.dirname(filePath),
549
+ path5.dirname(filePath),
585
550
  dependencyPath
586
551
  );
587
552
  let isDirectory = false;
@@ -603,9 +568,7 @@ var createDependencyGraph = async (directory) => {
603
568
  }
604
569
  }
605
570
  if (!isJavascriptModule(pathToDependencyFromDirectory)) {
606
- const pathWithExtension = checkFileExtensionsUntilItExists(
607
- pathToDependencyFromDirectory
608
- );
571
+ const pathWithExtension = path5.extname(pathToDependencyFromDirectory).length > 0 ? pathToDependencyFromDirectory : checkFileExtensionsUntilItExists(pathToDependencyFromDirectory);
609
572
  if (pathWithExtension) {
610
573
  pathToDependencyFromDirectory = pathWithExtension;
611
574
  } else if (isDev) {
@@ -618,10 +581,10 @@ var createDependencyGraph = async (directory) => {
618
581
  }
619
582
  );
620
583
  const moduleDependencies = importedPathsRelativeToDirectory.filter(
621
- (dependencyPath) => !dependencyPath.startsWith(".") && !path6.isAbsolute(dependencyPath)
584
+ (dependencyPath) => !dependencyPath.startsWith(".") && !path5.isAbsolute(dependencyPath)
622
585
  );
623
586
  const nonNodeModuleImportPathsRelativeToDirectory = importedPathsRelativeToDirectory.filter(
624
- (dependencyPath) => dependencyPath.startsWith(".") || path6.isAbsolute(dependencyPath)
587
+ (dependencyPath) => dependencyPath.startsWith(".") || path5.isAbsolute(dependencyPath)
625
588
  );
626
589
  return {
627
590
  dependencyPaths: nonNodeModuleImportPathsRelativeToDirectory,
@@ -629,17 +592,18 @@ var createDependencyGraph = async (directory) => {
629
592
  };
630
593
  };
631
594
  const updateModuleDependenciesInGraph = async (moduleFilePath) => {
632
- const module = graph[moduleFilePath] ?? {
633
- path: moduleFilePath,
634
- dependencyPaths: [],
635
- dependentPaths: [],
636
- moduleDependencies: []
637
- };
595
+ if (graph[moduleFilePath] === void 0) {
596
+ graph[moduleFilePath] = {
597
+ path: moduleFilePath,
598
+ dependencyPaths: [],
599
+ dependentPaths: [],
600
+ moduleDependencies: []
601
+ };
602
+ }
638
603
  const { moduleDependencies, dependencyPaths: newDependencyPaths } = await getDependencyPaths(moduleFilePath);
639
- module.moduleDependencies = moduleDependencies;
640
- for (const dependencyPath of module.dependencyPaths) {
641
- if (newDependencyPaths.includes(dependencyPath))
642
- continue;
604
+ graph[moduleFilePath].moduleDependencies = moduleDependencies;
605
+ for (const dependencyPath of graph[moduleFilePath].dependencyPaths) {
606
+ if (newDependencyPaths.includes(dependencyPath)) continue;
643
607
  const dependencyModule = graph[dependencyPath];
644
608
  if (dependencyModule !== void 0) {
645
609
  dependencyModule.dependentPaths = dependencyModule.dependentPaths.filter(
@@ -647,21 +611,21 @@ var createDependencyGraph = async (directory) => {
647
611
  );
648
612
  }
649
613
  }
650
- module.dependencyPaths = newDependencyPaths;
651
- for (const dependencyPath of newDependencyPaths) {
614
+ graph[moduleFilePath].dependencyPaths = newDependencyPaths;
615
+ for await (const dependencyPath of newDependencyPaths) {
616
+ if (graph[dependencyPath] === void 0) {
617
+ await updateModuleDependenciesInGraph(dependencyPath);
618
+ }
652
619
  const dependencyModule = graph[dependencyPath];
653
- if (dependencyModule !== void 0 && !dependencyModule.dependentPaths.includes(moduleFilePath)) {
620
+ if (dependencyModule === void 0) {
621
+ throw new Error(
622
+ `Loading the dependency path ${dependencyPath} did not initialize it at all. This is a bug in React Email.`
623
+ );
624
+ }
625
+ if (!dependencyModule.dependentPaths.includes(moduleFilePath)) {
654
626
  dependencyModule.dependentPaths.push(moduleFilePath);
655
- } else {
656
- graph[dependencyPath] = {
657
- path: dependencyPath,
658
- moduleDependencies: [],
659
- dependencyPaths: [],
660
- dependentPaths: [moduleFilePath]
661
- };
662
627
  }
663
628
  }
664
- graph[moduleFilePath] = module;
665
629
  };
666
630
  for (const filePath of modulePaths) {
667
631
  await updateModuleDependenciesInGraph(filePath);
@@ -746,11 +710,19 @@ var setupHotreloading = async (devServer2, emailDirRelativePath) => {
746
710
  let changes = [];
747
711
  const reload = debounce(() => {
748
712
  clients.forEach((client) => {
749
- client.emit("reload", changes);
713
+ client.emit(
714
+ "reload",
715
+ changes.filter(
716
+ (change) => (
717
+ // Ensures only changes inside the emails directory are emitted
718
+ path6.resolve(absolutePathToEmailsDirectory, change.filename).startsWith(absolutePathToEmailsDirectory)
719
+ )
720
+ )
721
+ );
750
722
  });
751
723
  changes = [];
752
724
  }, 150);
753
- const absolutePathToEmailsDirectory = path7.resolve(
725
+ const absolutePathToEmailsDirectory = path6.resolve(
754
726
  process.cwd(),
755
727
  emailDirRelativePath
756
728
  );
@@ -760,7 +732,7 @@ var setupHotreloading = async (devServer2, emailDirRelativePath) => {
760
732
  cwd: absolutePathToEmailsDirectory
761
733
  });
762
734
  const getFilesOutsideEmailsDirectory = () => Object.keys(dependencyGraph).filter(
763
- (p) => path7.relative(absolutePathToEmailsDirectory, p).startsWith("..")
735
+ (p) => path6.relative(absolutePathToEmailsDirectory, p).startsWith("..")
764
736
  );
765
737
  let filesOutsideEmailsDirectory = getFilesOutsideEmailsDirectory();
766
738
  for (const p of filesOutsideEmailsDirectory) {
@@ -772,11 +744,11 @@ var setupHotreloading = async (devServer2, emailDirRelativePath) => {
772
744
  process.on("SIGINT", exit);
773
745
  process.on("uncaughtException", exit);
774
746
  watcher.on("all", async (event, relativePathToChangeTarget) => {
775
- const file = relativePathToChangeTarget.split(path7.sep);
747
+ const file = relativePathToChangeTarget.split(path6.sep);
776
748
  if (file.length === 0) {
777
749
  return;
778
750
  }
779
- const pathToChangeTarget = path7.resolve(
751
+ const pathToChangeTarget = path6.resolve(
780
752
  absolutePathToEmailsDirectory,
781
753
  relativePathToChangeTarget
782
754
  );
@@ -800,7 +772,7 @@ var setupHotreloading = async (devServer2, emailDirRelativePath) => {
800
772
  for (const dependentPath of resolveDependentsOf(pathToChangeTarget)) {
801
773
  changes.push({
802
774
  event: "change",
803
- filename: path7.relative(absolutePathToEmailsDirectory, dependentPath)
775
+ filename: path6.relative(absolutePathToEmailsDirectory, dependentPath)
804
776
  });
805
777
  }
806
778
  reload();
@@ -808,6 +780,63 @@ var setupHotreloading = async (devServer2, emailDirRelativePath) => {
808
780
  return watcher;
809
781
  };
810
782
 
783
+ // src/cli/utils/tree.ts
784
+ import { promises as fs4 } from "node:fs";
785
+ import os from "node:os";
786
+ import path7 from "node:path";
787
+ var SYMBOLS = {
788
+ BRANCH: "\u251C\u2500\u2500 ",
789
+ EMPTY: "",
790
+ INDENT: " ",
791
+ LAST_BRANCH: "\u2514\u2500\u2500 ",
792
+ VERTICAL: "\u2502 "
793
+ };
794
+ var getTreeLines = async (dirPath, depth, currentDepth = 0) => {
795
+ const base = process.cwd();
796
+ const dirFullpath = path7.resolve(base, dirPath);
797
+ const dirname2 = path7.basename(dirFullpath);
798
+ let lines = [dirname2];
799
+ const dirStat = await fs4.stat(dirFullpath);
800
+ if (dirStat.isDirectory() && currentDepth < depth) {
801
+ const childDirents = await fs4.readdir(dirFullpath, { withFileTypes: true });
802
+ childDirents.sort((a, b) => {
803
+ if (a.isDirectory() && b.isFile()) {
804
+ return -1;
805
+ }
806
+ if (a.isFile() && b.isDirectory()) {
807
+ return 1;
808
+ }
809
+ return b.name > a.name ? -1 : 1;
810
+ });
811
+ for (let i = 0; i < childDirents.length; i++) {
812
+ const dirent = childDirents[i];
813
+ const isLast = i === childDirents.length - 1;
814
+ const branchingSymbol = isLast ? SYMBOLS.LAST_BRANCH : SYMBOLS.BRANCH;
815
+ const verticalSymbol = isLast ? SYMBOLS.INDENT : SYMBOLS.VERTICAL;
816
+ if (dirent.isFile()) {
817
+ lines.push(`${branchingSymbol}${dirent.name}`);
818
+ } else {
819
+ const pathToDirectory = path7.join(dirFullpath, dirent.name);
820
+ const treeLinesForSubDirectory = await getTreeLines(
821
+ pathToDirectory,
822
+ depth,
823
+ currentDepth + 1
824
+ );
825
+ lines = lines.concat(
826
+ treeLinesForSubDirectory.map(
827
+ (line, index) => index === 0 ? `${branchingSymbol}${line}` : `${verticalSymbol}${line}`
828
+ )
829
+ );
830
+ }
831
+ }
832
+ }
833
+ return lines;
834
+ };
835
+ var tree = async (dirPath, depth) => {
836
+ const lines = await getTreeLines(dirPath, depth);
837
+ return lines.join(os.EOL);
838
+ };
839
+
811
840
  // src/cli/commands/build.ts
812
841
  var buildPreviewApp = (absoluteDirectory) => {
813
842
  return new Promise((resolve, reject) => {
@@ -875,8 +904,8 @@ var getEmailSlugsFromEmailDirectory = (emailDirectory, emailsDirectoryAbsolutePa
875
904
  const directoryPathRelativeToEmailsDirectory = emailDirectory.absolutePath.replace(emailsDirectoryAbsolutePath, "").trim();
876
905
  const slugs = [];
877
906
  emailDirectory.emailFilenames.forEach(
878
- (filename) => slugs.push(
879
- path8.join(directoryPathRelativeToEmailsDirectory, filename).split(path8.sep).filter((segment) => segment.length > 0)
907
+ (filename2) => slugs.push(
908
+ path8.join(directoryPathRelativeToEmailsDirectory, filename2).split(path8.sep).filter((segment) => segment.length > 0)
880
909
  )
881
910
  );
882
911
  emailDirectory.subDirectories.forEach((directory) => {
@@ -1116,7 +1145,7 @@ var renderingUtilitiesExporter = (emailTemplates) => ({
1116
1145
  var getEmailTemplatesFromDirectory = (emailDirectory) => {
1117
1146
  const templatePaths = [];
1118
1147
  emailDirectory.emailFilenames.forEach(
1119
- (filename) => templatePaths.push(path10.join(emailDirectory.absolutePath, filename))
1148
+ (filename2) => templatePaths.push(path10.join(emailDirectory.absolutePath, filename2))
1120
1149
  );
1121
1150
  emailDirectory.subDirectories.forEach((directory) => {
1122
1151
  templatePaths.push(...getEmailTemplatesFromDirectory(directory));
@@ -1300,6 +1329,6 @@ program.command("export").description("Build the templates to the `out` director
1300
1329
  "To, or not to show a spinner with process information",
1301
1330
  false
1302
1331
  ).action(
1303
- ({ outDir, pretty, plainText, silent, dir: srcDir }) => exportTemplates(outDir, srcDir, { pretty, silent, plainText })
1332
+ ({ outDir, pretty, plainText, silent, dir: srcDir }) => exportTemplates(outDir, srcDir, { silent, plainText, pretty })
1304
1333
  );
1305
1334
  program.parse();