Skip to content

Commit

Permalink
fix: error handling in fs routes
Browse files Browse the repository at this point in the history
  • Loading branch information
marvinhagemeister committed May 27, 2024
1 parent d06dad5 commit b01803f
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 9 deletions.
16 changes: 16 additions & 0 deletions src/app_test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -470,3 +470,19 @@ Deno.test("FreshApp - sets error on context", async () => {
expect(thrown[0][0]).toEqual(thrown[0][1]);
expect(thrown[1][0]).toEqual(thrown[1][1]);
});

Deno.test("FreshApp - pass stuff in ctx.render()", async () => {
const app = new App<{ text: string }>()
.get("/", (ctx) => {
return ctx.render(<div>ok</div>, {
status: 416,
headers: { "X-Foo": "foo" },
});
});

const server = new FakeServer(await app.handler());
const res = await server.get("/");
await res.body?.cancel();
expect(res.status).toEqual(416);
expect(res.headers.get("X-Foo")).toEqual("foo");
});
6 changes: 5 additions & 1 deletion src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,11 @@ export class FreshReqContext<State> implements FreshContext<unknown, State> {
: new Headers();

headers.set("Content-Type", "text/html; charset=utf-8");
const responseInit: ResponseInit = { status: init.status ?? 200, headers };
const responseInit: ResponseInit = {
status: init.status ?? 200,
headers,
statusText: init.statusText,
};

let partialId = "";
if (this.url.searchParams.has("fresh-partial")) {
Expand Down
15 changes: 11 additions & 4 deletions src/plugins/fs_routes/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ export async function fsRoutes<State>(
const stack: InternalRoute<State>[] = [];
let hasApp = false;

const specialPaths = new Set<string>();

for (let i = 0; i < routeModules.length; i++) {
const routeMod = routeModules[i];
const normalized = routeMod.path;
Expand Down Expand Up @@ -251,10 +253,15 @@ export async function fsRoutes<State>(
}
let parent = mod.path.slice(0, -"_error".length);
parent = parent === "/" ? "*" : parent + "*";
app.all(
parent,
errorMiddleware(errorComponents, handler),
);

// Add error route as it's own route
if (!specialPaths.has(mod.path)) {
specialPaths.add(mod.path);
app.all(
parent,
errorMiddleware(errorComponents, handler),
);
}
middlewares.push(errorMiddleware(errorComponents, handler));
continue;
}
Expand Down
68 changes: 68 additions & 0 deletions src/plugins/fs_routes/mod_test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,74 @@ Deno.test("fsRoutes - async route components returning response", async () => {
expect(text).toEqual("index");
});

Deno.test(
"fsRoutes - returns response code from error route",
async () => {
const server = await createServer<{ text: string }>({
"routes/_app.tsx": {
default: (ctx) => {
return (
<div>
_app/<ctx.Component />
</div>
);
},
},
"routes/_error.tsx": {
default: () => <div>fail</div>,
},
"routes/index.tsx": {
default: () => <div>index</div>,
},
"routes/bar.tsx": {
default: () => <div>index</div>,
},
"routes/foo/index.tsx": {
default: () => <div>foo/index</div>,
},
"routes/foo/_error.tsx": {
default: () => {
throw new Error("fail");
},
},
"routes/foo/bar.tsx": {
default: () => <div>foo/index</div>,
},
});

let res = await server.get("/fooa");
await res.body?.cancel();
expect(res.status).toEqual(404);

res = await server.get("/foo/asdf");
await res.body?.cancel();
expect(res.status).toEqual(500);
},
);

Deno.test(
"fsRoutes - set headers from handler",
async () => {
const server = await createServer<{ text: string }>({
"routes/index.tsx": {
handler: (ctx) => {
return ctx.render(<h1>hello</h1>, {
headers: { "X-Foo": "123" },
status: 418,
statusText: "I'm a fresh teapot",
});
},
},
});

const res = await server.get("/");
await res.body?.cancel();
expect(res.status).toEqual(418);
expect(res.statusText).toEqual("I'm a fresh teapot");
expect(res.headers.get("X-Foo")).toEqual("123");
},
);

Deno.test("fsRoutes - sortRoutePaths", () => {
let routes = [
"/foo/[id]",
Expand Down
21 changes: 20 additions & 1 deletion src/plugins/fs_routes/render_middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { type AnyComponent, h, type RenderableProps, type VNode } from "preact";
import type { MiddlewareFn } from "../../middlewares/mod.ts";
import type { HandlerFn, PageResponse } from "../../handlers.ts";
import type { PageProps } from "../../runtime/server/mod.tsx";
import { HttpError } from "../../error.ts";

export type AsyncAnyComponent<P> = {
(
Expand All @@ -20,6 +21,7 @@ export function renderMiddleware<State>(
| AsyncAnyComponent<PageProps<unknown, State>>
>,
handler: HandlerFn<unknown, State> | undefined,
init?: ResponseInit | undefined,
): MiddlewareFn<State> {
return async (ctx) => {
let result: PageResponse<unknown> | undefined;
Expand Down Expand Up @@ -69,6 +71,23 @@ export function renderMiddleware<State>(
}
}

return ctx.render(vnode!);
let status: number | undefined = init?.status;
if (
ctx.error !== null && ctx.error !== undefined
) {
if (
ctx.error instanceof HttpError
) {
status = ctx.error.status;
} else {
status = 500;
}
}

return ctx.render(vnode!, {
status,
statusText: init?.statusText,
headers: init?.headers,
});
};
}
4 changes: 1 addition & 3 deletions src/runtime/server/preact_hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,10 @@ import {
import type { BuildCache } from "../../build_cache.ts";
import { BUILD_ID } from "../build_id.ts";
import { DEV_ERROR_OVERLAY_URL } from "../../constants.ts";
import {
getCodeFrame,
} from "../../dev/middlewares/error_overlay/code_frame.tsx";
import * as colors from "@std/fmt/colors";
import { escape as escapeHtml } from "@std/html";
import { HttpError } from "../../error.ts";
import { getCodeFrame } from "../../dev/middlewares/error_overlay/code_frame.tsx";

const enum OptionsType {
ATTR = "attr",
Expand Down

0 comments on commit b01803f

Please sign in to comment.