CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-expo

The Expo SDK provides a comprehensive set of libraries and tools for building cross-platform mobile applications using React Native.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

devtools.mddocs/

DevTools System

The Expo DevTools system provides a plugin-based architecture for integrating development tools, debuggers, and monitoring systems into your Expo applications. It enables real-time communication between development tools and running applications through a standardized plugin interface.

Capabilities

DevTools Plugin Client

Core client for connecting to and communicating with DevTools plugins during development.

/**
 * DevTools plugin client for communicating with development tools
 */
class DevToolsPluginClient {
  /**
   * Add a listener for messages from the DevTools plugin
   * @param listener - Function to handle incoming messages
   * @returns Subscription object for removing the listener
   */
  addMessageListener(listener: (message: any) => void): EventSubscription;

  /**
   * Add a listener for messages that only fires once
   * @param listener - Function to handle incoming message
   */
  addMessageListenerOnce(listener: (message: any) => void): void;

  /**
   * Send a message to the connected DevTools plugin
   * @param message - Message to send to the plugin
   */
  sendMessage(message: any): void;

  /**
   * Close the connection to the DevTools plugin
   * @returns Promise that resolves when connection is closed
   */
  closeAsync(): Promise<void>;

  /**
   * Check if the client is currently connected to a plugin
   * @returns True if connected, false otherwise
   */
  isConnected(): boolean;
}

Plugin Client Factory

Factory function for creating DevTools plugin clients with configuration options.

/**
 * Creates a DevTools plugin client for the specified plugin
 * @param pluginName - Name of the DevTools plugin to connect to
 * @param options - Optional configuration for the client connection
 * @returns Promise resolving to the connected plugin client
 */
function getDevToolsPluginClientAsync(
  pluginName: string,
  options?: DevToolsPluginClientOptions
): Promise<DevToolsPluginClient>;

/**
 * React hook to get the DevToolsPluginClient instance
 * @param pluginName - Name of the plugin to connect to
 * @param options - Optional configuration for the client
 * @returns DevToolsPluginClient instance or null if not connected
 */
function useDevToolsPluginClient(
  pluginName: string,
  options?: DevToolsPluginClientOptions
): DevToolsPluginClient | null;

/**
 * Enable or disable logging for DevTools
 * @param enabled - Whether to enable logging
 */
function setEnableLogging(enabled: boolean): void;

/**
 * Configuration options for DevTools plugin clients
 */
interface DevToolsPluginClientOptions {
  /** The underlying WebSocket binaryType ('arraybuffer' or 'blob') */
  websocketBinaryType?: 'arraybuffer' | 'blob';
}

React Hook Integration

React hook for managing DevTools plugin connections within React components.

/**
 * React hook for managing a DevTools plugin client connection
 * @param pluginName - Name of the DevTools plugin to connect to
 * @param options - Optional configuration for the client connection  
 * @returns The connected plugin client or null if not connected
 */
function useDevToolsPluginClient(
  pluginName: string,
  options?: DevToolsPluginClientOptions
): DevToolsPluginClient | null;

Logging Configuration

Control logging behavior for the DevTools system.

/**
 * Enable or disable logging for the DevTools system
 * @param enable - Whether to enable detailed logging
 */
function setEnableLogging(enable: boolean): void;

Usage Examples

Basic Plugin Connection

import { getDevToolsPluginClientAsync } from 'expo/devtools';

async function connectToDebugger() {
  try {
    const client = await getDevToolsPluginClientAsync('debugger', {
      websocketBinaryType: 'arraybuffer',
    });

    // Listen for messages from the debugger plugin
    const subscription = client.addMessageListener('breakpoint', (params) => {
      console.log('Debugger breakpoint:', params);
      handleBreakpoint(params);
    });

    // Send debugging information to the plugin
    client.sendMessage('state_update', {
      timestamp: Date.now(),
      data: { userId: 123, screen: 'Profile' },
    });

    return { client, subscription };
  } catch (error) {
    console.error('Failed to connect to debugger plugin:', error);
    return null;
  }
}

React Hook Usage

import React, { useEffect } from 'react';
import { useDevToolsPluginClient } from 'expo/devtools';

function DebuggingComponent() {
  const debugClient = useDevToolsPluginClient('performance-monitor', {
    autoReconnect: true,
  });

  useEffect(() => {
    if (debugClient?.isConnected()) {
      // Set up message listener
      const subscription = debugClient.addMessageListener((message) => {
        if (message.type === 'performance_request') {
          // Respond with performance metrics
          debugClient.sendMessage({
            type: 'performance_data',
            metrics: {
              renderTime: performance.now(),
              memoryUsage: performance.memory?.usedJSHeapSize,
            },
          });
        }
      });

      return () => subscription.remove();
    }
  }, [debugClient]);

  return null; // This is a debugging component
}

Custom DevTools Plugin

import { getDevToolsPluginClientAsync, setEnableLogging } from 'expo/devtools';

class CustomDevToolsPlugin {
  private client: DevToolsPluginClient | null = null;
  private messageSubscription: EventSubscription | null = null;

