router-generator: sibling `physical()` subtrees in a virtual route config generate duplicate identifiers
维护者通常 1 天内回复
评估
- 难度
- 2/5
- 预计耗时
- 1-3 小时
- 新手友好度
- 76/100
- Issue 类型
- 缺陷
- 描述清晰度
- 描述清楚
- 活跃度
- 活跃
- 技术栈
- typescript, vite
调研方向
阅读 packages/router-generator/src/filesystem/virtual/getRouteNodes.ts,重点关注 getRouteNodesRecursive 的物理分支,以及它如何构建路由变量名。使用链接中的 StackBlitz 复现问题并运行 npm build;当同级物理子树生成不同的导入标识符和路由标识符(例如 AccountsIndexRouteImport 和 BillingIndexRouteImport),且构建成功时,即表示完成。
由索引模型根据 Issue 内容生成。
描述
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
npm installnpm buildornpm 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 onmain)@tanstack/virtual-file-routes: 1.162.0@tanstack/react-router: 1.170.46vite: 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.
- 主要语言
- TypeScript
- 星标
- 15.3k
- 派生
- 1.9k
- 平均合并
- 1 天 8 小时
- 30 天内合并 PR
- 57
环境准备
在浏览器里用你自己的 GitHub 账号启动这个项目的开发容器。
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
TanStack/router 的其他 Issue
-
docs(router): clarify static title fallback in SPA mode可能已有人在做 @Rohan-Dedhia 于 2 天前认领。 未关闭
难度 1/5 1-3 小时 新手友好度 62/100
维护者通常 1 天内回复
-
router.startViewTransition leaves the ready/finished promises unhandled, so a skipped transition throws an unhandled AbortError可能已有人在做 @unjica 于 14 天前认领。 未关闭
难度 2/5 1-3 小时 新手友好度 74/100
TanStack/router#8776 · 3 条评论 · 2 个 reaction ·
维护者通常 1 天内回复
-
Router devtools crash when search params contain a BigInt (JSON.stringify in history tracking)可能已有人在做 @A-Raphie 于 18 天前认领。 未关闭
难度 2/5 1-3 小时 新手友好度 76/100
维护者通常 1 天内回复
-
docs: `createLink` has no API reference page; 3 how-to guides link to a missing `#createlink` anchor可能已有人在做 @A-Raphie 于 18 天前认领。 未关闭
难度 2/5 1-3 小时 新手友好度 88/100
维护者通常 1 天内回复
-
Inline scripts are duplicated after hydration when the CSP nonce is sent in a header可能已有人在做 @gabriel-le 于 39 天前认领。 未关闭
难度 1/5 1 小时以内 新手友好度 90/100
维护者通常 1 天内回复
相似的 Issue
-
bug
难度 2/5 1-3 小时 新手友好度 88/100
维护者通常 1 天内回复
-
pending close triaging
难度 2/5 1-3 小时 新手友好度 78/100
anomalyco/opencode#62582 · 1 条评论 ·
维护者通常 1 天内回复
-
feature request
难度 2/5 1-3 小时 新手友好度 72/100
callstack/react-native-paper#5189 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 68/100
live-codes/livecodes#1079 · 1 条评论 ·
维护者通常 1 天内回复
-
Add: ESP 2 RO未关闭approved check:passed streams:add
难度 2/5 1-3 小时 新手友好度 66/100
维护者通常 1 天内回复