.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / stylelint / lib / needlessDisables.js
1 /* @flow */
2 "use strict";
3
4 const _ = require("lodash");
5
6 /*:: type rangeDataType = {
7   all: Array<Object>,
8 }
9 */
10
11 /*:: type rangeType = {
12   end?: number,
13   start: number,
14   used?: boolean,
15 }*/
16
17 /*:: type unusedRangeT = {
18   start: number,
19   end?: number,
20 }*/
21
22 module.exports = function(
23   results /*: Array<stylelint$result>*/
24 ) /*: stylelint$needlessDisablesReport*/ {
25   const report = [];
26
27   results.forEach(result => {
28     // File with `CssSyntaxError` have not `_postcssResult`
29     if (!result._postcssResult) {
30       return;
31     }
32
33     const unused = { source: result.source, ranges: [] };
34     const rangeData /*: ?rangeDataType*/ = _.cloneDeep(
35       result._postcssResult.stylelint.disabledRanges
36     );
37
38     if (!rangeData) {
39       return;
40     }
41
42     result.warnings.forEach(warning => {
43       const rule /*: string*/ = warning.rule;
44
45       const ruleRanges /*: Array<Object>*/ = rangeData[rule];
46       if (ruleRanges) {
47         // Back to front so we get the *last* range that applies to the warning
48         for (const range of ruleRanges.reverse()) {
49           if (isWarningInRange(warning, range)) {
50             range.used = true;
51             return;
52           }
53         }
54       }
55
56       for (const range of rangeData.all.reverse()) {
57         if (isWarningInRange(warning, range)) {
58           range.used = true;
59           return;
60         }
61       }
62     });
63
64     Object.keys(rangeData).forEach(rule => {
65       rangeData[rule].forEach((range /*: rangeType*/) => {
66         // Is an equivalent range already marked as unused?
67         const alreadyMarkedUnused /*: ?unusedRangeT*/ = unused.ranges.find((
68           unusedRange /*: unusedRangeT*/
69         ) => {
70           return (
71             unusedRange.start === range.start && unusedRange.end === range.end
72           );
73         });
74
75         // If this range is unused and no equivalent is marked,
76         // mark this range as unused
77         if (!range.used && !alreadyMarkedUnused) {
78           unused.ranges.push(range);
79         }
80
81         // If this range is used but an equivalent has been marked as unused,
82         // remove that equivalent. This can happen because of the duplication
83         // of ranges in rule-specific range sets and the "all" range set
84         if (range.used && alreadyMarkedUnused) {
85           _.remove(unused.ranges, alreadyMarkedUnused);
86         }
87       });
88     });
89
90     unused.ranges = _.sortBy(unused.ranges, ["start", "end"]);
91
92     report.push(unused);
93   });
94
95   return report;
96 };
97
98 function isWarningInRange(
99   warning /*: {
100     column: number,
101     rule: string,
102     line: number,
103     severity: string,
104     text: string,
105   }*/,
106   range /*: {
107     rules?: Array<string>,
108     start: number,
109     end?: number,
110     used?: boolean,
111   }*/
112 ) /*: boolean*/ {
113   const rule = warning.rule,
114     line = warning.line;
115
116   // Need to check if range.end exist, because line number type cannot be compared to undefined
117   return (
118     range.start <= line &&
119     ((range.end !== undefined && range.end >= line) ||
120       range.end === undefined) &&
121     (!range.rules || range.rules.indexOf(rule) !== -1)
122   );
123 }