3 const _ = require("lodash");
4 const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule");
5 const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector");
6 const optionsMatches = require("../../utils/optionsMatches");
7 const parseSelector = require("../../utils/parseSelector");
8 const report = require("../../utils/report");
9 const resolvedNestedSelector = require("postcss-resolve-nested-selector");
10 const ruleMessages = require("../../utils/ruleMessages");
11 const validateOptions = require("../../utils/validateOptions");
13 const ruleName = "selector-max-attribute";
15 const messages = ruleMessages(ruleName, {
16 expected: (selector, max) =>
17 `Expected "${selector}" to have no more than ${max} attribute ${
18 max === 1 ? "selector" : "selectors"
22 function rule(max, options) {
23 return (root, result) => {
24 const validOptions = validateOptions(
31 return typeof max === "number" && max >= 0;
38 ignoreAttributes: [_.isString]
47 function checkSelector(selectorNode, ruleNode) {
48 const count = selectorNode.reduce((total, childNode) => {
49 // Only traverse inside actual selectors and :not()
50 if (childNode.type === "selector" || childNode.value === ":not") {
51 checkSelector(childNode, ruleNode);
54 if (childNode.type !== "attribute") {
55 // Not an attribute node -> ignore
58 if (optionsMatches(options, "ignoreAttributes", childNode.attribute)) {
59 // it's an attribute that is supposed to be ignored
67 selectorNode.type !== "root" &&
68 selectorNode.type !== "pseudo" &&
75 message: messages.expected(selectorNode, max),
81 root.walkRules(ruleNode => {
82 if (!isStandardSyntaxRule(ruleNode)) {
85 if (!isStandardSyntaxSelector(ruleNode.selector)) {
90 node => ["rule", "atrule"].indexOf(node.type) !== -1
93 // Skip unresolved nested selectors
97 ruleNode.selectors.forEach(selector => {
98 resolvedNestedSelector(selector, ruleNode).forEach(resolvedSelector => {
99 parseSelector(resolvedSelector, result, ruleNode, container =>
100 checkSelector(container, ruleNode)
108 rule.ruleName = ruleName;
109 rule.messages = messages;
110 module.exports = rule;