chore: run analysis before SSR build - #16966
Conversation
|
Install the latest version of pnpm add https://pkg.svelte.dev/@sveltejs/kit/c/305c6adf9385c576159576fefbf3f97c2341c052Open in |
|
The analysis Vite server called `listen()`, which occupied port 5173 mid-build. That made Playwright's `webServer` readiness check pass before `vite build` had finished, and would clash with a running dev server. The module runner doesn't need a listening server, so use `middlewareMode` instead. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
I investigated this failed test run, but the investigation came up empty. What I tried
The new analysis dev server temporarily binds the application's default port 5173 during |
…d, keeping the forked analyse worker's event loop alive and leaking a worker + dev server on every `vite build --watch` rebuild.
This commit fixes the issue reported at packages/kit/src/core/postbuild/analyse.js:176
## Bug
Commit `807451b` ("Update analyse.js") re-indented `analyse()` and, in doing so, dropped the `try { ... } finally { await vite_dev_server.close(); }` wrapper that previously surrounded the analysis body. As a result the dev server created by:
```js
const vite_dev_server = await vite.createServer(vite_config);
```
is never closed — neither on the success path nor on the several `throw new Error(...)` error paths inside the function.
### Why this leaks
`analyse` runs inside a worker via `forked(import.meta.url, analyse)` (`packages/kit/src/utils/fork.js`). After the worker posts its result, the parent only calls `worker.unref()` — it never calls `worker.terminate()`. `unref()` merely tells the parent not to wait for the worker; it does **not** stop it. So the worker only goes away if its own event loop drains and it exits on its own.
An open (unclosed) Vite dev server keeps open handles alive even with `middlewareMode: true, hmr: false, watch: null` (e.g. the dep optimizer / esbuild service). That keeps the worker's event loop alive, so the worker never exits.
For a one-shot `vite build` this is harmless (the main process exits shortly after). But in `vite build --watch`, `before_ssr_build_rerun` (`packages/kit/src/exports/vite/build/index.js`) calls `analyse()` on every rebuild, spawning a fresh worker each time. Since none of them exit, workers **and their dev servers accumulate across rebuilds** — a handle/memory leak.
### Empirical verification
I reproduced the exact configuration used by `analyse()` (`server: { middlewareMode: true, hmr: false, watch: null }`, `logLevel: 'silent'`) with Vite 8 and loaded a module via `ssrLoadModule`, using an **unref'd** watchdog timer so only the server's own handles could keep the process alive:
* **Without `close()`**: the process was still alive after 8 s and only exited when the watchdog fired → the server keeps the event loop alive (leak confirmed).
* **With `await server.close()`**: the process exited cleanly in ~48 ms.
This also refutes the concern that re-adding `close()` might reintroduce a hang in `middlewareMode`: `close()` completed in ~48 ms with no hang.
## Fix
Restored the `try { ... } finally { await vite_dev_server.close(); }` wrapper around the analysis body in `packages/kit/src/core/postbuild/analyse.js`, so the dev server is closed on both success and error paths. This lets each forked worker's event loop drain and the worker exit cleanly, preventing worker/dev-server accumulation across `vite build --watch` rebuilds. Verified with `node --check` and the empirical test above.
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: teemingc <chewteeming01@gmail.com>
This PR uses a Vite dev server to analyse the nodes rather than relying on the SSR build. Doing so allows us to:
cloudflare:workers)