diff --git a/packages/vike-node/src/plugin/plugins/devServerPlugin.ts b/packages/vike-node/src/plugin/plugins/devServerPlugin.ts index fc090d8..2dd8874 100644 --- a/packages/vike-node/src/plugin/plugins/devServerPlugin.ts +++ b/packages/vike-node/src/plugin/plugins/devServerPlugin.ts @@ -1,5 +1,5 @@ import { fork } from 'child_process' -import { ServerResponse, createServer, type IncomingMessage } from 'http' +import { createServer, type IncomingMessage, type Server } from 'http' import type { Plugin, ViteDevServer } from 'vite' import { globalStore } from '../../runtime/globalStore.js' import type { ConfigVikeNodeResolved } from '../../types.js' @@ -15,6 +15,7 @@ export function devServerPlugin(): Plugin { let resolvedConfig: ConfigVikeNodeResolved let entryAbs: string let HMRServer: ReturnType | undefined + let setupHMRProxyDone = false return { name: 'vite-node:devserver', apply: 'serve', @@ -60,7 +61,7 @@ export function devServerPlugin(): Plugin { viteDevServer = vite globalStore.viteDevServer = vite - globalStore.HMRProxy = HMRProxy + globalStore.setupHMRProxy = setupHMRProxy patchViteServer(vite) initializeServerEntry(vite) } @@ -95,30 +96,19 @@ export function devServerPlugin(): Plugin { vite.ssrLoadModule(entryAbs) } - function HMRProxy(req: IncomingMessage, res: ServerResponse, next?: (err?: unknown) => void): boolean { - const canHandle = req.url === VITE_HMR_PATH && req.headers.upgrade === 'websocket' - if (!canHandle) { - next?.() - return false + function setupHMRProxy(req: IncomingMessage) { + if (setupHMRProxyDone || isBun) { + return } - // Pause the socket to prevent data loss - req.socket.pause() - - // Prepare the socket for upgrade - res.detachSocket(req.socket) - req.socket.setTimeout(0) - req.socket.setNoDelay(true) - req.socket.setKeepAlive(true, 0) - - // Emit the upgrade event - assert(HMRServer) - HMRServer.emit('upgrade', req, req.socket, Buffer.alloc(0)) - - // Resume the socket - req.socket.resume() - - return true + setupHMRProxyDone = true + const server = (req.socket as any).server as Server + server.on('upgrade', (clientReq, clientSocket, wsHead) => { + if (clientReq.url === VITE_HMR_PATH) { + assert(HMRServer) + HMRServer.emit('upgrade', clientReq, clientSocket, wsHead) + } + }) } } diff --git a/packages/vike-node/src/runtime/frameworks/connect.ts b/packages/vike-node/src/runtime/frameworks/connect.ts index 7965288..5f4bbe1 100644 --- a/packages/vike-node/src/runtime/frameworks/connect.ts +++ b/packages/vike-node/src/runtime/frameworks/connect.ts @@ -34,12 +34,7 @@ function vike void { const handler = createHandler(options) return (req, res, next) => { - const handled = globalStore.HMRProxy(req, res) - if (handled) { - next?.() - return - } - + globalStore.setupHMRProxy(req) handler({ req, res, diff --git a/packages/vike-node/src/runtime/frameworks/fastify.ts b/packages/vike-node/src/runtime/frameworks/fastify.ts index a8a7d47..93a84c7 100644 --- a/packages/vike-node/src/runtime/frameworks/fastify.ts +++ b/packages/vike-node/src/runtime/frameworks/fastify.ts @@ -34,10 +34,7 @@ function vike(options?: VikeOptions): FastifyPluginCallback { const handler = createHandler(options) return function plugin(instance, _options, done) { instance.get('*', async (req, reply) => { - const handled = globalStore.HMRProxy(req.raw, reply.raw) - if (handled) { - return - } + globalStore.setupHMRProxy(req.raw) const { res, onReadable } = createServerResponse(req.raw) ;(async () => { const { readable, headers, statusCode } = await onReadable diff --git a/packages/vike-node/src/runtime/frameworks/hono.ts b/packages/vike-node/src/runtime/frameworks/hono.ts index e189008..a6d0d61 100644 --- a/packages/vike-node/src/runtime/frameworks/hono.ts +++ b/packages/vike-node/src/runtime/frameworks/hono.ts @@ -37,14 +37,7 @@ function vike(options?: VikeOptions): MiddlewareHandler { const handler = createHandler(options) return async function middleware(ctx, next) { const req = ctx.env.incoming as IncomingMessage - const res = ctx.env.outgoing as ServerResponse - const handled = globalStore.HMRProxy(req, res) - - if (handled) { - res.writeHead = () => res - return new Response() - } - + globalStore.setupHMRProxy(req) const response = await connectToWeb((req, res, next) => handler({ req, diff --git a/packages/vike-node/src/runtime/globalStore.ts b/packages/vike-node/src/runtime/globalStore.ts index 1c209dc..f6b0af4 100644 --- a/packages/vike-node/src/runtime/globalStore.ts +++ b/packages/vike-node/src/runtime/globalStore.ts @@ -1,17 +1,14 @@ -import type { IncomingMessage, ServerResponse } from 'http' +import type { IncomingMessage } from 'http' import type { ViteDevServer } from 'vite' -import { NextFunction } from './types.js' // @ts-expect-error export const globalStore = (globalThis.__vikeNode ||= { isPluginLoaded: false, // This is overridden in devServerPlugin - HMRProxy: (req: IncomingMessage, res: ServerResponse, next?: NextFunction) => { - next?.() - return false - } + // in production it's a no-op + setupHMRProxy: () => {} }) as { isPluginLoaded: boolean viteDevServer?: ViteDevServer - HMRProxy: (req: IncomingMessage, res: ServerResponse, next?: NextFunction) => boolean + setupHMRProxy: (req: IncomingMessage) => void }