import { computed, ref, watchEffect } from 'vue'
import { useEventListener } from '@vueuse/core'
-
-import { useFrameState, waitBridgeReady } from '~/composables'
+import { onRpcSeverReady, rpcServer } from '@vue/devtools-core'
+import { useFrameState } from '~/composables'
import { PANEL_MAX, PANEL_MIN } from '~/constants'
const props = defineProps<{
@@ -24,14 +24,16 @@ const { state, updateState } = useFrameState()
const container = ref()
const isResizing = ref(false)
-waitBridgeReady().then((bridge) => {
- // @TODO: add type
- bridge.on('update-client-state', (v) => {
- updateState({
- minimizePanelInactive: v.minimizePanelInteractive,
- closeOnOutsideClick: v.closeOnOutsideClick,
- preferShowFloatingPanel: v.showFloatingPanel,
- })
+onRpcSeverReady(() => {
+ // @ts-expect-error skip
+ rpcServer.functions.on('update-client-state', (v) => {
+ if (v) {
+ updateState({
+ minimizePanelInactive: v.minimizePanelInteractive,
+ closeOnOutsideClick: v.closeOnOutsideClick,
+ preferShowFloatingPanel: v.showFloatingPanel,
+ })
+ }
})
})
diff --git a/packages/overlay/src/composables/bridge.ts b/packages/overlay/src/composables/bridge.ts
deleted file mode 100644
index 92191dc3..00000000
--- a/packages/overlay/src/composables/bridge.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import type { Bridge } from '@vue/devtools-core'
-import { shallowRef, watchEffect } from 'vue'
-
-type BridgeType = InstanceType
-
-const bridgeRef = shallowRef(null)
-
-export function registerBridge(bridge: InstanceType) {
- bridgeRef.value = bridge
-}
-
-export function waitBridgeReady() {
- return new Promise((resolve) => {
- if (bridgeRef.value)
- resolve(bridgeRef.value)
- watchEffect(() => {
- if (bridgeRef.value)
- resolve(bridgeRef.value)
- })
- })
-}
diff --git a/packages/overlay/src/composables/index.ts b/packages/overlay/src/composables/index.ts
index 2876232a..fca38411 100644
--- a/packages/overlay/src/composables/index.ts
+++ b/packages/overlay/src/composables/index.ts
@@ -2,4 +2,3 @@ export * from './position'
export * from './iframe'
export * from './panel'
export * from './state'
-export * from './bridge'
diff --git a/packages/playground/applet/src/App.vue b/packages/playground/applet/src/App.vue
index 8b2f8270..92318263 100644
--- a/packages/playground/applet/src/App.vue
+++ b/packages/playground/applet/src/App.vue
@@ -1,7 +1,8 @@
diff --git a/packages/playground/basic/src/main.ts b/packages/playground/basic/src/main.ts
index 424f268f..6273fda2 100644
--- a/packages/playground/basic/src/main.ts
+++ b/packages/playground/basic/src/main.ts
@@ -2,6 +2,9 @@ import { createPinia } from 'pinia'
import type { RouteRecordRaw } from 'vue-router'
import { createRouter, createWebHistory } from 'vue-router'
import { VueQueryPlugin } from '@tanstack/vue-query'
+import { addCustomCommand } from '@vue/devtools-api'
+import { devtools } from '@vue/devtools'
+
import store from './stores/vuexStore'
import App from './App.vue'
@@ -17,7 +20,7 @@ const pinia = createPinia()
const app = createApp(App)
-// devtools.connect()
+devtools.connect()
const routes: RouteRecordRaw[] = [
{
@@ -65,16 +68,16 @@ app.use(store)
app.mount('#app')
// }, 2000)
-// setTimeout(() => {
-// addCustomCommand({
-// id: 'vueuse',
-// title: 'VueUse',
-// action: {
-// type: 'url',
-// src: 'https://vueuse.org/',
-// },
-// })
-// }, 2000)
+setTimeout(() => {
+ addCustomCommand({
+ id: 'vueuse',
+ title: 'VueUse',
+ action: {
+ type: 'url',
+ src: 'https://vueuse.org/',
+ },
+ })
+}, 2000)
// setTimeout(() => {
// addCustomTab({
@@ -91,4 +94,20 @@ app.mount('#app')
// },
// category: 'advanced',
// })
+// setTimeout(() => {
+// addCustomTab({
+// // unique identifier
+// name: 'vue-use1',
+// // title to display in the tab
+// title: 'VueUse1',
+// // any icon from Iconify, or a URL to an image
+// icon: 'i-logos-vueuse',
+// // iframe view
+// view: {
+// type: 'iframe',
+// src: 'https://vueuse.org/',
+// },
+// category: 'advanced',
+// })
+// }, 2000)
// }, 2000)
diff --git a/packages/playground/basic/vite.config.ts b/packages/playground/basic/vite.config.ts
index 9e833e3b..c4a575c0 100644
--- a/packages/playground/basic/vite.config.ts
+++ b/packages/playground/basic/vite.config.ts
@@ -12,7 +12,7 @@ export default defineConfig({
vue(),
commonjs(),
VueDevTools({
- launchEditor: 'code',
+ // launchEditor: 'code',
}),
Unocss(),
AutoImport({
diff --git a/packages/shared/src/env.ts b/packages/shared/src/env.ts
index 62dae47d..d79af817 100644
--- a/packages/shared/src/env.ts
+++ b/packages/shared/src/env.ts
@@ -14,3 +14,4 @@ export const isInIframe = isBrowser && target.self !== target.top
export const isInElectron = typeof navigator !== 'undefined' && navigator.userAgent.toLowerCase().includes('electron')
// @ts-expect-error skip type check
export const isNuxtApp = typeof window !== 'undefined' && !!window.__NUXT__
+export const isInSeparateWindow = !isInIframe && !isInChromePanel && !isInElectron
diff --git a/packages/vite/src/modules/get-config.ts b/packages/vite/src/modules/get-config.ts
deleted file mode 100644
index 386b81de..00000000
--- a/packages/vite/src/modules/get-config.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { defineViteServerAction } from '@vue/devtools-core'
-import type { ResolvedConfig } from 'vite'
-
-export function getViteConfig(config: ResolvedConfig, pluginOptions) {
- defineViteServerAction('get-vite-root', () => {
- return config.root
- })
-}
diff --git a/packages/vite/src/modules/graph.ts b/packages/vite/src/modules/graph.ts
deleted file mode 100644
index a69957b2..00000000
--- a/packages/vite/src/modules/graph.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import type { ViteInspectAPI } from 'vite-plugin-inspect'
-import { debounce } from 'perfect-debounce'
-import type { ViteDevServer } from 'vite'
-import { callViteClientListener, defineViteServerAction } from '@vue/devtools-core'
-
-export function setupGraphModule(options: { rpc: ViteInspectAPI['rpc'], server: ViteDevServer }) {
- const { rpc, server } = options
-
- defineViteServerAction('graph:get-modules', async () => {
- const list = await rpc.list()
- const modules = list?.modules || []
-
- return modules
- })
-
- const triggerModuleUpdated = callViteClientListener('graph:module-updated')
-
- const debouncedModuleUpdated = debounce(() => {
- triggerModuleUpdated()
- }, 100)
-
- server.middlewares.use((_, __, next) => {
- debouncedModuleUpdated()
- next()
- })
-}
diff --git a/packages/vite/src/modules/index.ts b/packages/vite/src/modules/index.ts
deleted file mode 100644
index bfa0cf2c..00000000
--- a/packages/vite/src/modules/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export * from './get-config'
-export * from './graph'
-export * from './assets'
diff --git a/packages/vite/src/overlay.js b/packages/vite/src/overlay.js
index 1c06632a..bedae99d 100644
--- a/packages/vite/src/overlay.js
+++ b/packages/vite/src/overlay.js
@@ -1,6 +1,6 @@
import vueDevToolsOptions from 'virtual:vue-devtools-options'
-import { initAppSeparateWindow, setDevToolsClientUrl } from '@vue/devtools-core'
-import { addCustomTab, devtools, setDevToolsEnv, setOpenInEditorBaseUrl, toggleComponentInspectorEnabled } from '@vue/devtools-kit'
+import { functions, setDevToolsClientUrl } from '@vue/devtools-core'
+import { addCustomTab, createRpcServer, devtools, setDevToolsEnv, setOpenInEditorBaseUrl, toggleComponentInspectorEnabled } from '@vue/devtools-kit'
function normalizeUrl(url) {
return new URL(`${vueDevToolsOptions.base || '/'}${url}`, import.meta.url).toString()
@@ -53,4 +53,10 @@ body.appendChild(script)
// Used in the browser extension
window.__VUE_DEVTOOLS_VITE_PLUGIN_CLIENT_URL__ = `${window.location.origin}${devtoolsClientUrl}`
-initAppSeparateWindow()
+createRpcServer(functions, {
+ preset: 'iframe',
+})
+
+createRpcServer(functions, {
+ preset: 'broadcast',
+})
diff --git a/packages/vite/src/modules/assets.ts b/packages/vite/src/rpc/assets.ts
similarity index 68%
rename from packages/vite/src/modules/assets.ts
rename to packages/vite/src/rpc/assets.ts
index ffe66dcb..1d267e7a 100644
--- a/packages/vite/src/modules/assets.ts
+++ b/packages/vite/src/rpc/assets.ts
@@ -1,12 +1,11 @@
import fsp from 'node:fs/promises'
-import type { ViteInspectAPI } from 'vite-plugin-inspect'
import { debounce } from 'perfect-debounce'
-import type { ResolvedConfig, ViteDevServer } from 'vite'
-import { callViteClientListener, defineViteServerAction } from '@vue/devtools-core'
-import type { AssetImporter, AssetInfo, AssetType, ImageMeta } from '@vue/devtools-core'
+import { getViteRpcServer } from '@vue/devtools-kit'
+import type { AssetImporter, AssetInfo, AssetType, ImageMeta, ViteRPCFunctions } from '@vue/devtools-core'
import fg from 'fast-glob'
import { join, resolve } from 'pathe'
import { imageMeta } from 'image-meta'
+import { RpcFunctionCtx } from './types'
const defaultAllowedExtensions = [
'png',
@@ -55,8 +54,8 @@ function guessType(path: string): AssetType {
return 'other'
}
-export function setupAssetsModule(options: { rpc: ViteInspectAPI['rpc'], server: ViteDevServer, config: ResolvedConfig }) {
- const { rpc, server, config } = options
+export function getAssetsFunctions(ctx: RpcFunctionCtx) {
+ const { server, config } = ctx
const _imageMetaCache = new Map()
let cache: AssetInfo[] | null = null
@@ -125,48 +124,45 @@ export function setupAssetsModule(options: { rpc: ViteInspectAPI['rpc'], server:
return importers
}
- defineViteServerAction('assets:get-static-assets', async () => {
- return await scan()
- })
-
- defineViteServerAction('assets:get-asset-importers', async (url: string) => {
- return await getAssetImporters(url)
- })
-
- defineViteServerAction('assets:get-image-meta', async (filepath: string) => {
- if (_imageMetaCache.has(filepath))
- return _imageMetaCache.get(filepath)
- try {
- const meta = imageMeta(await fsp.readFile(filepath)) as ImageMeta
- _imageMetaCache.set(filepath, meta)
- return meta
- }
- catch (e) {
- _imageMetaCache.set(filepath, undefined)
- console.error(e)
- return undefined
- }
- })
-
- defineViteServerAction('assets:get-text-asset-content', async (filepath: string, limit = 300) => {
- try {
- const content = await fsp.readFile(filepath, 'utf-8')
- return content.slice(0, limit)
- }
- catch (e) {
- console.error(e)
- return undefined
- }
- })
-
- const triggerAssetsUpdated = callViteClientListener('assets:updated')
-
const debouncedAssetsUpdated = debounce(() => {
- triggerAssetsUpdated()
+ getViteRpcServer?.()?.broadcast?.emit('assetsUpdated')
}, 100)
server.watcher.on('all', (event) => {
if (event !== 'change')
debouncedAssetsUpdated()
})
+
+ return {
+ async getStaticAssets() {
+ return await scan()
+ },
+ async getAssetImporters(url: string) {
+ return await getAssetImporters(url)
+ },
+ async getImageMeta(filepath: string) {
+ if (_imageMetaCache.has(filepath))
+ return _imageMetaCache.get(filepath)
+ try {
+ const meta = imageMeta(await fsp.readFile(filepath)) as ImageMeta
+ _imageMetaCache.set(filepath, meta)
+ return meta
+ }
+ catch (e) {
+ _imageMetaCache.set(filepath, undefined)
+ console.error(e)
+ return undefined
+ }
+ },
+ async getTextAssetContent(filepath: string, limit = 300) {
+ try {
+ const content = await fsp.readFile(filepath, 'utf-8')
+ return content.slice(0, limit)
+ }
+ catch (e) {
+ console.error(e)
+ return undefined
+ }
+ },
+ }
}
diff --git a/packages/vite/src/rpc/get-config.ts b/packages/vite/src/rpc/get-config.ts
new file mode 100644
index 00000000..dad5bfd8
--- /dev/null
+++ b/packages/vite/src/rpc/get-config.ts
@@ -0,0 +1,9 @@
+import { RpcFunctionCtx } from './types'
+
+export function getConfigFunctions(ctx: RpcFunctionCtx) {
+ return {
+ getRoot() {
+ return ctx.config.root
+ },
+ }
+}
diff --git a/packages/vite/src/rpc/graph.ts b/packages/vite/src/rpc/graph.ts
new file mode 100644
index 00000000..662ef51c
--- /dev/null
+++ b/packages/vite/src/rpc/graph.ts
@@ -0,0 +1,24 @@
+import { debounce } from 'perfect-debounce'
+import type { ModuleInfo, ViteRPCFunctions } from '@vue/devtools-core'
+import { getViteRpcServer } from '@vue/devtools-kit'
+import { RpcFunctionCtx } from './types'
+
+export function getGraphFunctions(ctx: RpcFunctionCtx) {
+ const { rpc, server } = ctx
+ const debouncedModuleUpdated = debounce(() => {
+ getViteRpcServer?.()?.broadcast?.emit('graphModuleUpdated')
+ }, 100)
+
+ server.middlewares.use((_, __, next) => {
+ debouncedModuleUpdated()
+ next()
+ })
+ return {
+ async getGraphModules(): Promise {
+ const list = await rpc.list()
+ const modules = list?.modules || []
+
+ return modules
+ },
+ }
+}
diff --git a/packages/vite/src/rpc/index.ts b/packages/vite/src/rpc/index.ts
new file mode 100644
index 00000000..105ee130
--- /dev/null
+++ b/packages/vite/src/rpc/index.ts
@@ -0,0 +1,15 @@
+import { getAssetsFunctions } from './assets'
+import { getConfigFunctions } from './get-config'
+import { getGraphFunctions } from './graph'
+import { RpcFunctionCtx } from './types'
+
+export function getRpcFunctions(ctx: RpcFunctionCtx) {
+ return {
+ heartbeat() {
+ return true
+ },
+ ...getAssetsFunctions(ctx),
+ ...getConfigFunctions(ctx),
+ ...getGraphFunctions(ctx),
+ }
+}
diff --git a/packages/vite/src/rpc/types.ts b/packages/vite/src/rpc/types.ts
new file mode 100644
index 00000000..4c11efc1
--- /dev/null
+++ b/packages/vite/src/rpc/types.ts
@@ -0,0 +1,8 @@
+import { ResolvedConfig, ViteDevServer } from 'vite'
+import { ViteInspectAPI } from 'vite-plugin-inspect/index'
+
+export interface RpcFunctionCtx {
+ rpc: ViteInspectAPI['rpc']
+ server: ViteDevServer
+ config: ResolvedConfig
+}
diff --git a/packages/vite/src/vite.ts b/packages/vite/src/vite.ts
index 584ceb0a..9a713cbd 100644
--- a/packages/vite/src/vite.ts
+++ b/packages/vite/src/vite.ts
@@ -4,14 +4,13 @@ import { normalizePath } from 'vite'
import type { PluginOption, ResolvedConfig, ViteDevServer } from 'vite'
import sirv from 'sirv'
import Inspect from 'vite-plugin-inspect'
+import { setViteServerContext } from '@vue/devtools-kit'
import VueInspector from 'vite-plugin-vue-inspector'
-import { initViteServerContext } from '@vue/devtools-core'
+import { createViteServerRpc } from '@vue/devtools-core'
import { bold, cyan, dim, green, yellow } from 'kolorist'
import type { VitePluginInspectorOptions } from 'vite-plugin-vue-inspector'
import { DIR_CLIENT } from './dir'
-import { getViteConfig, setupAssetsModule, setupGraphModule } from './modules'
-
-export type * from './modules'
+import { getRpcFunctions } from './rpc'
type DeepRequired = {
[P in keyof T]-?: T[P] extends object ? DeepRequired : Required;
@@ -99,17 +98,14 @@ export default function VitePluginVueDevTools(options?: VitePluginVueDevToolsOpt
}))
// vite client <-> server messaging
- initViteServerContext(server)
- getViteConfig(config, pluginOptions)
- setupGraphModule({
- rpc: inspect.api.rpc,
- server,
- })
- setupAssetsModule({
+ setViteServerContext(server)
+
+ const rpcFunctions = getRpcFunctions({
rpc: inspect.api.rpc,
server,
config,
})
+ createViteServerRpc(rpcFunctions)
const _printUrls = server.printUrls
const colorUrl = (url: string) =>
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1a9bde25..cab7516d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -198,6 +198,9 @@ importers:
'@vue/devtools-core':
specifier: workspace:^
version: link:../core
+ '@vue/devtools-kit':
+ specifier: workspace:^
+ version: link:../devtools-kit
'@vue/devtools-shared':
specifier: workspace:^
version: link:../shared
@@ -369,6 +372,9 @@ importers:
'@vue/devtools-shared':
specifier: workspace:^
version: link:../shared
+ birpc:
+ specifier: ^0.2.17
+ version: 0.2.17
hookable:
specifier: ^5.5.3
version: 5.5.3
@@ -381,6 +387,9 @@ importers:
speakingurl:
specifier: ^14.0.1
version: 14.0.1
+ superjson:
+ specifier: ^2.2.1
+ version: 2.2.1
devDependencies:
'@types/speakingurl':
specifier: ^13.0.6
@@ -397,6 +406,9 @@ importers:
'@vue/devtools-core':
specifier: workspace:^
version: link:../core
+ '@vue/devtools-kit':
+ specifier: workspace:^
+ version: link:../devtools-kit
'@vue/devtools-shared':
specifier: workspace:^
version: link:../shared
@@ -3559,6 +3571,9 @@ packages:
birpc@0.1.1:
resolution: {integrity: sha512-B64AGL4ug2IS2jvV/zjTYDD1L+2gOJTT7Rv+VaK7KVQtQOo/xZbCDsh7g727ipckmU+QJYRqo5RcifVr0Kgcmg==}
+ birpc@0.2.17:
+ resolution: {integrity: sha512-+hkTxhot+dWsLpp3gia5AkVHIsKlZybNT5gIYiDlNzJrmYPcTM9k5/w2uaj3IPpd7LlEYpmCj4Jj1nC41VhDFg==}
+
bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
@@ -4134,6 +4149,10 @@ packages:
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
engines: {node: '>= 0.6'}
+ copy-anything@3.0.5:
+ resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
+ engines: {node: '>=12.13'}
+
copy-webpack-plugin@9.1.0:
resolution: {integrity: sha512-rxnR7PaGigJzhqETHGmAcxKnLZSR5u1Y3/bcIv/1FnqXedcL/E2ewK7ZCNrArJKCiSv8yVXhTqetJh8inDvfsA==}
engines: {node: '>= 12.13.0'}
@@ -5584,6 +5603,10 @@ packages:
resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
engines: {node: '>=10'}
+ is-what@4.1.16:
+ resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==}
+ engines: {node: '>=12.13'}
+
is-wsl@2.2.0:
resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
engines: {node: '>=8'}
@@ -7460,6 +7483,10 @@ packages:
resolution: {integrity: sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==}
engines: {node: '>= 8.0'}
+ superjson@2.2.1:
+ resolution: {integrity: sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==}
+ engines: {node: '>=16'}
+
supports-color@5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}
@@ -11668,6 +11695,8 @@ snapshots:
birpc@0.1.1: {}
+ birpc@0.2.17: {}
+
bl@4.1.0:
dependencies:
buffer: 5.7.1
@@ -12139,6 +12168,10 @@ snapshots:
cookie@0.5.0: {}
+ copy-anything@3.0.5:
+ dependencies:
+ is-what: 4.1.16
+
copy-webpack-plugin@9.1.0(webpack@5.89.0(esbuild@0.21.4)):
dependencies:
fast-glob: 3.3.2
@@ -13927,6 +13960,8 @@ snapshots:
is-unicode-supported@0.1.0: {}
+ is-what@4.1.16: {}
+
is-wsl@2.2.0:
dependencies:
is-docker: 2.2.1
@@ -15856,6 +15891,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ superjson@2.2.1:
+ dependencies:
+ copy-anything: 3.0.5
+
supports-color@5.5.0:
dependencies:
has-flag: 3.0.0
diff --git a/scripts/extension-zip.ts b/scripts/extension-zip.ts
index 459de63a..5772909b 100644
--- a/scripts/extension-zip.ts
+++ b/scripts/extension-zip.ts
@@ -2,7 +2,7 @@ import path from 'node:path'
import fs from 'node:fs'
import { fileURLToPath } from 'node:url'
import archiver from 'archiver'
-import readDirGlob from 'readdir-glob'
+import readdirGlob from 'readdir-glob'
import ProgressBar from 'progress'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
@@ -44,7 +44,8 @@ async function zip(filename: string) {
}
async function parseFileStats() {
return new Promise((resolve, reject) => {
- const globber = readDirGlob(targetPkgDir, { pattern: INCLUDE_FILES, skip: EXCLUDE_DIRS, mark: true, stat: true })
+ // @ts-expect-error skip
+ const globber = readdirGlob.readdirGlob(targetPkgDir, { pattern: INCLUDE_FILES, skip: EXCLUDE_DIRS, mark: true, stat: true })
globber.on('match', (match) => {
if (!match.stat.isDirectory())
status.total++