Skip to content

Commit

Permalink
Turbopack react-refresh: perform full reload on runtime error (#62359)
Browse files Browse the repository at this point in the history
Previously, runtime errors would not be recovered from. Like the webpack
implementation, this addresses the issue by performing a full page
reload when recovering from a runtime error.

Test Plan: `test/development/acceptance-app/error-recovery.test.ts --
Error recovery app default stuck error`


Closes PACK-2569
  • Loading branch information
wbinnssmith authored Feb 23, 2024
1 parent 2451af9 commit 749e3c4
Show file tree
Hide file tree
Showing 9 changed files with 31 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@ import {
} from './error-overlay-reducer'
import { parseStack } from '../internal/helpers/parseStack'
import ReactDevOverlay from './ReactDevOverlay'
import {
RuntimeErrorHandler,
useErrorHandler,
} from '../internal/helpers/use-error-handler'
import { useErrorHandler } from '../internal/helpers/use-error-handler'
import { RuntimeErrorHandler } from '../internal/helpers/runtime-error-handler'
import {
useSendMessage,
useTurbopack,
Expand All @@ -42,6 +40,7 @@ import type {
TurbopackMsgToBrowser,
} from '../../../../server/dev/hot-reloader-types'
import { extractModulesFromTurbopackMessage } from '../../../../server/dev/extract-modules-from-turbopack-message'
import { REACT_REFRESH_FULL_RELOAD_FROM_ERROR } from '../../../dev/error-overlay/messages'

interface Dispatcher {
onBuildOk(): void
Expand Down Expand Up @@ -184,9 +183,7 @@ function tryApplyUpdates(
'Fast Refresh requires at least one parent function component in your React tree.'
)
} else if (RuntimeErrorHandler.hadRuntimeError) {
console.warn(
'[Fast Refresh] performing full reload because your application had an unrecoverable error'
)
console.warn(REACT_REFRESH_FULL_RELOAD_FROM_ERROR)
}
performFullReload(err, sendMessage)
return
Expand Down Expand Up @@ -391,6 +388,10 @@ function processMessage(
data: obj.data,
})
dispatcher.onRefresh()
if (RuntimeErrorHandler.hadRuntimeError) {
console.warn(REACT_REFRESH_FULL_RELOAD_FROM_ERROR)
performFullReload(null, sendMessage)
}
reportHmrLatency(sendMessage, updatedModules)
break
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const RuntimeErrorHandler = {
hadRuntimeError: false,
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ import { isNextRouterError } from '../../../is-next-router-error'

export type ErrorHandler = (error: Error) => void

export const RuntimeErrorHandler = {
hadRuntimeError: false,
}

function isHydrationError(error: Error): boolean {
return (
error.message.match(/(hydration|content does not match|did not match)/i) !=
Expand Down
6 changes: 6 additions & 0 deletions packages/next/src/client/dev/error-overlay/hot-dev-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ import type {
TurbopackMsgToBrowser,
} from '../../../server/dev/hot-reloader-types'
import { extractModulesFromTurbopackMessage } from '../../../server/dev/extract-modules-from-turbopack-message'
import { RuntimeErrorHandler } from '../../components/react-dev-overlay/internal/helpers/runtime-error-handler'
import { REACT_REFRESH_FULL_RELOAD_FROM_ERROR } from './messages'
// This alternative WebpackDevServer combines the functionality of:
// https://github.com/webpack/webpack-dev-server/blob/webpack-1/client/index.js
// https://github.com/webpack/webpack/blob/webpack-1/hot/dev-server.js
Expand Down Expand Up @@ -339,6 +341,10 @@ function processMessage(obj: HMR_ACTION_TYPES) {
data: obj.data,
})
}
if (RuntimeErrorHandler.hadRuntimeError) {
console.warn(REACT_REFRESH_FULL_RELOAD_FROM_ERROR)
performFullReload(null)
}
onRefresh()
reportHmrLatency(updatedModules)
break
Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/client/dev/error-overlay/messages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const REACT_REFRESH_FULL_RELOAD_FROM_ERROR =
'[Fast Refresh] performing full reload because your application had an unrecoverable error'
6 changes: 6 additions & 0 deletions packages/next/src/server/dev/hot-reloader-turbopack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import {
getEntryKey,
splitEntryKey,
} from './turbopack/entry-key'
import { FAST_REFRESH_RUNTIME_RELOAD } from './messages'

const wsServer = new ws.Server({ noServer: true })
const isTestMode = !!(
Expand Down Expand Up @@ -542,6 +543,11 @@ export async function createHotReloaderTurbopack(
case 'client-reload-page': // { clientId }
case 'client-removed-page': // { page }
case 'client-full-reload': // { stackTrace, hadRuntimeError }
const { hadRuntimeError } = parsedData
if (hadRuntimeError) {
Log.warn(FAST_REFRESH_RUNTIME_RELOAD)
}
break
case 'client-added-page':
// TODO
break
Expand Down
5 changes: 2 additions & 3 deletions packages/next/src/server/dev/hot-reloader-webpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ import {
import type { HMR_ACTION_TYPES } from './hot-reloader-types'
import type { WebpackError } from 'webpack'
import { PAGE_TYPES } from '../../lib/page-types'
import { FAST_REFRESH_RUNTIME_RELOAD } from './messages'

const MILLISECONDS_IN_NANOSECOND = 1_000_000
const isTestMode = !!(
Expand Down Expand Up @@ -494,9 +495,7 @@ export default class HotReloaderWebpack implements NextJsHotReloaderInterface {
}

if (hadRuntimeError) {
Log.warn(
`Fast Refresh had to perform a full reload due to a runtime error.`
)
Log.warn(FAST_REFRESH_RUNTIME_RELOAD)
break
}

Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/server/dev/messages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const FAST_REFRESH_RUNTIME_RELOAD =
'Fast Refresh had to perform a full reload due to a runtime error.'
3 changes: 2 additions & 1 deletion test/turbopack-tests-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -1083,9 +1083,10 @@
"Error recovery app turbo render error not shown right after syntax error",
"Error recovery app turbo server component can recover from a component error",
"Error recovery app turbo server component can recover from syntax error",
"Error recovery app turbo stuck error",
"Error recovery app turbo syntax > runtime error"
],
"failed": ["Error recovery app turbo stuck error"],
"failed": [],
"pending": [
"Error recovery app default can recover from a event handler error",
"Error recovery app default can recover from a syntax error without losing state",
Expand Down

0 comments on commit 749e3c4

Please sign in to comment.