agent-react-devtools 0.0.0 → 0.1.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/CHANGELOG.md +34 -0
- package/dist/cli.js +584 -0
- package/dist/cli.js.map +1 -0
- package/dist/daemon.js +1091 -0
- package/dist/daemon.js.map +1 -0
- package/package.json +35 -1
- package/src/__tests__/cli-parser.test.ts +76 -0
- package/src/__tests__/component-tree.test.ts +229 -0
- package/src/__tests__/formatters.test.ts +189 -0
- package/src/__tests__/profiler.test.ts +264 -0
- package/src/cli.ts +315 -0
- package/src/component-tree.ts +495 -0
- package/src/daemon-client.ts +144 -0
- package/src/daemon.ts +275 -0
- package/src/devtools-bridge.ts +391 -0
- package/src/formatters.ts +270 -0
- package/src/profiler.ts +356 -0
- package/src/types.ts +126 -0
- package/tsconfig.json +9 -0
- package/tsup.config.ts +17 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/daemon.ts","../src/devtools-bridge.ts","../src/component-tree.ts","../src/profiler.ts"],"sourcesContent":["import net from 'node:net';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { DevToolsBridge } from './devtools-bridge.js';\nimport { ComponentTree } from './component-tree.js';\nimport { Profiler } from './profiler.js';\nimport type { IpcCommand, IpcResponse, DaemonInfo, StatusInfo } from './types.js';\n\nconst DEFAULT_STATE_DIR = path.join(\n process.env.HOME || process.env.USERPROFILE || '/tmp',\n '.agent-react-devtools',\n);\n\nlet STATE_DIR = DEFAULT_STATE_DIR;\n\nfunction getSocketPath(): string {\n return path.join(STATE_DIR, 'daemon.sock');\n}\n\nfunction getDaemonInfoPath(): string {\n return path.join(STATE_DIR, 'daemon.json');\n}\n\nclass Daemon {\n private ipcServer: net.Server | null = null;\n private bridge: DevToolsBridge;\n private tree: ComponentTree;\n private profiler: Profiler;\n private port: number;\n private startedAt = Date.now();\n\n constructor(port: number) {\n this.port = port;\n this.tree = new ComponentTree();\n this.profiler = new Profiler();\n this.bridge = new DevToolsBridge(port, this.tree, this.profiler);\n }\n\n async start(): Promise<void> {\n // Ensure state directory exists\n fs.mkdirSync(STATE_DIR, { recursive: true });\n\n // Clean up stale socket\n const socketPath = getSocketPath();\n if (fs.existsSync(socketPath)) {\n try {\n fs.unlinkSync(socketPath);\n } catch {\n // ignore\n }\n }\n\n // Start WebSocket bridge\n await this.bridge.start();\n\n // Start IPC server\n await this.startIpc(socketPath);\n\n // Write daemon info\n const info: DaemonInfo = {\n pid: process.pid,\n port: this.port,\n socketPath,\n startedAt: this.startedAt,\n };\n fs.writeFileSync(getDaemonInfoPath(), JSON.stringify(info, null, 2));\n\n console.log(`Daemon started (pid=${process.pid}, port=${this.port})`);\n\n // Handle shutdown\n const shutdown = () => {\n this.stop();\n process.exit(0);\n };\n process.on('SIGTERM', shutdown);\n process.on('SIGINT', shutdown);\n }\n\n private startIpc(socketPath: string): Promise<void> {\n return new Promise((resolve, reject) => {\n this.ipcServer = net.createServer((conn) => {\n let buffer = '';\n\n conn.on('data', (chunk) => {\n buffer += chunk.toString();\n\n // Process complete messages (newline-delimited JSON)\n let newlineIdx: number;\n while ((newlineIdx = buffer.indexOf('\\n')) !== -1) {\n const line = buffer.slice(0, newlineIdx);\n buffer = buffer.slice(newlineIdx + 1);\n\n try {\n const cmd: IpcCommand = JSON.parse(line);\n this.handleCommand(cmd).then((response) => {\n conn.write(JSON.stringify(response) + '\\n');\n });\n } catch {\n const response: IpcResponse = {\n ok: false,\n error: 'Invalid JSON',\n };\n conn.write(JSON.stringify(response) + '\\n');\n }\n }\n });\n });\n\n this.ipcServer.on('error', reject);\n\n this.ipcServer.listen(socketPath, () => {\n resolve();\n });\n });\n }\n\n private async handleCommand(cmd: IpcCommand): Promise<IpcResponse> {\n try {\n switch (cmd.type) {\n case 'ping':\n return { ok: true, data: 'pong' };\n\n case 'status':\n return {\n ok: true,\n data: {\n daemonRunning: true,\n port: this.port,\n connectedApps: this.bridge.getConnectedAppCount(),\n componentCount: this.tree.getComponentCount(),\n profilingActive: this.profiler.isActive(),\n uptime: Date.now() - this.startedAt,\n } satisfies StatusInfo,\n };\n\n case 'get-tree':\n return {\n ok: true,\n data: this.tree.getTree(cmd.depth),\n };\n\n case 'get-component': {\n const resolvedId = this.tree.resolveId(cmd.id);\n if (resolvedId === undefined) {\n return { ok: false, error: `Component ${cmd.id} not found` };\n }\n const element = await this.bridge.inspectElement(resolvedId);\n if (!element) {\n return { ok: false, error: `Component ${cmd.id} not found` };\n }\n // Include the label if the request used one\n const label = typeof cmd.id === 'string' ? cmd.id : undefined;\n return { ok: true, data: element, label };\n }\n\n case 'find':\n return {\n ok: true,\n data: this.tree.findByName(cmd.name, cmd.exact),\n };\n\n case 'count':\n return {\n ok: true,\n data: this.tree.getCountByType(),\n };\n\n case 'profile-start':\n this.profiler.start(cmd.name);\n // Snapshot existing component names so they survive unmounts\n for (const id of this.tree.getAllNodeIds()) {\n const node = this.tree.getNode(id);\n if (node) this.profiler.trackComponent(id, node.displayName);\n }\n this.bridge.startProfiling();\n return { ok: true, data: 'Profiling started' };\n\n case 'profile-stop': {\n await this.bridge.stopProfilingAndCollect();\n const session = this.profiler.stop(this.tree);\n if (!session) {\n return { ok: false, error: 'No active profiling session' };\n }\n return { ok: true, data: session };\n }\n\n case 'profile-report': {\n const resolvedCompId = this.tree.resolveId(cmd.componentId);\n if (resolvedCompId === undefined) {\n return { ok: false, error: `Component ${cmd.componentId} not found` };\n }\n const report = this.profiler.getReport(resolvedCompId, this.tree);\n if (!report) {\n return {\n ok: false,\n error: `No profiling data for component ${cmd.componentId}`,\n };\n }\n const compLabel = typeof cmd.componentId === 'string' ? cmd.componentId : undefined;\n return { ok: true, data: report, label: compLabel };\n }\n\n case 'profile-slow':\n return {\n ok: true,\n data: this.profiler.getSlowest(this.tree, cmd.limit),\n };\n\n case 'profile-rerenders':\n return {\n ok: true,\n data: this.profiler.getMostRerenders(this.tree, cmd.limit),\n };\n\n case 'profile-timeline':\n return {\n ok: true,\n data: this.profiler.getTimeline(cmd.limit),\n };\n\n case 'profile-commit': {\n const detail = this.profiler.getCommitDetails(cmd.index, this.tree, cmd.limit);\n if (!detail) {\n return { ok: false, error: `Commit #${cmd.index} not found` };\n }\n return { ok: true, data: detail };\n }\n\n default:\n return { ok: false, error: `Unknown command: ${(cmd as any).type}` };\n }\n } catch (err) {\n return {\n ok: false,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n stop(): void {\n this.bridge.stop();\n if (this.ipcServer) {\n this.ipcServer.close();\n this.ipcServer = null;\n }\n // Clean up files\n try {\n fs.unlinkSync(getSocketPath());\n } catch {\n // ignore\n }\n try {\n fs.unlinkSync(getDaemonInfoPath());\n } catch {\n // ignore\n }\n console.log('Daemon stopped');\n }\n}\n\n// ── Main ──\n\nconst portArg = process.argv.find((a) => a.startsWith('--port='));\nconst port = portArg ? parseInt(portArg.split('=')[1], 10) : 8097;\n\nconst stateDirArg = process.argv.find((a) => a.startsWith('--state-dir='));\nif (stateDirArg) {\n STATE_DIR = stateDirArg.split('=')[1];\n}\n\nconst daemon = new Daemon(port);\ndaemon.start().catch((err) => {\n console.error('Failed to start daemon:', err);\n process.exit(1);\n});\n","import { WebSocketServer, WebSocket } from 'ws';\nimport type { ComponentTree } from './component-tree.js';\nimport type { Profiler } from './profiler.js';\nimport type { InspectedElement } from './types.js';\n\n/**\n * React DevTools protocol bridge.\n *\n * Implements the \"Wall\" messaging pattern that React DevTools uses:\n * - The backend (inside React app) sends operations, profiling data, etc.\n * - The frontend (us) can request element inspection, start/stop profiling, etc.\n *\n * Message format over WebSocket:\n * { event: string, payload: any }\n */\n\ninterface DevToolsMessage {\n event: string;\n payload: unknown;\n}\n\ninterface PendingInspection {\n resolve: (value: InspectedElement | null) => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\ninterface PendingProfilingCollect {\n resolve: () => void;\n timer: ReturnType<typeof setTimeout>;\n remaining: number;\n}\n\nexport class DevToolsBridge {\n private wss: WebSocketServer | null = null;\n private connections = new Set<WebSocket>();\n private port: number;\n private tree: ComponentTree;\n private profiler: Profiler;\n private pendingInspections = new Map<number, PendingInspection>();\n private pendingProfilingCollect: PendingProfilingCollect | null = null;\n private rendererIds = new Set<number>();\n /** Track which root fiber IDs belong to each WebSocket connection */\n private connectionRoots = new Map<WebSocket, Set<number>>();\n\n constructor(port: number, tree: ComponentTree, profiler: Profiler) {\n this.port = port;\n this.tree = tree;\n this.profiler = profiler;\n }\n\n async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n this.wss = new WebSocketServer({ port: this.port }, () => {\n resolve();\n });\n\n this.wss.on('error', (err) => {\n reject(err);\n });\n\n this.wss.on('connection', (ws) => {\n this.connections.add(ws);\n\n ws.on('message', (data) => {\n try {\n const msg: DevToolsMessage = JSON.parse(data.toString());\n this.handleMessage(ws, msg);\n } catch {\n // ignore parse errors\n }\n });\n\n ws.on('close', () => {\n this.cleanupConnection(ws);\n });\n\n ws.on('error', () => {\n this.cleanupConnection(ws);\n });\n });\n });\n }\n\n stop(): void {\n for (const conn of this.connections) {\n conn.close();\n }\n this.connections.clear();\n if (this.wss) {\n this.wss.close();\n this.wss = null;\n }\n }\n\n getConnectedAppCount(): number {\n return this.connections.size;\n }\n\n /**\n * Request detailed inspection of a specific element.\n * Sends a request to the React app and waits for the response.\n */\n inspectElement(id: number): Promise<InspectedElement | null> {\n const node = this.tree.getNode(id);\n if (!node) return Promise.resolve(null);\n\n return new Promise((resolve) => {\n const timer = setTimeout(() => {\n this.pendingInspections.delete(id);\n resolve(null);\n }, 5000);\n\n this.pendingInspections.set(id, { resolve, timer });\n\n this.sendToAll({\n event: 'inspectElement',\n payload: {\n id,\n rendererID: node.rendererId,\n forceFullData: true,\n requestID: id,\n path: null,\n },\n });\n });\n }\n\n startProfiling(): void {\n this.sendToAll({\n event: 'startProfiling',\n payload: { recordChangeDescriptions: true },\n });\n }\n\n /**\n * Stop profiling and request data from each renderer.\n * Returns a promise that resolves when profilingData arrives (or 5s timeout).\n */\n stopProfilingAndCollect(): Promise<void> {\n this.sendToAll({\n event: 'stopProfiling',\n payload: undefined,\n });\n\n // If no renderers known, resolve immediately\n if (this.rendererIds.size === 0) {\n return Promise.resolve();\n }\n\n // Request profiling data from each renderer\n for (const rendererID of this.rendererIds) {\n this.sendToAll({\n event: 'getProfilingData',\n payload: { rendererID },\n });\n }\n\n const expected = this.rendererIds.size;\n return new Promise<void>((resolve) => {\n const timer = setTimeout(() => {\n this.pendingProfilingCollect = null;\n resolve();\n }, 5000);\n\n this.pendingProfilingCollect = { resolve, timer, remaining: expected };\n });\n }\n\n private handleMessage(ws: WebSocket, msg: DevToolsMessage): void {\n switch (msg.event) {\n case 'backendInitialized':\n // Send the full frontend handshake sequence\n this.sendTo(ws, { event: 'getBridgeProtocol', payload: undefined });\n this.sendTo(ws, { event: 'getBackendVersion', payload: undefined });\n this.sendTo(ws, { event: 'getIfHasUnsupportedRendererVersion', payload: undefined });\n this.sendTo(ws, { event: 'getHookSettings', payload: undefined });\n this.sendTo(ws, { event: 'getProfilingStatus', payload: undefined });\n break;\n\n case 'bridgeProtocol':\n case 'backendVersion':\n case 'profilingStatus':\n case 'overrideComponentFilters':\n break;\n\n case 'operations':\n this.handleOperations(ws, msg.payload as number[]);\n break;\n\n case 'inspectedElement':\n this.handleInspectedElement(msg.payload);\n break;\n\n case 'profilingData':\n this.handleProfilingData(msg.payload);\n break;\n\n case 'renderer': {\n const payload = msg.payload as { id: number };\n this.rendererIds.add(payload.id);\n break;\n }\n\n case 'rendererAttached': {\n const payload = msg.payload as { id: number };\n this.rendererIds.add(payload.id);\n break;\n }\n\n case 'shutdown':\n ws.close();\n break;\n\n // Silently ignore known but unhandled events\n case 'hookSettings':\n case 'isBackendStorageAPISupported':\n case 'isReactNativeEnvironment':\n case 'isReloadAndProfileSupportedByBackend':\n case 'isSynchronousXHRSupported':\n case 'syncSelectionFromNativeElementsPanel':\n case 'unsupportedRendererVersion':\n break;\n\n default:\n break;\n }\n }\n\n private handleOperations(ws: WebSocket, operations: number[]): void {\n if (operations.length >= 2) {\n // Track renderer ID (first element of every operations array)\n this.rendererIds.add(operations[0]);\n\n // Track which root fiber IDs belong to this connection\n const rootFiberId = operations[1];\n let roots = this.connectionRoots.get(ws);\n if (!roots) {\n roots = new Set();\n this.connectionRoots.set(ws, roots);\n }\n roots.add(rootFiberId);\n }\n const added = this.tree.applyOperations(operations);\n\n // Cache display names during profiling so unmounted components are still identifiable\n if (this.profiler.isActive()) {\n for (const node of added) {\n this.profiler.trackComponent(node.id, node.displayName);\n }\n }\n }\n\n private cleanupConnection(ws: WebSocket): void {\n this.connections.delete(ws);\n // Remove all root trees that belonged to this connection\n const roots = this.connectionRoots.get(ws);\n if (roots) {\n for (const rootId of roots) {\n this.tree.removeRoot(rootId);\n }\n this.connectionRoots.delete(ws);\n }\n }\n\n private handleInspectedElement(payload: unknown): void {\n const data = payload as {\n type: string;\n id: number;\n value?: {\n id: number;\n displayName: string;\n type: number;\n key: string | null;\n props: Record<string, unknown>;\n state: Record<string, unknown> | null;\n hooks: unknown[] | null;\n };\n };\n\n if (data.type !== 'full-data' && data.type !== 'hydrated-path') {\n // No data available\n const pending = this.pendingInspections.get(data.id);\n if (pending) {\n clearTimeout(pending.timer);\n this.pendingInspections.delete(data.id);\n pending.resolve(null);\n }\n return;\n }\n\n const pending = this.pendingInspections.get(data.id);\n if (!pending || !data.value) return;\n\n clearTimeout(pending.timer);\n this.pendingInspections.delete(data.id);\n\n const node = this.tree.getNode(data.id);\n const inspected: InspectedElement = {\n id: data.id,\n displayName: data.value.displayName || node?.displayName || 'Unknown',\n type: node?.type || 'other',\n key: data.value.key,\n props: cleanDehydrated(data.value.props) as Record<string, unknown>,\n state: data.value.state\n ? (cleanDehydrated(data.value.state) as Record<string, unknown>)\n : null,\n hooks: data.value.hooks\n ? parseHooks(data.value.hooks)\n : null,\n renderedAt: null,\n };\n\n pending.resolve(inspected);\n }\n\n private handleProfilingData(payload: unknown): void {\n // React DevTools sends profiling data as a complex nested structure.\n // We forward it to the profiler for processing.\n this.profiler.processProfilingData(payload);\n\n // Resolve once all expected renderer responses have arrived\n if (this.pendingProfilingCollect) {\n this.pendingProfilingCollect.remaining--;\n if (this.pendingProfilingCollect.remaining <= 0) {\n clearTimeout(this.pendingProfilingCollect.timer);\n const pending = this.pendingProfilingCollect;\n this.pendingProfilingCollect = null;\n pending.resolve();\n }\n }\n }\n\n private sendTo(ws: WebSocket, msg: DevToolsMessage): void {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify(msg));\n }\n }\n\n private sendToAll(msg: DevToolsMessage): void {\n const raw = JSON.stringify(msg);\n for (const conn of this.connections) {\n if (conn.readyState === WebSocket.OPEN) {\n conn.send(raw);\n }\n }\n }\n}\n\n/**\n * React DevTools uses \"dehydrated\" values for complex objects.\n * These appear as objects with `type: 'string'` and other metadata.\n * We simplify them for display.\n */\nfunction cleanDehydrated(obj: unknown): unknown {\n if (obj === null || obj === undefined) return obj;\n if (typeof obj !== 'object') return obj;\n if (Array.isArray(obj)) return obj.map(cleanDehydrated);\n\n const record = obj as Record<string, unknown>;\n\n // Dehydrated value markers from React DevTools\n if ('type' in record && 'preview_short' in record) {\n return record['preview_short'];\n }\n\n const cleaned: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(record)) {\n cleaned[key] = cleanDehydrated(value);\n }\n return cleaned;\n}\n\nfunction parseHooks(hooks: unknown[]): { name: string; value: unknown; subHooks?: { name: string; value: unknown }[] }[] {\n return hooks.map((hook) => {\n const h = hook as {\n id: number | null;\n isStateEditable: boolean;\n name: string;\n value: unknown;\n subHooks?: unknown[];\n };\n const result: { name: string; value: unknown; subHooks?: { name: string; value: unknown }[] } = {\n name: h.name,\n value: cleanDehydrated(h.value),\n };\n if (h.subHooks && h.subHooks.length > 0) {\n result.subHooks = parseHooks(h.subHooks) as { name: string; value: unknown }[];\n }\n return result;\n });\n}\n","import type { ComponentNode, ComponentType } from './types.js';\n\n/**\n * React DevTools operations encoding (protocol v2):\n * Operations is a flat array of numbers representing tree mutations.\n *\n * Format: [rendererID, rootFiberID, stringTableSize, ...stringTable, ...ops]\n *\n * The string table encodes display names and keys. Each entry is:\n * [length, ...charCodes]\n * String ID 0 = null. String ID 1 = first entry, etc.\n *\n * Operation types (from React DevTools source):\n */\nconst TREE_OPERATION_ADD = 1;\nconst TREE_OPERATION_REMOVE = 2;\nconst TREE_OPERATION_REORDER_CHILDREN = 3;\nconst TREE_OPERATION_UPDATE_TREE_BASE_DURATION = 4;\nconst TREE_OPERATION_UPDATE_ERRORS_OR_WARNINGS = 5;\nconst TREE_OPERATION_REMOVE_ROOT = 6;\nconst TREE_OPERATION_SET_SUBTREE_MODE = 7;\n\n/**\n * Suspense tree operations (newer React DevTools backends, e.g. browser extension):\n */\nconst SUSPENSE_TREE_OPERATION_ADD = 8;\nconst SUSPENSE_TREE_OPERATION_REMOVE = 9;\nconst SUSPENSE_TREE_OPERATION_REORDER_CHILDREN = 10;\nconst SUSPENSE_TREE_OPERATION_RESIZE = 11;\nconst SUSPENSE_TREE_OPERATION_SUSPENDERS = 12;\nconst TREE_OPERATION_APPLIED_ACTIVITY_SLICE_CHANGE = 13;\n\n/**\n * Element types from React DevTools (react-devtools-shared/src/frontend/types.js)\n */\nconst ELEMENT_TYPE_CLASS = 1;\n// const ELEMENT_TYPE_CONTEXT = 2;\nconst ELEMENT_TYPE_FUNCTION = 5;\nconst ELEMENT_TYPE_FORWARD_REF = 6;\nconst ELEMENT_TYPE_HOST = 7;\nconst ELEMENT_TYPE_MEMO = 8;\n// const ELEMENT_TYPE_OTHER = 9;\nconst ELEMENT_TYPE_PROFILER = 10;\nconst ELEMENT_TYPE_ROOT = 11;\nconst ELEMENT_TYPE_SUSPENSE = 12;\n\nfunction toComponentType(elementType: number): ComponentType {\n switch (elementType) {\n case ELEMENT_TYPE_CLASS:\n return 'class';\n case ELEMENT_TYPE_FUNCTION:\n return 'function';\n case ELEMENT_TYPE_FORWARD_REF:\n return 'forwardRef';\n case ELEMENT_TYPE_HOST:\n return 'host';\n case ELEMENT_TYPE_MEMO:\n return 'memo';\n case ELEMENT_TYPE_PROFILER:\n return 'profiler';\n case ELEMENT_TYPE_SUSPENSE:\n return 'suspense';\n case ELEMENT_TYPE_ROOT:\n return 'other'; // roots are internal, map to 'other'\n default:\n return 'other';\n }\n}\n\n/**\n * Skip a variable-length rect encoding in the operations array.\n * Rects are encoded as: count, then count * 4 values (x, y, w, h each × 1000).\n * A count of -1 means null (no rects).\n * Returns the new index after skipping.\n */\nfunction skipRects(operations: number[], i: number): number {\n const count = operations[i++];\n if (count === -1) return i;\n return i + count * 4;\n}\n\nexport interface TreeNode {\n id: number;\n label: string;\n displayName: string;\n type: ComponentType;\n key: string | null;\n parentId: number | null;\n children: number[];\n depth: number;\n}\n\nexport class ComponentTree {\n private nodes = new Map<number, ComponentNode>();\n private roots: number[] = [];\n /** Index: lowercase display name → set of node ids */\n private nameIndex = new Map<string, Set<number>>();\n /** Label → real node ID (e.g., \"@c1\" → 10) */\n private labelToId = new Map<string, number>();\n /** Real node ID → label */\n private idToLabel = new Map<number, string>();\n /**\n * Whether the backend uses the extended ADD format (8 fields with namePropStringID).\n * Auto-detected from the presence of SUSPENSE_TREE_OPERATION opcodes.\n */\n private extendedAddFormat = false;\n\n applyOperations(operations: number[]): Array<{ id: number; displayName: string }> {\n if (operations.length < 2) return [];\n\n const added: Array<{ id: number; displayName: string }> = [];\n const rendererId = operations[0];\n // operations[1] is the root fiber ID\n let i = 2;\n\n // Parse the string table (protocol v2)\n const stringTable: Array<string | null> = [null]; // ID 0 = null\n const stringTableSize = operations[i++];\n const stringTableEnd = i + stringTableSize;\n while (i < stringTableEnd) {\n const strLen = operations[i++];\n let str = '';\n for (let j = 0; j < strLen; j++) {\n str += String.fromCodePoint(operations[i++]);\n }\n stringTable.push(str);\n }\n\n // Parse operations\n while (i < operations.length) {\n const op = operations[i];\n\n switch (op) {\n case TREE_OPERATION_ADD: {\n const id = operations[i + 1];\n const elementType = operations[i + 2];\n i += 3;\n\n if (elementType === ELEMENT_TYPE_ROOT) {\n // Root node: isStrictModeCompliant, supportsProfiling,\n // supportsStrictMode, hasOwnerMetadata\n i += 4;\n\n const node: ComponentNode = {\n id,\n displayName: 'Root',\n type: 'other',\n key: null,\n parentId: null,\n children: [],\n rendererId,\n };\n this.nodes.set(id, node);\n added.push({ id, displayName: node.displayName });\n if (!this.roots.includes(id)) {\n this.roots.push(id);\n }\n } else {\n const parentId = operations[i++];\n i++; // ownerID\n const displayNameStringId = operations[i++];\n const keyStringId = operations[i++];\n if (this.extendedAddFormat) {\n i++; // namePropStringID (added in newer backends)\n }\n\n const displayName =\n (displayNameStringId > 0 ? stringTable[displayNameStringId] : null) ||\n (elementType === ELEMENT_TYPE_HOST ? 'HostComponent' : 'Anonymous');\n const key = keyStringId > 0 ? stringTable[keyStringId] || null : null;\n\n const node: ComponentNode = {\n id,\n displayName,\n type: toComponentType(elementType),\n key,\n parentId: parentId === 0 ? null : parentId,\n children: [],\n rendererId,\n };\n\n this.nodes.set(id, node);\n added.push({ id, displayName });\n\n // Add to parent's children\n if (parentId === 0) {\n if (!this.roots.includes(id)) {\n this.roots.push(id);\n }\n } else {\n const parent = this.nodes.get(parentId);\n if (parent) {\n parent.children.push(id);\n }\n }\n\n // Update name index\n if (displayName) {\n const lower = displayName.toLowerCase();\n let set = this.nameIndex.get(lower);\n if (!set) {\n set = new Set();\n this.nameIndex.set(lower, set);\n }\n set.add(id);\n }\n }\n break;\n }\n\n case TREE_OPERATION_REMOVE: {\n const numRemoved = operations[i + 1];\n for (let j = 0; j < numRemoved; j++) {\n const id = operations[i + 2 + j];\n this.removeNode(id);\n }\n i += 2 + numRemoved;\n break;\n }\n\n case TREE_OPERATION_REORDER_CHILDREN: {\n const id = operations[i + 1];\n const numChildren = operations[i + 2];\n const newChildren: number[] = [];\n for (let j = 0; j < numChildren; j++) {\n newChildren.push(operations[i + 3 + j]);\n }\n const node = this.nodes.get(id);\n if (node) {\n node.children = newChildren;\n }\n i += 3 + numChildren;\n break;\n }\n\n case TREE_OPERATION_UPDATE_TREE_BASE_DURATION: {\n // id, baseDuration — skip\n i += 3;\n break;\n }\n\n case TREE_OPERATION_UPDATE_ERRORS_OR_WARNINGS: {\n // id, numErrors, numWarnings\n i += 4;\n break;\n }\n\n case TREE_OPERATION_REMOVE_ROOT: {\n const rootId = operations[i + 1];\n this.removeNode(rootId);\n i += 2;\n break;\n }\n\n case TREE_OPERATION_SET_SUBTREE_MODE: {\n // id, mode\n i += 3;\n break;\n }\n\n // ── Suspense tree operations (newer backends) ──\n\n case SUSPENSE_TREE_OPERATION_ADD: {\n // Presence of suspense ops means the backend also uses 8-field ADD\n this.extendedAddFormat = true;\n // fiberID, parentID, nameStringID, isSuspended, rects\n i += 5; // opcode + 4 fields\n i = skipRects(operations, i);\n break;\n }\n\n case SUSPENSE_TREE_OPERATION_REMOVE: {\n this.extendedAddFormat = true;\n // numIDs, then that many IDs\n const numIds = operations[i + 1];\n i += 2 + numIds;\n break;\n }\n\n case SUSPENSE_TREE_OPERATION_REORDER_CHILDREN: {\n this.extendedAddFormat = true;\n // parentID, numChildren, then that many child IDs\n const numSuspenseChildren = operations[i + 2];\n i += 3 + numSuspenseChildren;\n break;\n }\n\n case SUSPENSE_TREE_OPERATION_RESIZE: {\n this.extendedAddFormat = true;\n // fiberID, rects\n i += 2; // opcode + fiberID\n i = skipRects(operations, i);\n break;\n }\n\n case SUSPENSE_TREE_OPERATION_SUSPENDERS: {\n this.extendedAddFormat = true;\n // numChanges, then numChanges * 4 values\n const numChanges = operations[i + 1];\n i += 2 + numChanges * 4;\n break;\n }\n\n case TREE_OPERATION_APPLIED_ACTIVITY_SLICE_CHANGE: {\n this.extendedAddFormat = true;\n // id\n i += 2;\n break;\n }\n\n default:\n // Unknown operation — skip one value and try to continue.\n // Future protocol additions may cause brief misalignment but\n // subsequent operations batches will self-correct.\n i++;\n break;\n }\n }\n\n return added;\n }\n\n private removeNode(id: number): void {\n const node = this.nodes.get(id);\n if (!node) return;\n\n // Remove from parent's children\n if (node.parentId !== null) {\n const parent = this.nodes.get(node.parentId);\n if (parent) {\n parent.children = parent.children.filter((c) => c !== id);\n }\n }\n\n // Remove from roots\n this.roots = this.roots.filter((r) => r !== id);\n\n // Remove from name index\n if (node.displayName) {\n const lower = node.displayName.toLowerCase();\n const set = this.nameIndex.get(lower);\n if (set) {\n set.delete(id);\n if (set.size === 0) this.nameIndex.delete(lower);\n }\n }\n\n // Recursively remove children\n for (const childId of node.children) {\n this.removeNode(childId);\n }\n\n this.nodes.delete(id);\n }\n\n getNode(id: number): ComponentNode | undefined {\n return this.nodes.get(id);\n }\n\n getTree(maxDepth?: number): TreeNode[] {\n const result: TreeNode[] = [];\n\n // Rebuild label maps on every getTree() call\n this.labelToId.clear();\n this.idToLabel.clear();\n let labelCounter = 1;\n\n const walk = (id: number, depth: number) => {\n const node = this.nodes.get(id);\n if (!node) return;\n if (maxDepth !== undefined && depth > maxDepth) return;\n\n const label = `@c${labelCounter++}`;\n this.labelToId.set(label, node.id);\n this.idToLabel.set(node.id, label);\n\n result.push({\n id: node.id,\n label,\n displayName: node.displayName,\n type: node.type,\n key: node.key,\n parentId: node.parentId,\n children: node.children,\n depth,\n });\n\n for (const childId of node.children) {\n walk(childId, depth + 1);\n }\n };\n\n for (const rootId of this.roots) {\n walk(rootId, 0);\n }\n return result;\n }\n\n findByName(name: string, exact?: boolean): TreeNode[] {\n const results: TreeNode[] = [];\n\n if (exact) {\n const lower = name.toLowerCase();\n const ids = this.nameIndex.get(lower);\n if (ids) {\n for (const id of ids) {\n const node = this.nodes.get(id);\n if (node && node.displayName.toLowerCase() === lower) {\n results.push(this.toTreeNode(node));\n }\n }\n }\n } else {\n const lower = name.toLowerCase();\n for (const [indexName, ids] of this.nameIndex) {\n if (indexName.includes(lower)) {\n for (const id of ids) {\n const node = this.nodes.get(id);\n if (node) {\n results.push(this.toTreeNode(node));\n }\n }\n }\n }\n }\n\n return results;\n }\n\n getComponentCount(): number {\n return this.nodes.size;\n }\n\n getCountByType(): Record<string, number> {\n const counts: Record<string, number> = {};\n for (const node of this.nodes.values()) {\n counts[node.type] = (counts[node.type] || 0) + 1;\n }\n return counts;\n }\n\n getAllNodeIds(): number[] {\n return Array.from(this.nodes.keys());\n }\n\n getRootIds(): number[] {\n return [...this.roots];\n }\n\n removeRoot(rootId: number): void {\n this.removeNode(rootId);\n }\n\n /**\n * Resolve a label like \"@c3\" to a real node ID.\n * Returns undefined if label not found.\n */\n resolveLabel(label: string): number | undefined {\n return this.labelToId.get(label);\n }\n\n /**\n * Resolve either a label string (\"@c3\") or a numeric ID to a real node ID.\n */\n resolveId(id: number | string): number | undefined {\n if (typeof id === 'number') return id;\n if (id.startsWith('@c')) return this.labelToId.get(id);\n // Try parsing as number\n const num = parseInt(id, 10);\n return isNaN(num) ? undefined : num;\n }\n\n private toTreeNode(node: ComponentNode): TreeNode {\n // Calculate depth by walking up the tree\n let depth = 0;\n let current = node;\n while (current.parentId !== null) {\n depth++;\n const parent = this.nodes.get(current.parentId);\n if (!parent) break;\n current = parent;\n }\n\n return {\n id: node.id,\n label: this.idToLabel.get(node.id) || `@c?`,\n displayName: node.displayName,\n type: node.type,\n key: node.key,\n parentId: node.parentId,\n children: node.children,\n depth,\n };\n }\n}\n","import type {\n ProfilingSession,\n ProfilingCommit,\n ChangeDescription,\n ComponentRenderReport,\n RenderCause,\n} from './types.js';\nimport type { ComponentTree } from './component-tree.js';\n\nexport interface ProfileSummary {\n name: string;\n duration: number;\n commitCount: number;\n componentRenderCounts: { id: number; displayName: string; count: number }[];\n}\n\nexport interface TimelineEntry {\n index: number;\n timestamp: number;\n duration: number;\n componentCount: number;\n}\n\nexport interface CommitDetail {\n index: number;\n timestamp: number;\n duration: number;\n components: Array<{\n id: number;\n displayName: string;\n actualDuration: number;\n selfDuration: number;\n causes: RenderCause[];\n }>;\n totalComponents: number;\n}\n\nexport class Profiler {\n private session: ProfilingSession | null = null;\n /** Display names captured during profiling (survives unmounts) */\n private displayNames = new Map<number, string>();\n\n isActive(): boolean {\n return this.session !== null && this.session.stoppedAt === null;\n }\n\n start(name?: string): void {\n this.displayNames.clear();\n this.session = {\n name: name || `session-${Date.now()}`,\n startedAt: Date.now(),\n stoppedAt: null,\n commits: [],\n };\n }\n\n /** Cache a component's display name (call during profiling to survive unmounts) */\n trackComponent(id: number, displayName: string): void {\n this.displayNames.set(id, displayName);\n }\n\n stop(tree?: ComponentTree): ProfileSummary | null {\n if (!this.session) return null;\n this.session.stoppedAt = Date.now();\n\n const duration = this.session.stoppedAt - this.session.startedAt;\n\n // Count renders per component\n const renderCounts = new Map<number, number>();\n for (const commit of this.session.commits) {\n for (const [id] of commit.fiberActualDurations) {\n renderCounts.set(id, (renderCounts.get(id) || 0) + 1);\n }\n }\n\n const componentRenderCounts = Array.from(renderCounts.entries())\n .map(([id, count]) => ({\n id,\n displayName: tree?.getNode(id)?.displayName || this.displayNames.get(id) || '',\n count,\n }))\n .sort((a, b) => b.count - a.count);\n\n return {\n name: this.session.name,\n duration,\n commitCount: this.session.commits.length,\n componentRenderCounts,\n };\n }\n\n /**\n * Process profiling data sent from React DevTools.\n *\n * The data format varies between React versions. We handle the common\n * format where each commit contains:\n * - commitTime\n * - duration\n * - fiberActualDurations: [id, duration, ...]\n * - fiberSelfDurations: [id, duration, ...]\n * - changeDescriptions: Map<id, description>\n */\n processProfilingData(payload: unknown): void {\n if (!this.session || this.session.stoppedAt !== null) return;\n\n const data = payload as {\n dataForRoots?: Array<{\n commitData?: Array<{\n changeDescriptions?: Array<[number, unknown]> | Map<number, unknown>;\n duration?: number;\n fiberActualDurations?: Array<[number, number]> | number[];\n fiberSelfDurations?: Array<[number, number]> | number[];\n timestamp?: number;\n }>;\n operations?: unknown[];\n }>;\n // Alternative flat format\n commitData?: Array<{\n changeDescriptions?: Array<[number, unknown]> | Map<number, unknown>;\n duration?: number;\n fiberActualDurations?: Array<[number, number]> | number[];\n fiberSelfDurations?: Array<[number, number]> | number[];\n timestamp?: number;\n }>;\n };\n\n // Handle nested format (dataForRoots)\n const roots = data?.dataForRoots;\n if (roots) {\n for (const root of roots) {\n if (root.commitData) {\n for (const commitData of root.commitData) {\n this.processCommitData(commitData);\n }\n }\n }\n return;\n }\n\n // Handle flat format\n if (data?.commitData) {\n for (const commitData of data.commitData) {\n this.processCommitData(commitData);\n }\n }\n }\n\n private processCommitData(commitData: {\n changeDescriptions?: Array<[number, unknown]> | Map<number, unknown>;\n duration?: number;\n fiberActualDurations?: Array<[number, number]> | number[];\n fiberSelfDurations?: Array<[number, number]> | number[];\n timestamp?: number;\n }): void {\n const commit: ProfilingCommit = {\n timestamp: commitData.timestamp || Date.now(),\n duration: commitData.duration || 0,\n fiberActualDurations: new Map(),\n fiberSelfDurations: new Map(),\n changeDescriptions: new Map(),\n };\n\n // Parse fiber durations (can be [id, duration, id, duration, ...] or [[id, duration], ...])\n if (commitData.fiberActualDurations) {\n parseDurations(commitData.fiberActualDurations, commit.fiberActualDurations);\n }\n if (commitData.fiberSelfDurations) {\n parseDurations(commitData.fiberSelfDurations, commit.fiberSelfDurations);\n }\n\n // Parse change descriptions\n if (commitData.changeDescriptions) {\n const entries =\n commitData.changeDescriptions instanceof Map\n ? commitData.changeDescriptions.entries()\n : commitData.changeDescriptions[Symbol.iterator]();\n for (const [id, desc] of entries) {\n const d = desc as {\n didHooksChange?: boolean;\n isFirstMount?: boolean;\n props?: string[] | null;\n state?: string[] | null;\n hooks?: number[] | null;\n };\n commit.changeDescriptions.set(id as number, {\n didHooksChange: d.didHooksChange || false,\n isFirstMount: d.isFirstMount || false,\n props: d.props || null,\n state: d.state || null,\n hooks: d.hooks || null,\n });\n }\n }\n\n this.session!.commits.push(commit);\n }\n\n getReport(\n componentId: number,\n tree: ComponentTree,\n ): ComponentRenderReport | null {\n if (!this.session) return null;\n\n const node = tree.getNode(componentId);\n let renderCount = 0;\n let totalDuration = 0;\n let maxDuration = 0;\n const causeSet = new Set<RenderCause>();\n\n for (const commit of this.session.commits) {\n const duration = commit.fiberActualDurations.get(componentId);\n if (duration !== undefined) {\n renderCount++;\n totalDuration += duration;\n if (duration > maxDuration) maxDuration = duration;\n\n const desc = commit.changeDescriptions.get(componentId);\n if (desc) {\n for (const cause of describeCauses(desc)) {\n causeSet.add(cause);\n }\n }\n }\n }\n\n if (renderCount === 0) return null;\n\n return {\n id: componentId,\n displayName: node?.displayName || this.displayNames.get(componentId) || `Component#${componentId}`,\n renderCount,\n totalDuration,\n avgDuration: totalDuration / renderCount,\n maxDuration,\n causes: Array.from(causeSet),\n };\n }\n\n getSlowest(\n tree: ComponentTree,\n limit = 10,\n ): ComponentRenderReport[] {\n return this.getAllReports(tree)\n .sort((a, b) => b.avgDuration - a.avgDuration)\n .slice(0, limit);\n }\n\n getMostRerenders(\n tree: ComponentTree,\n limit = 10,\n ): ComponentRenderReport[] {\n return this.getAllReports(tree)\n .sort((a, b) => b.renderCount - a.renderCount)\n .slice(0, limit);\n }\n\n getCommitDetails(index: number, tree: ComponentTree, limit = 10): CommitDetail | null {\n if (!this.session) return null;\n if (index < 0 || index >= this.session.commits.length) return null;\n\n const commit = this.session.commits[index];\n const components: CommitDetail['components'] = [];\n\n for (const [id, actualDuration] of commit.fiberActualDurations) {\n const selfDuration = commit.fiberSelfDurations.get(id) || 0;\n const desc = commit.changeDescriptions.get(id);\n components.push({\n id,\n displayName: tree.getNode(id)?.displayName || this.displayNames.get(id) || `Component#${id}`,\n actualDuration,\n selfDuration,\n causes: desc ? describeCauses(desc) : [],\n });\n }\n\n components.sort((a, b) => b.selfDuration - a.selfDuration);\n\n const totalCount = components.length;\n\n return {\n index,\n timestamp: commit.timestamp,\n duration: commit.duration,\n components: limit > 0 ? components.slice(0, limit) : components,\n totalComponents: totalCount,\n };\n }\n\n getTimeline(limit?: number): TimelineEntry[] {\n if (!this.session) return [];\n\n const entries = this.session.commits.map((commit, index) => ({\n index,\n timestamp: commit.timestamp,\n duration: commit.duration,\n componentCount: commit.fiberActualDurations.size,\n }));\n\n if (limit) return entries.slice(0, limit);\n return entries;\n }\n\n private getAllReports(tree: ComponentTree): ComponentRenderReport[] {\n if (!this.session) return [];\n\n // Collect all component IDs that appear in profiling data\n const componentIds = new Set<number>();\n for (const commit of this.session.commits) {\n for (const id of commit.fiberActualDurations.keys()) {\n componentIds.add(id);\n }\n }\n\n const reports: ComponentRenderReport[] = [];\n for (const id of componentIds) {\n const report = this.getReport(id, tree);\n if (report) reports.push(report);\n }\n return reports;\n }\n}\n\nfunction parseDurations(\n raw: Array<[number, number]> | number[],\n target: Map<number, number>,\n): void {\n if (raw.length === 0) return;\n\n // Check if it's array of tuples or flat array\n if (Array.isArray(raw[0])) {\n // [[id, duration], ...]\n for (const [id, duration] of raw as Array<[number, number]>) {\n target.set(id, duration);\n }\n } else {\n // [id, duration, id, duration, ...]\n const flat = raw as number[];\n for (let i = 0; i < flat.length; i += 2) {\n target.set(flat[i], flat[i + 1]);\n }\n }\n}\n\nfunction describeCauses(desc: ChangeDescription): RenderCause[] {\n const causes: RenderCause[] = [];\n if (desc.isFirstMount) {\n causes.push('first-mount');\n return causes;\n }\n if (desc.props && desc.props.length > 0) causes.push('props-changed');\n if (desc.state && desc.state.length > 0) causes.push('state-changed');\n if (desc.didHooksChange) causes.push('hooks-changed');\n // If no specific cause found, it was likely parent-triggered\n if (causes.length === 0) causes.push('parent-rendered');\n return causes;\n}\n"],"mappings":"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","names":["port","pending","port"]}
|
package/package.json
CHANGED
|
@@ -1 +1,35 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"name": "agent-react-devtools",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI tool for AI agents to inspect React component trees and profile performance",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"agent-react-devtools": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsup",
|
|
11
|
+
"dev": "tsup --watch",
|
|
12
|
+
"test": "vitest run",
|
|
13
|
+
"test:watch": "vitest",
|
|
14
|
+
"typecheck": "tsc --noEmit",
|
|
15
|
+
"lint": "tsc --noEmit"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/piotrski/agent-react-devtools.git",
|
|
20
|
+
"directory": "packages/agent-react-devtools"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^22.0.0",
|
|
25
|
+
"@types/ws": "^8.5.0",
|
|
26
|
+
"tsup": "^8.0.0",
|
|
27
|
+
"typescript": "^5.5.0",
|
|
28
|
+
"vitest": "^2.0.0"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
32
|
+
"ws": "^8.18.0",
|
|
33
|
+
"zod": "^3.24.0"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// Inline the parseArgs function for testing (it's not exported from cli.ts)
|
|
4
|
+
function parseArgs(argv: string[]): {
|
|
5
|
+
command: string[];
|
|
6
|
+
flags: Record<string, string | boolean>;
|
|
7
|
+
} {
|
|
8
|
+
const command: string[] = [];
|
|
9
|
+
const flags: Record<string, string | boolean> = {};
|
|
10
|
+
|
|
11
|
+
for (let i = 0; i < argv.length; i++) {
|
|
12
|
+
const arg = argv[i];
|
|
13
|
+
if (arg.startsWith('--')) {
|
|
14
|
+
const key = arg.slice(2);
|
|
15
|
+
const eqIdx = key.indexOf('=');
|
|
16
|
+
if (eqIdx !== -1) {
|
|
17
|
+
flags[key.slice(0, eqIdx)] = key.slice(eqIdx + 1);
|
|
18
|
+
} else {
|
|
19
|
+
const next = argv[i + 1];
|
|
20
|
+
if (next && !next.startsWith('--')) {
|
|
21
|
+
flags[key] = next;
|
|
22
|
+
i++;
|
|
23
|
+
} else {
|
|
24
|
+
flags[key] = true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
command.push(arg);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return { command, flags };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe('CLI argument parser', () => {
|
|
35
|
+
it('should parse simple commands', () => {
|
|
36
|
+
const { command, flags } = parseArgs(['start']);
|
|
37
|
+
expect(command).toEqual(['start']);
|
|
38
|
+
expect(flags).toEqual({});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should parse commands with subcommands', () => {
|
|
42
|
+
const { command } = parseArgs(['get', 'tree']);
|
|
43
|
+
expect(command).toEqual(['get', 'tree']);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should parse --flag=value', () => {
|
|
47
|
+
const { flags } = parseArgs(['start', '--port=8098']);
|
|
48
|
+
expect(flags['port']).toBe('8098');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should parse --flag value', () => {
|
|
52
|
+
const { flags } = parseArgs(['start', '--port', '8098']);
|
|
53
|
+
expect(flags['port']).toBe('8098');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should parse boolean flags', () => {
|
|
57
|
+
const { flags } = parseArgs(['find', 'User', '--exact']);
|
|
58
|
+
expect(flags['exact']).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should parse mixed args', () => {
|
|
62
|
+
const { command, flags } = parseArgs([
|
|
63
|
+
'profile',
|
|
64
|
+
'slow',
|
|
65
|
+
'--limit',
|
|
66
|
+
'5',
|
|
67
|
+
]);
|
|
68
|
+
expect(command).toEqual(['profile', 'slow']);
|
|
69
|
+
expect(flags['limit']).toBe('5');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should parse component id as positional arg', () => {
|
|
73
|
+
const { command } = parseArgs(['get', 'component', '42']);
|
|
74
|
+
expect(command).toEqual(['get', 'component', '42']);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { ComponentTree } from '../component-tree.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Operations encoding reference (protocol v2):
|
|
6
|
+
* [rendererID, rootFiberID, stringTableSize, ...stringTable, ...ops]
|
|
7
|
+
*
|
|
8
|
+
* String table: for each string, [length, ...charCodes]. String ID 0 = null.
|
|
9
|
+
*
|
|
10
|
+
* TREE_OPERATION_ADD (1):
|
|
11
|
+
* 1, id, elementType, parentId, ownerID, displayNameStringID, keyStringID
|
|
12
|
+
*
|
|
13
|
+
* Element types (from react-devtools-shared/src/frontend/types.js):
|
|
14
|
+
* CLASS=1, FUNCTION=5, FORWARD_REF=6, HOST=7, MEMO=8, PROFILER=10, ROOT=11, SUSPENSE=12
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** Build a string table and return [tableData, stringIdMap] */
|
|
18
|
+
function buildStringTable(strings: string[]): [number[], Map<string, number>] {
|
|
19
|
+
const idMap = new Map<string, number>();
|
|
20
|
+
const data: number[] = [];
|
|
21
|
+
for (const s of strings) {
|
|
22
|
+
const id = idMap.size + 1; // 0 is reserved for null
|
|
23
|
+
if (!idMap.has(s)) {
|
|
24
|
+
idMap.set(s, id);
|
|
25
|
+
data.push(s.length, ...Array.from(s).map((c) => c.charCodeAt(0)));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return [data, idMap];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Build a complete operations array with string table */
|
|
32
|
+
function buildOps(
|
|
33
|
+
rendererID: number,
|
|
34
|
+
rootID: number,
|
|
35
|
+
strings: string[],
|
|
36
|
+
opsFn: (strId: (s: string) => number) => number[],
|
|
37
|
+
): number[] {
|
|
38
|
+
const [tableData, idMap] = buildStringTable(strings);
|
|
39
|
+
const strId = (s: string) => idMap.get(s) || 0;
|
|
40
|
+
const ops = opsFn(strId);
|
|
41
|
+
return [rendererID, rootID, tableData.length, ...tableData, ...ops];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function addOp(
|
|
45
|
+
id: number,
|
|
46
|
+
elementType: number,
|
|
47
|
+
parentId: number,
|
|
48
|
+
displayNameStrId: number,
|
|
49
|
+
keyStrId: number = 0,
|
|
50
|
+
): number[] {
|
|
51
|
+
return [1, id, elementType, parentId, 0, displayNameStrId, keyStrId];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function removeOp(...ids: number[]): number[] {
|
|
55
|
+
return [2, ids.length, ...ids];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function reorderOp(id: number, children: number[]): number[] {
|
|
59
|
+
return [3, id, children.length, ...children];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
describe('ComponentTree', () => {
|
|
63
|
+
let tree: ComponentTree;
|
|
64
|
+
|
|
65
|
+
beforeEach(() => {
|
|
66
|
+
tree = new ComponentTree();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should add nodes from operations', () => {
|
|
70
|
+
const ops = buildOps(1, 100, ['App', 'Header', 'Footer'], (s) => [
|
|
71
|
+
...addOp(1, 5, 0, s('App')), // Function component at root
|
|
72
|
+
...addOp(2, 8, 1, s('Header')), // Memo child
|
|
73
|
+
...addOp(3, 5, 1, s('Footer')), // Function child
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
tree.applyOperations(ops);
|
|
77
|
+
|
|
78
|
+
expect(tree.getComponentCount()).toBe(3);
|
|
79
|
+
|
|
80
|
+
const node1 = tree.getNode(1);
|
|
81
|
+
expect(node1).toBeDefined();
|
|
82
|
+
expect(node1!.displayName).toBe('App');
|
|
83
|
+
expect(node1!.type).toBe('function');
|
|
84
|
+
expect(node1!.children).toEqual([2, 3]);
|
|
85
|
+
|
|
86
|
+
const node2 = tree.getNode(2);
|
|
87
|
+
expect(node2!.displayName).toBe('Header');
|
|
88
|
+
expect(node2!.type).toBe('memo');
|
|
89
|
+
expect(node2!.parentId).toBe(1);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should handle keys', () => {
|
|
93
|
+
const ops = buildOps(1, 100, ['List', 'Item', 'item-1', 'item-2'], (s) => [
|
|
94
|
+
...addOp(1, 5, 0, s('List')),
|
|
95
|
+
...addOp(2, 5, 1, s('Item'), s('item-1')),
|
|
96
|
+
...addOp(3, 5, 1, s('Item'), s('item-2')),
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
tree.applyOperations(ops);
|
|
100
|
+
|
|
101
|
+
expect(tree.getNode(2)!.key).toBe('item-1');
|
|
102
|
+
expect(tree.getNode(3)!.key).toBe('item-2');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should remove nodes', () => {
|
|
106
|
+
const addOps = buildOps(1, 100, ['App', 'Child'], (s) => [
|
|
107
|
+
...addOp(1, 5, 0, s('App')),
|
|
108
|
+
...addOp(2, 5, 1, s('Child')),
|
|
109
|
+
]);
|
|
110
|
+
tree.applyOperations(addOps);
|
|
111
|
+
expect(tree.getComponentCount()).toBe(2);
|
|
112
|
+
|
|
113
|
+
// Remove ops still need a string table (can be empty)
|
|
114
|
+
const rmOps = [1, 100, 0, ...removeOp(2)];
|
|
115
|
+
tree.applyOperations(rmOps);
|
|
116
|
+
expect(tree.getComponentCount()).toBe(1);
|
|
117
|
+
expect(tree.getNode(1)!.children).toEqual([]);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should reorder children', () => {
|
|
121
|
+
const ops = buildOps(1, 100, ['App', 'A', 'B', 'C'], (s) => [
|
|
122
|
+
...addOp(1, 5, 0, s('App')),
|
|
123
|
+
...addOp(2, 5, 1, s('A')),
|
|
124
|
+
...addOp(3, 5, 1, s('B')),
|
|
125
|
+
...addOp(4, 5, 1, s('C')),
|
|
126
|
+
]);
|
|
127
|
+
tree.applyOperations(ops);
|
|
128
|
+
expect(tree.getNode(1)!.children).toEqual([2, 3, 4]);
|
|
129
|
+
|
|
130
|
+
const reorderOps = [1, 100, 0, ...reorderOp(1, [4, 2, 3])];
|
|
131
|
+
tree.applyOperations(reorderOps);
|
|
132
|
+
expect(tree.getNode(1)!.children).toEqual([4, 2, 3]);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should find by name (partial match)', () => {
|
|
136
|
+
const ops = buildOps(1, 100, ['App', 'UserProfile', 'UserCard', 'Footer'], (s) => [
|
|
137
|
+
...addOp(1, 5, 0, s('App')),
|
|
138
|
+
...addOp(2, 5, 1, s('UserProfile')),
|
|
139
|
+
...addOp(3, 5, 1, s('UserCard')),
|
|
140
|
+
...addOp(4, 5, 1, s('Footer')),
|
|
141
|
+
]);
|
|
142
|
+
tree.applyOperations(ops);
|
|
143
|
+
|
|
144
|
+
const results = tree.findByName('user');
|
|
145
|
+
expect(results).toHaveLength(2);
|
|
146
|
+
expect(results.map((r) => r.displayName).sort()).toEqual([
|
|
147
|
+
'UserCard',
|
|
148
|
+
'UserProfile',
|
|
149
|
+
]);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should find by name (exact match)', () => {
|
|
153
|
+
const ops = buildOps(1, 100, ['App', 'UserProfile', 'UserCard'], (s) => [
|
|
154
|
+
...addOp(1, 5, 0, s('App')),
|
|
155
|
+
...addOp(2, 5, 1, s('UserProfile')),
|
|
156
|
+
...addOp(3, 5, 1, s('UserCard')),
|
|
157
|
+
]);
|
|
158
|
+
tree.applyOperations(ops);
|
|
159
|
+
|
|
160
|
+
const results = tree.findByName('UserProfile', true);
|
|
161
|
+
expect(results).toHaveLength(1);
|
|
162
|
+
expect(results[0].displayName).toBe('UserProfile');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should get count by type', () => {
|
|
166
|
+
const ops = buildOps(1, 100, ['App', 'MemoComp', 'FuncComp', 'div'], (s) => [
|
|
167
|
+
...addOp(1, 5, 0, s('App')), // function
|
|
168
|
+
...addOp(2, 8, 1, s('MemoComp')), // memo
|
|
169
|
+
...addOp(3, 5, 1, s('FuncComp')), // function
|
|
170
|
+
...addOp(4, 7, 1, s('div')), // host
|
|
171
|
+
]);
|
|
172
|
+
tree.applyOperations(ops);
|
|
173
|
+
|
|
174
|
+
const counts = tree.getCountByType();
|
|
175
|
+
expect(counts['function']).toBe(2);
|
|
176
|
+
expect(counts['memo']).toBe(1);
|
|
177
|
+
expect(counts['host']).toBe(1);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should get tree with depth limit', () => {
|
|
181
|
+
const ops = buildOps(1, 100, ['App', 'Level1', 'Level2', 'Level3'], (s) => [
|
|
182
|
+
...addOp(1, 5, 0, s('App')),
|
|
183
|
+
...addOp(2, 5, 1, s('Level1')),
|
|
184
|
+
...addOp(3, 5, 2, s('Level2')),
|
|
185
|
+
...addOp(4, 5, 3, s('Level3')),
|
|
186
|
+
]);
|
|
187
|
+
tree.applyOperations(ops);
|
|
188
|
+
|
|
189
|
+
const fullTree = tree.getTree();
|
|
190
|
+
expect(fullTree).toHaveLength(4);
|
|
191
|
+
|
|
192
|
+
const shallow = tree.getTree(1);
|
|
193
|
+
expect(shallow).toHaveLength(2);
|
|
194
|
+
expect(shallow.map((n) => n.displayName)).toEqual(['App', 'Level1']);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should handle empty operations', () => {
|
|
198
|
+
tree.applyOperations([]);
|
|
199
|
+
expect(tree.getComponentCount()).toBe(0);
|
|
200
|
+
|
|
201
|
+
tree.applyOperations([1]);
|
|
202
|
+
expect(tree.getComponentCount()).toBe(0);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('should handle all element types', () => {
|
|
206
|
+
const ops = buildOps(
|
|
207
|
+
1, 100,
|
|
208
|
+
['ClassComp', 'FuncComp', 'ForwardRefComp', 'HostComp', 'MemoComp', 'ProfilerComp', 'SuspenseComp'],
|
|
209
|
+
(s) => [
|
|
210
|
+
...addOp(1, 1, 0, s('ClassComp')), // class = 1
|
|
211
|
+
...addOp(2, 5, 1, s('FuncComp')), // function = 5
|
|
212
|
+
...addOp(3, 6, 1, s('ForwardRefComp')), // forwardRef = 6
|
|
213
|
+
...addOp(4, 7, 1, s('HostComp')), // host = 7
|
|
214
|
+
...addOp(5, 8, 1, s('MemoComp')), // memo = 8
|
|
215
|
+
...addOp(6, 10, 1, s('ProfilerComp')), // profiler = 10
|
|
216
|
+
...addOp(7, 12, 1, s('SuspenseComp')), // suspense = 12
|
|
217
|
+
],
|
|
218
|
+
);
|
|
219
|
+
tree.applyOperations(ops);
|
|
220
|
+
|
|
221
|
+
expect(tree.getNode(1)!.type).toBe('class');
|
|
222
|
+
expect(tree.getNode(2)!.type).toBe('function');
|
|
223
|
+
expect(tree.getNode(3)!.type).toBe('forwardRef');
|
|
224
|
+
expect(tree.getNode(4)!.type).toBe('host');
|
|
225
|
+
expect(tree.getNode(5)!.type).toBe('memo');
|
|
226
|
+
expect(tree.getNode(6)!.type).toBe('profiler');
|
|
227
|
+
expect(tree.getNode(7)!.type).toBe('suspense');
|
|
228
|
+
});
|
|
229
|
+
});
|