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-class";
13 const messages = ruleMessages(ruleName, {
14 expected: (selector, max) =>
15 `Expected "${selector}" to have no more than ${max} ${
16 max === 1 ? "class" : "classes"
21 return (root, result) => {
22 const validOptions = validateOptions(result, ruleName, {
26 return typeof max === "number" && max >= 0;
34 function checkSelector(selectorNode, ruleNode) {
35 const count = selectorNode.reduce((total, childNode) => {
36 // Only traverse inside actual selectors and :not()
37 if (childNode.type === "selector" || childNode.value === ":not") {
38 checkSelector(childNode, ruleNode);
41 return (total += childNode.type === "class" ? 1 : 0);
45 selectorNode.type !== "root" &&
46 selectorNode.type !== "pseudo" &&
53 message: messages.expected(selectorNode, max),
59 root.walkRules(ruleNode => {
60 if (!isStandardSyntaxRule(ruleNode)) {
63 if (!isStandardSyntaxSelector(ruleNode.selector)) {
68 node => ["rule", "atrule"].indexOf(node.type) !== -1
71 // Skip unresolved nested selectors
75 ruleNode.selectors.forEach(selector => {
76 resolvedNestedSelector(selector, ruleNode).forEach(resolvedSelector => {
77 parseSelector(resolvedSelector, result, ruleNode, container =>
78 checkSelector(container, ruleNode)
86 rule.ruleName = ruleName;
87 rule.messages = messages;
88 module.exports = rule;