Skip to content

Conversation

@Sheraff
Copy link
Contributor

@Sheraff Sheraff commented Nov 26, 2025

Fixes #5969

This implementation, however elegant, would fails this test:

it('edge-case: deeper index route through skipped optional segments (should not match)', () => {
  const tree = makeTree(['/{-$foo}/{-$bar}/a/', '/a/$'])
  expect(findRouteMatch('/a/', tree)?.route.id).toBe('/{-$foo}/{-$bar}/a/')
  expect(findRouteMatch('/a', tree)?.route.id).toBe('/{-$foo}/{-$bar}/a/')
})

We're going to go w/ #5971 instead

Summary by CodeRabbit

  • Bug Fixes
    • Fixed wildcard route matching to properly handle trailing slash scenarios across different route configurations.
    • Improved path resolution for routes with wildcard segments by refining index-route handling during traversal.
    • Enhanced overall wildcard routing stability and consistency for better routing predictability.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 26, 2025

Walkthrough

This PR refactors wildcard segment handling in the route tree matching algorithm. It replaces a temporary wildcardMatch variable with stack-based exploration, adjusts index-route handling for wildcard nodes, and reorganizes the getNodeMatch control flow. Corresponding test coverage validates that trailing wildcard routes match expected URL paths, addressing issue #5969.

Changes

Cohort / File(s) Summary
Wildcard matching refactor
packages/router-core/src/new-process-route-tree.ts
Replaces temporary wildcardMatch variable with stack-based wildcard candidate exploration; adds index-route handling for wildcard segments; adjusts stack framing (index = partsLength, depth + 1) for wildcard matches; removes previous isFrameMoreSpecific check optimization.
Test coverage for issue #5969
packages/router-core/tests/new-process-route-tree.test.ts
Adds new test suite "edge-case #5969: trailing empty wildcard should match" with three test cases validating basic, layout-route, and index-route scenarios for trailing wildcard matching.

Sequence Diagram(s)

sequenceDiagram
    participant Stack as Exploration Stack
    participant Node as Route Node
    participant Match as Match Result

    rect rgb(240, 248, 255)
    Note over Stack,Match: Old Approach (wildcardMatch variable)
    Stack->>Stack: Create wildcardMatch temp
    Stack->>Node: Evaluate current segment
    alt Static/Dynamic match
        Node->>Match: Return early if bestMatch
    else Wildcard exists
        Stack->>Stack: Update wildcardMatch
        Stack->>Match: Return wildcardMatch on failure
    end
    end

    rect rgb(240, 250, 240)
    Note over Stack,Match: New Approach (Stack-based)
    Stack->>Stack: Process frame from stack
    Stack->>Node: Evaluate segment
    alt Static/Dynamic match
        Node->>Stack: Enqueue matching frame
    else Wildcard exists
        Node->>Stack: Push wildcard frame (index=partsLength, depth+1)
    end
    Stack->>Stack: Continue until stack empty
    Stack->>Match: Return best match found
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Control flow reorganization: The shift from variable-based tracking to stack-based exploration introduces subtle changes in how wildcard candidates are prioritized and explored; requires careful verification that all path combinations are still handled correctly.
  • Stack framing logic: The specific values (index = partsLength, depth = depth + 1) and their interaction with subsequent matching steps need verification to ensure correct traversal semantics.
  • Edge cases: New test suite addresses issue #5969 but review should confirm the fix doesn't inadvertently affect other route matching scenarios, particularly mixed static/dynamic/wildcard combinations.

Possibly related PRs

Suggested labels

package: router-core

Suggested reviewers

  • schiller-manuel
  • nlynzaad

Poem

