Skip to content

Commit 5039c35

Browse files
authored
Make routes tab great again (#204)
* Make routes tab great again * small bug fix * fix for custom ids * optimize the recursive routes call * fix for custom ids * fix * fix * please God * fix * fix * logs * test * test * remove logs * version bump
1 parent 87c9374 commit 5039c35

File tree

14 files changed

+281
-51
lines changed

14 files changed

+281
-51
lines changed

docs/app/components/ui/Sparkles.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ export const SparklesCore = (props: ParticlesProps) => {
3939

4040
const particlesLoaded = async (container?: Container) => {
4141
if (container) {
42-
console.log(container)
4342
controls.start({
4443
opacity: 1,
4544
transition: {

package-lock.json

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

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "react-router-devtools",
33
"description": "Devtools for React Router - debug, trace, find hydration errors, catch bugs and inspect server/client data with react-router-devtools",
44
"author": "Alem Tuzlak",
5-
"version": "1.1.10",
5+
"version": "5.0.0",
66
"license": "MIT",
77
"keywords": [
88
"react-router",
@@ -19,7 +19,6 @@
1919
],
2020
"private": false,
2121
"type": "module",
22-
"main": "./dist/index.cjs",
2322
"module": "./dist/index.js",
2423
"types": "./dist/index.d.ts",
2524
"exports": {
@@ -105,6 +104,8 @@
105104
"peerDependencies": {
106105
"react": ">=17",
107106
"react-dom": ">=17",
107+
"@types/react": ">=17",
108+
"@types/react-dom": ">=17",
108109
"react-router": ">=7.0.0",
109110
"vite": ">=5.0.0 || >=6.0.0"
110111
},
@@ -118,8 +119,6 @@
118119
"@types/babel__core": "^7.20.5",
119120
"@types/beautify": "^0.0.3",
120121
"@types/node": "^22.12.0",
121-
"@types/react": "^19.0.8",
122-
"@types/react-dom": "^19.0.3",
123122
"@vitest/coverage-v8": "^3.0.4",
124123
"@vitest/ui": "^3.0.4",
125124
"autoprefixer": "^10.4.20",
@@ -132,13 +131,13 @@
132131
"npm-run-all": "^4.1.5",
133132
"postcss": "^8.5.1",
134133
"prompt": "^1.3.0",
135-
"tailwind-merge": "^3.0.1",
136134
"tailwindcss": "^3.4.0",
137135
"tailwindcss-animate": "^1.0.7",
138136
"tsup": "^8.3.6",
139137
"tsx": "^4.19.2",
140138
"typescript": "^5.7.3",
141139
"vite": "^6.0.11",
140+
"vite-node": "^3.1.2",
142141
"vitest": "^3.0.4"
143142
},
144143
"dependencies": {
@@ -158,7 +157,8 @@
158157
"react-d3-tree": "^3.6.4",
159158
"react-diff-viewer-continued": "^4.0.5",
160159
"react-hotkeys-hook": "^4.6.1",
161-
"react-tooltip": "^5.28.0"
160+
"react-tooltip": "^5.28.0",
161+
"tailwind-merge": "^3.0.1"
162162
},
163163
"optionalDependencies": {
164164
"@biomejs/cli-darwin-arm64": "^1.9.4",

src/client/components/RouteInfo.tsx

Lines changed: 22 additions & 13 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,39 +15,49 @@ 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 text-white font-semibold">{routeToUse.url}</h1>
3134
<hr className="mb-4 mt-1" />
3235
<h3>
33-
<span className="text-gray-500">Path:</span> {path}
36+
<span className="text-gray-500">Path:</span>
37+
<span className="text-white"> {path}</span>
3438
</h3>
3539
<h3>
36-
<span className="text-gray-500">Url:</span> {pathToOpen}
40+
<span className="text-gray-500">Url:</span> <span className="text-white">{pathToOpen}</span>
3741
</h3>
3842
</>
3943
)}
4044
<div className="flex gap-2">
4145
<span className="whitespace-nowrap text-gray-500">Route file:</span>
42-
{route.id}
46+
{route.module ?? routeToUse.file}
4347
</div>
48+
4449
<div className="mb-4 mt-4 flex flex-col gap-2">
4550
<span className="text-gray-500">Components contained in the route:</span>
46-
<div className="flex gap-2">
51+
<div className="flex flex-wrap gap-2">
4752
<Tag className="h-max" color={route.hasLoader ? "GREEN" : "RED"}>
4853
Loader
4954
</Tag>
55+
<Tag className="h-max" color={route.hasClientLoader ? "GREEN" : "RED"}>
56+
Client Loader
57+
</Tag>
58+
<Tag className="h-max" color={route.hasClientAction ? "GREEN" : "RED"}>
59+
Client Action
60+
</Tag>
5061
<Tag className="h-max" color={route.hasAction ? "GREEN" : "RED"}>
5162
Action
5263
</Tag>
@@ -60,17 +71,15 @@ export const RouteInfo = ({ route, className, openNewRoute, onClose }: RouteInfo
6071
</div>
6172
{hasErrorBoundary ? (
6273
<div className="mr-2">
63-
{hasParentErrorBoundary
64-
? `Covered by parent ErrorBoundary located in: ${route.errorBoundary.errorBoundaryId}`
65-
: ""}
74+
{hasParentErrorBoundary ? `Covered by parent ErrorBoundary located in: ${errorBoundaryId}` : ""}
6675
</div>
6776
) : null}
6877
</div>
6978
{hasWildcard && (
7079
<>
7180
<p className="mb-2 text-gray-500">Wildcard parameters:</p>
7281
<div className={clsx("mb-4 grid w-full grid-cols-2 gap-2", isTreeView && "grid-cols-1")}>
73-
{route.url
82+
{routeToUse.url
7483
.split("/")
7584
.filter((p) => p.startsWith(":"))
7685
.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: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,29 @@ const ROUTE_FILLS = {
4848
PURPLE: "fill-purple-500 text-white",
4949
} as const
5050

51+
const UNDISCOVERED_ROUTE_FILLS = {
52+
GREEN: "fill-green-500/20 text-white",
53+
BLUE: "fill-blue-500/20 text-white",
54+
PURPLE: "fill-purple-500/20 text-white",
55+
}
56+
5157
export function getRouteColor(route: Route) {
58+
const isDiscovered = !!window.__reactRouterManifest?.routes[route.id]
59+
const FILL = isDiscovered ? ROUTE_FILLS : UNDISCOVERED_ROUTE_FILLS
5260
switch (getRouteType(route)) {
5361
case "ROOT":
54-
return ROUTE_FILLS.PURPLE
55-
case "LAYOUT":
56-
return ROUTE_FILLS.BLUE
62+
return FILL.PURPLE
63+
5764
case "ROUTE":
58-
return ROUTE_FILLS.GREEN
65+
return FILL.GREEN
66+
67+
case "LAYOUT":
68+
return FILL.BLUE
5969
}
6070
}
6171
export type ExtendedRoute = EntryRoute & {
6272
url: string
73+
file?: string
6374
errorBoundary: { hasErrorBoundary: boolean; errorBoundaryId: string | null }
6475
}
6576

@@ -89,8 +100,7 @@ export const createExtendedRoutes = () => {
89100
...route,
90101
// biome-ignore lint/style/noNonNullAssertion: <explanation>
91102
url: convertReactRouterPathToUrl(window.__reactRouterManifest!.routes, route),
92-
// biome-ignore lint/style/noNonNullAssertion: <explanation>
93-
errorBoundary: findParentErrorBoundary(window.__reactRouterManifest!.routes, route),
103+
errorBoundary: findParentErrorBoundary(route),
94104
}
95105
})
96106
.filter((route) => isLeafRoute(route as any))

0 commit comments

Comments
 (0)