-
Notifications
You must be signed in to change notification settings - Fork 53
fix(jsx-email): port build/compile path fixes to v3 #386
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: next/v3
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 }) => <h1>Hello {name}</h1>;\n`, | ||
| 'utf8' | ||
| ); | ||
|
Comment on lines
+17
to
+22
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test creates a To keep the test focused on path correctness (not JSX configuration), consider using a no-JSX module or an explicit runtime import/directive so the test fails only for the intended reason. SuggestionMake the entrypoint independent of JSX settings by exporting plain JS/TS: await writeFile(
entryPoint,
`export const Template = ({ name }: { name: string }) => "Hello " + name;\n`,
'utf8'
);If you specifically want JSX coverage, add an explicit directive/import appropriate to your expected runtime (e.g. Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion. |
||
|
|
||
| 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'); | ||
|
Comment on lines
+24
to
+33
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The new test verifies importability, but it never asserts that the compiled output landed under Also, dynamically importing SuggestionTighten the test so it asserts both location and importability. Example adjustments: import { pathToFileURL } from 'node:url';
import { resolve, relative, sep } from 'node:path';
// ...
const results = await compile({ files: [entryPoint], outDir });
const result = results[0];
if (!result) throw new Error('Expected compile to return at least one output');
// Assert it is inside outDir (no traversal)
const relToOut = relative(outDir, result.path);
expect(relToOut === '' || (!relToOut.startsWith(`..${sep}`) && relToOut !== '..')).toBe(true);
// Always import via file URL for cross-platform consistency
const mod = await import(pathToFileURL(result.path).toString());
expect(typeof mod.Template).toBe('function');Reply with "@CharlieHelps yes please" if you want me to add a commit that strengthens the test in this way. |
||
| } finally { | ||
| await rm(tmpRoot, { recursive: true, force: true }); | ||
| } | ||
| }); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
path.relative(outputBasePath, baseDir)can return paths like..whenbaseDiris outsideoutputBasePath(or when one is realpathed and the other isn’t). That would causejoin(out!, relativeBaseDir, templateName)to write outside ofout(directory traversal) or into unexpected locations. The oldreplacehack was also flawed, but it typically wouldn’t introduce..segments.Given this is a CLI that writes to disk based on user-provided inputs/flags, it’s worth hardening: either ensure
baseDiris actually underoutputBasePath(after normalizing/realpathing both), or clamp/fallback to''whenrelativeBaseDirescapes.Suggestion
Consider validating that the computed relative path stays within
out.For example:
If you’d like, reply with "@CharlieHelps yes please" and I can add a commit with this guard (plus a small unit test around the escaping case if the code is easily exercised).