.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / stylelint / lib / rules / no-extra-semicolons / index.js
1 "use strict";
2
3 const isCustomPropertySet = require("../../utils/isCustomPropertySet");
4 const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule");
5 const report = require("../../utils/report");
6 const ruleMessages = require("../../utils/ruleMessages");
7 const styleSearch = require("style-search");
8 const validateOptions = require("../../utils/validateOptions");
9
10 const ruleName = "no-extra-semicolons";
11
12 const messages = ruleMessages(ruleName, {
13   rejected: "Unexpected extra semicolon"
14 });
15
16 function getOffsetByNode(node) {
17   const string = node.root().source.input.css;
18   const nodeColumn = node.source.start.column;
19   const nodeLine = node.source.start.line;
20   let line = 1;
21   let column = 1;
22   let index = 0;
23
24   for (let i = 0; i < string.length; i++) {
25     if (column === nodeColumn && nodeLine === line) {
26       index = i;
27       break;
28     }
29
30     if (string[i] === "\n") {
31       column = 1;
32       line += 1;
33     } else {
34       column += 1;
35     }
36   }
37
38   return index;
39 }
40
41 const rule = function(actual) {
42   return (root, result) => {
43     const validOptions = validateOptions(result, ruleName, { actual });
44     if (!validOptions) {
45       return;
46     }
47
48     const rawAfterRoot = root.raws.after;
49
50     if (rawAfterRoot && rawAfterRoot.trim().length !== 0) {
51       styleSearch({ source: rawAfterRoot, target: ";" }, match => {
52         complain(
53           root.toString().length - rawAfterRoot.length + match.startIndex
54         );
55       });
56     }
57
58     root.walk(node => {
59       if (
60         node.type === "rule" &&
61         !isCustomPropertySet(node) &&
62         !isStandardSyntaxRule(node)
63       ) {
64         return;
65       }
66
67       let rawBeforeNode = node.raws.before;
68
69       if (rawBeforeNode && rawBeforeNode.trim().length !== 0) {
70         let allowedSemi = 0;
71
72         const next = node.next();
73
74         // Ignore semicolon before comment if next node is custom properties sets or comment
75         if (
76           node.type === "comment" &&
77           next &&
78           isCustomPropertySet(next) &&
79           node.parent.index(next) > 0
80         ) {
81           allowedSemi = 1;
82         }
83
84         const prev = node.prev();
85
86         // Adding previous node string to custom properties set if previous node is comment
87         if (
88           isCustomPropertySet(node) &&
89           node.parent.index(node) > 0 &&
90           prev &&
91           prev.type === "comment"
92         ) {
93           rawBeforeNode = prev.toString() + rawBeforeNode;
94           allowedSemi = 0;
95         }
96
97         styleSearch({ source: rawBeforeNode, target: ";" }, (match, count) => {
98           if (count === allowedSemi) {
99             return;
100           }
101
102           complain(
103             getOffsetByNode(node) - rawBeforeNode.length + match.startIndex
104           );
105         });
106       }
107
108       const rawAfterNode = node.raws.after;
109
110       if (rawAfterNode && rawAfterNode.trim().length !== 0) {
111         /**
112          * If the last child is a Less mixin followed by more than one semicolon,
113          * node.raws.after will be populated with that semicolon.
114          * Since we ignore Less mixins, exit here
115          */
116         if (
117           node.last &&
118           node.last.type === "rule" &&
119           !isCustomPropertySet(node.last) &&
120           !isStandardSyntaxRule(node.last)
121         ) {
122           return;
123         }
124
125         styleSearch({ source: rawAfterNode, target: ";" }, match => {
126           const index =
127             getOffsetByNode(node) +
128             node.toString().length -
129             1 -
130             rawAfterNode.length +
131             match.startIndex;
132
133           complain(index);
134         });
135       }
136
137       const rawOwnSemicolon = node.raws.ownSemicolon;
138
139       if (rawOwnSemicolon) {
140         let allowedSemi = 0;
141
142         if (isCustomPropertySet(node)) {
143           allowedSemi = 1;
144         }
145
146         styleSearch(
147           { source: rawOwnSemicolon, target: ";" },
148           (match, count) => {
149             if (count === allowedSemi) {
150               return;
151             }
152
153             const index =
154               getOffsetByNode(node) +
155               node.toString().length -
156               rawOwnSemicolon.length +
157               match.startIndex;
158             complain(index);
159           }
160         );
161       }
162     });
163
164     function complain(index) {
165       report({
166         message: messages.rejected,
167         node: root,
168         index,
169         result,
170         ruleName
171       });
172     }
173   };
174 };
175
176 rule.ruleName = ruleName;
177 rule.messages = messages;
178 module.exports = rule;