.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / stylelint / lib / rules / selector-max-attribute / index.js
1 "use strict";
2
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");
12
13 const ruleName = "selector-max-attribute";
14
15 const messages = ruleMessages(ruleName, {
16   expected: (selector, max) =>
17     `Expected "${selector}" to have no more than ${max} attribute ${
18       max === 1 ? "selector" : "selectors"
19     }`
20 });
21
22 function rule(max, options) {
23   return (root, result) => {
24     const validOptions = validateOptions(
25       result,
26       ruleName,
27       {
28         actual: max,
29         possible: [
30           function(max) {
31             return typeof max === "number" && max >= 0;
32           }
33         ]
34       },
35       {
36         actual: options,
37         possible: {
38           ignoreAttributes: [_.isString]
39         },
40         optional: true
41       }
42     );
43     if (!validOptions) {
44       return;
45     }
46
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);
52         }
53
54         if (childNode.type !== "attribute") {
55           // Not an attribute node -> ignore
56           return total;
57         }
58         if (optionsMatches(options, "ignoreAttributes", childNode.attribute)) {
59           // it's an attribute that is supposed to be ignored
60           return total;
61         }
62
63         return (total += 1);
64       }, 0);
65
66       if (
67         selectorNode.type !== "root" &&
68         selectorNode.type !== "pseudo" &&
69         count > max
70       ) {
71         report({
72           ruleName,
73           result,
74           node: ruleNode,
75           message: messages.expected(selectorNode, max),
76           word: selectorNode
77         });
78       }
79     }
80
81     root.walkRules(ruleNode => {
82       if (!isStandardSyntaxRule(ruleNode)) {
83         return;
84       }
85       if (!isStandardSyntaxSelector(ruleNode.selector)) {
86         return;
87       }
88       if (
89         ruleNode.nodes.some(
90           node => ["rule", "atrule"].indexOf(node.type) !== -1
91         )
92       ) {
93         // Skip unresolved nested selectors
94         return;
95       }
96
97       ruleNode.selectors.forEach(selector => {
98         resolvedNestedSelector(selector, ruleNode).forEach(resolvedSelector => {
99           parseSelector(resolvedSelector, result, ruleNode, container =>
100             checkSelector(container, ruleNode)
101           );
102         });
103       });
104     });
105   };
106 }
107
108 rule.ruleName = ruleName;
109 rule.messages = messages;
110 module.exports = rule;