postcss-calc 7.0.4 → 7.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # 7.0.5
2
+
3
+ - Fixed: reduction
4
+
1
5
  # 7.0.4
2
6
 
3
7
  - Fixed: strips away important factors from multiplications in calc() ([#107](https://github.com/postcss/postcss-calc/issues/107))
@@ -9,10 +9,6 @@ var _convertUnit = _interopRequireDefault(require("./convertUnit"));
9
9
 
10
10
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
11
 
12
- function isEqual(left, right) {
13
- return left.type === right.type && left.value === right.value;
14
- }
15
-
16
12
  function isValueType(type) {
17
13
  switch (type) {
18
14
  case 'LengthValue':
@@ -40,204 +36,132 @@ function flip(operator) {
40
36
  return operator === '+' ? '-' : '+';
41
37
  }
42
38
 
43
- function flipValue(node) {
44
- if (isValueType(node.type)) {
45
- node.value = -node.value;
46
- } else if (node.type === 'MathExpression') {
47
- if (node.operator === '*' || node.operator === '/') {
48
- node.left = flipValue(node.left);
49
- } else {
50
- node.left = flipValue(node.left);
51
- node.right = flipValue(node.right);
52
- }
53
- }
54
-
55
- return node;
39
+ function isAddSubOperator(operator) {
40
+ return operator === '+' || operator === '-';
56
41
  }
57
42
 
58
- function reduceAddSubExpression(node, precision) {
59
- // something + 0 => something
60
- // something - 0 => something
61
- if (isValueType(node.right.type) && node.right.value === 0) {
62
- return node.left;
63
- } // 0 + something => something
43
+ function collectAddSubItems(preOperator, node, collected, precision) {
44
+ if (!isAddSubOperator(preOperator)) {
45
+ throw new Error(`invalid operator ${preOperator}`);
46
+ }
64
47
 
48
+ var type = node.type;
65
49
 
66
- if (isValueType(node.left.type) && node.left.value === 0 && node.operator === "+") {
67
- return node.right;
68
- } // 0 - something => -something
50
+ if (isValueType(type)) {
51
+ var itemIndex = collected.findIndex(function (x) {
52
+ return x.node.type === type;
53
+ });
69
54
 
55
+ if (itemIndex >= 0) {
56
+ if (node.value === 0) {
57
+ return;
58
+ }
70
59
 
71
- if (isValueType(node.left.type) && node.left.value === 0 && node.operator === "-" && !isCSSFunction(node.right)) {
72
- return flipValue(node.right);
73
- } // value + value
74
- // value - value
60
+ var _covertNodesUnits = covertNodesUnits(collected[itemIndex].node, node, precision),
61
+ reducedNode = _covertNodesUnits.left,
62
+ current = _covertNodesUnits.right;
75
63
 
64
+ if (collected[itemIndex].preOperator === '-') {
65
+ collected[itemIndex].preOperator = '+';
66
+ reducedNode.value *= -1;
67
+ }
76
68
 
77
- if (isValueType(node.left.type) && node.left.type === node.right.type) {
78
- var operator = node.operator;
69
+ if (preOperator === "+") {
70
+ reducedNode.value += current.value;
71
+ } else {
72
+ reducedNode.value -= current.value;
73
+ } // make sure reducedNode.value >= 0
79
74
 
80
- var _covertNodesUnits = covertNodesUnits(node.left, node.right, precision),
81
- left = _covertNodesUnits.left,
82
- right = _covertNodesUnits.right;
83
75
 
84
- if (operator === "+") {
85
- left.value += right.value;
76
+ if (reducedNode.value >= 0) {
77
+ collected[itemIndex] = {
78
+ node: reducedNode,
79
+ preOperator: '+'
80
+ };
81
+ } else {
82
+ reducedNode.value *= -1;
83
+ collected[itemIndex] = {
84
+ node: reducedNode,
85
+ preOperator: '-'
86
+ };
87
+ }
86
88
  } else {
87
- left.value -= right.value;
88
- }
89
-
90
- return left;
91
- } // value <op> (expr)
92
-
93
-
94
- if (node.right.type === 'MathExpression' && (node.right.operator === '+' || node.right.operator === '-')) {
95
- // something - (something + something) => something - something - something
96
- // something - (something - something) => something - something + something
97
- if ((node.right.operator === '+' || node.right.operator === '-') && node.operator === '-') {
98
- node.right.operator = flip(node.right.operator);
99
- }
100
-
101
- if (isValueType(node.left.type)) {
102
- // value + (value + something) => value + something
103
- // value + (value - something) => value - something
104
- // value - (value + something) => value - something
105
- // value - (value - something) => value + something
106
- if (node.left.type === node.right.left.type) {
107
- var _left = node.left,
108
- _operator = node.operator,
109
- _right = node.right;
110
- node.left = reduce({
111
- type: 'MathExpression',
112
- operator: _operator,
113
- left: _left,
114
- right: _right.left
89
+ // make sure node.value >= 0
90
+ if (node.value >= 0) {
91
+ collected.push({
92
+ node,
93
+ preOperator
115
94
  });
116
- node.operator = _right.operator;
117
- node.right = _right.right;
118
- return reduce(node, precision);
119
- } // something + (something + value) => dimension + something
120
- // something + (something - value) => dimension + something
121
- // something - (something + value) => dimension - something
122
- // something - (something - value) => dimension - something
123
-
124
-
125
- if (node.left.type === node.right.right.type) {
126
- var _left2 = node.left,
127
- _right2 = node.right;
128
- node.left = reduce({
129
- type: 'MathExpression',
130
- operator: _right2.operator,
131
- left: _left2,
132
- right: _right2.right
95
+ } else {
96
+ node.value *= -1;
97
+ collected.push({
98
+ node,
99
+ preOperator: flip(preOperator)
133
100
  });
134
- node.right = _right2.left;
135
- return reduce(node, precision);
136
101
  }
137
102
  }
138
- } // (expr) <op> value
139
-
140
-
141
- if (node.left.type === 'MathExpression' && (node.left.operator === '+' || node.left.operator === '-') && isValueType(node.right.type)) {
142
- // (value + something) + value => value + something
143
- // (value - something) + value => value - something
144
- // (value + something) - value => value + something
145
- // (value - something) - value => value - something
146
- if (node.right.type === node.left.left.type) {
147
- var _left3 = node.left,
148
- _operator2 = node.operator,
149
- _right3 = node.right;
150
- _left3.left = reduce({
151
- type: 'MathExpression',
152
- operator: _operator2,
153
- left: _left3.left,
154
- right: _right3
155
- }, precision);
156
- return reduce(_left3, precision);
157
- } // (something + dimension) + dimension => something + dimension
158
- // (something - dimension) + dimension => something - dimension
159
- // (something + dimension) - dimension => something + dimension
160
- // (something - dimension) - dimension => something - dimension
161
-
162
-
163
- if (node.right.type === node.left.right.type) {
164
- var _left4 = node.left,
165
- _operator3 = node.operator,
166
- _right4 = node.right;
167
-
168
- if (_left4.operator === '-') {
169
- _left4.operator = _operator3 === '-' ? '-' : '+';
170
- _left4.right = reduce({
171
- type: 'MathExpression',
172
- operator: _operator3 === '-' ? '+' : '-',
173
- left: _right4,
174
- right: _left4.right
175
- }, precision);
103
+ } else if (type === "MathExpression") {
104
+ if (isAddSubOperator(node.operator)) {
105
+ collectAddSubItems(preOperator, node.left, collected, precision);
106
+ var collectRightOperator = preOperator === '-' ? flip(node.operator) : node.operator;
107
+ collectAddSubItems(collectRightOperator, node.right, collected, precision);
108
+ } else {
109
+ // * or /
110
+ var _reducedNode = reduce(node, precision); // prevent infinite recursive call
111
+
112
+
113
+ if (_reducedNode.type !== "MathExpression" || isAddSubOperator(_reducedNode.operator)) {
114
+ collectAddSubItems(preOperator, _reducedNode, collected, precision);
176
115
  } else {
177
- _left4.right = reduce({
178
- type: 'MathExpression',
179
- operator: _operator3,
180
- left: _left4.right,
181
- right: _right4
182
- }, precision);
116
+ collected.push({
117
+ node: _reducedNode,
118
+ preOperator
119
+ });
183
120
  }
121
+ }
122
+ } else {
123
+ collected.push({
124
+ node,
125
+ preOperator
126
+ });
127
+ }
128
+ }
184
129
 
185
- if (_left4.right.value < 0) {
186
- _left4.right.value *= -1;
187
- _left4.operator = _left4.operator === '-' ? '+' : '-';
188
- }
130
+ function reduceAddSubExpression(node, precision) {
131
+ var collected = [];
132
+ collectAddSubItems('+', node, collected, precision);
133
+ var withoutZeroItem = collected.filter(function (item) {
134
+ return !(isValueType(item.node.type) && item.node.value === 0);
135
+ });
136
+ var firstNonZeroItem = withoutZeroItem[0]; // could be undefined
137
+ // prevent producing "calc(-var(--a))" or "calc()"
138
+ // which is invalid css
139
+
140
+ if (!firstNonZeroItem || firstNonZeroItem.preOperator === '-' && !isValueType(firstNonZeroItem.node.type)) {
141
+ var firstZeroItem = collected.find(function (item) {
142
+ return isValueType(item.node.type) && item.node.value === 0;
143
+ });
144
+ withoutZeroItem.unshift(firstZeroItem);
145
+ } // make sure the preOperator of the first item is +
146
+
147
+
148
+ if (withoutZeroItem[0].preOperator === '-' && isValueType(withoutZeroItem[0].node.type)) {
149
+ withoutZeroItem[0].node.value *= -1;
150
+ withoutZeroItem[0].preOperator = '+';
151
+ }
189
152
 
190
- _left4.parenthesized = node.parenthesized;
191
- return reduce(_left4, precision);
192
- }
193
- } // (expr) + (expr) => number
194
- // (expr) - (expr) => number
195
-
196
-
197
- if (node.right.type === 'MathExpression' && node.left.type === 'MathExpression') {
198
- if (node.left.operator === "-" && node.right.operator === "+" || node.right.operator === "-" && node.left.operator === "+") {
199
- // (expr1 - something) + (expr2 + something) => expr1 + expr2
200
- // (expr1 + something) + (expr2 - something) => expr1 + expr2
201
- // (expr1 - something) - (expr2 + something) => expr1 + expr2
202
- // (expr1 + something) - (expr2 - something) => expr1 + expr2
203
- if (isEqual(node.left.right, node.right.right)) {
204
- var newNodes = covertNodesUnits(node.left.left, node.right.left, precision);
205
- node.left = newNodes.left;
206
- node.right = newNodes.right;
207
- return reduce(node, precision);
208
- } // (expr1 - something) + (something + expr2) => expr1 + expr2
209
- // (expr1 + something) + (something - expr2) => expr1 + expr2
210
- // (expr1 - something) - (something + expr2) => expr1 + expr2
211
- // (expr1 + something) - (something - expr2) => expr1 + expr2
212
-
213
-
214
- if (isEqual(node.left.right, node.right.left)) {
215
- var _newNodes = covertNodesUnits(node.left.left, node.right.right, precision);
216
-
217
- node.left = _newNodes.left;
218
- node.right = _newNodes.right;
219
- return reduce(node, precision);
220
- } // (expr1 / something) + (expr2 / something) => (expr1 + expr2) / something
221
- // (expr1 * something) + (expr2 * something) => (expr1 + expr2) * something
222
- // (expr1 / something) - (expr2 / something) => (expr1 - expr2) / something
223
- // (expr1 * something) - (expr2 * something) => (expr1 - expr2) * something
224
-
225
- } else if ((node.left.operator === "/" || node.left.operator === "*") && node.left.operator === node.right.operator && isEqual(node.left.right, node.right.right)) {
226
- return reduce({
227
- type: "MathExpression",
228
- operator: node.left.operator,
229
- left: {
230
- type: "MathExpression",
231
- operator: node.operator,
232
- left: node.left.left,
233
- right: node.right.left
234
- },
235
- right: node.left.right
236
- }, precision);
237
- }
153
+ var root = withoutZeroItem[0].node;
154
+
155
+ for (var i = 1; i < withoutZeroItem.length; i++) {
156
+ root = {
157
+ type: 'MathExpression',
158
+ operator: withoutZeroItem[i].preOperator,
159
+ left: root,
160
+ right: withoutZeroItem[i].node
161
+ };
238
162
  }
239
163
 
240
- return node;
164
+ return root;
241
165
  }
242
166
 
243
167
  function reduceDivisionExpression(node) {
@@ -249,51 +173,93 @@ function reduceDivisionExpression(node) {
249
173
  throw new Error(`Cannot divide by "${node.right.unit}", number expected`);
250
174
  }
251
175
 
252
- if (node.right.value === 0) {
253
- throw new Error('Cannot divide by zero');
254
- } // something / value
176
+ return applyNumberDivision(node.left, node.right.value);
177
+ } // apply (expr) / number
255
178
 
256
179
 
257
- if (isValueType(node.left.type)) {
258
- node.left.value /= node.right.value;
259
- return node.left;
180
+ function applyNumberDivision(node, divisor) {
181
+ if (divisor === 0) {
182
+ throw new Error('Cannot divide by zero');
260
183
  }
261
184
 
262
- return node;
185
+ if (isValueType(node.type)) {
186
+ node.value /= divisor;
187
+ return node;
188
+ }
189
+
190
+ if (node.type === "MathExpression" && isAddSubOperator(node.operator)) {
191
+ // turn (a + b) / num into a/num + b/num
192
+ // is good for further reduction
193
+ // checkout the test case
194
+ // "should reduce division before reducing additions"
195
+ return {
196
+ type: "MathExpression",
197
+ operator: node.operator,
198
+ left: applyNumberDivision(node.left, divisor),
199
+ right: applyNumberDivision(node.right, divisor)
200
+ };
201
+ } // it is impossible to reduce it into a single value
202
+ // .e.g the node contains css variable
203
+ // so we just preserve the division and let browser do it
204
+
205
+
206
+ return {
207
+ type: "MathExpression",
208
+ operator: '/',
209
+ left: node,
210
+ right: {
211
+ type: "Number",
212
+ value: divisor
213
+ }
214
+ };
263
215
  }
264
216
 
265
217
  function reduceMultiplicationExpression(node) {
266
218
  // (expr) * number
267
- if (node.left.type === 'MathExpression' && node.right.type === 'Number') {
268
- if (isValueType(node.left.left.type) && isValueType(node.left.right.type)) {
269
- node.left.left.value *= node.right.value;
270
- node.left.right.value *= node.right.value;
271
- return node.left;
272
- }
273
- } // something * number
274
-
275
-
276
- if (isValueType(node.left.type) && node.right.type === 'Number') {
277
- node.left.value *= node.right.value;
278
- return node.left;
219
+ if (node.right.type === 'Number') {
220
+ return applyNumberMultiplication(node.left, node.right.value);
279
221
  } // number * (expr)
280
222
 
281
223
 
282
- if (node.left.type === 'Number' && node.right.type === 'MathExpression') {
283
- if (isValueType(node.right.left.type) && isValueType(node.right.right.type)) {
284
- node.right.left.value *= node.left.value;
285
- node.right.right.value *= node.left.value;
286
- return node.right;
287
- }
288
- } // number * something
224
+ if (node.left.type === 'Number') {
225
+ return applyNumberMultiplication(node.right, node.left.value);
226
+ }
227
+
228
+ return node;
229
+ } // apply (expr) / number
289
230
 
290
231
 
291
- if (node.left.type === 'Number' && isValueType(node.right.type)) {
292
- node.right.value *= node.left.value;
293
- return node.right;
232
+ function applyNumberMultiplication(node, multiplier) {
233
+ if (isValueType(node.type)) {
234
+ node.value *= multiplier;
235
+ return node;
294
236
  }
295
237
 
296
- return node;
238
+ if (node.type === "MathExpression" && isAddSubOperator(node.operator)) {
239
+ // turn (a + b) * num into a*num + b*num
240
+ // is good for further reduction
241
+ // checkout the test case
242
+ // "should reduce multiplication before reducing additions"
243
+ return {
244
+ type: "MathExpression",
245
+ operator: node.operator,
246
+ left: applyNumberMultiplication(node.left, multiplier),
247
+ right: applyNumberMultiplication(node.right, multiplier)
248
+ };
249
+ } // it is impossible to reduce it into a single value
250
+ // .e.g the node contains css variable
251
+ // so we just preserve the division and let browser do it
252
+
253
+
254
+ return {
255
+ type: "MathExpression",
256
+ operator: '*',
257
+ left: node,
258
+ right: {
259
+ type: "Number",
260
+ value: multiplier
261
+ }
262
+ };
297
263
  }
298
264
 
299
265
  function covertNodesUnits(left, right, precision) {
@@ -327,14 +293,15 @@ function covertNodesUnits(left, right, precision) {
327
293
 
328
294
  function reduce(node, precision) {
329
295
  if (node.type === "MathExpression") {
296
+ if (isAddSubOperator(node.operator)) {
297
+ // reduceAddSubExpression will call reduce recursively
298
+ return reduceAddSubExpression(node, precision);
299
+ }
300
+
330
301
  node.left = reduce(node.left, precision);
331
302
  node.right = reduce(node.right, precision);
332
303
 
333
304
  switch (node.operator) {
334
- case "+":
335
- case "-":
336
- return reduceAddSubExpression(node, precision);
337
-
338
305
  case "/":
339
306
  return reduceDivisionExpression(node, precision);
340
307
 
@@ -350,17 +317,4 @@ function reduce(node, precision) {
350
317
 
351
318
  var _default = reduce;
352
319
  exports.default = _default;
353
-
354
- function isCSSFunction(node) {
355
- if (node.type === "Function") {
356
- return true;
357
- }
358
-
359
- if (node.type === "MathExpression") {
360
- return isCSSFunction(node.left) || isCSSFunction(node.right);
361
- }
362
-
363
- return false;
364
- }
365
-
366
320
  module.exports = exports.default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postcss-calc",
3
- "version": "7.0.4",
3
+ "version": "7.0.5",
4
4
  "description": "PostCSS plugin to reduce calc()",
5
5
  "keywords": [
6
6
  "css",