.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / stylelint / lib / assignDisabledRanges.js
1 /* @flow */
2 "use strict";
3 const _ = require("lodash");
4
5 const COMMAND_PREFIX = "stylelint-";
6 const disableCommand = COMMAND_PREFIX + "disable";
7 const enableCommand = COMMAND_PREFIX + "enable";
8 const disableLineCommand = COMMAND_PREFIX + "disable-line";
9 const disableNextLineCommand = COMMAND_PREFIX + "disable-next-line";
10 const ALL_RULES = "all";
11
12 /*:: type disabledRangeObject = {
13   [ruleName: string]: Array<{
14     start: number,
15     end?: number,
16   }>
17 }*/
18
19 // Run it like a plugin ...
20 module.exports = function(
21   root /*: Object*/,
22   result /*: Object*/
23 ) /*: postcss$result*/ {
24   result.stylelint = result.stylelint || {};
25
26   // Most of the functions below work via side effects mutating
27   // this object
28   const disabledRanges /*: disabledRangeObject*/ = {
29     all: []
30   };
31   result.stylelint.disabledRanges = disabledRanges;
32   root.walkComments(checkComment);
33
34   return result;
35
36   function processDisableLineCommand(comment /*: postcss$comment*/) {
37     getCommandRules(disableLineCommand, comment.text).forEach(ruleName => {
38       disableLine(comment.source.start.line, ruleName, comment);
39     });
40   }
41
42   function processDisableNextLineCommand(comment /*: postcss$comment*/) {
43     getCommandRules(disableNextLineCommand, comment.text).forEach(ruleName => {
44       disableLine(comment.source.start.line + 1, ruleName, comment);
45     });
46   }
47
48   function disableLine(
49     line /*: number*/,
50     ruleName /*: string*/,
51     comment /*: postcss$comment*/
52   ) {
53     if (ruleIsDisabled(ALL_RULES)) {
54       throw comment.error("All rules have already been disabled", {
55         plugin: "stylelint"
56       });
57     }
58     if (ruleIsDisabled(ruleName)) {
59       throw comment.error(`"${ruleName}" has already been disabled`, {
60         plugin: "stylelint"
61       });
62     }
63     if (ruleName === ALL_RULES) {
64       Object.keys(disabledRanges).forEach(disabledRuleName => {
65         startDisabledRange(line, disabledRuleName);
66         endDisabledRange(line, disabledRuleName);
67       });
68     } else {
69       startDisabledRange(line, ruleName);
70       endDisabledRange(line, ruleName);
71     }
72   }
73
74   function processDisableCommand(comment /*: postcss$comment*/) {
75     getCommandRules(disableCommand, comment.text).forEach(ruleToDisable => {
76       if (ruleToDisable === ALL_RULES) {
77         if (ruleIsDisabled(ALL_RULES)) {
78           throw comment.error("All rules have already been disabled", {
79             plugin: "stylelint"
80           });
81         }
82         Object.keys(disabledRanges).forEach(ruleName => {
83           startDisabledRange(comment.source.start.line, ruleName);
84         });
85         return;
86       }
87
88       if (ruleIsDisabled(ruleToDisable)) {
89         throw comment.error(`"${ruleToDisable}" has already been disabled`, {
90           plugin: "stylelint"
91         });
92       }
93       startDisabledRange(comment.source.start.line, ruleToDisable);
94     });
95   }
96
97   function processEnableCommand(comment /*: postcss$comment*/) {
98     getCommandRules(enableCommand, comment.text).forEach(ruleToEnable => {
99       if (ruleToEnable === ALL_RULES) {
100         if (
101           _.values(disabledRanges).every(
102             ranges => _.isEmpty(ranges) || !!_.last(ranges.end)
103           )
104         ) {
105           throw comment.error("No rules have been disabled", {
106             plugin: "stylelint"
107           });
108         }
109         Object.keys(disabledRanges).forEach(ruleName => {
110           if (!_.get(_.last(disabledRanges[ruleName]), "end")) {
111             endDisabledRange(comment.source.end.line, ruleName);
112           }
113         });
114         return;
115       }
116
117       if (
118         ruleIsDisabled(ALL_RULES) &&
119         disabledRanges[ruleToEnable] === undefined
120       ) {
121         // Get a starting point from the where all rules were disabled
122         if (!disabledRanges[ruleToEnable]) {
123           disabledRanges[ruleToEnable] = _.cloneDeep(disabledRanges.all);
124         } else {
125           disabledRanges[ruleToEnable].push(
126             _.clone(_.last(disabledRanges[ALL_RULES]))
127           );
128         }
129         endDisabledRange(comment.source.end.line, ruleToEnable);
130         return;
131       }
132
133       if (ruleIsDisabled(ruleToEnable)) {
134         endDisabledRange(comment.source.end.line, ruleToEnable);
135         return;
136       }
137
138       throw comment.error(`"${ruleToEnable}" has not been disabled`, {
139         plugin: "stylelint"
140       });
141     });
142   }
143
144   function checkComment(comment /*: postcss$comment*/) {
145     const text = comment.text;
146
147     // Ignore comments that are not relevant commands
148
149     if (text.indexOf(COMMAND_PREFIX) !== 0) {
150       return result;
151     }
152
153     if (text.indexOf(disableLineCommand) === 0) {
154       processDisableLineCommand(comment);
155     } else if (text.indexOf(disableNextLineCommand) === 0) {
156       processDisableNextLineCommand(comment);
157     } else if (text.indexOf(disableCommand) === 0) {
158       processDisableCommand(comment);
159     } else if (text.indexOf(enableCommand) === 0) {
160       processEnableCommand(comment);
161     }
162   }
163
164   function getCommandRules(
165     command /*: string*/,
166     fullText /*: string*/
167   ) /*: Array<string>*/ {
168     const rules = _.compact(fullText.slice(command.length).split(",")).map(r =>
169       r.trim()
170     );
171     if (_.isEmpty(rules)) {
172       return [ALL_RULES];
173     }
174     return rules;
175   }
176
177   function startDisabledRange(line /*: number*/, ruleName /*: string*/) {
178     const rangeObj = { start: line };
179     ensureRuleRanges(ruleName);
180     disabledRanges[ruleName].push(rangeObj);
181   }
182
183   function endDisabledRange(line /*: number*/, ruleName /*: string*/) {
184     const lastRangeForRule = _.last(disabledRanges[ruleName]);
185     if (!lastRangeForRule) {
186       return;
187     }
188     // Add an `end` prop to the last range of that rule
189     lastRangeForRule.end = line;
190   }
191
192   function ensureRuleRanges(ruleName /*: string*/) {
193     if (!disabledRanges[ruleName]) {
194       disabledRanges[ruleName] = _.cloneDeep(disabledRanges.all);
195     }
196   }
197
198   function ruleIsDisabled(ruleName /*: string*/) /*: boolean*/ {
199     if (disabledRanges[ruleName] === undefined) return false;
200     if (_.last(disabledRanges[ruleName]) === undefined) return false;
201     if (_.get(_.last(disabledRanges[ruleName]), "end") === undefined)
202       return true;
203     return false;
204   }
205 };