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");
16 const ruleName = "value-keyword-case";
18 const messages = ruleMessages(ruleName, {
19 expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`
22 // Operators are interpreted as "words" by the value parser, so we want to make sure to ignore them.
23 const ignoredCharacters = new Set(["+", "-", "/", "*", "%"]);
25 const mapLowercaseKeywordsToCamelCase = new Map();
26 keywordSets.camelCaseKeywords.forEach(func => {
27 mapLowercaseKeywordsToCamelCase.set(func.toLowerCase(), func);
30 const rule = function(expectation, options) {
31 return (root, result) => {
32 const validOptions = validateOptions(
37 possible: ["lower", "upper"]
42 ignoreProperties: [_.isString],
43 ignoreKeywords: [_.isString]
52 root.walkDecls(decl => {
53 const prop = decl.prop,
56 valueParser(value).walk(node => {
57 const valueLowerCase = node.value.toLowerCase();
59 // Ignore system colors
60 if (keywordSets.systemColors.has(valueLowerCase)) {
64 // Ignore keywords within `url` and `var` function
66 node.type === "function" &&
67 (valueLowerCase === "url" ||
68 valueLowerCase === "var" ||
69 valueLowerCase === "counter" ||
70 valueLowerCase === "counters" ||
71 valueLowerCase === "attr")
76 const keyword = node.value;
78 // Ignore css variables, and hex values, and math operators, and sass interpolation
80 node.type !== "word" ||
81 !isStandardSyntaxValue(node.value) ||
82 value.indexOf("#") !== -1 ||
83 ignoredCharacters.has(keyword) ||
84 getUnitFromValueNode(node)
90 prop === "animation" &&
91 !keywordSets.animationShorthandKeywords.has(valueLowerCase) &&
92 !keywordSets.animationNameKeywords.has(valueLowerCase)
97 prop === "animation-name" &&
98 !keywordSets.animationNameKeywords.has(valueLowerCase)
104 !keywordSets.fontShorthandKeywords.has(valueLowerCase) &&
105 !keywordSets.fontFamilyKeywords.has(valueLowerCase)
110 prop === "font-family" &&
111 !keywordSets.fontFamilyKeywords.has(valueLowerCase)
116 prop === "counter-increment" &&
117 isCounterIncrementCustomIdentValue(valueLowerCase)
122 prop === "counter-reset" &&
123 isCounterResetCustomIdentValue(valueLowerCase)
128 prop === "grid-row" &&
129 !keywordSets.gridRowKeywords.has(valueLowerCase)
134 prop === "grid-column" &&
135 !keywordSets.gridColumnKeywords.has(valueLowerCase)
140 prop === "grid-area" &&
141 !keywordSets.gridAreaKeywords.has(valueLowerCase)
146 prop === "list-style" &&
147 !keywordSets.listStyleShorthandKeywords.has(valueLowerCase) &&
148 !keywordSets.listStyleTypeKeywords.has(valueLowerCase)
153 prop === "list-style-type" &&
154 !keywordSets.listStyleTypeKeywords.has(valueLowerCase)
159 const ignoreKeywords = (options && options.ignoreKeywords) || [];
160 const ignoreProperties = (options && options.ignoreProperties) || [];
163 ignoreKeywords.length > 0 &&
164 matchesStringOrRegExp(keyword, ignoreKeywords)
170 ignoreProperties.length > 0 &&
171 matchesStringOrRegExp(prop, ignoreProperties)
176 const keywordLowerCase = keyword.toLocaleLowerCase();
177 let expectedKeyword = null;
180 expectation === "lower" &&
181 mapLowercaseKeywordsToCamelCase.has(keywordLowerCase)
183 expectedKeyword = mapLowercaseKeywordsToCamelCase.get(
186 } else if (expectation === "lower") {
187 expectedKeyword = keyword.toLowerCase();
189 expectedKeyword = keyword.toUpperCase();
192 if (keyword === expectedKeyword) {
197 message: messages.expected(keyword, expectedKeyword),
199 index: declarationValueIndex(decl) + node.sourceIndex,
208 rule.ruleName = ruleName;
209 rule.messages = messages;
210 module.exports = rule;