Skip to content

Commit

Permalink
fix(compiler): apply style on :host attributes in prod builds. (#49118)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
JeanMeche authored and atscott committed Oct 11, 2023
1 parent 926db6d commit 073ebfe
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 5 deletions.
14 changes: 9 additions & 5 deletions packages/compiler/src/shadow_css.ts
Expand Up @@ -713,7 +713,7 @@ export class ShadowCss {
// (ie: ".\fc ber" for ".über") is not a separator between 2 selectors
// also keep in mind that backslashes are replaced by a placeholder by SafeSelector
// These escaped selectors happen for example when esbuild runs with optimization.minify.
if (part.match(_placeholderRe) && selector[res.index + 1]?.match(/[a-fA-F\d]/)) {
if (part.match(/__esc-ph-(\d+)__/) && selector[res.index + 1]?.match(/[a-fA-F\d]/)) {
continue;
}

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

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

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

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

const _placeholderRe = /__ph-(\d+)__/g;

const BLOCK_PLACEHOLDER = '%BLOCK%';
const _ruleRe = new RegExp(
`(\\s*(?:${COMMENT_PLACEHOLDER}\\s*)*)([^;\\{\\}]+?)(\\s*)((?:{%BLOCK%}?\\s*;?)|(?:\\s*;))`,
Expand Down
13 changes: 13 additions & 0 deletions packages/compiler/test/shadow_css/host_and_host_context_spec.ts
Expand Up @@ -27,6 +27,19 @@ describe('ShadowCss, :host and :host-context', () => {
expect(shim(':host([a=b]) {}', 'contenta', 'a-host')).toEqualCss('[a=b][a-host] {}');
});

it('should handle attribute and next operator without spaces', () => {
expect(shim(':host[foo]>div {}', 'contenta', 'a-host'))
.toEqualCss(('[foo][a-host] > div[contenta] {}'));
});

// we know that the following test doesn't pass
// the host attribute is added before the space
// We advise to a more simple class name that doesn't require escaping
xit('should handle host with escaped class selector', () => {
// here we're looking to shim :host.prüfung (an escaped ü is replaced by "\\fc ")
expect(shim(':host.pr\\fc fung {}', 'contenta', 'a-host')).toEqual('.pr\\fc fung[a-host] {}');
});

it('should handle multiple tag selectors', () => {
expect(shim(':host(ul,li) {}', 'contenta', 'a-host')).toEqualCss('ul[a-host], li[a-host] {}');
expect(shim(':host(ul,li) > .z {}', 'contenta', 'a-host'))
Expand Down
1 change: 1 addition & 0 deletions packages/compiler/test/shadow_css/shadow_css_spec.ts
Expand Up @@ -72,6 +72,7 @@ describe('ShadowCss', () => {
expect(shim('one\\:two {}', 'contenta')).toEqualCss('one\\:two[contenta] {}');
expect(shim('one\\\\:two {}', 'contenta')).toEqualCss('one\\\\[contenta]:two {}');
expect(shim('.one\\:two {}', 'contenta')).toEqualCss('.one\\:two[contenta] {}');
expect(shim('.one\\:\\fc ber {}', 'contenta')).toEqualCss('.one\\:\\fc ber[contenta] {}');
expect(shim('.one\\:two .three\\:four {}', 'contenta'))
.toEqualCss('.one\\:two[contenta] .three\\:four[contenta] {}');
});
Expand Down

0 comments on commit 073ebfe

Please sign in to comment.