| // @ts-nocheck |
| |
| 'use strict'; |
| |
| const isStandardSyntaxDeclaration = require('../../utils/isStandardSyntaxDeclaration'); |
| const isStandardSyntaxProperty = require('../../utils/isStandardSyntaxProperty'); |
| const report = require('../../utils/report'); |
| const ruleMessages = require('../../utils/ruleMessages'); |
| const validateOptions = require('../../utils/validateOptions'); |
| const valueParser = require('postcss-value-parser'); |
| const vendor = require('../../utils/vendor'); |
| |
| const ruleName = 'shorthand-property-no-redundant-values'; |
| |
| const messages = ruleMessages(ruleName, { |
| rejected: (unexpected, expected) => |
| `Unexpected longhand value '${unexpected}' instead of '${expected}'`, |
| }); |
| |
| const propertiesWithShorthandNotation = new Set([ |
| 'margin', |
| 'padding', |
| 'border-color', |
| 'border-radius', |
| 'border-style', |
| 'border-width', |
| 'grid-gap', |
| ]); |
| |
| const ignoredCharacters = ['+', '*', '/', '(', ')', '$', '@', '--', 'var(']; |
| |
| function hasIgnoredCharacters(value) { |
| return ignoredCharacters.some((char) => value.includes(char)); |
| } |
| |
| function isShorthandProperty(property) { |
| return propertiesWithShorthandNotation.has(property); |
| } |
| |
| function canCondense(top, right, bottom, left) { |
| 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]; |
| } |
| |
| if (canCondenseToTwoValues(lowerTop, lowerRight, lowerBottom, lowerLeft)) { |
| return [top, right]; |
| } |
| |
| if (canCondenseToThreeValues(lowerTop, lowerRight, lowerBottom, lowerLeft)) { |
| return [top, right, bottom]; |
| } |
| |
| 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; |
| } |
| |
| function rule(actual, secondary, context) { |
| return (root, result) => { |
| const validOptions = validateOptions(result, ruleName, { actual }); |
| |
| if (!validOptions) { |
| return; |
| } |
| |
| root.walkDecls((decl) => { |
| if (!isStandardSyntaxDeclaration(decl) || !isStandardSyntaxProperty(decl.prop)) { |
| return; |
| } |
| |
| const prop = decl.prop; |
| const value = decl.value; |
| |
| const normalizedProp = vendor.unprefixed(prop.toLowerCase()); |
| |
| if (hasIgnoredCharacters(value) || !isShorthandProperty(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(Boolean).join(' '); |
| const valuesFormString = valuesToShorthand.join(' '); |
| |
| if (shortestFormString.toLowerCase() === valuesFormString.toLowerCase()) { |
| return; |
| } |
| |
| if (context.fix) { |
| decl.value = decl.value.replace(value, shortestFormString); |
| } else { |
| report({ |
| message: messages.rejected(value, shortestFormString), |
| node: decl, |
| result, |
| ruleName, |
| }); |
| } |
| }); |
| }; |
| } |
| |
| rule.ruleName = ruleName; |
| rule.messages = messages; |
| module.exports = rule; |