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