  async initialize(pluginName: string) {
    // Enable detailed logging for development
    setEnableLogging(__DEV__);

    try {
      this.client = await getDevToolsPluginClientAsync(pluginName, {
        requestInterceptor: (request) => {
          // Log all outgoing requests
          console.log('DevTools request:', request);
          return request;
        },
        responseInterceptor: (response) => {
          // Log all incoming responses
          console.log('DevTools response:', response);
          return response;
        },
      });

      this.messageSubscription = this.client.addMessageListener(
        this.handleMessage.bind(this)
      );

      console.log('DevTools plugin initialized successfully');
    } catch (error) {
      console.error('Failed to initialize DevTools plugin:', error);
    }
  }

  private handleMessage(message: any) {
    switch (message.type) {
      case 'inspect_component':
        this.sendComponentInfo(message.componentId);
        break;
      case 'update_props':
        this.updateComponentProps(message.componentId, message.props);
        break;
      case 'reload_request':
        this.handleReloadRequest();
        break;
      default:
        console.warn('Unknown DevTools message type:', message.type);
    }
  }

  sendComponentInfo(componentId: string) {
    if (this.client?.isConnected()) {
      this.client.sendMessage({
        type: 'component_info',
        componentId,
        data: {
          props: getComponentProps(componentId),
          state: getComponentState(componentId),
          children: getComponentChildren(componentId),
        },
      });
    }
  }

  async cleanup() {
    if (this.messageSubscription) {
      this.messageSubscription.remove();
      this.messageSubscription = null;
    }

    if (this.client) {
      await this.client.closeAsync();
      this.client = null;
    }
  }
}

Network Debugging Plugin

import { useDevToolsPluginClient } from 'expo/devtools';

function NetworkDebugger() {
  const networkClient = useDevToolsPluginClient('network-debugger');

  // Intercept fetch requests for debugging
  const originalFetch = global.fetch;
  global.fetch = async (input, init) => {
    const startTime = Date.now();
    
    // Log request to DevTools
    networkClient?.sendMessage({
      type: 'network_request',
      method: init?.method || 'GET',
      url: input.toString(),
      headers: init?.headers,
      timestamp: startTime,
    });

    try {
      const response = await originalFetch(input, init);
      const endTime = Date.now();

      // Log response to DevTools
      networkClient?.sendMessage({
        type: 'network_response',
        url: input.toString(),
        status: response.status,
        statusText: response.statusText,
        duration: endTime - startTime,
        timestamp: endTime,
      });

      return response;
    } catch (error) {
      // Log error to DevTools
      networkClient?.sendMessage({
        type: 'network_error',
        url: input.toString(),
        error: error.message,
        timestamp: Date.now(),
      });
      throw error;
    }
  };

  return null;
}

Plugin Types and Messages

Common Message Types

The DevTools system supports various message types for different debugging scenarios:

// Performance monitoring
interface PerformanceMessage {
  type: 'performance_data';
  metrics: {
    renderTime: number;
    memoryUsage?: number;
    bundleSize?: number;
  };
}

// Component inspection
interface ComponentMessage {
  type: 'component_info';
  componentId: string;
  data: {
    props: Record<string, any>;
    state: Record<string, any>;
    children: string[];
  };
}

// Network debugging
interface NetworkMessage {
  type: 'network_request' | 'network_response' | 'network_error';
  url: string;
  method?: string;
  status?: number;
  duration?: number;
  error?: string;
}

// State management
interface StateMessage {
  type: 'state_update';
  store: string;
  action: {
    type: string;
    payload: any;
  };
  newState: any;
}

Error Handling

Connection Errors

import { getDevToolsPluginClientAsync } from 'expo/devtools';

async function connectWithErrorHandling() {
  try {
    const client = await getDevToolsPluginClientAsync('my-plugin', {
      connectionTimeout: 3000,
    });
    
    if (!client.isConnected()) {
      throw new Error('Plugin client is not connected');
    }
    
    return client;
  } catch (error) {
    if (error.message.includes('timeout')) {
      console.warn('DevTools plugin connection timed out');
    } else if (error.message.includes('not found')) {
      console.warn('DevTools plugin not available');
    } else {
      console.error('Unexpected DevTools error:', error);
    }
    return null;
  }
}

Message Handling Errors

function setupRobustMessageHandling(client: DevToolsPluginClient) {
  const subscription = client.addMessageListener((message) => {
    try {
      // Validate message structure
      if (!message || typeof message !== 'object') {
        console.warn('Invalid DevTools message received:', message);
        return;
      }

      // Handle message based on type
      handleDevToolsMessage(message);
    } catch (error) {
      console.error('Error handling DevTools message:', error);
      
      // Send error back to plugin
      client.sendMessage({
        type: 'error',
        originalMessage: message,
        error: error.message,
      });
    }
  });

  return subscription;
}

Development vs Production

Conditional Loading

import { getDevToolsPluginClientAsync } from 'expo/devtools';

// Only enable DevTools in development
const client = __DEV__ 
  ? await getDevToolsPluginClientAsync('debugger')
  : null;

if (client) {
  // Development-only debugging code
  setupDevelopmentDebugging(client);
}

Production Safety

// Safe DevTools usage that won't break production
function safeDevToolsCall(callback: (client: DevToolsPluginClient) => void) {
  if (__DEV__ && window.__EXPO_DEVTOOLS_CLIENT__) {
    try {
      callback(window.__EXPO_DEVTOOLS_CLIENT__);
    } catch (error) {
      console.warn('DevTools operation failed:', error);
    }
  }
}

docs

devtools.md

dom-components.md

index.md

native-modules.md

utilities.md

winter-polyfills.md

tile.json