import { defineConfig, loadEnv, type Plugin } from "vite"; import react from "@vitejs/plugin-react-swc"; import path from "path"; import { execSync } from "child_process"; /** * Read the current git commit at build time so the Deploy Status panel can * show which frontend revision is live. Falls back gracefully in sandboxes * where `git` isn't available or the workspace isn't a git checkout. */ function readGit(cmd: string, fallback: string): string { try { return execSync(cmd, { stdio: ["ignore", "pipe", "ignore"] }) .toString() .trim(); } catch { return fallback; } } const GIT_COMMIT = process.env.VITE_GIT_COMMIT || readGit("git rev-parse --short HEAD", "unknown"); const GIT_COMMIT_FULL = process.env.VITE_GIT_COMMIT_FULL || readGit("git rev-parse HEAD", "unknown"); const GIT_COMMIT_DATE = process.env.VITE_GIT_COMMIT_DATE || readGit("git log -1 --format=%cI", ""); const GIT_BRANCH = process.env.VITE_GIT_BRANCH || readGit("git rev-parse --abbrev-ref HEAD", "unknown"); const BUILD_TIME = new Date().toISOString(); /** * How this build is deployed and which repo it came from — the update notice * says what "update" means for THIS instance: a fork on Vercel syncs on GitHub, * a Docker image pulls a newer tag, a source checkout pulls main. */ const DEPLOY_KIND = process.env.FLOWWINK_DEPLOY_KIND || (process.env.VERCEL ? "vercel" : "source"); function repoFromRemote(url: string): string { const m = url.match(/github\.com[:/]([^/]+\/[^/.]+?)(?:\.git)?$/); return m ? m[1] : ""; } const SOURCE_REPO = process.env.VERCEL_GIT_REPO_OWNER && process.env.VERCEL_GIT_REPO_SLUG ? `${process.env.VERCEL_GIT_REPO_OWNER}/${process.env.VERCEL_GIT_REPO_SLUG}` : process.env.FLOWWINK_SOURCE_REPO || repoFromRemote(readGit("git config --get remote.origin.url", "")); /** * Runtime-overridable Supabase config. Every `import.meta.env.VITE_SUPABASE_*` * access in src/ is compiled to a `__FLOWWINK_RUNTIME__.` lookup — a * global assembled by index.html from (a) the values baked in at build time * (Vite's %VITE_*% HTML replacement) and (b) /runtime-config.js overrides. * This lets one Docker image serve any Supabase instance: the container * entrypoint regenerates runtime-config.js from container env vars on every * start, so self-hosted deployments (Easypanel) switch instances with a * restart instead of a rebuild. On Vercel and `vite dev`, * public/runtime-config.js sets no overrides and the baked values apply — * behavior is unchanged. * * NB: the define value must be an entity name (dotted identifier chain) — * esbuild's dep-optimizer pass rejects arbitrary expressions. Vitest is * unaffected (vitest.config.ts does not extend this config). * * NB2: the baked values reach index.html through the __BAKED_VITE_*__ * placeholders + the plugin below — NOT Vite's %VITE_*% HTML replacement, * which resolves through `define` and would circularly insert the define * string itself instead of the value. */ const RUNTIME_ENV_KEYS = [ "VITE_SUPABASE_URL", "VITE_SUPABASE_PUBLISHABLE_KEY", "VITE_SUPABASE_PROJECT_ID", ]; const RUNTIME_ENV_DEFINES = Object.fromEntries( RUNTIME_ENV_KEYS.map((key) => [`import.meta.env.${key}`, `__FLOWWINK_RUNTIME__.${key}`]), ); function bakedEnvHtmlPlugin(mode: string): Plugin { const fileEnv = loadEnv(mode, process.cwd(), ""); return { name: "flowwink-baked-env-html", transformIndexHtml(html) { return html.replace(/__BAKED_(VITE_[A-Z_]+)__/g, (_, key) => { // Same precedence as Vite itself: real process env beats .env files. const value = process.env[key] ?? fileEnv[key] ?? ""; if (/["\\]/.test(value)) throw new Error(`${key} must not contain quotes/backslashes`); return value; }); }, }; } export default defineConfig(({ mode }) => ({ server: { host: "0.0.0.0", port: 8080, strictPort: true, }, plugins: [react(), bakedEnvHtmlPlugin(mode)], resolve: { dedupe: ["react", "react-dom", "@codemirror/state", "@codemirror/view"], alias: { "@": path.resolve(__dirname, "./src"), "@templates": path.resolve(__dirname, "./templates"), }, }, define: { ...RUNTIME_ENV_DEFINES, __GIT_COMMIT__: JSON.stringify(GIT_COMMIT), __GIT_COMMIT_FULL__: JSON.stringify(GIT_COMMIT_FULL), __GIT_COMMIT_DATE__: JSON.stringify(GIT_COMMIT_DATE), __GIT_BRANCH__: JSON.stringify(GIT_BRANCH), __BUILD_TIME__: JSON.stringify(BUILD_TIME), __DEPLOY_KIND__: JSON.stringify(DEPLOY_KIND), __SOURCE_REPO__: JSON.stringify(SOURCE_REPO), }, }));