From 17d01aff21ea1b843a601970b271b868f93bba33 Mon Sep 17 00:00:00 2001 From: ruinivist <179396038+ruinivist@users.noreply.github.com> Date: Fri, 29 May 2026 18:14:25 +0000 Subject: [PATCH] fix: properly bundle and cache frontend assets - Build frontend assets natively with Bun to dist/public/ via `src/index.html` entrypoint - Serve static assets in production with `Cache-Control: public, max-age=31536000, immutable` caching headers - Preserve native `bun run dev` routing for seamless HMR by retaining development mode conditions in the server router - Exclude `index.html` from long-term cache headers to allow for production updates --- package.json | 2 +- src/server.tsx | 40 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 7e0453e..57f34e3 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "dev": "bun --hot src/server.tsx", - "build": "rm -rf dist && bun build --target=bun ./src/server.tsx --outdir ./dist", + "build": "rm -rf dist && bun build --minify --target=browser ./src/index.html --outdir ./dist/public && bun build --target=bun ./src/server.tsx --outdir ./dist", "start": "cd dist && DATABASE_PATH=../data/excalidraw.sqlite bun ./server.js", "test": "bun test", "typecheck": "tsc --noEmit" diff --git a/src/server.tsx b/src/server.tsx index 8ed35f5..17456c0 100644 --- a/src/server.tsx +++ b/src/server.tsx @@ -14,7 +14,16 @@ export function createServer() { const drawing = drawingStore.ensureInitialDrawing(); return Response.redirect(new URL(`/d/${drawing.id}`, request.url), 302); }, - "/d/:id": index, + ...(process.cwd().endsWith("/dist") ? { + "/d/:id": async (req: Request) => { + let file = Bun.file("./public/index.html"); + return new Response(file, { + headers: { "Content-Type": "text/html; charset=utf-8" } + }); + } + } : { + "/d/:id": index + }), "/api/health": { GET: () => api.health(), }, @@ -28,7 +37,34 @@ export function createServer() { DELETE: (request: Request & { params: Record }) => api.deleteDrawing(request), }, }, - fetch() { + async fetch(request: Request) { + const url = new URL(request.url); + + const isProd = process.cwd().endsWith("/dist"); + + if (!isProd) { + return Response.json({ ok: false, error: "Not found" }, { status: 404 }); + } + + // Remove any "/public" prefix from the request URL if it accidentally got included + let reqPath = url.pathname; + if (reqPath.startsWith("/public/")) { + reqPath = reqPath.substring(7); + } + + // In dist, static files are inside public + const path = "./public" + reqPath; + const file = Bun.file(path); + if (await file.exists()) { + // Cache indefinitely except index.html + const headers: Record = {}; + if (!reqPath.endsWith("index.html")) { + headers["Cache-Control"] = "public, max-age=31536000, immutable"; + } else { + headers["Content-Type"] = "text/html; charset=utf-8"; + } + return new Response(file, { headers }); + } return Response.json({ ok: false, error: "Not found" }, { status: 404 }); }, } satisfies Parameters[0];