Skip to content

Commit 98ccb57

Browse files
JeanMechethePunderWoman
authored andcommitted
fix(compiler): handle css selectors with space after an escaped character. (#48558)
In Css, selectors with escaped characters require a space after if the following character is a hex character. ie: .\fc ber which matches class="über" These escaped selectors happen for example when esbuild run with `optimization.minify` fixes #48524 PR Close #48558
1 parent 64416a6 commit 98ccb57

File tree

2 files changed

+22
-1
lines changed

2 files changed

+22
-1
lines changed

packages/compiler/src/shadow_css.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ const animationKeywords = new Set([
152152
in comments in lieu of the next selector when running under polyfill.
153153
*/
154154
export class ShadowCss {
155+
// TODO: Is never re-assigned, could be removed.
155156
strictStyling: boolean = true;
156157

157158
/*
@@ -729,6 +730,15 @@ export class ShadowCss {
729730
while ((res = sep.exec(selector)) !== null) {
730731
const separator = res[1];
731732
const part = selector.slice(startIndex, res.index).trim();
733+
734+
// A space following an escaped hex value and followed by another hex character
735+
// (ie: ".\fc ber" for ".über") is not a separator between 2 selectors
736+
// also keep in mind that backslashes are replaced by a placeholder by SafeSelector
737+
// These escaped selectors happen for example when esbuild runs with optimization.minify.
738+
if (part.match(_placeholderRe) && selector[res.index + 1]?.match(/[a-fA-F\d]/)) {
739+
continue;
740+
}
741+
732742
shouldScope = shouldScope || part.indexOf(_polyfillHostNoCombinator) > -1;
733743
const scopedPart = shouldScope ? _scopeSelectorPart(part) : part;
734744
scopedSelector += `${scopedPart} ${separator} `;
@@ -777,7 +787,7 @@ class SafeSelector {
777787
}
778788

779789
restore(content: string): string {
780-
return content.replace(/__ph-(\d+)__/g, (_ph, index) => this.placeholders[+index]);
790+
return content.replace(_placeholderRe, (_ph, index) => this.placeholders[+index]);
781791
}
782792

783793
content(): string {
@@ -833,6 +843,8 @@ const _colonHostContextRe = /:host-context/gim;
833843

834844
const _commentRe = /\/\*[\s\S]*?\*\//g;
835845

846+
const _placeholderRe = /__ph-(\d+)__/g;
847+
836848
function stripComments(input: string): string {
837849
return input.replace(_commentRe, '');
838850
}

packages/compiler/test/shadow_css/shadow_css_spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,15 @@ describe('ShadowCss', () => {
7676
.toEqualCss('.one\\:two[contenta] .three\\:four[contenta] {}');
7777
});
7878

79+
it('should handle escaped selector with space (if followed by a hex char)', () => {
80+
// When esbuild runs with optimization.minify
81+
// selectors are escaped: .über becomes .\fc ber.
82+
// The space here isn't a separator between 2 selectors
83+
expect(shim('.\\fc ber {}', 'contenta')).toEqual('.\\fc ber[contenta] {}');
84+
expect(shim('.\\fc ker {}', 'contenta')).toEqual('.\\fc[contenta] ker[contenta] {}');
85+
expect(shim('.pr\\fc fung {}', 'contenta')).toEqual('.pr\\fc fung[contenta] {}');
86+
});
87+
7988
it('should handle ::shadow', () => {
8089
const css = shim('x::shadow > y {}', 'contenta');
8190
expect(css).toEqualCss('x[contenta] > y[contenta] {}');

0 commit comments

Comments
 (0)