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/CHANGELOG.md +26 -0
- package/dev/CHANGELOG.md +2 -0
- package/dev/index.js +0 -4
- package/dev/package.json +1 -1
- package/dist/index.js +6371 -76
- package/package.json +3 -3
- package/src/commands/build.ts +40 -20
- package/src/index.ts +92 -53
- package/src/utils/preview/get-env-variables-for-preview-app.ts +9 -5
- package/src/utils/preview/hot-reloading/create-dependency-graph.spec.ts +20 -1
- package/src/utils/preview/hot-reloading/test/dependency-graph/inner/path-aliases.ts +1 -0
- package/src/actions/email-validation/__snapshots__/check-images.spec.tsx.snap +0 -84
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-email",
|
|
3
|
-
"version": "5.2.
|
|
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.
|
|
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.
|
|
52
|
+
"@react-email/components": "1.0.7"
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
55
|
"build": "tsdown",
|
package/src/commands/build.ts
CHANGED
|
@@ -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().
|
|
26
|
-
const previewServerLocation = '${builtPreviewAppPath.
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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:
|
|
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(
|
|
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(
|
|
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
|
-
|
|
212
|
+
const relativeSource = path.relative(previewServerLocation, source);
|
|
195
213
|
return (
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
!/(
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
|
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
|
|
12
|
+
const requiredFlags = [
|
|
13
|
+
'--experimental-vm-modules',
|
|
14
|
+
'--disable-warning=ExperimentalWarning',
|
|
15
|
+
];
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
.
|
|
15
|
-
|
|
16
|
-
.version(packageJson.version);
|
|
17
|
+
const hasRequiredFlags = requiredFlags.every((flag) =>
|
|
18
|
+
process.execArgv.includes(flag),
|
|
19
|
+
);
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
'-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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(
|
|
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
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@/some-file';
|
|
@@ -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
|
-
`;
|