From 3be227a73167d1887feae5caa8369525f285ea1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20B=C3=B6hm?= <188768+fb55@users.noreply.github.com> Date: Thu, 31 Mar 2022 14:18:23 +0100 Subject: [PATCH] feat: Support pseudo elements with data (#762) --- src/__fixtures__/tests.ts | 29 ++++++++++++++++- src/parse.ts | 67 ++++++++++++++++++++++----------------- src/stringify.ts | 29 ++++++++++------- src/types.ts | 1 + 4 files changed, 84 insertions(+), 42 deletions(-) diff --git a/src/__fixtures__/tests.ts b/src/__fixtures__/tests.ts index ec788647..d22c18af 100644 --- a/src/__fixtures__/tests.ts +++ b/src/__fixtures__/tests.ts @@ -411,6 +411,33 @@ export const tests: [ { type: SelectorType.PseudoElement, name: "foo", + data: null, + }, + ], + ], + "pseudo-element", + ], + [ + "::foo()", + [ + [ + { + type: SelectorType.PseudoElement, + name: "foo", + data: "", + }, + ], + ], + "pseudo-element", + ], + [ + "::foo(bar())", + [ + [ + { + type: SelectorType.PseudoElement, + name: "foo", + data: "bar()", }, ], ], @@ -485,7 +512,7 @@ export const tests: [ { type: SelectorType.Pseudo, name: "contains", - data: "(a((foo\\))))", + data: "(a((foo))))", }, ], ], diff --git a/src/parse.ts b/src/parse.ts index 690ad6c5..3e277db1 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -175,6 +175,38 @@ function parseSelector( } } + function readValueWithParenthesis(): string { + selectorIndex += 1; + const start = selectorIndex; + let counter = 1; + + for ( + ; + counter > 0 && selectorIndex < selector.length; + selectorIndex++ + ) { + if ( + selector.charCodeAt(selectorIndex) === + CharCode.LeftParenthesis && + !isEscaped(selectorIndex) + ) { + counter++; + } else if ( + selector.charCodeAt(selectorIndex) === + CharCode.RightParenthesis && + !isEscaped(selectorIndex) + ) { + counter--; + } + } + + if (counter) { + throw new Error("Parenthesis not matched"); + } + + return unescapeCSS(selector.slice(start, selectorIndex - 1)); + } + function isEscaped(pos: number): boolean { let slashCount = 0; @@ -434,6 +466,11 @@ function parseSelector( tokens.push({ type: SelectorType.PseudoElement, name: getName(2).toLowerCase(), + data: + selector.charCodeAt(selectorIndex) === + CharCode.LeftParenthesis + ? readValueWithParenthesis() + : null, }); continue; } @@ -470,35 +507,7 @@ function parseSelector( selectorIndex += 1; } else { - selectorIndex += 1; - const start = selectorIndex; - let counter = 1; - - for ( - ; - counter > 0 && selectorIndex < selector.length; - selectorIndex++ - ) { - if ( - selector.charCodeAt(selectorIndex) === - CharCode.LeftParenthesis && - !isEscaped(selectorIndex) - ) { - counter++; - } else if ( - selector.charCodeAt(selectorIndex) === - CharCode.RightParenthesis && - !isEscaped(selectorIndex) - ) { - counter--; - } - } - - if (counter) { - throw new Error("Parenthesis not matched"); - } - - data = selector.slice(start, selectorIndex - 1); + data = readValueWithParenthesis(); if (stripQuotesFromPseudos.has(name)) { const quot = data.charCodeAt(0); diff --git a/src/stringify.ts b/src/stringify.ts index 4f809f8a..59b2b35e 100644 --- a/src/stringify.ts +++ b/src/stringify.ts @@ -69,20 +69,25 @@ function stringifyToken( return getNamespacedName(token); case SelectorType.PseudoElement: - return `::${escapeName(token.name, charsToEscapeInName)}`; + return `::${escapeName(token.name, charsToEscapeInName)}${ + token.data === null + ? "" + : `(${escapeName(token.data, charsToEscapeInPseudoValue)})` + }`; case SelectorType.Pseudo: - if (token.data === null) - return `:${escapeName(token.name, charsToEscapeInName)}`; - if (typeof token.data === "string") { - return `:${escapeName( - token.name, - charsToEscapeInName - )}(${escapeName(token.data, charsToEscapeInPseudoValue)})`; - } - return `:${escapeName(token.name, charsToEscapeInName)}(${stringify( - token.data - )})`; + return `:${escapeName(token.name, charsToEscapeInName)}${ + token.data === null + ? "" + : `(${ + typeof token.data === "string" + ? escapeName( + token.data, + charsToEscapeInPseudoValue + ) + : stringify(token.data) + })` + }`; case SelectorType.Attribute: { if ( diff --git a/src/types.ts b/src/types.ts index cbac6fa8..e838e037 100644 --- a/src/types.ts +++ b/src/types.ts @@ -56,6 +56,7 @@ export interface PseudoSelector { export interface PseudoElement { type: SelectorType.PseudoElement; name: string; + data: string | null; } export interface TagSelector {