3 const isStandardSyntaxDeclaration = require("../../utils/isStandardSyntaxDeclaration");
4 const isStandardSyntaxProperty = require("../../utils/isStandardSyntaxProperty");
5 const postcss = require("postcss");
6 const report = require("../../utils/report");
7 const ruleMessages = require("../../utils/ruleMessages");
8 const validateOptions = require("../../utils/validateOptions");
9 const valueParser = require("postcss-value-parser");
11 const ruleName = "shorthand-property-no-redundant-values";
13 const messages = ruleMessages(ruleName, {
14 rejected: (unexpected, expected) =>
15 `Unexpected longhand value '${unexpected}' instead of '${expected}'`
18 // Only these shorthand properties can have values, that can be written in shorter form (remove repetitions)
19 const shorthandableProperties = new Set([
29 const ignoredCharacters = [
42 function isIgnoredCharacters(value) {
43 return ignoredCharacters.some(char => value.indexOf(char) !== -1);
46 function canCondense(top, right) {
48 arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
50 arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
52 const lowerTop = top.toLowerCase();
53 const lowerRight = right.toLowerCase();
54 const lowerBottom = bottom && bottom.toLowerCase();
55 const lowerLeft = left && left.toLowerCase();
57 if (canCondenseToOneValue(lowerTop, lowerRight, lowerBottom, lowerLeft)) {
60 canCondenseToTwoValues(lowerTop, lowerRight, lowerBottom, lowerLeft)
64 canCondenseToThreeValues(lowerTop, lowerRight, lowerBottom, lowerLeft)
66 return [top, right, bottom];
68 return [top, right, bottom, left];
72 function canCondenseToOneValue(top, right, bottom, left) {
77 return (top === bottom && (bottom === left || !left)) || (!bottom && !left);
80 function canCondenseToTwoValues(top, right, bottom, left) {
82 (top === bottom && right === left) ||
83 (top === bottom && !left && top !== right)
87 function canCondenseToThreeValues(top, right, bottom, left) {
88 return right === left;
91 const rule = function(actual, secondary, context) {
92 return (root, result) => {
93 const validOptions = validateOptions(result, ruleName, { actual });
98 root.walkDecls(decl => {
100 !isStandardSyntaxDeclaration(decl) ||
101 !isStandardSyntaxProperty(decl.prop)
106 const prop = decl.prop,
109 const normalizedProp = postcss.vendor.unprefixed(prop.toLowerCase());
111 // Ignore not shorthandable properties, and math operations
113 isIgnoredCharacters(value) ||
114 !shorthandableProperties.has(normalizedProp)
119 const valuesToShorthand = [];
121 valueParser(value).walk(valueNode => {
122 if (valueNode.type !== "word") {
126 valuesToShorthand.push(valueParser.stringify(valueNode));
129 if (valuesToShorthand.length <= 1 || valuesToShorthand.length > 4) {
133 const shortestForm = canCondense.apply(undefined, valuesToShorthand);
134 const shortestFormString = shortestForm
139 const valuesFormString = valuesToShorthand.join(" ");
141 if (shortestFormString.toLowerCase() === valuesFormString.toLowerCase()) {
146 decl.value = decl.value.replace(value, shortestFormString);
149 message: messages.rejected(value, shortestFormString),
159 rule.ruleName = ruleName;
160 rule.messages = messages;
161 module.exports = rule;