* Fix known issues * Run format * Fix linting errors * Add comment in code for source of match logic * Add changeset
This commit is contained in:
5
.changeset/rich-dots-lay.md
Normal file
5
.changeset/rich-dots-lay.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'rrweb-snapshot': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix css parsing errors
|
||||||
@@ -425,21 +425,71 @@ export function parse(css: string, options: ParserOptions = {}): Stylesheet {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function selector() {
|
function selector() {
|
||||||
const m = match(/^([^{]+)/);
|
whitespace();
|
||||||
|
while (css[0] == '}') {
|
||||||
|
error('extra closing bracket');
|
||||||
|
css = css.slice(1);
|
||||||
|
whitespace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use match logic from https://github.com/NxtChg/pieces/blob/3eb39c8287a97632e9347a24f333d52d916bc816/js/css_parser/css_parse.js#L46C1-L47C1
|
||||||
|
const m = match(/^(("(?:\\"|[^"])*"|'(?:\\'|[^'])*'|[^{])+)/);
|
||||||
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 */
|
||||||
return trim(m[0])
|
const cleanedInput = m[0]
|
||||||
|
.trim()
|
||||||
.replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, '')
|
.replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, '')
|
||||||
|
|
||||||
|
// Handle strings by replacing commas inside them
|
||||||
.replace(/"(?:\\"|[^"])*"|'(?:\\'|[^'])*'/g, (m) => {
|
.replace(/"(?:\\"|[^"])*"|'(?:\\'|[^'])*'/g, (m) => {
|
||||||
return m.replace(/,/g, '\u200C');
|
return m.replace(/,/g, '\u200C');
|
||||||
})
|
|
||||||
.split(/\s*(?![^(]*\)),\s*/)
|
|
||||||
.map((s) => {
|
|
||||||
return s.replace(/\u200C/g, ',');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Split using a custom function and restore commas in strings
|
||||||
|
return customSplit(cleanedInput).map((s) =>
|
||||||
|
s.replace(/\u200C/g, ',').trim(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split selector correctly, ensuring not to split on comma if inside ().
|
||||||
|
*/
|
||||||
|
|
||||||
|
function customSplit(input: string) {
|
||||||
|
const result = [];
|
||||||
|
let currentSegment = '';
|
||||||
|
let depthParentheses = 0; // Track depth of parentheses
|
||||||
|
let depthBrackets = 0; // Track depth of square brackets
|
||||||
|
|
||||||
|
for (const char of input) {
|
||||||
|
if (char === '(') {
|
||||||
|
depthParentheses++;
|
||||||
|
} else if (char === ')') {
|
||||||
|
depthParentheses--;
|
||||||
|
} else if (char === '[') {
|
||||||
|
depthBrackets++;
|
||||||
|
} else if (char === ']') {
|
||||||
|
depthBrackets--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split point is a comma that is not inside parentheses or square brackets
|
||||||
|
if (char === ',' && depthParentheses === 0 && depthBrackets === 0) {
|
||||||
|
result.push(currentSegment);
|
||||||
|
currentSegment = '';
|
||||||
|
} else {
|
||||||
|
currentSegment += char;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the last segment
|
||||||
|
if (currentSegment) {
|
||||||
|
result.push(currentSegment);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -78,6 +78,35 @@ describe('css parser', () => {
|
|||||||
expect(errors[0].filename).toEqual('foo.css');
|
expect(errors[0].filename).toEqual('foo.css');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should parse selector with comma nested inside ()', () => {
|
||||||
|
const result = parse(
|
||||||
|
'[_nghost-ng-c4172599085]:not(.fit-content).aim-select:hover:not(:disabled, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--disabled, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--invalid, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--active) { border-color: rgb(84, 84, 84); }',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.parent).toEqual(null);
|
||||||
|
|
||||||
|
const rules = result.stylesheet!.rules;
|
||||||
|
expect(rules.length).toEqual(1);
|
||||||
|
|
||||||
|
let rule = rules[0] as Rule;
|
||||||
|
expect(rule.parent).toEqual(result);
|
||||||
|
expect(rule.selectors?.length).toEqual(1);
|
||||||
|
|
||||||
|
let decl = rule.declarations![0];
|
||||||
|
expect(decl.parent).toEqual(rule);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses { and } in attribute selectors correctly', () => {
|
||||||
|
const result = parse('foo[someAttr~="{someId}"] { color: red; }');
|
||||||
|
const rules = result.stylesheet!.rules;
|
||||||
|
|
||||||
|
expect(rules.length).toEqual(1);
|
||||||
|
|
||||||
|
const rule = rules[0] as Rule;
|
||||||
|
|
||||||
|
expect(rule.selectors![0]).toEqual('foo[someAttr~="{someId}"]');
|
||||||
|
});
|
||||||
|
|
||||||
it('should set parent property', () => {
|
it('should set parent property', () => {
|
||||||
const result = parse(
|
const result = parse(
|
||||||
'thing { test: value; }\n' +
|
'thing { test: value; }\n' +
|
||||||
|
|||||||
Reference in New Issue
Block a user