Skip to content

Commit

Permalink
fix: Router CLI can now handle "virtual" code split routes with no an…
Browse files Browse the repository at this point in the history
…chor route file
  • Loading branch information
tannerlinsley committed Jan 6, 2024
1 parent 576f16c commit 8c4a83f
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 67 deletions.
50 changes: 23 additions & 27 deletions examples/react/basic-file-based-codesplitting/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
import { lazyFn, lazyRouteComponent } from "@tanstack/react-router"
import { FileRoute, lazyFn, lazyRouteComponent } from "@tanstack/react-router"

import { Route as rootRoute } from "./routes/__root"
import { Route as PostsImport } from "./routes/posts"
import { Route as LayoutImport } from "./routes/_layout"
import { Route as IndexImport } from "./routes/index"
import { Route as PostsPostIdRouteImport } from "./routes/posts.$postId/route"
import { Route as LayoutLayoutBImport } from "./routes/_layout/layout-b"
import { Route as LayoutLayoutAImport } from "./routes/_layout/layout-a"
import { Route as PostsIndexImport } from "./routes/posts.index"
import { Route as PostsPostIdDeepImport } from "./routes/posts_.$postId.deep"

const PostsRoute = PostsImport.update({
const PostsComponentImport = new FileRoute("/posts").createRoute()

const PostsComponentRoute = PostsComponentImport.update({
path: "/posts",
getParentRoute: () => rootRoute,
} as any).update({
component: lazyRouteComponent(
() => import("./routes/posts.component"),
"component",
),
})
} as any)
.updateLoader({
loader: lazyFn(() => import("./routes/posts.loader"), "loader"),
})
.update({
component: lazyRouteComponent(
() => import("./routes/posts.component"),
"component",
),
})

