vite-uni-dev-tool 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/README.md +8 -0
  2. package/dist/const.cjs +1 -1
  3. package/dist/const.d.ts +1 -0
  4. package/dist/const.d.ts.map +1 -1
  5. package/dist/const.js +1 -1
  6. package/dist/core.d.ts.map +1 -1
  7. package/dist/core.js +2 -2
  8. package/dist/i18n/locales/en.cjs +1 -1
  9. package/dist/i18n/locales/en.d.ts +5 -0
  10. package/dist/i18n/locales/en.d.ts.map +1 -1
  11. package/dist/i18n/locales/en.js +1 -1
  12. package/dist/i18n/locales/zh-Hans.cjs +1 -1
  13. package/dist/i18n/locales/zh-Hans.d.ts +5 -0
  14. package/dist/i18n/locales/zh-Hans.d.ts.map +1 -1
  15. package/dist/i18n/locales/zh-Hans.js +1 -1
  16. package/dist/modules/devIntercept/index.cjs +9 -9
  17. package/dist/modules/devIntercept/index.d.ts +1 -1
  18. package/dist/modules/devIntercept/index.d.ts.map +1 -1
  19. package/dist/modules/devIntercept/index.js +9 -9
  20. package/dist/modules/devStore/index.cjs +1 -1
  21. package/dist/modules/devStore/index.d.ts.map +1 -1
  22. package/dist/modules/devStore/index.js +1 -1
  23. package/dist/plugins/uniDevTool/transform/transformMain.cjs +3 -3
  24. package/dist/plugins/uniDevTool/transform/transformMain.d.ts +2 -1
  25. package/dist/plugins/uniDevTool/transform/transformMain.d.ts.map +1 -1
  26. package/dist/plugins/uniDevTool/transform/transformMain.js +3 -3
  27. package/dist/plugins/uniDevTool/transform/transformVue.cjs +31 -25
  28. package/dist/plugins/uniDevTool/transform/transformVue.d.ts +2 -1
  29. package/dist/plugins/uniDevTool/transform/transformVue.d.ts.map +1 -1
  30. package/dist/plugins/uniDevTool/transform/transformVue.js +30 -24
  31. package/dist/plugins/uniDevTool/uniDevTool.cjs +3 -3
  32. package/dist/plugins/uniDevTool/uniDevTool.d.ts +3 -1
  33. package/dist/plugins/uniDevTool/uniDevTool.d.ts.map +1 -1
  34. package/dist/plugins/uniDevTool/uniDevTool.js +3 -3
  35. package/dist/type.d.ts +3 -0
  36. package/dist/type.d.ts.map +1 -1
  37. package/dist/v3/DevTool/components/BluetoothList/BluetoothItem.vue +199 -199
  38. package/dist/v3/DevTool/components/BluetoothList/BluetoothTool.vue +730 -730
  39. package/dist/v3/DevTool/components/BluetoothList/index.vue +167 -167
  40. package/dist/v3/DevTool/components/CaptureScreen/index.vue +109 -109
  41. package/dist/v3/DevTool/components/ConsoleList/ConsoleItem.vue +230 -225
  42. package/dist/v3/DevTool/components/ConsoleList/RunJSInput.vue +247 -247
  43. package/dist/v3/DevTool/components/ConsoleList/index.vue +171 -171
  44. package/dist/v3/DevTool/components/ConsoleList/staticTips.ts +1145 -1145
  45. package/dist/v3/DevTool/components/DevToolTitle/index.vue +24 -24
  46. package/dist/v3/DevTool/components/DevToolWindow/DevToolOverlay.vue +197 -197
  47. package/dist/v3/DevTool/components/DevToolWindow/hooks/dataUtils.ts +48 -48
  48. package/dist/v3/DevTool/components/DevToolWindow/hooks/useDevToolData.ts +387 -387
  49. package/dist/v3/DevTool/components/DevToolWindow/hooks/useDevToolHandlers.ts +629 -629
  50. package/dist/v3/DevTool/components/DevToolWindow/hooks/useDevToolOverlay.ts +201 -197
  51. package/dist/v3/DevTool/components/ElEvent/ElEventItem.vue +105 -105
  52. package/dist/v3/DevTool/components/ElEvent/index.vue +106 -106
  53. package/dist/v3/DevTool/components/Instance/components/InstanceTreeNode.vue +265 -265
  54. package/dist/v3/DevTool/components/Instance/flatten.ts +226 -226
  55. package/dist/v3/DevTool/components/Instance/index.vue +94 -94
  56. package/dist/v3/DevTool/components/Instance/registry.ts +49 -49
  57. package/dist/v3/DevTool/components/Instance/transformTree.ts +375 -375
  58. package/dist/v3/DevTool/components/Instance/transformTreeCtx.ts +268 -268
  59. package/dist/v3/DevTool/components/Instance/typing.d.ts +43 -43
  60. package/dist/v3/DevTool/components/InstanceDetail/index.vue +485 -485
  61. package/dist/v3/DevTool/components/JsonDetail/index.vue +70 -70
  62. package/dist/v3/DevTool/components/NFCList/NFCItem.vue +112 -112
  63. package/dist/v3/DevTool/components/NFCList/NFCTool.vue +454 -454
  64. package/dist/v3/DevTool/components/NFCList/const.ts +56 -56
  65. package/dist/v3/DevTool/components/NFCList/index.vue +94 -94
  66. package/dist/v3/DevTool/components/NetworkList/InterceptConfig.vue +624 -624
  67. package/dist/v3/DevTool/components/NetworkList/InterceptItem.vue +140 -140
  68. package/dist/v3/DevTool/components/NetworkList/NetworkDetail.vue +287 -287
  69. package/dist/v3/DevTool/components/NetworkList/NetworkIntercept.vue +88 -88
  70. package/dist/v3/DevTool/components/NetworkList/NetworkItem.vue +163 -163
  71. package/dist/v3/DevTool/components/NetworkList/NetworkSend.vue +589 -589
  72. package/dist/v3/DevTool/components/NetworkList/const.ts +4 -4
  73. package/dist/v3/DevTool/components/NetworkList/hooks/useNetworkForm.ts +86 -86
  74. package/dist/v3/DevTool/components/NetworkList/index.vue +160 -160
  75. package/dist/v3/DevTool/components/NetworkList/utils.ts +101 -101
  76. package/dist/v3/DevTool/components/Performance/index.vue +498 -498
  77. package/dist/v3/DevTool/components/Performance/modules/PerformanceMetrics.vue +153 -153
  78. package/dist/v3/DevTool/components/Performance/modules/usePerformanceChart.ts +460 -460
  79. package/dist/v3/DevTool/components/Performance/modules/usePerformanceData.ts +258 -258
  80. package/dist/v3/DevTool/components/PiniaList/index.vue +93 -93
  81. package/dist/v3/DevTool/components/RunJS/index.vue +148 -148
  82. package/dist/v3/DevTool/components/ScanCodeList/ScanCodeItem.vue +97 -97
  83. package/dist/v3/DevTool/components/ScanCodeList/index.vue +100 -100
  84. package/dist/v3/DevTool/components/SettingButton/index.vue +45 -45
  85. package/dist/v3/DevTool/components/SettingList/index.vue +218 -218
  86. package/dist/v3/DevTool/components/SettingList/modules/SettingBarrage.vue +304 -304
  87. package/dist/v3/DevTool/components/SettingList/modules/SettingDevTool.vue +212 -212
  88. package/dist/v3/DevTool/components/SettingList/modules/SettingInfo.vue +157 -157
  89. package/dist/v3/DevTool/components/SettingList/modules/SettingLanguage.vue +74 -74
  90. package/dist/v3/DevTool/components/SettingList/modules/SettingLog.vue +230 -230
  91. package/dist/v3/DevTool/components/SettingList/typing.d.ts +2 -2
  92. package/dist/v3/DevTool/components/SourceCode/Line.vue +127 -127
  93. package/dist/v3/DevTool/components/SourceCode/parseCode.ts +609 -609
  94. package/dist/v3/DevTool/components/StorageList/index.vue +174 -174
  95. package/dist/v3/DevTool/components/TransferList/TransferDetail.vue +268 -268
  96. package/dist/v3/DevTool/components/VuexList/index.vue +84 -84
  97. package/dist/v3/DevTool/index.vue +1 -0
  98. package/dist/v3/components/AppTransition/index.vue +176 -176
  99. package/dist/v3/components/AutoSizer/index.vue +192 -192
  100. package/dist/v3/components/AutoSizer/index1.vue +184 -184
  101. package/dist/v3/components/AutoSizer/utils.ts +49 -49
  102. package/dist/v3/components/Barrage/BarrageItem.vue +137 -137
  103. package/dist/v3/components/Barrage/index.vue +202 -202
  104. package/dist/v3/components/CircularButton/index.vue +84 -84
  105. package/dist/v3/components/CustomSwiper/CustomSwiperItem.vue +49 -49
  106. package/dist/v3/components/CustomSwiper/index.vue +104 -104
  107. package/dist/v3/components/DevErrorBoundary/index.vue +380 -0
  108. package/dist/v3/components/Empty/index.vue +29 -29
  109. package/dist/v3/components/FilterSelect/index.vue +179 -179
  110. package/dist/v3/components/JsonPretty/components/Brackets/index.vue +27 -27
  111. package/dist/v3/components/JsonPretty/components/Carets/index.vue +59 -59
  112. package/dist/v3/components/JsonPretty/components/CheckController/index.vue +136 -136
  113. package/dist/v3/components/JsonPretty/components/TreeNode/index.vue +387 -387
  114. package/dist/v3/components/JsonPretty/hooks/useClipboard.ts +21 -21
  115. package/dist/v3/components/JsonPretty/hooks/useError.ts +21 -21
  116. package/dist/v3/components/JsonPretty/type.ts +127 -127
  117. package/dist/v3/components/JsonPretty/utils/index.ts +169 -169
  118. package/dist/v3/components/MovableContainer/index.vue +8 -4
  119. package/dist/v3/components/Pick/index.vue +322 -322
  120. package/dist/v3/components/Tag/index.vue +113 -113
  121. package/dist/v3/components/VirtualList/AutoSize.vue +40 -40
  122. package/dist/v3/components/VirtualList/index.vue +416 -416
  123. package/dist/v3/hooks/useBluetooth/index.ts +561 -561
  124. package/dist/v3/hooks/useContainerStyle.ts +153 -153
  125. package/dist/v3/hooks/useNFC/index.ts +107 -107
  126. package/dist/v3/hooks/useNFC/typing.d.ts +396 -396
  127. package/dist/v3/hooks/useNFC/useNFCAndroid.ts +966 -966
  128. package/dist/v3/hooks/useNFC/useNFCMpWeiXin.ts +812 -812
  129. package/dist/v3/hooks/useNFC/utils.ts +754 -754
  130. package/dist/v3/hooks/useRequest/index.ts +586 -586
  131. package/dist/v3/hooks/useRequest/utils.ts +267 -267
  132. package/dist/v3/hooks/useScanCode/index.ts +206 -206
  133. package/dist/v3/hooks/useWebsocket/index.ts +253 -253
  134. package/dist/v3/styles/theme.ts +12 -12
  135. package/package.json +9 -1
