Skip to content

Conversation

@Sheraff
Copy link
Contributor

@Sheraff Sheraff commented Sep 11, 2025

  • move processRouteTree to a separate file because the logic is very self-contained
  • create a new function sortRoutes w/ the sorting logic, so it can be tested independently

This PR is a no-op, we're just moving code around, nothing was changed.

Summary by CodeRabbit

  • New Features

    • Added a public Route type for describing route objects in route trees.
  • Refactor

    • Consolidated route processing into a dedicated module for clearer separation and maintainability.
    • Updated public exports for route processing; existing imports may need to be adjusted.
    • No runtime behavior changes expected.
  • Tests

    • Updated tests to use the new public Route type and revised import paths.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 11, 2025

Walkthrough

The route-tree processing logic was extracted from router.ts into a new module process-route-tree.ts, with RouteLike moved to route.ts. Public exports in index.ts now re-export processRouteTree and ProcessRouteTreeResult from the new module. Tests were updated to import from the new paths and use RouteLike.

Changes

Cohort / File(s) Summary
Public exports update
packages/router-core/src/index.ts
Rewired exports: removed processRouteTree and ProcessRouteTreeResult re-exports from ./router; added direct re-exports from ./process-route-tree.
New route-tree processor
packages/router-core/src/process-route-tree.ts
Added new module exporting ProcessRouteTreeResult and processRouteTree. Implements traversal, duplicate ID checks, by-id/by-path maps, and route ranking/sorting based on parsed path segments.
RouteLike extraction
packages/router-core/src/route.ts
Introduced exported RouteLike interface defining the route shape (id, isRoot?, path?, fullPath, rank?, parentRoute?, children?, options?).
Router refactor to delegate
packages/router-core/src/router.ts
Removed in-file RouteLike, ProcessRouteTreeResult, processRouteTree, ranking helpers, and related constants. Now imports processRouteTree from ./process-route-tree and RouteLike from ./route. Cleaned unused imports.
Tests adjusted
packages/router-core/tests/processRouteTree.test.ts
Updated imports to use src/process-route-tree and RouteLike from src/route. Removed local TestRoute type. Updated helpers to construct RouteLike trees and set parentRoute links.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor App
  participant Router as RouterCore
  participant Proc as process-route-tree.ts

  App->>Router: buildRouteTree(routeTree)
  Router->>Proc: processRouteTree({ routeTree, initRoute? })
  activate Proc
  Proc->>Proc: Traverse tree (initRoute?, parent links)
  Proc->>Proc: Build routesById / routesByPath
  Proc->>Proc: Rank and sort flatRoutes
  Proc-->>Router: { routesById, routesByPath, flatRoutes }
  deactivate Proc
  Router-->>App: Build result continues with processed routes
Loading
sequenceDiagram
  autonumber
  participant Proc as process-route-tree
  participant Node as Route node
  participant Maps as {byId, byPath}
  participant Rank as sortRoutes

  Proc->>Node: visit(root)
  loop DFS over children
    Proc->>Node: initRoute?(node, index)
    Proc->>Maps: record id/path
  end
  Proc->>Rank: compute scores per route
  Rank-->>Proc: sorted flatRoutes
  Proc-->>Proc: return { routesById, routesByPath, flatRoutes }
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • schiller-manuel

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.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 succinctly and accurately summarizes the primary change—moving processRouteTree into its own file within the router-core package—and uses a clear conventional-commit style scope (refactor(router-core)), making it easy for teammates to understand the main intent when scanning history.

Poem

A hop through paths, I map and sort,
With whiskered care I rank each port.
From router’s burrow, logic flew,
To shining module, crisp and new.
IDs in clover, paths in line—
Flat routes gleam, oh how they shine!
Thump-thump: shipped on rabbit time. 🐇✨

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor-router-core-process-route-tree-own-file

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

@nx-cloud
Copy link

nx-cloud bot commented Sep 11, 2025

View your CI Pipeline Execution ↗ for commit a2a07b4

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ✅ Succeeded 1m 13s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 3s View ↗

☁️ Nx Cloud last updated this comment at 2025-09-11 16:28:27 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Sep 11, 2025

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/arktype-adapter@5128

@tanstack/directive-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/directive-functions-plugin@5128

@tanstack/eslint-plugin-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/eslint-plugin-router@5128

@tanstack/history

npm i https://pkg.pr.new/TanStack/router/@tanstack/history@5128

@tanstack/react-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router@5128

@tanstack/react-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-devtools@5128

@tanstack/react-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-ssr-query@5128

@tanstack/react-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start@5128

@tanstack/react-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-client@5128

@tanstack/react-start-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-plugin@5128

@tanstack/react-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-server@5128

@tanstack/router-cli

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-cli@5128

@tanstack/router-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-core@5128

@tanstack/router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools@5128

@tanstack/router-devtools-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools-core@5128

@tanstack/router-generator

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-generator@5128

@tanstack/router-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-plugin@5128

