Skip to content

Commit 5a6c4e8

Browse files
authored
feat(devtools): unify Data Inspector Vite instance and refresh builtin queries (#1047)
1 parent 8badc89 commit 5a6c4e8

2 files changed

Lines changed: 68 additions & 54 deletions

File tree

‎packages/devtools/src/integrations/data-inspector.ts‎

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
importtype{Nitro}from'nitropack'
22
importtype{Nuxt}from'nuxt/schema'
3-
importtype{ResolvedConfig}from'vite'
3+
importtype{ResolvedConfig,ViteDevServer}from'vite'
44
importtype{NuxtDevtoolsServerContext,NuxtServerData}from'../types'
55
import{createDataInspectorDevframe,registerDataSource}from'@devframes/plugin-data-inspector'
66
import{deprecate,NUXT_DEVTOOLS_GROUP_ID,onDevtoolsReady}from'@nuxt/devtools-kit'
@@ -19,8 +19,7 @@ import { mountDevframe } from '@vitejs/devtools-kit/node'
1919
*/
2020
interfaceCapturedServerData{
2121
nitro?: Nitro
22-
viteClient?: ResolvedConfig
23-
viteSsr?: ResolvedConfig
22+
vite?: ViteDevServer
2423
}
2524

2625
constcaptured: CapturedServerData={}
@@ -70,18 +69,14 @@ export function setup(ctx: NuxtDevtoolsServerContext): void {
7069
captured.nitro=nitro
7170
})
7271

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=configasunknownasResolvedConfig
83-
if(env.isServer)
84-
captured.viteSsr=configasunknownasResolvedConfig
72+
// Capture the live Vite dev server instance. Nuxt 5 unified the former client
73+
// and SSR Vite servers into a single dev server, so there is exactly one
74+
// instance to capture. The server carries far more than the resolved config
75+
// (which lives at `server.config`): its runtime `environments` (client/ssr),
76+
// module graph, plugin containers, watcher, and websocket. The Data Inspector
77+
// engine handles the deep/circular/function-valued branches.
78+
nuxt.hook('vite:serverCreated',(server)=>{
79+
captured.vite=serverasunknownasViteDevServer
8580
})
8681

8782
// Register the single live source early with a non-static factory, so Nuxt
@@ -97,25 +92,23 @@ export function setup(ctx: NuxtDevtoolsServerContext): void {
9792
data: ()=>({
9893
nuxt: nuxt.options,
9994
nitro: captured.nitro?.options,
100-
vite: {
101-
client: captured.viteClient,
102-
ssr: captured.viteSsr,
103-
},
95+
vite: captured.vite,
10496
}),
10597
queries: [
10698
{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},
99+
{title: 'Nuxt modules',query: 'nuxt.modules',excludeFunctions: true},
100+
{title: 'Vite plugins',query: 'vite.config.plugins',excludeFunctions: true},
101+
{title: 'Vite config',query: 'vite.config',excludeFunctions: true},
102+
{title: 'Vite environments',query: 'vite.environments',excludeFunctions: true},
103+
{title: 'Nitro routes',query: 'nitro.handlers',excludeFunctions: true},
110104
],
111105
})
112106

113107
// Avoid leaking a process-global source (e.g. across test fixtures).
114108
nuxt.hook('close',()=>{
115109
unregister()
116110
captured.nitro=undefined
117-
captured.viteClient=undefined
118-
captured.viteSsr=undefined
111+
captured.vite=undefined
119112
})
120113

