Skip to content

Commit 82509c9

Browse files
authoredOct 30, 2024··
feat: add node tags for component tree (#655)
1 parent b549a77 commit 82509c9

File tree

9 files changed

+56
-26
lines changed

9 files changed

+56
-26
lines changed
 

‎packages/applet/src/components/tree/TreeViewer.vue

+27-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
<script setup lang="ts">
22
import type { ComponentTreeNode, InspectorTree } from '@vue/devtools-kit'
3+
import { UNDEFINED } from '@vue/devtools-kit'
4+
import { vTooltip } from '@vue/devtools-ui'
35
import NodeTag from '~/components/basic/NodeTag.vue'
46
import ToggleExpanded from '~/components/basic/ToggleExpanded.vue'
57
import ComponentTreeViewer from '~/components/tree/TreeViewer.vue'
6-
78
import { useSelect } from '~/composables/select'
89
import { useToggleExpanded } from '~/composables/toggle-expanded'
910
10-
withDefaults(defineProps<{
11+
const props = withDefaults(defineProps<{
1112
data: ComponentTreeNode[] | InspectorTree[]
1213
depth: number
1314
withTag: boolean
@@ -54,8 +55,32 @@ function select(id: string) {
5455
<span font-state-field text-3.5>
5556
<span v-if="withTag" class="text-gray-400 dark:text-gray-600 group-hover:(text-white op50) [.active_&]:(op50 text-white!)">&lt;</span>
5657
<span group-hover:text-white class="ws-nowrap [.active_&]:(text-white)">{{ normalizeLabel(item) }}</span>
58+
<!-- @vue-expect-error skip type check -->
59+
<span
60+
v-if="(item.renderKey === 0 || !!item.renderKey) && item.renderKey !== UNDEFINED"
61+
class="text-xs opacity-50"
62+
:class="{
63+
'opacity-100': selectedNodeId === item.id,
64+
}"
65+
>
66+
<span :class="[selectedNodeId === item.id ? 'text-purple-200' : 'text-purple-500']"> key</span>=<span>{{ (item as ComponentTreeNode).renderKey }}</span>
67+
</span>
5768
<span v-if="withTag" class="text-gray-400 dark:text-gray-600 group-hover:(text-white op50) [.active_&]:(op50 text-white!)">&gt;</span>
5869
</span>
70+
<span
71+
v-if="(item as ComponentTreeNode).isFragment"
72+
v-tooltip="'Has multiple root DOM nodes'"
73+
class="ml-2 rounded-sm bg-blue-400 px-1 text-[0.75rem] leading-snug dark:bg-blue-800"
74+
>
75+
fragment
76+
</span>
77+
<span
78+
v-if="(item as ComponentTreeNode).inactive"
79+
v-tooltip="'Currently inactive but not destroyed'"
80+
class="ml-2 rounded-sm bg-gray-500 px-1 text-[0.75rem] leading-snug"
81+
>
82+
inactive
83+
</span>
5984
<NodeTag v-for="(_item, _index) in item.tags" :key="_index" :tag="_item" />
6085
</div>
6186
<div

‎packages/devtools-kit/src/api/v6/index.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { DevtoolsContext } from '../../ctx'
22
import type { App, ComponentBounds, ComponentInstance, CustomInspectorOptions, DevToolsPlugin, TimelineEventOptions, TimelineLayerOptions } from '../../types'
33
import { getPluginSettings, initPluginSettings } from '../../core/plugin/plugin-settings'
44

5-
import { DevToolsContextHookKeys, DevToolsV6PluginAPIHookKeys, DevToolsV6PluginAPIHooks } from '../../ctx/hook'
5+
import { DevToolsContextHookKeys, DevToolsV6PluginAPIHookKeys, DevToolsV6PluginAPIHookPayloads, DevToolsV6PluginAPIHooks } from '../../ctx/hook'
66
import { getActiveInspectors } from '../../ctx/inspector'
77
import { devtoolsHooks } from '../../hook'
88
import { DevToolsHooks } from '../../types'
@@ -96,6 +96,10 @@ export class DevToolsV6PluginAPI {
9696
this.hooks.callHook(DevToolsContextHookKeys.CUSTOM_INSPECTOR_SELECT_NODE, { inspectorId, nodeId, plugin: this.plugin })
9797
}
9898

99+
visitComponentTree(payload: DevToolsV6PluginAPIHookPayloads[DevToolsV6PluginAPIHookKeys.VISIT_COMPONENT_TREE]) {
100+
return this.hooks.callHook(DevToolsV6PluginAPIHookKeys.VISIT_COMPONENT_TREE, payload)
101+
}
102+
99103
// timeline
100104
now(): number {
101105
return Date.now()

‎packages/devtools-kit/src/core/app/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ function getAppRecordId(app: VueAppInstance['appContext']['app'], defaultId?: st
4646
return id
4747
}
4848

49-
export function createAppRecord(app: VueAppInstance['appContext']['app']): AppRecord {
49+
export function createAppRecord(app: VueAppInstance['appContext']['app'], types: Record<string, string | symbol>): AppRecord {
5050
const rootInstance = getAppRootInstance(app)
5151
if (rootInstance) {
5252
appRecordInfo.id++
@@ -56,6 +56,7 @@ export function createAppRecord(app: VueAppInstance['appContext']['app']): AppRe
5656
const record: AppRecord = {
5757
id,
5858
name,
59+
types,
5960
instanceMap: new Map(),
6061
perfGroupIds: new Map(),
6162
rootInstance,

‎packages/devtools-kit/src/core/component/tree/walker.ts

+11-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { SuspenseBoundary, VNode } from 'vue'
2+
import type { DevToolsPluginAPI } from '../../../api'
23
import type { ComponentTreeNode, VueAppInstance } from '../../../types'
34
import type { ComponentFilter } from './filter'
45
// import { devtoolsAppRecords, devtoolsContext } from '../../../state'
@@ -11,19 +12,22 @@ interface ComponentWalkerOptions {
1112
filterText?: string
1213
maxDepth: number | null
1314
recursively: boolean
15+
api: InstanceType<typeof DevToolsPluginAPI>
1416
}
1517

1618
export class ComponentWalker {
1719
maxDepth: number | null
1820
recursively: boolean
1921
componentFilter: InstanceType<typeof ComponentFilter>
22+
api: InstanceType<typeof DevToolsPluginAPI>
2023
// Dedupe instances (Some instances may be both on a component and on a child abstract/functional component)
2124
private captureIds: Map<string, undefined> = new Map()
2225
constructor(options: ComponentWalkerOptions) {
23-
const { filterText = '', maxDepth, recursively } = options
26+
const { filterText = '', maxDepth, recursively, api } = options
2427
this.componentFilter = createComponentFilter(filterText)
2528
this.maxDepth = maxDepth
2629
this.recursively = recursively
30+
this.api = api
2731
}
2832

2933
public getComponentTree(instance: VueAppInstance): Promise<ComponentTreeNode[]> {
@@ -157,13 +161,12 @@ export class ComponentWalker {
157161
this.mark(instance, true)
158162
}
159163

160-
// @TODO: impl
161-
// devtoolsContext.api.visitComponentTree({
162-
// treeNode,
163-
// componentInstance: instance,
164-
// app: instance.appContext.app,
165-
// filter: this.componentFilter.filter,
166-
// })
164+
this.api.visitComponentTree({
165+
treeNode,
166+
componentInstance: instance,
167+
app: instance.appContext.app,
168+
filter: this.componentFilter.filter,
169+
})
167170
return treeNode
168171
}
169172

‎packages/devtools-kit/src/core/component/utils/index.ts

+5-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { AppRecord, VueAppInstance } from '../../../types'
22
import { basename, classify } from '@vue/devtools-shared'
3-
import { Fragment } from '../../../shared/stub-vue'
43

54
function getComponentTypeName(options: VueAppInstance['type']) {
65
const name = options.name || options._componentTag || options.__VUE_DEVTOOLS_COMPONENT_GUSSED_NAME__ || options.__name
@@ -56,14 +55,11 @@ export async function getComponentId(options: { app: VueAppInstance, uid: number
5655

5756
export function isFragment(instance: VueAppInstance) {
5857
const subTreeType = instance.subTree?.type
59-
// TODO: resolve static type, the subTree.children of static type will be a string instead of children like Fragment
60-
// return subTreeType === Fragment || (
61-
// subTreeType === Static
62-
// // @ts-expect-error vue internal type
63-
// ? instance.subTree.staticCount > 1
64-
// : false
65-
// )
66-
return subTreeType === Fragment
58+
const appRecord = getAppRecord(instance)
59+
if (appRecord) {
60+
return appRecord?.types?.Fragment === subTreeType
61+
}
62+
return false
6763
}
6864

6965
export function isBeingDestroyed(instance: VueAppInstance) {

‎packages/devtools-kit/src/core/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ export function initDevTools() {
7979
})
8080

8181
// create app record
82-
hook.on.vueAppInit(async (app, version) => {
83-
const appRecord = createAppRecord(app)
82+
hook.on.vueAppInit(async (app, version, types) => {
83+
const appRecord = createAppRecord(app, types)
8484
const normalizedAppRecord = {
8585
...appRecord,
8686
app,

‎packages/devtools-kit/src/core/plugin/components.ts

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export function createComponentsDevToolsPlugin(app: App): [PluginDescriptor, Plu
3737
// @TODO: should make this configurable?
3838
maxDepth: 100,
3939
recursively: false,
40+
api,
4041
})
4142
// @ts-expect-error skip type @TODO
4243
payload.rootNodes = await walker.getComponentTree(instance)

‎packages/devtools-kit/src/hook/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,11 @@ export function createDevToolsHook(): DevToolsHook {
8484
export function subscribeDevToolsHook() {
8585
const hook = target.__VUE_DEVTOOLS_GLOBAL_HOOK__ as DevToolsHook
8686
// app init hook
87-
hook.on<DevToolsEvent[DevToolsHooks.APP_INIT]>(DevToolsHooks.APP_INIT, (app, version) => {
87+
hook.on<DevToolsEvent[DevToolsHooks.APP_INIT]>(DevToolsHooks.APP_INIT, (app, version, types) => {
8888
if (app?._instance?.type?.devtools?.hide)
8989
return
9090

91-
devtoolsHooks.callHook(DevToolsHooks.APP_INIT, app, version)
91+
devtoolsHooks.callHook(DevToolsHooks.APP_INIT, app, version, types)
9292
})
9393

9494
hook.on<DevToolsEvent[DevToolsHooks.APP_UNMOUNT]>(DevToolsHooks.APP_UNMOUNT, (app) => {

‎packages/devtools-kit/src/types/hook.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export enum DevToolsHooks {
2222
}
2323

2424
export interface DevToolsEvent {
25-
[DevToolsHooks.APP_INIT]: (app: VueAppInstance['appContext']['app'], version: string) => void | Promise<void>
25+
[DevToolsHooks.APP_INIT]: (app: VueAppInstance['appContext']['app'], version: string, types: Record<string, string | symbol>) => void | Promise<void>
2626
[DevToolsHooks.APP_CONNECTED]: () => void
2727
[DevToolsHooks.APP_UNMOUNT]: (app: VueAppInstance['appContext']['app']) => void | Promise<void>
2828
[DevToolsHooks.COMPONENT_ADDED]: (app: HookAppInstance, uid: number, parentUid: number, component: VueAppInstance) => void | Promise<void>

0 commit comments

Comments
 (0)
Please sign in to comment.