Skip to content

Commit f9dda69

Browse files
antfubotantfu
andauthored
feat(devtools): devtools:ready hook + nostics-driven deprecation foundation (#1021)
Co-authored-by: Anthony Fu <github@antfu.me>
1 parent fb3fac0 commit f9dda69

21 files changed

Lines changed: 1515 additions & 727 deletions

File tree

‎docs/content/2.module/1.utils-kit.md‎

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ We recommend module authors to install `@nuxt/devtools-kit` as a dependency and
1919

2020
### `addCustomTab()`
2121

22+
::warning
23+
**Deprecated.** `addCustomTab()` is soft-deprecated (`NDT_DEP_0005`) in favour of
24+
registering a dock entry on the Vite DevTools docks host
25+
(`nuxt.devtools.docks.register(...)`). It still works as a shim. Note the docks
26+
host does not yet cover `vnode` views or tab categories. See the
27+
[migration guide](/module/migration-v4#ndt_dep_0005).
28+
::
29+
2230
A shorthand for calling the hook `devtools:customTabs`.
2331

2432
```ts
@@ -56,10 +64,24 @@ const view: ModuleIframeView = {
5664

5765
### `refreshCustomTabs()`
5866

67+
::warning
68+
**Deprecated.** `refreshCustomTabs()` is soft-deprecated (`NDT_DEP_0006`). Update
69+
dock entries directly via the handle returned by
70+
`nuxt.devtools.docks.register(...)`. It still works as a shim. See the
71+
[migration guide](/module/migration-v4#ndt_dep_0006).
72+
::
73+
5974
A shorthand for call hook `devtools:customTabs:refresh`. It will refresh all custom tabs.
6075

6176
### `startSubprocess()`
6277

78+
::warning
79+
**Deprecated.** `startSubprocess()` is soft-deprecated (`NDT_DEP_0004`) in favour
80+
of the Vite DevTools terminals host
81+
(`nuxt.devtools.terminals.startChildProcess(...)`). It still works as a shim. See
82+
the [migration guide](/module/migration-v4#ndt_dep_0004).
83+
::
84+
6385
Start a sub process using `tinyexec` and create a terminal tab in DevTools.
6486

6587
```ts
@@ -90,6 +112,12 @@ subprocess.terminate()
90112

91113
### `extendServerRpc()`
92114

115+
::warning
116+
**Deprecated.** `extendServerRpc()` is soft-deprecated (`NDT_DEP_0003`) in favour
117+
of the Vite DevTools RPC registration. It still works as a shim. See the
118+
[migration guide](/module/migration-v4#ndt_dep_0003).
119+
::
120+
93121
Extend the server RPC with your own methods.
94122

95123
```ts
@@ -102,8 +130,95 @@ const rpc = extendServerRpc('my-module', {
102130
})
103131
```
104132

133+
The forward path registers functions on the Vite DevTools RPC host from the
134+
[`onDevtoolsReady()`](#ondevtoolsready) hook:
135+
136+
```ts
137+
import { onDevtoolsReady } from '@nuxt/devtools-kit'
138+
import { defineRpcFunction } from '@vitejs/devtools-kit'
139+
140+
export default defineNuxtModule({
141+
setup(_options, nuxt) {
142+
onDevtoolsReady((ctx) => {
143+
ctx.rpc.register(defineRpcFunction({
144+
name: 'my-module:my-method',
145+
type: 'query',
146+
setup: () => ({ handler: async () => 'hello' }),
147+
}))
148+
})
149+
},
150+
})
151+
```
152+
105153
Learn more about [Custom RPC functions](/module/guide#custom-rpc-functions).
106154

155+
### `onDevtoolsReady()`
156+
157+
**The recommended entry point for DevTools integration.** The callback runs once
158+
the Vite DevTools kit has connected and receives the connected
159+
`ViteDevToolsNodeContext`, so the full devframe surface is guaranteed to be
160+
available — no connect-safe buffering needed. Do all your DevTools integration here:
161+
162+
```ts
163+
import { onDevtoolsReady } from '@nuxt/devtools-kit'
164+
import { defineRpcFunction } from '@vitejs/devtools-kit'
165+
166+
export default defineNuxtModule({
167+
setup(_options, nuxt) {
168+
onDevtoolsReady((ctx) => {
169+
// `ctx` is the connected ViteDevToolsNodeContext
170+
ctx.docks.register({
171+
id: 'my-module',
172+
title: 'My Module',
173+
type: 'iframe',
174+
url: '/__my-module__/',
175+
})
176+
177+
ctx.rpc.register(defineRpcFunction({
178+
name: 'my-module:my-method',
179+
type: 'query',
180+
setup: () => ({ handler: async () => 'hello' }),
181+
}))
182+
183+
ctx.messages.info('My module is ready')
184+
})
185+
},
186+
})
187+
```
188+
189+
It is backed by the `devtools:ready` Nuxt hook:
190+
191+
```ts
192+
nuxt.hook('devtools:ready', (ctx) => {
193+
// ...
194+
})
195+
```
196+
197+
Inside `onDevtoolsReady()`, the `ctx` is the connected `ViteDevToolsNodeContext`,
198+
which exposes the full devframe-native surface:
199+
200+
- `ctx.docks` — register dock entries (iframe / action / json-render / launcher)
201+
- `ctx.terminals` — spawn and manage child processes / PTY sessions
202+
- `ctx.messages` — structured messages and toast notifications
203+
- `ctx.commands` — command-palette entries
204+
- `ctx.diagnostics` — structured, coded diagnostics (powered by [nostics](https://github.com/vercel-labs/nostics))
205+
- `ctx.rpc` — the devframe RPC host (`register`/`invokeLocal`/`broadcast`/`sharedState`/`streaming`/…)
206+
207+
If you need the raw context outside the hook, `nuxt.devtools.devtoolsKit` is
208+
available as an escape hatch (it is `undefined` until the kit connects).
209+
210+
See the [Vite DevTools Kit](https://github.com/vitejs/devtools) docs for the
211+
full host API.
212+
213+
### Deprecation diagnostics
214+
215+
Nuxt DevTools emits coded deprecation diagnostics (`NDT_DEP_xxxx`) through
216+
`nostics`. They print to the terminal and, once the Vite DevTools kit connects,
217+
also surface in the DevTools diagnostics UI, each with a code, a fix, and a link
218+
to the [migration guide](/module/migration-v4). See
219+
[`extendServerRpc()`](/module/migration-v4#ndt_dep_0003) and
220+
[`startSubprocess()`](/module/migration-v4#ndt_dep_0004).
221+
107222
## `@nuxt/devtools-kit/iframe-client`
108223

109224
To provide complex interactions for your module integrations, we recommend to host your own view and display it in devtools via iframe.

‎docs/content/2.module/3.migration-v4.md‎

Lines changed: 137 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,50 @@ startSubprocess({
3535
})
3636
```
3737

38-
### `getProcess()` is deprecated
38+
### `startSubprocess()` is deprecated {#ndt_dep_0004}
3939

40-
The return value of `startSubprocess()` now provides `getResult()` instead of `getProcess()`.
40+
`startSubprocess()` is soft-deprecated in favour of the Vite DevTools terminals
41+
host, used from the [`onDevtoolsReady`](/module/utils-kit#ondevtoolsready) hook.
42+
It still works as a shim, but emits the `NDT_DEP_0004` deprecation diagnostic.
4143

42-
- `getProcess()` still works but logs a deprecation warning and returns `ChildProcess | undefined` (was `ExecaChildProcess<string>`)
44+
```diff
45+
- import { startSubprocess } from '@nuxt/devtools-kit'
46+
+ import { onDevtoolsReady } from '@nuxt/devtools-kit'
47+
48+
- const subprocess = startSubprocess(
49+
- { command: 'vite', args: ['build', '--watch'] },
50+
- { id: 'my-module:build', name: 'Build', icon: 'ph:terminal-duotone' },
51+
- )
52+
+ onDevtoolsReady(async (ctx) => {
53+
+ const session = await ctx.terminals.startChildProcess(
54+
+ { command: 'vite', args: ['build', '--watch'], cwd: process.cwd() },
55+
+ { id: 'my-module:build', title: 'Build', icon: 'ph:terminal-duotone' },
56+
+ )
57+
+ })
58+
```
59+
60+
The terminals host session exposes `terminate()`, `restart()`,
61+
`getChildProcess()`, and `getResult()` — a `tinyexec`-style awaitable handle that
62+
resolves to `{ stdout, stderr, exitCode }` — so the `startSubprocess().getResult()`
63+
ergonomics carry over:
64+
65+
```ts
66+
onDevtoolsReady(async (ctx) => {
67+
const session = await ctx.terminals.startChildProcess(
68+
{ command: 'npm', args: ['install'] },
69+
{ id: 'my-module:install', title: 'Install' },
70+
)
71+
const { exitCode, stderr } = await session.getResult()
72+
if (exitCode !== 0)
73+
console.error(stderr)
74+
})
75+
```
76+
77+
### `getProcess()` is deprecated {#ndt_dep_0001}
78+
79+
The return value of `startSubprocess()` now also provides `getResult()`; use it instead of the deprecated `getProcess()` method.
80+
81+
- `getProcess()` still works but emits the `NDT_DEP_0001` deprecation diagnostic and returns `ChildProcess | undefined` (was `ExecaChildProcess<string>`)
4382
- `getResult()` returns a tinyexec `Result` object with `.kill()`, `.process`, `.pipe()`, and more
4483

4584
```diff
@@ -51,6 +90,101 @@ const subprocess = startSubprocess(/* ... */)
5190
+ result.process?.stdout?.on('data', handler)
5291
```
5392

93+
## `extendServerRpc()` is deprecated {#ndt_dep_0003}
94+
95+
`extendServerRpc()` is soft-deprecated in favour of the Vite DevTools RPC
96+
registration, done from the [`onDevtoolsReady`](/module/utils-kit#ondevtoolsready)
97+
hook. It still works as a shim, but emits the `NDT_DEP_0003` deprecation
98+
diagnostic.
99+
100+
```diff
101+
- import { extendServerRpc } from '@nuxt/devtools-kit'
102+
+ import { onDevtoolsReady } from '@nuxt/devtools-kit'
103+
+ import { defineRpcFunction } from '@vitejs/devtools-kit'
104+
105+
- const rpc = extendServerRpc('my-module', {
106+
- async getData() {
107+
- return 'hello'
108+
- },
109+
- })
110+
+ onDevtoolsReady((ctx) => {
111+
+ ctx.rpc.register(defineRpcFunction({
112+
+ name: 'my-module:get-data',
113+
+ type: 'query',
114+
+ setup: () => ({ handler: async () => 'hello' }),
115+
+ }))
116+
+ })
117+
```
118+
119+
To broadcast to clients from the same context, use `ctx.rpc.broadcast({ method, args, event })`.
120+
121+
## `nuxt.devtools.rpc` direct access is deprecated {#ndt_dep_0007}
122+
123+
Directly accessing `nuxt.devtools.rpc.broadcast` or `nuxt.devtools.rpc.functions`
124+
is deprecated (`NDT_DEP_0007`). They still work as a shim, but you should use the
125+
connected `ctx.rpc` (the devframe `RpcFunctionsHost`) from the
126+
[`onDevtoolsReady`](/module/utils-kit#ondevtoolsready) hook instead:
127+
128+
```diff
129+
- nuxt.devtools.rpc.broadcast.myEvent.asEvent(payload)
130+
+ onDevtoolsReady((ctx) => {
131+
+ ctx.rpc.broadcast({ method: 'myEvent', args: [payload], event: true })
132+
+ })
133+
134+
- nuxt.devtools.rpc.functions.myFn = handler
135+
+ onDevtoolsReady((ctx) => {
136+
+ ctx.rpc.register({ name: 'myFn', handler })
137+
+ })
138+
```
139+
140+
## `addCustomTab()` is deprecated {#ndt_dep_0005}
141+
142+
`addCustomTab()` is soft-deprecated in favour of registering a dock entry on the
143+
Vite DevTools docks host, from the
144+
[`onDevtoolsReady`](/module/utils-kit#ondevtoolsready) hook. It still works as a
145+
shim, but emits the `NDT_DEP_0005` deprecation diagnostic.
146+
147+
```diff
148+
- import { addCustomTab } from '@nuxt/devtools-kit'
149+
+ import { onDevtoolsReady } from '@nuxt/devtools-kit'
150+
151+
- addCustomTab({
152+
- name: 'my-module',
153+
- title: 'My Module',
154+
- icon: 'carbon:apps',
155+
- view: { type: 'iframe', src: '/url-to-your-module-view' },
156+
- })
157+
+ onDevtoolsReady((ctx) => {
158+
+ ctx.docks.register({
159+
+ id: 'my-module',
160+
+ title: 'My Module',
161+
+ icon: 'carbon:apps',
162+
+ type: 'iframe',
163+
+ url: '/url-to-your-module-view',
164+
+ })
165+
+ })
166+
```
167+
168+
::warning
169+
The docks host does not yet cover the Nuxt-specific custom-tab features such as
170+
`vnode` views or tab categories. If you rely on those, keep using
171+
`addCustomTab()` for now.
172+
::
173+
174+
## `refreshCustomTabs()` is deprecated {#ndt_dep_0006}
175+
176+
`refreshCustomTabs()` is soft-deprecated (`NDT_DEP_0006`). With the docks host you
177+
no longer re-run a hook to refresh — update the dock entry directly via the
178+
handle returned by `register()` inside the
179+
[`onDevtoolsReady`](/module/utils-kit#ondevtoolsready) hook:
180+
181+
```ts
182+
onDevtoolsReady((ctx) => {
183+
const entry = ctx.docks.register({ id: 'my-module', /* ... */ })
184+
entry.update({ title: 'My Module (updated)' })
185+
})
186+
```
187+
54188
## Global Install Support Removed
55189

56190
Nuxt DevTools no longer supports being installed globally.

‎packages/devtools-kit/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
},
4949
"dependencies": {
5050
"@nuxt/kit": "catalog:prod",
51+
"nostics": "catalog:prod",
5152
"tinyexec": "catalog:prod"
5253
},
5354
"devDependencies": {

‎packages/devtools-kit/src/_types/hooks.ts‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { ViteDevToolsNodeContext } from '@vitejs/devtools-kit'
12
import type { ModuleCustomTab } from './custom-tabs'
23
import type { NuxtDevtoolsInfo } from './server-ctx'
34
import type { TerminalState } from './terminals'
@@ -14,6 +15,17 @@ declare module '@nuxt/schema' {
1415
*/
1516
'devtools:initialized': (info: NuxtDevtoolsInfo) => void
1617

18+
/**
19+
* Called once the Vite DevTools kit has connected, with the connected
20+
* `ViteDevToolsNodeContext`.
21+
*
22+
* This is the recommended place to do all DevTools integration
23+
* (registering docks, terminals, messages, commands, RPC functions,
24+
* diagnostics, …): the kit is guaranteed to be available here, so you don't
25+
* need the connect-safe accessors on `nuxt.devtools`.
26+
*/
27+
'devtools:ready': (ctx: ViteDevToolsNodeContext) => void | Promise<void>
28+
1729
/**
1830
* Hooks to extend devtools tabs.
1931
*/

‎packages/devtools-kit/src/_types/options.ts‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,14 @@ export interface ModuleOptions {
4444
viteInspect?: boolean
4545

4646
/**
47-
* @deprecated Auth is now handled by Vite DevTools. This option is ignored.
47+
* Disable the DevTools client authorization prompt, allowing any browser to
48+
* connect without approving it first.
49+
*
50+
* Defaults to `true` in sandboxed environments (StackBlitz, CodeSandbox).
51+
*
52+
* Note: disabling authorization lets any browser (including other devices, if
53+
* you expose the dev server to your LAN/WAN) connect to DevTools and access
54+
* your server and filesystem. Only disable it in trusted environments.
4855
*/
4956
disableAuthorization?: boolean
5057

0 commit comments

Comments
 (0)