diff --git a/src/preview/rewriteStyleSheet.test.ts b/src/preview/rewriteStyleSheet.test.ts index 8fd9d03..8b496aa 100644 --- a/src/preview/rewriteStyleSheet.test.ts +++ b/src/preview/rewriteStyleSheet.test.ts @@ -215,12 +215,12 @@ describe("rewriteStyleSheet", () => { }) it('supports ":host" with classes', () => { - const sheet = new Sheet(":host(.a:hover, .b) .c { color: red }") + const sheet = new Sheet(":host(.a:hover) .c { color: red }") rewriteStyleSheet(sheet as any) const selectors = sheet.cssRules[0].getSelectors() - expect(selectors).toContain(":host(.a:hover, .b) .c") - expect(selectors).toContain(":host(.a.pseudo-hover, .b) .c") - expect(selectors).toContain(":host(.a.pseudo-hover-all, .b) .c") + expect(selectors).toContain(":host(.a:hover) .c") + expect(selectors).toContain(":host(.a.pseudo-hover) .c") + expect(selectors).toContain(":host(.a.pseudo-hover-all) .c") }) it('supports ":host" with state selectors in descendant selector', () => { @@ -232,6 +232,15 @@ describe("rewriteStyleSheet", () => { expect(selectors).toContain(":host(.a.pseudo-hover-all) .b") }) + it('supports ":host" with state selectors in :host and descendant selector', () => { + const sheet = new Sheet(":host(.a:focus) .b:hover { color: red }") + rewriteStyleSheet(sheet as any) + const selectors = sheet.cssRules[0].getSelectors() + expect(selectors).toContain(":host(.a:focus) .b:hover") + expect(selectors).toContain(":host(.a.pseudo-focus) .b.pseudo-hover") + expect(selectors).toContain(":host(.a.pseudo-focus-all.pseudo-hover-all) .b") + }) + it('supports "::slotted"', () => { const sheet = new Sheet("::slotted(:hover) { color: red }") rewriteStyleSheet(sheet as any) diff --git a/src/preview/rewriteStyleSheet.ts b/src/preview/rewriteStyleSheet.ts index 9ad1dca..cc9d569 100644 --- a/src/preview/rewriteStyleSheet.ts +++ b/src/preview/rewriteStyleSheet.ts @@ -39,15 +39,14 @@ const rewriteRule = ({ cssText, selectorText }: CSSStyleRule, shadowRoot?: Shado const statesAllClassSelectors = states.map((s) => `.pseudo-${s}-all`).join("") if (selector.startsWith(":host(")) { - const matches = selector.match(/^:host\((\S+)\) /) - if (matches && !matchOne.test(matches[1])) { - // If :host() did not contain states, then simple replacement won't work. + const matches = selector.match(/^:host\((\S+)\)\s+(.+)$/) + if (matches && matchOne.test(matches[2])) { + // If there are pseudo-state selectors outside of :host(), then simple replacement won't work. // E.g. :host(.foo#bar) .baz:hover:active -> :host(.foo#bar.pseudo-hover-all.pseudo-active-all) .baz - ancestorSelector = `:host(${matches[1]}${statesAllClassSelectors}) ${plainSelector.replace(matches[0], "")}` + // E.g. :host(.foo:focus) .bar:hover -> :host(.foo.pseudo-focus-all.pseudo-hover-all) .bar + ancestorSelector = `:host(${matches[1].replace(matchAll, "")}${statesAllClassSelectors}) ${matches[2].replace(matchAll, "")}` } else { ancestorSelector = states.reduce((acc, state) => acc.replace(replacementRegExp(state), `.pseudo-${state}-all`), selector) - // NOTE: Selectors with pseudo states on both :host and a descendant are not properly supported. - // E.g. :host(.foo:focus) .bar:hover -> :host(.foo.pseudo-focus-all.pseudo-hover-all) .bar } } else if (selector.startsWith("::slotted(") || shadowRoot) { // If removing pseudo-state selectors from inside ::slotted left it empty (thus invalid), must fix it by adding '*'.