Skip to content

Commit 52f8ee7

Browse files
antfuwebfansplz
authored andcommitted
docs: add devtools-kit skill
1 parent 3530e8a commit 52f8ee7

File tree

5 files changed

+1287
-0
lines changed

5 files changed

+1287
-0
lines changed

skills/vite-devtools-kit/SKILL.md

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
---
2+
name: writing-vite-devtools-integrations
3+
description: >
4+
Creates devtools integrations for Vite using @vitejs/devtools-kit.
5+
Use when building Vite plugins with devtools panels, RPC functions,
6+
dock entries, shared state, or any devtools-related functionality.
7+
Applies to files importing from @vitejs/devtools-kit or containing
8+
devtools.setup hooks in Vite plugins.
9+
---
10+
11+
# Vite DevTools Kit
12+
13+
Build custom developer tools that integrate with Vite DevTools using `@vitejs/devtools-kit`.
14+
15+
## Core Concepts
16+
17+
A DevTools plugin extends a Vite plugin with a `devtools.setup(ctx)` hook. The context provides:
18+
19+
| Property | Purpose |
20+
|----------|---------|
21+
| `ctx.docks` | Register dock entries (iframe, action, custom-render) |
22+
| `ctx.views` | Host static files for UI |
23+
| `ctx.rpc` | Register RPC functions, broadcast to clients |
24+
| `ctx.rpc.sharedState` | Synchronized server-client state |
25+
| `ctx.viteConfig` | Resolved Vite configuration |
26+
| `ctx.viteServer` | Dev server instance (dev mode only) |
27+
| `ctx.mode` | `'dev'` or `'build'` |
28+
29+
## Quick Start: Minimal Plugin
30+
31+
```ts
32+
/// <reference types="@vitejs/devtools-kit" />
33+
import type { Plugin } from 'vite'
34+
35+
export default function myPlugin(): Plugin {
36+
return {
37+
name: 'my-plugin',
38+
devtools: {
39+
setup(ctx) {
40+
ctx.docks.register({
41+
id: 'my-plugin',
42+
title: 'My Plugin',
43+
icon: 'ph:puzzle-piece-duotone',
44+
type: 'iframe',
45+
url: 'https://example.com/devtools',
46+
})
47+
},
48+
},
49+
}
50+
}
51+
```
52+
53+
## Quick Start: Full Integration
54+
55+
```ts
56+
/// <reference types="@vitejs/devtools-kit" />
57+
import type { Plugin } from 'vite'
58+
import { fileURLToPath } from 'node:url'
59+
import { defineRpcFunction } from '@vitejs/devtools-kit'
60+
61+
export default function myAnalyzer(): Plugin {
62+
const data = new Map<string, { size: number }>()
63+
64+
return {
65+
name: 'my-analyzer',
66+
67+
// Collect data in Vite hooks
68+
transform(code, id) {
69+
data.set(id, { size: code.length })
70+
},
71+
72+
devtools: {
73+
setup(ctx) {
74+
// 1. Host static UI
75+
const clientPath = fileURLToPath(
76+
new URL('../dist/client', import.meta.url)
77+
)
78+
ctx.views.hostStatic('/.my-analyzer/', clientPath)
79+
80+
// 2. Register dock entry
81+
ctx.docks.register({
82+
id: 'my-analyzer',
83+
title: 'Analyzer',
84+
icon: 'ph:chart-bar-duotone',
85+
type: 'iframe',
86+
url: '/.my-analyzer/',
87+
})
88+
89+
// 3. Register RPC function
90+
ctx.rpc.register(
91+
defineRpcFunction({
92+
name: 'my-analyzer:get-data',
93+
type: 'query',
94+
setup: () => ({
95+
handler: async () => Array.from(data.entries()),
96+
}),
97+
})
98+
)
99+
},
100+
},
101+
}
102+
}
103+
```
104+
105+
## Namespacing Convention
106+
107+
**CRITICAL**: Always prefix RPC functions, shared state keys, and dock IDs with your plugin name:
108+
109+
```ts
110+
// Good - namespaced
111+
'my-plugin:get-modules'
112+
'my-plugin:state'
113+
114+
// Bad - may conflict
115+
'get-modules'
116+
'state'
117+
```
118+
119+
## Dock Entry Types
120+
121+
| Type | Use Case |
122+
|------|----------|
123+
| `iframe` | Full UI panels, dashboards (most common) |
124+
| `action` | Buttons that trigger client-side scripts (inspectors, toggles) |
125+
| `custom-render` | Direct DOM access in panel (framework mounting) |
126+
127+
### Iframe Entry
128+
129+
```ts
130+
ctx.docks.register({
131+
id: 'my-plugin',
132+
title: 'My Plugin',
133+
icon: 'ph:house-duotone',
134+
type: 'iframe',
135+
url: '/.my-plugin/',
136+
})
137+
```
138+
139+
### Action Entry
140+
141+
```ts
142+
ctx.docks.register({
143+
id: 'my-inspector',
144+
title: 'Inspector',
145+
icon: 'ph:cursor-duotone',
146+
type: 'action',
147+
action: {
148+
importFrom: 'my-plugin/devtools-action',
149+
importName: 'default',
150+
},
151+
})
152+
```
153+
154+
### Custom Render Entry
155+
156+
```ts
157+
ctx.docks.register({
158+
id: 'my-custom',
159+
title: 'Custom View',
160+
icon: 'ph:code-duotone',
161+
type: 'custom-render',
162+
renderer: {
163+
importFrom: 'my-plugin/devtools-renderer',
164+
importName: 'default',
165+
},
166+
})
167+
```
168+
169+
## RPC Functions
170+
171+
### Server-Side Definition
172+
173+
```ts
174+
import { defineRpcFunction } from '@vitejs/devtools-kit'
175+
176+
const getModules = defineRpcFunction({
177+
name: 'my-plugin:get-modules',
178+
type: 'query', // 'query' | 'action' | 'static'
179+
setup: ctx => ({
180+
handler: async (filter?: string) => {
181+
// ctx has full DevToolsNodeContext
182+
return modules.filter(m => !filter || m.includes(filter))
183+
},
184+
}),
185+
})
186+
187+
// Register in setup
188+
ctx.rpc.register(getModules)
189+
```
190+
191+
### Client-Side Call (iframe)
192+
193+
```ts
194+
import { getDevToolsRpcClient } from '@vitejs/devtools-kit/client'
195+
196+
const rpc = await getDevToolsRpcClient()
197+
const modules = await rpc.call('my-plugin:get-modules', 'src/')
198+
```
199+
200+
### Client-Side Call (action/renderer script)
201+
202+
```ts
203+
import type { DevToolsClientScriptContext } from '@vitejs/devtools-kit/client'
204+
205+
export default function setup(ctx: DevToolsClientScriptContext) {
206+
ctx.current.events.on('entry:activated', async () => {
207+
const data = await ctx.current.rpc.call('my-plugin:get-data')
208+
})
209+
}
210+
```
211+
212+
### Broadcasting to Clients
213+
214+
```ts
215+
// Server broadcasts to all clients
216+
ctx.rpc.broadcast({
217+
method: 'my-plugin:on-update',
218+
args: [{ changedFile: '/src/main.ts' }],
219+
})
220+
```
221+
222+
## Type Safety
223+
224+
Extend the DevTools Kit interfaces for full type checking:
225+
226+
```ts
227+
// src/types.ts
228+
import '@vitejs/devtools-kit'
229+
230+
declare module '@vitejs/devtools-kit' {
231+
interface DevToolsRpcServerFunctions {
232+
'my-plugin:get-modules': (filter?: string) => Promise<Module[]>
233+
}
234+
235+
interface DevToolsRpcClientFunctions {
236+
'my-plugin:on-update': (data: { changedFile: string }) => void
237+
}
238+
239+
interface DevToolsRpcSharedStates {
240+
'my-plugin:state': MyPluginState
241+
}
242+
}
243+
```
244+
245+
## Shared State
246+
247+
### Server-Side
248+
249+
```ts
250+
const state = await ctx.rpc.sharedState.get('my-plugin:state', {
251+
initialValue: { count: 0, items: [] },
252+
})
253+
254+
// Read
255+
console.log(state.value())
256+
257+
// Mutate (auto-syncs to clients)
258+
state.mutate((draft) => {
259+
draft.count += 1
260+
draft.items.push('new item')
261+
})
262+
```
263+
264+
### Client-Side
265+
266+
```ts
267+
const client = await getDevToolsRpcClient()
268+
const state = await client.rpc.sharedState.get('my-plugin:state')
269+
270+
// Read
271+
console.log(state.value())
272+
273+
// Subscribe to changes
274+
state.on('updated', (newState) => {
275+
console.log('State updated:', newState)
276+
})
277+
```
278+
279+
## Client Scripts
280+
281+
For action buttons and custom renderers:
282+
283+
```ts
284+
// src/devtools-action.ts
285+
import type { DevToolsClientScriptContext } from '@vitejs/devtools-kit/client'
286+
287+
export default function setup(ctx: DevToolsClientScriptContext) {
288+
ctx.current.events.on('entry:activated', () => {
289+
console.log('Action activated')
290+
// Your inspector/tool logic here
291+
})
292+
293+
ctx.current.events.on('entry:deactivated', () => {
294+
console.log('Action deactivated')
295+
// Cleanup
296+
})
297+
}
298+
```
299+
300+
Export from package.json:
301+
302+
```json
303+
{
304+
"exports": {
305+
".": "./dist/index.mjs",
306+
"./devtools-action": "./dist/devtools-action.mjs"
307+
}
308+
}
309+
```
310+
311+
## Best Practices
312+
313+
1. **Always namespace** - Prefix all identifiers with your plugin name
314+
2. **Use type augmentation** - Extend `DevToolsRpcServerFunctions` for type-safe RPC
315+
3. **Keep state serializable** - No functions or circular references in shared state
316+
4. **Batch mutations** - Use single `mutate()` call for multiple changes
317+
5. **Host static files** - Use `ctx.views.hostStatic()` for your UI assets
318+
6. **Use Iconify icons** - Prefer `ph:*` (Phosphor) icons: `icon: 'ph:chart-bar-duotone'`
319+
320+
## Further Reading
321+
322+
- [RPC Patterns](./references/rpc-patterns.md) - Advanced RPC patterns and type utilities
323+
- [Dock Entry Types](./references/dock-entry-types.md) - Detailed dock configuration options
324+
- [Shared State Patterns](./references/shared-state-patterns.md) - Framework integration examples
325+
- [Project Structure](./references/project-structure.md) - Recommended file organization

0 commit comments

Comments
 (0)