Skip to content

Commit 006b09f

Browse files
authored
fix(core): declare client module resolution before plugin setup (#582)
1 parent f23bfbc commit 006b09f

4 files changed

Lines changed: 97 additions & 1 deletion

File tree

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import type { Plugin, ResolvedConfig, ViteDevServer } from 'vite'
2+
import process from 'node:process'
3+
import { afterEach, describe, expect, it, vi } from 'vitest'
4+
import { createDevToolsContext } from '../context'
5+
import '@vitejs/devtools-kit'
6+
7+
function createConfig(plugins: Plugin[], command: 'serve' | 'build' = 'serve'): ResolvedConfig {
8+
return {
9+
root: process.cwd(),
10+
command,
11+
plugins,
12+
} as unknown as ResolvedConfig
13+
}
14+
15+
function createViteServer(): ViteDevServer {
16+
return {
17+
middlewares: {
18+
use: vi.fn(),
19+
},
20+
} as unknown as ViteDevServer
21+
}
22+
23+
/** A plugin whose dock names its client script by npm specifier, as the kit docs show. */
24+
function createBareSpecifierPlugin(): Plugin {
25+
return {
26+
name: 'test-bare-specifier-dock',
27+
devtools: {
28+
setup(ctx) {
29+
ctx.docks.register({
30+
id: 'test-bare-specifier',
31+
title: 'Test',
32+
icon: 'ph:bug-duotone',
33+
type: 'action',
34+
action: { importFrom: 'my-plugin/devtools-action' },
35+
})
36+
},
37+
},
38+
}
39+
}
40+
41+
function spyOnDF8111() {
42+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
43+
return () => warn.mock.calls.filter(args => args.some(arg => String(arg).includes('DF8111'))).length
44+
}
45+
46+
describe('createDevToolsContext client module resolution', () => {
47+
afterEach(() => {
48+
vi.restoreAllMocks()
49+
})
50+
51+
it('declares the Vite `/@id/` resolver before plugin setup registers bare-specifier docks', async () => {
52+
const countDF8111 = spyOnDF8111()
53+
54+
const ctx = await createDevToolsContext(
55+
createConfig([createBareSpecifierPlugin()]),
56+
createViteServer(),
57+
)
58+
59+
expect(countDF8111()).toBe(0)
60+
expect(ctx.staticConfig.dock?.clientModuleResolution).toBe('/@id/{specifier}')
61+
})
62+
63+
it('keeps warning DF8111 without a dev server (standalone / build), where bare specifiers stay unresolvable', async () => {
64+
const countDF8111 = spyOnDF8111()
65+
66+
const ctx = await createDevToolsContext(
67+
createConfig([createBareSpecifierPlugin()], 'build'),
68+
)
69+
70+
expect(ctx.staticConfig.dock?.clientModuleResolution).toBeUndefined()
71+
expect(countDF8111()).toBe(1)
72+
})
73+
})

‎packages/core/src/node/constants.ts‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,10 @@ export const MARK_CHECK: string = c.green('✔')
44
export const MARK_INFO: string = c.blue('ℹ')
55
export const MARK_ERROR: string = c.red('✖')
66
export const MARK_NODE: string = '⬢'
7+
8+
/**
9+
* Client-module resolution template declared when a live Vite dev server backs
10+
* the requests: bare-specifier dock client scripts load through Vite's own
11+
* `/@id/` resolution.
12+
*/
13+
export const DEVTOOLS_CLIENT_MODULE_RESOLUTION = '/@id/{specifier}'

‎packages/core/src/node/context.ts‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { createKitContext, createViteDevToolsHost } from '@vitejs/devtools-kit/n
66
import { createDebug } from 'obug'
77
import { DEVTOOLS_ASSETS_BASE, dirAssets } from '../dirs'
88
import { getAuthHandler, isClientAuthDisabled } from './auth-handler'
9+
import { DEVTOOLS_CLIENT_MODULE_RESOLUTION } from './constants'
910
import { diagnostics } from './diagnostics'
1011
import {
1112
defaultResolvedDevToolsConfig,
@@ -61,6 +62,18 @@ export async function createDevToolsContext(
6162
// setup() hooks can reference DTK codes via `ctx.diagnostics.logger`.
6263
context.diagnostics.register(diagnostics)
6364

65+
// Declare Vite's bare-specifier resolution before any dock registers. The hub
66+
// checks for it inside `docks.register()`, so a plugin `setup()` hook naming
67+
// an npm module in `importFrom` would otherwise warn DF8111 about a script
68+
// that loads fine once `initHub` (in `createDevToolsHub`) declares the same
69+
// template. Live dev server only — see `createDevToolsHub` for why.
70+
if (viteServer) {
71+
context.staticConfig.dock = {
72+
...context.staticConfig.dock,
73+
clientModuleResolution: DEVTOOLS_CLIENT_MODULE_RESOLUTION,
74+
}
75+
}
76+
6477
// The hub no longer synthesizes built-in docks — Vite DevTools, as the
6578
// high-level integration, registers the viewer's native views it wants. The
6679
// terminals + messages panels come from the official `@devframes/plugin-terminals`

‎packages/core/src/node/server.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { ViteDevToolsUiOptions } from './ui'
66
import { initHub } from '@devframes/hub/initiate'
77
import { DEVTOOLS_CONNECTION_META_FILENAME, DEVTOOLS_MOUNT_PATH } from '@vitejs/devtools-kit/constants'
88
import { getAuthHandler, getBuildCapabilityToken, isBuildCapabilityAuth, isClientAuthDisabled } from './auth-handler'
9+
import { DEVTOOLS_CLIENT_MODULE_RESOLUTION } from './constants'
910
import { resolveDockRendererRegistrations } from './renderers'
1011
import { getResolvedDevToolsConfig } from './resolved-config'
1112
import { createViteDevToolsUi } from './ui'
@@ -99,7 +100,9 @@ export async function createDevToolsHub(options: CreateDevToolsHubOptions): Prom
99100
// of Vite's transform pipeline. Standalone (CLI) and build snapshots have
100101
// no module graph to resolve against, so the template stays undeclared
101102
// there and such scripts must ship a self-contained bundle URL instead.
102-
...(context.viteServer ? { clientModuleResolution: '/@id/{specifier}' } : {}),
103+
// `createDevToolsContext` already declares it ahead of the plugin setup
104+
// hooks; repeating it here covers a context assembled elsewhere.
105+
...(context.viteServer ? { clientModuleResolution: DEVTOOLS_CLIENT_MODULE_RESOLUTION } : {}),
103106
auth: authDisabled ? false : getAuthHandler(context),
104107
...(allowedOrigins ? { allowedOrigins } : {}),
105108
...(mcp !== undefined ? { mcp } : {}),

0 commit comments

Comments
 (0)