Skip to content

Conversation

@dmgnr
Copy link
Contributor

@dmgnr dmgnr commented Jan 3, 2026

Summary by CodeRabbit

  • Performance Improvements

    • Route matching now prioritises more specific routes, filters out invalid entries earlier, and stops on the first match for faster navigation and responsiveness.
  • Bug Fixes / Refactor

    • Simplified matching control flow to avoid ambiguous results, improving reliability and predictability of route resolution.

✏️ Tip: You can customize this high-level summary in your review settings.

@changeset-bot
Copy link

changeset-bot bot commented Jan 3, 2026

🦋 Changeset detected

Latest commit: f264a12

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link

coderabbitai bot commented Jan 3, 2026

Walkthrough

Pre-filters and pre-sorts route candidates in src/server/helpers/match.ts using a new scoreRoute heuristic that ranks static > params > catch-all (with root special-cased), switches matching to early-return on first match, and adds a changeset patch noting the route-matching priority fix.

Changes

Cohort / File(s) Summary
Route matching optimisation
src/server/helpers/match.ts
Filters out falsy route entries; adds scoreRoute(routePath: string): number to rank routes (static segments score higher, params and catch-alls lower, root handled specially); sorts candidates by score and changes loop to early-return on first successful match (removes previous matchedRoute accumulation).
Changeset
.changeset/khaki-coins-travel.md
Adds a patch changeset documenting a fix for incorrect route-matching priority.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hopped through paths with careful score,

Counted still steps, then bounded for more,
First clear match and I slowed my race,
Sorted the trails and left a neat trace,
A crunchy carrot for tidy place 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly describes the main change: fixing incorrect priority in route matching. The implementation confirms this is the primary focus, with the new scoreRoute heuristic addressing route prioritisation.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9999a47 and f264a12.

📒 Files selected for processing (1)
  • src/server/helpers/match.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/server/helpers/match.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added the server Modifications or updates to server-handling code label Jan 3, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/server/helpers/match.ts (2)

60-62: Consider pre-computing scores to improve performance.

The scoreRoute function is invoked multiple times for each route during the sort operation, causing redundant parsing of route paths. For better performance, especially with many routes, consider computing scores once before sorting.

🔎 Proposed optimisation
-  const sortedRoutes = Object.entries(appPathRoutes)
-    .filter(([routePath, file]) => routePath && file)
-    .sort(([a], [b]) => scoreRoute(b) - scoreRoute(a));
+  const sortedRoutes = Object.entries(appPathRoutes)
+    .filter(([routePath, file]) => routePath && file)
+    .map(([routePath, file]) => ({ routePath, file, score: scoreRoute(routePath) }))
+    .sort((a, b) => b.score - a.score)
+    .map(({ routePath, file }) => [routePath, file] as const);

64-70: Early exit pattern improves clarity and efficiency.

The refactoring to return immediately upon finding a match is cleaner and more efficient than accumulating a result. Good change.

Note: The function can return undefined when no route matches. Consider adding an explicit return type annotation (e.g., { filename: string; params: Record<string, string | string[]> } | undefined) for better type safety.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee4530e and 60b9d0c.

📒 Files selected for processing (1)
  • src/server/helpers/match.ts

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/server/helpers/match.ts (1)

73-89: Critical: Catch-all segments have the same priority as dynamic segments.

While the root route fix (Infinity) and optional catch-all distinction are now correct, a critical bug remains: catch-all segments [...param] (line 83) receive the same score (+2) as dynamic segments [param] (line 84). This means routes like /user/[id] and /blog/[...slug] have equal priority, even though the dynamic segment should be more specific and match first.

Expected priority (high to low):

  • Static segments about → +3 ✓
  • Dynamic segments [param] → +2 ✓
  • Catch-all [...param] → should be lower (e.g., +1)
  • Optional catch-all [[...param]] → -1 ✓
🔎 Proposed fix
 function scoreRoute(routePath: string) {
   const parts = routePath.split('/').filter(Boolean);
 
   let score = 0;
   // Root path
   if(parts.length === 0) score = Infinity;
   for (const part of parts) {
     // Optional catch-all
     if (part.startsWith('[[...')) score += -1;
     // Catch-all
-    else if (part.startsWith('[...')) score += 2;
+    else if (part.startsWith('[...')) score += 1;
     else if (part.startsWith('[')) score += 2;
     else score += 3;
   }
 
   return score + parts.length;
 }
🧹 Nitpick comments (1)
src/server/helpers/match.ts (1)

60-62: Verify that file is non-empty before destructuring.

The filter checks that file is truthy, but since file is an array, an empty array would pass the filter yet cause filePath to be undefined during destructuring at line 64. Consider strengthening the filter to verify the array has at least one element.

🔎 Proposed fix
  const sortedRoutes = Object.entries(appPathRoutes)
-    .filter(([routePath, file]) => routePath && file)
+    .filter(([routePath, file]) => routePath && file && file.length > 0)
     .sort(([a], [b]) => scoreRoute(b) - scoreRoute(a));
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7167cb4 and a2b4049.

📒 Files selected for processing (1)
  • src/server/helpers/match.ts
🔇 Additional comments (1)
src/server/helpers/match.ts (1)

64-70: Good refactor: early return simplifies control flow.

The early return pattern is cleaner than the previous matchedRoute accumulation approach. Note that the function implicitly returns undefined when no route matches, so ensure downstream code handles this case appropriately.

@dmgnr
Copy link
Contributor Author

dmgnr commented Jan 3, 2026

That should be clear to merge.
Fixes: #162

@k0d13
Copy link
Owner

k0d13 commented Jan 4, 2026

Build is failing

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/server/helpers/match.ts (1)

47-73: Add explicit return type annotation to findMatchingRoute.

With strict TypeScript enabled, the function should have an explicit return type. Add ReturnType annotation to clarify what this function returns:

export function findMatchingRoute(
  nextServer: NextNodeServer,
  requestPathname: string,
): { filename: string; params: Record<string, string | string[]> } | undefined

This improves type safety and helps the TypeScript compiler with type inference for callers.

🧹 Nitpick comments (2)
src/server/helpers/match.ts (2)

64-72: LGTM: Early return on first match is a good pattern.

The early return improves readability and ensures the highest-priority match is returned immediately.

Minor observation: the guard on line 65 (if (!filePath) continue) is redundant given the filter on line 61 already ensures non-empty arrays. Consider removing it for cleaner code.


75-91: Previous review concerns have been addressed.

The scoring logic now correctly handles:

  • Root path / receives Infinity score (highest priority)
  • Optional catch-all [[...param]] receives -1 (lower than catch-all's 0)

This establishes the correct priority order: static > dynamic > catch-all > optional catch-all.

Minor style nit: missing space after if on line 80.

🔎 Optional style fix
-  if(parts.length === 0) score = Infinity;
+  if (parts.length === 0) score = Infinity;
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a2b4049 and 9999a47.

📒 Files selected for processing (1)
  • src/server/helpers/match.ts
🔇 Additional comments (1)
src/server/helpers/match.ts (1)

60-62: LGTM: Route sorting logic is correct.

The filtering and descending sort by score ensures more specific routes are evaluated first, which is the correct approach for route-matching priority.

@k0d13
Copy link
Owner

k0d13 commented Jan 5, 2026

Linter is failing too

@dmgnr
Copy link
Contributor Author

dmgnr commented Jan 5, 2026

oh my god a single space

@k0d13 k0d13 merged commit 2f33fe6 into k0d13:main Jan 11, 2026
12 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

server Modifications or updates to server-handling code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants