@vxrn/vite-native-client 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/cjs/client.js +276 -0
- package/dist/cjs/client.js.map +6 -0
- package/dist/cjs/client.native.js +277 -0
- package/dist/cjs/client.native.js.map +6 -0
- package/dist/cjs/customEvent.js +14 -0
- package/dist/cjs/customEvent.js.map +6 -0
- package/dist/cjs/customEvent.native.js +15 -0
- package/dist/cjs/customEvent.native.js.map +6 -0
- package/dist/cjs/hmrPayload.js +14 -0
- package/dist/cjs/hmrPayload.js.map +6 -0
- package/dist/cjs/hmrPayload.native.js +15 -0
- package/dist/cjs/hmrPayload.native.js.map +6 -0
- package/dist/cjs/hot.js +14 -0
- package/dist/cjs/hot.js.map +6 -0
- package/dist/cjs/hot.native.js +15 -0
- package/dist/cjs/hot.native.js.map +6 -0
- package/dist/cjs/index.js +19 -0
- package/dist/cjs/index.js.map +6 -0
- package/dist/cjs/index.native.js +20 -0
- package/dist/cjs/index.native.js.map +6 -0
- package/dist/esm/client.js +276 -0
- package/dist/esm/client.js.map +6 -0
- package/dist/esm/client.native.js +276 -0
- package/dist/esm/client.native.js.map +6 -0
- package/dist/esm/customEvent.js +1 -0
- package/dist/esm/customEvent.js.map +6 -0
- package/dist/esm/customEvent.native.js +1 -0
- package/dist/esm/customEvent.native.js.map +6 -0
- package/dist/esm/hmrPayload.js +1 -0
- package/dist/esm/hmrPayload.js.map +6 -0
- package/dist/esm/hmrPayload.native.js +1 -0
- package/dist/esm/hmrPayload.native.js.map +6 -0
- package/dist/esm/hot.js +1 -0
- package/dist/esm/hot.js.map +6 -0
- package/dist/esm/hot.native.js +1 -0
- package/dist/esm/hot.native.js.map +6 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +6 -0
- package/dist/esm/index.native.js +2 -0
- package/dist/esm/index.native.js.map +6 -0
- package/package.json +39 -0
- package/src/client.ts +526 -0
- package/src/customEvent.ts +36 -0
- package/src/hmrPayload.ts +61 -0
- package/src/hot.ts +29 -0
- package/src/index.ts +1 -0
- package/types/client.d.ts +2 -0
- package/types/client.d.ts.map +1 -0
- package/types/customEvent.d.ts +26 -0
- package/types/customEvent.d.ts.map +1 -0
- package/types/hmrPayload.d.ts +49 -0
- package/types/hmrPayload.d.ts.map +1 -0
- package/types/hot.d.ts +18 -0
- package/types/hot.d.ts.map +1 -0
- package/types/index.d.ts +2 -0
- package/types/index.d.ts.map +1 -0
package/src/client.ts
ADDED
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
// import '@vite/env'
|
|
2
|
+
|
|
3
|
+
import type { InferCustomEventPayload } from './customEvent'
|
|
4
|
+
import type { ErrorPayload, HMRPayload, Update } from './hmrPayload'
|
|
5
|
+
import type { ModuleNamespace, ViteHotContext } from './hot'
|
|
6
|
+
|
|
7
|
+
// injected by the hmr plugin when served
|
|
8
|
+
declare const __BASE__: string
|
|
9
|
+
declare const __SERVER_HOST__: string
|
|
10
|
+
declare const __HMR_PROTOCOL__: string | null
|
|
11
|
+
declare const __HMR_HOSTNAME__: string | null
|
|
12
|
+
declare const __HMR_PORT__: number | null
|
|
13
|
+
declare const __HMR_DIRECT_TARGET__: string
|
|
14
|
+
declare const __HMR_BASE__: string
|
|
15
|
+
declare const __HMR_TIMEOUT__: number
|
|
16
|
+
declare const __HMR_ENABLE_OVERLAY__: boolean
|
|
17
|
+
|
|
18
|
+
console.log('[vite] connecting...')
|
|
19
|
+
|
|
20
|
+
const importMetaUrl = {
|
|
21
|
+
hostname: '127.0.0.1',
|
|
22
|
+
protocol: 'http',
|
|
23
|
+
port: 5173,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// use server configuration, then fallback to inference
|
|
27
|
+
const serverHost = __SERVER_HOST__
|
|
28
|
+
const socketProtocol =
|
|
29
|
+
__HMR_PROTOCOL__ || (importMetaUrl.protocol === 'https:' ? 'wss' : 'ws')
|
|
30
|
+
const hmrPort = __HMR_PORT__ || 5173
|
|
31
|
+
|
|
32
|
+
const socketHost = `${__HMR_HOSTNAME__ || importMetaUrl.hostname}:${
|
|
33
|
+
hmrPort || importMetaUrl.port
|
|
34
|
+
}${__HMR_BASE__}`
|
|
35
|
+
const directSocketHost = __HMR_DIRECT_TARGET__
|
|
36
|
+
const base = __BASE__ || '/'
|
|
37
|
+
const messageBuffer: string[] = []
|
|
38
|
+
|
|
39
|
+
let socket: WebSocket
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
let fallback: (() => void) | undefined
|
|
43
|
+
// only use fallback when port is inferred to prevent confusion
|
|
44
|
+
if (!hmrPort) {
|
|
45
|
+
fallback = () => {
|
|
46
|
+
// fallback to connecting directly to the hmr server
|
|
47
|
+
// for servers which does not support proxying websocket
|
|
48
|
+
socket = setupWebSocket(socketProtocol, directSocketHost, () => {
|
|
49
|
+
console.error(
|
|
50
|
+
'[vite] failed to connect to websocket.\n' +
|
|
51
|
+
'your current setup:\n' +
|
|
52
|
+
` (browser) ${JSON.stringify(
|
|
53
|
+
importMetaUrl
|
|
54
|
+
)} <--[HTTP]--> ${serverHost} (server)\n` +
|
|
55
|
+
` (browser) ${socketHost} <--[WebSocket (failing)]--> ${directSocketHost} (server)\n` +
|
|
56
|
+
'Check out your Vite / network configuration and https://vitejs.dev/config/server-options.html#server-hmr .'
|
|
57
|
+
)
|
|
58
|
+
})
|
|
59
|
+
socket.addEventListener(
|
|
60
|
+
'open',
|
|
61
|
+
() => {
|
|
62
|
+
console.log(
|
|
63
|
+
'[vite] Direct websocket connection fallback. Check out https://vitejs.dev/config/server-options.html#server-hmr to remove the previous connection error.'
|
|
64
|
+
)
|
|
65
|
+
},
|
|
66
|
+
{ once: true }
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
socket = setupWebSocket(socketProtocol, socketHost, fallback)
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.error(`[vite] failed to connect to websocket (${error}). `)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function setupWebSocket(
|
|
77
|
+
protocol: string,
|
|
78
|
+
hostAndPath: string,
|
|
79
|
+
onCloseWithoutOpen?: () => void
|
|
80
|
+
) {
|
|
81
|
+
const endpoint = `${protocol}://${hostAndPath}`
|
|
82
|
+
const socket = new WebSocket(endpoint, 'vite-hmr')
|
|
83
|
+
let isOpened = false
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* WARNING: passing an async function as a callback to socket listeners silently fails on native
|
|
87
|
+
*/
|
|
88
|
+
|
|
89
|
+
socket.addEventListener(
|
|
90
|
+
'open',
|
|
91
|
+
() => {
|
|
92
|
+
isOpened = true
|
|
93
|
+
notifyListeners('vite:ws:connect', { webSocket: socket })
|
|
94
|
+
},
|
|
95
|
+
{ once: true }
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
// Listen for messages
|
|
99
|
+
socket.addEventListener('message', ({ data }) => {
|
|
100
|
+
console.log('🌶️' + data)
|
|
101
|
+
handleMessage(JSON.parse(data))
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
socket.addEventListener('error', (err) => {
|
|
105
|
+
console.log('err' + err['message'] + err['stack'])
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
// ping server
|
|
109
|
+
socket.addEventListener('close', ({ wasClean }) => {
|
|
110
|
+
if (wasClean) return
|
|
111
|
+
|
|
112
|
+
if (!isOpened && onCloseWithoutOpen) {
|
|
113
|
+
onCloseWithoutOpen()
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
notifyListeners('vite:ws:disconnect', { webSocket: socket })
|
|
118
|
+
|
|
119
|
+
console.log(`[vite] server connection lost. polling for restart...`)
|
|
120
|
+
waitForSuccessfulPing(protocol, hostAndPath).then(() => {
|
|
121
|
+
console.log('shuld reload')
|
|
122
|
+
// location.reload()
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
return socket
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function warnFailedFetch(err: Error, path: string | string[]) {
|
|
130
|
+
console.error(`${err}`)
|
|
131
|
+
console.error(
|
|
132
|
+
`[hmr] Failed to reload ${path}. ` +
|
|
133
|
+
`This could be due to syntax errors or importing non-existent ` +
|
|
134
|
+
`modules. (see errors above)`
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
let isFirstUpdate = true
|
|
139
|
+
|
|
140
|
+
const debounceReload = (time: number) => {
|
|
141
|
+
let timer: ReturnType<typeof setTimeout> | null
|
|
142
|
+
return () => {
|
|
143
|
+
if (timer) {
|
|
144
|
+
clearTimeout(timer)
|
|
145
|
+
timer = null
|
|
146
|
+
}
|
|
147
|
+
timer = setTimeout(() => {
|
|
148
|
+
location.reload()
|
|
149
|
+
}, time)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const pageReload = debounceReload(50)
|
|
153
|
+
|
|
154
|
+
async function handleMessage(payload: HMRPayload) {
|
|
155
|
+
switch (payload.type) {
|
|
156
|
+
case 'connected':
|
|
157
|
+
console.log(`[vite] connected.`)
|
|
158
|
+
sendMessageBuffer()
|
|
159
|
+
// proxy(nginx, docker) hmr ws maybe caused timeout,
|
|
160
|
+
// so send ping package let ws keep alive.
|
|
161
|
+
setInterval(() => {
|
|
162
|
+
if (socket.readyState === socket.OPEN) {
|
|
163
|
+
socket.send('{"type":"ping"}')
|
|
164
|
+
}
|
|
165
|
+
}, __HMR_TIMEOUT__)
|
|
166
|
+
break
|
|
167
|
+
case 'update':
|
|
168
|
+
notifyListeners('vite:beforeUpdate', payload)
|
|
169
|
+
// if this is the first update and there's already an error overlay, it
|
|
170
|
+
// means the page opened with existing server compile error and the whole
|
|
171
|
+
// module script failed to load (since one of the nested imports is 500).
|
|
172
|
+
// in this case a normal update won't work and a full reload is needed.
|
|
173
|
+
if (isFirstUpdate && hasErrorOverlay()) {
|
|
174
|
+
// !
|
|
175
|
+
// window.location.reload()
|
|
176
|
+
return
|
|
177
|
+
} else {
|
|
178
|
+
clearErrorOverlay()
|
|
179
|
+
isFirstUpdate = false
|
|
180
|
+
}
|
|
181
|
+
await Promise.all(
|
|
182
|
+
payload.updates.map((update) => {
|
|
183
|
+
if (update.type === 'js-update') {
|
|
184
|
+
return queueUpdate(fetchUpdate(update))
|
|
185
|
+
}
|
|
186
|
+
})
|
|
187
|
+
)
|
|
188
|
+
notifyListeners('vite:afterUpdate', payload)
|
|
189
|
+
break
|
|
190
|
+
case 'custom': {
|
|
191
|
+
notifyListeners(payload.event, payload.data)
|
|
192
|
+
break
|
|
193
|
+
}
|
|
194
|
+
case 'full-reload':
|
|
195
|
+
notifyListeners('vite:beforeFullReload', payload)
|
|
196
|
+
if (payload.path && payload.path.endsWith('.html')) {
|
|
197
|
+
// if html file is edited, only reload the page if the browser is
|
|
198
|
+
// currently on that page.
|
|
199
|
+
const pagePath = decodeURI(location.pathname)
|
|
200
|
+
const payloadPath = base + payload.path.slice(1)
|
|
201
|
+
if (
|
|
202
|
+
pagePath === payloadPath ||
|
|
203
|
+
payload.path === '/index.html' ||
|
|
204
|
+
(pagePath.endsWith('/') && pagePath + 'index.html' === payloadPath)
|
|
205
|
+
) {
|
|
206
|
+
pageReload()
|
|
207
|
+
}
|
|
208
|
+
return
|
|
209
|
+
} else {
|
|
210
|
+
pageReload()
|
|
211
|
+
}
|
|
212
|
+
break
|
|
213
|
+
case 'prune':
|
|
214
|
+
notifyListeners('vite:beforePrune', payload)
|
|
215
|
+
// After an HMR update, some modules are no longer imported on the page
|
|
216
|
+
// but they may have left behind side effects that need to be cleaned up
|
|
217
|
+
// (.e.g style injections)
|
|
218
|
+
// TODO Trigger their dispose callbacks.
|
|
219
|
+
payload.paths.forEach((path) => {
|
|
220
|
+
const fn = pruneMap.get(path)
|
|
221
|
+
if (fn) {
|
|
222
|
+
fn(dataMap.get(path))
|
|
223
|
+
}
|
|
224
|
+
})
|
|
225
|
+
break
|
|
226
|
+
case 'error': {
|
|
227
|
+
notifyListeners('vite:error', payload)
|
|
228
|
+
const err = payload.err
|
|
229
|
+
if (enableOverlay) {
|
|
230
|
+
createErrorOverlay(err)
|
|
231
|
+
} else {
|
|
232
|
+
console.error(`[vite] Internal Server Error\n${err.message}\n${err.stack}`)
|
|
233
|
+
}
|
|
234
|
+
break
|
|
235
|
+
}
|
|
236
|
+
default: {
|
|
237
|
+
const check: never = payload
|
|
238
|
+
return check
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function notifyListeners<T extends string>(
|
|
244
|
+
event: T,
|
|
245
|
+
data: InferCustomEventPayload<T>
|
|
246
|
+
): void
|
|
247
|
+
function notifyListeners(event: string, data: any): void {
|
|
248
|
+
const cbs = customListenersMap.get(event)
|
|
249
|
+
if (cbs) {
|
|
250
|
+
cbs.forEach((cb) => cb(data))
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const enableOverlay = __HMR_ENABLE_OVERLAY__
|
|
255
|
+
|
|
256
|
+
function createErrorOverlay(err: ErrorPayload['err']) {
|
|
257
|
+
if (!enableOverlay) return
|
|
258
|
+
clearErrorOverlay()
|
|
259
|
+
console.log('create error', err)
|
|
260
|
+
// document.body.appendChild(new ErrorOverlay(err))
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function clearErrorOverlay() {
|
|
264
|
+
// document.querySelectorAll(overlayId).forEach((n) => (n as ErrorOverlay).close())
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function hasErrorOverlay() {
|
|
268
|
+
return false
|
|
269
|
+
// return document.querySelectorAll(overlayId).length
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
let pending = false
|
|
273
|
+
let queued: Promise<(() => void) | undefined>[] = []
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* buffer multiple hot updates triggered by the same src change
|
|
277
|
+
* so that they are invoked in the same order they were sent.
|
|
278
|
+
* (otherwise the order may be inconsistent because of the http request round trip)
|
|
279
|
+
*/
|
|
280
|
+
async function queueUpdate(p: Promise<(() => void) | undefined>) {
|
|
281
|
+
queued.push(p)
|
|
282
|
+
if (!pending) {
|
|
283
|
+
pending = true
|
|
284
|
+
await Promise.resolve()
|
|
285
|
+
pending = false
|
|
286
|
+
const loading = [...queued]
|
|
287
|
+
queued = []
|
|
288
|
+
;(await Promise.all(loading)).forEach((fn) => fn && fn())
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async function waitForSuccessfulPing(
|
|
293
|
+
socketProtocol: string,
|
|
294
|
+
hostAndPath: string,
|
|
295
|
+
ms = 1000
|
|
296
|
+
) {
|
|
297
|
+
const pingHostProtocol = socketProtocol === 'wss' ? 'https' : 'http'
|
|
298
|
+
|
|
299
|
+
const ping = async () => {
|
|
300
|
+
// A fetch on a websocket URL will return a successful promise with status 400,
|
|
301
|
+
// but will reject a networking error.
|
|
302
|
+
// When running on middleware mode, it returns status 426, and an cors error happens if mode is not no-cors
|
|
303
|
+
try {
|
|
304
|
+
await fetch(`${pingHostProtocol}://${hostAndPath}`, {
|
|
305
|
+
mode: 'no-cors',
|
|
306
|
+
headers: {
|
|
307
|
+
// Custom headers won't be included in a request with no-cors so (ab)use one of the
|
|
308
|
+
// safelisted headers to identify the ping request
|
|
309
|
+
Accept: 'text/x-vite-ping',
|
|
310
|
+
},
|
|
311
|
+
})
|
|
312
|
+
return true
|
|
313
|
+
} catch {}
|
|
314
|
+
return false
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (await ping()) {
|
|
318
|
+
return
|
|
319
|
+
}
|
|
320
|
+
await wait(ms)
|
|
321
|
+
|
|
322
|
+
// eslint-disable-next-line no-constant-condition
|
|
323
|
+
while (true) {
|
|
324
|
+
if (await ping()) {
|
|
325
|
+
break
|
|
326
|
+
}
|
|
327
|
+
await wait(ms)
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function wait(ms: number) {
|
|
332
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
async function fetchUpdate({
|
|
336
|
+
path,
|
|
337
|
+
acceptedPath,
|
|
338
|
+
timestamp,
|
|
339
|
+
explicitImportRequired,
|
|
340
|
+
}: Update) {
|
|
341
|
+
const mod = hotModulesMap.get(path)
|
|
342
|
+
|
|
343
|
+
if (!mod) {
|
|
344
|
+
// In a code-splitting project,
|
|
345
|
+
// it is common that the hot-updating module is not loaded yet.
|
|
346
|
+
// https://github.com/vitejs/vite/issues/721
|
|
347
|
+
return
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
let fetchedModule: ModuleNamespace | undefined
|
|
351
|
+
const isSelfUpdate = path === acceptedPath
|
|
352
|
+
|
|
353
|
+
// determine the qualified callbacks before we re-import the modules
|
|
354
|
+
const qualifiedCallbacks = mod.callbacks.filter(({ deps }) =>
|
|
355
|
+
deps.includes(acceptedPath)
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
if (isSelfUpdate || qualifiedCallbacks.length > 0) {
|
|
359
|
+
const disposer = disposeMap.get(acceptedPath)
|
|
360
|
+
if (disposer) await disposer(dataMap.get(acceptedPath))
|
|
361
|
+
const [acceptedPathWithoutQuery, query] = acceptedPath.split(`?`)
|
|
362
|
+
try {
|
|
363
|
+
const filePath = acceptedPathWithoutQuery
|
|
364
|
+
const finalQuery = `file?file=${encodeURIComponent(filePath)}&${
|
|
365
|
+
explicitImportRequired ? 'import&' : ''
|
|
366
|
+
}t=${timestamp}${query ? `&${query}` : ''}`
|
|
367
|
+
|
|
368
|
+
const scriptUrl =
|
|
369
|
+
// re-route to our cjs endpoint
|
|
370
|
+
`http://${serverHost.replace('5173', '8081')}` + finalQuery
|
|
371
|
+
|
|
372
|
+
console.log(`fetching update: ${JSON.stringify({ path, mod, scriptUrl })}`)
|
|
373
|
+
|
|
374
|
+
const source = await fetch(scriptUrl).then((res) => res.text())
|
|
375
|
+
|
|
376
|
+
const evaluatedModule = eval(source)
|
|
377
|
+
|
|
378
|
+
fetchedModule = evaluatedModule
|
|
379
|
+
} catch (e) {
|
|
380
|
+
warnFailedFetch(e as any, acceptedPath)
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return () => {
|
|
385
|
+
for (const { deps, fn } of qualifiedCallbacks) {
|
|
386
|
+
fn(deps.map((dep) => (dep === acceptedPath ? fetchedModule : undefined)))
|
|
387
|
+
}
|
|
388
|
+
const loggedPath = isSelfUpdate ? path : `${acceptedPath} via ${path}`
|
|
389
|
+
console.log(`[vite] hot updated: ${loggedPath}`)
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function sendMessageBuffer() {
|
|
394
|
+
if (socket.readyState === 1) {
|
|
395
|
+
messageBuffer.forEach((msg) => socket.send(msg))
|
|
396
|
+
messageBuffer.length = 0
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
interface HotModule {
|
|
401
|
+
id: string
|
|
402
|
+
callbacks: HotCallback[]
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
interface HotCallback {
|
|
406
|
+
// the dependencies must be fetchable paths
|
|
407
|
+
deps: string[]
|
|
408
|
+
fn: (modules: Array<ModuleNamespace | undefined>) => void
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
type CustomListenersMap = Map<string, ((data: any) => void)[]>
|
|
412
|
+
|
|
413
|
+
const hotModulesMap = new Map<string, HotModule>()
|
|
414
|
+
const disposeMap = new Map<string, (data: any) => void | Promise<void>>()
|
|
415
|
+
const pruneMap = new Map<string, (data: any) => void | Promise<void>>()
|
|
416
|
+
const dataMap = new Map<string, any>()
|
|
417
|
+
const customListenersMap: CustomListenersMap = new Map()
|
|
418
|
+
const ctxToListenersMap = new Map<string, CustomListenersMap>()
|
|
419
|
+
|
|
420
|
+
globalThis['createHotContext'] = function createHotContext(
|
|
421
|
+
ownerPath: string
|
|
422
|
+
): ViteHotContext {
|
|
423
|
+
if (!dataMap.has(ownerPath)) {
|
|
424
|
+
dataMap.set(ownerPath, {})
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// when a file is hot updated, a new context is created
|
|
428
|
+
// clear its stale callbacks
|
|
429
|
+
const mod = hotModulesMap.get(ownerPath)
|
|
430
|
+
if (mod) {
|
|
431
|
+
mod.callbacks = []
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// clear stale custom event listeners
|
|
435
|
+
const staleListeners = ctxToListenersMap.get(ownerPath)
|
|
436
|
+
if (staleListeners) {
|
|
437
|
+
for (const [event, staleFns] of staleListeners) {
|
|
438
|
+
const listeners = customListenersMap.get(event)
|
|
439
|
+
if (listeners) {
|
|
440
|
+
customListenersMap.set(
|
|
441
|
+
event,
|
|
442
|
+
listeners.filter((l) => !staleFns.includes(l))
|
|
443
|
+
)
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const newListeners: CustomListenersMap = new Map()
|
|
449
|
+
ctxToListenersMap.set(ownerPath, newListeners)
|
|
450
|
+
|
|
451
|
+
function acceptDeps(deps: string[], callback: HotCallback['fn'] = () => {}) {
|
|
452
|
+
const mod: HotModule = hotModulesMap.get(ownerPath) || {
|
|
453
|
+
id: ownerPath,
|
|
454
|
+
callbacks: [],
|
|
455
|
+
}
|
|
456
|
+
mod.callbacks.push({
|
|
457
|
+
deps,
|
|
458
|
+
fn: callback,
|
|
459
|
+
})
|
|
460
|
+
hotModulesMap.set(ownerPath, mod)
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const hot: ViteHotContext = {
|
|
464
|
+
get data() {
|
|
465
|
+
return dataMap.get(ownerPath)
|
|
466
|
+
},
|
|
467
|
+
|
|
468
|
+
accept(deps?: any, callback?: any) {
|
|
469
|
+
if (typeof deps === 'function' || !deps) {
|
|
470
|
+
// self-accept: hot.accept(() => {})
|
|
471
|
+
acceptDeps([ownerPath], ([mod]) => deps?.(mod))
|
|
472
|
+
} else if (typeof deps === 'string') {
|
|
473
|
+
// explicit deps
|
|
474
|
+
acceptDeps([deps], ([mod]) => callback?.(mod))
|
|
475
|
+
} else if (Array.isArray(deps)) {
|
|
476
|
+
acceptDeps(deps, callback)
|
|
477
|
+
} else {
|
|
478
|
+
throw new Error(`invalid hot.accept() usage.`)
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
|
|
482
|
+
// export names (first arg) are irrelevant on the client side, they're
|
|
483
|
+
// extracted in the server for propagation
|
|
484
|
+
acceptExports(_, callback) {
|
|
485
|
+
acceptDeps([ownerPath], ([mod]) => callback?.(mod))
|
|
486
|
+
},
|
|
487
|
+
|
|
488
|
+
dispose(cb) {
|
|
489
|
+
disposeMap.set(ownerPath, cb)
|
|
490
|
+
},
|
|
491
|
+
|
|
492
|
+
prune(cb) {
|
|
493
|
+
pruneMap.set(ownerPath, cb)
|
|
494
|
+
},
|
|
495
|
+
|
|
496
|
+
// Kept for backward compatibility (#11036)
|
|
497
|
+
// @ts-expect-error untyped
|
|
498
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
499
|
+
decline() {},
|
|
500
|
+
|
|
501
|
+
// tell the server to re-perform hmr propagation from this module as root
|
|
502
|
+
invalidate(message) {
|
|
503
|
+
notifyListeners('vite:invalidate', { path: ownerPath, message })
|
|
504
|
+
this.send('vite:invalidate', { path: ownerPath, message })
|
|
505
|
+
console.log(`[vite] invalidate ${ownerPath}${message ? `: ${message}` : ''}`)
|
|
506
|
+
},
|
|
507
|
+
|
|
508
|
+
// custom events
|
|
509
|
+
on(event, cb) {
|
|
510
|
+
const addToMap = (map: Map<string, any[]>) => {
|
|
511
|
+
const existing = map.get(event) || []
|
|
512
|
+
existing.push(cb)
|
|
513
|
+
map.set(event, existing)
|
|
514
|
+
}
|
|
515
|
+
addToMap(customListenersMap)
|
|
516
|
+
addToMap(newListeners)
|
|
517
|
+
},
|
|
518
|
+
|
|
519
|
+
send(event, data) {
|
|
520
|
+
messageBuffer.push(JSON.stringify({ type: 'custom', event, data }))
|
|
521
|
+
sendMessageBuffer()
|
|
522
|
+
},
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return hot
|
|
526
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ErrorPayload,
|
|
3
|
+
FullReloadPayload,
|
|
4
|
+
PrunePayload,
|
|
5
|
+
UpdatePayload,
|
|
6
|
+
} from './hmrPayload'
|
|
7
|
+
|
|
8
|
+
export interface CustomEventMap {
|
|
9
|
+
'vite:beforeUpdate': UpdatePayload
|
|
10
|
+
'vite:afterUpdate': UpdatePayload
|
|
11
|
+
'vite:beforePrune': PrunePayload
|
|
12
|
+
'vite:beforeFullReload': FullReloadPayload
|
|
13
|
+
'vite:error': ErrorPayload
|
|
14
|
+
'vite:invalidate': InvalidatePayload
|
|
15
|
+
'vite:ws:connect': WebSocketConnectionPayload
|
|
16
|
+
'vite:ws:disconnect': WebSocketConnectionPayload
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface WebSocketConnectionPayload {
|
|
20
|
+
/**
|
|
21
|
+
* @experimental
|
|
22
|
+
* We expose this instance experimentally to see potential usage.
|
|
23
|
+
* This might be removed in the future if we didn't find reasonable use cases.
|
|
24
|
+
* If you find this useful, please open an issue with details so we can discuss and make it stable API.
|
|
25
|
+
*/
|
|
26
|
+
webSocket: WebSocket
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface InvalidatePayload {
|
|
30
|
+
path: string
|
|
31
|
+
message: string | undefined
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type InferCustomEventPayload<T extends string> = T extends keyof CustomEventMap
|
|
35
|
+
? CustomEventMap[T]
|
|
36
|
+
: any
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export type HMRPayload =
|
|
2
|
+
| ConnectedPayload
|
|
3
|
+
| UpdatePayload
|
|
4
|
+
| FullReloadPayload
|
|
5
|
+
| CustomPayload
|
|
6
|
+
| ErrorPayload
|
|
7
|
+
| PrunePayload
|
|
8
|
+
|
|
9
|
+
export interface ConnectedPayload {
|
|
10
|
+
type: 'connected'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface UpdatePayload {
|
|
14
|
+
type: 'update'
|
|
15
|
+
updates: Update[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface Update {
|
|
19
|
+
type: 'js-update' | 'css-update'
|
|
20
|
+
path: string
|
|
21
|
+
acceptedPath: string
|
|
22
|
+
timestamp: number
|
|
23
|
+
/**
|
|
24
|
+
* @experimental internal
|
|
25
|
+
*/
|
|
26
|
+
explicitImportRequired?: boolean | undefined
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface PrunePayload {
|
|
30
|
+
type: 'prune'
|
|
31
|
+
paths: string[]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface FullReloadPayload {
|
|
35
|
+
type: 'full-reload'
|
|
36
|
+
path?: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface CustomPayload {
|
|
40
|
+
type: 'custom'
|
|
41
|
+
event: string
|
|
42
|
+
data?: any
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface ErrorPayload {
|
|
46
|
+
type: 'error'
|
|
47
|
+
err: {
|
|
48
|
+
[name: string]: any
|
|
49
|
+
message: string
|
|
50
|
+
stack: string
|
|
51
|
+
id?: string
|
|
52
|
+
frame?: string
|
|
53
|
+
plugin?: string
|
|
54
|
+
pluginCode?: string
|
|
55
|
+
loc?: {
|
|
56
|
+
file?: string
|
|
57
|
+
line: number
|
|
58
|
+
column: number
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
package/src/hot.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { InferCustomEventPayload } from './customEvent'
|
|
2
|
+
|
|
3
|
+
export type ModuleNamespace = Record<string, any> & {
|
|
4
|
+
[Symbol.toStringTag]: 'Module'
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface ViteHotContext {
|
|
8
|
+
readonly data: any
|
|
9
|
+
|
|
10
|
+
accept(): void
|
|
11
|
+
accept(cb: (mod: ModuleNamespace | undefined) => void): void
|
|
12
|
+
accept(dep: string, cb: (mod: ModuleNamespace | undefined) => void): void
|
|
13
|
+
accept(
|
|
14
|
+
deps: readonly string[],
|
|
15
|
+
cb: (mods: Array<ModuleNamespace | undefined>) => void
|
|
16
|
+
): void
|
|
17
|
+
|
|
18
|
+
acceptExports(
|
|
19
|
+
exportNames: string | readonly string[],
|
|
20
|
+
cb?: (mod: ModuleNamespace | undefined) => void
|
|
21
|
+
): void
|
|
22
|
+
|
|
23
|
+
dispose(cb: (data: any) => void): void
|
|
24
|
+
prune(cb: (data: any) => void): void
|
|
25
|
+
invalidate(message?: string): void
|
|
26
|
+
|
|
27
|
+
on<T extends string>(event: T, cb: (payload: InferCustomEventPayload<T>) => void): void
|
|
28
|
+
send<T extends string>(event: T, data?: InferCustomEventPayload<T>): void
|
|
29
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './client'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ErrorPayload, FullReloadPayload, PrunePayload, UpdatePayload } from './hmrPayload';
|
|
2
|
+
export interface CustomEventMap {
|
|
3
|
+
'vite:beforeUpdate': UpdatePayload;
|
|
4
|
+
'vite:afterUpdate': UpdatePayload;
|
|
5
|
+
'vite:beforePrune': PrunePayload;
|
|
6
|
+
'vite:beforeFullReload': FullReloadPayload;
|
|
7
|
+
'vite:error': ErrorPayload;
|
|
8
|
+
'vite:invalidate': InvalidatePayload;
|
|
9
|
+
'vite:ws:connect': WebSocketConnectionPayload;
|
|
10
|
+
'vite:ws:disconnect': WebSocketConnectionPayload;
|
|
11
|
+
}
|
|
12
|
+
export interface WebSocketConnectionPayload {
|
|
13
|
+
/**
|
|
14
|
+
* @experimental
|
|
15
|
+
* We expose this instance experimentally to see potential usage.
|
|
16
|
+
* This might be removed in the future if we didn't find reasonable use cases.
|
|
17
|
+
* If you find this useful, please open an issue with details so we can discuss and make it stable API.
|
|
18
|
+
*/
|
|
19
|
+
webSocket: WebSocket;
|
|
20
|
+
}
|
|
21
|
+
export interface InvalidatePayload {
|
|
22
|
+
path: string;
|
|
23
|
+
message: string | undefined;
|
|
24
|
+
}
|
|
25
|
+
export type InferCustomEventPayload<T extends string> = T extends keyof CustomEventMap ? CustomEventMap[T] : any;
|
|
26
|
+
//# sourceMappingURL=customEvent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"customEvent.d.ts","sourceRoot":"","sources":["../src/customEvent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACd,MAAM,cAAc,CAAA;AAErB,MAAM,WAAW,cAAc;IAC7B,mBAAmB,EAAE,aAAa,CAAA;IAClC,kBAAkB,EAAE,aAAa,CAAA;IACjC,kBAAkB,EAAE,YAAY,CAAA;IAChC,uBAAuB,EAAE,iBAAiB,CAAA;IAC1C,YAAY,EAAE,YAAY,CAAA;IAC1B,iBAAiB,EAAE,iBAAiB,CAAA;IACpC,iBAAiB,EAAE,0BAA0B,CAAA;IAC7C,oBAAoB,EAAE,0BAA0B,CAAA;CACjD;AAED,MAAM,WAAW,0BAA0B;IACzC;;;;;OAKG;IACH,SAAS,EAAE,SAAS,CAAA;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;CAC5B;AAED,MAAM,MAAM,uBAAuB,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,MAAM,cAAc,GAClF,cAAc,CAAC,CAAC,CAAC,GACjB,GAAG,CAAA"}
|