1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
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);
|