Skip to content

Commit f4b1455

Browse files
abdulsattarwjhsf
andauthored
fix: stylesheetToken being validated too late (#5672)
* fix: stylesheetToken being validated too late * test(wtr): use correct stylesheet token --------- Co-authored-by: Will Harney <wharney@salesforce.com>
1 parent f594b72 commit f4b1455

File tree

8 files changed

+107
-0
lines changed

8 files changed

+107
-0
lines changed

packages/@lwc/engine-core/src/framework/stylesheet.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,10 @@ export function getStylesheetsContent(vm: VM, template: Template): ReadonlyArray
265265
const { stylesheets, stylesheetToken } = template;
266266
const { stylesheets: vmStylesheets } = vm;
267267

268+
if (!isUndefined(stylesheetToken) && !isValidScopeToken(stylesheetToken)) {
269+
throw new Error('stylesheet token must be a valid string');
270+
}
271+
268272
const hasTemplateStyles = hasStyles(stylesheets);
269273
const hasVmStyles = hasStyles(vmStylesheets);
270274

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { createElement, setFeatureFlagForTest } from 'lwc';
2+
import HelloWorld from 'x/component';
3+
import { catchUnhandledRejectionsAndErrors } from '../../../helpers/utils.js';
4+
5+
describe('stylesheet validation', () => {
6+
let caughtError;
7+
8+
catchUnhandledRejectionsAndErrors((error) => {
9+
caughtError = error;
10+
});
11+
12+
beforeEach(() => {
13+
setFeatureFlagForTest('DISABLE_SCOPE_TOKEN_VALIDATION', false);
14+
});
15+
16+
afterEach(() => {
17+
caughtError = undefined;
18+
});
19+
20+
it('should not permit invalid stylesheets', async () => {
21+
const elm = createElement('x-component', { is: HelloWorld });
22+
23+
try {
24+
document.body.appendChild(elm);
25+
} catch (err) {
26+
// In synthetic custom element lifecycle, the error is thrown synchronously on `appendChild`
27+
caughtError = err;
28+
}
29+
30+
await Promise.resolve();
31+
32+
expect(caughtError).not.toBeUndefined();
33+
expect(caughtError.message).toMatch(
34+
/stylesheet token must be a valid string|Failed to execute 'setAttribute'|Invalid qualified name|String contains an invalid character|The string contains invalid characters/
35+
);
36+
expect(elm.stylesheet).not.toHaveBeenCalled();
37+
});
38+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { api, LightningElement } from 'lwc';
2+
import { fn } from '@vitest/spy';
3+
import tmpl from './template.html';
4+
5+
export default class extends LightningElement {
6+
@api stylesheet = fn();
7+
render() {
8+
tmpl.stylesheets = [this.stylesheet];
9+
tmpl.stylesheetToken = 'stylesheet.token';
10+
return tmpl;
11+
}
12+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<template>
2+
<p>Remuneration is an odd word.</p>
3+
</template>

packages/@lwc/integration-wtr/test/rendering/sanitize-stylesheet-token/index.spec.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { createElement, setFeatureFlagForTest } from 'lwc';
22
import Component from 'x/component';
33
import Scoping from 'x/scoping';
4+
import Indirect from 'x/indirect';
45
import { spyOn } from '@vitest/spy';
56
import { catchUnhandledRejectionsAndErrors } from '../../../helpers/utils.js';
67
import { resetAlreadyLoggedMessages, resetFragmentCache } from '../../../helpers/reset.js';
@@ -38,6 +39,11 @@ const components = [
3839
Ctor: Scoping,
3940
name: 'scoped styles',
4041
},
42+
{
43+
tagName: 'x-indirect',
44+
Ctor: Indirect,
45+
name: 'indirect styles',
46+
},
4147
];
4248

4349
props.forEach((prop) => {
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<template>
2+
<p>Lillian: Harold.</p>
3+
<p>Fiona: Shrek.</p>
4+
<p>Shrek: Fiona...</p>
5+
<p>Harold: Fiona!</p>
6+
<p>Fiona: Mommm!</p>
7+
<p>Lillian: Harold!</p>
8+
<p>Donkey: DONKEY!</p>
9+
</template>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { LightningElement, api } from 'lwc';
2+
import template from './indirect.html';
3+
4+
export default class Indirect extends LightningElement {
5+
@api propToUse;
6+
7+
render() {
8+
const token = 'stylesheet.token';
9+
10+
const { propToUse } = this;
11+
if (propToUse === 'stylesheetTokens') {
12+
template[propToUse] = {
13+
hostAttribute: token,
14+
shadowAttribute: token,
15+
};
16+
} else {
17+
template[propToUse] = token;
18+
}
19+
20+
return template;
21+
}
22+
}
23+
24+
const { stylesheetToken, stylesheetTokens, legacyStylesheetToken } = template;
25+
26+
Indirect.resetTemplate = () => {
27+
Object.assign(template, {
28+
stylesheetToken,
29+
stylesheetTokens,
30+
legacyStylesheetToken,
31+
});
32+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:host {
2+
color: red;
3+
}

0 commit comments

Comments
 (0)