Skip to content

Commit 6ff2cef

Browse files
committed
feat: add selector support
Fixes #60
1 parent a9692b0 commit 6ff2cef

File tree

3 files changed

+83
-0
lines changed

3 files changed

+83
-0
lines changed

docs/rules/require-baseline.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ This rule warns when it finds any of the following:
2323
- A media condition inside `@media` that isn't widely available.
2424
- A CSS property value that isn't widely available or otherwise isn't enclosed in a `@supports` block (currently limited to identifiers only).
2525
- A CSS property function that isn't widely available.
26+
- A CSS pseudo-element or pseudo-class selector that isn't widely available.
2627

2728
The data is provided via the [web-features](https://npmjs.com/package/web-features) package.
2829

@@ -39,6 +40,11 @@ a {
3940
width: abs(20% - 100px);
4041
}
4142

43+
/* invalid - :has() is not widely available */
44+
h1:has(+ h2) {
45+
margin: 0 0 0.25rem 0;
46+
}
47+
4248
/* invalid - device-posture is not widely available */
4349
@media (device-posture: folded) {
4450
a {

src/rules/require-baseline.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
atRules,
1616
mediaConditions,
1717
types,
18+
selectors,
1819
} from "../data/baseline-data.js";
1920
import { namedColors } from "../data/colors.js";
2021

@@ -347,6 +348,8 @@ export default {
347348
"Type '{{type}}' is not a {{availability}} available baseline feature.",
348349
notBaselineMediaCondition:
349350
"Media condition '{{condition}}' is not a {{availability}} available baseline feature.",
351+
notBaselineSelector:
352+
"Selector '{{selector}}' is not a {{availability}} available baseline feature.",
350353
},
351354
},
352355

@@ -625,6 +628,48 @@ export default {
625628
});
626629
}
627630
},
631+
632+
Selector(node) {
633+
for (const child of node.children) {
634+
const selector = child.name;
635+
636+
if (!selectors.has(selector)) {
637+
continue;
638+
}
639+
640+
const ruleLevel = selectors.get(selector);
641+
642+
if (ruleLevel < baselineLevel) {
643+
const loc = child.loc;
644+
645+
// some selectors are prefixed with the : or :: symbols
646+
let prefixSymbolLength = 0;
647+
if (child.type === "PseudoClassSelector") {
648+
prefixSymbolLength = 1;
649+
} else if (child.type === "PseudoElementSelector") {
650+
prefixSymbolLength = 2;
651+
}
652+
653+
context.report({
654+
loc: {
655+
start: loc.start,
656+
end: {
657+
line: loc.start.line,
658+
column:
659+
loc.start.column +
660+
selector.length +
661+
prefixSymbolLength,
662+
},
663+
},
664+
messageId: "notBaselineSelector",
665+
data: {
666+
selector,
667+
availability,
668+
},
669+
});
670+
}
671+
}
672+
},
628673
};
629674
},
630675
};

tests/rules/require-baseline.test.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,5 +340,37 @@ ruleTester.run("require-baseline", rule, {
340340
},
341341
],
342342
},
343+
{
344+
code: "h1:has(+ h2) { margin: 0 0 0.25rem 0; }",
345+
errors: [
346+
{
347+
messageId: "notBaselineSelector",
348+
data: {
349+
selector: "has",
350+
availability: "widely",
351+
},
352+
line: 1,
353+
column: 3,
354+
endLine: 1,
355+
endColumn: 7,
356+
},
357+
],
358+
},
359+
{
360+
code: "details::details-content { background-color: #a29bfe; }",
361+
errors: [
362+
{
363+
messageId: "notBaselineSelector",
364+
data: {
365+
selector: "details-content",
366+
availability: "widely",
367+
},
368+
line: 1,
369+
column: 8,
370+
endLine: 1,
371+
endColumn: 25,
372+
},
373+
],
374+
},
343375
],
344376
});

0 commit comments

Comments
 (0)