@tanstack/router-ssr-query-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-ssr-query-core@5128

@tanstack/router-utils

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-utils@5128

@tanstack/router-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-vite-plugin@5128

@tanstack/server-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/server-functions-plugin@5128

@tanstack/solid-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router@5128

@tanstack/solid-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-devtools@5128

@tanstack/solid-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start@5128

@tanstack/solid-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-client@5128

@tanstack/solid-start-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-plugin@5128

@tanstack/solid-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-server@5128

@tanstack/start-client-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-client-core@5128

@tanstack/start-plugin-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-plugin-core@5128

@tanstack/start-server-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-core@5128

@tanstack/start-server-functions-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-client@5128

@tanstack/start-server-functions-fetcher

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-fetcher@5128

@tanstack/start-server-functions-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-server@5128

@tanstack/start-storage-context

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-storage-context@5128

@tanstack/valibot-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/valibot-adapter@5128

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/TanStack/router/@tanstack/virtual-file-routes@5128

@tanstack/zod-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/zod-adapter@5128

commit: a2a07b4

@Sheraff Sheraff merged commit 65c07d7 into main Sep 11, 2025
5 checks passed
@Sheraff Sheraff deleted the refactor-router-core-process-route-tree-own-file branch September 11, 2025 16:32
Copy link
Contributor

@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

🧹 Nitpick comments (4)
packages/router-core/src/process-route-tree.ts (2)

14-22: Consider documenting the scoring constants.

While the constant names are descriptive, adding comments explaining the scoring strategy would help future maintainers understand why these specific values were chosen and how they affect route ranking.

+// Scoring constants for route ranking algorithm
+// Higher scores indicate higher priority in route matching
 const REQUIRED_PARAM_BASE_SCORE = 0.5
 const OPTIONAL_PARAM_BASE_SCORE = 0.4
 const WILDCARD_PARAM_BASE_SCORE = 0.25
+// Additional bonuses for params with prefixes/suffixes (more specific patterns)
 const BOTH_PRESENCE_BASE_SCORE = 0.05
 const PREFIX_PRESENCE_BASE_SCORE = 0.02
 const SUFFIX_PRESENCE_BASE_SCORE = 0.01
+// Length multipliers provide fine-grained differentiation
 const PREFIX_LENGTH_SCORE_MULTIPLIER = 0.0002
 const SUFFIX_LENGTH_SCORE_MULTIPLIER = 0.0001

97-111: Consider extracting the static-after-param detection logic.

The nested loop that checks for static segments after parameters could be extracted into a helper function for better readability and testability.

+function hasStaticSegmentAfter(parsed: ReadonlyArray<Segment>, startIndex: number): boolean {
+  for (let i = startIndex + 1; i < parsed.length; i++) {
+    const nextSegment = parsed[i]!
+    if (
+      nextSegment.type === SEGMENT_TYPE_PATHNAME &&
+      nextSegment.value !== '/'
+    ) {
+      return true
+    }
+  }
+  return false
+}

       if (baseScore) {
         // if there is any static segment (that is not an index) after a required / optional param,
         // we will boost this param so it ranks higher than a required/optional param without a static segment after it
         // JUST FOR SORTING, NOT FOR MATCHING
-        for (let i = index + 1; i < parsed.length; i++) {
-          const nextSegment = parsed[i]!
-          if (
-            nextSegment.type === SEGMENT_TYPE_PATHNAME &&
-            nextSegment.value !== '/'
-          ) {
-            hasStaticAfter = true
-            return handleParam(segment, baseScore + 0.2)
-          }
-        }
+        if (hasStaticSegmentAfter(parsed, index)) {
+          hasStaticAfter = true
+          return handleParam(segment, baseScore + 0.2)
+        }
 
         return handleParam(segment, baseScore)
       }
packages/router-core/src/router.ts (2)

942-951: Optional: remove downstream as casts by typing the destructure.

Annotate the destructured result with the exported result type to avoid three separate as assertions.

