Skip to content

Conversation

@schiller-manuel
Copy link
Contributor

@schiller-manuel schiller-manuel commented Jan 23, 2026

Summary by CodeRabbit

  • New Features

    • Server-side route-tree caching to speed up route resolution and reuse processed results.
    • Structured route processing that exposes optimized lookup maps for faster path and ID resolution.
  • Chores

    • Adjusted router initialization flow to apply cached or newly built route data consistently.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 23, 2026

📝 Walkthrough

Walkthrough

Reworks route-tree processing to return a structured ProcessRouteTreeResult (processed tree plus routesById/routesByPath) and updates RouterCore to use a server-side global __TSR_CACHE__, a class resolvePathCache, and a setRoutes(...) integration flow that consumes the new result.

Changes

Cohort / File(s) Summary
Route tree processing
packages/router-core/src/new-process-route-tree.ts
Adds exported ProcessRouteTreeResult<TRouteLike> interface; changes processRouteTree signature to return it and require id: string; initializes and populates routesById and routesByPath during parsing with duplicate checks; returns structured result including processedTree.
Router core integration & caching
packages/router-core/src/router.ts
Introduces global ambient __TSR_CACHE__ declaration; adds class property resolvePathCache!: LRUCache<string,string>; updates buildRouteTree to return ProcessRouteTreeResult and callers to use result.processedTree; implements cache-aware flow checking/using __TSR_CACHE__ or creating an LRU cache; adds setRoutes({ routesById, routesByPath, processedTree }) to apply build results.

Sequence Diagram

sequenceDiagram
    participant RC as RouterCore
    participant GC as __TSR_CACHE__ (Global)
    participant BRT as buildRouteTree()
    participant SR as setRoutes()

    RC->>RC: routeTree changed

    alt Server & cache hit
        RC->>GC: check cached processRouteTreeResult + resolvePathCache
        GC-->>RC: return cached result
        RC->>SR: setRoutes(cached result)
    else Cache miss / client
        RC->>BRT: create LRU cache → call buildRouteTree()
        BRT-->>RC: return ProcessRouteTreeResult
        RC->>GC: populate __TSR_CACHE__ (server) if applicable
        RC->>SR: setRoutes(new result)
    end

    SR->>RC: initialize processedTree, routesById, routesByPath
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~35 minutes

Possibly related PRs

Suggested labels

performance

Suggested reviewers

  • schiller-manuel

Poem

🐰 I hopped through nodes and maps so neat,

routes by id and paths complete.
Cached at dawn on server ground,
setRoutes makes the tree unbound.
Hop, cache, route — a tidy feat!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: cache routeTree on the server in-between requests' directly and clearly describes the main objective of the changes: implementing server-side caching of the routeTree between requests. The implementation adds global cache support (TSR_CACHE), caching logic in the router update flow, and an LRU cache property, all of which align with this title.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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

@nx-cloud
Copy link

nx-cloud bot commented Jan 23, 2026

🤖 Nx Cloud AI Fix Eligible

An automatically generated fix could have helped fix failing tasks for this run, but Self-healing CI is disabled for this workspace. Visit workspace settings to enable it and get automatic fixes in future runs.

To disable these notifications, a workspace admin can disable them in workspace settings.


View your CI Pipeline Execution ↗ for commit c435916

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

☁️ Nx Cloud last updated this comment at 2026-01-23 23:33:33 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 23, 2026

More templates

@tanstack/arktype-adapter

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

@tanstack/eslint-plugin-router

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

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-fn-stubs

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-fn-stubs@6475

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/vue-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router@6475

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start@6475

@tanstack/vue-start-client

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

@tanstack/vue-start-server

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

@tanstack/zod-adapter

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

commit: c435916

Comment on lines 1151 to 1155
@@ -1120,9 +1155,17 @@ export class RouterCore<
},
Copy link
Contributor

@Sheraff Sheraff Jan 23, 2026

Choose a reason for hiding this comment

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

is it OK that this won't be called again when reusing the cached process tree?

It seems to be doing a bunch of important things

this.parentRoute = this.options.getParentRoute?.()
this._path = path as TPath
this._id = id as TId
this._fullPath = fullPath as TFullPath
this._to = trimPathRight(fullPath) as TrimPathRight<TFullPath>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this sets path, id, to, fullPath of a route. those are static

Copy link
Contributor

Choose a reason for hiding this comment

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

oh right, and they are on the routeTree itself, which is what survives across requests anyway, regardless of this PR.

Ok so it is fine then

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: 2

Caution

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

⚠️ Outside diff range comments (3)
packages/router-core/src/new-process-route-tree.ts (1)

808-825: Avoid prototype-collision bugs in routesById/routesByPath.
Using {} plus in can mis-detect duplicates for ids like toString and allows __proto__ keys. Prefer Object.create(null) + hasOwnProperty (or a Map).

