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
import type { Nitro } from 'nitropack'
22
import type { Nuxt } from 'nuxt/schema'
3-
import type { ResolvedConfig } from 'vite'
3+
import type { ResolvedConfig, ViteDevServer } from 'vite'
44
import type { 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
interface CapturedServerData {
2121
nitro?: Nitro
22-
viteClient?: ResolvedConfig
23-
viteSsr?: ResolvedConfig
22+
vite?: ViteDevServer
2423
}
2524

2625
const captured: 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 = config as unknown as ResolvedConfig
83-
if (env.isServer)
84-
captured.viteSsr = config as unknown as ResolvedConfig
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 = server as unknown as ViteDevServer
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
export function getServerData(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+
const config = 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
export function resetCapturedServerData(): 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
import type { Nuxt } from 'nuxt/schema'
2-
import type { ResolvedConfig } from 'vite'
2+
import type { ResolvedConfig, ViteDevServer } from 'vite'
33
import {
44
getDataSource,
55
resetDataSources,
@@ -50,12 +50,23 @@ function fakeViteConfig(overrides: Partial<ResolvedConfig> = {}): ResolvedConfig
5050
} as unknown as ResolvedConfig
5151
}
5252

53-
/** Drive Nuxt's `vite:configResolved` hook for one environment. */
54-
async function captureViteEnv(fake: FakeNuxt, envName: 'client' | 'ssr', config: ResolvedConfig) {
55-
await fake.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+
function fakeViteServer(config: ResolvedConfig = fakeViteConfig()): ViteDevServer {
58+
return {
59+
config,
60+
environments: { client: { name: 'client' }, ssr: { name: 'ssr' } },
61+
} as unknown as ViteDevServer
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+
async function captureVite(fake: FakeNuxt, server: ViteDevServer) {
69+
await fake.callHook('vite:serverCreated', server)
5970
}
6071

6172
beforeEach(() => {
@@ -77,16 +88,18 @@ describe('data-inspector source registration', () => {
7788
expect(typeof entry!.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 } as any)
8394

8495
const queries = 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
const data = await resolveSourceData(getDataSource('nuxt:application')!) as any
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
const fake = fakeNuxt()
106119
setup({ nuxt: fake.nuxt } as any)
107120

108121
const nitro = { options: { preset: 'node' } }
109122
await fake.callHook('nitro:build:before', nitro)
110123

111-
const client = fakeViteConfig({ marker: 'client' } as any)
112-
const ssr = fakeViteConfig({ marker: 'ssr' } as any)
113-
await captureViteEnv(fake, 'client', client)
114-
await captureViteEnv(fake, 'ssr', ssr)
124+
const server = fakeViteServer(fakeViteConfig({ marker: 'vite' } as any))
125+
await captureVite(fake, server)
115126

116127
const data = await resolveSourceData(getDataSource('nuxt:application')!) as any
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
const options = { appId: 'app' }
136148
const fake = fakeNuxt(options)
137149
setup({ nuxt: fake.nuxt } as any)
138150

139151
const nitro = { options: { preset: 'node' } }
140152
await fake.callHook('nitro:build:before', nitro)
141-
await captureViteEnv(fake, 'client', fakeViteConfig())
142-
await captureViteEnv(fake, 'ssr', fakeViteConfig())
153+
await captureVite(fake, fakeViteServer())
143154

144155
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
145156
const data = 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] as any).api).toBeUndefined()
170+
expect(data.vite.client!.inlineConfig).toBeNull()
171+
expect((data.vite.client!.plugins[0] as any).api).toBeUndefined()
156172
})
157173

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

0 commit comments

Comments
 (0)