Skip to content

Commit 35defc3

Browse files
antfubotantfu
andauthored
feat(devtools)!: replace server discovery with the Data Inspector plugin (#1034)
Co-authored-by: Anthony Fu <github@antfu.me>
1 parent c081250 commit 35defc3

17 files changed

Lines changed: 644 additions & 948 deletions

File tree

‎docs/content/1.guide/1.features.md‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,26 @@ Tasks tab shows all nitro tasks in your project. You can pass task payloads, run
9292

9393
Learn more about [Nitro Tasks](https://nitro.build/guide/tasks)
9494

95+
## Data Inspector
96+
97+
The **Data Inspector** lives in the Nuxt group and is an interactive [jora](https://discoveryjs.github.io/jora/) query workbench over your live server-side configuration. It replaces the old read-only Nuxt Options Viewer.
98+
99+
Its `Nuxt Application` source exposes three root fields:
100+
101+
- `nuxt` — the resolved Nuxt options.
102+
- `nitro` — the resolved Nitro options (populated once Nitro is created).
103+
- `vite` — the resolved Vite `client` and `ssr` environment configs.
104+
105+
The source is live: every query reads the current state, so re-running a query after the Nitro/Vite hooks have completed reflects the latest values. Four read-only presets are provided — **Overview** (the whole object), **Nuxt options**, **Nitro options**, and **Vite configs** — each with function exclusion enabled so browsing configuration does not foreground methods. Your own custom queries are unrestricted and can invoke reachable functions or getters.
106+
107+
The panel's bundled UI includes an optional polling toggle (5s default, clamped 1–3600s) that pauses in background tabs.
108+
109+
Module authors who want to expose their own data can install [`@devframes/plugin-data-inspector`](https://www.npmjs.com/package/@devframes/plugin-data-inspector) and register sources through its `registerDataSource` API; they appear in the same inspector without any Nuxt-specific wrapper.
110+
111+
::warning
112+
The `getServerData` DevTools RPC that backed the old viewer is deprecated (`NDT_DEP_0009`). It still works as a compatibility shim but will be removed; use the Data Inspector panel instead.
113+
::
114+
95115
## VS Code Server
96116

97117
The VS Code Server integration in Nuxt DevTools enhances your development experience by bringing the power of Visual Studio Code directly into your browser. With this feature, you can seamlessly edit and debug your Nuxt projects using the familiar interface of VS Code.

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,14 @@ onDevtoolsReady((ctx) => {
212212
})
213213
```
214214

215+
## `getServerData()` RPC is deprecated {#ndt_dep_0009}
216+
217+
The read-only **Nuxt Options Viewer** page has been replaced by the [Data Inspector](/guide/features#data-inspector) panel in the `Nuxt` group, backed by the `@devframes/plugin-data-inspector` `Nuxt Application` source. Its live jora workbench supersedes the pre-serialized snapshot the old page rendered.
218+
219+
The `getServerData` server RPC that fed the old page is deprecated (`NDT_DEP_0009`). It still works as a compatibility shim — returning the legacy `NuxtServerData` shape (`{ nuxt, nitro, vite: { server, client } }`) with the Vite configs normalized for transport — but emits the diagnostic on first use and will be removed in a future major. Query the Data Inspector's `Nuxt Application` source instead of calling the RPC.
220+
221+
To expose your own inspectable data, install [`@devframes/plugin-data-inspector`](https://www.npmjs.com/package/@devframes/plugin-data-inspector) and register a source through its native `registerDataSource` API; there is no Nuxt-specific wrapper.
222+
215223
## Global Install Support Removed
216224

217225
Nuxt DevTools no longer supports being installed globally.

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ export interface ServerFunctions {
1313
// Static RPCs (can be provide on production build in the future)
1414
getServerConfig: () => NuxtOptions
1515
getServerDebugContext: () => Promise<ServerDebugContext | undefined>
16+
/**
17+
* @deprecated Replaced by the Data Inspector panel's live `Nuxt Application`
18+
* source. Kept as a compatibility shim (emits `NDT_DEP_0009`) for one
19+
* migration window and will be removed in a future major.
20+
*/
1621
getServerData: () => Promise<NuxtServerData>
1722
getServerRuntimeConfig: () => Record<string, any>
1823
getModuleOptions: () => ModuleOptions
@@ -95,6 +100,10 @@ export interface ClientFunctions {
95100
onTerminalExit: (_: { id: string, code?: number }) => void
96101
}
97102

103+
/**
104+
* @deprecated The payload of the deprecated {@link ServerFunctions.getServerData}
105+
* shim. Use the Data Inspector panel's live `Nuxt Application` source instead.
106+
*/
98107
export interface NuxtServerData {
99108
nuxt: NuxtOptions
100109
nitro?: Nitro['options']

‎packages/devtools-kit/src/diagnostics.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ export const diagnosticCodes = {
6969
why: (p: DeprecationParams) => `\`${p.api}\` is deprecated.`,
7070
fix: (p: DeprecationParams) => `Use \`${p.replacement}\` instead.`,
7171
},
72+
// NDT_DEP_0008 is reserved for the removed `vscode` module option.
73+
/** `getServerData()` RPC → the Data Inspector panel's `Nuxt Application` source. */
74+
NDT_DEP_0009: {
75+
why: (p: DeprecationParams) => `\`${p.api}\` is deprecated.`,
76+
fix: (p: DeprecationParams) => `Use \`${p.replacement}\` instead.`,
77+
},
7278
} satisfies Record<string, DiagnosticDefinition<DeprecationParams>>
7379

7480
export type NuxtDiagnosticCode = keyof typeof diagnosticCodes

‎packages/devtools-kit/test/diagnostics.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ describe('diagnosticCodes', () => {
2929
'NDT_DEP_0005',
3030
'NDT_DEP_0006',
3131
'NDT_DEP_0007',
32+
'NDT_DEP_0009',
3233
])
3334
})
3435
})

