react-email 5.0.0-canary.5 → 5.0.0-canary.7

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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # react-email
2
2
 
3
+ ## 5.0.0-canary.7
4
+
5
+ ## 5.0.0-canary.6
6
+
3
7
  ## 5.0.0-canary.5
4
8
 
5
9
  ## 5.0.0-canary.4
package/dist/index.js CHANGED
@@ -17,6 +17,7 @@ import { parse } from "@babel/parser";
17
17
  import traverseModule from "@babel/traverse";
18
18
  import { createMatchPath, loadConfig } from "tsconfig-paths";
19
19
  import http from "node:http";
20
+ import Conf from "conf";
20
21
  import * as nodeUtil from "node:util";
21
22
  import { lookup } from "mime-types";
22
23
  import os from "node:os";
@@ -86,7 +87,7 @@ const getEmailsDirectoryMetadata = async (absolutePathToEmailsDirectory, keepFil
86
87
  //#region package.json
87
88
  var package_default = {
88
89
  name: "react-email",
89
- version: "5.0.0-canary.5",
90
+ version: "5.0.0-canary.7",
90
91
  description: "A live preview of your emails right in your browser.",
91
92
  bin: { "email": "./dist/index.js" },
92
93
  type: "module",
@@ -110,6 +111,7 @@ var package_default = {
110
111
  "@babel/traverse": "^7.27.0",
111
112
  "chokidar": "^4.0.3",
112
113
  "commander": "^13.0.0",
114
+ "conf": "^15.0.2",
113
115
  "debounce": "^2.0.0",
114
116
  "esbuild": "^0.25.0",
115
117
  "glob": "^11.0.0",
@@ -242,10 +244,8 @@ module.exports = {
242
244
  const getEmailSlugsFromEmailDirectory = (emailDirectory, emailsDirectoryAbsolutePath) => {
243
245
  const directoryPathRelativeToEmailsDirectory = emailDirectory.absolutePath.replace(emailsDirectoryAbsolutePath, "").trim();
244
246
  const slugs = [];
245
- emailDirectory.emailFilenames.forEach((filename) => slugs.push(path.join(directoryPathRelativeToEmailsDirectory, filename).split(path.sep).filter((segment) => segment.length > 0)));
246
- emailDirectory.subDirectories.forEach((directory) => {
247
- slugs.push(...getEmailSlugsFromEmailDirectory(directory, emailsDirectoryAbsolutePath));
248
- });
247
+ for (const filename of emailDirectory.emailFilenames) slugs.push(path.join(directoryPathRelativeToEmailsDirectory, filename).split(path.sep).filter((segment) => segment.length > 0));
248
+ for (const directory of emailDirectory.subDirectories) slugs.push(...getEmailSlugsFromEmailDirectory(directory, emailsDirectoryAbsolutePath));
249
249
  return slugs;
250
250
  };
251
251
  const forceSSGForEmailPreviews = async (emailsDirPath, builtPreviewAppPath) => {
@@ -587,18 +587,27 @@ const setupHotreloading = async (devServer$1, emailDirRelativePath) => {
587
587
  return watcher;
588
588
  };
589
589
 
590
+ //#endregion
591
+ //#region src/utils/conf.ts
592
+ const encryptionKey = "h2#x658}1#qY(@!:7,BD1J)q12$[tM25";
593
+ const conf = new Conf({
594
+ projectName: "react-email",
595
+ encryptionKey
596
+ });
597
+
590
598
  //#endregion
591
599
  //#region src/utils/style-text.ts
592
600
  const styleText = nodeUtil.styleText ? nodeUtil.styleText : (_, text) => text;
593
601
 
594
602
  //#endregion
595
603
  //#region src/utils/preview/get-env-variables-for-preview-app.ts
596
- const getEnvVariablesForPreviewApp = (relativePathToEmailsDirectory, previewServerLocation, cwd) => {
604
+ const getEnvVariablesForPreviewApp = (relativePathToEmailsDirectory, previewServerLocation, cwd, resendApiKey) => {
597
605
  return {
598
606
  EMAILS_DIR_RELATIVE_PATH: relativePathToEmailsDirectory,
599
607
  EMAILS_DIR_ABSOLUTE_PATH: path.resolve(cwd, relativePathToEmailsDirectory),
600
608
  PREVIEW_SERVER_LOCATION: previewServerLocation,
601
- USER_PROJECT_LOCATION: cwd
609
+ USER_PROJECT_LOCATION: cwd,
610
+ RESEND_API_KEY: resendApiKey
602
611
  };
603
612
  };
604
613
 
@@ -648,7 +657,7 @@ const safeAsyncServerListen = (server, port) => {
648
657
  };
649
658
  const startDevServer = async (emailsDirRelativePath, staticBaseDirRelativePath, port) => {
650
659
  const [majorNodeVersion] = process.versions.node.split(".");
651
- if (majorNodeVersion && Number.parseInt(majorNodeVersion) < 20) {
660
+ if (majorNodeVersion && Number.parseInt(majorNodeVersion, 10) < 20) {
652
661
  console.error(` ${logSymbols.error} Node ${majorNodeVersion} is not supported. Please upgrade to Node 20 or higher.`);
653
662
  process.exit(1);
654
663
  }
@@ -701,7 +710,7 @@ const startDevServer = async (emailsDirRelativePath, staticBaseDirRelativePath,
701
710
  process.env = {
702
711
  NODE_ENV: "development",
703
712
  ...process.env,
704
- ...getEnvVariablesForPreviewApp(path.normalize(emailsDirRelativePath), previewServerLocation, process.cwd())
713
+ ...getEnvVariablesForPreviewApp(path.normalize(emailsDirRelativePath), previewServerLocation, process.cwd(), conf.get("resendApiKey"))
705
714
  };
706
715
  const app = (await previewServer.import("next", { default: true }))({
707
716
  dev: false,
@@ -803,7 +812,7 @@ const dev = async ({ dir: emailsDirRelativePath, port }) => {
803
812
  console.error(`Missing ${emailsDirRelativePath} folder`);
804
813
  process.exit(1);
805
814
  }
806
- await setupHotreloading(await startDevServer(emailsDirRelativePath, emailsDirRelativePath, Number.parseInt(port)), emailsDirRelativePath);
815
+ await setupHotreloading(await startDevServer(emailsDirRelativePath, emailsDirRelativePath, Number.parseInt(port, 10)), emailsDirRelativePath);
807
816
  } catch (error) {
808
817
  console.log(error);
809
818
  process.exit(1);
@@ -861,10 +870,8 @@ const renderingUtilitiesExporter = (emailTemplates) => ({
861
870
  //#region src/commands/export.ts
862
871
  const getEmailTemplatesFromDirectory = (emailDirectory) => {
863
872
  const templatePaths = [];
864
- emailDirectory.emailFilenames.forEach((filename) => templatePaths.push(path.join(emailDirectory.absolutePath, filename)));
865
- emailDirectory.subDirectories.forEach((directory) => {
866
- templatePaths.push(...getEmailTemplatesFromDirectory(directory));
867
- });
873
+ for (const filename of emailDirectory.emailFilenames) templatePaths.push(path.join(emailDirectory.absolutePath, filename));
874
+ for (const directory of emailDirectory.subDirectories) templatePaths.push(...getEmailTemplatesFromDirectory(directory));
868
875
  return templatePaths;
869
876
  };
870
877
  const require = createRequire(url.fileURLToPath(import.meta.url));
@@ -959,6 +966,22 @@ const exportTemplates = async (pathToWhereEmailMarkupShouldBeDumped, emailsDirec
959
966
  }
960
967
  };
961
968
 
969
+ //#endregion
970
+ //#region src/commands/resend-setup.ts
971
+ async function resendSetup(apiKey) {
972
+ const previousValue = conf.get("resendApiKey");
973
+ if (typeof previousValue === "string" && previousValue.length > 0) {
974
+ if (!(await prompts({
975
+ type: "confirm",
976
+ name: "replaceApiKey",
977
+ message: `You already have a Resend API Key configured (${styleText("grey", previousValue.slice(0, 11))}...). Do you want to replace it?`,
978
+ initial: false
979
+ })).replaceApiKey) process.exit(0);
980
+ }
981
+ conf.set("resendApiKey", apiKey);
982
+ console.info(`${logSymbols.success} Resend integration successfully set up`);
983
+ }
984
+
962
985
  //#endregion
963
986
  //#region src/commands/start.ts
964
987
  const start = async () => {
@@ -1001,6 +1024,7 @@ program.command("export").description("Build the templates to the `out` director
1001
1024
  plainText,
1002
1025
  pretty
1003
1026
  }));
1027
+ program.command("resend").command("setup").description("Sets up the integration between the React Email CLI, and your Resend account through an API Key").argument("apiKey", "API Key for use setting up the integration").action(resendSetup);
1004
1028
  program.parse();
1005
1029
 
1006
1030
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-email",
3
- "version": "5.0.0-canary.5",
3
+ "version": "5.0.0-canary.7",
4
4
  "description": "A live preview of your emails right in your browser.",
5
5
  "bin": {
6
6
  "email": "./dist/index.js"
@@ -24,6 +24,7 @@
24
24
  "@babel/traverse": "^7.27.0",
25
25
  "chokidar": "^4.0.3",
26
26
  "commander": "^13.0.0",
27
+ "conf": "^15.0.2",
27
28
  "debounce": "^2.0.0",
28
29
  "esbuild": "^0.25.0",
29
30
  "glob": "^11.0.0",
@@ -46,7 +47,7 @@
46
47
  "react": "19.0.0",
47
48
  "react-dom": "19.0.0",
48
49
  "typescript": "5.8.3",
49
- "@react-email/components": "1.0.0-canary.6"
50
+ "@react-email/components": "1.0.0-canary.7"
50
51
  },
51
52
  "scripts": {
52
53
  "build": "tsdown",
@@ -115,23 +115,23 @@ const getEmailSlugsFromEmailDirectory = (
115
115
  .trim();
116
116
 
117
117
  const slugs = [] as Array<string>[];
118
- emailDirectory.emailFilenames.forEach((filename) =>
118
+ for (const filename of emailDirectory.emailFilenames) {
119
119
  slugs.push(
120
120
  path
121
121
  .join(directoryPathRelativeToEmailsDirectory, filename)
122
122
  .split(path.sep)
123
123
  // sometimes it gets empty segments due to trailing slashes
124
124
  .filter((segment) => segment.length > 0),
125
- ),
126
- );
127
- emailDirectory.subDirectories.forEach((directory) => {
125
+ );
126
+ }
127
+ for (const directory of emailDirectory.subDirectories) {
128
128
  slugs.push(
129
129
  ...getEmailSlugsFromEmailDirectory(
130
130
  directory,
131
131
  emailsDirectoryAbsolutePath,
132
132
  ),
133
133
  );
134
- });
134
+ }
135
135
 
136
136
  return slugs;
137
137
  };
@@ -16,7 +16,7 @@ export const dev = async ({ dir: emailsDirRelativePath, port }: Args) => {
16
16
  const devServer = await startDevServer(
17
17
  emailsDirRelativePath,
18
18
  emailsDirRelativePath, // defaults to ./emails/static for the static files that are served to the preview
19
- Number.parseInt(port),
19
+ Number.parseInt(port, 10),
20
20
  );
21
21
 
22
22
  await setupHotreloading(devServer, emailsDirRelativePath);
@@ -19,12 +19,12 @@ import { registerSpinnerAutostopping } from '../utils/register-spinner-autostopp
19
19
 
20
20
  const getEmailTemplatesFromDirectory = (emailDirectory: EmailsDirectory) => {
21
21
  const templatePaths = [] as string[];
22
- emailDirectory.emailFilenames.forEach((filename) =>
23
- templatePaths.push(path.join(emailDirectory.absolutePath, filename)),
24
- );
25
- emailDirectory.subDirectories.forEach((directory) => {
22
+ for (const filename of emailDirectory.emailFilenames) {
23
+ templatePaths.push(path.join(emailDirectory.absolutePath, filename));
24
+ }
25
+ for (const directory of emailDirectory.subDirectories) {
26
26
  templatePaths.push(...getEmailTemplatesFromDirectory(directory));
27
- });
27
+ }
28
28
 
29
29
  return templatePaths;
30
30
  };
@@ -0,0 +1,22 @@
1
+ import logSymbols from 'log-symbols';
2
+ import prompts from 'prompts';
3
+ import { conf } from '../utils/conf.js';
4
+ import { styleText } from '../utils/style-text.js';
5
+
6
+ export async function resendSetup(apiKey: string) {
7
+ const previousValue = conf.get('resendApiKey');
8
+ if (typeof previousValue === 'string' && previousValue.length > 0) {
9
+ const response = await prompts({
10
+ type: 'confirm',
11
+ name: 'replaceApiKey',
12
+ message: `You already have a Resend API Key configured (${styleText('grey', previousValue.slice(0, 11))}...). Do you want to replace it?`,
13
+ initial: false,
14
+ });
15
+ if (!response.replaceApiKey) {
16
+ process.exit(0);
17
+ }
18
+ }
19
+
20
+ conf.set('resendApiKey', apiKey);
21
+ console.info(`${logSymbols.success} Resend integration successfully set up`);
22
+ }
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@ import { program } from 'commander';
3
3
  import { build } from './commands/build.js';
4
4
  import { dev } from './commands/dev.js';
5
5
  import { exportTemplates } from './commands/export.js';
6
+ import { resendSetup } from './commands/resend-setup.js';
6
7
  import { start } from './commands/start.js';
7
8
  import { packageJson } from './utils/packageJson.js';
8
9
 
@@ -52,4 +53,13 @@ program
52
53
  exportTemplates(outDir, srcDir, { silent, plainText, pretty }),
53
54
  );
54
55
 
56
+ program
57
+ .command('resend')
58
+ .command('setup')
59
+ .description(
60
+ 'Sets up the integration between the React Email CLI, and your Resend account through an API Key',
61
+ )
62
+ .argument('apiKey', 'API Key for use setting up the integration')
63
+ .action(resendSetup);
64
+
55
65
  program.parse();
@@ -16,6 +16,7 @@ exports[`tree(__dirname, 2) 1`] = `
16
16
  ├── types
17
17
  │ ├── hot-reload-change.ts
18
18
  │ └── hot-reload-event.ts
19
+ ├── conf.ts
19
20
  ├── get-emails-directory-metadata.spec.ts
20
21
  ├── get-emails-directory-metadata.ts
21
22
  ├── get-preview-server-location.ts
@@ -0,0 +1,9 @@
1
+ import Conf from 'conf';
2
+
3
+ // Just simple encryption. This isn't completely safe
4
+ // because anyone can find this key here
5
+ const encryptionKey = 'h2#x658}1#qY(@!:7,BD1J)q12$[tM25';
6
+
7
+ export const conf = new Conf<{
8
+ resendApiKey?: string;
9
+ }>({ projectName: 'react-email', encryptionKey });
@@ -4,11 +4,13 @@ export const getEnvVariablesForPreviewApp = (
4
4
  relativePathToEmailsDirectory: string,
5
5
  previewServerLocation: string,
6
6
  cwd: string,
7
+ resendApiKey?: string,
7
8
  ) => {
8
9
  return {
9
10
  EMAILS_DIR_RELATIVE_PATH: relativePathToEmailsDirectory,
10
11
  EMAILS_DIR_ABSOLUTE_PATH: path.resolve(cwd, relativePathToEmailsDirectory),
11
12
  PREVIEW_SERVER_LOCATION: previewServerLocation,
12
13
  USER_PROJECT_LOCATION: cwd,
14
+ RESEND_API_KEY: resendApiKey,
13
15
  } as const;
14
16
  };
@@ -5,6 +5,7 @@ import { createJiti } from 'jiti';
5
5
  import logSymbols from 'log-symbols';
6
6
  import ora from 'ora';
7
7
  import { registerSpinnerAutostopping } from '../../utils/register-spinner-autostopping.js';
8
+ import { conf } from '../conf.js';
8
9
  import { getPreviewServerLocation } from '../get-preview-server-location.js';
9
10
  import { packageJson } from '../packageJson.js';
10
11
  import { styleText } from '../style-text.js';
@@ -33,7 +34,7 @@ export const startDevServer = async (
33
34
  port: number,
34
35
  ): Promise<http.Server> => {
35
36
  const [majorNodeVersion] = process.versions.node.split('.');
36
- if (majorNodeVersion && Number.parseInt(majorNodeVersion) < 20) {
37
+ if (majorNodeVersion && Number.parseInt(majorNodeVersion, 10) < 20) {
37
38
  console.error(
38
39
  ` ${logSymbols.error} Node ${majorNodeVersion} is not supported. Please upgrade to Node 20 or higher.`,
39
40
  );
@@ -131,6 +132,7 @@ export const startDevServer = async (
131
132
  path.normalize(emailsDirRelativePath),
132
133
  previewServerLocation,
133
134
  process.cwd(),
135
+ conf.get('resendApiKey'),
134
136
  ),
135
137
  };
136
138
 
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import * as nodeUtil from 'node:util';
6
6
 
7
- type StyleTextFunction = (style: string, text: string) => string;
7
+ type StyleTextFunction = typeof nodeUtil.styleText;
8
8
 
9
9
  export const styleText: StyleTextFunction = (nodeUtil as any).styleText
10
10
  ? (nodeUtil as any).styleText