CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-preact

Fast 3kb React-compatible Virtual DOM library.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

devtools.mddocs/

Development Tools

Development utilities, debugging helpers, and React DevTools integration for enhanced developer experience. These tools help with debugging, profiling, and understanding component behavior during development.

Capabilities

Debug Module Utilities

Development utilities for debugging and inspecting component behavior.

/**
 * Resets the history of prop warnings
 * Useful for clearing accumulated warnings in development
 */
function resetPropWarnings(): void;

/**
 * Gets the currently rendering VNode
 * @returns The VNode currently being processed or null
 */
function getCurrentVNode(): VNode | null;

/**
 * Gets the display name of a component from a VNode
 * @param vnode - VNode to extract display name from
 * @returns Component display name or element type
 */
function getDisplayName(vnode: VNode): string;

/**
 * Gets the component stack trace for debugging
 * @param vnode - VNode to get stack trace for
 * @returns String representation of component hierarchy
 */
function getOwnerStack(vnode: VNode): string;

Usage Examples:

import { 
  resetPropWarnings, 
  getCurrentVNode, 
  getDisplayName, 
  getOwnerStack,
  createElement 
} from "preact/debug";

// Development debugging helper
function DebugInfo() {
  const handleDebugClick = () => {
    const currentVNode = getCurrentVNode();
    
    if (currentVNode) {
      console.log("Current VNode:", currentVNode);
      console.log("Display Name:", getDisplayName(currentVNode));
      console.log("Owner Stack:", getOwnerStack(currentVNode));
    } else {
      console.log("No VNode currently rendering");
    }
  };
  
  const clearWarnings = () => {
    resetPropWarnings();
    console.log("Prop warnings cleared");
  };
  
  return createElement("div", { className: "debug-panel" },
    createElement("h3", null, "Debug Tools"),
    createElement("button", { onClick: handleDebugClick }, "Log Current VNode"),
    createElement("button", { onClick: clearWarnings }, "Clear Prop Warnings")
  );
}

// Custom hook for component debugging
function useComponentDebug(componentName: string) {
  useEffect(() => {
    console.log(`${componentName} mounted`);
    
    return () => {
      console.log(`${componentName} unmounted`);
    };
  }, [componentName]);
  
  useEffect(() => {
    const currentVNode = getCurrentVNode();
    if (currentVNode) {
      console.log(`${componentName} rendered:`, {
        displayName: getDisplayName(currentVNode),
        ownerStack: getOwnerStack(currentVNode)
      });
    }
  });
}

// Component with debug information
function DebuggableComponent({ data }: { data: any }) {
  useComponentDebug('DebuggableComponent');
  
  return createElement("div", null,
    createElement("h4", null, "Debuggable Component"),
    createElement("pre", null, JSON.stringify(data, null, 2))
  );
}

DevTools Integration

React DevTools integration for component inspection and profiling.

/**
 * Adds a custom name to a hook value for React DevTools display
 * @param value - The hook value to name
 * @param name - Custom name to display in DevTools
 * @returns The original value unchanged
 */
function addHookName<T>(value: T, name: string): T;

Usage Examples:

import { addHookName } from "preact/devtools";
import { useState, useEffect, useMemo } from "preact/hooks";

// Custom hook with DevTools naming
function useCounter(initialValue = 0, step = 1) {
  const [count, setCount] = useState(initialValue);
  
  const increment = () => setCount(c => c + step);
  const decrement = () => setCount(c => c - step);
  const reset = () => setCount(initialValue);
  
  // These will show up with custom names in React DevTools
  return {
    count: addHookName(count, 'Count'),
    increment: addHookName(increment, 'Increment'),
    decrement: addHookName(decrement, 'Decrement'),
    reset: addHookName(reset, 'Reset')
  };
}

