react-email 5.2.4 → 5.2.6

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.4",
3
+ "version": "5.2.6",
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
@@ -135,8 +149,10 @@ const updatePackageJson = async (builtPreviewAppPath: string) => {
135
149
  devDependencies: Record<string, string>;
136
150
  };
137
151
  // Turbopack has some errors with the imports in @react-email/tailwind
138
- packageJson.scripts.build = 'next build';
139
- packageJson.scripts.start = 'next start';
152
+ packageJson.scripts.build =
153
+ 'cross-env NODE_OPTIONS="--experimental-vm-modules --disable-warning=ExperimentalWarning" next build';
154
+ packageJson.scripts.start =
155
+ 'cross-env NODE_OPTIONS="--experimental-vm-modules --disable-warning=ExperimentalWarning" next start';
140
156
  delete packageJson.scripts.postbuild;
141
157
 
142
158
  packageJson.name = 'preview-server';
@@ -162,6 +178,7 @@ export const build = async ({
162
178
  packageManager,
163
179
  }: Args) => {
164
180
  try {
181
+ const usersProjectLocation = process.cwd();
165
182
  const previewServerLocation = await getPreviewServerLocation();
166
183
 
167
184
  const spinner = ora({
@@ -175,10 +192,13 @@ export const build = async ({
175
192
  process.exit(1);
176
193
  }
177
194
 
178
- const emailsDirPath = path.join(process.cwd(), emailsDirRelativePath);
195
+ const emailsDirPath = path.join(
196
+ usersProjectLocation,
197
+ emailsDirRelativePath,
198
+ );
179
199
  const staticPath = path.join(emailsDirPath, 'static');
180
200
 
181
- const builtPreviewAppPath = path.join(process.cwd(), '.react-email');
201
+ const builtPreviewAppPath = path.join(usersProjectLocation, '.react-email');
182
202
 
183
203
  if (fs.existsSync(builtPreviewAppPath)) {
184
204
  spinner.text = 'Deleting pre-existing `.react-email` folder';
@@ -189,12 +209,11 @@ export const build = async ({
189
209
  await fs.promises.cp(previewServerLocation, builtPreviewAppPath, {
190
210
  recursive: true,
191
211
  filter: (source: string) => {
192
- // do not copy the CLI files
212
+ const relativeSource = path.relative(previewServerLocation, source);
193
213
  return (
194
- !/(\/|\\)cli(\/|\\)?/.test(source) &&
195
- !/(\/|\\)\.next(\/|\\)?/.test(source) &&
196
- !/(\/|\\)\.turbo(\/|\\)?/.test(source) &&
197
- !/(\/|\\)node_modules(\/|\\)?$/.test(source)
214
+ !/\.next/.test(relativeSource) &&
215
+ !/\.turbo/.test(relativeSource) &&
216
+ (isInReactEmailMonorepo || !/node_modules/.test(relativeSource))
198
217
  );
199
218
  },
200
219
  });
@@ -216,6 +235,7 @@ export const build = async ({
216
235
  await setNextEnvironmentVariablesForBuild(
217
236
  emailsDirRelativePath,
218
237
  builtPreviewAppPath,
238
+ usersProjectLocation,
219
239
  );
220
240
 
221
241
  spinner.text = 'Setting server side generation for the email preview pages';
@@ -224,12 +244,14 @@ export const build = async ({
224
244
  spinner.text = "Updating package.json's build and start scripts";
225
245
  await updatePackageJson(builtPreviewAppPath);
226
246
 
227
- spinner.text = 'Installing dependencies on `.react-email`';
228
- await installDependencies({
229
- cwd: builtPreviewAppPath,
230
- silent: true,
231
- packageManager,
232
- });
247
+ if (!isInReactEmailMonorepo) {
248
+ spinner.text = 'Installing dependencies on `.react-email`';
249
+ await installDependencies({
250
+ cwd: builtPreviewAppPath,
251
+ silent: true,
252
+ packageManager,
253
+ });
254
+ }
233
255
 
234
256
  spinner.stopAndPersist({
235
257
  text: 'Successfully prepared `.react-email` for `next build`',
@@ -238,10 +260,6 @@ export const build = async ({
238
260
 
239
261
  await runScript('build', {
240
262
  packageManager,
241
- env: {
242
- NODE_OPTIONS:
243
- '--experimental-vm-modules --disable-warning=ExperimentalWarning',
244
- },
245
263
  cwd: builtPreviewAppPath,
246
264
  });
247
265
  } catch (error) {
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
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,12 +7,14 @@ export const getEnvVariablesForPreviewApp = (
7
7
  resendApiKey?: string,
8
8
  ) => {
9
9
  return {
10
- NODE_OPTIONS:
11
- '--experimental-vm-modules --disable-warning=ExperimentalWarning',
12
- EMAILS_DIR_RELATIVE_PATH: relativePathToEmailsDirectory,
13
- EMAILS_DIR_ABSOLUTE_PATH: path.resolve(cwd, relativePathToEmailsDirectory),
14
- PREVIEW_SERVER_LOCATION: previewServerLocation,
15
- USER_PROJECT_LOCATION: cwd,
16
- 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,
17
19
  } as const;
18
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
- `;