diff --git a/src/runtime/entrypoints/client.ts b/src/runtime/entrypoints/client.ts index 21d1a656143..d3d6f7769f4 100644 --- a/src/runtime/entrypoints/client.ts +++ b/src/runtime/entrypoints/client.ts @@ -1,4 +1,5 @@ let ws: WebSocket; +let revision = 0; let reconnectTimer: number; const backoff = [ @@ -36,7 +37,7 @@ function reconnect() { backoffIdx++; try { - connect(true); + connect(); clearTimeout(reconnectTimer); } catch (_err) { reconnect(); @@ -44,23 +45,19 @@ function reconnect() { }, backoff[Math.min(backoffIdx, backoff.length - 1)]); } -function connect(forceReload?: boolean) { +function connect() { const url = new URL("/_frsh/alive", location.origin.replace("http", "ws")); ws = new WebSocket( url, ); ws.addEventListener("open", () => { - if (forceReload) { - location.reload(); - } else { - backoffIdx = 0; - console.log( - `%c Fresh %c Connected to development server.`, - "background-color: #86efac; color: black", - "color: inherit", - ); - } + backoffIdx = 0; + console.log( + `%c Fresh %c Connected to development server.`, + "background-color: #86efac; color: black", + "color: inherit", + ); }); ws.addEventListener("close", () => { @@ -75,7 +72,16 @@ connect(); function handleMessage(e: MessageEvent) { const data = JSON.parse(e.data); - console.log(data); + switch (data.type) { + case "initial-state": { + if (revision === 0) { + revision = data.revision; + } else if (revision < data.revision) { + // Needs reload + location.reload(); + } + } + } } function handleError(e: Event) { diff --git a/src/server/context.ts b/src/server/context.ts index 155d316f1e3..23d1e75ba8d 100644 --- a/src/server/context.ts +++ b/src/server/context.ts @@ -413,6 +413,7 @@ export class ServerContext { #plugins: Plugin[]; #builder: Builder | Promise | BuildSnapshot; #routerOptions: RouterOptions; + #revision = 0; constructor( routes: Route[], @@ -493,6 +494,13 @@ export class ServerContext { const isDev = this.#dev; const bundleAssetRoute = this.#bundleAssetRoute(); + if (this.#dev) { + this.#revision = Date.now(); + } + + // deno-lint-ignore no-this-alias + const _self = this; + return async function handler( req: Request, connInfo: ServeHandlerInfo = DEFAULT_CONN_INFO, @@ -511,7 +519,16 @@ export class ServerContext { // the client to know when the server is back up. Once we // have HMR we'll actively start sending messages back // and forth. - const { response } = Deno.upgradeWebSocket(req); + const { response, socket } = Deno.upgradeWebSocket(req); + + socket.addEventListener("open", () => { + socket.send( + JSON.stringify({ + type: "initial-state", + revision: _self.#revision, + }), + ); + }); return response; } else if (url.pathname === DEV_CLIENT_URL) {