diff --git a/crates/turbopack-cli/js/src/entry/client.ts b/crates/turbopack-cli/js/src/entry/client.ts index d2d5ab7a2670f..b77b5d603a975 100644 --- a/crates/turbopack-cli/js/src/entry/client.ts +++ b/crates/turbopack-cli/js/src/entry/client.ts @@ -1,9 +1,14 @@ import { connect } from "@vercel/turbopack-ecmascript-runtime/dev/client/hmr-client"; -import { connectHMR } from "@vercel/turbopack-ecmascript-runtime/dev/client/websocket"; +import { + connectHMR, + addMessageListener, + sendMessage, +} from "@vercel/turbopack-ecmascript-runtime/dev/client/websocket"; export function initializeHMR(options: { assetPrefix: string }) { connect({ - assetPrefix: options.assetPrefix, + addMessageListener, + sendMessage, }); connectHMR({ assetPrefix: options.assetPrefix, diff --git a/crates/turbopack-ecmascript-hmr-protocol/src/lib.rs b/crates/turbopack-ecmascript-hmr-protocol/src/lib.rs index d614d5e9bbdce..d5067b6a193fd 100644 --- a/crates/turbopack-ecmascript-hmr-protocol/src/lib.rs +++ b/crates/turbopack-ecmascript-hmr-protocol/src/lib.rs @@ -27,12 +27,14 @@ impl Display for ResourceIdentifier { } #[derive(Deserialize)] -#[serde(tag = "type", rename_all = "camelCase")] +#[serde(tag = "type")] pub enum ClientMessage { + #[serde(rename = "turbopack-subscribe")] Subscribe { #[serde(flatten)] resource: ResourceIdentifier, }, + #[serde(rename = "turbopack-unsubscribe")] Unsubscribe { #[serde(flatten)] resource: ResourceIdentifier, diff --git a/crates/turbopack-ecmascript-runtime/js/package.json b/crates/turbopack-ecmascript-runtime/js/package.json index ec6d9a91e5b88..33327c5289f15 100644 --- a/crates/turbopack-ecmascript-runtime/js/package.json +++ b/crates/turbopack-ecmascript-runtime/js/package.json @@ -14,6 +14,7 @@ "check:dev-runtime-none": "tsc -p src/dev/runtime/none" }, "exports": { + ".": "./src/main.js", "./*": "./src/*.ts" }, "dependencies": { diff --git a/crates/turbopack-ecmascript-runtime/js/src/dev/client/hmr-client.ts b/crates/turbopack-ecmascript-runtime/js/src/dev/client/hmr-client.ts index ae1bf6d68f8b6..eb7898fe64a15 100644 --- a/crates/turbopack-ecmascript-runtime/js/src/dev/client/hmr-client.ts +++ b/crates/turbopack-ecmascript-runtime/js/src/dev/client/hmr-client.ts @@ -3,21 +3,32 @@ /// /// -import { addEventListener, sendMessage } from "./websocket"; +import { + addMessageListener as turboSocketAddMessageListener, + sendMessage as turboSocketSendMessage, +} from "./websocket"; +type SendMessage = typeof import("./websocket").sendMessage; export type ClientOptions = { - assetPrefix: string; + addMessageListener: typeof import("./websocket").addMessageListener; + sendMessage: SendMessage; }; -export function connect({ assetPrefix }: ClientOptions) { - addEventListener((event) => { - switch (event.type) { - case "connected": - handleSocketConnected(); +export function connect({ + // TODO(WEB-1465) Remove this backwards compat fallback once + // vercel/next.js#54586 is merged. + addMessageListener = turboSocketAddMessageListener, + // TODO(WEB-1465) Remove this backwards compat fallback once + // vercel/next.js#54586 is merged. + sendMessage = turboSocketSendMessage, +}: ClientOptions) { + addMessageListener((msg) => { + switch (msg.type) { + case "turbopack-connected": + handleSocketConnected(sendMessage); break; - case "message": - const msg: ServerMessage = JSON.parse(event.message.data); - handleSocketMessage(msg); + default: + handleSocketMessage(msg.data as ServerMessage); break; } }); @@ -28,13 +39,13 @@ export function connect({ assetPrefix }: ClientOptions) { } globalThis.TURBOPACK_CHUNK_UPDATE_LISTENERS = { push: ([chunkPath, callback]: [ChunkPath, UpdateCallback]) => { - subscribeToChunkUpdate(chunkPath, callback); + subscribeToChunkUpdate(chunkPath, sendMessage, callback); }, }; if (Array.isArray(queued)) { for (const [chunkPath, callback] of queued) { - subscribeToChunkUpdate(chunkPath, callback); + subscribeToChunkUpdate(chunkPath, sendMessage, callback); } } } @@ -46,7 +57,7 @@ type UpdateCallbackSet = { const updateCallbackSets: Map = new Map(); -function sendJSON(message: ClientMessage) { +function sendJSON(sendMessage: SendMessage, message: ClientMessage) { sendMessage(JSON.stringify(message)); } @@ -59,23 +70,26 @@ function resourceKey(resource: ResourceIdentifier): ResourceKey { }); } -function subscribeToUpdates(resource: ResourceIdentifier): () => void { - sendJSON({ - type: "subscribe", +function subscribeToUpdates( + sendMessage: SendMessage, + resource: ResourceIdentifier +): () => void { + sendJSON(sendMessage, { + type: "turbopack-subscribe", ...resource, }); return () => { - sendJSON({ - type: "unsubscribe", + sendJSON(sendMessage, { + type: "turbopack-unsubscribe", ...resource, }); }; } -function handleSocketConnected() { +function handleSocketConnected(sendMessage: SendMessage) { for (const key of updateCallbackSets.keys()) { - subscribeToUpdates(JSON.parse(key)); + subscribeToUpdates(sendMessage, JSON.parse(key)); } } @@ -515,29 +529,39 @@ function handleSocketMessage(msg: ServerMessage) { } } -export function subscribeToChunkUpdate( +function subscribeToChunkUpdate( chunkPath: ChunkPath, + sendMessage: SendMessage, callback: UpdateCallback ): () => void { return subscribeToUpdate( { path: chunkPath, }, + sendMessage, callback ); } export function subscribeToUpdate( resource: ResourceIdentifier, + sendMessage: SendMessage, callback: UpdateCallback ) { + // TODO(WEB-1465) Remove this backwards compat fallback once + // vercel/next.js#54586 is merged. + if (callback === undefined) { + callback = sendMessage; + sendMessage = turboSocketSendMessage; + } + const key = resourceKey(resource); let callbackSet: UpdateCallbackSet; const existingCallbackSet = updateCallbackSets.get(key); if (!existingCallbackSet) { callbackSet = { callbacks: new Set([callback]), - unsubscribe: subscribeToUpdates(resource), + unsubscribe: subscribeToUpdates(sendMessage, resource), }; updateCallbackSets.set(key, callbackSet); } else { diff --git a/crates/turbopack-ecmascript-runtime/js/src/dev/client/index.ts b/crates/turbopack-ecmascript-runtime/js/src/dev/client/index.ts new file mode 100644 index 0000000000000..6b455222c37eb --- /dev/null +++ b/crates/turbopack-ecmascript-runtime/js/src/dev/client/index.ts @@ -0,0 +1,2 @@ +export * from "./hmr-client"; +export * from "./websocket"; diff --git a/crates/turbopack-ecmascript-runtime/js/src/dev/client/websocket.ts b/crates/turbopack-ecmascript-runtime/js/src/dev/client/websocket.ts index 53cf2eab42a4f..d7da330bb8173 100644 --- a/crates/turbopack-ecmascript-runtime/js/src/dev/client/websocket.ts +++ b/crates/turbopack-ecmascript-runtime/js/src/dev/client/websocket.ts @@ -1,7 +1,7 @@ // Adapted from https://github.com/vercel/next.js/blob/canary/packages/next/client/dev/error-overlay/websocket.ts let source: WebSocket; -const eventCallbacks: ((event: WebsocketEvent) => void)[] = []; +const eventCallbacks: ((msg: WebSocketMessage) => void)[] = []; // TODO: add timeout again // let lastActivity = Date.now() @@ -17,16 +17,16 @@ function getSocketProtocol(assetPrefix: string): string { return protocol === "http:" ? "ws" : "wss"; } -type WebsocketEvent = +export type WebSocketMessage = | { - type: "connected"; + type: "turbopack-connected"; } | { - type: "message"; - message: MessageEvent; + type: "turbopack-message"; + data: Record; }; -export function addEventListener(cb: (event: WebsocketEvent) => void) { +export function addMessageListener(cb: (msg: WebSocketMessage) => void) { eventCallbacks.push(cb); } @@ -51,10 +51,9 @@ export function connectHMR(options: HMROptions) { console.log("[HMR] connecting..."); function handleOnline() { + const connected = { type: "turbopack-connected" as const }; eventCallbacks.forEach((cb) => { - cb({ - type: "connected", - }); + cb(connected); }); if (options.log) console.log("[HMR] connected"); @@ -64,11 +63,12 @@ export function connectHMR(options: HMROptions) { function handleMessage(event: MessageEvent) { // lastActivity = Date.now() + const message = { + type: "turbopack-message" as const, + data: JSON.parse(event.data), + }; eventCallbacks.forEach((cb) => { - cb({ - type: "message", - message: event, - }); + cb(message); }); } diff --git a/crates/turbopack-ecmascript-runtime/js/src/dev/runtime/base/protocol.d.ts b/crates/turbopack-ecmascript-runtime/js/src/dev/runtime/base/protocol.d.ts index 8e59043ad35fe..1aa3bd258370a 100644 --- a/crates/turbopack-ecmascript-runtime/js/src/dev/runtime/base/protocol.d.ts +++ b/crates/turbopack-ecmascript-runtime/js/src/dev/runtime/base/protocol.d.ts @@ -91,11 +91,11 @@ type ResourceIdentifier = { }; type ClientMessageSubscribe = { - type: "subscribe"; + type: "turbopack-subscribe"; } & ResourceIdentifier; type ClientMessageUnsubscribe = { - type: "unsubscribe"; + type: "turbopack-unsubscribe"; } & ResourceIdentifier; type ClientMessage = ClientMessageSubscribe | ClientMessageUnsubscribe; diff --git a/crates/turbopack-ecmascript-runtime/js/src/main.js b/crates/turbopack-ecmascript-runtime/js/src/main.js new file mode 100644 index 0000000000000..dddce0ffc51a2 --- /dev/null +++ b/crates/turbopack-ecmascript-runtime/js/src/main.js @@ -0,0 +1 @@ +// required for NCC to work