A wildcard once lost in the stack,
Now finds its way safely back,
With index and depth aligned just right,
Routes match with pure delight,
No more regressions in flight! 🐰✨

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 (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: handling wildcard nodes as regular index nodes, which directly addresses the bug fix for issue #5969 about trailing splat route matching behavior.
Linked Issues check ✅ Passed The PR directly addresses the linked issue #5969 by implementing stack-based wildcard handling and index-route support for wildcard segments, which restores matching behavior for trailing splat routes like /alpha/bravo/$charlie/$.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing wildcard node handling in the route matching logic and adding corresponding test coverage for the reported issue.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-router-core-wildcard-node-is-regular-index-node

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

@nx-cloud
Copy link

nx-cloud bot commented Nov 26, 2025

View your CI Pipeline Execution ↗ for commit c36a8e0

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

☁️ Nx Cloud last updated this comment at 2025-11-26 12:39:38 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 26, 2025

More templates

@tanstack/arktype-adapter

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

@tanstack/directive-functions-plugin

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

@tanstack/eslint-plugin-router

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

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/server-functions-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/zod-adapter

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

commit: c36a8e0

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 (1)
packages/router-core/tests/new-process-route-tree.test.ts (1)

549-565: Good test coverage for the regression fix.

These tests properly verify the expected behavior for issue #5969:

  1. Basic case: Trailing wildcard matches paths with/without trailing content
  2. Layout route interaction: Wildcard takes precedence over non-index layout route
  3. Index route precedence: Actual index routes still win over wildcards

The test naming "should not match" on line 560 might be slightly confusing—consider clarifying that it's the wildcard that should not match (the index route wins).

-      it('with index route (should not match)', () => {
+      it('with index route (index wins over wildcard)', () => {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7ba00c and c36a8e0.

📒 Files selected for processing (2)
  • packages/router-core/src/new-process-route-tree.ts (3 hunks)
  • packages/router-core/tests/new-process-route-tree.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript strict mode with extensive type safety throughout the codebase

Files:

  • packages/router-core/src/new-process-route-tree.ts
  • packages/router-core/tests/new-process-route-tree.test.ts
🧠 Learnings (7)
📓 Common learnings
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.
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: 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: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T00:18:21.258Z
Learning: Applies to packages/solid-router/**/*.{ts,tsx} : Solid Router components and primitives should use the tanstack/solid-router package
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: 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.
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.
📚 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/src/new-process-route-tree.ts
  • packages/router-core/tests/new-process-route-tree.test.ts
📚 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/src/new-process-route-tree.ts
  • packages/router-core/tests/new-process-route-tree.test.ts
📚 Learning: 2025-11-25T00:18:21.258Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T00:18:21.258Z
Learning: Applies to **/src/routes/**/*.{ts,tsx} : Use file-based routing in src/routes/ directories or code-based routing with route definitions

Applied to files:

  • packages/router-core/src/new-process-route-tree.ts
  • packages/router-core/tests/new-process-route-tree.test.ts
📚 Learning: 2025-11-25T00:18:21.258Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T00:18:21.258Z
Learning: Applies to packages/solid-router/**/*.{ts,tsx} : Solid Router components and primitives should use the tanstack/solid-router package

Applied to files:

  • packages/router-core/src/new-process-route-tree.ts
  • packages/router-core/tests/new-process-route-tree.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/new-process-route-tree.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-core/tests/new-process-route-tree.test.ts
🧬 Code graph analysis (1)
packages/router-core/tests/new-process-route-tree.test.ts (1)
packages/router-core/src/new-process-route-tree.ts (1)
  • findRouteMatch (613-635)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Preview
  • GitHub Check: Test
🔇 Additional comments (2)
packages/router-core/src/new-process-route-tree.ts (2)

323-323: LGTM — Key fix for issue #5969.

Setting isIndex = true for wildcard nodes allows trailing splat routes (e.g., /a/$) to match paths both with and without trailing content (e.g., /a/ and /a). This restores the behavior from v1.136.4.

The precedence logic in getNodeMatch ensures actual index routes still win via the early return at line 881 (statics === partsLength && node.isIndex).


891-919: Refactor to stack-based wildcard exploration looks correct.

The change from a dedicated wildcardMatch variable to stack-based exploration is a clean simplification:

  • index: partsLength correctly signals that the wildcard consumes the remaining path
  • depth: depth + 1 properly tracks the wildcard node depth for isFrameMoreSpecific comparison
  • The break after pushing ensures only the first (highest priority) wildcard is considered

This aligns with the existing approach for other segment types and maintains correct prioritization.

@Sheraff Sheraff closed this Nov 26, 2025
@Sheraff Sheraff deleted the fix-router-core-wildcard-node-is-regular-index-node branch November 26, 2025 12:46
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.

Change in behavior for nested routing with trailing splat

2 participants