[inula-dev-tools]<feat> 解析 VNode tree
This commit is contained in:
parent
80c4282898
commit
42e8bd1171
|
@ -28,8 +28,6 @@ import {
|
||||||
import { helper } from '../injector';
|
import { helper } from '../injector';
|
||||||
import { JSXElement, ContextType } from '../../../inula/src/renderer/Types';
|
import { JSXElement, ContextType } from '../../../inula/src/renderer/Types';
|
||||||
import { decycle } from 'json-decycle';
|
import { decycle } from 'json-decycle';
|
||||||
import {arrify} from "ts-loader/dist/utils";
|
|
||||||
import {add} from "../../../inula/src/renderer/taskExecutor/TaskQueue";
|
|
||||||
|
|
||||||
// 展示值为 string 的可编辑模型
|
// 展示值为 string 的可编辑模型
|
||||||
type EditableStringType = 'string' | 'number' | 'undefined' | 'null';
|
type EditableStringType = 'string' | 'number' | 'undefined' | 'null';
|
||||||
|
|
|
@ -0,0 +1,229 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
|
||||||
|
*
|
||||||
|
* openInula is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { VNode } from '../../../inula/src/renderer/vnode/VNode';
|
||||||
|
import {
|
||||||
|
ClassComponent,
|
||||||
|
ContextConsumer,
|
||||||
|
ContextProvider,
|
||||||
|
ForwardRef,
|
||||||
|
FunctionComponent,
|
||||||
|
MemoComponent,
|
||||||
|
SuspenseComponent
|
||||||
|
} from '../../../inula/src/renderer/vnode/VNodeTags';
|
||||||
|
|
||||||
|
export type NameObj = {
|
||||||
|
itemName: string;
|
||||||
|
badge: Array<string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 建立双向映射关系,当用户在修改属性值后,可以找到对应的 VNode
|
||||||
|
export let VNodeToIdMap: Map<VNode, number>;
|
||||||
|
export let IdToVNodeMap: Map<number, VNode>;
|
||||||
|
|
||||||
|
if (!VNodeToIdMap) {
|
||||||
|
VNodeToIdMap = new Map<VNode, number>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IdToVNodeMap) {
|
||||||
|
IdToVNodeMap = new Map<number, VNode>();
|
||||||
|
}
|
||||||
|
|
||||||
|
let uid = 0;
|
||||||
|
function generateUid(vNode: VNode) {
|
||||||
|
const id = VNodeToIdMap.get(vNode);
|
||||||
|
if (id !== undefined) {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
uid++;
|
||||||
|
return uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
const componentType = [
|
||||||
|
ClassComponent,
|
||||||
|
FunctionComponent,
|
||||||
|
ContextProvider,
|
||||||
|
ContextConsumer,
|
||||||
|
ForwardRef,
|
||||||
|
SuspenseComponent,
|
||||||
|
MemoComponent,
|
||||||
|
];
|
||||||
|
|
||||||
|
const badgeNameArr: Array<string> = [
|
||||||
|
'withRouter(',
|
||||||
|
'SideEffect(',
|
||||||
|
'Connect(',
|
||||||
|
'injectIntl(',
|
||||||
|
'Pure(',
|
||||||
|
];
|
||||||
|
|
||||||
|
export function isUserComponent(tag: string) {
|
||||||
|
return componentType.includes(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getParentUserComponent(node: VNode) {
|
||||||
|
let parent = node.parent;
|
||||||
|
while (parent) {
|
||||||
|
if (isUserComponent(parent.tag)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
parent = parent.parent;
|
||||||
|
}
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContextName(node: VNode, type: string) {
|
||||||
|
const contextType = type;
|
||||||
|
if (!node.type.displayName) {
|
||||||
|
if (node.type.value) {
|
||||||
|
if (typeof node.type.value === 'object') {
|
||||||
|
return `Context.${contextType}`;
|
||||||
|
} else {
|
||||||
|
return `${node.type.value}.${contextType}`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (node.type._context?.displayName) {
|
||||||
|
return `${node.type._context.displayName}.${contextType}`;
|
||||||
|
}
|
||||||
|
return `Context.${contextType}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return `${node.type.displayName}.${contextType}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getForwardRefName = (node: VNode): NameObj => {
|
||||||
|
const forwardRefName: NameObj = {
|
||||||
|
itemName: '',
|
||||||
|
badge: ['ForwardRef'],
|
||||||
|
};
|
||||||
|
if (!node.type.render?.name) {
|
||||||
|
if (node.type.render?.name !== '') {
|
||||||
|
forwardRefName.itemName = node.type?.displayName ? node.type?.displayName : 'Anonymous';
|
||||||
|
} else {
|
||||||
|
forwardRefName.itemName = 'Anonymous';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
forwardRefName.itemName = node.type.render?.name;
|
||||||
|
}
|
||||||
|
return forwardRefName;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 用于结构组件名,例如: Pure(Memo(xxx)) => xxx 并且把 Pure Memo 加到 NameObj.badge 里
|
||||||
|
const parseComponentName = (name: NameObj): NameObj => {
|
||||||
|
badgeNameArr.forEach(badgeName => {
|
||||||
|
if (name.itemName.startsWith(badgeName)) {
|
||||||
|
// 截断开头的高阶组件名,并把最后一个 ) 替换为 ''。例如: Pure(Memo(xxx)) => Memo(xxx)) => Memo(xxx)
|
||||||
|
name.itemName = name.itemName.substring(badgeName.length).replace(/(\))(?!.*\1)/, '');
|
||||||
|
name.badge.push(badgeName.substring(0, badgeName.length - 1));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return name;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 取字符串括号里的值
|
||||||
|
const getValuesInParentheses = (name: string) => {
|
||||||
|
let result = name;
|
||||||
|
const regex = /\((.+?)\)/g;
|
||||||
|
const results = name.match(regex);
|
||||||
|
if (results) {
|
||||||
|
const option = results[0];
|
||||||
|
if (option) {
|
||||||
|
result = option.substring(1, option.length - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
function isNullOrUndefined(prop) {
|
||||||
|
return !prop || typeof prop === 'undefined' || prop === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseTreeRoot(travelVNodeTree, treeRoot: VNode) {
|
||||||
|
const result: any[] = [];
|
||||||
|
travelVNodeTree(treeRoot, (node: VNode) => {
|
||||||
|
const tag = node.tag;
|
||||||
|
|
||||||
|
if (isUserComponent(tag)) {
|
||||||
|
// 添加 ID
|
||||||
|
const id = generateUid(node);
|
||||||
|
result.push(id);
|
||||||
|
let nameObj: NameObj = {
|
||||||
|
itemName: '',
|
||||||
|
badge: [],
|
||||||
|
};
|
||||||
|
// 拿到不同类型的展示名字
|
||||||
|
if (tag === ContextProvider) {
|
||||||
|
nameObj.itemName = getContextName(node, 'Provider');
|
||||||
|
result.push(nameObj);
|
||||||
|
} else if (tag === ContextConsumer) {
|
||||||
|
nameObj.itemName = getContextName(node, 'Consumer');
|
||||||
|
result.push(nameObj);
|
||||||
|
} else if (tag === ForwardRef) {
|
||||||
|
const name = getForwardRefName(node);
|
||||||
|
result.push(name);
|
||||||
|
} else if (tag === SuspenseComponent) {
|
||||||
|
nameObj.itemName = 'Suspense';
|
||||||
|
result.push(nameObj);
|
||||||
|
} else if (tag === MemoComponent) {
|
||||||
|
const name = node.type?.displayName || node.type?.name || node.type.render?.name;
|
||||||
|
nameObj.itemName = !isNullOrUndefined(name) ? name : 'Anonymous';
|
||||||
|
nameObj.badge.push('Memo');
|
||||||
|
nameObj = parseComponentName(nameObj);
|
||||||
|
result.push(nameObj);
|
||||||
|
} else {
|
||||||
|
const name = node.type.displayName || node.type?.name;
|
||||||
|
nameObj.itemName = !isNullOrUndefined(name) ? name : 'Anonymous';
|
||||||
|
nameObj = parseComponentName(nameObj);
|
||||||
|
result.push(nameObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加父节点 ID
|
||||||
|
const parent = getParentUserComponent(node);
|
||||||
|
if (parent) {
|
||||||
|
const parentId = VNodeToIdMap.get(parent);
|
||||||
|
result.push(parentId);
|
||||||
|
} else {
|
||||||
|
result.push('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加节点 key 值
|
||||||
|
const key = node.key;
|
||||||
|
if (key !== null) {
|
||||||
|
result.push(key);
|
||||||
|
} else {
|
||||||
|
result.push('');
|
||||||
|
}
|
||||||
|
|
||||||
|
VNodeToIdMap.set(node, id);
|
||||||
|
IdToVNodeMap.set(id, node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function queryVNode(id: number): VNode | undefined {
|
||||||
|
return IdToVNodeMap.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearVNode(vNode: VNode) {
|
||||||
|
if (VNodeToIdMap.has(vNode)) {
|
||||||
|
const id = VNodeToIdMap.get(vNode);
|
||||||
|
VNodeToIdMap.delete(vNode);
|
||||||
|
IdToVNodeMap.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default parseTreeRoot;
|
Loading…
Reference in New Issue