Skip to content

Commit fb5ef00

Browse files
authored
fix(ssr): fix rendering of class with scope token (#5072)
1 parent f29ff6e commit fb5ef00

File tree

5 files changed

+27
-4
lines changed

5 files changed

+27
-4
lines changed

packages/@lwc/engine-server/src/__tests__/fixtures/attribute-class/with-scoped-styles-only-in-child/dynamic/expected.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,5 +221,18 @@
221221
</x-grandchild>
222222
</template>
223223
</x-child>
224+
<x-child class="lwc-5h3d35cke7v-host" data-lwc-host-scope-token="lwc-5h3d35cke7v-host">
225+
<template shadowrootmode="open">
226+
<style class="lwc-5h3d35cke7v" type="text/css">
227+
:host {background: blue;}
228+
</style>
229+
<div class="&quot;'<&amp; lwc-5h3d35cke7v">
230+
</div>
231+
<x-grandchild class="&quot;'<&amp; lwc-5h3d35cke7v">
232+
<template shadowrootmode="open">
233+
</template>
234+
</x-grandchild>
235+
</template>
236+
</x-child>
224237
</template>
225238
</x-parent>

packages/@lwc/engine-server/src/__tests__/fixtures/attribute-class/with-scoped-styles-only-in-child/dynamic/modules/x/parent/parent.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@
1616
<x-child dynamic={isUppercase}></x-child>
1717
<x-child dynamic={isTabs}></x-child>
1818
<x-child dynamic={isNewlines}></x-child>
19+
<x-child dynamic={hasEscapableCharacters}></x-child>
1920
</template>

packages/@lwc/engine-server/src/__tests__/fixtures/attribute-class/with-scoped-styles-only-in-child/dynamic/modules/x/parent/parent.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,7 @@ export default class extends LightningElement {
2121
isUppercase = 'FOO BaR';
2222
isTabs = '\tfoo\t';
2323
isNewlines = '\nfoo\n';
24+
// Skipping `>` because it messes up `formatHTML()`, and the important thing is checking
25+
// that we are calling `htmlEscape()` in attribute mode, so checking `<` is sufficient.
26+
hasEscapableCharacters = `"'<&`;
2427
}

packages/@lwc/ssr-compiler/src/__tests__/utils/expected-failures.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
// We should slowly drive down these test failures or at least document where we expect the failures
99
// TODO [#4815]: enable all SSR v2 tests
1010
export const expectedFailures = new Set([
11-
'attribute-class/with-scoped-styles-only-in-child/dynamic/index.js',
12-
'attribute-class/with-scoped-styles/dynamic/index.js',
1311
'attribute-global-html/as-component-prop/undeclared/index.js',
1412
'attribute-global-html/as-component-prop/without-@api/index.js',
1513
'exports/component-as-default/index.js',

packages/@lwc/ssr-compiler/src/compile-template/transformers/element.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,17 @@ const bYieldClassDynamicValue = esTemplateWithYield`
8181
const attrValue = normalizeClass(${/* attribute value expression */ is.expression});
8282
const shouldRenderScopeToken = hasScopedStylesheets || hasScopedStaticStylesheets(Cmp);
8383
84+
// Concatenate the scope token with the class attribute value as necessary.
85+
// If either is missing, render the other alone.
86+
let combinedValue = shouldRenderScopeToken ? stylesheetScopeToken : '';
8487
if (attrValue) {
85-
const prefix = shouldRenderScopeToken ? stylesheetScopeToken + ' ' : '';
86-
yield \` class="\${prefix}\${htmlEscape(String(attrValue), true)}"\`;
88+
if (combinedValue) {
89+
combinedValue += ' ';
90+
}
91+
combinedValue += htmlEscape(String(attrValue), true);
92+
}
93+
if (combinedValue) {
94+
yield \` class="\${combinedValue}"\`;
8795
}
8896
}
8997
`<EsBlockStatement>;

0 commit comments

Comments
 (0)