import { defineConfig, type Plugin } from "vite"; import react from "@vitejs/plugin-react-swc"; import { lingui } from "@lingui/vite-plugin"; import fs from "node:fs"; import path from "node:path"; const SITE_NAME = process.env.GERBEUR_SITE_NAME || "gerbeur"; function manifestPlugin(): Plugin { const cssPath = path.resolve("src/index.css"); const outPath = path.resolve("public/manifest.webmanifest"); function cssVar(rootBlock: string, name: string): string | undefined { return rootBlock.match( new RegExp(`${name.replace("-", "\\-")}:\\s*(#[0-9a-fA-F]{3,8})`), )?.[1]; } function generate() { const css = fs.readFileSync(cssPath, "utf-8"); // Only read the first :root block — dark-mode defaults, before any @media overrides const rootBlock = css.match(/:root\s*\{([^}]+)\}/)?.[1] ?? ""; const bgColor = cssVar(rootBlock, "--color-bg") ?? "#111827"; const manifest = { name: SITE_NAME, short_name: SITE_NAME, start_url: "/", display: "standalone", background_color: bgColor, theme_color: bgColor, icons: [{ src: "/favicon.svg", type: "image/svg+xml", sizes: "any" }], share_target: { action: "/", method: "GET", params: { url: "share_url", title: "share_title", text: "share_text" }, }, }; fs.writeFileSync(outPath, JSON.stringify(manifest, null, 2) + "\n"); } return { name: "generate-manifest", buildStart: generate, configureServer(server) { generate(); server.watcher.on("change", (file) => { if (path.resolve(file) === cssPath) generate(); }); }, }; } export default defineConfig({ server: { port: 3000, watch: { ignored: ["**/api/**"], }, }, build: { rollupOptions: { output: { manualChunks(id) { if (id.includes("react-markdown") || id.includes("remark-gfm") || id.includes("remark-parse") || id.includes("mdast") || id.includes("micromark") || id.includes("unist")) { return "vendor-markdown"; } if (id.includes("node_modules")) { return "vendor"; } }, }, }, }, plugins: [ manifestPlugin(), { // In dev the API server isn't serving index.html, so replace the placeholder here. // In production the Oak server does the replacement at runtime (see api/lib/static.ts). name: "inject-site-name", transformIndexHtml: { order: "pre", handler: (html, ctx) => ctx.server ? html.replaceAll("__SITE_NAME__", SITE_NAME) : html, }, }, lingui(), react({ plugins: [["@lingui/swc-plugin", {}]], }), ], });