react-email 4.0.8 → 4.0.10
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 +21 -0
- package/dist/cli/index.mjs +210 -181
- package/dist/preview/.next/BUILD_ID +1 -1
- package/dist/preview/.next/app-build-manifest.json +14 -14
- package/dist/preview/.next/app-path-routes-manifest.json +1 -1
- package/dist/preview/.next/build-manifest.json +2 -2
- package/dist/preview/.next/next-minimal-server.js.nft.json +1 -1
- package/dist/preview/.next/next-server.js.nft.json +1 -1
- package/dist/preview/.next/prerender-manifest.json +3 -3
- package/dist/preview/.next/server/app/_not-found/page.js +1 -1
- package/dist/preview/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/dist/preview/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/preview/.next/server/app/page.js +1 -1
- package/dist/preview/.next/server/app/page.js.nft.json +1 -1
- package/dist/preview/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/preview/.next/server/app/preview/[...slug]/page.js +137 -75
- package/dist/preview/.next/server/app/preview/[...slug]/page.js.nft.json +1 -1
- package/dist/preview/.next/server/app/preview/[...slug]/page_client-reference-manifest.js +1 -1
- package/dist/preview/.next/server/app-paths-manifest.json +1 -1
- package/dist/preview/.next/server/chunks/267.js +14 -0
- package/dist/preview/.next/server/chunks/348.js +1 -0
- package/dist/preview/.next/server/chunks/{886.js → 775.js} +3 -3
- package/dist/preview/.next/server/pages/500.html +1 -1
- package/dist/preview/.next/server/pages-manifest.json +1 -1
- package/dist/preview/.next/server/server-reference-manifest.js +1 -1
- package/dist/preview/.next/server/server-reference-manifest.json +1 -1
- package/dist/preview/.next/static/chunks/33-ff3f70a80570ecda.js +1 -0
- package/dist/preview/.next/static/chunks/416-9c899340cfaa07d4.js +1 -0
- package/dist/preview/.next/static/chunks/516-2716d86d2f8b9000.js +1 -0
- package/dist/preview/.next/static/chunks/587-311bc718c0ec09c4.js +1 -0
- package/dist/preview/.next/static/chunks/app/layout-298855388b1fb3f8.js +1 -0
- package/dist/preview/.next/static/chunks/app/{page-0ee3a37f3a3f6f17.js → page-1d98e2313c60dd77.js} +1 -1
- package/dist/preview/.next/static/chunks/app/preview/[...slug]/page-aa098d9c0dacb2db.js +1 -0
- package/dist/preview/.next/static/chunks/f33a14d2-a04f3be0523bd1fa.js +6 -0
- package/dist/preview/.next/static/css/6f42d128f111d7fa.css +3 -0
- package/dist/preview/.next/trace +27 -27
- package/package.json +44 -46
- package/src/actions/email-validation/check-compatibility.ts +1 -1
- package/src/actions/email-validation/check-images.spec.tsx +1 -1
- package/src/actions/email-validation/check-links.spec.tsx +1 -1
- package/src/actions/email-validation/quick-fetch.ts +1 -1
- package/src/actions/render-email-by-path.tsx +6 -6
- package/src/app/preview/[...slug]/preview.tsx +1 -1
- package/src/components/toolbar.tsx +1 -0
- package/src/contexts/emails.tsx +1 -3
- package/src/contexts/fragment-identifier.tsx +3 -1
- package/src/contexts/preview.tsx +1 -3
- package/src/hooks/use-email-rendering-result.ts +18 -5
- package/src/hooks/use-hot-reload.ts +1 -1
- package/src/utils/__snapshots__/get-email-component.spec.ts.snap +1 -1
- package/src/utils/caniemail/ast/get-object-variables.ts +1 -1
- package/src/utils/caniemail/tailwind/generate-tailwind-rules.ts +1 -1
- package/src/utils/caniemail/tailwind/get-tailwind-config.ts +1 -1
- package/src/utils/caniemail/tailwind/get-tailwind-metadata.ts +1 -1
- package/src/utils/contains-email-template.spec.ts +107 -0
- package/src/utils/contains-email-template.ts +33 -0
- package/src/utils/get-email-component.ts +16 -1
- package/src/utils/get-emails-directory-metadata.ts +24 -13
- package/src/utils/index.ts +2 -2
- package/src/utils/run-bundled-code.ts +1 -1
- package/tailwind.config.ts +2 -1
- package/tsconfig.json +1 -1
- package/dist/cli/index.d.mts +0 -1
- package/dist/cli/index.d.ts +0 -1
- package/dist/cli/index.js +0 -1320
- package/dist/index.js +0 -1234
- package/dist/preview/.next/server/chunks/265.js +0 -1
- package/dist/preview/.next/server/chunks/840.js +0 -14
- package/dist/preview/.next/static/chunks/246-e7336e2929971f63.js +0 -1
- package/dist/preview/.next/static/chunks/539-6e9405ecdc007bb7.js +0 -1
- package/dist/preview/.next/static/chunks/587-2b8de61789f0fd1b.js +0 -1
- package/dist/preview/.next/static/chunks/853-a01d49f63a859f3d.js +0 -1
- package/dist/preview/.next/static/chunks/afa401a5-55858bf5265319eb.js +0 -6
- package/dist/preview/.next/static/chunks/app/layout-a3d4e7b4de277118.js +0 -1
- package/dist/preview/.next/static/chunks/app/preview/[...slug]/page-cab7e5e69e3b91e2.js +0 -1
- package/dist/preview/.next/static/css/67e57230289273a9.css +0 -3
- /package/dist/preview/.next/static/{t22IN7aANTezJAJOfFnv- → JVasH_4pOTw7OpjH4h-g_}/_buildManifest.js +0 -0
- /package/dist/preview/.next/static/{t22IN7aANTezJAJOfFnv- → JVasH_4pOTw7OpjH4h-g_}/_ssgManifest.js +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# react-email
|
|
2
2
|
|
|
3
|
+
## 4.0.10
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 5ef9fe8: fix support for `import ... = require(...)` syntax
|
|
8
|
+
- 4c7f597: fix `email dev` not working with `traversal` error
|
|
9
|
+
|
|
10
|
+
## 4.0.9
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 643d841: Add .json import support for hot reloading
|
|
15
|
+
- f21a983: fix Node 18 support
|
|
16
|
+
- cd02449: Ensure dependencies outside emails directory are completely resolved
|
|
17
|
+
- 73a31ed: Fix dependent of dependents not causing hot reloads
|
|
18
|
+
- bdffd8c: fix backwards compatibility with `render` versions
|
|
19
|
+
- e7fa043: Fix access to files outside `static` directory
|
|
20
|
+
- 9aa033c: Use range of versions for dependencies
|
|
21
|
+
- ab70556: Fix non-email files being rendered during hot reloading
|
|
22
|
+
- 9c9aa5d: Add error message for when an email template does not have a default export
|
|
23
|
+
|
|
3
24
|
## 4.0.8
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
package/dist/cli/index.mjs
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
3
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
4
|
}) : x)(function(x) {
|
|
5
|
-
if (typeof require !== "undefined")
|
|
6
|
-
return require.apply(this, arguments);
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
6
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
7
|
});
|
|
9
8
|
|
|
@@ -13,10 +12,10 @@ import { program } from "commander";
|
|
|
13
12
|
// package.json
|
|
14
13
|
var package_default = {
|
|
15
14
|
name: "react-email",
|
|
16
|
-
version: "4.0.
|
|
15
|
+
version: "4.0.10",
|
|
17
16
|
description: "A live preview of your emails right in your browser.",
|
|
18
17
|
bin: {
|
|
19
|
-
email: "./dist/cli/index.
|
|
18
|
+
email: "./dist/cli/index.mjs"
|
|
20
19
|
},
|
|
21
20
|
scripts: {
|
|
22
21
|
build: "tsup-node && node ./scripts/build-preview-server.mjs && pnpm install --frozen-lockfile",
|
|
@@ -33,75 +32,76 @@ var package_default = {
|
|
|
33
32
|
url: "https://github.com/resend/react-email.git",
|
|
34
33
|
directory: "packages/react-email"
|
|
35
34
|
},
|
|
36
|
-
keywords: [
|
|
35
|
+
keywords: [
|
|
36
|
+
"react",
|
|
37
|
+
"email"
|
|
38
|
+
],
|
|
37
39
|
engines: {
|
|
38
40
|
node: ">=18.0.0"
|
|
39
41
|
},
|
|
40
42
|
dependencies: {
|
|
41
|
-
"@babel/parser": "7.
|
|
42
|
-
"@babel/traverse": "7.
|
|
43
|
-
chalk: "
|
|
44
|
-
chokidar: "4.0.3",
|
|
45
|
-
commander: "
|
|
46
|
-
debounce: "2.0.0",
|
|
47
|
-
esbuild: "0.25.0",
|
|
48
|
-
glob: "
|
|
49
|
-
"log-symbols": "
|
|
50
|
-
"mime-types": "
|
|
51
|
-
next: "15.2.4",
|
|
52
|
-
"normalize-path": "3.0.0",
|
|
53
|
-
ora: "
|
|
54
|
-
"socket.io": "4.8.1"
|
|
43
|
+
"@babel/parser": "^7.27.0",
|
|
44
|
+
"@babel/traverse": "^7.27.0",
|
|
45
|
+
chalk: "^5.0.0",
|
|
46
|
+
chokidar: "^4.0.3",
|
|
47
|
+
commander: "^13.0.0",
|
|
48
|
+
debounce: "^2.0.0",
|
|
49
|
+
esbuild: "^0.25.0",
|
|
50
|
+
glob: "^11.0.0",
|
|
51
|
+
"log-symbols": "^7.0.0",
|
|
52
|
+
"mime-types": "^3.0.0",
|
|
53
|
+
next: "^15.2.4",
|
|
54
|
+
"normalize-path": "^3.0.0",
|
|
55
|
+
ora: "^8.0.0",
|
|
56
|
+
"socket.io": "^4.8.1"
|
|
55
57
|
},
|
|
56
58
|
devDependencies: {
|
|
57
59
|
"@babel/core": "7.26.10",
|
|
58
|
-
"@lottiefiles/dotlottie-react": "0.
|
|
59
|
-
"@radix-ui/colors": "
|
|
60
|
-
"@radix-ui/react-collapsible": "1.1.
|
|
61
|
-
"@radix-ui/react-dropdown-menu": "2.1.
|
|
62
|
-
"@radix-ui/react-popover": "1.1.
|
|
63
|
-
"@radix-ui/react-slot": "1.
|
|
64
|
-
"@radix-ui/react-tabs": "1.1.
|
|
65
|
-
"@radix-ui/react-toggle-group": "1.1.
|
|
66
|
-
"@radix-ui/react-tooltip": "1.
|
|
60
|
+
"@lottiefiles/dotlottie-react": "0.13.3",
|
|
61
|
+
"@radix-ui/colors": "3.0.0",
|
|
62
|
+
"@radix-ui/react-collapsible": "1.1.7",
|
|
63
|
+
"@radix-ui/react-dropdown-menu": "2.1.10",
|
|
64
|
+
"@radix-ui/react-popover": "1.1.10",
|
|
65
|
+
"@radix-ui/react-slot": "1.2.0",
|
|
66
|
+
"@radix-ui/react-tabs": "1.1.7",
|
|
67
|
+
"@radix-ui/react-toggle-group": "1.1.6",
|
|
68
|
+
"@radix-ui/react-tooltip": "1.2.3",
|
|
67
69
|
"@react-email/components": "workspace:*",
|
|
68
|
-
"@swc/core": "1.
|
|
70
|
+
"@swc/core": "1.11.21",
|
|
69
71
|
"@types/babel__core": "7.20.5",
|
|
70
|
-
"@types/babel__traverse": "
|
|
72
|
+
"@types/babel__traverse": "7.20.7",
|
|
71
73
|
"@types/fs-extra": "11.0.1",
|
|
72
74
|
"@types/mime-types": "2.1.4",
|
|
73
|
-
"@types/node": "22.
|
|
75
|
+
"@types/node": "22.14.1",
|
|
74
76
|
"@types/normalize-path": "3.0.2",
|
|
75
77
|
"@types/react": "19.0.10",
|
|
76
78
|
"@types/react-dom": "19.0.4",
|
|
77
79
|
"@types/webpack": "5.28.5",
|
|
78
|
-
|
|
79
|
-
autoprefixer: "10.4.20",
|
|
80
|
+
autoprefixer: "10.4.21",
|
|
80
81
|
clsx: "2.1.1",
|
|
81
|
-
"framer-motion": "12.
|
|
82
|
+
"framer-motion": "12.7.5",
|
|
82
83
|
jiti: "2.4.2",
|
|
83
84
|
json5: "2.2.3",
|
|
84
85
|
"module-punycode": "npm:punycode@2.3.1",
|
|
85
|
-
"node-html-parser": "
|
|
86
|
-
postcss: "8.
|
|
87
|
-
"prettier-plugin-tailwindcss": "0.6.6",
|
|
86
|
+
"node-html-parser": "7.0.1",
|
|
87
|
+
postcss: "8.5.3",
|
|
88
88
|
"pretty-bytes": "6.1.1",
|
|
89
|
-
"prism-react-renderer": "2.1
|
|
89
|
+
"prism-react-renderer": "2.4.1",
|
|
90
90
|
react: "19.0.0",
|
|
91
91
|
"react-dom": "19.0.0",
|
|
92
|
-
sharp: "0.
|
|
93
|
-
"socket.io-client": "4.8.
|
|
94
|
-
sonner: "
|
|
95
|
-
"source-map-js": "1.
|
|
92
|
+
sharp: "0.34.1",
|
|
93
|
+
"socket.io-client": "4.8.1",
|
|
94
|
+
sonner: "2.0.3",
|
|
95
|
+
"source-map-js": "1.2.1",
|
|
96
96
|
spamc: "0.0.5",
|
|
97
|
-
"stacktrace-parser": "0.1.
|
|
98
|
-
"tailwind-merge": "
|
|
97
|
+
"stacktrace-parser": "0.1.11",
|
|
98
|
+
"tailwind-merge": "3.2.0",
|
|
99
99
|
tailwindcss: "3.4.0",
|
|
100
|
-
tsup: "
|
|
101
|
-
tsx: "4.
|
|
102
|
-
typescript: "5.8.
|
|
100
|
+
tsup: "8.4.0",
|
|
101
|
+
tsx: "4.19.3",
|
|
102
|
+
typescript: "5.8.3",
|
|
103
103
|
"use-debounce": "10.0.4",
|
|
104
|
-
zod: "3.24.
|
|
104
|
+
zod: "3.24.3"
|
|
105
105
|
}
|
|
106
106
|
};
|
|
107
107
|
|
|
@@ -115,17 +115,26 @@ import ora2 from "ora";
|
|
|
115
115
|
// src/utils/get-emails-directory-metadata.ts
|
|
116
116
|
import fs from "node:fs";
|
|
117
117
|
import path from "node:path";
|
|
118
|
-
var isFileAnEmail = (fullPath) => {
|
|
119
|
-
|
|
120
|
-
|
|
118
|
+
var isFileAnEmail = async (fullPath) => {
|
|
119
|
+
let fileHandle;
|
|
120
|
+
try {
|
|
121
|
+
fileHandle = await fs.promises.open(fullPath, "r");
|
|
122
|
+
} catch (exception) {
|
|
123
|
+
console.warn(exception);
|
|
121
124
|
return false;
|
|
122
|
-
|
|
123
|
-
|
|
125
|
+
}
|
|
126
|
+
const stat = await fileHandle.stat();
|
|
127
|
+
if (stat.isDirectory()) {
|
|
128
|
+
await fileHandle.close();
|
|
124
129
|
return false;
|
|
125
|
-
|
|
130
|
+
}
|
|
131
|
+
const { ext } = path.parse(fullPath);
|
|
132
|
+
if (![".js", ".tsx", ".jsx"].includes(ext)) {
|
|
133
|
+
await fileHandle.close();
|
|
126
134
|
return false;
|
|
127
135
|
}
|
|
128
|
-
const fileContents =
|
|
136
|
+
const fileContents = await fileHandle.readFile("utf8");
|
|
137
|
+
await fileHandle.close();
|
|
129
138
|
const hasES6DefaultExport = /\bexport\s+default\b/gm.test(fileContents);
|
|
130
139
|
const hasCommonJSExport = /\bmodule\.exports\s*=/gm.test(fileContents);
|
|
131
140
|
const hasNamedExport = /\bexport\s+\{[^}]*\bdefault\b[^}]*\}/gm.test(
|
|
@@ -148,14 +157,16 @@ var mergeDirectoriesWithSubDirectories = (emailsDirectoryMetadata) => {
|
|
|
148
157
|
return currentResultingMergedDirectory;
|
|
149
158
|
};
|
|
150
159
|
var getEmailsDirectoryMetadata = async (absolutePathToEmailsDirectory, keepFileExtensions = false, isSubDirectory = false, baseDirectoryPath = absolutePathToEmailsDirectory) => {
|
|
151
|
-
if (!fs.existsSync(absolutePathToEmailsDirectory))
|
|
152
|
-
return;
|
|
160
|
+
if (!fs.existsSync(absolutePathToEmailsDirectory)) return;
|
|
153
161
|
const dirents = await fs.promises.readdir(absolutePathToEmailsDirectory, {
|
|
154
162
|
withFileTypes: true
|
|
155
163
|
});
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
164
|
+
const isEmailPredicates = await Promise.all(
|
|
165
|
+
dirents.map(
|
|
166
|
+
(dirent) => isFileAnEmail(path.join(absolutePathToEmailsDirectory, dirent.name))
|
|
167
|
+
)
|
|
168
|
+
);
|
|
169
|
+
const emailFilenames = dirents.filter((_, i) => isEmailPredicates[i]).map(
|
|
159
170
|
(dirent) => keepFileExtensions ? dirent.name : dirent.name.replace(path.extname(dirent.name), "")
|
|
160
171
|
);
|
|
161
172
|
const subDirectories = await Promise.all(
|
|
@@ -212,76 +223,19 @@ var registerSpinnerAutostopping = (spinner) => {
|
|
|
212
223
|
spinners.add(spinner);
|
|
213
224
|
};
|
|
214
225
|
|
|
215
|
-
// src/cli/utils/tree.ts
|
|
216
|
-
import { promises as fs2 } from "node:fs";
|
|
217
|
-
import os from "node:os";
|
|
218
|
-
import path2 from "node:path";
|
|
219
|
-
var SYMBOLS = {
|
|
220
|
-
BRANCH: "\u251C\u2500\u2500 ",
|
|
221
|
-
EMPTY: "",
|
|
222
|
-
INDENT: " ",
|
|
223
|
-
LAST_BRANCH: "\u2514\u2500\u2500 ",
|
|
224
|
-
VERTICAL: "\u2502 "
|
|
225
|
-
};
|
|
226
|
-
var getTreeLines = async (dirPath, depth, currentDepth = 0) => {
|
|
227
|
-
const base = process.cwd();
|
|
228
|
-
const dirFullpath = path2.resolve(base, dirPath);
|
|
229
|
-
const dirname = path2.basename(dirFullpath);
|
|
230
|
-
let lines = [dirname];
|
|
231
|
-
const dirStat = await fs2.stat(dirFullpath);
|
|
232
|
-
if (dirStat.isDirectory() && currentDepth < depth) {
|
|
233
|
-
const childDirents = await fs2.readdir(dirFullpath, { withFileTypes: true });
|
|
234
|
-
childDirents.sort((a, b) => {
|
|
235
|
-
if (a.isDirectory() && b.isFile()) {
|
|
236
|
-
return -1;
|
|
237
|
-
}
|
|
238
|
-
if (a.isFile() && b.isDirectory()) {
|
|
239
|
-
return 1;
|
|
240
|
-
}
|
|
241
|
-
return b.name > a.name ? -1 : 1;
|
|
242
|
-
});
|
|
243
|
-
for (let i = 0; i < childDirents.length; i++) {
|
|
244
|
-
const dirent = childDirents[i];
|
|
245
|
-
const isLast = i === childDirents.length - 1;
|
|
246
|
-
const branchingSymbol = isLast ? SYMBOLS.LAST_BRANCH : SYMBOLS.BRANCH;
|
|
247
|
-
const verticalSymbol = isLast ? SYMBOLS.INDENT : SYMBOLS.VERTICAL;
|
|
248
|
-
if (dirent.isFile()) {
|
|
249
|
-
lines.push(`${branchingSymbol}${dirent.name}`);
|
|
250
|
-
} else {
|
|
251
|
-
const pathToDirectory = path2.join(dirFullpath, dirent.name);
|
|
252
|
-
const treeLinesForSubDirectory = await getTreeLines(
|
|
253
|
-
pathToDirectory,
|
|
254
|
-
depth,
|
|
255
|
-
currentDepth + 1
|
|
256
|
-
);
|
|
257
|
-
lines = lines.concat(
|
|
258
|
-
treeLinesForSubDirectory.map(
|
|
259
|
-
(line, index) => index === 0 ? `${branchingSymbol}${line}` : `${verticalSymbol}${line}`
|
|
260
|
-
)
|
|
261
|
-
);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
return lines;
|
|
266
|
-
};
|
|
267
|
-
var tree = async (dirPath, depth) => {
|
|
268
|
-
const lines = await getTreeLines(dirPath, depth);
|
|
269
|
-
return lines.join(os.EOL);
|
|
270
|
-
};
|
|
271
|
-
|
|
272
226
|
// src/cli/utils/preview/hot-reloading/setup-hot-reloading.ts
|
|
273
|
-
import
|
|
227
|
+
import path6 from "node:path";
|
|
274
228
|
import { watch } from "chokidar";
|
|
275
229
|
import debounce from "debounce";
|
|
276
230
|
import { Server as SocketServer } from "socket.io";
|
|
277
231
|
|
|
278
232
|
// src/cli/utils/preview/hot-reloading/create-dependency-graph.ts
|
|
279
|
-
import {
|
|
280
|
-
import
|
|
233
|
+
import { existsSync as existsSync2, promises as fs3, statSync } from "node:fs";
|
|
234
|
+
import path5 from "node:path";
|
|
281
235
|
|
|
282
236
|
// src/cli/utils/preview/start-dev-server.ts
|
|
283
237
|
import http from "node:http";
|
|
284
|
-
import
|
|
238
|
+
import path4 from "node:path";
|
|
285
239
|
import url from "node:url";
|
|
286
240
|
import chalk from "chalk";
|
|
287
241
|
import logSymbols2 from "log-symbols";
|
|
@@ -289,28 +243,33 @@ import next from "next";
|
|
|
289
243
|
import ora from "ora";
|
|
290
244
|
|
|
291
245
|
// src/cli/utils/preview/get-env-variables-for-preview-app.ts
|
|
292
|
-
import
|
|
246
|
+
import path2 from "node:path";
|
|
293
247
|
var getEnvVariablesForPreviewApp = (relativePathToEmailsDirectory, cwd) => {
|
|
294
248
|
return {
|
|
295
249
|
EMAILS_DIR_RELATIVE_PATH: relativePathToEmailsDirectory,
|
|
296
|
-
EMAILS_DIR_ABSOLUTE_PATH:
|
|
250
|
+
EMAILS_DIR_ABSOLUTE_PATH: path2.resolve(cwd, relativePathToEmailsDirectory),
|
|
297
251
|
USER_PROJECT_LOCATION: cwd,
|
|
298
252
|
NEXT_PUBLIC_IS_PREVIEW_DEVELOPMENT: isDev ? "true" : "false"
|
|
299
253
|
};
|
|
300
254
|
};
|
|
301
255
|
|
|
302
256
|
// src/cli/utils/preview/serve-static-file.ts
|
|
303
|
-
import { promises as
|
|
304
|
-
import
|
|
257
|
+
import { existsSync, promises as fs2 } from "node:fs";
|
|
258
|
+
import path3 from "node:path";
|
|
305
259
|
import { lookup } from "mime-types";
|
|
306
260
|
var serveStaticFile = async (res, parsedUrl, staticDirRelativePath) => {
|
|
307
|
-
const
|
|
308
|
-
const
|
|
309
|
-
const
|
|
310
|
-
const fileAbsolutePath =
|
|
261
|
+
const pathname = parsedUrl.pathname.replace("/static", "./static");
|
|
262
|
+
const ext = path3.parse(pathname).ext;
|
|
263
|
+
const staticBaseDir = path3.resolve(process.cwd(), staticDirRelativePath);
|
|
264
|
+
const fileAbsolutePath = path3.resolve(staticBaseDir, pathname);
|
|
265
|
+
if (!fileAbsolutePath.startsWith(staticBaseDir)) {
|
|
266
|
+
res.statusCode = 403;
|
|
267
|
+
res.end();
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
311
270
|
try {
|
|
312
|
-
const fileHandle = await
|
|
313
|
-
const fileData = await
|
|
271
|
+
const fileHandle = await fs2.open(fileAbsolutePath, "r");
|
|
272
|
+
const fileData = await fs2.readFile(fileHandle);
|
|
314
273
|
res.setHeader("Content-type", lookup(ext) || "text/plain");
|
|
315
274
|
res.end(fileData);
|
|
316
275
|
fileHandle.close();
|
|
@@ -346,9 +305,11 @@ var safeAsyncServerListen = (server, port) => {
|
|
|
346
305
|
});
|
|
347
306
|
});
|
|
348
307
|
};
|
|
349
|
-
var
|
|
350
|
-
var
|
|
351
|
-
var
|
|
308
|
+
var filename = url.fileURLToPath(import.meta.url);
|
|
309
|
+
var dirname = path4.dirname(filename);
|
|
310
|
+
var isDev = !filename.endsWith(path4.join("cli", "index.mjs"));
|
|
311
|
+
var cliPackageLocation = isDev ? path4.resolve(dirname, "../../../..") : path4.resolve(dirname, "../..");
|
|
312
|
+
var previewServerLocation = isDev ? path4.resolve(dirname, "../../../..") : path4.resolve(dirname, "../preview");
|
|
352
313
|
var startDevServer = async (emailsDirRelativePath, staticBaseDirRelativePath, port) => {
|
|
353
314
|
devServer = http.createServer((req, res) => {
|
|
354
315
|
if (!req.url) {
|
|
@@ -415,7 +376,7 @@ var startDevServer = async (emailsDirRelativePath, staticBaseDirRelativePath, po
|
|
|
415
376
|
...process.env,
|
|
416
377
|
...getEnvVariablesForPreviewApp(
|
|
417
378
|
// If we don't do normalization here, stuff like https://github.com/resend/react-email/issues/1354 happens.
|
|
418
|
-
|
|
379
|
+
path4.normalize(emailsDirRelativePath),
|
|
419
380
|
process.cwd()
|
|
420
381
|
)
|
|
421
382
|
};
|
|
@@ -475,7 +436,8 @@ process.on(
|
|
|
475
436
|
|
|
476
437
|
// src/cli/utils/preview/hot-reloading/get-imported-modules.ts
|
|
477
438
|
import { parse } from "@babel/parser";
|
|
478
|
-
import
|
|
439
|
+
import traverseModule from "@babel/traverse";
|
|
440
|
+
var traverse = traverseModule.default;
|
|
479
441
|
var getImportedModules = (contents) => {
|
|
480
442
|
const importedPaths = [];
|
|
481
443
|
const parsedContents = parse(contents, {
|
|
@@ -496,6 +458,9 @@ var getImportedModules = (contents) => {
|
|
|
496
458
|
importedPaths.push(node.source.value);
|
|
497
459
|
}
|
|
498
460
|
},
|
|
461
|
+
TSExternalModuleReference({ node }) {
|
|
462
|
+
importedPaths.push(node.expression.value);
|
|
463
|
+
},
|
|
499
464
|
CallExpression({ node }) {
|
|
500
465
|
if ("name" in node.callee && node.callee.name === "require") {
|
|
501
466
|
if (node.arguments.length === 1) {
|
|
@@ -513,9 +478,9 @@ var getImportedModules = (contents) => {
|
|
|
513
478
|
// src/cli/utils/preview/hot-reloading/create-dependency-graph.ts
|
|
514
479
|
var readAllFilesInsideDirectory = async (directory) => {
|
|
515
480
|
let allFilePaths = [];
|
|
516
|
-
const topLevelDirents = await
|
|
481
|
+
const topLevelDirents = await fs3.readdir(directory, { withFileTypes: true });
|
|
517
482
|
for await (const dirent of topLevelDirents) {
|
|
518
|
-
const pathToDirent =
|
|
483
|
+
const pathToDirent = path5.join(directory, dirent.name);
|
|
519
484
|
if (dirent.isDirectory()) {
|
|
520
485
|
allFilePaths = allFilePaths.concat(
|
|
521
486
|
await readAllFilesInsideDirectory(pathToDirent)
|
|
@@ -527,7 +492,7 @@ var readAllFilesInsideDirectory = async (directory) => {
|
|
|
527
492
|
return allFilePaths;
|
|
528
493
|
};
|
|
529
494
|
var isJavascriptModule = (filePath) => {
|
|
530
|
-
const extensionName =
|
|
495
|
+
const extensionName = path5.extname(filePath);
|
|
531
496
|
return [".js", ".ts", ".jsx", ".tsx", ".mjs", ".cjs"].includes(extensionName);
|
|
532
497
|
};
|
|
533
498
|
var checkFileExtensionsUntilItExists = (pathWithoutExtension) => {
|
|
@@ -565,23 +530,23 @@ var createDependencyGraph = async (directory) => {
|
|
|
565
530
|
])
|
|
566
531
|
);
|
|
567
532
|
const getDependencyPaths = async (filePath) => {
|
|
568
|
-
const contents = await
|
|
569
|
-
const importedPaths = getImportedModules(contents);
|
|
533
|
+
const contents = await fs3.readFile(filePath, "utf8");
|
|
534
|
+
const importedPaths = isJavascriptModule(filePath) ? getImportedModules(contents) : [];
|
|
570
535
|
const importedPathsRelativeToDirectory = importedPaths.map(
|
|
571
536
|
(dependencyPath) => {
|
|
572
537
|
const isModulePath = !dependencyPath.startsWith(".");
|
|
573
|
-
if (isModulePath ||
|
|
538
|
+
if (isModulePath || path5.isAbsolute(dependencyPath)) {
|
|
574
539
|
return dependencyPath;
|
|
575
540
|
}
|
|
576
|
-
let pathToDependencyFromDirectory =
|
|
541
|
+
let pathToDependencyFromDirectory = path5.resolve(
|
|
577
542
|
/*
|
|
578
543
|
path.resolve resolves paths differently from what imports on javascript do.
|
|
579
544
|
|
|
580
|
-
So if we wouldn't do this, for an email at "/path/to/email.tsx" with a
|
|
545
|
+
So if we wouldn't do this, for an email at "/path/to/email.tsx" with a dependency path of "./other-email"
|
|
581
546
|
would end up going into /path/to/email.tsx/other-email instead of /path/to/other-email which is the
|
|
582
547
|
one the import is meant to go to
|
|
583
548
|
*/
|
|
584
|
-
|
|
549
|
+
path5.dirname(filePath),
|
|
585
550
|
dependencyPath
|
|
586
551
|
);
|
|
587
552
|
let isDirectory = false;
|
|
@@ -603,9 +568,7 @@ var createDependencyGraph = async (directory) => {
|
|
|
603
568
|
}
|
|
604
569
|
}
|
|
605
570
|
if (!isJavascriptModule(pathToDependencyFromDirectory)) {
|
|
606
|
-
const pathWithExtension = checkFileExtensionsUntilItExists(
|
|
607
|
-
pathToDependencyFromDirectory
|
|
608
|
-
);
|
|
571
|
+
const pathWithExtension = path5.extname(pathToDependencyFromDirectory).length > 0 ? pathToDependencyFromDirectory : checkFileExtensionsUntilItExists(pathToDependencyFromDirectory);
|
|
609
572
|
if (pathWithExtension) {
|
|
610
573
|
pathToDependencyFromDirectory = pathWithExtension;
|
|
611
574
|
} else if (isDev) {
|
|
@@ -618,10 +581,10 @@ var createDependencyGraph = async (directory) => {
|
|
|
618
581
|
}
|
|
619
582
|
);
|
|
620
583
|
const moduleDependencies = importedPathsRelativeToDirectory.filter(
|
|
621
|
-
(dependencyPath) => !dependencyPath.startsWith(".") && !
|
|
584
|
+
(dependencyPath) => !dependencyPath.startsWith(".") && !path5.isAbsolute(dependencyPath)
|
|
622
585
|
);
|
|
623
586
|
const nonNodeModuleImportPathsRelativeToDirectory = importedPathsRelativeToDirectory.filter(
|
|
624
|
-
(dependencyPath) => dependencyPath.startsWith(".") ||
|
|
587
|
+
(dependencyPath) => dependencyPath.startsWith(".") || path5.isAbsolute(dependencyPath)
|
|
625
588
|
);
|
|
626
589
|
return {
|
|
627
590
|
dependencyPaths: nonNodeModuleImportPathsRelativeToDirectory,
|
|
@@ -629,17 +592,18 @@ var createDependencyGraph = async (directory) => {
|
|
|
629
592
|
};
|
|
630
593
|
};
|
|
631
594
|
const updateModuleDependenciesInGraph = async (moduleFilePath) => {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
595
|
+
if (graph[moduleFilePath] === void 0) {
|
|
596
|
+
graph[moduleFilePath] = {
|
|
597
|
+
path: moduleFilePath,
|
|
598
|
+
dependencyPaths: [],
|
|
599
|
+
dependentPaths: [],
|
|
600
|
+
moduleDependencies: []
|
|
601
|
+
};
|
|
602
|
+
}
|
|
638
603
|
const { moduleDependencies, dependencyPaths: newDependencyPaths } = await getDependencyPaths(moduleFilePath);
|
|
639
|
-
|
|
640
|
-
for (const dependencyPath of
|
|
641
|
-
if (newDependencyPaths.includes(dependencyPath))
|
|
642
|
-
continue;
|
|
604
|
+
graph[moduleFilePath].moduleDependencies = moduleDependencies;
|
|
605
|
+
for (const dependencyPath of graph[moduleFilePath].dependencyPaths) {
|
|
606
|
+
if (newDependencyPaths.includes(dependencyPath)) continue;
|
|
643
607
|
const dependencyModule = graph[dependencyPath];
|
|
644
608
|
if (dependencyModule !== void 0) {
|
|
645
609
|
dependencyModule.dependentPaths = dependencyModule.dependentPaths.filter(
|
|
@@ -647,21 +611,21 @@ var createDependencyGraph = async (directory) => {
|
|
|
647
611
|
);
|
|
648
612
|
}
|
|
649
613
|
}
|
|
650
|
-
|
|
651
|
-
for (const dependencyPath of newDependencyPaths) {
|
|
614
|
+
graph[moduleFilePath].dependencyPaths = newDependencyPaths;
|
|
615
|
+
for await (const dependencyPath of newDependencyPaths) {
|
|
616
|
+
if (graph[dependencyPath] === void 0) {
|
|
617
|
+
await updateModuleDependenciesInGraph(dependencyPath);
|
|
618
|
+
}
|
|
652
619
|
const dependencyModule = graph[dependencyPath];
|
|
653
|
-
if (dependencyModule
|
|
620
|
+
if (dependencyModule === void 0) {
|
|
621
|
+
throw new Error(
|
|
622
|
+
`Loading the dependency path ${dependencyPath} did not initialize it at all. This is a bug in React Email.`
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
if (!dependencyModule.dependentPaths.includes(moduleFilePath)) {
|
|
654
626
|
dependencyModule.dependentPaths.push(moduleFilePath);
|
|
655
|
-
} else {
|
|
656
|
-
graph[dependencyPath] = {
|
|
657
|
-
path: dependencyPath,
|
|
658
|
-
moduleDependencies: [],
|
|
659
|
-
dependencyPaths: [],
|
|
660
|
-
dependentPaths: [moduleFilePath]
|
|
661
|
-
};
|
|
662
627
|
}
|
|
663
628
|
}
|
|
664
|
-
graph[moduleFilePath] = module;
|
|
665
629
|
};
|
|
666
630
|
for (const filePath of modulePaths) {
|
|
667
631
|
await updateModuleDependenciesInGraph(filePath);
|
|
@@ -746,11 +710,19 @@ var setupHotreloading = async (devServer2, emailDirRelativePath) => {
|
|
|
746
710
|
let changes = [];
|
|
747
711
|
const reload = debounce(() => {
|
|
748
712
|
clients.forEach((client) => {
|
|
749
|
-
client.emit(
|
|
713
|
+
client.emit(
|
|
714
|
+
"reload",
|
|
715
|
+
changes.filter(
|
|
716
|
+
(change) => (
|
|
717
|
+
// Ensures only changes inside the emails directory are emitted
|
|
718
|
+
path6.resolve(absolutePathToEmailsDirectory, change.filename).startsWith(absolutePathToEmailsDirectory)
|
|
719
|
+
)
|
|
720
|
+
)
|
|
721
|
+
);
|
|
750
722
|
});
|
|
751
723
|
changes = [];
|
|
752
724
|
}, 150);
|
|
753
|
-
const absolutePathToEmailsDirectory =
|
|
725
|
+
const absolutePathToEmailsDirectory = path6.resolve(
|
|
754
726
|
process.cwd(),
|
|
755
727
|
emailDirRelativePath
|
|
756
728
|
);
|
|
@@ -760,7 +732,7 @@ var setupHotreloading = async (devServer2, emailDirRelativePath) => {
|
|
|
760
732
|
cwd: absolutePathToEmailsDirectory
|
|
761
733
|
});
|
|
762
734
|
const getFilesOutsideEmailsDirectory = () => Object.keys(dependencyGraph).filter(
|
|
763
|
-
(p) =>
|
|
735
|
+
(p) => path6.relative(absolutePathToEmailsDirectory, p).startsWith("..")
|
|
764
736
|
);
|
|
765
737
|
let filesOutsideEmailsDirectory = getFilesOutsideEmailsDirectory();
|
|
766
738
|
for (const p of filesOutsideEmailsDirectory) {
|
|
@@ -772,11 +744,11 @@ var setupHotreloading = async (devServer2, emailDirRelativePath) => {
|
|
|
772
744
|
process.on("SIGINT", exit);
|
|
773
745
|
process.on("uncaughtException", exit);
|
|
774
746
|
watcher.on("all", async (event, relativePathToChangeTarget) => {
|
|
775
|
-
const file = relativePathToChangeTarget.split(
|
|
747
|
+
const file = relativePathToChangeTarget.split(path6.sep);
|
|
776
748
|
if (file.length === 0) {
|
|
777
749
|
return;
|
|
778
750
|
}
|
|
779
|
-
const pathToChangeTarget =
|
|
751
|
+
const pathToChangeTarget = path6.resolve(
|
|
780
752
|
absolutePathToEmailsDirectory,
|
|
781
753
|
relativePathToChangeTarget
|
|
782
754
|
);
|
|
@@ -800,7 +772,7 @@ var setupHotreloading = async (devServer2, emailDirRelativePath) => {
|
|
|
800
772
|
for (const dependentPath of resolveDependentsOf(pathToChangeTarget)) {
|
|
801
773
|
changes.push({
|
|
802
774
|
event: "change",
|
|
803
|
-
filename:
|
|
775
|
+
filename: path6.relative(absolutePathToEmailsDirectory, dependentPath)
|
|
804
776
|
});
|
|
805
777
|
}
|
|
806
778
|
reload();
|
|
@@ -808,6 +780,63 @@ var setupHotreloading = async (devServer2, emailDirRelativePath) => {
|
|
|
808
780
|
return watcher;
|
|
809
781
|
};
|
|
810
782
|
|
|
783
|
+
// src/cli/utils/tree.ts
|
|
784
|
+
import { promises as fs4 } from "node:fs";
|
|
785
|
+
import os from "node:os";
|
|
786
|
+
import path7 from "node:path";
|
|
787
|
+
var SYMBOLS = {
|
|
788
|
+
BRANCH: "\u251C\u2500\u2500 ",
|
|
789
|
+
EMPTY: "",
|
|
790
|
+
INDENT: " ",
|
|
791
|
+
LAST_BRANCH: "\u2514\u2500\u2500 ",
|
|
792
|
+
VERTICAL: "\u2502 "
|
|
793
|
+
};
|
|
794
|
+
var getTreeLines = async (dirPath, depth, currentDepth = 0) => {
|
|
795
|
+
const base = process.cwd();
|
|
796
|
+
const dirFullpath = path7.resolve(base, dirPath);
|
|
797
|
+
const dirname2 = path7.basename(dirFullpath);
|
|
798
|
+
let lines = [dirname2];
|
|
799
|
+
const dirStat = await fs4.stat(dirFullpath);
|
|
800
|
+
if (dirStat.isDirectory() && currentDepth < depth) {
|
|
801
|
+
const childDirents = await fs4.readdir(dirFullpath, { withFileTypes: true });
|
|
802
|
+
childDirents.sort((a, b) => {
|
|
803
|
+
if (a.isDirectory() && b.isFile()) {
|
|
804
|
+
return -1;
|
|
805
|
+
}
|
|
806
|
+
if (a.isFile() && b.isDirectory()) {
|
|
807
|
+
return 1;
|
|
808
|
+
}
|
|
809
|
+
return b.name > a.name ? -1 : 1;
|
|
810
|
+
});
|
|
811
|
+
for (let i = 0; i < childDirents.length; i++) {
|
|
812
|
+
const dirent = childDirents[i];
|
|
813
|
+
const isLast = i === childDirents.length - 1;
|
|
814
|
+
const branchingSymbol = isLast ? SYMBOLS.LAST_BRANCH : SYMBOLS.BRANCH;
|
|
815
|
+
const verticalSymbol = isLast ? SYMBOLS.INDENT : SYMBOLS.VERTICAL;
|
|
816
|
+
if (dirent.isFile()) {
|
|
817
|
+
lines.push(`${branchingSymbol}${dirent.name}`);
|
|
818
|
+
} else {
|
|
819
|
+
const pathToDirectory = path7.join(dirFullpath, dirent.name);
|
|
820
|
+
const treeLinesForSubDirectory = await getTreeLines(
|
|
821
|
+
pathToDirectory,
|
|
822
|
+
depth,
|
|
823
|
+
currentDepth + 1
|
|
824
|
+
);
|
|
825
|
+
lines = lines.concat(
|
|
826
|
+
treeLinesForSubDirectory.map(
|
|
827
|
+
(line, index) => index === 0 ? `${branchingSymbol}${line}` : `${verticalSymbol}${line}`
|
|
828
|
+
)
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
return lines;
|
|
834
|
+
};
|
|
835
|
+
var tree = async (dirPath, depth) => {
|
|
836
|
+
const lines = await getTreeLines(dirPath, depth);
|
|
837
|
+
return lines.join(os.EOL);
|
|
838
|
+
};
|
|
839
|
+
|
|
811
840
|
// src/cli/commands/build.ts
|
|
812
841
|
var buildPreviewApp = (absoluteDirectory) => {
|
|
813
842
|
return new Promise((resolve, reject) => {
|
|
@@ -875,8 +904,8 @@ var getEmailSlugsFromEmailDirectory = (emailDirectory, emailsDirectoryAbsolutePa
|
|
|
875
904
|
const directoryPathRelativeToEmailsDirectory = emailDirectory.absolutePath.replace(emailsDirectoryAbsolutePath, "").trim();
|
|
876
905
|
const slugs = [];
|
|
877
906
|
emailDirectory.emailFilenames.forEach(
|
|
878
|
-
(
|
|
879
|
-
path8.join(directoryPathRelativeToEmailsDirectory,
|
|
907
|
+
(filename2) => slugs.push(
|
|
908
|
+
path8.join(directoryPathRelativeToEmailsDirectory, filename2).split(path8.sep).filter((segment) => segment.length > 0)
|
|
880
909
|
)
|
|
881
910
|
);
|
|
882
911
|
emailDirectory.subDirectories.forEach((directory) => {
|
|
@@ -1116,7 +1145,7 @@ var renderingUtilitiesExporter = (emailTemplates) => ({
|
|
|
1116
1145
|
var getEmailTemplatesFromDirectory = (emailDirectory) => {
|
|
1117
1146
|
const templatePaths = [];
|
|
1118
1147
|
emailDirectory.emailFilenames.forEach(
|
|
1119
|
-
(
|
|
1148
|
+
(filename2) => templatePaths.push(path10.join(emailDirectory.absolutePath, filename2))
|
|
1120
1149
|
);
|
|
1121
1150
|
emailDirectory.subDirectories.forEach((directory) => {
|
|
1122
1151
|
templatePaths.push(...getEmailTemplatesFromDirectory(directory));
|
|
@@ -1300,6 +1329,6 @@ program.command("export").description("Build the templates to the `out` director
|
|
|
1300
1329
|
"To, or not to show a spinner with process information",
|
|
1301
1330
|
false
|
|
1302
1331
|
).action(
|
|
1303
|
-
({ outDir, pretty, plainText, silent, dir: srcDir }) => exportTemplates(outDir, srcDir, {
|
|
1332
|
+
({ outDir, pretty, plainText, silent, dir: srcDir }) => exportTemplates(outDir, srcDir, { silent, plainText, pretty })
|
|
1304
1333
|
);
|
|
1305
1334
|
program.parse();
|