Experimental rewrite of React DevTools extension for debugging React applications with improved performance and multi-root support
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
The bridge communication system provides efficient message passing between different DevTools contexts (extension background, content script, injected script) with automatic message batching and event-driven architecture.
Main communication bridge that extends EventEmitter for bidirectional message passing with automatic batching.
/**
* Bridge for communication between DevTools contexts
* Automatically batches messages for improved performance
*/
class Bridge extends EventEmitter {
/**
* Create a new bridge instance
* @param wall - Low-level communication wall implementation
*/
constructor(wall: Wall);
/**
* Send a message through the bridge with optional transferable objects
* Messages are automatically batched for performance
* @param event - Event name/type
* @param payload - Data to send
* @param transferable - Optional transferable objects for performance
*/
send(event: string, payload: any, transferable?: Array<any>): void;
}Usage Examples:
import Bridge from "react-devtools-experimental/src/bridge";
// Create wall implementation for your context
const wall = {
listen: (callback) => {
// Set up message listener (e.g., chrome.runtime.onMessage)
chrome.runtime.onMessage.addListener(callback);
},
send: (event, payload, transferable) => {
// Send message (e.g., chrome.runtime.sendMessage)
chrome.runtime.sendMessage({ event, payload });
}
};
// Create bridge
const bridge = new Bridge(wall);
// Listen for events
bridge.on('element-selected', (data) => {
console.log('Element selected:', data);
});
// Send events
bridge.send('inspect-element', { id: 123, rendererID: 1 });Low-level communication abstraction that bridges must implement for their specific context.
interface Wall {
/**
* Set up listener for incoming messages
* @param fn - Callback function to handle messages
*/
listen: (fn: Function) => void;
/**
* Send message through the wall
* @param event - Event name/type
* @param payload - Data to send
* @param transferable - Optional transferable objects
*/
send: (event: string, payload: any, transferable?: Array<any>) => void;
}The bridge automatically batches messages within a time window to reduce communication overhead.
/**
* Message batching configuration
*/
const BATCH_DURATION = 100; // milliseconds
/**
* Internal message structure
*/
interface Message {
event: string;
payload: any;
}Usage Examples:
// Messages sent within 100ms are automatically batched
bridge.send('tree-operation', { type: 'ADD', id: 1 });
bridge.send('tree-operation', { type: 'ADD', id: 2 });
bridge.send('tree-operation', { type: 'ADD', id: 3 });
// All three messages sent as single batched message
// Batching can be bypassed for urgent messages by waiting
bridge.send('urgent-message', { critical: true });
await new Promise(resolve => setTimeout(resolve, 101));
bridge.send('another-message', { data: 'test' });Implementation for Chrome extension communication between background, content script, and injected contexts.
// Background script to content script
const backgroundWall = {
listen: (callback) => {
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
callback(message);
});
},
send: (event, payload) => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
chrome.tabs.sendMessage(tabs[0].id, { event, payload });
});
}
};
// Content script to injected script
const contentWall = {
listen: (callback) => {
window.addEventListener('message', (event) => {
if (event.source === window && event.data.source === 'react-devtools') {
callback(event.data);
}
});
},
send: (event, payload) => {
window.postMessage({
source: 'react-devtools',
event,
payload
}, '*');
}
};Implementation for standalone DevTools using WebSocket communication.
const websocketWall = {
listen: (callback) => {
this.socket.on('message', (message) => {
const data = JSON.parse(message);
callback(data);
});
},
send: (event, payload) => {
this.socket.send(JSON.stringify({ event, payload }));
}
};Implementation for development environments with hot reloading.
const devServerWall = {
listen: (callback) => {
// Set up Server-Sent Events or WebSocket
this.eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
callback(data);
};
},
send: (event, payload) => {
fetch('/devtools-message', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ event, payload })
});
}
};Standard events used throughout the DevTools communication protocol.
// Element inspection events
'inspect-element' // Request element inspection
'element-inspected' // Element inspection result
'select-element' // Select element in tree
'element-selected' // Element selection result
// Tree operations
'tree-operations' // Batch of tree changes
'tree-updated' // Tree update notification
// Profiling events
'start-profiling' // Start performance profiling
'stop-profiling' // Stop performance profiling
'profiling-data' // Profiling results
// State modification events
'override-props' // Modify component props
'override-state' // Modify component state
'override-context' // Modify context values
'override-hooks' // Modify hook state
// DOM inspection events
'start-dom-inspection' // Enter DOM inspection mode
'stop-dom-inspection' // Exit DOM inspection mode
'highlight-element' // Highlight element in DOM
'unhighlight-element' // Remove element highlighting
// Bridge lifecycle events
'bridge-ready' // Bridge initialization complete
'bridge-shutdown' // Bridge shutting downUsage Examples:
// Set up comprehensive event handling
const bridge = new Bridge(wall);
// Element inspection
bridge.on('inspect-element', ({ id, rendererID }) => {
const elementData = inspectElement(id, rendererID);
bridge.send('element-inspected', elementData);
});
// Profiling control
bridge.on('start-profiling', () => {
startProfilingSession();
bridge.send('profiling-started', { timestamp: Date.now() });
});
bridge.on('stop-profiling', () => {
const profilingData = stopProfilingSession();
bridge.send('profiling-data', profilingData);
});
// State modification
bridge.on('override-props', ({ id, path, value, rendererID }) => {
modifyComponentProps(id, path, value, rendererID);
bridge.send('props-overridden', { id, path, value });
});Bridge communication includes error handling and recovery mechanisms.
// Error events
'bridge-error' // Communication error occurred
'message-failed' // Specific message failed to send
'connection-lost' // Connection to other context lost
'connection-restored' // Connection restored
// Recovery events
'retry-message' // Retry failed message
'reconnect-bridge' // Attempt to reconnect
'bridge-reset' // Reset bridge stateUsage Examples:
// Handle bridge errors
bridge.on('bridge-error', (error) => {
console.error('Bridge communication error:', error);
// Attempt recovery
setTimeout(() => {
bridge.send('reconnect-bridge', {});
}, 1000);
});
// Handle connection loss
bridge.on('connection-lost', () => {
console.warn('DevTools connection lost');
// Show disconnected state in UI
showDisconnectedState();
});
bridge.on('connection-restored', () => {
console.log('DevTools connection restored');
// Restore normal operation
hideDisconnectedState();
// Resync data
bridge.send('request-full-sync', {});
});The bridge uses intelligent batching to optimize performance:
// Messages are batched within 100ms windows
// Multiple tree operations become single batch
bridge.send('tree-operation', { type: 'ADD', id: 1, parentID: 0 });
bridge.send('tree-operation', { type: 'ADD', id: 2, parentID: 1 });
bridge.send('tree-operation', { type: 'UPDATE', id: 1, name: 'NewName' });
// Results in single batched message:
// {
// event: 'batched-operations',
// payload: [
// { type: 'ADD', id: 1, parentID: 0 },
// { type: 'ADD', id: 2, parentID: 1 },
// { type: 'UPDATE', id: 1, name: 'NewName' }
// ]
// }Use transferable objects for large data to improve performance:
// Send large profiling data efficiently
const profilingBuffer = new ArrayBuffer(1024 * 1024);
bridge.send('profiling-data', profilingBuffer, [profilingBuffer]);
// Send element tree data
const treeBuffer = serializeElementTree(elementTree);
bridge.send('tree-data', treeBuffer, [treeBuffer]);The bridge includes cleanup mechanisms to prevent memory leaks:
// Clean up bridge resources
bridge.on('shutdown', () => {
// Clear message queues
bridge._messageQueue = [];
// Clear timeouts
if (bridge._timeoutID) {
clearTimeout(bridge._timeoutID);
}
// Remove all listeners
bridge.removeAllListeners();
});