UNPKG

stylelint

Version:
120 lines (96 loc) 3.43 kB
import valueParser from "postcss-value-parser" import { vendor } from "postcss" import { isStandardSyntaxDeclaration, isStandardSyntaxProperty, report, ruleMessages, validateOptions, } from "../../utils" import shorthandData from "../../reference/shorthandData" export const ruleName = "shorthand-property-no-redundant-values" export const messages = ruleMessages(ruleName, { rejected: (unexpected, expected) => `Unexpected longhand value '${unexpected}' instead of '${expected}'`, }) const shorthandableProperties = new Set(Object.keys(shorthandData)) const ignoredCharacters = [ "+", "-", "*", "/", "(", ")", "$", "@", "--", "var(", ] const ignoredShorthandProperties = new Set([ "background", "font", "border", "border-top", "border-bottom", "border-left", "border-right", "list-style", "transition", ]) function isIgnoredCharacters(value) { return ignoredCharacters.some(char => value.indexOf(char) !== -1) } function canCondense(top, right, bottom = null, left = null) { const lowerTop = top.toLowerCase() const lowerRight = right.toLowerCase() const lowerBottom = bottom && bottom.toLowerCase() const lowerLeft = left && left.toLowerCase() if (canCondenseToOneValue(lowerTop, lowerRight, lowerBottom, lowerLeft)) { return [top] } else if (canCondenseToTwoValues(lowerTop, lowerRight, lowerBottom, lowerLeft)) { return [ top, right ] } else if (canCondenseToThreeValues(lowerTop, lowerRight, lowerBottom, lowerLeft)) { return [ top, right, bottom ] } else { return [ top, right, bottom, left ] } } function canCondenseToOneValue(top, right, bottom, left) { if (top !== right) { return false } return top === bottom && (bottom === left || !left) || !bottom && !left } function canCondenseToTwoValues(top, right, bottom, left) { return top === bottom && right === left || top === bottom && !left && top !== right } function canCondenseToThreeValues(top, right, bottom, left) { return right === left } export default function (actual) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual }) if (!validOptions) { return } root.walkDecls(decl => { if ( !isStandardSyntaxDeclaration(decl) || !isStandardSyntaxProperty(decl.prop) ) { return } const { prop, value } = decl const normalizedProp = vendor.unprefixed(prop.toLowerCase()) // Ignore not shorthandable properties, and math operations if ( isIgnoredCharacters(value) || !shorthandableProperties.has(normalizedProp) || ignoredShorthandProperties.has(normalizedProp) ) { return } const valuesToShorthand = [] valueParser(value).walk((valueNode) => { if (valueNode.type !== "word") { return } valuesToShorthand.push(valueParser.stringify(valueNode)) }) if (valuesToShorthand.length <= 1 || valuesToShorthand.length > 4 ) { return } const shortestForm = canCondense(...valuesToShorthand) const shortestFormString = shortestForm.filter((value) => { return value }).join(" ") const valuesFormString = valuesToShorthand.join(" ") if (shortestFormString.toLowerCase() === valuesFormString.toLowerCase()) { return } report({ message: messages.rejected(value, shortestFormString), node: decl, result, ruleName, }) }) } }