Skip to content

Commit d5684da

Browse files
committed
Make routes tab great again
1 parent 87c9374 commit d5684da

File tree

9 files changed

+193
-30
lines changed

9 files changed

+193
-30
lines changed

package-lock.json

Lines changed: 39 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
"tsx": "^4.19.2",
140140
"typescript": "^5.7.3",
141141
"vite": "^6.0.11",
142+
"vite-node": "^3.1.2",
142143
"vitest": "^3.0.4"
143144
},
144145
"dependencies": {

src/client/components/RouteInfo.tsx

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { MouseEvent } from "react"
33
import { Link } from "react-router"
44
import { useSettingsContext } from "../context/useRDTContext.js"
55
import { type ExtendedRoute, constructRoutePath } from "../utils/routing.js"
6+
import { findParentErrorBoundary } from "../utils/sanitize.js"
67
import { Input } from "./Input.js"
78
import { Tag } from "./Tag.js"
89
import { Icon } from "./icon/Icon.js"
@@ -14,20 +15,22 @@ interface RouteInfoProps {
1415
onClose?: () => void
1516
}
1617

17-
export const RouteInfo = ({ route, className, openNewRoute, onClose }: RouteInfoProps) => {
18+
export const RouteInfo = ({ route: routeToUse, className, openNewRoute, onClose }: RouteInfoProps) => {
19+
const route = window.__reactRouterManifest?.routes[routeToUse.id] || routeToUse
1820
const { settings, setSettings } = useSettingsContext()
1921
const { routeWildcards, routeViewMode } = settings
20-
const { hasWildcard, path, pathToOpen } = constructRoutePath(route, routeWildcards)
22+
const { hasWildcard, path, pathToOpen } = constructRoutePath(routeToUse, routeWildcards)
2123
const isTreeView = routeViewMode === "tree"
22-
const hasParentErrorBoundary = route.errorBoundary.errorBoundaryId && route.errorBoundary.errorBoundaryId !== route.id
23-
const hasErrorBoundary = route.errorBoundary.hasErrorBoundary
24+
const { hasErrorBoundary, errorBoundaryId } = findParentErrorBoundary(route)
25+
const hasParentErrorBoundary = errorBoundaryId && errorBoundaryId !== route.id
26+
2427
return (
2528
<div className={clsx(className, "relative")}>
2629
{isTreeView && (
2730
<>
2831
<Icon onClick={onClose} className="absolute right-2 top-2 cursor-pointer text-red-600" name="X" />
2932

30-
<h1 className="text-xl font-semibold">{route.url}</h1>
33+
<h1 className="text-xl font-semibold">{routeToUse.url}</h1>
3134
<hr className="mb-4 mt-1" />
3235
<h3>
3336
<span className="text-gray-500">Path:</span> {path}
@@ -41,12 +44,19 @@ export const RouteInfo = ({ route, className, openNewRoute, onClose }: RouteInfo
4144
<span className="whitespace-nowrap text-gray-500">Route file:</span>
4245
{route.id}
4346
</div>
47+
4448
<div className="mb-4 mt-4 flex flex-col gap-2">
4549
<span className="text-gray-500">Components contained in the route:</span>
46-
<div className="flex gap-2">
50+
<div className="flex flex-wrap gap-2">
4751
<Tag className="h-max" color={route.hasLoader ? "GREEN" : "RED"}>
4852
Loader
4953
</Tag>
54+
<Tag className="h-max" color={route.hasClientLoader ? "GREEN" : "RED"}>
55+
Client Loader
56+
</Tag>
57+
<Tag className="h-max" color={route.hasClientAction ? "GREEN" : "RED"}>
58+
Client Action
59+
</Tag>
5060
<Tag className="h-max" color={route.hasAction ? "GREEN" : "RED"}>
5161
Action
5262
</Tag>
@@ -60,17 +70,15 @@ export const RouteInfo = ({ route, className, openNewRoute, onClose }: RouteInfo
6070
</div>
6171
{hasErrorBoundary ? (
6272
<div className="mr-2">
63-
{hasParentErrorBoundary
64-
? `Covered by parent ErrorBoundary located in: ${route.errorBoundary.errorBoundaryId}`
65-
: ""}
73+
{hasParentErrorBoundary ? `Covered by parent ErrorBoundary located in: ${errorBoundaryId}` : ""}
6674
</div>
6775
) : null}
6876
</div>
6977
{hasWildcard && (
7078
<>
7179
<p className="mb-2 text-gray-500">Wildcard parameters:</p>
7280
<div className={clsx("mb-4 grid w-full grid-cols-2 gap-2", isTreeView && "grid-cols-1")}>
73-
{route.url
81+
{routeToUse.url
7482
.split("/")
7583
.filter((p) => p.startsWith(":"))
7684
.map((param) => (

src/client/tabs/RoutesTab.tsx

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type MouseEvent, useState } from "react"
1+
import { type MouseEvent, useEffect, useState } from "react"
22
import { useMatches, useNavigate } from "react-router"
33
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "../components/Accordion.js"
44
import { NewRouteForm } from "../components/NewRouteForm.js"
@@ -22,7 +22,7 @@ const RoutesTab = () => {
2222
const { detachedWindow } = useDetachedWindowControls()
2323
const [activeRoute, setActiveRoute] = useState<ExtendedRoute | null>(null)
2424
const [routes] = useState<ExtendedRoute[]>(createExtendedRoutes() as ExtendedRoute[])
25-
const [treeRoutes] = useState(createRouteTree(window.__reactRouterManifest?.routes))
25+
const [treeRoutes, setTreeRoutes] = useState(createRouteTree(window.__reactRouterManifest?.routes))
2626
const isTreeView = routeViewMode === "tree"
2727
const openNewRoute = (path: string) => (e?: MouseEvent<HTMLDivElement | HTMLButtonElement>) => {
2828
e?.preventDefault()
@@ -32,6 +32,26 @@ const RoutesTab = () => {
3232
}
3333
}
3434

35+
useEffect(function fetchAllRoutesOnMount() {
36+
import.meta.hot?.send("routes-info")
37+
const cb = (event: any) => {
38+
const parsed = JSON.parse(event)
39+
const data = parsed.data as Record<string, any>[]
40+
41+
const routeObject: Record<string, any> = {}
42+
for (const route of data) {
43+
routeObject[route.id] = route
44+
}
45+
46+
routeObject.root = window.__reactRouterManifest?.routes?.root
47+
48+
setTreeRoutes(createRouteTree(routeObject))
49+
}
50+
import.meta.hot?.on("routes-info", cb)
51+
return () => {
52+
import.meta.hot?.off("routes-info", cb)
53+
}
54+
}, [])
3555
return (
3656
<div className={clsx("relative h-full w-full ", !isTreeView && "pt-8")}>
3757
<RouteToggle />
@@ -40,7 +60,11 @@ const RoutesTab = () => {
4060
<Tree
4161
translate={{ x: window.innerWidth / 2 - (isTreeView && activeRoute ? 0 : 0), y: 30 }}
4262
pathClassFunc={(link) =>
43-
activeRoutes.includes((link.target.data.attributes as any).id) ? "stroke-yellow-500" : "stroke-gray-400"
63+
activeRoutes.includes((link.target.data.attributes as any).id)
64+
? "stroke-yellow-500"
65+
: window.__reactRouterManifest?.routes?.[link.target.data.attributes.id]
66+
? "stroke-gray-400"
67+
: "stroke-gray-400/20"
4468
}
4569
renderCustomNodeElement={(props) =>
4670
RouteNode({

src/client/utils/routing.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,7 @@ export const createExtendedRoutes = () => {
8989
...route,
9090
// biome-ignore lint/style/noNonNullAssertion: <explanation>
9191
url: convertReactRouterPathToUrl(window.__reactRouterManifest!.routes, route),
92-
// biome-ignore lint/style/noNonNullAssertion: <explanation>
93-
errorBoundary: findParentErrorBoundary(window.__reactRouterManifest!.routes, route),
92+
errorBoundary: findParentErrorBoundary(route),
9493
}
9594
})
9695
.filter((route) => isLeafRoute(route as any))

src/client/utils/sanitize.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ export const convertReactRouterPathToUrl = (routes: any, route: Route) => {
2121
return output === "" ? "/" : output
2222
}
2323

24-
export const findParentErrorBoundary = (routes: RouteManifest, route: Route) => {
24+
export const findParentErrorBoundary = (route: Route) => {
25+
// biome-ignore lint/style/noNonNullAssertion: <explanation>
26+
const routes = window.__reactRouterManifest?.routes!
2527
let currentRoute: Route | null = route
2628

2729
while (currentRoute) {
@@ -64,7 +66,7 @@ const constructTree = (routes: any, parentId?: string): RawNodeDatum[] => {
6466
...route,
6567
url,
6668
},
67-
errorBoundary: findParentErrorBoundary(routes, route),
69+
errorBoundary: findParentErrorBoundary(route),
6870
children: constructTree(routes, route.id),
6971
}
7072
nodes.push(node)

src/vite/node-server.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { createServer, version as viteVersion } from "vite"
2+
import { ViteNodeRunner } from "vite-node/client"
3+
import { ViteNodeServer } from "vite-node/server"
4+
import { installSourcemapsSupport } from "vite-node/source-map"
5+
6+
// create vite server
7+
const server = await createServer({
8+
server: {
9+
preTransformRequests: false,
10+
hmr: false,
11+
watch: null,
12+
},
13+
14+
optimizeDeps: {
15+
noDiscovery: true,
16+
},
17+
configFile: false,
18+
envFile: false,
19+
plugins: [],
20+
})
21+
// For old Vite, this is need to initialize the plugins.
22+
if (Number(viteVersion.split(".")[0]) < 6) {
23+
await server.pluginContainer.buildStart({})
24+
}
25+
26+
// create vite-node server
27+
const node = new ViteNodeServer(server)
28+
29+
// fixes stacktraces in Errors
30+
installSourcemapsSupport({
31+
getSourceMap: (source) => node.getSourceMap(source),
32+
})
33+
34+
// create vite-node runner
35+
const runner = new ViteNodeRunner({
36+
root: server.config.root,
37+
base: server.config.base,
38+
// when having the server and runner in a different context,
39+
// you will need to handle the communication between them
40+
// and pass to this function
41+
fetchModule(id) {
42+
return node.fetchModule(id)
43+
},
44+
resolveId(id, importer) {
45+
return node.resolveId(id, importer)
46+
},
47+
})
48+
49+
export { runner }

0 commit comments

Comments
 (0)