@vitejs/devtools-kit 0.0.0-alpha.3 → 0.0.0-alpha.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client-script-BOHKZVPh.d.mts +1864 -0
- package/dist/client.d.mts +2 -0
- package/dist/client.mjs +1302 -0
- package/dist/constants.d.mts +7 -0
- package/dist/constants.mjs +20 -0
- package/dist/events-57bKw4ct.d.mts +71 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +7 -0
- package/dist/utils/events.d.mts +9 -0
- package/dist/utils/events.mjs +41 -0
- package/dist/utils/nanoid.d.mts +4 -0
- package/dist/utils/nanoid.mjs +11 -0
- package/dist/utils/shared-state.d.mts +53 -0
- package/dist/utils/shared-state.mjs +38 -0
- package/package.json +18 -13
- package/skills/vite-devtools-kit/SKILL.md +325 -0
- package/skills/vite-devtools-kit/references/dock-entry-types.md +240 -0
- package/skills/vite-devtools-kit/references/project-structure.md +247 -0
- package/skills/vite-devtools-kit/references/rpc-patterns.md +185 -0
- package/skills/vite-devtools-kit/references/shared-state-patterns.md +290 -0
- package/dist/client.d.ts +0 -17
- package/dist/client.js +0 -38
- package/dist/index.d.ts +0 -18
- package/dist/index.js +0 -17
- package/dist/rpc-ls6mUIa2.d.ts +0 -118
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# Dock Entry Types
|
|
2
|
+
|
|
3
|
+
Detailed configuration for each dock entry type.
|
|
4
|
+
|
|
5
|
+
## Common Properties
|
|
6
|
+
|
|
7
|
+
All dock entries share these properties:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
interface DockEntryBase {
|
|
11
|
+
id: string // Unique identifier
|
|
12
|
+
title: string // Display title
|
|
13
|
+
icon: string // URL, data URI, or Iconify name
|
|
14
|
+
category?: string // Grouping category
|
|
15
|
+
defaultOrder?: number // Sort order (higher = earlier)
|
|
16
|
+
isHidden?: boolean // Hide from dock
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Icons
|
|
21
|
+
|
|
22
|
+
<!-- eslint-skip -->
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
// Iconify (recommended)
|
|
26
|
+
icon: 'ph:chart-bar-duotone' // Phosphor Icons
|
|
27
|
+
icon: 'carbon:analytics' // Carbon Icons
|
|
28
|
+
icon: 'mdi:view-dashboard' // Material Design
|
|
29
|
+
|
|
30
|
+
// URL
|
|
31
|
+
icon: 'https://example.com/logo.svg'
|
|
32
|
+
|
|
33
|
+
// Data URI
|
|
34
|
+
icon: 'data:image/svg+xml,<svg>...</svg>'
|
|
35
|
+
|
|
36
|
+
// Light/dark variants
|
|
37
|
+
icon: {
|
|
38
|
+
light: 'https://example.com/logo-light.svg',
|
|
39
|
+
dark: 'https://example.com/logo-dark.svg',
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Browse icons at [Iconify](https://icon-sets.iconify.design/).
|
|
44
|
+
|
|
45
|
+
## Iframe Entries
|
|
46
|
+
|
|
47
|
+
Most common type. Displays your UI in an isolated iframe.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
interface IframeEntry extends DockEntryBase {
|
|
51
|
+
type: 'iframe'
|
|
52
|
+
url: string // URL to load
|
|
53
|
+
frameId?: string // Share iframe between entries
|
|
54
|
+
clientScript?: ClientScriptEntry // Optional client script
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Example
|
|
58
|
+
ctx.docks.register({
|
|
59
|
+
id: 'my-plugin',
|
|
60
|
+
title: 'My Plugin',
|
|
61
|
+
icon: 'ph:house-duotone',
|
|
62
|
+
type: 'iframe',
|
|
63
|
+
url: '/.my-plugin/',
|
|
64
|
+
})
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Hosting Your Own UI
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { fileURLToPath } from 'node:url'
|
|
71
|
+
|
|
72
|
+
const clientDist = fileURLToPath(
|
|
73
|
+
new URL('../dist/client', import.meta.url)
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
ctx.views.hostStatic('/.my-plugin/', clientDist)
|
|
77
|
+
|
|
78
|
+
ctx.docks.register({
|
|
79
|
+
id: 'my-plugin',
|
|
80
|
+
title: 'My Plugin',
|
|
81
|
+
icon: 'ph:house-duotone',
|
|
82
|
+
type: 'iframe',
|
|
83
|
+
url: '/.my-plugin/',
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Action Entries
|
|
88
|
+
|
|
89
|
+
Buttons that trigger client-side scripts. Perfect for inspectors and toggles.
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
interface ActionEntry extends DockEntryBase {
|
|
93
|
+
type: 'action'
|
|
94
|
+
action: {
|
|
95
|
+
importFrom: string // Package export path
|
|
96
|
+
importName?: string // Export name (default: 'default')
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Registration
|
|
101
|
+
ctx.docks.register({
|
|
102
|
+
id: 'my-inspector',
|
|
103
|
+
title: 'Inspector',
|
|
104
|
+
icon: 'ph:cursor-duotone',
|
|
105
|
+
type: 'action',
|
|
106
|
+
action: {
|
|
107
|
+
importFrom: 'my-plugin/devtools-action',
|
|
108
|
+
importName: 'default',
|
|
109
|
+
},
|
|
110
|
+
})
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Client Script Implementation
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
// src/devtools-action.ts
|
|
117
|
+
import type { DevToolsClientScriptContext } from '@vitejs/devtools-kit/client'
|
|
118
|
+
|
|
119
|
+
export default function setup(ctx: DevToolsClientScriptContext) {
|
|
120
|
+
let overlay: HTMLElement | null = null
|
|
121
|
+
|
|
122
|
+
ctx.current.events.on('entry:activated', () => {
|
|
123
|
+
overlay = document.createElement('div')
|
|
124
|
+
overlay.style.cssText = `
|
|
125
|
+
position: fixed;
|
|
126
|
+
inset: 0;
|
|
127
|
+
cursor: crosshair;
|
|
128
|
+
z-index: 99999;
|
|
129
|
+
`
|
|
130
|
+
overlay.onclick = (e) => {
|
|
131
|
+
const target = document.elementFromPoint(e.clientX, e.clientY)
|
|
132
|
+
console.log('Selected:', target)
|
|
133
|
+
}
|
|
134
|
+
document.body.appendChild(overlay)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
ctx.current.events.on('entry:deactivated', () => {
|
|
138
|
+
overlay?.remove()
|
|
139
|
+
overlay = null
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Package Export
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"exports": {
|
|
149
|
+
".": "./dist/index.mjs",
|
|
150
|
+
"./devtools-action": "./dist/devtools-action.mjs"
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Custom Render Entries
|
|
156
|
+
|
|
157
|
+
Render directly into the DevTools panel DOM. Use when you need direct DOM access or framework mounting.
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
interface CustomRenderEntry extends DockEntryBase {
|
|
161
|
+
type: 'custom-render'
|
|
162
|
+
renderer: {
|
|
163
|
+
importFrom: string
|
|
164
|
+
importName?: string
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
ctx.docks.register({
|
|
169
|
+
id: 'my-custom',
|
|
170
|
+
title: 'Custom View',
|
|
171
|
+
icon: 'ph:code-duotone',
|
|
172
|
+
type: 'custom-render',
|
|
173
|
+
renderer: {
|
|
174
|
+
importFrom: 'my-plugin/devtools-renderer',
|
|
175
|
+
importName: 'default',
|
|
176
|
+
},
|
|
177
|
+
})
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Renderer Implementation
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
// src/devtools-renderer.ts
|
|
184
|
+
import type { DevToolsClientScriptContext } from '@vitejs/devtools-kit/client'
|
|
185
|
+
|
|
186
|
+
export default function setup(ctx: DevToolsClientScriptContext) {
|
|
187
|
+
ctx.current.events.on('dom:panel:mounted', (panel) => {
|
|
188
|
+
// Vanilla JS
|
|
189
|
+
panel.innerHTML = `<div style="padding: 16px;">Hello</div>`
|
|
190
|
+
|
|
191
|
+
// Or mount Vue
|
|
192
|
+
// import { createApp } from 'vue'
|
|
193
|
+
// import App from './App.vue'
|
|
194
|
+
// createApp(App).mount(panel)
|
|
195
|
+
|
|
196
|
+
// Or mount React
|
|
197
|
+
// import { createRoot } from 'react-dom/client'
|
|
198
|
+
// createRoot(panel).render(<App />)
|
|
199
|
+
})
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Client Script Events
|
|
204
|
+
|
|
205
|
+
| Event | Payload | Description |
|
|
206
|
+
|-------|---------|-------------|
|
|
207
|
+
| `entry:activated` | - | Entry was selected |
|
|
208
|
+
| `entry:deactivated` | - | Entry was deselected |
|
|
209
|
+
| `entry:updated` | `DevToolsDockUserEntry` | Entry metadata changed |
|
|
210
|
+
| `dom:panel:mounted` | `HTMLDivElement` | Panel DOM ready (custom-render only) |
|
|
211
|
+
| `dom:iframe:mounted` | `HTMLIFrameElement` | Iframe mounted (iframe only) |
|
|
212
|
+
|
|
213
|
+
## Category Order
|
|
214
|
+
|
|
215
|
+
Default category ordering:
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
DEFAULT_CATEGORIES_ORDER = {
|
|
219
|
+
'~viteplus': -1000, // First
|
|
220
|
+
'default': 0,
|
|
221
|
+
'app': 100,
|
|
222
|
+
'framework': 200,
|
|
223
|
+
'web': 300,
|
|
224
|
+
'advanced': 400,
|
|
225
|
+
'~builtin': 1000, // Last
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Use `category` to group related entries:
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
ctx.docks.register({
|
|
233
|
+
id: 'my-plugin',
|
|
234
|
+
title: 'My Plugin',
|
|
235
|
+
icon: 'ph:house-duotone',
|
|
236
|
+
type: 'iframe',
|
|
237
|
+
url: '/.my-plugin/',
|
|
238
|
+
category: 'framework',
|
|
239
|
+
})
|
|
240
|
+
```
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# Project Structure
|
|
2
|
+
|
|
3
|
+
Recommended file organization for DevTools integrations.
|
|
4
|
+
|
|
5
|
+
## Basic Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
my-devtools-plugin/
|
|
9
|
+
├── src/
|
|
10
|
+
│ ├── node/
|
|
11
|
+
│ │ ├── index.ts # Plugin entry (exports main plugin)
|
|
12
|
+
│ │ ├── rpc/
|
|
13
|
+
│ │ │ ├── index.ts # RPC function exports
|
|
14
|
+
│ │ │ └── functions/ # Individual RPC functions
|
|
15
|
+
│ │ │ ├── get-modules.ts
|
|
16
|
+
│ │ │ └── get-stats.ts
|
|
17
|
+
│ │ └── utils.ts # Server-side utilities
|
|
18
|
+
│ ├── client/
|
|
19
|
+
│ │ ├── main.ts # Client app entry
|
|
20
|
+
│ │ ├── App.vue # Root component
|
|
21
|
+
│ │ └── composables/
|
|
22
|
+
│ │ └── rpc.ts # RPC composables
|
|
23
|
+
│ ├── types.ts # Type augmentations
|
|
24
|
+
│ └── shared/
|
|
25
|
+
│ └── constants.ts # Shared constants
|
|
26
|
+
├── dist/
|
|
27
|
+
│ ├── index.mjs # Node plugin bundle
|
|
28
|
+
│ └── client/ # Built client assets
|
|
29
|
+
├── package.json
|
|
30
|
+
└── tsconfig.json
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Package.json Configuration
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"name": "my-devtools-plugin",
|
|
38
|
+
"type": "module",
|
|
39
|
+
"exports": {
|
|
40
|
+
".": {
|
|
41
|
+
"import": "./dist/index.mjs",
|
|
42
|
+
"types": "./dist/index.d.ts"
|
|
43
|
+
},
|
|
44
|
+
"./devtools-action": {
|
|
45
|
+
"import": "./dist/devtools-action.mjs"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"files": ["dist"],
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsdown src/node/index.ts && vite build src/client",
|
|
51
|
+
"dev": "vite src/client"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@vitejs/devtools-kit": "^0.x.x",
|
|
56
|
+
"vite": "^6.x.x"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Plugin Entry (src/node/index.ts)
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
/// <reference types="@vitejs/devtools-kit" />
|
|
65
|
+
import type { Plugin } from 'vite'
|
|
66
|
+
import { fileURLToPath } from 'node:url'
|
|
67
|
+
import { rpcFunctions } from './rpc'
|
|
68
|
+
import '../types'
|
|
69
|
+
|
|
70
|
+
const clientDist = fileURLToPath(
|
|
71
|
+
new URL('../../dist/client', import.meta.url)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
export default function myPlugin(): Plugin {
|
|
75
|
+
return {
|
|
76
|
+
name: 'my-plugin',
|
|
77
|
+
|
|
78
|
+
devtools: {
|
|
79
|
+
setup(ctx) {
|
|
80
|
+
// Register all RPC functions
|
|
81
|
+
for (const fn of rpcFunctions) {
|
|
82
|
+
ctx.rpc.register(fn)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Host static UI
|
|
86
|
+
ctx.views.hostStatic('/.my-plugin/', clientDist)
|
|
87
|
+
|
|
88
|
+
// Register dock entry
|
|
89
|
+
ctx.docks.register({
|
|
90
|
+
id: 'my-plugin',
|
|
91
|
+
title: 'My Plugin',
|
|
92
|
+
icon: 'ph:puzzle-piece-duotone',
|
|
93
|
+
type: 'iframe',
|
|
94
|
+
url: '/.my-plugin/',
|
|
95
|
+
})
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## RPC Index (src/node/rpc/index.ts)
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import type { RpcDefinitionsToFunctions } from '@vitejs/devtools-kit'
|
|
106
|
+
import { getModules } from './functions/get-modules'
|
|
107
|
+
import { getStats } from './functions/get-stats'
|
|
108
|
+
import '@vitejs/devtools-kit'
|
|
109
|
+
|
|
110
|
+
export const rpcFunctions = [
|
|
111
|
+
getModules,
|
|
112
|
+
getStats,
|
|
113
|
+
] as const
|
|
114
|
+
|
|
115
|
+
export type ServerFunctions = RpcDefinitionsToFunctions<typeof rpcFunctions>
|
|
116
|
+
|
|
117
|
+
declare module '@vitejs/devtools-kit' {
|
|
118
|
+
export interface DevToolsRpcServerFunctions extends ServerFunctions {}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## RPC Function (src/node/rpc/functions/get-modules.ts)
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import type { Module } from '../../../types'
|
|
126
|
+
import { defineRpcFunction } from '@vitejs/devtools-kit'
|
|
127
|
+
|
|
128
|
+
export const getModules = defineRpcFunction({
|
|
129
|
+
name: 'my-plugin:get-modules',
|
|
130
|
+
type: 'query',
|
|
131
|
+
setup: (ctx) => {
|
|
132
|
+
return {
|
|
133
|
+
handler: async (): Promise<Module[]> => {
|
|
134
|
+
// Access vite config, server, etc. from ctx
|
|
135
|
+
const root = ctx.viteConfig.root
|
|
136
|
+
return []
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
})
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Type Augmentations (src/types.ts)
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
import '@vitejs/devtools-kit'
|
|
147
|
+
|
|
148
|
+
export interface Module {
|
|
149
|
+
id: string
|
|
150
|
+
size: number
|
|
151
|
+
imports: string[]
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface MyPluginState {
|
|
155
|
+
modules: Module[]
|
|
156
|
+
selectedId: string | null
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
declare module '@vitejs/devtools-kit' {
|
|
160
|
+
interface DevToolsRpcSharedStates {
|
|
161
|
+
'my-plugin:state': MyPluginState
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Client Entry (src/client/main.ts)
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import { createApp } from 'vue'
|
|
170
|
+
import App from './App.vue'
|
|
171
|
+
|
|
172
|
+
createApp(App).mount('#app')
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Client RPC Composable (src/client/composables/rpc.ts)
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
import { getDevToolsRpcClient } from '@vitejs/devtools-kit/client'
|
|
179
|
+
|
|
180
|
+
let clientPromise: Promise<Awaited<ReturnType<typeof getDevToolsRpcClient>>>
|
|
181
|
+
|
|
182
|
+
export function useRpc() {
|
|
183
|
+
if (!clientPromise) {
|
|
184
|
+
clientPromise = getDevToolsRpcClient()
|
|
185
|
+
}
|
|
186
|
+
return clientPromise
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Client App Component (src/client/App.vue)
|
|
191
|
+
|
|
192
|
+
```vue
|
|
193
|
+
<script setup lang="ts">
|
|
194
|
+
import type { Module } from '../types'
|
|
195
|
+
import { onMounted, shallowRef } from 'vue'
|
|
196
|
+
import { useRpc } from './composables/rpc'
|
|
197
|
+
|
|
198
|
+
const modules = shallowRef<Module[]>([])
|
|
199
|
+
const loading = shallowRef(true)
|
|
200
|
+
|
|
201
|
+
onMounted(async () => {
|
|
202
|
+
const rpc = await useRpc()
|
|
203
|
+
modules.value = await rpc.call('my-plugin:get-modules')
|
|
204
|
+
loading.value = false
|
|
205
|
+
})
|
|
206
|
+
</script>
|
|
207
|
+
|
|
208
|
+
<template>
|
|
209
|
+
<div class="p-4">
|
|
210
|
+
<h1 class="text-xl font-bold mb-4">
|
|
211
|
+
My Plugin
|
|
212
|
+
</h1>
|
|
213
|
+
<div v-if="loading">
|
|
214
|
+
Loading...
|
|
215
|
+
</div>
|
|
216
|
+
<ul v-else>
|
|
217
|
+
<li v-for="mod in modules" :key="mod.id">
|
|
218
|
+
{{ mod.id }} ({{ mod.size }} bytes)
|
|
219
|
+
</li>
|
|
220
|
+
</ul>
|
|
221
|
+
</div>
|
|
222
|
+
</template>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Vite Config for Client (src/client/vite.config.ts)
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
import vue from '@vitejs/plugin-vue'
|
|
229
|
+
import { defineConfig } from 'vite'
|
|
230
|
+
|
|
231
|
+
export default defineConfig({
|
|
232
|
+
plugins: [vue()],
|
|
233
|
+
build: {
|
|
234
|
+
outDir: '../../dist/client',
|
|
235
|
+
emptyOutDir: true,
|
|
236
|
+
},
|
|
237
|
+
})
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Real-World Reference
|
|
241
|
+
|
|
242
|
+
See [packages/vite](https://github.com/user/vite-devtools/tree/main/packages/vite) for a complete implementation example with:
|
|
243
|
+
|
|
244
|
+
- Multiple RPC functions organized by feature
|
|
245
|
+
- Nuxt-based client UI
|
|
246
|
+
- Complex data visualization
|
|
247
|
+
- Build session management
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# RPC Patterns
|
|
2
|
+
|
|
3
|
+
Advanced patterns for server-client communication in DevTools integrations.
|
|
4
|
+
|
|
5
|
+
## Function Types
|
|
6
|
+
|
|
7
|
+
| Type | Caching | Use Case |
|
|
8
|
+
|------|---------|----------|
|
|
9
|
+
| `query` | Can be cached | Read operations, data fetching |
|
|
10
|
+
| `action` | Never cached | Mutations, side effects |
|
|
11
|
+
| `static` | Cached indefinitely | Constants, configuration |
|
|
12
|
+
|
|
13
|
+
## Type-Safe RPC Setup
|
|
14
|
+
|
|
15
|
+
### Step 1: Define Types
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
// src/types.ts
|
|
19
|
+
import '@vitejs/devtools-kit'
|
|
20
|
+
|
|
21
|
+
interface Module {
|
|
22
|
+
id: string
|
|
23
|
+
size: number
|
|
24
|
+
imports: string[]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
declare module '@vitejs/devtools-kit' {
|
|
28
|
+
interface DevToolsRpcServerFunctions {
|
|
29
|
+
'my-plugin:list-modules': () => Promise<Module[]>
|
|
30
|
+
'my-plugin:get-module': (id: string) => Promise<Module | null>
|
|
31
|
+
'my-plugin:analyze': (options: { deep: boolean }) => Promise<void>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface DevToolsRpcClientFunctions {
|
|
35
|
+
'my-plugin:highlight': (selector: string) => void
|
|
36
|
+
'my-plugin:refresh': () => void
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Step 2: Import Types File
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
// src/node/plugin.ts
|
|
45
|
+
import '../types' // Side-effect import for type augmentation
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Step 3: Register Functions
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { defineRpcFunction } from '@vitejs/devtools-kit'
|
|
52
|
+
|
|
53
|
+
const listModules = defineRpcFunction({
|
|
54
|
+
name: 'my-plugin:list-modules',
|
|
55
|
+
type: 'query',
|
|
56
|
+
setup: () => ({
|
|
57
|
+
handler: async (): Promise<Module[]> => {
|
|
58
|
+
return Array.from(moduleMap.values())
|
|
59
|
+
},
|
|
60
|
+
}),
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Context Access in Setup
|
|
65
|
+
|
|
66
|
+
The `setup` function receives the full `DevToolsNodeContext`:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
defineRpcFunction({
|
|
70
|
+
name: 'my-plugin:get-config',
|
|
71
|
+
type: 'static',
|
|
72
|
+
setup: (ctx) => {
|
|
73
|
+
// Access at setup time (runs once)
|
|
74
|
+
const root = ctx.viteConfig.root
|
|
75
|
+
const isDev = ctx.mode === 'dev'
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
handler: async () => ({
|
|
79
|
+
root,
|
|
80
|
+
isDev,
|
|
81
|
+
plugins: ctx.viteConfig.plugins.map(p => p.name),
|
|
82
|
+
}),
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Broadcasting Patterns
|
|
89
|
+
|
|
90
|
+
### Basic Broadcast
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
// Notify all clients
|
|
94
|
+
ctx.rpc.broadcast({
|
|
95
|
+
method: 'my-plugin:refresh',
|
|
96
|
+
args: [],
|
|
97
|
+
})
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Broadcast with Data
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
ctx.viteServer?.watcher.on('change', (file) => {
|
|
104
|
+
ctx.rpc.broadcast({
|
|
105
|
+
method: 'my-plugin:file-changed',
|
|
106
|
+
args: [{ path: file, timestamp: Date.now() }],
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Optional Broadcast
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
// Won't error if no clients have registered the function
|
|
115
|
+
ctx.rpc.broadcast({
|
|
116
|
+
method: 'my-plugin:optional-update',
|
|
117
|
+
args: [data],
|
|
118
|
+
optional: true,
|
|
119
|
+
})
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Client Function Registration
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
// Client-side (action/renderer script)
|
|
126
|
+
export default function setup(ctx: DevToolsClientScriptContext) {
|
|
127
|
+
ctx.current.rpc.client.register({
|
|
128
|
+
name: 'my-plugin:highlight',
|
|
129
|
+
type: 'action',
|
|
130
|
+
handler: (selector: string) => {
|
|
131
|
+
const el = document.querySelector(selector)
|
|
132
|
+
if (el) {
|
|
133
|
+
el.style.outline = '3px solid red'
|
|
134
|
+
setTimeout(() => {
|
|
135
|
+
el.style.outline = ''
|
|
136
|
+
}, 2000)
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Collecting RPC Functions
|
|
144
|
+
|
|
145
|
+
Organize RPC functions in a registry pattern:
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
import { analyzeBundle } from './functions/analyze-bundle'
|
|
149
|
+
// src/node/rpc/index.ts
|
|
150
|
+
import { getModules } from './functions/get-modules'
|
|
151
|
+
import { getStats } from './functions/get-stats'
|
|
152
|
+
|
|
153
|
+
export const rpcFunctions = [
|
|
154
|
+
getModules,
|
|
155
|
+
getStats,
|
|
156
|
+
analyzeBundle,
|
|
157
|
+
] as const
|
|
158
|
+
|
|
159
|
+
// Register all in setup
|
|
160
|
+
for (const fn of rpcFunctions) {
|
|
161
|
+
ctx.rpc.register(fn)
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Type Extraction Utilities
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import type {
|
|
169
|
+
RpcDefinitionsFilter,
|
|
170
|
+
RpcDefinitionsToFunctions,
|
|
171
|
+
} from '@vitejs/devtools-kit'
|
|
172
|
+
|
|
173
|
+
// Extract all function types
|
|
174
|
+
export type ServerFunctions = RpcDefinitionsToFunctions<typeof rpcFunctions>
|
|
175
|
+
|
|
176
|
+
// Extract only static functions
|
|
177
|
+
export type StaticFunctions = RpcDefinitionsToFunctions<
|
|
178
|
+
RpcDefinitionsFilter<typeof rpcFunctions, 'static'>
|
|
179
|
+
>
|
|
180
|
+
|
|
181
|
+
// Augment the global interface
|
|
182
|
+
declare module '@vitejs/devtools-kit' {
|
|
183
|
+
export interface DevToolsRpcServerFunctions extends ServerFunctions {}
|
|
184
|
+
}
|
|
185
|
+
```
|