import tailwindcss from "@tailwindcss/vite"; import react from "@vitejs/plugin-react"; import { execSync } from "node:child_process"; import { readFileSync } from "node:fs"; import { rm } from "node:fs/promises"; import { join } from "node:path"; import { defineConfig, type Plugin } from "vite"; import { VitePWA } from "vite-plugin-pwa"; /** --color-space-900: the app's background, used for the splash and browser chrome. */ const THEME_COLOR = "#080d1d"; /** * Installable PWA (Step 11.1): manifest, and a service worker that precaches the * app shell, the self-hosted fonts (CR-005) and the generated map art (CR-003). * API calls are never cached; offline behaviour is Step 11.2. */ const pwa = VitePWA({ registerType: "autoUpdate", injectRegister: "auto", includeAssets: ["icons/favicon-32.png", "icons/apple-touch-icon.png"], manifest: { name: "Deadhead: Space Freight Tycoon", short_name: "Deadhead", description: "Run a freight company across the solar system.", theme_color: THEME_COLOR, background_color: THEME_COLOR, display: "standalone", orientation: "any", start_url: "/", scope: "/", icons: [ { src: "/icons/icon-192.png", sizes: "192x192", type: "image/png" }, { src: "/icons/icon-512.png", sizes: "512x512", type: "image/png" }, { src: "/icons/maskable-512.png", sizes: "512x512", type: "image/png", purpose: "maskable" }, ], }, workbox: { globPatterns: ["**/*.{js,css,html,woff2,png,webp,svg}"], // The source art is only a build input (decision 0.5.37); never precache it. globIgnores: ["assets/map/**"], navigateFallback: "/index.html", navigateFallbackDenylist: [/^\/api\//], cleanupOutdatedCaches: true, }, }); /** * The user's source map art (public/assets/map) is large and only an input to * scripts/map-art.mjs; the app loads public/assets/map-gen. Drop the sources * from the build output (decision 0.5.37). */ function omitSourceArt(): Plugin { let outDir = "dist"; return { name: "omit-source-art", apply: "build", configResolved(config) { outDir = config.build.outDir; }, async closeBundle() { await rm(join(outDir, "assets/map"), { recursive: true, force: true }); }, }; } /** Version from package.json and the git commit, shown in Settings → About (CR-011). */ function buildInfo() { const { version } = JSON.parse( readFileSync(new URL("./package.json", import.meta.url), "utf8"), ) as { version: string; }; let build = "dev"; try { build = execSync("git rev-parse --short HEAD", { stdio: ["ignore", "pipe", "ignore"] }) .toString() .trim(); } catch { // Not a git checkout (e.g. a tarball build): keep "dev". } return { __APP_VERSION__: JSON.stringify(version), __APP_BUILD__: JSON.stringify(build) }; } /** Host names the dev and preview servers answer to, besides localhost and IP addresses. */ const ALLOWED_HOSTS = ["deadhead.belowland.com"]; export default defineConfig({ define: buildInfo(), plugins: [react(), tailwindcss(), omitSourceArt(), pwa], server: { // Same-domain API in development too (decision 0.5.20). The server listens on // IPv4 only, so target 127.0.0.1 rather than localhost. // API_PROXY_TARGET lets a second dev stack (e.g. screenshots) use another API port. proxy: { "/api": process.env.API_PROXY_TARGET ?? "http://127.0.0.1:3000" }, allowedHosts: ALLOWED_HOSTS, }, preview: { allowedHosts: ALLOWED_HOSTS }, });