Skip to content

Commit b852fe7

Browse files
authored
fix: log warning on invalid route configurations (#2954)
Related to #2953. I found it confusing that there were unsupported route configurations, that fails silently without any errors or wanings. Though I think it is a good idea to not throw for this, as that would be worse DX. But logging a warning if the developer is migrating from Fresh v1, or if the developer is using code that will not work (e.g. copy pasting a handler from a route to `_middleware`), I think it's helpful where reasonable to help guide a bit more. Does a small refactor first to hopefully improve typing to be more correct for `InternalRoute.handlers`, then adds warnings for the following: - `_middleware` using Object form (`GET`, `POST` handlers) of the handler format - Helps find issues when moving handler from route to middleware - `_layout` using `handlers` - E.g. if developer moves a route to a layout - ~~`_404`, `_500` or `_error` using Object handler form (`GET`, `POST` handlers), which is not yet supported (See #2953)~~ - No longer necessary as it is fixed by #2955
1 parent 20162f6 commit b852fe7

File tree

3 files changed

+68
-9
lines changed

3 files changed

+68
-9
lines changed

src/handlers.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,10 @@ export type RouteHandler<Data, State> =
9898
| HandlerByMethod<Data, State>;
9999

100100
export function isHandlerByMethod<D, S>(
101-
handler: RouteHandler<D, S>,
101+
handler: RouteHandler<D, S> | RouteHandler<D, S>[] | null,
102102
): handler is HandlerByMethod<D, S> {
103-
return handler !== null && typeof handler === "object";
103+
return handler !== null && !Array.isArray(handler) &&
104+
typeof handler === "object";
104105
}
105106

106107
/**

src/plugins/fs_routes/mod.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ interface InternalRoute<State> {
2424
base: string;
2525
filePath: string;
2626
config: RouteConfig | null;
27-
handlers: RouteHandler<unknown, State> | null;
27+
handlers:
28+
| RouteHandler<unknown, State>[]
29+
| RouteHandler<unknown, State>
30+
| null;
2831
component: AnyComponent<PageProps<unknown, State>> | null;
2932
}
3033

@@ -212,10 +215,14 @@ export async function fsRoutes<State>(
212215
for (let k = 0; k < stack.length; k++) {
213216
const mod = stack[k];
214217
if (mod.path.endsWith("/_middleware")) {
215-
if (mod.handlers !== null && !isHandlerByMethod(mod.handlers)) {
218+
if (Array.isArray(mod.handlers)) {
219+
middlewares.push(...(mod.handlers as MiddlewareFn<State>[]));
220+
} else if (typeof mod.handlers === "function") {
216221
middlewares.push(mod.handlers as MiddlewareFn<State>);
217-
} else if (Array.isArray(mod.handlers)) {
218-
middlewares.push(...mod.handlers);
222+
} else if (isHandlerByMethod(mod.handlers)) {
223+
warnInvalidRoute(
224+
"Middleware does not support object handlers with GET, POST, etc.",
225+
);
219226
}
220227
}
221228

@@ -233,6 +240,8 @@ export async function fsRoutes<State>(
233240
// _layouts
234241
if (skipLayouts && mod.path.endsWith("/_layout")) {
235242
continue;
243+
} else if (mod.handlers !== null && mod.path.endsWith("/_layout")) {
244+
warnInvalidRoute("Layout does not support handlers");
236245
} else if (!skipLayouts && mod.config?.skipInheritedLayouts) {
237246
const first = components.length > 0 ? components[0] : null;
238247
components = [];
@@ -296,8 +305,7 @@ export async function fsRoutes<State>(
296305

297306
if (routeMod.component !== null) {
298307
components.push(routeMod.component);
299-
const missingGetHandler = handlers !== null &&
300-
isHandlerByMethod(handlers) &&
308+
const missingGetHandler = isHandlerByMethod(handlers) &&
301309
!Object.keys(handlers).includes("GET");
302310
if (missingGetHandler) {
303311
const combined = middlewares.concat(
@@ -366,6 +374,14 @@ function notFoundMiddleware<State>(
366374
};
367375
}
368376

377+
function warnInvalidRoute(message: string) {
378+
// deno-lint-ignore no-console
379+
console.warn(
380+
`🍋 %c[WARNING] Unsupported route config: ${message}`,
381+
"color:rgb(251, 184, 0)",
382+
);
383+
}
384+
369385
async function walkDir(
370386
dir: string,
371387
callback: (entry: WalkEntry) => void,

src/plugins/fs_routes/mod_test.tsx

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import {
88
} from "./mod.ts";
99
import { delay, FakeServer } from "../../test_utils.ts";
1010
import { createFakeFs } from "../../test_utils.ts";
11-
import { expect } from "@std/expect";
11+
import { expect, fn } from "@std/expect";
12+
import { stub } from "@std/testing/mock";
1213
import { type HandlerByMethod, type HandlerFn, page } from "../../handlers.ts";
1314
import type { Method } from "../../router.ts";
1415
import { parseHtml } from "../../../tests/test_utils.tsx";
@@ -1409,3 +1410,44 @@ Deno.test("support bigint keys", async () => {
14091410
expect(text).toContain("ok");
14101411
expect(text).toContain("key:9007199254740991");
14111412
});
1413+
1414+
Deno.test("fsRoutes - warn on _middleware with object handler", async () => {
1415+
// deno-lint-ignore no-explicit-any
1416+
using warnSpy = stub(console, "warn", fn(() => {}) as any);
1417+
const server = await createServer({
1418+
"routes/_middleware.ts": { handler: { GET: () => new Response("ok") } },
1419+
"routes/index.ts": { handler: () => new Response("ok") },
1420+
});
1421+
1422+
await server.get("/");
1423+
1424+
expect(warnSpy.fake).toHaveBeenCalledTimes(1);
1425+
expect(warnSpy.fake).toHaveBeenLastCalledWith(
1426+
"🍋 %c[WARNING] Unsupported route config: Middleware does not support object handlers with GET, POST, etc.",
1427+
expect.any(String),
1428+
);
1429+
});
1430+
1431+
Deno.test("fsRoutes - warn on _layout handler", async () => {
1432+
// deno-lint-ignore no-explicit-any
1433+
using warnSpy = stub(console, "warn", fn(() => {}) as any);
1434+
const server = await createServer({
1435+
"routes/_layout.ts": {
1436+
handler: () => new Response("ok"),
1437+
default: (ctx) => (
1438+
<div>
1439+
<ctx.Component />
1440+
</div>
1441+
),
1442+
},
1443+
"routes/index.ts": { default: () => <>ok</> },
1444+
});
1445+
1446+
await server.get("/");
1447+
1448+
expect(warnSpy.fake).toHaveBeenCalledTimes(1);
1449+
expect(warnSpy.fake).toHaveBeenLastCalledWith(
1450+
"🍋 %c[WARNING] Unsupported route config: Layout does not support handlers",
1451+
expect.any(String),
1452+
);
1453+
});

0 commit comments

Comments
 (0)