(
}
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;
}
diff --git a/src/plugins/fs_routes/mod_test.tsx b/src/plugins/fs_routes/mod_test.tsx
index 262e377ba54..693b2aa6361 100644
--- a/src/plugins/fs_routes/mod_test.tsx
+++ b/src/plugins/fs_routes/mod_test.tsx
@@ -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 (
+
+ _app/
+
+ );
+ },
+ },
+ "routes/_error.tsx": {
+ default: () => fail
,
+ },
+ "routes/index.tsx": {
+ default: () => index
,
+ },
+ "routes/bar.tsx": {
+ default: () => index
,
+ },
+ "routes/foo/index.tsx": {
+ default: () => foo/index
,
+ },
+ "routes/foo/_error.tsx": {
+ default: () => {
+ throw new Error("fail");
+ },
+ },
+ "routes/foo/bar.tsx": {
+ default: () => foo/index
,
+ },
+ });
+
+ 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(hello
, {
+ 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]",
diff --git a/src/plugins/fs_routes/render_middleware.ts b/src/plugins/fs_routes/render_middleware.ts
index d8fd7a1f945..1f5753895d8 100644
--- a/src/plugins/fs_routes/render_middleware.ts
+++ b/src/plugins/fs_routes/render_middleware.ts
@@ -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 { FreshReqContext, PageProps } from "../../context.ts";
+import { HttpError } from "../../error.ts";
export type AsyncAnyComponent = {
(
@@ -20,6 +21,7 @@ export function renderMiddleware(
| AsyncAnyComponent>
>,
handler: HandlerFn | undefined,
+ init?: ResponseInit | undefined,
): MiddlewareFn {
return async (ctx) => {
let result: PageResponse | undefined;
@@ -63,6 +65,23 @@ export function renderMiddleware(
}
}
- 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,
+ });
};
}
diff --git a/src/runtime/server/preact_hooks.tsx b/src/runtime/server/preact_hooks.tsx
index 9f1ad994741..e7eaab888ee 100644
--- a/src/runtime/server/preact_hooks.tsx
+++ b/src/runtime/server/preact_hooks.tsx
@@ -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",