From d3ae41a576fd123c0fea8346b9a9bebd092a80b9 Mon Sep 17 00:00:00 2001 From: CharlieHelps Date: Tue, 9 Dec 2025 23:35:40 +0000 Subject: [PATCH] fix(jsx-email): fix v3 build/compile output paths --- packages/jsx-email/src/cli/commands/build.ts | 5 ++- packages/jsx-email/src/renderer/compile.ts | 2 +- .../test/render/compile-path.test.ts | 38 +++++++++++++++++++ 3 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 packages/jsx-email/test/render/compile-path.test.ts diff --git a/packages/jsx-email/src/cli/commands/build.ts b/packages/jsx-email/src/cli/commands/build.ts index 277782ca..4ef535de 100644 --- a/packages/jsx-email/src/cli/commands/build.ts +++ b/packages/jsx-email/src/cli/commands/build.ts @@ -5,7 +5,7 @@ import os from 'node:os'; import chalkTmpl from 'chalk-template'; import { globby } from 'globby'; import micromatch from 'micromatch'; -import { basename, dirname, extname, join, posix, resolve, win32 } from 'path'; +import { basename, dirname, extname, join, posix, relative, resolve, win32 } from 'path'; import { isWindows } from 'std-env'; import { pathToFileURL } from 'url'; import type { InferOutput as Infer } from 'valibot'; @@ -121,8 +121,9 @@ export const build = async (options: BuildOptions): Promise => { const templateName = basename(path, fileExt).replace(/-[^-]{8}$/, ''); const component = componentExport(renderProps); const baseDir = dirname(path); + const relativeBaseDir = outputBasePath ? relative(outputBasePath, baseDir) : ''; const writePath = outputBasePath - ? join(out!, baseDir.replace(outputBasePath, ''), templateName) + ? join(out!, relativeBaseDir, templateName) : join(out!, templateName); // const writePath = outputBasePath // ? join(out!, baseDir.replace(outputBasePath, ''), templateName + extension) diff --git a/packages/jsx-email/src/renderer/compile.ts b/packages/jsx-email/src/renderer/compile.ts index 10b2b35e..2aaa5814 100644 --- a/packages/jsx-email/src/renderer/compile.ts +++ b/packages/jsx-email/src/renderer/compile.ts @@ -78,7 +78,7 @@ export const compile = async (options: CompileOptions): Promise if (!entryPoint) return null; return { entryPoint, - path: resolve('/', path) + path: resolve(originalCwd, path) }; }) .filter(Boolean as any); diff --git a/packages/jsx-email/test/render/compile-path.test.ts b/packages/jsx-email/test/render/compile-path.test.ts new file mode 100644 index 00000000..2f4c1c9f --- /dev/null +++ b/packages/jsx-email/test/render/compile-path.test.ts @@ -0,0 +1,38 @@ +import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'; +import { join } from 'node:path'; +import { pathToFileURL } from 'node:url'; + +import { isWindows } from 'std-env'; + +import { compile } from '../../src/renderer/compile.js'; + +describe('compile', () => { + it('returns an importable path for nested entrypoints', async () => { + const tmpRoot = await mkdtemp(join(process.cwd(), '.tmp-jsx-email-compile-')); + + try { + const entryDir = join(tmpRoot, 'templates', 'nested'); + await mkdir(entryDir, { recursive: true }); + + const entryPoint = join(entryDir, 'template.tsx'); + await writeFile( + entryPoint, + `export const Template = ({ name }: { name: string }) =>

Hello {name}

;\n`, + 'utf8' + ); + + const outDir = join(tmpRoot, 'out'); + const results = await compile({ files: [entryPoint], outDir }); + const result = results[0]; + + if (!result) throw new Error('Expected compile to return at least one output'); + + const compiledImportPath = isWindows ? pathToFileURL(result.path).toString() : result.path; + + const mod = await import(compiledImportPath); + expect(typeof mod.Template).toBe('function'); + } finally { + await rm(tmpRoot, { recursive: true, force: true }); + } + }); +});