Suggested fix
-  const routesById = {} as Record<string, TRouteLike>
-  const routesByPath = {} as Record<string, TRouteLike>
+  const routesById = Object.create(null) as Record<string, TRouteLike>
+  const routesByPath = Object.create(null) as Record<string, TRouteLike>
@@
-    invariant(
-      !(route.id in routesById),
+    invariant(
+      !Object.prototype.hasOwnProperty.call(routesById, route.id),
       `Duplicate routes found with id: ${String(route.id)}`,
     )
packages/router-core/src/router.ts (2)

28-44: Fix lint: type-only import & member ordering.
ESLint flags LRUCache as type-only and the member ordering in the type import.

Suggested fix
-import { createLRUCache, LRUCache } from './lru-cache'
+import { createLRUCache } from './lru-cache'
+import type { LRUCache } from './lru-cache'
@@
-import type {
-  ProcessedTree,
-  ProcessRouteTreeResult,
-} from './new-process-route-tree'
+import type {
+  ProcessRouteTreeResult,
+  ProcessedTree,
+} from './new-process-route-tree'

1164-1181: Avoid mutating shared cached maps when adding notFoundRoute.
When reusing the global cache, routesById/routesByPath are shared. Mutating them here can leak notFoundRoute across router instances. Clone before mutation or keep notFoundRoute separately.

Suggested fix
-    this.routesById = routesById as RoutesById<TRouteTree>
-    this.routesByPath = routesByPath as RoutesByPath<TRouteTree>
+    const routesByIdLocal = { ...routesById }
+    const routesByPathLocal = { ...routesByPath }
+    this.routesById = routesByIdLocal as RoutesById<TRouteTree>
+    this.routesByPath = routesByPathLocal as RoutesByPath<TRouteTree>
🤖 Fix all issues with AI agents
In `@packages/router-core/src/router.ts`:
- Around line 879-887: The global declaration for __TSR_CACHE__ triggers the
no-var lint; either replace the leading var with let in the declare global block
(changing "var __TSR_CACHE__" to "let __TSR_CACHE__") or, if using var is
necessary for global augmentation semantics, suppress the rule locally by adding
an eslint disable comment (e.g., // eslint-disable-next-line no-var) immediately
above the declaration; update the declaration around the __TSR_CACHE__ symbol
(and its properties routeTree, processRouteTreeResult, resolvePathCache and
related types ProcessRouteTreeResult, LRUCache, AnyRoute) accordingly so the
linter no-var complaint is resolved.
- Around line 1044-1066: The current server-side cache
(globalThis.__TSR_CACHE__) reuses processRouteTreeResult for the same
this.routeTree without accounting for options like caseSensitive and routeMasks;
update the cache key/shape or assert options match before reuse: when
checking/setting globalThis.__TSR_CACHE__ in the constructor logic around
processRouteTreeResult and resolvePathCache, include the route-matching options
(e.g., this.caseSensitive and this.routeMasks) in the cached object and in the
equality check (or throw/assert if they differ) so that this.buildRouteTree()
and this.setRoutes(processRouteTreeResult) never reuse a result produced under
different option values.

Comment on lines +1044 to 1066
let processRouteTreeResult: ProcessRouteTreeResult<TRouteTree>
if (
this.isServer &&
globalThis.__TSR_CACHE__ &&
globalThis.__TSR_CACHE__.routeTree === this.routeTree
) {
const cached = globalThis.__TSR_CACHE__
this.resolvePathCache = cached.resolvePathCache
processRouteTreeResult = cached.processRouteTreeResult as any
} else {
this.resolvePathCache = createLRUCache(1000)
processRouteTreeResult = this.buildRouteTree()
// only cache if nothing else is cached yet
if (this.isServer && globalThis.__TSR_CACHE__ === undefined) {
globalThis.__TSR_CACHE__ = {
routeTree: this.routeTree,
processRouteTreeResult: processRouteTreeResult as any,
resolvePathCache: this.resolvePathCache,
}
}
}
this.setRoutes(processRouteTreeResult)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Cache reuse should be gated by option-sensitive keys.
processRouteTree/masks depend on caseSensitive and routeMasks. Reusing cached results for the same routeTree but different options can yield incorrect matching. Consider including these options in the cache key (and cache shape) or asserting they match before reuse.

Possible direction
-      if (
-        this.isServer &&
-        globalThis.__TSR_CACHE__ &&
-        globalThis.__TSR_CACHE__.routeTree === this.routeTree
-      ) {
+      if (
+        this.isServer &&
+        globalThis.__TSR_CACHE__ &&
+        globalThis.__TSR_CACHE__.routeTree === this.routeTree &&
+        globalThis.__TSR_CACHE__.caseSensitive === this.options.caseSensitive &&
+        globalThis.__TSR_CACHE__.routeMasks === this.options.routeMasks
+      ) {
@@
-          globalThis.__TSR_CACHE__ = {
-            routeTree: this.routeTree,
+          globalThis.__TSR_CACHE__ = {
+            routeTree: this.routeTree,
+            caseSensitive: this.options.caseSensitive,
+            routeMasks: this.options.routeMasks,
             processRouteTreeResult: processRouteTreeResult as any,
             resolvePathCache: this.resolvePathCache,
           }
🤖 Prompt for AI Agents
In `@packages/router-core/src/router.ts` around lines 1044 - 1066, The current
server-side cache (globalThis.__TSR_CACHE__) reuses processRouteTreeResult for
the same this.routeTree without accounting for options like caseSensitive and
routeMasks; update the cache key/shape or assert options match before reuse:
when checking/setting globalThis.__TSR_CACHE__ in the constructor logic around
processRouteTreeResult and resolvePathCache, include the route-matching options
(e.g., this.caseSensitive and this.routeMasks) in the cached object and in the
equality check (or throw/assert if they differ) so that this.buildRouteTree()
and this.setRoutes(processRouteTreeResult) never reuse a result produced under
different option values.

@schiller-manuel schiller-manuel merged commit ad084d4 into main Jan 23, 2026
5 of 6 checks passed
@schiller-manuel schiller-manuel deleted the cache-tree branch January 23, 2026 23:37
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.

3 participants