UNPKG

2.91 kBJavaScriptView Raw
1'use strict';
2
3const blurFunctionArguments = require('../../utils/blurFunctionArguments');
4const report = require('../../utils/report');
5const ruleMessages = require('../../utils/ruleMessages');
6const styleSearch = require('style-search');
7const validateOptions = require('../../utils/validateOptions');
8
9const ruleName = 'color-hex-length';
10
11const messages = ruleMessages(ruleName, {
12 expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`,
13});
14
15function rule(expectation, _, context) {
16 return (root, result) => {
17 const validOptions = validateOptions(result, ruleName, {
18 actual: expectation,
19 possible: ['short', 'long'],
20 });
21
22 if (!validOptions) {
23 return;
24 }
25
26 root.walkDecls((decl) => {
27 const declString = blurFunctionArguments(decl.toString(), 'url');
28 const fixPositions = [];
29
30 styleSearch({ source: declString, target: '#' }, (match) => {
31 const hexMatch = /^#[0-9A-Za-z]+/.exec(declString.substr(match.startIndex));
32
33 if (!hexMatch) {
34 return;
35 }
36
37 const hexValue = hexMatch[0];
38
39 if (expectation === 'long' && hexValue.length !== 4 && hexValue.length !== 5) {
40 return;
41 }
42
43 if (expectation === 'short' && (hexValue.length < 6 || !canShrink(hexValue))) {
44 return;
45 }
46
47 const variant = expectation === 'long' ? longer : shorter;
48 const expectedHex = variant(hexValue);
49
50 if (context.fix) {
51 fixPositions.unshift({
52 expectedHex,
53 currentHex: hexValue,
54 startIndex: match.startIndex,
55 });
56
57 return;
58 }
59
60 report({
61 message: messages.expected(hexValue, expectedHex),
62 node: decl,
63 index: match.startIndex,
64 result,
65 ruleName,
66 });
67 });
68
69 if (fixPositions.length) {
70 const declProp = decl.prop;
71 const declBetween = decl.raws.between;
72
73 fixPositions.forEach((fixPosition) => {
74 // 1 — it's a # length
75 decl.value = replaceHex(
76 decl.value,
77 fixPosition.currentHex,
78 fixPosition.expectedHex,
79 fixPosition.startIndex - declProp.length - declBetween.length - 1,
80 );
81 });
82 }
83 });
84 };
85}
86
87function canShrink(hex) {
88 hex = hex.toLowerCase();
89
90 return (
91 hex[1] === hex[2] &&
92 hex[3] === hex[4] &&
93 hex[5] === hex[6] &&
94 (hex.length === 7 || (hex.length === 9 && hex[7] === hex[8]))
95 );
96}
97
98function shorter(hex) {
99 let hexVariant = '#';
100
101 for (let i = 1; i < hex.length; i += 2) {
102 hexVariant += hex[i];
103 }
104
105 return hexVariant;
106}
107
108function longer(hex) {
109 let hexVariant = '#';
110
111 for (let i = 1; i < hex.length; i++) {
112 hexVariant += hex[i] + hex[i];
113 }
114
115 return hexVariant;
116}
117
118function replaceHex(input, searchString, replaceString, startIndex) {
119 const offset = startIndex + 1;
120 const stringStart = input.slice(0, offset);
121 const stringEnd = input.slice(offset + searchString.length);
122
123 return stringStart + replaceString + stringEnd;
124}
125
126rule.ruleName = ruleName;
127rule.messages = messages;
128module.exports = rule;