Skip to content

Commit e42f4d3

Browse files
committed
Add error handling and extract common logic
1 parent 67c6171 commit e42f4d3

File tree

3 files changed

+94
-53
lines changed

3 files changed

+94
-53
lines changed

packages/next/src/server/dev/hot-reloader-turbopack.ts

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,11 @@ import { recordMcpTelemetry } from '../mcp/mcp-telemetry-tracker'
125125
import { getFileLogger } from './browser-logs/file-logger'
126126
import type { ServerCacheStatus } from '../../next-devtools/dev-overlay/cache-indicator'
127127
import type { Lockfile } from '../../build/lockfile'
128-
import { streamToUint8Array } from '../stream-utils/node-web-streams-helper'
128+
import {
129+
sendSerializedErrorsToClient,
130+
sendSerializedErrorsToClientForHtmlRequest,
131+
setErrorsRscStreamForHtmlRequest,
132+
} from './serialized-errors'
129133

130134
const wsServer = new ws.Server({ noServer: true })
131135
const isTestMode = !!(
@@ -444,10 +448,6 @@ export async function createHotReloaderTurbopack(
444448
const clientsWithoutHtmlRequestId = new Set<ws>()
445449
const clientsByHtmlRequestId = new Map<string, ws>()
446450
const cacheStatusesByHtmlRequestId = new Map<string, ServerCacheStatus>()
447-
const errorsRscStreamsByHtmlRequestId = new Map<
448-
string,
449-
ReadableStream<Uint8Array>
450-
>()
451451
const clientStates = new WeakMap<ws, ClientState>()
452452

453453
function sendToClient(client: ws, message: HmrMessageSentToBrowser) {
@@ -917,17 +917,10 @@ export async function createHotReloaderTurbopack(
917917
sendToClient.bind(null, client)
918918
)
919919

920-
const errorsRscStream =
921-
errorsRscStreamsByHtmlRequestId.get(htmlRequestId)
922-
if (errorsRscStream !== undefined) {
923-
streamToUint8Array(errorsRscStream).then((serializedErrors) =>
924-
sendToClient(client, {
925-
type: HMR_MESSAGE_SENT_TO_BROWSER.ERRORS_TO_SHOW_IN_BROWSER,
926-
serializedErrors,
927-
})
928-
)
929-
errorsRscStreamsByHtmlRequestId.delete(htmlRequestId)
930-
}
920+
sendSerializedErrorsToClientForHtmlRequest(
921+
htmlRequestId,
922+
sendToClient.bind(null, client)
923+
)
931924
} else {
932925
clientsWithoutHtmlRequestId.add(client)
933926
onUpgrade(client, { isLegacyClient: true })
@@ -1201,17 +1194,17 @@ export async function createHotReloaderTurbopack(
12011194

12021195
sendErrorsToBrowser(errorsRscStream, htmlRequestId) {
12031196
const client = clientsByHtmlRequestId.get(htmlRequestId)
1204-
if (client !== undefined) {
1205-
streamToUint8Array(errorsRscStream).then((serializedErrors) => {
1206-
sendToClient(client, {
1207-
type: HMR_MESSAGE_SENT_TO_BROWSER.ERRORS_TO_SHOW_IN_BROWSER,
1208-
serializedErrors,
1209-
})
1210-
})
1197+
1198+
if (client) {
1199+
// If the client is connected, we can send the errors immediately.
1200+
sendSerializedErrorsToClient(
1201+
errorsRscStream,
1202+
sendToClient.bind(null, client)
1203+
)
12111204
} else {
1212-
// If the client is not connected, store the errors stream so that we
1213-
// can send it when the client connects.
1214-
errorsRscStreamsByHtmlRequestId.set(htmlRequestId, errorsRscStream)
1205+
// Otherwise, store the errors stream so that we can send it when the
1206+
// client connects.
1207+
setErrorsRscStreamForHtmlRequest(htmlRequestId, errorsRscStream)
12151208
}
12161209
},
12171210

packages/next/src/server/dev/hot-reloader-webpack.ts

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,11 @@ import { recordMcpTelemetry } from '../mcp/mcp-telemetry-tracker'
114114
import { getFileLogger } from './browser-logs/file-logger'
115115
import type { ServerCacheStatus } from '../../next-devtools/dev-overlay/cache-indicator'
116116
import type { Lockfile } from '../../build/lockfile'
117-
import { streamToUint8Array } from '../stream-utils/node-web-streams-helper'
117+
import {
118+
sendSerializedErrorsToClient,
119+
sendSerializedErrorsToClientForHtmlRequest,
120+
setErrorsRscStreamForHtmlRequest,
121+
} from './serialized-errors'
118122

119123
const MILLISECONDS_IN_NANOSECOND = BigInt(1_000_000)
120124

@@ -259,10 +263,6 @@ export default class HotReloaderWebpack implements NextJsHotReloaderInterface {
259263
private reloadAfterInvalidation: boolean = false
260264
private isSrcDir: boolean
261265
private cacheStatusesByRequestId = new Map<string, ServerCacheStatus>()
262-
private errorsRscStreamsByHtmlRequestId = new Map<
263-
string,
264-
ReadableStream<Uint8Array>
265-
>()
266266

267267
public serverStats: webpack.Stats | null
268268
public edgeServerStats: webpack.Stats | null
@@ -651,6 +651,11 @@ export default class HotReloaderWebpack implements NextJsHotReloaderInterface {
651651
this.sendToClient.bind(this, client)
652652
)
653653

654+
sendSerializedErrorsToClientForHtmlRequest(
655+
htmlRequestId,
656+
this.sendToClient.bind(this, client)
657+
)
658+
654659
if (enableCacheComponents) {
655660
const status = this.cacheStatusesByRequestId.get(htmlRequestId)
656661
if (status) {
@@ -661,18 +666,6 @@ export default class HotReloaderWebpack implements NextJsHotReloaderInterface {
661666
this.cacheStatusesByRequestId.delete(htmlRequestId)
662667
}
663668
}
664-
665-
const errorsRscStream =
666-
this.errorsRscStreamsByHtmlRequestId.get(htmlRequestId)
667-
if (errorsRscStream !== undefined) {
668-
streamToUint8Array(errorsRscStream).then((serializedErrors) => {
669-
this.sendToClient(client, {
670-
type: HMR_MESSAGE_SENT_TO_BROWSER.ERRORS_TO_SHOW_IN_BROWSER,
671-
serializedErrors,
672-
})
673-
})
674-
this.errorsRscStreamsByHtmlRequestId.delete(htmlRequestId)
675-
}
676669
}
677670

678671
client.on('close', () => {
@@ -1821,17 +1814,17 @@ export default class HotReloaderWebpack implements NextJsHotReloaderInterface {
18211814
htmlRequestId: string
18221815
): void {
18231816
const client = this.webpackHotMiddleware?.getClient(htmlRequestId)
1824-
if (client !== undefined) {
1825-
streamToUint8Array(errorsRscStream).then((serializedErrors) => {
1826-
this.sendToClient(client, {
1827-
type: HMR_MESSAGE_SENT_TO_BROWSER.ERRORS_TO_SHOW_IN_BROWSER,
1828-
serializedErrors,
1829-
})
1830-
})
1817+
1818+
if (client) {
1819+
// If the client is connected, we can send the errors immediately.
1820+
sendSerializedErrorsToClient(
1821+
errorsRscStream,
1822+
this.sendToClient.bind(this, client)
1823+
)
18311824
} else {
1832-
// If the client is not connected, store the errors stream so that we can
1833-
// send it when the client connects.
1834-
this.errorsRscStreamsByHtmlRequestId.set(htmlRequestId, errorsRscStream)
1825+
// Otherwise, store the errors stream so that we can send it when the
1826+
// client connects.
1827+
setErrorsRscStreamForHtmlRequest(htmlRequestId, errorsRscStream)
18351828
}
18361829
}
18371830

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { streamToUint8Array } from '../stream-utils/node-web-streams-helper'
2+
import {
3+
HMR_MESSAGE_SENT_TO_BROWSER,
4+
type HmrMessageSentToBrowser,
5+
} from './hot-reloader-types'
6+
7+
const errorsRscStreamsByHtmlRequestId = new Map<
8+
string,
9+
ReadableStream<Uint8Array>
10+
>()
11+
12+
export function sendSerializedErrorsToClient(
13+
errorsRscStream: ReadableStream<Uint8Array>,
14+
sendToClient: (message: HmrMessageSentToBrowser) => void
15+
) {
16+
streamToUint8Array(errorsRscStream).then(
17+
(serializedErrors) => {
18+
sendToClient({
19+
type: HMR_MESSAGE_SENT_TO_BROWSER.ERRORS_TO_SHOW_IN_BROWSER,
20+
serializedErrors,
21+
})
22+
},
23+
(err) => {
24+
console.error(new Error('Failed to serialize errors.', { cause: err }))
25+
}
26+
)
27+
}
28+
29+
export function sendSerializedErrorsToClientForHtmlRequest(
30+
htmlRequestId: string,
31+
sendToClient: (message: HmrMessageSentToBrowser) => void
32+
) {
33+
const errorsRscStream = errorsRscStreamsByHtmlRequestId.get(htmlRequestId)
34+
35+
if (!errorsRscStream) {
36+
return
37+
}
38+
39+
errorsRscStreamsByHtmlRequestId.delete(htmlRequestId)
40+
41+
sendSerializedErrorsToClient(errorsRscStream, sendToClient)
42+
}
43+
44+
export function setErrorsRscStreamForHtmlRequest(
45+
htmlRequestId: string,
46+
errorsRscStream: ReadableStream<Uint8Array>
47+
) {
48+
// TODO: Clean up after a timeout, in case the client never connects, e.g.
49+
// when CURL'ing the page, or loading the page with JavaScript disabled etc.
50+
errorsRscStreamsByHtmlRequestId.set(htmlRequestId, errorsRscStream)
51+
}
52+
53+
export function deleteErrorsRscStreamForHtmlRequest(htmlRequestId: string) {
54+
errorsRscStreamsByHtmlRequestId.delete(htmlRequestId)
55+
}

0 commit comments

Comments
 (0)