.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / stylelint / lib / rules / string-quotes / index.js
1 "use strict";
2
3 const _ = require("lodash");
4 const atRuleParamIndex = require("../../utils/atRuleParamIndex");
5 const declarationValueIndex = require("../../utils/declarationValueIndex");
6 const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule");
7 const parseSelector = require("../../utils/parseSelector");
8 const report = require("../../utils/report");
9 const ruleMessages = require("../../utils/ruleMessages");
10 const validateOptions = require("../../utils/validateOptions");
11 const valueParser = require("postcss-value-parser");
12
13 const ruleName = "string-quotes";
14
15 const messages = ruleMessages(ruleName, {
16   expected: q => `Expected ${q} quotes`
17 });
18
19 const singleQuote = `'`;
20 const doubleQuote = `"`;
21
22 const rule = function(expectation, secondary, context) {
23   const correctQuote = expectation === "single" ? singleQuote : doubleQuote;
24   const erroneousQuote = expectation === "single" ? doubleQuote : singleQuote;
25
26   return (root, result) => {
27     const validOptions = validateOptions(
28       result,
29       ruleName,
30       {
31         actual: expectation,
32         possible: ["single", "double"]
33       },
34       {
35         actual: secondary,
36         possible: {
37           avoidEscape: _.isBoolean
38         },
39         optional: true
40       }
41     );
42
43     if (!validOptions) {
44       return;
45     }
46
47     const avoidEscape = _.get(secondary, "avoidEscape", true);
48
49     root.walk(node => {
50       switch (node.type) {
51         case "atrule":
52           checkDeclOrAtRule(node, node.params, atRuleParamIndex);
53           break;
54         case "decl":
55           checkDeclOrAtRule(node, node.value, declarationValueIndex);
56           break;
57         case "rule":
58           checkRule(node);
59           break;
60       }
61     });
62
63     function checkRule(rule) {
64       if (!isStandardSyntaxRule(rule)) {
65         return;
66       }
67       if (
68         rule.selector.indexOf("[") === -1 ||
69         rule.selector.indexOf("=") === -1
70       ) {
71         return;
72       }
73
74       const fixPositions = [];
75       parseSelector(rule.selector, result, rule, selectorTree => {
76         selectorTree.walkAttributes(attributeNode => {
77           if (
78             attributeNode.quoted &&
79             attributeNode.value.indexOf(erroneousQuote) !== -1
80           ) {
81             const needsEscape =
82               attributeNode.value.indexOf(correctQuote) !== -1;
83             if (avoidEscape && needsEscape) {
84               // don't consider this an error
85               return;
86             }
87
88             const openIndex =
89               // index of the start of our attribute node in our source
90               attributeNode.sourceIndex +
91               // length of our attribute
92               attributeNode.attribute.length +
93               // length of our operator , ie '='
94               attributeNode.operator.length +
95               // and the length of the quote
96               erroneousQuote.length;
97
98             // we currently don't fix escapes
99             if (context.fix && !needsEscape) {
100               const closeIndex =
101                 // our initial index
102                 openIndex +
103                 // the length of our value
104                 attributeNode.value.length -
105                 // with the length of our quote subtracted
106                 erroneousQuote.length;
107               fixPositions.push(openIndex, closeIndex);
108             } else {
109               report({
110                 message: messages.expected(expectation),
111                 node: rule,
112                 index: openIndex,
113                 result,
114                 ruleName
115               });
116             }
117           }
118         });
119       });
120       fixPositions.forEach(fixIndex => {
121         rule.selector = replaceQuote(rule.selector, fixIndex, correctQuote);
122       });
123     }
124
125     function checkDeclOrAtRule(node, value, getIndex) {
126       const fixPositions = [];
127       // Get out quickly if there are no erroneous quotes
128       if (value.indexOf(erroneousQuote) === -1) {
129         return;
130       } else if (node.type === "atrule" && node.name === "charset") {
131         // allow @charset rules to have double quotes, in spite of the configuration
132         // TODO: @charset should always use double-quotes, see https://github.com/stylelint/stylelint/issues/2788
133         return;
134       }
135
136       valueParser(value).walk(valueNode => {
137         if (valueNode.type === "string" && valueNode.quote === erroneousQuote) {
138           const needsEscape = valueNode.value.indexOf(correctQuote) !== -1;
139           if (avoidEscape && needsEscape) {
140             // don't consider this an error
141             return;
142           }
143           const openIndex = valueNode.sourceIndex;
144
145           // we currently don't fix escapes
146           if (context.fix && !needsEscape) {
147             const closeIndex =
148               openIndex + valueNode.value.length + erroneousQuote.length;
149             fixPositions.push(openIndex, closeIndex);
150           } else {
151             report({
152               message: messages.expected(expectation),
153               node,
154               index: getIndex(node) + openIndex,
155               result,
156               ruleName
157             });
158           }
159         }
160       });
161
162       fixPositions.forEach(fixIndex => {
163         if (node.type === "atrule") {
164           node.params = replaceQuote(node.params, fixIndex, correctQuote);
165         } else {
166           node.value = replaceQuote(node.value, fixIndex, correctQuote);
167         }
168       });
169     }
170   };
171 };
172
173 function replaceQuote(string, index, replace) {
174   return (
175     string.substring(0, index) +
176     replace +
177     string.substring(index + replace.length)
178   );
179 }
180 rule.ruleName = ruleName;
181 rule.messages = messages;
182 module.exports = rule;