// Complex custom hook with multiple named values
function useApiData<T>(url: string) {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);
  
  useEffect(() => {
    let cancelled = false;
    
    const fetchData = async () => {
      try {
        setLoading(true);
        setError(null);
        
        const response = await fetch(url);
        if (!response.ok) {
          throw new Error(`HTTP ${response.status}: ${response.statusText}`);
        }
        
        const result = await response.json();
        
        if (!cancelled) {
          setData(result);
          setLoading(false);
        }
      } catch (err) {
        if (!cancelled) {
          setError(err instanceof Error ? err : new Error(String(err)));
          setLoading(false);
        }
      }
    };
    
    fetchData();
    
    return () => {
      cancelled = true;
    };
  }, [url]);
  
  const refetch = () => {
    setData(null);
    setLoading(true);
    setError(null);
  };
  
  // Named values for better DevTools experience
  return {
    data: addHookName(data, 'API Data'),
    loading: addHookName(loading, 'Loading State'),
    error: addHookName(error, 'Error State'),
    refetch: addHookName(refetch, 'Refetch Function')
  };
}

// Component using named hooks
function UserProfile({ userId }: { userId: number }) {
  const { data: user, loading, error, refetch } = useApiData<User>(`/api/users/${userId}`);
  const counter = useCounter(0, 1);
  
  if (loading) return createElement("div", null, "Loading...");
  if (error) return createElement("div", null, `Error: ${error.message}`);
  if (!user) return createElement("div", null, "User not found");
  
  return createElement("div", null,
    createElement("h2", null, user.name),
    createElement("p", null, user.email),
    createElement("div", null,
      createElement("p", null, `Counter: ${counter.count}`),
      createElement("button", { onClick: counter.increment }, "+"),
      createElement("button", { onClick: counter.decrement }, "-"),
      createElement("button", { onClick: counter.reset }, "Reset")
    ),
    createElement("button", { onClick: refetch }, "Refetch User")
  );
}

Global Options and Hooks

Global configuration options for development and debugging.

/**
 * Global options object for customizing Preact behavior
 */
interface Options {
  /** Hook invoked when a VNode is created */
  vnode?(vnode: VNode): void;
  
  /** Hook invoked before a VNode is unmounted */
  unmount?(vnode: VNode): void;
  
  /** Hook invoked after a VNode has been rendered/updated */
  diffed?(vnode: VNode): void;
  
  /** Event processing hook */
  event?(e: Event): any;
  
  /** Custom requestAnimationFrame implementation */
  requestAnimationFrame?(callback: () => void): void;
  
  /** Custom render batching function */
  debounceRendering?(cb: () => void): void;
  
  /** Custom debug value formatter */
  useDebugValue?(value: string | number): void;
  
  /** Internal hook name tracking */
  _addHookName?(name: string | number): void;
  
  /** Suspense resolution hook */
  __suspenseDidResolve?(vnode: VNode, cb: () => void): void;
  
  /** Custom attribute serialization for precompiled JSX */
  attr?(name: string, value: any): string | void;
}

/**
 * Global options object - can be modified to customize Preact behavior
 */
const options: Options;

Usage Examples:

import { options } from "preact";
import { createElement } from "preact";

// Development logging setup
if (process.env.NODE_ENV === 'development') {
  // Log all VNode creations
  const originalVnode = options.vnode;
  options.vnode = (vnode) => {
    console.log('VNode created:', vnode.type, vnode.props);
    originalVnode?.(vnode);
  };
  
  // Log component unmounts
  const originalUnmount = options.unmount;
  options.unmount = (vnode) => {
    console.log('VNode unmounting:', vnode.type);
    originalUnmount?.(vnode);
  };
  
  // Log render completions
  const originalDiffed = options.diffed;
  options.diffed = (vnode) => {
    console.log('VNode diffed:', vnode.type);
    originalDiffed?.(vnode);
  };
  
  // Custom debug value formatting
  options.useDebugValue = (value) => {
    if (typeof value === 'object') {
      return JSON.stringify(value, null, 2);
    }
    return String(value);
  };
}

