import getComponentName from '../element/componentName'; import { readRoute } from '../element/findRoute'; import type { ReactElementFiber } from '../element/types'; import { moduleNameMap } from '../global'; const ignoreSet = new Set([ 'Context.Provider', 'Context.Consumer', 'RCTView', 'RCTText', 'TouchableText', ]); const ignorePrefix = 'RCT'; const forwardSet = new Set(['ForwardRef']); const nameMap = Object.assign({ 'ForwardRef(Pressable)': 'Pressable', }); interface NodeInfo { name: string; index: number; } function handleRootNode(nodeInfo: NodeInfo, rootTag: number) { const moduleName = moduleNameMap.get(rootTag); if (moduleName) { nodeInfo.name = moduleName; } } function handleRootScreen(nodeInfo: NodeInfo, route: any) { const screenName = route.name; if (screenName) { nodeInfo.name = screenName; } } function buildNodePath( node: ReactElementFiber, nodePath: NodeInfo[] ): NodeInfo[] { const nodeInfo = { name: getComponentName(node.elementType), index: node.index, }; nodePath.push(nodeInfo); const rootTag = node.memoizedProps?.rootTag; if (rootTag) { handleRootNode(nodeInfo, rootTag); return nodePath; } const route = readRoute(node); if (route) { handleRootScreen(nodeInfo, route); return nodePath; } if (!node.return) { return nodePath; } return buildNodePath(node.return, nodePath); } function toXPath(nodePath: NodeInfo[]): string[] { const xpath: string[] = []; let prevIndex = -1; for (const nodeInfo of nodePath) { if (ignoreSet.has(nodeInfo.name)) { continue; } if (nodeInfo.name && nodeInfo.name.startsWith(ignorePrefix)) { continue; } if (prevIndex >= 0) { nodeInfo.index = prevIndex; } if (nodeInfo.name && !forwardSet.has(nodeInfo.name)) { if (nodeInfo === nodePath[0]) { xpath.push(`${formatName(nodeInfo.name)}`); } else { xpath.push(`${formatName(nodeInfo.name)}[${nodeInfo.index}]`); } prevIndex = -1; } else { prevIndex = nodeInfo.index; } } return xpath; } export function formatName(componentName: string) { return nameMap[componentName] || componentName; } export default function getXPath(node: ReactElementFiber): string { if (!node) { return ''; } const nodePath = buildNodePath(node, []).reverse(); // console.info(nodePath); const xpath = toXPath(nodePath); return xpath.join('/'); }