Skip to content

Commit 9dad5b0

Browse files
committed
fix(tests): stop esbuild child process in global teardown for Node 22 CI
Node 22 changed stream.pipeline() to wait for the "close" event before completing (nodejs/node#53462). Vite's internal esbuild child process holds stdio handles open indefinitely, preventing the event loop from draining after tests finish. This causes vitest to hang in CI on Node 22 even though all tests pass. Adding a global teardown that calls esbuild.stop() explicitly kills the esbuild process and releases its handles so vitest can exit cleanly.
1 parent 617a63c commit 9dad5b0

2 files changed

Lines changed: 15 additions & 0 deletions

File tree

test/vitest-global-teardown.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { stop as stopEsbuild } from "esbuild";
2+
3+
// Node 22 changed stream.pipeline() to wait for the "close" event before
4+
// completing (nodejs/node#53462). The esbuild child process that vite spawns
5+
// for TypeScript transforms holds stdio handles open indefinitely, which
6+
// prevents the Node event loop from draining after tests finish. Explicitly
7+
// stopping esbuild releases those handles so vitest can exit cleanly.
8+
//
9+
// On Node 20 (and locally on Node 22 with faster shutdown timing), vitest
10+
// exits before this becomes a problem. In CI on Node 22, the slower runner
11+
// timing consistently triggers the hang.
12+
export async function teardown(): Promise<void> {
13+
await stopEsbuild();
14+
}

vitest.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export default defineConfig({
1818
test: {
1919
globals: true,
2020
environment: "node",
21+
globalSetup: ["./test/vitest-global-teardown.ts"],
2122
exclude: ["apps/**", "web-ui/**", "third_party/**", "**/node_modules/**", "**/dist/**", ".worktrees/**"],
2223
testTimeout: 15_000,
2324
},

0 commit comments

Comments
 (0)