import { defineConfig, loadEnv } from 'vite' import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' import { fileURLToPath, URL } from 'node:url' // In dev, Vite serves the SPA with HMR and proxies /api to the Go backend. The // backend listens on PANSY_PORT (default 8080); read it so the proxy target // tracks an overridden port. The production build is embedded into the Go binary // (see internal/webdist), so no proxy is involved there. export default defineConfig(({ mode }) => { const env = loadEnv(mode, process.cwd(), '') const backendPort = env.PANSY_PORT || process.env.PANSY_PORT || '8080' return { plugins: [react(), tailwindcss()], resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)), }, }, server: { port: 5173, proxy: { '/api': { target: `http://127.0.0.1:${backendPort}`, changeOrigin: true, }, }, }, build: { outDir: 'dist', sourcemap: true, rollupOptions: { output: { // One vendor chunk for ALL node_modules: it's rarely-changing, so it // caches across app deploys while the tiny app chunk churns. Kept as a // SINGLE chunk on purpose — splitting react-dom/scheduler into their own // chunk reorders their module init across chunk boundaries and breaks // React 19 at load ("Cannot set 'Activity' of undefined"). The heavy // routes are code-split separately via React.lazy (router.tsx), which is // where the real first-paint win is. manualChunks(id) { if (!id.includes('node_modules')) return undefined // Editor-only libs (the gesture engine) ride with the lazy editor // chunk instead of the eager vendor one, so the first paint doesn't // pay for code only the canvas needs. Everything else — react, tanstack // and the rest — stays in ONE vendor chunk; splitting react-dom out // reorders its init across chunks and breaks React 19 at load. if (id.includes('@use-gesture')) return undefined return 'vendor' }, }, }, }, } })