UNPKG

4.37 kBJavaScriptView Raw
1// @ts-nocheck
2
3'use strict';
4
5const _ = require('lodash');
6const declarationValueIndex = require('../../utils/declarationValueIndex');
7const isStandardSyntaxFunction = require('../../utils/isStandardSyntaxFunction');
8const isStandardSyntaxValue = require('../../utils/isStandardSyntaxValue');
9const keywordSets = require('../../reference/keywordSets');
10const namedColorDataHex = require('../../reference/namedColorData');
11const optionsMatches = require('../../utils/optionsMatches');
12const propertySets = require('../../reference/propertySets');
13const report = require('../../utils/report');
14const ruleMessages = require('../../utils/ruleMessages');
15const validateOptions = require('../../utils/validateOptions');
16const valueParser = require('postcss-value-parser');
17
18const generateColorFuncs = require('./generateColorFuncs');
19
20const ruleName = 'color-named';
21
22const messages = ruleMessages(ruleName, {
23 expected: (named, original) => `Expected "${original}" to be "${named}"`,
24 rejected: (named) => `Unexpected named color "${named}"`,
25});
26
27// Todo tested on case insensivity
28const NODE_TYPES = ['word', 'function'];
29
30function rule(expectation, options) {
31 return (root, result) => {
32 const validOptions = validateOptions(
33 result,
34 ruleName,
35 {
36 actual: expectation,
37 possible: ['never', 'always-where-possible'],
38 },
39 {
40 actual: options,
41 possible: {
42 ignoreProperties: [_.isString, _.isRegExp],
43 ignore: ['inside-function'],
44 },
45 optional: true,
46 },
47 );
48
49 if (!validOptions) {
50 return;
51 }
52
53 const namedColors = Object.keys(namedColorDataHex);
54 const namedColorData = {};
55
56 namedColors.forEach((name) => {
57 const hex = namedColorDataHex[name];
58
59 namedColorData[name] = {
60 hex,
61 func: generateColorFuncs(hex[0]),
62 };
63 });
64
65 root.walkDecls((decl) => {
66 if (propertySets.acceptCustomIdents.has(decl.prop)) {
67 return;
68 }
69
70 // Return early if the property is to be ignored
71 if (optionsMatches(options, 'ignoreProperties', decl.prop)) {
72 return;
73 }
74
75 valueParser(decl.value).walk((node) => {
76 const value = node.value;
77 const type = node.type;
78 const sourceIndex = node.sourceIndex;
79
80 if (optionsMatches(options, 'ignore', 'inside-function') && type === 'function') {
81 return false;
82 }
83
84 if (!isStandardSyntaxFunction(node)) {
85 return false;
86 }
87
88 if (!isStandardSyntaxValue(value)) {
89 return;
90 }
91
92 // Return early if neither a word nor a function
93 if (!NODE_TYPES.includes(type)) {
94 return;
95 }
96
97 // Check for named colors for "never" option
98 if (
99 expectation === 'never' &&
100 type === 'word' &&
101 namedColors.includes(value.toLowerCase())
102 ) {
103 complain(messages.rejected(value), decl, declarationValueIndex(decl) + sourceIndex);
104
105 return;
106 }
107
108 // Check "always-where-possible" option ...
109 if (expectation !== 'always-where-possible') {
110 return;
111 }
112
113 // First by checking for alternative color function representations ...
114 if (type === 'function' && keywordSets.colorFunctionNames.has(value.toLowerCase())) {
115 // Remove all spaces to match what's in `representations`
116 const normalizedFunctionString = valueParser.stringify(node).replace(/\s+/g, '');
117 let namedColor;
118
119 for (let i = 0, l = namedColors.length; i < l; i++) {
120 namedColor = namedColors[i];
121
122 if (namedColorData[namedColor].func.includes(normalizedFunctionString.toLowerCase())) {
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.includes(value.toLowerCase())) {
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;