const LayoutRoute = LayoutImport.update({
id: "/_layout",
Expand All @@ -31,8 +35,8 @@ const IndexRoute = IndexImport.update({
} as any)

const PostsPostIdRouteRoute = PostsPostIdRouteImport.update({
path: "/$postId",
getParentRoute: () => PostsRoute,
path: "/posts/$postId",
getParentRoute: () => rootRoute,
} as any)
.updateLoader({
loader: lazyFn(() => import("./routes/posts.$postId/loader"), "loader"),
Expand All @@ -54,11 +58,6 @@ const LayoutLayoutARoute = LayoutLayoutAImport.update({
getParentRoute: () => LayoutRoute,
} as any)

const PostsIndexRoute = PostsIndexImport.update({
path: "/",
getParentRoute: () => PostsRoute,
} as any)

const PostsPostIdDeepRoute = PostsPostIdDeepImport.update({
path: "/posts/$postId/deep",
getParentRoute: () => rootRoute,
Expand All @@ -74,14 +73,6 @@ declare module "@tanstack/react-router" {
preLoaderRoute: typeof LayoutImport
parentRoute: typeof rootRoute
}
"/posts": {
preLoaderRoute: typeof PostsImport
parentRoute: typeof rootRoute
}
"/posts/": {
preLoaderRoute: typeof PostsIndexImport
parentRoute: typeof PostsRoute
}
"/_layout/layout-a": {
preLoaderRoute: typeof LayoutLayoutAImport
parentRoute: typeof LayoutRoute
Expand All @@ -92,18 +83,23 @@ declare module "@tanstack/react-router" {
}
"/posts/$postId": {
preLoaderRoute: typeof PostsPostIdRouteImport
parentRoute: typeof PostsRoute
parentRoute: typeof rootRoute
}
"/posts_/$postId/deep": {
preLoaderRoute: typeof PostsPostIdDeepImport
parentRoute: typeof rootRoute
}
"/posts": {
preLoaderRoute: typeof PostsComponentImport
parentRoute: typeof rootRoute
}
}
}

export const routeTree = rootRoute.addChildren([
IndexRoute,
LayoutRoute.addChildren([LayoutLayoutARoute, LayoutLayoutBRoute]),
PostsRoute.addChildren([PostsIndexRoute, PostsPostIdRouteRoute]),
PostsPostIdRouteRoute,
PostsPostIdDeepRoute,
PostsComponentRoute,
])

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { FileRouteLoader } from '@tanstack/react-router'
import { fetchPosts } from '../posts'

export const loader = FileRouteLoader('/posts')(fetchPosts)

This file was deleted.

82 changes: 59 additions & 23 deletions packages/router-cli/src/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export type RouteNode = {
isRoute?: boolean
isLoader?: boolean
isComponent?: boolean
isVirtual?: boolean
isRoot?: boolean
children?: RouteNode[]
parent?: RouteNode
Expand Down Expand Up @@ -127,21 +128,27 @@ export async function generator(config: Config) {
const start = Date.now()
const routePathIdPrefix = config.routeFilePrefix ?? ''

let routeNodes = await getRouteNodes(config)
let preRouteNodes = await getRouteNodes(config)

routeNodes = multiSortBy(routeNodes, [
(d) => (d.routePath === '/' ? -1 : 1),
(d) => d.routePath?.split('/').length,
(d) => (d.routePath?.endsWith('/') ? -1 : 1),
(d) => d.routePath,
]).filter((d) => d.routePath !== `/${routePathIdPrefix + rootPathId}`)
const sortRouteNodes = (nodes: RouteNode[]): RouteNode[] => {
return multiSortBy(nodes, [
(d) => (d.routePath === '/' ? -1 : 1),
(d) => d.routePath?.split('/').length,
(d) => (d.routePath?.endsWith('/') ? -1 : 1),
(d) => d.routePath,
]).filter((d) => d.routePath !== `/${routePathIdPrefix + rootPathId}`)
}

preRouteNodes = sortRouteNodes(preRouteNodes)

const routeTree: RouteNode[] = []
const routePiecesByPath: Record<string, RouteSubNode> = {}

// Loop over the flat list of routeNodes and
// build up a tree based on the routeNodes' routePath
routeNodes = routeNodes.filter((node) => {
let routeNodes: RouteNode[] = []

const handleNode = (node: RouteNode) => {
if (config.future?.unstable_codeSplitting) {
node.isRoute = node.routePath?.endsWith('/route')
node.isComponent = node.routePath?.endsWith('/component')
Expand Down Expand Up @@ -180,7 +187,8 @@ export async function generator(config: Config) {
routePiecesByPath[node.routePath!]![
node.isLoader ? 'loader' : 'component'
] = node
return false

return
}
}

Expand All @@ -191,8 +199,25 @@ export async function generator(config: Config) {
routeTree.push(node)
}

return true
})
routeNodes.push(node)
}

preRouteNodes.forEach((node) => handleNode(node))

if (config.future?.unstable_codeSplitting) {
Object.keys(routePiecesByPath).forEach((routePath) => {
const found = routeNodes.find((d) => d.routePath === routePath)

if (!found) {
const pieces = routePiecesByPath[routePath]!
const componentOrLoader = pieces.component || pieces.loader
if (componentOrLoader) {
componentOrLoader.isVirtual = true
handleNode(componentOrLoader)
}
}
})
}

async function buildRouteConfig(
nodes: RouteNode[],
Expand Down Expand Up @@ -246,24 +271,35 @@ export async function generator(config: Config) {
])

const routeImports = [
`import { lazyFn, lazyRouteComponent } from '@tanstack/react-router'`,
`import { FileRoute, lazyFn, lazyRouteComponent } from '@tanstack/react-router'`,
'\n',
`import { Route as rootRoute } from './${sanitize(
path.relative(
path.dirname(config.generatedRouteTree),
path.resolve(config.routesDirectory, routePathIdPrefix + rootPathId),
),
)}'`,
...sortedRouteNodes.map((node) => {
return `import { Route as ${node.variableName}Import } from './${sanitize(
removeExt(
path.relative(
path.dirname(config.generatedRouteTree),
path.resolve(config.routesDirectory, node.filePath),
...sortedRouteNodes
.filter((d) => !d.isVirtual)
.map((node) => {
return `import { Route as ${
node.variableName
}Import } from './${sanitize(
removeExt(
path.relative(
path.dirname(config.generatedRouteTree),
path.resolve(config.routesDirectory, node.filePath),
),
),
),
)}'`
}),
)}'`
}),
'\n',
sortedRouteNodes
.filter((d) => d.isVirtual)
.map((node) => {
return `const ${node.variableName}Import = new FileRoute('${node.routePath}').createRoute()`
})
.join('\n'),
'\n',
sortedRouteNodes
.map((node) => {
Expand Down Expand Up @@ -450,7 +486,7 @@ export function hasParentRoute(
}
const segments = routePathToCheck.split('/')
segments.pop() // Remove the last segment
const parentRoute = segments.join('/')
const parentRoutePath = segments.join('/')

return hasParentRoute(routes, parentRoute)
return hasParentRoute(routes, parentRoutePath)
}

0 comments on commit 8c4a83f

Please sign in to comment.