'use strict'; const blurFunctionArguments = require('../../utils/blurFunctionArguments'); const report = require('../../utils/report'); const ruleMessages = require('../../utils/ruleMessages'); const styleSearch = require('style-search'); const validateOptions = require('../../utils/validateOptions'); const ruleName = 'color-hex-length'; const messages = ruleMessages(ruleName, { expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`, }); function rule(expectation, _, context) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, possible: ['short', 'long'], }); if (!validOptions) { return; } root.walkDecls((decl) => { const declString = blurFunctionArguments(decl.toString(), 'url'); const fixPositions = []; styleSearch({ source: declString, target: '#' }, (match) => { const hexMatch = /^#[0-9A-Za-z]+/.exec(declString.substr(match.startIndex)); if (!hexMatch) { return; } const hexValue = hexMatch[0]; if (expectation === 'long' && hexValue.length !== 4 && hexValue.length !== 5) { return; } if (expectation === 'short' && (hexValue.length < 6 || !canShrink(hexValue))) { return; } const variant = expectation === 'long' ? longer : shorter; const expectedHex = variant(hexValue); if (context.fix) { fixPositions.unshift({ expectedHex, currentHex: hexValue, startIndex: match.startIndex, }); return; } report({ message: messages.expected(hexValue, expectedHex), node: decl, index: match.startIndex, result, ruleName, }); }); if (fixPositions.length) { const declProp = decl.prop; const declBetween = decl.raws.between; fixPositions.forEach((fixPosition) => { // 1 — it's a # length decl.value = replaceHex( decl.value, fixPosition.currentHex, fixPosition.expectedHex, fixPosition.startIndex - declProp.length - declBetween.length - 1, ); }); } }); }; } function canShrink(hex) { hex = hex.toLowerCase(); return ( hex[1] === hex[2] && hex[3] === hex[4] && hex[5] === hex[6] && (hex.length === 7 || (hex.length === 9 && hex[7] === hex[8])) ); } function shorter(hex) { let hexVariant = '#'; for (let i = 1; i < hex.length; i += 2) { hexVariant += hex[i]; } return hexVariant; } function longer(hex) { let hexVariant = '#'; for (let i = 1; i < hex.length; i++) { hexVariant += hex[i] + hex[i]; } return hexVariant; } function replaceHex(input, searchString, replaceString, startIndex) { const offset = startIndex + 1; const stringStart = input.slice(0, offset); const stringEnd = input.slice(offset + searchString.length); return stringStart + replaceString + stringEnd; } rule.ruleName = ruleName; rule.messages = messages; module.exports = rule;