@tachui/devtools
Version:
Development & debugging tools for tachUI framework
526 lines (525 loc) • 14.4 kB
JavaScript
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