From ec300df5d797964d2626c28e2503ef40155f31d8 Mon Sep 17 00:00:00 2001 From: Sebastian Sebbie Silbermann Date: Mon, 2 Jun 2025 10:05:43 +0200 Subject: [PATCH] [dev-overlay] Inject `getSquashedHydrationErrorDetails` implementation --- .../react-dev-overlay/app/app-dev-overlay.tsx | 11 ++++++++++- .../pages/hydration-error-state.ts | 2 +- .../pages/pages-dev-overlay.tsx | 7 ++++++- .../errors/error-overlay/error-overlay.tsx | 4 ++++ .../react-dev-overlay/ui/container/errors.tsx | 16 ++++++++++++---- .../react-dev-overlay/ui/dev-overlay.stories.tsx | 13 ++++++++++++- .../react-dev-overlay/ui/dev-overlay.tsx | 6 ++++++ 7 files changed, 51 insertions(+), 8 deletions(-) diff --git a/packages/next/src/client/components/react-dev-overlay/app/app-dev-overlay.tsx b/packages/next/src/client/components/react-dev-overlay/app/app-dev-overlay.tsx index bb1b74b51ea4..80110ed2b045 100644 --- a/packages/next/src/client/components/react-dev-overlay/app/app-dev-overlay.tsx +++ b/packages/next/src/client/components/react-dev-overlay/app/app-dev-overlay.tsx @@ -72,6 +72,11 @@ function ReplaySsrOnlyErrors({ return null } +function getSquashedHydrationErrorDetails() { + // We don't squash hydration errors in the App Router. + return null +} + export function AppDevOverlay({ state, dispatch, @@ -99,7 +104,11 @@ export function AppDevOverlay({ <> {/* Fonts can only be loaded outside the Shadow DOM. */} - + ) diff --git a/packages/next/src/client/components/react-dev-overlay/pages/hydration-error-state.ts b/packages/next/src/client/components/react-dev-overlay/pages/hydration-error-state.ts index 85919f946f08..1529529ce928 100644 --- a/packages/next/src/client/components/react-dev-overlay/pages/hydration-error-state.ts +++ b/packages/next/src/client/components/react-dev-overlay/pages/hydration-error-state.ts @@ -8,7 +8,7 @@ import { isErrorMessageWithComponentStackDiff as isReact19HydrationWarning, } from '../../react-19-hydration-error' -type HydrationErrorState = { +export type HydrationErrorState = { // Hydration warning template format: warning?: string reactOutputComponentDiff?: string diff --git a/packages/next/src/client/components/react-dev-overlay/pages/pages-dev-overlay.tsx b/packages/next/src/client/components/react-dev-overlay/pages/pages-dev-overlay.tsx index f27060928a7d..5709bd316ad5 100644 --- a/packages/next/src/client/components/react-dev-overlay/pages/pages-dev-overlay.tsx +++ b/packages/next/src/client/components/react-dev-overlay/pages/pages-dev-overlay.tsx @@ -2,6 +2,7 @@ import { PagesDevOverlayErrorBoundary } from './pages-dev-overlay-error-boundary import { usePagesDevOverlay } from './hooks' import { FontStyles } from '../font/font-styles' import { DevOverlay } from '../ui/dev-overlay' +import { getSquashedHydrationErrorDetails } from './hydration-error-state' export type ErrorType = 'runtime' | 'build' @@ -22,7 +23,11 @@ export function PagesDevOverlay({ children }: PagesDevOverlayProps) { {/* Fonts can only be loaded outside the Shadow DOM. */} - + ) } diff --git a/packages/next/src/client/components/react-dev-overlay/ui/components/errors/error-overlay/error-overlay.tsx b/packages/next/src/client/components/react-dev-overlay/ui/components/errors/error-overlay/error-overlay.tsx index 150fc2820569..a6593902c3ca 100644 --- a/packages/next/src/client/components/react-dev-overlay/ui/components/errors/error-overlay/error-overlay.tsx +++ b/packages/next/src/client/components/react-dev-overlay/ui/components/errors/error-overlay/error-overlay.tsx @@ -9,6 +9,7 @@ import { BuildError } from '../../../container/build-error' import { Errors } from '../../../container/errors' import { useDelayedRender } from '../../../hooks/use-delayed-render' import type { ReadyRuntimeError } from '../../../../utils/get-error-by-type' +import type { HydrationErrorState } from '../../../../pages/hydration-error-state' const transitionDurationMs = 200 @@ -23,11 +24,13 @@ export interface ErrorBaseProps { export function ErrorOverlay({ state, dispatch, + getSquashedHydrationErrorDetails, runtimeErrors, errorCount, }: { state: OverlayState dispatch: OverlayDispatch + getSquashedHydrationErrorDetails: (error: Error) => HydrationErrorState | null runtimeErrors: ReadyRuntimeError[] errorCount: number }) { @@ -74,6 +77,7 @@ export function ErrorOverlay({ { dispatch({ type: ACTION_ERROR_OVERLAY_CLOSE }) diff --git a/packages/next/src/client/components/react-dev-overlay/ui/container/errors.tsx b/packages/next/src/client/components/react-dev-overlay/ui/container/errors.tsx index 3b8257e9b39d..70031ee92ab2 100644 --- a/packages/next/src/client/components/react-dev-overlay/ui/container/errors.tsx +++ b/packages/next/src/client/components/react-dev-overlay/ui/container/errors.tsx @@ -17,9 +17,10 @@ import { } from '../../../react-19-hydration-error' import type { ReadyRuntimeError } from '../../utils/get-error-by-type' import type { ErrorBaseProps } from '../components/errors/error-overlay/error-overlay' -import { getSquashedHydrationErrorDetails } from '../../pages/hydration-error-state' +import type { HydrationErrorState } from '../../pages/hydration-error-state' export interface ErrorsProps extends ErrorBaseProps { + getSquashedHydrationErrorDetails: (error: Error) => HydrationErrorState | null runtimeErrors: ReadyRuntimeError[] debugInfo: DebugInfo onClose: () => void @@ -72,7 +73,10 @@ const noErrorDetails = { notes: null, reactOutputComponentDiff: null, } -function useErrorDetails(error: Error | undefined): { +function useErrorDetails( + error: Error | undefined, + getSquashedHydrationErrorDetails: (error: Error) => HydrationErrorState | null +): { hydrationWarning: string | null notes: string | null reactOutputComponentDiff: string | null @@ -106,10 +110,11 @@ function useErrorDetails(error: Error | undefined): { notes, reactOutputComponentDiff: diff, } - }, [error]) + }, [error, getSquashedHydrationErrorDetails]) } export function Errors({ + getSquashedHydrationErrorDetails, runtimeErrors, debugInfo, onClose, @@ -127,7 +132,10 @@ export function Errors({ () => runtimeErrors[activeIdx] ?? null, [activeIdx, runtimeErrors] ) - const errorDetails = useErrorDetails(activeError?.error) + const errorDetails = useErrorDetails( + activeError?.error, + getSquashedHydrationErrorDetails + ) if (isLoading) { // TODO: better loading state diff --git a/packages/next/src/client/components/react-dev-overlay/ui/dev-overlay.stories.tsx b/packages/next/src/client/components/react-dev-overlay/ui/dev-overlay.stories.tsx index b3dababe5a51..ded4b633acf3 100644 --- a/packages/next/src/client/components/react-dev-overlay/ui/dev-overlay.stories.tsx +++ b/packages/next/src/client/components/react-dev-overlay/ui/dev-overlay.stories.tsx @@ -102,6 +102,10 @@ function useOverlayReducer() { }, initialState) } +function getNoSquashedHydrationErrorDetails() { + return null +} + export const Default: Story = { render: function DevOverlayStory() { const [state, dispatch] = useOverlayReducer() @@ -115,7 +119,14 @@ export const Default: Story = { objectFit: 'contain', }} /> - + ) }, diff --git a/packages/next/src/client/components/react-dev-overlay/ui/dev-overlay.tsx b/packages/next/src/client/components/react-dev-overlay/ui/dev-overlay.tsx index 1bde4b5e491c..d5304ccad2e4 100644 --- a/packages/next/src/client/components/react-dev-overlay/ui/dev-overlay.tsx +++ b/packages/next/src/client/components/react-dev-overlay/ui/dev-overlay.tsx @@ -10,13 +10,16 @@ import { DevToolsIndicator } from './components/errors/dev-tools-indicator/dev-t import { RenderError } from './container/runtime-error/render-error' import { DarkTheme } from './styles/dark-theme' import { useDevToolsScale } from './components/errors/dev-tools-indicator/dev-tools-info/preferences' +import type { HydrationErrorState } from '../pages/hydration-error-state' export function DevOverlay({ state, dispatch, + getSquashedHydrationErrorDetails, }: { state: OverlayState dispatch: OverlayDispatch + getSquashedHydrationErrorDetails: (error: Error) => HydrationErrorState | null }) { const [scale, setScale] = useDevToolsScale() return ( @@ -46,6 +49,9 @@ export function DevOverlay({