-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
fix(route-core, router-generator): resolve non-nested routes correctly in generator #5402
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
View your CI Pipeline Execution ↗ for commit 178a0a1
☁️ Nx Cloud last updated this comment at |
WalkthroughAdds an experimental nonNestedRoutes feature: docs and e2e harnesses (React & Solid) support a MODE-driven flag; generator and core parsing are updated to surface originalRoutePath and handle underscore rules; many generated snapshots/tests and path parsing refactor centralize behavior via parsePathname. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Dev
participant PW as Playwright
participant Vite as Vite
participant Plugin as tanstackRouter plugin
participant App
Dev->>PW: pnpm run test:e2e(:nonnested|:default)
PW->>PW: resolve mode via env/useExperimentalNonNestedRoutes
PW->>Vite: start server with MODE/VITE_MODE/PORT envs
Vite->>Plugin: init plugin with experimental.nonNestedRoutes=(MODE==='nonnested')
Plugin->>App: build/serve routes honoring nonNested flag
Dev->>App: run e2e assertions (branch on flag)
sequenceDiagram
autonumber
participant Gen as Generator
participant FS as getRouteNodes
participant Util as utils.determineInitialRoutePath
participant Node as RouteNode
Gen->>FS: read files (pass config.experimental.nonNestedRoutes)
FS->>Util: determineInitialRoutePath(filePathNoExt, config)
Util-->>FS: { routePath, originalRoutePath, isExperimentalNonNestedRoute }
FS->>Node: emit RouteNode with originalRoutePath & _isExperimentalNonNestedRoute
Gen->>Util: hasParentRoute(routes, node, routePath, originalRoutePath, config)
Util-->>Gen: resolved parent (non-nested aware)
Gen->>Gen: generate snapshots/types using config-driven underscore handling
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used🧠 Learnings (4)📓 Common learnings📚 Learning: 2025-10-08T08:11:47.088ZApplied to files:
📚 Learning: 2025-10-01T18:31:35.420ZApplied to files:
📚 Learning: 2025-09-23T17:36:12.598ZApplied to files:
⏰ 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). (1)
🔇 Additional comments (2)
Comment |
More templates
@tanstack/arktype-adapter
@tanstack/directive-functions-plugin
@tanstack/eslint-plugin-router
@tanstack/history
@tanstack/nitro-v2-vite-plugin
@tanstack/react-router
@tanstack/react-router-devtools
@tanstack/react-router-ssr-query
@tanstack/react-start
@tanstack/react-start-client
@tanstack/react-start-server
@tanstack/router-cli
@tanstack/router-core
@tanstack/router-devtools
@tanstack/router-devtools-core
@tanstack/router-generator
@tanstack/router-plugin
@tanstack/router-ssr-query-core
@tanstack/router-utils
@tanstack/router-vite-plugin
@tanstack/server-functions-plugin
@tanstack/solid-router
@tanstack/solid-router-devtools
@tanstack/solid-router-ssr-query
@tanstack/solid-start
@tanstack/solid-start-client
@tanstack/solid-start-server
@tanstack/start-client-core
@tanstack/start-plugin-core
@tanstack/start-server-core
@tanstack/start-static-server-functions
@tanstack/start-storage-context
@tanstack/valibot-adapter
@tanstack/virtual-file-routes
@tanstack/zod-adapter
commit: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
packages/router-generator/src/generator.ts (1)
1203-1224: Propagate originalRoutePath and flag to nested hasParentRoute call.The second hasParentRoute invocation omits experimental context and originalRoutePath, risking incorrect parent resolution for experimental non-nested routes.
Apply this diff:
- const possibleParentRoute = hasParentRoute( - parentRoute.children, - node, - node.routePath, - ) + const possibleParentRoute = hasParentRoute( + parentRoute.children, + node, + node.routePath, + useExperimentalNonNestedRoutes, + node.originalRoutePath, + )packages/router-core/src/path.ts (2)
344-350: Trailing-slash handling assigns the wrong substring (harmless now, but brittle).
pathname = pathname.substring(1)removes the first char, not the trailing slash. Sincepathnameisn’t used after this, it doesn’t break today, but it’s incorrect. Simplify by removing the assignment or fix it.- if (pathname.slice(-1) === '/') { - pathname = pathname.substring(1) - segments.push({ + if (pathname.slice(-1) === '/') { + segments.push({ type: SEGMENT_TYPE_PATHNAME, value: '/', }) }
441-447: leaveParams uses the wrong key; values will never append.
encodeParam(segment.value)looks up params['$foo'] instead of params['foo'].- if (leaveParams) { - const value = encodeParam(segment.value) - return `${segmentPrefix}${segment.value}${value ?? ''}${segmentSuffix}` - } - return `${segmentPrefix}${encodeParam(key) ?? 'undefined'}${segmentSuffix}` + if (leaveParams) { + const value = encodeParam(key) + return `${segmentPrefix}${segment.value}${value ?? ''}${segmentSuffix}` + } + return `${segmentPrefix}${encodeParam(key) ?? 'undefined'}${segmentSuffix}`
🧹 Nitpick comments (20)
packages/router-generator/tests/generator.test.ts (1)
249-264: Consider flatMap for clarity.The
reducelogic is correct but aflatMapor simple loop might be more readable:-const testCases = folderNames.reduce( - (accum: Array<{ folderName: string; nonNested: boolean }>, folderName) => { - accum.push({ - folderName, - nonNested: true, - }) - - accum.push({ - folderName, - nonNested: false, - }) - - return accum - }, - [], -) +const testCases = folderNames.flatMap(folderName => [ + { folderName, nonNested: true }, + { folderName, nonNested: false }, +])e2e/react-router/basic-file-based/vite.config.js (1)
7-15: Harden the flag check to also read VITE_MODE (and optionally use Vite’s mode).Your gating works; consider also checking VITE_MODE to align with the test env and make it more robust. Optional: switch to defineConfig(({ mode }) => ...) and compare mode directly.
Apply this minimal change:
experimental: { - nonNestedRoutes: process.env.MODE === 'nonnested', + nonNestedRoutes: + process.env.MODE === 'nonnested' || + process.env.VITE_MODE === 'nonnested', },Alternative (optional) using Vite’s mode:
export default defineConfig(({ mode }) => ({ plugins: [ tanstackRouter({ target: 'react', experimental: { nonNestedRoutes: mode === 'nonnested' }, }), react(), ], }))e2e/solid-router/basic-file-based/src/routes/posts_.$postId.edit.tsx (1)
8-11: Gate both getRouteApi and useParams ‘from’ with the same routeId.Right now, getRouteApi toggles the id, but useParams({ from }) below stays at '/posts_/$postId/edit'. In non-nested mode, that likely mismatches. Define one routeId and reuse it for both.
-const api = getRouteApi( - // @ts-expect-error path is updated with new Experimental Non Nested Paths to not include the trailing underscore - `/${useExperimentalNonNestedRoutes ? 'posts' : 'posts_'}/$postId/edit`, -) +const routeId = + `/${useExperimentalNonNestedRoutes ? 'posts' : 'posts_'}/$postId/edit` +// @ts-expect-error routeId differs under experimental non-nested paths +const api = getRouteApi(routeId)Also update the hook (outside this hunk) to match:
// @ts-expect-error routeId differs under experimental non-nested paths const paramsViaHook = useParams({ from: routeId })e2e/react-router/basic-file-based/package.json (1)
7-17: Make scripts cross‑platform (env vars and cleanup).Current scripts use POSIX env and rm; these break on Windows shells. Prefer cross-env and rimraf.
- "dev:nonnested": "MODE=nonnested VITE_MODE=nonnested vite --port 3000", + "dev:nonnested": "cross-env MODE=nonnested VITE_MODE=nonnested vite --port 3000", - "build:nonnested": "MODE=nonnested VITE_MODE=nonnested vite build && tsc --noEmit", + "build:nonnested": "cross-env MODE=nonnested VITE_MODE=nonnested vite build && tsc --noEmit", - "serve:nonnested": "MODE=nonnested VITE_MODE=nonnested vite preview", + "serve:nonnested": "cross-env MODE=nonnested VITE_MODE=nonnested vite preview", - "start:nonnested": "MODE=nonnested VITE_MODE=nonnested vite", + "start:nonnested": "cross-env MODE=nonnested VITE_MODE=nonnested vite", - "test:e2e:default": "rm -rf port*.txt; playwright test --project=chromium", + "test:e2e:default": "rimraf port*.txt && playwright test --project=chromium", - "test:e2e:nonnested": "rm -rf port*.txt; MODE=nonnested playwright test --project=chromium" + "test:e2e:nonnested": "rimraf port*.txt && cross-env MODE=nonnested playwright test --project=chromium"Add devDependencies:
- cross-env
- rimraf
packages/router-generator/src/types.ts (1)
7-7: Document new fields’ intent and stability.Add brief JSDoc to signal usage and experimental status to downstream consumers.
- originalRoutePath?: string + /** Unmodified path as discovered from the filesystem before normalization/escaping */ + originalRoutePath?: string ... - _isExperimentalNonNestedRoute?: boolean + /** @experimental Marker for routes detected as non-nested (subject to change) */ + _isExperimentalNonNestedRoute?: booleanAlso applies to: 17-17
packages/router-generator/tests/generator/nested-verboseFileRoutes-true/tests.nonnested.test-d.ts (1)
1-539: Deduplicate repeated string literals in unions to reduce noiseSeveral unions contain duplicates (e.g., '/posts/$postId' listed twice). It’s harmless but increases churn in snapshots and cognitive load. If these are generated, consider deduping before emit.
e2e/solid-router/basic-file-based/tests/params.spec.ts (1)
170-177: Stabilize after navigation to reduce flakesAfter clicking foo2Bar2Link and waiting for URL, add a networkidle wait before reading DOM.
await foo2Bar2Link.click() await page.waitForURL('/params-ps/non-nested/foo2/bar2') + await page.waitForLoadState('networkidle') const pagePathname2 = new URL(page.url()).pathnamepackages/router-generator/tests/deny-route-group-config.test.ts (2)
26-31: Preserve other experimental flags when setting nonNestedRoutesDeep-merge experimental to avoid clobbering other flags passed via inlineConfig.
- experimental: { - nonNestedRoutes: nonNested, - }, + experimental: { + ...(rest as any)?.experimental, + nonNestedRoutes: nonNested, + },
16-20: Use path utilities instead of string concatenation for generatedRouteTreeString concat works but path.join is safer and clearer.
- const { - generatedRouteTree = `/routeTree.${nonNested ? 'nonnested.' : ''}gen.ts`, - ...rest - } = inlineConfig + const { + generatedRouteTree = `routeTree.${nonNested ? 'nonnested.' : ''}gen.ts`, + ...rest + } = inlineConfig const dir = makeFolderDir(folder) const config = getConfig({ disableLogging: true, routesDirectory: dir, - generatedRouteTree: dir + generatedRouteTree, + generatedRouteTree: join(dir, generatedRouteTree),Also applies to: 23-26
e2e/solid-router/basic-file-based/package.json (2)
15-18: Ensure Vite sees the non-nested mode during e2e testsAlso set VITE_MODE for the Playwright run, so any Vite web server or plugin config keyed off VITE_MODE receives it.
- "test:e2e": "pnpm run test:e2e:nonnested && pnpm run test:e2e:default", + "test:e2e": "pnpm run test:e2e:nonnested && pnpm run test:e2e:default", "test:e2e:default": "rm -rf port*.txt; playwright test --project=chromium", - "test:e2e:nonnested": "rm -rf port*.txt; MODE=nonnested playwright test --project=chromium" + "test:e2e:nonnested": "rm -rf port*.txt; MODE=nonnested VITE_MODE=nonnested playwright test --project=chromium"
7-8: Optional: use cross-env for cross‑platform env vars in scriptsSetting env vars inline isn’t portable on Windows shells. Consider cross-env for dev/build/serve/start/test scripts.
- "dev:nonnested": "MODE=nonnested VITE_MODE=nonnested vite --port 3000", + "dev:nonnested": "cross-env MODE=nonnested VITE_MODE=nonnested vite --port 3000", - "build:nonnested": "MODE=nonnested VITE_MODE=nonnested vite build && tsc --noEmit", + "build:nonnested": "cross-env MODE=nonnested VITE_MODE=nonnested vite build && tsc --noEmit", - "serve:nonnested": "MODE=nonnested VITE_MODE=nonnested vite preview", + "serve:nonnested": "cross-env MODE=nonnested VITE_MODE=nonnested vite preview", - "start:nonnested": "MODE=nonnested VITE_MODE=nonnested vite", + "start:nonnested": "cross-env MODE=nonnested VITE_MODE=nonnested vite", - "test:e2e:nonnested": "rm -rf port*.txt; MODE=nonnested VITE_MODE=nonnested playwright test --project=chromium" + "test:e2e:nonnested": "rm -rf port*.txt; cross-env MODE=nonnested VITE_MODE=nonnested playwright test --project=chromium"Add devDependency:
"devDependencies": { + "cross-env": "^7.0.3",Also applies to: 10-15
packages/router-generator/src/generator.ts (1)
677-737: Use the locally merged config instead of this.config for inference helpers.buildRouteTree merges config into a local variable but passes this.config to:
- createRouteNodesByFullPath
- createRouteNodesByTo
- buildFileRoutesByPathInterface
- unions (fullPaths/to) derived from the above
This can desync behavior when opts.config is provided and is also inconsistent with surrounding code using the local config.
Apply this diff:
-${[...createRouteNodesByFullPath(acc.routeNodes, this.config).entries()] +${[...createRouteNodesByFullPath(acc.routeNodes, config).entries()]-${[...createRouteNodesByTo(acc.routeNodes, this.config).entries()] +${[...createRouteNodesByTo(acc.routeNodes, config).entries()]- ...createRouteNodesByFullPath( - acc.routeNodes, - this.config, - ).keys(), + ...createRouteNodesByFullPath( + acc.routeNodes, + config, + ).keys(),- ? [...createRouteNodesByTo(acc.routeNodes, this.config).keys()] + ? [...createRouteNodesByTo(acc.routeNodes, config).keys()]- config: this.config, + config,packages/router-generator/src/filesystem/physical/getRouteNodes.ts (1)
234-237: Optional: drop unused experimental from getRouteMeta signature.experimental isn’t used in getRouteMeta. Consider removing it (and corresponding calls) to reduce noise, unless reserved for near-term use.
packages/router-core/src/path.ts (2)
204-215: parsePathname cache ignores basePathValues; future divergence could return stale segments.If
basePathValueslater alters parsing, caching solely bypathnamewill mix results across modes.
- Either incorporate
basePathValuesinto the cache key, or maintain separate caches per mode.- If
basePathValuesis guaranteed no-op, add a comment asserting that to prevent misuse.Example (conceptual):
- const cached = cache?.get(pathname) + const cacheKey = basePathValues ? `${pathname}::base` : pathname + const cached = cache?.get(cacheKey) if (cached) return cached - const parsed = baseParsePathname(pathname, basePathValues) - cache?.set(pathname, parsed) + const parsed = baseParsePathname(pathname, basePathValues) + cache?.set(cacheKey, parsed)
567-586: Simplify prefix/suffix checks for wildcard with braces.Using the
'in'operator is redundant here and can be misleading. Just check truthiness of the prefix/suffix strings.- if ('prefixSegment' in routeSegment) { - if (!baseValue.startsWith(prefix)) { - return false - } - } - if ('suffixSegment' in routeSegment) { - if ( - !baseSegments[baseSegments.length - 1]?.value.endsWith(suffix) - ) { - return false - } - } + if (prefix && !baseValue.startsWith(prefix)) { + return false + } + if (suffix && !baseSegments[baseSegments.length - 1]?.value.endsWith(suffix)) { + return false + }packages/router-generator/tests/generator/only-root/routeTree.nonnested.generated.snapshot.ts (1)
13-15: Empty interfaces flagged by Biome; prefer type aliases or ignore via Biome.To satisfy
lint/suspicious/noEmptyInterface, switch to type aliases (no behavior change for snapshots).-export interface FileRoutesByFullPath {} -export interface FileRoutesByTo {} +export type FileRoutesByFullPath = {} +export type FileRoutesByTo = {} @@ -export interface RootRouteChildren {} +export type RootRouteChildren = {}Alternatively, annotate the file to ignore Biome for generated snapshots:
/* biome-ignore file: generated snapshot */Also applies to: 26-26
packages/router-generator/src/utils.ts (4)
196-210: Escape routeToken in dynamic regex and remove dead code in underscore helpers
- Prevent unintended regex behavior by escaping routeToken-derived fragments.
- Remove unused variables and fix ineffective early-return checks.
Apply this diff:
export function removeLeadingUnderscores(s: string, routeToken: string) { - const parts = s.split('/') - - if (parts.length === 0) return s + if (!s) return s - - const routeTokenToExclude = - routeToken[0] === '_' ? routeToken.slice(1) : routeToken - - const leadingUnderscoreRegex = - routeToken[0] === '_' - ? new RegExp(`(?<=^|\\/)_(?!${routeTokenToExclude})`, 'g') - : new RegExp(`(?<=^|\\/)_`, 'g') - - return s.replaceAll(leadingUnderscoreRegex, '') + const tokenStartsWithUnderscore = routeToken.startsWith('_') + const routeTokenToExclude = tokenStartsWithUnderscore + ? routeToken.slice(1) + : routeToken + const safe = escapeRegExp(routeTokenToExclude) + const leadingUnderscoreRegex = tokenStartsWithUnderscore + ? new RegExp(`(?<=^|\\/)_(?!${safe})`, 'g') + : /(?<=^|\/)_/g + return s.replace(leadingUnderscoreRegex, '') } export function removeTrailingUnderscores(s: string, routeToken: string) { - const parts = s.split('/') - - if (parts.length === 0) return s - - const routeTokenToExclude = - routeToken.slice(-1) === '_' ? routeToken.slice(0, -1) : routeToken - - const trailingUnderscoreRegex = - routeToken[0] === '_' - ? new RegExp(`(?<!${routeTokenToExclude})_(?=\\/|$)`, 'g') - : new RegExp(`_(?=\\/)|_$`, 'g') - - return s.replaceAll(trailingUnderscoreRegex, '') + if (!s) return s + const tokenStartsWithUnderscore = routeToken.startsWith('_') + const routeTokenToExclude = routeToken.endsWith('_') + ? routeToken.slice(0, -1) + : routeToken + const safe = escapeRegExp(routeTokenToExclude) + const trailingUnderscoreRegex = tokenStartsWithUnderscore + ? new RegExp(`(?<!${safe})_(?=\\/|$)`, 'g') + : /_(?=\/)|_$/g + return s.replace(trailingUnderscoreRegex, '') }Add this helper once in the module:
function escapeRegExp(s: string): string { return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') }Note: Addresses static analysis warnings on dynamic RegExp construction.
Also applies to: 212-226
356-362: Unused parameter in hasParentRoute signatureuseExperimentalNonNestedRoutes is never read. Remove or mark deprecated to avoid confusion; callers already rely on node._isExperimentalNonNestedRoute.
491-497: Potentially redundant cleanup stepremoveLayoutSegments strips underscore-prefixed segments already; removeLeadingUnderscores may be redundant here. Consider dropping it for clarity unless you have a case it still affects.
797-803: Fix stale JSDoc wordingComment says “pathless layout route” but function validates non-nested routes. Update wording to match behavior.
-/** - * Used to validate if a route is a pathless layout route +/** + * Validates whether a normalized route path represents a non‑nested (pathless) route
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (65)
docs/router/framework/react/routing/file-naming-conventions.md(1 hunks)docs/router/framework/react/routing/routing-concepts.md(1 hunks)e2e/react-router/basic-file-based/package.json(1 hunks)e2e/react-router/basic-file-based/playwright.config.ts(2 hunks)e2e/react-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/$bar.tsx(1 hunks)e2e/react-router/basic-file-based/src/routes/posts_.$postId.edit.tsx(1 hunks)e2e/react-router/basic-file-based/tests/non-nested-paths.spec.ts(2 hunks)e2e/react-router/basic-file-based/tests/params.spec.ts(3 hunks)e2e/react-router/basic-file-based/tests/utils/useExperimentalNonNestedRoutes.ts(1 hunks)e2e/react-router/basic-file-based/vite.config.js(1 hunks)e2e/solid-router/basic-file-based/package.json(1 hunks)e2e/solid-router/basic-file-based/playwright.config.ts(2 hunks)e2e/solid-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/$bar.tsx(1 hunks)e2e/solid-router/basic-file-based/src/routes/posts_.$postId.edit.tsx(1 hunks)e2e/solid-router/basic-file-based/tests/non-nested-paths.spec.ts(2 hunks)e2e/solid-router/basic-file-based/tests/params.spec.ts(3 hunks)e2e/solid-router/basic-file-based/tests/utils/useExperimentalNonNestedRoutes.ts(1 hunks)e2e/solid-router/basic-file-based/vite.config.js(1 hunks)packages/router-core/src/path.ts(9 hunks)packages/router-core/src/process-route-tree.ts(2 hunks)packages/router-core/tests/match-by-path.test.ts(0 hunks)packages/router-core/tests/path.test.ts(0 hunks)packages/router-generator/src/config.ts(1 hunks)packages/router-generator/src/filesystem/physical/getRouteNodes.ts(4 hunks)packages/router-generator/src/generator.ts(10 hunks)packages/router-generator/src/types.ts(2 hunks)packages/router-generator/src/utils.ts(15 hunks)packages/router-generator/tests/deny-route-group-config.test.ts(3 hunks)packages/router-generator/tests/generator.test.ts(5 hunks)packages/router-generator/tests/generator/append-and-prepend/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/custom-scaffolding/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/custom-tokens/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/dot-escaped/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/export-variations/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/file-modification-verboseFileRoutes-false/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/file-modification-verboseFileRoutes-true/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/flat/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/nested-layouts/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/nested-route-groups-with-layouts-before-physical/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/nested-verboseFileRoutes-false/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/nested-verboseFileRoutes-false/tests.nonnested.test-d.ts(1 hunks)packages/router-generator/tests/generator/nested-verboseFileRoutes-true/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/nested-verboseFileRoutes-true/tests.nonnested.test-d.ts(1 hunks)packages/router-generator/tests/generator/no-duplicate-route-segment/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/numbers-in-path/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/only-root/routeTree.nonnested.generated.snapshot.ts(1 hunks)packages/router-generator/tests/generator/only-root/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/path-above-route-in-group/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/prefix-suffix/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/route-groups/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/routeFileIgnore/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/routeFilePrefix/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/single-level/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/types-disabled/routeTree.nonnested.snapshot.js(1 hunks)packages/router-generator/tests/generator/types-disabled/routes/__root.tsx(1 hunks)packages/router-generator/tests/generator/types-disabled/routes/index.tsx(1 hunks)packages/router-generator/tests/generator/types-disabled/routes/posts.tsx(1 hunks)packages/router-generator/tests/generator/types-disabled/routes/posts/$postId.tsx(1 hunks)packages/router-generator/tests/generator/types-disabled/routes/users.$userId.tsx(1 hunks)packages/router-generator/tests/generator/virtual-config-file-default-export/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/virtual-config-file-named-export/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/virtual-inside-nested/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/virtual/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/utils.test.ts(8 hunks)
💤 Files with no reviewable changes (2)
- packages/router-core/tests/match-by-path.test.ts
- packages/router-core/tests/path.test.ts
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use TypeScript in strict mode with extensive type safety across the codebase
Files:
e2e/solid-router/basic-file-based/tests/utils/useExperimentalNonNestedRoutes.tse2e/react-router/basic-file-based/tests/utils/useExperimentalNonNestedRoutes.tse2e/react-router/basic-file-based/playwright.config.tse2e/react-router/basic-file-based/src/routes/posts_.$postId.edit.tsxe2e/react-router/basic-file-based/tests/non-nested-paths.spec.tse2e/react-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/$bar.tsxe2e/solid-router/basic-file-based/playwright.config.tspackages/router-generator/src/types.tspackages/router-generator/tests/generator/routeFileIgnore/routeTree.nonnested.snapshot.tse2e/solid-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/$bar.tsxe2e/solid-router/basic-file-based/tests/non-nested-paths.spec.tspackages/router-generator/tests/generator/only-root/routeTree.nonnested.generated.snapshot.tspackages/router-generator/tests/generator/types-disabled/routes/__root.tsxpackages/router-core/src/path.tspackages/router-generator/tests/generator/file-modification-verboseFileRoutes-false/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/nested-route-groups-with-layouts-before-physical/routeTree.nonnested.snapshot.tspackages/router-generator/src/config.tspackages/router-generator/tests/generator/nested-verboseFileRoutes-true/tests.nonnested.test-d.tspackages/router-generator/tests/generator/nested-verboseFileRoutes-true/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/nested-verboseFileRoutes-false/tests.nonnested.test-d.tspackages/router-generator/tests/generator/routeFilePrefix/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/append-and-prepend/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/file-modification-verboseFileRoutes-true/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/virtual/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/nested-layouts/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/virtual-config-file-default-export/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/export-variations/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/no-duplicate-route-segment/routeTree.nonnested.snapshot.tspackages/router-generator/src/filesystem/physical/getRouteNodes.tspackages/router-generator/src/utils.tspackages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/path-above-route-in-group/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/single-level/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/custom-tokens/routeTree.nonnested.snapshot.tse2e/solid-router/basic-file-based/tests/params.spec.tspackages/router-generator/tests/generator/numbers-in-path/routeTree.nonnested.snapshot.tspackages/router-generator/tests/deny-route-group-config.test.tspackages/router-generator/tests/generator.test.tspackages/router-generator/tests/generator/types-disabled/routes/posts.tsxe2e/solid-router/basic-file-based/src/routes/posts_.$postId.edit.tsxe2e/react-router/basic-file-based/tests/params.spec.tspackages/router-generator/tests/generator/virtual-inside-nested/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/types-disabled/routes/users.$userId.tsxpackages/router-generator/tests/generator/prefix-suffix/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/custom-scaffolding/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/only-root/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/types-disabled/routes/index.tsxpackages/router-core/src/process-route-tree.tspackages/router-generator/tests/generator/nested-verboseFileRoutes-false/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/dot-escaped/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/route-groups/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/types-disabled/routes/posts/$postId.tsxpackages/router-generator/tests/generator/virtual-config-file-named-export/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/flat/routeTree.nonnested.snapshot.tspackages/router-generator/src/generator.tspackages/router-generator/tests/utils.test.ts
e2e/**
📄 CodeRabbit inference engine (AGENTS.md)
Store end-to-end tests under the e2e/ directory
Files:
e2e/solid-router/basic-file-based/tests/utils/useExperimentalNonNestedRoutes.tse2e/react-router/basic-file-based/vite.config.jse2e/react-router/basic-file-based/tests/utils/useExperimentalNonNestedRoutes.tse2e/react-router/basic-file-based/playwright.config.tse2e/react-router/basic-file-based/src/routes/posts_.$postId.edit.tsxe2e/react-router/basic-file-based/tests/non-nested-paths.spec.tse2e/react-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/$bar.tsxe2e/solid-router/basic-file-based/playwright.config.tse2e/solid-router/basic-file-based/package.jsone2e/react-router/basic-file-based/package.jsone2e/solid-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/$bar.tsxe2e/solid-router/basic-file-based/tests/non-nested-paths.spec.tse2e/solid-router/basic-file-based/tests/params.spec.tse2e/solid-router/basic-file-based/vite.config.jse2e/solid-router/basic-file-based/src/routes/posts_.$postId.edit.tsxe2e/react-router/basic-file-based/tests/params.spec.ts
docs/**/*.{md,mdx}
📄 CodeRabbit inference engine (AGENTS.md)
Use internal docs links relative to the docs/ folder (e.g., ./guide/data-loading)
Files:
docs/router/framework/react/routing/routing-concepts.mddocs/router/framework/react/routing/file-naming-conventions.md
docs/{router,start}/**
📄 CodeRabbit inference engine (AGENTS.md)
Place router docs under docs/router/ and start framework docs under docs/start/
Files:
docs/router/framework/react/routing/routing-concepts.mddocs/router/framework/react/routing/file-naming-conventions.md
**/src/routes/**
📄 CodeRabbit inference engine (AGENTS.md)
Place file-based routes under src/routes/ directories
Files:
e2e/react-router/basic-file-based/src/routes/posts_.$postId.edit.tsxe2e/react-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/$bar.tsxe2e/solid-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/$bar.tsxe2e/solid-router/basic-file-based/src/routes/posts_.$postId.edit.tsx
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-generator/tests/generator/types-disabled/routeTree.nonnested.snapshot.jspackages/router-generator/src/types.tspackages/router-generator/tests/generator/routeFileIgnore/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/only-root/routeTree.nonnested.generated.snapshot.tspackages/router-generator/tests/generator/types-disabled/routes/__root.tsxpackages/router-generator/tests/generator/file-modification-verboseFileRoutes-false/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/nested-route-groups-with-layouts-before-physical/routeTree.nonnested.snapshot.tspackages/router-generator/src/config.tspackages/router-generator/tests/generator/nested-verboseFileRoutes-true/tests.nonnested.test-d.tspackages/router-generator/tests/generator/nested-verboseFileRoutes-true/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/nested-verboseFileRoutes-false/tests.nonnested.test-d.tspackages/router-generator/tests/generator/routeFilePrefix/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/append-and-prepend/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/file-modification-verboseFileRoutes-true/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/virtual/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/nested-layouts/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/virtual-config-file-default-export/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/export-variations/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/no-duplicate-route-segment/routeTree.nonnested.snapshot.tspackages/router-generator/src/filesystem/physical/getRouteNodes.tspackages/router-generator/src/utils.tspackages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/path-above-route-in-group/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/single-level/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/custom-tokens/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/numbers-in-path/routeTree.nonnested.snapshot.tspackages/router-generator/tests/deny-route-group-config.test.tspackages/router-generator/tests/generator.test.tspackages/router-generator/tests/generator/types-disabled/routes/posts.tsxpackages/router-generator/tests/generator/virtual-inside-nested/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/types-disabled/routes/users.$userId.tsxpackages/router-generator/tests/generator/prefix-suffix/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/custom-scaffolding/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/only-root/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/types-disabled/routes/index.tsxpackages/router-generator/tests/generator/nested-verboseFileRoutes-false/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/dot-escaped/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/route-groups/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/types-disabled/routes/posts/$postId.tsxpackages/router-generator/tests/generator/virtual-config-file-named-export/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/flat/routeTree.nonnested.snapshot.tspackages/router-generator/src/generator.tspackages/router-generator/tests/utils.test.ts
**/package.json
📄 CodeRabbit inference engine (AGENTS.md)
Use workspace:* protocol for internal dependencies in package.json files
Files:
e2e/solid-router/basic-file-based/package.jsone2e/react-router/basic-file-based/package.json
packages/router-core/**
📄 CodeRabbit inference engine (AGENTS.md)
Keep framework-agnostic core router logic in packages/router-core/
Files:
packages/router-core/src/path.tspackages/router-core/src/process-route-tree.ts
🧠 Learnings (1)
📚 Learning: 2025-10-01T18:31:35.420Z
Learnt from: schiller-manuel
PR: TanStack/router#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-generator/tests/generator/types-disabled/routeTree.nonnested.snapshot.jspackages/router-generator/tests/generator/routeFileIgnore/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/only-root/routeTree.nonnested.generated.snapshot.tspackages/router-generator/tests/generator/file-modification-verboseFileRoutes-false/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/nested-verboseFileRoutes-true/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/routeFilePrefix/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/append-and-prepend/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/file-modification-verboseFileRoutes-true/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/virtual/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/no-duplicate-route-segment/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/path-above-route-in-group/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/single-level/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/custom-tokens/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/custom-scaffolding/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/only-root/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/route-groups/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/virtual-config-file-named-export/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/flat/routeTree.nonnested.snapshot.ts
🧬 Code graph analysis (42)
e2e/react-router/basic-file-based/playwright.config.ts (2)
e2e/e2e-utils/src/derivePort.ts (2)
getTestServerPort(25-27)getDummyServerPort(21-23)e2e/react-router/basic-file-based/tests/utils/useExperimentalNonNestedRoutes.ts (1)
useExperimentalNonNestedRoutes(1-6)
e2e/react-router/basic-file-based/src/routes/posts_.$postId.edit.tsx (3)
e2e/react-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/$bar.tsx (1)
Route(4-6)e2e/react-router/basic-file-based/src/routes/params-ps/non-nested/route.tsx (1)
Route(3-5)e2e/react-router/basic-file-based/tests/utils/useExperimentalNonNestedRoutes.ts (1)
useExperimentalNonNestedRoutes(1-6)
e2e/react-router/basic-file-based/tests/non-nested-paths.spec.ts (1)
e2e/react-router/basic-file-based/tests/utils/useExperimentalNonNestedRoutes.ts (1)
useExperimentalNonNestedRoutes(1-6)
e2e/react-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/$bar.tsx (3)
e2e/react-router/basic-file-based/src/routes/params-ps/non-nested/route.tsx (1)
Route(3-5)e2e/react-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/route.tsx (1)
Route(3-3)e2e/react-router/basic-file-based/tests/utils/useExperimentalNonNestedRoutes.ts (1)
useExperimentalNonNestedRoutes(1-6)
packages/router-generator/tests/generator/types-disabled/routeTree.nonnested.snapshot.js (1)
packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts (1)
routeTree(93-95)
e2e/solid-router/basic-file-based/playwright.config.ts (2)
e2e/e2e-utils/src/derivePort.ts (2)
getTestServerPort(25-27)getDummyServerPort(21-23)e2e/solid-router/basic-file-based/tests/utils/useExperimentalNonNestedRoutes.ts (1)
useExperimentalNonNestedRoutes(1-6)
packages/router-generator/tests/generator/routeFileIgnore/routeTree.nonnested.snapshot.ts (2)
packages/router-generator/tests/generator/append-and-prepend/routeTree.nonnested.snapshot.ts (6)
FileRoutesByFullPath(18-20)FileRoutesByTo(21-23)FileRoutesById(24-27)FileRouteTypes(28-35)RootRouteChildren(36-38)routeTree(55-57)packages/router-generator/tests/generator/flat/routeTree.nonnested.snapshot.ts (6)
FileRoutesByFullPath(68-78)FileRoutesByTo(79-87)FileRoutesById(88-99)FileRouteTypes(100-133)RootRouteChildren(134-139)routeTree(245-247)
e2e/solid-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/$bar.tsx (1)
e2e/solid-router/basic-file-based/tests/utils/useExperimentalNonNestedRoutes.ts (1)
useExperimentalNonNestedRoutes(1-6)
e2e/solid-router/basic-file-based/tests/non-nested-paths.spec.ts (1)
e2e/solid-router/basic-file-based/tests/utils/useExperimentalNonNestedRoutes.ts (1)
useExperimentalNonNestedRoutes(1-6)
packages/router-generator/tests/generator/file-modification-verboseFileRoutes-false/routeTree.nonnested.snapshot.ts (2)
packages/router-generator/tests/generator/file-modification-verboseFileRoutes-true/routeTree.nonnested.snapshot.ts (6)
FileRoutesByFullPath(53-59)FileRoutesByTo(60-66)FileRoutesById(67-74)FileRouteTypes(75-88)RootRouteChildren(89-94)routeTree(153-155)packages/router-generator/tests/generator/file-modification-verboseFileRoutes-true/routeTree.snapshot.ts (1)
FileRoutesByPath(97-133)
packages/router-generator/tests/generator/nested-route-groups-with-layouts-before-physical/routeTree.nonnested.snapshot.ts (1)
packages/router-generator/tests/generator/nested-route-groups-with-layouts-before-physical/routeTree.snapshot.ts (1)
FileRouteTypes(96-114)
packages/router-generator/tests/generator/nested-verboseFileRoutes-true/routeTree.nonnested.snapshot.ts (2)
packages/router-generator/tests/generator/flat/routeTree.nonnested.snapshot.ts (2)
FileRoutesByFullPath(68-78)routeTree(245-247)packages/router-generator/tests/generator/nested-verboseFileRoutes-false/routeTree.nonnested.snapshot.ts (2)
FileRoutesByFullPath(70-80)routeTree(329-331)
packages/router-generator/tests/generator/routeFilePrefix/routeTree.nonnested.snapshot.ts (1)
packages/router-generator/tests/generator/export-variations/routeTree.nonnested.snapshot.ts (6)
FileRoutesByFullPath(27-30)FileRoutesByTo(31-34)FileRoutesById(35-39)FileRouteTypes(40-47)RootRouteChildren(48-51)routeTree(76-78)
packages/router-generator/tests/generator/append-and-prepend/routeTree.nonnested.snapshot.ts (1)
packages/router-generator/tests/generator/types-disabled/routeTree.nonnested.snapshot.js (1)
IndexRoute(22-26)
packages/router-generator/tests/generator/file-modification-verboseFileRoutes-true/routeTree.nonnested.snapshot.ts (2)
packages/router-generator/tests/generator/file-modification-verboseFileRoutes-false/routeTree.nonnested.snapshot.ts (6)
FileRoutesByFullPath(58-64)FileRoutesByTo(65-71)FileRoutesById(72-79)FileRouteTypes(80-93)RootRouteChildren(94-99)routeTree(205-207)packages/router-generator/tests/generator/file-modification-verboseFileRoutes-true/routeTree.snapshot.ts (1)
FileRoutesByPath(97-133)
packages/router-generator/tests/generator/virtual/routeTree.nonnested.snapshot.ts (2)
packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts (5)
FileRoutesByFullPath(32-36)FileRoutesByTo(37-41)FileRoutesById(42-47)FileRouteTypes(48-55)routeTree(93-95)packages/router-generator/tests/generator/only-root/routeTree.nonnested.generated.snapshot.ts (5)
FileRoutesByFullPath(13-13)FileRoutesByTo(14-14)FileRoutesById(15-17)FileRouteTypes(18-25)routeTree(33-35)
packages/router-generator/tests/generator/nested-layouts/routeTree.nonnested.snapshot.ts (1)
packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts (5)
FileRoutesByFullPath(32-36)FileRoutesByTo(37-41)FileRoutesById(42-47)FileRouteTypes(48-55)routeTree(93-95)
packages/router-generator/tests/generator/virtual-config-file-default-export/routeTree.nonnested.snapshot.ts (1)
packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts (6)
FileRoutesByFullPath(32-36)FileRoutesByTo(37-41)FileRoutesById(42-47)FileRouteTypes(48-55)RootRouteChildren(56-60)routeTree(93-95)
packages/router-generator/tests/generator/export-variations/routeTree.nonnested.snapshot.ts (1)
packages/router-generator/tests/generator/export-variations/routeTree.snapshot.ts (1)
FileRouteTypes(40-47)
packages/router-generator/tests/generator/no-duplicate-route-segment/routeTree.nonnested.snapshot.ts (2)
packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts (6)
FileRoutesByFullPath(32-36)FileRoutesByTo(37-41)FileRoutesById(42-47)FileRouteTypes(48-55)RootRouteChildren(56-60)routeTree(93-95)packages/router-generator/tests/generator/append-and-prepend/routeTree.nonnested.snapshot.ts (6)
FileRoutesByFullPath(18-20)FileRoutesByTo(21-23)FileRoutesById(24-27)FileRouteTypes(28-35)RootRouteChildren(36-38)routeTree(55-57)
packages/router-generator/src/filesystem/physical/getRouteNodes.ts (1)
packages/router-generator/src/utils.ts (1)
determineInitialRoutePath(57-144)
packages/router-generator/src/utils.ts (3)
packages/router-generator/src/config.ts (1)
Config(66-66)packages/router-generator/src/index.ts (1)
rootPathId(45-45)packages/router-generator/src/types.ts (1)
RouteNode(1-18)
packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts (1)
packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.snapshot.ts (1)
FileRouteTypes(36-43)
packages/router-generator/tests/generator/path-above-route-in-group/routeTree.nonnested.snapshot.ts (2)
packages/router-generator/tests/generator/flat/routeTree.nonnested.snapshot.ts (6)
FileRoutesByFullPath(68-78)FileRoutesByTo(79-87)FileRoutesById(88-99)FileRouteTypes(100-133)RootRouteChildren(134-139)routeTree(245-247)packages/router-generator/tests/generator/only-root/routeTree.nonnested.generated.snapshot.ts (6)
FileRoutesByFullPath(13-13)FileRoutesByTo(14-14)FileRoutesById(15-17)FileRouteTypes(18-25)RootRouteChildren(26-26)routeTree(33-35)
packages/router-generator/tests/generator/single-level/routeTree.nonnested.snapshot.ts (2)
packages/router-generator/tests/generator/types-disabled/routeTree.nonnested.snapshot.js (3)
PostsRoute(17-21)IndexRoute(22-26)rootRouteChildren(44-48)packages/router-generator/tests/generator/append-and-prepend/routeTree.nonnested.snapshot.ts (6)
FileRoutesByFullPath(18-20)FileRoutesByTo(21-23)FileRoutesById(24-27)FileRouteTypes(28-35)RootRouteChildren(36-38)routeTree(55-57)
packages/router-generator/tests/generator/custom-tokens/routeTree.nonnested.snapshot.ts (1)
packages/router-generator/tests/generator/flat/routeTree.nonnested.snapshot.ts (6)
FileRoutesByFullPath(68-78)FileRoutesByTo(79-87)FileRoutesById(88-99)FileRouteTypes(100-133)RootRouteChildren(134-139)routeTree(245-247)
e2e/solid-router/basic-file-based/tests/params.spec.ts (1)
e2e/solid-router/basic-file-based/tests/utils/useExperimentalNonNestedRoutes.ts (1)
useExperimentalNonNestedRoutes(1-6)
packages/router-generator/tests/generator/numbers-in-path/routeTree.nonnested.snapshot.ts (1)
packages/router-generator/tests/generator/numbers-in-path/routeTree.snapshot.ts (3)
FileRoutesByPath(83-119)FileRouteTypes(66-73)FileRoutesByFullPath(44-50)
packages/router-generator/tests/deny-route-group-config.test.ts (1)
packages/router-generator/src/config.ts (2)
Config(66-66)getConfig(76-151)
packages/router-generator/tests/generator.test.ts (1)
packages/router-generator/src/config.ts (2)
Config(66-66)getConfig(76-151)
e2e/solid-router/basic-file-based/src/routes/posts_.$postId.edit.tsx (2)
e2e/solid-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/$bar.tsx (1)
Route(4-6)e2e/solid-router/basic-file-based/tests/utils/useExperimentalNonNestedRoutes.ts (1)
useExperimentalNonNestedRoutes(1-6)
e2e/react-router/basic-file-based/tests/params.spec.ts (1)
e2e/react-router/basic-file-based/tests/utils/useExperimentalNonNestedRoutes.ts (1)
useExperimentalNonNestedRoutes(1-6)
packages/router-generator/tests/generator/virtual-inside-nested/routeTree.nonnested.snapshot.ts (2)
packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts (3)
FileRouteTypes(48-55)RootRouteChildren(56-60)routeTree(93-95)packages/router-generator/tests/generator/file-modification-verboseFileRoutes-true/routeTree.snapshot.ts (1)
FileRoutesByPath(97-133)
packages/router-generator/tests/generator/prefix-suffix/routeTree.nonnested.snapshot.ts (1)
packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts (6)
FileRoutesByFullPath(32-36)FileRoutesByTo(37-41)FileRoutesById(42-47)FileRouteTypes(48-55)RootRouteChildren(56-60)routeTree(93-95)
packages/router-generator/tests/generator/custom-scaffolding/routeTree.nonnested.snapshot.ts (1)
packages/router-generator/tests/generator/types-disabled/routeTree.nonnested.snapshot.js (1)
IndexRoute(22-26)
packages/router-core/src/process-route-tree.ts (2)
packages/router-core/src/path.ts (1)
parsePathname(204-215)packages/router-core/src/index.ts (1)
parsePathname(103-103)
packages/router-generator/tests/generator/nested-verboseFileRoutes-false/routeTree.nonnested.snapshot.ts (1)
e2e/react-router/js-only-file-based/src/routeTree.gen.js (4)
PostsRouteRoute(25-29)PostsIndexRoute(35-39)PostsRouteRouteWithChildren(68-70)PostsRouteRouteChildren(63-66)
packages/router-generator/tests/generator/dot-escaped/routeTree.nonnested.snapshot.ts (1)
packages/router-generator/tests/generator/export-variations/routeTree.nonnested.snapshot.ts (6)
FileRoutesByFullPath(27-30)FileRoutesByTo(31-34)FileRoutesById(35-39)FileRouteTypes(40-47)RootRouteChildren(48-51)routeTree(76-78)
packages/router-generator/tests/generator/virtual-config-file-named-export/routeTree.nonnested.snapshot.ts (3)
packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts (6)
FileRoutesByFullPath(32-36)FileRoutesByTo(37-41)FileRoutesById(42-47)FileRouteTypes(48-55)RootRouteChildren(56-60)routeTree(93-95)packages/router-generator/tests/generator/append-and-prepend/routeTree.nonnested.snapshot.ts (6)
FileRoutesByFullPath(18-20)FileRoutesByTo(21-23)FileRoutesById(24-27)FileRouteTypes(28-35)RootRouteChildren(36-38)routeTree(55-57)packages/router-generator/tests/generator/only-root/routeTree.nonnested.generated.snapshot.ts (6)
FileRoutesByFullPath(13-13)FileRoutesByTo(14-14)FileRoutesById(15-17)FileRouteTypes(18-25)RootRouteChildren(26-26)routeTree(33-35)
packages/router-generator/tests/generator/flat/routeTree.nonnested.snapshot.ts (1)
e2e/react-router/js-only-file-based/src/routeTree.gen.js (4)
PostsRouteRoute(25-29)PostsIndexRoute(35-39)PostsRouteRouteWithChildren(68-70)PostsRouteRouteChildren(63-66)
packages/router-generator/src/generator.ts (3)
packages/router-generator/src/utils.ts (8)
createRouteNodesByFullPath(505-515)createRouteNodesByTo(520-530)resetRegex(291-294)hasParentRoute(356-443)removeGroups(312-314)removeLeadingUnderscores(196-210)removeLayoutSegments(324-328)removeUnderscores(192-194)packages/router-generator/src/types.ts (2)
RouteNode(1-18)HandleNodeAccumulator(55-59)packages/router-generator/src/config.ts (1)
Config(66-66)
packages/router-generator/tests/utils.test.ts (2)
packages/router-generator/src/utils.ts (5)
determineInitialRoutePath(57-144)removeLeadingUnderscores(196-210)removeTrailingUnderscores(212-226)routePathToVariable(150-190)isValidNonNestedRoute(803-843)packages/router-generator/src/types.ts (1)
ImportDeclaration(49-53)
🪛 ast-grep (0.39.5)
packages/router-generator/src/utils.ts
[warning] 205-205: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp((?<=^|\\/)_(?!${routeTokenToExclude}), 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
[warning] 221-221: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp((?<!${routeTokenToExclude})_(?=\\/|$), 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
🪛 Biome (2.1.2)
packages/router-generator/tests/generator/only-root/routeTree.nonnested.generated.snapshot.ts
[error] 13-13: An empty interface is equivalent to {}.
Safe fix: Use a type alias instead.
(lint/suspicious/noEmptyInterface)
[error] 14-14: An empty interface is equivalent to {}.
Safe fix: Use a type alias instead.
(lint/suspicious/noEmptyInterface)
[error] 26-26: An empty interface is equivalent to {}.
Safe fix: Use a type alias instead.
(lint/suspicious/noEmptyInterface)
packages/router-generator/tests/generator/only-root/routeTree.nonnested.snapshot.ts
[error] 13-13: An empty interface is equivalent to {}.
Safe fix: Use a type alias instead.
(lint/suspicious/noEmptyInterface)
[error] 14-14: An empty interface is equivalent to {}.
Safe fix: Use a type alias instead.
(lint/suspicious/noEmptyInterface)
[error] 26-26: An empty interface is equivalent to {}.
Safe fix: Use a type alias instead.
(lint/suspicious/noEmptyInterface)
⏰ 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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (3)
e2e/solid-router/basic-file-based/src/routes/posts_.$postId.edit.tsx (1)
2-2: Consider renaming the imported constant.The
useExperimentalNonNestedRoutesidentifier follows React/Solid hook naming conventions (prefixed with "use"), but it's actually a boolean constant, not a function or hook. Consider renaming it to something likeisExperimentalNonNestedRoutesEnabledorEXPERIMENTAL_NON_NESTED_ROUTESto avoid confusion.packages/router-generator/src/utils.ts (2)
196-212: Consider the regex complexity and potential alternatives.The
removeLeadingUnderscoresfunction constructs regexes dynamically fromrouteToken. WhileescapeRegExpmitigates ReDoS risks, the regex logic is complex with negative lookahead/lookbehind assertions:
- Line 208:
(?<=^|\\/)_(?!${escapeRegExp(routeTokenToExclude)})For maintainability and potential performance improvements, consider whether a simpler string-based approach could achieve the same result (e.g., split, filter, and rejoin based on position and routeToken context). However, if the current regex approach is well-tested and performs adequately, it may be acceptable to defer this optimization.
Note: The static analysis warning about ReDoS at line 207 is likely a false positive given the
escapeRegExpcall, but the complexity warrants attention.
214-226: Consider the regex complexity and potential alternatives.The
removeTrailingUnderscoresfunction has similar complexity toremoveLeadingUnderscores, using dynamic regex construction with negative lookbehind:
- Line 222:
(?<!${escapeRegExp(routeTokenToExclude)})_(?=\\/|$)The same maintainability and performance considerations apply. While
escapeRegExpmitigates ReDoS risks, the regex complexity could be reduced with a string-based approach if performance or maintainability becomes a concern.Note: The static analysis warning about ReDoS at line 221 is likely a false positive given the
escapeRegExpcall, but warrants monitoring.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
e2e/solid-router/basic-file-based/src/routes/posts_.$postId.edit.tsx(1 hunks)packages/router-generator/src/filesystem/physical/getRouteNodes.ts(3 hunks)packages/router-generator/src/generator.ts(11 hunks)packages/router-generator/src/utils.ts(15 hunks)packages/router-generator/tests/generator.test.ts(5 hunks)
🧰 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-generator/tests/generator.test.tspackages/router-generator/src/utils.tse2e/solid-router/basic-file-based/src/routes/posts_.$postId.edit.tsxpackages/router-generator/src/generator.tspackages/router-generator/src/filesystem/physical/getRouteNodes.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-generator/tests/generator.test.tspackages/router-generator/src/utils.tspackages/router-generator/src/generator.tspackages/router-generator/src/filesystem/physical/getRouteNodes.ts
**/src/routes/**
📄 CodeRabbit inference engine (AGENTS.md)
Place file-based routes under src/routes/ directories
Files:
e2e/solid-router/basic-file-based/src/routes/posts_.$postId.edit.tsx
e2e/**
📄 CodeRabbit inference engine (AGENTS.md)
Store end-to-end tests under the e2e/ directory
Files:
e2e/solid-router/basic-file-based/src/routes/posts_.$postId.edit.tsx
🧬 Code graph analysis (5)
packages/router-generator/tests/generator.test.ts (1)
packages/router-generator/src/config.ts (2)
Config(66-66)getConfig(76-151)
packages/router-generator/src/utils.ts (3)
packages/router-generator/src/config.ts (1)
Config(66-66)packages/router-generator/src/index.ts (1)
rootPathId(45-45)packages/router-generator/src/types.ts (1)
RouteNode(1-18)
e2e/solid-router/basic-file-based/src/routes/posts_.$postId.edit.tsx (2)
e2e/solid-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/$bar.tsx (1)
Route(4-6)e2e/solid-router/basic-file-based/tests/utils/useExperimentalNonNestedRoutes.ts (1)
useExperimentalNonNestedRoutes(1-6)
packages/router-generator/src/generator.ts (3)
packages/router-generator/src/utils.ts (8)
createRouteNodesByFullPath(495-505)createRouteNodesByTo(510-520)resetRegex(291-294)hasParentRoute(356-436)removeGroups(312-314)removeLeadingUnderscores(200-212)removeLayoutSegments(324-328)removeUnderscores(192-194)packages/router-generator/src/types.ts (2)
RouteNode(1-18)HandleNodeAccumulator(55-59)packages/router-generator/src/config.ts (1)
Config(66-66)
packages/router-generator/src/filesystem/physical/getRouteNodes.ts (1)
packages/router-generator/src/utils.ts (1)
determineInitialRoutePath(57-144)
🪛 ast-grep (0.39.5)
packages/router-generator/src/utils.ts
[warning] 207-207: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp((?<=^|\\/)_(?!${escapeRegExp(routeTokenToExclude)}), 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
[warning] 221-221: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp((?<!${escapeRegExp(routeTokenToExclude)})_(?=\\/|$), 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
⏰ 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). (1)
- GitHub Check: Test
🔇 Additional comments (18)
e2e/solid-router/basic-file-based/src/routes/posts_.$postId.edit.tsx (2)
1-2: LGTM! Imports are clean and properly consolidated.The addition of
useExperimentalNonNestedRoutesis appropriate for testing both feature-flag modes in this e2e test file.
8-11: Runtime-conditional routes are test-only
Confirmed that conditionalgetRouteApianduseParamspatterns appear only undere2e/, not in production code.packages/router-generator/tests/generator.test.ts (4)
43-64: LGTM!The
nonNestedparameter is correctly threaded through the configuration and properly controls both the generated route tree filename and the experimental flag.
249-252: LGTM!The test case generation using
flatMapto run each test with bothnonNested: trueandnonNested: falseis a clean approach for comprehensive coverage.
254-287: LGTM!The test orchestration correctly passes the
nonNestedflag through the configuration pipeline and adjusts snapshot paths accordingly.
289-343: LGTM!The directory creation test correctly handles both
nonNestedmodes and properly constructs file paths and snapshot references.packages/router-generator/src/filesystem/physical/getRouteNodes.ts (3)
37-37: LGTM!The addition of
'experimental'to the config type correctly enables access to the experimental feature flags.
135-141: LGTM!The call to
determineInitialRoutePathcorrectly handles the updated return type, destructuring all three fields (routePath,originalRoutePath,isExperimentalNonNestedRoute) from the result.
196-204: LGTM!The addition of
_isExperimentalNonNestedRouteandoriginalRoutePathto the RouteNode correctly preserves experimental non-nested route metadata for downstream processing.packages/router-generator/src/generator.ts (3)
678-732: LGTM!The config is correctly threaded through
createRouteNodesByFullPath,createRouteNodesByTo, andbuildFileRoutesByPathInterfaceto enable config-driven path generation for the TypeScript interface types.
1190-1246: LGTM!The
handleNodemethod correctly implements the experimental non-nested routes behavior by conditionally applyingremoveLeadingUnderscoresinstead ofremoveUnderscores. The logic properly:
- Derives the experimental flag from config
- Passes
originalRoutePathtohasParentRoutefor accurate parent resolution- Applies the appropriate path cleaning based on the feature flag
The TODO comments appropriately document the intended breaking change in a future major version.
1280-1288: LGTM!The recursive
handleNodecalls correctly pass theconfigparameter through, ensuring consistent behavior for virtual nodes and parent nodes.Also applies to: 1327-1335
packages/router-generator/src/utils.ts (6)
57-144: LGTM!The updated
determineInitialRoutePathcorrectly implements the experimental non-nested routes feature by:
- Preserving the
originalRoutePathbefore transformations for downstream use- Determining whether the route qualifies as experimental non-nested via
isValidNonNestedRoute- Conditionally removing trailing underscores only when the feature flag is enabled and the route is not the root path
- Maintaining the existing escaping logic for bracket content
The change from returning a string to returning an object is a necessary breaking change to support the feature, and all call sites have been updated accordingly.
356-436: LGTM with note on complexity.The
hasParentRoutefunction correctly implements the experimental non-nested routes feature with custom parent resolution logic. The key changes:
- Adds
originalRoutePathToCheckparameter to track the original path before transformations- Introduces
getNonNestedSegmentsto identify non-nested route boundaries- Implements special filtering for experimental non-nested routes to exclude conflicting routes and include only layout/root routes that aren't marked as non-nested
The logic is complex but appears intentional for the feature's requirements. The bracket-matching filter in
getNonNestedSegments(lines 366-371) correctly handles escaped characters in route paths.
477-490: LGTM!The
inferFullPathfunction correctly implements the experimental behavior by:
- Preserving trailing underscores when the experimental flag is enabled (using
removeLayoutSegmentsonly)- Maintaining backward compatibility with the existing behavior (using
removeUnderscores) when the flag is offThe TODO comment appropriately documents the intended breaking change in a future major version.
495-548: LGTM!The functions
createRouteNodesByFullPath,createRouteNodesByTo, andinferTocorrectly thread the optionalconfigparameter through the path inference pipeline, enabling config-driven path generation.
715-741: LGTM!The
buildFileRoutesByPathInterfacefunction correctly accepts the optionalconfigparameter and passes it toinferFullPath(line 733) for accurate type generation.
787-833: LGTM!The
isValidNonNestedRoutefunction correctly implements the validation logic for experimental non-nested routes by:
- Returning
falseearly when the feature flag is not enabled- Checking if the last segment ends with an underscore (excluding
indexTokenandrouteToken)- Iterating through intermediate segments in reverse to find trailing underscores
- Excluding the
rootPathIdfrom being considered non-nestedThe logic correctly identifies non-nested routes based on trailing underscore markers while respecting the configuration tokens.
|
test failure is due to the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
packages/router-generator/tests/generator.test.ts (1)
117-118: Fix the path construction to match line 49.Line 118 is missing a dot after
routeTree, causing inconsistent naming. WhennonNestedistrue, the path becomes/routeTreenonnested..gen.js(missing separator dot, double dot before gen), while line 49 correctly produces/routeTree.nonnested.gen.ts.Apply this diff to fix the path construction:
config.generatedRouteTree = makeFolderDir(folderName) + - `/routeTree${nonNested ? 'nonnested.' : ''}.gen.js` + `/routeTree.${nonNested ? 'nonnested.' : ''}gen.js`
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/router-generator/src/utils.ts(15 hunks)packages/router-generator/tests/generator.test.ts(5 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use TypeScript in strict mode with extensive type safety across the codebase
Files:
packages/router-generator/tests/generator.test.tspackages/router-generator/src/utils.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-generator/tests/generator.test.tspackages/router-generator/src/utils.ts
🧬 Code graph analysis (2)
packages/router-generator/tests/generator.test.ts (1)
packages/router-generator/src/config.ts (2)
Config(66-66)getConfig(76-151)
packages/router-generator/src/utils.ts (3)
packages/router-generator/src/config.ts (1)
Config(66-66)packages/router-generator/src/index.ts (1)
rootPathId(45-45)packages/router-generator/src/types.ts (1)
RouteNode(1-18)
🪛 ast-grep (0.39.5)
packages/router-generator/src/utils.ts
[warning] 209-209: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp((?<=^|\\/)_(?!${escapedRouteToken}), 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
[warning] 225-225: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp((?<!${escapedRouteToken})_(?=\\/|$), 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
🔇 Additional comments (7)
packages/router-generator/tests/generator.test.ts (2)
249-287: LGTM! Comprehensive test coverage for both modes.The test structure correctly generates test cases for both
nonNested: trueandnonNested: falsescenarios across all folders. The snapshot path construction consistently uses the correct pattern, and thenonNestedparameter is properly threaded throughsetupConfigandrewriteConfigByFolderName.
289-343: LGTM! Directory creation test properly covers both modes.The directory creation test correctly handles both
nonNestedscenarios with proper cleanup logic and snapshot path construction matching the pattern established in the main test.packages/router-generator/src/utils.ts (5)
57-144: LGTM! Well-structured refactoring with backward compatibility.The function correctly:
- Returns a structured object with
routePath,isExperimentalNonNestedRoute, andoriginalRoutePath- Gates the non-nested route handling behind
config?.experimental?.nonNestedRoutes- Removes trailing underscores early (before escaping) to preserve escaped trailing underscores
- Maintains backward compatibility when the feature flag is disabled
The comment at lines 82-83 notes the TODO for renaming when no longer experimental, which is appropriate.
196-214: LGTM! Standard regex escaping with proper safeguards.The
escapeRegExpfunction provides standard protection against regex injection.removeLeadingUnderscorescorrectly handles the edge case whererouteTokenitself starts with an underscore by excluding it from the removal pattern.Note: The static analysis tool flags line 209 as a potential ReDoS risk. However, this is a false positive since
routeTokencomes from the developer-controlled Config object (not runtime user input) and is properly escaped.
481-552: LGTM! Consistent config propagation pattern.The config parameter is correctly threaded through:
inferFullPath→ uses config to determine underscore removal strategycreateRouteNodesByFullPath→ passes config toinferFullPathcreateRouteNodesByTo→ passes config toinferToinferTo→ delegates toinferFullPathwith configThe optional parameter approach maintains backward compatibility while enabling the new experimental feature.
791-837: LGTM! Clear validation logic for non-nested routes.The
isValidNonNestedRoutefunction correctly:
- Gates the check behind the experimental feature flag
- Excludes special cases (root route, empty paths)
- Identifies non-nested routes by checking for trailing underscores in segments
- Excludes
indexTokenandrouteTokenfrom the trailing underscore check- Properly handles multi-segment paths by walking backwards
The logic aligns with the file-based routing convention where trailing underscores denote pathless routes.
360-440: Add unit tests for non-nested parent route resolution.No existing tests cover the
_isExperimentalNonNestedRoutebranch inhasParentRoute. Add tests for:
- Multiple levels of non-nested routes
- Mixed nested and non-nested routes
- Segments with escaped underscores
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
packages/router-generator/tests/generator.test.ts (1)
43-64: LGTM! Clean parameter addition.The
nonNestedparameter is properly threaded through the config setup, with correct destructuring and default value handling.packages/router-generator/src/utils.ts (1)
218-234: LGTM! Correct implementation of removeTrailingUnderscores.The function correctly checks if the route token ends with an underscore using
routeToken.slice(-1) === '_'at line 221 (the past review comment flagging line 225 is outdated). The regex construction with negative lookbehind properly excludes the route token from removal.Note: The static analysis warning about ReDoS at line 230 is a false positive since
routeTokenis developer-controlled configuration.
🧹 Nitpick comments (1)
packages/router-generator/src/utils.ts (1)
76-76: Consider extracting splitRegex to module level.The
splitRegexpattern/(?<!\[)\.(?!\])/gis defined identically at both lines 76 and 106 (after variable assignment). Since this regex is used consistently throughout the function, consider extracting it to a module-level constant for maintainability.Apply this diff to extract the regex:
const BRACKET_CONTENT_RE = /\[(.*?)\]/g +const SPLIT_REGEX = /(?<!\[)\.(?!\])/g export function determineInitialRoutePath( routePath: string, config?: Pick<Config, 'experimental' | 'routeToken' | 'indexToken'>, ) { ... - const splitRegex = /(?<!\[)\.(?!\])/g - const originalRoutePath = - cleanPath(`/${(cleanPath(routePath) || '').split(splitRegex).join('/')}`) || + cleanPath(`/${(cleanPath(routePath) || '').split(SPLIT_REGEX).join('/')}`) || '' ... - const parts = cleanedRoutePath.split(splitRegex) + const parts = cleanedRoutePath.split(SPLIT_REGEX)Also applies to: 106-106
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/router-generator/src/utils.ts(15 hunks)packages/router-generator/tests/generator.test.ts(5 hunks)packages/router-generator/tests/utils.test.ts(8 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use TypeScript in strict mode with extensive type safety across the codebase
Files:
packages/router-generator/tests/generator.test.tspackages/router-generator/src/utils.tspackages/router-generator/tests/utils.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-generator/tests/generator.test.tspackages/router-generator/src/utils.tspackages/router-generator/tests/utils.test.ts
🧬 Code graph analysis (3)
packages/router-generator/tests/generator.test.ts (1)
packages/router-generator/src/config.ts (2)
Config(66-66)getConfig(76-151)
packages/router-generator/src/utils.ts (3)
packages/router-generator/src/config.ts (1)
Config(66-66)packages/router-generator/src/index.ts (1)
rootPathId(45-45)packages/router-generator/src/types.ts (1)
RouteNode(1-18)
packages/router-generator/tests/utils.test.ts (2)
packages/router-generator/src/utils.ts (5)
determineInitialRoutePath(57-144)removeLeadingUnderscores(200-216)removeTrailingUnderscores(218-234)routePathToVariable(150-190)isValidNonNestedRoute(801-841)packages/router-generator/src/types.ts (1)
ImportDeclaration(49-53)
🪛 ast-grep (0.39.5)
packages/router-generator/src/utils.ts
[warning] 211-211: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp((?<=^|\\/)_(?!${escapedRouteToken}), 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
[warning] 229-229: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp((?<!${escapedRouteToken})_(?=\\/|$), 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
⏰ 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: Test
- GitHub Check: Preview
🔇 Additional comments (11)
packages/router-generator/tests/utils.test.ts (4)
22-32: LGTM! Excellent use of parameterized tests.The
describe.eachpattern effectively tests both experimental non-nested and default modes, ensuring consistent behavior across configurations.
90-102: LGTM! Proper error handling test pattern.The test correctly uses
vi.spyOnto verify error messages and properly restores the console spy afterward.
118-173: LGTM! Comprehensive trailing underscore test coverage.The tests thoroughly cover various edge cases including route tokens with leading/trailing underscores, escaped underscores, and different path structures.
373-405: LGTM! Good coverage of non-nested route validation.The tests verify both valid and invalid non-nested routes, and confirm that the feature is properly gated behind the experimental flag.
packages/router-generator/tests/generator.test.ts (2)
72-160: LGTM! Consistent non-nested handling across test cases.The
rewriteConfigByFolderNamefunction correctly propagates thenonNestedparameter and updates file paths accordingly for each test case.
246-287: LGTM! Effective test parameterization.The
flatMappattern creates comprehensive test coverage by running each folder test with bothnonNested: trueandnonNested: false, ensuring backward compatibility while testing the new feature.packages/router-generator/src/utils.ts (5)
57-144: LGTM! Well-structured refactor of determineInitialRoutePath.The function now returns an object with
routePath,isExperimentalNonNestedRoute, andoriginalRoutePath, which provides callers with complete context about the path transformation. The early detection and removal of trailing underscores (lines 91-104) before escaping is the correct approach to preserve escaped underscores.
196-216: LGTM! Correct implementation of removeLeadingUnderscores.The function properly handles route tokens that start with underscores by excluding them from removal. The regex construction with negative lookahead is correct.
Note: The static analysis warning about ReDoS at line 212 is a false positive since
routeTokenis developer-controlled configuration.
364-444: Complex but correct non-nested route parent resolution.The
hasParentRoutefunction handles non-nested routes by:
- Bypassing standard parent lookup when
_isExperimentalNonNestedRouteis true (lines 393-395)- Using
getNonNestedSegmentshelper to identify non-nested segments (lines 370-382)- Special logic to find parents among other non-nested routes or layout/pathless routes (lines 397-426)
The logic is intricate but appears correct for the feature requirements.
485-498: LGTM! Config-driven path inference.The
inferFullPathfunction now conditionally appliesremoveLayoutSegmentsorremoveUnderscores + removeLayoutSegmentsbased on the experimental flag, properly supporting both modes.
801-841: LGTM! Clear non-nested route validation logic.The
isValidNonNestedRoutefunction correctly:
- Guards against disabled feature (lines 805-807)
- Validates segment structure (lines 809-813)
- Excludes
__root(lines 818-820)- Checks for trailing underscores in the last segment (lines 822-828)
- Traverses parent segments checking for non-nested patterns (lines 830-838)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/router-generator/.prettierignore(1 hunks)packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/router-generator/.prettierignore
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use TypeScript in strict mode with extensive type safety across the codebase
Files:
packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.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-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts
🧠 Learnings (1)
📚 Learning: 2025-10-01T18:31:35.420Z
Learnt from: schiller-manuel
PR: TanStack/router#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-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts
🧬 Code graph analysis (1)
packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts (2)
packages/router-generator/tests/generator/types-disabled/routeTree.nonnested.snapshot.js (1)
IndexRoute(22-26)packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.snapshot.ts (1)
FileRouteTypes(36-43)
⏰ 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: Test
- GitHub Check: Preview
🔇 Additional comments (1)
packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts (1)
11-77: Non-nested routing structure is correctly implemented.The snapshot correctly demonstrates non-nested routing behavior where all routes (
IndexRoute,NestedIndexRoute,NestedChildRoute) are direct children of the root route rather than being nested. The type interfaces, module augmentation, and route tree assembly follow the expected pattern for generated route trees.
...ges/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts
Show resolved
Hide resolved
# Conflicts: # packages/router-core/src/path.ts # packages/router-generator/src/generator.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/router-generator/tests/generator/route-groups/routeTree.nonnested.snapshot.ts (1)
7-10: Generated snapshot — LGTM; no code changes requested.Content aligns with the non-nested route behavior and types. Please ensure CI’s “automated fixes” step excludes these snapshot files from formatting so snapshots remain stable (e.g., add the tests/generator routeTree*.snapshot.* patterns to your formatter ignore).
Based on learnings
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/router-generator/tests/generator/nested-route-groups-with-layouts-before-physical/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/path-above-route-in-group/routeTree.nonnested.snapshot.ts(1 hunks)packages/router-generator/tests/generator/route-groups/routeTree.nonnested.snapshot.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use TypeScript in strict mode with extensive type safety across the codebase
Files:
packages/router-generator/tests/generator/nested-route-groups-with-layouts-before-physical/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/route-groups/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/path-above-route-in-group/routeTree.nonnested.snapshot.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-generator/tests/generator/nested-route-groups-with-layouts-before-physical/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/route-groups/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/path-above-route-in-group/routeTree.nonnested.snapshot.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: nlynzaad
PR: TanStack/router#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
PR: TanStack/router#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-generator/tests/generator/nested-route-groups-with-layouts-before-physical/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/route-groups/routeTree.nonnested.snapshot.tspackages/router-generator/tests/generator/path-above-route-in-group/routeTree.nonnested.snapshot.ts
📚 Learning: 2025-10-01T18:31:35.420Z
Learnt from: schiller-manuel
PR: TanStack/router#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-generator/tests/generator/route-groups/routeTree.nonnested.snapshot.ts
🧬 Code graph analysis (2)
packages/router-generator/tests/generator/nested-route-groups-with-layouts-before-physical/routeTree.nonnested.snapshot.ts (2)
packages/router-generator/tests/generator/route-groups/routeTree.nonnested.snapshot.ts (2)
FileRoutesById(100-112)routeTree(286-288)packages/router-generator/tests/generator/nested-layouts/routeTree.nonnested.snapshot.ts (2)
FileRoutesById(191-218)routeTree(635-637)
packages/router-generator/tests/generator/route-groups/routeTree.nonnested.snapshot.ts (1)
packages/router-generator/tests/generator/nested-route-groups-with-layouts-before-physical/routeTree.nonnested.snapshot.ts (6)
FileRoutesByFullPath(53-58)FileRoutesByTo(59-64)FileRoutesById(65-74)FileRouteTypes(75-90)RootRouteChildren(91-95)routeTree(194-196)
⏰ 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: Test
- GitHub Check: Preview
🔇 Additional comments (1)
packages/router-generator/tests/generator/nested-route-groups-with-layouts-before-physical/routeTree.nonnested.snapshot.ts (1)
1-196: Test snapshot file – no issues flagged.This is a generated test fixture used to verify the router-generator's output for the experimental non-nested routes feature. The file is intentionally preserved as-is to validate generator behavior.
Based on learnings
This PR reverts the work done in #5165, #5182 and #5169. These changes, although solving the original issue in #5164 introduced a slew of other problems related to trailing underscores, especially in code-based routing solutions. See #5296 and #5313.
The original problem was caused by route matching failing in the case of named params being used in conjunction with non-nested routes. This in turn was due to the trailing underscore being retained in the routeId but removed in the route path and when used with named params this would cause the match to fail.
The original attempt in resolving the issue was to resolve it at runtime and not affect the way users have been using and referencing non-nested paths. Unfortunately, this was not the right way to resolve the issue and didn't address the root cause. The root of the problem was how the non-nested indicator was being processed in the generation of the routes when using file-based routing.
A side effect of how we have been resolving non-nested paths in the generator till now was that we removed any trailing underscores from the route path and hence escaping trailing underscores was not possible.
This PR introduces changes to the generator to better deal with the non-nested indicating trailing underscores. Primarily this is done by identifying a route as a non-nested route as early as possible and then removing the trailing underscore at that point before the route is processed further. This allows us to align the routeId with the path.
By doing this ahead of escaping the required characters, we now also do not need to remove any trailing underscores that appear further in the process, which has the benefit of enabling the use of escaped trailing underscores.
Unfortunately, this change does result in a change of how non-nested paths are referenced in functions that take the routeId as an input, for example, useParams, useNavigate, getRouteApi, etc. Previously this would look like
useParams({ from: '/posts_/$postId/edit' })and now, with the removal of the trailing underscore in the routeId, this looks like:useParams({ from: '/posts/$postId/edit' }).This change in the referencing, although being more correct and more closely aligned to the actual path, does bring a change users might regard as breaking. Hence, we have opted to put this behind a feature flag in the router plugin under the experimental section:
Since this is behind a feature flag tests have been updated to reflect and test the expected behavior in both cases, where this is enabled and where it is not. This means we are testing for the expected errors where we know they now exist and the correct handling with the applied fix. This was done for router-generator, react-router/basic-file-based, solid-router/basic-file-based.
It also adds two notes to the documentation to communicate the new feature flag and escape functionality.
Summary by CodeRabbit
New Features
Documentation
Refactor
Tests / Chores