UNPKG

4.4 kBJavaScriptView Raw
1'use strict';
2
3const _ = require('lodash');
4const declarationValueIndex = require('../../utils/declarationValueIndex');
5const isStandardSyntaxFunction = require('../../utils/isStandardSyntaxFunction');
6const isStandardSyntaxValue = require('../../utils/isStandardSyntaxValue');
7const keywordSets = require('../../reference/keywordSets');
8const namedColorDataHex = require('../../reference/namedColorData');
9const optionsMatches = require('../../utils/optionsMatches');
10const propertySets = require('../../reference/propertySets');
11const report = require('../../utils/report');
12const ruleMessages = require('../../utils/ruleMessages');
13const validateOptions = require('../../utils/validateOptions');
14const valueParser = require('postcss-value-parser');
15
16const generateColorFuncs = require('./generateColorFuncs');
17
18const ruleName = 'color-named';
19
20const messages = ruleMessages(ruleName, {
21 expected: (named, original) => `Expected "${original}" to be "${named}"`,
22 rejected: (named) => `Unexpected named color "${named}"`,
23});
24
25// Todo tested on case insensivity
26const NODE_TYPES = ['word', 'function'];
27
28const rule = function(expectation, options) {
29 return (root, result) => {
30 const validOptions = validateOptions(
31 result,
32 ruleName,
33 {
34 actual: expectation,
35 possible: ['never', 'always-where-possible'],
36 },
37 {
38 actual: options,
39 possible: {
40 ignoreProperties: [_.isString, _.isRegExp],
41 ignore: ['inside-function'],
42 },
43 optional: true,
44 },
45 );
46
47 if (!validOptions) {
48 return;
49 }
50
51 const namedColors = Object.keys(namedColorDataHex);
52 const namedColorData = {};
53
54 namedColors.forEach((name) => {
55 const hex = namedColorDataHex[name];
56
57 namedColorData[name] = {
58 hex,
59 func: generateColorFuncs(hex[0]),
60 };
61 });
62
63 root.walkDecls((decl) => {
64 if (propertySets.acceptCustomIdents.has(decl.prop)) {
65 return;
66 }
67
68 // Return early if the property is to be ignored
69 if (optionsMatches(options, 'ignoreProperties', decl.prop)) {
70 return;
71 }
72
73 valueParser(decl.value).walk((node) => {
74 const value = node.value;
75 const type = node.type;
76 const sourceIndex = node.sourceIndex;
77
78 if (optionsMatches(options, 'ignore', 'inside-function') && type === 'function') {
79 return false;
80 }
81
82 if (!isStandardSyntaxFunction(node)) {
83 return false;
84 }
85
86 if (!isStandardSyntaxValue(value)) {
87 return;
88 }
89
90 // Return early if neither a word nor a function
91 if (NODE_TYPES.indexOf(type) === -1) {
92 return;
93 }
94
95 // Check for named colors for "never" option
96 if (
97 expectation === 'never' &&
98 type === 'word' &&
99 namedColors.indexOf(value.toLowerCase()) !== -1
100 ) {
101 complain(messages.rejected(value), decl, declarationValueIndex(decl) + sourceIndex);
102
103 return;
104 }
105
106 // Check "always-where-possible" option ...
107 if (expectation !== 'always-where-possible') {
108 return;
109 }
110
111 // First by checking for alternative color function representations ...
112 if (type === 'function' && keywordSets.colorFunctionNames.has(value.toLowerCase())) {
113 // Remove all spaces to match what's in `representations`
114 const normalizedFunctionString = valueParser.stringify(node).replace(/\s+/g, '');
115 let namedColor;
116
117 for (let i = 0, l = namedColors.length; i < l; i++) {
118 namedColor = namedColors[i];
119
120 if (
121 namedColorData[namedColor].func.indexOf(normalizedFunctionString.toLowerCase()) !== -1
122 ) {
123 complain(
124 messages.expected(namedColor, normalizedFunctionString),
125 decl,
126 declarationValueIndex(decl) + sourceIndex,
127 );
128
129 return; // Exit as soon as a problem is found
130 }
131 }
132
133 return;
134 }
135
136 // Then by checking for alternative hex representations
137 let namedColor;
138
139 for (let i = 0, l = namedColors.length; i < l; i++) {
140 namedColor = namedColors[i];
141
142 if (namedColorData[namedColor].hex.indexOf(value.toLowerCase()) !== -1) {
143 complain(
144 messages.expected(namedColor, value),
145 decl,
146 declarationValueIndex(decl) + sourceIndex,
147 );
148
149 return; // Exit as soon as a problem is found
150 }
151 }
152 });
153 });
154
155 function complain(message, node, index) {
156 report({
157 result,
158 ruleName,
159 message,
160 node,
161 index,
162 });
163 }
164 };
165};
166
167rule.ruleName = ruleName;
168rule.messages = messages;
169module.exports = rule;