From f65ee68fa1f0f732efe9f20857bbe75ae1797eb3 Mon Sep 17 00:00:00 2001 From: arlo Date: Mon, 22 Jan 2024 20:54:12 +0800 Subject: [PATCH 01/18] feat: init --- packages/devtools-kit-next/README.md | 3 + packages/devtools-kit-next/package.json | 37 ++ packages/devtools-kit-next/src/core/index.ts | 3 + packages/devtools-kit-next/src/index.ts | 9 + packages/devtools-kit-next/tsup.config.ts | 19 + packages/kit-playground/index.html | 18 + packages/kit-playground/package.json | 32 + packages/kit-playground/public/vite.svg | 1 + packages/kit-playground/src/App.vue | 14 + packages/kit-playground/src/main.ts | 38 ++ packages/kit-playground/src/pages/Home.vue | 9 + packages/kit-playground/src/stores/index.ts | 22 + packages/kit-playground/src/style.css | 16 + packages/kit-playground/tsconfig.json | 16 + packages/kit-playground/vite.config.ts | 24 + pnpm-lock.yaml | 626 ++++++++++++++++--- 16 files changed, 791 insertions(+), 96 deletions(-) create mode 100644 packages/devtools-kit-next/README.md create mode 100644 packages/devtools-kit-next/package.json create mode 100644 packages/devtools-kit-next/src/core/index.ts create mode 100644 packages/devtools-kit-next/src/index.ts create mode 100644 packages/devtools-kit-next/tsup.config.ts create mode 100644 packages/kit-playground/index.html create mode 100644 packages/kit-playground/package.json create mode 100644 packages/kit-playground/public/vite.svg create mode 100644 packages/kit-playground/src/App.vue create mode 100644 packages/kit-playground/src/main.ts create mode 100644 packages/kit-playground/src/pages/Home.vue create mode 100644 packages/kit-playground/src/stores/index.ts create mode 100644 packages/kit-playground/src/style.css create mode 100644 packages/kit-playground/tsconfig.json create mode 100644 packages/kit-playground/vite.config.ts diff --git a/packages/devtools-kit-next/README.md b/packages/devtools-kit-next/README.md new file mode 100644 index 00000000..dcc52418 --- /dev/null +++ b/packages/devtools-kit-next/README.md @@ -0,0 +1,3 @@ +# @vue/devtools-kit + +> Utility kit for DevTools. diff --git a/packages/devtools-kit-next/package.json b/packages/devtools-kit-next/package.json new file mode 100644 index 00000000..376f334f --- /dev/null +++ b/packages/devtools-kit-next/package.json @@ -0,0 +1,37 @@ +{ + "name": "@vue/devtools-kit-next", + "type": "module", + "version": "7.0.11", + "private": true, + "author": "webfansplz", + "license": "MIT", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.cjs" + } + }, + "main": "./dist/index.cjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "tsup --clean", + "prepare:type": "tsup --dts-only", + "stub": "tsup --watch --onSuccess 'tsup --dts-only'" + }, + "dependencies": { + "@vue/devtools-schema": "workspace:^", + "@vue/devtools-shared": "workspace:^", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1" + }, + "devDependencies": { + "vue": "^3.4.14", + "vue-router": "^4.2.5" + } +} diff --git a/packages/devtools-kit-next/src/core/index.ts b/packages/devtools-kit-next/src/core/index.ts new file mode 100644 index 00000000..8963ee4c --- /dev/null +++ b/packages/devtools-kit-next/src/core/index.ts @@ -0,0 +1,3 @@ +export function initDevTools() { + console.log('init') +} diff --git a/packages/devtools-kit-next/src/index.ts b/packages/devtools-kit-next/src/index.ts new file mode 100644 index 00000000..325f6aae --- /dev/null +++ b/packages/devtools-kit-next/src/index.ts @@ -0,0 +1,9 @@ +import { initDevTools } from './core/index' + +export const devtools = { + hook: {}, + init: initDevTools, + get api() { + return {} + }, +} diff --git a/packages/devtools-kit-next/tsup.config.ts b/packages/devtools-kit-next/tsup.config.ts new file mode 100644 index 00000000..fab00c00 --- /dev/null +++ b/packages/devtools-kit-next/tsup.config.ts @@ -0,0 +1,19 @@ +import type { Options } from 'tsup' + +export default { + entryPoints: [ + 'src/index.ts', + ], + esbuildOptions(options) { + if (options.format === 'esm') + options.outExtension = { '.js': '.mjs' } + }, + external: [ + 'vue', + ], + noExternal: ['speakingurl'], + clean: true, + format: ['esm', 'cjs'], + dts: true, + shims: true, +} diff --git a/packages/kit-playground/index.html b/packages/kit-playground/index.html new file mode 100644 index 00000000..ca074c8b --- /dev/null +++ b/packages/kit-playground/index.html @@ -0,0 +1,18 @@ + + + + + + + Vue DevTools Kit Playground + + + +
+
+ + + diff --git a/packages/kit-playground/package.json b/packages/kit-playground/package.json new file mode 100644 index 00000000..6e967a54 --- /dev/null +++ b/packages/kit-playground/package.json @@ -0,0 +1,32 @@ +{ + "name": "@vue/kit-devtools-playground", + "type": "module", + "version": "7.0.11", + "private": true, + "scripts": { + "app": "vue-devtools", + "build:playground": "vite build", + "dev": "vite", + "preview": "vite preview" + }, + "dependencies": { + "@vueuse/core": "^10.7.2", + "pinia": "^2.1.7", + "vue": "^3.4.14", + "vue-i18n": "^9.9.0", + "vue-router": "^4.2.5" + }, + "devDependencies": { + "@intlify/unplugin-vue-i18n": "^2.0.0", + "@vitejs/plugin-vue": "^5.0.3", + "@vue/devtools": "workspace:*", + "@vue/devtools-api": "workspace:*", + "@vue/devtools-kit-next": "workspace:*", + "sass": "^1.69.7", + "serve": "^14.2.1", + "typescript": "^5.3.3", + "vite": "^5.0.11", + "vite-plugin-inspect": "^0.8.1", + "vite-plugin-vue-devtools": "workspace:*" + } +} diff --git a/packages/kit-playground/public/vite.svg b/packages/kit-playground/public/vite.svg new file mode 100644 index 00000000..e7b8dfb1 --- /dev/null +++ b/packages/kit-playground/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/kit-playground/src/App.vue b/packages/kit-playground/src/App.vue new file mode 100644 index 00000000..3a898239 --- /dev/null +++ b/packages/kit-playground/src/App.vue @@ -0,0 +1,14 @@ + + + diff --git a/packages/kit-playground/src/main.ts b/packages/kit-playground/src/main.ts new file mode 100644 index 00000000..b114e849 --- /dev/null +++ b/packages/kit-playground/src/main.ts @@ -0,0 +1,38 @@ +import { createPinia } from 'pinia' +import { createApp } from 'vue' +import type { RouteRecordRaw } from 'vue-router' +import { createRouter, createWebHistory } from 'vue-router' + +import { devtools } from '@vue/devtools-kit-next' +import App from './App.vue' +import Home from './pages/Home.vue' + +import './style.css' + +devtools.init() + +const pinia = createPinia() + +const app = createApp(App) + +const routes: RouteRecordRaw[] = [ + { + path: '/', + component: Home, + name: 'home', + alias: '/index', + }, +] + +const router = createRouter({ + history: createWebHistory(), + routes, +}) + +app.use(router) +app.use(pinia) + +// setTimeout(() => { +// }, 2000) + +app.mount('#app') diff --git a/packages/kit-playground/src/pages/Home.vue b/packages/kit-playground/src/pages/Home.vue new file mode 100644 index 00000000..612c020d --- /dev/null +++ b/packages/kit-playground/src/pages/Home.vue @@ -0,0 +1,9 @@ + + + diff --git a/packages/kit-playground/src/stores/index.ts b/packages/kit-playground/src/stores/index.ts new file mode 100644 index 00000000..a4764680 --- /dev/null +++ b/packages/kit-playground/src/stores/index.ts @@ -0,0 +1,22 @@ +import { defineStore } from 'pinia' +import { computed, ref } from 'vue' + +export const useAppStore = defineStore('app', () => { + const count = ref(120) + function increment() { + count.value++ + } + const doubledCount = computed(() => count.value * 2) + + return { count, doubledCount, increment } +}) + +export const useCounterStore = defineStore('counter', () => { + const count = ref(10) + const name = ref('webfansplz!!!') + function increment() { + count.value++ + } + + return { count, name, increment } +}) diff --git a/packages/kit-playground/src/style.css b/packages/kit-playground/src/style.css new file mode 100644 index 00000000..f0a3c180 --- /dev/null +++ b/packages/kit-playground/src/style.css @@ -0,0 +1,16 @@ +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} diff --git a/packages/kit-playground/tsconfig.json b/packages/kit-playground/tsconfig.json new file mode 100644 index 00000000..c934cb0a --- /dev/null +++ b/packages/kit-playground/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ESNext", + "jsx": "preserve", + "lib": ["ESNext", "DOM"], + "useDefineForClassFields": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "strict": true, + "sourceMap": true, + "esModuleInterop": true, + "isolatedModules": true, + "skipLibCheck": true + } +} diff --git a/packages/kit-playground/vite.config.ts b/packages/kit-playground/vite.config.ts new file mode 100644 index 00000000..579cec38 --- /dev/null +++ b/packages/kit-playground/vite.config.ts @@ -0,0 +1,24 @@ +import path from 'node:path' +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +// import VueDevtools from 'vite-plugin-vue-devtools' +import VueI18n from '@intlify/unplugin-vue-i18n/vite' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + vue(), + // VueDevtools(), + // https://github.com/intlify/bundle-tools/tree/main/packages/unplugin-vue-i18n + VueI18n({ + runtimeOnly: true, + compositionOnly: true, + fullInstall: true, + include: [path.resolve('./locales/**')], + }), + ], + server: { + port: 3000, + }, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ac471f9d..b0d4ab30 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -88,7 +88,7 @@ importers: version: 0.13.1 tsup: specifier: ^8.0.1 - version: 8.0.1(postcss@8.4.32)(typescript@5.3.3) + version: 8.0.1(postcss@8.4.33)(typescript@5.3.3) tsx: specifier: ^4.7.0 version: 4.7.0 @@ -100,10 +100,10 @@ importers: version: 2.0.0(typescript@5.3.3) unocss: specifier: ^0.58.3 - version: 0.58.3(postcss@8.4.32)(rollup@3.28.1)(vite@5.0.11) + version: 0.58.3(postcss@8.4.33)(rollup@3.28.1)(vite@5.0.11) vite: specifier: ^5.0.11 - version: 5.0.11(@types/node@20.11.5)(sass@1.69.7) + version: 5.0.11(@types/node@20.11.5) vitest: specifier: ^1.2.0 version: 1.2.0(@types/node@20.11.5) @@ -124,7 +124,7 @@ importers: version: 0.26.0(rollup@3.28.1)(vue@3.4.14) vitepress: specifier: 1.0.0-rc.30 - version: 1.0.0-rc.30(@algolia/client-search@4.22.0)(@types/node@20.11.5)(postcss@8.4.32)(search-insights@2.13.0)(typescript@5.3.3) + version: 1.0.0-rc.30(@algolia/client-search@4.22.0)(@types/node@20.11.5)(postcss@8.4.33)(search-insights@2.13.0)(typescript@5.3.3) vue: specifier: ^3.4.14 version: 3.4.14(typescript@5.3.3) @@ -243,7 +243,7 @@ importers: version: 2.9.0 unocss: specifier: ^0.58.3 - version: 0.58.3(postcss@8.4.32)(rollup@3.28.1)(vite@5.0.11) + version: 0.58.3(postcss@8.4.33)(rollup@3.28.1)(vite@5.0.11) unplugin: specifier: ^1.6.0 version: 1.6.0 @@ -343,6 +343,34 @@ importers: specifier: ^4.2.5 version: 4.2.5(vue@3.4.14) + packages/devtools-kit-next: + dependencies: + '@vue/devtools-schema': + specifier: workspace:^ + version: link:../schema + '@vue/devtools-shared': + specifier: workspace:^ + version: link:../shared + hookable: + specifier: ^5.5.3 + version: 5.5.3 + mitt: + specifier: ^3.0.1 + version: 3.0.1 + perfect-debounce: + specifier: ^1.0.0 + version: 1.0.0 + speakingurl: + specifier: ^14.0.1 + version: 14.0.1 + devDependencies: + vue: + specifier: ^3.4.14 + version: 3.4.14(typescript@5.3.3) + vue-router: + specifier: ^4.2.5 + version: 4.2.5(vue@3.4.14) + packages/electron: dependencies: '@vue/devtools-core': @@ -386,6 +414,58 @@ importers: specifier: ^3.4.14 version: 3.4.14(typescript@5.3.3) + packages/kit-playground: + dependencies: + '@vueuse/core': + specifier: ^10.7.2 + version: 10.7.2(vue@3.4.14) + pinia: + specifier: ^2.1.7 + version: 2.1.7(typescript@5.3.3)(vue@3.4.14) + vue: + specifier: ^3.4.14 + version: 3.4.14(typescript@5.3.3) + vue-i18n: + specifier: ^9.9.0 + version: 9.9.0(vue@3.4.14) + vue-router: + specifier: ^4.2.5 + version: 4.2.5(vue@3.4.14) + devDependencies: + '@intlify/unplugin-vue-i18n': + specifier: ^2.0.0 + version: 2.0.0(rollup@3.28.1)(vue-i18n@9.9.0) + '@vitejs/plugin-vue': + specifier: ^5.0.3 + version: 5.0.3(vite@5.0.11)(vue@3.4.14) + '@vue/devtools': + specifier: workspace:* + version: link:../devtools + '@vue/devtools-api': + specifier: workspace:* + version: link:../devtools-api + '@vue/devtools-kit-next': + specifier: workspace:* + version: link:../devtools-kit-next + sass: + specifier: ^1.69.7 + version: 1.69.7 + serve: + specifier: ^14.2.1 + version: 14.2.1 + typescript: + specifier: ^5.3.3 + version: 5.3.3 + vite: + specifier: ^5.0.11 + version: 5.0.11(@types/node@20.11.5)(sass@1.69.7) + vite-plugin-inspect: + specifier: ^0.8.1 + version: 0.8.1(rollup@3.28.1)(vite@5.0.11) + vite-plugin-vue-devtools: + specifier: workspace:* + version: link:../vite + packages/overlay: dependencies: '@vue/devtools-core': @@ -525,7 +605,7 @@ importers: version: 0.17.8(@types/node@20.11.5)(vite@5.0.11) unocss: specifier: ^0.58.3 - version: 0.58.3(postcss@8.4.32)(rollup@3.28.1)(vite@5.0.11) + version: 0.58.3(postcss@8.4.33)(rollup@3.28.1)(vite@5.0.11) vite-plugin-dts: specifier: ^3.7.1 version: 3.7.1(@types/node@20.11.5)(rollup@3.28.1)(typescript@5.3.3)(vite@5.0.11) @@ -543,7 +623,7 @@ importers: version: 10.7.2(vue@3.4.14) unocss: specifier: ^0.58.3 - version: 0.58.3(postcss@8.4.32)(rollup@3.28.1)(vite@5.0.11) + version: 0.58.3(postcss@8.4.33)(rollup@3.28.1)(vite@5.0.11) vue: specifier: ^3.4.14 version: 3.4.14(typescript@5.3.3) @@ -556,7 +636,7 @@ importers: version: 5.3.3 vite: specifier: ^5.0.11 - version: 5.0.11(@types/node@20.11.5)(sass@1.69.7) + version: 5.0.11(@types/node@20.11.5) vue-tsc: specifier: ^1.8.27 version: 1.8.27(typescript@5.3.3) @@ -3051,7 +3131,7 @@ packages: chokidar: 3.5.3 pathe: 1.1.2 picocolors: 1.0.0 - vite: 5.0.11(@types/node@20.11.5)(sass@1.69.7) + vite: 5.0.11(@types/node@20.11.5) dev: true /@histoire/vendors@0.17.8: @@ -3161,7 +3241,7 @@ packages: '@intlify/bundle-utils': 7.5.0(vue-i18n@9.9.0) '@intlify/shared': 9.9.0 '@rollup/pluginutils': 5.1.0(rollup@3.28.1) - '@vue/compiler-sfc': 3.4.4 + '@vue/compiler-sfc': 3.4.14 debug: 4.3.4 fast-glob: 3.3.2 js-yaml: 4.1.0 @@ -4440,7 +4520,7 @@ packages: gzip-size: 6.0.0 sirv: 2.0.4 - /@unocss/postcss@0.58.3(postcss@8.4.32): + /@unocss/postcss@0.58.3(postcss@8.4.33): resolution: {integrity: sha512-y1WQNvLUidypCu/tr6oJfaV4pjd8Lsk1N27ASEVsvockOH3MekRYpHtJfTl2fMk+1Y98AHv7hPAVjM2NlvhDow==} engines: {node: '>=14'} peerDependencies: @@ -4452,7 +4532,7 @@ packages: css-tree: 2.3.1 fast-glob: 3.3.2 magic-string: 0.30.5 - postcss: 8.4.32 + postcss: 8.4.33 /@unocss/preset-attributify@0.58.3: resolution: {integrity: sha512-iDXNfnSC0SI51UnMltHmMcPr2SYYkimo86i+SBQqc/WBGcCF7fFqFj8G2WsZfwHvU9SdAHF8tYIwNq06w1WSeg==} @@ -4613,7 +4693,7 @@ packages: vite: ^5.0.0 vue: ^3.2.25 dependencies: - vite: 5.0.11(@types/node@20.11.5)(sass@1.69.7) + vite: 5.0.11(@types/node@20.11.5) vue: 3.4.0(typescript@5.3.3) dev: true @@ -4624,7 +4704,7 @@ packages: vite: ^5.0.0 vue: ^3.2.25 dependencies: - vite: 5.0.11(@types/node@20.11.5)(sass@1.69.7) + vite: 5.0.11(@types/node@20.11.5) vue: 3.4.14(typescript@5.3.3) dev: true @@ -5147,16 +5227,6 @@ packages: estree-walker: 2.0.2 source-map-js: 1.0.2 - /@vue/compiler-core@3.4.4: - resolution: {integrity: sha512-U5AdCN+6skzh2bSJrkMj2KZsVkUpgK8/XlxjSRYQZhNPcvt9/kmgIMpFEiTyK+Dz5E1J+8o8//BEIX+bakgVSw==} - dependencies: - '@babel/parser': 7.23.6 - '@vue/shared': 3.4.4 - entities: 4.5.0 - estree-walker: 2.0.2 - source-map-js: 1.0.2 - dev: true - /@vue/compiler-dom@3.4.0: resolution: {integrity: sha512-E957uOhpoE48YjZGWeAoLmNYd3UeU4oIP8kJi8Rcsb9l2tV8Z48Jn07Zgq1aW0v3vuhlmydEKkKKbhLpADHXEA==} dependencies: @@ -5169,13 +5239,6 @@ packages: '@vue/compiler-core': 3.4.14 '@vue/shared': 3.4.14 - /@vue/compiler-dom@3.4.4: - resolution: {integrity: sha512-iSwkdDULCN+Vr8z6uwdlL044GJ/nUmECxP9vu7MzEs4Qma0FwDLYvnvRcyO0ZITuu3Os4FptGUDnhi1kOLSaGw==} - dependencies: - '@vue/compiler-core': 3.4.4 - '@vue/shared': 3.4.4 - dev: true - /@vue/compiler-sfc@2.7.15: resolution: {integrity: sha512-FCvIEevPmgCgqFBH7wD+3B97y7u7oj/Wr69zADBf403Tui377bThTjBvekaZvlRr4IwUAu3M6hYZeULZFJbdYg==} dependencies: @@ -5210,20 +5273,6 @@ packages: postcss: 8.4.33 source-map-js: 1.0.2 - /@vue/compiler-sfc@3.4.4: - resolution: {integrity: sha512-OTFcU6vUxUNHBcarzkp4g6d25nvcmDvFDzPRvSrIsByFFPRYN+y3b+j9HxYwt6nlWvGyFCe0roeJdJlfYxbCBg==} - dependencies: - '@babel/parser': 7.23.6 - '@vue/compiler-core': 3.4.4 - '@vue/compiler-dom': 3.4.4 - '@vue/compiler-ssr': 3.4.4 - '@vue/shared': 3.4.4 - estree-walker: 2.0.2 - magic-string: 0.30.5 - postcss: 8.4.32 - source-map-js: 1.0.2 - dev: true - /@vue/compiler-ssr@3.4.0: resolution: {integrity: sha512-+oXKy105g9DIYQKDi3Gwung0xqQX5gJHr0GR+Vf7yK/WkNDM6q61ummcKmKAB85EIst8y3vj2PA9z9YU5Oc4DQ==} dependencies: @@ -5236,13 +5285,6 @@ packages: '@vue/compiler-dom': 3.4.14 '@vue/shared': 3.4.14 - /@vue/compiler-ssr@3.4.4: - resolution: {integrity: sha512-1DU9DflSSQlx/M61GEBN+NbT/anUki2ooDo9IXfTckCeKA/2IKNhY8KbG3x6zkd3KGrxzteC7de6QL88vEb41Q==} - dependencies: - '@vue/compiler-dom': 3.4.4 - '@vue/shared': 3.4.4 - dev: true - /@vue/component-compiler-utils@3.3.0: resolution: {integrity: sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==} dependencies: @@ -5325,8 +5367,8 @@ packages: dependencies: '@volar/language-core': 1.11.1 '@volar/source-map': 1.11.1 - '@vue/compiler-dom': 3.4.0 - '@vue/shared': 3.4.0 + '@vue/compiler-dom': 3.4.14 + '@vue/shared': 3.4.14 computeds: 0.0.1 minimatch: 9.0.3 muggle-string: 0.3.1 @@ -5395,10 +5437,6 @@ packages: /@vue/shared@3.4.14: resolution: {integrity: sha512-nmi3BtLpvqXAWoRZ6HQ+pFJOHBU4UnH3vD3opgmwXac7vhaHKA9nj1VeGjMggdB9eLtW83eHyPCmOU1qzdsC7Q==} - /@vue/shared@3.4.4: - resolution: {integrity: sha512-abSgiVRhfjfl3JALR/cSuBl74hGJ3SePgf1mKzodf1eMWLwHZbfEGxT2cNJSsNiw44jEgrO7bNkhchaWA7RwNw==} - dev: true - /@vue/web-component-wrapper@1.3.0: resolution: {integrity: sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==} dev: true @@ -7233,18 +7271,27 @@ packages: postcss: 8.4.32 dev: true + /css-declaration-sorter@6.4.1(postcss@8.4.33): + resolution: {integrity: sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==} + engines: {node: ^10 || ^12 || >=14} + peerDependencies: + postcss: ^8.0.9 + dependencies: + postcss: 8.4.33 + dev: true + /css-loader@6.8.1(webpack@5.89.0): resolution: {integrity: sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==} engines: {node: '>= 12.13.0'} peerDependencies: webpack: ^5.0.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.32) - postcss: 8.4.32 - postcss-modules-extract-imports: 3.0.0(postcss@8.4.32) - postcss-modules-local-by-default: 4.0.3(postcss@8.4.32) - postcss-modules-scope: 3.1.0(postcss@8.4.32) - postcss-modules-values: 4.0.0(postcss@8.4.32) + icss-utils: 5.1.0(postcss@8.4.33) + postcss: 8.4.33 + postcss-modules-extract-imports: 3.0.0(postcss@8.4.33) + postcss-modules-local-by-default: 4.0.3(postcss@8.4.33) + postcss-modules-scope: 3.1.0(postcss@8.4.33) + postcss-modules-values: 4.0.0(postcss@8.4.33) postcss-value-parser: 4.2.0 semver: 7.5.4 webpack: 5.89.0(esbuild@0.19.10) @@ -7269,10 +7316,10 @@ packages: esbuild: optional: true dependencies: - cssnano: 5.1.15(postcss@8.4.32) + cssnano: 5.1.15(postcss@8.4.33) esbuild: 0.19.10 jest-worker: 27.5.1 - postcss: 8.4.32 + postcss: 8.4.33 schema-utils: 4.2.0 serialize-javascript: 6.0.1 source-map: 0.6.1 @@ -7353,6 +7400,44 @@ packages: postcss-unique-selectors: 5.1.1(postcss@8.4.32) dev: true + /cssnano-preset-default@5.2.14(postcss@8.4.33): + resolution: {integrity: sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + css-declaration-sorter: 6.4.1(postcss@8.4.33) + cssnano-utils: 3.1.0(postcss@8.4.33) + postcss: 8.4.33 + postcss-calc: 8.2.4(postcss@8.4.33) + postcss-colormin: 5.3.1(postcss@8.4.33) + postcss-convert-values: 5.1.3(postcss@8.4.33) + postcss-discard-comments: 5.1.2(postcss@8.4.33) + postcss-discard-duplicates: 5.1.0(postcss@8.4.33) + postcss-discard-empty: 5.1.1(postcss@8.4.33) + postcss-discard-overridden: 5.1.0(postcss@8.4.33) + postcss-merge-longhand: 5.1.7(postcss@8.4.33) + postcss-merge-rules: 5.1.4(postcss@8.4.33) + postcss-minify-font-values: 5.1.0(postcss@8.4.33) + postcss-minify-gradients: 5.1.1(postcss@8.4.33) + postcss-minify-params: 5.1.4(postcss@8.4.33) + postcss-minify-selectors: 5.2.1(postcss@8.4.33) + postcss-normalize-charset: 5.1.0(postcss@8.4.33) + postcss-normalize-display-values: 5.1.0(postcss@8.4.33) + postcss-normalize-positions: 5.1.1(postcss@8.4.33) + postcss-normalize-repeat-style: 5.1.1(postcss@8.4.33) + postcss-normalize-string: 5.1.0(postcss@8.4.33) + postcss-normalize-timing-functions: 5.1.0(postcss@8.4.33) + postcss-normalize-unicode: 5.1.1(postcss@8.4.33) + postcss-normalize-url: 5.1.0(postcss@8.4.33) + postcss-normalize-whitespace: 5.1.1(postcss@8.4.33) + postcss-ordered-values: 5.1.3(postcss@8.4.33) + postcss-reduce-initial: 5.1.2(postcss@8.4.33) + postcss-reduce-transforms: 5.1.0(postcss@8.4.33) + postcss-svgo: 5.1.0(postcss@8.4.33) + postcss-unique-selectors: 5.1.1(postcss@8.4.33) + dev: true + /cssnano-utils@3.1.0(postcss@8.4.32): resolution: {integrity: sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==} engines: {node: ^10 || ^12 || >=14.0} @@ -7362,6 +7447,15 @@ packages: postcss: 8.4.32 dev: true + /cssnano-utils@3.1.0(postcss@8.4.33): + resolution: {integrity: sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.33 + dev: true + /cssnano@5.1.15(postcss@8.4.32): resolution: {integrity: sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==} engines: {node: ^10 || ^12 || >=14.0} @@ -7374,6 +7468,18 @@ packages: yaml: 1.10.2 dev: true + /cssnano@5.1.15(postcss@8.4.33): + resolution: {integrity: sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + cssnano-preset-default: 5.2.14(postcss@8.4.33) + lilconfig: 2.1.0 + postcss: 8.4.33 + yaml: 1.10.2 + dev: true + /csso@4.2.0: resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} engines: {node: '>=8.0.0'} @@ -9669,7 +9775,7 @@ packages: sade: 1.8.1 shiki-es: 0.2.0 sirv: 2.0.4 - vite: 5.0.11(@types/node@20.11.5)(sass@1.69.7) + vite: 5.0.11(@types/node@20.11.5) vite-node: 0.34.6(@types/node@20.11.5) transitivePeerDependencies: - '@types/node' @@ -9916,13 +10022,13 @@ packages: safer-buffer: 2.1.2 dev: true - /icss-utils@5.1.0(postcss@8.4.32): + /icss-utils@5.1.0(postcss@8.4.33): resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.32 + postcss: 8.4.33 dev: true /ieee754@1.2.1: @@ -12036,6 +12142,16 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-calc@8.2.4(postcss@8.4.33): + resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==} + peerDependencies: + postcss: ^8.2.2 + dependencies: + postcss: 8.4.33 + postcss-selector-parser: 6.0.13 + postcss-value-parser: 4.2.0 + dev: true + /postcss-colormin@5.3.1(postcss@8.4.32): resolution: {integrity: sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==} engines: {node: ^10 || ^12 || >=14.0} @@ -12049,6 +12165,19 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-colormin@5.3.1(postcss@8.4.33): + resolution: {integrity: sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.22.2 + caniuse-api: 3.0.0 + colord: 2.9.3 + postcss: 8.4.33 + postcss-value-parser: 4.2.0 + dev: true + /postcss-convert-values@5.1.3(postcss@8.4.32): resolution: {integrity: sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==} engines: {node: ^10 || ^12 || >=14.0} @@ -12060,6 +12189,17 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-convert-values@5.1.3(postcss@8.4.33): + resolution: {integrity: sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.22.2 + postcss: 8.4.33 + postcss-value-parser: 4.2.0 + dev: true + /postcss-discard-comments@5.1.2(postcss@8.4.32): resolution: {integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==} engines: {node: ^10 || ^12 || >=14.0} @@ -12069,6 +12209,15 @@ packages: postcss: 8.4.32 dev: true + /postcss-discard-comments@5.1.2(postcss@8.4.33): + resolution: {integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.33 + dev: true + /postcss-discard-duplicates@5.1.0(postcss@8.4.32): resolution: {integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==} engines: {node: ^10 || ^12 || >=14.0} @@ -12078,6 +12227,15 @@ packages: postcss: 8.4.32 dev: true + /postcss-discard-duplicates@5.1.0(postcss@8.4.33): + resolution: {integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.33 + dev: true + /postcss-discard-empty@5.1.1(postcss@8.4.32): resolution: {integrity: sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==} engines: {node: ^10 || ^12 || >=14.0} @@ -12087,6 +12245,15 @@ packages: postcss: 8.4.32 dev: true + /postcss-discard-empty@5.1.1(postcss@8.4.33): + resolution: {integrity: sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.33 + dev: true + /postcss-discard-overridden@5.1.0(postcss@8.4.32): resolution: {integrity: sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==} engines: {node: ^10 || ^12 || >=14.0} @@ -12096,7 +12263,16 @@ packages: postcss: 8.4.32 dev: true - /postcss-load-config@4.0.2(postcss@8.4.32): + /postcss-discard-overridden@5.1.0(postcss@8.4.33): + resolution: {integrity: sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.33 + dev: true + + /postcss-load-config@4.0.2(postcss@8.4.33): resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} engines: {node: '>= 14'} peerDependencies: @@ -12109,7 +12285,7 @@ packages: optional: true dependencies: lilconfig: 3.0.0 - postcss: 8.4.32 + postcss: 8.4.33 yaml: 2.3.4 dev: true @@ -12138,6 +12314,17 @@ packages: stylehacks: 5.1.1(postcss@8.4.32) dev: true + /postcss-merge-longhand@5.1.7(postcss@8.4.33): + resolution: {integrity: sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.33 + postcss-value-parser: 4.2.0 + stylehacks: 5.1.1(postcss@8.4.33) + dev: true + /postcss-merge-rules@5.1.4(postcss@8.4.32): resolution: {integrity: sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==} engines: {node: ^10 || ^12 || >=14.0} @@ -12151,6 +12338,19 @@ packages: postcss-selector-parser: 6.0.13 dev: true + /postcss-merge-rules@5.1.4(postcss@8.4.33): + resolution: {integrity: sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.22.2 + caniuse-api: 3.0.0 + cssnano-utils: 3.1.0(postcss@8.4.33) + postcss: 8.4.33 + postcss-selector-parser: 6.0.13 + dev: true + /postcss-minify-font-values@5.1.0(postcss@8.4.32): resolution: {integrity: sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==} engines: {node: ^10 || ^12 || >=14.0} @@ -12161,6 +12361,16 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-minify-font-values@5.1.0(postcss@8.4.33): + resolution: {integrity: sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.33 + postcss-value-parser: 4.2.0 + dev: true + /postcss-minify-gradients@5.1.1(postcss@8.4.32): resolution: {integrity: sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==} engines: {node: ^10 || ^12 || >=14.0} @@ -12173,6 +12383,18 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-minify-gradients@5.1.1(postcss@8.4.33): + resolution: {integrity: sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + colord: 2.9.3 + cssnano-utils: 3.1.0(postcss@8.4.33) + postcss: 8.4.33 + postcss-value-parser: 4.2.0 + dev: true + /postcss-minify-params@5.1.4(postcss@8.4.32): resolution: {integrity: sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==} engines: {node: ^10 || ^12 || >=14.0} @@ -12185,6 +12407,18 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-minify-params@5.1.4(postcss@8.4.33): + resolution: {integrity: sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.22.2 + cssnano-utils: 3.1.0(postcss@8.4.33) + postcss: 8.4.33 + postcss-value-parser: 4.2.0 + dev: true + /postcss-minify-selectors@5.2.1(postcss@8.4.32): resolution: {integrity: sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==} engines: {node: ^10 || ^12 || >=14.0} @@ -12195,45 +12429,55 @@ packages: postcss-selector-parser: 6.0.13 dev: true - /postcss-modules-extract-imports@3.0.0(postcss@8.4.32): + /postcss-minify-selectors@5.2.1(postcss@8.4.33): + resolution: {integrity: sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.33 + postcss-selector-parser: 6.0.13 + dev: true + + /postcss-modules-extract-imports@3.0.0(postcss@8.4.33): resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.32 + postcss: 8.4.33 dev: true - /postcss-modules-local-by-default@4.0.3(postcss@8.4.32): + /postcss-modules-local-by-default@4.0.3(postcss@8.4.33): resolution: {integrity: sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.32) - postcss: 8.4.32 + icss-utils: 5.1.0(postcss@8.4.33) + postcss: 8.4.33 postcss-selector-parser: 6.0.13 postcss-value-parser: 4.2.0 dev: true - /postcss-modules-scope@3.1.0(postcss@8.4.32): + /postcss-modules-scope@3.1.0(postcss@8.4.33): resolution: {integrity: sha512-SaIbK8XW+MZbd0xHPf7kdfA/3eOt7vxJ72IRecn3EzuZVLr1r0orzf0MX/pN8m+NMDoo6X/SQd8oeKqGZd8PXg==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.32 + postcss: 8.4.33 postcss-selector-parser: 6.0.13 dev: true - /postcss-modules-values@4.0.0(postcss@8.4.32): + /postcss-modules-values@4.0.0(postcss@8.4.33): resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.32) - postcss: 8.4.32 + icss-utils: 5.1.0(postcss@8.4.33) + postcss: 8.4.33 dev: true /postcss-normalize-charset@5.1.0(postcss@8.4.32): @@ -12245,6 +12489,15 @@ packages: postcss: 8.4.32 dev: true + /postcss-normalize-charset@5.1.0(postcss@8.4.33): + resolution: {integrity: sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.33 + dev: true + /postcss-normalize-display-values@5.1.0(postcss@8.4.32): resolution: {integrity: sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==} engines: {node: ^10 || ^12 || >=14.0} @@ -12255,6 +12508,16 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-normalize-display-values@5.1.0(postcss@8.4.33): + resolution: {integrity: sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.33 + postcss-value-parser: 4.2.0 + dev: true + /postcss-normalize-positions@5.1.1(postcss@8.4.32): resolution: {integrity: sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==} engines: {node: ^10 || ^12 || >=14.0} @@ -12265,6 +12528,16 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-normalize-positions@5.1.1(postcss@8.4.33): + resolution: {integrity: sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.33 + postcss-value-parser: 4.2.0 + dev: true + /postcss-normalize-repeat-style@5.1.1(postcss@8.4.32): resolution: {integrity: sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==} engines: {node: ^10 || ^12 || >=14.0} @@ -12275,6 +12548,16 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-normalize-repeat-style@5.1.1(postcss@8.4.33): + resolution: {integrity: sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.33 + postcss-value-parser: 4.2.0 + dev: true + /postcss-normalize-string@5.1.0(postcss@8.4.32): resolution: {integrity: sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==} engines: {node: ^10 || ^12 || >=14.0} @@ -12285,6 +12568,16 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-normalize-string@5.1.0(postcss@8.4.33): + resolution: {integrity: sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.33 + postcss-value-parser: 4.2.0 + dev: true + /postcss-normalize-timing-functions@5.1.0(postcss@8.4.32): resolution: {integrity: sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==} engines: {node: ^10 || ^12 || >=14.0} @@ -12295,6 +12588,16 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-normalize-timing-functions@5.1.0(postcss@8.4.33): + resolution: {integrity: sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.33 + postcss-value-parser: 4.2.0 + dev: true + /postcss-normalize-unicode@5.1.1(postcss@8.4.32): resolution: {integrity: sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==} engines: {node: ^10 || ^12 || >=14.0} @@ -12306,6 +12609,17 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-normalize-unicode@5.1.1(postcss@8.4.33): + resolution: {integrity: sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.22.2 + postcss: 8.4.33 + postcss-value-parser: 4.2.0 + dev: true + /postcss-normalize-url@5.1.0(postcss@8.4.32): resolution: {integrity: sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==} engines: {node: ^10 || ^12 || >=14.0} @@ -12317,6 +12631,17 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-normalize-url@5.1.0(postcss@8.4.33): + resolution: {integrity: sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + normalize-url: 6.1.0 + postcss: 8.4.33 + postcss-value-parser: 4.2.0 + dev: true + /postcss-normalize-whitespace@5.1.1(postcss@8.4.32): resolution: {integrity: sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==} engines: {node: ^10 || ^12 || >=14.0} @@ -12327,6 +12652,16 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-normalize-whitespace@5.1.1(postcss@8.4.33): + resolution: {integrity: sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.33 + postcss-value-parser: 4.2.0 + dev: true + /postcss-ordered-values@5.1.3(postcss@8.4.32): resolution: {integrity: sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==} engines: {node: ^10 || ^12 || >=14.0} @@ -12338,6 +12673,17 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-ordered-values@5.1.3(postcss@8.4.33): + resolution: {integrity: sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + cssnano-utils: 3.1.0(postcss@8.4.33) + postcss: 8.4.33 + postcss-value-parser: 4.2.0 + dev: true + /postcss-reduce-initial@5.1.2(postcss@8.4.32): resolution: {integrity: sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==} engines: {node: ^10 || ^12 || >=14.0} @@ -12349,6 +12695,17 @@ packages: postcss: 8.4.32 dev: true + /postcss-reduce-initial@5.1.2(postcss@8.4.33): + resolution: {integrity: sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.22.2 + caniuse-api: 3.0.0 + postcss: 8.4.33 + dev: true + /postcss-reduce-transforms@5.1.0(postcss@8.4.32): resolution: {integrity: sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==} engines: {node: ^10 || ^12 || >=14.0} @@ -12359,6 +12716,16 @@ packages: postcss-value-parser: 4.2.0 dev: true + /postcss-reduce-transforms@5.1.0(postcss@8.4.33): + resolution: {integrity: sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.33 + postcss-value-parser: 4.2.0 + dev: true + /postcss-selector-parser@6.0.13: resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==} engines: {node: '>=4'} @@ -12378,6 +12745,17 @@ packages: svgo: 2.8.0 dev: true + /postcss-svgo@5.1.0(postcss@8.4.33): + resolution: {integrity: sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.33 + postcss-value-parser: 4.2.0 + svgo: 2.8.0 + dev: true + /postcss-unique-selectors@5.1.1(postcss@8.4.32): resolution: {integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==} engines: {node: ^10 || ^12 || >=14.0} @@ -12388,6 +12766,16 @@ packages: postcss-selector-parser: 6.0.13 dev: true + /postcss-unique-selectors@5.1.1(postcss@8.4.33): + resolution: {integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.33 + postcss-selector-parser: 6.0.13 + dev: true + /postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} dev: true @@ -13761,6 +14149,17 @@ packages: postcss-selector-parser: 6.0.13 dev: true + /stylehacks@5.1.1(postcss@8.4.33): + resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.22.2 + postcss: 8.4.33 + postcss-selector-parser: 6.0.13 + dev: true + /sucrase@3.34.0: resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==} engines: {node: '>=8'} @@ -14061,7 +14460,7 @@ packages: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} dev: true - /tsup@8.0.1(postcss@8.4.32)(typescript@5.3.3): + /tsup@8.0.1(postcss@8.4.33)(typescript@5.3.3): resolution: {integrity: sha512-hvW7gUSG96j53ZTSlT4j/KL0q1Q2l6TqGBFc6/mu/L46IoNWqLLUzLRLP1R8Q7xrJTmkDxxDoojV5uCVs1sVOg==} engines: {node: '>=18'} hasBin: true @@ -14088,8 +14487,8 @@ packages: execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - postcss: 8.4.32 - postcss-load-config: 4.0.2(postcss@8.4.32) + postcss: 8.4.33 + postcss-load-config: 4.0.2(postcss@8.4.33) resolve-from: 5.0.0 rollup: 4.7.0 source-map: 0.8.0-beta.0 @@ -14410,7 +14809,7 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} - /unocss@0.58.3(postcss@8.4.32)(rollup@3.28.1)(vite@5.0.11): + /unocss@0.58.3(postcss@8.4.33)(rollup@3.28.1)(vite@5.0.11): resolution: {integrity: sha512-2rnvghfiIDRQ2cOrmN4P7J7xV2p3yBK+bPAt1aoUxCXcszkLczAnQzh9c7IZ+p70kSVstK45cJTYV6TMzOLF7Q==} engines: {node: '>=14'} peerDependencies: @@ -14426,7 +14825,7 @@ packages: '@unocss/cli': 0.58.3(rollup@3.28.1) '@unocss/core': 0.58.3 '@unocss/extractor-arbitrary-variants': 0.58.3 - '@unocss/postcss': 0.58.3(postcss@8.4.32) + '@unocss/postcss': 0.58.3(postcss@8.4.33) '@unocss/preset-attributify': 0.58.3 '@unocss/preset-icons': 0.58.3 '@unocss/preset-mini': 0.58.3 @@ -14690,7 +15089,7 @@ packages: vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 dependencies: birpc: 0.2.14 - vite: 5.0.11(@types/node@20.11.5)(sass@1.69.7) + vite: 5.0.11(@types/node@20.11.5) vite-hot-client: 0.2.3(vite@5.0.11) dev: false @@ -14731,7 +15130,7 @@ packages: mlly: 1.4.2 pathe: 1.1.2 picocolors: 1.0.0 - vite: 5.0.11(@types/node@20.11.5)(sass@1.69.7) + vite: 5.0.11(@types/node@20.11.5) transitivePeerDependencies: - '@types/node' - less @@ -14752,7 +15151,7 @@ packages: debug: 4.3.4 pathe: 1.1.2 picocolors: 1.0.0 - vite: 5.0.11(@types/node@20.11.5)(sass@1.69.7) + vite: 5.0.11(@types/node@20.11.5) transitivePeerDependencies: - '@types/node' - less @@ -14780,7 +15179,7 @@ packages: debug: 4.3.4 kolorist: 1.8.0 typescript: 5.3.3 - vite: 5.0.11(@types/node@20.11.5)(sass@1.69.7) + vite: 5.0.11(@types/node@20.11.5) vue-tsc: 1.8.27(typescript@5.3.3) transitivePeerDependencies: - '@types/node' @@ -14830,7 +15229,7 @@ packages: open: 9.1.0 picocolors: 1.0.0 sirv: 2.0.4 - vite: 5.0.11(@types/node@20.11.5)(sass@1.69.7) + vite: 5.0.11(@types/node@20.11.5) transitivePeerDependencies: - rollup - supports-color @@ -14897,7 +15296,7 @@ packages: dependencies: '@types/node': 18.19.8 esbuild: 0.15.18 - postcss: 8.4.32 + postcss: 8.4.33 resolve: 1.22.8 rollup: 2.79.1 optionalDependencies: @@ -14969,12 +15368,47 @@ packages: dependencies: '@types/node': 18.19.8 esbuild: 0.19.10 - postcss: 8.4.32 + postcss: 8.4.33 rollup: 4.7.0 optionalDependencies: fsevents: 2.3.3 dev: true + /vite@5.0.11(@types/node@20.11.5): + resolution: {integrity: sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.11.5 + esbuild: 0.19.10 + postcss: 8.4.33 + rollup: 4.7.0 + optionalDependencies: + fsevents: 2.3.3 + /vite@5.0.11(@types/node@20.11.5)(sass@1.69.7): resolution: {integrity: sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -15011,7 +15445,7 @@ packages: optionalDependencies: fsevents: 2.3.3 - /vitepress@1.0.0-rc.30(@algolia/client-search@4.22.0)(@types/node@20.11.5)(postcss@8.4.32)(search-insights@2.13.0)(typescript@5.3.3): + /vitepress@1.0.0-rc.30(@algolia/client-search@4.22.0)(@types/node@20.11.5)(postcss@8.4.33)(search-insights@2.13.0)(typescript@5.3.3): resolution: {integrity: sha512-OolAbFU2hjs0KuIpPq0wRd4vJlTMvrFgHSh/hB+XQid7U31KtB6F1NxWihMwKkwncpxu9mt2Somet5AGiyTgPA==} hasBin: true peerDependencies: @@ -15034,7 +15468,7 @@ packages: mark.js: 8.11.1 minisearch: 6.3.0 mrmime: 1.0.1 - postcss: 8.4.32 + postcss: 8.4.33 shikiji: 0.7.6 shikiji-transformers: 0.7.6 vite: 5.0.10(@types/node@20.11.5) @@ -15111,7 +15545,7 @@ packages: strip-literal: 1.3.0 tinybench: 2.5.1 tinypool: 0.8.1 - vite: 5.0.11(@types/node@20.11.5)(sass@1.69.7) + vite: 5.0.11(@types/node@20.11.5) vite-node: 1.2.0(@types/node@20.11.5) why-is-node-running: 2.2.2 transitivePeerDependencies: From 45c12af858ba3babd1194a5abd51b2aa9d5da3d4 Mon Sep 17 00:00:00 2001 From: arlo Date: Tue, 23 Jan 2024 21:39:17 +0800 Subject: [PATCH 02/18] feat: init hook --- package.json | 5 +- .../__tests__/fixtures/App.vue | 9 + .../devtools-kit-next/__tests__/hook.test.ts | 18 ++ packages/devtools-kit-next/src/core/index.ts | 7 + packages/devtools-kit-next/src/hook/index.ts | 125 +++++++++ packages/devtools-kit-next/src/hook/types.ts | 32 +++ pnpm-lock.yaml | 258 ++++++++++++++---- vitest.config.ts | 6 + 8 files changed, 408 insertions(+), 52 deletions(-) create mode 100644 packages/devtools-kit-next/__tests__/fixtures/App.vue create mode 100644 packages/devtools-kit-next/__tests__/hook.test.ts create mode 100644 packages/devtools-kit-next/src/hook/index.ts create mode 100644 packages/devtools-kit-next/src/hook/types.ts diff --git a/package.json b/package.json index 038714bc..0b9b5e9e 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "release:beta": "bumpp -r && nr build && pnpm -r publish --access public --tag beta", "dep:up": "taze -I major -r", "prepare": "simple-git-hooks", - "test": "vitest", + "test": "vitest --environment jsdom", "play": "nr -C packages/playground dev", "play:webpack": "nr -C packages/webpack-playground dev", "docs": "pnpm -C docs run docs:dev", @@ -73,7 +73,9 @@ "@types/node": "^20.11.5", "@unocss/eslint-plugin": "^0.58.3", "@vue/devtools-core": "workspace:^", + "@vue/devtools-kit-next": "workspace:^", "@vue/devtools-schema": "workspace:^", + "@vue/test-utils": "^2.4.3", "archiver": "^6.0.1", "bumpp": "^9.3.0", "cross-env": "^7.0.3", @@ -83,6 +85,7 @@ "eslint-ts-patch": "8.55.0-1", "fast-glob": "^3.3.2", "fs-extra": "^11.2.0", + "jsdom": "^24.0.0", "lint-staged": "^15.2.0", "npm-run-all": "^4.1.5", "pnpm": "^8.14.1", diff --git a/packages/devtools-kit-next/__tests__/fixtures/App.vue b/packages/devtools-kit-next/__tests__/fixtures/App.vue new file mode 100644 index 00000000..9df26813 --- /dev/null +++ b/packages/devtools-kit-next/__tests__/fixtures/App.vue @@ -0,0 +1,9 @@ + + + diff --git a/packages/devtools-kit-next/__tests__/hook.test.ts b/packages/devtools-kit-next/__tests__/hook.test.ts new file mode 100644 index 00000000..956b2c1d --- /dev/null +++ b/packages/devtools-kit-next/__tests__/hook.test.ts @@ -0,0 +1,18 @@ +import { devtools } from '@vue/devtools-kit-next' +import { mount } from '@vue/test-utils' +import { describe, it } from 'vitest' +import App from './fixtures/App.vue' + +describe('hook', () => { + it('should work', async () => { + devtools.init() + /** + * Work failed now, Wait for: + * https://github.com/vuejs/test-utils/issues/2310 + */ + const app = mount(App, { + attachTo: document.body, + }) + await new Promise(() => {}) + }) +}) diff --git a/packages/devtools-kit-next/src/core/index.ts b/packages/devtools-kit-next/src/core/index.ts index 8963ee4c..722b7589 100644 --- a/packages/devtools-kit-next/src/core/index.ts +++ b/packages/devtools-kit-next/src/core/index.ts @@ -1,3 +1,10 @@ +import { target } from '@vue/devtools-shared' +import { createDevToolsHook, hook } from '../hook' + export function initDevTools() { console.log('init') + target.__VUE_DEVTOOLS_GLOBAL_HOOK__ = createDevToolsHook() + hook.on.vueAppInit((app, version) => { + console.log('!@') + }) } diff --git a/packages/devtools-kit-next/src/hook/index.ts b/packages/devtools-kit-next/src/hook/index.ts new file mode 100644 index 00000000..4fd9312e --- /dev/null +++ b/packages/devtools-kit-next/src/hook/index.ts @@ -0,0 +1,125 @@ +import type { DevtoolsHook, PluginDescriptor, PluginSetupFunction, VueAppInstance } from '@vue/devtools-schema' +import { target } from '@vue/devtools-shared' +import type { HookKeys, Hookable } from 'hookable' +import { createHooks } from 'hookable' +import type { App } from 'vue' +import { DevToolsHooks } from './types' + +type HookAppInstance = App & VueAppInstance +interface DevToolsEvent { + [DevToolsHooks.APP_INIT]: (app: VueAppInstance['appContext']['app'], version: string) => void + [DevToolsHooks.APP_CONNECTED]: () => void + [DevToolsHooks.COMPONENT_ADDED]: (app: HookAppInstance, uid: number, parentUid: number, component: VueAppInstance) => void + [DevToolsHooks.COMPONENT_UPDATED]: DevToolsEvent['component:added'] + [DevToolsHooks.COMPONENT_REMOVED]: DevToolsEvent['component:added'] + [DevToolsHooks.SETUP_DEVTOOLS_PLUGIN]: (pluginDescriptor: PluginDescriptor, setupFn: PluginSetupFunction) => void +} + +export const devtoolsHooks: Hookable> = target.__VUE_DEVTOOLS_HOOK ??= createHooks() + +const on = { + vueAppInit(fn: DevToolsEvent[DevToolsHooks.APP_INIT]) { + devtoolsHooks.hook(DevToolsHooks.APP_INIT, fn) + }, + vueAppConnected(fn: DevToolsEvent[DevToolsHooks.APP_CONNECTED]) { + devtoolsHooks.hook(DevToolsHooks.APP_CONNECTED, fn) + }, + componentAdded(fn: DevToolsEvent[DevToolsHooks.COMPONENT_ADDED]) { + return devtoolsHooks.hook(DevToolsHooks.COMPONENT_ADDED, fn) + }, + componentUpdated(fn: DevToolsEvent[DevToolsHooks.COMPONENT_UPDATED]) { + return devtoolsHooks.hook(DevToolsHooks.COMPONENT_UPDATED, fn) + }, + componentRemoved(fn: DevToolsEvent[DevToolsHooks.COMPONENT_REMOVED]) { + return devtoolsHooks.hook(DevToolsHooks.COMPONENT_REMOVED, fn) + }, + setupDevtoolsPlugin(fn: DevToolsEvent[DevToolsHooks.SETUP_DEVTOOLS_PLUGIN]) { + devtoolsHooks.hook(DevToolsHooks.SETUP_DEVTOOLS_PLUGIN, fn) + }, +} + +export function createDevToolsHook(): DevtoolsHook { + return { + id: 'vue-devtools-next', + enabled: false, + appRecords: [], + apps: {}, + events: new Map(), + on(event, fn) { + if (!this.events.has(event)) + this.events.set(event, []) + + this.events.get(event)?.push(fn) + // cleanup function + return () => this.off(event, fn) + }, + once(event, fn) { + const onceFn = (...args) => { + this.off(event, onceFn) + fn(...args) + } + + this.on(event, onceFn) + return [event, onceFn] as const + }, + off(event, fn) { + if (this.events.has(event)) { + const eventCallbacks = this.events.get(event)! + const index = eventCallbacks.indexOf(fn) + if (index !== -1) + eventCallbacks.splice(index, 1) + } + }, + emit(event, ...payload) { + if (this.events.has(event)) + this.events.get(event)!.forEach(fn => fn(...payload)) + }, + } +} + +export function subscribeDevToolsHook() { + const hook = target.__VUE_DEVTOOLS_GLOBAL_HOOK__ + // app init hook + hook.on(DevToolsHooks.APP_INIT, (app: VueAppInstance['appContext']['app'], version: string) => { + if (app?._instance?.type?.devtools?.hide) + return + + devtoolsHooks.callHook(DevToolsHooks.APP_INIT, app, version) + }) + + // component added hook + hook.on(DevToolsHooks.COMPONENT_ADDED, async (app: HookAppInstance, uid: number, parentUid: number, component: VueAppInstance) => { + if (app?._instance?.type?.devtools?.hide) + return + + if (!app || (typeof uid !== 'number' && !uid) || !component) + return + + devtoolsHooks.callHook(DevToolsHooks.COMPONENT_ADDED, app, uid, parentUid, component) + }) + + // component updated hook + hook.on(DevToolsHooks.COMPONENT_UPDATED, (app: HookAppInstance, uid: number, parentUid: number, component: VueAppInstance) => { + if (!app || (typeof uid !== 'number' && !uid) || !component) + return + + devtoolsHooks.callHook(DevToolsHooks.COMPONENT_UPDATED, app, uid, parentUid, component) + }) + + // component removed hook + hook.on(DevToolsHooks.COMPONENT_REMOVED, async (app: HookAppInstance, uid: number, parentUid: number, component: VueAppInstance) => { + if (!app || (typeof uid !== 'number' && !uid) || !component) + return + + devtoolsHooks.callHook(DevToolsHooks.COMPONENT_REMOVED, app, uid, parentUid, component) + }) + + // devtools plugin setup + hook.on(DevToolsHooks.SETUP_DEVTOOLS_PLUGIN, (pluginDescriptor: PluginDescriptor, setupFn: PluginSetupFunction) => { + devtoolsHooks.callHook(DevToolsHooks.SETUP_DEVTOOLS_PLUGIN, pluginDescriptor, setupFn) + }) +} + +export const hook = { + on, +} diff --git a/packages/devtools-kit-next/src/hook/types.ts b/packages/devtools-kit-next/src/hook/types.ts new file mode 100644 index 00000000..f10b6a3c --- /dev/null +++ b/packages/devtools-kit-next/src/hook/types.ts @@ -0,0 +1,32 @@ +import type { AppRecord } from '@vue/devtools-schema' + +export enum DevToolsHooks { + // internal + APP_INIT = 'app:init', + APP_UNMOUNT = 'app:unmount', + COMPONENT_UPDATED = 'component:updated', + COMPONENT_ADDED = 'component:added', + COMPONENT_REMOVED = 'component:removed', + COMPONENT_EMIT = 'component:emit', + PERFORMANCE_START = 'perf:start', + PERFORMANCE_END = 'perf:end', + ADD_ROUTE = 'router:add-route', + REMOVE_ROUTE = 'router:remove-route', + RENDER_TRACKED = 'render:tracked', + RENDER_TRIGGERED = 'render:triggered', + APP_CONNECTED = 'app:connected', + SETUP_DEVTOOLS_PLUGIN = 'devtools-plugin:setup', +} + +export interface DevtoolsHook { + id: string + enabled?: boolean + events: Map + emit: (event: DevToolsHooks, ...payload: any[]) => void + on: (event: DevToolsHooks, handler: Function) => () => void + once: (event: DevToolsHooks, handler: Function) => void + off: (event: DevToolsHooks, handler: Function) => void + appRecords: AppRecord[] + apps: Record + cleanupBuffer?: (matchArg: unknown) => boolean +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b0d4ab30..c0eb767f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,9 +32,15 @@ importers: '@vue/devtools-core': specifier: workspace:^ version: link:packages/core + '@vue/devtools-kit-next': + specifier: workspace:^ + version: link:packages/devtools-kit-next '@vue/devtools-schema': specifier: workspace:^ version: link:packages/schema + '@vue/test-utils': + specifier: ^2.4.3 + version: 2.4.3(vue@3.4.14) archiver: specifier: ^6.0.1 version: 6.0.1 @@ -62,6 +68,9 @@ importers: fs-extra: specifier: ^11.2.0 version: 11.2.0 + jsdom: + specifier: ^24.0.0 + version: 24.0.0 lint-staged: specifier: ^15.2.0 version: 15.2.0 @@ -103,10 +112,10 @@ importers: version: 0.58.3(postcss@8.4.33)(rollup@3.28.1)(vite@5.0.11) vite: specifier: ^5.0.11 - version: 5.0.11(@types/node@20.11.5) + version: 5.0.11(@types/node@20.11.5)(sass@1.69.7) vitest: specifier: ^1.2.0 - version: 1.2.0(@types/node@20.11.5) + version: 1.2.0(@types/node@20.11.5)(jsdom@24.0.0) vue: specifier: ^3.4.14 version: 3.4.14(typescript@5.3.3) @@ -636,7 +645,7 @@ importers: version: 5.3.3 vite: specifier: ^5.0.11 - version: 5.0.11(@types/node@20.11.5) + version: 5.0.11(@types/node@20.11.5)(sass@1.69.7) vue-tsc: specifier: ^1.8.27 version: 1.8.27(typescript@5.3.3) @@ -3131,7 +3140,7 @@ packages: chokidar: 3.5.3 pathe: 1.1.2 picocolors: 1.0.0 - vite: 5.0.11(@types/node@20.11.5) + vite: 5.0.11(@types/node@20.11.5)(sass@1.69.7) dev: true /@histoire/vendors@0.17.8: @@ -3517,6 +3526,10 @@ packages: - supports-color dev: true + /@one-ini/wasm@0.1.1: + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + dev: true + /@pkgjs/parseargs@0.11.0: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -4693,7 +4706,7 @@ packages: vite: ^5.0.0 vue: ^3.2.25 dependencies: - vite: 5.0.11(@types/node@20.11.5) + vite: 5.0.11(@types/node@20.11.5)(sass@1.69.7) vue: 3.4.0(typescript@5.3.3) dev: true @@ -4704,7 +4717,7 @@ packages: vite: ^5.0.0 vue: ^3.2.25 dependencies: - vite: 5.0.11(@types/node@20.11.5) + vite: 5.0.11(@types/node@20.11.5)(sass@1.69.7) vue: 3.4.14(typescript@5.3.3) dev: true @@ -5437,6 +5450,20 @@ packages: /@vue/shared@3.4.14: resolution: {integrity: sha512-nmi3BtLpvqXAWoRZ6HQ+pFJOHBU4UnH3vD3opgmwXac7vhaHKA9nj1VeGjMggdB9eLtW83eHyPCmOU1qzdsC7Q==} + /@vue/test-utils@2.4.3(vue@3.4.14): + resolution: {integrity: sha512-F4K7mF+ad++VlTrxMJVRnenKSJmO6fkQt2wpRDiKDesQMkfpniGWsqEi/JevxGBo2qEkwwjvTUAoiGJLNx++CA==} + peerDependencies: + '@vue/server-renderer': ^3.0.1 + vue: ^3.0.1 + peerDependenciesMeta: + '@vue/server-renderer': + optional: true + dependencies: + js-beautify: 1.14.11 + vue: 3.4.14(typescript@5.3.3) + vue-component-type-helpers: 1.8.27 + dev: true + /@vue/web-component-wrapper@1.3.0: resolution: {integrity: sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==} dev: true @@ -6841,6 +6868,11 @@ packages: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} dev: true + /commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + dev: true + /commander@11.1.0: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} @@ -6925,6 +6957,13 @@ packages: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true + /config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + dev: true + /connect-history-api-fallback@2.0.0: resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} engines: {node: '>=0.8'} @@ -7502,6 +7541,13 @@ packages: cssom: 0.3.8 dev: true + /cssstyle@4.0.1: + resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==} + engines: {node: '>=18'} + dependencies: + rrweb-cssom: 0.6.0 + dev: true + /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -7514,6 +7560,14 @@ packages: whatwg-url: 11.0.0 dev: true + /data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.0.0 + dev: true + /dayjs@1.11.10: resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==} dev: true @@ -7817,6 +7871,17 @@ packages: engines: {node: '>=6.0.0'} dev: true + /editorconfig@1.0.4: + resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==} + engines: {node: '>=14'} + hasBin: true + dependencies: + '@one-ini/wasm': 0.1.1 + commander: 10.0.1 + minimatch: 9.0.1 + semver: 7.5.4 + dev: true + /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: true @@ -8620,7 +8685,7 @@ packages: '@typescript-eslint/eslint-plugin': 6.13.2(@typescript-eslint/parser@6.13.2)(eslint-ts-patch@8.55.0-1)(typescript@5.3.3) '@typescript-eslint/utils': 6.16.0(eslint-ts-patch@8.55.0-1)(typescript@5.3.3) eslint: /eslint-ts-patch@8.55.0-1 - vitest: 1.2.0(@types/node@20.11.5) + vitest: 1.2.0(@types/node@20.11.5)(jsdom@24.0.0) transitivePeerDependencies: - supports-color - typescript @@ -9775,7 +9840,7 @@ packages: sade: 1.8.1 shiki-es: 0.2.0 sirv: 2.0.4 - vite: 5.0.11(@types/node@20.11.5) + vite: 5.0.11(@types/node@20.11.5)(sass@1.69.7) vite-node: 0.34.6(@types/node@20.11.5) transitivePeerDependencies: - '@types/node' @@ -9821,6 +9886,13 @@ packages: whatwg-encoding: 2.0.0 dev: true + /html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + dependencies: + whatwg-encoding: 3.1.1 + dev: true + /html-entities@2.4.0: resolution: {integrity: sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==} dev: true @@ -10483,6 +10555,17 @@ packages: engines: {node: '>=10'} dev: true + /js-beautify@1.14.11: + resolution: {integrity: sha512-rPogWqAfoYh1Ryqqh2agUpVfbxAhbjuN1SmU86dskQUKouRiggUTCO4+2ym9UPXllc2WAp0J+T5qxn7Um3lCdw==} + engines: {node: '>=14'} + hasBin: true + dependencies: + config-chain: 1.1.13 + editorconfig: 1.0.4 + glob: 10.3.10 + nopt: 7.2.0 + dev: true + /js-message@1.0.7: resolution: {integrity: sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==} engines: {node: '>=0.6.0'} @@ -10552,6 +10635,42 @@ packages: - utf-8-validate dev: true + /jsdom@24.0.0: + resolution: {integrity: sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^2.11.2 + peerDependenciesMeta: + canvas: + optional: true + dependencies: + cssstyle: 4.0.1 + data-urls: 5.0.0 + decimal.js: 10.4.3 + form-data: 4.0.0 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.0 + https-proxy-agent: 7.0.2 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.7 + parse5: 7.1.2 + rrweb-cssom: 0.6.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 4.1.3 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.0.0 + ws: 8.16.0 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + /jsesc@0.5.0: resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} hasBin: true @@ -11222,6 +11341,13 @@ packages: brace-expansion: 2.0.1 dev: true + /minimatch@9.0.1: + resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minimatch@9.0.3: resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} engines: {node: '>=16 || 14 >=14.17'} @@ -12909,6 +13035,10 @@ packages: resolution: {integrity: sha512-9t5qARVofg2xQqKtytzt+lZ4d1Qvj8t5B8fEwXK6qOfgRLgH/b13QlgEyDh033NOS31nXeFbYv7CLUDG1CeifQ==} dev: true + /proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + dev: true + /proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -13352,6 +13482,10 @@ packages: '@rollup/rollup-win32-x64-msvc': 4.7.0 fsevents: 2.3.3 + /rrweb-cssom@0.6.0: + resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} + dev: true + /run-applescript@5.0.0: resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==} engines: {node: '>=12'} @@ -14434,6 +14568,13 @@ packages: punycode: 2.3.1 dev: true + /tr46@5.0.0: + resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} + engines: {node: '>=18'} + dependencies: + punycode: 2.3.1 + dev: true + /tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -15089,7 +15230,7 @@ packages: vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 dependencies: birpc: 0.2.14 - vite: 5.0.11(@types/node@20.11.5) + vite: 5.0.11(@types/node@20.11.5)(sass@1.69.7) vite-hot-client: 0.2.3(vite@5.0.11) dev: false @@ -15130,7 +15271,7 @@ packages: mlly: 1.4.2 pathe: 1.1.2 picocolors: 1.0.0 - vite: 5.0.11(@types/node@20.11.5) + vite: 5.0.11(@types/node@20.11.5)(sass@1.69.7) transitivePeerDependencies: - '@types/node' - less @@ -15151,7 +15292,7 @@ packages: debug: 4.3.4 pathe: 1.1.2 picocolors: 1.0.0 - vite: 5.0.11(@types/node@20.11.5) + vite: 5.0.11(@types/node@20.11.5)(sass@1.69.7) transitivePeerDependencies: - '@types/node' - less @@ -15179,7 +15320,7 @@ packages: debug: 4.3.4 kolorist: 1.8.0 typescript: 5.3.3 - vite: 5.0.11(@types/node@20.11.5) + vite: 5.0.11(@types/node@20.11.5)(sass@1.69.7) vue-tsc: 1.8.27(typescript@5.3.3) transitivePeerDependencies: - '@types/node' @@ -15229,7 +15370,7 @@ packages: open: 9.1.0 picocolors: 1.0.0 sirv: 2.0.4 - vite: 5.0.11(@types/node@20.11.5) + vite: 5.0.11(@types/node@20.11.5)(sass@1.69.7) transitivePeerDependencies: - rollup - supports-color @@ -15374,41 +15515,6 @@ packages: fsevents: 2.3.3 dev: true - /vite@5.0.11(@types/node@20.11.5): - resolution: {integrity: sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - dependencies: - '@types/node': 20.11.5 - esbuild: 0.19.10 - postcss: 8.4.33 - rollup: 4.7.0 - optionalDependencies: - fsevents: 2.3.3 - /vite@5.0.11(@types/node@20.11.5)(sass@1.69.7): resolution: {integrity: sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -15439,7 +15545,7 @@ packages: dependencies: '@types/node': 20.11.5 esbuild: 0.19.10 - postcss: 8.4.32 + postcss: 8.4.33 rollup: 4.7.0 sass: 1.69.7 optionalDependencies: @@ -15501,7 +15607,7 @@ packages: - universal-cookie dev: true - /vitest@1.2.0(@types/node@20.11.5): + /vitest@1.2.0(@types/node@20.11.5)(jsdom@24.0.0): resolution: {integrity: sha512-Ixs5m7BjqvLHXcibkzKRQUvD/XLw0E3rvqaCMlrm/0LMsA0309ZqYvTlPzkhh81VlEyVZXFlwWnkhb6/UMtcaQ==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -15537,6 +15643,7 @@ packages: chai: 4.3.10 debug: 4.3.4 execa: 8.0.1 + jsdom: 24.0.0 local-pkg: 0.5.0 magic-string: 0.30.5 pathe: 1.1.2 @@ -15545,7 +15652,7 @@ packages: strip-literal: 1.3.0 tinybench: 2.5.1 tinypool: 0.8.1 - vite: 5.0.11(@types/node@20.11.5) + vite: 5.0.11(@types/node@20.11.5)(sass@1.69.7) vite-node: 1.2.0(@types/node@20.11.5) why-is-node-running: 2.2.2 transitivePeerDependencies: @@ -15558,6 +15665,10 @@ packages: - terser dev: true + /vue-component-type-helpers@1.8.27: + resolution: {integrity: sha512-0vOfAtI67UjeO1G6UiX5Kd76CqaQ67wrRZiOe7UAb9Jm6GzlUr/fC7CV90XfwapJRjpCMaZFhv1V0ajWRmE9Dg==} + dev: true + /vue-demi@0.14.6(vue@3.4.0): resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==} engines: {node: '>=12'} @@ -15905,6 +16016,13 @@ packages: xml-name-validator: 4.0.0 dev: true + /w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + dependencies: + xml-name-validator: 5.0.0 + dev: true + /walk-up-path@3.0.1: resolution: {integrity: sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==} dev: true @@ -16126,6 +16244,13 @@ packages: iconv-lite: 0.6.3 dev: true + /whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + dependencies: + iconv-lite: 0.6.3 + dev: true + /whatwg-fetch@3.6.20: resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} dev: true @@ -16135,6 +16260,11 @@ packages: engines: {node: '>=12'} dev: true + /whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + dev: true + /whatwg-url@11.0.0: resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} engines: {node: '>=12'} @@ -16143,6 +16273,14 @@ packages: webidl-conversions: 7.0.0 dev: true + /whatwg-url@14.0.0: + resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==} + engines: {node: '>=18'} + dependencies: + tr46: 5.0.0 + webidl-conversions: 7.0.0 + dev: true + /whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} dependencies: @@ -16295,11 +16433,29 @@ packages: utf-8-validate: optional: true + /ws@8.16.0: + resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: true + /xml-name-validator@4.0.0: resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} engines: {node: '>=12'} dev: true + /xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + dev: true + /xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} dev: true diff --git a/vitest.config.ts b/vitest.config.ts index 47cdb031..c881b9fb 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,6 +1,12 @@ import { defineConfig } from 'vitest/config' +import Vue from '@vitejs/plugin-vue' export default defineConfig({ + plugins: [Vue()], + define: { + __DEV__: true, + __FEATURE_PROD_DEVTOOLS__: true, + }, test: { globals: true, }, From 9dfb923acfd84cc070857fe89fd83961ed9412be Mon Sep 17 00:00:00 2001 From: arlo Date: Fri, 26 Jan 2024 12:54:17 +0800 Subject: [PATCH 03/18] chore: update --- package.json | 2 +- packages/devtools-kit-next/__tests__/hook.test.ts | 1 + packages/devtools-kit-next/src/core/index.ts | 4 ++-- pnpm-lock.yaml | 8 ++++---- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 0b9b5e9e..b2554a28 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "@vue/devtools-core": "workspace:^", "@vue/devtools-kit-next": "workspace:^", "@vue/devtools-schema": "workspace:^", - "@vue/test-utils": "^2.4.3", + "@vue/test-utils": "^2.4.4", "archiver": "^6.0.1", "bumpp": "^9.3.0", "cross-env": "^7.0.3", diff --git a/packages/devtools-kit-next/__tests__/hook.test.ts b/packages/devtools-kit-next/__tests__/hook.test.ts index 956b2c1d..54cea844 100644 --- a/packages/devtools-kit-next/__tests__/hook.test.ts +++ b/packages/devtools-kit-next/__tests__/hook.test.ts @@ -13,6 +13,7 @@ describe('hook', () => { const app = mount(App, { attachTo: document.body, }) + await new Promise(() => {}) }) }) diff --git a/packages/devtools-kit-next/src/core/index.ts b/packages/devtools-kit-next/src/core/index.ts index 722b7589..3baf1d78 100644 --- a/packages/devtools-kit-next/src/core/index.ts +++ b/packages/devtools-kit-next/src/core/index.ts @@ -1,10 +1,10 @@ import { target } from '@vue/devtools-shared' -import { createDevToolsHook, hook } from '../hook' +import { createDevToolsHook, hook, subscribeDevToolsHook } from '../hook' export function initDevTools() { - console.log('init') target.__VUE_DEVTOOLS_GLOBAL_HOOK__ = createDevToolsHook() hook.on.vueAppInit((app, version) => { console.log('!@') }) + subscribeDevToolsHook() } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c0eb767f..19bff071 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -39,8 +39,8 @@ importers: specifier: workspace:^ version: link:packages/schema '@vue/test-utils': - specifier: ^2.4.3 - version: 2.4.3(vue@3.4.14) + specifier: ^2.4.4 + version: 2.4.4(vue@3.4.14) archiver: specifier: ^6.0.1 version: 6.0.1 @@ -5450,8 +5450,8 @@ packages: /@vue/shared@3.4.14: resolution: {integrity: sha512-nmi3BtLpvqXAWoRZ6HQ+pFJOHBU4UnH3vD3opgmwXac7vhaHKA9nj1VeGjMggdB9eLtW83eHyPCmOU1qzdsC7Q==} - /@vue/test-utils@2.4.3(vue@3.4.14): - resolution: {integrity: sha512-F4K7mF+ad++VlTrxMJVRnenKSJmO6fkQt2wpRDiKDesQMkfpniGWsqEi/JevxGBo2qEkwwjvTUAoiGJLNx++CA==} + /@vue/test-utils@2.4.4(vue@3.4.14): + resolution: {integrity: sha512-8jkRxz8pNhClAf4Co4ZrpAoFISdvT3nuSkUlY6Ys6rmTpw3DMWG/X3mw3gQ7QJzgCZO9f+zuE2kW57fi09MW7Q==} peerDependencies: '@vue/server-renderer': ^3.0.1 vue: ^3.0.1 From fbad334e3db0f1282dd080a455c12303a5d9fe56 Mon Sep 17 00:00:00 2001 From: arlo Date: Sat, 27 Jan 2024 20:56:19 +0800 Subject: [PATCH 04/18] chore: update --- .../devtools-kit-next/__tests__/hook.test.ts | 24 ++++++++++--------- packages/devtools-kit-next/src/core/index.ts | 3 --- packages/devtools-kit-next/src/hook/index.ts | 4 +++- packages/devtools-kit-next/src/index.ts | 5 ++-- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/packages/devtools-kit-next/__tests__/hook.test.ts b/packages/devtools-kit-next/__tests__/hook.test.ts index 54cea844..cd8771b9 100644 --- a/packages/devtools-kit-next/__tests__/hook.test.ts +++ b/packages/devtools-kit-next/__tests__/hook.test.ts @@ -1,19 +1,21 @@ import { devtools } from '@vue/devtools-kit-next' import { mount } from '@vue/test-utils' -import { describe, it } from 'vitest' +import { describe, expect, it } from 'vitest' import App from './fixtures/App.vue' describe('hook', () => { - it('should work', async () => { - devtools.init() - /** - * Work failed now, Wait for: - * https://github.com/vuejs/test-utils/issues/2310 - */ - const app = mount(App, { - attachTo: document.body, - }) + it('should work w/ app init hook', async () => { + await new Promise((resolve) => { + devtools.init() - await new Promise(() => {}) + devtools.hook.on.vueAppInit((app, version) => { + expect(app).toBeTypeOf('object') + expect(version).toBeTypeOf('string') + resolve() + }) + mount(App, { + attachTo: document.body, + }) + }) }) }) diff --git a/packages/devtools-kit-next/src/core/index.ts b/packages/devtools-kit-next/src/core/index.ts index 3baf1d78..b38ff3a9 100644 --- a/packages/devtools-kit-next/src/core/index.ts +++ b/packages/devtools-kit-next/src/core/index.ts @@ -3,8 +3,5 @@ import { createDevToolsHook, hook, subscribeDevToolsHook } from '../hook' export function initDevTools() { target.__VUE_DEVTOOLS_GLOBAL_HOOK__ = createDevToolsHook() - hook.on.vueAppInit((app, version) => { - console.log('!@') - }) subscribeDevToolsHook() } diff --git a/packages/devtools-kit-next/src/hook/index.ts b/packages/devtools-kit-next/src/hook/index.ts index 4fd9312e..b849e689 100644 --- a/packages/devtools-kit-next/src/hook/index.ts +++ b/packages/devtools-kit-next/src/hook/index.ts @@ -5,6 +5,8 @@ import { createHooks } from 'hookable' import type { App } from 'vue' import { DevToolsHooks } from './types' +export { DevToolsHooks } from './types' + type HookAppInstance = App & VueAppInstance interface DevToolsEvent { [DevToolsHooks.APP_INIT]: (app: VueAppInstance['appContext']['app'], version: string) => void @@ -122,4 +124,4 @@ export function subscribeDevToolsHook() { export const hook = { on, -} +} as const diff --git a/packages/devtools-kit-next/src/index.ts b/packages/devtools-kit-next/src/index.ts index 325f6aae..06d06f11 100644 --- a/packages/devtools-kit-next/src/index.ts +++ b/packages/devtools-kit-next/src/index.ts @@ -1,9 +1,10 @@ import { initDevTools } from './core/index' +import { hook } from './hook' export const devtools = { - hook: {}, + hook, init: initDevTools, get api() { return {} }, -} +} as const From 87ed9cbe9b5012461e548990a7ce88b88c647ca1 Mon Sep 17 00:00:00 2001 From: arlo Date: Sat, 27 Jan 2024 21:32:33 +0800 Subject: [PATCH 05/18] chore: improve hook type --- packages/devtools-kit-next/package.json | 1 + packages/devtools-kit-next/src/core/index.ts | 2 +- packages/devtools-kit-next/src/hook/index.ts | 50 ++++++++------------ packages/devtools-kit-next/src/hook/types.ts | 32 +++++++++++-- packages/devtools-kit-next/src/index.ts | 2 +- 5 files changed, 49 insertions(+), 38 deletions(-) diff --git a/packages/devtools-kit-next/package.json b/packages/devtools-kit-next/package.json index 376f334f..e560739f 100644 --- a/packages/devtools-kit-next/package.json +++ b/packages/devtools-kit-next/package.json @@ -7,6 +7,7 @@ "license": "MIT", "exports": { ".": { + "types": "./dist/index.d.ts", "import": "./dist/index.mjs", "require": "./dist/index.cjs" } diff --git a/packages/devtools-kit-next/src/core/index.ts b/packages/devtools-kit-next/src/core/index.ts index b38ff3a9..f913b1ae 100644 --- a/packages/devtools-kit-next/src/core/index.ts +++ b/packages/devtools-kit-next/src/core/index.ts @@ -1,5 +1,5 @@ import { target } from '@vue/devtools-shared' -import { createDevToolsHook, hook, subscribeDevToolsHook } from '../hook' +import { createDevToolsHook, subscribeDevToolsHook } from '../hook' export function initDevTools() { target.__VUE_DEVTOOLS_GLOBAL_HOOK__ = createDevToolsHook() diff --git a/packages/devtools-kit-next/src/hook/index.ts b/packages/devtools-kit-next/src/hook/index.ts index b849e689..803efbd7 100644 --- a/packages/devtools-kit-next/src/hook/index.ts +++ b/packages/devtools-kit-next/src/hook/index.ts @@ -1,46 +1,34 @@ -import type { DevtoolsHook, PluginDescriptor, PluginSetupFunction, VueAppInstance } from '@vue/devtools-schema' import { target } from '@vue/devtools-shared' import type { HookKeys, Hookable } from 'hookable' import { createHooks } from 'hookable' -import type { App } from 'vue' -import { DevToolsHooks } from './types' - -export { DevToolsHooks } from './types' - -type HookAppInstance = App & VueAppInstance -interface DevToolsEvent { - [DevToolsHooks.APP_INIT]: (app: VueAppInstance['appContext']['app'], version: string) => void - [DevToolsHooks.APP_CONNECTED]: () => void - [DevToolsHooks.COMPONENT_ADDED]: (app: HookAppInstance, uid: number, parentUid: number, component: VueAppInstance) => void - [DevToolsHooks.COMPONENT_UPDATED]: DevToolsEvent['component:added'] - [DevToolsHooks.COMPONENT_REMOVED]: DevToolsEvent['component:added'] - [DevToolsHooks.SETUP_DEVTOOLS_PLUGIN]: (pluginDescriptor: PluginDescriptor, setupFn: PluginSetupFunction) => void -} +import { DevToolsEvent, DevToolsHook, DevToolsHooks, VueHooks } from './types' + +export { VueHooks } from './types' export const devtoolsHooks: Hookable> = target.__VUE_DEVTOOLS_HOOK ??= createHooks() -const on = { - vueAppInit(fn: DevToolsEvent[DevToolsHooks.APP_INIT]) { +const on: VueHooks['on'] = { + vueAppInit(fn) { devtoolsHooks.hook(DevToolsHooks.APP_INIT, fn) }, - vueAppConnected(fn: DevToolsEvent[DevToolsHooks.APP_CONNECTED]) { + vueAppConnected(fn) { devtoolsHooks.hook(DevToolsHooks.APP_CONNECTED, fn) }, - componentAdded(fn: DevToolsEvent[DevToolsHooks.COMPONENT_ADDED]) { + componentAdded(fn) { return devtoolsHooks.hook(DevToolsHooks.COMPONENT_ADDED, fn) }, - componentUpdated(fn: DevToolsEvent[DevToolsHooks.COMPONENT_UPDATED]) { + componentUpdated(fn) { return devtoolsHooks.hook(DevToolsHooks.COMPONENT_UPDATED, fn) }, - componentRemoved(fn: DevToolsEvent[DevToolsHooks.COMPONENT_REMOVED]) { + componentRemoved(fn) { return devtoolsHooks.hook(DevToolsHooks.COMPONENT_REMOVED, fn) }, - setupDevtoolsPlugin(fn: DevToolsEvent[DevToolsHooks.SETUP_DEVTOOLS_PLUGIN]) { + setupDevtoolsPlugin(fn) { devtoolsHooks.hook(DevToolsHooks.SETUP_DEVTOOLS_PLUGIN, fn) }, } -export function createDevToolsHook(): DevtoolsHook { +export function createDevToolsHook(): DevToolsHook { return { id: 'vue-devtools-next', enabled: false, @@ -80,9 +68,9 @@ export function createDevToolsHook(): DevtoolsHook { } export function subscribeDevToolsHook() { - const hook = target.__VUE_DEVTOOLS_GLOBAL_HOOK__ + const hook = target.__VUE_DEVTOOLS_GLOBAL_HOOK__ as DevToolsHook // app init hook - hook.on(DevToolsHooks.APP_INIT, (app: VueAppInstance['appContext']['app'], version: string) => { + hook.on(DevToolsHooks.APP_INIT, (app, version) => { if (app?._instance?.type?.devtools?.hide) return @@ -90,7 +78,7 @@ export function subscribeDevToolsHook() { }) // component added hook - hook.on(DevToolsHooks.COMPONENT_ADDED, async (app: HookAppInstance, uid: number, parentUid: number, component: VueAppInstance) => { + hook.on(DevToolsHooks.COMPONENT_ADDED, async (app, uid, parentUid, component) => { if (app?._instance?.type?.devtools?.hide) return @@ -101,7 +89,7 @@ export function subscribeDevToolsHook() { }) // component updated hook - hook.on(DevToolsHooks.COMPONENT_UPDATED, (app: HookAppInstance, uid: number, parentUid: number, component: VueAppInstance) => { + hook.on(DevToolsHooks.COMPONENT_UPDATED, (app, uid, parentUid, component) => { if (!app || (typeof uid !== 'number' && !uid) || !component) return @@ -109,7 +97,7 @@ export function subscribeDevToolsHook() { }) // component removed hook - hook.on(DevToolsHooks.COMPONENT_REMOVED, async (app: HookAppInstance, uid: number, parentUid: number, component: VueAppInstance) => { + hook.on(DevToolsHooks.COMPONENT_REMOVED, async (app, uid, parentUid, component) => { if (!app || (typeof uid !== 'number' && !uid) || !component) return @@ -117,11 +105,11 @@ export function subscribeDevToolsHook() { }) // devtools plugin setup - hook.on(DevToolsHooks.SETUP_DEVTOOLS_PLUGIN, (pluginDescriptor: PluginDescriptor, setupFn: PluginSetupFunction) => { + hook.on(DevToolsHooks.SETUP_DEVTOOLS_PLUGIN, (pluginDescriptor, setupFn) => { devtoolsHooks.callHook(DevToolsHooks.SETUP_DEVTOOLS_PLUGIN, pluginDescriptor, setupFn) }) } -export const hook = { +export const hook: VueHooks = { on, -} as const +} diff --git a/packages/devtools-kit-next/src/hook/types.ts b/packages/devtools-kit-next/src/hook/types.ts index f10b6a3c..9a9a997a 100644 --- a/packages/devtools-kit-next/src/hook/types.ts +++ b/packages/devtools-kit-next/src/hook/types.ts @@ -1,5 +1,7 @@ -import type { AppRecord } from '@vue/devtools-schema' +import type { AppRecord, PluginDescriptor, PluginSetupFunction, VueAppInstance } from '@vue/devtools-schema' +import type { App } from 'vue' +type HookAppInstance = App & VueAppInstance export enum DevToolsHooks { // internal APP_INIT = 'app:init', @@ -18,15 +20,35 @@ export enum DevToolsHooks { SETUP_DEVTOOLS_PLUGIN = 'devtools-plugin:setup', } -export interface DevtoolsHook { +export interface DevToolsEvent { + [DevToolsHooks.APP_INIT]: (app: VueAppInstance['appContext']['app'], version: string) => void + [DevToolsHooks.APP_CONNECTED]: () => void + [DevToolsHooks.COMPONENT_ADDED]: (app: HookAppInstance, uid: number, parentUid: number, component: VueAppInstance) => void + [DevToolsHooks.COMPONENT_UPDATED]: DevToolsEvent['component:added'] + [DevToolsHooks.COMPONENT_REMOVED]: DevToolsEvent['component:added'] + [DevToolsHooks.SETUP_DEVTOOLS_PLUGIN]: (pluginDescriptor: PluginDescriptor, setupFn: PluginSetupFunction) => void +} + +export interface DevToolsHook { id: string enabled?: boolean events: Map emit: (event: DevToolsHooks, ...payload: any[]) => void - on: (event: DevToolsHooks, handler: Function) => () => void - once: (event: DevToolsHooks, handler: Function) => void - off: (event: DevToolsHooks, handler: Function) => void + on: (event: DevToolsHooks, handler: T) => () => void + once: (event: DevToolsHooks, handler: T) => void + off: (event: DevToolsHooks, handler: T) => void appRecords: AppRecord[] apps: Record cleanupBuffer?: (matchArg: unknown) => boolean } + +export interface VueHooks { + on: { + vueAppInit(fn: DevToolsEvent[DevToolsHooks.APP_INIT]): void + vueAppConnected(fn: DevToolsEvent[DevToolsHooks.APP_CONNECTED]): void + componentAdded(fn: DevToolsEvent[DevToolsHooks.COMPONENT_ADDED]): () => void + componentUpdated(fn: DevToolsEvent[DevToolsHooks.COMPONENT_UPDATED]): () => void + componentRemoved(fn: DevToolsEvent[DevToolsHooks.COMPONENT_REMOVED]): () => void + setupDevtoolsPlugin(fn: DevToolsEvent[DevToolsHooks.SETUP_DEVTOOLS_PLUGIN]): void + } +} diff --git a/packages/devtools-kit-next/src/index.ts b/packages/devtools-kit-next/src/index.ts index 06d06f11..9ee4b6cd 100644 --- a/packages/devtools-kit-next/src/index.ts +++ b/packages/devtools-kit-next/src/index.ts @@ -7,4 +7,4 @@ export const devtools = { get api() { return {} }, -} as const +} From d0346bbf730a47e1af6a97927bca06bcf832a820 Mon Sep 17 00:00:00 2001 From: arlo Date: Sun, 28 Jan 2024 19:22:53 +0800 Subject: [PATCH 06/18] chore: update --- .../__tests__/fixtures/App.vue | 8 +- .../__tests__/fixtures/components/Name.vue | 11 ++ .../devtools-kit-next/__tests__/hook.test.ts | 16 +++ packages/devtools-kit-next/src/hook/index.ts | 4 +- packages/devtools-kit-next/src/types/app.ts | 101 ++++++++++++++++++ .../src/{hook/types.ts => types/hook.ts} | 2 +- packages/devtools-kit-next/src/types/index.ts | 2 + packages/schema/global.d.ts | 2 +- 8 files changed, 141 insertions(+), 5 deletions(-) create mode 100644 packages/devtools-kit-next/__tests__/fixtures/components/Name.vue create mode 100644 packages/devtools-kit-next/src/types/app.ts rename packages/devtools-kit-next/src/{hook/types.ts => types/hook.ts} (98%) create mode 100644 packages/devtools-kit-next/src/types/index.ts diff --git a/packages/devtools-kit-next/__tests__/fixtures/App.vue b/packages/devtools-kit-next/__tests__/fixtures/App.vue index 9df26813..af51c014 100644 --- a/packages/devtools-kit-next/__tests__/fixtures/App.vue +++ b/packages/devtools-kit-next/__tests__/fixtures/App.vue @@ -1,9 +1,15 @@ diff --git a/packages/devtools-kit-next/__tests__/fixtures/components/Name.vue b/packages/devtools-kit-next/__tests__/fixtures/components/Name.vue new file mode 100644 index 00000000..9bc3e2a6 --- /dev/null +++ b/packages/devtools-kit-next/__tests__/fixtures/components/Name.vue @@ -0,0 +1,11 @@ + + + diff --git a/packages/devtools-kit-next/__tests__/hook.test.ts b/packages/devtools-kit-next/__tests__/hook.test.ts index cd8771b9..bde3c81a 100644 --- a/packages/devtools-kit-next/__tests__/hook.test.ts +++ b/packages/devtools-kit-next/__tests__/hook.test.ts @@ -18,4 +18,20 @@ describe('hook', () => { }) }) }) + + it('should work w/ component updated hook', async () => { + await new Promise((resolve) => { + devtools.init() + + devtools.hook.on.componentUpdated((_, __, ___, component) => { + expect(component.setupState.count).toBe(10) + resolve() + }) + + const app = mount<{}, { }, { count: number, visible: boolean }>(App, { + attachTo: document.body, + }) + app.vm.count = 10 + }) + }) }) diff --git a/packages/devtools-kit-next/src/hook/index.ts b/packages/devtools-kit-next/src/hook/index.ts index 803efbd7..f72ae7cb 100644 --- a/packages/devtools-kit-next/src/hook/index.ts +++ b/packages/devtools-kit-next/src/hook/index.ts @@ -1,9 +1,9 @@ import { target } from '@vue/devtools-shared' import type { HookKeys, Hookable } from 'hookable' import { createHooks } from 'hookable' -import { DevToolsEvent, DevToolsHook, DevToolsHooks, VueHooks } from './types' +import { DevToolsEvent, DevToolsHook, DevToolsHooks, VueHooks } from '../types' -export { VueHooks } from './types' +export { VueHooks } from '../types' export const devtoolsHooks: Hookable> = target.__VUE_DEVTOOLS_HOOK ??= createHooks() diff --git a/packages/devtools-kit-next/src/types/app.ts b/packages/devtools-kit-next/src/types/app.ts new file mode 100644 index 00000000..4ffba677 --- /dev/null +++ b/packages/devtools-kit-next/src/types/app.ts @@ -0,0 +1,101 @@ +import type { App, ComponentInternalInstance, ComponentOptions, ConcreteComponent, SuspenseBoundary, VNode } from 'vue' + +// @TODO +export type PluginApi = any + +export declare type PluginSettingsItem = { + label: string + description?: string +} & ({ + type: 'boolean' + defaultValue: boolean +} | { + type: 'choice' + defaultValue: string | number + options: { + value: string | number + label: string + }[] + component?: 'select' | 'button-group' +} | { + type: 'text' + defaultValue: string +}) + +export interface PluginDescriptor { + id: string + label: string + app: VueAppInstance + packageName?: string + homepage?: string + componentStateTypes?: string[] + logo?: string + disableAppScope?: boolean + disablePluginScope?: boolean + /** + * Run the plugin setup and expose the api even if the devtools is not opened yet. + * Useful to record timeline events early. + */ + enableEarlyProxy?: boolean + settings?: Record +} + +export type PluginSetupFunction = (api: PluginApi) => void + +export type VueAppInstance = ComponentInternalInstance & { + type: { + _componentTag: string | undefined + components: Record + __VUE_DEVTOOLS_COMPONENT_GUSSED_NAME__: string + __isKeepAlive: boolean + devtools: { + hide: boolean + } + mixins: ComponentOptions[] + extends: ComponentOptions + vuex: { + getters: Record + } + computed: Record + } + __v_cache: Cache + __VUE_DEVTOOLS_UID__: string + _isBeingDestroyed: boolean + _instance: VueAppInstance + _container: { + _vnode: { + component: VueAppInstance + } + } + isUnmounted: boolean + parent: VueAppInstance + appContext: { + app: VueAppInstance & App & { + __VUE_DEVTOOLS_APP_RECORD_ID__: string + __VUE_DEVTOOLS_APP_RECORD__: AppRecord + } + } + __VUE_DEVTOOLS_APP_RECORD__: AppRecord + suspense: SuspenseBoundary & { suspenseKey: string } + renderContext: Record + devtoolsRawSetupState: Record + setupState: Record + provides: Record + ctx: Record +} +export interface AppRecord { + id: string | number + name: string + app?: App + version?: string + types?: Record + instanceMap: Map + rootInstance: VueAppInstance + api?: PluginApi + routerId?: string + moduleDetectives?: { + vueRouter: boolean + pinia: boolean + vueI18n: boolean + } +} diff --git a/packages/devtools-kit-next/src/hook/types.ts b/packages/devtools-kit-next/src/types/hook.ts similarity index 98% rename from packages/devtools-kit-next/src/hook/types.ts rename to packages/devtools-kit-next/src/types/hook.ts index 9a9a997a..dbfcdc08 100644 --- a/packages/devtools-kit-next/src/hook/types.ts +++ b/packages/devtools-kit-next/src/types/hook.ts @@ -1,5 +1,5 @@ -import type { AppRecord, PluginDescriptor, PluginSetupFunction, VueAppInstance } from '@vue/devtools-schema' import type { App } from 'vue' +import type { AppRecord, PluginDescriptor, PluginSetupFunction, VueAppInstance } from './app' type HookAppInstance = App & VueAppInstance export enum DevToolsHooks { diff --git a/packages/devtools-kit-next/src/types/index.ts b/packages/devtools-kit-next/src/types/index.ts new file mode 100644 index 00000000..e2f95f57 --- /dev/null +++ b/packages/devtools-kit-next/src/types/index.ts @@ -0,0 +1,2 @@ +export * from './app' +export * from './hook' diff --git a/packages/schema/global.d.ts b/packages/schema/global.d.ts index 6a36f3c0..65656be6 100644 --- a/packages/schema/global.d.ts +++ b/packages/schema/global.d.ts @@ -5,7 +5,7 @@ import type { AppRecord, DevToolsState } from './src/types/vue' /* eslint-disable vars-on-top, no-var */ declare global { - var __VUE_DEVTOOLS_GLOBAL_HOOK__: DevtoolsHook + var __VUE_DEVTOOLS_GLOBAL_HOOK__: any var __VUE_DEVTOOLS_CLIENT_URL__: string var __VUE_DEVTOOLS_BRIDGE__: BridgeInstanceType var __VUE_DEVTOOLS_OVERLAY_BRIDGE__: BridgeInstanceType From 5425547b1777dbdb50591e2da3181c7de04fda36 Mon Sep 17 00:00:00 2001 From: arlo Date: Sun, 28 Jan 2024 19:47:06 +0800 Subject: [PATCH 07/18] chore: update --- packages/devtools-kit-next/package.json | 6 ++---- packages/devtools-kit-next/src/types/app.ts | 2 +- packages/devtools-kit-next/tsup.config.ts | 10 +++------- packages/schema/global.d.ts | 1 - 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/packages/devtools-kit-next/package.json b/packages/devtools-kit-next/package.json index e560739f..06829904 100644 --- a/packages/devtools-kit-next/package.json +++ b/packages/devtools-kit-next/package.json @@ -7,14 +7,12 @@ "license": "MIT", "exports": { ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.mjs", + "import": "./dist/index.js", "require": "./dist/index.cjs" } }, "main": "./dist/index.cjs", - "module": "./dist/index.mjs", - "types": "./dist/index.d.ts", + "module": "./dist/index.js", "files": [ "dist" ], diff --git a/packages/devtools-kit-next/src/types/app.ts b/packages/devtools-kit-next/src/types/app.ts index 4ffba677..f838b300 100644 --- a/packages/devtools-kit-next/src/types/app.ts +++ b/packages/devtools-kit-next/src/types/app.ts @@ -1,4 +1,4 @@ -import type { App, ComponentInternalInstance, ComponentOptions, ConcreteComponent, SuspenseBoundary, VNode } from 'vue' +import type { App, ComponentInternalInstance, ComponentOptions, SuspenseBoundary } from 'vue' // @TODO export type PluginApi = any diff --git a/packages/devtools-kit-next/tsup.config.ts b/packages/devtools-kit-next/tsup.config.ts index fab00c00..b1ac6862 100644 --- a/packages/devtools-kit-next/tsup.config.ts +++ b/packages/devtools-kit-next/tsup.config.ts @@ -1,13 +1,9 @@ -import type { Options } from 'tsup' +import { defineConfig } from 'tsup' -export default { +export default defineConfig({ entryPoints: [ 'src/index.ts', ], - esbuildOptions(options) { - if (options.format === 'esm') - options.outExtension = { '.js': '.mjs' } - }, external: [ 'vue', ], @@ -16,4 +12,4 @@ export default { format: ['esm', 'cjs'], dts: true, shims: true, -} +}) diff --git a/packages/schema/global.d.ts b/packages/schema/global.d.ts index cb9afc40..a434b632 100644 --- a/packages/schema/global.d.ts +++ b/packages/schema/global.d.ts @@ -1,5 +1,4 @@ import type { DevToolsContext } from './src/types/context' -import type { DevtoolsHook } from './src/types/hook' import type { AppRecord, DevToolsState } from './src/types/vue' /* eslint-disable vars-on-top, no-var */ From e7f17cce57425aab52102f944b32847533d606e1 Mon Sep 17 00:00:00 2001 From: arlo Date: Sun, 28 Jan 2024 19:52:15 +0800 Subject: [PATCH 08/18] chore: update --- packages/devtools-kit-next/global.d.ts | 6 ++++++ packages/devtools-kit-next/package.json | 2 ++ packages/devtools-kit-next/src/state/index.ts | 0 packages/devtools-kit-next/types.d.ts | 2 ++ 4 files changed, 10 insertions(+) create mode 100644 packages/devtools-kit-next/global.d.ts create mode 100644 packages/devtools-kit-next/src/state/index.ts create mode 100644 packages/devtools-kit-next/types.d.ts diff --git a/packages/devtools-kit-next/global.d.ts b/packages/devtools-kit-next/global.d.ts new file mode 100644 index 00000000..1a40ae16 --- /dev/null +++ b/packages/devtools-kit-next/global.d.ts @@ -0,0 +1,6 @@ +/* eslint-disable vars-on-top, no-var */ +declare global { + var __VUE_DEVTOOLS_GLOBAL_HOOK__: unknown +} + +export { } diff --git a/packages/devtools-kit-next/package.json b/packages/devtools-kit-next/package.json index 06829904..10ad6560 100644 --- a/packages/devtools-kit-next/package.json +++ b/packages/devtools-kit-next/package.json @@ -13,7 +13,9 @@ }, "main": "./dist/index.cjs", "module": "./dist/index.js", + "types": "./types.d.ts", "files": [ + "**.d.ts", "dist" ], "scripts": { diff --git a/packages/devtools-kit-next/src/state/index.ts b/packages/devtools-kit-next/src/state/index.ts new file mode 100644 index 00000000..e69de29b diff --git a/packages/devtools-kit-next/types.d.ts b/packages/devtools-kit-next/types.d.ts new file mode 100644 index 00000000..e4001646 --- /dev/null +++ b/packages/devtools-kit-next/types.d.ts @@ -0,0 +1,2 @@ +/// +export * from './dist/index' From dd723141bb98e24a2b7740f9231d20ba4f09d0fc Mon Sep 17 00:00:00 2001 From: arlo Date: Tue, 30 Jan 2024 18:12:10 +0800 Subject: [PATCH 09/18] chore: update --- .../devtools-kit-next/__tests__/hook.test.ts | 11 ++- packages/devtools-kit-next/global.d.ts | 7 ++ packages/devtools-kit-next/package.json | 1 + packages/devtools-kit-next/src/core/index.ts | 18 +++- .../devtools-kit-next/src/state/app-record.ts | 83 +++++++++++++++++++ .../devtools-kit-next/src/state/global.ts | 39 +++++++++ packages/devtools-kit-next/src/state/index.ts | 2 + packages/devtools-kit-next/src/types/index.ts | 1 + packages/devtools-kit-next/src/types/state.ts | 14 ++++ packages/schema/global.d.ts | 4 +- pnpm-lock.yaml | 7 ++ 11 files changed, 180 insertions(+), 7 deletions(-) create mode 100644 packages/devtools-kit-next/src/state/app-record.ts create mode 100644 packages/devtools-kit-next/src/state/global.ts create mode 100644 packages/devtools-kit-next/src/types/state.ts diff --git a/packages/devtools-kit-next/__tests__/hook.test.ts b/packages/devtools-kit-next/__tests__/hook.test.ts index bde3c81a..fc58d4cf 100644 --- a/packages/devtools-kit-next/__tests__/hook.test.ts +++ b/packages/devtools-kit-next/__tests__/hook.test.ts @@ -1,13 +1,18 @@ import { devtools } from '@vue/devtools-kit-next' import { mount } from '@vue/test-utils' import { describe, expect, it } from 'vitest' +import { resetDevToolsState } from '../src/state' import App from './fixtures/App.vue' describe('hook', () => { + beforeAll(() => { + devtools.init() + }) + afterEach(() => { + resetDevToolsState() + }) it('should work w/ app init hook', async () => { await new Promise((resolve) => { - devtools.init() - devtools.hook.on.vueAppInit((app, version) => { expect(app).toBeTypeOf('object') expect(version).toBeTypeOf('string') @@ -21,8 +26,6 @@ describe('hook', () => { it('should work w/ component updated hook', async () => { await new Promise((resolve) => { - devtools.init() - devtools.hook.on.componentUpdated((_, __, ___, component) => { expect(component.setupState.count).toBe(10) resolve() diff --git a/packages/devtools-kit-next/global.d.ts b/packages/devtools-kit-next/global.d.ts index 1a40ae16..32fc9aed 100644 --- a/packages/devtools-kit-next/global.d.ts +++ b/packages/devtools-kit-next/global.d.ts @@ -1,6 +1,13 @@ +import type { DevToolsState } from './src/types' + /* eslint-disable vars-on-top, no-var */ declare global { var __VUE_DEVTOOLS_GLOBAL_HOOK__: unknown + var __VUE_DEVTOOLS_GLOBAL_STATE__: DevToolsState + var __VUE_DEVTOOLS_APP_RECROD_INFO__: { + id: number + appIds: Set + } } export { } diff --git a/packages/devtools-kit-next/package.json b/packages/devtools-kit-next/package.json index 10ad6560..a3db2f5e 100644 --- a/packages/devtools-kit-next/package.json +++ b/packages/devtools-kit-next/package.json @@ -32,6 +32,7 @@ "speakingurl": "^14.0.1" }, "devDependencies": { + "@types/speakingurl": "^13.0.6", "vue": "^3.4.14", "vue-router": "^4.2.5" } diff --git a/packages/devtools-kit-next/src/core/index.ts b/packages/devtools-kit-next/src/core/index.ts index f913b1ae..3958bac8 100644 --- a/packages/devtools-kit-next/src/core/index.ts +++ b/packages/devtools-kit-next/src/core/index.ts @@ -1,7 +1,23 @@ import { target } from '@vue/devtools-shared' -import { createDevToolsHook, subscribeDevToolsHook } from '../hook' +import { createDevToolsHook, hook, subscribeDevToolsHook } from '../hook' + +import { createAppRecord, devtoolsAppRecords } from '../state' export function initDevTools() { target.__VUE_DEVTOOLS_GLOBAL_HOOK__ = createDevToolsHook() subscribeDevToolsHook() + + // create app record + hook.on.vueAppInit(async (app, version) => { + const record = createAppRecord(app) + devtoolsAppRecords.value = [ + ...devtoolsAppRecords.value, + { + ...record, + app, + version, + }, + ] + console.log(devtoolsAppRecords.value.length) + }) } diff --git a/packages/devtools-kit-next/src/state/app-record.ts b/packages/devtools-kit-next/src/state/app-record.ts new file mode 100644 index 00000000..2c5c8db2 --- /dev/null +++ b/packages/devtools-kit-next/src/state/app-record.ts @@ -0,0 +1,83 @@ +import { target } from '@vue/devtools-shared' +import slug from 'speakingurl' +import { AppRecord, VueAppInstance } from '../types' +import { devtoolsState } from './global' + +interface DevToolsAppRecords { + value: AppRecord[] +} + +export const devtoolsAppRecords = new Proxy(devtoolsState.appRecords as unknown as DevToolsAppRecords, { + get(_, property) { + if (property === 'value') + return devtoolsState.appRecords + }, + set(target, property, value) { + if (property === 'value') + devtoolsState.appRecords = value + + return true + }, +}) + +const appRecordInfo = target.__VUE_DEVTOOLS_APP_RECROD_INFO__ ??= { + id: 0, + appIds: new Set(), +} + +function getAppRecordName(app: VueAppInstance['appContext']['app'], fallbackName: string) { + return app?._component?.name || `App ${fallbackName}` +} + +function getAppRootInstance(app: VueAppInstance['appContext']['app']) { + if (app._instance) + return app._instance + + else if (app._container?._vnode?.component) + return app._container?._vnode?.component +} + +function getAppRecordId(app: VueAppInstance['appContext']['app'], defaultId?: string): string { + if (app.__VUE_DEVTOOLS_APP_RECORD_ID__ != null) + return app.__VUE_DEVTOOLS_APP_RECORD_ID__ + + let id = defaultId ?? (appRecordInfo.id++).toString() + + if (defaultId && appRecordInfo.appIds.has(id)) { + let count = 1 + while (appRecordInfo.appIds.has(`${defaultId}_${count}`)) + count++ + id = `${defaultId}_${count}` + } + + appRecordInfo.appIds.add(id) + + app.__VUE_DEVTOOLS_APP_RECORD_ID__ = id + return id +} + +export function createAppRecord(app: VueAppInstance['appContext']['app']): AppRecord { + const rootInstance = getAppRootInstance(app) + if (rootInstance) { + appRecordInfo.id++ + const name = getAppRecordName(app, appRecordInfo.id.toString()) + const id = getAppRecordId(app, slug(name)) + + const record: AppRecord = { + id, + name, + instanceMap: new Map(), + rootInstance, + } + + app.__VUE_DEVTOOLS_APP_RECORD__ = record + const rootId = `${record.id}:root` + record.instanceMap.set(rootId, record.rootInstance) + record.rootInstance.__VUE_DEVTOOLS_UID__ = rootId + + return record + } + else { + return {} as AppRecord + } +} diff --git a/packages/devtools-kit-next/src/state/global.ts b/packages/devtools-kit-next/src/state/global.ts new file mode 100644 index 00000000..ce02fa9d --- /dev/null +++ b/packages/devtools-kit-next/src/state/global.ts @@ -0,0 +1,39 @@ +import { target as global } from '@vue/devtools-shared' + +const STATE_KEY = '__VUE_DEVTOOLS_GLOBAL_STATE__' +const INITIAL_STATE = { + connected: false, + clientConnected: false, + appRecords: [], + activeAppRecord: null, + selectedComponentId: null, + pluginBuffer: [], + tabs: [], + commands: [], + vitePluginDetected: false, + activeAppRecordId: null, +} + +global[STATE_KEY] ??= INITIAL_STATE + +export function resetDevToolsState() { + global[STATE_KEY] = INITIAL_STATE +} + +export const devtoolsState = new Proxy(global[STATE_KEY], { + get(target, property) { + return global[STATE_KEY][property] + }, + deleteProperty(target, property) { + delete target[property] + return true + }, + set(target, property, value) { + const oldState = { ...global[STATE_KEY] } + + target[property] = value + // sync to global to ensure the state is consistent + global[STATE_KEY][property] = value + return true + }, +}) diff --git a/packages/devtools-kit-next/src/state/index.ts b/packages/devtools-kit-next/src/state/index.ts index e69de29b..6b27d77b 100644 --- a/packages/devtools-kit-next/src/state/index.ts +++ b/packages/devtools-kit-next/src/state/index.ts @@ -0,0 +1,2 @@ +export * from './global' +export * from './app-record' diff --git a/packages/devtools-kit-next/src/types/index.ts b/packages/devtools-kit-next/src/types/index.ts index e2f95f57..eafc1cfd 100644 --- a/packages/devtools-kit-next/src/types/index.ts +++ b/packages/devtools-kit-next/src/types/index.ts @@ -1,2 +1,3 @@ export * from './app' export * from './hook' +export * from './state' diff --git a/packages/devtools-kit-next/src/types/state.ts b/packages/devtools-kit-next/src/types/state.ts new file mode 100644 index 00000000..a1225617 --- /dev/null +++ b/packages/devtools-kit-next/src/types/state.ts @@ -0,0 +1,14 @@ +import type { AppRecord, PluginDescriptor, PluginSetupFunction } from './app' + +export interface DevToolsState { + connected: boolean + clientConnected: boolean + vitePluginDetected: boolean + appRecords: AppRecord[] + activeAppRecord: AppRecord | null + selectedComponentId: string | null + pluginBuffer: [PluginDescriptor, PluginSetupFunction][] + tabs: unknown[] + commands: unknown[] + activeAppRecordId: string | null +} diff --git a/packages/schema/global.d.ts b/packages/schema/global.d.ts index a434b632..14107539 100644 --- a/packages/schema/global.d.ts +++ b/packages/schema/global.d.ts @@ -1,5 +1,5 @@ import type { DevToolsContext } from './src/types/context' -import type { AppRecord, DevToolsState } from './src/types/vue' +import type { AppRecord } from './src/types/vue' /* eslint-disable vars-on-top, no-var */ declare global { @@ -20,7 +20,7 @@ declare global { appIds: Set } // devtools global state - var __VUE_DEVTOOLS_GLOBAL_STATE__: DevToolsState + var __VUE_DEVTOOLS_GLOBAL_STATE__: any // devtools context var __VUE_DEVTOOLS_CONTEXT__: DevToolsContext // router diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b34e686f..e453e634 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -382,6 +382,9 @@ importers: specifier: ^14.0.1 version: 14.0.1 devDependencies: + '@types/speakingurl': + specifier: ^13.0.6 + version: 13.0.6 vue: specifier: ^3.4.14 version: 3.4.15(typescript@5.3.3) @@ -4273,6 +4276,10 @@ packages: '@types/node': 20.11.6 dev: true + /@types/speakingurl@13.0.6: + resolution: {integrity: sha512-ywkRHNHBwq0mFs/2HRgW6TEBAzH66G8f2Txzh1aGR0UC9ZoAUHfHxLZGDhwMpck4BpSnB61eNFIFmlV+TJ+KUA==} + dev: true + /@types/splitpanes@2.2.6: resolution: {integrity: sha512-3dV5sO1Ht74iER4jJU03mreL3f+Q2h47ZqXS6Sfbqc6hkCvsDrX1GA0NbYWRdNvZemPyTDzUoApWKeoGbALwkQ==} dependencies: From 203e35632c6034353c6a719a2ca6d65a13abd4ba Mon Sep 17 00:00:00 2001 From: arlo Date: Wed, 31 Jan 2024 18:50:36 +0800 Subject: [PATCH 10/18] chore: update --- .../devtools-kit-next/__tests__/state.test.ts | 27 +++++++++++++++++++ packages/devtools-kit-next/src/core/index.ts | 18 +++++++++---- packages/devtools-kit-next/src/index.ts | 2 ++ .../devtools-kit-next/src/state/app-record.ts | 25 +++++++++++++++++ .../devtools-kit-next/src/state/global.ts | 5 +++- 5 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 packages/devtools-kit-next/__tests__/state.test.ts diff --git a/packages/devtools-kit-next/__tests__/state.test.ts b/packages/devtools-kit-next/__tests__/state.test.ts new file mode 100644 index 00000000..81aaeba7 --- /dev/null +++ b/packages/devtools-kit-next/__tests__/state.test.ts @@ -0,0 +1,27 @@ +import { devtools } from '@vue/devtools-kit-next' +import { mount } from '@vue/test-utils' +import { describe, expect, it } from 'vitest' +import { resetDevToolsState } from '../src/state' +import App from './fixtures/App.vue' + +describe('app record', () => { + beforeAll(() => { + devtools.init() + }) + afterEach(() => { + resetDevToolsState() + }) + it('should work', async () => { + await new Promise((resolve) => { + devtools.hook.on.vueAppInit(() => { + const records = devtools.state.appRecords + expect(records.length).toBe(1) + expect(devtools.state.connected).toBe(true) + resolve() + }) + mount(App, { + attachTo: document.body, + }) + }) + }) +}) diff --git a/packages/devtools-kit-next/src/core/index.ts b/packages/devtools-kit-next/src/core/index.ts index 3958bac8..24fab6e3 100644 --- a/packages/devtools-kit-next/src/core/index.ts +++ b/packages/devtools-kit-next/src/core/index.ts @@ -1,11 +1,12 @@ import { target } from '@vue/devtools-shared' -import { createDevToolsHook, hook, subscribeDevToolsHook } from '../hook' - -import { createAppRecord, devtoolsAppRecords } from '../state' +import { createDevToolsHook, devtoolsHooks, hook, subscribeDevToolsHook } from '../hook' +import { DevToolsHooks } from '../types' +import { createAppRecord, devtoolsAppRecords, devtoolsState, setActiveAppRecord } from '../state' export function initDevTools() { + // @TODO + target.__VUE_DEVTOOLS_GLOBAL_HOOK__ = createDevToolsHook() - subscribeDevToolsHook() // create app record hook.on.vueAppInit(async (app, version) => { @@ -18,6 +19,13 @@ export function initDevTools() { version, }, ] - console.log(devtoolsAppRecords.value.length) + + if (devtoolsAppRecords.value.length === 1) { + setActiveAppRecord(devtoolsAppRecords.value[0]) + devtoolsState.connected = true + devtoolsHooks.callHook(DevToolsHooks.APP_CONNECTED) + } }) + + subscribeDevToolsHook() } diff --git a/packages/devtools-kit-next/src/index.ts b/packages/devtools-kit-next/src/index.ts index 9ee4b6cd..5e747740 100644 --- a/packages/devtools-kit-next/src/index.ts +++ b/packages/devtools-kit-next/src/index.ts @@ -1,7 +1,9 @@ import { initDevTools } from './core/index' import { hook } from './hook' +import { devtoolsState } from './state' export const devtools = { + state: devtoolsState, hook, init: initDevTools, get api() { diff --git a/packages/devtools-kit-next/src/state/app-record.ts b/packages/devtools-kit-next/src/state/app-record.ts index 2c5c8db2..c88f1c37 100644 --- a/packages/devtools-kit-next/src/state/app-record.ts +++ b/packages/devtools-kit-next/src/state/app-record.ts @@ -5,17 +5,29 @@ import { devtoolsState } from './global' interface DevToolsAppRecords { value: AppRecord[] + active: AppRecord + activeId: string } export const devtoolsAppRecords = new Proxy(devtoolsState.appRecords as unknown as DevToolsAppRecords, { get(_, property) { if (property === 'value') return devtoolsState.appRecords + else if (property === 'active') + return devtoolsState.activeAppRecord + else if (property === 'activeId') + return devtoolsState.activeAppRecordId }, set(target, property, value) { if (property === 'value') devtoolsState.appRecords = value + else if (property === 'active') + devtoolsState.activeAppRecord = value + + else if (property === 'activeId') + devtoolsState.activeAppRecordId = value + return true }, }) @@ -81,3 +93,16 @@ export function createAppRecord(app: VueAppInstance['appContext']['app']): AppRe return {} as AppRecord } } + +export async function setActiveAppRecord(appRecord: AppRecord) { + // @TODO + devtoolsAppRecords.active = appRecord + devtoolsAppRecords.activeId = `${appRecord.id}` +} + +export async function toggleActiveAppRecord(id: string) { + // @TODO + const appRecord = devtoolsAppRecords.value.find(record => record.id === id) + if (appRecord) + setActiveAppRecord(appRecord) +} diff --git a/packages/devtools-kit-next/src/state/global.ts b/packages/devtools-kit-next/src/state/global.ts index ce02fa9d..cf29f0a1 100644 --- a/packages/devtools-kit-next/src/state/global.ts +++ b/packages/devtools-kit-next/src/state/global.ts @@ -1,4 +1,7 @@ import { target as global } from '@vue/devtools-shared' +import type { DevToolsState } from '../types' + +export type { DevToolsState } from '../types' const STATE_KEY = '__VUE_DEVTOOLS_GLOBAL_STATE__' const INITIAL_STATE = { @@ -20,7 +23,7 @@ export function resetDevToolsState() { global[STATE_KEY] = INITIAL_STATE } -export const devtoolsState = new Proxy(global[STATE_KEY], { +export const devtoolsState: DevToolsState = new Proxy(global[STATE_KEY], { get(target, property) { return global[STATE_KEY][property] }, From 856369de5c882ac9ced3bb94de2db376ee6341da Mon Sep 17 00:00:00 2001 From: arlo Date: Thu, 1 Feb 2024 18:22:32 +0800 Subject: [PATCH 11/18] chore: update --- packages/devtools-kit-next/global.d.ts | 5 +- packages/devtools-kit-next/src/api/api.ts | 37 ++++++++++ packages/devtools-kit-next/src/api/hook.ts | 33 +++++++++ packages/devtools-kit-next/src/api/index.ts | 2 + packages/devtools-kit-next/src/api/on.ts | 12 ++++ packages/devtools-kit-next/src/api/plugin.ts | 54 ++++++++++++++ .../src/core/custom-command/index.ts | 51 ++++++++++++++ .../src/core/custom-tab/index.ts | 11 +++ .../src/core/custom-tab/types.ts | 61 ++++++++++++++++ packages/devtools-kit-next/src/core/index.ts | 6 ++ .../src/core/inspector/index.ts | 33 +++++++++ .../src/core/router/index.ts | 70 +++++++++++++++++++ .../src/core/timeline/index.ts | 27 +++++++ packages/devtools-kit-next/src/hook/index.ts | 3 + packages/devtools-kit-next/src/index.ts | 8 ++- .../src/plugins/component.ts | 19 +++++ .../devtools-kit-next/src/plugins/index.ts | 1 + .../devtools-kit-next/src/state/app-record.ts | 29 ++++++-- .../devtools-kit-next/src/state/context.ts | 40 +++++++++++ packages/devtools-kit-next/src/state/index.ts | 4 +- .../devtools-kit-next/src/state/router.ts | 18 +++++ .../src/state/{global.ts => state.ts} | 3 + packages/devtools-kit-next/src/types/api.ts | 0 packages/devtools-kit-next/src/types/app.ts | 3 +- .../devtools-kit-next/src/types/context.ts | 17 +++++ packages/devtools-kit-next/src/types/hook.ts | 1 + packages/devtools-kit-next/src/types/index.ts | 2 + .../devtools-kit-next/src/types/router.ts | 8 +++ packages/devtools-kit-next/src/types/state.ts | 6 +- 29 files changed, 552 insertions(+), 12 deletions(-) create mode 100644 packages/devtools-kit-next/src/api/api.ts create mode 100644 packages/devtools-kit-next/src/api/hook.ts create mode 100644 packages/devtools-kit-next/src/api/index.ts create mode 100644 packages/devtools-kit-next/src/api/on.ts create mode 100644 packages/devtools-kit-next/src/api/plugin.ts create mode 100644 packages/devtools-kit-next/src/core/custom-command/index.ts create mode 100644 packages/devtools-kit-next/src/core/custom-tab/index.ts create mode 100644 packages/devtools-kit-next/src/core/custom-tab/types.ts create mode 100644 packages/devtools-kit-next/src/core/inspector/index.ts create mode 100644 packages/devtools-kit-next/src/core/router/index.ts create mode 100644 packages/devtools-kit-next/src/core/timeline/index.ts create mode 100644 packages/devtools-kit-next/src/plugins/component.ts create mode 100644 packages/devtools-kit-next/src/plugins/index.ts create mode 100644 packages/devtools-kit-next/src/state/context.ts create mode 100644 packages/devtools-kit-next/src/state/router.ts rename packages/devtools-kit-next/src/state/{global.ts => state.ts} (98%) create mode 100644 packages/devtools-kit-next/src/types/api.ts create mode 100644 packages/devtools-kit-next/src/types/context.ts create mode 100644 packages/devtools-kit-next/src/types/router.ts diff --git a/packages/devtools-kit-next/global.d.ts b/packages/devtools-kit-next/global.d.ts index 32fc9aed..9f3d8856 100644 --- a/packages/devtools-kit-next/global.d.ts +++ b/packages/devtools-kit-next/global.d.ts @@ -1,13 +1,16 @@ -import type { DevToolsState } from './src/types' +import type { DevToolsContext, DevToolsState, Router, RouterInfo } from './src/types' /* eslint-disable vars-on-top, no-var */ declare global { var __VUE_DEVTOOLS_GLOBAL_HOOK__: unknown var __VUE_DEVTOOLS_GLOBAL_STATE__: DevToolsState + var __VUE_DEVTOOLS_CONTEXT__: DevToolsContext var __VUE_DEVTOOLS_APP_RECROD_INFO__: { id: number appIds: Set } + var __VUE_DEVTOOLS_ROUTER__: Router | null + var __VUE_DEVTOOLS_ROUTER_INFO__: RouterInfo } export { } diff --git a/packages/devtools-kit-next/src/api/api.ts b/packages/devtools-kit-next/src/api/api.ts new file mode 100644 index 00000000..96524819 --- /dev/null +++ b/packages/devtools-kit-next/src/api/api.ts @@ -0,0 +1,37 @@ +import { TimelineLayerItem, addTimelineLayer } from '../core/timeline' +import { InspectorApiPayload, addInspector } from '../core/inspector' +import { DevToolsEventParams, DevToolsEvents, apiHooks } from './hook' +import { on } from './on' + +export { collectDevToolsPlugin } from './plugin' + +export class DevToolsPluginApi { + public on: typeof on + constructor() { + this.on = on + } + + // #region compatible with old devtools + + // timeline layer + addTimelineLayer(payload: TimelineLayerItem) { + addTimelineLayer(payload) + } + + // timeline event + addTimelineEvent(...params: DevToolsEventParams) { + apiHooks.callHook(DevToolsEvents.ADD_TIMELINE_EVENT, ...params) + } + + // add inspector + addInspector(payload: InspectorApiPayload) { + addInspector({ + id: payload.id, + nodeId: payload.id, + filter: '', + treeFilterPlaceholder: payload.treeFilterPlaceholder || '', + }) + } + + // #endregion compatible with old devtools +} diff --git a/packages/devtools-kit-next/src/api/hook.ts b/packages/devtools-kit-next/src/api/hook.ts new file mode 100644 index 00000000..465a5274 --- /dev/null +++ b/packages/devtools-kit-next/src/api/hook.ts @@ -0,0 +1,33 @@ +import type { HookKeys, Hookable } from 'hookable' +import { target } from '@vue/devtools-shared' +import { createHooks } from 'hookable' +import type { TimelineEvent } from '../core/timeline' +import type { RouterInfo } from '../types' + +export enum DevToolsEvents { + DEVTOOLS_STATE_UPDATED = 'devtools:state-updated', + DEVTOOLS_CONNECTED_UPDATED = 'devtools:connected-updated', + ROUTER_INFO_UPDATED = 'router-info:updated', + COMPONENT_STATE_INSPECT = 'component-state:inspect', + TOGGLE_COMPONENT_INSPECTOR = 'component-inspector:toggle', + GET_COMPONENT_BOUNDING_RECT = 'component-bounding-rect:get', + SCROLL_TO_COMPONENT = 'scroll-to-component', + GET_INSPECTOR_TREE = 'inspector-tree:get', + SEND_INSPECTOR_TREE = 'inspector-tree:send', + GET_INSPECTOR_STATE = 'inspector-state:get', + EDIT_INSPECTOR_STATE = 'inspector-state:edit', + SEND_INSPECTOR_STATE = 'inspector-state:send', + VISIT_COMPONENT_TREE = 'component-tree:visit', + ADD_TIMELINE_EVENT = 'timeline:add-event', + CUSTOM_TABS_UPDATED = 'custom-tabs:updated', + CUSTOM_COMMANDS_UPDATED = 'custom-commands:updated', +} + +export interface DevToolsEvent { + [DevToolsEvents.ADD_TIMELINE_EVENT]: (payload: TimelineEvent) => void + [DevToolsEvents.ROUTER_INFO_UPDATED]: (routerInfo: RouterInfo) => void +} + +export type DevToolsEventParams = Parameters + +export const apiHooks: Hookable> = target.__VUE_DEVTOOLS_API_HOOK ??= createHooks() diff --git a/packages/devtools-kit-next/src/api/index.ts b/packages/devtools-kit-next/src/api/index.ts new file mode 100644 index 00000000..4c94c70d --- /dev/null +++ b/packages/devtools-kit-next/src/api/index.ts @@ -0,0 +1,2 @@ +export * from './api' +export { setupDevToolsPlugin } from './plugin' diff --git a/packages/devtools-kit-next/src/api/on.ts b/packages/devtools-kit-next/src/api/on.ts new file mode 100644 index 00000000..61dced95 --- /dev/null +++ b/packages/devtools-kit-next/src/api/on.ts @@ -0,0 +1,12 @@ +import { DevToolsEvents, apiHooks } from './hook' +import type { DevToolsEvent } from './hook' + +export const on = { + addTimelineEvent(fn: DevToolsEvent[DevToolsEvents.ADD_TIMELINE_EVENT]) { + apiHooks.hook(DevToolsEvents.ADD_TIMELINE_EVENT, fn) + }, + + routerInfoUpdated(fn: DevToolsEvent[DevToolsEvents.ROUTER_INFO_UPDATED]) { + apiHooks.hook(DevToolsEvents.ROUTER_INFO_UPDATED, fn) + }, +} as const diff --git a/packages/devtools-kit-next/src/api/plugin.ts b/packages/devtools-kit-next/src/api/plugin.ts new file mode 100644 index 00000000..38fa335b --- /dev/null +++ b/packages/devtools-kit-next/src/api/plugin.ts @@ -0,0 +1,54 @@ +import { PluginDescriptor, PluginSetupFunction, VueAppInstance } from '../types' +import { devtoolsAppRecords, devtoolsState } from '../state' +import { hook } from '../hook' +import { getRouterDevToolsId } from '../core/router' +import type { DevToolsPluginApi } from './api' + +export function collectDevToolsPlugin(pluginDescriptor: PluginDescriptor, setupFn: PluginSetupFunction) { + devtoolsState.pluginBuffer.push([pluginDescriptor, setupFn]) +} + +export function setupDevToolsPlugin(pluginDescriptor: PluginDescriptor, setupFn: PluginSetupFunction) { + return hook.setupDevToolsPlugin(pluginDescriptor, setupFn) +} + +export function registerPlugin(app: VueAppInstance, api: DevToolsPluginApi) { + const plugins = devtoolsState.pluginBuffer.filter(([plugin]) => plugin.app === app) + plugins.forEach(async ([plugin, setupFn]) => { + if (plugin.packageName === 'vue-query') { + /** + * Skip it for now because plugin api doesn't support vue-query devtools plugin: + * https://github.com/TanStack/query/blob/main/packages/vue-query/src/devtools/devtools.ts + * @TODO: Need to discuss if we should be full compatible with the old devtools plugin api. + */ + return + } + + // edge case for router plugin + if (plugin.packageName === 'vue-router') { + const id = getRouterDevToolsId(`${plugin.id}`) + if (plugin.app === app) { + devtoolsAppRecords.value = devtoolsAppRecords.value.map(item => ({ + ...item, + routerId: id, + })) + } + } + setupFn(api) + }) + + devtoolsAppRecords.value = devtoolsAppRecords.value.map((record) => { + const globalProperties = record.app?.config?.globalProperties + if (!globalProperties) + return record + + return { + ...record, + moduleDetectives: { + vueRouter: !!globalProperties.$router, + pinia: !!globalProperties.$pinia, + vueI18n: !!globalProperties.$i18n, + }, + } + }) +} diff --git a/packages/devtools-kit-next/src/core/custom-command/index.ts b/packages/devtools-kit-next/src/core/custom-command/index.ts new file mode 100644 index 00000000..69d940d7 --- /dev/null +++ b/packages/devtools-kit-next/src/core/custom-command/index.ts @@ -0,0 +1,51 @@ +import { devtoolsState } from '../../state' + +export interface CustomCommandAction { + type: 'url' + /** + * Url of the action, if set, execute the action will open the url + */ + src: string +} + +export interface CustomCommand { + /** + * The id of the command, should be unique + */ + id: string + title: string + description?: string + /** + * Order of the command, bigger number will be shown first + * @default 0 + */ + order?: number + /** + * Icon of the tab, support any Iconify icons, or a url to an image + */ + icon?: string + /** + * - action of the command + * - __NOTE__: This will be ignored if `children` is set + */ + action?: CustomCommandAction + /** + * - children of action, if set, execute the action will show the children + */ + children?: Omit[] +} + +export function addCustomCommand(action: CustomCommand) { + if (devtoolsState.commands.some(t => t.id === action.id)) + return + + devtoolsState.commands.push(action) +} + +export function removeCustomCommand(actionId: string) { + const index = devtoolsState.commands.findIndex(t => t.id === actionId) + if (index === -1) + return + + devtoolsState.commands.splice(index, 1) +} diff --git a/packages/devtools-kit-next/src/core/custom-tab/index.ts b/packages/devtools-kit-next/src/core/custom-tab/index.ts new file mode 100644 index 00000000..732c45e7 --- /dev/null +++ b/packages/devtools-kit-next/src/core/custom-tab/index.ts @@ -0,0 +1,11 @@ +import { devtoolsState } from '../../state' +import type { CustomTab } from './types' + +export type { CustomTab } from './types' + +export function addCustomTab(tab: CustomTab) { + if (devtoolsState.tabs.some(t => t.name === tab.name)) + return + + devtoolsState.tabs.push(tab) +} diff --git a/packages/devtools-kit-next/src/core/custom-tab/types.ts b/packages/devtools-kit-next/src/core/custom-tab/types.ts new file mode 100644 index 00000000..ce07bff0 --- /dev/null +++ b/packages/devtools-kit-next/src/core/custom-tab/types.ts @@ -0,0 +1,61 @@ +import type { VNode } from 'vue' + +type TabCategory = + | 'pinned' + | 'app' + | 'modules' + | 'advanced' + +export type ModuleView = ModuleIframeView | ModuleVNodeView + +export interface ModuleIframeView { + /** + * Iframe view + */ + type: 'iframe' + /** + * Url of the iframe + */ + src: string + /** + * Persist the iframe instance even if the tab is not active + * + * @default true + */ + persistent?: boolean +} + +export interface ModuleVNodeView { + /** + * Vue's VNode view + */ + type: 'vnode' + /** + * Send vnode to the client, they must be static and serializable + */ + vnode: VNode +} + +export interface CustomTab { + /** + * The name of the tab, must be unique + */ + name: string + /** + * Icon of the tab, support any Iconify icons, or a url to an image + */ + icon?: string + /** + * Title of the tab + */ + title: string + /** + * Main view of the tab + */ + view: ModuleView + /** + * Category of the tab + * @default 'app' + */ + category?: TabCategory +} diff --git a/packages/devtools-kit-next/src/core/index.ts b/packages/devtools-kit-next/src/core/index.ts index 24fab6e3..80155330 100644 --- a/packages/devtools-kit-next/src/core/index.ts +++ b/packages/devtools-kit-next/src/core/index.ts @@ -2,21 +2,27 @@ import { target } from '@vue/devtools-shared' import { createDevToolsHook, devtoolsHooks, hook, subscribeDevToolsHook } from '../hook' import { DevToolsHooks } from '../types' import { createAppRecord, devtoolsAppRecords, devtoolsState, setActiveAppRecord } from '../state' +import { DevToolsPluginApi, collectDevToolsPlugin } from '../api' export function initDevTools() { // @TODO target.__VUE_DEVTOOLS_GLOBAL_HOOK__ = createDevToolsHook() + // setup old devtools plugin (compatible with pinia, router, etc) + hook.on.setupDevtoolsPlugin(collectDevToolsPlugin) + // create app record hook.on.vueAppInit(async (app, version) => { const record = createAppRecord(app) + const api = new DevToolsPluginApi() devtoolsAppRecords.value = [ ...devtoolsAppRecords.value, { ...record, app, version, + api, }, ] diff --git a/packages/devtools-kit-next/src/core/inspector/index.ts b/packages/devtools-kit-next/src/core/inspector/index.ts new file mode 100644 index 00000000..64bb2958 --- /dev/null +++ b/packages/devtools-kit-next/src/core/inspector/index.ts @@ -0,0 +1,33 @@ +import { devtoolsContext } from '../../state' + +export interface Inspector { + id: string + nodeId: string + filter: string + treeFilterPlaceholder: string +} + +export interface InspectorApiPayload { + id: string + label: string + icon?: string + treeFilterPlaceholder?: string + actions?: { + icon: string + tooltip: string + action: (payload: unknown) => void + }[] +} + +export function addInspector(payload: Inspector) { + devtoolsContext.inspector.push(payload) +} + +export function getInspector(inspectorId: string) { + return devtoolsContext.inspector.find(inspector => inspector.id === inspectorId) +} + +export function updateInspector(inspectorId: string, payload: Partial) { + const inspector = getInspector(inspectorId) + inspector && Object.assign(inspector, payload) +} diff --git a/packages/devtools-kit-next/src/core/router/index.ts b/packages/devtools-kit-next/src/core/router/index.ts new file mode 100644 index 00000000..96d79fde --- /dev/null +++ b/packages/devtools-kit-next/src/core/router/index.ts @@ -0,0 +1,70 @@ +import type { RouteLocationNormalizedLoaded, RouteRecordRaw, Router } from 'vue-router' +import { deepClone, target as global } from '@vue/devtools-shared' +import { debounce } from 'perfect-debounce' +import { ROUTER_INFO_KEY, ROUTER_KEY } from '../../state' +import type { AppRecord } from '../../types' +import { hook } from '../../hook' +import { DevToolsEvents, apiHooks } from '../../api/hook' + +function getRoutes(router?: Router) { + const routesMap = new Map() + return (router?.getRoutes() || []).filter(i => !routesMap.has(i.path) && routesMap.set(i.path, 1)) +} + +function filterRoutes(routes: RouteRecordRaw[]) { + return routes.map((item) => { + let { path, name, children } = item + if (children?.length) + children = filterRoutes(children) + + return { + path, + name, + children, + } + }) +} + +function filterCurrentRoute(route: RouteLocationNormalizedLoaded & { href?: string } | undefined) { + if (route) { + const { fullPath, hash, href, path, name, matched, params, query } = route + return { + fullPath, + hash, + href, + path, + name, + params, + query, + matched: filterRoutes(matched), + } + } + return route +} + +export function normalizeRouterInfo(appRecord: AppRecord) { + function init() { + const router = appRecord.app?.config.globalProperties.$router as Router | undefined + const currentRoute = filterCurrentRoute(router?.currentRoute.value) + const routes = filterRoutes(getRoutes(router)) + const c = console.warn + console.warn = () => {} + global[ROUTER_INFO_KEY] = { + currentRoute: currentRoute ? deepClone(currentRoute) : {}, + routes: deepClone(routes), + } + global[ROUTER_KEY] = router! + console.warn = c + } + + init() + + // @TODO: use another way to watch router + hook.on.componentUpdated(debounce(() => { + init() + apiHooks.callHook(DevToolsEvents.ROUTER_INFO_UPDATED, global[ROUTER_INFO_KEY]) + }, 200)) +} +export function getRouterDevToolsId(id: string) { + return id.replace(/\D/g, '') || '0' +} diff --git a/packages/devtools-kit-next/src/core/timeline/index.ts b/packages/devtools-kit-next/src/core/timeline/index.ts new file mode 100644 index 00000000..2e9047ec --- /dev/null +++ b/packages/devtools-kit-next/src/core/timeline/index.ts @@ -0,0 +1,27 @@ +import { devtoolsContext } from '../../state' + +export interface TimelineEvent { + event: { + groupId: number + time: number + title: string + subtitle: string + // @TODO: InspectorCustomState type + data: Record + } + layerId: string +} + +export interface TimelineLayerItem { + id: string + label: string + color: number +} + +export function addTimelineLayer(payload: TimelineLayerItem) { + devtoolsContext.timelineLayer.push(payload) +} + +export function getTimelineLayer() { + return devtoolsContext.timelineLayer +} diff --git a/packages/devtools-kit-next/src/hook/index.ts b/packages/devtools-kit-next/src/hook/index.ts index f72ae7cb..c42f113d 100644 --- a/packages/devtools-kit-next/src/hook/index.ts +++ b/packages/devtools-kit-next/src/hook/index.ts @@ -112,4 +112,7 @@ export function subscribeDevToolsHook() { export const hook: VueHooks = { on, + setupDevToolsPlugin(pluginDescriptor, setupFn) { + return devtoolsHooks.callHook(DevToolsHooks.SETUP_DEVTOOLS_PLUGIN, pluginDescriptor, setupFn) + }, } diff --git a/packages/devtools-kit-next/src/index.ts b/packages/devtools-kit-next/src/index.ts index 5e747740..7e1fe14e 100644 --- a/packages/devtools-kit-next/src/index.ts +++ b/packages/devtools-kit-next/src/index.ts @@ -1,12 +1,18 @@ import { initDevTools } from './core/index' import { hook } from './hook' -import { devtoolsState } from './state' +import { devtoolsContext, devtoolsState } from './state' +import { setupDevToolsPlugin } from './api' export const devtools = { state: devtoolsState, + context: devtoolsContext, hook, init: initDevTools, get api() { return {} }, } + +export { + setupDevToolsPlugin, +} diff --git a/packages/devtools-kit-next/src/plugins/component.ts b/packages/devtools-kit-next/src/plugins/component.ts new file mode 100644 index 00000000..9114c49b --- /dev/null +++ b/packages/devtools-kit-next/src/plugins/component.ts @@ -0,0 +1,19 @@ +import { VueAppInstance } from '../types' +import { setupDevToolsPlugin } from '../api' + +const INSPECTOR_ID = '__vue-devtools-component-plugin__' + +export function registerComponentDevToolsPlugin(app: VueAppInstance) { + setupDevToolsPlugin({ + id: INSPECTOR_ID, + label: 'Components', + app, + }, (api) => { + // @TODO + api.addInspector({ + id: INSPECTOR_ID, + label: 'Components', + treeFilterPlaceholder: 'Search components', + }) + }) +} diff --git a/packages/devtools-kit-next/src/plugins/index.ts b/packages/devtools-kit-next/src/plugins/index.ts new file mode 100644 index 00000000..452e5c21 --- /dev/null +++ b/packages/devtools-kit-next/src/plugins/index.ts @@ -0,0 +1 @@ +export * from './component' diff --git a/packages/devtools-kit-next/src/state/app-record.ts b/packages/devtools-kit-next/src/state/app-record.ts index c88f1c37..ac5dcd0d 100644 --- a/packages/devtools-kit-next/src/state/app-record.ts +++ b/packages/devtools-kit-next/src/state/app-record.ts @@ -1,7 +1,10 @@ import { target } from '@vue/devtools-shared' import slug from 'speakingurl' import { AppRecord, VueAppInstance } from '../types' -import { devtoolsState } from './global' +import { DevToolsPluginApi } from '../api' +import { registerPlugin } from '../api/plugin' +import { devtoolsState } from './state' +import { devtoolsContext } from './context' interface DevToolsAppRecords { value: AppRecord[] @@ -19,14 +22,24 @@ export const devtoolsAppRecords = new Proxy(devtoolsState.ap return devtoolsState.activeAppRecordId }, set(target, property, value) { - if (property === 'value') + if (property === 'value') { devtoolsState.appRecords = value + } - else if (property === 'active') - devtoolsState.activeAppRecord = value + else if (property === 'active') { + const _value = value as AppRecord - else if (property === 'activeId') + // sync to context + devtoolsState.activeAppRecord = _value + devtoolsContext.appRecord = _value + devtoolsContext.api = _value.api! + // @TODO: check ? + // devtoolsContext.inspector = _value.inspector ?? [] + } + + else if (property === 'activeId') { devtoolsState.activeAppRecordId = value + } return true }, @@ -98,11 +111,15 @@ export async function setActiveAppRecord(appRecord: AppRecord) { // @TODO devtoolsAppRecords.active = appRecord devtoolsAppRecords.activeId = `${appRecord.id}` + registerPlugin(appRecord.app as unknown as VueAppInstance, appRecord.api!) } export async function toggleActiveAppRecord(id: string) { // @TODO const appRecord = devtoolsAppRecords.value.find(record => record.id === id) - if (appRecord) + if (appRecord) { + const api = new DevToolsPluginApi() + appRecord.api = api setActiveAppRecord(appRecord) + } } diff --git a/packages/devtools-kit-next/src/state/context.ts b/packages/devtools-kit-next/src/state/context.ts new file mode 100644 index 00000000..4605a85b --- /dev/null +++ b/packages/devtools-kit-next/src/state/context.ts @@ -0,0 +1,40 @@ +import { deepClone, target as global } from '@vue/devtools-shared' +import type { DevToolsContext } from '../types' +import { ROUTER_KEY } from './router' + +const CONTEXT_KEY = '__VUE_DEVTOOLS_CONTEXT__' +const INITIAL_CONTEXT = { + appRecord: null, + api: null, + inspector: [], + timelineLayer: [], + routerInfo: {}, + router: null, + activeInspectorTreeId: '', + componentPluginHookBuffer: [], +} as unknown as DevToolsContext + +global[CONTEXT_KEY] ??= deepClone(INITIAL_CONTEXT) + +function resetDevToolsContext() { + global[CONTEXT_KEY] = deepClone(INITIAL_CONTEXT) +} + +export const devtoolsContext = new Proxy(global[CONTEXT_KEY], { + get(target, property) { + if (property === 'router') + return global[ROUTER_KEY] + + else if (property === 'clear') + return resetDevToolsContext + + return global[CONTEXT_KEY][property] + }, + set(target, property, value) { + // if (property === 'componentPluginHookBuffer') + // global[CONTEXT_KEY][property] = value + + global[CONTEXT_KEY][property] = value + return true + }, +}) diff --git a/packages/devtools-kit-next/src/state/index.ts b/packages/devtools-kit-next/src/state/index.ts index 6b27d77b..7fc10a7c 100644 --- a/packages/devtools-kit-next/src/state/index.ts +++ b/packages/devtools-kit-next/src/state/index.ts @@ -1,2 +1,4 @@ -export * from './global' +export * from './state' export * from './app-record' +export * from './context' +export * from './router' diff --git a/packages/devtools-kit-next/src/state/router.ts b/packages/devtools-kit-next/src/state/router.ts new file mode 100644 index 00000000..a8549de3 --- /dev/null +++ b/packages/devtools-kit-next/src/state/router.ts @@ -0,0 +1,18 @@ +import { target as global } from '@vue/devtools-shared' +import { RouterInfo } from '../types' + +export const ROUTER_KEY = '__VUE_DEVTOOLS_ROUTER__' +export const ROUTER_INFO_KEY = '__VUE_DEVTOOLS_ROUTER_INFO__' + +global[ROUTER_INFO_KEY] ??= { + currentRoute: null, + routes: [], +} + +global[ROUTER_KEY] ??= null + +export const devtoolsRouterInfo: RouterInfo = new Proxy(global[ROUTER_INFO_KEY], { + get(target, property) { + return global[ROUTER_INFO_KEY][property] + }, +}) diff --git a/packages/devtools-kit-next/src/state/global.ts b/packages/devtools-kit-next/src/state/state.ts similarity index 98% rename from packages/devtools-kit-next/src/state/global.ts rename to packages/devtools-kit-next/src/state/state.ts index cf29f0a1..06b865fe 100644 --- a/packages/devtools-kit-next/src/state/global.ts +++ b/packages/devtools-kit-next/src/state/state.ts @@ -37,6 +37,9 @@ export const devtoolsState: DevToolsState = new Proxy(global[STATE_KEY], { target[property] = value // sync to global to ensure the state is consistent global[STATE_KEY][property] = value + + // @TODO + return true }, }) diff --git a/packages/devtools-kit-next/src/types/api.ts b/packages/devtools-kit-next/src/types/api.ts new file mode 100644 index 00000000..e69de29b diff --git a/packages/devtools-kit-next/src/types/app.ts b/packages/devtools-kit-next/src/types/app.ts index f838b300..571efbbe 100644 --- a/packages/devtools-kit-next/src/types/app.ts +++ b/packages/devtools-kit-next/src/types/app.ts @@ -1,7 +1,8 @@ import type { App, ComponentInternalInstance, ComponentOptions, SuspenseBoundary } from 'vue' +import type { DevToolsPluginApi } from '../api' // @TODO -export type PluginApi = any +export type PluginApi = DevToolsPluginApi export declare type PluginSettingsItem = { label: string diff --git a/packages/devtools-kit-next/src/types/context.ts b/packages/devtools-kit-next/src/types/context.ts new file mode 100644 index 00000000..503dc7ea --- /dev/null +++ b/packages/devtools-kit-next/src/types/context.ts @@ -0,0 +1,17 @@ +import type { TimelineLayerItem } from '../core/timeline' +import type { DevToolsPluginApi } from '../api' +import type { Inspector } from '../core/inspector' +import type { AppRecord } from './app' +import type { Router, RouterInfo } from './router' + +export interface DevToolsContext { + appRecord: AppRecord | null + api: DevToolsPluginApi + inspector: Inspector[] + timelineLayer: TimelineLayerItem[] + routerInfo: RouterInfo + router: Router | null + activeInspectorTreeId: string + componentPluginHookBuffer: (() => void)[] + clear: () => void +} diff --git a/packages/devtools-kit-next/src/types/hook.ts b/packages/devtools-kit-next/src/types/hook.ts index dbfcdc08..b9dc6383 100644 --- a/packages/devtools-kit-next/src/types/hook.ts +++ b/packages/devtools-kit-next/src/types/hook.ts @@ -51,4 +51,5 @@ export interface VueHooks { componentRemoved(fn: DevToolsEvent[DevToolsHooks.COMPONENT_REMOVED]): () => void setupDevtoolsPlugin(fn: DevToolsEvent[DevToolsHooks.SETUP_DEVTOOLS_PLUGIN]): void } + setupDevToolsPlugin(pluginDescriptor: PluginDescriptor, setupFn: PluginSetupFunction): void } diff --git a/packages/devtools-kit-next/src/types/index.ts b/packages/devtools-kit-next/src/types/index.ts index eafc1cfd..f6ec969d 100644 --- a/packages/devtools-kit-next/src/types/index.ts +++ b/packages/devtools-kit-next/src/types/index.ts @@ -1,3 +1,5 @@ export * from './app' export * from './hook' export * from './state' +export * from './context' +export * from './router' diff --git a/packages/devtools-kit-next/src/types/router.ts b/packages/devtools-kit-next/src/types/router.ts new file mode 100644 index 00000000..ff8f1759 --- /dev/null +++ b/packages/devtools-kit-next/src/types/router.ts @@ -0,0 +1,8 @@ +import type { RouteLocationNormalizedLoaded, RouteRecordNormalized } from 'vue-router' + +export type { Router } from 'vue-router' +export interface RouterInfo { + currentRoute: RouteLocationNormalizedLoaded | null | {} + routes: RouteRecordNormalized[] + // router: Router | null +} diff --git a/packages/devtools-kit-next/src/types/state.ts b/packages/devtools-kit-next/src/types/state.ts index a1225617..9f5ea35f 100644 --- a/packages/devtools-kit-next/src/types/state.ts +++ b/packages/devtools-kit-next/src/types/state.ts @@ -1,3 +1,5 @@ +import type { CustomCommand } from '../core/custom-command' +import type { CustomTab } from '../core/custom-tab' import type { AppRecord, PluginDescriptor, PluginSetupFunction } from './app' export interface DevToolsState { @@ -8,7 +10,7 @@ export interface DevToolsState { activeAppRecord: AppRecord | null selectedComponentId: string | null pluginBuffer: [PluginDescriptor, PluginSetupFunction][] - tabs: unknown[] - commands: unknown[] + tabs: CustomTab[] + commands: CustomCommand[] activeAppRecordId: string | null } From 6ade1c3a69405717a1a95c917006037a5cb159e8 Mon Sep 17 00:00:00 2001 From: arlo Date: Fri, 2 Feb 2024 22:53:25 +0800 Subject: [PATCH 12/18] chore: update --- docs/netlify.toml | 10 +- package.json | 1 - packages/core/src/bridge/core.ts | 4 +- packages/devtools-kit-next/README.md | 3 - packages/devtools-kit-next/package.json | 39 --- packages/devtools-kit-next/src/api/api.ts | 37 --- packages/devtools-kit-next/src/api/hook.ts | 33 --- packages/devtools-kit-next/src/api/index.ts | 2 - packages/devtools-kit-next/src/api/on.ts | 12 - packages/devtools-kit-next/src/api/plugin.ts | 54 ---- .../src/core/custom-command/index.ts | 51 ---- .../src/core/custom-tab/index.ts | 11 - .../src/core/custom-tab/types.ts | 61 ---- packages/devtools-kit-next/src/core/index.ts | 37 --- .../src/core/router/index.ts | 70 ----- .../src/core/timeline/index.ts | 27 -- packages/devtools-kit-next/src/index.ts | 18 -- .../src/plugins/component.ts | 19 -- packages/devtools-kit-next/src/state/state.ts | 45 --- packages/devtools-kit-next/src/types/api.ts | 0 packages/devtools-kit-next/tsup.config.ts | 15 - .../component/format.test.ts} | 4 +- .../__tests__/fixtures/App.vue | 0 .../__tests__/fixtures/components/Name.vue | 0 .../__tests__/hook.test.ts | 3 +- .../__tests__/state.test.ts | 3 +- .../global.d.ts | 5 +- packages/devtools-kit/package.json | 9 +- packages/devtools-kit/src/api/api.ts | 230 +++++++++++++++ packages/devtools-kit/src/api/hook.ts | 67 +++++ packages/devtools-kit/src/api/index.ts | 222 +------------- packages/devtools-kit/src/api/off.ts | 4 +- packages/devtools-kit/src/api/on.ts | 105 ++----- packages/devtools-kit/src/api/plugin.ts | 28 +- .../src/core/app-record/index.ts} | 68 +---- .../src/core/component-highlighter/index.ts | 252 ++++++++++++++++ .../types.ts | 2 +- .../src/core/component-inspector/index.ts | 274 +++--------------- .../src/core/component/general/index.ts | 1 - .../src/core/component/state/bounding-rect.ts | 4 +- .../src/core/component/state/custom.ts | 2 +- .../src/core/component/state/editor.ts | 6 +- .../src/core/component/state/index.ts | 7 +- .../src/core/component/state/process.ts | 4 +- .../src/core/component/state/reviver.ts | 2 +- .../src/core/component/tree/el.ts | 4 +- .../src/core/component/tree/filter.ts | 4 +- .../src/core/component/tree/index.ts | 6 +- .../src/core/component/tree/walker.ts | 6 +- .../src/core/component/types/bounding-rect.ts | 3 +- .../src/core/component/types/editor.ts | 2 +- .../src/core/component/types/index.ts | 4 +- .../src/core/component/types/state.ts | 9 +- .../src/core/component/types/tree.ts | 2 +- .../{general/util.ts => utils/index.ts} | 2 +- .../src/core/custom-command/index.ts | 8 +- .../devtools-kit/src/core/custom-tab/index.ts | 4 +- .../src/core/general/app-record.ts | 33 --- packages/devtools-kit/src/core/general/app.ts | 65 ----- .../devtools-kit/src/core/general/hook.ts | 132 --------- .../src/core/general/inspector.ts | 15 - .../devtools-kit/src/core/general/state.ts | 137 --------- .../src/core/{general => }/index.ts | 39 +-- .../src/core/inspector/index.ts | 0 .../src/core/open-in-editor/index.ts | 3 +- .../devtools-kit/src/core/plugins/index.ts | 1 - .../devtools-kit/src/core/router/index.ts | 99 +++---- .../devtools-kit/src/core/timeline/index.ts | 23 +- .../devtools-kit/src/core/timeline/types.ts | 16 - .../src/core/vue-inspector/index.ts | 59 ---- .../src/hook/index.ts | 0 packages/devtools-kit/src/index.ts | 28 +- .../components.ts => plugins/component.ts} | 28 +- .../src/plugins/index.ts | 0 packages/devtools-kit/src/state/app-record.ts | 58 ++++ .../src/state/context.ts | 3 - packages/devtools-kit/src/state/env.ts | 17 ++ .../src/state/index.ts | 1 + .../src/state/router.ts | 0 packages/devtools-kit/src/state/state.ts | 79 +++++ .../src/types/app.ts | 1 - .../src/types/context.ts | 0 packages/devtools-kit/src/types/env.ts | 3 + .../src/types/hook.ts | 0 .../src/types/index.ts | 1 + .../src/types/router.ts | 0 .../src/types/state.ts | 0 .../types.d.ts | 0 packages/kit-playground/index.html | 18 -- packages/kit-playground/package.json | 32 -- packages/kit-playground/public/vite.svg | 1 - packages/kit-playground/src/App.vue | 14 - packages/kit-playground/src/main.ts | 38 --- packages/kit-playground/src/pages/Home.vue | 9 - packages/kit-playground/src/stores/index.ts | 22 -- packages/kit-playground/src/style.css | 16 - packages/kit-playground/tsconfig.json | 16 - packages/kit-playground/vite.config.ts | 24 -- packages/schema/global.d.ts | 40 --- packages/vite/src/overlay.js | 6 +- pnpm-lock.yaml | 86 ------ 101 files changed, 980 insertions(+), 2088 deletions(-) delete mode 100644 packages/devtools-kit-next/README.md delete mode 100644 packages/devtools-kit-next/package.json delete mode 100644 packages/devtools-kit-next/src/api/api.ts delete mode 100644 packages/devtools-kit-next/src/api/hook.ts delete mode 100644 packages/devtools-kit-next/src/api/index.ts delete mode 100644 packages/devtools-kit-next/src/api/on.ts delete mode 100644 packages/devtools-kit-next/src/api/plugin.ts delete mode 100644 packages/devtools-kit-next/src/core/custom-command/index.ts delete mode 100644 packages/devtools-kit-next/src/core/custom-tab/index.ts delete mode 100644 packages/devtools-kit-next/src/core/custom-tab/types.ts delete mode 100644 packages/devtools-kit-next/src/core/index.ts delete mode 100644 packages/devtools-kit-next/src/core/router/index.ts delete mode 100644 packages/devtools-kit-next/src/core/timeline/index.ts delete mode 100644 packages/devtools-kit-next/src/index.ts delete mode 100644 packages/devtools-kit-next/src/plugins/component.ts delete mode 100644 packages/devtools-kit-next/src/state/state.ts delete mode 100644 packages/devtools-kit-next/src/types/api.ts delete mode 100644 packages/devtools-kit-next/tsup.config.ts rename packages/devtools-kit/{src/core/component/state/__tests__/format.spec.ts => __tests__/component/format.test.ts} (97%) rename packages/{devtools-kit-next => devtools-kit}/__tests__/fixtures/App.vue (100%) rename packages/{devtools-kit-next => devtools-kit}/__tests__/fixtures/components/Name.vue (100%) rename packages/{devtools-kit-next => devtools-kit}/__tests__/hook.test.ts (91%) rename packages/{devtools-kit-next => devtools-kit}/__tests__/state.test.ts (86%) rename packages/{devtools-kit-next => devtools-kit}/global.d.ts (63%) create mode 100644 packages/devtools-kit/src/api/api.ts create mode 100644 packages/devtools-kit/src/api/hook.ts rename packages/{devtools-kit-next/src/state/app-record.ts => devtools-kit/src/core/app-record/index.ts} (59%) create mode 100644 packages/devtools-kit/src/core/component-highlighter/index.ts rename packages/devtools-kit/src/core/{component-inspector => component-highlighter}/types.ts (80%) delete mode 100644 packages/devtools-kit/src/core/component/general/index.ts rename packages/devtools-kit/src/core/component/{general/util.ts => utils/index.ts} (98%) delete mode 100644 packages/devtools-kit/src/core/general/app-record.ts delete mode 100644 packages/devtools-kit/src/core/general/app.ts delete mode 100644 packages/devtools-kit/src/core/general/hook.ts delete mode 100644 packages/devtools-kit/src/core/general/inspector.ts delete mode 100644 packages/devtools-kit/src/core/general/state.ts rename packages/devtools-kit/src/core/{general => }/index.ts (59%) rename packages/{devtools-kit-next => devtools-kit}/src/core/inspector/index.ts (100%) delete mode 100644 packages/devtools-kit/src/core/plugins/index.ts delete mode 100644 packages/devtools-kit/src/core/timeline/types.ts delete mode 100644 packages/devtools-kit/src/core/vue-inspector/index.ts rename packages/{devtools-kit-next => devtools-kit}/src/hook/index.ts (100%) rename packages/devtools-kit/src/{core/plugins/components.ts => plugins/component.ts} (88%) rename packages/{devtools-kit-next => devtools-kit}/src/plugins/index.ts (100%) create mode 100644 packages/devtools-kit/src/state/app-record.ts rename packages/{devtools-kit-next => devtools-kit}/src/state/context.ts (90%) create mode 100644 packages/devtools-kit/src/state/env.ts rename packages/{devtools-kit-next => devtools-kit}/src/state/index.ts (82%) rename packages/{devtools-kit-next => devtools-kit}/src/state/router.ts (100%) create mode 100644 packages/devtools-kit/src/state/state.ts rename packages/{devtools-kit-next => devtools-kit}/src/types/app.ts (99%) rename packages/{devtools-kit-next => devtools-kit}/src/types/context.ts (100%) create mode 100644 packages/devtools-kit/src/types/env.ts rename packages/{devtools-kit-next => devtools-kit}/src/types/hook.ts (100%) rename packages/{devtools-kit-next => devtools-kit}/src/types/index.ts (84%) rename packages/{devtools-kit-next => devtools-kit}/src/types/router.ts (100%) rename packages/{devtools-kit-next => devtools-kit}/src/types/state.ts (100%) rename packages/{devtools-kit-next => devtools-kit}/types.d.ts (100%) delete mode 100644 packages/kit-playground/index.html delete mode 100644 packages/kit-playground/package.json delete mode 100644 packages/kit-playground/public/vite.svg delete mode 100644 packages/kit-playground/src/App.vue delete mode 100644 packages/kit-playground/src/main.ts delete mode 100644 packages/kit-playground/src/pages/Home.vue delete mode 100644 packages/kit-playground/src/stores/index.ts delete mode 100644 packages/kit-playground/src/style.css delete mode 100644 packages/kit-playground/tsconfig.json delete mode 100644 packages/kit-playground/vite.config.ts diff --git a/docs/netlify.toml b/docs/netlify.toml index 68bc366c..8a44df6d 100644 --- a/docs/netlify.toml +++ b/docs/netlify.toml @@ -1,6 +1,6 @@ -[build.environment] - NODE_VERSION = "18" - [build] - command = "pnpm run docs:build" - publish = ".vitepress/dist" +command = "pnpm run docs:build" +publish = ".vitepress/dist" + +[build.environment] +NODE_VERSION = "18" diff --git a/package.json b/package.json index 5d74e7d5..9ec1243a 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,6 @@ "@types/node": "^20.11.6", "@unocss/eslint-plugin": "^0.58.3", "@vue/devtools-core": "workspace:^", - "@vue/devtools-kit-next": "workspace:^", "@vue/devtools-schema": "workspace:^", "@vue/test-utils": "^2.4.4", "archiver": "^6.0.1", diff --git a/packages/core/src/bridge/core.ts b/packages/core/src/bridge/core.ts index 8368b985..47aab9aa 100644 --- a/packages/core/src/bridge/core.ts +++ b/packages/core/src/bridge/core.ts @@ -2,7 +2,7 @@ import { NOOP } from '@vue/devtools-shared' import type { Emitter, EventType, Handler } from 'mitt' import mitt from 'mitt' import { parse } from '@vue/devtools-kit' -import type { InspectorStateEditorPayload, ScrollToComponentOptions, ToggleComponentInspectorOptions } from '@vue/devtools-kit' +import type { ComponentHighLighterOptions, InspectorStateEditorPayload, ScrollToComponentOptions } from '@vue/devtools-kit' export interface BridgeAdapterOptions { tracker: (fn: Function) => void @@ -91,7 +91,7 @@ export const bridgeRpcEvents = { export type BridgeRpcEvents = typeof bridgeRpcEvents export type BridgeRpcEventName = BridgeRpcEvents[keyof BridgeRpcEvents] export interface BridgeRpcEventPayload { - [bridgeRpcEvents.toggleComponentInspector]: ToggleComponentInspectorOptions + [bridgeRpcEvents.toggleComponentInspector]: ComponentHighLighterOptions [bridgeRpcEvents.scrollToComponent]: ScrollToComponentOptions [bridgeRpcEvents.inspectComponentInspector]: string [bridgeRpcEvents.componentBoundingRect]: { diff --git a/packages/devtools-kit-next/README.md b/packages/devtools-kit-next/README.md deleted file mode 100644 index dcc52418..00000000 --- a/packages/devtools-kit-next/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @vue/devtools-kit - -> Utility kit for DevTools. diff --git a/packages/devtools-kit-next/package.json b/packages/devtools-kit-next/package.json deleted file mode 100644 index a3db2f5e..00000000 --- a/packages/devtools-kit-next/package.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "@vue/devtools-kit-next", - "type": "module", - "version": "7.0.11", - "private": true, - "author": "webfansplz", - "license": "MIT", - "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" - } - }, - "main": "./dist/index.cjs", - "module": "./dist/index.js", - "types": "./types.d.ts", - "files": [ - "**.d.ts", - "dist" - ], - "scripts": { - "build": "tsup --clean", - "prepare:type": "tsup --dts-only", - "stub": "tsup --watch --onSuccess 'tsup --dts-only'" - }, - "dependencies": { - "@vue/devtools-schema": "workspace:^", - "@vue/devtools-shared": "workspace:^", - "hookable": "^5.5.3", - "mitt": "^3.0.1", - "perfect-debounce": "^1.0.0", - "speakingurl": "^14.0.1" - }, - "devDependencies": { - "@types/speakingurl": "^13.0.6", - "vue": "^3.4.14", - "vue-router": "^4.2.5" - } -} diff --git a/packages/devtools-kit-next/src/api/api.ts b/packages/devtools-kit-next/src/api/api.ts deleted file mode 100644 index 96524819..00000000 --- a/packages/devtools-kit-next/src/api/api.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { TimelineLayerItem, addTimelineLayer } from '../core/timeline' -import { InspectorApiPayload, addInspector } from '../core/inspector' -import { DevToolsEventParams, DevToolsEvents, apiHooks } from './hook' -import { on } from './on' - -export { collectDevToolsPlugin } from './plugin' - -export class DevToolsPluginApi { - public on: typeof on - constructor() { - this.on = on - } - - // #region compatible with old devtools - - // timeline layer - addTimelineLayer(payload: TimelineLayerItem) { - addTimelineLayer(payload) - } - - // timeline event - addTimelineEvent(...params: DevToolsEventParams) { - apiHooks.callHook(DevToolsEvents.ADD_TIMELINE_EVENT, ...params) - } - - // add inspector - addInspector(payload: InspectorApiPayload) { - addInspector({ - id: payload.id, - nodeId: payload.id, - filter: '', - treeFilterPlaceholder: payload.treeFilterPlaceholder || '', - }) - } - - // #endregion compatible with old devtools -} diff --git a/packages/devtools-kit-next/src/api/hook.ts b/packages/devtools-kit-next/src/api/hook.ts deleted file mode 100644 index 465a5274..00000000 --- a/packages/devtools-kit-next/src/api/hook.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { HookKeys, Hookable } from 'hookable' -import { target } from '@vue/devtools-shared' -import { createHooks } from 'hookable' -import type { TimelineEvent } from '../core/timeline' -import type { RouterInfo } from '../types' - -export enum DevToolsEvents { - DEVTOOLS_STATE_UPDATED = 'devtools:state-updated', - DEVTOOLS_CONNECTED_UPDATED = 'devtools:connected-updated', - ROUTER_INFO_UPDATED = 'router-info:updated', - COMPONENT_STATE_INSPECT = 'component-state:inspect', - TOGGLE_COMPONENT_INSPECTOR = 'component-inspector:toggle', - GET_COMPONENT_BOUNDING_RECT = 'component-bounding-rect:get', - SCROLL_TO_COMPONENT = 'scroll-to-component', - GET_INSPECTOR_TREE = 'inspector-tree:get', - SEND_INSPECTOR_TREE = 'inspector-tree:send', - GET_INSPECTOR_STATE = 'inspector-state:get', - EDIT_INSPECTOR_STATE = 'inspector-state:edit', - SEND_INSPECTOR_STATE = 'inspector-state:send', - VISIT_COMPONENT_TREE = 'component-tree:visit', - ADD_TIMELINE_EVENT = 'timeline:add-event', - CUSTOM_TABS_UPDATED = 'custom-tabs:updated', - CUSTOM_COMMANDS_UPDATED = 'custom-commands:updated', -} - -export interface DevToolsEvent { - [DevToolsEvents.ADD_TIMELINE_EVENT]: (payload: TimelineEvent) => void - [DevToolsEvents.ROUTER_INFO_UPDATED]: (routerInfo: RouterInfo) => void -} - -export type DevToolsEventParams = Parameters - -export const apiHooks: Hookable> = target.__VUE_DEVTOOLS_API_HOOK ??= createHooks() diff --git a/packages/devtools-kit-next/src/api/index.ts b/packages/devtools-kit-next/src/api/index.ts deleted file mode 100644 index 4c94c70d..00000000 --- a/packages/devtools-kit-next/src/api/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './api' -export { setupDevToolsPlugin } from './plugin' diff --git a/packages/devtools-kit-next/src/api/on.ts b/packages/devtools-kit-next/src/api/on.ts deleted file mode 100644 index 61dced95..00000000 --- a/packages/devtools-kit-next/src/api/on.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { DevToolsEvents, apiHooks } from './hook' -import type { DevToolsEvent } from './hook' - -export const on = { - addTimelineEvent(fn: DevToolsEvent[DevToolsEvents.ADD_TIMELINE_EVENT]) { - apiHooks.hook(DevToolsEvents.ADD_TIMELINE_EVENT, fn) - }, - - routerInfoUpdated(fn: DevToolsEvent[DevToolsEvents.ROUTER_INFO_UPDATED]) { - apiHooks.hook(DevToolsEvents.ROUTER_INFO_UPDATED, fn) - }, -} as const diff --git a/packages/devtools-kit-next/src/api/plugin.ts b/packages/devtools-kit-next/src/api/plugin.ts deleted file mode 100644 index 38fa335b..00000000 --- a/packages/devtools-kit-next/src/api/plugin.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { PluginDescriptor, PluginSetupFunction, VueAppInstance } from '../types' -import { devtoolsAppRecords, devtoolsState } from '../state' -import { hook } from '../hook' -import { getRouterDevToolsId } from '../core/router' -import type { DevToolsPluginApi } from './api' - -export function collectDevToolsPlugin(pluginDescriptor: PluginDescriptor, setupFn: PluginSetupFunction) { - devtoolsState.pluginBuffer.push([pluginDescriptor, setupFn]) -} - -export function setupDevToolsPlugin(pluginDescriptor: PluginDescriptor, setupFn: PluginSetupFunction) { - return hook.setupDevToolsPlugin(pluginDescriptor, setupFn) -} - -export function registerPlugin(app: VueAppInstance, api: DevToolsPluginApi) { - const plugins = devtoolsState.pluginBuffer.filter(([plugin]) => plugin.app === app) - plugins.forEach(async ([plugin, setupFn]) => { - if (plugin.packageName === 'vue-query') { - /** - * Skip it for now because plugin api doesn't support vue-query devtools plugin: - * https://github.com/TanStack/query/blob/main/packages/vue-query/src/devtools/devtools.ts - * @TODO: Need to discuss if we should be full compatible with the old devtools plugin api. - */ - return - } - - // edge case for router plugin - if (plugin.packageName === 'vue-router') { - const id = getRouterDevToolsId(`${plugin.id}`) - if (plugin.app === app) { - devtoolsAppRecords.value = devtoolsAppRecords.value.map(item => ({ - ...item, - routerId: id, - })) - } - } - setupFn(api) - }) - - devtoolsAppRecords.value = devtoolsAppRecords.value.map((record) => { - const globalProperties = record.app?.config?.globalProperties - if (!globalProperties) - return record - - return { - ...record, - moduleDetectives: { - vueRouter: !!globalProperties.$router, - pinia: !!globalProperties.$pinia, - vueI18n: !!globalProperties.$i18n, - }, - } - }) -} diff --git a/packages/devtools-kit-next/src/core/custom-command/index.ts b/packages/devtools-kit-next/src/core/custom-command/index.ts deleted file mode 100644 index 69d940d7..00000000 --- a/packages/devtools-kit-next/src/core/custom-command/index.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { devtoolsState } from '../../state' - -export interface CustomCommandAction { - type: 'url' - /** - * Url of the action, if set, execute the action will open the url - */ - src: string -} - -export interface CustomCommand { - /** - * The id of the command, should be unique - */ - id: string - title: string - description?: string - /** - * Order of the command, bigger number will be shown first - * @default 0 - */ - order?: number - /** - * Icon of the tab, support any Iconify icons, or a url to an image - */ - icon?: string - /** - * - action of the command - * - __NOTE__: This will be ignored if `children` is set - */ - action?: CustomCommandAction - /** - * - children of action, if set, execute the action will show the children - */ - children?: Omit[] -} - -export function addCustomCommand(action: CustomCommand) { - if (devtoolsState.commands.some(t => t.id === action.id)) - return - - devtoolsState.commands.push(action) -} - -export function removeCustomCommand(actionId: string) { - const index = devtoolsState.commands.findIndex(t => t.id === actionId) - if (index === -1) - return - - devtoolsState.commands.splice(index, 1) -} diff --git a/packages/devtools-kit-next/src/core/custom-tab/index.ts b/packages/devtools-kit-next/src/core/custom-tab/index.ts deleted file mode 100644 index 732c45e7..00000000 --- a/packages/devtools-kit-next/src/core/custom-tab/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { devtoolsState } from '../../state' -import type { CustomTab } from './types' - -export type { CustomTab } from './types' - -export function addCustomTab(tab: CustomTab) { - if (devtoolsState.tabs.some(t => t.name === tab.name)) - return - - devtoolsState.tabs.push(tab) -} diff --git a/packages/devtools-kit-next/src/core/custom-tab/types.ts b/packages/devtools-kit-next/src/core/custom-tab/types.ts deleted file mode 100644 index ce07bff0..00000000 --- a/packages/devtools-kit-next/src/core/custom-tab/types.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type { VNode } from 'vue' - -type TabCategory = - | 'pinned' - | 'app' - | 'modules' - | 'advanced' - -export type ModuleView = ModuleIframeView | ModuleVNodeView - -export interface ModuleIframeView { - /** - * Iframe view - */ - type: 'iframe' - /** - * Url of the iframe - */ - src: string - /** - * Persist the iframe instance even if the tab is not active - * - * @default true - */ - persistent?: boolean -} - -export interface ModuleVNodeView { - /** - * Vue's VNode view - */ - type: 'vnode' - /** - * Send vnode to the client, they must be static and serializable - */ - vnode: VNode -} - -export interface CustomTab { - /** - * The name of the tab, must be unique - */ - name: string - /** - * Icon of the tab, support any Iconify icons, or a url to an image - */ - icon?: string - /** - * Title of the tab - */ - title: string - /** - * Main view of the tab - */ - view: ModuleView - /** - * Category of the tab - * @default 'app' - */ - category?: TabCategory -} diff --git a/packages/devtools-kit-next/src/core/index.ts b/packages/devtools-kit-next/src/core/index.ts deleted file mode 100644 index 80155330..00000000 --- a/packages/devtools-kit-next/src/core/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { target } from '@vue/devtools-shared' -import { createDevToolsHook, devtoolsHooks, hook, subscribeDevToolsHook } from '../hook' -import { DevToolsHooks } from '../types' -import { createAppRecord, devtoolsAppRecords, devtoolsState, setActiveAppRecord } from '../state' -import { DevToolsPluginApi, collectDevToolsPlugin } from '../api' - -export function initDevTools() { - // @TODO - - target.__VUE_DEVTOOLS_GLOBAL_HOOK__ = createDevToolsHook() - - // setup old devtools plugin (compatible with pinia, router, etc) - hook.on.setupDevtoolsPlugin(collectDevToolsPlugin) - - // create app record - hook.on.vueAppInit(async (app, version) => { - const record = createAppRecord(app) - const api = new DevToolsPluginApi() - devtoolsAppRecords.value = [ - ...devtoolsAppRecords.value, - { - ...record, - app, - version, - api, - }, - ] - - if (devtoolsAppRecords.value.length === 1) { - setActiveAppRecord(devtoolsAppRecords.value[0]) - devtoolsState.connected = true - devtoolsHooks.callHook(DevToolsHooks.APP_CONNECTED) - } - }) - - subscribeDevToolsHook() -} diff --git a/packages/devtools-kit-next/src/core/router/index.ts b/packages/devtools-kit-next/src/core/router/index.ts deleted file mode 100644 index 96d79fde..00000000 --- a/packages/devtools-kit-next/src/core/router/index.ts +++ /dev/null @@ -1,70 +0,0 @@ -import type { RouteLocationNormalizedLoaded, RouteRecordRaw, Router } from 'vue-router' -import { deepClone, target as global } from '@vue/devtools-shared' -import { debounce } from 'perfect-debounce' -import { ROUTER_INFO_KEY, ROUTER_KEY } from '../../state' -import type { AppRecord } from '../../types' -import { hook } from '../../hook' -import { DevToolsEvents, apiHooks } from '../../api/hook' - -function getRoutes(router?: Router) { - const routesMap = new Map() - return (router?.getRoutes() || []).filter(i => !routesMap.has(i.path) && routesMap.set(i.path, 1)) -} - -function filterRoutes(routes: RouteRecordRaw[]) { - return routes.map((item) => { - let { path, name, children } = item - if (children?.length) - children = filterRoutes(children) - - return { - path, - name, - children, - } - }) -} - -function filterCurrentRoute(route: RouteLocationNormalizedLoaded & { href?: string } | undefined) { - if (route) { - const { fullPath, hash, href, path, name, matched, params, query } = route - return { - fullPath, - hash, - href, - path, - name, - params, - query, - matched: filterRoutes(matched), - } - } - return route -} - -export function normalizeRouterInfo(appRecord: AppRecord) { - function init() { - const router = appRecord.app?.config.globalProperties.$router as Router | undefined - const currentRoute = filterCurrentRoute(router?.currentRoute.value) - const routes = filterRoutes(getRoutes(router)) - const c = console.warn - console.warn = () => {} - global[ROUTER_INFO_KEY] = { - currentRoute: currentRoute ? deepClone(currentRoute) : {}, - routes: deepClone(routes), - } - global[ROUTER_KEY] = router! - console.warn = c - } - - init() - - // @TODO: use another way to watch router - hook.on.componentUpdated(debounce(() => { - init() - apiHooks.callHook(DevToolsEvents.ROUTER_INFO_UPDATED, global[ROUTER_INFO_KEY]) - }, 200)) -} -export function getRouterDevToolsId(id: string) { - return id.replace(/\D/g, '') || '0' -} diff --git a/packages/devtools-kit-next/src/core/timeline/index.ts b/packages/devtools-kit-next/src/core/timeline/index.ts deleted file mode 100644 index 2e9047ec..00000000 --- a/packages/devtools-kit-next/src/core/timeline/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { devtoolsContext } from '../../state' - -export interface TimelineEvent { - event: { - groupId: number - time: number - title: string - subtitle: string - // @TODO: InspectorCustomState type - data: Record - } - layerId: string -} - -export interface TimelineLayerItem { - id: string - label: string - color: number -} - -export function addTimelineLayer(payload: TimelineLayerItem) { - devtoolsContext.timelineLayer.push(payload) -} - -export function getTimelineLayer() { - return devtoolsContext.timelineLayer -} diff --git a/packages/devtools-kit-next/src/index.ts b/packages/devtools-kit-next/src/index.ts deleted file mode 100644 index 7e1fe14e..00000000 --- a/packages/devtools-kit-next/src/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { initDevTools } from './core/index' -import { hook } from './hook' -import { devtoolsContext, devtoolsState } from './state' -import { setupDevToolsPlugin } from './api' - -export const devtools = { - state: devtoolsState, - context: devtoolsContext, - hook, - init: initDevTools, - get api() { - return {} - }, -} - -export { - setupDevToolsPlugin, -} diff --git a/packages/devtools-kit-next/src/plugins/component.ts b/packages/devtools-kit-next/src/plugins/component.ts deleted file mode 100644 index 9114c49b..00000000 --- a/packages/devtools-kit-next/src/plugins/component.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { VueAppInstance } from '../types' -import { setupDevToolsPlugin } from '../api' - -const INSPECTOR_ID = '__vue-devtools-component-plugin__' - -export function registerComponentDevToolsPlugin(app: VueAppInstance) { - setupDevToolsPlugin({ - id: INSPECTOR_ID, - label: 'Components', - app, - }, (api) => { - // @TODO - api.addInspector({ - id: INSPECTOR_ID, - label: 'Components', - treeFilterPlaceholder: 'Search components', - }) - }) -} diff --git a/packages/devtools-kit-next/src/state/state.ts b/packages/devtools-kit-next/src/state/state.ts deleted file mode 100644 index 06b865fe..00000000 --- a/packages/devtools-kit-next/src/state/state.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { target as global } from '@vue/devtools-shared' -import type { DevToolsState } from '../types' - -export type { DevToolsState } from '../types' - -const STATE_KEY = '__VUE_DEVTOOLS_GLOBAL_STATE__' -const INITIAL_STATE = { - connected: false, - clientConnected: false, - appRecords: [], - activeAppRecord: null, - selectedComponentId: null, - pluginBuffer: [], - tabs: [], - commands: [], - vitePluginDetected: false, - activeAppRecordId: null, -} - -global[STATE_KEY] ??= INITIAL_STATE - -export function resetDevToolsState() { - global[STATE_KEY] = INITIAL_STATE -} - -export const devtoolsState: DevToolsState = new Proxy(global[STATE_KEY], { - get(target, property) { - return global[STATE_KEY][property] - }, - deleteProperty(target, property) { - delete target[property] - return true - }, - set(target, property, value) { - const oldState = { ...global[STATE_KEY] } - - target[property] = value - // sync to global to ensure the state is consistent - global[STATE_KEY][property] = value - - // @TODO - - return true - }, -}) diff --git a/packages/devtools-kit-next/src/types/api.ts b/packages/devtools-kit-next/src/types/api.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/packages/devtools-kit-next/tsup.config.ts b/packages/devtools-kit-next/tsup.config.ts deleted file mode 100644 index b1ac6862..00000000 --- a/packages/devtools-kit-next/tsup.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { defineConfig } from 'tsup' - -export default defineConfig({ - entryPoints: [ - 'src/index.ts', - ], - external: [ - 'vue', - ], - noExternal: ['speakingurl'], - clean: true, - format: ['esm', 'cjs'], - dts: true, - shims: true, -}) diff --git a/packages/devtools-kit/src/core/component/state/__tests__/format.spec.ts b/packages/devtools-kit/__tests__/component/format.test.ts similarity index 97% rename from packages/devtools-kit/src/core/component/state/__tests__/format.spec.ts rename to packages/devtools-kit/__tests__/component/format.test.ts index a63eb35f..51b30832 100644 --- a/packages/devtools-kit/src/core/component/state/__tests__/format.spec.ts +++ b/packages/devtools-kit/__tests__/component/format.test.ts @@ -1,5 +1,5 @@ -import * as format from '../format' -import { INFINITY, NAN, NEGATIVE_INFINITY, UNDEFINED } from '../constants' +import * as format from '../../src/core/component/state/format' +import { INFINITY, NAN, NEGATIVE_INFINITY, UNDEFINED } from '../../src/core/component/state/constants' describe('format: displayText and rawValue can be calculated by formatInspectorStateValue, getRawValue', () => { describe('type: literals', () => { diff --git a/packages/devtools-kit-next/__tests__/fixtures/App.vue b/packages/devtools-kit/__tests__/fixtures/App.vue similarity index 100% rename from packages/devtools-kit-next/__tests__/fixtures/App.vue rename to packages/devtools-kit/__tests__/fixtures/App.vue diff --git a/packages/devtools-kit-next/__tests__/fixtures/components/Name.vue b/packages/devtools-kit/__tests__/fixtures/components/Name.vue similarity index 100% rename from packages/devtools-kit-next/__tests__/fixtures/components/Name.vue rename to packages/devtools-kit/__tests__/fixtures/components/Name.vue diff --git a/packages/devtools-kit-next/__tests__/hook.test.ts b/packages/devtools-kit/__tests__/hook.test.ts similarity index 91% rename from packages/devtools-kit-next/__tests__/hook.test.ts rename to packages/devtools-kit/__tests__/hook.test.ts index fc58d4cf..f3eee720 100644 --- a/packages/devtools-kit-next/__tests__/hook.test.ts +++ b/packages/devtools-kit/__tests__/hook.test.ts @@ -1,6 +1,5 @@ -import { devtools } from '@vue/devtools-kit-next' +import { devtools } from '@vue/devtools-kit' import { mount } from '@vue/test-utils' -import { describe, expect, it } from 'vitest' import { resetDevToolsState } from '../src/state' import App from './fixtures/App.vue' diff --git a/packages/devtools-kit-next/__tests__/state.test.ts b/packages/devtools-kit/__tests__/state.test.ts similarity index 86% rename from packages/devtools-kit-next/__tests__/state.test.ts rename to packages/devtools-kit/__tests__/state.test.ts index 81aaeba7..aebd065f 100644 --- a/packages/devtools-kit-next/__tests__/state.test.ts +++ b/packages/devtools-kit/__tests__/state.test.ts @@ -1,6 +1,5 @@ -import { devtools } from '@vue/devtools-kit-next' +import { devtools } from '@vue/devtools-kit' import { mount } from '@vue/test-utils' -import { describe, expect, it } from 'vitest' import { resetDevToolsState } from '../src/state' import App from './fixtures/App.vue' diff --git a/packages/devtools-kit-next/global.d.ts b/packages/devtools-kit/global.d.ts similarity index 63% rename from packages/devtools-kit-next/global.d.ts rename to packages/devtools-kit/global.d.ts index 9f3d8856..5eb95498 100644 --- a/packages/devtools-kit-next/global.d.ts +++ b/packages/devtools-kit/global.d.ts @@ -1,8 +1,8 @@ -import type { DevToolsContext, DevToolsState, Router, RouterInfo } from './src/types' +import type { DevToolsContext, DevToolsEnv, DevToolsHook, DevToolsState, Router, RouterInfo } from './src/types' /* eslint-disable vars-on-top, no-var */ declare global { - var __VUE_DEVTOOLS_GLOBAL_HOOK__: unknown + var __VUE_DEVTOOLS_GLOBAL_HOOK__: DevToolsHook var __VUE_DEVTOOLS_GLOBAL_STATE__: DevToolsState var __VUE_DEVTOOLS_CONTEXT__: DevToolsContext var __VUE_DEVTOOLS_APP_RECROD_INFO__: { @@ -11,6 +11,7 @@ declare global { } var __VUE_DEVTOOLS_ROUTER__: Router | null var __VUE_DEVTOOLS_ROUTER_INFO__: RouterInfo + var __VUE_DEVTOOLS_ENV__: DevToolsEnv } export { } diff --git a/packages/devtools-kit/package.json b/packages/devtools-kit/package.json index ee3c99c2..c804eda5 100644 --- a/packages/devtools-kit/package.json +++ b/packages/devtools-kit/package.json @@ -1,7 +1,8 @@ { "name": "@vue/devtools-kit", "type": "module", - "version": "7.0.13", + "version": "7.0.11", + "private": true, "author": "webfansplz", "license": "MIT", "exports": { @@ -12,7 +13,9 @@ }, "main": "./dist/index.cjs", "module": "./dist/index.js", + "types": "./types.d.ts", "files": [ + "**.d.ts", "dist" ], "scripts": { @@ -21,7 +24,6 @@ "stub": "tsup --watch --onSuccess 'tsup --dts-only'" }, "dependencies": { - "@vue/devtools-schema": "workspace:^", "@vue/devtools-shared": "workspace:^", "hookable": "^5.5.3", "mitt": "^3.0.1", @@ -29,7 +31,8 @@ "speakingurl": "^14.0.1" }, "devDependencies": { - "vue": "^3.4.15", + "@types/speakingurl": "^13.0.6", + "vue": "^3.4.14", "vue-router": "^4.2.5" } } diff --git a/packages/devtools-kit/src/api/api.ts b/packages/devtools-kit/src/api/api.ts new file mode 100644 index 00000000..487d14b0 --- /dev/null +++ b/packages/devtools-kit/src/api/api.ts @@ -0,0 +1,230 @@ +import { TimelineLayerItem, addTimelineLayer } from '../core/timeline' +import { InspectorApiPayload, addInspector, getInspector, updateInspector } from '../core/inspector' +import { toggleActiveAppRecord } from '../core/app-record' +import type { VueAppInstance } from '../types' +import { highlight as highlightElement, inspectComponentHighLighter, scrollToComponent, toggleComponentHighLighter, unhighlight as unhighlightElement } from '../core/component-highlighter' +import { devtoolsContext } from '../state' +import { now as nowFn, parse, stringify } from '../shared' +import { StateEditor } from '../core/component/state/editor' +import type { InspectorStateEditorPayload } from '../core/component/types' +import { addCustomTab } from '../core/custom-tab' +import type { CustomTab } from '../core/custom-tab/types' +import { addCustomCommand, removeCustomCommand } from '../core/custom-command' +import type { CustomCommand } from '../core/custom-command' +import { getComponentInspector } from '../core/component-inspector' +import type { OpenInEditorOptions } from '../core/open-in-editor' +import { openInEditor } from '../core/open-in-editor' + +import { DevToolsEventParams, DevToolsEvents, apiHooks } from './hook' +import { on } from './on' +import { remove } from './off' + +export { collectDevToolsPlugin } from './plugin' + +export class DevToolsPluginApi { + public on: typeof on + public clear = remove + constructor() { + this.on = on + } + + // #region compatible with old devtools + + // timeline layer + addTimelineLayer(payload: TimelineLayerItem) { + addTimelineLayer(payload) + } + + // timeline event + addTimelineEvent(...params: DevToolsEventParams) { + apiHooks.callHook(DevToolsEvents.ADD_TIMELINE_EVENT, ...params) + } + + // add inspector + addInspector(payload: InspectorApiPayload) { + addInspector({ + id: payload.id, + nodeId: payload.id, + filter: '', + treeFilterPlaceholder: payload.treeFilterPlaceholder || '', + }) + } + + highlightElement(instance: VueAppInstance) { + highlightElement(instance) + } + + unhighlightElement() { + unhighlightElement() + } + + // inspector + + async getInspectorTree(payload: DevToolsEventParams[0] = {}) { + const { inspectorId, filter = '', instanceId = '' } = payload + const _payload = { + app: devtoolsContext.appRecord?.app, + inspectorId, + instanceId, + filter, + rootNodes: [], + } + + updateInspector(inspectorId!, { + filter, + }) + + await new Promise((resolve) => { + // @ts-expect-error hookable + apiHooks.callHookWith(async (callbacks) => { + await Promise.all(callbacks.map(cb => cb(_payload))) + resolve() + }, DevToolsEvents.GET_INSPECTOR_TREE) + }) + + return stringify(_payload.rootNodes) as string + } + + getInspectorState(payload: { inspectorId?: string, nodeId?: string } = {}) { + const { inspectorId, nodeId } = payload + const _payload = { + app: devtoolsContext.appRecord?.app, + inspectorId, + nodeId, + } + + updateInspector(inspectorId!, { + nodeId, + }) + // @ts-expect-error hookable + apiHooks.callHookWith((callbacks) => { + callbacks.forEach(cb => cb(_payload)) + }, DevToolsEvents.GET_INSPECTOR_STATE) + + // @ts-expect-error TODO: types + const state = _payload.state + + delete state.instance + return stringify(state) as string + } + + async editInspectorState(payload: InspectorStateEditorPayload) { + const stateEditor = new StateEditor() + apiHooks.callHook(DevToolsEvents.EDIT_INSPECTOR_STATE, { + ...payload, + app: devtoolsContext.appRecord?.app, + set: (obj, path = payload.path, value = payload.state.value, cb) => { + stateEditor.set(obj, path, value, cb || stateEditor.createDefaultSetCallback(payload.state)) + }, + }) + } + + async sendInspectorTree(inspectorId: string) { + const inspector = getInspector(inspectorId) + if (inspector) { + const res = await this.getInspectorTree({ + inspectorId, + }) + apiHooks.callHook(DevToolsEvents.SEND_INSPECTOR_TREE, stringify({ + inspectorId, + data: parse(res), + }) as string) + } + } + + async sendInspectorState(inspectorId: string) { + const inspector = getInspector(inspectorId) + if (inspector && inspector.nodeId) { + const res = await this.getInspectorState({ + inspectorId, + nodeId: inspector.nodeId, + }) + apiHooks.callHook(DevToolsEvents.SEND_INSPECTOR_STATE, stringify({ ...parse(res), inspectorId }) as string) + } + } + + async getComponentInstances(app: VueAppInstance) { + const appRecord = app.__VUE_DEVTOOLS_APP_RECORD__ + const appId = appRecord.id.toString() + const instances = [...appRecord.instanceMap] + .filter(([key]) => key.split(':')[0] === appId) + .map(([,instance]) => instance) + return instances + } + + visitComponentTree(...params: DevToolsEventParams) { + apiHooks.callHook(DevToolsEvents.VISIT_COMPONENT_TREE, ...params) + } + + notifyComponentUpdate() {} + now() { + return nowFn() + } + + getSettings() { + return { + logStoreChanges: null, + } + } + + // #endregion compatible with old devtools + + // #region highlighter + toggleComponentInspector(...params: DevToolsEventParams) { + return toggleComponentHighLighter(...params) + } + + inspectComponentInspector() { + return inspectComponentHighLighter() + } + + scrollToComponent(...params: DevToolsEventParams) { + return scrollToComponent(...params) + } + + getComponentBoundingRect(...params: DevToolsEventParams) { + const { inspectorId, instanceId = '' } = params[0] + const _payload = { + app: devtoolsContext.appRecord?.app, + inspectorId, + instanceId, + rect: { + top: 0, + left: 0, + width: 0, + height: 0, + }, + } + // @ts-expect-error hookable + apiHooks.callHookWith((callbacks) => { + callbacks.map(cb => cb(_payload)) + }, DevToolsEvents.GET_COMPONENT_BOUNDING_RECT) + return stringify(_payload.rect) as string + } + + // #endregion highlighter + + toggleApp(id: string) { + return toggleActiveAppRecord(id) + } + + addCustomTab(tab: CustomTab) { + addCustomTab(tab) + } + + addCustomCommand(action: CustomCommand) { + addCustomCommand(action) + } + + removeCustomCommand(actionId: CustomCommand['id']) { + removeCustomCommand(actionId) + } + + openInEditor(payload: OpenInEditorOptions) { + openInEditor(payload) + } + + getVueInspector() { + return getComponentInspector() + } +} diff --git a/packages/devtools-kit/src/api/hook.ts b/packages/devtools-kit/src/api/hook.ts new file mode 100644 index 00000000..266dc512 --- /dev/null +++ b/packages/devtools-kit/src/api/hook.ts @@ -0,0 +1,67 @@ +import type { HookKeys, Hookable } from 'hookable' +import { target } from '@vue/devtools-shared' +import { createHooks } from 'hookable' +import type { TimelineEvent } from '../core/timeline' +import type { ComponentHighLighterOptions, ScrollToComponentOptions } from '../core/component-highlighter' +import type { ComponentBoundingRectApiPayload, ComponentTreeNode, InspectorStateApiPayload, InspectorStateEditorPayload, InspectorTreeApiPayload } from '../core/component/types' +import type { DevToolsState, RouterInfo, VueAppInstance } from '../types' +import type { CustomTab } from '../core/custom-tab/types' +import type { CustomCommand } from '../core/custom-command' + +export enum DevToolsEvents { + DEVTOOLS_STATE_UPDATED = 'devtools:state-updated', + DEVTOOLS_CONNECTED_UPDATED = 'devtools:connected-updated', + ROUTER_INFO_UPDATED = 'router-info:updated', + COMPONENT_STATE_INSPECT = 'component-state:inspect', + TOGGLE_COMPONENT_HIGHLIGHTER = 'component-highlighter:toggle', + GET_COMPONENT_BOUNDING_RECT = 'component-bounding-rect:get', + SCROLL_TO_COMPONENT = 'scroll-to-component', + GET_INSPECTOR_TREE = 'inspector-tree:get', + SEND_INSPECTOR_TREE = 'inspector-tree:send', + GET_INSPECTOR_STATE = 'inspector-state:get', + EDIT_INSPECTOR_STATE = 'inspector-state:edit', + SEND_INSPECTOR_STATE = 'inspector-state:send', + VISIT_COMPONENT_TREE = 'component-tree:visit', + ADD_TIMELINE_EVENT = 'timeline:add-event', + CUSTOM_TABS_UPDATED = 'custom-tabs:updated', + CUSTOM_COMMANDS_UPDATED = 'custom-commands:updated', +} + +export interface DevToolsEvent { + // timeline + [DevToolsEvents.ADD_TIMELINE_EVENT]: (payload: TimelineEvent) => void + // router + [DevToolsEvents.ROUTER_INFO_UPDATED]: (routerInfo: RouterInfo) => void + // highlighter + [DevToolsEvents.TOGGLE_COMPONENT_HIGHLIGHTER]: (payload: ComponentHighLighterOptions) => void + [DevToolsEvents.SCROLL_TO_COMPONENT]: (payload: ScrollToComponentOptions) => void + [DevToolsEvents.GET_COMPONENT_BOUNDING_RECT]: (payload: ComponentBoundingRectApiPayload) => void + // state + [DevToolsEvents.DEVTOOLS_STATE_UPDATED]: (state: DevToolsState, oldState: DevToolsState) => void + [DevToolsEvents.DEVTOOLS_CONNECTED_UPDATED]: (state: DevToolsState, oldState: DevToolsState) => void + // inspector + [DevToolsEvents.COMPONENT_STATE_INSPECT]: (payload: { + componentInstance: VueAppInstance | undefined + app: VueAppInstance | undefined + instanceData: InspectorStateApiPayload['state'] + }) => void + [DevToolsEvents.GET_INSPECTOR_TREE]: (payload: InspectorTreeApiPayload) => void + [DevToolsEvents.SEND_INSPECTOR_TREE]: (payload: string) => void + [DevToolsEvents.GET_INSPECTOR_STATE]: (payload: InspectorStateApiPayload) => void + [DevToolsEvents.EDIT_INSPECTOR_STATE]: (payload: InspectorStateEditorPayload) => void + [DevToolsEvents.SEND_INSPECTOR_STATE]: (payload: string) => void + [DevToolsEvents.VISIT_COMPONENT_TREE]: (payload: { + componentInstance: VueAppInstance | undefined + app: VueAppInstance | undefined + treeNode: ComponentTreeNode + filter: string + }) => void + // custom tabs + [DevToolsEvents.CUSTOM_TABS_UPDATED]: (payload: CustomTab[]) => void + // custom command + [DevToolsEvents.CUSTOM_COMMANDS_UPDATED]: (payload: CustomCommand[]) => void +} + +export type DevToolsEventParams = Parameters + +export const apiHooks: Hookable> = target.__VUE_DEVTOOLS_API_HOOK ??= createHooks() diff --git a/packages/devtools-kit/src/api/index.ts b/packages/devtools-kit/src/api/index.ts index 2dbcba52..be4f099c 100644 --- a/packages/devtools-kit/src/api/index.ts +++ b/packages/devtools-kit/src/api/index.ts @@ -1,221 +1,3 @@ -import { devtoolsContext } from '../core/general/state' -import { now as nowFn, parse, stringify } from '../shared' -import type { AddInspectorApiPayload, InspectorStateEditorPayload } from '../core/component/types' -import { addInspector, getInspector, updateInspector } from '../core/general/inspector' -import type { TimelineEvent } from '../core/timeline' -import { addTimelineLayer } from '../core/timeline' -import { StateEditor } from '../core/component/state/editor' -import { openInEditor } from '../core/open-in-editor' -import { toggleAppRecord } from '../core/general/app-record' -import type { OpenInEditorOptions } from '../core/open-in-editor' -import { addCustomTab } from '../core/custom-tab' -import type { CustomTab } from '../core/custom-tab/types' -import { addCustomCommand, removeCustomCommand } from '../core/custom-command' -import type { CustomCommand } from '../core/custom-command' - -import { getVueInspector } from '../core/vue-inspector' -import { highlight as highlightElement, inspectComponentInspector, scrollToComponent, toggleComponentInspector, unhighlight as unhighlightElement } from '../core/component-inspector' -import { clear } from './off' -import type { DevToolsEvent } from './on' -import { DevToolsEvents, apiHooks, on } from './on' - -export { DevToolsEvents, apiHooks } from './on' +export * from './api' export * from './plugin' - -export class DevToolsPluginApi { - public on: typeof on - public clear: typeof clear - constructor() { - this.on = on - this.clear = clear - } - - toggleApp(id: string) { - return toggleAppRecord(id) - } - - addTimelineEvent(payload: TimelineEvent) { - apiHooks.callHook(DevToolsEvents.ADD_TIMELINE_EVENT, payload) - } - - toggleComponentInspector(payload: Parameters[0]) { - return toggleComponentInspector(payload) - } - - inspectComponentInspector() { - return inspectComponentInspector() - } - - scrollToComponent(payload: Parameters[0]) { - return scrollToComponent(payload) - } - - getComponentBoundingRect(payload: Parameters[0]) { - const { inspectorId, instanceId = '' } = payload - const _payload = { - app: devtoolsContext.appRecord.app, - inspectorId, - instanceId, - rect: { - top: 0, - left: 0, - width: 0, - height: 0, - }, - } - // @ts-expect-error hookable - apiHooks.callHookWith((callbacks) => { - callbacks.map(cb => cb(_payload)) - }, DevToolsEvents.GET_COMPONENT_BOUNDING_RECT) - return stringify(_payload.rect) as string - } - - async getInspectorTree(payload: Parameters[0] = {}) { - const { inspectorId, filter = '', instanceId = '' } = payload - const _payload = { - app: devtoolsContext.appRecord.app, - inspectorId, - instanceId, - filter, - rootNodes: [], - } - - updateInspector(inspectorId!, { - filter, - }) - - await new Promise((resolve) => { - // @ts-expect-error hookable - apiHooks.callHookWith(async (callbacks) => { - await Promise.all(callbacks.map(cb => cb(_payload))) - resolve() - }, DevToolsEvents.GET_INSPECTOR_TREE) - }) - - return stringify(_payload.rootNodes) as string - } - - getInspectorState(payload: { inspectorId?: string, nodeId?: string } = {}) { - const { inspectorId, nodeId } = payload - const _payload = { - app: devtoolsContext.appRecord.app, - inspectorId, - nodeId, - } - - updateInspector(inspectorId!, { - nodeId, - }) - // @ts-expect-error hookable - apiHooks.callHookWith((callbacks) => { - callbacks.forEach(cb => cb(_payload)) - }, DevToolsEvents.GET_INSPECTOR_STATE) - - // @ts-expect-error TODO: types - const state = _payload.state - - delete state.instance - return stringify(state) as string - } - - async editInspectorState(payload: InspectorStateEditorPayload) { - const stateEditor = new StateEditor() - apiHooks.callHook(DevToolsEvents.EDIT_INSPECTOR_STATE, { - ...payload, - app: devtoolsContext.appRecord.app, - set: (obj, path = payload.path, value = payload.state.value, cb) => { - stateEditor.set(obj, path, value, cb || stateEditor.createDefaultSetCallback(payload.state)) - }, - }) - } - - async sendInspectorTree(inspectorId: string) { - const inspector = getInspector(inspectorId) - if (inspector) { - const res = await this.getInspectorTree({ - inspectorId, - }) - apiHooks.callHook(DevToolsEvents.SEND_INSPECTOR_TREE, stringify({ - inspectorId, - data: parse(res), - }) as string) - } - } - - async sendInspectorState(inspectorId: string) { - const inspector = getInspector(inspectorId) - if (inspector && inspector.nodeId) { - const res = await this.getInspectorState({ - inspectorId, - nodeId: inspector.nodeId, - }) - apiHooks.callHook(DevToolsEvents.SEND_INSPECTOR_STATE, stringify({ ...parse(res), inspectorId }) as string) - } - } - - addCustomTab(tab: CustomTab) { - addCustomTab(tab) - } - - addCustomCommand(action: CustomCommand) { - addCustomCommand(action) - } - - removeCustomCommand(actionId: CustomCommand['id']) { - removeCustomCommand(actionId) - } - - addInspector(payload: AddInspectorApiPayload) { - addInspector({ - id: payload.id, - nodeId: '', - filter: '', - treeFilterPlaceholder: payload.treeFilterPlaceholder ?? '', - }) - } - - openInEditor(payload: OpenInEditorOptions) { - openInEditor(payload) - } - - highlightElement(instance) { - highlightElement(instance) - } - - unhighlightElement() { - unhighlightElement() - } - - async getComponentInstances(app) { - const appRecord = app.__VUE_DEVTOOLS_APP_RECORD__ - const appId = appRecord.id.toString() - const instances = [...appRecord.instanceMap] - .filter(([key]) => key.split(':')[0] === appId) - .map(([,instance]) => instance) - return instances - } - - // Vite only - getVueInspector() { - return getVueInspector() - } - - visitComponentTree(payload: Parameters[0]) { - apiHooks.callHook(DevToolsEvents.VISIT_COMPONENT_TREE, payload) - } - - addTimelineLayer(payload: { id: string, label: string, color: number }) { - addTimelineLayer(payload) - } - - notifyComponentUpdate() {} - now() { - return nowFn() - } - - getSettings() { - return { - logStoreChanges: null, - } - } -} +export * from './hook' diff --git a/packages/devtools-kit/src/api/off.ts b/packages/devtools-kit/src/api/off.ts index 40d9a203..f10322f9 100644 --- a/packages/devtools-kit/src/api/off.ts +++ b/packages/devtools-kit/src/api/off.ts @@ -1,5 +1,5 @@ -import { apiHooks } from './on' +import { apiHooks } from './hook' -export function clear() { +export function remove() { apiHooks.removeAllHooks() } diff --git a/packages/devtools-kit/src/api/on.ts b/packages/devtools-kit/src/api/on.ts index 1e682336..a36a38c8 100644 --- a/packages/devtools-kit/src/api/on.ts +++ b/packages/devtools-kit/src/api/on.ts @@ -1,81 +1,11 @@ -import type { DevToolsState, VueAppInstance } from '@vue/devtools-schema' -import { target } from '@vue/devtools-shared' -import type { HookKeys, Hookable } from 'hookable' -import { createHooks } from 'hookable' -import type { ComponentBoundingRectApiPayload, ComponentTreeNode, InspectorState, InspectorStateApiPayload, InspectorStateEditorPayload, InspectorTreeApiPayload } from '../core/component/types' -import type { ScrollToComponentOptions, ToggleComponentInspectorOptions } from '../core/component-inspector/types' -import type { TimelineEvent } from '../core/timeline' -import type { RouterInfo } from '../core/router' -import type { CustomTab } from '../core/custom-tab/types' -import type { CustomCommand } from '../core/custom-command' - -export enum DevToolsEvents { - DEVTOOLS_STATE_UPDATED = 'devtools:state-updated', - DEVTOOLS_CONNECTED_UPDATED = 'devtools:connected-updated', - ROUTER_INFO_UPDATED = 'router-info:updated', - COMPONENT_STATE_INSPECT = 'component-state:inspect', - TOGGLE_COMPONENT_INSPECTOR = 'component-inspector:toggle', - GET_COMPONENT_BOUNDING_RECT = 'component-bounding-rect:get', - SCROLL_TO_COMPONENT = 'scroll-to-component', - GET_INSPECTOR_TREE = 'inspector-tree:get', - SEND_INSPECTOR_TREE = 'inspector-tree:send', - GET_INSPECTOR_STATE = 'inspector-state:get', - EDIT_INSPECTOR_STATE = 'inspector-state:edit', - SEND_INSPECTOR_STATE = 'inspector-state:send', - VISIT_COMPONENT_TREE = 'component-tree:visit', - ADD_TIMELINE_EVENT = 'timeline:add-event', - CUSTOM_TABS_UPDATED = 'custom-tabs:updated', - CUSTOM_COMMANDS_UPDATED = 'custom-commands:updated', -} - -export interface DevToolsEvent { - [DevToolsEvents.DEVTOOLS_STATE_UPDATED]: (state: DevToolsState, oldState: DevToolsState) => void - [DevToolsEvents.DEVTOOLS_CONNECTED_UPDATED]: (state: DevToolsState, oldState: DevToolsState) => void - [DevToolsEvents.ROUTER_INFO_UPDATED]: (routerInfo: RouterInfo) => void - [DevToolsEvents.COMPONENT_STATE_INSPECT]: (payload: { - componentInstance: VueAppInstance | undefined - app: VueAppInstance | undefined - instanceData: { - id: string - name: string - file: string | undefined - state: InspectorState[] - instance: VueAppInstance | undefined - } - }) => void - [DevToolsEvents.TOGGLE_COMPONENT_INSPECTOR]: (payload: ToggleComponentInspectorOptions) => void - [DevToolsEvents.GET_COMPONENT_BOUNDING_RECT]: (payload: ComponentBoundingRectApiPayload) => void - [DevToolsEvents.SCROLL_TO_COMPONENT]: (payload: ScrollToComponentOptions) => void - [DevToolsEvents.GET_INSPECTOR_TREE]: (payload: InspectorTreeApiPayload) => void - [DevToolsEvents.SEND_INSPECTOR_TREE]: (payload: string) => void - [DevToolsEvents.GET_INSPECTOR_STATE]: (payload: InspectorStateApiPayload) => void - [DevToolsEvents.EDIT_INSPECTOR_STATE]: (payload: InspectorStateEditorPayload) => void - [DevToolsEvents.SEND_INSPECTOR_STATE]: (payload: string) => void - [DevToolsEvents.VISIT_COMPONENT_TREE]: (payload: { - componentInstance: VueAppInstance | undefined - app: VueAppInstance | undefined - treeNode: ComponentTreeNode - filter: string - }) => void - [DevToolsEvents.ADD_TIMELINE_EVENT]: (payload: TimelineEvent) => void - [DevToolsEvents.CUSTOM_TABS_UPDATED]: (payload: CustomTab[]) => void - [DevToolsEvents.CUSTOM_COMMANDS_UPDATED]: (payload: CustomCommand[]) => void -} - -export const apiHooks: Hookable> = target.__VUE_DEVTOOLS_API_HOOK ??= createHooks() -// export const apiHooks: Hookable> = createHooks() +import { DevToolsEvents, apiHooks } from './hook' +import type { DevToolsEvent } from './hook' export const on = { - devtoolsStateUpdated(fn: DevToolsEvent[DevToolsEvents.DEVTOOLS_STATE_UPDATED]) { - apiHooks.hook(DevToolsEvents.DEVTOOLS_STATE_UPDATED, fn) - }, - routerInfoUpdated(fn: DevToolsEvent[DevToolsEvents.ROUTER_INFO_UPDATED]) { - apiHooks.hook(DevToolsEvents.ROUTER_INFO_UPDATED, fn) - }, - getComponentBoundingRect(fn: DevToolsEvent[DevToolsEvents.GET_COMPONENT_BOUNDING_RECT]) { - apiHooks.hook(DevToolsEvents.GET_COMPONENT_BOUNDING_RECT, fn) + // #region compatible with old devtools + addTimelineEvent(fn: DevToolsEvent[DevToolsEvents.ADD_TIMELINE_EVENT]) { + apiHooks.hook(DevToolsEvents.ADD_TIMELINE_EVENT, fn) }, - // compatible inspectComponent(fn: DevToolsEvent[DevToolsEvents.COMPONENT_STATE_INSPECT]) { apiHooks.hook(DevToolsEvents.COMPONENT_STATE_INSPECT, fn) }, @@ -88,24 +18,39 @@ export const on = { getInspectorState(fn: DevToolsEvent[DevToolsEvents.GET_INSPECTOR_STATE]) { apiHooks.hook(DevToolsEvents.GET_INSPECTOR_STATE, fn) }, - // private sendInspectorTree(fn: DevToolsEvent[DevToolsEvents.SEND_INSPECTOR_TREE]) { apiHooks.hook(DevToolsEvents.SEND_INSPECTOR_TREE, fn) }, sendInspectorState(fn: DevToolsEvent[DevToolsEvents.SEND_INSPECTOR_STATE]) { apiHooks.hook(DevToolsEvents.SEND_INSPECTOR_STATE, fn) }, - addTimelineEvent(fn: DevToolsEvent[DevToolsEvents.ADD_TIMELINE_EVENT]) { - apiHooks.hook(DevToolsEvents.ADD_TIMELINE_EVENT, fn) - }, editInspectorState(fn: DevToolsEvent[DevToolsEvents.EDIT_INSPECTOR_STATE]) { apiHooks.hook(DevToolsEvents.EDIT_INSPECTOR_STATE, fn) }, editComponentState() {}, + // #endregion compatible with old devtools + + // router + routerInfoUpdated(fn: DevToolsEvent[DevToolsEvents.ROUTER_INFO_UPDATED]) { + apiHooks.hook(DevToolsEvents.ROUTER_INFO_UPDATED, fn) + }, + + // component highlighter + getComponentBoundingRect(fn: DevToolsEvent[DevToolsEvents.GET_COMPONENT_BOUNDING_RECT]) { + apiHooks.hook(DevToolsEvents.GET_COMPONENT_BOUNDING_RECT, fn) + }, + + // custom tabs customTabsUpdated(fn: DevToolsEvent[DevToolsEvents.CUSTOM_TABS_UPDATED]) { apiHooks.hook(DevToolsEvents.CUSTOM_TABS_UPDATED, fn) }, + + // custom commands customCommandsUpdated(fn: DevToolsEvent[DevToolsEvents.CUSTOM_COMMANDS_UPDATED]) { apiHooks.hook(DevToolsEvents.CUSTOM_COMMANDS_UPDATED, fn) }, -} + + devtoolsStateUpdated(fn: DevToolsEvent[DevToolsEvents.DEVTOOLS_STATE_UPDATED]) { + apiHooks.hook(DevToolsEvents.DEVTOOLS_STATE_UPDATED, fn) + }, +} as const diff --git a/packages/devtools-kit/src/api/plugin.ts b/packages/devtools-kit/src/api/plugin.ts index e2540ebf..38fa335b 100644 --- a/packages/devtools-kit/src/api/plugin.ts +++ b/packages/devtools-kit/src/api/plugin.ts @@ -1,17 +1,18 @@ -import { DevToolsHooks } from '@vue/devtools-schema' -import type { PluginDescriptor, PluginSetupFunction, VueAppInstance } from '@vue/devtools-schema' -import { getAppRecord } from '../core/component/general' -import { devtoolsState } from '../core/general/state' -import { devtoolsHooks } from '../core/general/hook' +import { PluginDescriptor, PluginSetupFunction, VueAppInstance } from '../types' +import { devtoolsAppRecords, devtoolsState } from '../state' +import { hook } from '../hook' import { getRouterDevToolsId } from '../core/router' -import type { DevToolsPluginApi } from './index' +import type { DevToolsPluginApi } from './api' -export function collectRegisteredPlugin(pluginDescriptor: PluginDescriptor, setupFn: PluginSetupFunction) { +export function collectDevToolsPlugin(pluginDescriptor: PluginDescriptor, setupFn: PluginSetupFunction) { devtoolsState.pluginBuffer.push([pluginDescriptor, setupFn]) } -export async function registerPlugin(options: { app: VueAppInstance, api: DevToolsPluginApi }) { - const { app, api } = options +export function setupDevToolsPlugin(pluginDescriptor: PluginDescriptor, setupFn: PluginSetupFunction) { + return hook.setupDevToolsPlugin(pluginDescriptor, setupFn) +} + +export function registerPlugin(app: VueAppInstance, api: DevToolsPluginApi) { const plugins = devtoolsState.pluginBuffer.filter(([plugin]) => plugin.app === app) plugins.forEach(async ([plugin, setupFn]) => { if (plugin.packageName === 'vue-query') { @@ -23,12 +24,11 @@ export async function registerPlugin(options: { app: VueAppInstance, api: DevToo return } - const appRecord = await getAppRecord(plugin.app) // edge case for router plugin if (plugin.packageName === 'vue-router') { const id = getRouterDevToolsId(`${plugin.id}`) if (plugin.app === app) { - devtoolsState.appRecords = devtoolsState.appRecords.map(item => ({ + devtoolsAppRecords.value = devtoolsAppRecords.value.map(item => ({ ...item, routerId: id, })) @@ -37,7 +37,7 @@ export async function registerPlugin(options: { app: VueAppInstance, api: DevToo setupFn(api) }) - devtoolsState.appRecords = devtoolsState.appRecords.map((record) => { + devtoolsAppRecords.value = devtoolsAppRecords.value.map((record) => { const globalProperties = record.app?.config?.globalProperties if (!globalProperties) return record @@ -52,7 +52,3 @@ export async function registerPlugin(options: { app: VueAppInstance, api: DevToo } }) } - -export function setupDevToolsPlugin(pluginDescriptor: PluginDescriptor, setupFn: PluginSetupFunction) { - return devtoolsHooks.callHook(DevToolsHooks.SETUP_DEVTOOLS_PLUGIN, pluginDescriptor, setupFn) -} diff --git a/packages/devtools-kit-next/src/state/app-record.ts b/packages/devtools-kit/src/core/app-record/index.ts similarity index 59% rename from packages/devtools-kit-next/src/state/app-record.ts rename to packages/devtools-kit/src/core/app-record/index.ts index ac5dcd0d..1260aec5 100644 --- a/packages/devtools-kit-next/src/state/app-record.ts +++ b/packages/devtools-kit/src/core/app-record/index.ts @@ -1,54 +1,9 @@ -import { target } from '@vue/devtools-shared' import slug from 'speakingurl' -import { AppRecord, VueAppInstance } from '../types' -import { DevToolsPluginApi } from '../api' -import { registerPlugin } from '../api/plugin' -import { devtoolsState } from './state' -import { devtoolsContext } from './context' - -interface DevToolsAppRecords { - value: AppRecord[] - active: AppRecord - activeId: string -} - -export const devtoolsAppRecords = new Proxy(devtoolsState.appRecords as unknown as DevToolsAppRecords, { - get(_, property) { - if (property === 'value') - return devtoolsState.appRecords - else if (property === 'active') - return devtoolsState.activeAppRecord - else if (property === 'activeId') - return devtoolsState.activeAppRecordId - }, - set(target, property, value) { - if (property === 'value') { - devtoolsState.appRecords = value - } - - else if (property === 'active') { - const _value = value as AppRecord - - // sync to context - devtoolsState.activeAppRecord = _value - devtoolsContext.appRecord = _value - devtoolsContext.api = _value.api! - // @TODO: check ? - // devtoolsContext.inspector = _value.inspector ?? [] - } - - else if (property === 'activeId') { - devtoolsState.activeAppRecordId = value - } - - return true - }, -}) - -const appRecordInfo = target.__VUE_DEVTOOLS_APP_RECROD_INFO__ ??= { - id: 0, - appIds: new Set(), -} +import { AppRecord, VueAppInstance } from '../../types' +import { DevToolsPluginApi } from '../../api' +import { registerPlugin } from '../../api/plugin' +import { registerComponentDevToolsPlugin } from '../../plugins' +import { appRecordInfo, devtoolsAppRecords, devtoolsContext, devtoolsState } from '../../state' function getAppRecordName(app: VueAppInstance['appContext']['app'], fallbackName: string) { return app?._component?.name || `App ${fallbackName}` @@ -108,18 +63,27 @@ export function createAppRecord(app: VueAppInstance['appContext']['app']): AppRe } export async function setActiveAppRecord(appRecord: AppRecord) { - // @TODO + await registerComponentDevToolsPlugin(appRecord?.app as unknown as VueAppInstance) devtoolsAppRecords.active = appRecord devtoolsAppRecords.activeId = `${appRecord.id}` registerPlugin(appRecord.app as unknown as VueAppInstance, appRecord.api!) } export async function toggleActiveAppRecord(id: string) { - // @TODO + devtoolsContext.componentPluginHookBuffer.forEach(cleanup => cleanup()) + devtoolsContext.api.clear() + devtoolsContext.clear() const appRecord = devtoolsAppRecords.value.find(record => record.id === id) if (appRecord) { + devtoolsState.pluginBuffer = devtoolsState.pluginBuffer.filter(([plugin]) => plugin.id !== 'components') const api = new DevToolsPluginApi() appRecord.api = api setActiveAppRecord(appRecord) + + // @TODO: find a better way to handle it + window.postMessage({ + event: 'toggle-app-record', + target: 'vue-devtools', + }) } } diff --git a/packages/devtools-kit/src/core/component-highlighter/index.ts b/packages/devtools-kit/src/core/component-highlighter/index.ts new file mode 100644 index 00000000..587b091a --- /dev/null +++ b/packages/devtools-kit/src/core/component-highlighter/index.ts @@ -0,0 +1,252 @@ +import type { VueAppInstance } from '../../types' +import { getComponentId, getComponentInstance, getInstanceName } from '../component/utils' +import { devtoolsContext } from '../../state' +import { getRootElementsFromComponentInstance } from '../component/tree/el' +import { getComponentBoundingRect } from '../component/state/bounding-rect' +import type { ComponentHighLighterOptions, ScrollToComponentOptions } from './types' + +export type * from './types' + +const CONTAINER_ELEMENT_ID = '__vue-devtools-component-inspector__' +const CARD_ELEMENT_ID = '__vue-devtools-component-inspector__card__' +const COMPONENT_NAME_ELEMENT_ID = '__vue-devtools-component-inspector__name__' +const INDICATOR_ELEMENT_ID = '__vue-devtools-component-inspector__indicator__' + +const containerStyles = { + display: 'block', + zIndex: 2147483640, + position: 'fixed', + backgroundColor: '#42b88325', + border: '1px solid #42b88350', + borderRadius: '5px', + transition: 'all 0.1s ease-in', + pointerEvents: 'none', +} + +const cardStyles = { + fontFamily: 'Arial, Helvetica, sans-serif', + padding: '5px 8px', + borderRadius: '4px', + textAlign: 'left', + position: 'absolute', + left: 0, + color: '#e9e9e9', + fontSize: '14px', + fontWeight: 600, + lineHeight: '24px', + backgroundColor: '#42b883', + boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1)', +} + +const indicatorStyles = { + display: 'inline-block', + fontWeight: 400, + fontStyle: 'normal', + fontSize: '12px', + opacity: 0.7, +} + +function getContainerElement() { + return document.getElementById(CONTAINER_ELEMENT_ID) +} + +function getCardElement() { + return document.getElementById(CARD_ELEMENT_ID) +} + +function getIndicatorElement() { + return document.getElementById(INDICATOR_ELEMENT_ID) +} + +function getNameElement() { + return document.getElementById(COMPONENT_NAME_ELEMENT_ID) +} + +function getStyles(bounds: ComponentHighLighterOptions['bounds']) { + return { + left: `${Math.round(bounds.left * 100) / 100}px`, + top: `${Math.round(bounds.top * 100) / 100}px`, + width: `${Math.round(bounds.width * 100) / 100}px`, + height: `${Math.round(bounds.height * 100) / 100}px`, + } satisfies Partial +} + +function create(options: ComponentHighLighterOptions & { elementId?: string, style?: Partial }) { + // create container + const containerEl = document.createElement('div') + containerEl.id = options.elementId ?? CONTAINER_ELEMENT_ID + Object.assign(containerEl.style, { + ...containerStyles, + ...getStyles(options.bounds), + ...options.style, + }) + + // create card + const cardEl = document.createElement('span') + cardEl.id = CARD_ELEMENT_ID + Object.assign(cardEl.style, { + ...cardStyles, + top: options.bounds.top < 35 ? 0 : '-35px', + }) + + // create name + const nameEl = document.createElement('span') + nameEl.id = COMPONENT_NAME_ELEMENT_ID + nameEl.innerHTML = `<${options.name}>  ` + + // create indicator + const indicatorEl = document.createElement('i') + indicatorEl.id = INDICATOR_ELEMENT_ID + indicatorEl.innerHTML = `${Math.round(options.bounds.width * 100) / 100} x ${Math.round(options.bounds.height * 100) / 100}` + Object.assign(indicatorEl.style, indicatorStyles) + + // append + cardEl.appendChild(nameEl) + cardEl.appendChild(indicatorEl) + containerEl.appendChild(cardEl) + document.body.appendChild(containerEl) + return containerEl +} + +function update(options: ComponentHighLighterOptions) { + const containerEl = getContainerElement() + const cardEl = getCardElement()! + const nameEl = getNameElement()! + const indicatorEl = getIndicatorElement()! + + if (containerEl) { + Object.assign(containerEl.style, { + ...containerStyles, + ...getStyles(options.bounds), + }) + Object.assign(cardEl.style, { + top: options.bounds.top < 35 ? 0 : '-35px', + }) + nameEl.innerHTML = `<${options.name}>  ` + indicatorEl.innerHTML = `${Math.round(options.bounds.width * 100) / 100} x ${Math.round(options.bounds.height * 100) / 100}` + } +} + +export function toggleComponentHighLighter(options: ComponentHighLighterOptions) { + if (options.visible) { + const instance = getComponentInstance(devtoolsContext.appRecord!, options.id) + if (instance && (options.bounds.width || options.bounds.height)) { + const name = getInstanceName(instance) + const el = getContainerElement() + el ? update({ ...options, name }) : create({ ...options, name }) + } + } + else { + const el = getContainerElement() + if (el) + el.style.display = 'none' + } +} + +export function highlight(instance: VueAppInstance) { + const bounds = getComponentBoundingRect(instance) + const name = getInstanceName(instance) + const container = getContainerElement() + container ? update({ bounds, name }) : create({ bounds, name }) +} + +export function unhighlight() { + const el = getContainerElement() + if (el) + el.style.display = 'none' +} + +let inspectInstance: VueAppInstance = null! +function inspectFn(e: MouseEvent) { + const target = e.target as { __vueParentComponent?: VueAppInstance } + if (target) { + const instance = target.__vueParentComponent + if (instance) { + inspectInstance = instance + const el = instance.vnode.el as HTMLElement | undefined + if (el) { + const bounds = getComponentBoundingRect(instance) + const name = getInstanceName(instance) + const container = getContainerElement() + container ? update({ bounds, name }) : create({ bounds, name }) + } + } + } +} + +function selectComponentFn(e: MouseEvent, cb) { + e.preventDefault() + e.stopPropagation() + if (inspectInstance) { + const app = devtoolsContext.appRecord?.app as unknown as VueAppInstance + getComponentId({ + app, + uid: app.uid, + instance: inspectInstance, + }).then((id) => { + cb(id) + }) + } +} + +export function inspectComponentHighLighter() { + window.addEventListener('mouseover', inspectFn) + return new Promise((resolve) => { + function onSelect(e: MouseEvent) { + e.preventDefault() + e.stopPropagation() + selectComponentFn(e, (id: string) => { + window.removeEventListener('click', onSelect) + window.removeEventListener('mouseover', inspectFn) + const el = getContainerElement() + if (el) + el.style.display = 'none' + resolve(JSON.stringify({ id })) + }) + } + window.addEventListener('click', onSelect) + }) +} + +export function scrollToComponent(options: ScrollToComponentOptions) { + const instance = getComponentInstance(devtoolsContext.appRecord!, options.id) + if (instance) { + const [el] = getRootElementsFromComponentInstance(instance) + // @ts-expect-error type mismatch + if (typeof el.scrollIntoView === 'function') { + // @ts-expect-error type mismatch + el.scrollIntoView({ + behavior: 'smooth', + }) + } + else { + const bounds = getComponentBoundingRect(instance) + const scrollTarget = document.createElement('div') + const styles = { + ...getStyles(bounds), + position: 'absolute', + } + Object.assign(scrollTarget.style, styles) + document.body.appendChild(scrollTarget) + scrollTarget.scrollIntoView({ + behavior: 'smooth', + }) + setTimeout(() => { + document.body.removeChild(scrollTarget) + }, 2000) + } + + setTimeout(() => { + const bounds = getComponentBoundingRect(instance) + if (bounds.width || bounds.height) { + const name = getInstanceName(instance) + const el = getContainerElement() + el ? update({ ...options, name, bounds }) : create({ ...options, name, bounds }) + setTimeout(() => { + if (el) + el.style.display = 'none' + }, 1500) + } + }, 1200) + } +} diff --git a/packages/devtools-kit/src/core/component-inspector/types.ts b/packages/devtools-kit/src/core/component-highlighter/types.ts similarity index 80% rename from packages/devtools-kit/src/core/component-inspector/types.ts rename to packages/devtools-kit/src/core/component-highlighter/types.ts index b320cef7..f10139cd 100644 --- a/packages/devtools-kit/src/core/component-inspector/types.ts +++ b/packages/devtools-kit/src/core/component-highlighter/types.ts @@ -1,6 +1,6 @@ import type { ComponentBoundingRect } from '../component/types' -export interface ToggleComponentInspectorOptions { +export interface ComponentHighLighterOptions { bounds: ComponentBoundingRect name?: string id?: string diff --git a/packages/devtools-kit/src/core/component-inspector/index.ts b/packages/devtools-kit/src/core/component-inspector/index.ts index e27bb3c1..566232ff 100644 --- a/packages/devtools-kit/src/core/component-inspector/index.ts +++ b/packages/devtools-kit/src/core/component-inspector/index.ts @@ -1,251 +1,59 @@ -import type { VueAppInstance } from '@vue/devtools-schema' -import { getComponentInstance } from '../component/general' -import { devtoolsContext } from '../general/state' -import { getComponentId, getInstanceName } from '../component/general/util' -import { getRootElementsFromComponentInstance } from '../component/tree/el' -import { getComponentBoundingRect } from '../component/state/bounding-rect' -import type { ScrollToComponentOptions, ToggleComponentInspectorOptions } from './types' +import { target } from '@vue/devtools-shared' -const CONTAINER_ELEMENT_ID = '__vue-devtools-component-inspector__' -const CARD_ELEMENT_ID = '__vue-devtools-component-inspector__card__' -const COMPONENT_NAME_ELEMENT_ID = '__vue-devtools-component-inspector__name__' -const INDICATOR_ELEMENT_ID = '__vue-devtools-component-inspector__indicator__' - -const containerStyles = { - display: 'block', - zIndex: 2147483640, - position: 'fixed', - backgroundColor: '#42b88325', - border: '1px solid #42b88350', - borderRadius: '5px', - transition: 'all 0.1s ease-in', - pointerEvents: 'none', -} - -const cardStyles = { - fontFamily: 'Arial, Helvetica, sans-serif', - padding: '5px 8px', - borderRadius: '4px', - textAlign: 'left', - position: 'absolute', - left: 0, - color: '#e9e9e9', - fontSize: '14px', - fontWeight: 600, - lineHeight: '24px', - backgroundColor: '#42b883', - boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1)', -} - -const indicatorStyles = { - display: 'inline-block', - fontWeight: 400, - fontStyle: 'normal', - fontSize: '12px', - opacity: 0.7, -} - -function getContainerElement() { - return document.getElementById(CONTAINER_ELEMENT_ID) -} - -function getCardElement() { - return document.getElementById(CARD_ELEMENT_ID) -} - -function getIndicatorElement() { - return document.getElementById(INDICATOR_ELEMENT_ID) -} - -function getNameElement() { - return document.getElementById(COMPONENT_NAME_ELEMENT_ID) -} - -function getStyles(bounds: ToggleComponentInspectorOptions['bounds']) { - return { - left: `${Math.round(bounds.left * 100) / 100}px`, - top: `${Math.round(bounds.top * 100) / 100}px`, - width: `${Math.round(bounds.width * 100) / 100}px`, - height: `${Math.round(bounds.height * 100) / 100}px`, - } satisfies Partial -} - -function create(options: ToggleComponentInspectorOptions & { elementId?: string, style?: Partial }) { - // create container - const containerEl = document.createElement('div') - containerEl.id = options.elementId ?? CONTAINER_ELEMENT_ID - Object.assign(containerEl.style, { - ...containerStyles, - ...getStyles(options.bounds), - ...options.style, - }) - - // create card - const cardEl = document.createElement('span') - cardEl.id = CARD_ELEMENT_ID - Object.assign(cardEl.style, { - ...cardStyles, - top: options.bounds.top < 35 ? 0 : '-35px', - }) - - // create name - const nameEl = document.createElement('span') - nameEl.id = COMPONENT_NAME_ELEMENT_ID - nameEl.innerHTML = `<${options.name}>  ` - - // create indicator - const indicatorEl = document.createElement('i') - indicatorEl.id = INDICATOR_ELEMENT_ID - indicatorEl.innerHTML = `${Math.round(options.bounds.width * 100) / 100} x ${Math.round(options.bounds.height * 100) / 100}` - Object.assign(indicatorEl.style, indicatorStyles) - - // append - cardEl.appendChild(nameEl) - cardEl.appendChild(indicatorEl) - containerEl.appendChild(cardEl) - document.body.appendChild(containerEl) - return containerEl -} - -function update(options: ToggleComponentInspectorOptions) { - const containerEl = getContainerElement() - const cardEl = getCardElement()! - const nameEl = getNameElement()! - const indicatorEl = getIndicatorElement()! - - if (containerEl) { - Object.assign(containerEl.style, { - ...containerStyles, - ...getStyles(options.bounds), - }) - Object.assign(cardEl.style, { - top: options.bounds.top < 35 ? 0 : '-35px', - }) - nameEl.innerHTML = `<${options.name}>  ` - indicatorEl.innerHTML = `${Math.round(options.bounds.width * 100) / 100} x ${Math.round(options.bounds.height * 100) / 100}` +export interface ComponentInspector { + enabled: boolean + position: { + x: number + y: number } -} - -export function toggleComponentInspector(options: ToggleComponentInspectorOptions) { - if (options.visible) { - const instance = getComponentInstance(devtoolsContext.appRecord!, options.id) - if (instance && (options.bounds.width || options.bounds.height)) { - const name = getInstanceName(instance) - const el = getContainerElement() - el ? update({ ...options, name }) : create({ ...options, name }) - } - } - else { - const el = getContainerElement() - if (el) - el.style.display = 'none' + linkParams: { + file: string + line: number + column: number } -} -export function highlight(instance: VueAppInstance) { - const bounds = getComponentBoundingRect(instance) - const name = getInstanceName(instance) - const container = getContainerElement() - container ? update({ bounds, name }) : create({ bounds, name }) + enable: () => void + disable: () => void + toggleEnabled: () => void + openInEditor: (baseUrl: string, file: string, line: number, column: number) => void + onUpdated: () => void } -export function unhighlight() { - const el = getContainerElement() - if (el) - el.style.display = 'none' -} - -let inspectInstance: VueAppInstance = null! -function inspectFn(e: MouseEvent) { - const target = e.target as { __vueParentComponent?: VueAppInstance } - if (target) { - const instance = target.__vueParentComponent - if (instance) { - inspectInstance = instance - const el = instance.vnode.el as HTMLElement | undefined - if (el) { - const bounds = getComponentBoundingRect(instance) - const name = getInstanceName(instance) - const container = getContainerElement() - container ? update({ bounds, name }) : create({ bounds, name }) - } +function waitForInspectorInit(cb: () => void) { + let total = 0 + const timer = setInterval(() => { + if (target.__VUE_INSPECTOR__) { + clearInterval(timer) + total += 30 + cb() } - } + if (total >=/* 5s */ 5000) + clearInterval(timer) + }, 30) } -function selectComponentFn(e: MouseEvent, cb) { - e.preventDefault() - e.stopPropagation() - if (inspectInstance) { - const app = devtoolsContext.appRecord?.app as unknown as VueAppInstance - getComponentId({ - app, - uid: app.uid, - instance: inspectInstance, - }).then((id) => { - cb(id) - }) +function setupInspector() { + const inspector = target.__VUE_INSPECTOR__ + const _openInEditor = inspector.openInEditor + inspector.openInEditor = async (...params: Parameters) => { + inspector.disable() + _openInEditor(...params) } } -export function inspectComponentInspector() { - window.addEventListener('mouseover', inspectFn) - return new Promise((resolve) => { - function onSelect(e: MouseEvent) { - e.preventDefault() - e.stopPropagation() - selectComponentFn(e, (id: string) => { - window.removeEventListener('click', onSelect) - window.removeEventListener('mouseover', inspectFn) - const el = getContainerElement() - if (el) - el.style.display = 'none' - resolve(JSON.stringify({ id })) - }) +export function getComponentInspector(): Promise { + return new Promise((resolve) => { + function setup() { + setupInspector() + resolve(target.__VUE_INSPECTOR__) } - window.addEventListener('click', onSelect) - }) -} - -export function scrollToComponent(options: ScrollToComponentOptions) { - const instance = getComponentInstance(devtoolsContext.appRecord!, options.id) - if (instance) { - const [el] = getRootElementsFromComponentInstance(instance) - // @ts-expect-error type mismatch - if (typeof el.scrollIntoView === 'function') { - // @ts-expect-error type mismatch - el.scrollIntoView({ - behavior: 'smooth', + if (!target.__VUE_INSPECTOR__) { + waitForInspectorInit(() => { + setup() }) } else { - const bounds = getComponentBoundingRect(instance) - const scrollTarget = document.createElement('div') - const styles = { - ...getStyles(bounds), - position: 'absolute', - } - Object.assign(scrollTarget.style, styles) - document.body.appendChild(scrollTarget) - scrollTarget.scrollIntoView({ - behavior: 'smooth', - }) - setTimeout(() => { - document.body.removeChild(scrollTarget) - }, 2000) + setup() } - - setTimeout(() => { - const bounds = getComponentBoundingRect(instance) - if (bounds.width || bounds.height) { - const name = getInstanceName(instance) - const el = getContainerElement() - el ? update({ ...options, name, bounds }) : create({ ...options, name, bounds }) - setTimeout(() => { - if (el) - el.style.display = 'none' - }, 1500) - } - }, 1200) - } + }) } diff --git a/packages/devtools-kit/src/core/component/general/index.ts b/packages/devtools-kit/src/core/component/general/index.ts deleted file mode 100644 index 1871e743..00000000 --- a/packages/devtools-kit/src/core/component/general/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './util' diff --git a/packages/devtools-kit/src/core/component/state/bounding-rect.ts b/packages/devtools-kit/src/core/component/state/bounding-rect.ts index 71e180b4..1e020ce7 100644 --- a/packages/devtools-kit/src/core/component/state/bounding-rect.ts +++ b/packages/devtools-kit/src/core/component/state/bounding-rect.ts @@ -1,6 +1,6 @@ -import type { VueAppInstance } from '@vue/devtools-schema' +import type { VueAppInstance } from '../../../types' import type { ComponentBoundingRect } from '../types' -import { isFragment } from '../general/util' +import { isFragment } from '../utils' // #region rect util function createRect() { diff --git a/packages/devtools-kit/src/core/component/state/custom.ts b/packages/devtools-kit/src/core/component/state/custom.ts index 21cb1603..179c1b30 100644 --- a/packages/devtools-kit/src/core/component/state/custom.ts +++ b/packages/devtools-kit/src/core/component/state/custom.ts @@ -1,5 +1,5 @@ import type { InspectorState } from '../types' -import { getComponentName, getInstanceName } from '../general/util' +import { getComponentName, getInstanceName } from '../utils' import { processInstanceState } from './process' import { escape, getSetupStateType, toRaw } from './util' diff --git a/packages/devtools-kit/src/core/component/state/editor.ts b/packages/devtools-kit/src/core/component/state/editor.ts index 0816647b..5dd209cb 100644 --- a/packages/devtools-kit/src/core/component/state/editor.ts +++ b/packages/devtools-kit/src/core/component/state/editor.ts @@ -1,7 +1,7 @@ import type { MaybeRef, Ref } from 'vue' import { isReactive, isRef, toRaw } from 'vue' -import { getComponentInstance } from '../general' -import { devtoolsContext } from '../../general' +import { getComponentInstance } from '../utils' +import { devtoolsContext } from '../../../state' import type { InspectorStateEditorPayload, PropPath } from '../types' @@ -124,7 +124,7 @@ export async function editComponentState(payload: InspectorStateEditorPayload, s const { path, nodeId, state, type } = payload // assert data types, currently no... // if (!['data', 'props', 'computed', 'setup'].includes(dataType)) - const instance = getComponentInstance(devtoolsContext.appRecord, nodeId) + const instance = getComponentInstance(devtoolsContext.appRecord!, nodeId) if (!instance) return diff --git a/packages/devtools-kit/src/core/component/state/index.ts b/packages/devtools-kit/src/core/component/state/index.ts index f128c4b5..49ced730 100644 --- a/packages/devtools-kit/src/core/component/state/index.ts +++ b/packages/devtools-kit/src/core/component/state/index.ts @@ -1,10 +1,9 @@ -import { devtoolsContext } from '../../general/state' -import { getComponentInstance } from '../general' -import { getInstanceName, getUniqueComponentId } from '../general/util' +import { devtoolsContext } from '../../../state' +import { getComponentInstance, getInstanceName, getUniqueComponentId } from '../utils' import { processInstanceState } from './process' export function getInstanceState(params: { instanceId: string }) { - const instance = getComponentInstance(devtoolsContext.appRecord, params.instanceId) + const instance = getComponentInstance(devtoolsContext.appRecord!, params.instanceId) const id = getUniqueComponentId(instance!) const name = getInstanceName(instance!) const file = instance?.type?.__file diff --git a/packages/devtools-kit/src/core/component/state/process.ts b/packages/devtools-kit/src/core/component/state/process.ts index 63b04a54..e11bacbe 100644 --- a/packages/devtools-kit/src/core/component/state/process.ts +++ b/packages/devtools-kit/src/core/component/state/process.ts @@ -1,7 +1,7 @@ -import type { VueAppInstance } from '@vue/devtools-schema' import { camelize } from '@vue/devtools-shared' +import type { VueAppInstance } from '../../../types' import type { InspectorState } from '../types' -import { returnError } from '../general/util' +import { returnError } from '../utils' import { vueBuiltins } from './constants' import { getPropType, getSetupStateType, toRaw } from './util' diff --git a/packages/devtools-kit/src/core/component/state/reviver.ts b/packages/devtools-kit/src/core/component/state/reviver.ts index 5867f2d5..fdd7de89 100644 --- a/packages/devtools-kit/src/core/component/state/reviver.ts +++ b/packages/devtools-kit/src/core/component/state/reviver.ts @@ -1,5 +1,5 @@ import { target } from '@vue/devtools-shared' -import { devtoolsContext } from '../../general/state' +import { devtoolsContext } from '../../../state' import { INFINITY, NAN, NEGATIVE_INFINITY, UNDEFINED, specialTypeRE, symbolRE } from './constants' export function reviveSet(val) { diff --git a/packages/devtools-kit/src/core/component/tree/el.ts b/packages/devtools-kit/src/core/component/tree/el.ts index 9f33bd36..ceb4c31d 100644 --- a/packages/devtools-kit/src/core/component/tree/el.ts +++ b/packages/devtools-kit/src/core/component/tree/el.ts @@ -1,6 +1,6 @@ -import type { VueAppInstance } from '@vue/devtools-schema' import type { VNode } from 'vue' -import { isFragment } from '../general/util' +import type { VueAppInstance } from '../../../types' +import { isFragment } from '../utils' export function getRootElementsFromComponentInstance(instance: VueAppInstance): VNode[] { if (isFragment(instance)) diff --git a/packages/devtools-kit/src/core/component/tree/filter.ts b/packages/devtools-kit/src/core/component/tree/filter.ts index 9dd370e2..09d8bf1f 100644 --- a/packages/devtools-kit/src/core/component/tree/filter.ts +++ b/packages/devtools-kit/src/core/component/tree/filter.ts @@ -1,6 +1,6 @@ -import type { VueAppInstance } from '@vue/devtools-schema' import { classify, kebabize } from '@vue/devtools-shared' -import { getInstanceName } from '../general/util' +import type { VueAppInstance } from '../../../types' +import { getInstanceName } from '../utils' export class ComponentFilter { filter: string diff --git a/packages/devtools-kit/src/core/component/tree/index.ts b/packages/devtools-kit/src/core/component/tree/index.ts index 02930c0e..b31b5f90 100644 --- a/packages/devtools-kit/src/core/component/tree/index.ts +++ b/packages/devtools-kit/src/core/component/tree/index.ts @@ -1,6 +1,6 @@ -import type { AppRecord } from '@vue/devtools-schema' -import { devtoolsContext } from '../../general/state' -import { getComponentInstance } from '../general' +import type { AppRecord } from '../../../types' +import { devtoolsContext } from '../../../state' +import { getComponentInstance } from '../utils' import { ComponentWalker } from './walker' export async function getComponentTree(options: { appRecord?: AppRecord, instanceId?: string, filterText?: string, maxDepth?: number, recursively?: boolean }) { diff --git a/packages/devtools-kit/src/core/component/tree/walker.ts b/packages/devtools-kit/src/core/component/tree/walker.ts index 740d60e8..0f2dbfcc 100644 --- a/packages/devtools-kit/src/core/component/tree/walker.ts +++ b/packages/devtools-kit/src/core/component/tree/walker.ts @@ -1,8 +1,8 @@ -import type { VueAppInstance } from '@vue/devtools-schema' import type { SuspenseBoundary, VNode } from 'vue' +import type { VueAppInstance } from '../../../types' import type { ComponentTreeNode } from '../types' -import { getAppRecord, getInstanceName, getRenderKey, getUniqueComponentId, isBeingDestroyed, isFragment } from '../general/util' -import { devtoolsContext } from '../../general' +import { getAppRecord, getInstanceName, getRenderKey, getUniqueComponentId, isBeingDestroyed, isFragment } from '../utils' +import { devtoolsContext } from '../../../state' import { getRootElementsFromComponentInstance } from './el' import type { ComponentFilter } from './filter' import { createComponentFilter } from './filter' diff --git a/packages/devtools-kit/src/core/component/types/bounding-rect.ts b/packages/devtools-kit/src/core/component/types/bounding-rect.ts index 6820aefd..961411f9 100644 --- a/packages/devtools-kit/src/core/component/types/bounding-rect.ts +++ b/packages/devtools-kit/src/core/component/types/bounding-rect.ts @@ -1,4 +1,4 @@ -import type { VueAppInstance } from '@vue/devtools-schema' +import type { VueAppInstance } from '../../../types' export interface ComponentBoundingRect { left: number @@ -8,6 +8,7 @@ export interface ComponentBoundingRect { width: number height: number } + export interface ComponentBoundingRectApiPayload { app?: VueAppInstance inspectorId?: string diff --git a/packages/devtools-kit/src/core/component/types/editor.ts b/packages/devtools-kit/src/core/component/types/editor.ts index 17dd9272..22aaa1b8 100644 --- a/packages/devtools-kit/src/core/component/types/editor.ts +++ b/packages/devtools-kit/src/core/component/types/editor.ts @@ -1,4 +1,4 @@ -import type { AppRecord } from '@vue/devtools-schema' +import type { AppRecord } from '../../../types' import type { Recordable } from '../state/editor' export type PropPath = string | string[] diff --git a/packages/devtools-kit/src/core/component/types/index.ts b/packages/devtools-kit/src/core/component/types/index.ts index a0dcea7a..cabe050e 100644 --- a/packages/devtools-kit/src/core/component/types/index.ts +++ b/packages/devtools-kit/src/core/component/types/index.ts @@ -1,4 +1,4 @@ -export * from './state' export * from './tree' -export * from './editor' export * from './bounding-rect' +export * from './state' +export * from './editor' diff --git a/packages/devtools-kit/src/core/component/types/state.ts b/packages/devtools-kit/src/core/component/types/state.ts index c261f990..7087f862 100644 --- a/packages/devtools-kit/src/core/component/types/state.ts +++ b/packages/devtools-kit/src/core/component/types/state.ts @@ -1,4 +1,4 @@ -import type { VueAppInstance } from '@vue/devtools-schema' +import type { VueAppInstance } from '../../../types' export interface InspectorCustomState { _custom?: { @@ -33,6 +33,13 @@ export interface InspectorStateApiPayload { app: VueAppInstance inspectorId: string nodeId: string + state: { + id: string + name: string + file: string | undefined + state: InspectorState[] + instance: VueAppInstance | undefined + } } export interface AddInspectorApiPayload { diff --git a/packages/devtools-kit/src/core/component/types/tree.ts b/packages/devtools-kit/src/core/component/types/tree.ts index 5a2101e6..d649a3c1 100644 --- a/packages/devtools-kit/src/core/component/types/tree.ts +++ b/packages/devtools-kit/src/core/component/types/tree.ts @@ -1,4 +1,4 @@ -import type { VueAppInstance } from '@vue/devtools-schema' +import type { VueAppInstance } from '../../../types' export interface InspectorNodeTag { label: string diff --git a/packages/devtools-kit/src/core/component/general/util.ts b/packages/devtools-kit/src/core/component/utils/index.ts similarity index 98% rename from packages/devtools-kit/src/core/component/general/util.ts rename to packages/devtools-kit/src/core/component/utils/index.ts index 12e3ed59..527eafd9 100644 --- a/packages/devtools-kit/src/core/component/general/util.ts +++ b/packages/devtools-kit/src/core/component/utils/index.ts @@ -1,6 +1,6 @@ -import type { AppRecord, VueAppInstance } from '@vue/devtools-schema' import { basename, classify } from '@vue/devtools-shared' import { Fragment } from 'vue' +import type { AppRecord, VueAppInstance } from '../../../types' function getComponentTypeName(options: VueAppInstance['type']) { return options.name || options._componentTag || options.__VUE_DEVTOOLS_COMPONENT_GUSSED_NAME__ || options.__name diff --git a/packages/devtools-kit/src/core/custom-command/index.ts b/packages/devtools-kit/src/core/custom-command/index.ts index d5f558ff..69d940d7 100644 --- a/packages/devtools-kit/src/core/custom-command/index.ts +++ b/packages/devtools-kit/src/core/custom-command/index.ts @@ -1,4 +1,4 @@ -import { devtoolsState } from '../general/state' +import { devtoolsState } from '../../state' export interface CustomCommandAction { type: 'url' @@ -36,14 +36,14 @@ export interface CustomCommand { } export function addCustomCommand(action: CustomCommand) { - if ((devtoolsState.commands as unknown as CustomCommand[]).some(t => t.id === action.id)) + if (devtoolsState.commands.some(t => t.id === action.id)) return devtoolsState.commands.push(action) } -export function removeCustomCommand(actionId: CustomCommand['id']) { - const index = (devtoolsState.commands as unknown as CustomCommand[]).findIndex(t => t.id === actionId) +export function removeCustomCommand(actionId: string) { + const index = devtoolsState.commands.findIndex(t => t.id === actionId) if (index === -1) return diff --git a/packages/devtools-kit/src/core/custom-tab/index.ts b/packages/devtools-kit/src/core/custom-tab/index.ts index ac68fc53..732c45e7 100644 --- a/packages/devtools-kit/src/core/custom-tab/index.ts +++ b/packages/devtools-kit/src/core/custom-tab/index.ts @@ -1,10 +1,10 @@ -import { devtoolsState } from '../general/state' +import { devtoolsState } from '../../state' import type { CustomTab } from './types' export type { CustomTab } from './types' export function addCustomTab(tab: CustomTab) { - if ((devtoolsState.tabs as unknown as CustomTab[]).some(t => t.name === tab.name)) + if (devtoolsState.tabs.some(t => t.name === tab.name)) return devtoolsState.tabs.push(tab) diff --git a/packages/devtools-kit/src/core/general/app-record.ts b/packages/devtools-kit/src/core/general/app-record.ts deleted file mode 100644 index 08d5c344..00000000 --- a/packages/devtools-kit/src/core/general/app-record.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { AppRecord, VueAppInstance } from '@vue/devtools-schema' -import { registerComponentsDevTools } from '../plugins' -import { DevToolsPluginApi, registerPlugin } from '../../api' -import { devtoolsContext, devtoolsState } from './state' - -export async function setActiveAppRecord(appRecord: AppRecord) { - await registerComponentsDevTools(appRecord!.app as unknown as VueAppInstance) - devtoolsState.activeAppRecord = appRecord - devtoolsState.activeAppRecordId = `${appRecord.id}` - registerPlugin({ - app: appRecord.app as unknown as VueAppInstance, - api: appRecord.api, - }) -} - -export async function toggleAppRecord(id: string) { - devtoolsContext.componentPluginHookBuffer.forEach(cleanup => cleanup()) - devtoolsContext.api.clear() - devtoolsContext.clear() - const appRecord = devtoolsState.appRecords.find(record => record.id === id) - if (appRecord) { - devtoolsState.pluginBuffer = devtoolsState.pluginBuffer.filter(([plugin]) => plugin.id !== 'components') - const api = new DevToolsPluginApi() - appRecord.api = api - setActiveAppRecord(appRecord) - - // @TODO: find a better way to handle it - window.postMessage({ - event: 'toggle-app-record', - target: 'vue-devtools', - }) - } -} diff --git a/packages/devtools-kit/src/core/general/app.ts b/packages/devtools-kit/src/core/general/app.ts deleted file mode 100644 index 81a12deb..00000000 --- a/packages/devtools-kit/src/core/general/app.ts +++ /dev/null @@ -1,65 +0,0 @@ -import type { AppRecord, VueAppInstance } from '@vue/devtools-schema' -import { target } from '@vue/devtools-shared' -import slug from 'speakingurl' - -const appRecordInfo = target.__VUE_DEVTOOLS_APP_RECROD_INFO__ ??= { - id: 0, - appIds: new Set(), -} - -function getAppRecordName(app: VueAppInstance['appContext']['app'], fallbackName: string) { - return app?._component?.name || `App ${fallbackName}` -} - -function getAppRootInstance(app: VueAppInstance['appContext']['app']) { - if (app._instance) - return app._instance - - else if (app._container?._vnode?.component) - return app._container?._vnode?.component -} - -function getAppRecordId(app: VueAppInstance['appContext']['app'], defaultId?: string): string { - if (app.__VUE_DEVTOOLS_APP_RECORD_ID__ != null) - return app.__VUE_DEVTOOLS_APP_RECORD_ID__ - - let id = defaultId ?? (appRecordInfo.id++).toString() - - if (defaultId && appRecordInfo.appIds.has(id)) { - let count = 1 - while (appRecordInfo.appIds.has(`${defaultId}_${count}`)) - count++ - id = `${defaultId}_${count}` - } - - appRecordInfo.appIds.add(id) - - app.__VUE_DEVTOOLS_APP_RECORD_ID__ = id - return id -} - -export function createAppRecord(app: VueAppInstance['appContext']['app']): AppRecord { - const rootInstance = getAppRootInstance(app) - if (rootInstance) { - appRecordInfo.id++ - const name = getAppRecordName(app, appRecordInfo.id.toString()) - const id = getAppRecordId(app, slug(name)) - - const record: AppRecord = { - id, - name, - instanceMap: new Map(), - rootInstance, - } - - app.__VUE_DEVTOOLS_APP_RECORD__ = record - const rootId = `${record.id}:root` - record.instanceMap.set(rootId, record.rootInstance) - record.rootInstance.__VUE_DEVTOOLS_UID__ = rootId - - return record - } - else { - return {} as AppRecord - } -} diff --git a/packages/devtools-kit/src/core/general/hook.ts b/packages/devtools-kit/src/core/general/hook.ts deleted file mode 100644 index 20f1da31..00000000 --- a/packages/devtools-kit/src/core/general/hook.ts +++ /dev/null @@ -1,132 +0,0 @@ -import type { DevtoolsHook, PluginDescriptor, PluginSetupFunction, VueAppInstance } from '@vue/devtools-schema' -import { DevToolsHooks } from '@vue/devtools-schema' -import { target } from '@vue/devtools-shared' -import type { HookKeys, Hookable } from 'hookable' -import { createHooks } from 'hookable' -import type { App } from 'vue' - -type HookAppInstance = App & VueAppInstance -interface DevToolsEvent { - [DevToolsHooks.APP_INIT]: (app: VueAppInstance['appContext']['app'], version: string) => void - [DevToolsHooks.APP_CONNECTED]: () => void - [DevToolsHooks.COMPONENT_ADDED]: (app: HookAppInstance, uid: number, parentUid: number, component: VueAppInstance) => void - [DevToolsHooks.COMPONENT_UPDATED]: DevToolsEvent['component:added'] - [DevToolsHooks.COMPONENT_REMOVED]: DevToolsEvent['component:added'] - [DevToolsHooks.SETUP_DEVTOOLS_PLUGIN]: (pluginDescriptor: PluginDescriptor, setupFn: PluginSetupFunction) => void -} - -export const devtoolsHooks: Hookable> = target.__VUE_DEVTOOLS_HOOK ??= createHooks() - -const on = { - vueAppInit(fn: DevToolsEvent[DevToolsHooks.APP_INIT]) { - devtoolsHooks.hook(DevToolsHooks.APP_INIT, fn) - }, - vueAppConnected(fn: DevToolsEvent[DevToolsHooks.APP_CONNECTED]) { - devtoolsHooks.hook(DevToolsHooks.APP_CONNECTED, fn) - }, - componentAdded(fn: DevToolsEvent[DevToolsHooks.COMPONENT_ADDED]) { - return devtoolsHooks.hook(DevToolsHooks.COMPONENT_ADDED, fn) - }, - componentUpdated(fn: DevToolsEvent[DevToolsHooks.COMPONENT_UPDATED]) { - return devtoolsHooks.hook(DevToolsHooks.COMPONENT_UPDATED, fn) - }, - componentRemoved(fn: DevToolsEvent[DevToolsHooks.COMPONENT_REMOVED]) { - return devtoolsHooks.hook(DevToolsHooks.COMPONENT_REMOVED, fn) - }, - setupDevtoolsPlugin(fn: DevToolsEvent[DevToolsHooks.SETUP_DEVTOOLS_PLUGIN]) { - devtoolsHooks.hook(DevToolsHooks.SETUP_DEVTOOLS_PLUGIN, fn) - }, -} - -export function createDevToolsHook(): DevtoolsHook { - return { - id: 'vue-devtools-next', - enabled: false, - appRecords: [], - apps: {}, - events: new Map(), - on(event, fn) { - if (!this.events.has(event)) - this.events.set(event, []) - - this.events.get(event)?.push(fn) - // cleanup function - return () => this.off(event, fn) - }, - once(event, fn) { - const onceFn = (...args) => { - this.off(event, onceFn) - fn(...args) - } - - this.on(event, onceFn) - return [event, onceFn] as const - }, - off(event, fn) { - if (this.events.has(event)) { - const eventCallbacks = this.events.get(event)! - const index = eventCallbacks.indexOf(fn) - if (index !== -1) - eventCallbacks.splice(index, 1) - } - }, - emit(event, ...payload) { - if (this.events.has(event)) - this.events.get(event)!.forEach(fn => fn(...payload)) - }, - } -} - -export function subscribeDevToolsHook() { - const hook = target.__VUE_DEVTOOLS_GLOBAL_HOOK__ - // app init hook - hook.on(DevToolsHooks.APP_INIT, (app: VueAppInstance['appContext']['app'], version: string) => { - if (app?._instance?.type?.devtools?.hide) - return - - devtoolsHooks.callHook(DevToolsHooks.APP_INIT, app, version) - // const record = createAppRecord(app) - - // hook.appRecords.push({ - // ...record, - // app, - // version, - // }) - }) - - // component added hook - hook.on(DevToolsHooks.COMPONENT_ADDED, async (app: HookAppInstance, uid: number, parentUid: number, component: VueAppInstance) => { - if (app?._instance?.type?.devtools?.hide) - return - - if (!app || (typeof uid !== 'number' && !uid) || !component) - return - - devtoolsHooks.callHook(DevToolsHooks.COMPONENT_ADDED, app, uid, parentUid, component) - }) - - // component updated hook - hook.on(DevToolsHooks.COMPONENT_UPDATED, (app: HookAppInstance, uid: number, parentUid: number, component: VueAppInstance) => { - if (!app || (typeof uid !== 'number' && !uid) || !component) - return - - devtoolsHooks.callHook(DevToolsHooks.COMPONENT_UPDATED, app, uid, parentUid, component) - }) - - // component removed hook - hook.on(DevToolsHooks.COMPONENT_REMOVED, async (app: HookAppInstance, uid: number, parentUid: number, component: VueAppInstance) => { - if (!app || (typeof uid !== 'number' && !uid) || !component) - return - - devtoolsHooks.callHook(DevToolsHooks.COMPONENT_REMOVED, app, uid, parentUid, component) - }) - - // devtools plugin setup - hook.on(DevToolsHooks.SETUP_DEVTOOLS_PLUGIN, (pluginDescriptor: PluginDescriptor, setupFn: PluginSetupFunction) => { - devtoolsHooks.callHook(DevToolsHooks.SETUP_DEVTOOLS_PLUGIN, pluginDescriptor, setupFn) - }) -} - -export const hook = { - on, -} diff --git a/packages/devtools-kit/src/core/general/inspector.ts b/packages/devtools-kit/src/core/general/inspector.ts deleted file mode 100644 index 446e58ce..00000000 --- a/packages/devtools-kit/src/core/general/inspector.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { DevToolsContext } from '@vue/devtools-schema' -import { devtoolsContext } from './state' - -export function addInspector(payload: DevToolsContext['inspector'][0]) { - devtoolsContext.inspector.push(payload) -} - -export function getInspector(inspectorId: string) { - return devtoolsContext.inspector.find(inspector => inspector.id === inspectorId) -} - -export function updateInspector(inspectorId: string, payload: Partial) { - const inspector = getInspector(inspectorId) - inspector && Object.assign(inspector, payload) -} diff --git a/packages/devtools-kit/src/core/general/state.ts b/packages/devtools-kit/src/core/general/state.ts deleted file mode 100644 index 3ab9de49..00000000 --- a/packages/devtools-kit/src/core/general/state.ts +++ /dev/null @@ -1,137 +0,0 @@ -import type { AppRecord, DevToolsState } from '@vue/devtools-schema' -import { deepClone, target as global } from '@vue/devtools-shared' -import { debounce } from 'perfect-debounce' -import type { DevToolsPluginApi } from '../../api' -import { DevToolsEvents, apiHooks } from '../../api' -import type { Router, RouterInfo } from '../router' -import { RouterKey, devtoolsRouterInfo, normalizeRouterInfo } from '../router' - -const StateKey = '__VUE_DEVTOOLS_GLOBAL_STATE__' -const ContextKey = '__VUE_DEVTOOLS_CONTEXT__' -const DefaultContext = { - appRecord: null, - api: null, - inspector: [], - timelineLayer: [], - routerInfo: {}, - router: null, - activeInspectorTreeId: '', - componentPluginHookBuffer: [], -} - -global[StateKey] ??= { - connected: false, - clientConnected: false, - appRecords: [], - activeAppRecord: null, - selectedComponentId: null, - pluginBuffer: [], - tabs: [], - commands: [], - vitePluginDetected: false, - activeAppRecordId: null, -} - -global[ContextKey] ??= deepClone(DefaultContext) - -const callStateUpdatedHook = debounce((state: DevToolsState, oldState: DevToolsState) => { - apiHooks.callHook(DevToolsEvents.DEVTOOLS_STATE_UPDATED, state, oldState) -}, 80) - -const callConnectedUpdatedHook = debounce((state: DevToolsState, oldState: DevToolsState) => { - apiHooks.callHook(DevToolsEvents.DEVTOOLS_CONNECTED_UPDATED, state, oldState) -}, 80) - -export const devtoolsState = new Proxy(global[StateKey], { - get(target, property) { - return global[StateKey][property] - }, - set(target, property, value) { - const oldState = { ...global[StateKey] } - - target[property] = value - // sync to global to ensure the state is consistent - global[StateKey][property] = value - if (property === 'activeAppRecord') { - // update context - global[ContextKey].appRecord = value - global[ContextKey].api = value.api - global[ContextKey].inspector = value.inspector ?? [] - normalizeRouterInfo(value) - global[ContextKey].routerInfo = devtoolsRouterInfo - } - - callStateUpdatedHook(global[StateKey], oldState) - if (['connected', 'clientConnected'].includes(property.toString()) && oldState[property] !== value) - callConnectedUpdatedHook(global[StateKey], oldState) - - return true - }, - deleteProperty(target, property) { - delete target[property] - return true - }, -}) - -Object.defineProperty(devtoolsState.tabs, 'push', { - configurable: true, - value(...items: unknown[]) { - const result = Array.prototype.push.apply(this, items) - devtoolsState.tabs = this - apiHooks.callHook(DevToolsEvents.CUSTOM_TABS_UPDATED, this) - return result - }, -}) - -;['push', 'splice'].forEach((method) => { - Object.defineProperty(devtoolsState.commands, method, { - configurable: true, - value(...args: unknown[]) { - const result = Array.prototype[method].apply(this, args) - devtoolsState.commands = this - apiHooks.callHook(DevToolsEvents.CUSTOM_COMMANDS_UPDATED, this) - return result - }, - }) -}) - -export const devtoolsContext = new Proxy(global[ContextKey], { - get(target, property) { - if (property === 'router') - return global[RouterKey] - - else if (property === 'clear') - return clearDevToolsContext - - return global[ContextKey][property] - }, - set(target, property, value) { - if (property === 'componentPluginHookBuffer') - global[ContextKey][property] = value - - return true - }, -}) as unknown as { - appRecord: AppRecord - api: DevToolsPluginApi - inspector: { - id: string - nodeId: string - filter: string - treeFilterPlaceholder: string - }[] - timelineLayer: { - id: string - label: string - color: number - }[] - routerInfo: RouterInfo - router: Router - activeInspectorTreeId: string - componentPluginHookBuffer: (() => void)[] - clear: () => void -} - -function clearDevToolsContext() { - global[ContextKey] = deepClone(DefaultContext) -} diff --git a/packages/devtools-kit/src/core/general/index.ts b/packages/devtools-kit/src/core/index.ts similarity index 59% rename from packages/devtools-kit/src/core/general/index.ts rename to packages/devtools-kit/src/core/index.ts index 3e97e607..cf936fdd 100644 --- a/packages/devtools-kit/src/core/general/index.ts +++ b/packages/devtools-kit/src/core/index.ts @@ -1,18 +1,17 @@ import { target } from '@vue/devtools-shared' -import { DevToolsHooks } from '@vue/devtools-schema' -import { DevToolsEvents, DevToolsPluginApi, apiHooks, collectRegisteredPlugin } from '../../api' -import { createAppRecord } from './app' -import { createDevToolsHook, devtoolsHooks, hook, subscribeDevToolsHook } from './hook' -import { devtoolsContext, devtoolsState } from './state' -import { setActiveAppRecord } from './app-record' +import { createDevToolsHook, devtoolsHooks, hook, subscribeDevToolsHook } from '../hook' +import { DevToolsHooks } from '../types' +import { devtoolsAppRecords, devtoolsState, getDevToolsEnv } from '../state' +import { DevToolsEvents, DevToolsPluginApi, apiHooks, collectDevToolsPlugin } from '../api' +import { createAppRecord, setActiveAppRecord } from './app-record' -// usage: inject to user application and call it before the vue app is created export function initDevTools() { - devtoolsState.vitePluginDetected = !!target.__VUE_DEVTOOLS_VITE_PLUGIN_DETECTED__ + devtoolsState.vitePluginDetected = getDevToolsEnv().vitePluginDetected + + const isDevToolsNext = target.__VUE_DEVTOOLS_GLOBAL_HOOK__?.id === 'vue-devtools-next' - const isNewDevTools = target.__VUE_DEVTOOLS_GLOBAL_HOOK__?.id === 'vue-devtools-next' // de-duplicate - if (target.__VUE_DEVTOOLS_GLOBAL_HOOK__ && isNewDevTools) + if (target.__VUE_DEVTOOLS_GLOBAL_HOOK__ && isDevToolsNext) return // compatible with old devtools @@ -22,17 +21,15 @@ export function initDevTools() { else target.__VUE_DEVTOOLS_GLOBAL_HOOK__ = createDevToolsHook() - target.__VUE_DEVTOOLS_APP_RECORDS__ ??= [] - - // devtools plugin setup hook - hook.on.setupDevtoolsPlugin(collectRegisteredPlugin) + // setup old devtools plugin (compatible with pinia, router, etc) + hook.on.setupDevtoolsPlugin(collectDevToolsPlugin) // create app record hook.on.vueAppInit(async (app, version) => { const record = createAppRecord(app) const api = new DevToolsPluginApi() - devtoolsState.appRecords = [ - ...(devtoolsState.appRecords ?? []), + devtoolsAppRecords.value = [ + ...devtoolsAppRecords.value, { ...record, app, @@ -41,8 +38,8 @@ export function initDevTools() { }, ] - if (devtoolsState.appRecords.length === 1) { - await setActiveAppRecord(devtoolsState.appRecords[0]) + if (devtoolsAppRecords.value.length === 1) { + await setActiveAppRecord(devtoolsAppRecords.value[0]) devtoolsState.connected = true devtoolsHooks.callHook(DevToolsHooks.APP_CONNECTED) } @@ -84,9 +81,3 @@ export function onDevToolsClientConnected(fn: () => void) { }) }) } - -export { - devtoolsContext, - devtoolsState, - hook, -} diff --git a/packages/devtools-kit-next/src/core/inspector/index.ts b/packages/devtools-kit/src/core/inspector/index.ts similarity index 100% rename from packages/devtools-kit-next/src/core/inspector/index.ts rename to packages/devtools-kit/src/core/inspector/index.ts diff --git a/packages/devtools-kit/src/core/open-in-editor/index.ts b/packages/devtools-kit/src/core/open-in-editor/index.ts index 70d4ca94..11477cf6 100644 --- a/packages/devtools-kit/src/core/open-in-editor/index.ts +++ b/packages/devtools-kit/src/core/open-in-editor/index.ts @@ -1,4 +1,5 @@ import { target } from '@vue/devtools-shared' +import { devtoolsState } from '../../state' export interface OpenInEditorOptions { file?: string @@ -10,7 +11,7 @@ export function openInEditor(options: OpenInEditorOptions = {}) { const { file, line = 0, column = 0 } = options if (file) { const baseUrl = window.location.origin - if (target.__VUE_DEVTOOLS_VITE_PLUGIN_DETECTED__) { + if (devtoolsState.vitePluginDetected) { target.__VUE_INSPECTOR__.openInEditor(baseUrl, file, line, column) } else { diff --git a/packages/devtools-kit/src/core/plugins/index.ts b/packages/devtools-kit/src/core/plugins/index.ts deleted file mode 100644 index cb64ac1b..00000000 --- a/packages/devtools-kit/src/core/plugins/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './components' diff --git a/packages/devtools-kit/src/core/router/index.ts b/packages/devtools-kit/src/core/router/index.ts index 0edecb63..96d79fde 100644 --- a/packages/devtools-kit/src/core/router/index.ts +++ b/packages/devtools-kit/src/core/router/index.ts @@ -1,79 +1,59 @@ -import type { RouteLocationNormalizedLoaded, RouteRecordNormalized, RouteRecordRaw, Router } from 'vue-router' -import type { AppRecord } from '@vue/devtools-schema' +import type { RouteLocationNormalizedLoaded, RouteRecordRaw, Router } from 'vue-router' import { deepClone, target as global } from '@vue/devtools-shared' import { debounce } from 'perfect-debounce' -import { DevToolsEvents, apiHooks } from '../../api' -import { hook } from '../general/hook' +import { ROUTER_INFO_KEY, ROUTER_KEY } from '../../state' +import type { AppRecord } from '../../types' +import { hook } from '../../hook' +import { DevToolsEvents, apiHooks } from '../../api/hook' -const RouterInfoKey = '__VUE_DEVTOOLS_ROUTER_INFO__' -export const RouterKey = '__VUE_DEVTOOLS_ROUTER__' - -export type { Router } from 'vue-router' -export interface RouterInfo { - currentRoute: RouteLocationNormalizedLoaded | null - routes: RouteRecordNormalized[] - router: Router | null +function getRoutes(router?: Router) { + const routesMap = new Map() + return (router?.getRoutes() || []).filter(i => !routesMap.has(i.path) && routesMap.set(i.path, 1)) } -global[RouterInfoKey] ??= { - currentRoute: null, - routes: [], - router: null, -} as RouterInfo - -global[RouterKey] ??= null as unknown as Router - -export const devtoolsRouterInfo: RouterInfo = new Proxy(global[RouterInfoKey], { - get(target, property) { - return global[RouterInfoKey][property] - }, -}) +function filterRoutes(routes: RouteRecordRaw[]) { + return routes.map((item) => { + let { path, name, children } = item + if (children?.length) + children = filterRoutes(children) -export function normalizeRouterInfo(appRecord: AppRecord) { - const getRoutes = (router?: Router) => { - const routesMap = new Map() - return (router?.getRoutes() || []).filter(i => !routesMap.has(i.path) && routesMap.set(i.path, 1)) - } - function filterRoutes(routes: RouteRecordRaw[]) { - return routes.map((item) => { - let { path, name, children } = item - if (children?.length) - children = filterRoutes(children) + return { + path, + name, + children, + } + }) +} - return { - path, - name, - children, - } - }) - } - function filterCurrentRoute(route: RouteLocationNormalizedLoaded & { href?: string } | undefined) { - if (route) { - const { fullPath, hash, href, path, name, matched, params, query } = route - return { - fullPath, - hash, - href, - path, - name, - params, - query, - matched: filterRoutes(matched), - } +function filterCurrentRoute(route: RouteLocationNormalizedLoaded & { href?: string } | undefined) { + if (route) { + const { fullPath, hash, href, path, name, matched, params, query } = route + return { + fullPath, + hash, + href, + path, + name, + params, + query, + matched: filterRoutes(matched), } - return route } + return route +} + +export function normalizeRouterInfo(appRecord: AppRecord) { function init() { const router = appRecord.app?.config.globalProperties.$router as Router | undefined const currentRoute = filterCurrentRoute(router?.currentRoute.value) const routes = filterRoutes(getRoutes(router)) const c = console.warn console.warn = () => {} - global[RouterInfoKey] = { + global[ROUTER_INFO_KEY] = { currentRoute: currentRoute ? deepClone(currentRoute) : {}, routes: deepClone(routes), } - global[RouterKey] = router + global[ROUTER_KEY] = router! console.warn = c } @@ -82,10 +62,9 @@ export function normalizeRouterInfo(appRecord: AppRecord) { // @TODO: use another way to watch router hook.on.componentUpdated(debounce(() => { init() - apiHooks.callHook(DevToolsEvents.ROUTER_INFO_UPDATED, global[RouterInfoKey]) + apiHooks.callHook(DevToolsEvents.ROUTER_INFO_UPDATED, global[ROUTER_INFO_KEY]) }, 200)) } - export function getRouterDevToolsId(id: string) { return id.replace(/\D/g, '') || '0' } diff --git a/packages/devtools-kit/src/core/timeline/index.ts b/packages/devtools-kit/src/core/timeline/index.ts index 394dcef3..2e9047ec 100644 --- a/packages/devtools-kit/src/core/timeline/index.ts +++ b/packages/devtools-kit/src/core/timeline/index.ts @@ -1,9 +1,24 @@ -import type { DevToolsContext } from '@vue/devtools-schema' -import { devtoolsContext } from '../general/state' +import { devtoolsContext } from '../../state' -export type * from './types' +export interface TimelineEvent { + event: { + groupId: number + time: number + title: string + subtitle: string + // @TODO: InspectorCustomState type + data: Record + } + layerId: string +} + +export interface TimelineLayerItem { + id: string + label: string + color: number +} -export function addTimelineLayer(payload: DevToolsContext['timelineLayer'][0]) { +export function addTimelineLayer(payload: TimelineLayerItem) { devtoolsContext.timelineLayer.push(payload) } diff --git a/packages/devtools-kit/src/core/timeline/types.ts b/packages/devtools-kit/src/core/timeline/types.ts deleted file mode 100644 index e402fd30..00000000 --- a/packages/devtools-kit/src/core/timeline/types.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { InspectorCustomState } from '../component/types' - -export interface TimelineEventData { - data: Record -} - -export interface TimelineEvent { - event: { - groupId: number - time: number - title: string - subtitle: string - data: TimelineEventData - } - layerId: string -} diff --git a/packages/devtools-kit/src/core/vue-inspector/index.ts b/packages/devtools-kit/src/core/vue-inspector/index.ts deleted file mode 100644 index e53a9848..00000000 --- a/packages/devtools-kit/src/core/vue-inspector/index.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { target } from '@vue/devtools-shared' - -export interface VueInspector { - enabled: boolean - position: { - x: number - y: number - } - linkParams: { - file: string - line: number - column: number - } - - enable: () => void - disable: () => void - toggleEnabled: () => void - openInEditor: (baseUrl: string, file: string, line: number, column: number) => void - onUpdated: () => void -} - -function waitForInspectorInit(cb: () => void) { - let total = 0 - const timer = setInterval(() => { - if (target.__VUE_INSPECTOR__) { - clearInterval(timer) - total += 30 - cb() - } - if (total >=/* 5s */ 5000) - clearInterval(timer) - }, 30) -} - -function setupInspector() { - const inspector = target.__VUE_INSPECTOR__ - const _openInEditor = inspector.openInEditor - inspector.openInEditor = async (...params: Parameters) => { - inspector.disable() - _openInEditor(...params) - } -} - -export function getVueInspector(): Promise { - return new Promise((resolve) => { - function setup() { - setupInspector() - resolve(target.__VUE_INSPECTOR__) - } - if (!target.__VUE_INSPECTOR__) { - waitForInspectorInit(() => { - setup() - }) - } - else { - setup() - } - }) -} diff --git a/packages/devtools-kit-next/src/hook/index.ts b/packages/devtools-kit/src/hook/index.ts similarity index 100% rename from packages/devtools-kit-next/src/hook/index.ts rename to packages/devtools-kit/src/hook/index.ts diff --git a/packages/devtools-kit/src/index.ts b/packages/devtools-kit/src/index.ts index c7c822fe..7827ea6a 100644 --- a/packages/devtools-kit/src/index.ts +++ b/packages/devtools-kit/src/index.ts @@ -1,24 +1,29 @@ -import { devtoolsContext, devtoolsState, hook, initDevTools, onDevToolsClientConnected, onDevToolsConnected } from './core/general' +import { initDevTools, onDevToolsClientConnected, onDevToolsConnected } from './core' +import { hook } from './hook' +import { devtoolsContext, devtoolsState, setDevToolsEnv } from './state' +import { setupDevToolsPlugin } from './api' import { addCustomTab } from './core/custom-tab' import { addCustomCommand, removeCustomCommand } from './core/custom-command' -import { setupDevToolsPlugin } from './api/plugin' -export type * from './core/component/types' -export type * from './core/timeline/types' -export type * from './core/router' -export type * from './core/open-in-editor' -export type * from './core/vue-inspector' -export type * from './core/component-inspector/types' -export type * from './core/custom-tab/types' +export type * from './core/custom-tab' export type * from './core/custom-command' +export type * from './core/timeline' +export type * from './core/open-in-editor' +export type * from './core/component-highlighter' +export type * from './core/component/types' +export type * from './core/component-inspector' +export type * from './core/inspector' +export type * from './types' -export * from './shared' +export { parse, stringify } from './shared' +export { formatInspectorStateValue, getInspectorStateValueType, getRawValue, toEdit, toSubmit } from './core/component/state/format' +export { UNDEFINED } from './core/component/state/constants' export const devtools = { state: devtoolsState, context: devtoolsContext, - init: initDevTools, hook, + init: initDevTools, get api() { return devtoolsContext.api }, @@ -31,4 +36,5 @@ export { addCustomCommand, removeCustomCommand, setupDevToolsPlugin, + setDevToolsEnv, } diff --git a/packages/devtools-kit/src/core/plugins/components.ts b/packages/devtools-kit/src/plugins/component.ts similarity index 88% rename from packages/devtools-kit/src/core/plugins/components.ts rename to packages/devtools-kit/src/plugins/component.ts index 79f8a100..d7c0a07b 100644 --- a/packages/devtools-kit/src/core/plugins/components.ts +++ b/packages/devtools-kit/src/plugins/component.ts @@ -1,18 +1,17 @@ -import type { VueAppInstance } from '@vue/devtools-schema' import { debounce } from 'perfect-debounce' -import { setupDevToolsPlugin } from '../../api/plugin' -import { getAppRecord, getComponentId, getComponentInstance } from '../component/general' -import { devtoolsContext } from '../general/state' -import { ComponentWalker } from '../component/tree/walker' -import { getInstanceState } from '../component/state' -import { editState } from '../component/state/editor' -import { getComponentBoundingRect } from '../component/state/bounding-rect' -import { DevToolsEvents, apiHooks } from '../../api/on' -import { hook } from '../general/hook' - -const INSPECTOR_ID = 'components' - -export function registerComponentsDevTools(app: VueAppInstance) { +import { VueAppInstance } from '../types' +import { DevToolsEvents, setupDevToolsPlugin } from '../api' +import { devtoolsContext } from '../state' +import { hook } from '../hook' +import { getAppRecord, getComponentId, getComponentInstance } from '../core/component/utils' +import { getComponentBoundingRect } from '../core/component/state/bounding-rect' +import { ComponentWalker } from '../core/component/tree/walker' +import { editState } from '../core/component/state/editor' +import { getInstanceState } from '../core/component/state' + +const INSPECTOR_ID = '__vue-devtools-component-plugin__' + +export function registerComponentDevToolsPlugin(app: VueAppInstance) { setupDevToolsPlugin({ id: INSPECTOR_ID, label: 'Components', @@ -82,6 +81,7 @@ export function registerComponentsDevTools(app: VueAppInstance) { }) api.on.editInspectorState(async (payload) => { + // @ts-expect-error expected type if (payload.app === app && payload.inspectorId === INSPECTOR_ID) { editState(payload) await api.sendInspectorState('components') diff --git a/packages/devtools-kit-next/src/plugins/index.ts b/packages/devtools-kit/src/plugins/index.ts similarity index 100% rename from packages/devtools-kit-next/src/plugins/index.ts rename to packages/devtools-kit/src/plugins/index.ts diff --git a/packages/devtools-kit/src/state/app-record.ts b/packages/devtools-kit/src/state/app-record.ts new file mode 100644 index 00000000..7df39a1b --- /dev/null +++ b/packages/devtools-kit/src/state/app-record.ts @@ -0,0 +1,58 @@ +import { target } from '@vue/devtools-shared' +import { AppRecord } from '../types' +import { normalizeRouterInfo } from '../core/router' +import { callConnectedUpdatedHook, callStateUpdatedHook, devtoolsState } from './state' +import { devtoolsRouterInfo } from './router' +import { devtoolsContext } from './context' + +interface DevToolsAppRecords { + value: AppRecord[] + active: AppRecord + activeId: string +} + +export const devtoolsAppRecords = new Proxy(devtoolsState.appRecords as unknown as DevToolsAppRecords, { + get(_, property) { + if (property === 'value') + return devtoolsState.appRecords + else if (property === 'active') + return devtoolsState.activeAppRecord + else if (property === 'activeId') + return devtoolsState.activeAppRecordId + }, + set(target, property, value) { + const oldState = { ...devtoolsState } + + if (property === 'value') { + devtoolsState.appRecords = value + } + + else if (property === 'active') { + const _value = value as AppRecord + + // sync to context + devtoolsState.activeAppRecord = _value + devtoolsContext.appRecord = _value + devtoolsContext.api = _value.api! + // @ts-expect-error expected type + devtoolsContext.inspector = _value.inspector ?? [] + normalizeRouterInfo(value) + devtoolsContext.routerInfo = devtoolsRouterInfo + } + + else if (property === 'activeId') { + devtoolsState.activeAppRecordId = value + } + + callStateUpdatedHook(devtoolsState, oldState) + if (['connected', 'clientConnected'].includes(property.toString()) && oldState[property] !== value) + callConnectedUpdatedHook(devtoolsState, oldState) + + return true + }, +}) + +export const appRecordInfo = target.__VUE_DEVTOOLS_APP_RECROD_INFO__ ??= { + id: 0, + appIds: new Set(), +} diff --git a/packages/devtools-kit-next/src/state/context.ts b/packages/devtools-kit/src/state/context.ts similarity index 90% rename from packages/devtools-kit-next/src/state/context.ts rename to packages/devtools-kit/src/state/context.ts index 4605a85b..e0ed2d3a 100644 --- a/packages/devtools-kit-next/src/state/context.ts +++ b/packages/devtools-kit/src/state/context.ts @@ -31,9 +31,6 @@ export const devtoolsContext = new Proxy(global[CONTEXT_KEY], { return global[CONTEXT_KEY][property] }, set(target, property, value) { - // if (property === 'componentPluginHookBuffer') - // global[CONTEXT_KEY][property] = value - global[CONTEXT_KEY][property] = value return true }, diff --git a/packages/devtools-kit/src/state/env.ts b/packages/devtools-kit/src/state/env.ts new file mode 100644 index 00000000..db3acc18 --- /dev/null +++ b/packages/devtools-kit/src/state/env.ts @@ -0,0 +1,17 @@ +import { target } from '@vue/devtools-shared' +import { DevToolsEnv } from '../types' + +target.__VUE_DEVTOOLS_ENV__ ??= { + vitePluginDetected: false, +} + +export function getDevToolsEnv() { + return target.__VUE_DEVTOOLS_ENV__ +} + +export function setDevToolsEnv(env: Partial) { + target.__VUE_DEVTOOLS_ENV__ = { + ...target.__VUE_DEVTOOLS_ENV__, + ...env, + } +} diff --git a/packages/devtools-kit-next/src/state/index.ts b/packages/devtools-kit/src/state/index.ts similarity index 82% rename from packages/devtools-kit-next/src/state/index.ts rename to packages/devtools-kit/src/state/index.ts index 7fc10a7c..49120dee 100644 --- a/packages/devtools-kit-next/src/state/index.ts +++ b/packages/devtools-kit/src/state/index.ts @@ -2,3 +2,4 @@ export * from './state' export * from './app-record' export * from './context' export * from './router' +export * from './env' diff --git a/packages/devtools-kit-next/src/state/router.ts b/packages/devtools-kit/src/state/router.ts similarity index 100% rename from packages/devtools-kit-next/src/state/router.ts rename to packages/devtools-kit/src/state/router.ts diff --git a/packages/devtools-kit/src/state/state.ts b/packages/devtools-kit/src/state/state.ts new file mode 100644 index 00000000..fe0480a4 --- /dev/null +++ b/packages/devtools-kit/src/state/state.ts @@ -0,0 +1,79 @@ +import { target as global } from '@vue/devtools-shared' +import { debounce } from 'perfect-debounce' +import type { DevToolsState } from '../types' +import { DevToolsEvents, apiHooks } from '../api' + +export type { DevToolsState } from '../types' + +const STATE_KEY = '__VUE_DEVTOOLS_GLOBAL_STATE__' +const INITIAL_STATE = { + connected: false, + clientConnected: false, + appRecords: [], + activeAppRecord: null, + selectedComponentId: null, + pluginBuffer: [], + tabs: [], + commands: [], + vitePluginDetected: false, + activeAppRecordId: null, +} + +global[STATE_KEY] ??= INITIAL_STATE + +export function resetDevToolsState() { + global[STATE_KEY] = INITIAL_STATE +} + +export const callStateUpdatedHook = debounce((state: DevToolsState, oldState: DevToolsState) => { + apiHooks.callHook(DevToolsEvents.DEVTOOLS_STATE_UPDATED, state, oldState) +}, 80) + +export const callConnectedUpdatedHook = debounce((state: DevToolsState, oldState: DevToolsState) => { + apiHooks.callHook(DevToolsEvents.DEVTOOLS_CONNECTED_UPDATED, state, oldState) +}, 80) + +export const devtoolsState: DevToolsState = new Proxy(global[STATE_KEY], { + get(target, property) { + return global[STATE_KEY][property] + }, + deleteProperty(target, property) { + delete target[property] + return true + }, + set(target, property, value) { + const oldState = { ...global[STATE_KEY] } + + target[property] = value + // sync to global to ensure the state is consistent + global[STATE_KEY][property] = value + + callStateUpdatedHook(global[STATE_KEY], oldState) + if (['connected', 'clientConnected'].includes(property.toString()) && oldState[property] !== value) + callConnectedUpdatedHook(global[STATE_KEY], oldState) + + return true + }, +}) + +Object.defineProperty(devtoolsState.tabs, 'push', { + configurable: true, + value(...items: unknown[]) { + const result = Array.prototype.push.apply(this, items) + devtoolsState.tabs = this + apiHooks.callHook(DevToolsEvents.CUSTOM_TABS_UPDATED, this) + return result + }, +}) + +;['push', 'splice'].forEach((method) => { + Object.defineProperty(devtoolsState.commands, method, { + configurable: true, + value(...args: unknown[]) { + const result = Array.prototype[method].apply(this, args) + devtoolsState.commands = this + apiHooks.callHook(DevToolsEvents.CUSTOM_COMMANDS_UPDATED, this) + return result + }, + }) +}) diff --git a/packages/devtools-kit-next/src/types/app.ts b/packages/devtools-kit/src/types/app.ts similarity index 99% rename from packages/devtools-kit-next/src/types/app.ts rename to packages/devtools-kit/src/types/app.ts index 571efbbe..5f43caff 100644 --- a/packages/devtools-kit-next/src/types/app.ts +++ b/packages/devtools-kit/src/types/app.ts @@ -1,7 +1,6 @@ import type { App, ComponentInternalInstance, ComponentOptions, SuspenseBoundary } from 'vue' import type { DevToolsPluginApi } from '../api' -// @TODO export type PluginApi = DevToolsPluginApi export declare type PluginSettingsItem = { diff --git a/packages/devtools-kit-next/src/types/context.ts b/packages/devtools-kit/src/types/context.ts similarity index 100% rename from packages/devtools-kit-next/src/types/context.ts rename to packages/devtools-kit/src/types/context.ts diff --git a/packages/devtools-kit/src/types/env.ts b/packages/devtools-kit/src/types/env.ts new file mode 100644 index 00000000..2025a9de --- /dev/null +++ b/packages/devtools-kit/src/types/env.ts @@ -0,0 +1,3 @@ +export interface DevToolsEnv { + vitePluginDetected: boolean +} diff --git a/packages/devtools-kit-next/src/types/hook.ts b/packages/devtools-kit/src/types/hook.ts similarity index 100% rename from packages/devtools-kit-next/src/types/hook.ts rename to packages/devtools-kit/src/types/hook.ts diff --git a/packages/devtools-kit-next/src/types/index.ts b/packages/devtools-kit/src/types/index.ts similarity index 84% rename from packages/devtools-kit-next/src/types/index.ts rename to packages/devtools-kit/src/types/index.ts index f6ec969d..13c2de45 100644 --- a/packages/devtools-kit-next/src/types/index.ts +++ b/packages/devtools-kit/src/types/index.ts @@ -3,3 +3,4 @@ export * from './hook' export * from './state' export * from './context' export * from './router' +export * from './env' diff --git a/packages/devtools-kit-next/src/types/router.ts b/packages/devtools-kit/src/types/router.ts similarity index 100% rename from packages/devtools-kit-next/src/types/router.ts rename to packages/devtools-kit/src/types/router.ts diff --git a/packages/devtools-kit-next/src/types/state.ts b/packages/devtools-kit/src/types/state.ts similarity index 100% rename from packages/devtools-kit-next/src/types/state.ts rename to packages/devtools-kit/src/types/state.ts diff --git a/packages/devtools-kit-next/types.d.ts b/packages/devtools-kit/types.d.ts similarity index 100% rename from packages/devtools-kit-next/types.d.ts rename to packages/devtools-kit/types.d.ts diff --git a/packages/kit-playground/index.html b/packages/kit-playground/index.html deleted file mode 100644 index ca074c8b..00000000 --- a/packages/kit-playground/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - Vue DevTools Kit Playground - - - -
-
- - - diff --git a/packages/kit-playground/package.json b/packages/kit-playground/package.json deleted file mode 100644 index 6e967a54..00000000 --- a/packages/kit-playground/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "@vue/kit-devtools-playground", - "type": "module", - "version": "7.0.11", - "private": true, - "scripts": { - "app": "vue-devtools", - "build:playground": "vite build", - "dev": "vite", - "preview": "vite preview" - }, - "dependencies": { - "@vueuse/core": "^10.7.2", - "pinia": "^2.1.7", - "vue": "^3.4.14", - "vue-i18n": "^9.9.0", - "vue-router": "^4.2.5" - }, - "devDependencies": { - "@intlify/unplugin-vue-i18n": "^2.0.0", - "@vitejs/plugin-vue": "^5.0.3", - "@vue/devtools": "workspace:*", - "@vue/devtools-api": "workspace:*", - "@vue/devtools-kit-next": "workspace:*", - "sass": "^1.69.7", - "serve": "^14.2.1", - "typescript": "^5.3.3", - "vite": "^5.0.11", - "vite-plugin-inspect": "^0.8.1", - "vite-plugin-vue-devtools": "workspace:*" - } -} diff --git a/packages/kit-playground/public/vite.svg b/packages/kit-playground/public/vite.svg deleted file mode 100644 index e7b8dfb1..00000000 --- a/packages/kit-playground/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/kit-playground/src/App.vue b/packages/kit-playground/src/App.vue deleted file mode 100644 index 3a898239..00000000 --- a/packages/kit-playground/src/App.vue +++ /dev/null @@ -1,14 +0,0 @@ - - - diff --git a/packages/kit-playground/src/main.ts b/packages/kit-playground/src/main.ts deleted file mode 100644 index b114e849..00000000 --- a/packages/kit-playground/src/main.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { createPinia } from 'pinia' -import { createApp } from 'vue' -import type { RouteRecordRaw } from 'vue-router' -import { createRouter, createWebHistory } from 'vue-router' - -import { devtools } from '@vue/devtools-kit-next' -import App from './App.vue' -import Home from './pages/Home.vue' - -import './style.css' - -devtools.init() - -const pinia = createPinia() - -const app = createApp(App) - -const routes: RouteRecordRaw[] = [ - { - path: '/', - component: Home, - name: 'home', - alias: '/index', - }, -] - -const router = createRouter({ - history: createWebHistory(), - routes, -}) - -app.use(router) -app.use(pinia) - -// setTimeout(() => { -// }, 2000) - -app.mount('#app') diff --git a/packages/kit-playground/src/pages/Home.vue b/packages/kit-playground/src/pages/Home.vue deleted file mode 100644 index 612c020d..00000000 --- a/packages/kit-playground/src/pages/Home.vue +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/packages/kit-playground/src/stores/index.ts b/packages/kit-playground/src/stores/index.ts deleted file mode 100644 index a4764680..00000000 --- a/packages/kit-playground/src/stores/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { defineStore } from 'pinia' -import { computed, ref } from 'vue' - -export const useAppStore = defineStore('app', () => { - const count = ref(120) - function increment() { - count.value++ - } - const doubledCount = computed(() => count.value * 2) - - return { count, doubledCount, increment } -}) - -export const useCounterStore = defineStore('counter', () => { - const count = ref(10) - const name = ref('webfansplz!!!') - function increment() { - count.value++ - } - - return { count, name, increment } -}) diff --git a/packages/kit-playground/src/style.css b/packages/kit-playground/src/style.css deleted file mode 100644 index f0a3c180..00000000 --- a/packages/kit-playground/src/style.css +++ /dev/null @@ -1,16 +0,0 @@ -:root { - font-family: Inter, Avenir, Helvetica, Arial, sans-serif; - font-size: 16px; - line-height: 24px; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; -} diff --git a/packages/kit-playground/tsconfig.json b/packages/kit-playground/tsconfig.json deleted file mode 100644 index c934cb0a..00000000 --- a/packages/kit-playground/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "compilerOptions": { - "target": "ESNext", - "jsx": "preserve", - "lib": ["ESNext", "DOM"], - "useDefineForClassFields": true, - "module": "ESNext", - "moduleResolution": "Node", - "resolveJsonModule": true, - "strict": true, - "sourceMap": true, - "esModuleInterop": true, - "isolatedModules": true, - "skipLibCheck": true - } -} diff --git a/packages/kit-playground/vite.config.ts b/packages/kit-playground/vite.config.ts deleted file mode 100644 index 579cec38..00000000 --- a/packages/kit-playground/vite.config.ts +++ /dev/null @@ -1,24 +0,0 @@ -import path from 'node:path' -import { defineConfig } from 'vite' -import vue from '@vitejs/plugin-vue' - -// import VueDevtools from 'vite-plugin-vue-devtools' -import VueI18n from '@intlify/unplugin-vue-i18n/vite' - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [ - vue(), - // VueDevtools(), - // https://github.com/intlify/bundle-tools/tree/main/packages/unplugin-vue-i18n - VueI18n({ - runtimeOnly: true, - compositionOnly: true, - fullInstall: true, - include: [path.resolve('./locales/**')], - }), - ], - server: { - port: 3000, - }, -}) diff --git a/packages/schema/global.d.ts b/packages/schema/global.d.ts index 14107539..f0a766d3 100644 --- a/packages/schema/global.d.ts +++ b/packages/schema/global.d.ts @@ -1,41 +1 @@ -import type { DevToolsContext } from './src/types/context' -import type { AppRecord } from './src/types/vue' - -/* eslint-disable vars-on-top, no-var */ -declare global { - var __VUE_DEVTOOLS_GLOBAL_HOOK__: any - var __VUE_DEVTOOLS_CLIENT_URL__: string - - // FIXME: the type should be BridgeInstanceType - var __VUE_DEVTOOLS_BRIDGE__: any - var __VUE_DEVTOOLS_OVERLAY_BRIDGE__: any - var __VUE_DEVTOOLS_PANEL_BRIDGE__: any - - var __VUE_DEVTOOLS_CLIENT_CONNECTED__: boolean - // app record info - var __VUE_DEVTOOLS_APP_RECORDS__: AppRecord[] - var __VUE_DEVTOOLS_ACTIVE_APP_RECORD__: AppRecord - var __VUE_DEVTOOLS_APP_RECROD_INFO__: { - id: number - appIds: Set - } - // devtools global state - var __VUE_DEVTOOLS_GLOBAL_STATE__: any - // devtools context - var __VUE_DEVTOOLS_CONTEXT__: DevToolsContext - // router - var __VUE_DEVTOOLS_ROUTER__: unknown - // toggle overlay - var __VUE_DEVTOOLS_TOGGLE_OVERLAY__: (visible: boolean) => void - // is vite plugin detected - var __VUE_DEVTOOLS_VITE_PLUGIN_DETECTED__: boolean - // is browser extension detected - var __VUE_DEVTOOLS_BROWSER_EXTENSION_DETECTED__: boolean - // vite client url - var __VUE_DEVTOOLS_VITE_PLUGIN_CLIENT_URL__: string - // remote devtools option - var __VUE_DEVTOOLS_HOST__: string - var __VUE_DEVTOOLS_PORT__: number -} - export { } diff --git a/packages/vite/src/overlay.js b/packages/vite/src/overlay.js index 3575b047..429ecdcf 100644 --- a/packages/vite/src/overlay.js +++ b/packages/vite/src/overlay.js @@ -1,13 +1,15 @@ import vueDevToolsOptions from 'virtual:vue-devtools-options' import { Bridge, prepareInjection, setDevToolsClientUrl } from '@vue/devtools-core' import { BROADCAST_CHANNEL_NAME } from '@vue/devtools-shared' -import { addCustomTab, devtools } from '@vue/devtools-kit' +import { addCustomTab, devtools, setDevToolsEnv } from '@vue/devtools-kit' const overlayDir = `${vueDevToolsOptions.base || '/'}@id/virtual:vue-devtools-path:overlay` const body = document.getElementsByTagName('body')[0] const head = document.getElementsByTagName('head')[0] -window.__VUE_DEVTOOLS_VITE_PLUGIN_DETECTED__ = true +setDevToolsEnv({ + vitePluginDetected: true, +}) const devtoolsClientUrl = `${vueDevToolsOptions.clientHost || ''}${vueDevToolsOptions.base || '/'}__devtools__/` setDevToolsClientUrl(devtoolsClientUrl) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e453e634..f72afe7d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,9 +35,6 @@ importers: '@vue/devtools-core': specifier: workspace:^ version: link:packages/core - '@vue/devtools-kit-next': - specifier: workspace:^ - version: link:packages/devtools-kit-next '@vue/devtools-schema': specifier: workspace:^ version: link:packages/schema @@ -335,37 +332,6 @@ importers: packages/devtools-kit: dependencies: - '@vue/devtools-schema': - specifier: workspace:^ - version: link:../schema - '@vue/devtools-shared': - specifier: workspace:^ - version: link:../shared - hookable: - specifier: ^5.5.3 - version: 5.5.3 - mitt: - specifier: ^3.0.1 - version: 3.0.1 - perfect-debounce: - specifier: ^1.0.0 - version: 1.0.0 - speakingurl: - specifier: ^14.0.1 - version: 14.0.1 - devDependencies: - vue: - specifier: ^3.4.15 - version: 3.4.15(typescript@5.3.3) - vue-router: - specifier: ^4.2.5 - version: 4.2.5(vue@3.4.15) - - packages/devtools-kit-next: - dependencies: - '@vue/devtools-schema': - specifier: workspace:^ - version: link:../schema '@vue/devtools-shared': specifier: workspace:^ version: link:../shared @@ -435,58 +401,6 @@ importers: specifier: ^3.4.15 version: 3.4.15(typescript@5.3.3) - packages/kit-playground: - dependencies: - '@vueuse/core': - specifier: ^10.7.2 - version: 10.7.2(vue@3.4.15) - pinia: - specifier: ^2.1.7 - version: 2.1.7(typescript@5.3.3)(vue@3.4.15) - vue: - specifier: ^3.4.14 - version: 3.4.15(typescript@5.3.3) - vue-i18n: - specifier: ^9.9.0 - version: 9.9.0(vue@3.4.15) - vue-router: - specifier: ^4.2.5 - version: 4.2.5(vue@3.4.15) - devDependencies: - '@intlify/unplugin-vue-i18n': - specifier: ^2.0.0 - version: 2.0.0(rollup@3.28.1)(vue-i18n@9.9.0) - '@vitejs/plugin-vue': - specifier: ^5.0.3 - version: 5.0.3(vite@5.0.12)(vue@3.4.15) - '@vue/devtools': - specifier: workspace:* - version: link:../devtools - '@vue/devtools-api': - specifier: workspace:* - version: link:../devtools-api - '@vue/devtools-kit-next': - specifier: workspace:* - version: link:../devtools-kit-next - sass: - specifier: ^1.69.7 - version: 1.70.0 - serve: - specifier: ^14.2.1 - version: 14.2.1 - typescript: - specifier: ^5.3.3 - version: 5.3.3 - vite: - specifier: ^5.0.11 - version: 5.0.12(@types/node@20.11.6)(sass@1.70.0) - vite-plugin-inspect: - specifier: ^0.8.1 - version: 0.8.3(rollup@3.28.1)(vite@5.0.12) - vite-plugin-vue-devtools: - specifier: workspace:* - version: link:../vite - packages/overlay: dependencies: '@vue/devtools-core': From ffd28082d0f51601d92aae35eb70e63536e879cb Mon Sep 17 00:00:00 2001 From: arlo Date: Fri, 2 Feb 2024 23:09:36 +0800 Subject: [PATCH 13/18] chore: update --- packages/devtools-kit/src/api/api.ts | 2 +- packages/devtools-kit/src/plugins/component.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/devtools-kit/src/api/api.ts b/packages/devtools-kit/src/api/api.ts index 487d14b0..29e29f79 100644 --- a/packages/devtools-kit/src/api/api.ts +++ b/packages/devtools-kit/src/api/api.ts @@ -44,7 +44,7 @@ export class DevToolsPluginApi { addInspector(payload: InspectorApiPayload) { addInspector({ id: payload.id, - nodeId: payload.id, + nodeId: '', filter: '', treeFilterPlaceholder: payload.treeFilterPlaceholder || '', }) diff --git a/packages/devtools-kit/src/plugins/component.ts b/packages/devtools-kit/src/plugins/component.ts index d7c0a07b..f8f744e3 100644 --- a/packages/devtools-kit/src/plugins/component.ts +++ b/packages/devtools-kit/src/plugins/component.ts @@ -1,6 +1,6 @@ import { debounce } from 'perfect-debounce' import { VueAppInstance } from '../types' -import { DevToolsEvents, setupDevToolsPlugin } from '../api' +import { DevToolsEvents, apiHooks, setupDevToolsPlugin } from '../api' import { devtoolsContext } from '../state' import { hook } from '../hook' import { getAppRecord, getComponentId, getComponentInstance } from '../core/component/utils' @@ -9,7 +9,7 @@ import { ComponentWalker } from '../core/component/tree/walker' import { editState } from '../core/component/state/editor' import { getInstanceState } from '../core/component/state' -const INSPECTOR_ID = '__vue-devtools-component-plugin__' +const INSPECTOR_ID = 'components' export function registerComponentDevToolsPlugin(app: VueAppInstance) { setupDevToolsPlugin({ From a7b5926c1a6f03ab768f45ac33e511d86fc8a669 Mon Sep 17 00:00:00 2001 From: arlo Date: Sat, 3 Feb 2024 11:46:07 +0800 Subject: [PATCH 14/18] chore: update --- packages/client/package.json | 1 - packages/core/package.json | 1 - packages/core/src/vue-plugin.ts | 3 +-- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/client/package.json b/packages/client/package.json index 545b7ac7..1df74051 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -28,7 +28,6 @@ "@unocss/runtime": "^0.58.3", "@vue/devtools-core": "workspace:^", "@vue/devtools-kit": "workspace:^", - "@vue/devtools-schema": "workspace:*", "@vue/devtools-shared": "workspace:^", "@vue/devtools-ui": "workspace:*", "@vueuse/core": "^10.7.2", diff --git a/packages/core/package.json b/packages/core/package.json index 1bdc21e6..fbe16e8d 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -27,7 +27,6 @@ }, "dependencies": { "@vue/devtools-kit": "workspace:^", - "@vue/devtools-schema": "workspace:^", "@vue/devtools-shared": "workspace:^", "birpc": "^0.2.14", "fast-glob": "^3.3.2", diff --git a/packages/core/src/vue-plugin.ts b/packages/core/src/vue-plugin.ts index 582fa816..da47e234 100644 --- a/packages/core/src/vue-plugin.ts +++ b/packages/core/src/vue-plugin.ts @@ -1,7 +1,6 @@ import type { App, InjectionKey, Plugin, Ref } from 'vue' import { inject, ref } from 'vue' -import type { CustomCommand, CustomTab } from '@vue/devtools-kit' -import type { AppRecord } from '@vue/devtools-schema' +import type { AppRecord, CustomCommand, CustomTab } from '@vue/devtools-kit' import type { BridgeInstanceType } from './bridge/core' import { DevToolsRpc } from './bridge' From b5df518bead72c5117a55ab2a992c128b0f44cca Mon Sep 17 00:00:00 2001 From: arlo Date: Sat, 3 Feb 2024 12:00:55 +0800 Subject: [PATCH 15/18] chore: update --- packages/schema/global.d.ts | 1 - packages/schema/types.d.ts | 1 - 2 files changed, 2 deletions(-) delete mode 100644 packages/schema/global.d.ts diff --git a/packages/schema/global.d.ts b/packages/schema/global.d.ts deleted file mode 100644 index f0a766d3..00000000 --- a/packages/schema/global.d.ts +++ /dev/null @@ -1 +0,0 @@ -export { } diff --git a/packages/schema/types.d.ts b/packages/schema/types.d.ts index e4001646..9247c2a8 100644 --- a/packages/schema/types.d.ts +++ b/packages/schema/types.d.ts @@ -1,2 +1 @@ -/// export * from './dist/index' From e342d5456d49dac870350fcb8861bf1cdd4d8996 Mon Sep 17 00:00:00 2001 From: arlo Date: Sat, 3 Feb 2024 12:07:46 +0800 Subject: [PATCH 16/18] chore: fix lint --- package.json | 2 +- packages/devtools-kit/global.d.ts | 2 ++ pnpm-lock.yaml | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 6f2818f2..afa6f2f1 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "@types/node": "^20.11.10", "@unocss/eslint-plugin": "^0.58.4", "@vue/devtools-core": "workspace:^", - "@vue/devtools-schema": "workspace:^", + "@vue/devtools-kit": "workspace:^", "@vue/test-utils": "^2.4.4", "archiver": "^6.0.1", "bumpp": "^9.3.0", diff --git a/packages/devtools-kit/global.d.ts b/packages/devtools-kit/global.d.ts index 332b8f15..dcbaea1a 100644 --- a/packages/devtools-kit/global.d.ts +++ b/packages/devtools-kit/global.d.ts @@ -13,6 +13,8 @@ declare global { var __VUE_DEVTOOLS_ROUTER_INFO__: RouterInfo var __VUE_DEVTOOLS_ENV__: DevToolsEnv var __VUE_DEVTOOLS_COMPONENT_INSPECTOR_ENABLED__: boolean + var __VUE_DEVTOOLS_VITE_PLUGIN_DETECTED__: boolean + var __VUE_DEVTOOLS_VITE_PLUGIN_CLIENT_URL__: string } export { } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fbbfb441..644db47d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,9 +35,9 @@ importers: '@vue/devtools-core': specifier: workspace:^ version: link:packages/core - '@vue/devtools-schema': + '@vue/devtools-kit': specifier: workspace:^ - version: link:packages/schema + version: link:packages/devtools-kit '@vue/test-utils': specifier: ^2.4.4 version: 2.4.4(vue@3.4.15) From d98a6e844b5e66eedbb5cd9c37da1b71532bef5b Mon Sep 17 00:00:00 2001 From: arlo Date: Sat, 3 Feb 2024 12:52:19 +0800 Subject: [PATCH 17/18] chore: update --- .../__tests__/component/format.test.ts | 20 +++++++------ packages/devtools-kit/package.json | 3 ++ .../src/core/component/state/custom.ts | 16 +++++------ .../src/core/component/state/format.ts | 28 ++++++++++++++----- .../src/core/component/types/custom.ts | 1 + .../src/core/component/types/index.ts | 1 + packages/devtools-kit/src/index.ts | 2 +- 7 files changed, 47 insertions(+), 24 deletions(-) create mode 100644 packages/devtools-kit/src/core/component/types/custom.ts diff --git a/packages/devtools-kit/__tests__/component/format.test.ts b/packages/devtools-kit/__tests__/component/format.test.ts index 51b30832..e715a76d 100644 --- a/packages/devtools-kit/__tests__/component/format.test.ts +++ b/packages/devtools-kit/__tests__/component/format.test.ts @@ -1,7 +1,9 @@ import * as format from '../../src/core/component/state/format' import { INFINITY, NAN, NEGATIVE_INFINITY, UNDEFINED } from '../../src/core/component/state/constants' -describe('format: displayText and rawValue can be calculated by formatInspectorStateValue, getRawValue', () => { +import { customTypeEnums } from '../../src/core/component/types' + +describe('format: displayText and rawValue can be calculated by formatInspectorStateValue, getRaw', () => { describe('type: literals', () => { // eslint-disable-next-line test/consistent-test-it test.each([ @@ -16,7 +18,7 @@ describe('format: displayText and rawValue can be calculated by formatInspectorS { literal: UNDEFINED, displayText: 'undefined' }, ])('type: %s', (value) => { const displayText = format.formatInspectorStateValue(value.literal) - const rawValue = format.getRawValue(value.literal).value + const rawValue = format.getRaw(value.literal).value expect(displayText).toBe(value.displayText) expect(rawValue).toBe(value.literal) @@ -26,7 +28,7 @@ describe('format: displayText and rawValue can be calculated by formatInspectorS it('type: plain object', () => { const value = { foo: 'bar' } const displayText = format.formatInspectorStateValue(value) - const rawValue = format.getRawValue(value).value + const rawValue = format.getRaw(value).value expect(displayText).toBe('Object') expect(rawValue).toEqual(value) @@ -35,7 +37,7 @@ describe('format: displayText and rawValue can be calculated by formatInspectorS it('type: array', () => { const value = ['foo', { bar: 'baz' }] const displayText = format.formatInspectorStateValue(value) - const rawValue = format.getRawValue(value).value + const rawValue = format.getRaw(value).value expect(displayText).toBe('Array[2]') expect(rawValue).toEqual(value) @@ -45,7 +47,7 @@ describe('format: displayText and rawValue can be calculated by formatInspectorS it('type: common custom', () => { const value = { _custom: { displayText: 'custom-display', value: Symbol(123) } } const displayText = format.formatInspectorStateValue(value) - const rawValue = format.getRawValue(value).value + const rawValue = format.getRaw(value).value expect(displayText).toBe(value._custom.displayText) expect(rawValue).toEqual(value._custom.value) @@ -62,7 +64,7 @@ describe('format: displayText and rawValue can be calculated by formatInspectorS } const displayText = format.formatInspectorStateValue(value) - const rawValue = format.getRawValue(value).value + const rawValue = format.getRaw(value).value expect(displayText).toBe(value._custom.value._custom.displayText) expect(rawValue).toEqual(value._custom.value._custom.value) @@ -87,8 +89,9 @@ describe('format: toEdit', () => { { value: { foo: NAN }, target: '{"foo":NaN}' }, { value: { foo: NEGATIVE_INFINITY }, target: '{"foo":-Infinity}' }, { value: { foo: UNDEFINED }, target: '{"foo":undefined}' }, + { value: '123', customType: 'bigint' as customTypeEnums, target: '123' }, ])('value: $value will be deserialized to target', (value) => { - const deserialized = format.toEdit(value.value) + const deserialized = format.toEdit(value.value, value.customType) expect(deserialized).toBe(value.target) }) }) @@ -113,8 +116,9 @@ describe('format: toSubmit', () => { { value: '{"foo":undefined}', target: {} }, // Regex test: The token in key field kept untouched. { value: '{"undefined": NaN }', target: { undefined: Number.NaN } }, + { value: '123', customType: 'bigint' as customTypeEnums, target: BigInt(123) }, ])('value: $value will be serialized to target', (value) => { - const serialized = format.toSubmit(value.value) + const serialized = format.toSubmit(value.value, value.customType) expect(serialized).toStrictEqual(value.target) }) }) diff --git a/packages/devtools-kit/package.json b/packages/devtools-kit/package.json index 7815393e..935ee219 100644 --- a/packages/devtools-kit/package.json +++ b/packages/devtools-kit/package.json @@ -22,6 +22,9 @@ "prepare:type": "tsup --dts-only", "stub": "tsup --watch --onSuccess 'tsup --dts-only'" }, + "peerDependencies": { + "vue": "^3.0.0" + }, "dependencies": { "@vue/devtools-shared": "workspace:^", "hookable": "^5.5.3", diff --git a/packages/devtools-kit/src/core/component/state/custom.ts b/packages/devtools-kit/src/core/component/state/custom.ts index 179c1b30..5ba7ab86 100644 --- a/packages/devtools-kit/src/core/component/state/custom.ts +++ b/packages/devtools-kit/src/core/component/state/custom.ts @@ -1,4 +1,4 @@ -import type { InspectorState } from '../types' +import type { InspectorState, customTypeEnums } from '../types' import { getComponentName, getInstanceName } from '../utils' import { processInstanceState } from './process' import { escape, getSetupStateType, toRaw } from './util' @@ -21,7 +21,7 @@ export function getFunctionDetails(func: Function) { const name = typeof func.name === 'string' ? func.name : '' return { _custom: { - type: 'function', + type: 'function' satisfies customTypeEnums, displayText: `function ${escape(name)}${args}`, tooltipText: string.trim() ? `
${string}
` : null, }, @@ -64,7 +64,7 @@ export function getSetDetails(val: Set) { const list = Array.from(val) return { _custom: { - type: 'set', + type: 'set' satisfies customTypeEnums, displayText: `Set[${list.length}]`, value: list, readOnly: true, @@ -120,7 +120,7 @@ function namedNodeMapToObject(map: NamedNodeMap) { export function getStoreDetails(store) { return { _custom: { - type: 'store', + type: 'store' satisfies customTypeEnums, displayText: 'Store', value: { state: store.state, @@ -179,7 +179,7 @@ export function getComponentDefinitionDetails(definition) { } return { _custom: { - type: 'component-definition', + type: 'component-definition' satisfies customTypeEnums, displayText: display, tooltipText: 'Component definition', ...definition.__file @@ -195,7 +195,7 @@ export function getHTMLElementDetails(value: HTMLElement) { try { return { _custom: { - type: 'HTMLElement', + type: 'HTMLElement' satisfies customTypeEnums, displayText: `<${value.tagName.toLowerCase()}>`, value: namedNodeMapToObject(value.attributes), }, @@ -204,7 +204,7 @@ export function getHTMLElementDetails(value: HTMLElement) { catch (e) { return { _custom: { - type: 'HTMLElement', + type: 'HTMLElement' satisfies customTypeEnums, displayText: `${String(value)}`, }, } @@ -232,7 +232,7 @@ export function getObjectDetails(object: Record) { if (typeof object.__asyncLoader === 'function') { return { _custom: { - type: 'component-definition', + type: 'component-definition' satisfies customTypeEnums, display: 'Async component definition', }, } diff --git a/packages/devtools-kit/src/core/component/state/format.ts b/packages/devtools-kit/src/core/component/state/format.ts index df4cf0c1..7a686176 100644 --- a/packages/devtools-kit/src/core/component/state/format.ts +++ b/packages/devtools-kit/src/core/component/state/format.ts @@ -1,4 +1,4 @@ -import { InspectorCustomState, InspectorState } from '../types' +import { InspectorCustomState, InspectorState, customTypeEnums } from '../types' import { INFINITY, NAN, NEGATIVE_INFINITY, UNDEFINED, rawTypeRE, specialTypeRE } from './constants' import { isPlainObject } from './is' import { escape, internalStateTokenToString, replaceStringToToken, replaceTokenToString } from './util' @@ -87,30 +87,44 @@ export function formatInspectorStateValue(value, quotes = false) { return value } -export function getRawValue(value: InspectorState['value']) { +export function getRaw(value: InspectorState['value']): { + value: object | string | number | boolean | null + inherit: {} | { abstract: true } + customType?: customTypeEnums +} { + let customType: customTypeEnums const isCustom = getInspectorStateValueType(value) === 'custom' let inherit = {} if (isCustom) { const data = value as InspectorCustomState const customValue = data._custom?.value + const currentCustomType = data._custom?.type const nestedCustom = typeof customValue === 'object' && customValue !== null && '_custom' in customValue - ? getRawValue(customValue) - : { inherit: undefined, value: undefined } + ? getRaw(customValue) + : { inherit: undefined, value: undefined, customType: undefined } inherit = nestedCustom.inherit || data._custom?.fields || {} value = nestedCustom.value || customValue as string + customType = nestedCustom.customType || currentCustomType as customTypeEnums } // @ts-expect-error @TODO: type if (value && value._isArray) // @ts-expect-error @TODO: type value = value.items - return { value, inherit } + // @ts-expect-error customType map be assigned as undefined. + return { value, inherit, customType } } -export function toEdit(value: unknown) { +export function toEdit(value: unknown, customType?: customTypeEnums) { + if (customType === 'bigint') + return value as string + return replaceTokenToString(JSON.stringify(value)) } -export function toSubmit(value: string) { +export function toSubmit(value: string, customType?: customTypeEnums) { + if (customType === 'bigint') + return BigInt(value) + return JSON.parse(replaceStringToToken(value), reviver) } diff --git a/packages/devtools-kit/src/core/component/types/custom.ts b/packages/devtools-kit/src/core/component/types/custom.ts new file mode 100644 index 00000000..f4269f14 --- /dev/null +++ b/packages/devtools-kit/src/core/component/types/custom.ts @@ -0,0 +1 @@ +export type customTypeEnums = 'function' | 'bigint' | 'map' | 'set' | 'store' | 'router' | 'component' | 'component-definition' | 'HTMLElement' | 'component-definition' diff --git a/packages/devtools-kit/src/core/component/types/index.ts b/packages/devtools-kit/src/core/component/types/index.ts index cabe050e..5d6d11ca 100644 --- a/packages/devtools-kit/src/core/component/types/index.ts +++ b/packages/devtools-kit/src/core/component/types/index.ts @@ -2,3 +2,4 @@ export * from './tree' export * from './bounding-rect' export * from './state' export * from './editor' +export * from './custom' diff --git a/packages/devtools-kit/src/index.ts b/packages/devtools-kit/src/index.ts index 671cf565..1556b959 100644 --- a/packages/devtools-kit/src/index.ts +++ b/packages/devtools-kit/src/index.ts @@ -17,7 +17,7 @@ export type * from './core/inspector' export type * from './types' export { parse, stringify } from './shared' -export { formatInspectorStateValue, getInspectorStateValueType, getRawValue, toEdit, toSubmit } from './core/component/state/format' +export { formatInspectorStateValue, getInspectorStateValueType, getRaw, toEdit, toSubmit } from './core/component/state/format' export { UNDEFINED } from './core/component/state/constants' export const devtools = { From 9cb944081cc84abf22e2a03d75decc65f8f28353 Mon Sep 17 00:00:00 2001 From: arlo Date: Sat, 3 Feb 2024 12:59:15 +0800 Subject: [PATCH 18/18] chore: fix types --- packages/devtools-kit/global.d.ts | 1 + tsconfig.json | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/devtools-kit/global.d.ts b/packages/devtools-kit/global.d.ts index dcbaea1a..bcc83fd3 100644 --- a/packages/devtools-kit/global.d.ts +++ b/packages/devtools-kit/global.d.ts @@ -15,6 +15,7 @@ declare global { var __VUE_DEVTOOLS_COMPONENT_INSPECTOR_ENABLED__: boolean var __VUE_DEVTOOLS_VITE_PLUGIN_DETECTED__: boolean var __VUE_DEVTOOLS_VITE_PLUGIN_CLIENT_URL__: string + var __VUE_DEVTOOLS_BROWSER_EXTENSION_DETECTED__: boolean } export { } diff --git a/tsconfig.json b/tsconfig.json index 5c65505c..94492949 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,8 +18,7 @@ "types": [ "chrome", "vite/client", - "vitest/globals", - "@vue/devtools-schema" + "vitest/globals" ], "allowJs": true, "strict": true,