import type esbuild from 'esbuild' import type {IncomingMessage, ServerResponse} from 'http' export function createEsbuildLiveReloadTools(): { handleRequest(req: IncomingMessage, res: ServerResponse): boolean hasOpenConnections(): boolean esbuildBanner: esbuild.BuildOptions['banner'] esbuildWatch: esbuild.WatchMode } { const openResponses = new Set() return { handleRequest(req, res) { // If special /esbuild url requested, subscribe clients to changes if (req.url === '/esbuild') { res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', Connection: 'keep-alive', }) res.write('data: open\n\n') openResponses.add(res) res.on('close', () => openResponses.delete(res)) return true // handled } return false }, hasOpenConnections() { return openResponses.size > 0 }, esbuildBanner: { // Below uses function toString to insert raw source code of the function into the JS source. // This is being used so we can at least get a few type completions, but please understand that // you cannot reference any non-global browser values from within the function. js: `;(${function liveReloadClientSetup() { // from packages/playground/devEnv/createEsbuildLiveReloadTools.ts function connect() { try { const es = new EventSource('/esbuild') es.onmessage = (evt) => { switch (evt.data) { case 'reload': location.reload() break case 'open': console.log('%cLive reload ready', 'color: gray') break } } es.onerror = attemptConnect } catch (err) { attemptConnect() } } function attemptConnect() { setTimeout(() => connect(), 1000) } attemptConnect() }.toString()})();`, }, esbuildWatch: { onRebuild(error, res) { if (!error) { if (openResponses.size > 0) { console.error(`Reloading for ${openResponses.size} clients...`) // Notify clients on rebuild openResponses.forEach((res) => res.write('data: reload\n\n')) openResponses.clear() } } else { console.error('Rebuild had errors...') } }, }, } }