| "use strict"; |
| |
| const _ = require("lodash"); |
| const declarationValueIndex = require("../../utils/declarationValueIndex"); |
| const isStandardSyntaxFunction = require("../../utils/isStandardSyntaxFunction"); |
| const isStandardSyntaxValue = require("../../utils/isStandardSyntaxValue"); |
| const keywordSets = require("../../reference/keywordSets"); |
| const namedColorDataHex = require("../../reference/namedColorData"); |
| const optionsMatches = require("../../utils/optionsMatches"); |
| const propertySets = require("../../reference/propertySets"); |
| const report = require("../../utils/report"); |
| const ruleMessages = require("../../utils/ruleMessages"); |
| const validateOptions = require("../../utils/validateOptions"); |
| const valueParser = require("postcss-value-parser"); |
| |
| const generateColorFuncs = require("./generateColorFuncs"); |
| |
| const ruleName = "color-named"; |
| |
| const messages = ruleMessages(ruleName, { |
| expected: (named, original) => `Expected "${original}" to be "${named}"`, |
| rejected: named => `Unexpected named color "${named}"` |
| }); |
| |
| // Todo tested on case insensivity |
| const NODE_TYPES = ["word", "function"]; |
| |
| const rule = function(expectation, options) { |
| return (root, result) => { |
| const validOptions = validateOptions( |
| result, |
| ruleName, |
| { |
| actual: expectation, |
| possible: ["never", "always-where-possible"] |
| }, |
| { |
| actual: options, |
| possible: { |
| ignoreProperties: [_.isString, _.isRegExp], |
| ignore: ["inside-function"] |
| }, |
| optional: true |
| } |
| ); |
| |
| if (!validOptions) { |
| return; |
| } |
| |
| const namedColors = Object.keys(namedColorDataHex); |
| const namedColorData = {}; |
| |
| namedColors.forEach(name => { |
| const hex = namedColorDataHex[name]; |
| |
| namedColorData[name] = { |
| hex, |
| func: generateColorFuncs(hex[0]) |
| }; |
| }); |
| |
| root.walkDecls(decl => { |
| if (propertySets.acceptCustomIdents.has(decl.prop)) { |
| return; |
| } |
| |
| // Return early if the property is to be ignored |
| if (optionsMatches(options, "ignoreProperties", decl.prop)) { |
| return; |
| } |
| |
| valueParser(decl.value).walk(node => { |
| const value = node.value; |
| const type = node.type; |
| const sourceIndex = node.sourceIndex; |
| |
| if ( |
| optionsMatches(options, "ignore", "inside-function") && |
| type === "function" |
| ) { |
| return false; |
| } |
| |
| if (!isStandardSyntaxFunction(node)) { |
| return false; |
| } |
| |
| if (!isStandardSyntaxValue(value)) { |
| return; |
| } |
| |
| // Return early if neither a word nor a function |
| if (NODE_TYPES.indexOf(type) === -1) { |
| return; |
| } |
| |
| // Check for named colors for "never" option |
| if ( |
| expectation === "never" && |
| type === "word" && |
| namedColors.indexOf(value.toLowerCase()) !== -1 |
| ) { |
| complain( |
| messages.rejected(value), |
| decl, |
| declarationValueIndex(decl) + sourceIndex |
| ); |
| |
| return; |
| } |
| |
| // Check "always-where-possible" option ... |
| if (expectation !== "always-where-possible") { |
| return; |
| } |
| |
| // First by checking for alternative color function representations ... |
| if ( |
| type === "function" && |
| keywordSets.colorFunctionNames.has(value.toLowerCase()) |
| ) { |
| // Remove all spaces to match what's in `representations` |
| const normalizedFunctionString = valueParser |
| .stringify(node) |
| .replace(/\s+/g, ""); |
| let namedColor; |
| |
| for (let i = 0, l = namedColors.length; i < l; i++) { |
| namedColor = namedColors[i]; |
| |
| if ( |
| namedColorData[namedColor].func.indexOf( |
| normalizedFunctionString.toLowerCase() |
| ) !== -1 |
| ) { |
| complain( |
| messages.expected(namedColor, normalizedFunctionString), |
| decl, |
| declarationValueIndex(decl) + sourceIndex |
| ); |
| |
| return; // Exit as soon as a problem is found |
| } |
| } |
| |
| return; |
| } |
| |
| // Then by checking for alternative hex representations |
| let namedColor; |
| |
| for (let i = 0, l = namedColors.length; i < l; i++) { |
| namedColor = namedColors[i]; |
| |
| if ( |
| namedColorData[namedColor].hex.indexOf(value.toLowerCase()) !== -1 |
| ) { |
| complain( |
| messages.expected(namedColor, value), |
| decl, |
| declarationValueIndex(decl) + sourceIndex |
| ); |
| |
| return; // Exit as soon as a problem is found |
| } |
| } |
| }); |
| }); |
| |
| function complain(message, node, index) { |
| report({ |
| result, |
| ruleName, |
| message, |
| node, |
| index |
| }); |
| } |
| }; |
| }; |
| |
| rule.ruleName = ruleName; |
| rule.messages = messages; |
| module.exports = rule; |