rsnext/packages/next/client/dev/error-overlay/websocket.ts
JJ Kasper 75748caf7f
Migrate server-sent events HMR connection to WebSocket (#29903)
This replaces the server-sent events HMR connection with a WebSocket connection to prevent hitting browser connection limits, allow sending events back from the browser, and overall better performance. 

This approach sets up the the `upgrade` event listener on the server immediately when created via `next dev` and on the first request using `req.socket.server` when created via a custom server. In a follow-up PR we can push the files changed via the WebSocket as well. 

x-ref: https://github.com/vercel/next.js/issues/10061
x-ref: https://github.com/vercel/next.js/issues/8064
x-ref: https://github.com/vercel/next.js/issues/4495

## Bug

- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`

## Feature

- [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have helpful link attached, see `contributing.md`

## Documentation / Examples

- [ ] Make sure the linting passes
2021-10-15 07:09:54 +00:00

59 lines
1.3 KiB
TypeScript

let source: WebSocket
const eventCallbacks: ((event: any) => void)[] = []
let lastActivity = Date.now()
export function addMessageListener(cb: (event: any) => void) {
eventCallbacks.push(cb)
}
export function sendMessage(data: any) {
if (!source || source.readyState !== source.OPEN) return
return source.send(data)
}
export function connectHMR(options: {
path: string
timeout: number
log?: boolean
}) {
if (!options.timeout) {
options.timeout = 5 * 1000
}
init()
let timer = setInterval(function () {
if (Date.now() - lastActivity > options.timeout) {
handleDisconnect()
}
}, options.timeout / 2)
function init() {
if (source) source.close()
const { protocol, hostname, port } = location
const url = `${protocol === 'http:' ? 'ws' : 'wss'}://${hostname}:${port}`
source = new window.WebSocket(`${url}${options.path}`)
source.onopen = handleOnline
source.onerror = handleDisconnect
source.onmessage = handleMessage
}
function handleOnline() {
if (options.log) console.log('[HMR] connected')
lastActivity = Date.now()
}
function handleMessage(event: any) {
lastActivity = Date.now()
eventCallbacks.forEach((cb) => {
cb(event)
})
}
function handleDisconnect() {
clearInterval(timer)
source.close()
setTimeout(init, options.timeout)
}
}