3 const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule");
4 const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector");
5 const parseSelector = require("../../utils/parseSelector");
6 const report = require("../../utils/report");
7 const resolvedNestedSelector = require("postcss-resolve-nested-selector");
8 const ruleMessages = require("../../utils/ruleMessages");
9 const validateOptions = require("../../utils/validateOptions");
11 const ruleName = "selector-max-compound-selectors";
13 const messages = ruleMessages(ruleName, {
14 expected: (selector, max) =>
15 `Expected "${selector}" to have no more than ${max} compound ${
16 max === 1 ? "selector" : "selectors"
20 const rule = function(max) {
21 return (root, result) => {
22 const validOptions = validateOptions(result, ruleName, {
26 return typeof max === "number" && max > 0;
34 // Finds actual selectors in selectorNode object and checks them
35 function checkSelector(selectorNode, rule) {
36 let compoundCount = 1;
38 selectorNode.each(childNode => {
39 // Only traverse inside actual selectors and :not()
40 if (childNode.type === "selector" || childNode.value === ":not") {
41 checkSelector(childNode, rule);
44 // Compound selectors are separated by combinators, so increase count when meeting one
45 if (childNode.type === "combinator") {
51 selectorNode.type !== "root" &&
52 selectorNode.type !== "pseudo" &&
59 message: messages.expected(selectorNode, max),
65 root.walkRules(rule => {
66 if (!isStandardSyntaxRule(rule)) {
69 if (!isStandardSyntaxSelector(rule.selector)) {
73 // Using `rule.selectors` gets us each selector if there is a comma separated set
74 rule.selectors.forEach(selector => {
75 resolvedNestedSelector(selector, rule).forEach(resolvedSelector => {
76 // Process each resolved selector with `checkSelector` via postcss-selector-parser
77 parseSelector(resolvedSelector, result, rule, s =>
78 checkSelector(s, rule)
86 rule.ruleName = ruleName;
87 rule.messages = messages;
88 module.exports = rule;