-import { processRouteTree } from './process-route-tree'
+import { processRouteTree, type ProcessRouteTreeResult } from './process-route-tree'
@@
-    const { routesById, routesByPath, flatRoutes } = processRouteTree({
+    const { routesById, routesByPath, flatRoutes }: ProcessRouteTreeResult<TRouteTree> = processRouteTree({
       routeTree: this.routeTree,
       initRoute: (route, i) => {
         route.init({
           originalIndex: i,
         })
       },
     })
@@
-    this.routesById = routesById as RoutesById<TRouteTree>
-    this.routesByPath = routesByPath as RoutesByPath<TRouteTree>
-    this.flatRoutes = flatRoutes as Array<AnyRoute>
+    this.routesById = routesById
+    this.routesByPath = routesByPath
+    this.flatRoutes = flatRoutes

959-963: Nit: avoid magic number for not-found route index.

Use Number.MAX_SAFE_INTEGER for clarity.

-      notFoundRoute.init({
-        originalIndex: 99999999999,
-      })
+      notFoundRoute.init({
+        originalIndex: Number.MAX_SAFE_INTEGER,
+      })
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e216765 and a2a07b4.

📒 Files selected for processing (5)
  • packages/router-core/src/index.ts (1 hunks)
  • packages/router-core/src/process-route-tree.ts (1 hunks)
  • packages/router-core/src/route.ts (1 hunks)
  • packages/router-core/src/router.ts (3 hunks)
  • packages/router-core/tests/processRouteTree.test.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
packages/router-core/src/process-route-tree.ts (2)
packages/router-core/src/route.ts (1)
  • RouteLike (1753-1764)
packages/router-core/src/path.ts (4)
  • SEGMENT_TYPE_PARAM (7-7)
  • SEGMENT_TYPE_OPTIONAL_PARAM (9-9)
  • SEGMENT_TYPE_WILDCARD (8-8)
  • SEGMENT_TYPE_PATHNAME (6-6)
packages/router-core/tests/processRouteTree.test.ts (1)
packages/router-core/src/route.ts (1)
  • RouteLike (1753-1764)
🔇 Additional comments (13)
packages/router-core/src/route.ts (1)

1753-1764: LGTM! Well-structured interface for route-like objects.

The RouteLike interface provides a clean abstraction for representing routes with just the essential properties needed for route tree processing. The optional properties are appropriately marked, and the interface is well-placed in the route module.

packages/router-core/src/index.ts (1)

197-198: LGTM! Clean re-export from the new module.

The exports are correctly updated to re-export from the new process-route-tree module, maintaining the same public API while improving code organization.

packages/router-core/tests/processRouteTree.test.ts (3)

2-5: LGTM! Imports correctly updated to use the new module structure.

The test file properly imports processRouteTree from the new module and uses the public RouteLike type, aligning with the refactored code organization.


9-12: LGTM! Return type properly updated to use RouteLike.

The function signature correctly uses the RouteLike type from the public API.


40-40: LGTM! Consistent type usage throughout test helpers.

The createRouteTree function also correctly returns the RouteLike type, maintaining consistency across all test utilities.

packages/router-core/src/process-route-tree.ts (5)

23-50: LGTM! Clean parameter scoring logic.

The handleParam function elegantly handles different parameter configurations with appropriate scoring adjustments based on prefix/suffix presence and length.


52-174: Well-structured route sorting implementation with comprehensive ranking criteria.

The sortRoutes function implements a sophisticated multi-criteria sorting algorithm that handles various edge cases. The segment-by-segment comparison with multiple tie-breakers ensures predictable and intuitive route matching behavior.


176-180: LGTM! Clean type definition for the processing result.

The ProcessRouteTreeResult type clearly defines the expected output structure with appropriate generic constraints.


182-228: Well-implemented route tree processing with proper error handling.

The processRouteTree function correctly:

  • Detects duplicate route IDs with clear error messages
  • Handles root routes appropriately
  • Manages path trimming consistently
  • Provides an optional initialization hook for routes

206-213: Confirm/document trailing-slash override behavior.

The condition lets index routes (childRoute.fullPath.endsWith('/')) overwrite non-index routes for the same trimmed path — packages/router-core/src/process-route-tree.ts (lines 206–213). The docs describe index-route/trailing-slash semantics and expose a RouterOptions.trailingSlash setting, so this looks intentional. (tanstack.com) There is a reported types vs runtime mismatch around routes-by-path that you may want to address or note in a test/inline comment (#3780). (github.com)

packages/router-core/src/router.ts (3)

32-32: Type-only import is spot on.

Keeps runtime clean and tree-shakeable.


56-56: Resolved — RouteLike exposes fullPath, path, parentRoute, and options.caseSensitive.
Interface in packages/router-core/src/route.ts includes fullPath, path?, parentRoute?, and options?.caseSensitive.


15-15: Extraction OK — sorting tie-break uses insertion order, not route.originalIndex.

sortRoutes (packages/router-core/src/process-route-tree.ts) falls back to return a.index - b.index where index is the position in the array passed (from Object.values(routesById)); processRouteTree builds routesById via depth‑first recurseRoutes. router.ts still calls route.init({ originalIndex: i }), but that originalIndex property is not used by the comparator. Confirm the recursion/insertion order matches the pre‑extraction ordering, or change the comparator to use route.originalIndex if that was the intended tie‑breaker.

LadyBluenotes pushed a commit to LadyBluenotes/router that referenced this pull request Sep 19, 2025
- move `processRouteTree` to a separate file because the logic is very
self-contained
- create a new function `sortRoutes` w/ the sorting logic, so it can be
tested independently

This PR is a no-op, we're just moving code around, *nothing* was
changed.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- New Features
- Added a public Route type for describing route objects in route trees.

- Refactor
- Consolidated route processing into a dedicated module for clearer
separation and maintainability.
- Updated public exports for route processing; existing imports may need
to be adjusted.
  - No runtime behavior changes expected.

- Tests
- Updated tests to use the new public Route type and revised import
paths.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants