@vitejs/devtools-kit 0.0.0-alpha.8 → 0.1.0

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.
@@ -0,0 +1,292 @@
1
+ # Shared State Patterns
2
+
3
+ Synchronized state between server and all clients.
4
+
5
+ ## Basic Usage
6
+
7
+ Shared state is available during `devtools.setup()` — you can initialize it directly in the setup hook.
8
+
9
+ ### Server-Side
10
+
11
+ ```ts
12
+ const state = await ctx.rpc.sharedState.get('my-plugin:state', {
13
+ initialValue: {
14
+ count: 0,
15
+ items: [],
16
+ settings: { theme: 'dark' },
17
+ },
18
+ })
19
+
20
+ // Read
21
+ const current = state.value()
22
+
23
+ // Mutate (syncs to all clients)
24
+ state.mutate((draft) => {
25
+ draft.count += 1
26
+ draft.items.push({ id: Date.now(), name: 'New' })
27
+ })
28
+ ```
29
+
30
+ ### Client-Side
31
+
32
+ ```ts
33
+ import { getDevToolsRpcClient } from '@vitejs/devtools-kit/client'
34
+
35
+ const client = await getDevToolsRpcClient()
36
+ const state = await client.rpc.sharedState.get('my-plugin:state')
37
+
38
+ // Read
39
+ console.log(state.value())
40
+
41
+ // Subscribe
42
+ state.on('updated', (newState) => {
43
+ console.log('Updated:', newState)
44
+ })
45
+ ```
46
+
47
+ ## Type-Safe Shared State
48
+
49
+ ```ts
50
+ // src/types.ts
51
+ interface MyPluginState {
52
+ count: number
53
+ items: Array<{ id: string, name: string }>
54
+ settings: {
55
+ theme: 'light' | 'dark'
56
+ notifications: boolean
57
+ }
58
+ }
59
+
60
+ declare module '@vitejs/devtools-kit' {
61
+ interface DevToolsRpcSharedStates {
62
+ 'my-plugin:state': MyPluginState
63
+ }
64
+ }
65
+ ```
66
+
67
+ ## Vue Integration
68
+
69
+ ```ts
70
+ // composables/useSharedState.ts
71
+ import { getDevToolsRpcClient } from '@vitejs/devtools-kit/client'
72
+ import { onMounted, shallowRef } from 'vue'
73
+
74
+ export function useSharedState<T>(name: string) {
75
+ const state = shallowRef<T | null>(null)
76
+ const loading = shallowRef(true)
77
+
78
+ onMounted(async () => {
79
+ const client = await getDevToolsRpcClient()
80
+ const shared = await client.rpc.sharedState.get<T>(name)
81
+
82
+ state.value = shared.value()
83
+ loading.value = false
84
+
85
+ shared.on('updated', (newState) => {
86
+ state.value = newState
87
+ })
88
+ })
89
+
90
+ return { state, loading }
91
+ }
92
+
93
+ // Usage in component
94
+ const { state, loading } = useSharedState<MyPluginState>('my-plugin:state')
95
+ ```
96
+
97
+ ### Full Vue Component Example
98
+
99
+ ```vue
100
+ <script setup lang="ts">
101
+ import { getDevToolsRpcClient } from '@vitejs/devtools-kit/client'
102
+ import { onMounted, shallowRef } from 'vue'
103
+
104
+ interface PluginState {
105
+ count: number
106
+ items: string[]
107
+ }
108
+
109
+ const state = shallowRef<PluginState | null>(null)
110
+
111
+ onMounted(async () => {
112
+ const client = await getDevToolsRpcClient()
113
+ const shared = await client.rpc.sharedState.get<PluginState>('my-plugin:state')
114
+
115
+ state.value = shared.value()
116
+
117
+ shared.on('updated', (newState) => {
118
+ state.value = newState
119
+ })
120
+ })
121
+ </script>
122
+
123
+ <template>
124
+ <div v-if="state">
125
+ <p>Count: {{ state.count }}</p>
126
+ <ul>
127
+ <li v-for="item in state.items" :key="item">
128
+ {{ item }}
129
+ </li>
130
+ </ul>
131
+ </div>
132
+ </template>
133
+ ```
134
+
135
+ ## React Integration
136
+
137
+ ```tsx
138
+ import { getDevToolsRpcClient } from '@vitejs/devtools-kit/client'
139
+ import { useEffect, useState } from 'react'
140
+
141
+ function useSharedState<T>(name: string, fallback: T) {
142
+ const [state, setState] = useState<T>(fallback)
143
+ const [loading, setLoading] = useState(true)
144
+
145
+ useEffect(() => {
146
+ let mounted = true
147
+
148
+ async function init() {
149
+ const client = await getDevToolsRpcClient()
150
+ const shared = await client.rpc.sharedState.get<T>(name)
151
+
152
+ if (mounted) {
153
+ setState(shared.value() ?? fallback)
154
+ setLoading(false)
155
+
156
+ shared.on('updated', (newState) => {
157
+ if (mounted)
158
+ setState(newState)
159
+ })
160
+ }
161
+ }
162
+
163
+ init()
164
+ return () => {
165
+ mounted = false
166
+ }
167
+ }, [name])
168
+
169
+ return { state, loading }
170
+ }
171
+
172
+ // Usage
173
+ function MyComponent() {
174
+ const { state, loading } = useSharedState('my-plugin:state', { count: 0 })
175
+
176
+ if (loading)
177
+ return <div>Loading...</div>
178
+
179
+ return (
180
+ <div>
181
+ Count:
182
+ {state.count}
183
+ </div>
184
+ )
185
+ }
186
+ ```
187
+
188
+ ## Svelte Integration
189
+
190
+ ```svelte
191
+ <script lang="ts">
192
+ import { onMount } from 'svelte'
193
+ import { writable } from 'svelte/store'
194
+ import { getDevToolsRpcClient } from '@vitejs/devtools-kit/client'
195
+
196
+ interface PluginState {
197
+ count: number
198
+ }
199
+
200
+ const state = writable<PluginState>({ count: 0 })
201
+
202
+ onMount(async () => {
203
+ const client = await getDevToolsRpcClient()
204
+ const shared = await client.rpc.sharedState.get<PluginState>('my-plugin:state')
205
+
206
+ state.set(shared.value())
207
+
208
+ shared.on('updated', (newState) => {
209
+ state.set(newState)
210
+ })
211
+ })
212
+ </script>
213
+
214
+ <p>Count: {$state.count}</p>
215
+ ```
216
+
217
+ ## Best Practices
218
+
219
+ ### Batch Mutations
220
+
221
+ ```ts
222
+ // Good - single sync event
223
+ state.mutate((draft) => {
224
+ draft.count += 1
225
+ draft.lastUpdate = Date.now()
226
+ draft.items.push(newItem)
227
+ })
228
+
229
+ // Bad - multiple sync events
230
+ state.mutate((d) => {
231
+ d.count += 1
232
+ })
233
+ state.mutate((d) => {
234
+ d.lastUpdate = Date.now()
235
+ })
236
+ state.mutate((d) => {
237
+ d.items.push(newItem)
238
+ })
239
+ ```
240
+
241
+ ### Keep State Small
242
+
243
+ For large datasets, store IDs in shared state and fetch details via RPC:
244
+
245
+ <!-- eslint-skip -->
246
+ ```ts
247
+ // Shared state (small)
248
+ {
249
+ moduleIds: ['a', 'b', 'c'],
250
+ selectedId: 'a'
251
+ }
252
+
253
+ // Fetch full data via RPC when needed
254
+ const module = await rpc.call('my-plugin:get-module', state.selectedId)
255
+ ```
256
+
257
+ ### Serializable Data Only
258
+
259
+ <!-- eslint-skip -->
260
+ ```ts
261
+ // Bad - functions can't be serialized
262
+ { count: 0, increment: () => {} }
263
+
264
+ // Bad - circular references
265
+ const obj = { child: null }
266
+ obj.child = obj
267
+
268
+ // Good - plain data
269
+ { count: 0, items: [{ id: 1 }] }
270
+ ```
271
+
272
+ ### Real-Time Updates Example
273
+
274
+ ```ts
275
+ const plugin: Plugin = {
276
+ devtools: {
277
+ async setup(ctx) {
278
+ const state = await ctx.rpc.sharedState.get('my-plugin:state', {
279
+ initialValue: { modules: [], lastUpdate: 0 },
280
+ })
281
+
282
+ // Update state when Vite processes modules
283
+ ctx.viteServer?.watcher.on('change', (file) => {
284
+ state.mutate((draft) => {
285
+ draft.modules.push(file)
286
+ draft.lastUpdate = Date.now()
287
+ })
288
+ })
289
+ }
290
+ }
291
+ }
292
+ ```
@@ -1,156 +0,0 @@
1
- import { BirpcFn, BirpcReturn as BirpcReturn$1 } from "birpc";
2
- import { Plugin, ResolvedConfig, ViteDevServer } from "vite";
3
-
4
- //#region src/types/rpc-augments.d.ts
5
- /**
6
- * To be extended
7
- */
8
- interface DevToolsRpcClientFunctions {}
9
- /**
10
- * To be extended
11
- */
12
- interface DevToolsRpcServerFunctions {}
13
- //#endregion
14
- //#region src/types/utils.d.ts
15
- type Thenable<T> = T | Promise<T>;
16
- type EntriesToObject<T extends readonly [string, any][]> = { [K in T[number] as K[0]]: K[1] };
17
- //#endregion
18
- //#region src/types/views.d.ts
19
- interface DevToolsDockHost {
20
- views: Map<string, DevToolsDockEntry>;
21
- register: (entry: DevToolsDockEntry) => void;
22
- update: (entry: DevToolsDockEntry) => void;
23
- values: () => DevToolsDockEntry[];
24
- }
25
- interface DevToolsViewHost {
26
- /**
27
- * @internal
28
- */
29
- buildStaticDirs: {
30
- baseUrl: string;
31
- distDir: string;
32
- }[];
33
- /**
34
- * Helper to host static files
35
- * - In `dev` mode, it will register middleware to `viteServer.middlewares` to host the static files
36
- * - In `build` mode, it will copy the static files to the dist directory
37
- */
38
- hostStatic: (baseUrl: string, distDir: string) => void;
39
- }
40
- interface DevToolsDockEntryBase {
41
- id: string;
42
- title: string;
43
- icon: string;
44
- }
45
- interface ClientScriptEntry {
46
- /**
47
- * The filepath or module name to import from
48
- */
49
- importFrom: string;
50
- /**
51
- * The name to import the module as
52
- *
53
- * @default 'default'
54
- */
55
- importName?: string;
56
- }
57
- interface DevToolsViewIframe extends DevToolsDockEntryBase {
58
- type: 'iframe';
59
- url: string;
60
- /**
61
- * The id of the iframe, if multiple tabs is assigned with the same id, the iframe will be shared.
62
- *
63
- * When not provided, it would be treated as a unique frame.
64
- */
65
- frameId?: string;
66
- /**
67
- * Optional script to import into the iframe
68
- */
69
- import?: ClientScriptEntry;
70
- }
71
- interface DevToolsViewWebComponent extends DevToolsDockEntryBase {
72
- type: 'webcomponent';
73
- import: ClientScriptEntry;
74
- }
75
- interface DevToolsViewAction extends DevToolsDockEntryBase {
76
- type: 'action';
77
- import: ClientScriptEntry;
78
- }
79
- interface DevToolsViewCustomRender extends DevToolsDockEntryBase {
80
- type: 'custom-render';
81
- import: ClientScriptEntry;
82
- }
83
- type DevToolsDockEntry = DevToolsViewIframe | DevToolsViewWebComponent | DevToolsViewAction | DevToolsViewCustomRender;
84
- //#endregion
85
- //#region src/types/vite-plugin.d.ts
86
- interface DevToolsCapabilities {
87
- rpc?: boolean;
88
- views?: boolean;
89
- }
90
- interface DevToolsPluginOptions {
91
- capabilities?: {
92
- dev?: DevToolsCapabilities | boolean;
93
- build?: DevToolsCapabilities | boolean;
94
- };
95
- setup: (context: DevToolsNodeContext) => void | Promise<void>;
96
- }
97
- interface DevToolsNodeContext {
98
- readonly cwd: string;
99
- readonly mode: 'dev' | 'build';
100
- readonly viteConfig: ResolvedConfig;
101
- readonly viteServer?: ViteDevServer;
102
- rpc: RpcFunctionsHost;
103
- docks: DevToolsDockHost;
104
- views: DevToolsViewHost;
105
- utils: DevToolsNodeUtils;
106
- }
107
- interface DevToolsNodeUtils {
108
- clientEntryFromSimpleFunction: (fn: () => void) => ClientScriptEntry;
109
- }
110
- interface ConnectionMeta {
111
- backend: 'websocket' | 'static';
112
- websocket?: number | string;
113
- }
114
- //#endregion
115
- //#region src/types/rpc.d.ts
116
- /**
117
- * Type of the RPC function,
118
- * - static: A function that returns a static data (can be cached and dumped)
119
- * - action: A function that performs an action (no data returned)
120
- * - query: A function that queries a resource
121
- */
122
- type RpcFunctionType = 'static' | 'action' | 'query';
123
- interface RpcFunctionsHost {
124
- context: DevToolsNodeContext;
125
- readonly functions: DevToolsRpcServerFunctions;
126
- readonly definitions: Map<string, RpcFunctionDefinition<string, any, any, any>>;
127
- register: (fn: RpcFunctionDefinition<string, any, any, any>) => void;
128
- update: (fn: RpcFunctionDefinition<string, any, any, any>) => void;
129
- }
130
- interface RpcFunctionSetupResult<ARGS extends any[], RETURN = void> {
131
- handler: (...args: ARGS) => RETURN;
132
- }
133
- interface RpcFunctionDefinition<NAME extends string, TYPE extends RpcFunctionType, ARGS extends any[] = [], RETURN = void> {
134
- name: NAME;
135
- type: TYPE;
136
- setup: (context: DevToolsNodeContext) => Thenable<RpcFunctionSetupResult<ARGS, RETURN>>;
137
- handler?: (...args: ARGS) => RETURN;
138
- __resolved?: RpcFunctionSetupResult<ARGS, RETURN>;
139
- __promise?: Thenable<RpcFunctionSetupResult<ARGS, RETURN>>;
140
- }
141
- type RpcDefinitionsToFunctions<T extends readonly RpcFunctionDefinition<any, any, any>[]> = EntriesToObject<{ [K in keyof T]: [T[K]['name'], Awaited<ReturnType<T[K]['setup']>>['handler']] }>;
142
- type RpcDefinitionsFilter<T extends readonly RpcFunctionDefinition<any, any, any>[], Type extends RpcFunctionType> = { [K in keyof T]: T[K] extends {
143
- type: Type;
144
- } ? T[K] : never };
145
- //#endregion
146
- //#region src/types/vite-augment.d.ts
147
- declare module 'vite' {
148
- interface Plugin {
149
- devtools?: DevToolsPluginOptions;
150
- }
151
- }
152
- interface PluginWithDevTools extends Plugin {
153
- devtools?: DevToolsPluginOptions;
154
- }
155
- //#endregion
156
- export { DevToolsViewWebComponent as C, DevToolsRpcServerFunctions as D, DevToolsRpcClientFunctions as E, DevToolsViewIframe as S, Thenable as T, DevToolsDockEntryBase as _, RpcDefinitionsToFunctions as a, DevToolsViewCustomRender as b, RpcFunctionType as c, DevToolsCapabilities as d, DevToolsNodeContext as f, DevToolsDockEntry as g, ClientScriptEntry as h, RpcDefinitionsFilter as i, RpcFunctionsHost as l, DevToolsPluginOptions as m, BirpcFn as n, RpcFunctionDefinition as o, DevToolsNodeUtils as p, BirpcReturn$1 as r, RpcFunctionSetupResult as s, PluginWithDevTools as t, ConnectionMeta as u, DevToolsDockHost as v, EntriesToObject as w, DevToolsViewHost as x, DevToolsViewAction as y };