-
Notifications
You must be signed in to change notification settings - Fork 400
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: refactored hydration logging and related tests (#5086)
* feat: refactored hydration logging, related test changes * fix: remove console usage * fix: stringify * fix: reduce nodes to nodeName for browser parity * fix: disable_static_content_optimization test exceptions, logging refactor * fix: changes for bundle size reduction * fix: remove redundant non-prod checks * fix: irregular naming, type guard * fix: review comments * fix: typing for static element comparison * fix: missing util * fix: move pretty print classes to util * fix: naming, attribute prettifier * fix: rename logging function
- Loading branch information
1 parent
f080072
commit 47c46eb
Showing
47 changed files
with
316 additions
and
270 deletions.
There are no files selected for viewing
105 changes: 105 additions & 0 deletions
105
packages/@lwc/engine-core/src/framework/hydration-utils.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/* | ||
* Copyright (c) 2024, Salesforce, Inc. | ||
* All rights reserved. | ||
* SPDX-License-Identifier: MIT | ||
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT | ||
*/ | ||
import { ArrayPush, ArrayJoin, ArraySort, ArrayFrom, isNull, isUndefined } from '@lwc/shared'; | ||
|
||
import { assertNotProd } from './utils'; | ||
|
||
// Errors that occured during the hydration process | ||
let hydrationErrors: Array<HydrationError> = []; | ||
|
||
// These values are the ones from Node.nodeType (https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType) | ||
const enum EnvNodeTypes { | ||
ELEMENT = 1, | ||
TEXT = 3, | ||
COMMENT = 8, | ||
} | ||
|
||
interface HydrationError { | ||
type: string; | ||
serverRendered: any; | ||
clientExpected: any; | ||
} | ||
|
||
export type Classes = Omit<Set<string>, 'add'>; | ||
|
||
/* | ||
Prints attributes as null or "value" | ||
*/ | ||
export function prettyPrintAttribute(attribute: string, value: any): string { | ||
assertNotProd(); // this method should never leak to prod | ||
return `${attribute}=${isNull(value) || isUndefined(value) ? value : `"${value}"`}`; | ||
} | ||
|
||
/* | ||
Sorts and stringifies classes | ||
*/ | ||
export function prettyPrintClasses(classes: Classes) { | ||
assertNotProd(); // this method should never leak to prod | ||
const value = JSON.stringify(ArrayJoin.call(ArraySort.call(ArrayFrom(classes)), ' ')); | ||
return `class=${value}`; | ||
} | ||
|
||
/* | ||
Hydration errors occur before the source node has been fully hydrated, | ||
queue them so they can be logged later against the mounted node. | ||
*/ | ||
export function queueHydrationError(type: string, serverRendered?: any, clientExpected?: any) { | ||
assertNotProd(); // this method should never leak to prod | ||
ArrayPush.call(hydrationErrors, { type, serverRendered, clientExpected }); | ||
} | ||
|
||
/* | ||
Flushes (logs) any queued errors after the source node has been mounted. | ||
*/ | ||
export function flushHydrationErrors(source?: Node | null) { | ||
assertNotProd(); // this method should never leak to prod | ||
for (const hydrationError of hydrationErrors) { | ||
logHydrationWarning( | ||
`Hydration ${hydrationError.type} mismatch on:`, | ||
source, | ||
`\n- rendered on server:`, | ||
hydrationError.serverRendered, | ||
`\n- expected on client:`, | ||
hydrationError.clientExpected || source | ||
); | ||
} | ||
hydrationErrors = []; | ||
} | ||
|
||
export function isTypeElement(node?: Node): node is Element { | ||
const isCorrectType = node?.nodeType === EnvNodeTypes.ELEMENT; | ||
if (process.env.NODE_ENV !== 'production' && !isCorrectType) { | ||
queueHydrationError('node', node); | ||
} | ||
return isCorrectType; | ||
} | ||
|
||
export function isTypeText(node?: Node): node is Text { | ||
const isCorrectType = node?.nodeType === EnvNodeTypes.TEXT; | ||
if (process.env.NODE_ENV !== 'production' && !isCorrectType) { | ||
queueHydrationError('node', node); | ||
} | ||
return isCorrectType; | ||
} | ||
|
||
export function isTypeComment(node?: Node): node is Comment { | ||
const isCorrectType = node?.nodeType === EnvNodeTypes.COMMENT; | ||
if (process.env.NODE_ENV !== 'production' && !isCorrectType) { | ||
queueHydrationError('node', node); | ||
} | ||
return isCorrectType; | ||
} | ||
|
||
/* | ||
logger.ts converts all args to a string, losing object referenences and has | ||
legacy bloat which would have meant more pathing. | ||
*/ | ||
export function logHydrationWarning(...args: any) { | ||
assertNotProd(); // this method should never leak to prod | ||
/* eslint-disable-next-line no-console */ | ||
console.warn('[LWC warn:', ...args); | ||
} |
Oops, something went wrong.