Skip to content

fix: Fix escaped underscore index route generation#7453

Merged
schiller-manuel merged 1 commit into
mainfrom
fix-generator-escaped-index
May 20, 2026
Merged

fix: Fix escaped underscore index route generation#7453
schiller-manuel merged 1 commit into
mainfrom
fix-generator-escaped-index

Conversation

@schiller-manuel
Copy link
Copy Markdown
Collaborator

@schiller-manuel schiller-manuel commented May 20, 2026

Summary by CodeRabbit

  • Bug Fixes
    • Fixed preservation of escaped underscore characters in route segments when the router generator creates index routes across physical and virtual route trees, including support for pathless layouts and virtual subtree configurations.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f1282808-e5d4-4230-befc-6940ec5da3d2

📥 Commits

Reviewing files that changed from the base of the PR and between 35a7d9c and 93138b0.

📒 Files selected for processing (44)
  • .changeset/bright-bats-fold.md
  • packages/router-generator/src/filesystem/physical/getRouteNodes.ts
  • packages/router-generator/src/filesystem/virtual/getRouteNodes.ts
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routeTree.snapshot.ts
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/[_]root-index/index.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/[__root-index]/index.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/_layout/[_]nested-index/$id.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/_layout/[_]nested-index/index.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/_layout/[__double-index]/$id.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/_layout/[__double-index]/index.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/_layout/_nested/[_]deep-index/index.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/_layout/nested-trailing[_]/index.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routeTree.snapshot.ts
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes.ts
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/deep-index.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/double-id.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/double-index.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/escaped-id.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/escaped-index.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/inner-layout.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/nested-id.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/nested-index.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/root-id.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/root-index.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/trail-index.tsx
  • packages/router-generator/tests/generator/virtual-physical-pathless-layout-literal-index/routeTree.snapshot.ts
  • packages/router-generator/tests/generator/virtual-physical-pathless-layout-literal-index/routes.ts
  • packages/router-generator/tests/generator/virtual-physical-pathless-layout-literal-index/routes/__root.tsx
  • packages/router-generator/tests/generator/virtual-physical-pathless-layout-literal-index/routes/layout.tsx
  • packages/router-generator/tests/generator/virtual-physical-pathless-layout-literal-index/routes/physical-routes/[_]nested/$id.tsx
  • packages/router-generator/tests/generator/virtual-physical-pathless-layout-literal-index/routes/physical-routes/[_]nested/index.tsx
  • packages/router-generator/tests/generator/virtual-physical-pathless-layout-literal-index/tsr.config.json
  • packages/router-generator/tests/generator/virtual-physical-prefix-escaped-underscore/routeTree.snapshot.ts
  • packages/router-generator/tests/generator/virtual-physical-prefix-escaped-underscore/routes.ts
  • packages/router-generator/tests/generator/virtual-physical-prefix-escaped-underscore/routes/__root.tsx
  • packages/router-generator/tests/generator/virtual-physical-prefix-escaped-underscore/routes/physical-routes/$id.tsx
  • packages/router-generator/tests/generator/virtual-physical-prefix-escaped-underscore/routes/physical-routes/index.tsx
  • packages/router-generator/tests/generator/virtual-physical-prefix-escaped-underscore/tsr.config.json
  • packages/router-generator/tests/generator/virtual-subtree-in-escaped-physical-directory/routeTree.snapshot.ts
  • packages/router-generator/tests/generator/virtual-subtree-in-escaped-physical-directory/routes/[_]area/__virtual.ts
  • packages/router-generator/tests/generator/virtual-subtree-in-escaped-physical-directory/routes/[_]area/child.tsx
  • packages/router-generator/tests/generator/virtual-subtree-in-escaped-physical-directory/routes/[_]area/index.tsx
  • packages/router-generator/tests/generator/virtual-subtree-in-escaped-physical-directory/routes/__root.tsx
  • packages/router-generator/tests/utils.test.ts

📝 Walkthrough

Walkthrough

This PR fixes escaped underscore segment preservation during index route generation across physical, virtual, and hybrid route trees. The core changes refactor prefix/path computation in route node generators to use determineInitialRoutePath and propagate metadata consistently. Test fixtures and comprehensive test coverage validate the fix across pathless layouts, virtual subtrees, and hybrid scenarios.

