Skip to content

Conversation

@Sheraff
Copy link
Contributor

@Sheraff Sheraff commented Nov 1, 2025

Warning

untested, DO NOT MERGE

Design exploration of a rewrite of processRouteTree to yield a trie-like tree of segments. This should replace

  • packages/router-core/src/process-route-tree.ts
  • parts of packages/router-core/src/path.ts

Goals:

  • faster pre-processing at start-time
  • faster matching at run-time
  • more consistent priority matching

Non-goals:

  • low RAM usage

to-do:

  • fix path "preparation" (leading/trailing /)
  • implement sorting of same-type siblings at the node level (case sensitivity, prefix/suffix length)
  • implement single-match ("does A match B")
  • implement all-route-match ("which route does A match")
  • bench pre-processing against current implementation
  • bench single-match against current implementation
  • bench all-route-match against current implementation

Bench:

  • pre-processing
    • consistently >2x faster
  • needle-in-a-haystack route matching
    • for very small route trees: 3.5x faster
    • for very large route trees: 950x faster (430 routes)
    • non-matching: 90x faster (430 routes, random path "that almost matches" requested, this is a worst-case-scenario simulation)
  • 1-to-1 route matching (useMatchRoute)
    • for very simple routes: 1.9x slower
    • for very complex routes: 1.3x slower (might not be statistically significant)
  • resolvePath
    • 2.7x faster (added a LRU cache to this, it's called a lot and is easy to cache, but function itself could be improved)
  • interpolatePath
    • 1.4x faster

Notes:

  • in its current implementation, 1-to-1 route matching (useMatchRoute) does not care about the routeTree. It will match 2 arbitrary strings. This can lead to unexpected results, and is probably not the behavior we want in the long term.
    For now, this PR implements findSingleMatch to preserve that behavior, and this method is slower than the current implementation. However, when we switch to matching against the routeTree (using findRouteMatch instead), it will be faster.

Summary by CodeRabbit

  • Deprecations

    • The caseSensitive option on match-route options is deprecated; configure case sensitivity on routes or the router instead.
  • Breaking Changes

    • Legacy path parsing/matching exports and related types removed in favor of the new pipeline.
  • New Features

    • New processed route-tree matcher and updated path resolution/interpolation for improved matching and param handling.
  • Tests / Quality

    • Extensive new unit tests for the route-tree matcher; tests updated to the new APIs.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 1, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR replaces legacy path parsing/matching and flat route lists with a segment-tree based route processor, adds a new route-tree implementation and tests, removes several path-related public exports, updates RouterCore to use a ProcessedTree, and marks MatchRouteOptions.caseSensitive as deprecated in docs and JSDoc.

Changes

Cohort / File(s) Summary
Documentation & Deprecation
docs/router/framework/react/api/router/MatchRouteOptionsType.md, packages/router-core/src/Matches.ts
caseSensitive?: boolean in MatchRouteOptions annotated as deprecated; JSDoc updated to recommend route-level or router-wide case sensitivity configuration.
Removed Legacy Route Processor
packages/router-core/src/process-route-tree.ts
Entire file removed — old route-tree building, scoring, sorting, flat route generation, and related exported types/functions deleted.
New Route Processing Module
packages/router-core/src/new-process-route-tree.ts
New segment-based parsing and matching subsystem: parsing primitives (parseSegment), node types/factories, tree sorting, processing APIs (processRouteTree, processFlatRouteList, findRouteMatch, findFlatMatch, findSingleMatch, trimPathRight) and related types exported.
Path Utilities Refactor
packages/router-core/src/path.ts
Rewrote path utilities to string-based model; removed old parsing/matching exports (parsePathname, matchPathname, matchByPath, Segment, segment constants); added joinPaths, cleanPath, trimPath*, removeTrailingSlash, exactPathTest, resolvePath; interpolatePath reimplemented to use new segment parsing.
Router Core Migration
packages/router-core/src/router.ts
Replaced public flatRoutes with processedTree: ProcessedTree<...>; updated imports and matching calls to use new-process-route-tree APIs; simplified GetMatchRoutesFn to take only pathname and return foundRoute.
Barrel & Public Exports
packages/router-core/src/index.ts, packages/react-router/src/index.tsx, packages/solid-router/src/index.tsx
Removed re-exports: parsePathname, matchPathname, matchByPath, Segment, and process-route-tree related types/functions from public barrels and framework packages.
Call Site Adjustments
packages/react-router/src/useBlocker.tsx, packages/solid-router/src/useBlocker.tsx, packages/start-server-core/src/createStartHandler.ts
Simplified calls to router.getMatchedRoutes by dropping an explicit undefined second argument (now called with only pathname).
HMR & Plugin Updates
packages/router-plugin/src/core/route-hmr-statement.ts
Updated imports to include AnyRouter; replaced flatRoutes in-place swap with segment-tree walk/replace (walkReplaceSegmentTree) to update processedTree nodes and clear caches.
Devtools Surface
packages/router-devtools-core/src/BaseTanStackRouterDevtoolsPanel.tsx
Removed flatRoutes key from public explorerState shaping; explorer no longer exposes flatRoutes.
Tests: New & Updated
packages/router-core/tests/new-process-route-tree.test.ts, packages/router-core/tests/*.test.ts
Added extensive tests for new-process-route-tree; updated tests to import parsing/matching primitives from new-process-route-tree and path changes; some tests recreate match helpers using processRouteTree and findSingleMatch.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Router
    participant Old as "Old Processor\n(process-route-tree)"
    participant New as "New Processor\n(new-process-route-tree)"

    rect rgb(240,228,228)
    Note over Old: Legacy flow (removed)
    User->>Router: buildRouteTree(routeTree)
    Router->>Old: processRouteTree(routeTree)
    Old->>Old: sort & flatten routes
    Old-->>Router: flatRoutes
    User->>Router: getMatchedRoutes(pathname, routePathname)
    Router->>Old: matchPathname / matchByPath
    Old-->>Router: matchedRoutes
    end

    rect rgb(228,240,232)
    Note over New: New flow (added)
    User->>Router: buildRouteTree(routeTree)
    Router->>New: processRouteTree(routeTree, caseSensitive)
    New->>New: parseSegments -> build segment tree
    New-->>Router: processedTree
    User->>Router: getMatchedRoutes(pathname)
    Router->>New: findRouteMatch(pathname, processedTree)
    New->>New: DFS traversal, extract params
    New-->>Router: matchedRoutes + foundRoute + params
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60–90 minutes

  • Pay special attention to:
    • packages/router-core/src/new-process-route-tree.ts — algorithmic complexity: parsing, trie construction, matching, params extraction.
    • packages/router-core/src/path.ts — refactor of interpolation/resolution semantics.
    • packages/router-core/src/router.ts — API/signature changes and state migration to processedTree.
    • Cross-package re-exports and test updates to ensure no removed symbols remain referenced.

Possibly related PRs

Suggested reviewers

  • schiller-manuel
  • nlynzaad

Poem

🐇
I hopped the branches, carved a tree,
Turned flat lists into roots for me.
Segments tucked in leaf and vine —
I traced your path and found the line. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.93% 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
Title check ✅ Passed The title clearly and specifically describes the main refactoring: replacing the flatRoutes representation with a segment tree in the router-core's processRouteTree function.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

@nx-cloud
Copy link

nx-cloud bot commented Nov 1, 2025

View your CI Pipeline Execution ↗ for commit 320cdbd

Command Status Duration Result
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 1s View ↗

☁️ Nx Cloud last updated this comment at 2025-11-13 00:32:57 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 1, 2025

More templates

@tanstack/arktype-adapter

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

@tanstack/directive-functions-plugin

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

@tanstack/eslint-plugin-router

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

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/server-functions-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/zod-adapter

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

commit: 320cdbd

Comment on lines 236 to 250
function sortDynamic(a: { prefix?: string, suffix?: string }, b: { prefix?: string, suffix?: string }) {
if (a.prefix && b.prefix && a.prefix !== b.prefix) {
if (a.prefix.startsWith(b.prefix)) return -1
if (b.prefix.startsWith(a.prefix)) return 1
}
if (a.suffix && b.suffix && a.suffix !== b.suffix) {
if (a.suffix.endsWith(b.suffix)) return -1
if (b.suffix.endsWith(a.suffix)) return 1
}
if (a.prefix && !b.prefix) return -1
if (!a.prefix && b.prefix) return 1
if (a.suffix && !b.suffix) return -1
if (!a.suffix && b.suffix) return 1
return 0
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

should rank case-sensitive routes w/ prefix/suffix higher than case-insensitive ones

@Sheraff
Copy link
Contributor Author

Sheraff commented Nov 12, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 12, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

♻️ Duplicate comments (4)
packages/router-core/src/new-process-route-tree.ts (3)

170-170: Critical: Fix depth calculation when reusing existing nodes.

Lines 170, 185, 222, 260, and 288 use ++depth to set next.depth, which mutates the outer depth variable. When the loop encounters an existing child node, depth is incremented but never synchronized with the child's actual depth. This causes subsequent siblings to receive incorrect depth values, breaking buildBranch and extractParams for routes that share prefixes (e.g., /foo/bar followed by /foo/baz).

Apply this diff to compute depth from the parent and resync for both new and existing nodes:

           if (caseSensitive) {
             const existingNode = node.static?.get(value)
             if (existingNode) {
               nextNode = existingNode
             } else {
               node.static ??= new Map()
               const next = createStaticNode<TRouteLike>(
                 route.fullPath ?? route.from,
               )
               next.parent = node
-              next.depth = ++depth
+              next.depth = node.depth + 1
               nextNode = next
               node.static.set(value, next)
             }
           } else {
             const name = value.toLowerCase()
             const existingNode = node.staticInsensitive?.get(name)
             if (existingNode) {
               nextNode = existingNode
             } else {
               node.staticInsensitive ??= new Map()
               const next = createStaticNode<TRouteLike>(
                 route.fullPath ?? route.from,
               )
               next.parent = node
-              next.depth = ++depth
+              next.depth = node.depth + 1
               nextNode = next
               node.staticInsensitive.set(name, next)
             }
           }

Apply similar fixes to dynamic/optional/wildcard node creation:

               )
               nextNode = next
-              next.depth = ++depth
+              next.depth = node.depth + 1
               next.parent = node

And resync depth after advancing to the next node:

       node = nextNode
+      depth = node.depth
     }

Also applies to: 185-185, 222-222, 260-260, 288-288


546-546: Critical: Pass caseSensitive parameter to parseSegments.

Line 546 hardcodes caseSensitive as the first argument but then calls parseSegments(caseSensitive, ...). However, the temporary tree is always built with case-insensitive semantics because the hardcoded value should match the caller's caseSensitive parameter. Currently, static segments are lowercased regardless of the caller's flag, breaking router.matchRoute({ caseSensitive: true }).

The code currently passes caseSensitive correctly on line 546. If tests are failing, verify the logic inside parseSegments properly honors the flag.


895-898: Critical: Fix wildcard suffix validation to use the correct segment.

Lines 895-898 check the suffix for wildcards, but line 896 constructs end from parts.slice(index).join('/') which represents the rest of the path, not the current segment. For wildcards in the middle of a path, this reads the wrong portion and breaks suffix matching.

Apply this diff:

           if (suffix) {
-            const end = parts.slice(index).join('/').slice(-suffix.length)
-            const casePart = segment.caseSensitive ? end : end.toLowerCase()
-            if (casePart !== suffix) continue
+            const lastPart = parts[parts.length - 1]!
+            const casePart = segment.caseSensitive ? lastPart : lastPart.toLowerCase()
+            if (!casePart.endsWith(suffix)) continue
           }
packages/router-core/src/path.ts (1)

194-201: Fix undefined prefix/suffix in optional param serialization.

Line 200 uses template literals with prefix and suffix variables that may be undefined. When either is undefined, the resulting string will contain the literal text "undefined", corrupting the path template (e.g., "undefined{-$id}undefined").

Apply this diff to normalize undefined values to empty strings:

     } else {
       // SEGMENT_TYPE_OPTIONAL_PARAM
-      joined += `${prefix}{-$${value}}${suffix}`
+      const safePrefix = prefix ?? ''
+      const safeSuffix = suffix ?? ''
+      joined += `${safePrefix}{-$${value}}${safeSuffix}`
     }
🧹 Nitpick comments (5)
packages/router-core/src/new-process-route-tree.ts (1)

311-328: Consider adding alphabetical fallback to sortDynamic.

The sortDynamic function prioritizes prefix/suffix length but doesn't provide a stable fallback when segments are identical. This can lead to non-deterministic ordering across environments or V8 versions.

Add a final alphabetical comparison:

   if (a.suffix && !b.suffix) return -1
   if (!a.suffix && b.suffix) return 1
+  // Fallback to alphabetical order for stability
+  const aStr = (a.prefix ?? '') + (a.suffix ?? '')
+  const bStr = (b.prefix ?? '') + (b.suffix ?? '')
+  return aStr.localeCompare(bStr)
-  return 0
 }
packages/router-core/tests/optional-path-params-clean.test.ts (1)

3-10: Fix import order and type import style.

Lines 7 and 9 violate ESLint rules for import ordering and type import style. These are minor style issues but should be fixed for consistency.

Apply this diff:

 import {
   findSingleMatch,
   parseSegment,
   processRouteTree,
-  SEGMENT_TYPE_OPTIONAL_PARAM,
   SEGMENT_TYPE_PATHNAME,
-  type SegmentKind,
+  SEGMENT_TYPE_OPTIONAL_PARAM,
 } from '../src/new-process-route-tree'
+import type { SegmentKind } from '../src/new-process-route-tree'
packages/router-core/tests/new-process-route-tree.test.ts (1)

4-4: Remove unused import or implement processFlatRouteList tests.

Line 4 imports processFlatRouteList but it's only used in a describe.todo block (lines 317-329). Either implement the tests or remove the import to clean up the code.

If tests aren't ready, remove the import:

 import {
   findRouteMatch,
-  processFlatRouteList,
   processRouteTree,
 } from '../src/new-process-route-tree'

Also applies to: 317-329

packages/router-core/tests/path.test.ts (1)

9-18: Fix import issues: remove unused import and fix style.

Line 13 imports SEGMENT_TYPE_OPTIONAL_PARAM but it's never used. Lines 13 and 17 also violate import ordering and type import style rules.

Apply this diff:

 import {
   findSingleMatch,
   parseSegment,
   processRouteTree,
-  SEGMENT_TYPE_OPTIONAL_PARAM,
   SEGMENT_TYPE_PARAM,
   SEGMENT_TYPE_PATHNAME,
   SEGMENT_TYPE_WILDCARD,
-  type SegmentKind,
 } from '../src/new-process-route-tree'
+import type { SegmentKind } from '../src/new-process-route-tree'
packages/router-core/tests/optional-path-params.test.ts (1)

3-12: Fix import order and type import style.

Lines 7 and 11 violate ESLint rules for import ordering and type import style, similar to other test files.

Apply this diff:

 import {
   findSingleMatch,
   parseSegment,
   processRouteTree,
-  SEGMENT_TYPE_OPTIONAL_PARAM,
   SEGMENT_TYPE_PARAM,
   SEGMENT_TYPE_PATHNAME,
   SEGMENT_TYPE_WILDCARD,
-  type SegmentKind,
+  SEGMENT_TYPE_OPTIONAL_PARAM,
 } from '../src/new-process-route-tree'
+import type { SegmentKind } from '../src/new-process-route-tree'
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58baf3e and b8aead3.

📒 Files selected for processing (10)
  • packages/router-core/src/new-process-route-tree.ts (1 hunks)
  • packages/router-core/src/path.ts (5 hunks)
  • packages/router-core/src/router.ts (10 hunks)
  • packages/router-core/tests/match-by-path.test.ts (1 hunks)
  • packages/router-core/tests/new-process-route-tree.test.ts (1 hunks)
  • packages/router-core/tests/optional-path-params-clean.test.ts (2 hunks)
  • packages/router-core/tests/optional-path-params.test.ts (5 hunks)
  • packages/router-core/tests/path.test.ts (17 hunks)
  • packages/router-devtools-core/src/BaseTanStackRouterDevtoolsPanel.tsx (0 hunks)
  • packages/router-plugin/src/core/route-hmr-statement.ts (3 hunks)
💤 Files with no reviewable changes (1)
  • packages/router-devtools-core/src/BaseTanStackRouterDevtoolsPanel.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • packages/router-core/tests/optional-path-params-clean.test.ts
  • packages/router-plugin/src/core/route-hmr-statement.ts
  • packages/router-core/tests/match-by-path.test.ts
  • packages/router-core/src/path.ts
  • packages/router-core/src/router.ts
  • packages/router-core/tests/optional-path-params.test.ts
  • packages/router-core/tests/new-process-route-tree.test.ts
  • packages/router-core/src/new-process-route-tree.ts
  • packages/router-core/tests/path.test.ts
packages/router-core/**

📄 CodeRabbit inference engine (AGENTS.md)

Keep framework-agnostic core router logic in packages/router-core/

Files:

  • packages/router-core/tests/optional-path-params-clean.test.ts
  • packages/router-core/tests/match-by-path.test.ts
  • packages/router-core/src/path.ts
  • packages/router-core/src/router.ts
  • packages/router-core/tests/optional-path-params.test.ts
  • packages/router-core/tests/new-process-route-tree.test.ts
  • packages/router-core/src/new-process-route-tree.ts
  • packages/router-core/tests/path.test.ts
packages/{router-cli,router-generator,router-plugin,virtual-file-routes}/**

📄 CodeRabbit inference engine (AGENTS.md)

Keep CLI, generators, bundler plugins, and virtual file routing utilities in their dedicated tooling package directories

Files:

  • packages/router-plugin/src/core/route-hmr-statement.ts
packages/router-plugin/**

📄 CodeRabbit inference engine (AGENTS.md)

Use unplugin for universal bundler plugins in the router-plugin package

Files:

  • packages/router-plugin/src/core/route-hmr-statement.ts
🧠 Learnings (12)
📓 Common learnings
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/router-core/** : Keep framework-agnostic core router logic in packages/router-core/
Learnt from: nlynzaad
Repo: TanStack/router PR: 5182
File: e2e/react-router/basic-file-based/tests/non-nested-paths.spec.ts:167-172
Timestamp: 2025-09-22T00:56:53.426Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments during path parsing, but preserved in base path segments. This is the expected behavior implemented in PR #5182.
Learnt from: nlynzaad
Repo: TanStack/router PR: 5182
File: e2e/react-router/basic-file-based/src/routes/non-nested/named/$baz_.bar.tsx:3-5
Timestamp: 2025-09-22T00:56:49.237Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments (e.g., `$baz_` becomes `baz` in generated types) but should be preserved in base path segments. This is the correct behavior as of the fix in PR #5182.
Learnt from: nlynzaad
Repo: TanStack/router PR: 5402
File: packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts:19-21
Timestamp: 2025-10-08T08:11:47.088Z
Learning: Test snapshot files in the router-generator tests directory (e.g., files matching the pattern `packages/router-generator/tests/generator/**/routeTree*.snapshot.ts` or `routeTree*.snapshot.js`) should not be modified or have issues flagged, as they are fixtures used to verify the generator's output and are intentionally preserved as-is.
📚 Learning: 2025-10-08T08:11:47.088Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5402
File: packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts:19-21
Timestamp: 2025-10-08T08:11:47.088Z
Learning: Test snapshot files in the router-generator tests directory (e.g., files matching the pattern `packages/router-generator/tests/generator/**/routeTree*.snapshot.ts` or `routeTree*.snapshot.js`) should not be modified or have issues flagged, as they are fixtures used to verify the generator's output and are intentionally preserved as-is.

Applied to files:

  • packages/router-core/tests/optional-path-params-clean.test.ts
  • packages/router-plugin/src/core/route-hmr-statement.ts
  • packages/router-core/tests/match-by-path.test.ts
  • packages/router-core/src/path.ts
  • packages/router-core/src/router.ts
  • packages/router-core/tests/optional-path-params.test.ts
  • packages/router-core/tests/new-process-route-tree.test.ts
  • packages/router-core/src/new-process-route-tree.ts
  • packages/router-core/tests/path.test.ts
📚 Learning: 2025-10-14T18:59:33.990Z
Learnt from: FatahChan
Repo: TanStack/router PR: 5475
File: e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx:8-0
Timestamp: 2025-10-14T18:59:33.990Z
Learning: In TanStack Router e2e test files, when a route parameter is validated at the route level (e.g., using zod in validateSearch or param validation), switch statements on that parameter do not require a default case, as the validation ensures only expected values will reach the switch.

Applied to files:

  • packages/router-core/tests/optional-path-params-clean.test.ts
  • packages/router-core/tests/match-by-path.test.ts
  • packages/router-core/tests/optional-path-params.test.ts
  • packages/router-core/tests/new-process-route-tree.test.ts
  • packages/router-core/tests/path.test.ts
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/router-core/** : Keep framework-agnostic core router logic in packages/router-core/

Applied to files:

  • packages/router-core/tests/optional-path-params-clean.test.ts
  • packages/router-plugin/src/core/route-hmr-statement.ts
  • packages/router-core/tests/match-by-path.test.ts
  • packages/router-core/src/path.ts
  • packages/router-core/src/router.ts
  • packages/router-core/tests/optional-path-params.test.ts
  • packages/router-core/tests/new-process-route-tree.test.ts
  • packages/router-core/src/new-process-route-tree.ts
  • packages/router-core/tests/path.test.ts
📚 Learning: 2025-10-01T18:30:26.591Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 5330
File: packages/router-core/src/router.ts:2231-2245
Timestamp: 2025-10-01T18:30:26.591Z
Learning: In `packages/router-core/src/router.ts`, the `resolveRedirect` method intentionally strips the router's origin from redirect URLs when they match (e.g., `https://foo.com/bar` → `/bar` for same-origin redirects) while preserving the full URL for cross-origin redirects. This logic should not be removed or simplified to use `location.publicHref` directly.

Applied to files:

  • packages/router-core/tests/optional-path-params-clean.test.ts
  • packages/router-plugin/src/core/route-hmr-statement.ts
  • packages/router-core/src/path.ts
  • packages/router-core/src/router.ts
  • packages/router-core/tests/optional-path-params.test.ts
  • packages/router-core/src/new-process-route-tree.ts
📚 Learning: 2025-09-28T21:41:45.233Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5284
File: e2e/react-start/basic/server.js:50-0
Timestamp: 2025-09-28T21:41:45.233Z
Learning: In Express v5, catch-all routes must use named wildcards. Use `/*splat` to match everything except root path, or `/{*splat}` (with braces) to match including root path. The old `*` syntax is not allowed and will cause "Missing parameter name" errors. This breaking change requires explicit naming of wildcard parameters.

Applied to files:

  • packages/router-core/tests/optional-path-params-clean.test.ts
  • packages/router-core/tests/match-by-path.test.ts
  • packages/router-core/src/path.ts
  • packages/router-core/tests/optional-path-params.test.ts
  • packages/router-core/tests/new-process-route-tree.test.ts
  • packages/router-core/src/new-process-route-tree.ts
  • packages/router-core/tests/path.test.ts
📚 Learning: 2025-10-01T18:31:35.420Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 5330
File: e2e/react-start/custom-basepath/src/routeTree.gen.ts:58-61
Timestamp: 2025-10-01T18:31:35.420Z
Learning: Do not review files named `routeTree.gen.ts` in TanStack Router repositories, as these are autogenerated files that should not be manually modified.

Applied to files:

  • packages/router-plugin/src/core/route-hmr-statement.ts
  • packages/router-core/tests/match-by-path.test.ts
  • packages/router-core/src/router.ts
  • packages/router-core/tests/new-process-route-tree.test.ts
  • packages/router-core/src/new-process-route-tree.ts
  • packages/router-core/tests/path.test.ts
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/router-plugin/** : Use unplugin for universal bundler plugins in the router-plugin package

Applied to files:

  • packages/router-plugin/src/core/route-hmr-statement.ts
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{router-cli,router-generator,router-plugin,virtual-file-routes}/** : Keep CLI, generators, bundler plugins, and virtual file routing utilities in their dedicated tooling package directories

Applied to files:

  • packages/router-plugin/src/core/route-hmr-statement.ts
  • packages/router-core/src/path.ts
  • packages/router-core/src/router.ts
  • packages/router-core/tests/new-process-route-tree.test.ts
  • packages/router-core/src/new-process-route-tree.ts
  • packages/router-core/tests/path.test.ts
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to packages/{router-devtools,*-router-devtools}/** : Keep router devtools packages in packages/router-devtools/ and packages/*-router-devtools/

Applied to files:

  • packages/router-plugin/src/core/route-hmr-statement.ts
  • packages/router-core/src/new-process-route-tree.ts
📚 Learning: 2025-09-22T00:56:49.237Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5182
File: e2e/react-router/basic-file-based/src/routes/non-nested/named/$baz_.bar.tsx:3-5
Timestamp: 2025-09-22T00:56:49.237Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments (e.g., `$baz_` becomes `baz` in generated types) but should be preserved in base path segments. This is the correct behavior as of the fix in PR #5182.

Applied to files:

  • packages/router-core/src/path.ts
  • packages/router-core/src/new-process-route-tree.ts
  • packages/router-core/tests/path.test.ts
📚 Learning: 2025-09-22T00:56:53.426Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5182
File: e2e/react-router/basic-file-based/tests/non-nested-paths.spec.ts:167-172
Timestamp: 2025-09-22T00:56:53.426Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments during path parsing, but preserved in base path segments. This is the expected behavior implemented in PR #5182.

Applied to files:

  • packages/router-core/src/path.ts
  • packages/router-core/src/new-process-route-tree.ts
  • packages/router-core/tests/path.test.ts
🪛 ESLint
packages/router-core/tests/optional-path-params-clean.test.ts

[error] 7-7: Member 'SEGMENT_TYPE_OPTIONAL_PARAM' of the import declaration should be sorted alphabetically.

(sort-imports)


[error] 9-9: Prefer using a top-level type-only import instead of inline type specifiers.

(import/consistent-type-specifier-style)

packages/router-core/tests/optional-path-params.test.ts

[error] 7-7: Member 'SEGMENT_TYPE_OPTIONAL_PARAM' of the import declaration should be sorted alphabetically.

(sort-imports)


[error] 11-11: Prefer using a top-level type-only import instead of inline type specifiers.

(import/consistent-type-specifier-style)

packages/router-core/tests/new-process-route-tree.test.ts

[error] 4-4: 'processFlatRouteList' is defined but never used.

(unused-imports/no-unused-imports)

packages/router-core/tests/path.test.ts

[error] 13-13: Member 'SEGMENT_TYPE_OPTIONAL_PARAM' of the import declaration should be sorted alphabetically.

(sort-imports)


[error] 13-13: 'SEGMENT_TYPE_OPTIONAL_PARAM' is defined but never used.

(unused-imports/no-unused-imports)


[error] 17-17: Prefer using a top-level type-only import instead of inline type specifiers.

(import/consistent-type-specifier-style)

🪛 GitHub Actions: pr
packages/router-core/tests/match-by-path.test.ts

[error] 42-42: Multiple test failures reported in match-by-path tests: expected undefined vs {} or mismatched shape in various matchByPath assertions.

packages/router-core/src/router.ts

[warning] 2094-2097: Unused eslint-disable directive (no problems were reported from '@typescript-eslint/require-await'); Async arrow function has no 'await' expression (require-await).

🔇 Additional comments (17)
packages/router-core/src/new-process-route-tree.ts (1)

502-502: Verify depth initialization consistency across processing functions.

Line 502 initializes depth at 0 when calling parseSegments, which is consistent with processRouteTree (line 596). Confirm this is correct for flat route lists and that buildBranch works as expected when mixing flat and tree routes.

If depth 0 is correct for the root, this is fine. Otherwise, verify against tests.

packages/router-plugin/src/core/route-hmr-statement.ts (1)

25-28: Document limitation: HMR does not handle new route additions.

The TODO on line 27 indicates that adding a new route requires rebuilding the entire tree, which the current HMR logic doesn't support. This means HMR will only work for updates to existing routes, not for adding/removing routes from the tree.

Consider documenting this limitation in a comment or the PR description so users are aware that adding new routes during development will require a full reload.

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

233-350: Impressive refactor: cursor-based interpolation is much cleaner.

The new cursor-based approach to path interpolation is a significant improvement over the old implementation. It's more direct, easier to follow, and handles all segment types (pathname, param, wildcard, optional) in a unified way. The elimination of the intermediate segment array and direct string assembly should also improve performance.

packages/router-core/tests/match-by-path.test.ts (1)

12-24: Fix matchByPath wrapper to return undefined for non-matches.

The wrapper returns match ? match.params : undefined (line 23), but then line 24 is missing in the displayed code. Based on the pipeline failures expecting undefined but receiving {}, verify that the wrapper correctly returns undefined for non-matching paths rather than defaulting to an empty object.

Ensure the logic matches test expectations:

const matchByPath = (
  from: string,
  options: { to: string; caseSensitive?: boolean; fuzzy?: boolean },
) => {
  const match = findSingleMatch(
    options.to,
    options.caseSensitive ?? false,
    options.fuzzy ?? false,
    from,
    processedTree,
  )
  return match ? match.params : undefined  // Don't default to {}
}
⛔ Skipped due to learnings
Learnt from: nlynzaad
Repo: TanStack/router PR: 5402
File: packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts:19-21
Timestamp: 2025-10-08T08:11:47.088Z
Learning: Test snapshot files in the router-generator tests directory (e.g., files matching the pattern `packages/router-generator/tests/generator/**/routeTree*.snapshot.ts` or `routeTree*.snapshot.js`) should not be modified or have issues flagged, as they are fixtures used to verify the generator's output and are intentionally preserved as-is.
Learnt from: FatahChan
Repo: TanStack/router PR: 5475
File: e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx:8-0
Timestamp: 2025-10-14T18:59:33.990Z
Learning: In TanStack Router e2e test files, when a route parameter is validated at the route level (e.g., using zod in validateSearch or param validation), switch statements on that parameter do not require a default case, as the validation ensures only expected values will reach the switch.
packages/router-core/src/router.ts (13)

12-17: LGTM: Import changes align with the new route-tree architecture.

The new imports from ./new-process-route-tree provide the necessary utilities for the processedTree-based matching pipeline.


25-25: LGTM: LRU cache import supports performance optimization.

This import enables the caching mechanism for path resolution mentioned in the PR benchmarks (~2.7× faster resolvePath).


38-38: LGTM: Type import necessary for the new architecture.


698-702: Simplified signature aligns with the new architecture.

The GetMatchRoutesFn signature now accepts only a pathname and returns a complete match result object. This simplification is appropriate given that the processedTree encapsulates the matching logic.


905-905: Verify the any type parameters in ProcessedTree.

The ProcessedTree<TRouteTree, any, any> uses any for two generic parameters. Please ensure these cannot be more specifically typed for better type safety, or confirm that any is intentional for this context.

Based on coding guidelines for TypeScript strict mode.


1097-1109: LGTM: buildRouteTree correctly migrates to processedTree architecture.

The method now properly destructures the new return value from processRouteTree and stores the processedTree on the router instance.


1207-1208: LGTM: LRU cache initialization for path resolution performance.

A cache size of 1000 entries is reasonable and aligns with the PR's performance improvements (~2.7× faster resolvePath).


1215-1215: LGTM: Cache correctly threaded through to resolvePath.


1540-1546: LGTM: getMatchedRoutes simplified to delegate to the new matching logic.

The method now correctly passes processedTree to the standalone helper function and accepts only the pathname parameter.


1607-1607: LGTM: Correctly uses the new getMatchedRoutes signature.


1777-1793: Route masking correctly migrated to findFlatMatch.

The masking logic now uses findFlatMatch with the processedTree. The flow properly extracts params and route properties from the match result.


2503-2528: matchRoute correctly uses findSingleMatch with processedTree.

The method now delegates to findSingleMatch and properly accesses the params property from the returned match. The logic for parameter and search comparison is preserved.


2621-2651: The review comment's premise is incorrect.

The third parameter to findRouteMatch is fuzzy (controls fuzzy/partial matching), not caseSensitive. Case sensitivity is already handled during route tree construction via processRouteTree(routeTree, this.options.caseSensitive, ...) at router initialization (line 1099). The processedTree passed to getMatchedRoutes already encodes case sensitivity rules from route definitions, so findRouteMatch does not need a separate caseSensitive parameter.

Likely an incorrect or invalid review comment.

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