121114
// Mount the Data Inspector's bundled SPA as a member of the Nuxt group. The
@@ -141,20 +134,26 @@ export function setup(ctx: NuxtDevtoolsServerContext): void {
141134
* use; it will be removed in a future major.
142135
*
143136
* Unlike the live source, this returns the legacy `NuxtServerData` shape
144-
* (`vite: { server, client }`) with the Vite configs normalized for RPC
145-
* transport.
137+
* (`vite: { server, client }`) with the resolved Vite config normalized for RPC
138+
* transport. Nuxt 5 unified the former client and SSR Vite servers into a
139+
* single dev server instance, but the legacy shape predates that; this shim
140+
* keeps the old contract alive independently by reading the one dev server's
141+
* resolved config (`server.config`) and projecting it onto both `server` and
142+
* `client` (each normalized separately, so consumers that mutate one field
143+
* don't affect the other).
146144
*/
147145
exportfunctiongetServerData(nuxt: Nuxt): NuxtServerData{
148146
deprecate(nuxt,'NDT_DEP_0009',{
149147
api: 'getServerData()',
150148
replacement: 'the Nuxt Application source in the Data Inspector panel',
151149
})
150+
constconfig=captured.vite?.config
152151
return{
153152
nuxt: nuxt.options,
154153
nitro: captured.nitro?.options,
155154
vite: {
156-
server: captured.viteSsr ? normalizeViteConfig(captured.viteSsr) : undefined,
157-
client: captured.viteClient ? normalizeViteConfig(captured.viteClient) : undefined,
155+
server: config ? normalizeViteConfig(config) : undefined,
156+
client: config ? normalizeViteConfig(config) : undefined,
158157
},
159158
// `nuxt.options` is `@nuxt/schema`'s `NuxtOptions` while `NuxtServerData`
160159
// pins the structurally-identical `nuxt/schema` one; bridge the two.
@@ -166,6 +165,5 @@ export function getServerData(nuxt: Nuxt): NuxtServerData {
166165
*/
167166
exportfunctionresetCapturedServerData(): void{
168167
captured.nitro=undefined
169-
captured.viteClient=undefined
170-
captured.viteSsr=undefined
168+
captured.vite=undefined
171169
}

‎packages/devtools/test/data-inspector.test.ts‎

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
importtype{Nuxt}from'nuxt/schema'
2-
importtype{ResolvedConfig}from'vite'
2+
importtype{ResolvedConfig,ViteDevServer}from'vite'
33
import{
44
getDataSource,
55
resetDataSources,
@@ -50,12 +50,23 @@ function fakeViteConfig(overrides: Partial<ResolvedConfig> = {}): ResolvedConfig
5050
}asunknownasResolvedConfig
5151
}
5252

53-
/** Drive Nuxt's `vite:configResolved` hook for one environment. */
54-
asyncfunctioncaptureViteEnv(fake: FakeNuxt,envName: 'client'|'ssr',config: ResolvedConfig){
55-
awaitfake.callHook('vite:configResolved',config,{
56-
isClient: envName==='client',
57-
isServer: envName==='ssr',
58-
})
53+
/**
54+
* Minimal Vite dev server stub. The resolved config lives at `.config`, and the
55+
* runtime environments at `.environments`, mirroring `ViteDevServer`.
56+
*/
57+
functionfakeViteServer(config: ResolvedConfig=fakeViteConfig()): ViteDevServer{
58+
return{
59+
config,
60+
environments: {client: {name: 'client'},ssr: {name: 'ssr'}},
61+
}asunknownasViteDevServer
62+
}
63+
64+
/**
65+
* Drive Nuxt's `vite:serverCreated` hook. Nuxt 5 unified the client and SSR
66+
* Vite servers into a single instance, so this fires once with one dev server.
67+
*/
68+
asyncfunctioncaptureVite(fake: FakeNuxt,server: ViteDevServer){
69+
awaitfake.callHook('vite:serverCreated',server)
5970
}
6071

6172
beforeEach(()=>{
@@ -77,16 +88,18 @@ describe('data-inspector source registration', () => {
7788
expect(typeofentry!.data).toBe('function')
7889
})
7990

80-
it('exposes the four read-only suggested queries with function exclusion',()=>{
91+
it('exposes the read-only suggested queries with function exclusion',()=>{
8192
const{ nuxt }=fakeNuxt()
8293
setup({ nuxt }asany)
8394

8495
constqueries=getDataSource('nuxt:application')!.queries!
8596
expect(queries).toEqual([
8697
{title: 'Overview',query: '',excludeFunctions: true},
87-
{title: 'Nuxt options',query: 'nuxt',excludeFunctions: true},
88-
{title: 'Nitro options',query: 'nitro',excludeFunctions: true},
89-
{title: 'Vite configs',query: 'vite',excludeFunctions: true},
98+
{title: 'Nuxt modules',query: 'nuxt.modules',excludeFunctions: true},
99+
{title: 'Vite plugins',query: 'vite.config.plugins',excludeFunctions: true},
100+
{title: 'Vite config',query: 'vite.config',excludeFunctions: true},
101+
{title: 'Vite environments',query: 'vite.environments',excludeFunctions: true},
102+
{title: 'Nitro routes',query: 'nitro.handlers',excludeFunctions: true},
90103
])
91104
})
92105

@@ -98,26 +111,25 @@ describe('data-inspector source registration', () => {
98111
constdata=awaitresolveSourceData(getDataSource('nuxt:application')!)asany
99112
expect(data.nuxt).toBe(options)
100113
expect(data.nitro).toBeUndefined()
101-
expect(data.vite).toEqual({client: undefined,ssr: undefined})
114+
expect(data.vite).toBeUndefined()
102115
})
103116

104-
it('populates Nitro and raw Vite configs after the hooks fire',async()=>{
117+
it('populates Nitro and the raw unified Vite dev server after the hooks fire',async()=>{
105118
constfake=fakeNuxt()
106119
setup({nuxt: fake.nuxt}asany)
107120

108121
constnitro={options: {preset: 'node'}}
109122
awaitfake.callHook('nitro:build:before',nitro)
110123

111-
constclient=fakeViteConfig({marker: 'client'}asany)
112-
constssr=fakeViteConfig({marker: 'ssr'}asany)
113-
awaitcaptureViteEnv(fake,'client',client)
114-
awaitcaptureViteEnv(fake,'ssr',ssr)
124+
constserver=fakeViteServer(fakeViteConfig({marker: 'vite'}asany))
125+
awaitcaptureVite(fake,server)
115126

116127
constdata=awaitresolveSourceData(getDataSource('nuxt:application')!)asany
117128
expect(data.nitro).toBe(nitro.options)
118-
// The live source hands the RAW config objects to the Data Inspector engine.
119-
expect(data.vite.client).toBe(client)
120-
expect(data.vite.ssr).toBe(ssr)
129+
// The live source hands the RAW dev server instance to the Data Inspector
130+
// engine; the resolved config is reachable at `vite.config`.
131+
expect(data.vite).toBe(server)
132+
expect(data.vite.config).toBe(server.config)
121133
})
122134

123135
it('unregisters the source on the Nuxt `close` hook',async()=>{
@@ -131,28 +143,32 @@ describe('data-inspector source registration', () => {
131143
})
132144

133145
describe('getServerData deprecated shim',()=>{
134-
it('returns the legacy `vite: { server, client }` shape with normalized configs',async()=>{
146+
it('projects the unified config onto the legacy `vite: { server, client }` shape, normalized',async()=>{
135147
constoptions={appId: 'app'}
136148
constfake=fakeNuxt(options)
137149
setup({nuxt: fake.nuxt}asany)
138150

139151
constnitro={options: {preset: 'node'}}
140152
awaitfake.callHook('nitro:build:before',nitro)
141-
awaitcaptureViteEnv(fake,'client',fakeViteConfig())
142-
awaitcaptureViteEnv(fake,'ssr',fakeViteConfig())
153+
awaitcaptureVite(fake,fakeViteServer())
143154

144155
constwarn=vi.spyOn(console,'warn').mockImplementation(()=>{})
145156
constdata=getServerData(fake.nuxt)
146157
warn.mockRestore()
147158

148159
expect(data.nuxt).toBe(options)
149160
expect(data.nitro).toBe(nitro.options)
150-
// Legacy field naming: `ssr` capture maps to `vite.server`.
161+
// Nuxt 5 has one Vite dev server; the shim reads its resolved config
162+
// (`server.config`) and projects it onto both fields.
151163
expect(data.vite).toHaveProperty('server')
152164
expect(data.vite).toHaveProperty('client')
165+
// Each field is normalized independently (separate object identities).
166+
expect(data.vite.server).not.toBe(data.vite.client)
153167
// Normalization strips the live Vite branches for RPC transport.
154168
expect(data.vite.server!.inlineConfig).toBeNull()
155169
expect((data.vite.server!.plugins[0]asany).api).toBeUndefined()
170+
expect(data.vite.client!.inlineConfig).toBeNull()
171+
expect((data.vite.client!.plugins[0]asany).api).toBeUndefined()
156172
})
157173

158174
it('emits the NDT_DEP_0009 deprecation once per process',()=>{

0 commit comments

Comments
 (0)