react-email 5.2.5 → 5.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-email",
3
- "version": "5.2.5",
3
+ "version": "5.2.7",
4
4
  "description": "A live preview of your emails right in your browser.",
5
5
  "bin": {
6
6
  "email": "./dist/index.js"
@@ -43,13 +43,13 @@
43
43
  "@types/babel__traverse": "7.20.7",
44
44
  "@types/mime-types": "2.1.4",
45
45
  "@types/prompts": "2.4.9",
46
- "next": "16.0.10",
46
+ "next": "16.1.5",
47
47
  "react": "19.0.0",
48
48
  "react-dom": "19.0.0",
49
49
  "shlex": "3.0.0",
50
50
  "tsx": "4.21.0",
51
51
  "typescript": "5.8.3",
52
- "@react-email/components": "1.0.4"
52
+ "@react-email/components": "1.0.7"
53
53
  },
54
54
  "scripts": {
55
55
  "build": "tsdown",
@@ -1,5 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { getPackages } from '@manypkg/get-packages';
3
5
  import logSymbols from 'log-symbols';
4
6
  import { installDependencies, type PackageManagerName, runScript } from 'nypm';
5
7
  import ora from 'ora';
@@ -15,25 +17,37 @@ interface Args {
15
17
  packageManager: PackageManagerName;
16
18
  }
17
19
 
20
+ const dirname = path.dirname(fileURLToPath(import.meta.url));
21
+ const isInReactEmailMonorepo = !dirname.includes('node_modules');
22
+
18
23
  const setNextEnvironmentVariablesForBuild = async (
19
24
  emailsDirRelativePath: string,
20
25
  builtPreviewAppPath: string,
26
+ usersProjectLocation: string,
21
27
  ) => {
28
+ let rootDir = 'previewServerLocation';
29
+ if (isInReactEmailMonorepo) {
30
+ rootDir = `'${await getPackages(usersProjectLocation).then((p) => p.rootDir.replaceAll('\\', '/'))}'`;
31
+ }
22
32
  const nextConfigContents = `
23
33
  import path from 'path';
24
34
  const emailsDirRelativePath = path.normalize('${emailsDirRelativePath}');
25
- const userProjectLocation = '${process.cwd().replace(/\\/g, '/')}';
26
- const previewServerLocation = '${builtPreviewAppPath.replace(/\\/g, '/')}';
35
+ const userProjectLocation = '${process.cwd().replaceAll('\\', '/')}';
36
+ const previewServerLocation = '${builtPreviewAppPath.replaceAll('\\', '/')}';
37
+ const rootDir = ${rootDir};
27
38
  /** @type {import('next').NextConfig} */
28
39
  const nextConfig = {
29
40
  env: {
30
41
  NEXT_PUBLIC_IS_BUILDING: 'true',
31
- EMAILS_DIR_RELATIVE_PATH: emailsDirRelativePath,
32
- EMAILS_DIR_ABSOLUTE_PATH: path.resolve(userProjectLocation, emailsDirRelativePath),
33
- PREVIEW_SERVER_LOCATION: previewServerLocation,
34
- USER_PROJECT_LOCATION: userProjectLocation
42
+ REACT_EMAIL_INTERNAL_EMAILS_DIR_RELATIVE_PATH: emailsDirRelativePath,
43
+ REACT_EMAIL_INTERNAL_EMAILS_DIR_ABSOLUTE_PATH: path.resolve(userProjectLocation, emailsDirRelativePath),
44
+ REACT_EMAIL_INTERNAL_PREVIEW_SERVER_LOCATION: previewServerLocation,
45
+ REACT_EMAIL_INTERNAL_USER_PROJECT_LOCATION: userProjectLocation
46
+ },
47
+ turbopack: {
48
+ root: rootDir,
35
49
  },
36
- outputFileTracingRoot: previewServerLocation,
50
+ outputFileTracingRoot: rootDir,
37
51
  serverExternalPackages: ['esbuild'],
38
52
  typescript: {
39
53
  ignoreBuildErrors: true
@@ -164,6 +178,7 @@ export const build = async ({
164
178
  packageManager,
165
179
  }: Args) => {
166
180
  try {
181
+ const usersProjectLocation = process.cwd();
167
182
  const previewServerLocation = await getPreviewServerLocation();
168
183
 
169
184
  const spinner = ora({
@@ -177,10 +192,13 @@ export const build = async ({
177
192
  process.exit(1);
178
193
  }
179
194
 
180
- const emailsDirPath = path.join(process.cwd(), emailsDirRelativePath);
195
+ const emailsDirPath = path.join(
196
+ usersProjectLocation,
197
+ emailsDirRelativePath,
198
+ );
181
199
  const staticPath = path.join(emailsDirPath, 'static');
182
200
 
183
- const builtPreviewAppPath = path.join(process.cwd(), '.react-email');
201
+ const builtPreviewAppPath = path.join(usersProjectLocation, '.react-email');
184
202
 
185
203
  if (fs.existsSync(builtPreviewAppPath)) {
186
204
  spinner.text = 'Deleting pre-existing `.react-email` folder';
@@ -191,12 +209,11 @@ export const build = async ({
191
209
  await fs.promises.cp(previewServerLocation, builtPreviewAppPath, {
192
210
  recursive: true,
193
211
  filter: (source: string) => {
194
- // do not copy the CLI files
212
+ const relativeSource = path.relative(previewServerLocation, source);
195
213
  return (
196
- !/(\/|\\)cli(\/|\\)?/.test(source) &&
197
- !/(\/|\\)\.next(\/|\\)?/.test(source) &&
198
- !/(\/|\\)\.turbo(\/|\\)?/.test(source) &&
199
- !/(\/|\\)node_modules(\/|\\)?$/.test(source)
214
+ !/\.next/.test(relativeSource) &&
215
+ !/\.turbo/.test(relativeSource) &&
216
+ (isInReactEmailMonorepo || !/node_modules/.test(relativeSource))
200
217
  );
201
218
  },
202
219
  });
@@ -218,6 +235,7 @@ export const build = async ({
218
235
  await setNextEnvironmentVariablesForBuild(
219
236
  emailsDirRelativePath,
220
237
  builtPreviewAppPath,
238
+ usersProjectLocation,
221
239
  );
222
240
 
223
241
  spinner.text = 'Setting server side generation for the email preview pages';
@@ -226,12 +244,14 @@ export const build = async ({
226
244
  spinner.text = "Updating package.json's build and start scripts";
227
245
  await updatePackageJson(builtPreviewAppPath);
228
246
 
229
- spinner.text = 'Installing dependencies on `.react-email`';
230
- await installDependencies({
231
- cwd: builtPreviewAppPath,
232
- silent: true,
233
- packageManager,
234
- });
247
+ if (!isInReactEmailMonorepo) {
248
+ spinner.text = 'Installing dependencies on `.react-email`';
249
+ await installDependencies({
250
+ cwd: builtPreviewAppPath,
251
+ silent: true,
252
+ packageManager,
253
+ });
254
+ }
235
255
 
236
256
  spinner.stopAndPersist({
237
257
  text: 'Successfully prepared `.react-email` for `next build`',
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
- #!/usr/bin/env -S node --experimental-vm-modules --disable-warning=ExperimentalWarning
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'node:child_process';
2
3
  import { program } from 'commander';
3
4
  import { build } from './commands/build.js';
4
5
  import { dev } from './commands/dev.js';
@@ -8,64 +9,102 @@ import { resendSetup } from './commands/resend/setup.js';
8
9
  import { start } from './commands/start.js';
9
10
  import { packageJson } from './utils/packageJson.js';
10
11
 
11
- const PACKAGE_NAME = 'react-email';
12
+ const requiredFlags = [
13
+ '--experimental-vm-modules',
14
+ '--disable-warning=ExperimentalWarning',
15
+ ];
12
16
 
13
- program
14
- .name(PACKAGE_NAME)
15
- .description('A live preview of your emails right in your browser')
16
- .version(packageJson.version);
17
+ const hasRequiredFlags = requiredFlags.every((flag) =>
18
+ process.execArgv.includes(flag),
19
+ );
17
20
 
18
- program
19
- .command('dev')
20
- .description('Starts the preview email development app')
21
- .option('-d, --dir <path>', 'Directory with your email templates', './emails')
22
- .option('-p --port <port>', 'Port to run dev server on', '3000')
23
- .action(dev);
21
+ if (!hasRequiredFlags) {
22
+ const child = spawn(
23
+ process.execPath,
24
+ [
25
+ ...requiredFlags,
26
+ ...process.execArgv,
27
+ process.argv[1] ?? '',
28
+ ...process.argv.slice(2),
29
+ ],
30
+ { stdio: 'inherit' },
31
+ );
24
32
 
25
- program
26
- .command('build')
27
- .description('Copies the preview app for onto .react-email and builds it')
28
- .option('-d, --dir <path>', 'Directory with your email templates', './emails')
29
- .option(
30
- '-p --packageManager <name>',
31
- 'Package name to use on installation on `.react-email`',
32
- 'npm',
33
- )
34
- .action(build);
33
+ child.on('exit', (code) => {
34
+ process.exit(code ?? 0);
35
+ });
36
+ } else {
37
+ const PACKAGE_NAME = 'react-email';
35
38
 
36
- program
37
- .command('start')
38
- .description('Runs the built preview app that is inside of ".react-email"')
39
- .action(start);
39
+ program
40
+ .name(PACKAGE_NAME)
41
+ .description('A live preview of your emails right in your browser')
42
+ .version(packageJson.version);
40
43
 
41
- program
42
- .command('export')
43
- .description('Build the templates to the `out` directory')
44
- .option('--outDir <path>', 'Output directory', 'out')
45
- .option('-p, --pretty', 'Pretty print the output', false)
46
- .option('-t, --plainText', 'Set output format as plain text', false)
47
- .option('-d, --dir <path>', 'Directory with your email templates', './emails')
48
- .option(
49
- '-s, --silent',
50
- 'To, or not to show a spinner with process information',
51
- false,
52
- )
53
- .action(({ outDir, pretty, plainText, silent, dir: srcDir }) =>
54
- exportTemplates(outDir, srcDir, { silent, plainText, pretty }),
55
- );
44
+ program
45
+ .command('dev')
46
+ .description('Starts the preview email development app')
47
+ .option(
48
+ '-d, --dir <path>',
49
+ 'Directory with your email templates',
50
+ './emails',
51
+ )
52
+ .option('-p --port <port>', 'Port to run dev server on', '3000')
53
+ .action(dev);
54
+
55
+ program
56
+ .command('build')
57
+ .description('Copies the preview app for onto .react-email and builds it')
58
+ .option(
59
+ '-d, --dir <path>',
60
+ 'Directory with your email templates',
61
+ './emails',
62
+ )
63
+ .option(
64
+ '-p --packageManager <name>',
65
+ 'Package name to use on installation on `.react-email`',
66
+ 'npm',
67
+ )
68
+ .action(build);
69
+
70
+ program
71
+ .command('start')
72
+ .description('Runs the built preview app that is inside of ".react-email"')
73
+ .action(start);
74
+
75
+ program
76
+ .command('export')
77
+ .description('Build the templates to the `out` directory')
78
+ .option('--outDir <path>', 'Output directory', 'out')
79
+ .option('-p, --pretty', 'Pretty print the output', false)
80
+ .option('-t, --plainText', 'Set output format as plain text', false)
81
+ .option(
82
+ '-d, --dir <path>',
83
+ 'Directory with your email templates',
84
+ './emails',
85
+ )
86
+ .option(
87
+ '-s, --silent',
88
+ 'To, or not to show a spinner with process information',
89
+ false,
90
+ )
91
+ .action(({ outDir, pretty, plainText, silent, dir: srcDir }) =>
92
+ exportTemplates(outDir, srcDir, { silent, plainText, pretty }),
93
+ );
56
94
 
57
- const resend = program.command('resend');
95
+ const resend = program.command('resend');
58
96
 
59
- resend
60
- .command('setup')
61
- .description(
62
- 'Sets up the integration between the React Email CLI, and your Resend account through an API Key',
63
- )
64
- .action(resendSetup);
97
+ resend
98
+ .command('setup')
99
+ .description(
100
+ 'Sets up the integration between the React Email CLI, and your Resend account through an API Key',
101
+ )
102
+ .action(resendSetup);
65
103
 
66
- resend
67
- .command('reset')
68
- .description('Deletes your API Key from the React Email configuration')
69
- .action(resendReset);
104
+ resend
105
+ .command('reset')
106
+ .description('Deletes your API Key from the React Email configuration')
107
+ .action(resendReset);
70
108
 
71
- program.parse();
109
+ program.parse();
110
+ }
@@ -7,10 +7,14 @@ export const getEnvVariablesForPreviewApp = (
7
7
  resendApiKey?: string,
8
8
  ) => {
9
9
  return {
10
- EMAILS_DIR_RELATIVE_PATH: relativePathToEmailsDirectory,
11
- EMAILS_DIR_ABSOLUTE_PATH: path.resolve(cwd, relativePathToEmailsDirectory),
12
- PREVIEW_SERVER_LOCATION: previewServerLocation,
13
- USER_PROJECT_LOCATION: cwd,
14
- RESEND_API_KEY: resendApiKey,
10
+ REACT_EMAIL_INTERNAL_EMAILS_DIR_RELATIVE_PATH:
11
+ relativePathToEmailsDirectory,
12
+ REACT_EMAIL_INTERNAL_EMAILS_DIR_ABSOLUTE_PATH: path.resolve(
13
+ cwd,
14
+ relativePathToEmailsDirectory,
15
+ ),
16
+ REACT_EMAIL_INTERNAL_PREVIEW_SERVER_LOCATION: previewServerLocation,
17
+ REACT_EMAIL_INTERNAL_USER_PROJECT_LOCATION: cwd,
18
+ REACT_EMAIL_INTERNAL_RESEND_API_KEY: resendApiKey,
15
19
  } as const;
16
20
  };
@@ -5,7 +5,10 @@ import {
5
5
  type DependencyGraph,
6
6
  } from './create-dependency-graph.js';
7
7
 
8
- const testingDiretctory = path.join(__dirname, './test/dependency-graph/inner');
8
+ const testingDiretctory = path.join(
9
+ import.meta.dirname,
10
+ './test/dependency-graph/inner',
11
+ );
9
12
 
10
13
  const pathToTemporaryFile = path.join(
11
14
  testingDiretctory,
@@ -58,6 +61,14 @@ describe('createDependencyGraph()', async () => {
58
61
  );
59
62
  expect(relativePathDependencyGraph).toMatchInlineSnapshot(`
60
63
  {
64
+ "../../some-file.ts": {
65
+ "dependencyPaths": [],
66
+ "dependentPaths": [
67
+ "path-aliases.ts",
68
+ ],
69
+ "moduleDependencies": [],
70
+ "path": "../../some-file.ts",
71
+ },
61
72
  "../outer.ts": {
62
73
  "dependencyPaths": [
63
74
  "outer-dependency.ts",
@@ -120,6 +131,14 @@ describe('createDependencyGraph()', async () => {
120
131
  "moduleDependencies": [],
121
132
  "path": "outer-dependency.ts",
122
133
  },
134
+ "path-aliases.ts": {
135
+ "dependencyPaths": [
136
+ "../../some-file.ts",
137
+ ],
138
+ "dependentPaths": [],
139
+ "moduleDependencies": [],
140
+ "path": "path-aliases.ts",
141
+ },
123
142
  }
124
143
  `);
125
144
  });
@@ -1,84 +0,0 @@
1
- // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
-
3
- exports[`checkImages() 1`] = `
4
- [
5
- {
6
- "checks": [
7
- {
8
- "metadata": {
9
- "alt": undefined,
10
- },
11
- "passed": false,
12
- "type": "accessibility",
13
- },
14
- {
15
- "passed": true,
16
- "type": "syntax",
17
- },
18
- {
19
- "passed": true,
20
- "type": "security",
21
- },
22
- {
23
- "metadata": {
24
- "fetchStatusCode": 200,
25
- },
26
- "passed": true,
27
- "type": "fetch_attempt",
28
- },
29
- {
30
- "metadata": {
31
- "byteCount": 26808,
32
- },
33
- "passed": true,
34
- "type": "image_size",
35
- },
36
- ],
37
- "codeLocation": {
38
- "column": 3,
39
- "line": 2,
40
- },
41
- "source": "https://resend.com/static/brand/resend-icon-white.png",
42
- "status": "warning",
43
- },
44
- {
45
- "checks": [
46
- {
47
- "metadata": {
48
- "alt": "codepen challenges",
49
- },
50
- "passed": true,
51
- "type": "accessibility",
52
- },
53
- {
54
- "passed": true,
55
- "type": "syntax",
56
- },
57
- {
58
- "passed": true,
59
- "type": "security",
60
- },
61
- {
62
- "metadata": {
63
- "fetchStatusCode": 200,
64
- },
65
- "passed": true,
66
- "type": "fetch_attempt",
67
- },
68
- {
69
- "metadata": {
70
- "byteCount": 111922,
71
- },
72
- "passed": true,
73
- "type": "image_size",
74
- },
75
- ],
76
- "codeLocation": {
77
- "column": 3,
78
- "line": 3,
79
- },
80
- "source": "/static/codepen-challengers.png",
81
- "status": "success",
82
- },
83
- ]
84
- `;