feat(error-overlay): version staleness in Pages Router (#62942)
This commit is contained in:
parent
4c62c3002e
commit
0a73e89880
6 changed files with 47 additions and 38 deletions
|
@ -9,6 +9,7 @@ import { ErrorBoundary } from './ErrorBoundary'
|
|||
import { Base } from '../internal/styles/Base'
|
||||
import { ComponentStyles } from '../internal/styles/ComponentStyles'
|
||||
import { CssReset } from '../internal/styles/CssReset'
|
||||
import type { VersionInfo } from '../../../../server/dev/parse-version-info'
|
||||
|
||||
type RefreshState =
|
||||
| {
|
||||
|
@ -27,6 +28,7 @@ type OverlayState = {
|
|||
buildError: string | null
|
||||
errors: SupportedErrorEvent[]
|
||||
refreshState: RefreshState
|
||||
versionInfo: VersionInfo
|
||||
}
|
||||
|
||||
function pushErrorFilterDuplicates(
|
||||
|
@ -102,6 +104,9 @@ function reducer(state: OverlayState, ev: Bus.BusEvent): OverlayState {
|
|||
return state
|
||||
}
|
||||
}
|
||||
case Bus.TYPE_VERSION_INFO: {
|
||||
return { ...state, versionInfo: ev.versionInfo }
|
||||
}
|
||||
default: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const _: never = ev
|
||||
|
@ -139,6 +144,7 @@ const ReactDevOverlay: React.FunctionComponent<ReactDevOverlayProps> =
|
|||
refreshState: {
|
||||
type: 'idle',
|
||||
},
|
||||
versionInfo: { installed: '0.0.0', staleness: 'unknown' },
|
||||
})
|
||||
|
||||
React.useEffect(() => {
|
||||
|
@ -182,12 +188,16 @@ const ReactDevOverlay: React.FunctionComponent<ReactDevOverlayProps> =
|
|||
<ComponentStyles />
|
||||
|
||||
{displayPrevented ? null : hasBuildError ? (
|
||||
<BuildError message={state.buildError!} />
|
||||
<BuildError
|
||||
message={state.buildError!}
|
||||
versionInfo={state.versionInfo}
|
||||
/>
|
||||
) : hasRuntimeErrors ? (
|
||||
<Errors
|
||||
isAppDir={false}
|
||||
errors={state.errors}
|
||||
initialDisplayState={'fullscreen'}
|
||||
versionInfo={state.versionInfo}
|
||||
/>
|
||||
) : undefined}
|
||||
</ShadowPortal>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import type { StackFrame } from 'next/dist/compiled/stacktrace-parser'
|
||||
import type { ComponentStackFrame } from '../internal/helpers/parse-component-stack'
|
||||
import type { VersionInfo } from '../../../../server/dev/parse-version-info'
|
||||
|
||||
export const TYPE_BUILD_OK = 'build-ok'
|
||||
export const TYPE_BUILD_ERROR = 'build-error'
|
||||
|
@ -7,6 +8,7 @@ export const TYPE_REFRESH = 'fast-refresh'
|
|||
export const TYPE_BEFORE_REFRESH = 'before-fast-refresh'
|
||||
export const TYPE_UNHANDLED_ERROR = 'unhandled-error'
|
||||
export const TYPE_UNHANDLED_REJECTION = 'unhandled-rejection'
|
||||
export const TYPE_VERSION_INFO = 'version-info'
|
||||
|
||||
export type BuildOk = { type: typeof TYPE_BUILD_OK }
|
||||
export type BuildError = {
|
||||
|
@ -26,6 +28,12 @@ export type UnhandledRejection = {
|
|||
reason: Error
|
||||
frames: StackFrame[]
|
||||
}
|
||||
|
||||
export type VersionInfoEvent = {
|
||||
type: typeof TYPE_VERSION_INFO
|
||||
versionInfo: VersionInfo
|
||||
}
|
||||
|
||||
export type BusEvent =
|
||||
| BuildOk
|
||||
| BuildError
|
||||
|
@ -33,6 +41,7 @@ export type BusEvent =
|
|||
| BeforeFastRefresh
|
||||
| UnhandledError
|
||||
| UnhandledRejection
|
||||
| VersionInfoEvent
|
||||
|
||||
export type BusEventHandler = (ev: BusEvent) => void
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
hydrationErrorState,
|
||||
patchConsoleError,
|
||||
} from '../internal/helpers/hydration-error-info'
|
||||
import type { VersionInfo } from '../../../../server/dev/parse-version-info'
|
||||
|
||||
// Patch console.error to collect information about hydration errors
|
||||
patchConsoleError()
|
||||
|
@ -121,6 +122,10 @@ function onBeforeRefresh() {
|
|||
Bus.emit({ type: Bus.TYPE_BEFORE_REFRESH })
|
||||
}
|
||||
|
||||
function onVersionInfo(versionInfo: VersionInfo) {
|
||||
Bus.emit({ type: Bus.TYPE_VERSION_INFO, versionInfo })
|
||||
}
|
||||
|
||||
export { getErrorByType } from '../internal/helpers/getErrorByType'
|
||||
export { getServerError } from '../internal/helpers/nodeStackFrames'
|
||||
export { default as ReactDevOverlay } from './ReactDevOverlay'
|
||||
|
@ -131,4 +136,5 @@ export {
|
|||
unregister,
|
||||
onBeforeRefresh,
|
||||
onRefresh,
|
||||
onVersionInfo,
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import {
|
|||
onBuildOk,
|
||||
onBeforeRefresh,
|
||||
onRefresh,
|
||||
onVersionInfo,
|
||||
} from '../../components/react-dev-overlay/pages/client'
|
||||
import stripAnsi from 'next/dist/compiled/strip-ansi'
|
||||
import { addMessageListener, sendMessage } from './websocket'
|
||||
|
@ -278,6 +279,10 @@ function processMessage(obj: HMR_ACTION_TYPES) {
|
|||
}
|
||||
|
||||
const { errors, warnings } = obj
|
||||
|
||||
// Is undefined when it's a 'built' event
|
||||
if ('versionInfo' in obj) onVersionInfo(obj.versionInfo)
|
||||
|
||||
const hasErrors = Boolean(errors && errors.length)
|
||||
if (hasErrors) {
|
||||
sendMessage(
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
check,
|
||||
getBrowserBodyText,
|
||||
getRedboxHeader,
|
||||
getRedboxDescription,
|
||||
getRedboxSource,
|
||||
hasRedbox,
|
||||
renderViaHTTP,
|
||||
|
@ -727,14 +728,9 @@ describe.each([[''], ['/docs']])(
|
|||
)
|
||||
|
||||
expect(await hasRedbox(browser)).toBe(true)
|
||||
expect(await getRedboxHeader(browser)).toMatchInlineSnapshot(`
|
||||
"1 of 1 unhandled error
|
||||
Server Error
|
||||
|
||||
Error: The default export is not a React Component in page: "/hmr/about5"
|
||||
|
||||
This error happened while generating the page. Any console logs will be displayed in the terminal window."
|
||||
`)
|
||||
expect(await getRedboxDescription(browser)).toMatchInlineSnapshot(
|
||||
`"Error: The default export is not a React Component in page: "/hmr/about5""`
|
||||
)
|
||||
|
||||
await next.patchFile(aboutPage, aboutContent)
|
||||
|
||||
|
@ -830,14 +826,9 @@ describe.each([[''], ['/docs']])(
|
|||
)
|
||||
|
||||
expect(await hasRedbox(browser)).toBe(true)
|
||||
expect(await getRedboxHeader(browser)).toMatchInlineSnapshot(`
|
||||
"1 of 1 unhandled error
|
||||
Server Error
|
||||
|
||||
Error: The default export is not a React Component in page: "/hmr/about7"
|
||||
|
||||
This error happened while generating the page. Any console logs will be displayed in the terminal window."
|
||||
`)
|
||||
expect(await getRedboxDescription(browser)).toMatchInlineSnapshot(
|
||||
`"Error: The default export is not a React Component in page: "/hmr/about7""`
|
||||
)
|
||||
|
||||
await next.patchFile(aboutPage, aboutContent)
|
||||
|
||||
|
@ -885,9 +876,7 @@ describe.each([[''], ['/docs']])(
|
|||
)
|
||||
|
||||
expect(await hasRedbox(browser)).toBe(true)
|
||||
expect(await getRedboxHeader(browser)).toMatchInlineSnapshot(
|
||||
`"Failed to compile"`
|
||||
)
|
||||
expect(await getRedboxHeader(browser)).toMatch('Failed to compile')
|
||||
expect(await getRedboxSource(browser)).toMatchInlineSnapshot(`
|
||||
"./components/parse-error.xyz
|
||||
Module parse failed: Unexpected token (3:0)
|
||||
|
@ -949,9 +938,7 @@ describe.each([[''], ['/docs']])(
|
|||
)
|
||||
|
||||
expect(await hasRedbox(browser)).toBe(true)
|
||||
expect(await getRedboxHeader(browser)).toMatchInlineSnapshot(
|
||||
`"Failed to compile"`
|
||||
)
|
||||
expect(await getRedboxHeader(browser)).toMatch('Failed to compile')
|
||||
let redboxSource = await getRedboxSource(browser)
|
||||
|
||||
redboxSource = redboxSource.replace(`${next.testDir}`, '.')
|
||||
|
@ -1023,12 +1010,9 @@ describe.each([[''], ['/docs']])(
|
|||
await browser.elementByCss('#error-in-gip-link').click()
|
||||
|
||||
expect(await hasRedbox(browser)).toBe(true)
|
||||
expect(await getRedboxHeader(browser)).toMatchInlineSnapshot(`
|
||||
"1 of 1 unhandled error
|
||||
Unhandled Runtime Error
|
||||
|
||||
Error: an-expected-error-in-gip"
|
||||
`)
|
||||
expect(await getRedboxDescription(browser)).toMatchInlineSnapshot(
|
||||
`"Error: an-expected-error-in-gip"`
|
||||
)
|
||||
|
||||
await next.patchFile(
|
||||
erroredPage,
|
||||
|
@ -1067,14 +1051,9 @@ describe.each([[''], ['/docs']])(
|
|||
browser = await webdriver(next.url, basePath + '/hmr/error-in-gip')
|
||||
|
||||
expect(await hasRedbox(browser)).toBe(true)
|
||||
expect(await getRedboxHeader(browser)).toMatchInlineSnapshot(`
|
||||
"1 of 1 unhandled error
|
||||
Server Error
|
||||
|
||||
Error: an-expected-error-in-gip
|
||||
|
||||
This error happened while generating the page. Any console logs will be displayed in the terminal window."
|
||||
`)
|
||||
expect(await getRedboxDescription(browser)).toMatchInlineSnapshot(
|
||||
`"Error: an-expected-error-in-gip"`
|
||||
)
|
||||
|
||||
const erroredPage = join('pages', 'hmr', 'error-in-gip.js')
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ function runTests({ isDev }) {
|
|||
if (isDev) {
|
||||
const browser = await webdriver(appPort, '/')
|
||||
expect(await hasRedbox(browser)).toBe(true)
|
||||
expect(await getRedboxHeader(browser)).toBe('Failed to compile')
|
||||
expect(await getRedboxHeader(browser)).toMatch('Failed to compile')
|
||||
const source = await getRedboxSource(browser)
|
||||
if (process.env.TURBOPACK) {
|
||||
expect(source).toMatchInlineSnapshot(`
|
||||
|
|
Loading…
Reference in a new issue