Refactor build pipeline to esbuild and tsx test runner#59
Refactor build pipeline to esbuild and tsx test runner#59
Conversation
Co-authored-by: FlandiaYingman <9929037+FlandiaYingman@users.noreply.github.com>
Co-authored-by: FlandiaYingman <9929037+FlandiaYingman@users.noreply.github.com>
Co-authored-by: FlandiaYingman <9929037+FlandiaYingman@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR refactors the project’s build and test workflow by switching production builds from tsc to an esbuild-driven script, and switching test execution to run TypeScript tests directly via tsx --test (no precompiled test output).
Changes:
- Add an esbuild-based build script (
scripts/build.js) to emit ESM JS intodist/src. - Update test helper to construct Fastify instances directly (avoiding
fastify-cli/helperundertsx). - Update
package.jsonscripts/deps and lockfile to includeesbuildandtsx.
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
yarn.lock |
Adds lock entries for esbuild, tsx, and transitive deps. |
test/helper.ts |
Replaces fastify-cli test helper usage with direct Fastify instance setup. |
scripts/build.js |
New esbuild build script that compiles src/**/*.ts into dist/src. |
package.json |
Updates build/test scripts and adds esbuild + tsx dev dependencies. |
Comments suppressed due to low confidence (1)
package.json:18
yarn devstill runstsc -wwhilebuildis now esbuild-based. Sincedev:startserves fromdist/src/app.js, changes during development will be emitted bytsc(not esbuild), which can lead to runtime differences vs the production build pipeline this PR introduces. Consider updatingwatch/devto use an esbuild watch mode (or otherwise ensure dev and prod builds use the same compiler/output semantics).
"build": "node scripts/build.js",
"build:test": "yarn build && node scripts/copy-test-assets.js",
"watch": "tsc -w",
"start": "fastify start -l info dist/src/app.js --options",
"dev": "yarn run build && concurrently -k -p '[{name}]' -n TypeScript,App -c yellow.bold,cyan.bold yarn:watch yarn:dev:start",
"dev:start": "fastify start --ignore-watch='.ts$' -w -l debug -P dist/src/app.js --options",
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
test/helper.ts
Outdated
| const fastify = Fastify(); | ||
| const appConfig = await config(); | ||
| await fastify.register(app, appConfig); | ||
| await fastify.ready(); |
There was a problem hiding this comment.
This test helper now creates Fastify() with default server options and then registers the app plugin. That bypasses the exported options from src/app.ts (e.g., pluginTimeout) which Fastify CLI typically uses when starting the app, so tests may run with materially different server configuration than production. Consider passing the relevant server options into the Fastify(...) constructor (and/or merging them with the test config) to keep parity.
There was a problem hiding this comment.
Passed the app’s pluginTimeout into the Fastify constructor so tests use the same server options as production in 35e50c8.
test/helper.ts
Outdated
|
|
||
| // Tear down our app after we are done | ||
| t.after(() => void app.close()); | ||
| t.after(() => void fastify.close()); |
There was a problem hiding this comment.
fastify.close() returns a Promise. Registering teardown as t.after(() => void fastify.close()) drops that Promise, so the test runner may not wait for the server to fully close (potentially causing flakiness or open handles). Prefer making the after-hook async and awaiting fastify.close() so teardown is guaranteed to complete.
| t.after(() => void fastify.close()); | |
| t.after(async () => { | |
| await fastify.close(); | |
| }); |
There was a problem hiding this comment.
Teardown now awaits fastify.close() via an async after-hook to avoid open handles (35e50c8).
| await build({ | ||
| entryPoints, | ||
| outdir: "dist/src", | ||
| outbase: "src", | ||
| platform: "node", | ||
| format: "esm", | ||
| sourcemap: true, | ||
| }); |
There was a problem hiding this comment.
src/app.ts imports ../package.json (relative to dist/src/app.js at runtime this becomes dist/package.json). The build script writes to dist/src but does not copy package.json into dist/, so yarn start / the Docker runner stage will fail with a missing JSON module at runtime. Consider copying package.json to dist/package.json as part of the build (or otherwise adjusting how app metadata is loaded so it resolves correctly after build).
There was a problem hiding this comment.
The build script now copies package.json into dist after building so the runtime JSON import resolves (35e50c8).
scripts/build.js
Outdated
| const entryPoints = await glob("src/**/*.ts"); | ||
|
|
||
| try { | ||
| await build({ | ||
| entryPoints, | ||
| outdir: "dist/src", | ||
| outbase: "src", | ||
| platform: "node", | ||
| format: "esm", | ||
| sourcemap: true, | ||
| }); |
There was a problem hiding this comment.
The build script doesn't clean dist/src before emitting outputs. Because Fastify autoload loads everything under dist/src/plugins and dist/src/routes, stale JS files from previous builds (e.g., after renames/deletes) can remain and still be autoloaded at runtime. Consider deleting/emptying dist/src at the start of the build to avoid shipping removed routes/plugins.
There was a problem hiding this comment.
The build script now removes dist/src before emitting to avoid stale autoloaded files (35e50c8).
|
@copilot apply changes based on the comments in this thread. In addition, remove all unused things (scripts, files, etc.) |
Co-authored-by: FlandiaYingman <9929037+FlandiaYingman@users.noreply.github.com>
Applied the review feedback and removed the unused build:test script and copy-test-assets helper in 35e50c8. |
Refactor the build system so production builds use esbuild and tests run directly via tsx without compiling or typechecking test sources.
tscbuild with an esbuild-driven script that glob-resolves TS entry points and outputs ESM todist/src.dist/srcbefore emitting and copypackage.jsonintodist/for runtime metadata resolution.tsx --testagainst.test.tsfiles, bypassing precompilation.{ "scripts": { "build": "node scripts/build.js", "test": "FASTIFY_AUTOLOAD_TYPESCRIPT=1 tsx --test --experimental-test-coverage test/**/*.test.ts" } }💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.