.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / stylelint / lib / rules / shorthand-property-no-redundant-values / index.js
1 "use strict";
2
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");
10
11 const ruleName = "shorthand-property-no-redundant-values";
12
13 const messages = ruleMessages(ruleName, {
14   rejected: (unexpected, expected) =>
15     `Unexpected longhand value '${unexpected}' instead of '${expected}'`
16 });
17
18 // Only these shorthand properties can have values, that can be written in shorter form (remove repetitions)
19 const shorthandableProperties = new Set([
20   "margin",
21   "padding",
22   "border-color",
23   "border-radius",
24   "border-style",
25   "border-width",
26   "grid-gap"
27 ]);
28
29 const ignoredCharacters = [
30   "+",
31   "-",
32   "*",
33   "/",
34   "(",
35   ")",
36   "$",
37   "@",
38   "--",
39   "var("
40 ];
41
42 function isIgnoredCharacters(value) {
43   return ignoredCharacters.some(char => value.indexOf(char) !== -1);
44 }
45
46 function canCondense(top, right) {
47   const bottom =
48     arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
49   const left =
50     arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
51
52   const lowerTop = top.toLowerCase();
53   const lowerRight = right.toLowerCase();
54   const lowerBottom = bottom && bottom.toLowerCase();
55   const lowerLeft = left && left.toLowerCase();
56
57   if (canCondenseToOneValue(lowerTop, lowerRight, lowerBottom, lowerLeft)) {
58     return [top];
59   } else if (
60     canCondenseToTwoValues(lowerTop, lowerRight, lowerBottom, lowerLeft)
61   ) {
62     return [top, right];
63   } else if (
64     canCondenseToThreeValues(lowerTop, lowerRight, lowerBottom, lowerLeft)
65   ) {
66     return [top, right, bottom];
67   } else {
68     return [top, right, bottom, left];
69   }
70 }
71
72 function canCondenseToOneValue(top, right, bottom, left) {
73   if (top !== right) {
74     return false;
75   }
76
77   return (top === bottom && (bottom === left || !left)) || (!bottom && !left);
78 }
79
80 function canCondenseToTwoValues(top, right, bottom, left) {
81   return (
82     (top === bottom && right === left) ||
83     (top === bottom && !left && top !== right)
84   );
85 }
86
87 function canCondenseToThreeValues(top, right, bottom, left) {
88   return right === left;
89 }
90
91 const rule = function(actual, secondary, context) {
92   return (root, result) => {
93     const validOptions = validateOptions(result, ruleName, { actual });
94     if (!validOptions) {
95       return;
96     }
97
98     root.walkDecls(decl => {
99       if (
100         !isStandardSyntaxDeclaration(decl) ||
101         !isStandardSyntaxProperty(decl.prop)
102       ) {
103         return;
104       }
105
106       const prop = decl.prop,
107         value = decl.value;
108
109       const normalizedProp = postcss.vendor.unprefixed(prop.toLowerCase());
110
111       // Ignore not shorthandable properties, and math operations
112       if (
113         isIgnoredCharacters(value) ||
114         !shorthandableProperties.has(normalizedProp)
115       ) {
116         return;
117       }
118
119       const valuesToShorthand = [];
120
121       valueParser(value).walk(valueNode => {
122         if (valueNode.type !== "word") {
123           return;
124         }
125
126         valuesToShorthand.push(valueParser.stringify(valueNode));
127       });
128
129       if (valuesToShorthand.length <= 1 || valuesToShorthand.length > 4) {
130         return;
131       }
132
133       const shortestForm = canCondense.apply(undefined, valuesToShorthand);
134       const shortestFormString = shortestForm
135         .filter(value => {
136           return value;
137         })
138         .join(" ");
139       const valuesFormString = valuesToShorthand.join(" ");
140
141       if (shortestFormString.toLowerCase() === valuesFormString.toLowerCase()) {
142         return;
143       }
144
145       if (context.fix) {
146         decl.value = decl.value.replace(value, shortestFormString);
147       } else {
148         report({
149           message: messages.rejected(value, shortestFormString),
150           node: decl,
151           result,
152           ruleName
153         });
154       }
155     });
156   };
157 };
158
159 rule.ruleName = ruleName;
160 rule.messages = messages;
161 module.exports = rule;