1import assert from 'assert'; 2import { EventEmitter } from 'events'; 3import WebSocket from 'ws'; 4 5let serverInstance: WebSocket.WebSocketServer | null = null; 6 7const eventEmitter = new EventEmitter(); 8 9/** 10 * Private command to support DevTools frontend reload. 11 * 12 * The react-devtools maintains state between frontend(webpage) and backend(app). 13 * If we reload the frontend without reloading the app, the react-devtools will stuck on incorrect state. 14 * We introduce this special reload command. 15 * As long as the frontend reload, we will close app's WebSocket connection and tell app to reconnect again. 16 */ 17const RELOAD_COMMAND = 'Expo::RELOAD'; 18 19/** 20 * Start the react-devtools WebSocket proxy server 21 */ 22export async function startReactDevToolsProxyAsync(options?: { port: number }) { 23 if (serverInstance != null) { 24 return; 25 } 26 27 serverInstance = new WebSocket.WebSocketServer({ port: options?.port ?? 8097 }); 28 29 serverInstance.on('connection', function connection(ws) { 30 ws.on('message', function message(rawData, isBinary) { 31 assert(!isBinary); 32 const data = rawData.toString(); 33 34 if (data === RELOAD_COMMAND) { 35 closeAllOtherClients(ws); 36 eventEmitter.emit(RELOAD_COMMAND); 37 return; 38 } 39 40 serverInstance?.clients.forEach(function each(client) { 41 if (client !== ws && client.readyState === WebSocket.OPEN) { 42 client.send(data, { binary: isBinary }); 43 } 44 }); 45 }); 46 }); 47 48 serverInstance.on('close', function () { 49 serverInstance = null; 50 }); 51} 52 53/** 54 * Close the WebSocket server 55 */ 56export function closeReactDevToolsProxy() { 57 serverInstance?.close(); 58 serverInstance = null; 59} 60 61/** 62 * add event listener from react-devtools frontend reload 63 */ 64export function addReactDevToolsReloadListener(listener: (...args: any[]) => void) { 65 eventEmitter.addListener(RELOAD_COMMAND, listener); 66} 67 68/** 69 * Close all other WebSocket clients other than the current `self` client 70 */ 71function closeAllOtherClients(self: WebSocket.WebSocket) { 72 serverInstance?.clients.forEach(function each(client) { 73 if (client !== self && client.readyState === WebSocket.OPEN) { 74 client.close(); 75 } 76 }); 77} 78