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.
- package/README.md +8 -0
- package/dist/const.cjs +1 -1
- package/dist/const.d.ts +1 -0
- package/dist/const.d.ts.map +1 -1
- package/dist/const.js +1 -1
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +2 -2
- package/dist/i18n/locales/en.cjs +1 -1
- package/dist/i18n/locales/en.d.ts +5 -0
- package/dist/i18n/locales/en.d.ts.map +1 -1
- package/dist/i18n/locales/en.js +1 -1
- package/dist/i18n/locales/zh-Hans.cjs +1 -1
- package/dist/i18n/locales/zh-Hans.d.ts +5 -0
- package/dist/i18n/locales/zh-Hans.d.ts.map +1 -1
- package/dist/i18n/locales/zh-Hans.js +1 -1
- package/dist/modules/devIntercept/index.cjs +9 -9
- package/dist/modules/devIntercept/index.d.ts +1 -1
- package/dist/modules/devIntercept/index.d.ts.map +1 -1
- package/dist/modules/devIntercept/index.js +9 -9
- package/dist/modules/devStore/index.cjs +1 -1
- package/dist/modules/devStore/index.d.ts.map +1 -1
- package/dist/modules/devStore/index.js +1 -1
- package/dist/plugins/uniDevTool/transform/transformMain.cjs +3 -3
- package/dist/plugins/uniDevTool/transform/transformMain.d.ts +2 -1
- package/dist/plugins/uniDevTool/transform/transformMain.d.ts.map +1 -1
- package/dist/plugins/uniDevTool/transform/transformMain.js +3 -3
- package/dist/plugins/uniDevTool/transform/transformVue.cjs +31 -25
- package/dist/plugins/uniDevTool/transform/transformVue.d.ts +2 -1
- package/dist/plugins/uniDevTool/transform/transformVue.d.ts.map +1 -1
- package/dist/plugins/uniDevTool/transform/transformVue.js +30 -24
- package/dist/plugins/uniDevTool/uniDevTool.cjs +3 -3
- package/dist/plugins/uniDevTool/uniDevTool.d.ts +3 -1
- package/dist/plugins/uniDevTool/uniDevTool.d.ts.map +1 -1
- package/dist/plugins/uniDevTool/uniDevTool.js +3 -3
- package/dist/type.d.ts +3 -0
- package/dist/type.d.ts.map +1 -1
- package/dist/v3/DevTool/components/BluetoothList/BluetoothItem.vue +199 -199
- package/dist/v3/DevTool/components/BluetoothList/BluetoothTool.vue +730 -730
- package/dist/v3/DevTool/components/BluetoothList/index.vue +167 -167
- package/dist/v3/DevTool/components/CaptureScreen/index.vue +109 -109
- package/dist/v3/DevTool/components/ConsoleList/ConsoleItem.vue +230 -225
- package/dist/v3/DevTool/components/ConsoleList/RunJSInput.vue +247 -247
- package/dist/v3/DevTool/components/ConsoleList/index.vue +171 -171
- package/dist/v3/DevTool/components/ConsoleList/staticTips.ts +1145 -1145
- package/dist/v3/DevTool/components/DevToolTitle/index.vue +24 -24
- package/dist/v3/DevTool/components/DevToolWindow/DevToolOverlay.vue +197 -197
- package/dist/v3/DevTool/components/DevToolWindow/hooks/dataUtils.ts +48 -48
- package/dist/v3/DevTool/components/DevToolWindow/hooks/useDevToolData.ts +387 -387
- package/dist/v3/DevTool/components/DevToolWindow/hooks/useDevToolHandlers.ts +629 -629
- package/dist/v3/DevTool/components/DevToolWindow/hooks/useDevToolOverlay.ts +201 -197
- package/dist/v3/DevTool/components/ElEvent/ElEventItem.vue +105 -105
- package/dist/v3/DevTool/components/ElEvent/index.vue +106 -106
- package/dist/v3/DevTool/components/Instance/components/InstanceTreeNode.vue +265 -265
- package/dist/v3/DevTool/components/Instance/flatten.ts +226 -226
- package/dist/v3/DevTool/components/Instance/index.vue +94 -94
- package/dist/v3/DevTool/components/Instance/registry.ts +49 -49
- package/dist/v3/DevTool/components/Instance/transformTree.ts +375 -375
- package/dist/v3/DevTool/components/Instance/transformTreeCtx.ts +268 -268
- package/dist/v3/DevTool/components/Instance/typing.d.ts +43 -43
- package/dist/v3/DevTool/components/InstanceDetail/index.vue +485 -485
- package/dist/v3/DevTool/components/JsonDetail/index.vue +70 -70
- package/dist/v3/DevTool/components/NFCList/NFCItem.vue +112 -112
- package/dist/v3/DevTool/components/NFCList/NFCTool.vue +454 -454
- package/dist/v3/DevTool/components/NFCList/const.ts +56 -56
- package/dist/v3/DevTool/components/NFCList/index.vue +94 -94
- package/dist/v3/DevTool/components/NetworkList/InterceptConfig.vue +624 -624
- package/dist/v3/DevTool/components/NetworkList/InterceptItem.vue +140 -140
- package/dist/v3/DevTool/components/NetworkList/NetworkDetail.vue +287 -287
- package/dist/v3/DevTool/components/NetworkList/NetworkIntercept.vue +88 -88
- package/dist/v3/DevTool/components/NetworkList/NetworkItem.vue +163 -163
- package/dist/v3/DevTool/components/NetworkList/NetworkSend.vue +589 -589
- package/dist/v3/DevTool/components/NetworkList/const.ts +4 -4
- package/dist/v3/DevTool/components/NetworkList/hooks/useNetworkForm.ts +86 -86
- package/dist/v3/DevTool/components/NetworkList/index.vue +160 -160
- package/dist/v3/DevTool/components/NetworkList/utils.ts +101 -101
- package/dist/v3/DevTool/components/Performance/index.vue +498 -498
- package/dist/v3/DevTool/components/Performance/modules/PerformanceMetrics.vue +153 -153
- package/dist/v3/DevTool/components/Performance/modules/usePerformanceChart.ts +460 -460
- package/dist/v3/DevTool/components/Performance/modules/usePerformanceData.ts +258 -258
- package/dist/v3/DevTool/components/PiniaList/index.vue +93 -93
- package/dist/v3/DevTool/components/RunJS/index.vue +148 -148
- package/dist/v3/DevTool/components/ScanCodeList/ScanCodeItem.vue +97 -97
- package/dist/v3/DevTool/components/ScanCodeList/index.vue +100 -100
- package/dist/v3/DevTool/components/SettingButton/index.vue +45 -45
- package/dist/v3/DevTool/components/SettingList/index.vue +218 -218
- package/dist/v3/DevTool/components/SettingList/modules/SettingBarrage.vue +304 -304
- package/dist/v3/DevTool/components/SettingList/modules/SettingDevTool.vue +212 -212
- package/dist/v3/DevTool/components/SettingList/modules/SettingInfo.vue +157 -157
- package/dist/v3/DevTool/components/SettingList/modules/SettingLanguage.vue +74 -74
- package/dist/v3/DevTool/components/SettingList/modules/SettingLog.vue +230 -230
- package/dist/v3/DevTool/components/SettingList/typing.d.ts +2 -2
- package/dist/v3/DevTool/components/SourceCode/Line.vue +127 -127
- package/dist/v3/DevTool/components/SourceCode/parseCode.ts +609 -609
- package/dist/v3/DevTool/components/StorageList/index.vue +174 -174
- package/dist/v3/DevTool/components/TransferList/TransferDetail.vue +268 -268
- package/dist/v3/DevTool/components/VuexList/index.vue +84 -84
- package/dist/v3/DevTool/index.vue +1 -0
- package/dist/v3/components/AppTransition/index.vue +176 -176
- package/dist/v3/components/AutoSizer/index.vue +192 -192
- package/dist/v3/components/AutoSizer/index1.vue +184 -184
- package/dist/v3/components/AutoSizer/utils.ts +49 -49
- package/dist/v3/components/Barrage/BarrageItem.vue +137 -137
- package/dist/v3/components/Barrage/index.vue +202 -202
- package/dist/v3/components/CircularButton/index.vue +84 -84
- package/dist/v3/components/CustomSwiper/CustomSwiperItem.vue +49 -49
- package/dist/v3/components/CustomSwiper/index.vue +104 -104
- package/dist/v3/components/DevErrorBoundary/index.vue +380 -0
- package/dist/v3/components/Empty/index.vue +29 -29
- package/dist/v3/components/FilterSelect/index.vue +179 -179
- package/dist/v3/components/JsonPretty/components/Brackets/index.vue +27 -27
- package/dist/v3/components/JsonPretty/components/Carets/index.vue +59 -59
- package/dist/v3/components/JsonPretty/components/CheckController/index.vue +136 -136
- package/dist/v3/components/JsonPretty/components/TreeNode/index.vue +387 -387
- package/dist/v3/components/JsonPretty/hooks/useClipboard.ts +21 -21
- package/dist/v3/components/JsonPretty/hooks/useError.ts +21 -21
- package/dist/v3/components/JsonPretty/type.ts +127 -127
- package/dist/v3/components/JsonPretty/utils/index.ts +169 -169
- package/dist/v3/components/MovableContainer/index.vue +8 -4
- package/dist/v3/components/Pick/index.vue +322 -322
- package/dist/v3/components/Tag/index.vue +113 -113
- package/dist/v3/components/VirtualList/AutoSize.vue +40 -40
- package/dist/v3/components/VirtualList/index.vue +416 -416
- package/dist/v3/hooks/useBluetooth/index.ts +561 -561
- package/dist/v3/hooks/useContainerStyle.ts +153 -153
- package/dist/v3/hooks/useNFC/index.ts +107 -107
- package/dist/v3/hooks/useNFC/typing.d.ts +396 -396
- package/dist/v3/hooks/useNFC/useNFCAndroid.ts +966 -966
- package/dist/v3/hooks/useNFC/useNFCMpWeiXin.ts +812 -812
- package/dist/v3/hooks/useNFC/utils.ts +754 -754
- package/dist/v3/hooks/useRequest/index.ts +586 -586
- package/dist/v3/hooks/useRequest/utils.ts +267 -267
- package/dist/v3/hooks/useScanCode/index.ts +206 -206
- package/dist/v3/hooks/useWebsocket/index.ts +253 -253
- package/dist/v3/styles/theme.ts +12 -12
- 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
|
+
}
|