Skip to content

Commit 073ebfe

Browse files
JeanMecheatscott
authored andcommitted
fix(compiler): apply style on :host attributes in prod builds. (#49118)
In prod builds, selectors are optimized and spaces a removed. #48558 introduced a regression on selectors without spaces. This commit fixes tihs. Fixes #49100 PR Close #49118
1 parent 926db6d commit 073ebfe

File tree

3 files changed

+23
-5
lines changed

3 files changed

+23
-5
lines changed

packages/compiler/src/shadow_css.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,7 @@ export class ShadowCss {
713713
// (ie: ".\fc ber" for ".über") is not a separator between 2 selectors
714714
// also keep in mind that backslashes are replaced by a placeholder by SafeSelector
715715
// These escaped selectors happen for example when esbuild runs with optimization.minify.
716-
if (part.match(_placeholderRe) && selector[res.index + 1]?.match(/[a-fA-F\d]/)) {
716+
if (part.match(/__esc-ph-(\d+)__/) && selector[res.index + 1]?.match(/[a-fA-F\d]/)) {
717717
continue;
718718
}
719719

@@ -752,7 +752,13 @@ class SafeSelector {
752752
// pseudo-class, but writing `.foo\:blue` will match, because the colon was escaped.
753753
// Replace all escape sequences (`\` followed by a character) with a placeholder so
754754
// that our handling of pseudo-selectors doesn't mess with them.
755-
selector = this._escapeRegexMatches(selector, /(\\.)/g);
755+
// Escaped characters have a specific placeholder so they can be detected separately.
756+
selector = selector.replace(/(\\.)/g, (_, keep) => {
757+
const replaceBy = `__esc-ph-${this.index}__`;
758+
this.placeholders.push(keep);
759+
this.index++;
760+
return replaceBy;
761+
});
756762

757763
// Replaces the expression in `:nth-child(2n + 1)` with a placeholder.
758764
// WS and "+" would otherwise be interpreted as selector separators.
@@ -765,7 +771,7 @@ class SafeSelector {
765771
}
766772

767773
restore(content: string): string {
768-
return content.replace(_placeholderRe, (_ph, index) => this.placeholders[+index]);
774+
return content.replace(/__(?:ph|esc-ph)-(\d+)__/g, (_ph, index) => this.placeholders[+index]);
769775
}
770776

771777
content(): string {
@@ -825,8 +831,6 @@ const _commentWithHashRe = /\/\*\s*#\s*source(Mapping)?URL=/g;
825831
const COMMENT_PLACEHOLDER = '%COMMENT%';
826832
const _commentWithHashPlaceHolderRe = new RegExp(COMMENT_PLACEHOLDER, 'g');
827833

828-
const _placeholderRe = /__ph-(\d+)__/g;
829-
830834
const BLOCK_PLACEHOLDER = '%BLOCK%';
831835
const _ruleRe = new RegExp(
832836
`(\\s*(?:${COMMENT_PLACEHOLDER}\\s*)*)([^;\\{\\}]+?)(\\s*)((?:{%BLOCK%}?\\s*;?)|(?:\\s*;))`,

packages/compiler/test/shadow_css/host_and_host_context_spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,19 @@ describe('ShadowCss, :host and :host-context', () => {
2727
expect(shim(':host([a=b]) {}', 'contenta', 'a-host')).toEqualCss('[a=b][a-host] {}');
2828
});
2929

30+
it('should handle attribute and next operator without spaces', () => {
31+
expect(shim(':host[foo]>div {}', 'contenta', 'a-host'))
32+
.toEqualCss(('[foo][a-host] > div[contenta] {}'));
33+
});
34+
35+
// we know that the following test doesn't pass
36+
// the host attribute is added before the space
37+
// We advise to a more simple class name that doesn't require escaping
38+
xit('should handle host with escaped class selector', () => {
39+
// here we're looking to shim :host.prüfung (an escaped ü is replaced by "\\fc ")
40+
expect(shim(':host.pr\\fc fung {}', 'contenta', 'a-host')).toEqual('.pr\\fc fung[a-host] {}');
41+
});
42+
3043
it('should handle multiple tag selectors', () => {
3144
expect(shim(':host(ul,li) {}', 'contenta', 'a-host')).toEqualCss('ul[a-host], li[a-host] {}');
3245
expect(shim(':host(ul,li) > .z {}', 'contenta', 'a-host'))

packages/compiler/test/shadow_css/shadow_css_spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ describe('ShadowCss', () => {
7272
expect(shim('one\\:two {}', 'contenta')).toEqualCss('one\\:two[contenta] {}');
7373
expect(shim('one\\\\:two {}', 'contenta')).toEqualCss('one\\\\[contenta]:two {}');
7474
expect(shim('.one\\:two {}', 'contenta')).toEqualCss('.one\\:two[contenta] {}');
75+
expect(shim('.one\\:\\fc ber {}', 'contenta')).toEqualCss('.one\\:\\fc ber[contenta] {}');
7576
expect(shim('.one\\:two .three\\:four {}', 'contenta'))
7677
.toEqualCss('.one\\:two[contenta] .three\\:four[contenta] {}');
7778
});

0 commit comments

Comments
 (0)