Skip to content

Commit

Permalink
fix: more deterministic route matching for routes with multiple slugs
Browse files Browse the repository at this point in the history
  • Loading branch information
justinawrey committed Sep 3, 2022
1 parent b2cb72c commit 3d71ae4
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 6 deletions.
5 changes: 5 additions & 0 deletions core/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ function handleRoutes(routes: Route[]): http.Handler {
const exactRoutes = routes.filter((route) => !route.hasSlugs);
const slugRoutes = Route.sort(routes.filter((route) => route.hasSlugs));

log.debug(
"slug matching order:",
slugRoutes.map((route) => route.parsed),
);

// Make a map out of the exact routes for easier lookup
const exactRouteMap = new Map<string, Route>(
exactRoutes.map((route) => [route.parsed, route]),
Expand Down
47 changes: 41 additions & 6 deletions core/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,23 @@ export class Route {
);
}

// Sort an array of routes by length, longest to shortest
// Sort an array of routes by number of raw parts, longest to shortest
static sort(routes: Route[]): Route[] {
routes.sort((a, b) => b.length - a.length);
routes.sort((a, b) => {
const lengthFactor = b.rawParts.length - a.rawParts.length;

// If they're the same length, we could be in an inconclusive situation like:
// a: /[test]/raw
// b: /blog/[id]
// In this case, sort by longest 'raw base path'
if (lengthFactor === 0) {
return b.baseLength - a.baseLength;
}

// Otherwise just sort by sheer number of raw parts
return lengthFactor;
});

return routes;
}

Expand All @@ -87,16 +101,37 @@ export class Route {
return this.parts.length;
}

// The length of the 'base' of the route, e.g. how many raw
// parts it has before a slug part occurs.
// For example, /hi/its/[me] has baseLength 2.
get baseLength(): number {
let baseLength = 0;

for (const part of this.parts) {
if (!isSlug(part)) {
baseLength++;
} else {
break;
}
}

return baseLength;
}

// Slugs from the filename, with the '[' and ']' characters stripped away
get slugs(): string[] {
get slugParts(): string[] {
return this.parts.filter((part) => isSlug(part)).map((slug) =>
slug.slice(1, -1)
);
}

// Whether or not this route has slugs in it
get rawParts(): string[] {
return this.parts.filter((part) => !isSlug(part));
}

// Whether or not this route has slugParts in it
get hasSlugs(): boolean {
return this.slugs.length > 0;
return this.slugParts.length > 0;
}

get regEx(): RegExp {
Expand All @@ -116,7 +151,7 @@ export class Route {

const matchObj: Record<string, string> = {};
for (const [index, match] of matches.slice(1).entries()) {
matchObj[this.slugs[index]] = match;
matchObj[this.slugParts[index]] = match;
}

return matchObj;
Expand Down
7 changes: 7 additions & 0 deletions example/pages/[test]/raw.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { type FsHandler } from "../../../mod.ts";

const handler: FsHandler = (_req, query) => {
return new Response(`/${query.test}/raw`);
};

export default handler;

0 comments on commit 3d71ae4

Please sign in to comment.