react-email 3.0.6 → 4.0.0-alpha.0

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 (103) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/cli/index.js +768 -763
  3. package/dist/cli/index.mjs +480 -476
  4. package/dist/preview/.next/BUILD_ID +1 -1
  5. package/dist/preview/.next/app-build-manifest.json +14 -12
  6. package/dist/preview/.next/build-manifest.json +5 -5
  7. package/dist/preview/.next/cache/.rscinfo +1 -1
  8. package/dist/preview/.next/cache/webpack/client-production/0.pack +0 -0
  9. package/dist/preview/.next/cache/webpack/client-production/index.pack +0 -0
  10. package/dist/preview/.next/cache/webpack/edge-server-production/index.pack +0 -0
  11. package/dist/preview/.next/cache/webpack/server-production/0.pack +0 -0
  12. package/dist/preview/.next/cache/webpack/server-production/index.pack +0 -0
  13. package/dist/preview/.next/next-minimal-server.js.nft.json +1 -1
  14. package/dist/preview/.next/next-server.js.nft.json +1 -1
  15. package/dist/preview/.next/prerender-manifest.json +1 -1
  16. package/dist/preview/.next/server/app/_not-found/page.js +1 -1
  17. package/dist/preview/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/dist/preview/.next/server/app/page.js +1 -1
  19. package/dist/preview/.next/server/app/page.js.nft.json +1 -1
  20. package/dist/preview/.next/server/app/page_client-reference-manifest.js +1 -1
  21. package/dist/preview/.next/server/app/preview/[...slug]/page.js +6 -6
  22. package/dist/preview/.next/server/app/preview/[...slug]/page.js.nft.json +1 -1
  23. package/dist/preview/.next/server/app/preview/[...slug]/page_client-reference-manifest.js +1 -1
  24. package/dist/preview/.next/server/chunks/273.js +1 -0
  25. package/dist/preview/.next/server/chunks/594.js +10 -0
  26. package/dist/preview/.next/server/middleware-build-manifest.js +1 -1
  27. package/dist/preview/.next/server/pages/500.html +1 -1
  28. package/dist/preview/.next/server/server-reference-manifest.js +1 -1
  29. package/dist/preview/.next/server/server-reference-manifest.json +1 -1
  30. package/dist/preview/.next/server/webpack-runtime.js +1 -1
  31. package/dist/preview/.next/static/chunks/18b16e15-6ad9b58e10ff8891.js +1 -0
  32. package/dist/preview/.next/static/chunks/490-48951f2e19ae3aef.js +1 -0
  33. package/dist/preview/.next/static/chunks/600-2e2ca4c8bbd97b61.js +1 -0
  34. package/dist/preview/.next/static/chunks/app/{layout-a2901ed1c2c53661.js → layout-490964e2c3604d33.js} +1 -1
  35. package/dist/preview/.next/static/chunks/app/page-d2432acd08db8fc0.js +1 -0
  36. package/dist/preview/.next/static/chunks/app/preview/[...slug]/page-f4e211e00c026401.js +1 -0
  37. package/dist/preview/.next/static/chunks/webpack-7bf1ffb05f5540be.js +1 -0
  38. package/dist/preview/.next/static/css/5e0736cafbb392a9.css +3 -0
  39. package/dist/preview/.next/trace +21 -21
  40. package/package.json +12 -7
  41. package/postcss.config.js +1 -1
  42. package/src/actions/email-validation/check-links.ts +88 -0
  43. package/src/actions/email-validation/get-line-and-column-from-index.spec.ts +22 -0
  44. package/src/actions/email-validation/get-line-and-column-from-index.ts +43 -0
  45. package/src/actions/email-validation/quick-fetch.ts +12 -0
  46. package/src/actions/get-email-path-from-slug.ts +7 -4
  47. package/src/actions/render-email-by-path.tsx +3 -3
  48. package/src/animated-icons-data/help.json +1082 -0
  49. package/src/animated-icons-data/link.json +1309 -0
  50. package/src/animated-icons-data/load.json +443 -0
  51. package/src/animated-icons-data/mail.json +1320 -0
  52. package/src/app/globals.css +0 -24
  53. package/src/app/layout.tsx +7 -3
  54. package/src/app/page.tsx +9 -10
  55. package/src/app/preview/[...slug]/page.tsx +3 -2
  56. package/src/app/preview/[...slug]/preview.tsx +5 -5
  57. package/src/app/preview/[...slug]/rendering-error.tsx +6 -6
  58. package/src/components/button.tsx +8 -8
  59. package/src/components/code-container.tsx +7 -7
  60. package/src/components/code-snippet.tsx +11 -0
  61. package/src/components/code.tsx +4 -4
  62. package/src/components/heading.tsx +1 -1
  63. package/src/components/icons/icon-button.tsx +1 -1
  64. package/src/components/icons/icon-circle-check.tsx +21 -0
  65. package/src/components/icons/icon-circle-close.tsx +17 -0
  66. package/src/components/icons/icon-circle-warning.tsx +17 -0
  67. package/src/components/icons/icon-email.tsx +18 -0
  68. package/src/components/icons/icon-link.tsx +14 -0
  69. package/src/components/icons/icon-stamp.tsx +14 -0
  70. package/src/components/send.tsx +9 -9
  71. package/src/components/shell.tsx +32 -34
  72. package/src/components/sidebar/{sidebar-directory-children.tsx → file-tree-directory-children.tsx} +22 -18
  73. package/src/components/sidebar/{sidebar-directory.tsx → file-tree-directory.tsx} +11 -12
  74. package/src/components/sidebar/file-tree.tsx +31 -0
  75. package/src/components/sidebar/link-checker.tsx +291 -0
  76. package/src/components/sidebar/sidebar.tsx +296 -22
  77. package/src/components/text.tsx +1 -1
  78. package/src/components/tooltip-content.tsx +3 -3
  79. package/src/components/tooltip.tsx +1 -1
  80. package/src/components/topbar.tsx +14 -17
  81. package/src/hooks/use-email-rendering-result.ts +2 -2
  82. package/src/hooks/use-icon-animation.ts +44 -0
  83. package/src/utils/cn.ts +1 -1
  84. package/src/utils/esbuild/renderring-utilities-exporter.ts +1 -1
  85. package/src/utils/get-email-component.ts +6 -6
  86. package/src/utils/get-emails-directory-metadata.spec.ts +0 -1
  87. package/src/utils/improve-error-with-sourcemap.ts +1 -1
  88. package/src/utils/static-node-modules-for-vm.ts +6 -6
  89. package/tsconfig.json +2 -6
  90. package/.eslintrc.js +0 -52
  91. package/.prettierignore +0 -3
  92. package/.prettierrc.js +0 -8
  93. package/dist/preview/.next/cache/eslint/.cache_1c3sgg +0 -1
  94. package/dist/preview/.next/server/chunks/391.js +0 -1
  95. package/dist/preview/.next/server/chunks/720.js +0 -10
  96. package/dist/preview/.next/static/chunks/12-b9450aa0845e7574.js +0 -1
  97. package/dist/preview/.next/static/chunks/154-4202f86af36ccff4.js +0 -1
  98. package/dist/preview/.next/static/chunks/app/page-54a86772095e22e0.js +0 -1
  99. package/dist/preview/.next/static/chunks/app/preview/[...slug]/page-2bfad134b65ddd79.js +0 -1
  100. package/dist/preview/.next/static/chunks/webpack-9255716c9496e606.js +0 -1
  101. package/dist/preview/.next/static/css/eb0a93282704d7ab.css +0 -3
  102. /package/dist/preview/.next/static/{Trk1e7GzgKOLunAXBDCy- → fZaiKz58wDr55pxLu9uHa}/_buildManifest.js +0 -0
  103. /package/dist/preview/.next/static/{Trk1e7GzgKOLunAXBDCy- → fZaiKz58wDr55pxLu9uHa}/_ssgManifest.js +0 -0
@@ -13,7 +13,7 @@ import { program } from "commander";
13
13
  // package.json