// Performance monitoring
function setupPerformanceMonitoring() {
  const renderTimes = new Map<string, number>();
  
  const originalVnode = options.vnode;
  options.vnode = (vnode) => {
    if (typeof vnode.type === 'function') {
      const componentName = vnode.type.displayName || vnode.type.name || 'Anonymous';
      renderTimes.set(componentName, performance.now());
    }
    originalVnode?.(vnode);
  };
  
  const originalDiffed = options.diffed;
  options.diffed = (vnode) => {
    if (typeof vnode.type === 'function') {
      const componentName = vnode.type.displayName || vnode.type.name || 'Anonymous';
      const startTime = renderTimes.get(componentName);
      
      if (startTime) {
        const renderTime = performance.now() - startTime;
        console.log(`${componentName} rendered in ${renderTime.toFixed(2)}ms`);
        renderTimes.delete(componentName);
      }
    }
    originalDiffed?.(vnode);
  };
}

// Custom event handling
function setupEventLogging() {
  const originalEvent = options.event;
  options.event = (e) => {
    // Log all events in development
    if (process.env.NODE_ENV === 'development') {
      console.log('Event:', e.type, e.target);
    }
    
    // Call original event handler if it exists
    return originalEvent?.(e);
  };
}

// Custom render batching for testing
function setupTestBatching() {
  options.debounceRendering = (callback) => {
    // Immediate rendering for tests
    callback();
  };
}

// Component that uses global options
function DebuggedApp() {
  useEffect(() => {
    if (process.env.NODE_ENV === 'development') {
      setupPerformanceMonitoring();
      setupEventLogging();
    }
  }, []);
  
  return createElement("div", null,
    createElement("h1", null, "Debugged Application"),
    createElement("button", { 
      onClick: () => console.log("Button clicked") 
    }, "Test Button")
  );
}

Development Environment Setup

Utilities and patterns for setting up effective development environments.

Usage Examples:

// Development environment configuration
function setupDevelopmentEnvironment() {
  // Auto-import debug module in development
  if (process.env.NODE_ENV === 'development') {
    import('preact/debug').then(() => {
      console.log('Preact debug mode enabled');
    });
    
    // Import devtools integration
    import('preact/devtools').then(() => {
      console.log('React DevTools integration enabled');
    });
  }
}

// Error boundary with debug information
class DebugErrorBoundary extends Component<
  { children: ComponentChildren; fallback?: ComponentChildren },
  { hasError: boolean; error?: Error }
> {
  state = { hasError: false };
  
  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error };
  }
  
  componentDidCatch(error: Error, errorInfo: any) {
    console.error('Error caught by boundary:', error);
    console.error('Error info:', errorInfo);
    
    if (process.env.NODE_ENV === 'development') {
      const currentVNode = getCurrentVNode();
      if (currentVNode) {
        console.error('Error in component:', getDisplayName(currentVNode));
        console.error('Component stack:', getOwnerStack(currentVNode));
      }
    }
  }
  
  render() {
    if (this.state.hasError) {
      return this.props.fallback || createElement("div", null,
        createElement("h2", null, "Something went wrong"),
        process.env.NODE_ENV === 'development' && 
          createElement("pre", null, this.state.error?.stack)
      );
    }
    
    return this.props.children;
  }
}

// Development-only component inspector
function ComponentInspector({ children }: { children: ComponentChildren }) {
  if (process.env.NODE_ENV !== 'development') {
    return children;
  }
  
  return createElement("dev-tools", {
    style: { position: 'relative' }
  },
    children,
    createElement(DebugInfo)
  );
}

Install with Tessl CLI

npx tessl i tessl/npm-preact

docs

compat.md

components.md

context.md

core.md

devtools.md

hooks.md

index.md

jsx-runtime.md

testing.md

tile.json