.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / stylelint / lib / rules / value-keyword-case / index.js
1 "use strict";
2
3 const _ = require("lodash");
4 const declarationValueIndex = require("../../utils/declarationValueIndex");
5 const getUnitFromValueNode = require("../../utils/getUnitFromValueNode");
6 const isCounterIncrementCustomIdentValue = require("../../utils/isCounterIncrementCustomIdentValue");
7 const isCounterResetCustomIdentValue = require("../../utils/isCounterResetCustomIdentValue");
8 const isStandardSyntaxValue = require("../../utils/isStandardSyntaxValue");
9 const keywordSets = require("../../reference/keywordSets");
10 const matchesStringOrRegExp = require("../../utils/matchesStringOrRegExp");
11 const report = require("../../utils/report");
12 const ruleMessages = require("../../utils/ruleMessages");
13 const validateOptions = require("../../utils/validateOptions");
14 const valueParser = require("postcss-value-parser");
15
16 const ruleName = "value-keyword-case";
17
18 const messages = ruleMessages(ruleName, {
19   expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`
20 });
21
22 // Operators are interpreted as "words" by the value parser, so we want to make sure to ignore them.
23 const ignoredCharacters = new Set(["+", "-", "/", "*", "%"]);
24
25 const mapLowercaseKeywordsToCamelCase = new Map();
26 keywordSets.camelCaseKeywords.forEach(func => {
27   mapLowercaseKeywordsToCamelCase.set(func.toLowerCase(), func);
28 });
29
30 const rule = function(expectation, options) {
31   return (root, result) => {
32     const validOptions = validateOptions(
33       result,
34       ruleName,
35       {
36         actual: expectation,
37         possible: ["lower", "upper"]
38       },
39       {
40         actual: options,
41         possible: {
42           ignoreProperties: [_.isString],
43           ignoreKeywords: [_.isString]
44         },
45         optional: true
46       }
47     );
48     if (!validOptions) {
49       return;
50     }
51
52     root.walkDecls(decl => {
53       const prop = decl.prop,
54         value = decl.value;
55
56       valueParser(value).walk(node => {
57         const valueLowerCase = node.value.toLowerCase();
58
59         // Ignore system colors
60         if (keywordSets.systemColors.has(valueLowerCase)) {
61           return;
62         }
63
64         // Ignore keywords within `url` and `var` function
65         if (
66           node.type === "function" &&
67           (valueLowerCase === "url" ||
68             valueLowerCase === "var" ||
69             valueLowerCase === "counter" ||
70             valueLowerCase === "counters" ||
71             valueLowerCase === "attr")
72         ) {
73           return false;
74         }
75
76         const keyword = node.value;
77
78         // Ignore css variables, and hex values, and math operators, and sass interpolation
79         if (
80           node.type !== "word" ||
81           !isStandardSyntaxValue(node.value) ||
82           value.indexOf("#") !== -1 ||
83           ignoredCharacters.has(keyword) ||
84           getUnitFromValueNode(node)
85         ) {
86           return;
87         }
88
89         if (
90           prop === "animation" &&
91           !keywordSets.animationShorthandKeywords.has(valueLowerCase) &&
92           !keywordSets.animationNameKeywords.has(valueLowerCase)
93         ) {
94           return;
95         }
96         if (
97           prop === "animation-name" &&
98           !keywordSets.animationNameKeywords.has(valueLowerCase)
99         ) {
100           return;
101         }
102         if (
103           prop === "font" &&
104           !keywordSets.fontShorthandKeywords.has(valueLowerCase) &&
105           !keywordSets.fontFamilyKeywords.has(valueLowerCase)
106         ) {
107           return;
108         }
109         if (
110           prop === "font-family" &&
111           !keywordSets.fontFamilyKeywords.has(valueLowerCase)
112         ) {
113           return;
114         }
115         if (
116           prop === "counter-increment" &&
117           isCounterIncrementCustomIdentValue(valueLowerCase)
118         ) {
119           return;
120         }
121         if (
122           prop === "counter-reset" &&
123           isCounterResetCustomIdentValue(valueLowerCase)
124         ) {
125           return;
126         }
127         if (
128           prop === "grid-row" &&
129           !keywordSets.gridRowKeywords.has(valueLowerCase)
130         ) {
131           return;
132         }
133         if (
134           prop === "grid-column" &&
135           !keywordSets.gridColumnKeywords.has(valueLowerCase)
136         ) {
137           return;
138         }
139         if (
140           prop === "grid-area" &&
141           !keywordSets.gridAreaKeywords.has(valueLowerCase)
142         ) {
143           return;
144         }
145         if (
146           prop === "list-style" &&
147           !keywordSets.listStyleShorthandKeywords.has(valueLowerCase) &&
148           !keywordSets.listStyleTypeKeywords.has(valueLowerCase)
149         ) {
150           return;
151         }
152         if (
153           prop === "list-style-type" &&
154           !keywordSets.listStyleTypeKeywords.has(valueLowerCase)
155         ) {
156           return;
157         }
158
159         const ignoreKeywords = (options && options.ignoreKeywords) || [];
160         const ignoreProperties = (options && options.ignoreProperties) || [];
161
162         if (
163           ignoreKeywords.length > 0 &&
164           matchesStringOrRegExp(keyword, ignoreKeywords)
165         ) {
166           return;
167         }
168
169         if (
170           ignoreProperties.length > 0 &&
171           matchesStringOrRegExp(prop, ignoreProperties)
172         ) {
173           return;
174         }
175
176         const keywordLowerCase = keyword.toLocaleLowerCase();
177         let expectedKeyword = null;
178
179         if (
180           expectation === "lower" &&
181           mapLowercaseKeywordsToCamelCase.has(keywordLowerCase)
182         ) {
183           expectedKeyword = mapLowercaseKeywordsToCamelCase.get(
184             keywordLowerCase
185           );
186         } else if (expectation === "lower") {
187           expectedKeyword = keyword.toLowerCase();
188         } else {
189           expectedKeyword = keyword.toUpperCase();
190         }
191
192         if (keyword === expectedKeyword) {
193           return;
194         }
195
196         report({
197           message: messages.expected(keyword, expectedKeyword),
198           node: decl,
199           index: declarationValueIndex(decl) + node.sourceIndex,
200           result,
201           ruleName
202         });
203       });
204     });
205   };
206 };
207
208 rule.ruleName = ruleName;
209 rule.messages = messages;
210 module.exports = rule;