Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ packages/kit/skills
*.tsbuildinfo
docs/.vitepress/cache
.turbo
.context
1 change: 1 addition & 0 deletions alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const alias = {
'@vitejs/devtools-kit/utils/shared-state': r('kit/src/utils/shared-state.ts'),
'@vitejs/devtools-kit': r('kit/src/index.ts'),
'@vitejs/devtools-rolldown': r('rolldown/src/index.ts'),
'@vitejs/devtools-self-inspect': r('self-inspect/src/index.ts'),
'@vitejs/devtools/client/inject': r('core/src/client/inject/index.ts'),
'@vitejs/devtools/client/webcomponents': r('core/src/client/webcomponents/index.ts'),
'@vitejs/devtools': r('core/src/index.ts'),
Expand Down
22 changes: 22 additions & 0 deletions docs/kit/devtools-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,28 @@ export default function myAnalyzerPlugin(): Plugin {
}
```

## Debugging with Self Inspect

When developing or debugging a DevTools plugin, you can install `@vitejs/devtools-self-inspect` to get a live view of all registered RPC functions, dock entries, client scripts, and DevTools-enabled plugins:

```bash
pnpm add -D @vitejs/devtools-self-inspect
```

```ts [vite.config.ts]
import { DevToolsSelfInspect } from '@vitejs/devtools-self-inspect'

export default defineConfig({
plugins: [
DevTools(),
DevToolsSelfInspect(),
// ...your plugins
],
})
```

This adds a "Self Inspect" panel to DevTools that shows the internal state of the DevTools system — helpful for verifying that your plugin's RPC functions, docks, and client scripts are registered correctly.

## Next Steps

- **[Dock System](./dock-system)** - Learn about different dock entry types (iframe, action, custom renderer)
Expand Down
6 changes: 4 additions & 2 deletions packages/core/playground/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import VueRouter from 'unplugin-vue-router/vite'
import { defineConfig } from 'vite'
import Tracer from 'vite-plugin-vue-tracer'
import { alias } from '../../../alias'
import { DevTools } from '../../core/src'
import { buildCSS } from '../../core/src/client/webcomponents/scripts/build-css'
// eslint-disable-next-line ts/ban-ts-comment
// @ts-ignore ignore the type error
import { DevToolsRolldownUI } from '../../rolldown/src/node'
import { DevTools } from '../src'
import { buildCSS } from '../src/client/webcomponents/scripts/build-css'
import { DevToolsSelfInspect } from '../../self-inspect/src/node'

declare module '@vitejs/devtools-kit' {
interface DevToolsRpcSharedStates {
Expand All @@ -29,6 +30,7 @@ export default defineConfig({
plugins: [
VueRouter(),
Vue(),
DevToolsSelfInspect(),
{
name: 'build-css',
handleHotUpdate({ file }) {
Expand Down
36 changes: 36 additions & 0 deletions packages/self-inspect/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# @vitejs/devtools-self-inspect

A Vite DevTools plugin for inspecting the DevTools itself. Useful when developing or debugging DevTools plugins built with `@vitejs/devtools-kit`.

## Features

- List all registered RPC functions with their metadata (type, schema, cacheability, etc.)
- List all registered dock entries
- List all registered client scripts
- List all Vite plugins with DevTools support and their capabilities

## Installation

```bash
pnpm add -D @vitejs/devtools-self-inspect
```

## Usage

Add the plugin to your Vite config:

```ts
import DevTools from '@vitejs/devtools'
import { DevToolsSelfInspect } from '@vitejs/devtools-self-inspect'
// vite.config.ts
import { defineConfig } from 'vite'

export default defineConfig({
plugins: [
DevTools(),
DevToolsSelfInspect(),
],
})
```

A "Self Inspect" panel will appear in the DevTools dock, giving you a live view of the registered RPC functions, docks, client scripts, and DevTools-enabled plugins.
49 changes: 49 additions & 0 deletions packages/self-inspect/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"name": "@vitejs/devtools-self-inspect",
"type": "module",
"version": "0.0.0-alpha.34",
"description": "DevTools for inspecting the DevTools itself",
"author": "VoidZero Inc.",
"license": "MIT",
"homepage": "https://github.com/vitejs/devtools#readme",
"repository": {
"directory": "packages/self-inspect",
"type": "git",
"url": "git+https://github.com/vitejs/devtools.git"
},
"bugs": "https://github.com/vitejs/devtools/issues",
"keywords": [
"devtools",
"self-inspect"
],
"sideEffects": false,
"exports": {
".": "./dist/index.mjs",
"./package.json": "./package.json"
},
"types": "./dist/index.d.mts",
"files": [
"dist"
],
"scripts": {
"build": "pnpm dev:prepare && nuxi build src && tsdown",
"dev": "nuxi dev src",
"dev:prepare": "nuxi prepare src",
"prepack": "pnpm build"
},
"dependencies": {
"@vitejs/devtools-kit": "workspace:*",
"@vitejs/devtools-rpc": "workspace:*",
"birpc": "catalog:deps",
"pathe": "catalog:deps"
},
"devDependencies": {
"@unocss/nuxt": "catalog:build",
"@vueuse/core": "catalog:frontend",
"@vueuse/nuxt": "catalog:build",
"structured-clone-es": "catalog:deps",
"tsdown": "catalog:build",
"unocss": "catalog:build",
"vite-hot-client": "catalog:frontend"
}
}
73 changes: 73 additions & 0 deletions packages/self-inspect/src/app/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<script setup lang="ts">
import { useHead } from '#app/composables/head'
import { toggleDark } from '@vitejs/devtools-ui/composables/dark'
import { useRefresh } from './composables/refresh'
import { connect, connectionState } from './composables/rpc'
import './styles/global.css'

useHead({
title: 'DevTools Self Inspect',
})

connect()

const navItems = [
{ title: 'RPC Functions', to: '/rpc', icon: 'i-ph-plugs-connected-duotone' },
{ title: 'Docks', to: '/docks', icon: 'i-ph-layout-duotone' },
{ title: 'Client Scripts', to: '/scripts', icon: 'i-ph-code-duotone' },
{ title: 'Plugins', to: '/plugins', icon: 'i-ph-puzzle-piece-duotone' },
]

const { refresh, loading } = useRefresh()
</script>

<template>
<div v-if="connectionState.error" text-red p4>
{{ connectionState.error }}
</div>
<VisualLoading
v-else-if="!connectionState.connected"
text="Connecting..."
/>
<div v-else h-vh flex="~ col" of-hidden>
<div flex="~ items-center" border="b base" h9 shrink-0>
<NuxtLink
v-for="item in navItems" :key="item.to"
:to="item.to"
flex="~ items-center gap-1.5"
px3 h-full text-sm
op50 hover:op100 transition-colors
border="b-2 transparent"
active-class="op100! border-b-primary!"
>
<span :class="item.icon" text-base />
<span>{{ item.title }}</span>
</NuxtLink>
<div flex-1 />
<button
mr2 p1.5 rounded
hover:bg-active
op50 hover:op100
transition-colors
title="Refresh"
:disabled="loading"
@click="refresh"
>
<span i-ph-arrow-clockwise text-sm :class="loading ? 'animate-spin' : ''" />
</button>
<button
mr2 p1.5 rounded
hover:bg-active
op50 hover:op100
transition-colors
title="Toggle dark mode"
@click="toggleDark"
>
<span class="i-ph-sun-duotone dark:i-ph-moon-duotone" text-sm />
</button>
</div>
<div flex-1 of-auto>
<NuxtPage />
</div>
</div>
</template>
78 changes: 78 additions & 0 deletions packages/self-inspect/src/app/components/ClientScriptsList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<script setup lang="ts">
import type { ClientScriptInfo } from '../../types'
import { computed } from 'vue'

const props = defineProps<{
scripts: ClientScriptInfo[]
}>()

const grouped = computed(() => {
const groups = new Map<string, ClientScriptInfo[]>()
for (const script of props.scripts) {
const type = script.dockType
if (!groups.has(type))
groups.set(type, [])
groups.get(type)!.push(script)
}
return Array.from(groups.entries()).sort(([a], [b]) => a.localeCompare(b))
})

function shortPath(path: string): string {
const parts = path.split('/')
if (parts.length <= 3)
return path
return `.../${parts.slice(-3).join('/')}`
}
</script>

<template>
<div v-if="scripts.length === 0" flex="~ items-center justify-center" py8 op50>
No client scripts registered.
</div>
<div v-else flex="~ col gap-3" p4>
<div text-xs op60>
{{ scripts.length }} client scripts registered
</div>

<div v-for="[type, typeScripts] in grouped" :key="type">
<div flex="~ items-center gap-2" mb1 mt2>
<DisplayBadge :text="type" />
<DisplayNumberBadge :value="typeScripts.length" />
</div>
<table w-full text-sm>
<thead>
<tr border="b base" text-left>
<th px2 py1 font-medium op60 text-xs>
Dock ID
</th>
<th px2 py1 font-medium op60 text-xs>
Dock Title
</th>
<th px2 py1 font-medium op60 text-xs>
Import From
</th>
<th px2 py1 font-medium op60 text-xs>
Import Name
</th>
</tr>
</thead>
<tbody>
<tr v-for="script in typeScripts" :key="script.dockId" border="b base" hover:bg-active>
<td px2 py1.5 font-mono text-xs>
{{ script.dockId }}
</td>
<td px2 py1.5>
{{ script.dockTitle }}
</td>
<td px2 py1.5 font-mono text-xs max-w-60 truncate :title="script.script.importFrom">
{{ shortPath(script.script.importFrom) }}
</td>
<td px2 py1.5 font-mono text-xs>
{{ script.script.importName ?? 'default' }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
77 changes: 77 additions & 0 deletions packages/self-inspect/src/app/components/DevtoolsPluginsList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<script setup lang="ts">
import type { DevtoolsPluginInfo } from '../../types'
import { computed, ref } from 'vue'

const props = defineProps<{
plugins: DevtoolsPluginInfo[]
}>()

const showAll = ref(false)

const devtoolsCount = computed(() => props.plugins.filter(p => p.hasDevtools).length)

const filtered = computed(() => {
if (showAll.value)
return props.plugins
return props.plugins.filter(p => p.hasDevtools)
})
</script>

<template>
<div flex="~ col gap-3" p4>
<div flex="~ items-center gap-3" text-xs>
<span op60>
{{ devtoolsCount }} devtools plugins / {{ plugins.length }} total
</span>
<label flex="~ items-center gap-1.5" op60 cursor-pointer select-none>
<input v-model="showAll" type="checkbox">
Show all
</label>
</div>

<table w-full text-sm>
<thead>
<tr border="b base" text-left>
<th px2 py1 font-medium op60 text-xs>
Plugin Name
</th>
<th px2 py1 font-medium op60 text-xs text-center>
DevTools
</th>
<th px2 py1 font-medium op60 text-xs text-center>
Setup
</th>
<th px2 py1 font-medium op60 text-xs>
Capabilities
</th>
</tr>
</thead>
<tbody>
<tr
v-for="plugin in filtered" :key="plugin.name"
border="b base" hover:bg-active
:class="!plugin.hasDevtools ? 'op40' : ''"
>
<td px2 py1.5 font-mono text-xs>
{{ plugin.name }}
</td>
<td px2 py1.5 text-center>
<span v-if="plugin.hasDevtools" i-ph-check text-green />
<span v-else op20>-</span>
</td>
<td px2 py1.5 text-center>
<span v-if="plugin.hasSetup" i-ph-check text-green />
<span v-else op20>-</span>
</td>
<td px2 py1.5 flex="~ items-center gap-1">
<template v-if="plugin.capabilities">
<DisplayBadge v-if="plugin.capabilities.dev" text="dev" />
<DisplayBadge v-if="plugin.capabilities.build" text="build" />
</template>
<span v-else op20>-</span>
</td>
</tr>
</tbody>
</table>
</div>
</template>
Loading
Loading