From 6ad1af5678815a000c6f349567768dd5e05fddc9 Mon Sep 17 00:00:00 2001 From: leesb971204 Date: Wed, 16 Apr 2025 09:57:51 +0900 Subject: [PATCH 1/9] fix(router-core): filter routes with children that only have pathless layout Signed-off-by: leesb971204 --- packages/router-core/src/router.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/router-core/src/router.ts b/packages/router-core/src/router.ts index e458e3e931..67178a22fc 100644 --- a/packages/router-core/src/router.ts +++ b/packages/router-core/src/router.ts @@ -1040,6 +1040,13 @@ export class RouterCore< // Sort by original index return a.index - b.index }) + .filter((d) => { + if (!d.child.children) return true + return ( + Array.from(d.child.children).length > 0 && + Array.from(d.child.children).every((c) => (c as AnyRoute).path) + ) + }) .map((d, i) => { d.child.rank = i return d.child From 5a76ed70dc8f2bc5d892d516350fb79f47f770ba Mon Sep 17 00:00:00 2001 From: leesb971204 Date: Thu, 17 Apr 2025 11:15:52 +0900 Subject: [PATCH 2/9] fix(router-core): remove filter for child routes in router Signed-off-by: leesb971204 --- packages/router-core/src/router.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/router-core/src/router.ts b/packages/router-core/src/router.ts index 67178a22fc..e458e3e931 100644 --- a/packages/router-core/src/router.ts +++ b/packages/router-core/src/router.ts @@ -1040,13 +1040,6 @@ export class RouterCore< // Sort by original index return a.index - b.index }) - .filter((d) => { - if (!d.child.children) return true - return ( - Array.from(d.child.children).length > 0 && - Array.from(d.child.children).every((c) => (c as AnyRoute).path) - ) - }) .map((d, i) => { d.child.rank = i return d.child From c641bce7d5f4ce53fdeda479d7116bb5574408fd Mon Sep 17 00:00:00 2001 From: leesb971204 Date: Thu, 17 Apr 2025 11:16:49 +0900 Subject: [PATCH 3/9] fix(router-generator): include layout type in isNonPath node check Signed-off-by: leesb971204 --- packages/router-generator/src/generator.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/router-generator/src/generator.ts b/packages/router-generator/src/generator.ts index b7c579daec..60be269ea0 100644 --- a/packages/router-generator/src/generator.ts +++ b/packages/router-generator/src/generator.ts @@ -214,7 +214,8 @@ export async function generator(config: Config, root: string) { node.isNonPath = lastRouteSegment.startsWith('_') || - routeGroupPatternRegex.test(lastRouteSegment) + routeGroupPatternRegex.test(lastRouteSegment) || + node._fsRouteType === 'layout' node.cleanedPath = removeGroups( removeUnderscores(removeLayoutSegments(node.path)) ?? '', From fc20f4a60083a4cc2c310c5932414e508618faf9 Mon Sep 17 00:00:00 2001 From: leesb971204 Date: Thu, 17 Apr 2025 13:02:21 +0900 Subject: [PATCH 4/9] fix(router-generator): add logic to set isNonPath for nodes with children that are all pathless layouts Signed-off-by: leesb971204 --- packages/router-generator/src/generator.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/router-generator/src/generator.ts b/packages/router-generator/src/generator.ts index 60be269ea0..15c6d6f2f2 100644 --- a/packages/router-generator/src/generator.ts +++ b/packages/router-generator/src/generator.ts @@ -214,8 +214,7 @@ export async function generator(config: Config, root: string) { node.isNonPath = lastRouteSegment.startsWith('_') || - routeGroupPatternRegex.test(lastRouteSegment) || - node._fsRouteType === 'layout' + routeGroupPatternRegex.test(lastRouteSegment) node.cleanedPath = removeGroups( removeUnderscores(removeLayoutSegments(node.path)) ?? '', @@ -410,6 +409,17 @@ export async function generator(config: Config, root: string) { for (const node of onlyGeneratorRouteNodes) { await handleNode(node) } + + for (const node of routeNodes) { + if (node.children && node.children.length > 0) { + const allPathlessLayout = node.children.every( + (child) => child._fsRouteType === 'pathless_layout', + ) + + node.isNonPath = allPathlessLayout + } + } + checkRouteFullPathUniqueness( preRouteNodes.filter( (d) => From a5bf820419e07cce335af374f3642b4ddeac6fe7 Mon Sep 17 00:00:00 2001 From: leesb971204 Date: Thu, 17 Apr 2025 15:31:46 +0900 Subject: [PATCH 5/9] fix(router-generator): enhance isNonPath logic for parent nodes and add updateIsNonPath utility function Signed-off-by: leesb971204 --- packages/router-generator/src/generator.ts | 32 +++++++++++++++------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/packages/router-generator/src/generator.ts b/packages/router-generator/src/generator.ts index 15c6d6f2f2..8495365b5d 100644 --- a/packages/router-generator/src/generator.ts +++ b/packages/router-generator/src/generator.ts @@ -403,6 +403,14 @@ export async function generator(config: Config, root: string) { routeTree.push(node) } + if (node.children && node.children.length > 0) { + node.isNonPath = updateIsNonPath(node) + } + + if (node.parent) { + node.parent.isNonPath = updateIsNonPath(node.parent) + } + routeNodes.push(node) } @@ -410,16 +418,6 @@ export async function generator(config: Config, root: string) { await handleNode(node) } - for (const node of routeNodes) { - if (node.children && node.children.length > 0) { - const allPathlessLayout = node.children.every( - (child) => child._fsRouteType === 'pathless_layout', - ) - - node.isNonPath = allPathlessLayout - } - } - checkRouteFullPathUniqueness( preRouteNodes.filter( (d) => @@ -1105,3 +1103,17 @@ export function startAPIRouteSegmentsFromTSRFilePath( return segments } + +/** + * Only true if all children are pathless_layout or + * if children are of different types but all their children are pathless_layout + * @param node + * @returns + */ +export const updateIsNonPath = (node: RouteNode) => { + return node.children?.every( + (child) => + child._fsRouteType === 'pathless_layout' || + (child.children?.length && child.isNonPath), + ) +} From fcbc6bd0cad7a7d7868d291d0500770c2ddaf27e Mon Sep 17 00:00:00 2001 From: leesb971204 Date: Fri, 18 Apr 2025 17:14:26 +0900 Subject: [PATCH 6/9] fix(router-generator): add check for pathless_layout first in updateIsNonPath function Signed-off-by: leesb971204 --- packages/router-generator/src/generator.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/router-generator/src/generator.ts b/packages/router-generator/src/generator.ts index 8495365b5d..4d33a68614 100644 --- a/packages/router-generator/src/generator.ts +++ b/packages/router-generator/src/generator.ts @@ -1111,6 +1111,7 @@ export function startAPIRouteSegmentsFromTSRFilePath( * @returns */ export const updateIsNonPath = (node: RouteNode) => { + if (node._fsRouteType === 'pathless_layout') return true return node.children?.every( (child) => child._fsRouteType === 'pathless_layout' || From 2cbf653deb1c597961250521becae85d4484181e Mon Sep 17 00:00:00 2001 From: leesb971204 Date: Fri, 18 Apr 2025 17:27:44 +0900 Subject: [PATCH 7/9] fix(router-generator): update determineNodePath to handle pathless_layout correctly Signed-off-by: leesb971204 --- packages/router-generator/src/generator.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/router-generator/src/generator.ts b/packages/router-generator/src/generator.ts index 4d33a68614..553aae45cf 100644 --- a/packages/router-generator/src/generator.ts +++ b/packages/router-generator/src/generator.ts @@ -844,7 +844,12 @@ function removeGroups(s: string) { */ function determineNodePath(node: RouteNode) { return (node.path = node.parent - ? node.routePath?.replace(node.parent.routePath ?? '', '') || '/' + ? node.routePath?.replace( + node.parent._fsRouteType === 'pathless_layout' + ? (node.parent.path ?? '') + : (node.parent.routePath ?? ''), + '', + ) || '/' : node.routePath) } From 415b8211c0ba04c831b9c269d2cad90013f0fcae Mon Sep 17 00:00:00 2001 From: leesb971204 Date: Fri, 2 May 2025 11:28:01 +0900 Subject: [PATCH 8/9] fix(router-generator): refactor isNonPath condition and determineNodePath Signed-off-by: leesb971204 --- packages/router-generator/src/generator.ts | 32 +++++----------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/packages/router-generator/src/generator.ts b/packages/router-generator/src/generator.ts index 553aae45cf..1877f68c71 100644 --- a/packages/router-generator/src/generator.ts +++ b/packages/router-generator/src/generator.ts @@ -402,13 +402,12 @@ export async function generator(config: Config, root: string) { } else { routeTree.push(node) } - - if (node.children && node.children.length > 0) { - node.isNonPath = updateIsNonPath(node) - } - - if (node.parent) { - node.parent.isNonPath = updateIsNonPath(node.parent) + if ( + node._fsRouteType === 'layout' && + node.isVirtual && + node.isVirtualParentRoute + ) { + node.isNonPath = node.children?.every((d) => d.isNonPath) } routeNodes.push(node) @@ -845,9 +844,7 @@ function removeGroups(s: string) { function determineNodePath(node: RouteNode) { return (node.path = node.parent ? node.routePath?.replace( - node.parent._fsRouteType === 'pathless_layout' - ? (node.parent.path ?? '') - : (node.parent.routePath ?? ''), + node.parent.isNonPath ? '' : (node.parent.routePath ?? ''), '', ) || '/' : node.routePath) @@ -1108,18 +1105,3 @@ export function startAPIRouteSegmentsFromTSRFilePath( return segments } - -/** - * Only true if all children are pathless_layout or - * if children are of different types but all their children are pathless_layout - * @param node - * @returns - */ -export const updateIsNonPath = (node: RouteNode) => { - if (node._fsRouteType === 'pathless_layout') return true - return node.children?.every( - (child) => - child._fsRouteType === 'pathless_layout' || - (child.children?.length && child.isNonPath), - ) -} From c495f0968c5513632b1f5407ca5755214fcb2def Mon Sep 17 00:00:00 2001 From: leesb971204 Date: Tue, 13 May 2025 11:05:07 +0900 Subject: [PATCH 9/9] fix(router-generator): add virtual index route creation for empty layout routes Signed-off-by: leesb971204 --- packages/router-generator/src/generator.ts | 75 ++++++++++++++++++---- packages/router-generator/src/types.ts | 2 + 2 files changed, 63 insertions(+), 14 deletions(-) diff --git a/packages/router-generator/src/generator.ts b/packages/router-generator/src/generator.ts index 1877f68c71..2a751ec7ce 100644 --- a/packages/router-generator/src/generator.ts +++ b/packages/router-generator/src/generator.ts @@ -402,13 +402,6 @@ export async function generator(config: Config, root: string) { } else { routeTree.push(node) } - if ( - node._fsRouteType === 'layout' && - node.isVirtual && - node.isVirtualParentRoute - ) { - node.isNonPath = node.children?.every((d) => d.isNonPath) - } routeNodes.push(node) } @@ -417,6 +410,29 @@ export async function generator(config: Config, root: string) { await handleNode(node) } + // Detect empty layout routes and add a virtual index route + const layoutRoutesWithoutIndex = routeNodes.filter((node) => { + if (node._fsRouteType !== 'layout') return false + + if (node.children && node.children.length > 0) { + if (node.children.some((child) => child.cleanedPath === '/')) { + return false + } + } + return true + }) + + // Add a virtual index route to empty layout routes + for (const layoutRoute of layoutRoutesWithoutIndex) { + const virtualIndexRoute = createVirtualIndexRouteWithRedirect(layoutRoute) + routeNodes.push(virtualIndexRoute) + if (!layoutRoute.children) { + layoutRoute.children = [] + } + layoutRoute.children.push(virtualIndexRoute) + virtualIndexRoute.parent = layoutRoute + } + checkRouteFullPathUniqueness( preRouteNodes.filter( (d) => @@ -537,6 +553,7 @@ export async function generator(config: Config, root: string) { const imports = Object.entries({ createFileRoute: sortedRouteNodes.some((d) => d.isVirtual), + redirect: routeNodes.some((node) => node.isVirtualRedirectIndex), lazyFn: sortedRouteNodes.some( (node) => routePiecesByPath[node.routePath!]?.loader, ), @@ -585,9 +602,14 @@ export async function generator(config: Config, root: string) { virtualRouteNodes.length ? '// Create Virtual Routes' : '', virtualRouteNodes .map((node) => { - return `const ${ - node.variableName - }Import = createFileRoute('${node.routePath}')()` + if (node.isVirtualRedirectIndex) { + return `const ${node.variableName}Import = createFileRoute('${node.routePath}')({ + loader: () => { + redirect({ to: '/', throw: true }) + } +})` + } + return `const ${node.variableName}Import = createFileRoute('${node.routePath}')()` }) .join('\n'), '// Create/Update Routes', @@ -843,10 +865,7 @@ function removeGroups(s: string) { */ function determineNodePath(node: RouteNode) { return (node.path = node.parent - ? node.routePath?.replace( - node.parent.isNonPath ? '' : (node.parent.routePath ?? ''), - '', - ) || '/' + ? node.routePath?.replace(node.parent.routePath ?? '', '') || '/' : node.routePath) } @@ -1105,3 +1124,31 @@ export function startAPIRouteSegmentsFromTSRFilePath( return segments } + +/** + * Creates a virtual index route with a redirect to the root route. + * + * @param parentRoute - The parent layout route that does not have an index route. + * @returns The created virtual index route node. + */ +export const createVirtualIndexRouteWithRedirect = ( + parentRoute: RouteNode, +): RouteNode => { + const indexRoutePath = `${parentRoute.routePath}/` + const variableName = routePathToVariable(`${indexRoutePath}index`) + + const virtualIndexRoute: RouteNode = { + filePath: '', + fullPath: '', + variableName, + routePath: indexRoutePath, + path: '/', + cleanedPath: '/', + isVirtual: true, + _fsRouteType: 'static', + isVirtualRedirectIndex: true, + redirectTo: '/', + } + + return virtualIndexRoute +} diff --git a/packages/router-generator/src/types.ts b/packages/router-generator/src/types.ts index a8ba30feee..c4a8b34e17 100644 --- a/packages/router-generator/src/types.ts +++ b/packages/router-generator/src/types.ts @@ -12,6 +12,8 @@ export type RouteNode = { isVirtual?: boolean children?: Array parent?: RouteNode + isVirtualRedirectIndex?: boolean + redirectTo?: string } export interface GetRouteNodesResult {