Skip to content

Commit b7cdf34

Browse files
committed
feat(core): introduce @vitejs/devtools/preamble for explicit devtools client injection
1 parent 13352a8 commit b7cdf34

File tree

9 files changed

+70
-7
lines changed

9 files changed

+70
-7
lines changed

docs/guide/index.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,18 @@ pnpm dev
110110

111111
Then open your app in the browser and open the DevTools panel.
112112

113+
#### Projects without an HTML entry
114+
115+
The embedded DevTools client is usually injected through Vite's `transformIndexHtml` hook. If your app does not start from an HTML entry, keep the `DevTools()` plugin enabled and import the preamble manually in your client entry instead:
116+
117+
```ts twoslash
118+
import '@vitejs/devtools/preamble'
119+
```
120+
121+
This loads the same DevTools client that would normally be added to `index.html`. Put it in a browser entry such as `main.ts` or `entry.client.ts`, not in server-only files or shared SSR entry files.
122+
123+
If your project does have an HTML entry, avoid importing the preamble in addition to the HTML injection, as that would inject the client twice and create duplicate dock elements.
124+
113125
#### Building with the App
114126

115127
You can also generate a static DevTools build alongside your app's build output by enabling the `build.withApp` option:

packages/core/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,17 @@
2828
"./config": "./dist/config.js",
2929
"./dirs": "./dist/dirs.js",
3030
"./internal": "./dist/internal.js",
31-
"./package.json": "./package.json"
31+
"./package.json": "./package.json",
32+
"./preamble": "./types/preamble.d.ts"
3233
},
3334
"types": "./dist/index.d.ts",
3435
"bin": {
3536
"vite-devtools": "./bin.js"
3637
},
3738
"files": [
3839
"bin.js",
39-
"dist"
40+
"dist",
41+
"types"
4042
],
4143
"scripts": {
4244
"build": "pnpm build:js && pnpm build:standalone",
@@ -55,6 +57,7 @@
5557
"vite": "*"
5658
},
5759
"dependencies": {
60+
"@rolldown/pluginutils": "catalog:deps",
5861
"@vitejs/devtools-kit": "workspace:*",
5962
"@vitejs/devtools-rolldown": "workspace:*",
6063
"@vitejs/devtools-rpc": "workspace:*",

packages/core/src/node/plugins/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Plugin } from 'vite'
22
import { DevToolsBuild } from './build'
33
import { DevToolsInjection } from './injection'
4+
import { DevToolsPreamble } from './preamble'
45
import { DevToolsServer } from './server'
56

67
export interface DevToolsOptions {
@@ -38,6 +39,7 @@ export async function DevTools(options: DevToolsOptions = {}): Promise<Plugin[]>
3839
const plugins = [
3940
DevToolsInjection(),
4041
DevToolsServer(),
42+
DevToolsPreamble({ name: '@vitejs/devtools/preamble' }),
4143
]
4244

4345
if (build?.withApp) {

packages/core/src/node/plugins/injection.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,26 @@ import process from 'node:process'
33
import { join, normalize } from 'pathe'
44
import { dirDist } from '../../dirs'
55

6+
export function getClientInjectUrl() {
7+
const fileUrl = process.env.VITE_DEVTOOLS_LOCAL_DEV
8+
? normalize(join(dirDist, '..', 'src/client/inject/index.ts'))
9+
: normalize(join(dirDist, 'client/inject.js'))
10+
11+
return `/@fs/${fileUrl}`
12+
}
13+
614
export function DevToolsInjection(): Plugin {
715
return {
816
name: 'vite:devtools:injection',
917
enforce: 'post',
18+
apply: 'serve',
1019
transformIndexHtml() {
11-
const fileUrl = process.env.VITE_DEVTOOLS_LOCAL_DEV
12-
? normalize(join(dirDist, '..', 'src/client/inject/index.ts'))
13-
: normalize(join(dirDist, 'client/inject.js'))
20+
const fileUrl = getClientInjectUrl()
1421
return [
1522
{
1623
tag: 'script',
1724
attrs: {
18-
src: `/@fs/${fileUrl}`,
25+
src: fileUrl,
1926
type: 'module',
2027
},
2128
injectTo: 'body',
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import type { Plugin } from 'vite'
2+
import { exactRegex } from '@rolldown/pluginutils'
3+
import { getClientInjectUrl } from './injection'
4+
5+
export function DevToolsPreamble({ name }: { name: string }): Plugin {
6+
return {
7+
name: 'vite:devtools:preamble',
8+
apply: 'serve',
9+
resolveId: {
10+
order: 'pre',
11+
filter: { id: exactRegex(name) },
12+
handler(source) {
13+
if (source === name) {
14+
return `\0${source}`
15+
}
16+
},
17+
},
18+
load: {
19+
filter: { id: exactRegex(`\0${name}`) },
20+
handler(id) {
21+
if (id === `\0${name}`) {
22+
return `import ${JSON.stringify(getClientInjectUrl())}`
23+
}
24+
},
25+
},
26+
}
27+
}

packages/core/tsdown.config.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ const define = {
88
}
99

1010
export default defineConfig({
11-
exports: true,
11+
exports: {
12+
customExports: {
13+
'./preamble': './types/preamble.d.ts',
14+
},
15+
},
1216
plugins: [
1317
Vue({
1418
isProduction: true,

packages/core/types/preamble.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {}

pnpm-lock.yaml

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

pnpm-workspace.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ catalogs:
5252
deps:
5353
'@pnpm/read-project-manifest': ^1001.2.5
5454
'@rolldown/debug': ^1.0.0-rc.11
55+
'@rolldown/pluginutils': ^1.0.0-rc.11
5556
ansis: ^4.2.0
5657
birpc: ^4.0.0
5758
cac: ^7.0.0

0 commit comments

Comments
 (0)