Repository: denoland/fresh (TypeScript)

Issue:
bug: middleware `next()` does not invoke handlers
middleware declared in `_middleware` files do not work properly when `ctx.next()` is called.

```sh
/
├── /main.ts
├── /routes
│   ├── index.tsx
│   └── middleware.tsx
├── /utils.ts
└── ...
```

Steps to reproduce:
1. create new fresh project with `deno run -Ar jsr:@fresh/init@2.0.0-alpha.20`
2. add `/routes/_middleware.tsx` file
3. add simple middleware code
```ts
// /routes/_middleware.ts
import { define } from "../utils.ts";

export default define.middleware((ctx) => {
  console.log(`${ctx.req.method} ${ctx.req.url}`);
  return ctx.next();
});
```

This should  invoke the `routes/index.tsx` handler according to the [doc](https://fresh.deno.dev/docs/canary/concepts/middleware) but always returns a `not found` response.

The same middleware declared in `main.ts` works as expected.



Discussion:

Patch:
diff --git a/src/plugins/fs_routes/mod.ts b/src/plugins/fs_routes/mod.ts
index b7db2cca839..c44e3d8bbc0 100644
--- a/src/plugins/fs_routes/mod.ts
+++ b/src/plugins/fs_routes/mod.ts
@@ -142,13 +142,15 @@ export async function fsRoutes<State>(
         routePath.slice(0, routePath.lastIndexOf("."))
       }`;
       const base = normalizedPath.slice(0, normalizedPath.lastIndexOf("/"));
+      const isMiddleware = normalizedPath.endsWith("/_middleware");
       return {
         path: normalizedPath,
         filePath: routePath,
         base,
-        handlers: mod.handlers ?? mod.handler ?? null,
+        handlers: mod.handlers ?? mod.handler ??
+          (isMiddleware ? mod.default ?? null : null),
         config: mod.config ?? null,
-        component: mod.default ?? null,
+        component: isMiddleware ? null : mod.default ?? null,
       } as InternalRoute<State>;
     }),
   );


Test patch:
diff --git a/src/plugins/fs_routes/mod_test.tsx b/src/plugins/fs_routes/mod_test.tsx
index 303f777b5bc..41db660b96a 100644
--- a/src/plugins/fs_routes/mod_test.tsx
+++ b/src/plugins/fs_routes/mod_test.tsx
@@ -12,6 +12,7 @@ import { expect } from "@std/expect";
 import { type HandlerByMethod, type HandlerFn, page } from "../../handlers.ts";
 import type { Method } from "../../router.ts";
 import { parseHtml } from "../../../tests/test_utils.tsx";
+import type { FreshContext } from "fresh";
 
 async function createServer<T>(
   files: Record<string, string | Uint8Array | FreshFsItem<T>>,
@@ -171,6 +172,23 @@ Deno.test("fsRoutes - add middleware for function handler", async () => {
   expect(doc.body.firstChild?.textContent).toEqual("ok");
 });
 
+Deno.test("fsRoutes - middleware", async () => {
+  const server = await createServer<{ text: string }>({
+    "routes/index.ts": { handler: (ctx) => new Response(ctx.state.text) },
+    "routes/_middleware.ts": {
+      default: ((ctx: FreshContext<{ text: string }>) => {
+        ctx.state.text = "ok";
+        return ctx.next();
+        // deno-lint-ignore no-explicit-any
+      }) as any,
+    },
+  });
+
+  const res = await server.get("/");
+  expect(res.status).toEqual(200);
+  expect(await res.text()).toEqual("ok");
+});
+
 Deno.test("fsRoutes - nested middlewares", async () => {
   const server = await createServer<{ text: string }>({
     "routes/_middleware.ts": {