const WS_RETRY_MS = 1_000; function refetchCSS(file) { const node = Array.from(document.head.childNodes).find( (n) => n.href && n.href.includes(file), ); if (!node) { console.warn("Could not find node", file); console.log(Array.from(document.head.childNodes)); return; } const url = new URL(node.href); url.searchParams.set("v", parseInt(url.searchParams.get("v") || "0") + 1); const n2 = node.cloneNode(); n2.href = url.toString(); document.head.appendChild(n2); setTimeout(() => { document.head.removeChild(node); }, 100); } let __file_to_module = {}; function refetchJS(file) { if (file in __file_to_module) { const o = __file_to_module[file]; if ("__cleanup" in o) { o.__cleanup(); } else { console.warn("no cleanup"); } } import(`/static/${file}?v=${Date.now()}`) .then((m) => { __file_to_module[file] = m; }) .catch((e) => { console.error(e); }); return; } function connect() { console.log("try to connect through websocket"); const ws = new WebSocket(`ws://${window.location.host}/dev-hr/ws`); const file2timeout = new Map(); ws.addEventListener("message", (msg) => { const file = msg.data; // the path to a changed file relative to `static/`. if (file2timeout.has(file)) clearTimeout(file2timeout.get(file)); file2timeout.set( file, setTimeout(() => { if (file.slice(-4) === ".css") refetchCSS(file); else if (file.slice(-3) === ".js") refetchJS(file); else console.warn("unknown file extension", file); }, 100), ); }); ws.addEventListener("open", () => { console.log("websocket connected"); }); ws.addEventListener("close", () => { console.log("websocket disconnected"); setTimeout(() => { try { connect(); } catch (e) { console.error(e); } }, WS_RETRY_MS); }); ws.addEventListener("error", (e) => { console.error(e); ws.close(); }); } setTimeout(() => connect(), WS_RETRY_MS);