react-email 4.2.10 → 4.2.12

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.
package/dist/index.js CHANGED
@@ -1,296 +1,244 @@
1
1
  #!/usr/bin/env node
2
-
3
- // src/index.ts
2
+ import { createRequire } from "node:module";
4
3
  import { program } from "commander";
5
-
6
- // src/commands/build.ts
7
4
  import { spawn } from "node:child_process";
8
- import fs2 from "node:fs";
9
- import path3 from "node:path";
10
- import logSymbols2 from "log-symbols";
11
- import ora from "ora";
12
-
13
- // src/utils/get-emails-directory-metadata.ts
14
- import fs from "node:fs";
5
+ import fs, { existsSync, promises, statSync, unlinkSync, writeFileSync } from "node:fs";
15
6
  import path from "node:path";
16
- var isFileAnEmail = async (fullPath) => {
17
- let fileHandle;
18
- try {
19
- fileHandle = await fs.promises.open(fullPath, "r");
20
- } catch (exception) {
21
- console.warn(exception);
22
- return false;
23
- }
24
- const stat = await fileHandle.stat();
25
- if (stat.isDirectory()) {
26
- await fileHandle.close();
27
- return false;
28
- }
29
- const { ext } = path.parse(fullPath);
30
- if (![".js", ".tsx", ".jsx"].includes(ext)) {
31
- await fileHandle.close();
32
- return false;
33
- }
34
- const fileContents = await fileHandle.readFile("utf8");
35
- await fileHandle.close();
36
- const hasES6DefaultExport = /\bexport\s+default\b/gm.test(fileContents);
37
- const hasCommonJSExport = /\bmodule\.exports\s*=/gm.test(fileContents);
38
- const hasNamedExport = /\bexport\s+\{[^}]*\bdefault\b[^}]*\}/gm.test(
39
- fileContents
40
- );
41
- return hasES6DefaultExport || hasCommonJSExport || hasNamedExport;
42
- };
43
- var mergeDirectoriesWithSubDirectories = (emailsDirectoryMetadata) => {
44
- let currentResultingMergedDirectory = emailsDirectoryMetadata;
45
- while (currentResultingMergedDirectory.emailFilenames.length === 0 && currentResultingMergedDirectory.subDirectories.length === 1) {
46
- const onlySubDirectory = currentResultingMergedDirectory.subDirectories[0];
47
- currentResultingMergedDirectory = {
48
- ...onlySubDirectory,
49
- directoryName: path.join(
50
- currentResultingMergedDirectory.directoryName,
51
- onlySubDirectory.directoryName
52
- )
53
- };
54
- }
55
- return currentResultingMergedDirectory;
56
- };
57
- var getEmailsDirectoryMetadata = async (absolutePathToEmailsDirectory, keepFileExtensions = false, isSubDirectory = false, baseDirectoryPath = absolutePathToEmailsDirectory) => {
58
- if (!fs.existsSync(absolutePathToEmailsDirectory)) return;
59
- const dirents = await fs.promises.readdir(absolutePathToEmailsDirectory, {
60
- withFileTypes: true
61
- });
62
- const isEmailPredicates = await Promise.all(
63
- dirents.map(
64
- (dirent) => isFileAnEmail(path.join(absolutePathToEmailsDirectory, dirent.name))
65
- )
66
- );
67
- const emailFilenames = dirents.filter((_, i) => isEmailPredicates[i]).map(
68
- (dirent) => keepFileExtensions ? dirent.name : dirent.name.replace(path.extname(dirent.name), "")
69
- );
70
- const subDirectories = await Promise.all(
71
- dirents.filter(
72
- (dirent) => dirent.isDirectory() && !dirent.name.startsWith("_") && dirent.name !== "static"
73
- ).map((dirent) => {
74
- const direntAbsolutePath = path.join(
75
- absolutePathToEmailsDirectory,
76
- dirent.name
77
- );
78
- return getEmailsDirectoryMetadata(
79
- direntAbsolutePath,
80
- keepFileExtensions,
81
- true,
82
- baseDirectoryPath
83
- );
84
- })
85
- );
86
- const emailsMetadata = {
87
- absolutePath: absolutePathToEmailsDirectory,
88
- relativePath: path.relative(
89
- baseDirectoryPath,
90
- absolutePathToEmailsDirectory
91
- ),
92
- directoryName: absolutePathToEmailsDirectory.split(path.sep).pop(),
93
- emailFilenames,
94
- subDirectories
95
- };
96
- return isSubDirectory ? mergeDirectoriesWithSubDirectories(emailsMetadata) : emailsMetadata;
97
- };
98
-
99
- // src/utils/get-preview-server-location.ts
100
- import path2 from "node:path";
7
+ import logSymbols from "log-symbols";
8
+ import ora from "ora";
101
9
  import url from "node:url";
102
10
  import { createJiti } from "jiti";
103
11
  import { addDevDependency } from "nypm";
104
12
  import prompts from "prompts";
13
+ import { watch } from "chokidar";
14
+ import debounce from "debounce";
15
+ import { Server } from "socket.io";
16
+ import { parse } from "@babel/parser";
17
+ import traverseModule from "@babel/traverse";
18
+ import { createMatchPath, loadConfig } from "tsconfig-paths";
19
+ import http from "node:http";
20
+ import { styleText } from "node:util";
21
+ import { lookup } from "mime-types";
22
+ import os from "node:os";
23
+ import { build } from "esbuild";
24
+ import { glob } from "glob";
25
+ import normalize from "normalize-path";
105
26
 
106
- // package.json
27
+ //#region src/utils/get-emails-directory-metadata.ts
28
+ const isFileAnEmail = async (fullPath) => {
29
+ let fileHandle;
30
+ try {
31
+ fileHandle = await fs.promises.open(fullPath, "r");
32
+ } catch (exception) {
33
+ console.warn(exception);
34
+ return false;
35
+ }
36
+ if ((await fileHandle.stat()).isDirectory()) {
37
+ await fileHandle.close();
38
+ return false;
39
+ }
40
+ const { ext } = path.parse(fullPath);
41
+ if (![
42
+ ".js",
43
+ ".tsx",
44
+ ".jsx"
45
+ ].includes(ext)) {
46
+ await fileHandle.close();
47
+ return false;
48
+ }
49
+ const fileContents = await fileHandle.readFile("utf8");
50
+ await fileHandle.close();
51
+ const hasES6DefaultExport = /\bexport\s+default\b/gm.test(fileContents);
52
+ const hasCommonJSExport = /\bmodule\.exports\s*=/gm.test(fileContents);
53
+ const hasNamedExport = /\bexport\s+\{[^}]*\bdefault\b[^}]*\}/gm.test(fileContents);
54
+ return hasES6DefaultExport || hasCommonJSExport || hasNamedExport;
55
+ };
56
+ const mergeDirectoriesWithSubDirectories = (emailsDirectoryMetadata) => {
57
+ let currentResultingMergedDirectory = emailsDirectoryMetadata;
58
+ while (currentResultingMergedDirectory.emailFilenames.length === 0 && currentResultingMergedDirectory.subDirectories.length === 1) {
59
+ const onlySubDirectory = currentResultingMergedDirectory.subDirectories[0];
60
+ currentResultingMergedDirectory = {
61
+ ...onlySubDirectory,
62
+ directoryName: path.join(currentResultingMergedDirectory.directoryName, onlySubDirectory.directoryName)
63
+ };
64
+ }
65
+ return currentResultingMergedDirectory;
66
+ };
67
+ const getEmailsDirectoryMetadata = async (absolutePathToEmailsDirectory, keepFileExtensions = false, isSubDirectory = false, baseDirectoryPath = absolutePathToEmailsDirectory) => {
68
+ if (!fs.existsSync(absolutePathToEmailsDirectory)) return;
69
+ const dirents = await fs.promises.readdir(absolutePathToEmailsDirectory, { withFileTypes: true });
70
+ const isEmailPredicates = await Promise.all(dirents.map((dirent) => isFileAnEmail(path.join(absolutePathToEmailsDirectory, dirent.name))));
71
+ const emailFilenames = dirents.filter((_, i) => isEmailPredicates[i]).map((dirent) => keepFileExtensions ? dirent.name : dirent.name.replace(path.extname(dirent.name), ""));
72
+ const subDirectories = await Promise.all(dirents.filter((dirent) => dirent.isDirectory() && !dirent.name.startsWith("_") && dirent.name !== "static").map((dirent) => {
73
+ const direntAbsolutePath = path.join(absolutePathToEmailsDirectory, dirent.name);
74
+ return getEmailsDirectoryMetadata(direntAbsolutePath, keepFileExtensions, true, baseDirectoryPath);
75
+ }));
76
+ const emailsMetadata = {
77
+ absolutePath: absolutePathToEmailsDirectory,
78
+ relativePath: path.relative(baseDirectoryPath, absolutePathToEmailsDirectory),
79
+ directoryName: absolutePathToEmailsDirectory.split(path.sep).pop(),
80
+ emailFilenames,
81
+ subDirectories
82
+ };
83
+ return isSubDirectory ? mergeDirectoriesWithSubDirectories(emailsMetadata) : emailsMetadata;
84
+ };
85
+
86
+ //#endregion
87
+ //#region package.json
88
+ var name = "react-email";
89
+ var version = "4.2.12";
90
+ var description = "A live preview of your emails right in your browser.";
91
+ var bin = { "email": "./dist/index.js" };
92
+ var type = "module";
93
+ var scripts = {
94
+ "build": "tsdown",
95
+ "build:watch": "tsdown --watch src",
96
+ "clean": "rm -rf dist",
97
+ "test": "vitest run",
98
+ "test:watch": "vitest"
99
+ };
100
+ var license = "MIT";
101
+ var repository = {
102
+ "type": "git",
103
+ "url": "https://github.com/resend/react-email.git",
104
+ "directory": "packages/react-email"
105
+ };
106
+ var keywords = ["react", "email"];
107
+ var engines = { "node": ">=18.0.0" };
108
+ var dependencies = {
109
+ "@babel/parser": "^7.27.0",
110
+ "@babel/traverse": "^7.27.0",
111
+ "chokidar": "^4.0.3",
112
+ "commander": "^13.0.0",
113
+ "debounce": "^2.0.0",
114
+ "esbuild": "^0.25.0",
115
+ "glob": "^11.0.0",
116
+ "jiti": "2.4.2",
117
+ "log-symbols": "^7.0.0",
118
+ "mime-types": "^3.0.0",
119
+ "normalize-path": "^3.0.0",
120
+ "nypm": "0.6.0",
121
+ "ora": "^8.0.0",
122
+ "prompts": "2.4.2",
123
+ "socket.io": "^4.8.1",
124
+ "tsconfig-paths": "4.2.0"
125
+ };
126
+ var devDependencies = {
127
+ "@react-email/components": "workspace:*",
128
+ "@types/babel__core": "7.20.5",
129
+ "@types/babel__traverse": "7.20.7",
130
+ "@types/mime-types": "2.1.4",
131
+ "@types/prompts": "2.4.9",
132
+ "next": "^15.3.2",
133
+ "react": "19.0.0",
134
+ "react-dom": "19.0.0",
135
+ "typescript": "5.8.3"
136
+ };
107
137
  var package_default = {
108
- name: "react-email",
109
- version: "4.2.10",
110
- description: "A live preview of your emails right in your browser.",
111
- bin: {
112
- email: "./dist/index.js"
113
- },
114
- type: "module",
115
- scripts: {
116
- build: "tsup-node",
117
- "build:watch": "tsup-node --watch src",
118
- clean: "rm -rf dist",
119
- test: "vitest run",
120
- "test:watch": "vitest"
121
- },
122
- license: "MIT",
123
- repository: {
124
- type: "git",
125
- url: "https://github.com/resend/react-email.git",
126
- directory: "packages/react-email"
127
- },
128
- keywords: [
129
- "react",
130
- "email"
131
- ],
132
- engines: {
133
- node: ">=18.0.0"
134
- },
135
- dependencies: {
136
- "@babel/parser": "^7.27.0",
137
- "@babel/traverse": "^7.27.0",
138
- chokidar: "^4.0.3",
139
- commander: "^13.0.0",
140
- debounce: "^2.0.0",
141
- esbuild: "^0.25.0",
142
- glob: "^11.0.0",
143
- jiti: "2.4.2",
144
- "log-symbols": "^7.0.0",
145
- "mime-types": "^3.0.0",
146
- "normalize-path": "^3.0.0",
147
- nypm: "0.6.0",
148
- ora: "^8.0.0",
149
- prompts: "2.4.2",
150
- "socket.io": "^4.8.1",
151
- "tsconfig-paths": "4.2.0"
152
- },
153
- devDependencies: {
154
- "@react-email/components": "workspace:*",
155
- "@types/babel__core": "7.20.5",
156
- "@types/babel__traverse": "7.20.7",
157
- "@types/mime-types": "2.1.4",
158
- "@types/prompts": "2.4.9",
159
- next: "^15.3.2",
160
- react: "19.0.0",
161
- "react-dom": "19.0.0",
162
- tsup: "8.4.0",
163
- typescript: "5.8.3"
164
- }
138
+ name,
139
+ version,
140
+ description,
141
+ bin,
142
+ type,
143
+ scripts,
144
+ license,
145
+ repository,
146
+ keywords,
147
+ engines,
148
+ dependencies,
149
+ devDependencies
165
150
  };
