-
Notifications
You must be signed in to change notification settings - Fork 59
feat: bundler (esbuild, nextjs) integration tests #699
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: canary
Are you sure you want to change the base?
Changes from 81 commits
ef6e3d2
71e8a7d
5a88b17
302e620
1375e0b
f3f8df3
be7f792
4658a96
e5bbd56
f5abf15
3f9190f
a7ba412
f1e7df4
ed2e3c3
06dba00
0dae1bc
ebc0e95
177020a
6f8a641
499ec8e
ffa8622
446fb1e
feb8fd1
f98e4c2
1b1ac45
a1ec05f
1b4ad28
8cde75f
2a57b77
d6c46f2
5e19da5
d70ba6e
a83f4db
8cac8bd
40c68e8
2f650ee
0d32c63
85b8635
2352714
ea82ac0
0b02b80
f8535e6
84b76ff
e72ac57
e688309
6919b60
ef5f40a
ab1d509
ae5b029
a0b0943
c0b1b4f
e648bb9
34b2267
98fa90c
5d9d306
a5df0a5
9e4b04a
294aeba
bfb4026
2a5fd6d
2022031
54569ba
236c533
64a674f
ffe4fce
d4e34ee
798d80b
41b7f7b
aae3602
653a6a3
27a9a6c
4ba8340
afc815a
4747aea
def00a1
de2e0e3
f6b5102
f0ccb0e
8d6cb55
ac6889e
e00acf7
b4e9697
5858436
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 |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| node_modules | ||
| dist | ||
| build | ||
| .env.test |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| index.js |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| import { Resend } from 'resend'; | ||
|
|
||
| new Resend(''); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| { | ||
| "dependencies": { | ||
| "esbuild": "0.25.11", | ||
| "resend": "../.." | ||
| }, | ||
| "scripts": { | ||
| "build": "esbuild ./index.ts --bundle --platform=node --target=node18 --outfile=./index.js" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,93 @@ | ||||||
| import { spawnSync } from 'node:child_process'; | ||||||
| import fs from 'node:fs'; | ||||||
| import os from 'node:os'; | ||||||
| import path from 'node:path'; | ||||||
|
|
||||||
| describe('integrations', () => { | ||||||
| const sdkPath = path.resolve(__dirname, '..'); | ||||||
|
|
||||||
| beforeAll(() => { | ||||||
| const build = spawnSync('pnpm build', { | ||||||
| stdio: 'inherit', | ||||||
| cwd: path.resolve(__dirname, '..'), | ||||||
| shell: true, | ||||||
| }); | ||||||
| if (build.status !== 0) { | ||||||
| throw new Error('SDK build failed'); | ||||||
| } | ||||||
| }); | ||||||
|
|
||||||
| /** | ||||||
| * Create an extra temporary copy of the given integration so that there's @react-email/render module resolution from ../node_modules | ||||||
| * | ||||||
| * Also modifies the package.json to point to the SDK with an absolute path. | ||||||
| */ | ||||||
| async function prepareTemporaryIntegrationCopy(integrationPath: string) { | ||||||
| const temporaryIntegrationPath = path.resolve( | ||||||
| os.tmpdir(), | ||||||
| `resend-node-integration-${path.basename(integrationPath)}`, | ||||||
| ); | ||||||
| if (fs.existsSync(temporaryIntegrationPath)) { | ||||||
| await fs.promises.rm(temporaryIntegrationPath, { | ||||||
| recursive: true, | ||||||
| force: true, | ||||||
| }); | ||||||
| } | ||||||
| await fs.promises.mkdir(temporaryIntegrationPath, { recursive: true }); | ||||||
| await fs.promises.cp( | ||||||
| path.resolve(__dirname, integrationPath), | ||||||
| temporaryIntegrationPath, | ||||||
| { | ||||||
| recursive: true, | ||||||
| }, | ||||||
| ); | ||||||
|
|
||||||
| const testingLockPackageJson: { dependencies: Record<string, string> } = | ||||||
| JSON.parse( | ||||||
| await fs.promises.readFile( | ||||||
| path.resolve(temporaryIntegrationPath, 'package.json'), | ||||||
| 'utf8', | ||||||
| ), | ||||||
| ); | ||||||
| testingLockPackageJson.dependencies.resend = sdkPath; | ||||||
| await fs.promises.writeFile( | ||||||
| path.resolve(temporaryIntegrationPath, 'package.json'), | ||||||
| JSON.stringify(testingLockPackageJson, null, 2), | ||||||
| ); | ||||||
|
|
||||||
| return temporaryIntegrationPath; | ||||||
| } | ||||||
|
|
||||||
| test('nextjs', { timeout: 30_000 }, async () => { | ||||||
| const temporaryNextApp = await prepareTemporaryIntegrationCopy('./nextjs'); | ||||||
|
|
||||||
| const buildInstall = spawnSync( | ||||||
| 'npm install --install-links && npm run build', | ||||||
| { | ||||||
| stdio: 'inherit', | ||||||
| cwd: temporaryNextApp, | ||||||
| shell: true, | ||||||
| }, | ||||||
| ); | ||||||
| if (buildInstall.status !== 0) { | ||||||
| throw new Error('next.js build failed'); | ||||||
|
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 esbuild integration test throws an error saying "next.js build failed", so an esbuild failure would surface with the wrong integration name, making it harder to triage. Prompt for AI agents
Suggested change
|
||||||
| } | ||||||
| }); | ||||||
|
|
||||||
| test('esbuild', { timeout: 30_000 }, async () => { | ||||||
| const temporaryIntegration = | ||||||
| await prepareTemporaryIntegrationCopy('./esbuild'); | ||||||
|
|
||||||
| const buildInstall = spawnSync( | ||||||
| 'npm install --install-links && npm run build', | ||||||
| { | ||||||
| stdio: 'inherit', | ||||||
| cwd: temporaryIntegration, | ||||||
| shell: true, | ||||||
| }, | ||||||
| ); | ||||||
| if (buildInstall.status !== 0) { | ||||||
| throw new Error('next.js build failed'); | ||||||
| } | ||||||
| }); | ||||||
| }); | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
|
||
| # dependencies | ||
| package-lock.json | ||
| /node_modules | ||
| /.pnp | ||
| .pnp.* | ||
| .yarn/* | ||
| !.yarn/patches | ||
| !.yarn/plugins | ||
| !.yarn/releases | ||
| !.yarn/versions | ||
|
|
||
| # testing | ||
| /coverage | ||
|
|
||
| # next.js | ||
| /.next/ | ||
| /out/ | ||
|
|
||
| # production | ||
| /build | ||
|
|
||
| # misc | ||
| .DS_Store | ||
| *.pem | ||
|
|
||
| # debug | ||
| npm-debug.log* | ||
| yarn-debug.log* | ||
| yarn-error.log* | ||
| .pnpm-debug.log* | ||
|
|
||
| # env files (can opt-in for committing if needed) | ||
| .env* | ||
|
|
||
| # vercel | ||
| .vercel | ||
|
|
||
| # typescript | ||
| *.tsbuildinfo | ||
| next-env.d.ts |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| import { Resend } from 'resend'; | ||
|
|
||
| export function GET() { | ||
|
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. Rule violated: Initialisms and Acronyms Naming Conventions Rename the Prompt for AI agents |
||
| new Resend(''); | ||
|
|
||
| return new Response('Hello from this API route!', { status: 200 }); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| module.exports = {}; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "dependencies": { | ||
| "next": "15.5.6", | ||
| "react": "19.2.0", | ||
| "react-dom": "19.2.0", | ||
| "resend": "../.." | ||
| }, | ||
| "scripts": { | ||
| "build": "next build" | ||
| } | ||
| } |
Check failure
Code scanning / CodeQL
Insecure temporary file High test
Copilot Autofix
AI 15 days ago
To securely create a temporary directory, use a well-established library like
tmp, which ensures random directory names, exclusive creation, and appropriate permissions.Steps:
tmpin the test file.path.resolve(os.tmpdir(), ...), calltmp.dirSync()(or its async equivalent), which returns a unique, safely created temp directory path.tmphandle creation and permissions.Required changes:
import tmp from 'tmp';near the top.prepareTemporaryIntegrationCopy, replace the explicit construction oftemporaryIntegrationPath, its manual deletion, and the explicitmkdirwith a singletmp.dirSync()call.temporaryIntegrationPathas needed.