Skip to content

Commit a31a25f

Browse files
committed
feat(vs-code-ext): Use vm to render email previews instead of creating .vscpreview folder
1 parent ff1bbb9 commit a31a25f

File tree

2 files changed

+63
-57
lines changed

2 files changed

+63
-57
lines changed

vs-code-extension/.eslintrc.json

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
{
2-
"root": true,
3-
"parser": "@typescript-eslint/parser",
4-
"parserOptions": {
5-
"ecmaVersion": 6,
6-
"sourceType": "module"
7-
},
8-
"plugins": [
9-
"@typescript-eslint"
10-
],
11-
"rules": {
12-
"@typescript-eslint/naming-convention": "warn",
13-
"@typescript-eslint/semi": "warn",
14-
"curly": "warn",
15-
"eqeqeq": "warn",
16-
"no-throw-literal": "warn",
17-
"semi": "off"
18-
},
19-
"ignorePatterns": [
20-
"out",
21-
"dist",
22-
"**/*.d.ts"
23-
]
2+
"root": true,
3+
"parser": "@typescript-eslint/parser",
4+
"parserOptions": {
5+
"ecmaVersion": 6,
6+
"sourceType": "module"
7+
},
8+
"plugins": [
9+
"@typescript-eslint"
10+
],
11+
"rules": {
12+
"@typescript-eslint/naming-convention": "off",
13+
"@typescript-eslint/semi": "warn",
14+
"curly": "warn",
15+
"eqeqeq": "warn",
16+
"no-throw-literal": "warn",
17+
"semi": "off"
18+
},
19+
"ignorePatterns": [
20+
"out",
21+
"dist",
22+
"**/*.d.ts"
23+
]
2424
}

vs-code-extension/src/render-open-email-file.ts

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
11
import * as vscode from "vscode";
22

3-
import * as crypto from "crypto";
4-
import { basename, join } from "path";
5-
import { tmpdir } from "os";
3+
import * as path from "path";
4+
import * as vm from "vm";
65
import * as esbuild from "esbuild";
76

87
import { render } from "@react-email/render";
9-
import { unlink } from "fs/promises";
10-
11-
const extensionPreviewFolder = ".vscpreview" as const;
128

139
export type BuiltEmail =
1410
| {
15-
filename: string;
16-
html: string;
17-
text: string;
18-
valid: true;
19-
}
11+
filename: string;
12+
html: string;
13+
text: string;
14+
valid: true;
15+
}
2016
| { valid: false };
2117

2218
export async function renderOpenEmailFile(
@@ -32,51 +28,61 @@ export async function renderOpenEmailFile(
3228
}
3329

3430
const currentlyOpenTabFilePath = activeEditor.document.fileName; // actually a path not the name of the file
35-
const currentlyOpenTabFilename = basename(currentlyOpenTabFilePath, ".tsx");
36-
37-
// saves the temporary previews generated in the tmp folder
38-
const previewDirectory = join(tmpdir(), extensionPreviewFolder);
39-
40-
// this hash is needed so the the import doesn't get from its cache
41-
const renderingHash = crypto.randomBytes(20).toString("hex");
42-
43-
const builtFileWithCurrentContents = join(
44-
previewDirectory,
45-
`${currentlyOpenTabFilename}-${renderingHash}.js`,
31+
const currentlyOpenTabFilename = path.basename(
32+
currentlyOpenTabFilePath,
33+
".tsx",
4634
);
4735

4836
try {
49-
await esbuild.build({
37+
const buildResult = await esbuild.build({
5038
bundle: true,
5139
entryPoints: [currentlyOpenTabFilePath],
5240
platform: "node",
53-
write: true,
41+
write: false,
5442
jsx: "automatic",
43+
loader: {
44+
// eslint-disable-next-line @typescript-eslint/naming-convention
45+
".js": "jsx",
46+
},
5547
format: "cjs",
56-
outfile: builtFileWithCurrentContents,
5748
});
5849

59-
// for future people debugging this: if this doesnt update the preview, might be because import keeps a cache
60-
// and the hash is not being unique (unlikely though)
61-
const email = require(builtFileWithCurrentContents);
50+
console.log(global);
6251

63-
if (typeof email.default === "undefined") {
64-
await unlink(builtFileWithCurrentContents);
52+
const fakeContext = {
53+
...global,
54+
process: {
55+
env: {}
56+
},
57+
module: { exports: { default: undefined as unknown } },
58+
__filanem: currentlyOpenTabFilePath,
59+
__dirname: path.dirname(currentlyOpenTabFilePath),
60+
};
61+
62+
try {
63+
vm.runInNewContext(buildResult.outputFiles[0].text, fakeContext, {
64+
filename: currentlyOpenTabFilePath,
65+
});
66+
} catch (exception) {
67+
throw exception;
68+
}
6569

66-
// this means there is no "export default ..." in the file
67-
return { valid: false };
70+
if (typeof fakeContext.module.exports.default !== "function") {
71+
return {
72+
valid: false,
73+
};
6874
}
6975

70-
const comp = email.default(email.default.PreviewProps ?? {}); // this may come without a defualt which might cause problems
76+
const email = fakeContext.module.exports.default;
77+
78+
const comp = email("PreviewProps" in email ? email.PreviewProps : {}); // this may come without a defualt which might cause problems
7179
const emailAsText = render(comp, {
7280
plainText: true,
7381
pretty: false,
7482
});
7583

7684
const emailAsHTML = render(comp, { pretty: false });
7785

78-
await unlink(builtFileWithCurrentContents);
79-
8086
return {
8187
filename: currentlyOpenTabFilename,
8288
html: emailAsHTML,

0 commit comments

Comments
 (0)