-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathclient.mjs
More file actions
120 lines (92 loc) · 2.66 KB
/
client.mjs
File metadata and controls
120 lines (92 loc) · 2.66 KB
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
export function main() {
const url = new URL(import.meta.url)
const clientKey = url.searchParams.get(`key`) || undefined
const delay = 1024
let req
let timer
reinit()
function reinit() {
deinit()
req = new EventSource(new URL(`events`, url))
req.onmessage = onEventSourceMessage
req.onerror = onEventSourceError
}
function deinit() {
if (timer) clearTimeout(timer)
timer = undefined
try {req?.close()}
finally {req = undefined}
}
function onEventSourceError() {
deinit()
timer = setTimeout(reinit, delay)
}
function onEventSourceMessage({data}) {
onMessage(JSON.parse(data || `null`))
}
function onMessage(msg) {
if (!msg) return
const {type, key} = msg
if (!equiv(key, clientKey)) return
if (type === `deinit`) {onDeinit(); return}
if (type === `change` || type === `rename`) onChange(msg)
}
function onDeinit() {deinit()}
function onChange(msg) {
const ext = extName(msg.path)
if (ext === `.css`) {
onStylesheetChanged(msg)
return
}
if (ext === `.map`) {
return
}
window.location.reload()
}
function onStylesheetChanged({path}) {
if (!path) return
path = rootedPath(path)
const prev = findSimilarStylesheets(path)
if (!prev.length) return
const link = document.createElement(`link`)
link.rel = `stylesheet`
link.href = salted(path)
link.onerror = link.remove
link.onload = linkOnLoad
last(prev).insertAdjacentElement(`afterend`, link)
}
function findSimilarStylesheets(pathname) {
return filter(
document.head.querySelectorAll(`link[rel=stylesheet]`),
node => new URL(node.href).pathname === pathname,
)
}
function linkOnLoad() {
this.onerror = null
this.onload = null
const links = findSimilarStylesheets(new URL(this.href).pathname)
for (const node of init(links)) node.remove()
}
function rootedPath(path) {
path = path.replace(/^[/]*/g, ``)
return baseExists() ? path : `/${path}`
}
function baseExists() {
const node = document.head.querySelector(`base`)
return Boolean(node && node.href)
}
function salted(str) {return `${str}?${salt()}`}
function salt() {
return String(Math.random()).replace(/\d*\./, ``).slice(0, 6)
}
function extName(path = ``) {
const match = path.match(/.([.][^.]+)$/)
return !match ? `` : match[1]
}
function equiv(a, b) {
return (a == null && b == null) || Object.is(a, b)
}
function init(list) {return list.slice(0, list.length - 1)}
function last(list) {return list[list.length - 1]}
function filter(list, fun) {return Array.prototype.filter.call(list ?? [], fun)}
}