.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / stylelint / lib / rules / no-descending-specificity / index.js
1 "use strict";
2
3 const _ = require("lodash");
4 const findAtRuleContext = require("../../utils/findAtRuleContext");
5 const isCustomPropertySet = require("../../utils/isCustomPropertySet");
6 const keywordSets = require("../../reference/keywordSets");
7 const nodeContextLookup = require("../../utils/nodeContextLookup");
8 const parseSelector = require("../../utils/parseSelector");
9 const report = require("../../utils/report");
10 const resolvedNestedSelector = require("postcss-resolve-nested-selector");
11 const ruleMessages = require("../../utils/ruleMessages");
12 const specificity = require("specificity");
13 const validateOptions = require("../../utils/validateOptions");
14
15 const ruleName = "no-descending-specificity";
16
17 const messages = ruleMessages(ruleName, {
18   rejected: (b, a) => `Expected selector "${b}" to come before selector "${a}"`
19 });
20
21 const rule = function(actual) {
22   return (root, result) => {
23     const validOptions = validateOptions(result, ruleName, { actual });
24     if (!validOptions) {
25       return;
26     }
27
28     const selectorContextLookup = nodeContextLookup();
29
30     root.walkRules(rule => {
31       // Ignore custom property set `--foo: {};`
32       if (isCustomPropertySet(rule)) {
33         return;
34       }
35
36       const comparisonContext = selectorContextLookup.getContext(
37         rule,
38         findAtRuleContext(rule)
39       );
40
41       rule.selectors.forEach(selector => {
42         const trimSelector = selector.trim();
43         // Ignore `.selector, { }`
44         if (trimSelector === "") {
45           return;
46         }
47
48         // The edge-case of duplicate selectors will act acceptably
49         const index = rule.selector.indexOf(trimSelector);
50         // Resolve any nested selectors before checking
51         resolvedNestedSelector(selector, rule).forEach(resolvedSelector => {
52           parseSelector(resolvedSelector, result, rule, s =>
53             checkSelector(s, rule, index, comparisonContext)
54           );
55         });
56       });
57     });
58
59     function checkSelector(selectorNode, rule, sourceIndex, comparisonContext) {
60       const selector = selectorNode.toString();
61       const referenceSelectorNode = lastCompoundSelectorWithoutPseudoClasses(
62         selectorNode
63       );
64       const selectorSpecificity = specificity.calculate(selector)[0]
65         .specificityArray;
66       const entry = { selector, specificity: selectorSpecificity };
67
68       if (!comparisonContext.has(referenceSelectorNode)) {
69         comparisonContext.set(referenceSelectorNode, [entry]);
70         return;
71       }
72
73       const priorComparableSelectors = comparisonContext.get(
74         referenceSelectorNode
75       );
76
77       priorComparableSelectors.forEach(priorEntry => {
78         if (
79           specificity.compare(selectorSpecificity, priorEntry.specificity) ===
80           -1
81         ) {
82           report({
83             ruleName,
84             result,
85             node: rule,
86             message: messages.rejected(selector, priorEntry.selector),
87             index: sourceIndex
88           });
89         }
90       });
91
92       priorComparableSelectors.push(entry);
93     }
94   };
95 };
96
97 function lastCompoundSelectorWithoutPseudoClasses(selectorNode) {
98   const nodesAfterLastCombinator = _.last(
99     selectorNode.nodes[0].split(node => {
100       return node.type === "combinator";
101     })
102   );
103
104   const nodesWithoutPseudoClasses = nodesAfterLastCombinator
105     .filter(node => {
106       return (
107         node.type !== "pseudo" ||
108         keywordSets.pseudoElements.has(node.value.replace(/:/g, ""))
109       );
110     })
111     .join("");
112
113   return nodesWithoutPseudoClasses.toString();
114 }
115
116 rule.ruleName = ruleName;
117 rule.messages = messages;
118 module.exports = rule;