diff --git a/libs/horizon/src/external/devtools.ts b/libs/horizon/src/external/devtools.ts index 10e70e80..e1edf1ff 100644 --- a/libs/horizon/src/external/devtools.ts +++ b/libs/horizon/src/external/devtools.ts @@ -1,27 +1,60 @@ import { travelVNodeTree } from '../renderer/vnode/VNodeUtils'; -import { Hook, Reducer, Ref } from '../renderer/hooks/HookType'; +import { + Hook, + Reducer, + Ref, + Effect, + CallBack, + Memo +} from '../renderer/hooks/HookType'; import { VNode } from '../renderer/vnode/VNode'; import { launchUpdateFromVNode } from '../renderer/TreeBuilder'; import { DomComponent } from '../renderer/vnode/VNodeTags'; +import { getElementTag } from '../renderer/vnode/VNodeCreator'; +import { JSXElement } from '../renderer/Types'; +import { EffectConstant } from '../renderer/hooks/EffectConstant'; + +const isEffectHook = (state: any): state is Effect => !!state.effect; +const isRefHook = (state: any): state is Ref => Object.prototype.hasOwnProperty.call(state, 'current'); +const isCallbackHook = (state: any): state is CallBack => Object.prototype.hasOwnProperty.call(state, 'func'); +const isMemoHook = (state: any): state is Memo => Object.prototype.hasOwnProperty.call(state, 'result'); + +const HookName = { + StateHook: 'State', + EffectHook: 'Effect', + LayoutEffectHook: 'LayoutEffect', + MemoHook: 'Memo', + RefHook: 'Ref', + ReducerHook: 'Reducer', + CallbackHook: 'Callback' +}; export const helper = { - travelVNodeTree: (rootVNode, fun) => { - travelVNodeTree(rootVNode, fun, null, rootVNode, null); + travelVNodeTree: (rootVNode, fun, childFilter: ((node: VNode) => boolean) | null = null) => { + travelVNodeTree(rootVNode, fun, childFilter, rootVNode, null); }, // 获取 hook 名,hIndex值和存储的值 - // 目前只处理 useState和useRef - getHookInfo:(hook: Hook) => { + getHookInfo: (hook: Hook) => { const { hIndex, state } = hook; if ((state as Reducer).trigger) { if ((state as Reducer).isUseState) { - return {name: 'state', hIndex, value: (state as Reducer).stateValue}; + return { name: HookName.StateHook, hIndex, value: (state as Reducer).stateValue }; + } else if ((state as Reducer).reducer) { + return { name: HookName.ReducerHook, hIndex, value: (state as Reducer).stateValue }; } - } else if ((state as Ref).current) { - return {name: 'ref', hIndex, value: (state as Ref).current}; + } else if (isRefHook(state)) { + return { name: HookName.RefHook, hIndex, value: (state as Ref).current }; + } else if (isEffectHook(state)) { + const name = state.effectConstant == EffectConstant.LayoutEffect ? HookName.LayoutEffectHook : HookName.EffectHook; + return { name, hIndex, value: (state as Effect).effect }; + } else if (isCallbackHook(state)) { + return { name: HookName.CallbackHook, hIndex, value: (state as CallBack).func }; + } else if (isMemoHook(state)) { + return { name: HookName.MemoHook, hIndex, value: (state as Memo).result }; } return null; }, - updateProps: (vNode: VNode, props: any) =>{ + updateProps: (vNode: VNode, props: any) => { vNode.devProps = props; launchUpdateFromVNode(vNode); }, @@ -44,7 +77,7 @@ export const helper = { }, getComponentInfo: (vNode: VNode) => { const { props, state, hooks } = vNode; - const info:any = {}; + const info: any = {}; if (props && Object.keys(props).length !== 0) { info['Props'] = props; } @@ -53,7 +86,7 @@ export const helper = { } if (hooks && hooks.length !== 0) { const logHookInfo: any[] = []; - hooks.forEach((hook) =>{ + hooks.forEach((hook) => { const state = hook.state as Reducer; if (state.trigger && state.isUseState) { logHookInfo.push(state.stateValue); @@ -72,6 +105,9 @@ export const helper = { }, null, vNode, null); return info; }, + getElementTag: (element: JSXElement) => { + return getElementTag(element); + } }; export function injectUpdater() { diff --git a/libs/horizon/src/renderer/hooks/HookType.ts b/libs/horizon/src/renderer/hooks/HookType.ts index e965fdf1..dce9e1c8 100644 --- a/libs/horizon/src/renderer/hooks/HookType.ts +++ b/libs/horizon/src/renderer/hooks/HookType.ts @@ -1,5 +1,5 @@ import {EffectConstant} from './EffectConstant'; - +type ValueOf = T[keyof T]; export interface Hook { state: Reducer | Effect | Memo | CallBack | Ref; hIndex: number; @@ -25,7 +25,7 @@ export type Effect = { effect: () => (() => void) | void; removeEffect: (() => void) | void; dependencies: Array | null; - effectConstant: typeof EffectConstant; + effectConstant: ValueOf; }; export type Memo = { diff --git a/libs/horizon/src/renderer/vnode/VNodeCreator.ts b/libs/horizon/src/renderer/vnode/VNodeCreator.ts index 8b7ad630..c5b4c306 100644 --- a/libs/horizon/src/renderer/vnode/VNodeCreator.ts +++ b/libs/horizon/src/renderer/vnode/VNodeCreator.ts @@ -132,6 +132,31 @@ export function createUndeterminedVNode(type, key, props) { return vNode; } +export function getElementTag(element: JSXElement): string { + const type = element.type; + + if (type === TYPE_STRICT_MODE || type === TYPE_FRAGMENT || type === TYPE_PROFILER) { + return Fragment; + } else { + let vNodeTag = FunctionComponent; + const componentType = typeof type; + + if (componentType === 'function') { + if (isClassComponent(type)) { + vNodeTag = ClassComponent; + } + } else if (componentType === 'string') { + vNodeTag = DomComponent; + } else if (type === TYPE_SUSPENSE) { + vNodeTag = SuspenseComponent; + } else if (componentType === 'object' && type !== null && typeMap[type.vtype]) { + vNodeTag = typeMap[type.vtype]; + } + + return vNodeTag; + } +} + export function createTreeRootVNode(container) { const vNode = newVirtualNode(TreeRoot, null, null, container); vNode.path = '0';