import path from "node:path"; import { execSync } from "node:child_process"; import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import tailwindcss from "@tailwindcss/vite"; import { TanStackRouterVite } from "@tanstack/router-plugin/vite"; import { sentryVitePlugin } from "@sentry/vite-plugin"; // Git sha local — utilisé en dev quand on n'a pas APP_VERSION du CI. // En prod c'est le CI qui passe APP_VERSION=${{ github.sha }} en build-arg. function resolveAppVersion(): string { if (process.env.APP_VERSION) return process.env.APP_VERSION; try { return execSync("git rev-parse --short HEAD").toString().trim(); } catch { return "dev"; } } // Sentry source map upload — actif uniquement si SENTRY_AUTH_TOKEN est défini // (typiquement injecté par le CI Gitea, pas en local). Sans ça, les stack // traces dans Sentry seraient illisibles (JS minifié). // // `filesToDeleteAfterUpload` est CRITIQUE : sans ça les .map sont copiés // dans le dist/ → COPY-és dans l'image Docker → servis par nginx → leak // du code source du SPA en prod. Avec ça, Sentry a les sourcemaps pour // désobfusquer les stack traces, et nginx ne sert plus que les .js minifiés. const sentryPlugin = process.env.SENTRY_AUTH_TOKEN ? sentryVitePlugin({ org: process.env.SENTRY_ORG ?? "rubis", project: "rubis-web", authToken: process.env.SENTRY_AUTH_TOKEN, sourcemaps: { assets: ["./dist/**"], filesToDeleteAfterUpload: ["./dist/**/*.map"], }, release: { name: process.env.APP_VERSION ?? "dev" }, }) : null; const APP_VERSION = resolveAppVersion(); // https://vite.dev/config/ export default defineConfig({ // Injecte VITE_APP_VERSION dans `import.meta.env` au build/dev sans avoir // à l'écrire dans .env (où on ne peut pas évaluer du shell). En CI, le // build-arg APP_VERSION est passé via process.env et override. define: { "import.meta.env.VITE_APP_VERSION": JSON.stringify(APP_VERSION), }, plugins: [ // Doit être déclaré AVANT react() (cf. doc TanStack Router). TanStackRouterVite({ routesDirectory: "./src/routes", generatedRouteTree: "./src/routeTree.gen.ts", autoCodeSplitting: true, }), react(), tailwindcss(), // Sentry doit être en dernier pour ne uploader que les artefacts finaux. ...(sentryPlugin ? [sentryPlugin] : []), ], resolve: { alias: { "@": path.resolve(__dirname, "./src"), }, }, server: { port: 5173, strictPort: true, proxy: { "/ingest/static": { target: "https://eu-assets.i.posthog.com", changeOrigin: true, rewrite: (path) => path.replace(/^\/ingest/, ""), }, "/ingest/array": { target: "https://eu-assets.i.posthog.com", changeOrigin: true, rewrite: (path) => path.replace(/^\/ingest/, ""), }, "/ingest": { target: "https://eu.i.posthog.com", changeOrigin: true, rewrite: (path) => path.replace(/^\/ingest/, ""), }, }, }, build: { // Source maps requises pour que Sentry désobfusque les stack traces. // En prod, le sentryPlugin les supprime du dist/ après upload pour // qu'elles ne soient pas servies publiquement par nginx. sourcemap: true, }, });