14
14
  var package_default = {
15
15
  name: "react-email",
16
- version: "3.0.6",
16
+ version: "4.0.0-alpha.0",
17
17
  description: "A live preview of your emails right in your browser.",
18
18
  bin: {
19
19
  email: "./dist/cli/index.js"
@@ -23,8 +23,7 @@ var package_default = {
23
23
  dev: "tsup-node --watch",
24
24
  test: "vitest run",
25
25
  "test:watch": "vitest",
26
- clean: "rm -rf dist",
27
- lint: "eslint . && tsc"
26
+ clean: "rm -rf dist"
28
27
  },
29
28
  license: "MIT",
30
29
  repository: {
@@ -40,8 +39,8 @@ var package_default = {
40
39
  node: ">=18.0.0"
41
40
  },
42
41
  dependencies: {
43
- "@babel/core": "7.24.5",
44
42
  "@babel/parser": "7.24.5",
43
+ "@babel/traverse": "7.25.6",
45
44
  chalk: "4.1.2",
46
45
  chokidar: "4.0.3",
47
46
  commander: "11.1.0",
@@ -55,16 +54,22 @@ var package_default = {
55
54
  ora: "5.4.1",
56
55
  "socket.io": "4.8.0"
57
56
  },
57
+ overrides: {
58
+ react: "^19",
59
+ "react-dom": "^19"
60
+ },
58
61
  devDependencies: {
59
62
  "@radix-ui/colors": "1.0.1",
60
63
  "@radix-ui/react-collapsible": "1.1.0",
61
64
  "@radix-ui/react-popover": "1.1.1",
62
65
  "@radix-ui/react-slot": "1.1.0",
66
+ "@radix-ui/react-tabs": "1.1.1",
63
67
  "@radix-ui/react-toggle-group": "1.1.0",
64
68
  "@radix-ui/react-tooltip": "1.1.2",
65
69
  "@react-email/render": "workspace:*",
66
70
  "@swc/core": "1.4.15",
67
71
  "@types/babel__core": "7.20.5",
72
+ "@types/babel__traverse": "*",
68
73
  "@types/fs-extra": "11.0.1",
69
74
  "@types/mime-types": "2.1.4",
70
75
  "@types/node": "22.10.2",
@@ -75,11 +80,11 @@ var package_default = {
75
80
  "@vercel/style-guide": "5.1.0",
76
81
  autoprefixer: "10.4.20",
77
82
  clsx: "2.1.0",
78
- eslint: "8.50.0",
79
- "eslint-config-prettier": "9.0.0",
80
- "eslint-config-turbo": "2.1.0",
81
83
  "framer-motion": "12.0.0-alpha.2",
84
+ "lottie-react": "^2.4.0",
85
+ "node-html-parser": "6.1.13",
82
86
  postcss: "8.4.40",
87
+ "prettier-plugin-tailwindcss": "0.6.6",
83
88
  "prism-react-renderer": "2.1.0",
84
89
  react: "^19",
85
90
  "react-dom": "^19",
@@ -97,13 +102,112 @@ var package_default = {
97
102
  }
98
103
  };
99
104
 
100
- // src/cli/commands/dev.ts
101
- import fs4 from "node:fs";
105
+ // src/cli/commands/build.ts
106
+ import { spawn } from "node:child_process";
107
+ import fs5 from "node:fs";
108
+ import path8 from "node:path";
109
+ import logSymbols3 from "log-symbols";
110
+ import ora2 from "ora";
111
+
112
+ // src/utils/get-emails-directory-metadata.ts
113
+ import fs from "node:fs";
114
+ import path from "node:path";
115
+ var isFileAnEmail = (fullPath) => {
116
+ const stat = fs.statSync(fullPath);
117
+ if (stat.isDirectory())
118
+ return false;
119
+ const { ext } = path.parse(fullPath);
120
+ if (![".js", ".tsx", ".jsx"].includes(ext))
121
+ return false;
122
+ if (!fs.existsSync(fullPath)) {
123
+ return false;
124
+ }
125
+ const fileContents = fs.readFileSync(fullPath, "utf8");
126
+ return /\bexport\s+default\b/gm.test(fileContents);
127
+ };
128
+ var mergeDirectoriesWithSubDirectories = (emailsDirectoryMetadata) => {
129
+ let currentResultingMergedDirectory = emailsDirectoryMetadata;
130
+ while (currentResultingMergedDirectory.emailFilenames.length === 0 && currentResultingMergedDirectory.subDirectories.length === 1) {
131
+ const onlySubDirectory = currentResultingMergedDirectory.subDirectories[0];
132
+ currentResultingMergedDirectory = {
133
+ ...onlySubDirectory,
134
+ directoryName: path.join(
135
+ currentResultingMergedDirectory.directoryName,
136
+ onlySubDirectory.directoryName
137
+ )
138
+ };
139
+ }
140
+ return currentResultingMergedDirectory;
141
+ };
142
+ var getEmailsDirectoryMetadata = async (absolutePathToEmailsDirectory, keepFileExtensions = false, isSubDirectory = false, baseDirectoryPath = absolutePathToEmailsDirectory) => {
143
+ if (!fs.existsSync(absolutePathToEmailsDirectory))
144
+ return;
145
+ const dirents = await fs.promises.readdir(absolutePathToEmailsDirectory, {
146
+ withFileTypes: true
147
+ });
148
+ const emailFilenames = dirents.filter(
149
+ (dirent) => isFileAnEmail(path.join(absolutePathToEmailsDirectory, dirent.name))
150
+ ).map(
151
+ (dirent) => keepFileExtensions ? dirent.name : dirent.name.replace(path.extname(dirent.name), "")
152
+ );
153
+ const subDirectories = await Promise.all(
154
+ dirents.filter(
155
+ (dirent) => dirent.isDirectory() && !dirent.name.startsWith("_") && dirent.name !== "static"
156
+ ).map((dirent) => {
157
+ const direntAbsolutePath = path.join(
158
+ absolutePathToEmailsDirectory,
159
+ dirent.name
160
+ );
161
+ return getEmailsDirectoryMetadata(
162
+ direntAbsolutePath,
163
+ keepFileExtensions,
164
+ true,
165
+ baseDirectoryPath
166
+ );
167
+ })
168
+ );
169
+ const emailsMetadata = {
170
+ absolutePath: absolutePathToEmailsDirectory,
171
+ relativePath: path.relative(
172
+ baseDirectoryPath,
173
+ absolutePathToEmailsDirectory
174
+ ),
175
+ directoryName: absolutePathToEmailsDirectory.split(path.sep).pop(),
176
+ emailFilenames,
177
+ subDirectories
178
+ };
179
+ return isSubDirectory ? mergeDirectoriesWithSubDirectories(emailsMetadata) : emailsMetadata;
180
+ };
181
+
182
+ // src/utils/register-spinner-autostopping.ts
183
+ import logSymbols from "log-symbols";
184
+ var spinners = /* @__PURE__ */ new Set();
185
+ process.on("SIGINT", () => {
186
+ spinners.forEach((spinner) => {
187
+ if (spinner.isSpinning) {
188
+ spinner.stop();
189
+ }
190
+ });
191
+ });
192
+ process.on("exit", (code) => {
193
+ if (code !== 0) {
194
+ spinners.forEach((spinner) => {
195
+ if (spinner.isSpinning) {
196
+ spinner.stopAndPersist({
197
+ symbol: logSymbols.error
198
+ });
199
+ }
200
+ });
201
+ }
202
+ });
203
+ var registerSpinnerAutostopping = (spinner) => {
204
+ spinners.add(spinner);
205
+ };
102
206
 
103
207
  // src/cli/utils/tree.ts
104
- import { promises as fs } from "node:fs";
208
+ import { promises as fs2 } from "node:fs";
105
209
  import os from "node:os";
106
- import path from "node:path";
210
+ import path2 from "node:path";
107
211
  var SYMBOLS = {
108
212
  BRANCH: "\u251C\u2500\u2500 ",
109
213
  EMPTY: "",
@@ -113,12 +217,12 @@ var SYMBOLS = {
113
217
  };
114
218
  var getTreeLines = async (dirPath, depth, currentDepth = 0) => {
115
219
  const base = process.cwd();
116
- const dirFullpath = path.resolve(base, dirPath);
117
- const dirname = path.basename(dirFullpath);
220
+ const dirFullpath = path2.resolve(base, dirPath);
221
+ const dirname = path2.basename(dirFullpath);
118
222
  let lines = [dirname];
119
- const dirStat = await fs.stat(dirFullpath);
223
+ const dirStat = await fs2.stat(dirFullpath);
120
224
  if (dirStat.isDirectory() && currentDepth < depth) {
121
- const childDirents = await fs.readdir(dirFullpath, { withFileTypes: true });
225
+ const childDirents = await fs2.readdir(dirFullpath, { withFileTypes: true });
122
226
  childDirents.sort((a, b) => {
123
227
  if (a.isDirectory() && b.isFile()) {
124
228
  return -1;
@@ -136,7 +240,7 @@ var getTreeLines = async (dirPath, depth, currentDepth = 0) => {
136
240
  if (dirent.isFile()) {
137
241
  lines.push(`${branchingSymbol}${dirent.name}`);
138
242
  } else {
139
- const pathToDirectory = path.join(dirFullpath, dirent.name);
243
+ const pathToDirectory = path2.join(dirFullpath, dirent.name);
140
244
  const treeLinesForSubDirectory = await getTreeLines(
141
245
  pathToDirectory,
142
246
  depth,
@@ -158,98 +262,46 @@ var tree = async (dirPath, depth) => {
158
262
  };
159
263
 
160
264
  // src/cli/utils/preview/hot-reloading/setup-hot-reloading.ts
161
- import path6 from "node:path";
162
- import { Server as SocketServer } from "socket.io";
265
+ import path7 from "node:path";
163
266
  import { watch } from "chokidar";
164
267
  import debounce from "debounce";
268
+ import { Server as SocketServer } from "socket.io";
165
269
 
166
270
  // src/cli/utils/preview/hot-reloading/create-dependency-graph.ts
167
- import path5 from "node:path";
168
- import { existsSync, promises as fs3, statSync } from "node:fs";
169
-
170
- // src/cli/utils/preview/hot-reloading/get-imported-modules.ts
171
- import { traverse } from "@babel/core";
172
- import { parse } from "@babel/parser";
173
- var getImportedModules = (contents) => {
174
- const importedPaths = [];
175
- const parsedContents = parse(contents, {
176
- sourceType: "unambiguous",
177
- strictMode: false,
178
- errorRecovery: true,
179
- plugins: ["jsx", "typescript", "decorators"]
180
- });
181
- traverse(parsedContents, {
182
- ImportDeclaration({ node }) {
183
- importedPaths.push(node.source.value);
184
- },
185
- ExportAllDeclaration({ node }) {
186
- importedPaths.push(node.source.value);
187
- },
188
- ExportNamedDeclaration({ node }) {
189
- if (node.source) {
190
- importedPaths.push(node.source.value);
191
- }
192
- },
193
- CallExpression({ node }) {
194
- if ("name" in node.callee && node.callee.name === "require") {
195
- if (node.arguments.length === 1) {
196
- const importPathNode = node.arguments[0];
197
- if (importPathNode.type === "StringLiteral") {
198
- importedPaths.push(importPathNode.value);
199
- }
200
- }
201
- }
202
- }
203
- });
204
- return importedPaths;
205
- };
271
+ import { promises as fs4, existsSync, statSync } from "node:fs";
272
+ import path6 from "node:path";
206
273
 
207
274
  // src/cli/utils/preview/start-dev-server.ts
208
- import path4 from "node:path";
209
275
  import http from "node:http";
276
+ import path5 from "node:path";
210
277
  import url from "node:url";
278
+ import chalk from "chalk";
279
+ import logSymbols2 from "log-symbols";
211
280
  import next from "next";
212
281
  import ora from "ora";
213
- import logSymbols2 from "log-symbols";
214
- import chalk from "chalk";
215
282
 
216
- // src/utils/register-spinner-autostopping.ts
217
- import logSymbols from "log-symbols";
218
- var spinners = /* @__PURE__ */ new Set();
219
- process.on("SIGINT", () => {
220
- spinners.forEach((spinner) => {
221
- if (spinner.isSpinning) {
222
- spinner.stop();
223
- }
224
- });
225
- });
226
- process.on("exit", (code) => {
227
- if (code !== 0) {
228
- spinners.forEach((spinner) => {
229
- if (spinner.isSpinning) {
230
- spinner.stopAndPersist({
231
- symbol: logSymbols.error
232
- });
233
- }
234
- });
235
- }
236
- });
237
- var registerSpinnerAutostopping = (spinner) => {
238
- spinners.add(spinner);
283
+ // src/cli/utils/preview/get-env-variables-for-preview-app.ts
284
+ import path3 from "node:path";
285
+ var getEnvVariablesForPreviewApp = (relativePathToEmailsDirectory, cwd) => {
286
+ return {
287
+ EMAILS_DIR_RELATIVE_PATH: relativePathToEmailsDirectory,
288
+ EMAILS_DIR_ABSOLUTE_PATH: path3.resolve(cwd, relativePathToEmailsDirectory),
289
+ USER_PROJECT_LOCATION: cwd
290
+ };
239
291
  };
240
292
 
241
293
  // src/cli/utils/preview/serve-static-file.ts
242
- import path2 from "node:path";
243
- import { promises as fs2 } from "node:fs";
294
+ import { promises as fs3 } from "node:fs";
295
+ import path4 from "node:path";
244
296
  import { lookup } from "mime-types";
245
297
  var serveStaticFile = async (res, parsedUrl, staticDirRelativePath) => {
246
- const staticBaseDir = path2.join(process.cwd(), staticDirRelativePath);
298
+ const staticBaseDir = path4.join(process.cwd(), staticDirRelativePath);
247
299
  const pathname = parsedUrl.pathname;
248
- const ext = path2.parse(pathname).ext;
249
- let fileAbsolutePath = path2.join(staticBaseDir, pathname);
250
- const fileHandle = await fs2.open(fileAbsolutePath, "r");
300
+ const ext = path4.parse(pathname).ext;
301
+ const fileAbsolutePath = path4.join(staticBaseDir, pathname);
302
+ const fileHandle = await fs3.open(fileAbsolutePath, "r");
251
303
  try {
252
- const fileData = await fs2.readFile(fileHandle);
304
+ const fileData = await fs3.readFile(fileHandle);
253
305
  res.setHeader("Content-type", lookup(ext) || "text/plain");
254
306
  res.end(fileData);
255
307
  } catch (exception) {
@@ -266,16 +318,6 @@ var serveStaticFile = async (res, parsedUrl, staticDirRelativePath) => {
266
318
  }
267
319
  };
268
320
 
269
- // src/cli/utils/preview/get-env-variables-for-preview-app.ts
270
- import path3 from "node:path";
271
- var getEnvVariablesForPreviewApp = (relativePathToEmailsDirectory, cwd) => {
272
- return {
273
- EMAILS_DIR_RELATIVE_PATH: relativePathToEmailsDirectory,
274
- EMAILS_DIR_ABSOLUTE_PATH: path3.resolve(cwd, relativePathToEmailsDirectory),
275
- USER_PROJECT_LOCATION: cwd
276
- };
277
- };
278
-
279
321
  // src/cli/utils/preview/start-dev-server.ts
280
322
  var devServer;
281
323
  var safeAsyncServerListen = (server, port) => {
@@ -290,9 +332,9 @@ var safeAsyncServerListen = (server, port) => {
290
332
  });
291
333
  });
292
334
  };
293
- var isDev = !__filename.endsWith(path4.join("cli", "index.js"));
294
- var cliPacakgeLocation = isDev ? path4.resolve(__dirname, "../../../..") : path4.resolve(__dirname, "../..");
295
- var previewServerLocation = isDev ? path4.resolve(__dirname, "../../../..") : path4.resolve(__dirname, "../preview");
335
+ var isDev = !__filename.endsWith(path5.join("cli", "index.js"));
336
+ var cliPacakgeLocation = isDev ? path5.resolve(__dirname, "../../../..") : path5.resolve(__dirname, "../..");
337
+ var previewServerLocation = isDev ? path5.resolve(__dirname, "../../../..") : path5.resolve(__dirname, "../preview");
296
338
  var startDevServer = async (emailsDirRelativePath, staticBaseDirRelativePath, port) => {
297
339
  devServer = http.createServer((req, res) => {
298
340
  if (!req.url) {
@@ -307,7 +349,7 @@ var startDevServer = async (emailsDirRelativePath, staticBaseDirRelativePath, po
307
349
  res.setHeader("Pragma", "no-cache");
308
350
  res.setHeader("Expires", "-1");
309
351
  try {
310
- if (parsedUrl.path && parsedUrl.path.includes("static/") && !parsedUrl.path.includes("_next/static/")) {
352
+ if (parsedUrl.path?.includes("static/") && !parsedUrl.path.includes("_next/static/")) {
311
353
  void serveStaticFile(res, parsedUrl, staticBaseDirRelativePath);
312
354
  } else if (!isNextReady) {
313
355
  void nextReadyPromise.then(
@@ -359,7 +401,7 @@ var startDevServer = async (emailsDirRelativePath, staticBaseDirRelativePath, po
359
401
  NODE_ENV: "development",
360
402
  ...getEnvVariablesForPreviewApp(
361
403
  // If we don't do normalization here, stuff like https://github.com/resend/react-email/issues/1354 happens.
362
- path4.normalize(emailsDirRelativePath),
404
+ path5.normalize(emailsDirRelativePath),
363
405
  process.cwd()
364
406
  )
365
407
  };
@@ -417,12 +459,49 @@ process.on(
417
459
  makeExitHandler({ shouldKillProcess: true, killWithErrorCode: true })
418
460
  );
419
461
 
462
+ // src/cli/utils/preview/hot-reloading/get-imported-modules.ts
463
+ import { parse } from "@babel/parser";
464
+ import traverse from "@babel/traverse";
465
+ var getImportedModules = (contents) => {
466
+ const importedPaths = [];
467
+ const parsedContents = parse(contents, {
468
+ sourceType: "unambiguous",
469
+ strictMode: false,
470
+ errorRecovery: true,
471
+ plugins: ["jsx", "typescript", "decorators"]
472
+ });
473
+ traverse(parsedContents, {
474
+ ImportDeclaration({ node }) {
475
+ importedPaths.push(node.source.value);
476
+ },
477
+ ExportAllDeclaration({ node }) {
478
+ importedPaths.push(node.source.value);
479
+ },
480
+ ExportNamedDeclaration({ node }) {
481
+ if (node.source) {
482
+ importedPaths.push(node.source.value);
483
+ }
484
+ },
485
+ CallExpression({ node }) {
486
+ if ("name" in node.callee && node.callee.name === "require") {
487
+ if (node.arguments.length === 1) {
488
+ const importPathNode = node.arguments[0];
489
+ if (importPathNode.type === "StringLiteral") {
490
+ importedPaths.push(importPathNode.value);
491
+ }
492
+ }
493
+ }
494
+ }
495
+ });
496
+ return importedPaths;
497
+ };
498
+
420
499
  // src/cli/utils/preview/hot-reloading/create-dependency-graph.ts
421
500
  var readAllFilesInsideDirectory = async (directory) => {
422
501
  let allFilePaths = [];
423
- const topLevelDirents = await fs3.readdir(directory, { withFileTypes: true });
502
+ const topLevelDirents = await fs4.readdir(directory, { withFileTypes: true });
424
503
  for await (const dirent of topLevelDirents) {
425
- const pathToDirent = path5.join(directory, dirent.name);
504
+ const pathToDirent = path6.join(directory, dirent.name);
426
505
  if (dirent.isDirectory()) {
427
506
  allFilePaths = allFilePaths.concat(
428
507
  await readAllFilesInsideDirectory(pathToDirent)
@@ -434,7 +513,7 @@ var readAllFilesInsideDirectory = async (directory) => {
434
513
  return allFilePaths;
435
514
  };
436
515
  var isJavascriptModule = (filePath) => {
437
- const extensionName = path5.extname(filePath);
516
+ const extensionName = path6.extname(filePath);
438
517
  return [".js", ".ts", ".jsx", ".tsx", ".mjs", ".cjs"].includes(extensionName);
439
518
  };
440
519
  var checkFileExtensionsUntilItExists = (pathWithoutExtension) => {
@@ -472,64 +551,63 @@ var createDependencyGraph = async (directory) => {
472
551
  ])
473
552
  );
474
553
  const getDependencyPaths = async (filePath) => {
475
- const contents = await fs3.readFile(filePath, "utf8");
554
+ const contents = await fs4.readFile(filePath, "utf8");
476
555
  const importedPaths = getImportedModules(contents);
477
556
  const importedPathsRelativeToDirectory = importedPaths.map(
478
557
  (dependencyPath) => {
479
558
  const isModulePath = !dependencyPath.startsWith(".");
480
- if (!isModulePath && !path5.isAbsolute(dependencyPath)) {
481
- let pathToDependencyFromDirectory = path5.resolve(
482
- /*
483
- path.resolve resolves paths differently from what imports on javascript do.
484
-
485
- So if we wouldn't do this, for an email at "/path/to/email.tsx" with a dependecy path of "./other-email"
486
- would end up going into /path/to/email.tsx/other-email instead of /path/to/other-email which is the
487
- one the import is meant to go to
488
- */
489
- path5.dirname(filePath),
490
- dependencyPath
559
+ if (isModulePath || path6.isAbsolute(dependencyPath)) {
560
+ return dependencyPath;
561
+ }
562
+ let pathToDependencyFromDirectory = path6.resolve(
563
+ /*
564
+ path.resolve resolves paths differently from what imports on javascript do.
565
+
566
+ So if we wouldn't do this, for an email at "/path/to/email.tsx" with a dependecy path of "./other-email"
567
+ would end up going into /path/to/email.tsx/other-email instead of /path/to/other-email which is the
568
+ one the import is meant to go to
569
+ */
570
+ path6.dirname(filePath),
571
+ dependencyPath
572
+ );
573
+ let isDirectory = false;
574
+ try {
575
+ isDirectory = statSync(pathToDependencyFromDirectory).isDirectory();
576
+ } catch (_) {
577
+ }
578
+ if (isDirectory) {
579
+ const pathToSubDirectory = pathToDependencyFromDirectory;
580
+ const pathWithExtension = checkFileExtensionsUntilItExists(
581
+ `${pathToSubDirectory}/index`
491
582
  );
492
- let isDirectory = false;
493
- try {
494
- isDirectory = statSync(pathToDependencyFromDirectory).isDirectory();
495
- } catch (_) {
496
- }
497
- if (isDirectory) {
498
- const pathToSubDirectory = pathToDependencyFromDirectory;
499
- const pathWithExtension = checkFileExtensionsUntilItExists(
500
- `${pathToSubDirectory}/index`
583
+ if (pathWithExtension) {
584
+ pathToDependencyFromDirectory = pathWithExtension;
585
+ } else if (isDev) {
586
+ console.warn(
587
+ `Could not find index file for directory at ${pathToDependencyFromDirectory}. This is probably going to cause issues with both hot reloading and your code.`
501
588
  );
502
- if (pathWithExtension) {
503
- pathToDependencyFromDirectory = pathWithExtension;
504
- } else if (isDev) {
505
- console.warn(
506
- `Could not find index file for directory at ${pathToDependencyFromDirectory}. This is probably going to cause issues with both hot reloading and your code.`
507
- );
508
- }
509
589
  }
510
- if (!isJavascriptModule(pathToDependencyFromDirectory)) {
511
- const pathWithExtension = checkFileExtensionsUntilItExists(
512
- pathToDependencyFromDirectory
590
+ }
591
+ if (!isJavascriptModule(pathToDependencyFromDirectory)) {
592
+ const pathWithExtension = checkFileExtensionsUntilItExists(
593
+ pathToDependencyFromDirectory
594
+ );
595
+ if (pathWithExtension) {
596
+ pathToDependencyFromDirectory = pathWithExtension;
597
+ } else if (isDev) {
598
+ console.warn(
599
+ `Could not determine the file extension for the file at ${pathToDependencyFromDirectory}`
513
600
  );
514
- if (pathWithExtension) {
515
- pathToDependencyFromDirectory = pathWithExtension;
516
- } else if (isDev) {
517
- console.warn(
518
- `Could not determine the file extension for the file at ${pathToDependencyFromDirectory}`
519
- );
520
- }
521
601
  }
522
- return pathToDependencyFromDirectory;
523
- } else {
524
- return dependencyPath;
525
602
  }
603
+ return pathToDependencyFromDirectory;
526
604
  }
527
605
  );
528
606
  const moduleDependencies = importedPathsRelativeToDirectory.filter(
529
- (dependencyPath) => !dependencyPath.startsWith(".") && !path5.isAbsolute(dependencyPath)
607
+ (dependencyPath) => !dependencyPath.startsWith(".") && !path6.isAbsolute(dependencyPath)
530
608
  );
531
609
  const nonNodeModuleImportPathsRelativeToDirectory = importedPathsRelativeToDirectory.filter(
532
- (dependencyPath) => dependencyPath.startsWith(".") || path5.isAbsolute(dependencyPath)
610
+ (dependencyPath) => dependencyPath.startsWith(".") || path6.isAbsolute(dependencyPath)
533
611
  );
534
612
  return {
535
613
  dependencyPaths: nonNodeModuleImportPathsRelativeToDirectory,
@@ -658,7 +736,7 @@ var setupHotreloading = async (devServer2, emailDirRelativePath) => {
658
736
  });
659
737
  changes = [];
660
738
  }, 150);
661
- const absolutePathToEmailsDirectory = path6.resolve(
739
+ const absolutePathToEmailsDirectory = path7.resolve(
662
740
  process.cwd(),
663
741
  emailDirRelativePath
664
742
  );
@@ -668,7 +746,7 @@ var setupHotreloading = async (devServer2, emailDirRelativePath) => {
668
746
  cwd: absolutePathToEmailsDirectory
669
747
  });
670
748
  const getFilesOutsideEmailsDirectory = () => Object.keys(dependencyGraph).filter(
671
- (p) => path6.relative(absolutePathToEmailsDirectory, p).startsWith("..")
749
+ (p) => path7.relative(absolutePathToEmailsDirectory, p).startsWith("..")
672
750
  );
673
751
  let filesOutsideEmailsDirectory = getFilesOutsideEmailsDirectory();
674
752
  for (const p of filesOutsideEmailsDirectory) {
@@ -680,11 +758,11 @@ var setupHotreloading = async (devServer2, emailDirRelativePath) => {
680
758
  process.on("SIGINT", exit);
681
759
  process.on("uncaughtException", exit);
682
760
  watcher.on("all", async (event, relativePathToChangeTarget) => {
683
- const file = relativePathToChangeTarget.split(path6.sep);
761
+ const file = relativePathToChangeTarget.split(path7.sep);
684
762
  if (file.length === 0) {
685
763
  return;
686
764
  }
687
- const pathToChangeTarget = path6.resolve(
765
+ const pathToChangeTarget = path7.resolve(
688
766
  absolutePathToEmailsDirectory,
689
767
  relativePathToChangeTarget
690
768
  );
@@ -708,7 +786,7 @@ var setupHotreloading = async (devServer2, emailDirRelativePath) => {
708
786
  for (const dependentPath of resolveDependentsOf(pathToChangeTarget)) {
709
787
  changes.push({
710
788
  event: "change",
711
- filename: path6.relative(absolutePathToEmailsDirectory, dependentPath)
789
+ filename: path7.relative(absolutePathToEmailsDirectory, dependentPath)
712
790
  });
713
791
  }
714
792
  reload();
@@ -716,300 +794,7 @@ var setupHotreloading = async (devServer2, emailDirRelativePath) => {
716
794
  return watcher;
717
795
  };
718
796
 
719
- // src/cli/commands/dev.ts
720
- var dev = async ({ dir: emailsDirRelativePath, port }) => {
721
- try {
722
- if (!fs4.existsSync(emailsDirRelativePath)) {
723
- console.error(`Missing ${emailsDirRelativePath} folder`);
724
- process.exit(1);
725
- }
726
- const devServer2 = await startDevServer(
727
- emailsDirRelativePath,
728
- emailsDirRelativePath,
729
- // defaults to ./emails/static for the static files that are served to the preview
730
- Number.parseInt(port)
731
- );
732
- await setupHotreloading(devServer2, emailsDirRelativePath);
733
- } catch (error) {
734
- console.log(error);
735
- process.exit(1);
736
- }
737
- };
738
-
739
- // src/cli/commands/export.ts
740
- import fs7, { unlinkSync, writeFileSync } from "node:fs";
741
- import path9 from "node:path";
742
- import { glob } from "glob";
743
- import { build } from "esbuild";
744
- import ora2 from "ora";
745
- import logSymbols3 from "log-symbols";
746
- import normalize from "normalize-path";
747
-
748
- // src/utils/get-emails-directory-metadata.ts
749
- import fs5 from "node:fs";
750
- import path7 from "node:path";
751
- var isFileAnEmail = (fullPath) => {
752
- const stat = fs5.statSync(fullPath);
753
- if (stat.isDirectory())
754
- return false;
755
- const { ext } = path7.parse(fullPath);
756
- if (![".js", ".tsx", ".jsx"].includes(ext))
757
- return false;
758
- if (!fs5.existsSync(fullPath)) {
759
- return false;
760
- }
761
- const fileContents = fs5.readFileSync(fullPath, "utf8");
762
- return /\bexport\s+default\b/gm.test(fileContents);
763
- };
764
- var mergeDirectoriesWithSubDirectories = (emailsDirectoryMetadata) => {
765
- let currentResultingMergedDirectory = emailsDirectoryMetadata;
766
- while (currentResultingMergedDirectory.emailFilenames.length === 0 && currentResultingMergedDirectory.subDirectories.length === 1) {
767
- const onlySubDirectory = currentResultingMergedDirectory.subDirectories[0];
768
- currentResultingMergedDirectory = {
769
- ...onlySubDirectory,
770
- directoryName: path7.join(
771
- currentResultingMergedDirectory.directoryName,
772
- onlySubDirectory.directoryName
773
- )
774
- };
775
- }
776
- return currentResultingMergedDirectory;
777
- };
778
- var getEmailsDirectoryMetadata = async (absolutePathToEmailsDirectory, keepFileExtensions = false, isSubDirectory = false, baseDirectoryPath = absolutePathToEmailsDirectory) => {
779
- if (!fs5.existsSync(absolutePathToEmailsDirectory))
780
- return;
781
- const dirents = await fs5.promises.readdir(absolutePathToEmailsDirectory, {
782
- withFileTypes: true
783
- });
784
- const emailFilenames = dirents.filter(
785
- (dirent) => isFileAnEmail(path7.join(absolutePathToEmailsDirectory, dirent.name))
786
- ).map(
787
- (dirent) => keepFileExtensions ? dirent.name : dirent.name.replace(path7.extname(dirent.name), "")
788
- );
789
- const subDirectories = await Promise.all(
790
- dirents.filter(
791
- (dirent) => dirent.isDirectory() && !dirent.name.startsWith("_") && dirent.name !== "static"
792
- ).map((dirent) => {
793
- const direntAbsolutePath = path7.join(
794
- absolutePathToEmailsDirectory,
795
- dirent.name
796
- );
797
- return getEmailsDirectoryMetadata(
798
- direntAbsolutePath,
799
- keepFileExtensions,
800
- true,
801
- baseDirectoryPath
802
- );
803
- })
804
- );
805
- const emailsMetadata = {
806
- absolutePath: absolutePathToEmailsDirectory,
807
- relativePath: path7.relative(
808
- baseDirectoryPath,
809
- absolutePathToEmailsDirectory
810
- ),
811
- directoryName: absolutePathToEmailsDirectory.split(path7.sep).pop(),
812
- emailFilenames,
813
- subDirectories
814
- };
815
- return isSubDirectory ? mergeDirectoriesWithSubDirectories(emailsMetadata) : emailsMetadata;
816
- };
817
-
818
- // src/utils/esbuild/renderring-utilities-exporter.ts
819
- import path8 from "node:path";
820
- import { promises as fs6 } from "node:fs";
821
-
822
- // src/utils/esbuild/escape-string-for-regex.ts
823
- function escapeStringForRegex(string) {
824
- return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
825
- }
826
-
827
- // src/utils/esbuild/renderring-utilities-exporter.ts
828
- var renderingUtilitiesExporter = (emailTemplates) => ({
829
- name: "rendering-utilities-exporter",
830
- setup: (b) => {
831
- b.onLoad(
832
- {
833
- filter: new RegExp(
834
- emailTemplates.map((emailPath) => escapeStringForRegex(emailPath)).join("|")
835
- )
836
- },
837
- async ({ path: pathToFile }) => {
838
- return {
839
- contents: `${await fs6.readFile(pathToFile, "utf8")};
840
- export { render } from 'react-email-module-that-will-export-render'
841
- export { createElement as reactEmailCreateReactElement } from 'react';
842
- `,
843
- loader: path8.extname(pathToFile).slice(1)
844
- };
845
- }
846
- );
847
- b.onResolve(
848
- { filter: /^react-email-module-that-will-export-render$/ },
849
- async (args) => {
850
- const options = {
851
- kind: "import-statement",
852
- importer: args.importer,
853
- resolveDir: args.resolveDir,
854
- namespace: args.namespace
855
- };
856
- let result = await b.resolve("@react-email/render", options);
857
- if (result.errors.length === 0) {
858
- return result;
859
- }
860
- result = await b.resolve("@react-email/components", options);
861
- if (result.errors.length > 0 && result.errors[0]) {
862
- result.errors[0].text = "Failed trying to import `render` from either `@react-email/render` or `@react-email/components` to be able to render your email template.\n Maybe you don't have either of them installed?";
863
- }
864
- return result;
865
- }
866
- );
867
- }
868
- });
869
-
870
- // src/cli/commands/export.ts
871
- var getEmailTemplatesFromDirectory = (emailDirectory) => {
872
- const templatePaths = [];
873
- emailDirectory.emailFilenames.forEach(
874
- (filename) => templatePaths.push(path9.join(emailDirectory.absolutePath, filename))
875
- );
876
- emailDirectory.subDirectories.forEach((directory) => {
877
- templatePaths.push(...getEmailTemplatesFromDirectory(directory));
878
- });
879
- return templatePaths;
880
- };
881
- var exportTemplates = async (pathToWhereEmailMarkupShouldBeDumped, emailsDirectoryPath, options) => {
882
- if (fs7.existsSync(pathToWhereEmailMarkupShouldBeDumped)) {
883
- fs7.rmSync(pathToWhereEmailMarkupShouldBeDumped, { recursive: true });
884
- }
885
- let spinner;
886
- if (!options.silent) {
887
- spinner = ora2("Preparing files...\n").start();
888
- registerSpinnerAutostopping(spinner);
889
- }
890
- const emailsDirectoryMetadata = await getEmailsDirectoryMetadata(
891
- path9.resolve(process.cwd(), emailsDirectoryPath),
892
- true
893
- );
894
- if (typeof emailsDirectoryMetadata === "undefined") {
895
- if (spinner) {
896
- spinner.stopAndPersist({
897
- symbol: logSymbols3.error,
898
- text: `Could not find the directory at ${emailsDirectoryPath}`
899
- });
900
- }
901
- return;
902
- }
903
- const allTemplates = getEmailTemplatesFromDirectory(emailsDirectoryMetadata);
904
- try {
905
- await build({
906
- bundle: true,
907
- entryPoints: allTemplates,
908
- plugins: [renderingUtilitiesExporter(allTemplates)],
909
- platform: "node",
910
- format: "cjs",
911
- loader: { ".js": "jsx" },
912
- outExtension: { ".js": ".cjs" },
913
- jsx: "transform",
914
- write: true,
915
- outdir: pathToWhereEmailMarkupShouldBeDumped
916
- });
917
- } catch (exception) {
918
- const buildFailure = exception;
919
- if (spinner) {
920
- spinner.stopAndPersist({
921
- symbol: logSymbols3.error,
922
- text: "Failed to build emails"
923
- });
924
- }
925
- process.exit(1);
926
- }
927
- if (spinner) {
928
- spinner.succeed();
929
- }
930
- const allBuiltTemplates = glob.sync(
931
- normalize(`${pathToWhereEmailMarkupShouldBeDumped}/**/*.cjs`),
932
- {
933
- absolute: true
934
- }
935
- );
936
- for await (const template of allBuiltTemplates) {
937
- try {
938
- if (spinner) {
939
- spinner.text = `rendering ${template.split("/").pop()}`;
940
- spinner.render();
941
- }
942
- delete __require.cache[template];
943
- const emailModule = __require(template);
944
- const rendered = await emailModule.render(
945
- emailModule.reactEmailCreateReactElement(emailModule.default, {}),
946
- options
947
- );
948
- const htmlPath = template.replace(
949
- ".cjs",
950
- options.plainText ? ".txt" : ".html"
951
- );
952
- writeFileSync(htmlPath, rendered);
953
- unlinkSync(template);
954
- } catch (exception) {
955
- if (spinner) {
956
- spinner.stopAndPersist({
957
- symbol: logSymbols3.error,
958
- text: `failed when rendering ${template.split("/").pop()}`
959
- });
960
- }
961
- console.error(exception);
962
- process.exit(1);
963
- }
964
- }
965
- if (spinner) {
966
- spinner.succeed("Rendered all files");
967
- spinner.text = "Copying static files";
968
- spinner.render();
969
- }
970
- const staticDirectoryPath = path9.join(emailsDirectoryPath, "static");
971
- if (fs7.existsSync(staticDirectoryPath)) {
972
- const pathToDumpStaticFilesInto = path9.join(
973
- pathToWhereEmailMarkupShouldBeDumped,
974
- "static"
975
- );
976
- if (fs7.existsSync(pathToDumpStaticFilesInto))
977
- await fs7.promises.rm(pathToDumpStaticFilesInto, { recursive: true });
978
- try {
979
- await fs7.promises.cp(staticDirectoryPath, pathToDumpStaticFilesInto, {
980
- recursive: true
981
- });
982
- } catch (exception) {
983
- console.error(exception);
984
- if (spinner) {
985
- spinner.stopAndPersist({
986
- symbol: logSymbols3.error,
987
- text: "Failed to copy static files"
988
- });
989
- }
990
- console.error(
991
- `Something went wrong while copying the file to ${pathToWhereEmailMarkupShouldBeDumped}/static, ${exception}`
992
- );
993
- process.exit(1);
994
- }
995
- }
996
- if (spinner && !options.silent) {
997
- spinner.succeed();
998
- const fileTree = await tree(pathToWhereEmailMarkupShouldBeDumped, 4);
999
- console.log(fileTree);
1000
- spinner.stopAndPersist({
1001
- symbol: logSymbols3.success,
1002
- text: "Successfully exported emails"
1003
- });
1004
- }
1005
- };
1006
-
1007
797
  // src/cli/commands/build.ts
1008
- import fs8 from "node:fs";
1009
- import path10 from "node:path";
1010
- import ora3 from "ora";
1011
- import { spawn } from "node:child_process";
1012
- import logSymbols4 from "log-symbols";
1013
798
  var buildPreviewApp = (absoluteDirectory) => {
1014
799
  return new Promise((resolve, reject) => {
1015
800
  const nextBuild = spawn("npm", ["run", "build"], {
@@ -1066,8 +851,8 @@ module.exports = {
1066
851
  webpackBuildWorker: true
1067
852
  },
1068
853
  }`;
1069
- await fs8.promises.writeFile(
1070
- path10.resolve(builtPreviewAppPath, "./next.config.js"),
854
+ await fs5.promises.writeFile(
855
+ path8.resolve(builtPreviewAppPath, "./next.config.js"),
1071
856
  nextConfigContents,
1072
857
  "utf8"
1073
858
  );
@@ -1077,7 +862,7 @@ var getEmailSlugsFromEmailDirectory = (emailDirectory, emailsDirectoryAbsolutePa
1077
862
  const slugs = [];
1078
863
  emailDirectory.emailFilenames.forEach(
1079
864
  (filename) => slugs.push(
1080
- path10.join(directoryPathRelativeToEmailsDirectory, filename).split(path10.sep).filter((segment) => segment.length > 0)
865
+ path8.join(directoryPathRelativeToEmailsDirectory, filename).split(path8.sep).filter((segment) => segment.length > 0)
1081
866
  )
1082
867
  );
1083
868
  emailDirectory.subDirectories.forEach((directory) => {
@@ -1100,21 +885,21 @@ var forceSSGForEmailPreviews = async (emailsDirPath, builtPreviewAppPath) => {
1100
885
  emailsDirPath
1101
886
  ).map((slug) => ({ slug }));
1102
887
  const removeForceDynamic = async (filePath) => {
1103
- const contents = await fs8.promises.readFile(filePath, "utf8");
1104
- await fs8.promises.writeFile(
888
+ const contents = await fs5.promises.readFile(filePath, "utf8");
889
+ await fs5.promises.writeFile(
1105
890
  filePath,
1106
891
  contents.replace("export const dynamic = 'force-dynamic';", ""),
1107
892
  "utf8"
1108
893
  );
1109
894
  };
1110
895
  await removeForceDynamic(
1111
- path10.resolve(builtPreviewAppPath, "./src/app/layout.tsx")
896
+ path8.resolve(builtPreviewAppPath, "./src/app/layout.tsx")
1112
897
  );
1113
898
  await removeForceDynamic(
1114
- path10.resolve(builtPreviewAppPath, "./src/app/preview/[...slug]/page.tsx")
899
+ path8.resolve(builtPreviewAppPath, "./src/app/preview/[...slug]/page.tsx")
1115
900
  );
1116
- await fs8.promises.appendFile(
1117
- path10.resolve(builtPreviewAppPath, "./src/app/preview/[...slug]/page.tsx"),
901
+ await fs5.promises.appendFile(
902
+ path8.resolve(builtPreviewAppPath, "./src/app/preview/[...slug]/page.tsx"),
1118
903
  `
1119
904
 
1120
905
  export function generateStaticParams() {
@@ -1126,16 +911,16 @@ export function generateStaticParams() {
1126
911
  );
1127
912
  };
1128
913
  var updatePackageJson = async (builtPreviewAppPath) => {
1129
- const packageJsonPath = path10.resolve(builtPreviewAppPath, "./package.json");
914
+ const packageJsonPath = path8.resolve(builtPreviewAppPath, "./package.json");
1130
915
  const packageJson = JSON.parse(
1131
- await fs8.promises.readFile(packageJsonPath, "utf8")
916
+ await fs5.promises.readFile(packageJsonPath, "utf8")
1132
917
  );
1133
918
  packageJson.scripts.build = "next build";
1134
919
  packageJson.scripts.start = "next start";
1135
920
  packageJson.name = "preview-server";
1136
921
  delete packageJson.devDependencies["@react-email/render"];
1137
922
  delete packageJson.devDependencies["@react-email/components"];
1138
- await fs8.promises.writeFile(
923
+ await fs5.promises.writeFile(
1139
924
  packageJsonPath,
1140
925
  JSON.stringify(packageJson),
1141
926
  "utf8"
@@ -1166,41 +951,41 @@ var npmInstall = async (builtPreviewAppPath, packageManager) => {
1166
951
  });
1167
952
  });
1168
953
  };
1169
- var build2 = async ({
954
+ var build = async ({
1170
955
  dir: emailsDirRelativePath,
1171
956
  packageManager
1172
957
  }) => {
1173
958
  try {
1174
- const spinner = ora3({
959
+ const spinner = ora2({
1175
960
  text: "Starting build process...",
1176
961
  prefixText: " "
1177
962
  }).start();
1178
963
  registerSpinnerAutostopping(spinner);
1179
964
  spinner.text = `Checking if ${emailsDirRelativePath} folder exists`;
1180
- if (!fs8.existsSync(emailsDirRelativePath)) {
965
+ if (!fs5.existsSync(emailsDirRelativePath)) {
1181
966
  process.exit(1);
1182
967
  }
1183
- const emailsDirPath = path10.join(process.cwd(), emailsDirRelativePath);
1184
- const staticPath = path10.join(emailsDirPath, "static");
1185
- const builtPreviewAppPath = path10.join(process.cwd(), ".react-email");
1186
- if (fs8.existsSync(builtPreviewAppPath)) {
968
+ const emailsDirPath = path8.join(process.cwd(), emailsDirRelativePath);
969
+ const staticPath = path8.join(emailsDirPath, "static");
970
+ const builtPreviewAppPath = path8.join(process.cwd(), ".react-email");
971
+ if (fs5.existsSync(builtPreviewAppPath)) {
1187
972
  spinner.text = "Deleting pre-existing `.react-email` folder";
1188
- await fs8.promises.rm(builtPreviewAppPath, { recursive: true });
973
+ await fs5.promises.rm(builtPreviewAppPath, { recursive: true });
1189
974
  }
1190
975
  spinner.text = "Copying preview app from CLI to `.react-email`";
1191
- await fs8.promises.cp(cliPacakgeLocation, builtPreviewAppPath, {
976
+ await fs5.promises.cp(cliPacakgeLocation, builtPreviewAppPath, {
1192
977
  recursive: true,
1193
978
  filter: (source) => {
1194
979
  return !/(\/|\\)cli(\/|\\)?/.test(source) && !/(\/|\\)\.next(\/|\\)?/.test(source) && !/(\/|\\)\.turbo(\/|\\)?/.test(source) && !/(\/|\\)node_modules(\/|\\)?$/.test(source);
1195
980
  }
1196
981
  });
1197
- if (fs8.existsSync(staticPath)) {
982
+ if (fs5.existsSync(staticPath)) {
1198
983
  spinner.text = "Copying `static` folder into `.react-email/public/static`";
1199
- const builtStaticDirectory = path10.resolve(
984
+ const builtStaticDirectory = path8.resolve(
1200
985
  builtPreviewAppPath,
1201
986
  "./public/static"
1202
987
  );
1203
- await fs8.promises.cp(staticPath, builtStaticDirectory, {
988
+ await fs5.promises.cp(staticPath, builtStaticDirectory, {
1204
989
  recursive: true
1205
990
  });
1206
991
  }
@@ -1217,7 +1002,7 @@ var build2 = async ({
1217
1002
  await npmInstall(builtPreviewAppPath, packageManager);
1218
1003
  spinner.stopAndPersist({
1219
1004
  text: "Successfully prepared `.react-email` for `next build`",
1220
- symbol: logSymbols4.success
1005
+ symbol: logSymbols3.success
1221
1006
  });
1222
1007
  await buildPreviewApp(builtPreviewAppPath);
1223
1008
  } catch (error) {
@@ -1226,10 +1011,229 @@ var build2 = async ({
1226
1011
  }
1227
1012
  };
1228
1013
 
1014
+ // src/cli/commands/dev.ts
1015
+ import fs6 from "node:fs";
1016
+ var dev = async ({ dir: emailsDirRelativePath, port }) => {
1017
+ try {
1018
+ if (!fs6.existsSync(emailsDirRelativePath)) {
1019
+ console.error(`Missing ${emailsDirRelativePath} folder`);
1020
+ process.exit(1);
1021
+ }
1022
+ const devServer2 = await startDevServer(
1023
+ emailsDirRelativePath,
1024
+ emailsDirRelativePath,
1025
+ // defaults to ./emails/static for the static files that are served to the preview
1026
+ Number.parseInt(port)
1027
+ );
1028
+ await setupHotreloading(devServer2, emailsDirRelativePath);
1029
+ } catch (error) {
1030
+ console.log(error);
1031
+ process.exit(1);
1032
+ }
1033
+ };
1034
+
1035
+ // src/cli/commands/export.ts
1036
+ import fs8, { unlinkSync, writeFileSync } from "node:fs";
1037
+ import path10 from "node:path";
1038
+ import { build as build2 } from "esbuild";
1039
+ import { glob } from "glob";
1040
+ import logSymbols4 from "log-symbols";
1041
+ import normalize from "normalize-path";
1042
+ import ora3 from "ora";
1043
+
1044
+ // src/utils/esbuild/renderring-utilities-exporter.ts
1045
+ import { promises as fs7 } from "node:fs";
1046
+ import path9 from "node:path";
1047
+
1048
+ // src/utils/esbuild/escape-string-for-regex.ts
1049
+ function escapeStringForRegex(string) {
1050
+ return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
1051
+ }
1052
+
1053
+ // src/utils/esbuild/renderring-utilities-exporter.ts
1054
+ var renderingUtilitiesExporter = (emailTemplates) => ({
1055
+ name: "rendering-utilities-exporter",
1056
+ setup: (b) => {
1057
+ b.onLoad(
1058
+ {
1059
+ filter: new RegExp(
1060
+ emailTemplates.map((emailPath) => escapeStringForRegex(emailPath)).join("|")
1061
+ )
1062
+ },
1063
+ async ({ path: pathToFile }) => {
1064
+ return {
1065
+ contents: `${await fs7.readFile(pathToFile, "utf8")};
1066
+ export { render } from 'react-email-module-that-will-export-render'
1067
+ export { createElement as reactEmailCreateReactElement } from 'react';
1068
+ `,
1069
+ loader: path9.extname(pathToFile).slice(1)
1070
+ };
1071
+ }
1072
+ );
1073
+ b.onResolve(
1074
+ { filter: /^react-email-module-that-will-export-render$/ },
1075
+ async (args) => {
1076
+ const options = {
1077
+ kind: "import-statement",
1078
+ importer: args.importer,
1079
+ resolveDir: args.resolveDir,
1080
+ namespace: args.namespace
1081
+ };
1082
+ let result = await b.resolve("@react-email/render", options);
1083
+ if (result.errors.length === 0) {
1084
+ return result;
1085
+ }
1086
+ result = await b.resolve("@react-email/components", options);
1087
+ if (result.errors.length > 0 && result.errors[0]) {
1088
+ result.errors[0].text = "Failed trying to import `render` from either `@react-email/render` or `@react-email/components` to be able to render your email template.\n Maybe you don't have either of them installed?";
1089
+ }
1090
+ return result;
1091
+ }
1092
+ );
1093
+ }
1094
+ });
1095
+
1096
+ // src/cli/commands/export.ts
1097
+ var getEmailTemplatesFromDirectory = (emailDirectory) => {
1098
+ const templatePaths = [];
1099
+ emailDirectory.emailFilenames.forEach(
1100
+ (filename) => templatePaths.push(path10.join(emailDirectory.absolutePath, filename))
1101
+ );
1102
+ emailDirectory.subDirectories.forEach((directory) => {
1103
+ templatePaths.push(...getEmailTemplatesFromDirectory(directory));
1104
+ });
1105
+ return templatePaths;
1106
+ };
1107
+ var exportTemplates = async (pathToWhereEmailMarkupShouldBeDumped, emailsDirectoryPath, options) => {
1108
+ if (fs8.existsSync(pathToWhereEmailMarkupShouldBeDumped)) {
1109
+ fs8.rmSync(pathToWhereEmailMarkupShouldBeDumped, { recursive: true });
1110
+ }
1111
+ let spinner;
1112
+ if (!options.silent) {
1113
+ spinner = ora3("Preparing files...\n").start();
1114
+ registerSpinnerAutostopping(spinner);
1115
+ }
1116
+ const emailsDirectoryMetadata = await getEmailsDirectoryMetadata(
1117
+ path10.resolve(process.cwd(), emailsDirectoryPath),
1118
+ true
1119
+ );
1120
+ if (typeof emailsDirectoryMetadata === "undefined") {
1121
+ if (spinner) {
1122
+ spinner.stopAndPersist({
1123
+ symbol: logSymbols4.error,
1124
+ text: `Could not find the directory at ${emailsDirectoryPath}`
1125
+ });
1126
+ }
1127
+ return;
1128
+ }
1129
+ const allTemplates = getEmailTemplatesFromDirectory(emailsDirectoryMetadata);
1130
+ try {
1131
+ await build2({
1132
+ bundle: true,
1133
+ entryPoints: allTemplates,
1134
+ plugins: [renderingUtilitiesExporter(allTemplates)],
1135
+ platform: "node",
1136
+ format: "cjs",
1137
+ loader: { ".js": "jsx" },
1138
+ outExtension: { ".js": ".cjs" },
1139
+ jsx: "transform",
1140
+ write: true,
1141
+ outdir: pathToWhereEmailMarkupShouldBeDumped
1142
+ });
1143
+ } catch (exception) {
1144
+ const buildFailure = exception;
1145
+ if (spinner) {
1146
+ spinner.stopAndPersist({
1147
+ symbol: logSymbols4.error,
1148
+ text: "Failed to build emails"
1149
+ });
1150
+ }
1151
+ process.exit(1);
1152
+ }
1153
+ if (spinner) {
1154
+ spinner.succeed();
1155
+ }
1156
+ const allBuiltTemplates = glob.sync(
1157
+ normalize(`${pathToWhereEmailMarkupShouldBeDumped}/**/*.cjs`),
1158
+ {
1159
+ absolute: true
1160
+ }
1161
+ );
1162
+ for await (const template of allBuiltTemplates) {
1163
+ try {
1164
+ if (spinner) {
1165
+ spinner.text = `rendering ${template.split("/").pop()}`;
1166
+ spinner.render();
1167
+ }
1168
+ delete __require.cache[template];
1169
+ const emailModule = __require(template);
1170
+ const rendered = await emailModule.render(
1171
+ emailModule.reactEmailCreateReactElement(emailModule.default, {}),
1172
+ options
1173
+ );
1174
+ const htmlPath = template.replace(
1175
+ ".cjs",
1176
+ options.plainText ? ".txt" : ".html"
1177
+ );
1178
+ writeFileSync(htmlPath, rendered);
1179
+ unlinkSync(template);
1180
+ } catch (exception) {
1181
+ if (spinner) {
1182
+ spinner.stopAndPersist({
1183
+ symbol: logSymbols4.error,
1184
+ text: `failed when rendering ${template.split("/").pop()}`
1185
+ });
1186
+ }
1187
+ console.error(exception);
1188
+ process.exit(1);
1189
+ }
1190
+ }
1191
+ if (spinner) {
1192
+ spinner.succeed("Rendered all files");
1193
+ spinner.text = "Copying static files";
1194
+ spinner.render();
1195
+ }
1196
+ const staticDirectoryPath = path10.join(emailsDirectoryPath, "static");
1197
+ if (fs8.existsSync(staticDirectoryPath)) {
1198
+ const pathToDumpStaticFilesInto = path10.join(
1199
+ pathToWhereEmailMarkupShouldBeDumped,
1200
+ "static"
1201
+ );
1202
+ if (fs8.existsSync(pathToDumpStaticFilesInto))
1203
+ await fs8.promises.rm(pathToDumpStaticFilesInto, { recursive: true });
1204
+ try {
1205
+ await fs8.promises.cp(staticDirectoryPath, pathToDumpStaticFilesInto, {
1206
+ recursive: true
1207
+ });
1208
+ } catch (exception) {
1209
+ console.error(exception);
1210
+ if (spinner) {
1211
+ spinner.stopAndPersist({
1212
+ symbol: logSymbols4.error,
1213
+ text: "Failed to copy static files"
1214
+ });
1215
+ }
1216
+ console.error(
1217
+ `Something went wrong while copying the file to ${pathToWhereEmailMarkupShouldBeDumped}/static, ${exception}`
1218
+ );
1219
+ process.exit(1);
1220
+ }
1221
+ }
1222
+ if (spinner && !options.silent) {
1223
+ spinner.succeed();
1224
+ const fileTree = await tree(pathToWhereEmailMarkupShouldBeDumped, 4);
1225
+ console.log(fileTree);
1226
+ spinner.stopAndPersist({
1227
+ symbol: logSymbols4.success,
1228
+ text: "Successfully exported emails"
1229
+ });
1230
+ }
1231
+ };
1232
+
1229
1233
  // src/cli/commands/start.ts
1234
+ import { spawn as spawn2 } from "node:child_process";
1230
1235
  import fs9 from "node:fs";
1231
1236
  import path11 from "node:path";
1232
- import { spawn as spawn2 } from "node:child_process";
1233
1237
  var start = async () => {
1234
1238
  try {
1235
1239
  const usersProjectLocation = process.cwd();
@@ -1267,7 +1271,7 @@ program.command("build").description("Copies the preview app for onto .react-ema
1267
1271
  "-p --packageManager <name>",
1268
1272
  "Package name to use on installation on `.react-email`",
1269
1273
  "npm"
1270
- ).action(build2);
1274
+ ).action(build);
1271
1275
  program.command("start").description('Runs the built preview app that is inside of ".react-email"').action(start);
1272
1276
  program.command("export").description("Build the templates to the `out` directory").option("--outDir <path>", "Output directory", "out").option("-p, --pretty", "Pretty print the output", false).option("-t, --plainText", "Set output format as plain text", false).option("-d, --dir <path>", "Directory with your email templates", "./emails").option(
1273
1277
  "-s, --silent",