Skip to content

Commit 24bc5be

Browse files
committed
feat: add go to source functionality to devtools
1 parent 1ff5f18 commit 24bc5be

File tree

12 files changed

+538
-231
lines changed

12 files changed

+538
-231
lines changed

.changeset/good-guests-prove.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@tanstack/devtools-vite': patch
3+
'@tanstack/devtools': patch
4+
---
5+
6+
Add go to source functionality to devtools

docs/vite-plugin.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,14 @@ const open = (file: string, line: number) => {
9191
enhancedLogs: {
9292
enabled: true
9393
}
94-
}
94+
}
95+
```
96+
97+
## Features
98+
99+
### Go to source
100+
101+
Allows you to open the source location on anything in your browser by clicking on it.
102+
103+
To trigger this behavior you need the Devtools Vite plugin installed and configured and
104+
the Panel available on the page. Simply click on any element while holding down the Shift and Ctrl (or Meta) keys.

examples/react/basic/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"zod": "^4.0.14"
2121
},
2222
"devDependencies": {
23+
"vite-plugin-inspect": "11.3.2",
2324
"@tanstack/devtools-vite": "0.2.3",
2425
"@types/react": "^19.1.2",
2526
"@types/react-dom": "^19.1.2",
@@ -38,4 +39,4 @@
3839
"last 1 safari version"
3940
]
4041
}
41-
}
42+
}

examples/react/basic/vite.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { defineConfig } from 'vite'
22
import react from '@vitejs/plugin-react'
33
import { devtools } from '@tanstack/devtools-vite'
4+
import Inspect from 'vite-plugin-inspect'
5+
46
// https://vite.dev/config/
57
export default defineConfig({
68
plugins: [
79
devtools(),
10+
Inspect(),
811
react({
912
// babel: {
1013
// plugins: [['babel-plugin-react-compiler', { target: '19' }]],

examples/solid/basic/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
"solid-js": "^1.9.7"
1818
},
1919
"devDependencies": {
20+
"vite-plugin-inspect": "11.3.2",
21+
"@tanstack/devtools-vite": "0.2.3",
2022
"vite": "^7.0.6",
2123
"vite-plugin-solid": "^2.11.6"
2224
},
@@ -32,4 +34,4 @@
3234
"last 1 safari version"
3335
]
3436
}
35-
}
37+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { defineConfig } from 'vite'
22
import solid from 'vite-plugin-solid'
3-
3+
import { devtools } from '@tanstack/devtools-vite'
44
// https://vite.dev/config/
55
export default defineConfig({
6-
plugins: [solid({})],
6+
plugins: [devtools(), solid({}),],
77
})

packages/devtools-vite/package.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,18 @@
5151
"vite": "^7.0.0"
5252
},
5353
"dependencies": {
54+
"@babel/core": "^7.28.3",
55+
"@babel/generator": "^7.28.3",
56+
"@babel/parser": "^7.28.3",
57+
"@babel/preset-react": "^7.27.1",
58+
"@babel/traverse": "^7.28.3",
59+
"@babel/types": "^7.28.2",
5460
"@tanstack/devtools-event-bus": "workspace:*",
5561
"chalk": "^5.6.0"
62+
},
63+
"devDependencies": {
64+
"@types/babel__core": "^7.20.5",
65+
"@types/babel__generator": "^7.27.0",
66+
"@types/babel__traverse": "^7.28.0"
5667
}
5768
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { parse } from "@babel/parser"
2+
import * as t from "@babel/types"
3+
import generate from "@babel/generator"
4+
import traverse from "@babel/traverse"
5+
6+
export { parse, t }
7+
8+
export const trav =
9+
typeof (traverse as any).default !== "undefined"
10+
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
11+
? ((traverse as any).default as typeof import("@babel/traverse").default)
12+
: traverse
13+
14+
export const gen =
15+
typeof (generate as any).default !== "undefined"
16+
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
17+
? ((generate as any).default as typeof import("@babel/generator").default)
18+
: generate
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { normalizePath } from "vite";
2+
import { gen, parse, t, trav } from "./babel";
3+
import type { types as Babel } from "@babel/core"
4+
import type { ParseResult } from "@babel/parser"
5+
6+
7+
const transform = (ast: ParseResult<Babel.File>, file: string) => {
8+
let didTransform = false;
9+
trav(ast, {
10+
JSXOpeningElement(path,) {
11+
const loc = path.node.loc;
12+
if (!loc) return;
13+
const line = loc.start.line;
14+
const column = loc.start.column;
15+
16+
// Inject data-source as a string: "<file>:<line>:<column>"
17+
path.node.attributes.push(
18+
t.jsxAttribute(
19+
t.jsxIdentifier('data-source'),
20+
t.stringLiteral(`${file}:${line}:${column}`)
21+
)
22+
);
23+
24+
didTransform = true;
25+
}
26+
})
27+
28+
return didTransform
29+
}
30+
31+
32+
export function addSourceToJsx(code: string, id: string) {
33+
const [filePath] = id.split("?")
34+
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
35+
const location = filePath?.replace(normalizePath(process.cwd()), '')!
36+
37+
try {
38+
const ast = parse(code, { sourceType: "module", plugins: ["jsx", "typescript"] });
39+
const didTransform = transform(ast, location)
40+
if (!didTransform) {
41+
return { code }
42+
}
43+
return gen(ast, { sourceMaps: true, filename: id, sourceFileName: filePath })
44+
} catch (e) {
45+
46+
return { code }
47+
}
48+
}

packages/devtools-vite/src/plugin.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import chalk from 'chalk'
33
import { ServerEventBus } from '@tanstack/devtools-event-bus/server'
44
import { handleDevToolsViteRequest } from './utils'
55
import { DEFAULT_EDITOR_CONFIG, handleOpenSource } from './editor'
6+
import { addSourceToJsx } from './inject-source'
67
import type { EditorConfig } from './editor'
78
import type { ServerEventBusConfig } from '@tanstack/devtools-event-bus/server'
89
import type { Plugin } from 'vite'
@@ -40,6 +41,24 @@ export const devtools = (args?: TanStackDevtoolsViteConfig): Array<Plugin> => {
4041
const bus = new ServerEventBus(args?.eventBusConfig)
4142

4243
return [
44+
{
45+
enforce: "pre",
46+
name: '@tanstack/devtools:inject-source',
47+
apply(config) {
48+
return config.mode === 'development'
49+
},
50+
transform(code, id) {
51+
if (
52+
id.includes('node_modules') ||
53+
id.includes('?raw') ||
54+
id.includes('dist') ||
55+
id.includes('build')
56+
)
57+
return code
58+
59+
return addSourceToJsx(code, id)
60+
}
61+
},
4362
{
4463
enforce: 'pre',
4564
name: '@tanstack/devtools:custom-server',
@@ -87,6 +106,12 @@ export const devtools = (args?: TanStackDevtoolsViteConfig): Array<Plugin> => {
87106
}),
88107
)
89108
},
109+
transform(code) {
110+
if (code.includes("__TSD_PORT__")) {
111+
code = code.replace("__TSD_PORT__", String(port))
112+
}
113+
return code
114+
}
90115
},
91116
{
92117
name: '@tanstack/devtools:better-console-logs',

0 commit comments

Comments
 (0)