‎packages/devtools/.discoveryrc.cjs‎

Lines changed: 0 additions & 13 deletions
This file was deleted.

‎packages/devtools/client/composables/utils.ts‎

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { AsyncDataOptions } from '#app'
44
import type { ComponentRelationship, ComponentWithRelationships, NormalizedHeadTag, SocialPreviewCard, SocialPreviewResolved } from '~/../src/types'
55
import { useSessionStorage } from '@vueuse/core'
66
import { relative } from 'pathe'
7-
import { isRef, triggerRef } from 'vue'
7+
import { triggerRef } from 'vue'
88
import { useAsyncData } from '#app/composables/asyncData'
99
import { useNuxtApp } from '#app/nuxt'
1010
import { useState } from '#imports'
@@ -184,27 +184,6 @@ export function reloadPage() {
184184
location.reload()
185185
}
186186

187-
export function jsonStringifyCircular(params: any) {
188-
const seen: any[] = []
189-
const result = JSON.stringify(params, (key, value) => {
190-
if (typeof value === 'function')
191-
return value.toString()
192-
if (isRef(value))
193-
value = value.value
194-
if (typeof value === 'object' && value !== null) {
195-
if (key === 'devServer') // TODO: do this better
196-
return undefined
197-
const index = seen.indexOf(value)
198-
if (index >= 0)
199-
// return structuredClone(seen[index])
200-
return `<Circular #${index}>`
201-
seen.push(value)
202-
}
203-
return value
204-
})
205-
return result
206-
}
207-
208187
export function useNuxtCompatibilityVersion() {
209188
const config = useServerConfig()
210189
return config.value?.future.compatibilityVersion

‎packages/devtools/client/pages/modules/server-discovery.vue‎

Lines changed: 0 additions & 41 deletions
This file was deleted.

‎packages/devtools/package.json‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,11 @@
3232
"dist"
3333
],
3434
"scripts": {
35-
"build": "pnpm dev:prepare && pnpm build:module && pnpm build:discovery && pnpm build:client",
35+
"build": "pnpm dev:prepare && pnpm build:module && pnpm build:client",
3636
"build:client": "nuxi generate client && node scripts/copy-client.mjs",
3737
"build:module": "nuxt-build-module build",
38-
"build:discovery": "discovery-build -c .discoveryrc.cjs -s -o client/public/discovery",
39-
"dev:discovery": "discovery -c .discoveryrc.cjs",
4038
"stub": "nuxt-build-module build --stub",
41-
"dev": "pnpm build:discovery && nuxi dev client",
39+
"dev": "nuxi dev client",
4240
"dev:playground": "pnpm build && nuxi dev playground",
4341
"dev:prepare": "pnpm run stub && nuxi prepare client",
4442
"prepare": "tsx scripts/prepare.ts",
@@ -48,6 +46,7 @@
4846
"vite": "^8.0.14"
4947
},
5048
"dependencies": {
49+
"@devframes/plugin-data-inspector": "catalog:prod",
5150
"@nuxt/devtools-kit": "workspace:*",
5251
"@nuxt/kit": "catalog:prod",
5352
"@vitejs/devtools": "catalog:buildtools",
@@ -83,8 +82,6 @@
8382
},
8483
"devDependencies": {
8584
"@antfu/utils": "catalog:frontend",
86-
"@discoveryjs/cli": "catalog:buildtools",
87-
"@discoveryjs/discovery": "catalog:buildtools",
8885
"@iconify-json/bxl": "catalog:icons",
8986
"@iconify-json/carbon": "catalog:icons",
9087
"@iconify-json/logos": "catalog:icons",
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import type { Nitro } from 'nitropack'
2+
import type { Nuxt } from 'nuxt/schema'
3+
import type { ResolvedConfig } from 'vite'
4+
import type { NuxtDevtoolsServerContext, NuxtServerData } from '../types'
5+
import { createDataInspectorDevframe, registerDataSource } from '@devframes/plugin-data-inspector'
6+
import { deprecate, NUXT_DEVTOOLS_GROUP_ID, onDevtoolsReady } from '@nuxt/devtools-kit'
7+
import { mountDevframe } from '@vitejs/devtools-kit/node'
8+
9+
/**
10+
* Live capture of the Nuxt server-side configuration surfaced by the Data
11+
* Inspector's `nuxt:application` source (and by the deprecated `getServerData`
12+
* RPC shim below).
13+
*
14+
* This state is module-scoped on purpose: Devframe's data-source registry is
15+
* process-global and not host-scoped, so this integration supports exactly one
16+
* active Nuxt DevTools instance per Node process, keyed by the fixed
17+
* `nuxt:application` source id. Running two live Nuxt DevTools hosts in one
18+
* process is unsupported.
19+
*/
20+
interface CapturedServerData {
21+
nitro?: Nitro
22+
viteClient?: ResolvedConfig
23+
viteSsr?: ResolvedConfig
24+
}
25+
26+
const captured: CapturedServerData = {}
27+
28+
/**
29+
* Strip the live Vite instance and other non-serializable branches so the
30+
* config survives the RPC transport used by the deprecated `getServerData`
31+
* shim. Preserved verbatim from the removed `server-rpc/server-data.ts` to keep
32+
* the shim's payload byte-for-byte compatible for its migration window.
33+
*
34+
* The live Data Inspector source does **not** use this — it hands the raw
35+
* objects to the Data Inspector engine, which owns normalization, circular
36+
* references, and depth limits.
37+
*/
38+
function normalizeViteConfig(config: ResolvedConfig): ResolvedConfig {
39+
return {
40+
...config,
41+
environments: Object.fromEntries(
42+
Object.entries(config.env ?? {}).map(([key, _]) => {
43+
return [key, null]
44+
}),
45+
),
46+
plugins: (config.plugins ?? []).map((i) => {
47+
const clone = { ...i }
48+
delete clone.api
49+
return clone
50+
}),
51+
inlineConfig: null,
52+
} as any as ResolvedConfig
53+
}
54+
55+
/**
56+
* Register the live `Nuxt Application` Data Inspector source, capture the raw
57+
* Nitro/Vite configuration as the Nuxt hooks fire, and mount the Data Inspector
58+
* Devframe into the Nuxt dock group.
59+
*
60+
* Always mounted when Nuxt DevTools is enabled; there is no module option.
61+
* Ecosystem modules that want more sources install
62+
* `@devframes/plugin-data-inspector` themselves and use its native
63+
* `registerDataSource`.
64+
*/
65+
export function setup(ctx: NuxtDevtoolsServerContext): void {
66+
const { nuxt } = ctx
67+
68+
// Capture raw Nitro options once Nitro is created.
69+
nuxt.hook('nitro:build:before', (nitro) => {
70+
captured.nitro = nitro
71+
})
72+
73+
// Capture the raw resolved Vite config for each environment. Nuxt fires
74+
// `vite:configResolved` once with `isClient` and once with `isServer`, for
75+
// both serial Vite servers and Environment API projections, so the source
76+
// contract stays exactly `vite: { client, ssr }`. Nuxt marks this hook
77+
// deprecated, but it is the only current host API that reports both configs
78+
// semantically; the returned-environment-plugin path is unreliable in Vite 8
79+
// (Vite resolves those plugins after top-level `configResolved`).
80+
nuxt.hook('vite:configResolved', (config, env) => {
81+
if (env.isClient)
82+
captured.viteClient = config as unknown as ResolvedConfig
83+
if (env.isServer)
84+
captured.viteSsr = config as unknown as ResolvedConfig
85+
})
86+
87+
// Register the single live source early with a non-static factory, so Nuxt
88+
// options are immediately queryable and the Nitro/Vite fields populate as the
89+
// hooks above run. The registry is process-global and shared with the mounted
90+
// definition, so no context threading is required.
91+
const unregister = registerDataSource({
92+
id: 'nuxt:application',
93+
title: 'Nuxt Application',
94+
description: 'Live Nuxt, Nitro, and Vite configuration',
95+
icon: 'i-ph:database-duotone',
96+
static: false,
97+
data: () => ({
98+
nuxt: nuxt.options,
99+
nitro: captured.nitro?.options,
100+
vite: {
101+
client: captured.viteClient,
102+
ssr: captured.viteSsr,
103+
},
104+
}),
105+
queries: [
106+
{ title: 'Overview', query: '', excludeFunctions: true },
107+
{ title: 'Nuxt options', query: 'nuxt', excludeFunctions: true },
108+
{ title: 'Nitro options', query: 'nitro', excludeFunctions: true },
109+
{ title: 'Vite configs', query: 'vite', excludeFunctions: true },
110+
],
111+
})
112+
113+
// Avoid leaking a process-global source (e.g. across test fixtures).
114+
nuxt.hook('close', () => {
115+
unregister()
116+
captured.nitro = undefined
117+
captured.viteClient = undefined
118+
captured.viteSsr = undefined
119+
})
120+
121+
// Mount the Data Inspector's bundled SPA as a member of the Nuxt group. The
122+
// definition defaults to the `~builtin` category, so the category must be
123+
// overridden per-mount: group members do not inherit their group's category.
124+
const definition = createDataInspectorDevframe({ exampleSource: false })
125+
onDevtoolsReady((kit) => {
126+
return mountDevframe(kit, definition, {
127+
dock: {
128+
groupId: NUXT_DEVTOOLS_GROUP_ID,
129+
category: 'framework',
130+
defaultOrder: -100,
131+
},
132+
})
133+
}, nuxt)
134+
}
135+
136+
/**
137+
* @deprecated The read-only Nuxt Options Viewer has been replaced by the Data
138+
* Inspector panel's live `Nuxt Application` source. This RPC is kept as a
139+
* compatibility shim for one migration window and emits `NDT_DEP_0009` on first
140+
* use; it will be removed in a future major.
141+
*
142+
* Unlike the live source, this returns the legacy `NuxtServerData` shape
143+
* (`vite: { server, client }`) with the Vite configs normalized for RPC
144+
* transport.
145+
*/
146+
export function getServerData(nuxt: Nuxt): NuxtServerData {
147+
deprecate(nuxt, 'NDT_DEP_0009', {
148+
api: 'getServerData()',
149+
replacement: 'the Nuxt Application source in the Data Inspector panel',
150+
})
151+
return {
152+
nuxt: nuxt.options,
153+
nitro: captured.nitro?.options,
154+
vite: {
155+
server: captured.viteSsr ? normalizeViteConfig(captured.viteSsr) : undefined,
156+
client: captured.viteClient ? normalizeViteConfig(captured.viteClient) : undefined,
157+
},
158+
// `nuxt.options` is `@nuxt/schema`'s `NuxtOptions` while `NuxtServerData`
159+
// pins the structurally-identical `nuxt/schema` one; bridge the two.
160+
} as NuxtServerData
161+
}
162+
163+
/**
164+
* @internal
165+
*/
166+
export function resetCapturedServerData(): void {
167+
captured.nitro = undefined
168+
captured.viteClient = undefined
169+
captured.viteSsr = undefined
170+
}

0 commit comments

Comments
 (0)