@@ -1,375 +1,375 @@
1
- import { isArray, isObject, isString } from '../../../../utils';
2
- import {
3
- type ComponentInternalInstance,
4
- type VNode,
5
- isRef,
6
- isReactive,
7
- } from 'vue';
8
- import { getRecordedChildren } from './registry';
9
-
10
- // 类型守卫函数,检查是否为VNode类型
11
- function isVNode(child: any): child is VNode {
12
- return child != null && typeof child === 'object' && 'type' in child;
13
- }
14
-
15
- // 类型守卫函数,检查是否为包含component属性的对象
16
- function hasComponentProperty(
17
- child: any,
18
- ): child is VNode & { component?: ComponentInternalInstance } {
19
- return isVNode(child) && 'component' in child;
20
- }
21
-
22
- // 类型守卫函数,检查是否为Symbol类型且具有description属性
23
- function isSymbolWithDescription(
24
- value: any,
25
- ): value is symbol & { description?: string } {
26
- return typeof value === 'symbol';
27
- }
28
-
29
- interface SimpleTreeNode {
30
- type: string;
31
- uid: string | number;
32
- children?: SimpleTreeNode[];
33
- content?: string;
34
- hasChildren?: boolean;
35
- // 分类存储属性
36
- props?: Record<string, any>;
37
- styles?: Record<string, any>;
38
- other?: Record<string, any>;
39
- provides?: Record<string, any>;
40
- data?: Record<string, any>;
41
- functions?: Record<string, any>;
42
- emits?: Record<string, any>;
43
- }
44
-
45
- /**
46
- * 分类提取属性
47
- */
48
- function extractCategorizedData(vnode: any): {
49
- props: Record<string, any>;
50
- styles: Record<string, any>;
51
- other: Record<string, any>;
52
- provides: Record<string, any>;
53
- data: Record<string, any>;
54
- functions: Record<string, any>;
55
- emits: Record<string, any>;
56
- } {
57
- const styles: any = {};
58
- const props: any = {};
59
- const other: any = {};
60
- const provides: any = {};
61
- const data: any = {};
62
- const emits: any = {};
63
-
64
- const vnodeProps = vnode.props || {};
65
-
66
- // 1. 提取样式相关 (id, style)
67
- const styleKeys = ['id', 'style'];
68
-
69
- // 处理样式属性
70
- styleKeys.forEach((key) => {
71
- let val = vnodeProps[key];
72
- if (val !== undefined && val !== null && val !== '') {
73
- if (key === 'style' && isObject(val)) {
74
- val = Object.entries(val)
75
- .filter(([_, v]) => v !== undefined && v !== null && v !== '')
76
- .map(([k, v]) => `${k}: ${v}`)
77
- .join('; ');
78
- }
79
- if (val !== '') {
80
- styles[key] = val;
81
- }
82
- }
83
- });
84
-
85
- // 处理 Props 属性 (包括 class, data- 以及其他)
86
- Object.keys(vnodeProps).forEach((key) => {
87
- // 排除已处理的样式属性
88
- if (!styleKeys.includes(key)) {
89
- // 统一处理 className -> class
90
- const finalKey = key === 'className' ? 'class' : key;
91
- props[finalKey] = vnodeProps[key];
92
- }
93
- });
94
-
95
- // 3. 提取其他元数据
96
- other.uid = vnode.uid;
97
- if (vnode.type) {
98
- other.type = isString(vnode.type)
99
- ? vnode.type
100
- : isObject(vnode.type)
101
- ? (vnode.type as any).name || (vnode.type as any).__name || 'Component'
102
- : String(vnode.type);
103
-
104
- if (isObject(vnode.type) && (vnode.type as any).__file) {
105
- other.file = (vnode.type as any).__file;
106
- }
107
- }
108
-
109
- // 如果有 slots
110
- if (vnode.children && !isArray(vnode.children) && isObject(vnode.children)) {
111
- other.slots = Object.keys(vnode.children);
112
- }
113
-
114
- // 4. 提取 provides
115
- const currentProvides = vnode.provides;
116
-
117
- if (currentProvides) {
118
- Reflect.ownKeys(currentProvides).forEach((key) => {
119
- // 排除 Vue 内部属性
120
- if (typeof key === 'string' && key.startsWith('__v')) return;
121
-
122
- const value = currentProvides[key];
123
- const keyStr = typeof key === 'symbol' ? String(key) : key;
124
- provides[keyStr] = value;
125
- });
126
- }
127
-
128
- // 5. 提取 data (仅当 vnode.component 存在时)
129
- const functions: any = {};
130
- if (vnode.setupState || vnode.data) {
131
- // 优先取 setupState (Script Setup / Composition API), 其次取 data (Options API)
132
- // 注意:setupState 包含了响应式引用的解包值
133
- const allData: any = {};
134
-
135
- // 处理 setupState
136
- if (isObject(vnode.setupState)) {
137
- Object.assign(allData, vnode.setupState);
138
- }
139
-
140
- // 处理 Options API data
141
- if (isObject(vnode.data)) {
142
- Object.assign(allData, vnode.data);
143
- }
144
-
145
- if (isObject(allData)) {
146
- Reflect.ownKeys(allData).forEach((key) => {
147
- // 排除 Vue 内部属性
148
- if (
149
- typeof key === 'string' &&
150
- (key.startsWith('__v') || key.startsWith('$') || key.startsWith('_'))
151
- )
152
- return;
153
-
154
- const value = (allData as any)[key];
155
- let keyStr = typeof key === 'symbol' ? String(key) : key;
156
-
157
- // 标记 Ref 和 Reactive
158
- // 优先检查 devtoolsRawSetupState 获取原始 Ref
159
- const rawSetupState = (vnode as any).devtoolsRawSetupState || {};
160
- const rawValue = rawSetupState[key];
161
-
162
- if (rawValue && isRef(rawValue)) {
163
- keyStr += ' (Ref)';
164
- } else if (isReactive(value)) {
165
- keyStr += ' (Reactive)';
166
- }
167
-
168
- if (typeof value === 'function') {
169
- functions[keyStr] = value;
170
- } else {
171
- // 尝试区分类型(Ref, Reactive, Computed 等在 setupState 中通常已被解包,难以直接判断原始类型,
172
- // 除非使用 devtools 专用钩子。这里暂只做基础值存储)
173
- data[keyStr] = value;
174
- }
175
- });
176
- }
177
- }
178
-
179
- // 6. 提取 emits
180
- // 优先从组件实例获取归一化的 emitsOptions, 其次从类型定义获取
181
- const emitsOptions = vnode?.emitsOptions || vnode.type?.emits;
182
- if (emitsOptions) {
183
- if (isArray(emitsOptions)) {
184
- emitsOptions.forEach((emit: string) => {
185
- emits[emit] = 'function(...args) {...}'; // 简单标记存在
186
- });
187
- } else if (isObject(emitsOptions)) {
188
- Reflect.ownKeys(emitsOptions).forEach((key) => {
189
- if (typeof key === 'string') {
190
- emits[key] = 'function(...args) {...}';
191
- }
192
- });
193
- }
194
- }
195
-
196
- return { props, styles, other, provides, data, functions, emits };
197
- }
198
-
199
- let uidCount = 0;
200
- // 导出重置函数,供外部调用
201
- export function resetUidCount() {
202
- uidCount = 0;
203
- }
204
-
205
- function getNextUid() {
206
- return `manual-${++uidCount}`;
207
- }
208
-
209
- function extractNameFromFile(file: string): string | undefined {
210
- if (!file) return undefined;
211
- const match = file.match(/([^/|\\]+)\.[^.]+$/);
212
- return match ? match[1] : undefined;
213
- }
214
-
215
- function getComponentName(type: any): string {
216
- if (!type) return 'Anonymous';
217
- if (typeof type === 'string') return type;
218
- if (typeof type === 'object') {
219
- return (
220
- type.name ||
221
- type.__name ||
222
- (type.__file ? extractNameFromFile(type.__file) : undefined) ||
223
- 'Anonymous'
224
- );
225
- }
226
- if (typeof type === 'function') {
227
- return type.name || type.__name || 'Anonymous';
228
- }
229
- return 'Anonymous';
230
- }
231
-
232
- // ... existing code ...
233
-
234
- // ... types ...
235
-
236
- export function simpleTree(vNode?: any, depth = 0): SimpleTreeNode | null {
237
- // ... check valid ...
238
- if (vNode === null || typeof vNode !== 'object' || depth > 40) {
239
- return null;
240
- }
241
- // ... rest of simpleTree func ...
242
-
243
- // 确保只使用必需的属性
244
- const simplifiedInstance: InstanceTree.SimplifiedComponentInternalInstance = {
245
- type: vNode.type,
246
- subTree: vNode.subTree,
247
- uid: vNode.uid,
248
- ...(vNode.component && { component: vNode.component }),
249
- ...(vNode.children && { children: vNode.children }),
250
- };
251
-
252
- const children: SimpleTreeNode[] = [];
253
-
254
- // 获取子节点
255
- if (simplifiedInstance.subTree) {
256
- if ('data-dev-tool' in (simplifiedInstance.subTree.props ?? {})) {
257
- return null;
258
- } else {
259
- children.push(
260
- ...getSimpleChildrenFromVNode(simplifiedInstance.subTree, depth),
261
- );
262
- }
263
- }
264
-
265
- // 插槽 fallback
266
- if (children.length === 0 && simplifiedInstance.children) {
267
- if (isArray(simplifiedInstance.children)) {
268
- const slotChildren = getSimpleChildrenFromVNode(
269
- {
270
- children: simplifiedInstance.children,
271
- },
272
- depth,
273
- );
274
- children.push(...slotChildren);
275
- }
276
- }
277
-
278
- // GLOBAL fallback
279
- if (children.length === 0 && vNode.uid !== undefined) {
280
- const recordedChildren = getRecordedChildren(vNode.uid);
281
- recordedChildren.forEach((childInst) => {
282
- const childTree = simpleTree(childInst, depth + 1);
283
- if (childTree) {
284
- children.push(childTree);
285
- }
286
- });
287
- }
288
-
289
- const hasChildrenFlag = children.length > 0;
290
- const uid = vNode.uid !== undefined ? vNode.uid : getNextUid();
291
- const typeName = getComponentName(simplifiedInstance.type);
292
-
293
- const nodeInfo: SimpleTreeNode = {
294
- type: typeName,
295
- uid,
296
- children,
297
- hasChildren: hasChildrenFlag,
298
- ...extractCategorizedData(vNode),
299
- };
300
-
301
- return nodeInfo;
302
- }
303
-
304
- /**
305
- * 从 VNode 获取简化的子节点 (仅保留组件节点)
306
- */
307
- function getSimpleChildrenFromVNode(
308
- vnode: any,
309
- depth: number = 0,
310
- ): SimpleTreeNode[] {
311
- const children: SimpleTreeNode[] = [];
312
- if (vnode.children) {
313
- // 统一转为数组处理,兼容单节点和多节点
314
- const rawChildren = isArray(vnode.children)
315
- ? vnode.children
316
- : isObject(vnode.children)
317
- ? [vnode.children]
318
- : isString(vnode.children)
319
- ? [vnode.children]
320
- : [];
321
-
322
- for (const child of rawChildren) {
323
- if (
324
- child &&
325
- typeof child === 'object' &&
326
- 'props' in child &&
327
- child.props &&
328
- 'data-dev-tool' in child.props
329
- ) {
330
- continue;
331
- }
332
-
333
- // 1. 如果是组件实例,直接处理
334
- if (hasComponentProperty(child) && child?.component) {
335
- const childTree = simpleTree(child.component, depth + 1);
336
- if (childTree) {
337
- children.push(childTree);
338
- }
339
- }
340
- // 2. 如果是函数式组件
341
- else if (isVNode(child) && typeof child.type === 'function') {
342
- const uid =
343
- (child as any).uid !== undefined ? (child as any).uid : getNextUid();
344
- const typeName = getComponentName(child.type);
345
- children.push({
346
- type: typeName,
347
- uid,
348
- children: [],
349
- hasChildren: false,
350
- ...extractCategorizedData(child),
351
- });
352
- }
353
- // 3. 如果是 VNode 且是 Fragmentation/Block (带 children 的非组件节点),需要深入查找内部的组件
354
- else if (isVNode(child)) {
355
- // 如果是 Fragment,递归查找子节点
356
- if (child.type && isSymbolWithDescription(child.type)) {
357
- const desc = child.type.description;
358
- const isFragment = desc === 'v-fgt' || desc === 'Fragment';
359
- if (isFragment) {
360
- const fragmentChildren = getSimpleChildrenFromVNode(child, depth);
361
- children.push(...fragmentChildren);
362
- }
363
- }
364
- // 如果是普通 HTML 元素或其他容器,也需要递归查找其 children 中的组件
365
- // 例如 <view><MyComponent /></view>,我们需要找到 MyComponent
366
- else if (isString(child.type) || isObject(child.type)) {
367
- // 递归查找子节点中的组件,但不把当前节点加入树中(因为我们只关心组件)
368
- const subChildren = getSimpleChildrenFromVNode(child, depth);
369
- children.push(...subChildren);
370
- }
371
- }
372
- }
373
- }
374
- return children;
375
- }
1
+ import { isArray, isObject, isString } from '../../../../utils';
2
+ import {
3
+ type ComponentInternalInstance,
4
+ type VNode,
5
+ isRef,
6
+ isReactive,
7
+ } from 'vue';
8
+ import { getRecordedChildren } from './registry';
9
+
10
+ // 类型守卫函数,检查是否为VNode类型
11
+ function isVNode(child: any): child is VNode {
12
+ return child != null && typeof child === 'object' && 'type' in child;
13
+ }
14
+
15
+ // 类型守卫函数,检查是否为包含component属性的对象
16
+ function hasComponentProperty(
17
+ child: any,
18
+ ): child is VNode & { component?: ComponentInternalInstance } {
19
+ return isVNode(child) && 'component' in child;
20
+ }
21
+
22
+ // 类型守卫函数,检查是否为Symbol类型且具有description属性
23
+ function isSymbolWithDescription(
24
+ value: any,
25
+ ): value is symbol & { description?: string } {
26
+ return typeof value === 'symbol';
27
+ }
28
+
29
+ interface SimpleTreeNode {
30
+ type: string;
31
+ uid: string | number;
32
+ children?: SimpleTreeNode[];
33
+ content?: string;
34
+ hasChildren?: boolean;
35
+ // 分类存储属性
36
+ props?: Record<string, any>;
37
+ styles?: Record<string, any>;
38
+ other?: Record<string, any>;
39
+ provides?: Record<string, any>;
40
+ data?: Record<string, any>;
41
+ functions?: Record<string, any>;
42
+ emits?: Record<string, any>;
43
+ }
44
+
45
+ /**
46
+ * 分类提取属性
47
+ */
48
+ function extractCategorizedData(vnode: any): {
49
+ props: Record<string, any>;
50
+ styles: Record<string, any>;
51
+ other: Record<string, any>;
52
+ provides: Record<string, any>;
53
+ data: Record<string, any>;
54
+ functions: Record<string, any>;
55
+ emits: Record<string, any>;
56
+ } {
57
+ const styles: any = {};
58
+ const props: any = {};
59
+ const other: any = {};
60
+ const provides: any = {};
61
+ const data: any = {};
62
+ const emits: any = {};
63
+
64
+ const vnodeProps = vnode.props || {};
65
+
66
+ // 1. 提取样式相关 (id, style)
67
+ const styleKeys = ['id', 'style'];
68
+
69
+ // 处理样式属性
70
+ styleKeys.forEach((key) => {
71
+ let val = vnodeProps[key];
72
+ if (val !== undefined && val !== null && val !== '') {
73
+ if (key === 'style' && isObject(val)) {
74
+ val = Object.entries(val)
75
+ .filter(([_, v]) => v !== undefined && v !== null && v !== '')
76
+ .map(([k, v]) => `${k}: ${v}`)
77
+ .join('; ');
78
+ }
79
+ if (val !== '') {
80
+ styles[key] = val;
81
+ }
82
+ }
83
+ });
84
+
85
+ // 处理 Props 属性 (包括 class, data- 以及其他)
86
+ Object.keys(vnodeProps).forEach((key) => {
87
+ // 排除已处理的样式属性
88
+ if (!styleKeys.includes(key)) {
89
+ // 统一处理 className -> class
90
+ const finalKey = key === 'className' ? 'class' : key;
91
+ props[finalKey] = vnodeProps[key];
92
+ }
93
+ });
94
+
95
+ // 3. 提取其他元数据
96
+ other.uid = vnode.uid;
97
+ if (vnode.type) {
98
+ other.type = isString(vnode.type)
99
+ ? vnode.type
100
+ : isObject(vnode.type)
101
+ ? (vnode.type as any).name || (vnode.type as any).__name || 'Component'
102
+ : String(vnode.type);
103
+
104
+ if (isObject(vnode.type) && (vnode.type as any).__file) {
105
+ other.file = (vnode.type as any).__file;
106
+ }
107
+ }
108
+
109
+ // 如果有 slots
110
+ if (vnode.children && !isArray(vnode.children) && isObject(vnode.children)) {
111
+ other.slots = Object.keys(vnode.children);
112
+ }
113
+
114
+ // 4. 提取 provides
115
+ const currentProvides = vnode.provides;
116
+
117
+ if (currentProvides) {
118
+ Reflect.ownKeys(currentProvides).forEach((key) => {
119
+ // 排除 Vue 内部属性
120
+ if (typeof key === 'string' && key.startsWith('__v')) return;
121
+
122
+ const value = currentProvides[key];
123
+ const keyStr = typeof key === 'symbol' ? String(key) : key;
124
+ provides[keyStr] = value;
125
+ });
126
+ }
127
+
128
+ // 5. 提取 data (仅当 vnode.component 存在时)
129
+ const functions: any = {};
130
+ if (vnode.setupState || vnode.data) {
131
+ // 优先取 setupState (Script Setup / Composition API), 其次取 data (Options API)
132
+ // 注意:setupState 包含了响应式引用的解包值
133
+ const allData: any = {};
134
+
135
+ // 处理 setupState
136
+ if (isObject(vnode.setupState)) {
137
+ Object.assign(allData, vnode.setupState);
138
+ }
139
+
140
+ // 处理 Options API data
141
+ if (isObject(vnode.data)) {
142
+ Object.assign(allData, vnode.data);
143
+ }
144
+
145
+ if (isObject(allData)) {
146
+ Reflect.ownKeys(allData).forEach((key) => {
147
+ // 排除 Vue 内部属性
148
+ if (
149
+ typeof key === 'string' &&
150
+ (key.startsWith('__v') || key.startsWith('$') || key.startsWith('_'))
151
+ )
152
+ return;
153
+
154
+ const value = (allData as any)[key];
155
+ let keyStr = typeof key === 'symbol' ? String(key) : key;
156
+
157
+ // 标记 Ref 和 Reactive
158
+ // 优先检查 devtoolsRawSetupState 获取原始 Ref
159
+ const rawSetupState = (vnode as any).devtoolsRawSetupState || {};
160
+ const rawValue = rawSetupState[key];
161
+
162
+ if (rawValue && isRef(rawValue)) {
163
+ keyStr += ' (Ref)';
164
+ } else if (isReactive(value)) {
165
+ keyStr += ' (Reactive)';
166
+ }
167
+
168
+ if (typeof value === 'function') {
169
+ functions[keyStr] = value;
170
+ } else {
171
+ // 尝试区分类型(Ref, Reactive, Computed 等在 setupState 中通常已被解包,难以直接判断原始类型,
172
+ // 除非使用 devtools 专用钩子。这里暂只做基础值存储)
173
+ data[keyStr] = value;
174
+ }
175
+ });
176
+ }
177
+ }
178
+
179
+ // 6. 提取 emits
180
+ // 优先从组件实例获取归一化的 emitsOptions, 其次从类型定义获取
181
+ const emitsOptions = vnode?.emitsOptions || vnode.type?.emits;
182
+ if (emitsOptions) {
183
+ if (isArray(emitsOptions)) {
184
+ emitsOptions.forEach((emit: string) => {
185
+ emits[emit] = 'function(...args) {...}'; // 简单标记存在
186
+ });
187
+ } else if (isObject(emitsOptions)) {
188
+ Reflect.ownKeys(emitsOptions).forEach((key) => {
189
+ if (typeof key === 'string') {
190
+ emits[key] = 'function(...args) {...}';
191
+ }
192
+ });
193
+ }
194
+ }
195
+
196
+ return { props, styles, other, provides, data, functions, emits };
197
+ }
198
+
199
+ let uidCount = 0;
200
+ // 导出重置函数,供外部调用
201
+ export function resetUidCount() {
202
+ uidCount = 0;
203
+ }
204
+
205
+ function getNextUid() {
206
+ return `manual-${++uidCount}`;
207
+ }
208
+
209
+ function extractNameFromFile(file: string): string | undefined {
210
+ if (!file) return undefined;
211
+ const match = file.match(/([^/|\\]+)\.[^.]+$/);
212
+ return match ? match[1] : undefined;
213
+ }
214
+
215
+ function getComponentName(type: any): string {
216
+ if (!type) return 'Anonymous';
217
+ if (typeof type === 'string') return type;
218
+ if (typeof type === 'object') {
219
+ return (
220
+ type.name ||
221
+ type.__name ||
222
+ (type.__file ? extractNameFromFile(type.__file) : undefined) ||
223
+ 'Anonymous'
224
+ );
225
+ }
226
+ if (typeof type === 'function') {
227
+ return type.name || type.__name || 'Anonymous';
228
+ }
229
+ return 'Anonymous';
230
+ }
231
+
232
+ // ... existing code ...
233
+
234
+ // ... types ...
235
+
236
+ export function simpleTree(vNode?: any, depth = 0): SimpleTreeNode | null {
237
+ // ... check valid ...
238
+ if (vNode === null || typeof vNode !== 'object' || depth > 40) {
239
+ return null;
240
+ }
241
+ // ... rest of simpleTree func ...
242
+
243
+ // 确保只使用必需的属性
244
+ const simplifiedInstance: InstanceTree.SimplifiedComponentInternalInstance = {
245
+ type: vNode.type,
246
+ subTree: vNode.subTree,
247
+ uid: vNode.uid,
248
+ ...(vNode.component && { component: vNode.component }),
249
+ ...(vNode.children && { children: vNode.children }),
250
+ };
251
+
252
+ const children: SimpleTreeNode[] = [];
253
+
254
+ // 获取子节点
255
+ if (simplifiedInstance.subTree) {
256
+ if ('data-dev-tool' in (simplifiedInstance.subTree.props ?? {})) {
257
+ return null;
258
+ } else {
259
+ children.push(
260
+ ...getSimpleChildrenFromVNode(simplifiedInstance.subTree, depth),
261
+ );
262
+ }
263
+ }
264
+
265
+ // 插槽 fallback
266
+ if (children.length === 0 && simplifiedInstance.children) {
267
+ if (isArray(simplifiedInstance.children)) {
268
+ const slotChildren = getSimpleChildrenFromVNode(
269
+ {
270
+ children: simplifiedInstance.children,
271
+ },
272
+ depth,
273
+ );
274
+ children.push(...slotChildren);
275
+ }
276
+ }
277
+
278
+ // GLOBAL fallback
279
+ if (children.length === 0 && vNode.uid !== undefined) {
280
+ const recordedChildren = getRecordedChildren(vNode.uid);
281
+ recordedChildren.forEach((childInst) => {
282
+ const childTree = simpleTree(childInst, depth + 1);
283
+ if (childTree) {
284
+ children.push(childTree);
285
+ }
286
+ });
287
+ }
288
+
289
+ const hasChildrenFlag = children.length > 0;
290
+ const uid = vNode.uid !== undefined ? vNode.uid : getNextUid();
291
+ const typeName = getComponentName(simplifiedInstance.type);
292
+
293
+ const nodeInfo: SimpleTreeNode = {
294
+ type: typeName,
295
+ uid,
296
+ children,
297
+ hasChildren: hasChildrenFlag,
298
+ ...extractCategorizedData(vNode),
299
+ };
300
+
301
+ return nodeInfo;
302
+ }
303
+
304
+ /**
305
+ * 从 VNode 获取简化的子节点 (仅保留组件节点)
306
+ */
307
+ function getSimpleChildrenFromVNode(
308
+ vnode: any,
309
+ depth: number = 0,
310
+ ): SimpleTreeNode[] {
311
+ const children: SimpleTreeNode[] = [];
312
+ if (vnode.children) {
313
+ // 统一转为数组处理,兼容单节点和多节点
314
+ const rawChildren = isArray(vnode.children)
315
+ ? vnode.children
316
+ : isObject(vnode.children)
317
+ ? [vnode.children]
318
+ : isString(vnode.children)
319
+ ? [vnode.children]
320
+ : [];
321
+
322
+ for (const child of rawChildren) {
323
+ if (
324
+ child &&
325
+ typeof child === 'object' &&
326
+ 'props' in child &&
327
+ child.props &&
328
+ 'data-dev-tool' in child.props
329
+ ) {
330
+ continue;
331
+ }
332
+
333
+ // 1. 如果是组件实例,直接处理
334
+ if (hasComponentProperty(child) && child?.component) {
335
+ const childTree = simpleTree(child.component, depth + 1);
336
+ if (childTree) {
337
+ children.push(childTree);
338
+ }
339
+ }
340
+ // 2. 如果是函数式组件
341
+ else if (isVNode(child) && typeof child.type === 'function') {
342
+ const uid =
343
+ (child as any).uid !== undefined ? (child as any).uid : getNextUid();
344
+ const typeName = getComponentName(child.type);
345
+ children.push({
346
+ type: typeName,
347
+ uid,
348
+ children: [],
349
+ hasChildren: false,
350
+ ...extractCategorizedData(child),
351
+ });
352
+ }
353
+ // 3. 如果是 VNode 且是 Fragmentation/Block (带 children 的非组件节点),需要深入查找内部的组件
354
+ else if (isVNode(child)) {
355
+ // 如果是 Fragment,递归查找子节点
356
+ if (child.type && isSymbolWithDescription(child.type)) {
357
+ const desc = child.type.description;
358
+ const isFragment = desc === 'v-fgt' || desc === 'Fragment';
359
+ if (isFragment) {
360
+ const fragmentChildren = getSimpleChildrenFromVNode(child, depth);
361
+ children.push(...fragmentChildren);
362
+ }
363
+ }
364
+ // 如果是普通 HTML 元素或其他容器,也需要递归查找其 children 中的组件
365
+ // 例如 <view><MyComponent /></view>,我们需要找到 MyComponent
366
+ else if (isString(child.type) || isObject(child.type)) {
367
+ // 递归查找子节点中的组件,但不把当前节点加入树中(因为我们只关心组件)
368
+ const subChildren = getSimpleChildrenFromVNode(child, depth);
369
+ children.push(...subChildren);
370
+ }
371
+ }
372
+ }
373
+ }
374
+ return children;
375
+ }