166
151
 
167
- // src/utils/get-preview-server-location.ts
168
- var ensurePreviewServerInstalled = async (message) => {
169
- const response = await prompts({
170
- type: "confirm",
171
- name: "installPreviewServer",
172
- message,
173
- initial: true
174
- });
175
- if (response.installPreviewServer) {
176
- console.log('Installing "@react-email/preview-server"');
177
- await addDevDependency(
178
- `@react-email/preview-server@${package_default.version}`
179
- );
180
- process.exit(0);
181
- } else {
182
- process.exit(0);
183
- }
152
+ //#endregion
153
+ //#region src/utils/get-preview-server-location.ts
154
+ const ensurePreviewServerInstalled = async (message) => {
155
+ if ((await prompts({
156
+ type: "confirm",
157
+ name: "installPreviewServer",
158
+ message,
159
+ initial: true
160
+ })).installPreviewServer) {
161
+ console.log("Installing \"@react-email/preview-server\"");
162
+ await addDevDependency(`@react-email/preview-server@${package_default.version}`);
163
+ process.exit(0);
164
+ } else process.exit(0);
184
165
  };
185
- var getPreviewServerLocation = async () => {
186
- const usersProject = createJiti(process.cwd());
187
- let previewServerLocation;
188
- try {
189
- previewServerLocation = path2.dirname(
190
- url.fileURLToPath(usersProject.esmResolve("@react-email/preview-server"))
191
- );
192
- } catch (_exception) {
193
- await ensurePreviewServerInstalled(
194
- 'To run the preview server, the package "@react-email/preview-server" must be installed. Would you like to install it?'
195
- );
196
- }
197
- const { version } = await usersProject.import("@react-email/preview-server");
198
- if (version !== package_default.version) {
199
- await ensurePreviewServerInstalled(
200
- `To run the preview server, the version of "@react-email/preview-server" must match the version of "react-email" (${package_default.version}). Would you like to install it?`
201
- );
202
- }
203
- return previewServerLocation;
166
+ const getPreviewServerLocation = async () => {
167
+ const usersProject = createJiti(process.cwd());
168
+ let previewServerLocation;
169
+ try {
170
+ previewServerLocation = path.dirname(url.fileURLToPath(usersProject.esmResolve("@react-email/preview-server")));
171
+ } catch (_exception) {
172
+ await ensurePreviewServerInstalled("To run the preview server, the package \"@react-email/preview-server\" must be installed. Would you like to install it?");
173
+ }
174
+ const { version: version$1 } = await usersProject.import("@react-email/preview-server");
175
+ if (version$1 !== package_default.version) await ensurePreviewServerInstalled(`To run the preview server, the version of "@react-email/preview-server" must match the version of "react-email" (${package_default.version}). Would you like to install it?`);
176
+ return previewServerLocation;
204
177
  };
205
178
 
206
- // src/utils/register-spinner-autostopping.ts
207
- import logSymbols from "log-symbols";
208
- var spinners = /* @__PURE__ */ new Set();
179
+ //#endregion
180
+ //#region src/utils/register-spinner-autostopping.ts
181
+ const spinners = /* @__PURE__ */ new Set();
209
182
  process.on("SIGINT", () => {
210
- spinners.forEach((spinner) => {
211
- if (spinner.isSpinning) {
212
- spinner.stop();
213
- }
214
- });
183
+ spinners.forEach((spinner) => {
184
+ if (spinner.isSpinning) spinner.stop();
185
+ });
215
186
  });
216
187
  process.on("exit", (code) => {
217
- if (code !== 0) {
218
- spinners.forEach((spinner) => {
219
- if (spinner.isSpinning) {
220
- spinner.stopAndPersist({
221
- symbol: logSymbols.error
222
- });
223
- }
224
- });
225
- }
188
+ if (code !== 0) spinners.forEach((spinner) => {
189
+ if (spinner.isSpinning) spinner.stopAndPersist({ symbol: logSymbols.error });
190
+ });
226
191
  });
227
- var registerSpinnerAutostopping = (spinner) => {
228
- spinners.add(spinner);
192
+ const registerSpinnerAutostopping = (spinner) => {
193
+ spinners.add(spinner);
229
194
  };
230
195
 
231
- // src/commands/build.ts
232
- var buildPreviewApp = (absoluteDirectory) => {
233
- return new Promise((resolve, reject) => {
234
- const nextBuild = spawn("npm", ["run", "build"], {
235
- cwd: absoluteDirectory,
236
- shell: true
237
- });
238
- nextBuild.stdout.pipe(process.stdout);
239
- nextBuild.stderr.pipe(process.stderr);
240
- nextBuild.on("close", (code) => {
241
- if (code === 0) {
242
- resolve();
243
- } else {
244
- reject(
245
- new Error(
246
- `Unable to build the Next app and it exited with code: ${code}`
247
- )
248
- );
249
- }
250
- });
251
- });
196
+ //#endregion
197
+ //#region src/commands/build.ts
198
+ const buildPreviewApp = (absoluteDirectory) => {
199
+ return new Promise((resolve, reject) => {
200
+ const nextBuild = spawn("npm", ["run", "build"], {
201
+ cwd: absoluteDirectory,
202
+ shell: true
203
+ });
204
+ nextBuild.stdout.pipe(process.stdout);
205
+ nextBuild.stderr.pipe(process.stderr);
206
+ nextBuild.on("close", (code) => {
207
+ if (code === 0) resolve();
208
+ else reject(/* @__PURE__ */ new Error(`Unable to build the Next app and it exited with code: ${code}`));
209
+ });
210
+ });
252
211
  };
253
- var npmInstall = async (builtPreviewAppPath, packageManager) => {
254
- return new Promise((resolve, reject) => {
255
- const childProc = spawn(
256
- packageManager,
257
- [
258
- "install",
259
- packageManager === "deno" ? "" : "--include=dev",
260
- packageManager === "deno" ? "--quiet" : "--silent"
261
- ],
262
- {
263
- cwd: builtPreviewAppPath,
264
- shell: true
265
- }
266
- );
267
- childProc.stdout.pipe(process.stdout);
268
- childProc.stderr.pipe(process.stderr);
269
- childProc.on("close", (code) => {
270
- if (code === 0) {
271
- resolve();
272
- } else {
273
- reject(
274
- new Error(
275
- `Unable to install the dependencies and it exited with code: ${code}`
276
- )
277
- );
278
- }
279
- });
280
- });
212
+ const npmInstall = async (builtPreviewAppPath, packageManager) => {
213
+ return new Promise((resolve, reject) => {
214
+ const childProc = spawn(packageManager, [
215
+ "install",
216
+ packageManager === "deno" ? "" : "--include=dev",
217
+ packageManager === "deno" ? "--quiet" : "--silent"
218
+ ], {
219
+ cwd: builtPreviewAppPath,
220
+ shell: true
221
+ });
222
+ childProc.stdout.pipe(process.stdout);
223
+ childProc.stderr.pipe(process.stderr);
224
+ childProc.on("close", (code) => {
225
+ if (code === 0) resolve();
226
+ else reject(/* @__PURE__ */ new Error(`Unable to install the dependencies and it exited with code: ${code}`));
227
+ });
228
+ });
281
229
  };
282
- var setNextEnvironmentVariablesForBuild = async (emailsDirRelativePath, builtPreviewAppPath) => {
283
- const nextConfigContents = `
230
+ const setNextEnvironmentVariablesForBuild = async (emailsDirRelativePath, builtPreviewAppPath) => {
231
+ const nextConfigContents = `
284
232
  const path = require('path');
285
233
  const emailsDirRelativePath = path.normalize('${emailsDirRelativePath}');
286
- const userProjectLocation = '${process.cwd()}';
234
+ const userProjectLocation = '${process.cwd().replace(/\\/g, "/")}';
287
235
  /** @type {import('next').NextConfig} */
288
236
  module.exports = {
289
237
  env: {
290
238
  NEXT_PUBLIC_IS_BUILDING: 'true',
291
239
  EMAILS_DIR_RELATIVE_PATH: emailsDirRelativePath,
292
240
  EMAILS_DIR_ABSOLUTE_PATH: path.resolve(userProjectLocation, emailsDirRelativePath),
293
- PREVIEW_SERVER_LOCATION: '${builtPreviewAppPath}',
241
+ PREVIEW_SERVER_LOCATION: '${builtPreviewAppPath.replace(/\\/g, "/")}',
294
242
  USER_PROJECT_LOCATION: userProjectLocation
295
243
  },
296
244
  // this is needed so that the code for building emails works properly
@@ -315,1114 +263,776 @@ module.exports = {
315
263
  webpackBuildWorker: true
316
264
  },
317
265
  }`;
318
- await fs2.promises.writeFile(
319
- path3.resolve(builtPreviewAppPath, "./next.config.js"),
320
- nextConfigContents,
321
- "utf8"
322
- );
266
+ await fs.promises.writeFile(path.resolve(builtPreviewAppPath, "./next.config.js"), nextConfigContents, "utf8");
323
267
  };
324
- var getEmailSlugsFromEmailDirectory = (emailDirectory, emailsDirectoryAbsolutePath) => {
325
- const directoryPathRelativeToEmailsDirectory = emailDirectory.absolutePath.replace(emailsDirectoryAbsolutePath, "").trim();
326
- const slugs = [];
327
- emailDirectory.emailFilenames.forEach(
328
- (filename2) => slugs.push(
329
- path3.join(directoryPathRelativeToEmailsDirectory, filename2).split(path3.sep).filter((segment) => segment.length > 0)
330
- )
331
- );
332
- emailDirectory.subDirectories.forEach((directory) => {
333
- slugs.push(
334
- ...getEmailSlugsFromEmailDirectory(
335
- directory,
336
- emailsDirectoryAbsolutePath
337
- )
338
- );
339
- });
340
- return slugs;
268
+ const getEmailSlugsFromEmailDirectory = (emailDirectory, emailsDirectoryAbsolutePath) => {
269
+ const directoryPathRelativeToEmailsDirectory = emailDirectory.absolutePath.replace(emailsDirectoryAbsolutePath, "").trim();
270
+ const slugs = [];
271
+ emailDirectory.emailFilenames.forEach((filename$1) => slugs.push(path.join(directoryPathRelativeToEmailsDirectory, filename$1).split(path.sep).filter((segment) => segment.length > 0)));
272
+ emailDirectory.subDirectories.forEach((directory) => {
273
+ slugs.push(...getEmailSlugsFromEmailDirectory(directory, emailsDirectoryAbsolutePath));
274
+ });
275
+ return slugs;
341
276
  };
342
- var forceSSGForEmailPreviews = async (emailsDirPath, builtPreviewAppPath) => {
343
- const emailDirectoryMetadata = (
344
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
345
- await getEmailsDirectoryMetadata(emailsDirPath)
346
- );
347
- const parameters = getEmailSlugsFromEmailDirectory(
348
- emailDirectoryMetadata,
349
- emailsDirPath
350
- ).map((slug) => ({ slug }));
351
- const removeForceDynamic = async (filePath) => {
352
- const contents = await fs2.promises.readFile(filePath, "utf8");
353
- await fs2.promises.writeFile(
354
- filePath,
355
- contents.replace("export const dynamic = 'force-dynamic';", ""),
356
- "utf8"
357
- );
358
- };
359
- await removeForceDynamic(
360
- path3.resolve(builtPreviewAppPath, "./src/app/layout.tsx")
361
- );
362
- await removeForceDynamic(
363
- path3.resolve(builtPreviewAppPath, "./src/app/preview/[...slug]/page.tsx")
364
- );
365
- await fs2.promises.appendFile(
366
- path3.resolve(builtPreviewAppPath, "./src/app/preview/[...slug]/page.tsx"),
367
- `
277
+ const forceSSGForEmailPreviews = async (emailsDirPath, builtPreviewAppPath) => {
278
+ const emailDirectoryMetadata = await getEmailsDirectoryMetadata(emailsDirPath);
279
+ const parameters = getEmailSlugsFromEmailDirectory(emailDirectoryMetadata, emailsDirPath).map((slug) => ({ slug }));
280
+ const removeForceDynamic = async (filePath) => {
281
+ const contents = await fs.promises.readFile(filePath, "utf8");
282
+ await fs.promises.writeFile(filePath, contents.replace("export const dynamic = 'force-dynamic';", ""), "utf8");
283
+ };
284
+ await removeForceDynamic(path.resolve(builtPreviewAppPath, "./src/app/layout.tsx"));
285
+ await removeForceDynamic(path.resolve(builtPreviewAppPath, "./src/app/preview/[...slug]/page.tsx"));
286
+ await fs.promises.appendFile(path.resolve(builtPreviewAppPath, "./src/app/preview/[...slug]/page.tsx"), `
368
287
 
369
288
  export function generateStaticParams() {
370
289
  return Promise.resolve(
371
290
  ${JSON.stringify(parameters)}
372
291
  );
373
- }`,
374
- "utf8"
375
- );
292
+ }`, "utf8");
376
293
  };
377
- var updatePackageJson = async (builtPreviewAppPath) => {
378
- const packageJsonPath = path3.resolve(builtPreviewAppPath, "./package.json");
379
- const packageJson = JSON.parse(
380
- await fs2.promises.readFile(packageJsonPath, "utf8")
381
- );
382
- packageJson.scripts.build = "next build";
383
- packageJson.scripts.start = "next start";
384
- delete packageJson.scripts.postbuild;
385
- packageJson.name = "preview-server";
386
- delete packageJson.devDependencies["@react-email/render"];
387
- delete packageJson.devDependencies["@react-email/components"];
388
- delete packageJson.scripts.prepare;
389
- await fs2.promises.writeFile(
390
- packageJsonPath,
391
- JSON.stringify(packageJson),
392
- "utf8"
393
- );
294
+ const updatePackageJson = async (builtPreviewAppPath) => {
295
+ const packageJsonPath = path.resolve(builtPreviewAppPath, "./package.json");
296
+ const packageJson = JSON.parse(await fs.promises.readFile(packageJsonPath, "utf8"));
297
+ packageJson.scripts.build = "next build";
298
+ packageJson.scripts.start = "next start";
299
+ delete packageJson.scripts.postbuild;
300
+ packageJson.name = "preview-server";
301
+ delete packageJson.devDependencies["@react-email/render"];
302
+ delete packageJson.devDependencies["@react-email/components"];
303
+ delete packageJson.scripts.prepare;
304
+ await fs.promises.writeFile(packageJsonPath, JSON.stringify(packageJson), "utf8");
394
305
  };
395
- var build = async ({
396
- dir: emailsDirRelativePath,
397
- packageManager
398
- }) => {
399
- try {
400
- const previewServerLocation = await getPreviewServerLocation();
401
- const spinner = ora({
402
- text: "Starting build process...",
403
- prefixText: " "
404
- }).start();
405
- registerSpinnerAutostopping(spinner);
406
- spinner.text = `Checking if ${emailsDirRelativePath} folder exists`;
407
- if (!fs2.existsSync(emailsDirRelativePath)) {
408
- process.exit(1);
409
- }
410
- const emailsDirPath = path3.join(process.cwd(), emailsDirRelativePath);
411
- const staticPath = path3.join(emailsDirPath, "static");
412
- const builtPreviewAppPath = path3.join(process.cwd(), ".react-email");
413
- if (fs2.existsSync(builtPreviewAppPath)) {
414
- spinner.text = "Deleting pre-existing `.react-email` folder";
415
- await fs2.promises.rm(builtPreviewAppPath, { recursive: true });
416
- }
417
- spinner.text = "Copying preview app from CLI to `.react-email`";
418
- await fs2.promises.cp(previewServerLocation, builtPreviewAppPath, {
419
- recursive: true,
420
- filter: (source) => {
421
- return !/(\/|\\)cli(\/|\\)?/.test(source) && !/(\/|\\)\.next(\/|\\)?/.test(source) && !/(\/|\\)\.turbo(\/|\\)?/.test(source) && !/(\/|\\)node_modules(\/|\\)?$/.test(source);
422
- }
423
- });
424
- if (fs2.existsSync(staticPath)) {
425
- spinner.text = "Copying `static` folder into `.react-email/public/static`";
426
- const builtStaticDirectory = path3.resolve(
427
- builtPreviewAppPath,
428
- "./public/static"
429
- );
430
- await fs2.promises.cp(staticPath, builtStaticDirectory, {
431
- recursive: true
432
- });
433
- }
434
- spinner.text = "Setting Next environment variables for preview app to work properly";
435
- await setNextEnvironmentVariablesForBuild(
436
- emailsDirRelativePath,
437
- builtPreviewAppPath
438
- );
439
- spinner.text = "Setting server side generation for the email preview pages";
440
- await forceSSGForEmailPreviews(emailsDirPath, builtPreviewAppPath);
441
- spinner.text = "Updating package.json's build and start scripts";
442
- await updatePackageJson(builtPreviewAppPath);
443
- spinner.text = "Installing dependencies on `.react-email`";
444
- await npmInstall(builtPreviewAppPath, packageManager);
445
- spinner.stopAndPersist({
446
- text: "Successfully prepared `.react-email` for `next build`",
447
- symbol: logSymbols2.success
448
- });
449
- await buildPreviewApp(builtPreviewAppPath);
450
- } catch (error) {
451
- console.log(error);
452
- process.exit(1);
453
- }
306
+ const build$1 = async ({ dir: emailsDirRelativePath, packageManager }) => {
307
+ try {
308
+ const previewServerLocation = await getPreviewServerLocation();
309
+ const spinner = ora({
310
+ text: "Starting build process...",
311
+ prefixText: " "
312
+ }).start();
313
+ registerSpinnerAutostopping(spinner);
314
+ spinner.text = `Checking if ${emailsDirRelativePath} folder exists`;
315
+ if (!fs.existsSync(emailsDirRelativePath)) process.exit(1);
316
+ const emailsDirPath = path.join(process.cwd(), emailsDirRelativePath);
317
+ const staticPath = path.join(emailsDirPath, "static");
318
+ const builtPreviewAppPath = path.join(process.cwd(), ".react-email");
319
+ if (fs.existsSync(builtPreviewAppPath)) {
320
+ spinner.text = "Deleting pre-existing `.react-email` folder";
321
+ await fs.promises.rm(builtPreviewAppPath, { recursive: true });
322
+ }
323
+ spinner.text = "Copying preview app from CLI to `.react-email`";
324
+ await fs.promises.cp(previewServerLocation, builtPreviewAppPath, {
325
+ recursive: true,
326
+ filter: (source) => {
327
+ return !/(\/|\\)cli(\/|\\)?/.test(source) && !/(\/|\\)\.next(\/|\\)?/.test(source) && !/(\/|\\)\.turbo(\/|\\)?/.test(source) && !/(\/|\\)node_modules(\/|\\)?$/.test(source);
328
+ }
329
+ });
330
+ if (fs.existsSync(staticPath)) {
331
+ spinner.text = "Copying `static` folder into `.react-email/public/static`";
332
+ const builtStaticDirectory = path.resolve(builtPreviewAppPath, "./public/static");
333
+ await fs.promises.cp(staticPath, builtStaticDirectory, { recursive: true });
334
+ }
335
+ spinner.text = "Setting Next environment variables for preview app to work properly";
336
+ await setNextEnvironmentVariablesForBuild(emailsDirRelativePath, builtPreviewAppPath);
337
+ spinner.text = "Setting server side generation for the email preview pages";
338
+ await forceSSGForEmailPreviews(emailsDirPath, builtPreviewAppPath);
339
+ spinner.text = "Updating package.json's build and start scripts";
340
+ await updatePackageJson(builtPreviewAppPath);
341
+ spinner.text = "Installing dependencies on `.react-email`";
342
+ await npmInstall(builtPreviewAppPath, packageManager);
343
+ spinner.stopAndPersist({
344
+ text: "Successfully prepared `.react-email` for `next build`",
345
+ symbol: logSymbols.success
346
+ });
347
+ await buildPreviewApp(builtPreviewAppPath);
348
+ } catch (error) {
349
+ console.log(error);
350
+ process.exit(1);
351
+ }
454
352
  };
455
353
 
456
- // src/commands/dev.ts
457
- import fs6 from "node:fs";
458
-
459
- // src/utils/preview/hot-reloading/setup-hot-reloading.ts
460
- import path6 from "node:path";
461
- import { watch } from "chokidar";
462
- import debounce from "debounce";
463
- import { Server as SocketServer } from "socket.io";
464
-
465
- // src/utils/preview/hot-reloading/create-dependency-graph.ts
466
- import { existsSync, promises as fs3, statSync } from "node:fs";
467
- import path5 from "node:path";
468
-
469
- // src/utils/preview/hot-reloading/get-imported-modules.ts
470
- import { parse } from "@babel/parser";
471
- import traverseModule from "@babel/traverse";
472
- var traverse = (
473
- // we keep this check here so that this still works with the dev:preview
474
- // script's use of tsx
475
- typeof traverseModule === "function" ? traverseModule : traverseModule.default
476
- );
477
- var getImportedModules = (contents) => {
478
- const importedPaths = [];
479
- const parsedContents = parse(contents, {
480
- sourceType: "unambiguous",
481
- strictMode: false,
482
- errorRecovery: true,
483
- plugins: ["jsx", "typescript", "decorators"]
484
- });
485
- traverse(parsedContents, {
486
- ImportDeclaration({ node }) {
487
- importedPaths.push(node.source.value);
488
- },
489
- ExportAllDeclaration({ node }) {
490
- importedPaths.push(node.source.value);
491
- },
492
- ExportNamedDeclaration({ node }) {
493
- if (node.source) {
494
- importedPaths.push(node.source.value);
495
- }
496
- },
497
- TSExternalModuleReference({ node }) {
498
- importedPaths.push(node.expression.value);
499
- },
500
- CallExpression({ node }) {
501
- if ("name" in node.callee && node.callee.name === "require") {
502
- if (node.arguments.length === 1) {
503
- const importPathNode = node.arguments[0];
504
- if (importPathNode.type === "StringLiteral") {
505
- importedPaths.push(importPathNode.value);
506
- }
507
- }
508
- }
509
- }
510
- });
511
- return importedPaths;
354
+ //#endregion
355
+ //#region src/utils/preview/hot-reloading/get-imported-modules.ts
356
+ const traverse = typeof traverseModule === "function" ? traverseModule : traverseModule.default;
357
+ const getImportedModules = (contents) => {
358
+ const importedPaths = [];
359
+ const parsedContents = parse(contents, {
360
+ sourceType: "unambiguous",
361
+ strictMode: false,
362
+ errorRecovery: true,
363
+ plugins: [
364
+ "jsx",
365
+ "typescript",
366
+ "decorators"
367
+ ]
368
+ });
369
+ traverse(parsedContents, {
370
+ ImportDeclaration({ node }) {
371
+ importedPaths.push(node.source.value);
372
+ },
373
+ ExportAllDeclaration({ node }) {
374
+ importedPaths.push(node.source.value);
375
+ },
376
+ ExportNamedDeclaration({ node }) {
377
+ if (node.source) importedPaths.push(node.source.value);
378
+ },
379
+ TSExternalModuleReference({ node }) {
380
+ importedPaths.push(node.expression.value);
381
+ },
382
+ CallExpression({ node }) {
383
+ if ("name" in node.callee && node.callee.name === "require") {
384
+ if (node.arguments.length === 1) {
385
+ const importPathNode = node.arguments[0];
386
+ if (importPathNode.type === "StringLiteral") importedPaths.push(importPathNode.value);
387
+ }
388
+ }
389
+ }
390
+ });
391
+ return importedPaths;
512
392
  };
513
393
 
514
- // src/utils/preview/hot-reloading/resolve-path-aliases.ts
515
- import path4 from "node:path";
516
- import { createMatchPath, loadConfig } from "tsconfig-paths";
517
- var resolvePathAliases = (importPaths, projectPath) => {
518
- const configLoadResult = loadConfig(projectPath);
519
- if (configLoadResult.resultType === "success") {
520
- const matchPath = createMatchPath(
521
- configLoadResult.absoluteBaseUrl,
522
- configLoadResult.paths
523
- );
524
- return importPaths.map((importedPath) => {
525
- const unaliasedPath = matchPath(importedPath, void 0, void 0, [
526
- ".tsx",
527
- ".ts",
528
- ".js",
529
- ".jsx",
530
- ".cjs",
531
- ".mjs"
532
- ]);
533
- if (unaliasedPath) {
534
- return `./${path4.relative(projectPath, unaliasedPath)}`;
535
- }
536
- return importedPath;
537
- });
538
- }
539
- return importPaths;
394
+ //#endregion
395
+ //#region src/utils/preview/hot-reloading/resolve-path-aliases.ts
396
+ const resolvePathAliases = (importPaths, projectPath) => {
397
+ const configLoadResult = loadConfig(projectPath);
398
+ if (configLoadResult.resultType === "success") {
399
+ const matchPath = createMatchPath(configLoadResult.absoluteBaseUrl, configLoadResult.paths);
400
+ return importPaths.map((importedPath) => {
401
+ const unaliasedPath = matchPath(importedPath, void 0, void 0, [
402
+ ".tsx",
403
+ ".ts",
404
+ ".js",
405
+ ".jsx",
406
+ ".cjs",
407
+ ".mjs"
408
+ ]);
409
+ if (unaliasedPath) return `./${path.relative(projectPath, unaliasedPath)}`;
410
+ return importedPath;
411
+ });
412
+ }
413
+ return importPaths;
540
414
  };
541
415
 
542
- // src/utils/preview/hot-reloading/create-dependency-graph.ts
543
- var readAllFilesInsideDirectory = async (directory) => {
544
- let allFilePaths = [];
545
- const topLevelDirents = await fs3.readdir(directory, { withFileTypes: true });
546
- for await (const dirent of topLevelDirents) {
547
- const pathToDirent = path5.join(directory, dirent.name);
548
- if (dirent.isDirectory()) {
549
- allFilePaths = allFilePaths.concat(
550
- await readAllFilesInsideDirectory(pathToDirent)
551
- );
552
- } else {
553
- allFilePaths.push(pathToDirent);
554
- }
555
- }
556
- return allFilePaths;
416
+ //#endregion
417
+ //#region src/utils/preview/hot-reloading/create-dependency-graph.ts
418
+ const readAllFilesInsideDirectory = async (directory) => {
419
+ let allFilePaths = [];
420
+ const topLevelDirents = await promises.readdir(directory, { withFileTypes: true });
421
+ for await (const dirent of topLevelDirents) {
422
+ const pathToDirent = path.join(directory, dirent.name);
423
+ if (dirent.isDirectory()) allFilePaths = allFilePaths.concat(await readAllFilesInsideDirectory(pathToDirent));
424
+ else allFilePaths.push(pathToDirent);
425
+ }
426
+ return allFilePaths;
557
427
  };
558
- var javascriptExtensions = [".js", ".ts", ".jsx", ".tsx", ".mjs", ".cjs"];
559
- var isJavascriptModule = (filePath) => {
560
- const extensionName = path5.extname(filePath);
561
- return javascriptExtensions.includes(extensionName);
428
+ const javascriptExtensions = [
429
+ ".js",
430
+ ".ts",
431
+ ".jsx",
432
+ ".tsx",
433
+ ".mjs",
434
+ ".cjs"
435
+ ];
436
+ const isJavascriptModule = (filePath) => {
437
+ const extensionName = path.extname(filePath);
438
+ return javascriptExtensions.includes(extensionName);
562
439
  };
563
- var checkFileExtensionsUntilItExists = (pathWithoutExtension) => {
564
- if (existsSync(`${pathWithoutExtension}.ts`)) {
565
- return `${pathWithoutExtension}.ts`;
566
- }
567
- if (existsSync(`${pathWithoutExtension}.tsx`)) {
568
- return `${pathWithoutExtension}.tsx`;
569
- }
570
- if (existsSync(`${pathWithoutExtension}.js`)) {
571
- return `${pathWithoutExtension}.js`;
572
- }
573
- if (existsSync(`${pathWithoutExtension}.jsx`)) {
574
- return `${pathWithoutExtension}.jsx`;
575
- }
576
- if (existsSync(`${pathWithoutExtension}.mjs`)) {
577
- return `${pathWithoutExtension}.mjs`;
578
- }
579
- if (existsSync(`${pathWithoutExtension}.cjs`)) {
580
- return `${pathWithoutExtension}.cjs`;
581
- }
440
+ const checkFileExtensionsUntilItExists = (pathWithoutExtension) => {
441
+ if (existsSync(`${pathWithoutExtension}.ts`)) return `${pathWithoutExtension}.ts`;
442
+ if (existsSync(`${pathWithoutExtension}.tsx`)) return `${pathWithoutExtension}.tsx`;
443
+ if (existsSync(`${pathWithoutExtension}.js`)) return `${pathWithoutExtension}.js`;
444
+ if (existsSync(`${pathWithoutExtension}.jsx`)) return `${pathWithoutExtension}.jsx`;
445
+ if (existsSync(`${pathWithoutExtension}.mjs`)) return `${pathWithoutExtension}.mjs`;
446
+ if (existsSync(`${pathWithoutExtension}.cjs`)) return `${pathWithoutExtension}.cjs`;
582
447
  };
583
- var createDependencyGraph = async (directory) => {
584
- const filePaths = await readAllFilesInsideDirectory(directory);
585
- const modulePaths = filePaths.filter(isJavascriptModule);
586
- const graph = Object.fromEntries(
587
- modulePaths.map((path14) => [
588
- path14,
589
- {
590
- path: path14,
591
- dependencyPaths: [],
592
- dependentPaths: [],
593
- moduleDependencies: []
594
- }
595
- ])
596
- );
597
- const getDependencyPaths = async (filePath) => {
598
- const contents = await fs3.readFile(filePath, "utf8");
599
- const importedPaths = isJavascriptModule(filePath) ? resolvePathAliases(getImportedModules(contents), path5.dirname(filePath)) : [];
600
- const importedPathsRelativeToDirectory = importedPaths.map(
601
- (dependencyPath) => {
602
- const isModulePath = !dependencyPath.startsWith(".");
603
- if (isModulePath || path5.isAbsolute(dependencyPath)) {
604
- return dependencyPath;
605
- }
606
- let pathToDependencyFromDirectory = path5.resolve(
607
- /*
608
- path.resolve resolves paths differently from what imports on javascript do.
609
-
610
- So if we wouldn't do this, for an email at "/path/to/email.tsx" with a dependency path of "./other-email"
611
- would end up going into /path/to/email.tsx/other-email instead of /path/to/other-email which is the
612
- one the import is meant to go to
613
- */
614
- path5.dirname(filePath),
615
- dependencyPath
616
- );
617
- let isDirectory = false;
618
- try {
619
- isDirectory = statSync(pathToDependencyFromDirectory).isDirectory();
620
- } catch (_) {
621
- }
622
- if (isDirectory) {
623
- const pathToSubDirectory = pathToDependencyFromDirectory;
624
- const pathWithExtension = checkFileExtensionsUntilItExists(
625
- `${pathToSubDirectory}/index`
626
- );
627
- if (pathWithExtension) {
628
- pathToDependencyFromDirectory = pathWithExtension;
629
- } else {
630
- console.warn(
631
- `Could not find index file for directory at ${pathToDependencyFromDirectory}. This is probably going to cause issues with both hot reloading and your code.`
632
- );
633
- }
634
- }
635
- const extension = path5.extname(pathToDependencyFromDirectory);
636
- const pathWithEnsuredExtension = (() => {
637
- if (extension.length > 0 && javascriptExtensions.includes(extension)) {
638
- if (existsSync(pathToDependencyFromDirectory)) {
639
- return pathToDependencyFromDirectory;
640
- }
641
- return checkFileExtensionsUntilItExists(
642
- pathToDependencyFromDirectory.replace(extension, "")
643
- );
644
- }
645
- return checkFileExtensionsUntilItExists(
646
- pathToDependencyFromDirectory
647
- );
648
- })();
649
- if (pathWithEnsuredExtension) {
650
- pathToDependencyFromDirectory = pathWithEnsuredExtension;
651
- } else {
652
- console.warn(
653
- `Could not find file at ${pathToDependencyFromDirectory}`
654
- );
655
- }
656
- return pathToDependencyFromDirectory;
657
- }
658
- );
659
- const moduleDependencies = importedPathsRelativeToDirectory.filter(
660
- (dependencyPath) => !dependencyPath.startsWith(".") && !path5.isAbsolute(dependencyPath)
661
- );
662
- const nonNodeModuleImportPathsRelativeToDirectory = importedPathsRelativeToDirectory.filter(
663
- (dependencyPath) => dependencyPath.startsWith(".") || path5.isAbsolute(dependencyPath)
664
- );
665
- return {
666
- dependencyPaths: nonNodeModuleImportPathsRelativeToDirectory,
667
- moduleDependencies
668
- };
669
- };
670
- const updateModuleDependenciesInGraph = async (moduleFilePath) => {
671
- if (graph[moduleFilePath] === void 0) {
672
- graph[moduleFilePath] = {
673
- path: moduleFilePath,
674
- dependencyPaths: [],
675
- dependentPaths: [],
676
- moduleDependencies: []
677
- };
678
- }
679
- const { moduleDependencies, dependencyPaths: newDependencyPaths } = await getDependencyPaths(moduleFilePath);
680
- graph[moduleFilePath].moduleDependencies = moduleDependencies;
681
- for (const dependencyPath of graph[moduleFilePath].dependencyPaths) {
682
- if (newDependencyPaths.includes(dependencyPath)) continue;
683
- const dependencyModule = graph[dependencyPath];
684
- if (dependencyModule !== void 0) {
685
- dependencyModule.dependentPaths = dependencyModule.dependentPaths.filter(
686
- (dependentPath) => dependentPath !== moduleFilePath
687
- );
688
- }
689
- }
690
- graph[moduleFilePath].dependencyPaths = newDependencyPaths;
691
- for await (const dependencyPath of newDependencyPaths) {
692
- if (graph[dependencyPath] === void 0) {
693
- await updateModuleDependenciesInGraph(dependencyPath);
694
- }
695
- const dependencyModule = graph[dependencyPath];
696
- if (dependencyModule === void 0) {
697
- throw new Error(
698
- `Loading the dependency path ${dependencyPath} did not initialize it at all. This is a bug in React Email.`
699
- );
700
- }
701
- if (!dependencyModule.dependentPaths.includes(moduleFilePath)) {
702
- dependencyModule.dependentPaths.push(moduleFilePath);
703
- }
704
- }
705
- };
706
- for (const filePath of modulePaths) {
707
- await updateModuleDependenciesInGraph(filePath);
708
- }
709
- const removeModuleFromGraph = (filePath) => {
710
- const module = graph[filePath];
711
- if (module) {
712
- for (const dependencyPath of module.dependencyPaths) {
713
- if (graph[dependencyPath]) {
714
- graph[dependencyPath].dependentPaths = graph[dependencyPath].dependentPaths.filter(
715
- (dependentPath) => dependentPath !== filePath
716
- );
717
- }
718
- }
719
- delete graph[filePath];
720
- }
721
- };
722
- return [
723
- graph,
724
- async (event, pathToModified) => {
725
- switch (event) {
726
- case "change":
727
- if (isJavascriptModule(pathToModified)) {
728
- await updateModuleDependenciesInGraph(pathToModified);
729
- }
730
- break;
731
- case "add":
732
- if (isJavascriptModule(pathToModified)) {
733
- await updateModuleDependenciesInGraph(pathToModified);
734
- }
735
- break;
736
- case "addDir": {
737
- const filesInsideAddedDirectory = await readAllFilesInsideDirectory(pathToModified);
738
- const modulesInsideAddedDirectory = filesInsideAddedDirectory.filter(isJavascriptModule);
739
- for await (const filePath of modulesInsideAddedDirectory) {
740
- await updateModuleDependenciesInGraph(filePath);
741
- }
742
- break;
743
- }
744
- case "unlink":
745
- if (isJavascriptModule(pathToModified)) {
746
- removeModuleFromGraph(pathToModified);
747
- }
748
- break;
749
- case "unlinkDir": {
750
- const filesInsideDeletedDirectory = await readAllFilesInsideDirectory(pathToModified);
751
- const modulesInsideDeletedDirectory = filesInsideDeletedDirectory.filter(isJavascriptModule);
752
- for await (const filePath of modulesInsideDeletedDirectory) {
753
- removeModuleFromGraph(filePath);
754
- }
755
- break;
756
- }
757
- }
758
- },
759
- {
760
- /**
761
- * Resolves all modules that depend on the specified module, directly or indirectly.
762
- *
763
- * @param pathToModule - The path to the module whose dependents we want to find
764
- * @returns An array of paths to all modules that depend on the specified module
765
- */
766
- resolveDependentsOf: function resolveDependentsOf(pathToModule) {
767
- const dependentPaths = /* @__PURE__ */ new Set();
768
- const stack = [pathToModule];
769
- while (stack.length > 0) {
770
- const currentPath = stack.pop();
771
- const moduleEntry = graph[currentPath];
772
- if (!moduleEntry) continue;
773
- for (const dependentPath of moduleEntry.dependentPaths) {
774
- if (dependentPaths.has(dependentPath) || dependentPath === pathToModule)
775
- continue;
776
- dependentPaths.add(dependentPath);
777
- stack.push(dependentPath);
778
- }
779
- }
780
- return [...dependentPaths.values()];
781
- }
782
- }
783
- ];
448
+ /**
449
+ * Creates a stateful dependency graph that is structured in a way that you can get
450
+ * the dependents of a module from its path.
451
+ *
452
+ * Stateful in the sense that it provides a `getter` and an "`updater`". The updater
453
+ * will receive changes to the files, that can be perceived through some file watching mechanism,
454
+ * so that it doesn't need to recompute the entire dependency graph but only the parts changed.
455
+ */
456
+ const createDependencyGraph = async (directory) => {
457
+ const modulePaths = (await readAllFilesInsideDirectory(directory)).filter(isJavascriptModule);
458
+ const graph = Object.fromEntries(modulePaths.map((path$1) => [path$1, {
459
+ path: path$1,
460
+ dependencyPaths: [],
461
+ dependentPaths: [],
462
+ moduleDependencies: []
463
+ }]));
464
+ const getDependencyPaths = async (filePath) => {
465
+ const contents = await promises.readFile(filePath, "utf8");
466
+ const importedPathsRelativeToDirectory = (isJavascriptModule(filePath) ? resolvePathAliases(getImportedModules(contents), path.dirname(filePath)) : []).map((dependencyPath) => {
467
+ if (!dependencyPath.startsWith(".") || path.isAbsolute(dependencyPath)) return dependencyPath;
468
+ let pathToDependencyFromDirectory = path.resolve(path.dirname(filePath), dependencyPath);
469
+ let isDirectory = false;
470
+ try {
471
+ isDirectory = statSync(pathToDependencyFromDirectory).isDirectory();
472
+ } catch (_) {}
473
+ if (isDirectory) {
474
+ const pathWithExtension = checkFileExtensionsUntilItExists(`${pathToDependencyFromDirectory}/index`);
475
+ if (pathWithExtension) pathToDependencyFromDirectory = pathWithExtension;
476
+ else console.warn(`Could not find index file for directory at ${pathToDependencyFromDirectory}. This is probably going to cause issues with both hot reloading and your code.`);
477
+ }
478
+ const extension = path.extname(pathToDependencyFromDirectory);
479
+ const pathWithEnsuredExtension = (() => {
480
+ if (extension.length > 0 && javascriptExtensions.includes(extension)) {
481
+ if (existsSync(pathToDependencyFromDirectory)) return pathToDependencyFromDirectory;
482
+ return checkFileExtensionsUntilItExists(pathToDependencyFromDirectory.replace(extension, ""));
483
+ }
484
+ return checkFileExtensionsUntilItExists(pathToDependencyFromDirectory);
485
+ })();
486
+ if (pathWithEnsuredExtension) pathToDependencyFromDirectory = pathWithEnsuredExtension;
487
+ else console.warn(`Could not find file at ${pathToDependencyFromDirectory}`);
488
+ return pathToDependencyFromDirectory;
489
+ });
490
+ const moduleDependencies = importedPathsRelativeToDirectory.filter((dependencyPath) => !dependencyPath.startsWith(".") && !path.isAbsolute(dependencyPath));
491
+ return {
492
+ dependencyPaths: importedPathsRelativeToDirectory.filter((dependencyPath) => dependencyPath.startsWith(".") || path.isAbsolute(dependencyPath)),
493
+ moduleDependencies
494
+ };
495
+ };
496
+ const updateModuleDependenciesInGraph = async (moduleFilePath) => {
497
+ if (graph[moduleFilePath] === void 0) graph[moduleFilePath] = {
498
+ path: moduleFilePath,
499
+ dependencyPaths: [],
500
+ dependentPaths: [],
501
+ moduleDependencies: []
502
+ };
503
+ const { moduleDependencies, dependencyPaths: newDependencyPaths } = await getDependencyPaths(moduleFilePath);
504
+ graph[moduleFilePath].moduleDependencies = moduleDependencies;
505
+ for (const dependencyPath of graph[moduleFilePath].dependencyPaths) {
506
+ if (newDependencyPaths.includes(dependencyPath)) continue;
507
+ const dependencyModule = graph[dependencyPath];
508
+ if (dependencyModule !== void 0) dependencyModule.dependentPaths = dependencyModule.dependentPaths.filter((dependentPath) => dependentPath !== moduleFilePath);
509
+ }
510
+ graph[moduleFilePath].dependencyPaths = newDependencyPaths;
511
+ for await (const dependencyPath of newDependencyPaths) {
512
+ if (graph[dependencyPath] === void 0) await updateModuleDependenciesInGraph(dependencyPath);
513
+ const dependencyModule = graph[dependencyPath];
514
+ if (dependencyModule === void 0) throw new Error(`Loading the dependency path ${dependencyPath} did not initialize it at all. This is a bug in React Email.`);
515
+ if (!dependencyModule.dependentPaths.includes(moduleFilePath)) dependencyModule.dependentPaths.push(moduleFilePath);
516
+ }
517
+ };
518
+ for (const filePath of modulePaths) await updateModuleDependenciesInGraph(filePath);
519
+ const removeModuleFromGraph = (filePath) => {
520
+ const module = graph[filePath];
521
+ if (module) {
522
+ for (const dependencyPath of module.dependencyPaths) if (graph[dependencyPath]) graph[dependencyPath].dependentPaths = graph[dependencyPath].dependentPaths.filter((dependentPath) => dependentPath !== filePath);
523
+ delete graph[filePath];
524
+ }
525
+ };
526
+ return [
527
+ graph,
528
+ async (event, pathToModified) => {
529
+ switch (event) {
530
+ case "change":
531
+ if (isJavascriptModule(pathToModified)) await updateModuleDependenciesInGraph(pathToModified);
532
+ break;
533
+ case "add":
534
+ if (isJavascriptModule(pathToModified)) await updateModuleDependenciesInGraph(pathToModified);
535
+ break;
536
+ case "addDir": {
537
+ const modulesInsideAddedDirectory = (await readAllFilesInsideDirectory(pathToModified)).filter(isJavascriptModule);
538
+ for await (const filePath of modulesInsideAddedDirectory) await updateModuleDependenciesInGraph(filePath);
539
+ break;
540
+ }
541
+ case "unlink":
542
+ if (isJavascriptModule(pathToModified)) removeModuleFromGraph(pathToModified);
543
+ break;
544
+ case "unlinkDir": {
545
+ const modulesInsideDeletedDirectory = (await readAllFilesInsideDirectory(pathToModified)).filter(isJavascriptModule);
546
+ for await (const filePath of modulesInsideDeletedDirectory) removeModuleFromGraph(filePath);
547
+ break;
548
+ }
549
+ }
550
+ },
551
+ { resolveDependentsOf: function resolveDependentsOf(pathToModule) {
552
+ const dependentPaths = /* @__PURE__ */ new Set();
553
+ const stack = [pathToModule];
554
+ while (stack.length > 0) {
555
+ const currentPath = stack.pop();
556
+ const moduleEntry = graph[currentPath];
557
+ if (!moduleEntry) continue;
558
+ for (const dependentPath of moduleEntry.dependentPaths) {
559
+ if (dependentPaths.has(dependentPath) || dependentPath === pathToModule) continue;
560
+ dependentPaths.add(dependentPath);
561
+ stack.push(dependentPath);
562
+ }
563
+ }
564
+ return [...dependentPaths.values()];
565
+ } }
566
+ ];
784
567
  };
785
568
 
786
- // src/utils/preview/hot-reloading/setup-hot-reloading.ts
787
- var setupHotreloading = async (devServer2, emailDirRelativePath) => {
788
- let clients = [];
789
- const io = new SocketServer(devServer2);
790
- io.on("connection", (client) => {
791
- clients.push(client);
792
- client.on("disconnect", () => {
793
- clients = clients.filter((item) => item !== client);
794
- });
795
- });
796
- let changes = [];
797
- const reload = debounce(() => {
798
- clients.forEach((client) => {
799
- client.emit(
800
- "reload",
801
- changes.filter(
802
- (change) => (
803
- // Ensures only changes inside the emails directory are emitted
804
- path6.resolve(absolutePathToEmailsDirectory, change.filename).startsWith(absolutePathToEmailsDirectory)
805
- )
806
- )
807
- );
808
- });
809
- changes = [];
810
- }, 150);
811
- const absolutePathToEmailsDirectory = path6.resolve(
812
- process.cwd(),
813
- emailDirRelativePath
814
- );
815
- const [dependencyGraph, updateDependencyGraph, { resolveDependentsOf }] = await createDependencyGraph(absolutePathToEmailsDirectory);
816
- const watcher = watch("", {
817
- ignoreInitial: true,
818
- cwd: absolutePathToEmailsDirectory
819
- });
820
- const getFilesOutsideEmailsDirectory = () => Object.keys(dependencyGraph).filter(
821
- (p) => path6.relative(absolutePathToEmailsDirectory, p).startsWith("..")
822
- );
823
- let filesOutsideEmailsDirectory = getFilesOutsideEmailsDirectory();
824
- for (const p of filesOutsideEmailsDirectory) {
825
- watcher.add(p);
826
- }
827
- const exit = async () => {
828
- await watcher.close();
829
- };
830
- process.on("SIGINT", exit);
831
- process.on("uncaughtException", exit);
832
- watcher.on("all", async (event, relativePathToChangeTarget) => {
833
- const file = relativePathToChangeTarget.split(path6.sep);
834
- if (file.length === 0) {
835
- return;
836
- }
837
- const pathToChangeTarget = path6.resolve(
838
- absolutePathToEmailsDirectory,
839
- relativePathToChangeTarget
840
- );
841
- await updateDependencyGraph(event, pathToChangeTarget);
842
- const newFilesOutsideEmailsDirectory = getFilesOutsideEmailsDirectory();
843
- for (const p of filesOutsideEmailsDirectory) {
844
- if (!newFilesOutsideEmailsDirectory.includes(p)) {
845
- watcher.unwatch(p);
846
- }
847
- }
848
- for (const p of newFilesOutsideEmailsDirectory) {
849
- if (!filesOutsideEmailsDirectory.includes(p)) {
850
- watcher.add(p);
851
- }
852
- }
853
- filesOutsideEmailsDirectory = newFilesOutsideEmailsDirectory;
854
- changes.push({
855
- event,
856
- filename: relativePathToChangeTarget
857
- });
858
- for (const dependentPath of resolveDependentsOf(pathToChangeTarget)) {
859
- changes.push({
860
- event: "change",
861
- filename: path6.relative(absolutePathToEmailsDirectory, dependentPath)
862
- });
863
- }
864
- reload();
865
- });
866
- return watcher;
569
+ //#endregion
570
+ //#region src/utils/preview/hot-reloading/setup-hot-reloading.ts
571
+ const setupHotreloading = async (devServer$1, emailDirRelativePath) => {
572
+ let clients = [];
573
+ new Server(devServer$1).on("connection", (client) => {
574
+ clients.push(client);
575
+ client.on("disconnect", () => {
576
+ clients = clients.filter((item) => item !== client);
577
+ });
578
+ });
579
+ let changes = [];
580
+ const reload = debounce(() => {
581
+ clients.forEach((client) => {
582
+ client.emit("reload", changes.filter((change) => path.resolve(absolutePathToEmailsDirectory, change.filename).startsWith(absolutePathToEmailsDirectory)));
583
+ });
584
+ changes = [];
585
+ }, 150);
586
+ const absolutePathToEmailsDirectory = path.resolve(process.cwd(), emailDirRelativePath);
587
+ const [dependencyGraph, updateDependencyGraph, { resolveDependentsOf }] = await createDependencyGraph(absolutePathToEmailsDirectory);
588
+ const watcher = watch("", {
589
+ ignoreInitial: true,
590
+ cwd: absolutePathToEmailsDirectory
591
+ });
592
+ const getFilesOutsideEmailsDirectory = () => Object.keys(dependencyGraph).filter((p) => path.relative(absolutePathToEmailsDirectory, p).startsWith(".."));
593
+ let filesOutsideEmailsDirectory = getFilesOutsideEmailsDirectory();
594
+ for (const p of filesOutsideEmailsDirectory) watcher.add(p);
595
+ const exit = async () => {
596
+ await watcher.close();
597
+ };
598
+ process.on("SIGINT", exit);
599
+ process.on("uncaughtException", exit);
600
+ watcher.on("all", async (event, relativePathToChangeTarget) => {
601
+ if (relativePathToChangeTarget.split(path.sep).length === 0) return;
602
+ const pathToChangeTarget = path.resolve(absolutePathToEmailsDirectory, relativePathToChangeTarget);
603
+ await updateDependencyGraph(event, pathToChangeTarget);
604
+ const newFilesOutsideEmailsDirectory = getFilesOutsideEmailsDirectory();
605
+ for (const p of filesOutsideEmailsDirectory) if (!newFilesOutsideEmailsDirectory.includes(p)) watcher.unwatch(p);
606
+ for (const p of newFilesOutsideEmailsDirectory) if (!filesOutsideEmailsDirectory.includes(p)) watcher.add(p);
607
+ filesOutsideEmailsDirectory = newFilesOutsideEmailsDirectory;
608
+ changes.push({
609
+ event,
610
+ filename: relativePathToChangeTarget
611
+ });
612
+ for (const dependentPath of resolveDependentsOf(pathToChangeTarget)) changes.push({
613
+ event: "change",
614
+ filename: path.relative(absolutePathToEmailsDirectory, dependentPath)
615
+ });
616
+ reload();
617
+ });
618
+ return watcher;
867
619
  };
868
620
 
869
- // src/utils/preview/start-dev-server.ts
870
- import http from "node:http";
871
- import path9 from "node:path";
872
- import url2 from "node:url";
873
- import { styleText } from "node:util";
874
- import { createJiti as createJiti2 } from "jiti";
875
- import logSymbols3 from "log-symbols";
876
- import ora2 from "ora";
877
-
878
- // src/utils/preview/get-env-variables-for-preview-app.ts
879
- import path7 from "node:path";
880
- var getEnvVariablesForPreviewApp = (relativePathToEmailsDirectory, previewServerLocation, cwd) => {
881
- return {
882
- EMAILS_DIR_RELATIVE_PATH: relativePathToEmailsDirectory,
883
- EMAILS_DIR_ABSOLUTE_PATH: path7.resolve(cwd, relativePathToEmailsDirectory),
884
- PREVIEW_SERVER_LOCATION: previewServerLocation,
885
- USER_PROJECT_LOCATION: cwd
886
- };
621
+ //#endregion
622
+ //#region src/utils/preview/get-env-variables-for-preview-app.ts
623
+ const getEnvVariablesForPreviewApp = (relativePathToEmailsDirectory, previewServerLocation, cwd) => {
624
+ return {
625
+ EMAILS_DIR_RELATIVE_PATH: relativePathToEmailsDirectory,
626
+ EMAILS_DIR_ABSOLUTE_PATH: path.resolve(cwd, relativePathToEmailsDirectory),
627
+ PREVIEW_SERVER_LOCATION: previewServerLocation,
628
+ USER_PROJECT_LOCATION: cwd
629
+ };
887
630
  };
888
631
 
889
- // src/utils/preview/serve-static-file.ts
890
- import { existsSync as existsSync2, promises as fs4 } from "node:fs";
891
- import path8 from "node:path";
892
- import { lookup } from "mime-types";
893
- var serveStaticFile = async (res, parsedUrl, staticDirRelativePath) => {
894
- const pathname = parsedUrl.pathname.replace("/static", "./static");
895
- const ext = path8.parse(pathname).ext;
896
- const staticBaseDir = path8.resolve(process.cwd(), staticDirRelativePath);
897
- const fileAbsolutePath = path8.resolve(staticBaseDir, pathname);
898
- if (!fileAbsolutePath.startsWith(staticBaseDir)) {
899
- res.statusCode = 403;
900
- res.end();
901
- return;
902
- }
903
- try {
904
- const fileHandle = await fs4.open(fileAbsolutePath, "r");
905
- const fileData = await fs4.readFile(fileHandle);
906
- res.setHeader("Content-type", lookup(ext) || "text/plain");
907
- res.end(fileData);
908
- fileHandle.close();
909
- } catch (exception) {
910
- if (!existsSync2(fileAbsolutePath)) {
911
- res.statusCode = 404;
912
- res.end();
913
- } else {
914
- const sanitizedFilePath = fileAbsolutePath.replace(/\n|\r/g, "");
915
- console.error(
916
- `Could not read file at %s to be served, here's the exception:`,
917
- sanitizedFilePath,
918
- exception
919
- );
920
- res.statusCode = 500;
921
- res.end(
922
- "Could not read file to be served! Check your terminal for more information."
923
- );
924
- }
925
- }
632
+ //#endregion
633
+ //#region src/utils/preview/serve-static-file.ts
634
+ const serveStaticFile = async (res, parsedUrl, staticDirRelativePath) => {
635
+ const pathname = parsedUrl.pathname.replace("/static", "./static");
636
+ const ext = path.parse(pathname).ext;
637
+ const staticBaseDir = path.resolve(process.cwd(), staticDirRelativePath);
638
+ const fileAbsolutePath = path.resolve(staticBaseDir, pathname);
639
+ if (!fileAbsolutePath.startsWith(staticBaseDir)) {
640
+ res.statusCode = 403;
641
+ res.end();
642
+ return;
643
+ }
644
+ try {
645
+ const fileHandle = await promises.open(fileAbsolutePath, "r");
646
+ const fileData = await promises.readFile(fileHandle);
647
+ res.setHeader("Content-type", lookup(ext) || "text/plain");
648
+ res.end(fileData);
649
+ fileHandle.close();
650
+ } catch (exception) {
651
+ if (!existsSync(fileAbsolutePath)) {
652
+ res.statusCode = 404;
653
+ res.end();
654
+ } else {
655
+ const sanitizedFilePath = fileAbsolutePath.replace(/\n|\r/g, "");
656
+ console.error(`Could not read file at %s to be served, here's the exception:`, sanitizedFilePath, exception);
657
+ res.statusCode = 500;
658
+ res.end("Could not read file to be served! Check your terminal for more information.");
659
+ }
660
+ }
926
661
  };
927
662
 
928
- // src/utils/preview/start-dev-server.ts
929
- var devServer;
930
- var safeAsyncServerListen = (server, port) => {
931
- return new Promise((resolve) => {
932
- server.listen(port, () => {
933
- resolve({ portAlreadyInUse: false });
934
- });
935
- server.on("error", (e) => {
936
- if (e.code === "EADDRINUSE") {
937
- resolve({ portAlreadyInUse: true });
938
- }
939
- });
940
- });
663
+ //#endregion
664
+ //#region src/utils/preview/start-dev-server.ts
665
+ let devServer;
666
+ const safeAsyncServerListen = (server, port) => {
667
+ return new Promise((resolve) => {
668
+ server.listen(port, () => {
669
+ resolve({ portAlreadyInUse: false });
670
+ });
671
+ server.on("error", (e) => {
672
+ if (e.code === "EADDRINUSE") resolve({ portAlreadyInUse: true });
673
+ });
674
+ });
941
675
  };
942
- var startDevServer = async (emailsDirRelativePath, staticBaseDirRelativePath, port) => {
943
- const [majorNodeVersion] = process.versions.node.split(".");
944
- if (majorNodeVersion && Number.parseInt(majorNodeVersion) < 18) {
945
- console.error(
946
- ` ${logSymbols3.error} Node ${majorNodeVersion} is not supported. Please upgrade to Node 18 or higher.`
947
- );
948
- process.exit(1);
949
- }
950
- const previewServerLocation = await getPreviewServerLocation();
951
- const previewServer = createJiti2(previewServerLocation);
952
- devServer = http.createServer((req, res) => {
953
- if (!req.url) {
954
- res.end(404);
955
- return;
956
- }
957
- const parsedUrl = url2.parse(req.url, true);
958
- res.setHeader(
959
- "Cache-Control",
960
- "no-cache, max-age=0, must-revalidate, no-store"
961
- );
962
- res.setHeader("Pragma", "no-cache");
963
- res.setHeader("Expires", "-1");
964
- try {
965
- if (parsedUrl.path?.includes("static/") && !parsedUrl.path.includes("_next/static/")) {
966
- void serveStaticFile(res, parsedUrl, staticBaseDirRelativePath);
967
- } else if (!isNextReady) {
968
- void nextReadyPromise.then(
969
- () => nextHandleRequest?.(req, res, parsedUrl)
970
- );
971
- } else {
972
- void nextHandleRequest?.(req, res, parsedUrl);
973
- }
974
- } catch (e) {
975
- console.error("caught error", e);
976
- res.writeHead(500);
977
- res.end();
978
- }
979
- });
980
- const { portAlreadyInUse } = await safeAsyncServerListen(devServer, port);
981
- if (!portAlreadyInUse) {
982
- console.log(
983
- styleText("greenBright", ` React Email ${package_default.version}`)
984
- );
985
- console.log(` Running preview at: http://localhost:${port}
986
- `);
987
- } else {
988
- const nextPortToTry = port + 1;
989
- console.warn(
990
- ` ${logSymbols3.warning} Port ${port} is already in use, trying ${nextPortToTry}`
991
- );
992
- return startDevServer(
993
- emailsDirRelativePath,
994
- staticBaseDirRelativePath,
995
- nextPortToTry
996
- );
997
- }
998
- devServer.on("close", async () => {
999
- await app.close();
1000
- });
1001
- devServer.on("error", (e) => {
1002
- spinner.stopAndPersist({
1003
- symbol: logSymbols3.error,
1004
- text: `Preview Server had an error: ${e}`
1005
- });
1006
- process.exit(1);
1007
- });
1008
- const spinner = ora2({
1009
- text: "Getting react-email preview server ready...\n",
1010
- prefixText: " "
1011
- }).start();
1012
- registerSpinnerAutostopping(spinner);
1013
- const timeBeforeNextReady = performance.now();
1014
- process.env = {
1015
- NODE_ENV: "development",
1016
- ...process.env,
1017
- ...getEnvVariablesForPreviewApp(
1018
- // If we don't do normalization here, stuff like https://github.com/resend/react-email/issues/1354 happens.
1019
- path9.normalize(emailsDirRelativePath),
1020
- previewServerLocation,
1021
- process.cwd()
1022
- )
1023
- };
1024
- const next = await previewServer.import(
1025
- "next",
1026
- {
1027
- default: true
1028
- }
1029
- );
1030
- const app = next({
1031
- // passing in env here does not get the environment variables there
1032
- dev: false,
1033
- conf: {
1034
- images: {
1035
- // This is to avoid the warning with sharp
1036
- unoptimized: true
1037
- }
1038
- },
1039
- hostname: "localhost",
1040
- port,
1041
- dir: previewServerLocation
1042
- });
1043
- let isNextReady = false;
1044
- const nextReadyPromise = app.prepare();
1045
- try {
1046
- await nextReadyPromise;
1047
- } catch (exception) {
1048
- spinner.stopAndPersist({
1049
- symbol: logSymbols3.error,
1050
- text: ` Preview Server had an error: ${exception}`
1051
- });
1052
- process.exit(1);
1053
- }
1054
- isNextReady = true;
1055
- const nextHandleRequest = app.getRequestHandler();
1056
- const secondsToNextReady = ((performance.now() - timeBeforeNextReady) / 1e3).toFixed(1);
1057
- spinner.stopAndPersist({
1058
- text: `Ready in ${secondsToNextReady}s
1059
- `,
1060
- symbol: logSymbols3.success
1061
- });
1062
- return devServer;
676
+ const startDevServer = async (emailsDirRelativePath, staticBaseDirRelativePath, port) => {
677
+ const [majorNodeVersion] = process.versions.node.split(".");
678
+ if (majorNodeVersion && Number.parseInt(majorNodeVersion) < 18) {
679
+ console.error(` ${logSymbols.error} Node ${majorNodeVersion} is not supported. Please upgrade to Node 18 or higher.`);
680
+ process.exit(1);
681
+ }
682
+ const previewServerLocation = await getPreviewServerLocation();
683
+ const previewServer = createJiti(previewServerLocation);
684
+ devServer = http.createServer((req, res) => {
685
+ if (!req.url) {
686
+ res.end(404);
687
+ return;
688
+ }
689
+ const parsedUrl = url.parse(req.url, true);
690
+ res.setHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store");
691
+ res.setHeader("Pragma", "no-cache");
692
+ res.setHeader("Expires", "-1");
693
+ try {
694
+ if (parsedUrl.path?.includes("static/") && !parsedUrl.path.includes("_next/static/")) serveStaticFile(res, parsedUrl, staticBaseDirRelativePath);
695
+ else if (!isNextReady) nextReadyPromise.then(() => nextHandleRequest?.(req, res, parsedUrl));
696
+ else nextHandleRequest?.(req, res, parsedUrl);
697
+ } catch (e) {
698
+ console.error("caught error", e);
699
+ res.writeHead(500);
700
+ res.end();
701
+ }
702
+ });
703
+ const { portAlreadyInUse } = await safeAsyncServerListen(devServer, port);
704
+ if (!portAlreadyInUse) {
705
+ console.log(styleText("greenBright", ` React Email ${package_default.version}`));
706
+ console.log(` Running preview at: http://localhost:${port}\n`);
707
+ } else {
708
+ const nextPortToTry = port + 1;
709
+ console.warn(` ${logSymbols.warning} Port ${port} is already in use, trying ${nextPortToTry}`);
710
+ return startDevServer(emailsDirRelativePath, staticBaseDirRelativePath, nextPortToTry);
711
+ }
712
+ devServer.on("close", async () => {
713
+ await app.close();
714
+ });
715
+ devServer.on("error", (e) => {
716
+ spinner.stopAndPersist({
717
+ symbol: logSymbols.error,
718
+ text: `Preview Server had an error: ${e}`
719
+ });
720
+ process.exit(1);
721
+ });
722
+ const spinner = ora({
723
+ text: "Getting react-email preview server ready...\n",
724
+ prefixText: " "
725
+ }).start();
726
+ registerSpinnerAutostopping(spinner);
727
+ const timeBeforeNextReady = performance.now();
728
+ process.env = {
729
+ NODE_ENV: "development",
730
+ ...process.env,
731
+ ...getEnvVariablesForPreviewApp(path.normalize(emailsDirRelativePath), previewServerLocation, process.cwd())
732
+ };
733
+ const app = (await previewServer.import("next", { default: true }))({
734
+ dev: false,
735
+ conf: { images: { unoptimized: true } },
736
+ hostname: "localhost",
737
+ port,
738
+ dir: previewServerLocation
739
+ });
740
+ let isNextReady = false;
741
+ const nextReadyPromise = app.prepare();
742
+ try {
743
+ await nextReadyPromise;
744
+ } catch (exception) {
745
+ spinner.stopAndPersist({
746
+ symbol: logSymbols.error,
747
+ text: ` Preview Server had an error: ${exception}`
748
+ });
749
+ process.exit(1);
750
+ }
751
+ isNextReady = true;
752
+ const nextHandleRequest = app.getRequestHandler();
753
+ const secondsToNextReady = ((performance.now() - timeBeforeNextReady) / 1e3).toFixed(1);
754
+ spinner.stopAndPersist({
755
+ text: `Ready in ${secondsToNextReady}s\n`,
756
+ symbol: logSymbols.success
757
+ });
758
+ return devServer;
1063
759
  };
1064
- var makeExitHandler = (options) => (codeSignalOrError) => {
1065
- if (typeof devServer !== "undefined") {
1066
- console.log("\nshutting down dev server");
1067
- devServer.close();
1068
- devServer = void 0;
1069
- }
1070
- if (codeSignalOrError instanceof Error) {
1071
- console.error(codeSignalOrError);
1072
- }
1073
- if (options?.shouldKillProcess) {
1074
- process.exit(options.killWithErrorCode ? 1 : 0);
1075
- }
760
+ const makeExitHandler = (options) => (codeSignalOrError) => {
761
+ if (typeof devServer !== "undefined") {
762
+ console.log("\nshutting down dev server");
763
+ devServer.close();
764
+ devServer = void 0;
765
+ }
766
+ if (codeSignalOrError instanceof Error) console.error(codeSignalOrError);
767
+ if (options?.shouldKillProcess) process.exit(options.killWithErrorCode ? 1 : 0);
1076
768
  };
1077
769
  process.on("exit", makeExitHandler());
1078
- process.on(
1079
- "SIGINT",
1080
- makeExitHandler({ shouldKillProcess: true, killWithErrorCode: false })
1081
- );
1082
- process.on(
1083
- "SIGUSR1",
1084
- makeExitHandler({ shouldKillProcess: true, killWithErrorCode: false })
1085
- );
1086
- process.on(
1087
- "SIGUSR2",
1088
- makeExitHandler({ shouldKillProcess: true, killWithErrorCode: false })
1089
- );
1090
- process.on(
1091
- "uncaughtException",
1092
- makeExitHandler({ shouldKillProcess: true, killWithErrorCode: true })
1093
- );
770
+ process.on("SIGINT", makeExitHandler({
771
+ shouldKillProcess: true,
772
+ killWithErrorCode: false
773
+ }));
774
+ process.on("SIGUSR1", makeExitHandler({
775
+ shouldKillProcess: true,
776
+ killWithErrorCode: false
777
+ }));
778
+ process.on("SIGUSR2", makeExitHandler({
779
+ shouldKillProcess: true,
780
+ killWithErrorCode: false
781
+ }));
782
+ process.on("uncaughtException", makeExitHandler({
783
+ shouldKillProcess: true,
784
+ killWithErrorCode: true
785
+ }));
1094
786
 
1095
- // src/utils/tree.ts
1096
- import { promises as fs5 } from "node:fs";
1097
- import os from "node:os";
1098
- import path10 from "node:path";
1099
- var SYMBOLS = {
1100
- BRANCH: "\u251C\u2500\u2500 ",
1101
- EMPTY: "",
1102
- INDENT: " ",
1103
- LAST_BRANCH: "\u2514\u2500\u2500 ",
1104
- VERTICAL: "\u2502 "
787
+ //#endregion
788
+ //#region src/utils/tree.ts
789
+ const SYMBOLS = {
790
+ BRANCH: "├── ",
791
+ EMPTY: "",
792
+ INDENT: " ",
793
+ LAST_BRANCH: "└── ",
794
+ VERTICAL: ""
1105
795
  };
1106
- var getTreeLines = async (dirPath, depth, currentDepth = 0) => {
1107
- const base = process.cwd();
1108
- const dirFullpath = path10.resolve(base, dirPath);
1109
- const dirname = path10.basename(dirFullpath);
1110
- let lines = [dirname];
1111
- const dirStat = await fs5.stat(dirFullpath);
1112
- if (dirStat.isDirectory() && currentDepth < depth) {
1113
- const childDirents = await fs5.readdir(dirFullpath, { withFileTypes: true });
1114
- childDirents.sort((a, b) => {
1115
- if (a.isDirectory() && b.isFile()) {
1116
- return -1;
1117
- }
1118
- if (a.isFile() && b.isDirectory()) {
1119
- return 1;
1120
- }
1121
- return b.name > a.name ? -1 : 1;
1122
- });
1123
- for (let i = 0; i < childDirents.length; i++) {
1124
- const dirent = childDirents[i];
1125
- const isLast = i === childDirents.length - 1;
1126
- const branchingSymbol = isLast ? SYMBOLS.LAST_BRANCH : SYMBOLS.BRANCH;
1127
- const verticalSymbol = isLast ? SYMBOLS.INDENT : SYMBOLS.VERTICAL;
1128
- if (dirent.isFile()) {
1129
- lines.push(`${branchingSymbol}${dirent.name}`);
1130
- } else {
1131
- const pathToDirectory = path10.join(dirFullpath, dirent.name);
1132
- const treeLinesForSubDirectory = await getTreeLines(
1133
- pathToDirectory,
1134
- depth,
1135
- currentDepth + 1
1136
- );
1137
- lines = lines.concat(
1138
- treeLinesForSubDirectory.map(
1139
- (line, index) => index === 0 ? `${branchingSymbol}${line}` : `${verticalSymbol}${line}`
1140
- )
1141
- );
1142
- }
1143
- }
1144
- }
1145
- return lines;
796
+ const getTreeLines = async (dirPath, depth, currentDepth = 0) => {
797
+ const base = process.cwd();
798
+ const dirFullpath = path.resolve(base, dirPath);
799
+ let lines = [path.basename(dirFullpath)];
800
+ if ((await promises.stat(dirFullpath)).isDirectory() && currentDepth < depth) {
801
+ const childDirents = await promises.readdir(dirFullpath, { withFileTypes: true });
802
+ childDirents.sort((a, b) => {
803
+ if (a.isDirectory() && b.isFile()) return -1;
804
+ if (a.isFile() && b.isDirectory()) return 1;
805
+ return b.name > a.name ? -1 : 1;
806
+ });
807
+ for (let i = 0; i < childDirents.length; i++) {
808
+ const dirent = childDirents[i];
809
+ const isLast = i === childDirents.length - 1;
810
+ const branchingSymbol = isLast ? SYMBOLS.LAST_BRANCH : SYMBOLS.BRANCH;
811
+ const verticalSymbol = isLast ? SYMBOLS.INDENT : SYMBOLS.VERTICAL;
812
+ if (dirent.isFile()) lines.push(`${branchingSymbol}${dirent.name}`);
813
+ else {
814
+ const pathToDirectory = path.join(dirFullpath, dirent.name);
815
+ const treeLinesForSubDirectory = await getTreeLines(pathToDirectory, depth, currentDepth + 1);
816
+ lines = lines.concat(treeLinesForSubDirectory.map((line, index) => index === 0 ? `${branchingSymbol}${line}` : `${verticalSymbol}${line}`));
817
+ }
818
+ }
819
+ }
820
+ return lines;
1146
821
  };
1147
- var tree = async (dirPath, depth) => {
1148
- const lines = await getTreeLines(dirPath, depth);
1149
- return lines.join(os.EOL);
822
+ const tree = async (dirPath, depth) => {
823
+ return (await getTreeLines(dirPath, depth)).join(os.EOL);
1150
824
  };
1151
825
 
1152
- // src/commands/dev.ts
1153
- var dev = async ({ dir: emailsDirRelativePath, port }) => {
1154
- try {
1155
- if (!fs6.existsSync(emailsDirRelativePath)) {
1156
- console.error(`Missing ${emailsDirRelativePath} folder`);
1157
- process.exit(1);
1158
- }
1159
- const devServer2 = await startDevServer(
1160
- emailsDirRelativePath,
1161
- emailsDirRelativePath,
1162
- // defaults to ./emails/static for the static files that are served to the preview
1163
- Number.parseInt(port)
1164
- );
1165
- await setupHotreloading(devServer2, emailsDirRelativePath);
1166
- } catch (error) {
1167
- console.log(error);
1168
- process.exit(1);
1169
- }
826
+ //#endregion
827
+ //#region src/commands/dev.ts
828
+ const dev = async ({ dir: emailsDirRelativePath, port }) => {
829
+ try {
830
+ if (!fs.existsSync(emailsDirRelativePath)) {
831
+ console.error(`Missing ${emailsDirRelativePath} folder`);
832
+ process.exit(1);
833
+ }
834
+ const devServer$1 = await startDevServer(emailsDirRelativePath, emailsDirRelativePath, Number.parseInt(port));
835
+ await setupHotreloading(devServer$1, emailsDirRelativePath);
836
+ } catch (error) {
837
+ console.log(error);
838
+ process.exit(1);
839
+ }
1170
840
  };
1171
841
 
1172
- // src/commands/export.ts
1173
- import fs8, { unlinkSync, writeFileSync } from "node:fs";
1174
- import { createRequire } from "node:module";
1175
- import path12 from "node:path";
1176
- import url3 from "node:url";
1177
- import { build as build2 } from "esbuild";
1178
- import { glob } from "glob";
1179
- import logSymbols4 from "log-symbols";
1180
- import normalize from "normalize-path";
1181
- import ora3 from "ora";
1182
-
1183
- // src/utils/esbuild/renderring-utilities-exporter.ts
1184
- import { promises as fs7 } from "node:fs";
1185
- import path11 from "node:path";
1186
-
1187
- // src/utils/esbuild/escape-string-for-regex.ts
842
+ //#endregion
843
+ //#region src/utils/esbuild/escape-string-for-regex.ts
1188
844
  function escapeStringForRegex(string) {
1189
- return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
845
+ return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
1190
846
  }
1191
847
 
1192
- // src/utils/esbuild/renderring-utilities-exporter.ts
1193
- var renderingUtilitiesExporter = (emailTemplates) => ({
1194
- name: "rendering-utilities-exporter",
1195
- setup: (b) => {
1196
- b.onLoad(
1197
- {
1198
- filter: new RegExp(
1199
- emailTemplates.map((emailPath) => escapeStringForRegex(emailPath)).join("|")
1200
- )
1201
- },
1202
- async ({ path: pathToFile }) => {
1203
- return {
1204
- contents: `${await fs7.readFile(pathToFile, "utf8")};
848
+ //#endregion
849
+ //#region src/utils/esbuild/renderring-utilities-exporter.ts
850
+ /**
851
+ * Made to export the `render` function out of the user's email template
852
+ * so that issues like https://github.com/resend/react-email/issues/649 don't
853
+ * happen.
854
+ *
855
+ * This also exports the `createElement` from the user's React version as well
856
+ * to avoid mismatches.
857
+ *
858
+ * This avoids multiple versions of React being involved, i.e., the version
859
+ * in the CLI vs. the version the user has on their emails.
860
+ */
861
+ const renderingUtilitiesExporter = (emailTemplates) => ({
862
+ name: "rendering-utilities-exporter",
863
+ setup: (b) => {
864
+ b.onLoad({ filter: new RegExp(emailTemplates.map((emailPath) => escapeStringForRegex(emailPath)).join("|")) }, async ({ path: pathToFile }) => {
865
+ return {
866
+ contents: `${await promises.readFile(pathToFile, "utf8")};
1205
867
  export { render } from 'react-email-module-that-will-export-render'
1206
868
  export { createElement as reactEmailCreateReactElement } from 'react';
1207
869
  `,
1208
- loader: path11.extname(pathToFile).slice(1)
1209
- };
1210
- }
1211
- );
1212
- b.onResolve(
1213
- { filter: /^react-email-module-that-will-export-render$/ },
1214
- async (args) => {
1215
- const options = {
1216
- kind: "import-statement",
1217
- importer: args.importer,
1218
- resolveDir: args.resolveDir,
1219
- namespace: args.namespace
1220
- };
1221
- let result = await b.resolve("@react-email/render", options);
1222
- if (result.errors.length === 0) {
1223
- return result;
1224
- }
1225
- result = await b.resolve("@react-email/components", options);
1226
- if (result.errors.length > 0 && result.errors[0]) {
1227
- 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?";
1228
- }
1229
- return result;
1230
- }
1231
- );
1232
- }
870
+ loader: path.extname(pathToFile).slice(1)
871
+ };
872
+ });
873
+ b.onResolve({ filter: /^react-email-module-that-will-export-render$/ }, async (args) => {
874
+ const options = {
875
+ kind: "import-statement",
876
+ importer: args.importer,
877
+ resolveDir: args.resolveDir,
878
+ namespace: args.namespace
879
+ };
880
+ let result = await b.resolve("@react-email/render", options);
881
+ if (result.errors.length === 0) return result;
882
+ result = await b.resolve("@react-email/components", options);
883
+ if (result.errors.length > 0 && result.errors[0]) 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?";
884
+ return result;
885
+ });
886
+ }
1233
887
  });
1234
888
 
1235
- // src/commands/export.ts
1236
- var getEmailTemplatesFromDirectory = (emailDirectory) => {
1237
- const templatePaths = [];
1238
- emailDirectory.emailFilenames.forEach(
1239
- (filename2) => templatePaths.push(path12.join(emailDirectory.absolutePath, filename2))
1240
- );
1241
- emailDirectory.subDirectories.forEach((directory) => {
1242
- templatePaths.push(...getEmailTemplatesFromDirectory(directory));
1243
- });
1244
- return templatePaths;
889
+ //#endregion
890
+ //#region src/commands/export.ts
891
+ const getEmailTemplatesFromDirectory = (emailDirectory) => {
892
+ const templatePaths = [];
893
+ emailDirectory.emailFilenames.forEach((filename$1) => templatePaths.push(path.join(emailDirectory.absolutePath, filename$1)));
894
+ emailDirectory.subDirectories.forEach((directory) => {
895
+ templatePaths.push(...getEmailTemplatesFromDirectory(directory));
896
+ });
897
+ return templatePaths;
1245
898
  };
1246
- var filename = url3.fileURLToPath(import.meta.url);
1247
- var require2 = createRequire(filename);
1248
- var exportTemplates = async (pathToWhereEmailMarkupShouldBeDumped, emailsDirectoryPath, options) => {
1249
- if (fs8.existsSync(pathToWhereEmailMarkupShouldBeDumped)) {
1250
- fs8.rmSync(pathToWhereEmailMarkupShouldBeDumped, { recursive: true });
1251
- }
1252
- let spinner;
1253
- if (!options.silent) {
1254
- spinner = ora3("Preparing files...\n").start();
1255
- registerSpinnerAutostopping(spinner);
1256
- }
1257
- const emailsDirectoryMetadata = await getEmailsDirectoryMetadata(
1258
- path12.resolve(process.cwd(), emailsDirectoryPath),
1259
- true
1260
- );
1261
- if (typeof emailsDirectoryMetadata === "undefined") {
1262
- if (spinner) {
1263
- spinner.stopAndPersist({
1264
- symbol: logSymbols4.error,
1265
- text: `Could not find the directory at ${emailsDirectoryPath}`
1266
- });
1267
- }
1268
- return;
1269
- }
1270
- const allTemplates = getEmailTemplatesFromDirectory(emailsDirectoryMetadata);
1271
- try {
1272
- await build2({
1273
- bundle: true,
1274
- entryPoints: allTemplates,
1275
- format: "cjs",
1276
- jsx: "automatic",
1277
- loader: { ".js": "jsx" },
1278
- logLevel: "silent",
1279
- outExtension: { ".js": ".cjs" },
1280
- outdir: pathToWhereEmailMarkupShouldBeDumped,
1281
- platform: "node",
1282
- plugins: [renderingUtilitiesExporter(allTemplates)],
1283
- write: true
1284
- });
1285
- } catch (exception) {
1286
- if (spinner) {
1287
- spinner.stopAndPersist({
1288
- symbol: logSymbols4.error,
1289
- text: "Failed to build emails"
1290
- });
1291
- }
1292
- const buildFailure = exception;
1293
- console.error(`
1294
- ${buildFailure.message}`);
1295
- process.exit(1);
1296
- }
1297
- if (spinner) {
1298
- spinner.succeed();
1299
- }
1300
- const allBuiltTemplates = glob.sync(
1301
- normalize(`${pathToWhereEmailMarkupShouldBeDumped}/**/*.cjs`),
1302
- {
1303
- absolute: true
1304
- }
1305
- );
1306
- for await (const template of allBuiltTemplates) {
1307
- try {
1308
- if (spinner) {
1309
- spinner.text = `rendering ${template.split("/").pop()}`;
1310
- spinner.render();
1311
- }
1312
- delete require2.cache[template];
1313
- const emailModule = require2(template);
1314
- const rendered = await emailModule.render(
1315
- emailModule.reactEmailCreateReactElement(emailModule.default, {}),
1316
- options
1317
- );
1318
- const htmlPath = template.replace(
1319
- ".cjs",
1320
- options.plainText ? ".txt" : ".html"
1321
- );
1322
- writeFileSync(htmlPath, rendered);
1323
- unlinkSync(template);
1324
- } catch (exception) {
1325
- if (spinner) {
1326
- spinner.stopAndPersist({
1327
- symbol: logSymbols4.error,
1328
- text: `failed when rendering ${template.split("/").pop()}`
1329
- });
1330
- }
1331
- console.error(exception);
1332
- process.exit(1);
1333
- }
1334
- }
1335
- if (spinner) {
1336
- spinner.succeed("Rendered all files");
1337
- spinner.text = "Copying static files";
1338
- spinner.render();
1339
- }
1340
- const staticDirectoryPath = path12.join(emailsDirectoryPath, "static");
1341
- if (fs8.existsSync(staticDirectoryPath)) {
1342
- const pathToDumpStaticFilesInto = path12.join(
1343
- pathToWhereEmailMarkupShouldBeDumped,
1344
- "static"
1345
- );
1346
- if (fs8.existsSync(pathToDumpStaticFilesInto))
1347
- await fs8.promises.rm(pathToDumpStaticFilesInto, { recursive: true });
1348
- try {
1349
- await fs8.promises.cp(staticDirectoryPath, pathToDumpStaticFilesInto, {
1350
- recursive: true
1351
- });
1352
- } catch (exception) {
1353
- console.error(exception);
1354
- if (spinner) {
1355
- spinner.stopAndPersist({
1356
- symbol: logSymbols4.error,
1357
- text: "Failed to copy static files"
1358
- });
1359
- }
1360
- console.error(
1361
- `Something went wrong while copying the file to ${pathToWhereEmailMarkupShouldBeDumped}/static, ${exception}`
1362
- );
1363
- process.exit(1);
1364
- }
1365
- }
1366
- if (spinner && !options.silent) {
1367
- spinner.succeed();
1368
- const fileTree = await tree(pathToWhereEmailMarkupShouldBeDumped, 4);
1369
- console.log(fileTree);
1370
- spinner.stopAndPersist({
1371
- symbol: logSymbols4.success,
1372
- text: "Successfully exported emails"
1373
- });
1374
- }
899
+ const filename = url.fileURLToPath(import.meta.url);
900
+ const require = createRequire(filename);
901
+ const exportTemplates = async (pathToWhereEmailMarkupShouldBeDumped, emailsDirectoryPath, options) => {
902
+ if (fs.existsSync(pathToWhereEmailMarkupShouldBeDumped)) fs.rmSync(pathToWhereEmailMarkupShouldBeDumped, { recursive: true });
903
+ let spinner;
904
+ if (!options.silent) {
905
+ spinner = ora("Preparing files...\n").start();
906
+ registerSpinnerAutostopping(spinner);
907
+ }
908
+ const emailsDirectoryMetadata = await getEmailsDirectoryMetadata(path.resolve(process.cwd(), emailsDirectoryPath), true);
909
+ if (typeof emailsDirectoryMetadata === "undefined") {
910
+ if (spinner) spinner.stopAndPersist({
911
+ symbol: logSymbols.error,
912
+ text: `Could not find the directory at ${emailsDirectoryPath}`
913
+ });
914
+ return;
915
+ }
916
+ const allTemplates = getEmailTemplatesFromDirectory(emailsDirectoryMetadata);
917
+ try {
918
+ await build({
919
+ bundle: true,
920
+ entryPoints: allTemplates,
921
+ format: "cjs",
922
+ jsx: "automatic",
923
+ loader: { ".js": "jsx" },
924
+ logLevel: "silent",
925
+ outExtension: { ".js": ".cjs" },
926
+ outdir: pathToWhereEmailMarkupShouldBeDumped,
927
+ platform: "node",
928
+ plugins: [renderingUtilitiesExporter(allTemplates)],
929
+ write: true
930
+ });
931
+ } catch (exception) {
932
+ if (spinner) spinner.stopAndPersist({
933
+ symbol: logSymbols.error,
934
+ text: "Failed to build emails"
935
+ });
936
+ const buildFailure = exception;
937
+ console.error(`\n${buildFailure.message}`);
938
+ process.exit(1);
939
+ }
940
+ if (spinner) spinner.succeed();
941
+ const allBuiltTemplates = glob.sync(normalize(`${pathToWhereEmailMarkupShouldBeDumped}/**/*.cjs`), { absolute: true });
942
+ for await (const template of allBuiltTemplates) try {
943
+ if (spinner) {
944
+ spinner.text = `rendering ${template.split("/").pop()}`;
945
+ spinner.render();
946
+ }
947
+ delete require.cache[template];
948
+ const emailModule = require(template);
949
+ const rendered = await emailModule.render(emailModule.reactEmailCreateReactElement(emailModule.default, {}), options);
950
+ const htmlPath = template.replace(".cjs", options.plainText ? ".txt" : ".html");
951
+ writeFileSync(htmlPath, rendered);
952
+ unlinkSync(template);
953
+ } catch (exception) {
954
+ if (spinner) spinner.stopAndPersist({
955
+ symbol: logSymbols.error,
956
+ text: `failed when rendering ${template.split("/").pop()}`
957
+ });
958
+ console.error(exception);
959
+ process.exit(1);
960
+ }
961
+ if (spinner) {
962
+ spinner.succeed("Rendered all files");
963
+ spinner.text = "Copying static files";
964
+ spinner.render();
965
+ }
966
+ const staticDirectoryPath = path.join(emailsDirectoryPath, "static");
967
+ if (fs.existsSync(staticDirectoryPath)) {
968
+ const pathToDumpStaticFilesInto = path.join(pathToWhereEmailMarkupShouldBeDumped, "static");
969
+ if (fs.existsSync(pathToDumpStaticFilesInto)) await fs.promises.rm(pathToDumpStaticFilesInto, { recursive: true });
970
+ try {
971
+ await fs.promises.cp(staticDirectoryPath, pathToDumpStaticFilesInto, { recursive: true });
972
+ } catch (exception) {
973
+ console.error(exception);
974
+ if (spinner) spinner.stopAndPersist({
975
+ symbol: logSymbols.error,
976
+ text: "Failed to copy static files"
977
+ });
978
+ console.error(`Something went wrong while copying the file to ${pathToWhereEmailMarkupShouldBeDumped}/static, ${exception}`);
979
+ process.exit(1);
980
+ }
981
+ }
982
+ if (spinner && !options.silent) {
983
+ spinner.succeed();
984
+ const fileTree = await tree(pathToWhereEmailMarkupShouldBeDumped, 4);
985
+ console.log(fileTree);
986
+ spinner.stopAndPersist({
987
+ symbol: logSymbols.success,
988
+ text: "Successfully exported emails"
989
+ });
990
+ }
1375
991
  };
1376
992
 
1377
- // src/commands/start.ts
1378
- import { spawn as spawn2 } from "node:child_process";
1379
- import fs9 from "node:fs";
1380
- import path13 from "node:path";
1381
- var start = async () => {
1382
- try {
1383
- const previewServerLocation = await getPreviewServerLocation();
1384
- const usersProjectLocation = process.cwd();
1385
- const builtPreviewPath = path13.resolve(
1386
- usersProjectLocation,
1387
- "./.react-email"
1388
- );
1389
- if (!fs9.existsSync(builtPreviewPath)) {
1390
- console.error(
1391
- "Could not find .react-email, maybe you haven't ran email build?"
1392
- );
1393
- process.exit(1);
1394
- }
1395
- const nextStart = spawn2("npx", ["next", "start", builtPreviewPath], {
1396
- cwd: previewServerLocation,
1397
- stdio: "inherit"
1398
- });
1399
- process.on("SIGINT", () => {
1400
- nextStart.kill("SIGINT");
1401
- });
1402
- nextStart.on("exit", (code) => {
1403
- process.exit(code ?? 0);
1404
- });
1405
- } catch (error) {
1406
- console.log(error);
1407
- process.exit(1);
1408
- }
993
+ //#endregion
994
+ //#region src/commands/start.ts
995
+ const start = async () => {
996
+ try {
997
+ const previewServerLocation = await getPreviewServerLocation();
998
+ const usersProjectLocation = process.cwd();
999
+ const builtPreviewPath = path.resolve(usersProjectLocation, "./.react-email");
1000
+ if (!fs.existsSync(builtPreviewPath)) {
1001
+ console.error("Could not find .react-email, maybe you haven't ran email build?");
1002
+ process.exit(1);
1003
+ }
1004
+ const nextStart = spawn("npx", [
1005
+ "next",
1006
+ "start",
1007
+ builtPreviewPath
1008
+ ], {
1009
+ cwd: previewServerLocation,
1010
+ stdio: "inherit"
1011
+ });
1012
+ process.on("SIGINT", () => {
1013
+ nextStart.kill("SIGINT");
1014
+ });
1015
+ nextStart.on("exit", (code) => {
1016
+ process.exit(code ?? 0);
1017
+ });
1018
+ } catch (error) {
1019
+ console.log(error);
1020
+ process.exit(1);
1021
+ }
1409
1022
  };
1410
1023
 
1411
- // src/index.ts
1412
- var PACKAGE_NAME = "react-email";
1413
- program.name(PACKAGE_NAME).description("A live preview of your emails right in your browser").version(package_default.version);
1024
+ //#endregion
1025
+ //#region src/index.ts
1026
+ program.name("react-email").description("A live preview of your emails right in your browser").version(package_default.version);
1414
1027
  program.command("dev").description("Starts the preview email development app").option("-d, --dir <path>", "Directory with your email templates", "./emails").option("-p --port <port>", "Port to run dev server on", "3000").action(dev);
1415
- program.command("build").description("Copies the preview app for onto .react-email and builds it").option("-d, --dir <path>", "Directory with your email templates", "./emails").option(
1416
- "-p --packageManager <name>",
1417
- "Package name to use on installation on `.react-email`",
1418
- "npm"
1419
- ).action(build);
1420
- program.command("start").description('Runs the built preview app that is inside of ".react-email"').action(start);
1421
- 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(
1422
- "-s, --silent",
1423
- "To, or not to show a spinner with process information",
1424
- false
1425
- ).action(
1426
- ({ outDir, pretty, plainText, silent, dir: srcDir }) => exportTemplates(outDir, srcDir, { silent, plainText, pretty })
1427
- );
1028
+ program.command("build").description("Copies the preview app for onto .react-email and builds it").option("-d, --dir <path>", "Directory with your email templates", "./emails").option("-p --packageManager <name>", "Package name to use on installation on `.react-email`", "npm").action(build$1);
1029
+ program.command("start").description("Runs the built preview app that is inside of \".react-email\"").action(start);
1030
+ 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("-s, --silent", "To, or not to show a spinner with process information", false).action(({ outDir, pretty, plainText, silent, dir: srcDir }) => exportTemplates(outDir, srcDir, {
1031
+ silent,
1032
+ plainText,
1033
+ pretty
1034
+ }));
1428
1035
  program.parse();
1036
+
1037
+ //#endregion
1038
+ export { };