Skip to content

Commit 2efe23f

Browse files
feat: add go to source functionality to devtools (#56)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 115440b commit 2efe23f

File tree

12 files changed

+464
-229
lines changed

12 files changed

+464
-229
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
@@ -24,7 +24,8 @@
2424
"@types/react": "^19.1.2",
2525
"@types/react-dom": "^19.1.2",
2626
"@vitejs/plugin-react": "^4.5.2",
27-
"vite": "^7.0.6"
27+
"vite": "^7.0.6",
28+
"vite-plugin-inspect": "11.3.2"
2829
},
2930
"browserslist": {
3031
"production": [

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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
"solid-js": "^1.9.7"
1818
},
1919
"devDependencies": {
20+
"@tanstack/devtools-vite": "0.2.3",
2021
"vite": "^7.0.6",
22+
"vite-plugin-inspect": "11.3.2",
2123
"vite-plugin-solid": "^2.11.6"
2224
},
2325
"browserslist": {
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: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,17 @@
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/traverse": "^7.28.3",
58+
"@babel/types": "^7.28.2",
5459
"@tanstack/devtools-event-bus": "workspace:*",
5560
"chalk": "^5.6.0"
61+
},
62+
"devDependencies": {
63+
"@types/babel__core": "^7.20.5",
64+
"@types/babel__generator": "^7.27.0",
65+
"@types/babel__traverse": "^7.28.0"
5666
}
5767
}
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: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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+
const transform = (ast: ParseResult<Babel.File>, file: string) => {
7+
let didTransform = false
8+
trav(ast, {
9+
JSXOpeningElement(path) {
10+
const loc = path.node.loc
11+
if (!loc) return
12+
const line = loc.start.line
13+
const column = loc.start.column
14+
15+
// Inject data-source as a string: "<file>:<line>:<column>"
16+
path.node.attributes.push(
17+
t.jsxAttribute(
18+
t.jsxIdentifier('data-tsd-source'),
19+
t.stringLiteral(`${file}:${line}:${column}`),
20+
),
21+
)
22+
23+
didTransform = true
24+
},
25+
})
26+
27+
return didTransform
28+
}
29+
30+
export function addSourceToJsx(code: string, id: string) {
31+
const [filePath] = id.split('?')
32+
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
33+
const location = filePath?.replace(normalizePath(process.cwd()), '')!
34+
35+
try {
36+
const ast = parse(code, {
37+
sourceType: 'module',
38+
plugins: ['jsx', 'typescript'],
39+
})
40+
const didTransform = transform(ast, location)
41+
if (!didTransform) {
42+
return { code }
43+
}
44+
return gen(ast, {
45+
sourceMaps: true,
46+
filename: id,
47+
sourceFileName: filePath,
48+
})
49+
} catch (e) {
50+
return { code }
51+
}
52+
}

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)