trails/apps/journal/vite.config.ts
Ullrich Schäfer 97cc759dca Pre-warm Vite dep discovery for the journal dev server
Adds `server.warmup.clientFiles` so Vite eagerly transforms the
journal entry + every page route at server startup. Without it,
the first visit to a previously-unseen route triggers Vite's
"new dependencies optimized → reloading" path, which is a full
page reload — fine as a one-time blip in everyday dev, painful
when an e2e run is hitting many routes back-to-back on a fresh
server (each new route reload races with whatever the test was
doing).

This is a partial improvement, not a complete fix, for the local
auth e2e flakiness investigated earlier today. The deeper issue
there is a separate React-hydration-vs-Playwright-click race in
dev mode (production CI doesn't see it because production JS
hydrates faster). That needs a different fix at the test/fixture
layer; this commit just removes one source of noise from the
investigation by eliminating the dep-discovery reload.

Glob picks up `.tsx` files only — `.ts` route handlers under
app/routes/ are server-only API endpoints and Vite refuses to
transform them for the client (errors "Server-only module
referenced by client"). Page components alone drive the
dep-discovery we care about.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 19:51:39 +02:00

94 lines
4.2 KiB
TypeScript

import { reactRouter } from "@react-router/dev/vite";
import { sentryVitePlugin } from "@sentry/vite-plugin";
import tailwindcss from "@tailwindcss/vite";
import { defineConfig } from "vite";
import path from "node:path";
export default defineConfig({
build: {
sourcemap: "hidden",
},
plugins: [
tailwindcss(),
reactRouter(),
// Local HTTPS dev is OPT-IN via `HTTPS=1`. Plain HTTP is the default
// and the right choice for ~everything: WebAuthn (passkeys) treats
// `localhost` as a secure context regardless of scheme, magic links
// work over HTTP, sessions / Terms gate / SSE all work over HTTP.
// CI doesn't set HTTPS=1 either — the e2e suite runs against the
// production `react-router-serve` build over plain HTTP and passes
// cleanly. So most contributors should never touch this flag.
//
// The one case where you DO need HTTPS=1 locally:
// - Wahoo OAuth callback testing — Wahoo (and most OAuth
// providers) reject `http://` redirect URIs, so the
// /api/sync/connect/wahoo flow can only complete against an
// HTTPS dev server. See docs/tooling.md for the full
// command (`HTTPS=1 ORIGIN=https://localhost:3000 pnpm
// --filter @trails-cool/journal dev`) and gotchas.
//
// If you find yourself wanting HTTPS=1 for any other reason, write
// it down in docs/tooling.md — the assumption that "everything but
// Wahoo works over HTTP locally" is what keeps the CI/local
// configurations symmetric.
...(process.env.HTTPS === "1" ? [import("@vitejs/plugin-basic-ssl").then((m) => m.default())] : []),
sentryVitePlugin({
org: "trails-qq",
project: "journal",
release: { name: process.env.SENTRY_RELEASE },
sourcemaps: { filesToDeleteAfterUpload: ["./build/**/*.map"] },
disable: !process.env.SENTRY_AUTH_TOKEN,
telemetry: false,
}),
],
resolve: {
alias: {
"~": path.resolve(__dirname, "./app"),
},
},
server: {
port: 3000,
host: true,
// Pre-transform the entry + every route at server startup so Vite
// discovers every dependency it'll ever need before the first
// browser request lands. Two real benefits:
//
// 1. Eliminates the `[vite] new dependencies optimized → reloading`
// full page reload that otherwise fires on the *first* visit to
// a route Vite hasn't seen yet. For everyday dev this reload is
// a one-time blip; for batch operations (e.g. e2e runs hitting
// many routes back-to-back on a fresh server) it's a real source
// of flakiness.
//
// 2. Hydration is faster on first visit because the JS bundle is
// already optimized, which incidentally narrows (but does NOT
// fully close) a separate Playwright-vs-hydration race in the
// auth e2e tests. That race is hydration-timing, not Vite
// dep-discovery, and needs a different fix to fully resolve.
//
// Listing all route .tsx files via a glob keeps this future-proof
// — any new route added under app/routes/ is included automatically.
warmup: {
clientFiles: [
"./app/entry.client.tsx",
"./app/root.tsx",
// Page routes only (.tsx). The .ts handlers under routes/ are
// server-only API endpoints that import `*.server.ts` modules
// — Vite refuses to transform those for the client and prints
// "Server-only module referenced by client" if you include them.
// Page components alone are enough to drive dep discovery.
"./app/routes/**/*.tsx",
],
},
// Force HTTP/1.1 over TLS in HTTPS dev. Vite v8 starts an
// `http2.createSecureServer({ allowHTTP1: true })` for any HTTPS
// config, so ALPN negotiates h2 by default. That breaks
// `useFetcher().Form` POSTs because React Router's CSRF check in
// `singleFetchAction` compares `Origin` against the `Host` header,
// which HTTP/2 replaces with `:authority` and Node doesn't
// synthesize back. Restricting ALPN to `http/1.1` keeps the Host
// header intact and lets fetcher form submissions through. Only
// matters when `HTTPS=1` is set — see the plugins comment above.
https: process.env.HTTPS === "1" ? { ALPNProtocols: ["http/1.1"] } : undefined,
},
});