-
-
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
base: main
Are you sure you want to change the base?
Conversation
View your CI Pipeline Execution ↗ for commit 43220cf
☁️ Nx Cloud last updated this comment at |
WalkthroughEnables an experimental nonNestedRoutes flow across docs, e2e harnesses (React/Solid), router-generator, and router-core: adds docs and test-mode flags, threads an experimental flag through generator APIs and snapshots, refactors router-core path parsing to centralize via parsePathname, and adds many non-nested snapshots/tests and helper utilities. 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: run test:e2e(:nonnested|:default)
PW->>PW: resolve mode via env/useExperimentalNonNestedRoutes
PW->>Vite: start server with MODE/VITE_MODE/PORT env
Vite->>Plugin: initialize plugin with experimental.nonNestedRoutes=(MODE==='nonnested')
Plugin->>App: build/serve routes honoring nonNested flag
Dev->>App: execute e2e tests (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)
Util-->>Gen: resolved parent (non-nested aware)
Gen->>Gen: generate snapshots/types using config-driven underscore handling
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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-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. Sincepathname
isn’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
reduce
logic is correct but aflatMap
or 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()).pathname
packages/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
basePathValues
later alters parsing, caching solely bypathname
will mix results across modes.
- Either incorporate
basePathValues
into the cache key, or maintain separate caches per mode.- If
basePathValues
is 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.ts
e2e/react-router/basic-file-based/tests/utils/useExperimentalNonNestedRoutes.ts
e2e/react-router/basic-file-based/playwright.config.ts
e2e/react-router/basic-file-based/src/routes/posts_.$postId.edit.tsx
e2e/react-router/basic-file-based/tests/non-nested-paths.spec.ts
e2e/react-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/$bar.tsx
e2e/solid-router/basic-file-based/playwright.config.ts
packages/router-generator/src/types.ts
packages/router-generator/tests/generator/routeFileIgnore/routeTree.nonnested.snapshot.ts
e2e/solid-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/$bar.tsx
e2e/solid-router/basic-file-based/tests/non-nested-paths.spec.ts
packages/router-generator/tests/generator/only-root/routeTree.nonnested.generated.snapshot.ts
packages/router-generator/tests/generator/types-disabled/routes/__root.tsx
packages/router-core/src/path.ts
packages/router-generator/tests/generator/file-modification-verboseFileRoutes-false/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/nested-route-groups-with-layouts-before-physical/routeTree.nonnested.snapshot.ts
packages/router-generator/src/config.ts
packages/router-generator/tests/generator/nested-verboseFileRoutes-true/tests.nonnested.test-d.ts
packages/router-generator/tests/generator/nested-verboseFileRoutes-true/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/nested-verboseFileRoutes-false/tests.nonnested.test-d.ts
packages/router-generator/tests/generator/routeFilePrefix/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/append-and-prepend/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/file-modification-verboseFileRoutes-true/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/virtual/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/nested-layouts/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/virtual-config-file-default-export/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/export-variations/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/no-duplicate-route-segment/routeTree.nonnested.snapshot.ts
packages/router-generator/src/filesystem/physical/getRouteNodes.ts
packages/router-generator/src/utils.ts
packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/path-above-route-in-group/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/single-level/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/custom-tokens/routeTree.nonnested.snapshot.ts
e2e/solid-router/basic-file-based/tests/params.spec.ts
packages/router-generator/tests/generator/numbers-in-path/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/deny-route-group-config.test.ts
packages/router-generator/tests/generator.test.ts
packages/router-generator/tests/generator/types-disabled/routes/posts.tsx
e2e/solid-router/basic-file-based/src/routes/posts_.$postId.edit.tsx
e2e/react-router/basic-file-based/tests/params.spec.ts
packages/router-generator/tests/generator/virtual-inside-nested/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/types-disabled/routes/users.$userId.tsx
packages/router-generator/tests/generator/prefix-suffix/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/custom-scaffolding/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/only-root/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/types-disabled/routes/index.tsx
packages/router-core/src/process-route-tree.ts
packages/router-generator/tests/generator/nested-verboseFileRoutes-false/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/dot-escaped/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/route-groups/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/types-disabled/routes/posts/$postId.tsx
packages/router-generator/tests/generator/virtual-config-file-named-export/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/flat/routeTree.nonnested.snapshot.ts
packages/router-generator/src/generator.ts
packages/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.ts
e2e/react-router/basic-file-based/vite.config.js
e2e/react-router/basic-file-based/tests/utils/useExperimentalNonNestedRoutes.ts
e2e/react-router/basic-file-based/playwright.config.ts
e2e/react-router/basic-file-based/src/routes/posts_.$postId.edit.tsx
e2e/react-router/basic-file-based/tests/non-nested-paths.spec.ts
e2e/react-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/$bar.tsx
e2e/solid-router/basic-file-based/playwright.config.ts
e2e/solid-router/basic-file-based/package.json
e2e/react-router/basic-file-based/package.json
e2e/solid-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/$bar.tsx
e2e/solid-router/basic-file-based/tests/non-nested-paths.spec.ts
e2e/solid-router/basic-file-based/tests/params.spec.ts
e2e/solid-router/basic-file-based/vite.config.js
e2e/solid-router/basic-file-based/src/routes/posts_.$postId.edit.tsx
e2e/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.md
docs/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.md
docs/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.tsx
e2e/react-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/$bar.tsx
e2e/solid-router/basic-file-based/src/routes/params-ps/non-nested/$foo_/$bar.tsx
e2e/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.js
packages/router-generator/src/types.ts
packages/router-generator/tests/generator/routeFileIgnore/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/only-root/routeTree.nonnested.generated.snapshot.ts
packages/router-generator/tests/generator/types-disabled/routes/__root.tsx
packages/router-generator/tests/generator/file-modification-verboseFileRoutes-false/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/nested-route-groups-with-layouts-before-physical/routeTree.nonnested.snapshot.ts
packages/router-generator/src/config.ts
packages/router-generator/tests/generator/nested-verboseFileRoutes-true/tests.nonnested.test-d.ts
packages/router-generator/tests/generator/nested-verboseFileRoutes-true/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/nested-verboseFileRoutes-false/tests.nonnested.test-d.ts
packages/router-generator/tests/generator/routeFilePrefix/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/append-and-prepend/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/file-modification-verboseFileRoutes-true/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/virtual/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/nested-layouts/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/virtual-config-file-default-export/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/export-variations/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/no-duplicate-route-segment/routeTree.nonnested.snapshot.ts
packages/router-generator/src/filesystem/physical/getRouteNodes.ts
packages/router-generator/src/utils.ts
packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/path-above-route-in-group/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/single-level/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/custom-tokens/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/numbers-in-path/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/deny-route-group-config.test.ts
packages/router-generator/tests/generator.test.ts
packages/router-generator/tests/generator/types-disabled/routes/posts.tsx
packages/router-generator/tests/generator/virtual-inside-nested/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/types-disabled/routes/users.$userId.tsx
packages/router-generator/tests/generator/prefix-suffix/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/custom-scaffolding/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/only-root/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/types-disabled/routes/index.tsx
packages/router-generator/tests/generator/nested-verboseFileRoutes-false/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/dot-escaped/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/route-groups/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/types-disabled/routes/posts/$postId.tsx
packages/router-generator/tests/generator/virtual-config-file-named-export/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/flat/routeTree.nonnested.snapshot.ts
packages/router-generator/src/generator.ts
packages/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.json
e2e/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.ts
packages/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.js
packages/router-generator/tests/generator/routeFileIgnore/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/only-root/routeTree.nonnested.generated.snapshot.ts
packages/router-generator/tests/generator/file-modification-verboseFileRoutes-false/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/nested-verboseFileRoutes-true/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/routeFilePrefix/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/append-and-prepend/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/file-modification-verboseFileRoutes-true/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/virtual/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/no-duplicate-route-segment/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/path-above-route-in-group/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/single-level/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/custom-tokens/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/custom-scaffolding/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/only-root/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/route-groups/routeTree.nonnested.snapshot.ts
packages/router-generator/tests/generator/virtual-config-file-named-export/routeTree.nonnested.snapshot.ts
packages/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
useExperimentalNonNestedRoutes
identifier 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 likeisExperimentalNonNestedRoutesEnabled
orEXPERIMENTAL_NON_NESTED_ROUTES
to avoid confusion.packages/router-generator/src/utils.ts (2)
196-212
: Consider the regex complexity and potential alternatives.The
removeLeadingUnderscores
function constructs regexes dynamically fromrouteToken
. WhileescapeRegExp
mitigates 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
escapeRegExp
call, but the complexity warrants attention.
214-226
: Consider the regex complexity and potential alternatives.The
removeTrailingUnderscores
function has similar complexity toremoveLeadingUnderscores
, using dynamic regex construction with negative lookbehind:
- Line 222:
(?<!${escapeRegExp(routeTokenToExclude)})_(?=\\/|$)
The same maintainability and performance considerations apply. While
escapeRegExp
mitigates 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
escapeRegExp
call, 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.ts
packages/router-generator/src/utils.ts
e2e/solid-router/basic-file-based/src/routes/posts_.$postId.edit.tsx
packages/router-generator/src/generator.ts
packages/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.ts
packages/router-generator/src/utils.ts
packages/router-generator/src/generator.ts
packages/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
useExperimentalNonNestedRoutes
is appropriate for testing both feature-flag modes in this e2e test file.
8-11
: Runtime-conditional routes are test-only
Confirmed that conditionalgetRouteApi
anduseParams
patterns appear only undere2e/
, not in production code.packages/router-generator/tests/generator.test.ts (4)
43-64
: LGTM!The
nonNested
parameter 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
flatMap
to run each test with bothnonNested: true
andnonNested: false
is a clean approach for comprehensive coverage.
254-287
: LGTM!The test orchestration correctly passes the
nonNested
flag through the configuration pipeline and adjusts snapshot paths accordingly.
289-343
: LGTM!The directory creation test correctly handles both
nonNested
modes 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
determineInitialRoutePath
correctly handles the updated return type, destructuring all three fields (routePath
,originalRoutePath
,isExperimentalNonNestedRoute
) from the result.
196-204
: LGTM!The addition of
_isExperimentalNonNestedRoute
andoriginalRoutePath
to 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
, andbuildFileRoutesByPathInterface
to enable config-driven path generation for the TypeScript interface types.
1190-1246
: LGTM!The
handleNode
method correctly implements the experimental non-nested routes behavior by conditionally applyingremoveLeadingUnderscores
instead ofremoveUnderscores
. The logic properly:
- Derives the experimental flag from config
- Passes
originalRoutePath
tohasParentRoute
for 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
handleNode
calls correctly pass theconfig
parameter 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
determineInitialRoutePath
correctly implements the experimental non-nested routes feature by:
- Preserving the
originalRoutePath
before 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
hasParentRoute
function correctly implements the experimental non-nested routes feature with custom parent resolution logic. The key changes:
- Adds
originalRoutePathToCheck
parameter to track the original path before transformations- Introduces
getNonNestedSegments
to 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
inferFullPath
function correctly implements the experimental behavior by:
- Preserving trailing underscores when the experimental flag is enabled (using
removeLayoutSegments
only)- 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
, andinferTo
correctly thread the optionalconfig
parameter through the path inference pipeline, enabling config-driven path generation.
715-741
: LGTM!The
buildFileRoutesByPathInterface
function correctly accepts the optionalconfig
parameter and passes it toinferFullPath
(line 733) for accurate type generation.
787-833
: LGTM!The
isValidNonNestedRoute
function correctly implements the validation logic for experimental non-nested routes by:
- Returning
false
early when the feature flag is not enabled- Checking if the last segment ends with an underscore (excluding
indexToken
androuteToken
)- Iterating through intermediate segments in reverse to find trailing underscores
- Excluding the
rootPathId
from 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. WhennonNested
istrue
, 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.ts
packages/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.ts
packages/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: true
andnonNested: false
scenarios across all folders. The snapshot path construction consistently uses the correct pattern, and thenonNested
parameter is properly threaded throughsetupConfig
andrewriteConfigByFolderName
.
289-343
: LGTM! Directory creation test properly covers both modes.The directory creation test correctly handles both
nonNested
scenarios 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
escapeRegExp
function provides standard protection against regex injection.removeLeadingUnderscores
correctly handles the edge case whererouteToken
itself 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
routeToken
comes 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 toinferFullPath
createRouteNodesByTo
→ passes config toinferTo
inferTo
→ delegates toinferFullPath
with configThe optional parameter approach maintains backward compatibility while enabling the new experimental feature.
791-837
: LGTM! Clear validation logic for non-nested routes.The
isValidNonNestedRoute
function 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
indexToken
androuteToken
from 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
_isExperimentalNonNestedRoute
branch 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
nonNested
parameter 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
routeToken
is developer-controlled configuration.
🧹 Nitpick comments (1)
packages/router-generator/src/utils.ts (1)
76-76
: Consider extracting splitRegex to module level.The
splitRegex
pattern/(?<!\[)\.(?!\])/g
is 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.ts
packages/router-generator/src/utils.ts
packages/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.ts
packages/router-generator/src/utils.ts
packages/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.each
pattern 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.spyOn
to 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
rewriteConfigByFolderName
function correctly propagates thenonNested
parameter and updates file paths accordingly for each test case.
246-287
: LGTM! Effective test parameterization.The
flatMap
pattern creates comprehensive test coverage by running each folder test with bothnonNested: true
andnonNested: 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
routeToken
is developer-controlled configuration.
364-444
: Complex but correct non-nested route parent resolution.The
hasParentRoute
function handles non-nested routes by:
- Bypassing standard parent lookup when
_isExperimentalNonNestedRoute
is true (lines 393-395)- Using
getNonNestedSegments
helper 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
inferFullPath
function now conditionally appliesremoveLayoutSegments
orremoveUnderscores + removeLayoutSegments
based on the experimental flag, properly supporting both modes.
801-841
: LGTM! Clear non-nested route validation logic.The
isValidNonNestedRoute
function 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.
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