fix: serve dev public assets before html shell

(cherry picked from commit 700c0df5bf82e0915b0971495cefb417150a5df8)
This commit is contained in:
Dotta committed 2026-04-15 09:12:52 -05:00
1 parent 32a9165ddf
commit 9c231d925a
4 files changed
+35 -1

No files matched your search

@@ -0,0 +1,27 @@
import { describe, expect, it } from "vitest";
import type { Request } from "express";
import { shouldServeViteDevHtml } from "../app.js";
function createRequest(path: string, acceptsResult: string | false): Request {
return {
path,
accepts: () => acceptsResult,
} as unknown as Request;
}
describe("shouldServeViteDevHtml", () => {
it("serves HTML shell for document requests", () => {
expect(shouldServeViteDevHtml(createRequest("/", "html"))).toBe(true);
expect(shouldServeViteDevHtml(createRequest("/issues/abc", "html"))).toBe(true);
});
it("skips public assets even when the client accepts */*", () => {
expect(shouldServeViteDevHtml(createRequest("/sw.js", "html"))).toBe(false);
expect(shouldServeViteDevHtml(createRequest("/site.webmanifest", "html"))).toBe(false);
});
it("skips vite asset requests", () => {
expect(shouldServeViteDevHtml(createRequest("/@vite/client", "html"))).toBe(false);
expect(shouldServeViteDevHtml(createRequest("/src/main.tsx", "html"))).toBe(false);
});
});
@@ -56,6 +56,7 @@ describe("shouldSilenceHttpSuccessLog", () => {
expect(shouldSilenceHttpSuccessLog("GET", "/@fs/Users/dotta/paperclip/ui/src/main.tsx", 200)).toBe(true);
expect(shouldSilenceHttpSuccessLog("GET", "/src/App.tsx?t=123", 200)).toBe(true);
expect(shouldSilenceHttpSuccessLog("GET", "/site.webmanifest", 200)).toBe(true);
expect(shouldSilenceHttpSuccessLog("GET", "/sw.js", 200)).toBe(true);
});
it("keeps normal successful application requests", () => {
+6 -1
View File
@@ -70,6 +70,7 @@ const VITE_DEV_STATIC_PATHS = new Set([
"/favicon.ico",
"/favicon.svg",
"/site.webmanifest",
"/sw.js",
]);
export function resolveViteHmrPort(serverPort: number): number {
@@ -79,7 +80,7 @@ export function resolveViteHmrPort(serverPort: number): number {
return Math.max(1_024, serverPort - 10_000);
}
function shouldServeViteDevHtml(req: ExpressRequest): boolean {
export function shouldServeViteDevHtml(req: ExpressRequest): boolean {
const pathname = req.path;
if (VITE_DEV_STATIC_PATHS.has(pathname)) return false;
if (VITE_DEV_ASSET_PREFIXES.some((prefix) => pathname.startsWith(prefix))) return false;
@@ -310,6 +311,7 @@ export async function createApp(
if (opts.uiMode === "vite-dev") {
const uiRoot = path.resolve(__dirname, "../../ui");
const publicUiRoot = path.resolve(uiRoot, "public");
const hmrPort = resolveViteHmrPort(opts.serverPort);
const { createServer: createViteServer } = await import("vite");
const vite = await createViteServer({
@@ -332,6 +334,9 @@ export async function createApp(
});
const renderViteHtml = viteHtmlRenderer;
if (fs.existsSync(publicUiRoot)) {
app.use(express.static(publicUiRoot, { index: false }));
}
app.get(/.*/, async (req, res, next) => {
if (!shouldServeViteDevHtml(req)) {
next();
+1
View File
@@ -25,6 +25,7 @@ const SILENCED_SUCCESS_STATIC_PREFIXES = [
const SILENCED_SUCCESS_STATIC_PATHS = new Set([
"/favicon.ico",
"/site.webmanifest",
"/sw.js",
]);
function normalizePath(url: string): string {