react-email 2.1.3 → 2.1.4
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/cli/index.js +464 -367
- package/cli/index.mjs +304 -239
- package/package.json +6 -5
- package/src/actions/get-email-path-from-slug.ts +5 -3
- package/src/utils/render-resolver-esbuild-plugin.ts +11 -1
package/cli/index.mjs
CHANGED
|
@@ -13,7 +13,7 @@ import { program } from "commander";
|
|
|
13
13
|
// package.json
|
|
14
14
|
var package_default = {
|
|
15
15
|
name: "react-email",
|
|
16
|
-
version: "2.1.
|
|
16
|
+
version: "2.1.4",
|
|
17
17
|
description: "A live preview of your emails right in your browser.",
|
|
18
18
|
bin: {
|
|
19
19
|
email: "./cli/index.js"
|
|
@@ -40,7 +40,8 @@ var package_default = {
|
|
|
40
40
|
node: ">=18.0.0"
|
|
41
41
|
},
|
|
42
42
|
dependencies: {
|
|
43
|
-
"@babel/
|
|
43
|
+
"@babel/core": "7.24.5",
|
|
44
|
+
"@babel/parser": "7.24.5",
|
|
44
45
|
"@radix-ui/colors": "1.0.1",
|
|
45
46
|
"@radix-ui/react-collapsible": "1.0.3",
|
|
46
47
|
"@radix-ui/react-popover": "1.0.7",
|
|
@@ -52,7 +53,6 @@ var package_default = {
|
|
|
52
53
|
"@types/react-dom": "^18.2.0",
|
|
53
54
|
"@types/webpack": "5.28.5",
|
|
54
55
|
autoprefixer: "10.4.14",
|
|
55
|
-
"babel-walk": "3.0.0",
|
|
56
56
|
chalk: "4.1.2",
|
|
57
57
|
chokidar: "3.5.3",
|
|
58
58
|
clsx: "2.1.0",
|
|
@@ -83,8 +83,9 @@ var package_default = {
|
|
|
83
83
|
typescript: "5.1.6"
|
|
84
84
|
},
|
|
85
85
|
devDependencies: {
|
|
86
|
-
"@react-email/components": "0.0.
|
|
87
|
-
"@react-email/render": "0.0.
|
|
86
|
+
"@react-email/components": "0.0.19",
|
|
87
|
+
"@react-email/render": "0.0.15",
|
|
88
|
+
"@types/babel__core": "7.20.5",
|
|
88
89
|
"@types/fs-extra": "11.0.1",
|
|
89
90
|
"@types/mime-types": "2.1.4",
|
|
90
91
|
"@types/node": "18.0.0",
|
|
@@ -158,33 +159,18 @@ var tree = async (dirPath, depth) => {
|
|
|
158
159
|
};
|
|
159
160
|
|
|
160
161
|
// src/cli/utils/preview/hot-reloading/setup-hot-reloading.ts
|
|
161
|
-
import
|
|
162
|
+
import path6 from "path";
|
|
162
163
|
import { Server as SocketServer } from "socket.io";
|
|
163
164
|
import { watch } from "chokidar";
|
|
164
165
|
import debounce from "debounce";
|
|
165
166
|
|
|
166
167
|
// src/cli/utils/preview/hot-reloading/create-dependency-graph.ts
|
|
167
|
-
import
|
|
168
|
-
import { promises as
|
|
168
|
+
import path5 from "path";
|
|
169
|
+
import { existsSync, promises as fs3, statSync } from "fs";
|
|
169
170
|
|
|
170
171
|
// src/cli/utils/preview/hot-reloading/get-imported-modules.ts
|
|
172
|
+
import { traverse } from "@babel/core";
|
|
171
173
|
import { parse } from "@babel/parser";
|
|
172
|
-
import * as walk from "babel-walk";
|
|
173
|
-
var importVisitor = walk.simple({
|
|
174
|
-
ImportDeclaration(node, importedPaths) {
|
|
175
|
-
importedPaths.push(node.source.value);
|
|
176
|
-
},
|
|
177
|
-
CallExpression(node, importedPaths) {
|
|
178
|
-
if ("name" in node.callee && node.callee.name === "require") {
|
|
179
|
-
if (node.arguments.length === 1) {
|
|
180
|
-
const importPathNode = node.arguments[0];
|
|
181
|
-
if (importPathNode.type === "StringLiteral") {
|
|
182
|
-
importedPaths.push(importPathNode.value);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
174
|
var getImportedModules = (contents) => {
|
|
189
175
|
const importedPaths = [];
|
|
190
176
|
const parsedContents = parse(contents, {
|
|
@@ -193,16 +179,227 @@ var getImportedModules = (contents) => {
|
|
|
193
179
|
errorRecovery: true,
|
|
194
180
|
plugins: ["jsx", "typescript"]
|
|
195
181
|
});
|
|
196
|
-
|
|
182
|
+
traverse(parsedContents, {
|
|
183
|
+
ImportDeclaration({ node }) {
|
|
184
|
+
importedPaths.push(node.source.value);
|
|
185
|
+
},
|
|
186
|
+
ExportAllDeclaration({ node }) {
|
|
187
|
+
importedPaths.push(node.source.value);
|
|
188
|
+
},
|
|
189
|
+
ExportNamedDeclaration({ node }) {
|
|
190
|
+
if (node.source) {
|
|
191
|
+
importedPaths.push(node.source.value);
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
CallExpression({ node }) {
|
|
195
|
+
if ("name" in node.callee && node.callee.name === "require") {
|
|
196
|
+
if (node.arguments.length === 1) {
|
|
197
|
+
const importPathNode = node.arguments[0];
|
|
198
|
+
if (importPathNode.type === "StringLiteral") {
|
|
199
|
+
importedPaths.push(importPathNode.value);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
});
|
|
197
205
|
return importedPaths;
|
|
198
206
|
};
|
|
199
207
|
|
|
208
|
+
// src/cli/utils/preview/start-dev-server.ts
|
|
209
|
+
import path4 from "path";
|
|
210
|
+
import http from "http";
|
|
211
|
+
import url from "url";
|
|
212
|
+
import next from "next";
|
|
213
|
+
import ora from "ora";
|
|
214
|
+
import logSymbols from "log-symbols";
|
|
215
|
+
import chalk from "chalk";
|
|
216
|
+
|
|
217
|
+
// src/cli/utils/close-ora-on-sigint.ts
|
|
218
|
+
var closeOraOnSIGNIT = (spinner) => {
|
|
219
|
+
process.on("SIGINT", () => {
|
|
220
|
+
spinner.stop();
|
|
221
|
+
});
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// src/cli/utils/preview/serve-static-file.ts
|
|
225
|
+
import path2 from "path";
|
|
226
|
+
import { promises as fs2 } from "fs";
|
|
227
|
+
import { lookup } from "mime-types";
|
|
228
|
+
var serveStaticFile = async (res, parsedUrl, staticDirRelativePath) => {
|
|
229
|
+
const staticBaseDir = path2.join(process.cwd(), staticDirRelativePath);
|
|
230
|
+
const pathname = parsedUrl.pathname;
|
|
231
|
+
const ext = path2.parse(pathname).ext;
|
|
232
|
+
let fileAbsolutePath = path2.join(staticBaseDir, pathname);
|
|
233
|
+
const fileHandle = await fs2.open(fileAbsolutePath, "r");
|
|
234
|
+
try {
|
|
235
|
+
const fileData = await fs2.readFile(fileHandle);
|
|
236
|
+
res.setHeader("Content-type", lookup(ext) || "text/plain");
|
|
237
|
+
res.end(fileData);
|
|
238
|
+
} catch (exception) {
|
|
239
|
+
console.error(
|
|
240
|
+
`Could not read file at ${fileAbsolutePath} to be served, here's the exception:`,
|
|
241
|
+
exception
|
|
242
|
+
);
|
|
243
|
+
res.statusCode = 500;
|
|
244
|
+
res.end(
|
|
245
|
+
`Could not read file to be served! Check your terminal for more information.`
|
|
246
|
+
);
|
|
247
|
+
} finally {
|
|
248
|
+
fileHandle.close();
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// src/cli/utils/preview/get-env-variables-for-preview-app.ts
|
|
253
|
+
import path3 from "path";
|
|
254
|
+
var getEnvVariablesForPreviewApp = (relativePathToEmailsDirectory, cliPackageLocation, cwd) => {
|
|
255
|
+
return {
|
|
256
|
+
NEXT_PUBLIC_EMAILS_DIR_RELATIVE_PATH: relativePathToEmailsDirectory,
|
|
257
|
+
NEXT_PUBLIC_CLI_PACKAGE_LOCATION: cliPackageLocation,
|
|
258
|
+
NEXT_PUBLIC_OS_PATH_SEPARATOR: path3.sep,
|
|
259
|
+
NEXT_PUBLIC_USER_PROJECT_LOCATION: cwd
|
|
260
|
+
};
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// src/cli/utils/preview/start-dev-server.ts
|
|
264
|
+
var devServer;
|
|
265
|
+
var safeAsyncServerListen = (server, port) => {
|
|
266
|
+
return new Promise((resolve) => {
|
|
267
|
+
server.listen(port, () => {
|
|
268
|
+
resolve({ portAlreadyInUse: false });
|
|
269
|
+
});
|
|
270
|
+
server.on("error", (e) => {
|
|
271
|
+
if (e.code === "EADDRINUSE") {
|
|
272
|
+
resolve({ portAlreadyInUse: true });
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
};
|
|
277
|
+
var isRunningBuilt = __filename.endsWith(path4.join("cli", "index.js"));
|
|
278
|
+
var cliPacakgeLocation = isRunningBuilt ? path4.resolve(__dirname, "..") : path4.resolve(__dirname, "../../../..");
|
|
279
|
+
var startDevServer = async (emailsDirRelativePath, staticBaseDirRelativePath, port) => {
|
|
280
|
+
devServer = http.createServer((req, res) => {
|
|
281
|
+
if (!req.url) {
|
|
282
|
+
res.end(404);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
const parsedUrl = url.parse(req.url, true);
|
|
286
|
+
res.setHeader(
|
|
287
|
+
"Cache-Control",
|
|
288
|
+
"no-cache, max-age=0, must-revalidate, no-store"
|
|
289
|
+
);
|
|
290
|
+
res.setHeader("Pragma", "no-cache");
|
|
291
|
+
res.setHeader("Expires", "-1");
|
|
292
|
+
try {
|
|
293
|
+
if (parsedUrl.path && parsedUrl.path.includes("static/") && !parsedUrl.path.includes("_next/static/")) {
|
|
294
|
+
void serveStaticFile(res, parsedUrl, staticBaseDirRelativePath);
|
|
295
|
+
} else if (!isNextReady) {
|
|
296
|
+
void nextReadyPromise.then(
|
|
297
|
+
() => nextHandleRequest?.(req, res, parsedUrl)
|
|
298
|
+
);
|
|
299
|
+
} else {
|
|
300
|
+
void nextHandleRequest?.(req, res, parsedUrl);
|
|
301
|
+
}
|
|
302
|
+
} catch (e) {
|
|
303
|
+
console.error("caught error", e);
|
|
304
|
+
res.writeHead(500);
|
|
305
|
+
res.end();
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
const { portAlreadyInUse } = await safeAsyncServerListen(devServer, port);
|
|
309
|
+
if (!portAlreadyInUse) {
|
|
310
|
+
console.log(chalk.greenBright(` React Email ${package_default.version}`));
|
|
311
|
+
console.log(` Running preview at: http://localhost:${port}
|
|
312
|
+
`);
|
|
313
|
+
} else {
|
|
314
|
+
const nextPortToTry = port + 1;
|
|
315
|
+
console.warn(
|
|
316
|
+
` ${logSymbols.warning} Port ${port} is already in use, trying ${nextPortToTry}`
|
|
317
|
+
);
|
|
318
|
+
return startDevServer(
|
|
319
|
+
emailsDirRelativePath,
|
|
320
|
+
staticBaseDirRelativePath,
|
|
321
|
+
nextPortToTry
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
devServer.on("close", async () => {
|
|
325
|
+
await app.close();
|
|
326
|
+
});
|
|
327
|
+
devServer.on("error", (e) => {
|
|
328
|
+
console.error(
|
|
329
|
+
` ${logSymbols.error} preview server error: `,
|
|
330
|
+
JSON.stringify(e)
|
|
331
|
+
);
|
|
332
|
+
process.exit(1);
|
|
333
|
+
});
|
|
334
|
+
const spinner = ora({
|
|
335
|
+
text: "Getting react-email preview server ready...\n",
|
|
336
|
+
prefixText: " "
|
|
337
|
+
}).start();
|
|
338
|
+
closeOraOnSIGNIT(spinner);
|
|
339
|
+
const timeBeforeNextReady = performance.now();
|
|
340
|
+
process.env = {
|
|
341
|
+
...process.env,
|
|
342
|
+
...getEnvVariablesForPreviewApp(
|
|
343
|
+
// If we don't do normalization here, stuff like https://github.com/resend/react-email/issues/1354 happens.
|
|
344
|
+
path4.normalize(emailsDirRelativePath),
|
|
345
|
+
cliPacakgeLocation,
|
|
346
|
+
process.cwd()
|
|
347
|
+
)
|
|
348
|
+
};
|
|
349
|
+
const app = next({
|
|
350
|
+
// passing in env here does not get the environment variables there
|
|
351
|
+
dev: true,
|
|
352
|
+
hostname: "localhost",
|
|
353
|
+
port,
|
|
354
|
+
dir: cliPacakgeLocation
|
|
355
|
+
});
|
|
356
|
+
let isNextReady = false;
|
|
357
|
+
const nextReadyPromise = app.prepare();
|
|
358
|
+
await nextReadyPromise;
|
|
359
|
+
isNextReady = true;
|
|
360
|
+
const nextHandleRequest = app.getRequestHandler();
|
|
361
|
+
const secondsToNextReady = ((performance.now() - timeBeforeNextReady) / 1e3).toFixed(1);
|
|
362
|
+
spinner.stopAndPersist({
|
|
363
|
+
text: `Ready in ${secondsToNextReady}s
|
|
364
|
+
`,
|
|
365
|
+
symbol: logSymbols.success
|
|
366
|
+
});
|
|
367
|
+
return devServer;
|
|
368
|
+
};
|
|
369
|
+
var makeExitHandler = (options) => (_codeOrSignal) => {
|
|
370
|
+
if (typeof devServer !== "undefined") {
|
|
371
|
+
console.log("\nshutting down dev server");
|
|
372
|
+
devServer.close();
|
|
373
|
+
devServer = void 0;
|
|
374
|
+
}
|
|
375
|
+
if (options?.shouldKillProcess) {
|
|
376
|
+
process.exit(options.killWithErrorCode ? 1 : 0);
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
process.on("exit", makeExitHandler());
|
|
380
|
+
process.on(
|
|
381
|
+
"SIGINT",
|
|
382
|
+
makeExitHandler({ shouldKillProcess: true, killWithErrorCode: false })
|
|
383
|
+
);
|
|
384
|
+
process.on(
|
|
385
|
+
"SIGUSR1",
|
|
386
|
+
makeExitHandler({ shouldKillProcess: true, killWithErrorCode: false })
|
|
387
|
+
);
|
|
388
|
+
process.on(
|
|
389
|
+
"SIGUSR2",
|
|
390
|
+
makeExitHandler({ shouldKillProcess: true, killWithErrorCode: false })
|
|
391
|
+
);
|
|
392
|
+
process.on(
|
|
393
|
+
"uncaughtException",
|
|
394
|
+
makeExitHandler({ shouldKillProcess: true, killWithErrorCode: true })
|
|
395
|
+
);
|
|
396
|
+
|
|
200
397
|
// src/cli/utils/preview/hot-reloading/create-dependency-graph.ts
|
|
201
398
|
var readAllFilesInsideDirectory = async (directory) => {
|
|
202
399
|
let allFilePaths = [];
|
|
203
|
-
const topLevelDirents = await
|
|
400
|
+
const topLevelDirents = await fs3.readdir(directory, { withFileTypes: true });
|
|
204
401
|
for await (const dirent of topLevelDirents) {
|
|
205
|
-
const pathToDirent =
|
|
402
|
+
const pathToDirent = path5.join(directory, dirent.name);
|
|
206
403
|
if (dirent.isDirectory()) {
|
|
207
404
|
allFilePaths = allFilePaths.concat(
|
|
208
405
|
await readAllFilesInsideDirectory(pathToDirent)
|
|
@@ -214,9 +411,24 @@ var readAllFilesInsideDirectory = async (directory) => {
|
|
|
214
411
|
return allFilePaths;
|
|
215
412
|
};
|
|
216
413
|
var isJavascriptModule = (filePath) => {
|
|
217
|
-
const extensionName =
|
|
414
|
+
const extensionName = path5.extname(filePath);
|
|
218
415
|
return [".js", ".ts", ".jsx", ".tsx", ".mjs", ".cjs"].includes(extensionName);
|
|
219
416
|
};
|
|
417
|
+
var checkFileExtensionsUntilItExists = (pathWithoutExtension) => {
|
|
418
|
+
if (existsSync(`${pathWithoutExtension}.ts`)) {
|
|
419
|
+
return `${pathWithoutExtension}.ts`;
|
|
420
|
+
} else if (existsSync(`${pathWithoutExtension}.tsx`)) {
|
|
421
|
+
return `${pathWithoutExtension}.tsx`;
|
|
422
|
+
} else if (existsSync(`${pathWithoutExtension}.js`)) {
|
|
423
|
+
return `${pathWithoutExtension}.js`;
|
|
424
|
+
} else if (existsSync(`${pathWithoutExtension}.jsx`)) {
|
|
425
|
+
return `${pathWithoutExtension}.jsx`;
|
|
426
|
+
} else if (existsSync(`${pathWithoutExtension}.mjs`)) {
|
|
427
|
+
return `${pathWithoutExtension}.mjs`;
|
|
428
|
+
} else if (existsSync(`${pathWithoutExtension}.cjs`)) {
|
|
429
|
+
return `${pathWithoutExtension}.cjs`;
|
|
430
|
+
}
|
|
431
|
+
};
|
|
220
432
|
var createDependencyGraph = async (directory) => {
|
|
221
433
|
const filePaths = await readAllFilesInsideDirectory(directory);
|
|
222
434
|
const modulePaths = filePaths.filter(isJavascriptModule);
|
|
@@ -232,13 +444,13 @@ var createDependencyGraph = async (directory) => {
|
|
|
232
444
|
])
|
|
233
445
|
);
|
|
234
446
|
const getDependencyPaths = async (filePath) => {
|
|
235
|
-
const contents = await
|
|
447
|
+
const contents = await fs3.readFile(filePath, "utf8");
|
|
236
448
|
const importedPaths = getImportedModules(contents);
|
|
237
449
|
const importedPathsRelativeToDirectory = importedPaths.map(
|
|
238
450
|
(dependencyPath) => {
|
|
239
451
|
const isModulePath = !dependencyPath.startsWith(".");
|
|
240
|
-
if (!isModulePath && !
|
|
241
|
-
let pathToDependencyFromDirectory =
|
|
452
|
+
if (!isModulePath && !path5.isAbsolute(dependencyPath)) {
|
|
453
|
+
let pathToDependencyFromDirectory = path5.resolve(
|
|
242
454
|
/*
|
|
243
455
|
path.resolve resolves paths differently from what imports on javascript do.
|
|
244
456
|
|
|
@@ -246,13 +458,38 @@ var createDependencyGraph = async (directory) => {
|
|
|
246
458
|
would end up going into /path/to/email.tsx/other-email instead of /path/to/other-email which is the
|
|
247
459
|
one the import is meant to go to
|
|
248
460
|
*/
|
|
249
|
-
|
|
461
|
+
path5.dirname(filePath),
|
|
250
462
|
dependencyPath
|
|
251
463
|
);
|
|
464
|
+
let isDirectory = false;
|
|
465
|
+
try {
|
|
466
|
+
isDirectory = statSync(pathToDependencyFromDirectory).isDirectory();
|
|
467
|
+
} catch (_) {
|
|
468
|
+
}
|
|
469
|
+
if (isDirectory) {
|
|
470
|
+
const pathToSubDirectory = pathToDependencyFromDirectory;
|
|
471
|
+
const pathWithExtension = checkFileExtensionsUntilItExists(
|
|
472
|
+
`${pathToSubDirectory}/index`
|
|
473
|
+
);
|
|
474
|
+
if (pathWithExtension) {
|
|
475
|
+
pathToDependencyFromDirectory = pathWithExtension;
|
|
476
|
+
} else if (!isRunningBuilt) {
|
|
477
|
+
console.warn(
|
|
478
|
+
`Could not find index file for directory at ${pathToDependencyFromDirectory}. This is probably going to cause issues with both hot reloading and your code.`
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
252
482
|
if (!isJavascriptModule(pathToDependencyFromDirectory)) {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
)
|
|
483
|
+
const pathWithExtension = checkFileExtensionsUntilItExists(
|
|
484
|
+
pathToDependencyFromDirectory
|
|
485
|
+
);
|
|
486
|
+
if (pathWithExtension) {
|
|
487
|
+
pathToDependencyFromDirectory = pathWithExtension;
|
|
488
|
+
} else if (!isRunningBuilt) {
|
|
489
|
+
console.warn(
|
|
490
|
+
`Could not determine the file extension for the file at ${pathWithExtension}`
|
|
491
|
+
);
|
|
492
|
+
}
|
|
256
493
|
}
|
|
257
494
|
return pathToDependencyFromDirectory;
|
|
258
495
|
} else {
|
|
@@ -261,10 +498,10 @@ var createDependencyGraph = async (directory) => {
|
|
|
261
498
|
}
|
|
262
499
|
);
|
|
263
500
|
const moduleDependencies = importedPathsRelativeToDirectory.filter(
|
|
264
|
-
(dependencyPath) => !dependencyPath.startsWith(".") && !
|
|
501
|
+
(dependencyPath) => !dependencyPath.startsWith(".") && !path5.isAbsolute(dependencyPath)
|
|
265
502
|
);
|
|
266
503
|
const nonNodeModuleImportPathsRelativeToDirectory = importedPathsRelativeToDirectory.filter(
|
|
267
|
-
(dependencyPath) => dependencyPath.startsWith(".") ||
|
|
504
|
+
(dependencyPath) => dependencyPath.startsWith(".") || path5.isAbsolute(dependencyPath)
|
|
268
505
|
);
|
|
269
506
|
return {
|
|
270
507
|
dependencyPaths: nonNodeModuleImportPathsRelativeToDirectory,
|
|
@@ -375,7 +612,7 @@ var setupHotreloading = async (devServer2, emailDirRelativePath) => {
|
|
|
375
612
|
});
|
|
376
613
|
const watcher = watch(emailDirRelativePath, {
|
|
377
614
|
ignoreInitial: true,
|
|
378
|
-
cwd:
|
|
615
|
+
cwd: path6.resolve(process.cwd()),
|
|
379
616
|
// eslint-disable-next-line prefer-named-capture-group
|
|
380
617
|
ignored: /(^|[/\\])\../
|
|
381
618
|
});
|
|
@@ -391,19 +628,31 @@ var setupHotreloading = async (devServer2, emailDirRelativePath) => {
|
|
|
391
628
|
});
|
|
392
629
|
changes = [];
|
|
393
630
|
}, 150);
|
|
394
|
-
const absolutePathToEmailsDirectory =
|
|
631
|
+
const absolutePathToEmailsDirectory = path6.resolve(
|
|
395
632
|
process.cwd(),
|
|
396
633
|
emailDirRelativePath
|
|
397
634
|
);
|
|
398
635
|
const [dependencyGraph, updateDependencyGraph] = await createDependencyGraph(
|
|
399
636
|
absolutePathToEmailsDirectory
|
|
400
637
|
);
|
|
638
|
+
const resolveDependentsOf = (pathToChangeTarget) => {
|
|
639
|
+
const moduleEntry = dependencyGraph[pathToChangeTarget];
|
|
640
|
+
const dependentPaths = [];
|
|
641
|
+
if (moduleEntry) {
|
|
642
|
+
for (const dependentPath of moduleEntry.dependentPaths) {
|
|
643
|
+
const dependentsOfDependent = resolveDependentsOf(dependentPath);
|
|
644
|
+
dependentPaths.push(...dependentsOfDependent);
|
|
645
|
+
dependentPaths.push(dependentPath);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
return dependentPaths;
|
|
649
|
+
};
|
|
401
650
|
watcher.on("all", async (event, relativePathToChangeTarget) => {
|
|
402
|
-
const file = relativePathToChangeTarget.split(
|
|
651
|
+
const file = relativePathToChangeTarget.split(path6.sep);
|
|
403
652
|
if (file.length === 0) {
|
|
404
653
|
return;
|
|
405
654
|
}
|
|
406
|
-
const pathToChangeTarget =
|
|
655
|
+
const pathToChangeTarget = path6.resolve(
|
|
407
656
|
process.cwd(),
|
|
408
657
|
relativePathToChangeTarget
|
|
409
658
|
);
|
|
@@ -412,208 +661,17 @@ var setupHotreloading = async (devServer2, emailDirRelativePath) => {
|
|
|
412
661
|
event,
|
|
413
662
|
filename: relativePathToChangeTarget
|
|
414
663
|
});
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
)
|
|
422
|
-
);
|
|
664
|
+
for (const dependentPath of resolveDependentsOf(pathToChangeTarget)) {
|
|
665
|
+
changes.push({
|
|
666
|
+
event: "change",
|
|
667
|
+
filename: path6.relative(absolutePathToEmailsDirectory, dependentPath)
|
|
668
|
+
});
|
|
669
|
+
}
|
|
423
670
|
reload();
|
|
424
671
|
});
|
|
425
672
|
return watcher;
|
|
426
673
|
};
|
|
427
674
|
|
|
428
|
-
// src/cli/utils/preview/start-dev-server.ts
|
|
429
|
-
import path6 from "path";
|
|
430
|
-
import http from "http";
|
|
431
|
-
import url from "url";
|
|
432
|
-
import next from "next";
|
|
433
|
-
import ora from "ora";
|
|
434
|
-
import logSymbols from "log-symbols";
|
|
435
|
-
import chalk from "chalk";
|
|
436
|
-
|
|
437
|
-
// src/cli/utils/close-ora-on-sigint.ts
|
|
438
|
-
var closeOraOnSIGNIT = (spinner) => {
|
|
439
|
-
process.on("SIGINT", () => {
|
|
440
|
-
spinner.stop();
|
|
441
|
-
});
|
|
442
|
-
};
|
|
443
|
-
|
|
444
|
-
// src/cli/utils/preview/serve-static-file.ts
|
|
445
|
-
import path4 from "path";
|
|
446
|
-
import { promises as fs3 } from "fs";
|
|
447
|
-
import { lookup } from "mime-types";
|
|
448
|
-
var serveStaticFile = async (res, parsedUrl, staticDirRelativePath) => {
|
|
449
|
-
const staticBaseDir = path4.join(process.cwd(), staticDirRelativePath);
|
|
450
|
-
const pathname = parsedUrl.pathname;
|
|
451
|
-
const ext = path4.parse(pathname).ext;
|
|
452
|
-
let fileAbsolutePath = path4.join(staticBaseDir, pathname);
|
|
453
|
-
const fileHandle = await fs3.open(fileAbsolutePath, "r");
|
|
454
|
-
try {
|
|
455
|
-
const fileData = await fs3.readFile(fileHandle);
|
|
456
|
-
res.setHeader("Content-type", lookup(ext) || "text/plain");
|
|
457
|
-
res.end(fileData);
|
|
458
|
-
} catch (exception) {
|
|
459
|
-
console.error(
|
|
460
|
-
`Could not read file at ${fileAbsolutePath} to be served, here's the exception:`,
|
|
461
|
-
exception
|
|
462
|
-
);
|
|
463
|
-
res.statusCode = 500;
|
|
464
|
-
res.end(
|
|
465
|
-
`Could not read file to be served! Check your terminal for more information.`
|
|
466
|
-
);
|
|
467
|
-
} finally {
|
|
468
|
-
fileHandle.close();
|
|
469
|
-
}
|
|
470
|
-
};
|
|
471
|
-
|
|
472
|
-
// src/cli/utils/preview/get-env-variables-for-preview-app.ts
|
|
473
|
-
import path5 from "path";
|
|
474
|
-
var getEnvVariablesForPreviewApp = (relativePathToEmailsDirectory, cliPackageLocation, cwd) => {
|
|
475
|
-
return {
|
|
476
|
-
NEXT_PUBLIC_EMAILS_DIR_RELATIVE_PATH: relativePathToEmailsDirectory,
|
|
477
|
-
NEXT_PUBLIC_CLI_PACKAGE_LOCATION: cliPackageLocation,
|
|
478
|
-
NEXT_PUBLIC_OS_PATH_SEPARATOR: path5.sep,
|
|
479
|
-
NEXT_PUBLIC_USER_PROJECT_LOCATION: cwd
|
|
480
|
-
};
|
|
481
|
-
};
|
|
482
|
-
|
|
483
|
-
// src/cli/utils/preview/start-dev-server.ts
|
|
484
|
-
var devServer;
|
|
485
|
-
var safeAsyncServerListen = (server, port) => {
|
|
486
|
-
return new Promise((resolve) => {
|
|
487
|
-
server.listen(port, () => {
|
|
488
|
-
resolve({ portAlreadyInUse: false });
|
|
489
|
-
});
|
|
490
|
-
server.on("error", (e) => {
|
|
491
|
-
if (e.code === "EADDRINUSE") {
|
|
492
|
-
resolve({ portAlreadyInUse: true });
|
|
493
|
-
}
|
|
494
|
-
});
|
|
495
|
-
});
|
|
496
|
-
};
|
|
497
|
-
var isRunningBuilt = __filename.endsWith(path6.join("cli", "index.js"));
|
|
498
|
-
var cliPacakgeLocation = isRunningBuilt ? path6.resolve(__dirname, "..") : path6.resolve(__dirname, "../../../..");
|
|
499
|
-
var startDevServer = async (emailsDirRelativePath, staticBaseDirRelativePath, port) => {
|
|
500
|
-
devServer = http.createServer((req, res) => {
|
|
501
|
-
if (!req.url) {
|
|
502
|
-
res.end(404);
|
|
503
|
-
return;
|
|
504
|
-
}
|
|
505
|
-
const parsedUrl = url.parse(req.url, true);
|
|
506
|
-
res.setHeader(
|
|
507
|
-
"Cache-Control",
|
|
508
|
-
"no-cache, max-age=0, must-revalidate, no-store"
|
|
509
|
-
);
|
|
510
|
-
res.setHeader("Pragma", "no-cache");
|
|
511
|
-
res.setHeader("Expires", "-1");
|
|
512
|
-
try {
|
|
513
|
-
if (parsedUrl.path && parsedUrl.path.includes("static/") && !parsedUrl.path.includes("_next/static/")) {
|
|
514
|
-
void serveStaticFile(res, parsedUrl, staticBaseDirRelativePath);
|
|
515
|
-
} else if (!isNextReady) {
|
|
516
|
-
void nextReadyPromise.then(
|
|
517
|
-
() => nextHandleRequest?.(req, res, parsedUrl)
|
|
518
|
-
);
|
|
519
|
-
} else {
|
|
520
|
-
void nextHandleRequest?.(req, res, parsedUrl);
|
|
521
|
-
}
|
|
522
|
-
} catch (e) {
|
|
523
|
-
console.error("caught error", e);
|
|
524
|
-
res.writeHead(500);
|
|
525
|
-
res.end();
|
|
526
|
-
}
|
|
527
|
-
});
|
|
528
|
-
const { portAlreadyInUse } = await safeAsyncServerListen(devServer, port);
|
|
529
|
-
if (!portAlreadyInUse) {
|
|
530
|
-
console.log(chalk.greenBright(` React Email ${package_default.version}`));
|
|
531
|
-
console.log(` Running preview at: http://localhost:${port}
|
|
532
|
-
`);
|
|
533
|
-
} else {
|
|
534
|
-
const nextPortToTry = port + 1;
|
|
535
|
-
console.warn(
|
|
536
|
-
` ${logSymbols.warning} Port ${port} is already in use, trying ${nextPortToTry}`
|
|
537
|
-
);
|
|
538
|
-
return startDevServer(
|
|
539
|
-
emailsDirRelativePath,
|
|
540
|
-
staticBaseDirRelativePath,
|
|
541
|
-
nextPortToTry
|
|
542
|
-
);
|
|
543
|
-
}
|
|
544
|
-
devServer.on("close", async () => {
|
|
545
|
-
await app.close();
|
|
546
|
-
});
|
|
547
|
-
devServer.on("error", (e) => {
|
|
548
|
-
console.error(
|
|
549
|
-
` ${logSymbols.error} preview server error: `,
|
|
550
|
-
JSON.stringify(e)
|
|
551
|
-
);
|
|
552
|
-
process.exit(1);
|
|
553
|
-
});
|
|
554
|
-
const spinner = ora({
|
|
555
|
-
text: "Getting react-email preview server ready...\n",
|
|
556
|
-
prefixText: " "
|
|
557
|
-
}).start();
|
|
558
|
-
closeOraOnSIGNIT(spinner);
|
|
559
|
-
const timeBeforeNextReady = performance.now();
|
|
560
|
-
process.env = {
|
|
561
|
-
...process.env,
|
|
562
|
-
...getEnvVariablesForPreviewApp(
|
|
563
|
-
// If we don't do normalization here, stuff like https://github.com/resend/react-email/issues/1354 happens.
|
|
564
|
-
path6.normalize(emailsDirRelativePath),
|
|
565
|
-
cliPacakgeLocation,
|
|
566
|
-
process.cwd()
|
|
567
|
-
)
|
|
568
|
-
};
|
|
569
|
-
const app = next({
|
|
570
|
-
// passing in env here does not get the environment variables there
|
|
571
|
-
dev: true,
|
|
572
|
-
hostname: "localhost",
|
|
573
|
-
port,
|
|
574
|
-
dir: cliPacakgeLocation
|
|
575
|
-
});
|
|
576
|
-
let isNextReady = false;
|
|
577
|
-
const nextReadyPromise = app.prepare();
|
|
578
|
-
await nextReadyPromise;
|
|
579
|
-
isNextReady = true;
|
|
580
|
-
const nextHandleRequest = app.getRequestHandler();
|
|
581
|
-
const secondsToNextReady = ((performance.now() - timeBeforeNextReady) / 1e3).toFixed(1);
|
|
582
|
-
spinner.stopAndPersist({
|
|
583
|
-
text: `Ready in ${secondsToNextReady}s
|
|
584
|
-
`,
|
|
585
|
-
symbol: logSymbols.success
|
|
586
|
-
});
|
|
587
|
-
return devServer;
|
|
588
|
-
};
|
|
589
|
-
var makeExitHandler = (options) => (_codeOrSignal) => {
|
|
590
|
-
if (typeof devServer !== "undefined") {
|
|
591
|
-
console.log("\nshutting down dev server");
|
|
592
|
-
devServer.close();
|
|
593
|
-
devServer = void 0;
|
|
594
|
-
}
|
|
595
|
-
if (options?.shouldKillProcess) {
|
|
596
|
-
process.exit(options.killWithErrorCode ? 1 : 0);
|
|
597
|
-
}
|
|
598
|
-
};
|
|
599
|
-
process.on("exit", makeExitHandler());
|
|
600
|
-
process.on(
|
|
601
|
-
"SIGINT",
|
|
602
|
-
makeExitHandler({ shouldKillProcess: true, killWithErrorCode: false })
|
|
603
|
-
);
|
|
604
|
-
process.on(
|
|
605
|
-
"SIGUSR1",
|
|
606
|
-
makeExitHandler({ shouldKillProcess: true, killWithErrorCode: false })
|
|
607
|
-
);
|
|
608
|
-
process.on(
|
|
609
|
-
"SIGUSR2",
|
|
610
|
-
makeExitHandler({ shouldKillProcess: true, killWithErrorCode: false })
|
|
611
|
-
);
|
|
612
|
-
process.on(
|
|
613
|
-
"uncaughtException",
|
|
614
|
-
makeExitHandler({ shouldKillProcess: true, killWithErrorCode: true })
|
|
615
|
-
);
|
|
616
|
-
|
|
617
675
|
// src/cli/commands/dev.ts
|
|
618
676
|
var dev = async ({ dir: emailsDirRelativePath, port }) => {
|
|
619
677
|
try {
|
|
@@ -704,11 +762,18 @@ var getEmailsDirectoryMetadata = async (absolutePathToEmailsDirectory) => {
|
|
|
704
762
|
// src/utils/render-resolver-esbuild-plugin.ts
|
|
705
763
|
import path8 from "path";
|
|
706
764
|
import { promises as fs6 } from "fs";
|
|
765
|
+
function escapeStringForRegex(string) {
|
|
766
|
+
return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
|
|
767
|
+
}
|
|
707
768
|
var renderResolver = (emailTemplates) => ({
|
|
708
769
|
name: "render-resolver",
|
|
709
770
|
setup: (b) => {
|
|
710
771
|
b.onLoad(
|
|
711
|
-
{
|
|
772
|
+
{
|
|
773
|
+
filter: new RegExp(
|
|
774
|
+
emailTemplates.map((emailPath) => escapeStringForRegex(emailPath)).join("|")
|
|
775
|
+
)
|
|
776
|
+
},
|
|
712
777
|
async ({ path: pathToFile }) => {
|
|
713
778
|
return {
|
|
714
779
|
contents: `${await fs6.readFile(pathToFile, "utf8")};
|