better splitting of selectors (#1440)
* better splitting of selectors - overlapping with #1401 * Add test from example at https://github.com/PostHog/posthog/pull/21427 * ignore brackets inside selector strings * Add another test as noticed that it's possible to escape strings * Ensure we are ignoring commas within strings Co-authored-by: Eoghan Murray <eoghan@getthere.ie>
This commit is contained in:
5
.changeset/modern-doors-watch.md
Normal file
5
.changeset/modern-doors-watch.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'rrweb-snapshot': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
better nested css selector splitting when commas or brackets happen to be in quoted text
|
||||||
@@ -437,6 +437,7 @@ export function parse(css: string, options: ParserOptions = {}): Stylesheet {
|
|||||||
if (!m) {
|
if (!m) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @fix Remove all comments from selectors
|
/* @fix Remove all comments from selectors
|
||||||
* http://ostermiller.org/findcomment.html */
|
* http://ostermiller.org/findcomment.html */
|
||||||
const cleanedInput = m[0]
|
const cleanedInput = m[0]
|
||||||
@@ -463,9 +464,16 @@ export function parse(css: string, options: ParserOptions = {}): Stylesheet {
|
|||||||
let currentSegment = '';
|
let currentSegment = '';
|
||||||
let depthParentheses = 0; // Track depth of parentheses
|
let depthParentheses = 0; // Track depth of parentheses
|
||||||
let depthBrackets = 0; // Track depth of square brackets
|
let depthBrackets = 0; // Track depth of square brackets
|
||||||
|
let currentStringChar = null;
|
||||||
|
|
||||||
for (const char of input) {
|
for (const char of input) {
|
||||||
if (char === '(') {
|
const hasStringEscape = currentSegment.endsWith('\\');
|
||||||
|
|
||||||
|
if (currentStringChar) {
|
||||||
|
if (currentStringChar === char && !hasStringEscape) {
|
||||||
|
currentStringChar = null;
|
||||||
|
}
|
||||||
|
} else if (char === '(') {
|
||||||
depthParentheses++;
|
depthParentheses++;
|
||||||
} else if (char === ')') {
|
} else if (char === ')') {
|
||||||
depthParentheses--;
|
depthParentheses--;
|
||||||
@@ -473,6 +481,8 @@ export function parse(css: string, options: ParserOptions = {}): Stylesheet {
|
|||||||
depthBrackets++;
|
depthBrackets++;
|
||||||
} else if (char === ']') {
|
} else if (char === ']') {
|
||||||
depthBrackets--;
|
depthBrackets--;
|
||||||
|
} else if ('\'"'.includes(char)) {
|
||||||
|
currentStringChar = char;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split point is a comma that is not inside parentheses or square brackets
|
// Split point is a comma that is not inside parentheses or square brackets
|
||||||
|
|||||||
@@ -148,6 +148,50 @@ describe('css parser', () => {
|
|||||||
expect(out3).toEqual('[data-aa\\:other] { color: red; }');
|
expect(out3).toEqual('[data-aa\\:other] { color: red; }');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('parses nested commas in selectors correctly', () => {
|
||||||
|
const result = parse(
|
||||||
|
`
|
||||||
|
body > ul :is(li:not(:first-of-type) a:hover, li:not(:first-of-type).active a) {
|
||||||
|
background: red;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
expect((result.stylesheet!.rules[0] as Rule)!.selectors!.length).toEqual(1);
|
||||||
|
|
||||||
|
const trickresult = parse(
|
||||||
|
`
|
||||||
|
li[attr="weirdly("] a:hover, li[attr="weirdly)"] a {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
(trickresult.stylesheet!.rules[0] as Rule)!.selectors!.length,
|
||||||
|
).toEqual(2);
|
||||||
|
|
||||||
|
const weirderresult = parse(
|
||||||
|
`
|
||||||
|
li[attr="weirder\\"("] a:hover, li[attr="weirder\\")"] a {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
(weirderresult.stylesheet!.rules[0] as Rule)!.selectors!.length,
|
||||||
|
).toEqual(2);
|
||||||
|
|
||||||
|
const commainstrresult = parse(
|
||||||
|
`
|
||||||
|
li[attr="has,comma"] a:hover {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
(commainstrresult.stylesheet!.rules[0] as Rule)!.selectors!.length,
|
||||||
|
).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
it('parses imports with quotes correctly', () => {
|
it('parses imports with quotes correctly', () => {
|
||||||
const out1 = escapeImportStatement({
|
const out1 = escapeImportStatement({
|
||||||
cssText: `@import url("/foo.css;900;800"");`,
|
cssText: `@import url("/foo.css;900;800"");`,
|
||||||
|
|||||||
Reference in New Issue
Block a user