Skip to content

Commit 226e250

Browse files
fix: middleware regressions (#3106)
Fixes some regressions found after merging #3086 - Missing 404 middlewares when mounting sub-apps - revert more staticFiles logic to always require the middleware like before
1 parent c279efa commit 226e250

File tree

7 files changed

+69
-71
lines changed

7 files changed

+69
-71
lines changed

src/app.ts

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import {
3030
segmentToMiddlewares,
3131
} from "./segments.ts";
3232
import { isHandlerByMethod, type PageResponse } from "./handlers.ts";
33-
import { staticFiles } from "./middlewares/static_files.ts";
3433

3534
// TODO: Completed type clashes in older Deno versions
3635
// deno-lint-ignore no-explicit-any
@@ -39,9 +38,6 @@ export const DEFAULT_CONN_INFO: any = {
3938
remoteAddr: { transport: "tcp", hostname: "localhost", port: 1234 },
4039
};
4140

42-
/** Used to group mounted apps. Only internal */
43-
let INTERNAL_ID = 0;
44-
4541
const DEFAULT_RENDER = <State>(): Promise<PageResponse<State>> =>
4642
// deno-lint-ignore no-explicit-any
4743
Promise.resolve({ data: {} as any });
@@ -182,7 +178,6 @@ export class App<State> {
182178
method: Method | "ALL";
183179
pattern: string;
184180
fns: MiddlewareFn<State>[];
185-
unshift: boolean;
186181
}[] = [];
187182

188183
static {
@@ -343,38 +338,38 @@ export class App<State> {
343338
method: Method | "ALL",
344339
path: string,
345340
fns: MiddlewareFn<State>[],
346-
unshift = false,
347341
) {
348342
const segment = getOrCreateSegment<State>(this.#root, path, false);
349343
const result = segmentToMiddlewares(segment);
350344

351345
result.push(...fns);
352346

353347
const resPath = mergePath(this.config.basePath, path);
354-
this.#addRoute(method, resPath, result, unshift);
348+
this.#addRoute(method, resPath, result);
355349
}
356350

357351
#addRoute(
358352
method: Method | "ALL",
359353
path: string,
360354
fns: MiddlewareFn<State>[],
361-
unshift = false,
362355
) {
363-
this.#routeDefs.push({ method, pattern: path, fns, unshift });
356+
this.#routeDefs.push({ method, pattern: path, fns });
364357
}
365358

366359
mountApp(path: string, app: App<State>): this {
367-
const segmentPath = mergePath(path, `/__${INTERNAL_ID++}/`);
360+
const segmentPath = mergePath("", path);
368361
const segment = getOrCreateSegment(this.#root, segmentPath, true);
369362
const fns = segmentToMiddlewares(segment);
370363

364+
segment.middlewares.push(...app.#root.middlewares);
365+
371366
const routes = app.#routeDefs;
372367
for (let i = 0; i < routes.length; i++) {
373368
const route = routes[i];
374369

375370
const merged = mergePath(path, route.pattern);
376371
const mergedFns = [...fns, ...route.fns];
377-
this.#addRoute(route.method, merged, mergedFns, route.unshift);
372+
this.#addRoute(route.method, merged, mergedFns);
378373
}
379374

380375
app.#islandRegistry.forEach((value, key) => {
@@ -408,20 +403,22 @@ export class App<State> {
408403
return missingBuildHandler;
409404
}
410405

411-
// Fallthrough
412-
this.#addMiddleware(
413-
"ALL",
414-
"*",
415-
[...this.#root.middlewares, staticFiles()],
416-
true,
417-
);
418-
419406
for (let i = 0; i < this.#routeDefs.length; i++) {
420407
const route = this.#routeDefs[i];
421-
this.#router.add(route.method, route.pattern, route.fns, route.unshift);
408+
if (route.method === "ALL") {
409+
this.#router.add("GET", route.pattern, route.fns);
410+
this.#router.add("DELETE", route.pattern, route.fns);
411+
this.#router.add("HEAD", route.pattern, route.fns);
412+
this.#router.add("OPTIONS", route.pattern, route.fns);
413+
this.#router.add("PATCH", route.pattern, route.fns);
414+
this.#router.add("POST", route.pattern, route.fns);
415+
this.#router.add("PUT", route.pattern, route.fns);
416+
} else {
417+
this.#router.add(route.method, route.pattern, route.fns);
418+
}
422419
}
423420

424-
const rootMiddlewares = this.#root.middlewares;
421+
const rootMiddlewares = segmentToMiddlewares(this.#root);
425422

426423
return async (
427424
req: Request,

src/app_test.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,29 @@ Deno.test(
412412
},
413413
);
414414

415+
Deno.test("App - .mountApp() fallback route", async () => {
416+
let called = "";
417+
const innerApp = new App<{ text: string }>()
418+
.use(function Inner(ctx) {
419+
called += "_Inner";
420+
return ctx.next();
421+
})
422+
.get("/", (ctx) => new Response(ctx.state.text));
423+
424+
const app = new App<{ text: string }>()
425+
.use(function Outer(ctx) {
426+
called += "Outer";
427+
return ctx.next();
428+
})
429+
.mountApp("/", innerApp);
430+
431+
const server = new FakeServer(app.handler());
432+
433+
const res = await server.get("/invalid");
434+
await res.body?.cancel();
435+
expect(called).toEqual("Outer_Inner");
436+
});
437+
415438
Deno.test("App - catches errors", async () => {
416439
let thrownErr: unknown | null = null;
417440
const app = new App<{ text: string }>()

src/router.ts

Lines changed: 18 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,18 @@ export interface DynamicRouteDef<T> {
2121
byMethod: RouteByMethod<T>;
2222
}
2323

24+
function newByMethod<T>(): RouteByMethod<T> {
25+
return {
26+
GET: [],
27+
POST: [],
28+
PATCH: [],
29+
DELETE: [],
30+
PUT: [],
31+
HEAD: [],
32+
OPTIONS: [],
33+
};
34+
}
35+
2436
export interface RouteResult<T> {
2537
params: Record<string, string>;
2638
handlers: T[];
@@ -33,7 +45,6 @@ export interface Router<T> {
3345
method: Method | "OPTIONS" | "ALL",
3446
pathname: string,
3547
handlers: T[],
36-
unshift?: boolean,
3748
): void;
3849
match(method: Method, url: URL, init?: T[]): RouteResult<T>;
3950
getAllowedMethods(pattern: string): string[];
@@ -56,43 +67,25 @@ export class UrlPatternRouter<T> implements Router<T> {
5667
}
5768

5869
add(
59-
method: Method | "ALL",
70+
method: Method,
6071
pathname: string,
6172
handlers: T[],
62-
unshift = false,
6373
) {
6474
let allowed = this.#allowed.get(pathname);
6575
if (allowed === undefined) {
6676
allowed = new Set();
6777
this.#allowed.set(pathname, allowed);
6878
}
69-
if (method === "ALL") {
70-
allowed.add("GET");
71-
allowed.add("POST");
72-
allowed.add("PATCH");
73-
allowed.add("PUT");
74-
allowed.add("DELETE");
75-
allowed.add("HEAD");
76-
allowed.add("OPTIONS");
77-
} else {
78-
allowed.add(method);
79-
}
79+
80+
allowed.add(method);
8081

8182
let byMethod: RouteByMethod<T>;
8283
if (IS_PATTERN.test(pathname)) {
8384
let def = this.#dynamics.get(pathname);
8485
if (def === undefined) {
8586
def = {
8687
pattern: new URLPattern({ pathname }),
87-
byMethod: {
88-
DELETE: [],
89-
GET: [],
90-
HEAD: [],
91-
OPTIONS: [],
92-
PATCH: [],
93-
POST: [],
94-
PUT: [],
95-
},
88+
byMethod: newByMethod(),
9689
};
9790
this.#dynamics.set(pathname, def);
9891
this.#dynamicArr.push(def);
@@ -104,34 +97,15 @@ export class UrlPatternRouter<T> implements Router<T> {
10497
if (def === undefined) {
10598
def = {
10699
pattern: pathname,
107-
byMethod: {
108-
DELETE: [],
109-
GET: [],
110-
HEAD: [],
111-
OPTIONS: [],
112-
PATCH: [],
113-
POST: [],
114-
PUT: [],
115-
},
100+
byMethod: newByMethod(),
116101
};
117102
this.#statics.set(pathname, def);
118103
}
119104

120105
byMethod = def.byMethod;
121106
}
122107

123-
const fn = unshift ? "unshift" : "push";
124-
if (method === "ALL") {
125-
byMethod.DELETE[fn](...handlers);
126-
byMethod.GET[fn](...handlers);
127-
byMethod.HEAD[fn](...handlers);
128-
byMethod.OPTIONS[fn](...handlers);
129-
byMethod.PATCH[fn](...handlers);
130-
byMethod.POST[fn](...handlers);
131-
byMethod.PUT[fn](...handlers);
132-
} else {
133-
byMethod[method][fn](...handlers);
134-
}
108+
byMethod[method].push(...handlers);
135109
}
136110

137111
match(method: Method, url: URL, init: T[] = []): RouteResult<T> {

tests/active_links_test.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { App } from "fresh";
1+
import { App, staticFiles } from "fresh";
22
import {
33
allIslandApp,
44
assertNotSelector,
@@ -27,7 +27,8 @@ function testApp<T>(): App<T> {
2727
const app = new App<T>()
2828
.island(selfCounter, "SelfCounter", SelfCounter)
2929
.island(partialInIsland, "PartialInIsland", PartialInIsland)
30-
.island(jsonIsland, "JsonIsland", JsonIsland);
30+
.island(jsonIsland, "JsonIsland", JsonIsland)
31+
.use(staticFiles());
3132
setBuildCache(app, getBuildCache(allIslandApp));
3233
return app;
3334
}

tests/islands_test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { App, fsRoutes } from "fresh";
1+
import { App, fsRoutes, staticFiles } from "fresh";
22
import { Counter } from "./fixtures_islands/Counter.tsx";
33
import { IslandInIsland } from "./fixtures_islands/IslandInIsland.tsx";
44
import { JsonIsland } from "./fixtures_islands/JsonIsland.tsx";
@@ -37,6 +37,7 @@ await buildProd(allIslandApp);
3737
function testApp(config?: FreshConfig) {
3838
const app = new App(config);
3939
setBuildCache(app, getBuildCache(allIslandApp));
40+
app.use(staticFiles());
4041
return app;
4142
}
4243

tests/partials_test.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { App } from "fresh";
1+
import { App, staticFiles } from "fresh";
22
import { Partial } from "fresh/runtime";
33
import {
44
allIslandApp,
@@ -40,7 +40,8 @@ function testApp<T>(): App<T> {
4040
.island(selfCounter, "SelfCounter", SelfCounter)
4141
.island(partialInIsland, "PartialInIsland", PartialInIsland)
4242
.island(jsonIsland, "JsonIsland", JsonIsland)
43-
.island(optOutPartialLink, "OptOutPartialLink", OptOutPartialLink);
43+
.island(optOutPartialLink, "OptOutPartialLink", OptOutPartialLink)
44+
.use(staticFiles());
4445

4546
setBuildCache(app, getBuildCache(allIslandApp));
4647
return app;

www/main.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { App, fsRoutes, trailingSlashes } from "fresh";
1+
import { App, fsRoutes, staticFiles, trailingSlashes } from "fresh";
22

33
export const app = new App({ root: import.meta.url })
4+
.use(staticFiles())
45
.use(trailingSlashes("never"));
56

67
await fsRoutes(app, {

0 commit comments

Comments
 (0)