--- /dev/null
+"use strict";
+
+const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule");
+const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector");
+const parseSelector = require("../../utils/parseSelector");
+const report = require("../../utils/report");
+const resolvedNestedSelector = require("postcss-resolve-nested-selector");
+const ruleMessages = require("../../utils/ruleMessages");
+const validateOptions = require("../../utils/validateOptions");
+
+const ruleName = "selector-max-compound-selectors";
+
+const messages = ruleMessages(ruleName, {
+ expected: (selector, max) =>
+ `Expected "${selector}" to have no more than ${max} compound ${
+ max === 1 ? "selector" : "selectors"
+ }`
+});
+
+const rule = function(max) {
+ return (root, result) => {
+ const validOptions = validateOptions(result, ruleName, {
+ actual: max,
+ possible: [
+ function(max) {
+ return typeof max === "number" && max > 0;
+ }
+ ]
+ });
+ if (!validOptions) {
+ return;
+ }
+
+ // Finds actual selectors in selectorNode object and checks them
+ function checkSelector(selectorNode, rule) {
+ let compoundCount = 1;
+
+ selectorNode.each(childNode => {
+ // Only traverse inside actual selectors and :not()
+ if (childNode.type === "selector" || childNode.value === ":not") {
+ checkSelector(childNode, rule);
+ }
+
+ // Compound selectors are separated by combinators, so increase count when meeting one
+ if (childNode.type === "combinator") {
+ compoundCount++;
+ }
+ });
+
+ if (
+ selectorNode.type !== "root" &&
+ selectorNode.type !== "pseudo" &&
+ compoundCount > max
+ ) {
+ report({
+ ruleName,
+ result,
+ node: rule,
+ message: messages.expected(selectorNode, max),
+ word: selectorNode
+ });
+ }
+ }
+
+ root.walkRules(rule => {
+ if (!isStandardSyntaxRule(rule)) {
+ return;
+ }
+ if (!isStandardSyntaxSelector(rule.selector)) {
+ return;
+ }
+
+ // Using `rule.selectors` gets us each selector if there is a comma separated set
+ rule.selectors.forEach(selector => {
+ resolvedNestedSelector(selector, rule).forEach(resolvedSelector => {
+ // Process each resolved selector with `checkSelector` via postcss-selector-parser
+ parseSelector(resolvedSelector, result, rule, s =>
+ checkSelector(s, rule)
+ );
+ });
+ });
+ });
+ };
+};
+
+rule.ruleName = ruleName;
+rule.messages = messages;
+module.exports = rule;