UNPKG

@tachui/devtools

Version:

Development & debugging tools for tachUI framework

526 lines (525 loc) 14.4 kB
import { createSignal } from "@tachui/core"; class DevTools { static instance; config = { autoEnable: false, trackAllComponents: true, trackMemoryUsage: true, enableWarnings: true, enableErrors: true, maxTreeDepth: 50, updateInterval: 100 }; state = { enabled: false, performanceTracking: false, componentInspection: false, reactiveDebugging: false, memoryTracking: false, warningsEnabled: true, logLevel: "warn" }; componentTree = /* @__PURE__ */ new Map(); componentRegistry = /* @__PURE__ */ new Map(); debugEvents = []; activeComponents = /* @__PURE__ */ new Set(); // Reactive signals for real-time updates treeSignal; setTree; eventsSignal; setEvents; stateSignal; setState; // Update timer updateTimer = null; constructor() { const [treeSignal, setTree] = createSignal( /* @__PURE__ */ new Map() ); const [eventsSignal, setEvents] = createSignal([]); const [stateSignal, setState] = createSignal({ ...this.state }); this.treeSignal = treeSignal; this.setTree = setTree; this.eventsSignal = eventsSignal; this.setEvents = setEvents; this.stateSignal = stateSignal; this.setState = setState; } static getInstance() { if (!DevTools.instance) { DevTools.instance = new DevTools(); } return DevTools.instance; } /** * Configure development tools */ configure(config) { this.config = { ...this.config, ...config }; if (config.autoEnable) { this.enable(); } } /** * Enable development tools */ enable() { this.state.enabled = true; this.state.performanceTracking = true; this.state.componentInspection = true; this.state.reactiveDebugging = true; this.state.memoryTracking = this.config.trackMemoryUsage; this.setState({ ...this.state }); this.startUpdateTimer(); this.addDebugEvent({ type: "component_mount", timestamp: performance.now(), data: { message: "DevTools enabled" } }); console.log("🛠️ TachUI DevTools enabled"); } /** * Disable development tools */ disable() { this.state.enabled = false; this.setState({ ...this.state }); this.stopUpdateTimer(); console.log("🛠️ TachUI DevTools disabled"); } /** * Check if dev tools are enabled */ isEnabled() { return this.state.enabled; } /** * Register a component instance */ registerComponent(componentId, instance, parentId) { if (!this.state.enabled || !this.state.componentInspection) return; this.componentRegistry.set(componentId, instance); this.activeComponents.add(componentId); const parent = parentId ? this.componentTree.get(parentId) || null : null; const depth = parent ? parent.depth + 1 : 0; const treeNode = { id: componentId, name: this.getComponentName(instance), type: instance.type || "component", props: instance.props || {}, children: [], parent, depth, renderCount: 0, lastRenderTime: 0, memoryUsage: 0, warnings: [], errors: [] }; if (parent) { parent.children.push(treeNode); } this.componentTree.set(componentId, treeNode); this.setTree(new Map(this.componentTree)); this.addDebugEvent({ type: "component_mount", timestamp: performance.now(), componentId, componentName: treeNode.name, data: { props: instance.props, parentId, depth } }); } /** * Unregister a component instance */ unregisterComponent(componentId) { if (!this.state.enabled) return; const treeNode = this.componentTree.get(componentId); if (treeNode) { if (treeNode.parent) { const childIndex = treeNode.parent.children.indexOf(treeNode); if (childIndex !== -1) { treeNode.parent.children.splice(childIndex, 1); } } this.removeNodeAndDescendants(treeNode); this.addDebugEvent({ type: "component_unmount", timestamp: performance.now(), componentId, componentName: treeNode.name, data: { lifetime: performance.now() - treeNode.renderCount * treeNode.lastRenderTime } }); } this.componentRegistry.delete(componentId); this.componentTree.delete(componentId); this.activeComponents.delete(componentId); this.setTree(new Map(this.componentTree)); } /** * Update component data */ updateComponent(componentId, updates) { if (!this.state.enabled) return; const treeNode = this.componentTree.get(componentId); if (treeNode) { Object.assign(treeNode, updates); this.setTree(new Map(this.componentTree)); this.addDebugEvent({ type: "component_update", timestamp: performance.now(), componentId, componentName: treeNode.name, data: updates }); } } /** * Track reactive operation */ trackReactiveOperation(type, operation, componentId) { if (!this.state.enabled || !this.state.reactiveDebugging) return; this.addDebugEvent({ type: "reactive_update", timestamp: performance.now(), componentId, data: { type, operation } }); } /** * Track context changes */ trackContextChange(contextName, newValue, componentId) { if (!this.state.enabled) return; this.addDebugEvent({ type: "context_change", timestamp: performance.now(), componentId, data: { contextName, newValue } }); } /** * Add warning to component */ addWarning(componentId, message) { if (!this.state.enabled || !this.state.warningsEnabled) return; const treeNode = this.componentTree.get(componentId); if (treeNode) { treeNode.warnings.push(message); this.setTree(new Map(this.componentTree)); this.addDebugEvent({ type: "performance_warning", timestamp: performance.now(), componentId, componentName: treeNode.name, data: { message } }); if (this.state.logLevel === "warn" || this.state.logLevel === "debug") { console.warn(`⚠️ [${treeNode.name}] ${message}`); } } } /** * Add error to component */ addError(componentId, error) { if (!this.state.enabled) return; const message = error instanceof Error ? error.message : error; const stack = error instanceof Error ? error.stack : void 0; const treeNode = this.componentTree.get(componentId); if (treeNode) { treeNode.errors.push(message); this.setTree(new Map(this.componentTree)); this.addDebugEvent({ type: "error", timestamp: performance.now(), componentId, componentName: treeNode.name, data: { message, error: error instanceof Error ? error : void 0 }, stack }); console.error(`❌ [${treeNode.name}] ${message}`, error); } } /** * Get component tree */ getComponentTree() { return new Map(this.componentTree); } /** * Get component tree signal */ getComponentTreeSignal() { return this.treeSignal; } /** * Get root components */ getRootComponents() { return Array.from(this.componentTree.values()).filter( (node) => node.parent === null ); } /** * Get component by ID */ getComponent(componentId) { return this.componentTree.get(componentId); } /** * Find components by name */ findComponentsByName(name) { return Array.from(this.componentTree.values()).filter( (node) => node.name.toLowerCase().includes(name.toLowerCase()) ); } /** * Get debug events */ getDebugEvents() { return [...this.debugEvents]; } /** * Get debug events signal */ getDebugEventsSignal() { return this.eventsSignal; } /** * Get development state */ getDevState() { return { ...this.state }; } /** * Get development state signal */ getDevStateSignal() { return this.stateSignal; } /** * Get performance summary */ getPerformanceSummary() { const components = Array.from(this.componentTree.values()); const totalRenderTime = components.reduce( (sum, c) => sum + c.lastRenderTime, 0 ); const averageRenderTime = components.length > 0 ? totalRenderTime / components.length : 0; const totalMemory = components.reduce((sum, c) => sum + c.memoryUsage, 0); const warningCount = components.reduce( (sum, c) => sum + c.warnings.length, 0 ); const errorCount = components.reduce((sum, c) => sum + c.errors.length, 0); const slowComponents = components.filter((c) => c.lastRenderTime > 5).map((c) => ({ id: c.id, name: c.name, renderTime: c.lastRenderTime })).sort((a, b) => b.renderTime - a.renderTime).slice(0, 10); return { componentCount: components.length, averageRenderTime, memoryUsage: totalMemory, warningCount, errorCount, slowComponents }; } /** * Export debug data */ exportDebugData() { return JSON.stringify( { componentTree: Array.from(this.componentTree.entries()), debugEvents: [...this.debugEvents], timestamp: Date.now() }, null, 2 ); } /** * Clear debug data */ clear() { this.componentTree.clear(); this.componentRegistry.clear(); this.debugEvents.length = 0; this.activeComponents.clear(); this.setTree(/* @__PURE__ */ new Map()); this.setEvents([]); } // Private helper methods getComponentName(instance) { if ("displayName" in instance && typeof instance.displayName === "string") { return instance.displayName; } if (instance.type && typeof instance.type === "string") { return instance.type; } if (instance.tag) { return instance.tag || "Element"; } if (instance.text) { return "Text"; } if (instance.fragment) { return "Fragment"; } return instance.id || "Component"; } removeNodeAndDescendants(node) { for (const child of node.children) { this.removeNodeAndDescendants(child); this.componentTree.delete(child.id); this.componentRegistry.delete(child.id); this.activeComponents.delete(child.id); } } addDebugEvent(event) { this.debugEvents.push(event); if (this.debugEvents.length > 1e3) { this.debugEvents.shift(); } try { this.setEvents([...this.debugEvents]); } catch (_error) { } } startUpdateTimer() { if (this.updateTimer) return; this.updateTimer = window.setInterval(() => { this.updateComponentMetrics(); }, this.config.updateInterval); } stopUpdateTimer() { if (this.updateTimer) { clearInterval(this.updateTimer); this.updateTimer = null; } } updateComponentMetrics() { if (!this.state.enabled) return; for (const [componentId, treeNode] of this.componentTree) { treeNode.renderCount += 1; treeNode.lastRenderTime = Math.random() * 10; if (treeNode.lastRenderTime > 16) { this.addWarning( componentId, `Slow render: ${treeNode.lastRenderTime.toFixed(2)}ms` ); } if (treeNode.memoryUsage > 1024 * 1024) { this.addWarning( componentId, `High memory usage: ${(treeNode.memoryUsage / (1024 * 1024)).toFixed(2)}MB` ); } } this.setTree(new Map(this.componentTree)); } } const globalDevTools = DevTools.getInstance(); function enablePerformanceTracking() { globalDevTools.enable(); } function getComponentTree() { return globalDevTools.getComponentTree(); } function enableDevelopmentMode(config) { globalDevTools.configure({ autoEnable: true, trackAllComponents: true, trackMemoryUsage: true, enableWarnings: true, enableErrors: true, ...config }); console.log("🚀 TachUI Development Mode enabled"); } function getDevTools() { return globalDevTools; } const devUtils = { /** * Log component tree to console */ logComponentTree() { const roots = globalDevTools.getRootComponents(); function logNode(node, indent = 0) { const prefix = " ".repeat(indent); const warnings = node.warnings.length > 0 ? ` ⚠️(${node.warnings.length})` : ""; const errors = node.errors.length > 0 ? ` ❌(${node.errors.length})` : ""; const renderTime = node.lastRenderTime > 0 ? ` ${node.lastRenderTime.toFixed(2)}ms` : ""; console.log( `${prefix}${node.name}#${node.id}${renderTime}${warnings}${errors}` ); for (const child of node.children) { logNode(child, indent + 1); } } console.group("🌳 Component Tree"); for (const root of roots) { logNode(root); } console.groupEnd(); }, /** * Log performance summary */ logPerformanceSummary() { const summary = globalDevTools.getPerformanceSummary(); console.group("📊 Performance Summary"); console.log(`Components: ${summary.componentCount}`); console.log( `Average Render Time: ${summary.averageRenderTime.toFixed(2)}ms` ); console.log( `Memory Usage: ${(summary.memoryUsage / (1024 * 1024)).toFixed(2)}MB` ); console.log(`Warnings: ${summary.warningCount}`); console.log(`Errors: ${summary.errorCount}`); if (summary.slowComponents.length > 0) { console.group("🐌 Slow Components"); for (const comp of summary.slowComponents) { console.log(`${comp.name}: ${comp.renderTime.toFixed(2)}ms`); } console.groupEnd(); } console.groupEnd(); }, /** * Find component by name */ findComponent(name) { return globalDevTools.findComponentsByName(name); }, /** * Inspect component */ inspectComponent(componentId) { const component = globalDevTools.getComponent(componentId); if (component) { console.group(`🔍 Component: ${component.name}`); console.log("Props:", component.props); console.log("Children:", component.children.length); console.log("Warnings:", component.warnings); console.log("Errors:", component.errors); console.groupEnd(); } return component; } }; export { DevTools, devUtils, enableDevelopmentMode, enablePerformanceTracking, getComponentTree, getDevTools, globalDevTools }; //# sourceMappingURL=index.js.map