.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / stylelint / lib / rules / no-duplicate-selectors / index.js
1 "use strict";
2
3 const _ = require("lodash");
4 const findAtRuleContext = require("../../utils/findAtRuleContext");
5 const isKeyframeRule = require("../../utils/isKeyframeRule");
6 const nodeContextLookup = require("../../utils/nodeContextLookup");
7 const normalizeSelector = require("normalize-selector");
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 = "no-duplicate-selectors";
14
15 const messages = ruleMessages(ruleName, {
16   rejected: (selector, firstDuplicateLine) =>
17     `Unexpected duplicate selector "${selector}", first used at line ${
18       firstDuplicateLine
19     }`
20 });
21
22 const rule = function(actual) {
23   return (root, result) => {
24     const validOptions = validateOptions(result, ruleName, { actual });
25     if (!validOptions) {
26       return;
27     }
28
29     // The top level of this map will be rule sources.
30     // Each source maps to another map, which maps rule parents to a set of selectors.
31     // This ensures that selectors are only checked against selectors
32     // from other rules that share the same parent and the same source.
33     const selectorContextLookup = nodeContextLookup();
34
35     root.walkRules(rule => {
36       if (isKeyframeRule(rule)) {
37         return;
38       }
39
40       const contextSelectorSet = selectorContextLookup.getContext(
41         rule,
42         findAtRuleContext(rule)
43       );
44       const resolvedSelectors = rule.selectors.reduce((result, selector) => {
45         return _.union(result, resolvedNestedSelector(selector, rule));
46       }, []);
47       const normalizedSelectorList = resolvedSelectors.map(normalizeSelector);
48       const selectorLine = rule.source.start.line;
49
50       // Complain if the same selector list occurs twice
51
52       // Sort the selectors list so that the order of the constituents
53       // doesn't matter
54       const sortedSelectorList = normalizedSelectorList
55         .slice()
56         .sort()
57         .join(",");
58       if (contextSelectorSet.has(sortedSelectorList)) {
59         // If the selector isn't nested we can use its raw value; otherwise,
60         // we have to approximate something for the message -- which is close enough
61         const isNestedSelector =
62           resolvedSelectors.join(",") !== rule.selectors.join(",");
63         const selectorForMessage = isNestedSelector
64           ? resolvedSelectors.join(", ")
65           : rule.selector;
66         const previousDuplicatePosition = contextSelectorSet.get(
67           sortedSelectorList
68         );
69
70         return report({
71           result,
72           ruleName,
73           node: rule,
74           message: messages.rejected(
75             selectorForMessage,
76             previousDuplicatePosition
77           )
78         });
79       }
80
81       contextSelectorSet.set(sortedSelectorList, selectorLine);
82
83       // Or complain if one selector list contains the same selector more than one
84       rule.selectors.forEach((selector, i) => {
85         if (
86           _.includes(
87             normalizedSelectorList.slice(0, i),
88             normalizeSelector(selector)
89           )
90         ) {
91           report({
92             result,
93             ruleName,
94             node: rule,
95             message: messages.rejected(selector, selectorLine)
96           });
97         }
98       });
99     });
100   };
101 };
102
103 rule.ruleName = ruleName;
104 rule.messages = messages;
105 module.exports = rule;