Changes

Escape-handling route generation logic

Layer / File(s) Summary
Core escape-handling logic refactor
packages/router-generator/src/filesystem/physical/getRouteNodes.ts, packages/router-generator/src/filesystem/virtual/getRouteNodes.ts
Virtual-subtree embedding and index token handling now compute prefixPath/originalPrefixPath via determineInitialRoutePath and rebuild route metadata using createRoutePathSegmentMetadata. Index nodes now return originalRoutePath and propagate parent metadata. A conditional that forced originalRoutePath to '/' for index tokens was removed.
Patch changelog entry
.changeset/bright-bats-fold.md
Changeset documents the fix for preserving escaped underscore segments during index route generation across physical/virtual trees, pathless layouts, physical() prefixes, and __virtual.ts subtrees.

Physical pathless layout test fixture

Layer / File(s) Summary
Physical route tree snapshot and wiring
packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routeTree.snapshot.ts
Route tree snapshot expands imports and route constants for root-index and layout nested routes with escaped underscores. Type interfaces (FileRoutesByFullPath, FileRoutesByTo, FileRoutesById, FileRouteTypes) gain entries for new routes. Module augmentation adds FileRoutesByPath entries including /_root-index/ and /_layout/... nested paths with trailing underscores and $id parameters. Route children wiring links new routes to the tree.
Physical route modules for escaped underscore cases
packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/*
Route files define TanStack Router file routes for physical paths including escaped root-index variants (/_root-index/, /__root-index/), layout-nested routes with single and double underscores (_nested-index, __double-index), trailing underscores (nested-trailing_), and parametric $id variants.

Virtual pathless layout test fixture

Layer / File(s) Summary
Virtual route tree snapshot and wiring
packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routeTree.snapshot.ts
Route tree snapshot adds imports and route constants for root-id, root-index, multiple layout index/id variants, and an inner layout with deep-index child. Type maps expand to include escaped variants (_nested-index, _escaped-index, __double-index, trail_). Module augmentation extends FileRoutesByPath for new root/layout routes and introduces inner layout children wiring.
Virtual route configuration and modules
packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes.ts, routes/*.tsx
Configuration wraps route targets in arrays with index() helper. Route modules export TanStack Router file routes for root-index, nested variants with escaped/double underscores, trail-index, and an inner layout structure with a deep-index child route.

Virtual-physical hybrid test fixtures

Layer / File(s) Summary
Literal index in pathless layout fixture
packages/router-generator/tests/generator/virtual-physical-pathless-layout-literal-index/*
Fixture demonstrates literal index handling with a virtual layout route mapped to physical routes. Includes route tree snapshot, configuration, root route, layout route, and physical nested/parametric route modules under the layout.
Escaped physical prefix fixture
packages/router-generator/tests/generator/virtual-physical-prefix-escaped-underscore/*
Fixture shows virtual root route routing to physical directory with escaped underscore prefix (/[_]api). Includes route tree snapshot, virtual configuration with physical('/[_]api', 'physical-routes'), root route, and API index/parametric route modules.
Virtual subtree in escaped physical directory fixture
packages/router-generator/tests/generator/virtual-subtree-in-escaped-physical-directory/*
Fixture nests a virtual subtree configuration (__virtual.ts) inside an escaped physical directory ([_]area). Includes route tree snapshot, virtual subtree config defining index and child routes, and route modules for area index/child plus root route.

Escape-detection test coverage

Layer / File(s) Summary
Test imports and new escape handling coverage
packages/router-generator/tests/utils.test.ts
Test utilities now import createLiteralRoutePathSegmentMetadata and add coverage for inferFullPath with index-route scenarios, createRoutePathSegmentMetadata with escaped leading/trailing underscore preservation and multiple literal underscore tracking, and removeLayoutSegmentsAndUnderscoresWithEscape with mixed escape scenarios.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • TanStack/router#6250: Updates to escape-detection logic in getRouteNodes.ts for handling escaped underscore route segments in virtual subtrees.
  • TanStack/router#6156: Adjusts originalRoutePath/prefixPath computation to preserve escaped underscore segments in route node generation.
  • TanStack/router#7408: Updates getRouteNodes.ts to refactor prefix computation and route-segment metadata for escaped underscore preservation under pathless layouts.

Suggested labels

package: router-generator

Suggested reviewers

  • nlynzaad
  • Sheraff

Poem

🐰 Underscores once slipped away, but now they stay,
Escaped and doubled, path by path aligned,
Through physical and virtual trees they play,
With metadata precision, no escape left behind!
Route routes route right—let chaos unwind! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: Fix escaped underscore index route generation' clearly and specifically identifies the main change: fixing the generation of index routes with escaped underscores.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-generator-escaped-index

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

@nx-cloud
Copy link
Copy Markdown
Contributor

nx-cloud Bot commented May 20, 2026

View your CI Pipeline Execution ↗ for commit 93138b0

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ✅ Succeeded 1m View ↗

☁️ Nx Cloud last updated this comment at 2026-05-20 22:03:12 UTC

@github-actions
Copy link
Copy Markdown
Contributor

🚀 Changeset Version Preview

1 package(s) bumped directly, 8 bumped as dependents.

🟩 Patch bumps

Package Version Reason
@tanstack/router-generator 1.167.7 → 1.167.8 Changeset
@tanstack/react-start 1.168.8 → 1.168.9 Dependent
@tanstack/react-start-rsc 0.1.8 → 0.1.9 Dependent
@tanstack/router-cli 1.167.7 → 1.167.8 Dependent
@tanstack/router-plugin 1.168.8 → 1.168.9 Dependent
@tanstack/router-vite-plugin 1.167.8 → 1.167.9 Dependent
@tanstack/solid-start 1.168.8 → 1.168.9 Dependent
@tanstack/start-plugin-core 1.171.1 → 1.171.2 Dependent
@tanstack/vue-start 1.168.8 → 1.168.9 Dependent

@github-actions
Copy link
Copy Markdown
Contributor

Bundle Size Benchmarks

  • Commit: b8403dbdc0c3
  • Measured at: 2026-05-20T21:42:15.868Z
  • Baseline source: history:35a7d9cd5a0e
  • Dashboard: bundle-size history
Scenario Current (gzip) Delta vs baseline Initial gzip Raw Brotli Trend
react-router.minimal 87.30 KiB 0 B (0.00%) 87.16 KiB 274.02 KiB 75.93 KiB █████████▁▁
react-router.full 90.82 KiB 0 B (0.00%) 90.68 KiB 285.51 KiB 78.89 KiB ▆▆▆▆▆▆▆▆█▁▁
solid-router.minimal 35.53 KiB 0 B (0.00%) 35.41 KiB 106.33 KiB 31.98 KiB ▅▅▅▅▅████▁▁
solid-router.full 40.27 KiB 0 B (0.00%) 40.15 KiB 120.52 KiB 36.23 KiB ▁▁▁▁▁▂▂▂▅██
vue-router.minimal 53.32 KiB 0 B (0.00%) 53.20 KiB 151.46 KiB 47.92 KiB ████████▁▆▆
vue-router.full 58.45 KiB 0 B (0.00%) 58.32 KiB 167.62 KiB 52.37 KiB ████████▁██
react-start.minimal 102.02 KiB 0 B (0.00%) 101.89 KiB 322.47 KiB 88.26 KiB ▁▁▁▁▁▅▅▅▄██
react-start.deferred-hydration 103.07 KiB 0 B (0.00%) 102.19 KiB 324.11 KiB 89.26 KiB ████▁▁
react-start.full 105.42 KiB 0 B (0.00%) 105.28 KiB 332.78 KiB 91.04 KiB ▆▆▆▆▆████▁▁
react-start.rsbuild.minimal 99.64 KiB 0 B (0.00%) 99.46 KiB 316.93 KiB 85.78 KiB ▆▆▆▆▆▆▆▆▁██
react-start.rsbuild.full 102.93 KiB 0 B (0.00%) 102.76 KiB 327.34 KiB 88.52 KiB ████████▁▃▃
solid-start.minimal 49.67 KiB 0 B (0.00%) 49.54 KiB 152.45 KiB 43.83 KiB ▁▁▁▁▁███▇▄▄
solid-start.deferred-hydration 53.74 KiB 0 B (0.00%) 50.40 KiB 160.99 KiB 47.75 KiB ███▇▁▁
solid-start.full 55.46 KiB 0 B (0.00%) 55.33 KiB 169.34 KiB 48.83 KiB ▁▁▁▁▁███▆▆▆

Current gzip tracks all emitted client JS chunks. Initial gzip tracks only the entry/import graph. Trend sparkline is historical current gzip ending with this PR measurement; lower is better.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 20, 2026

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/@tanstack/arktype-adapter@7453

@tanstack/eslint-plugin-router

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

@tanstack/eslint-plugin-start

npm i https://pkg.pr.new/@tanstack/eslint-plugin-start@7453

@tanstack/history

npm i https://pkg.pr.new/@tanstack/history@7453

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

npm i https://pkg.pr.new/@tanstack/react-start@7453

@tanstack/react-start-client

npm i https://pkg.pr.new/@tanstack/react-start-client@7453

@tanstack/react-start-rsc

npm i https://pkg.pr.new/@tanstack/react-start-rsc@7453

@tanstack/react-start-server

npm i https://pkg.pr.new/@tanstack/react-start-server@7453

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

npm i https://pkg.pr.new/@tanstack/solid-start@7453

@tanstack/solid-start-client

npm i https://pkg.pr.new/@tanstack/solid-start-client@7453

@tanstack/solid-start-server

npm i https://pkg.pr.new/@tanstack/solid-start-server@7453

@tanstack/start-client-core

npm i https://pkg.pr.new/@tanstack/start-client-core@7453

@tanstack/start-fn-stubs

npm i https://pkg.pr.new/@tanstack/start-fn-stubs@7453

@tanstack/start-plugin-core

npm i https://pkg.pr.new/@tanstack/start-plugin-core@7453

@tanstack/start-server-core

npm i https://pkg.pr.new/@tanstack/start-server-core@7453

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

npm i https://pkg.pr.new/@tanstack/start-storage-context@7453

@tanstack/valibot-adapter

npm i https://pkg.pr.new/@tanstack/valibot-adapter@7453

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/@tanstack/virtual-file-routes@7453

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

npm i https://pkg.pr.new/@tanstack/vue-start@7453

@tanstack/vue-start-client

npm i https://pkg.pr.new/@tanstack/vue-start-client@7453

@tanstack/vue-start-server

npm i https://pkg.pr.new/@tanstack/vue-start-server@7453

@tanstack/zod-adapter

npm i https://pkg.pr.new/@tanstack/zod-adapter@7453

commit: 93138b0

Copy link
Copy Markdown
Contributor

@nx-cloud nx-cloud Bot left a comment

Choose a reason for hiding this comment

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

Important

At least one additional CI pipeline execution has run since the conclusion below was written and it may no longer be applicable.

Nx Cloud has identified a possible root cause for your failed CI:

This CI failure appears to be related to the environment or external dependencies rather than your code changes.

No code changes were suggested for this issue.

Trigger a rerun:

Rerun CI

Nx Cloud View detailed reasoning on Nx Cloud ↗


🎓 Learn more about Self-Healing CI on nx.dev

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 20, 2026

Merging this PR will not alter performance

✅ 5 untouched benchmarks
⏩ 1 skipped benchmark1


Comparing fix-generator-escaped-index (93138b0) with main (35a7d9c)

Open in CodSpeed

Footnotes

  1. 1 benchmark was skipped, so the baseline result was used instead. If it was deleted from the codebase, click here and archive it to remove it from the performance reports.

@schiller-manuel schiller-manuel merged commit 7df0d02 into main May 20, 2026
22 of 23 checks passed
@schiller-manuel schiller-manuel deleted the fix-generator-escaped-index branch May 20, 2026 22:03
@github-actions github-actions Bot mentioned this pull request May 20, 2026
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.

1 participant