3 const _ = require("lodash");
4 const declarationValueIndex = require("../../utils/declarationValueIndex");
5 const isStandardSyntaxFunction = require("../../utils/isStandardSyntaxFunction");
6 const isStandardSyntaxValue = require("../../utils/isStandardSyntaxValue");
7 const keywordSets = require("../../reference/keywordSets");
8 const namedColorDataHex = require("../../reference/namedColorData");
9 const optionsMatches = require("../../utils/optionsMatches");
10 const propertySets = require("../../reference/propertySets");
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 generateColorFuncs = require("./generateColorFuncs");
18 const ruleName = "color-named";
20 const messages = ruleMessages(ruleName, {
21 expected: (named, original) => `Expected "${original}" to be "${named}"`,
22 rejected: named => `Unexpected named color "${named}"`
25 // Todo tested on case insensivity
26 const NODE_TYPES = ["word", "function"];
28 const rule = function(expectation, options) {
29 return (root, result) => {
30 const validOptions = validateOptions(
35 possible: ["never", "always-where-possible"]
40 ignoreProperties: [_.isString],
41 ignore: ["inside-function"]
51 const namedColors = Object.keys(namedColorDataHex);
52 const namedColorData = {};
53 namedColors.forEach(name => {
54 const hex = namedColorDataHex[name];
55 namedColorData[name] = {
57 func: generateColorFuncs(hex[0])
61 root.walkDecls(decl => {
62 if (propertySets.acceptCustomIdents.has(decl.prop)) {
66 // Return early if the property is to be ignored
67 if (optionsMatches(options, "ignoreProperties", decl.prop)) {
71 valueParser(decl.value).walk(node => {
72 const value = node.value,
74 sourceIndex = node.sourceIndex;
77 optionsMatches(options, "ignore", "inside-function") &&
83 if (!isStandardSyntaxFunction(node)) {
87 if (!isStandardSyntaxValue(value)) {
90 // Return early if neither a word nor a function
91 if (NODE_TYPES.indexOf(type) === -1) {
95 // Check for named colors for "never" option
97 expectation === "never" &&
99 namedColors.indexOf(value.toLowerCase()) !== -1
102 messages.rejected(value),
104 declarationValueIndex(decl) + sourceIndex
109 // Check "always-where-possible" option ...
110 if (expectation !== "always-where-possible") {
114 // First by checking for alternative color function representations ...
116 type === "function" &&
117 keywordSets.colorFunctionNames.has(value.toLowerCase())
119 // Remove all spaces to match what's in `representations`
120 const normalizedFunctionString = valueParser
122 .replace(/\s+/g, "");
124 for (let i = 0, l = namedColors.length; i < l; i++) {
125 namedColor = namedColors[i];
127 namedColorData[namedColor].func.indexOf(
128 normalizedFunctionString.toLowerCase()
132 messages.expected(namedColor, normalizedFunctionString),
134 declarationValueIndex(decl) + sourceIndex
136 return; // Exit as soon as a problem is found
142 // Then by checking for alternative hex representations
144 for (let i = 0, l = namedColors.length; i < l; i++) {
145 namedColor = namedColors[i];
147 namedColorData[namedColor].hex.indexOf(value.toLowerCase()) !== -1
150 messages.expected(namedColor, value),
152 declarationValueIndex(decl) + sourceIndex
154 return; // Exit as soon as a problem is found
160 function complain(message, node, index) {
172 rule.ruleName = ruleName;
173 rule.messages = messages;
174 module.exports = rule;