Skip to content

Commit 1edcc67

Browse files
committed
Support relative selectors in pseudo-classes
1 parent e099000 commit 1edcc67

File tree

2 files changed

+35
-5
lines changed

2 files changed

+35
-5
lines changed

src/parser.ts

+12-5
Original file line numberDiff line numberDiff line change
@@ -299,13 +299,13 @@ export function createParser(
299299
}
300300
}
301301

302-
function parseSelector(): AstSelector {
302+
function parseSelector(relative = false): AstSelector {
303303
skipWhitespace();
304-
const rules: AstRule[] = [parseRule()];
304+
const rules: AstRule[] = [parseRule(relative)];
305305
while (is(',')) {
306306
next();
307307
skipWhitespace();
308-
rules.push(parseRule());
308+
rules.push(parseRule(relative));
309309
}
310310
return {
311311
type: 'Selector',
@@ -493,7 +493,7 @@ export function createParser(
493493
};
494494
assert(pseudo.argument.value, 'Expected pseudo-class argument value.');
495495
} else if (pseudoDefinition.type === 'Selector') {
496-
pseudo.argument = parseSelector();
496+
pseudo.argument = parseSelector(true);
497497
} else if (pseudoDefinition.type === 'Formula') {
498498
const [a, b] = parseFormula();
499499
pseudo.argument = {
@@ -596,9 +596,16 @@ export function createParser(
596596
}
597597
}
598598

599-
function parseRule(): AstRule {
599+
function parseRule(relative = false): AstRule {
600600
const rule: Partial<AstRule> = {};
601601
let isRuleStart = true;
602+
if (relative) {
603+
const combinator = matchMulticharIndex(combinatorsIndex);
604+
if (combinator) {
605+
rule.combinator = combinator;
606+
skipWhitespace();
607+
}
608+
}
602609
while (pos < l) {
603610
if (isTagStart()) {
604611
assert(isRuleStart, 'Unexpected tag/namespace start.');

test/parser.test.ts

+23
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,29 @@ describe('parse()', () => {
11231123
})
11241124
);
11251125
});
1126+
it('should parse a relative nested selector', () => {
1127+
expect(parse(':has(>div)')).toEqual(
1128+
ast.selector({
1129+
rules: [
1130+
ast.rule({
1131+
pseudoClasses: [
1132+
ast.pseudoClass({
1133+
name: 'has',
1134+
argument: ast.selector({
1135+
rules: [
1136+
ast.rule({
1137+
tag: ast.tagName({name: 'div'}),
1138+
combinator: '>'
1139+
})
1140+
]
1141+
})
1142+
})
1143+
]
1144+
})
1145+
]
1146+
})
1147+
);
1148+
});
11261149
it('should fail on empty substitute name', () => {
11271150
expect(() =>
11281151
createParser({

0 commit comments

Comments
 (0)