Skip to content

Commit 1953ee5

Browse files
authored
implement go to source back into react-router-devtools (#216)
1 parent a0fd444 commit 1953ee5

File tree

10 files changed

+1278
-113
lines changed

10 files changed

+1278
-113
lines changed

package.json

Lines changed: 1 addition & 1 deletion
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": "5.0.7",
5+
"version": "5.1.0",
66
"license": "MIT",
77
"keywords": [
88
"react-router",

src/client/hooks/useOpenElementSource.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ const useOpenElementSource = () => {
1212
e.stopPropagation()
1313
e.preventDefault()
1414
const target = e.target as HTMLElement
15-
const rdtSource = target?.getAttribute("data-source")
15+
const rdtSource = target?.getAttribute("data-rrdt-source")
1616

1717
if (rdtSource) {
18-
const [source, line, column] = rdtSource.split(":::")
18+
const [source, line, column] = rdtSource.split(":")
1919
return sendJsonMessage({
2020
type: "open-source",
2121
data: { source, line, column },

src/client/hooks/useReactTreeListeners.ts

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,6 @@ import { useHtmlErrors } from "../context/useRDTContext.js"
66

77
export const ROUTE_CLASS = "outlet-route"
88

9-
const isSourceElement = (fiberNode: any) => {
10-
return (
11-
fiberNode?.elementType &&
12-
fiberNode?.stateNode &&
13-
fiberNode?._debugSource &&
14-
!fiberNode?.stateNode?.getAttribute?.("data-source")
15-
)
16-
}
17-
18-
const isJsxFile = (fiberNode: Fiber<any>) =>
19-
fiberNode?._debugSource?.fileName?.includes("tsx") || fiberNode?._debugSource?.fileName?.includes("jsx")
20-
219
export function useReactTreeListeners() {
2210
const invalidHtmlCollection = useRef<HTMLError[]>([])
2311
const { setHtmlErrors } = useHtmlErrors()
@@ -155,18 +143,6 @@ export function useReactTreeListeners() {
155143

156144
onCommitFiberRoot((root) =>
157145
traverseFiber(root.current, (fiberNode) => {
158-
if (isSourceElement(fiberNode) && typeof import.meta.hot !== "undefined") {
159-
const originalSource = fiberNode?._debugSource
160-
const source = fiberNode?._debugOwner?._debugSource ?? fiberNode?._debugSource
161-
const line = source?.fileName?.startsWith("/") ? originalSource?.lineNumber : source?.lineNumber
162-
const fileName = source?.fileName?.startsWith("/") ? originalSource?.fileName : source?.fileName
163-
164-
fiberNode.stateNode?.setAttribute?.(
165-
"data-source",
166-
`${fileName}:::${line}` //
167-
)
168-
}
169-
170146
if (fiberNode?.stateNode && fiberNode?.elementType === "form") {
171147
findIncorrectHtml(fiberNode.child, fiberNode, "form")
172148
}

src/client/tabs/PageTab.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import clsx from "clsx"
2-
import { useMemo } from "react"
32
import { useMatches, useRevalidator } from "react-router"
43

54
import { RouteSegmentInfo } from "../components/RouteSegmentInfo.js"
65

76
const PageTab = () => {
87
const routes = useMatches()
9-
const reversed = useMemo(() => routes.reverse(), [routes])
108
const { revalidate, state } = useRevalidator()
119

1210
return (
@@ -32,7 +30,7 @@ const PageTab = () => {
3230
<ol
3331
className={clsx("relative border-l border-gray-700", state === "loading" && "pointer-events-none opacity-50")}
3432
>
35-
{reversed.map((route, i) => (
33+
{routes.map((route, i) => (
3634
<RouteSegmentInfo route={route} i={i} key={route.id} />
3735
))}
3836
</ol>

src/vite/editor.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,16 @@ export type OpenSourceData = {
1717

1818
export type EditorConfig = {
1919
name: string
20-
open(path: string, lineNumber: string | undefined): void
20+
open(path: string, lineNumber: string | undefined, columnNumber: string | undefined): void
2121
}
2222

2323
export const DEFAULT_EDITOR_CONFIG: EditorConfig = {
2424
name: "VSCode",
25-
open: async (path, lineNumber) => {
25+
open: async (path, lineNumber, columnNumber) => {
2626
const { exec } = await import("node:child_process")
27-
exec(`code -g "${normalizePath(path).replaceAll("$", "\\$")}${lineNumber ? `:${lineNumber}` : ""}"`)
27+
exec(
28+
`code -g "${normalizePath(path).replaceAll("$", "\\$")}${lineNumber ? `:${lineNumber}` : ""}${columnNumber ? `:${columnNumber}` : ""}"`
29+
)
2830
},
2931
}
3032

@@ -35,14 +37,16 @@ export const handleOpenSource = async ({
3537
}: {
3638
data: OpenSourceData
3739
appDir: string
38-
openInEditor: (path: string, lineNum: string | undefined) => Promise<void>
40+
openInEditor: (path: string, lineNum: string | undefined, columnNum: string | undefined) => Promise<void>
3941
}) => {
40-
const { source, line, routeID } = data.data
42+
const { source, line, column, routeID } = data.data
43+
4144
const lineNum = line ? `${line}` : undefined
45+
const columnNum = column ? `${column}` : undefined
4246
const fs = await import("node:fs")
4347
const path = await import("node:path")
4448
if (source) {
45-
return openInEditor(source, lineNum)
49+
return openInEditor(source, lineNum, columnNum)
4650
}
4751

4852
if (routeID) {
@@ -64,7 +68,7 @@ export const handleOpenSource = async ({
6468
if (!fs.existsSync(appDir)) return
6569
const filesInReactRouterPath = fs.readdirSync(appDir)
6670
const rootFile = findFileByExtension("root", filesInReactRouterPath)
67-
rootFile && openInEditor(path.join(appDir, rootFile), lineNum)
71+
rootFile && openInEditor(path.join(appDir, rootFile), lineNum, columnNum)
6872
return
6973
}
7074

@@ -74,9 +78,9 @@ export const handleOpenSource = async ({
7478
if (type === "directory") {
7579
const filesInFolderRoute = fs.readdirSync(validPath)
7680
const routeFile = findFileByExtension("route", filesInFolderRoute)
77-
routeFile && openInEditor(path.join(appDir, routeID, routeFile), lineNum)
81+
routeFile && openInEditor(path.join(appDir, routeID, routeFile), lineNum, columnNum)
7882
return
7983
}
80-
return openInEditor(validPath, lineNum)
84+
return openInEditor(validPath, lineNum, columnNum)
8185
}
8286
}

src/vite/file.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { GENERATORS, type Generator } from "./generators"
55
export type WriteFileData = {
66
type: "write-file"
77
path: string
8-
openInEditor: (path: string, lineNum: string | undefined) => void
8+
openInEditor: (path: string, lineNum: string | undefined, columnNum: string | undefined) => void
99
options: {
1010
loader: boolean
1111
clientLoader: boolean
@@ -66,5 +66,5 @@ export const handleWriteFile = async ({ path, options, openInEditor, appDir }: W
6666
.filter(Boolean)
6767
.join("\n\n")
6868
await writeFile(outputFile, fileContent)
69-
openInEditor(outputFile, undefined)
69+
openInEditor(outputFile, undefined, undefined)
7070
}

src/vite/plugin.tsx

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { handleDevToolsViteRequest, processPlugins } from "./utils.js"
1212
import { augmentDataFetchingFunctions } from "./utils/data-functions-augment.js"
1313
import { injectRdtClient } from "./utils/inject-client.js"
1414
import { injectContext } from "./utils/inject-context.js"
15+
import { addSourceToJsx } from "./utils/inject-source.js"
1516
// this should mirror the types in server/config.ts as well as they are bundled separately.
1617
declare global {
1718
interface Window {
@@ -98,6 +99,19 @@ export const reactRouterDevTools: (args?: ReactRouterViteConfig) => Plugin[] = (
9899
process.rdt_config = serverConfig
99100
}
100101
return [
102+
{
103+
enforce: "pre",
104+
name: "react-router-devtools:inject-source",
105+
apply(config) {
106+
return config.mode === "development"
107+
},
108+
transform(code, id) {
109+
if (id.includes("node_modules") || id.includes("?raw") || id.includes("dist") || id.includes("build"))
110+
return code
111+
112+
return addSourceToJsx(code, id)
113+
},
114+
},
101115
{
102116
name: "react-router-devtools",
103117
apply(config) {
@@ -259,11 +273,15 @@ export const reactRouterDevTools: (args?: ReactRouterViteConfig) => Plugin[] = (
259273
})
260274
const channel = server.hot
261275
const editor = args?.editor ?? DEFAULT_EDITOR_CONFIG
262-
const openInEditor = async (path: string | undefined, lineNum: string | undefined) => {
276+
const openInEditor = async (
277+
path: string | undefined,
278+
lineNum: string | undefined,
279+
columnNum: string | undefined
280+
) => {
263281
if (!path) {
264282
return
265283
}
266-
editor.open(path, lineNum)
284+
editor.open(path, lineNum, columnNum)
267285
}
268286
server.middlewares.use((req, res, next) =>
269287
handleDevToolsViteRequest(req, res, next, (parsedData) => {
@@ -343,7 +361,19 @@ export const reactRouterDevTools: (args?: ReactRouterViteConfig) => Plugin[] = (
343361
)
344362
})
345363

346-
server.hot.on("open-source", (data: OpenSourceData) => handleOpenSource({ data, openInEditor, appDir }))
364+
server.hot.on("open-source", (data: OpenSourceData) =>
365+
handleOpenSource({
366+
data: {
367+
...data,
368+
data: {
369+
...data.data,
370+
source: data.data.source ? normalizePath(`${process.cwd()}/${data.data.source}`) : undefined,
371+
},
372+
},
373+
openInEditor,
374+
appDir,
375+
})
376+
)
347377
server.hot.on("add-route", (data: WriteFileData) => handleWriteFile({ ...data, openInEditor, appDir }))
348378
}
349379
},

0 commit comments

Comments
 (0)