diff --git a/packages/@lwc/engine-core/src/framework/hydration.ts b/packages/@lwc/engine-core/src/framework/hydration.ts index 74904e1482..fb8b62ef23 100644 --- a/packages/@lwc/engine-core/src/framework/hydration.ts +++ b/packages/@lwc/engine-core/src/framework/hydration.ts @@ -19,7 +19,10 @@ import { isAPIFeatureEnabled, isFalse, StringSplit, - parseStyleText, ArrayFrom, ArraySort, StringTrim, + parseStyleText, + ArrayFrom, + ArraySort, + ArrayFilter, } from '@lwc/shared'; import { logWarn } from '../shared/logger'; @@ -47,7 +50,6 @@ import { VCustomElement, VStatic, VFragment, - isVCustomElement, VElementData, VStaticPartData, VStaticPartText, @@ -57,7 +59,7 @@ import { import { patchProps } from './modules/props'; import { applyEventListeners } from './modules/events'; import { hydrateStaticParts, traverseAndSetElements } from './modules/static-parts'; -import { getScopeTokenClass, getStylesheetTokenHost } from './stylesheet'; +import { getScopeTokenClass } from './stylesheet'; import { renderComponent } from './component'; import { applyRefs } from './modules/refs'; import { isSanitizedHtmlContentEqual } from './sanitized-html-content'; @@ -529,7 +531,7 @@ function isMatchingElement( vnode: VBaseElement, elm: Element, renderer: RendererAPI, - shouldValidateAttr: AttrValidationPredicate = () => true, + shouldValidateAttr: AttrValidationPredicate = () => true ) { const { getProperty } = renderer; if (vnode.sel.toLowerCase() !== getProperty(elm, 'tagName').toLowerCase()) { @@ -633,15 +635,15 @@ function validateClassAttr( // Use a Set because we don't care to validate mismatches for 1) different ordering in SSR vs CSR, or 2) // duplicated class names. These don't have an effect on rendered styles. - const elmClasses = new Set(ArrayFrom(elm.classList)) - let vnodeClasses: Set + const elmClasses = new Set(ArrayFrom(elm.classList)); + let vnodeClasses: Set; if (!isUndefined(className)) { - vnodeClasses = new Set(StringSplit.call(StringTrim.call(className), /\s+/)); + vnodeClasses = new Set(ArrayFilter.call(StringSplit.call(className, /\s+/), Boolean)); } else if (!isUndefined(classMap)) { vnodeClasses = new Set(keys(classMap)); } else { - vnodeClasses = new Set() + vnodeClasses = new Set(); } // ---------- Step 2: handle the scope tokens @@ -656,7 +658,7 @@ function validateClassAttr( // Consequently, hydration mismatches will occur if scoped CSS token classnames // are rendered during SSR. This needs to be accounted for when validating. if (!isNull(scopeToken)) { - vnodeClasses.add(scopeToken) + vnodeClasses.add(scopeToken); } // This tells us which `*-host` scope token was rendered to the element's class. @@ -664,8 +666,8 @@ function validateClassAttr( // FIXME: correctly validate the host scope token class const elmHostScopeToken = renderer.getAttribute(elm, 'data-lwc-host-scope-token'); if (!isNull(elmHostScopeToken)) { - elmClasses.delete(elmHostScopeToken) - vnodeClasses.delete(elmHostScopeToken) + elmClasses.delete(elmHostScopeToken); + vnodeClasses.delete(elmHostScopeToken); } // ---------- Step 3: check for compatibility @@ -677,29 +679,26 @@ function validateClassAttr( } else { for (const vnodeClass of vnodeClasses) { if (!elmClasses.has(vnodeClass)) { - nodesAreCompatible = false + nodesAreCompatible = false; } } for (const elmClass of elmClasses) { if (!vnodeClasses.has(elmClass)) { - nodesAreCompatible = false + nodesAreCompatible = false; } } } if (process.env.NODE_ENV !== 'production' && !nodesAreCompatible) { - const prettyPrint = (set: Set) => JSON.stringify( - ArrayJoin.call(ArraySort.call(ArrayFrom(set)), ' ') - ) + const prettyPrint = (set: Set) => + JSON.stringify(ArrayJoin.call(ArraySort.call(ArrayFrom(set)), ' ')); logWarn( `Mismatch hydrating element <${getProperty( elm, 'tagName' - ).toLowerCase()}>: attribute "class" has different values, expected ${ - prettyPrint(vnodeClasses) - } but found ${ - prettyPrint(elmClasses) - }`, + ).toLowerCase()}>: attribute "class" has different values, expected ${prettyPrint( + vnodeClasses + )} but found ${prettyPrint(elmClasses)}`, vnode.owner ); } diff --git a/packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/dynamic-empty-in-ssr-null-string-in-client/index.spec.js b/packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/dynamic-empty-in-ssr-null-string-in-client/index.spec.js index ce7699cc3c..7615b65828 100644 --- a/packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/dynamic-empty-in-ssr-null-string-in-client/index.spec.js +++ b/packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/dynamic-empty-in-ssr-null-string-in-client/index.spec.js @@ -24,7 +24,7 @@ export default { TestUtils.expectConsoleCallsDev(consoleCalls, { error: [], warn: [ - 'Mismatch hydrating element

: attribute "class" has different values, expected "null" but found null', + 'Mismatch hydrating element

: attribute "class" has different values, expected "null" but found ""', 'Hydration completed with errors.', ], }); diff --git a/packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/dynamic-same-different-order-throws/index.spec.js b/packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/dynamic-same-different-order-does-not-throw/index.spec.js similarity index 60% rename from packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/dynamic-same-different-order-throws/index.spec.js rename to packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/dynamic-same-different-order-does-not-throw/index.spec.js index 2e0a9a7d01..d60cecb71e 100644 --- a/packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/dynamic-same-different-order-throws/index.spec.js +++ b/packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/dynamic-same-different-order-does-not-throw/index.spec.js @@ -15,15 +15,12 @@ export default { test(target, snapshots, consoleCalls) { const p = target.shadowRoot.querySelector('p'); - expect(p).not.toBe(snapshots.p); - expect(p.className).not.toBe(snapshots.classes); + expect(p).toBe(snapshots.p); + expect(p.className).toBe(snapshots.classes); TestUtils.expectConsoleCallsDev(consoleCalls, { error: [], - warn: [ - 'Mismatch hydrating element

: attribute "class" has different values, expected "c3 c2 c1" but found "c1 c2 c3"', - 'Hydration completed with errors.', - ], + warn: [], }); }, }; diff --git a/packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/dynamic-same-different-order-throws/x/main/main.html b/packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/dynamic-same-different-order-does-not-throw/x/main/main.html similarity index 100% rename from packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/dynamic-same-different-order-throws/x/main/main.html rename to packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/dynamic-same-different-order-does-not-throw/x/main/main.html diff --git a/packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/dynamic-same-different-order-throws/x/main/main.js b/packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/dynamic-same-different-order-does-not-throw/x/main/main.js similarity index 100% rename from packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/dynamic-same-different-order-throws/x/main/main.js rename to packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/dynamic-same-different-order-does-not-throw/x/main/main.js diff --git a/packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/empty-string-on-server-nonempty-on-client/index.spec.js b/packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/empty-string-on-server-nonempty-on-client/index.spec.js index a91b0fd100..5d15bb0477 100644 --- a/packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/empty-string-on-server-nonempty-on-client/index.spec.js +++ b/packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/empty-string-on-server-nonempty-on-client/index.spec.js @@ -20,7 +20,7 @@ export default { TestUtils.expectConsoleCallsDev(consoleCalls, { error: [], warn: [ - 'Mismatch hydrating element

: attribute "class" has different values, expected "yolo" but found null', + 'Mismatch hydrating element

: attribute "class" has different values, expected "yolo" but found ""', 'Hydration completed with errors.', ], }); diff --git a/packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/empty-string/index.spec.js b/packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/empty-string/index.spec.js index db4aec58ec..6766da610d 100644 --- a/packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/empty-string/index.spec.js +++ b/packages/@lwc/integration-karma/test-hydration/mismatches/class-attr/empty-string/index.spec.js @@ -9,9 +9,14 @@ export default { classes: p.className, }; }, - test(target, snapshots) { + test(target, snapshots, consoleCalls) { const p = target.shadowRoot.querySelector('p'); expect(p).toBe(snapshots.p); expect(p.className).toBe(snapshots.classes); + + TestUtils.expectConsoleCallsDev(consoleCalls, { + error: [], + warn: [], + }); }, }; diff --git a/packages/@lwc/integration-karma/test-hydration/stylesheet/host-scope-token/basic/.only b/packages/@lwc/integration-karma/test-hydration/stylesheet/host-scope-token/basic/.only deleted file mode 100644 index e69de29bb2..0000000000