diff --git a/src/preview/rewriteStyleSheet.test.ts b/src/preview/rewriteStyleSheet.test.ts index 6ab1d75..03258c7 100644 --- a/src/preview/rewriteStyleSheet.test.ts +++ b/src/preview/rewriteStyleSheet.test.ts @@ -121,4 +121,16 @@ describe("rewriteStyleSheet", () => { expect(sheet.cssRules[3].selectorText).toContain(".pseudo-hover .test2") }) + + it("supports classnames with a colon followed by state", () => { + const sheet = new Sheet(".focus:hover:border-red:hover:focus {border: red;}") + rewriteStyleSheet(sheet) + expect(sheet.cssRules[0].selectorText).toContain(".focus:hover:border-red:hover:focus") + expect(sheet.cssRules[0].selectorText).toContain( + ".focus:hover:border-red.pseudo-hover.pseudo-focus" + ) + expect(sheet.cssRules[0].selectorText).toContain( + ".pseudo-hover.pseudo-focus .focus:hover:border-red" + ) + }) }) diff --git a/src/preview/rewriteStyleSheet.ts b/src/preview/rewriteStyleSheet.ts index 3bde4b6..527278c 100644 --- a/src/preview/rewriteStyleSheet.ts +++ b/src/preview/rewriteStyleSheet.ts @@ -3,7 +3,14 @@ import { splitSelectors } from "./splitSelectors" const pseudoStates = Object.values(PSEUDO_STATES) const matchOne = new RegExp(`:(${pseudoStates.join("|")})`) -const matchAll = new RegExp(`:(${pseudoStates.join("|")})`, "g") + +const getMatcherAll = (state: string | null = null) => { + if (!state) { + state = pseudoStates.join("|") + } + + return new RegExp(`:(${state})(?=(?::(${pseudoStates.join("|")}))*$|\\\))`, "g") +} const warnings = new Set() const warnOnce = (message: string) => { @@ -29,13 +36,13 @@ const rewriteRule = ({ cssText, selectorText }: CSSStyleRule, shadowRoot?: Shado } const states: string[] = [] - const plainSelector = selector.replace(matchAll, (_, state) => { + const plainSelector = selector.replace(getMatcherAll(), (_, state) => { states.push(state) return "" }) const classSelector = states.reduce((acc, state) => { if (isExcludedPseudoElement(selector, state)) return "" - return acc.replace(new RegExp(`:${state}`, "g"), `.pseudo-${state}`) + return acc.replace(getMatcherAll(state), `.pseudo-${state}`) }, selector) if (selector.startsWith(":host(") || selector.startsWith("::slotted(")) {