web/vite.config.ts
Rama dev · réplica de lectura del repositorio original.
import { fileURLToPath } from "node:url";
import react from "@vitejs/plugin-react";
// From `vitest/config`, not `vite` — vite 8's own defineConfig rejects the `test` key
// (TS2769: 'test' does not exist in type 'UserConfigExport'). vitest re-exports a widened
// version that accepts it, which keeps test config colocated with the build config.
import { defineConfig } from "vitest/config";
export default defineConfig({
plugins: [react()],
build: {
rolldownOptions: {
output: {
// Vendors that change rarely get their own long-cached chunks; every page is already its own
// chunk via `React.lazy` in App.tsx. `codeSplitting` is rolldown's replacement for Rollup's
// `manualChunks`, which it still accepts but has deprecated.
codeSplitting: {
groups: [
{ name: "react", test: /[\\/]node_modules[\\/](react|react-dom|scheduler|react-router)[\\/]/ },
{ name: "query", test: /[\\/]node_modules[\\/]@tanstack[\\/]/ },
{ name: "icons", test: /[\\/]node_modules[\\/]lucide-react[\\/]/ },
{
name: "markdown",
test: /[\\/]node_modules[\\/](react-markdown|remark-[^\\/]+|micromark[^\\/]*|mdast-[^\\/]+|hast-[^\\/]+|unist-[^\\/]+|unified|vfile[^\\/]*)[\\/]/,
},
],
},
},
},
},
experimental: {
// URLs INSIDE the bundle are relative; only index.html names `/assets/...` absolutely.
//
// Behind APP_BASE_PATH the server rewrites `="/assets/` in index.html and nothing else
// (`shortlist/server/base_path.py::render_shell`). Lazy routes and the self-hosted fonts make
// Vite write asset URLs into the JS (the dynamic-import preload list) and the CSS (`url()` for
// each .woff2); left absolute, those would load from the proxy's root and the app would break
// under a prefix as a blank page. Relative, they resolve against the file that names them, which
// the rewritten shell already loaded from the right place. index.html itself must stay absolute:
// it is served for every deep link (`/runs/12`), where a relative `assets/` would miss.
renderBuiltUrl: (_filename, { hostType }) => (hostType === "html" ? undefined : { relative: true }),
},
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
server: {
proxy: {
// Defaults to the port a deployed container uses, which is the common case: hack on the UI
// against a real backend with real data. Point it somewhere else when the backend is also
// changing, or when you don't want clicks landing on a live server:
// SHORTLIST_API_PROXY=http://localhost:5960 pnpm dev (see scripts/devrun.sh)
"/api": {
target: process.env.SHORTLIST_API_PROXY ?? "http://localhost:5959",
changeOrigin: true,
},
},
},
test: {
environment: "jsdom",
setupFiles: ["./src/test/setup.ts"],
css: false,
// vitest's default is 5000ms, and it measures WALL CLOCK while every other worker is competing
// for the same cores. These are component tests: each one boots a jsdom environment and drives
// it through real `userEvent` interactions, so the per-test cost is a little work and a large
// multiple of that in scheduling. Measured on this suite: `row-editor.test.tsx` runs its 106
// tests in 11s ON ITS OWN and takes 145s inside the full run — the same tests, 13x the wall
// clock, purely from contention. At a 5s budget that put a shifting handful over the line on
// every run: 0 to 6 failures, never the same ones twice, in whichever files happened to be
// scheduled together. Every one of them passed when its file was run alone.
//
// So the budget was wrong, not the tests. A generous timeout costs nothing on a passing run —
// it is a ceiling, not a delay — and a genuinely hung test still fails, just later. Do NOT tune
// this back down to make the suite "feel faster": it makes nothing faster, it only decides how
// much CPU starvation gets reported as a test failure. CI runs this on a 2-core runner with no
// retry, where the starvation is worse than on any laptop.
testTimeout: 30_000,
hookTimeout: 30_000,
},
});