From ab617d57d758f5014b6551a65b88ae05bbc0801d Mon Sep 17 00:00:00 2001 From: Alexander Dmitryuk Date: Fri, 6 Dec 2024 14:21:46 +0500 Subject: [PATCH 1/2] fix(hydration): don't error if data-allow-mismatch provided for fragment --- .../runtime-core/__tests__/hydration.spec.ts | 4 ++ packages/runtime-core/src/hydration.ts | 10 +++-- scripts/setup-vitest.ts | 43 +++++++++++++++++++ 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/packages/runtime-core/__tests__/hydration.spec.ts b/packages/runtime-core/__tests__/hydration.spec.ts index 56011d06359..628ed7ff6f9 100644 --- a/packages/runtime-core/__tests__/hydration.spec.ts +++ b/packages/runtime-core/__tests__/hydration.spec.ts @@ -668,6 +668,7 @@ describe('SSR hydration', () => { ) expect(teleportContainer.innerHTML).toBe('Teleported Comp1') expect(`Hydration children mismatch`).toHaveBeenWarned() + expect(`Hydration completed but contains mismatches.`).toHaveBeenErrored() toggle.value = false await nextTick() @@ -2271,6 +2272,9 @@ describe('SSR hydration', () => { '
foo
bar
baz
', ) expect(`Hydration node mismatch`).not.toHaveBeenWarned() + expect( + `Hydration completed but contains mismatches.`, + ).not.toHaveBeenErrored() }) test('fragment too many children', () => { diff --git a/packages/runtime-core/src/hydration.ts b/packages/runtime-core/src/hydration.ts index a94ff356810..a13b81aab3d 100644 --- a/packages/runtime-core/src/hydration.ts +++ b/packages/runtime-core/src/hydration.ts @@ -56,7 +56,7 @@ export enum DOMNodeTypes { let hasLoggedMismatchError = false const logMismatchError = () => { - if (__TEST__ || hasLoggedMismatchError) { + if (hasLoggedMismatchError) { return } // this error should show up in production @@ -657,9 +657,11 @@ export function createHydrationFunctions( if (next && isComment(next) && next.data === ']') { return nextSibling((vnode.anchor = next)) } else { - // fragment didn't hydrate successfully, since we didn't get a end anchor - // back. This should have led to node/children mismatch warnings. - logMismatchError() + if (!isMismatchAllowed(node.parentElement, 1 /* CHILDREN */)) { + // fragment didn't hydrate successfully, since we didn't get a end anchor + // back. This should have led to node/children mismatch warnings. + logMismatchError() + } // since the anchor is missing, we need to create one and insert it insert((vnode.anchor = createComment(`]`)), container, next) diff --git a/scripts/setup-vitest.ts b/scripts/setup-vitest.ts index 08203572aff..fb8653b2130 100644 --- a/scripts/setup-vitest.ts +++ b/scripts/setup-vitest.ts @@ -6,6 +6,7 @@ declare module 'vitest' { } interface CustomMatchers { + toHaveBeenErrored(): R toHaveBeenWarned(): R toHaveBeenWarnedLast(): R toHaveBeenWarnedTimes(n: number): R @@ -14,6 +15,27 @@ interface CustomMatchers { vi.stubGlobal('MathMLElement', class MathMLElement {}) expect.extend({ + toHaveBeenErrored(received: string) { + const passed = error.mock.calls.some(args => args[0].includes(received)) + if (passed) { + asserted.add(received) + return { + pass: true, + message: () => `expected "${received}" not to have been errored.`, + } + } else { + const msgs = error.mock.calls.map(args => args[0]).join('\n - ') + return { + pass: false, + message: () => + `expected "${received}" to have been errored` + + (msgs.length + ? `.\n\nActual messages:\n\n - ${msgs}` + : ` but no error was recorded.`), + } + } + }, + toHaveBeenWarned(received: string) { const passed = warn.mock.calls.some(args => args[0].includes(received)) if (passed) { @@ -79,16 +101,21 @@ expect.extend({ }) let warn: MockInstance +let error: MockInstance const asserted: Set = new Set() beforeEach(() => { asserted.clear() warn = vi.spyOn(console, 'warn') warn.mockImplementation(() => {}) + + error = vi.spyOn(console, 'error') + error.mockImplementation(() => {}) }) afterEach(() => { const assertedArray = Array.from(asserted) + const nonAssertedWarnings = warn.mock.calls .map(args => args[0]) .filter(received => { @@ -104,4 +131,20 @@ afterEach(() => { )}`, ) } + + const nonAssertedErrors = error.mock.calls + .map(args => args[0]) + .filter(received => { + return !assertedArray.some(assertedMsg => { + return received.includes(assertedMsg) + }) + }) + error.mockRestore() + if (nonAssertedErrors.length) { + throw new Error( + `test case threw unexpected errors:\n - ${nonAssertedErrors.join( + '\n - ', + )}`, + ) + } }) From 9e909466d103658fac2604f72e9287e8559edc90 Mon Sep 17 00:00:00 2001 From: daiwei Date: Mon, 9 Dec 2024 11:23:21 +0800 Subject: [PATCH 2/2] chore: minor tweaks --- packages/runtime-core/src/hydration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime-core/src/hydration.ts b/packages/runtime-core/src/hydration.ts index a13b81aab3d..242d41a7c2b 100644 --- a/packages/runtime-core/src/hydration.ts +++ b/packages/runtime-core/src/hydration.ts @@ -657,7 +657,7 @@ export function createHydrationFunctions( if (next && isComment(next) && next.data === ']') { return nextSibling((vnode.anchor = next)) } else { - if (!isMismatchAllowed(node.parentElement, 1 /* CHILDREN */)) { + if (!isMismatchAllowed(container, MismatchTypes.CHILDREN)) { // fragment didn't hydrate successfully, since we didn't get a end anchor // back. This should have led to node/children mismatch warnings. logMismatchError()