Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 33 additions & 13 deletions packages/react-email/src/utils/preview/start-dev-server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { existsSync, promises as fs } from 'node:fs';
import http from 'node:http';
import os from 'node:os';
import path from 'node:path';
import url from 'node:url';
import { createJiti } from 'jiti';
Expand Down Expand Up @@ -135,6 +137,24 @@ export const startDevServer = async (
conf.get('resendApiKey'),
),
};
if (!process.env.ESBUILD_BINARY_PATH) {
try {
const esbuild = createJiti(previewServer.esmResolve('esbuild'));
const platformPackage = `@esbuild/${process.platform}-${os.arch()}`;
const subpath =
process.platform === 'win32' ? 'esbuild.exe' : 'bin/esbuild';
const candidateBinaryPath = path.join(
await fs.realpath(esbuild.esmResolve(platformPackage)),
subpath,
Comment on lines +146 to +148
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: esmResolve() returns the module entry path (like .../bin/esbuild), so joining subpath again produces a non-existent path and prevents ESBUILD_BINARY_PATH from being set. Use the entry file’s directory before appending subpath so the binary can be found and the OSS-400 fix works.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/react-email/src/utils/preview/start-dev-server.ts, line 146:

<comment>`esmResolve()` returns the module entry path (like `.../bin/esbuild`), so joining `subpath` again produces a non-existent path and prevents `ESBUILD_BINARY_PATH` from being set. Use the entry file’s directory before appending `subpath` so the binary can be found and the OSS-400 fix works.</comment>

<file context>
@@ -139,34 +139,21 @@ export const startDevServer = async (
-        nodeModulesDir,
-        platformPkg,
-        'package.json',
+      const candidateBinaryPath = path.join(
+        await fs.realpath(esbuild.esmResolve(platformPackage)),
+        subpath,
</file context>
Suggested change
const candidateBinaryPath = path.join(
await fs.realpath(esbuild.esmResolve(platformPackage)),
subpath,
const candidateBinaryPath = path.join(
path.dirname(await fs.realpath(esbuild.esmResolve(platformPackage))),
subpath,
);

);
if (existsSync(candidateBinaryPath)) {
process.env.ESBUILD_BINARY_PATH =
await fs.realpath(candidateBinaryPath);
}
} catch (exception) {
console.error(exception);
}
}

const next = await previewServer.import<typeof import('next')['default']>(
'next',
Expand Down Expand Up @@ -194,21 +214,21 @@ const makeExitHandler =
| { shouldKillProcess: false }
| { shouldKillProcess: true; killWithErrorCode: boolean },
) =>
(codeSignalOrError: number | NodeJS.Signals | Error) => {
if (typeof devServer !== 'undefined') {
console.log('\nshutting down dev server');
devServer.close();
devServer = undefined;
}
(codeSignalOrError: number | NodeJS.Signals | Error) => {
if (typeof devServer !== 'undefined') {
console.log('\nshutting down dev server');
devServer.close();
devServer = undefined;
}

if (codeSignalOrError instanceof Error) {
console.error(codeSignalOrError);
}
if (codeSignalOrError instanceof Error) {
console.error(codeSignalOrError);
}

if (options?.shouldKillProcess) {
process.exit(options.killWithErrorCode ? 1 : 0);
}
};
if (options?.shouldKillProcess) {
process.exit(options.killWithErrorCode ? 1 : 0);
}
};

// do something when app is closing
process.on('exit', makeExitHandler());
Expand Down
Loading