router-generator: sibling `physical()` subtrees in a virtual route config generate duplicate identifiers

Đang mở Phù hợp với người mới
#8,879 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

@karlshea đang làm issue này rồi.

Từ ngày 5/11/2026.

  • #8880 của @karlshea — đang mở

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
76/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
typescript, vite
Lĩnh vực
backend-api-design

Hướng nghiên cứu

Đọc packages/router-generator/src/filesystem/virtual/getRouteNodes.ts, tập trung vào nhánh vật lý của getRouteNodesRecursive và cách nhánh này tạo tên biến route. Tái hiện vấn đề bằng StackBlitz được liên kết và chạy npm build; công việc hoàn tất khi các cây con vật lý cùng cấp tạo ra các định danh import và route riêng biệt, chẳng hạn AccountsIndexRouteImport và BillingIndexRouteImport, đồng thời quá trình build thành công.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

Which project does this relate to?

Router

Describe the bug

When a virtual route config mounts two physical() directories under different paths, route files with the same path relative to their mounted directory get the same generated variable name. The generated routeTree.gen.ts then declares the same import binding and the same const twice, so the Vite build fails with "Identifier IndexRouteImport has already been declared".

Route ids and paths are generated correctly (/accounts/, /billing/); only the variable names collide.

Complete minimal reproducer

https://stackblitz.com/edit/github-eufsm66w?file=vite.config.js

Steps to Reproduce the Bug
  1. npm install
  2. npm build or npm run dev
[PARSE_ERROR] Identifier `IndexRouteImport` has already been declared
    ╭─[ src/routeTree.gen.ts:12:19 ]
    │
 12 │ import { Route as IndexRouteImport } from './features/accounts/routes/index'
    │                   ────────┬───────  
    │                           ╰───────── `IndexRouteImport` has already been declared here
 13 │ import { Route as IndexRouteImport } from './features/billing/routes/index'
    │                   ────────┬───────  
    │                           ╰───────── It can not be redeclared here
────╯

Generated src/routeTree.gen.ts:

import { Route as rootRouteImport } from './routes/__root'
import { Route as IndexRouteImport } from './features/accounts/routes/index'
import { Route as IndexRouteImport } from './features/billing/routes/index'

const IndexRoute = IndexRouteImport.update({
  id: '/accounts/',
  path: '/accounts/',
  getParentRoute: () => rootRouteImport,
} as any)
const IndexRoute = IndexRouteImport.update({
  id: '/billing/',
  path: '/billing/',
  getParentRoute: () => rootRouteImport,
} as any)
Expected behavior

Distinct names derived from the route path, as file-based routing produces for routes/accounts/index.tsx and routes/billing/index.tsx: AccountsIndexRouteImport / BillingIndexRouteImport.

Screenshots or Videos

No response

Platform
  • OS: macOS
  • Node: v24.20.0
  • @tanstack/router-plugin: 1.168.48
  • @tanstack/router-generator: 1.167.46 (same code on main)
  • @tanstack/virtual-file-routes: 1.162.0
  • @tanstack/react-router: 1.170.46
  • vite: 8.3.5
Additional context

Cause

packages/router-generator/src/filesystem/virtual/getRouteNodes.ts, physical branch of getRouteNodesRecursive. For each file in the mounted directory:


const pathPrefix = cleanPath(`${parent?.routePath ?? ''}${routePathPrefix}`) 
// '/accounts' + '' = '/accounts'

const routePath = cleanPath(`${pathPrefix}${subtreeNode.routePath}`)
// '/accounts' + '/' = '/accounts/' (correct)

subtreeNode.variableName = routePathToVariable(
  `${routePathPrefix}/${removeExt(subtreeNode.filePath)}`,
)
// '' + '/index' = 'Index' (collides)

The route id is built from pathPrefix, which includes the parent route's path. The variable name is built from routePathPrefix, the prefix given to physical(), which does not. Both mounts use '', so both index files are named Index.

Fix: build the name from pathPrefix as well.

subtreeNode.variableName = routePathToVariable(
  `${pathPrefix}/${removeExt(subtreeNode.filePath)}`,
)

With that change the repro builds and generates AccountsIndexRouteImport and BillingIndexRouteImport.

Ngôn ngữ chính
TypeScript
Star
15.3k
Fork
1.9k
Merge trung bình
1 ngày 8 giờ
Pull request đã merge (30 ngày)
57

Chuẩn bị môi trường

Mở trong Codespaces

Khởi chạy dev container của dự án ngay trên trình duyệt, bằng tài khoản GitHub của bạn.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của TanStack/router

Tất cả issue của TanStack/router

Issue tương tự

Thêm issue về TypeScript

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.