From 4b652df204507962033784827778497042fe3916 Mon Sep 17 00:00:00 2001
From: Jiachi Liu
Date: Sat, 16 Mar 2024 17:49:04 +0100
Subject: [PATCH 1/3] fix(error-overlay): matching html tag with brackets in
hydration error
---
.../RuntimeError/component-stack-pseudo-html.tsx | 5 ++++-
.../internal/container/RuntimeError/index.tsx | 2 +-
.../internal/helpers/hydration-error-info.ts | 11 +++++------
3 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/component-stack-pseudo-html.tsx b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/component-stack-pseudo-html.tsx
index db1bec664331a..88501559a8a6d 100644
--- a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/component-stack-pseudo-html.tsx
+++ b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/component-stack-pseudo-html.tsx
@@ -73,7 +73,10 @@ export function PseudoHtmlDiff({
const [isHtmlCollapsed, toggleCollapseHtml] = useState(shouldCollapse)
const htmlComponents = useMemo(() => {
- const tagNames = isHtmlTagsWarning ? [firstContent, secondContent] : []
+ const tagNames = isHtmlTagsWarning
+ ? // tags could have < or > in the name, so we always remove them to match
+ [firstContent.replace(/<|>/g, ''), secondContent.replace(/<|>/g, '')]
+ : []
const nestedHtmlStack: React.ReactNode[] = []
let lastText = ''
diff --git a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx
index 9f72196cf5889..8e9ec07791633 100644
--- a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx
+++ b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx
@@ -216,6 +216,6 @@ export const styles = css`
font-size: 0;
}
[data-nextjs-container-errors-pseudo-html--tag-adjacent='false'] {
- color: var(--color-accents-3);
+ color: var(--color-accents-1);
}
`
diff --git a/packages/next/src/client/components/react-dev-overlay/internal/helpers/hydration-error-info.ts b/packages/next/src/client/components/react-dev-overlay/internal/helpers/hydration-error-info.ts
index bfd111d268762..78fd18aa89d4b 100644
--- a/packages/next/src/client/components/react-dev-overlay/internal/helpers/hydration-error-info.ts
+++ b/packages/next/src/client/components/react-dev-overlay/internal/helpers/hydration-error-info.ts
@@ -20,9 +20,9 @@ const isHtmlTagsWarning = (msg: NullableText) =>
Boolean(msg && htmlTagsWarnings.has(msg))
const isTextMismatchWarning = (msg: NullableText) =>
- Boolean(msg && textMismatchWarnings.has(msg))
+ Boolean(msg && textMismatchWarning === msg)
const isTextInTagsMismatchWarning = (msg: NullableText) =>
- Boolean(msg && textInTagsMismatchWarnings.has(msg))
+ Boolean(msg && textAndTagsMismatchWarnings.has(msg))
const isKnownHydrationWarning = (msg: NullableText) =>
isHtmlTagsWarning(msg) ||
@@ -37,13 +37,12 @@ const htmlTagsWarnings = new Set([
'Warning: Expected server HTML to contain a matching <%s> in <%s>.%s',
'Warning: Did not expect server HTML to contain a <%s> in <%s>.%s',
])
-const textInTagsMismatchWarnings = new Set([
+const textAndTagsMismatchWarnings = new Set([
'Warning: Expected server HTML to contain a matching text node for "%s" in <%s>.%s',
'Warning: Did not expect server HTML to contain the text node "%s" in <%s>.%s',
])
-const textMismatchWarnings = new Set([
- 'Warning: Text content did not match. Server: "%s" Client: "%s"%s',
-])
+const textMismatchWarning =
+ 'Warning: Text content did not match. Server: "%s" Client: "%s"%s'
/**
* Patch console.error to capture hydration errors.
From 6d323da56b48dffab936f9fc42145cd90cc28cca Mon Sep 17 00:00:00 2001
From: Jiachi Liu
Date: Sat, 16 Mar 2024 18:08:01 +0100
Subject: [PATCH 2/3] add test
---
.../acceptance-app/hydration-error.test.ts | 65 ++++++++++++++++++-
1 file changed, 64 insertions(+), 1 deletion(-)
diff --git a/test/development/acceptance-app/hydration-error.test.ts b/test/development/acceptance-app/hydration-error.test.ts
index 3465997d0d7dc..30818fc4b08c9 100644
--- a/test/development/acceptance-app/hydration-error.test.ts
+++ b/test/development/acceptance-app/hydration-error.test.ts
@@ -349,7 +349,7 @@ describe('Error overlay for hydration errors', () => {
await cleanup()
})
- it('should only show one hydration error when bad nesting happened', async () => {
+ it('should only show one hydration error when bad nesting happened - p under p', async () => {
const { cleanup, session, browser } = await sandbox(
next,
new Map([
@@ -412,6 +412,69 @@ describe('Error overlay for hydration errors', () => {
await cleanup()
})
+ it('should only show one hydration error when bad nesting happened - div under p', async () => {
+ const { cleanup, session, browser } = await sandbox(
+ next,
+ new Map([
+ [
+ 'app/page.js',
+ outdent`
+ 'use client'
+
+ export default function Page() {
+ return (
+
+
Nested div under p tag
+
+ )
+ }
+ `,
+ ],
+ ])
+ )
+
+ await session.waitForAndOpenRuntimeError()
+ expect(await session.hasRedbox()).toBe(true)
+
+ const totalErrorCount = await browser
+ .elementByCss('[data-nextjs-dialog-header-total-count]')
+ .text()
+ expect(totalErrorCount).toBe('1')
+
+ const description = await session.getRedboxDescription()
+ expect(description).toContain(
+ 'Error: Hydration failed because the initial UI does not match what was rendered on the server.'
+ )
+ const warning = await session.getRedboxDescriptionWarning()
+ expect(warning).toContain(
+ 'In HTML, cannot be a descendant of
.\nThis will cause a hydration error.'
+ )
+
+ const pseudoHtml = await session.getRedboxComponentStack()
+
+ // Turbopack currently has longer component stack trace
+ if (isTurbopack) {
+ expect(pseudoHtml).toMatchInlineSnapshot(`
+ "...
+
+
+ ^^^
+
+ ^^^^^"
+ `)
+ } else {
+ expect(pseudoHtml).toMatchInlineSnapshot(`
+ "
+
+ ^^^
+
+ ^^^^^"
+ `)
+ }
+
+ await cleanup()
+ })
+
it('should show the highlighted bad nesting html snippet when bad nesting happened', async () => {
const { cleanup, session } = await sandbox(
next,
From d0c5c595c718faac52cc64342abc150f831fc244 Mon Sep 17 00:00:00 2001
From: Jiachi Liu
Date: Sat, 16 Mar 2024 18:26:57 +0100
Subject: [PATCH 3/3] simplify
---
.../react-dev-overlay/internal/helpers/hydration-error-info.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/packages/next/src/client/components/react-dev-overlay/internal/helpers/hydration-error-info.ts b/packages/next/src/client/components/react-dev-overlay/internal/helpers/hydration-error-info.ts
index 78fd18aa89d4b..0832cd19b31f6 100644
--- a/packages/next/src/client/components/react-dev-overlay/internal/helpers/hydration-error-info.ts
+++ b/packages/next/src/client/components/react-dev-overlay/internal/helpers/hydration-error-info.ts
@@ -19,8 +19,7 @@ export const getHydrationWarningType = (
const isHtmlTagsWarning = (msg: NullableText) =>
Boolean(msg && htmlTagsWarnings.has(msg))
-const isTextMismatchWarning = (msg: NullableText) =>
- Boolean(msg && textMismatchWarning === msg)
+const isTextMismatchWarning = (msg: NullableText) => textMismatchWarning === msg
const isTextInTagsMismatchWarning = (msg: NullableText) =>
Boolean(msg && textAndTagsMismatchWarnings.has(msg))