From 1f85de115608b9a12521dccd9df5c78edac796e2 Mon Sep 17 00:00:00 2001 From: gabriel miranda Date: Wed, 17 Sep 2025 17:09:55 -0300 Subject: [PATCH 1/8] update README --- benchmarks/tailwind-component/README.md | 26 +++++++------------------ 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/benchmarks/tailwind-component/README.md b/benchmarks/tailwind-component/README.md index 234d6d7e05..b202ecde74 100644 --- a/benchmarks/tailwind-component/README.md +++ b/benchmarks/tailwind-component/README.md @@ -10,6 +10,7 @@ determining the performance hits that the Tailwind component causes to try impro ├── src | ├── emails | ├── benchmark-0.0.12-vs-local-version.ts +| ├── benchmark-0.0.17-vs-local-version.ts | ├── benchmark-with-vs-without.ts | └── tailwind-render.ts ├── tailwind.config.js @@ -25,26 +26,13 @@ The `emails` folder contains examples to be used across different benchmarks. ## Running benchmarks -To avoid ESM problems, these benchmarks need to be compiled using `tsup`, -which can be done by running `pnpm compile`, and then using `node` directly. -Something like the following if you want to run the `with-vs-without` benchmark: - -```sh -pnpm compile && node ./dist/benchmark-with-vs-without.js -``` - -They are each compiled into a different entry on the `./dist` folder with their respective names. - We have scripts for each benchmark on our `./package.json` that you can try running: ```json -"scripts": { - "with-vs-without": "pnpm compile && node ./dist/benchmark-with-vs-without.js", - "before-perf-vs-after-perf": "pnpm compile && node ./dist/benchmark-0.0.12-vs-local-version", - - "flamegraph-render-tailwind": "pnpm compile && node --prof ./dist/tailwind-render && node --prof-process --preprocess -j isolate*.log | flamebearer", - - "compile": "tsup src/*.ts", - "lint": "eslint ." -}, + "scripts": { + "with-vs-without": "tsx ./src/benchmark-with-vs-without", + "0.0.17-vs-local": "tsx --max-old-space-size=256 ./src/benchmark-0.0.17-vs-local-version", + "0.0.12-vs-local": "tsx ./src/benchmark-0.0.12-vs-local-version", + "flamegraph-render-tailwind": "tsx --prof ./src/tailwind-render && node --prof-process --preprocess -j isolate*.log | flamebearer" + }, ``` From fcb2a50382ef8e8c631806b6a0aa02cc4576a9c5 Mon Sep 17 00:00:00 2001 From: gabriel miranda Date: Wed, 17 Sep 2025 17:14:12 -0300 Subject: [PATCH 2/8] use ESM for the tailwind-component benchmark --- benchmarks/tailwind-component/package.json | 2 +- benchmarks/tailwind-component/tsconfig.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/benchmarks/tailwind-component/package.json b/benchmarks/tailwind-component/package.json index 4f7c6b2967..512f619005 100644 --- a/benchmarks/tailwind-component/package.json +++ b/benchmarks/tailwind-component/package.json @@ -1,7 +1,7 @@ { "name": "@benchmarks/tailwind-component", "private": true, - "main": "dist/benchmark.js", + "type": "module", "version": "0.0.0", "scripts": { "with-vs-without": "tsx ./src/benchmark-with-vs-without", diff --git a/benchmarks/tailwind-component/tsconfig.json b/benchmarks/tailwind-component/tsconfig.json index 89dac25114..4912a1a40f 100644 --- a/benchmarks/tailwind-component/tsconfig.json +++ b/benchmarks/tailwind-component/tsconfig.json @@ -7,7 +7,8 @@ "target": "esnext", "noUncheckedIndexedAccess": true, "resolveJsonModule": true, - "moduleResolution": "Node", + "moduleResolution": "nodenext", + "module": "esnext", "declarationMap": false, "declaration": false, "outDir": "dist" From 0bed26312b6c25fe2774acb8502912e466a125ac Mon Sep 17 00:00:00 2001 From: gabriel miranda Date: Wed, 17 Sep 2025 17:17:53 -0300 Subject: [PATCH 3/8] update to using ESM, and moduleResolution 'bundler' --- benchmarks/preview-server/package.json | 1 + .../src/local-vs-2.1.7-canary.2-on-startup.ts | 42 +++++++-------- .../src/local-vs-2.1.7-canary.2.ts | 52 +++++++++---------- benchmarks/preview-server/tsconfig.json | 16 ++++++ .../src/benchmark-0.0.12-vs-local-version.tsx | 44 +++++++--------- .../src/benchmark-0.0.17-vs-local-version.tsx | 45 +++++++--------- .../src/benchmark-with-vs-without.tsx | 28 ++++------ benchmarks/tailwind-component/tsconfig.json | 2 +- 8 files changed, 111 insertions(+), 119 deletions(-) create mode 100644 benchmarks/preview-server/tsconfig.json diff --git a/benchmarks/preview-server/package.json b/benchmarks/preview-server/package.json index 9d54d2be3c..e0704fcb77 100644 --- a/benchmarks/preview-server/package.json +++ b/benchmarks/preview-server/package.json @@ -2,6 +2,7 @@ "name": "@benchmarks/preview-server", "private": true, "version": "0.0.0", + "type": "module", "scripts": { "local-vs-2.1.7-canary.2": "tsx ./src/local-vs-2.1.7-canary.2.ts", "local-vs-2.1.7-canary.2-on-startup": "tsx ./src/local-vs-2.1.7-canary.2-on-startup.ts" diff --git a/benchmarks/preview-server/src/local-vs-2.1.7-canary.2-on-startup.ts b/benchmarks/preview-server/src/local-vs-2.1.7-canary.2-on-startup.ts index 457a07b7e4..b03bd1061c 100644 --- a/benchmarks/preview-server/src/local-vs-2.1.7-canary.2-on-startup.ts +++ b/benchmarks/preview-server/src/local-vs-2.1.7-canary.2-on-startup.ts @@ -15,28 +15,26 @@ const pathToLocalCliScript = path.resolve( './node_modules/react-email/dist/cli/index.js', ); -(async () => { - const bench = new Bench({ - iterations: 30, - }); +const bench = new Bench({ + iterations: 30, +}); - bench - .add('startup on local', async () => { - const server = await runServer(pathToLocalCliScript); - await fetch(`${server.url}/preview/magic-links/notion-magic-link`); - server.subprocess.kill(); - }) - .add('startup on 2.1.7-canary.2', async () => { - const server = await runServer(pathToCanaryCliScript); - await fetch(`${server.url}/preview/magic-links/notion-magic-link`); - server.subprocess.kill(); - }); +bench + .add('startup on local', async () => { + const server = await runServer(pathToLocalCliScript); + await fetch(`${server.url}/preview/magic-links/notion-magic-link`); + server.subprocess.kill(); + }) + .add('startup on 2.1.7-canary.2', async () => { + const server = await runServer(pathToCanaryCliScript); + await fetch(`${server.url}/preview/magic-links/notion-magic-link`); + server.subprocess.kill(); + }); - await bench.run(); +await bench.run(); - await fs.writeFile( - 'startup-bench-results-30-iterations.json', - JSON.stringify(bench.results), - 'utf8', - ); -})(); +await fs.writeFile( + 'startup-bench-results-30-iterations.json', + JSON.stringify(bench.results), + 'utf8', +); diff --git a/benchmarks/preview-server/src/local-vs-2.1.7-canary.2.ts b/benchmarks/preview-server/src/local-vs-2.1.7-canary.2.ts index d79d698061..f0a9eeaaeb 100644 --- a/benchmarks/preview-server/src/local-vs-2.1.7-canary.2.ts +++ b/benchmarks/preview-server/src/local-vs-2.1.7-canary.2.ts @@ -15,32 +15,30 @@ const pathToLocalCliScript = path.resolve( './node_modules/react-email/dist/cli/index.js', ); -(async () => { - const bench = new Bench({ - iterations: 30, +const bench = new Bench({ + iterations: 30, +}); + +const localServer = await runServer(pathToLocalCliScript); +const canaryServer = await runServer(pathToCanaryCliScript); +bench + .add('local', async () => { + await fetch(`${localServer.url}/preview/magic-links/notion-magic-link`); + }) + .add('2.1.7-canary.2', async () => { + await fetch(`${canaryServer.url}/preview/magic-links/notion-magic-link`); }); - const localServer = await runServer(pathToLocalCliScript); - const canaryServer = await runServer(pathToCanaryCliScript); - bench - .add('local', async () => { - await fetch(`${localServer.url}/preview/magic-links/notion-magic-link`); - }) - .add('2.1.7-canary.2', async () => { - await fetch(`${canaryServer.url}/preview/magic-links/notion-magic-link`); - }); - - await fetch(`${localServer.url}/preview/magic-links/notion-magic-link`); - await fetch(`${canaryServer.url}/preview/magic-links/notion-magic-link`); - - await bench.run(); - - localServer.subprocess.kill(); - canaryServer.subprocess.kill(); - - await fs.writeFile( - 'bench-results-30-iterations.json', - JSON.stringify(bench.results), - 'utf8', - ); -})(); +await fetch(`${localServer.url}/preview/magic-links/notion-magic-link`); +await fetch(`${canaryServer.url}/preview/magic-links/notion-magic-link`); + +await bench.run(); + +localServer.subprocess.kill(); +canaryServer.subprocess.kill(); + +await fs.writeFile( + 'bench-results-30-iterations.json', + JSON.stringify(bench.results), + 'utf8', +); diff --git a/benchmarks/preview-server/tsconfig.json b/benchmarks/preview-server/tsconfig.json new file mode 100644 index 0000000000..ae3329377e --- /dev/null +++ b/benchmarks/preview-server/tsconfig.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "tsconfig/react-library.json", + "include": ["src"], + "exclude": ["node_modules"], + "compilerOptions": { + "target": "esnext", + "noUncheckedIndexedAccess": true, + "resolveJsonModule": true, + "moduleResolution": "bundler", + "module": "esnext", + "declarationMap": false, + "declaration": false, + "outDir": "dist" + } +} diff --git a/benchmarks/tailwind-component/src/benchmark-0.0.12-vs-local-version.tsx b/benchmarks/tailwind-component/src/benchmark-0.0.12-vs-local-version.tsx index a097d2dd77..de5488c082 100644 --- a/benchmarks/tailwind-component/src/benchmark-0.0.12-vs-local-version.tsx +++ b/benchmarks/tailwind-component/src/benchmark-0.0.12-vs-local-version.tsx @@ -5,32 +5,24 @@ import { Tailwind as VersionTwelveTailwind } from 'tailwind-0.0.12'; import { Bench } from 'tinybench'; import EmailWithTailwind from './emails/with-tailwind.js'; -const main = async () => { - const bench = new Bench({ - iterations: 100, - }); - - bench - .add('local', async () => { - await render(); - }) - .add('0.0.12', async () => { - // @ts-expect-error - await render(); - }); +const bench = new Bench({ + iterations: 100, +}); - await bench.run(); +bench + .add('local', async () => { + await render(); + }) + .add('0.0.12', async () => { + // @ts-expect-error + await render(); + }); - return bench; -}; +await bench.run(); -main() - .then((bench) => { - writeFileSync( - 'bench-results-100-iterations.json', - JSON.stringify(bench.results), - 'utf-8', - ); - console.table(bench.table()); - }) - .catch(console.error); +writeFileSync( + 'bench-results-100-iterations.json', + JSON.stringify(bench.results), + 'utf-8', +); +console.table(bench.table()); diff --git a/benchmarks/tailwind-component/src/benchmark-0.0.17-vs-local-version.tsx b/benchmarks/tailwind-component/src/benchmark-0.0.17-vs-local-version.tsx index b833451b29..adb69610f1 100644 --- a/benchmarks/tailwind-component/src/benchmark-0.0.17-vs-local-version.tsx +++ b/benchmarks/tailwind-component/src/benchmark-0.0.17-vs-local-version.tsx @@ -5,31 +5,26 @@ import { Tailwind as VersionSeventeenTailwind } from 'tailwind-0.0.17'; import { Bench } from 'tinybench'; import EmailWithTailwind from './emails/with-tailwind.js'; -const main = async () => { - const bench = new Bench({ - iterations: 100, - }); - - bench - .add('local', async () => { - await render(); - }) - .add('0.0.17', async () => { - await render(); - }); +const bench = new Bench({ + iterations: 100, +}); - await bench.run(); +bench + .add('local', async () => { + await render(); + }) + .add('0.0.17', async () => { + // Doing as any here because of the React types mismatch between versions, but things should be fine + await render( + , + ); + }); - return bench; -}; +await bench.run(); -main() - .then((bench) => { - writeFileSync( - 'bench-results-100-iterations.json', - JSON.stringify(bench.results), - 'utf-8', - ); - console.table(bench.table()); - }) - .catch(console.error); +writeFileSync( + 'bench-results-100-iterations.json', + JSON.stringify(bench.results), + 'utf-8', +); +console.table(bench.table()); diff --git a/benchmarks/tailwind-component/src/benchmark-with-vs-without.tsx b/benchmarks/tailwind-component/src/benchmark-with-vs-without.tsx index b4b30952cb..ab12989131 100644 --- a/benchmarks/tailwind-component/src/benchmark-with-vs-without.tsx +++ b/benchmarks/tailwind-component/src/benchmark-with-vs-without.tsx @@ -7,24 +7,16 @@ import EmailWithoutTailwind from './emails/without-tailwind.js'; // import like this instead of installing from the workspace // to still be able to test versions that are already published -async function main() { - const bench = new Bench({ time: 100 }); +const bench = new Bench({ time: 100 }); - bench - .add('without tailwind', async () => { - await render(); - }) - .add('with current tailwind', async () => { - await render(); - }); - - await bench.run(); +bench + .add('without tailwind', async () => { + await render(); + }) + .add('with current tailwind', async () => { + await render(); + }); - return bench; -} +await bench.run(); -main() - .then((bench) => { - console.table(bench.table()); - }) - .catch(console.error); +console.table(bench.table()); diff --git a/benchmarks/tailwind-component/tsconfig.json b/benchmarks/tailwind-component/tsconfig.json index 4912a1a40f..0cb9b5b8d2 100644 --- a/benchmarks/tailwind-component/tsconfig.json +++ b/benchmarks/tailwind-component/tsconfig.json @@ -7,7 +7,7 @@ "target": "esnext", "noUncheckedIndexedAccess": true, "resolveJsonModule": true, - "moduleResolution": "nodenext", + "moduleResolution": "bundler", "module": "esnext", "declarationMap": false, "declaration": false, From c73996b9169b86c525f85546598fdb583c68512a Mon Sep 17 00:00:00 2001 From: gabriel miranda Date: Wed, 17 Sep 2025 17:35:00 -0300 Subject: [PATCH 4/8] use import.meta.dirname inplace of __dirname --- .../preview-server/src/local-vs-2.1.7-canary.2-on-startup.ts | 4 ++-- benchmarks/preview-server/src/local-vs-2.1.7-canary.2.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/benchmarks/preview-server/src/local-vs-2.1.7-canary.2-on-startup.ts b/benchmarks/preview-server/src/local-vs-2.1.7-canary.2-on-startup.ts index b03bd1061c..faaacae2d2 100644 --- a/benchmarks/preview-server/src/local-vs-2.1.7-canary.2-on-startup.ts +++ b/benchmarks/preview-server/src/local-vs-2.1.7-canary.2-on-startup.ts @@ -4,13 +4,13 @@ import { Bench } from 'tinybench'; import { runServer } from './utils/run-server'; const pathToCanaryCliScript = path.resolve( - __dirname, + import.meta.dirname, '../', './node_modules/react-email-2.1.7-canary.2/cli/index.js', ); const pathToLocalCliScript = path.resolve( - __dirname, + import.meta.dirname, '../', './node_modules/react-email/dist/cli/index.js', ); diff --git a/benchmarks/preview-server/src/local-vs-2.1.7-canary.2.ts b/benchmarks/preview-server/src/local-vs-2.1.7-canary.2.ts index f0a9eeaaeb..6645861d6a 100644 --- a/benchmarks/preview-server/src/local-vs-2.1.7-canary.2.ts +++ b/benchmarks/preview-server/src/local-vs-2.1.7-canary.2.ts @@ -4,13 +4,13 @@ import { Bench } from 'tinybench'; import { runServer } from './utils/run-server'; const pathToCanaryCliScript = path.resolve( - __dirname, + import.meta.dirname, '../', './node_modules/react-email-2.1.7-canary.2/cli/index.js', ); const pathToLocalCliScript = path.resolve( - __dirname, + import.meta.dirname, '../', './node_modules/react-email/dist/cli/index.js', ); From 398bbb24f1a8b2a1e075ac4e5a0c3ce5723452c6 Mon Sep 17 00:00:00 2001 From: gabriel miranda Date: Wed, 17 Sep 2025 17:35:04 -0300 Subject: [PATCH 5/8] update file extensions in README --- benchmarks/tailwind-component/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/benchmarks/tailwind-component/README.md b/benchmarks/tailwind-component/README.md index b202ecde74..614c1e9b8b 100644 --- a/benchmarks/tailwind-component/README.md +++ b/benchmarks/tailwind-component/README.md @@ -9,10 +9,10 @@ determining the performance hits that the Tailwind component causes to try impro ├── package.json ├── src | ├── emails -| ├── benchmark-0.0.12-vs-local-version.ts -| ├── benchmark-0.0.17-vs-local-version.ts -| ├── benchmark-with-vs-without.ts -| └── tailwind-render.ts +| ├── benchmark-0.0.12-vs-local-version.tsx +| ├── benchmark-0.0.17-vs-local-version.tsx +| ├── benchmark-with-vs-without.tsx +| └── tailwind-render.tsx ├── tailwind.config.js └── tsconfig.json ``` From 654fc938fa6bf7750da8632f87f2950f007bcd18 Mon Sep 17 00:00:00 2001 From: gabriel miranda Date: Wed, 17 Sep 2025 17:41:28 -0300 Subject: [PATCH 6/8] use warmupIterations intead of fetching manually --- benchmarks/preview-server/src/local-vs-2.1.7-canary.2.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/benchmarks/preview-server/src/local-vs-2.1.7-canary.2.ts b/benchmarks/preview-server/src/local-vs-2.1.7-canary.2.ts index 6645861d6a..417da3834e 100644 --- a/benchmarks/preview-server/src/local-vs-2.1.7-canary.2.ts +++ b/benchmarks/preview-server/src/local-vs-2.1.7-canary.2.ts @@ -17,6 +17,7 @@ const pathToLocalCliScript = path.resolve( const bench = new Bench({ iterations: 30, + warmupIterations: 5, }); const localServer = await runServer(pathToLocalCliScript); @@ -29,9 +30,6 @@ bench await fetch(`${canaryServer.url}/preview/magic-links/notion-magic-link`); }); -await fetch(`${localServer.url}/preview/magic-links/notion-magic-link`); -await fetch(`${canaryServer.url}/preview/magic-links/notion-magic-link`); - await bench.run(); localServer.subprocess.kill(); From a4f17cc265396aaff65caf99454ecb9d5d997b3b Mon Sep 17 00:00:00 2001 From: gabriel miranda Date: Wed, 17 Sep 2025 17:49:29 -0300 Subject: [PATCH 7/8] remove unsused function --- .../run-server-and-fetch-preview-page.ts | 40 ------------------- 1 file changed, 40 deletions(-) delete mode 100644 benchmarks/preview-server/src/utils/run-server-and-fetch-preview-page.ts diff --git a/benchmarks/preview-server/src/utils/run-server-and-fetch-preview-page.ts b/benchmarks/preview-server/src/utils/run-server-and-fetch-preview-page.ts deleted file mode 100644 index 39015ebd37..0000000000 --- a/benchmarks/preview-server/src/utils/run-server-and-fetch-preview-page.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { spawn } from 'node:child_process'; -import path from 'node:path'; - -const decoder = new TextDecoder(); - -export function runServerAndFetchPreviewPage(pathToCliScript: string) { - return new Promise((resolve, reject) => { - const node = spawn('node', [pathToCliScript, 'dev'], { - cwd: path.resolve(__dirname, '../../../../apps/demo'), - }); - - node.stdout.on('data', (data) => { - const content = decoder.decode(data); - if (content.includes('Running preview at')) { - const url = /http:\/\/localhost:[\d]+/.exec(content)?.[0]; - if (url) { - fetch(`${url}/preview/magic-links/notion-magic-link`) - .then(async () => { - node.kill(); - resolve(); - }) - .catch(() => { - node.kill(); - reject(); - }); - } else { - node.kill(); - reject( - new Error( - 'URL was non existant in the same line, maybe we changed the way this is displayed?', - { - cause: { content, pathToCliScript }, - }, - ), - ); - } - } - }); - }); -} From 79fdf6aa290e1984248229929151d431f77cde45 Mon Sep 17 00:00:00 2001 From: gabriel miranda Date: Wed, 17 Sep 2025 17:51:11 -0300 Subject: [PATCH 8/8] try avoinding race conditions of sub processes not being closed --- .../src/local-vs-2.1.7-canary.2-on-startup.ts | 16 +++++++++++---- .../preview-server/src/utils/run-server.ts | 20 ++++++++++++++++++- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/benchmarks/preview-server/src/local-vs-2.1.7-canary.2-on-startup.ts b/benchmarks/preview-server/src/local-vs-2.1.7-canary.2-on-startup.ts index faaacae2d2..7a139e58a0 100644 --- a/benchmarks/preview-server/src/local-vs-2.1.7-canary.2-on-startup.ts +++ b/benchmarks/preview-server/src/local-vs-2.1.7-canary.2-on-startup.ts @@ -21,14 +21,22 @@ const bench = new Bench({ bench .add('startup on local', async () => { - const server = await runServer(pathToLocalCliScript); - await fetch(`${server.url}/preview/magic-links/notion-magic-link`); - server.subprocess.kill(); + try { + const server = await runServer(pathToLocalCliScript); + await fetch(`${server.url}/preview/magic-links/notion-magic-link`); + if (!server.subprocess.kill()) { + throw new Error('could not close sub process for preview server'); + } + } catch (err) { + console.error('Error starting local server:', err); + } }) .add('startup on 2.1.7-canary.2', async () => { const server = await runServer(pathToCanaryCliScript); await fetch(`${server.url}/preview/magic-links/notion-magic-link`); - server.subprocess.kill(); + if (!server.subprocess.kill()) { + throw new Error('could not close sub process for preview server'); + } }); await bench.run(); diff --git a/benchmarks/preview-server/src/utils/run-server.ts b/benchmarks/preview-server/src/utils/run-server.ts index cdd3ec62e5..ce4c94a3fe 100644 --- a/benchmarks/preview-server/src/utils/run-server.ts +++ b/benchmarks/preview-server/src/utils/run-server.ts @@ -11,7 +11,25 @@ export interface Server { export function runServer(pathToCliScript: string) { return new Promise((resolve, reject) => { const node = spawn('node', [pathToCliScript, 'dev'], { - cwd: path.resolve(__dirname, '../../../../apps/demo'), + cwd: path.resolve(import.meta.dirname, '../../../../apps/demo'), + }); + + const kill = () => { + node.kill(); + }; + + process.addListener('exit', kill); + process.addListener('SIGINT', kill); + process.addListener('SIGTERM', kill); + process.addListener('SIGUSR1', kill); + process.addListener('SIGUSR2', kill); + + node.on('close', () => { + process.removeListener('exit', kill); + process.removeListener('SIGINT', kill); + process.removeListener('SIGTERM', kill); + process.removeListener('SIGUSR1', kill); + process.removeListener('SIGUSR2', kill); }); node.stdout.on('data', (data) => {