postcss-calc 7.0.1 → 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.
@@ -5,19 +5,10 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
 
8
- var _convert = _interopRequireDefault(require("./convert"));
8
+ var _convertUnit = _interopRequireDefault(require("./convertUnit"));
9
9
 
10
10
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
11
 
12
- function reduce(node, precision) {
13
- if (node.type === "MathExpression") return reduceMathExpression(node, precision);
14
- return node;
15
- }
16
-
17
- function isEqual(left, right) {
18
- return left.type === right.type && left.value === right.value;
19
- }
20
-
21
12
  function isValueType(type) {
22
13
  switch (type) {
23
14
  case 'LengthValue':
@@ -34,217 +25,291 @@ function isValueType(type) {
34
25
  case 'VminValue':
35
26
  case 'VmaxValue':
36
27
  case 'PercentageValue':
37
- case 'Value':
28
+ case 'Number':
38
29
  return true;
39
30
  }
40
31
 
41
32
  return false;
42
33
  }
43
34
 
44
- function convertMathExpression(node, precision) {
45
- var nodes = (0, _convert.default)(node.left, node.right, precision);
46
- var left = reduce(nodes.left, precision);
47
- var right = reduce(nodes.right, precision);
35
+ function flip(operator) {
36
+ return operator === '+' ? '-' : '+';
37
+ }
38
+
39
+ function isAddSubOperator(operator) {
40
+ return operator === '+' || operator === '-';
41
+ }
48
42
 
49
- if (left.type === "MathExpression" && right.type === "MathExpression") {
50
- if (left.operator === '/' && right.operator === '*' || left.operator === '-' && right.operator === '+' || left.operator === '*' && right.operator === '/' || left.operator === '+' && right.operator === '-') {
51
- if (isEqual(left.right, right.right)) nodes = (0, _convert.default)(left.left, right.left, precision);else if (isEqual(left.right, right.left)) nodes = (0, _convert.default)(left.left, right.right, precision);
52
- left = reduce(nodes.left, precision);
53
- right = reduce(nodes.right, precision);
54
- }
43
+ function collectAddSubItems(preOperator, node, collected, precision) {
44
+ if (!isAddSubOperator(preOperator)) {
45
+ throw new Error(`invalid operator ${preOperator}`);
55
46
  }
56
47
 
57
- node.left = left;
58
- node.right = right;
59
- return node;
60
- }
48
+ var type = node.type;
61
49
 
62
- function flip(operator) {
63
- return operator === '+' ? '-' : '+';
64
- }
50
+ if (isValueType(type)) {
51
+ var itemIndex = collected.findIndex(function (x) {
52
+ return x.node.type === type;
53
+ });
54
+
55
+ if (itemIndex >= 0) {
56
+ if (node.value === 0) {
57
+ return;
58
+ }
59
+
60
+ var _covertNodesUnits = covertNodesUnits(collected[itemIndex].node, node, precision),
61
+ reducedNode = _covertNodesUnits.left,
62
+ current = _covertNodesUnits.right;
65
63
 
66
- function flipValue(node) {
67
- if (isValueType(node.type)) node.value = -node.value;else if (node.type == 'MathExpression') {
68
- node.left = flipValue(node.left);
69
- node.right = flipValue(node.right);
64
+ if (collected[itemIndex].preOperator === '-') {
65
+ collected[itemIndex].preOperator = '+';
66
+ reducedNode.value *= -1;
67
+ }
68
+
69
+ if (preOperator === "+") {
70
+ reducedNode.value += current.value;
71
+ } else {
72
+ reducedNode.value -= current.value;
73
+ } // make sure reducedNode.value >= 0
74
+
75
+
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
+ }
88
+ } else {
89
+ // make sure node.value >= 0
90
+ if (node.value >= 0) {
91
+ collected.push({
92
+ node,
93
+ preOperator
94
+ });
95
+ } else {
96
+ node.value *= -1;
97
+ collected.push({
98
+ node,
99
+ preOperator: flip(preOperator)
100
+ });
101
+ }
102
+ }
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);
115
+ } else {
116
+ collected.push({
117
+ node: _reducedNode,
118
+ preOperator
119
+ });
120
+ }
121
+ }
122
+ } else {
123
+ collected.push({
124
+ node,
125
+ preOperator
126
+ });
70
127
  }
71
- return node;
72
128
  }
73
129
 
74
130
  function reduceAddSubExpression(node, precision) {
75
- var _node = node,
76
- left = _node.left,
77
- right = _node.right,
78
- op = _node.operator;
79
- if (left.type === 'Function' || right.type === 'Function') return node; // something + 0 => something
80
- // something - 0 => something
81
-
82
- if (right.value === 0) return left; // 0 + something => something
83
-
84
- if (left.value === 0 && op === "+") return right; // 0 - something => -something
85
-
86
- if (left.value === 0 && op === "-") return flipValue(right); // value + value
87
- // value - value
88
-
89
- if (left.type === right.type && isValueType(left.type)) {
90
- node = Object.assign({}, left);
91
- if (op === "+") node.value = left.value + right.value;else node.value = left.value - right.value;
92
- } // value <op> (expr)
93
-
94
-
95
- if (isValueType(left.type) && (right.operator === '+' || right.operator === '-') && right.type === 'MathExpression') {
96
- // value + (value + something) => (value + value) + something
97
- // value + (value - something) => (value + value) - something
98
- // value - (value + something) => (value - value) - something
99
- // value - (value - something) => (value - value) + something
100
- if (left.type === right.left.type) {
101
- node = Object.assign({}, node);
102
- node.left = reduce({
103
- type: 'MathExpression',
104
- operator: op,
105
- left: left,
106
- right: right.left
107
- }, precision);
108
- node.right = right.right;
109
- node.operator = op === '-' ? flip(right.operator) : right.operator;
110
- return reduce(node, precision);
111
- } // value + (something + value) => (value + value) + something
112
- // value + (something - value) => (value - value) + something
113
- // value - (something + value) => (value - value) - something
114
- // value - (something - value) => (value + value) - something
115
- else if (left.type === right.right.type) {
116
- node = Object.assign({}, node);
117
- node.left = reduce({
118
- type: 'MathExpression',
119
- operator: op === '-' ? flip(right.operator) : right.operator,
120
- left: left,
121
- right: right.right
122
- }, precision);
123
- node.right = right.left;
124
- return reduce(node, precision);
125
- } // value - (something + something) => value - something - something
126
- else if (op === '-' && right.operator === '+') {
127
- node = Object.assign({}, node);
128
- node.right.operator = '-';
129
- return reduce(node, precision);
130
- }
131
- } // (expr) <op> value
132
-
133
-
134
- if (left.type === 'MathExpression' && (left.operator === '+' || left.operator === '-') && isValueType(right.type)) {
135
- // (value + something) + value => (value + value) + something
136
- // (value - something) + value => (value + value) - something
137
- // (value + something) - value => (value - value) + something
138
- // (value - something) - value => (value - value) - something
139
- if (right.type === left.left.type) {
140
- node = Object.assign({}, left);
141
- node.left = reduce({
142
- type: 'MathExpression',
143
- operator: op,
144
- left: left.left,
145
- right: right
146
- }, precision);
147
- return reduce(node, precision);
148
- } // (something + value) + value => something + (value + value)
149
- // (something - value1) + value2 => something - (value2 - value1)
150
- // (something + value) - value => something + (value - value)
151
- // (something - value) - value => something - (value + value)
152
- else if (right.type === left.right.type) {
153
- node = Object.assign({}, left);
154
-
155
- if (left.operator === '-') {
156
- node.right = reduce({
157
- type: 'MathExpression',
158
- operator: flip(op),
159
- left: left.right,
160
- right: right
161
- }, precision);
162
-
163
- if (node.right.value && node.right.value < 0) {
164
- node.right.value = Math.abs(node.right.value);
165
- node.operator = '+';
166
- } else {
167
- node.operator = left.operator;
168
- }
169
- } else {
170
- node.right = reduce({
171
- type: 'MathExpression',
172
- operator: op,
173
- left: left.right,
174
- right: right
175
- }, precision);
176
- }
177
-
178
- if (node.right.value < 0) {
179
- node.right.value *= -1;
180
- node.operator = node.operator === '-' ? '+' : '-';
181
- }
182
-
183
- return reduce(node, precision);
184
- }
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 = '+';
185
151
  }
186
152
 
187
- if (left.type === 'MathExpression' && right.type === 'MathExpression' && op === '-' && right.operator === '-') {
188
- node.right.operator = flip(node.right.operator);
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
+ };
189
162
  }
190
163
 
191
- return node;
164
+ return root;
192
165
  }
193
166
 
194
167
  function reduceDivisionExpression(node) {
195
- if (!isValueType(node.right.type)) return node;
196
- if (node.right.type !== 'Value') throw new Error(`Cannot divide by "${node.right.unit}", number expected`);
197
- if (node.right.value === 0) throw new Error('Cannot divide by zero'); // something / value
168
+ if (!isValueType(node.right.type)) {
169
+ return node;
170
+ }
198
171
 
199
- if (isValueType(node.left.type)) {
200
- node.left.value /= node.right.value;
201
- return node.left;
172
+ if (node.right.type !== 'Number') {
173
+ throw new Error(`Cannot divide by "${node.right.unit}", number expected`);
202
174
  }
203
175
 
204
- return node;
176
+ return applyNumberDivision(node.left, node.right.value);
177
+ } // apply (expr) / number
178
+
179
+
180
+ function applyNumberDivision(node, divisor) {
181
+ if (divisor === 0) {
182
+ throw new Error('Cannot divide by zero');
183
+ }
184
+
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
+ };
205
215
  }
206
216
 
207
217
  function reduceMultiplicationExpression(node) {
208
- // (expr) * value
209
- if (node.left.type === 'MathExpression' && node.right.type === 'Value') {
210
- if (isValueType(node.left.left.type) && isValueType(node.left.right.type)) {
211
- node.left.left.value *= node.right.value;
212
- node.left.right.value *= node.right.value;
213
- return node.left;
214
- }
215
- } // something * value
216
- else if (isValueType(node.left.type) && node.right.type === 'Value') {
217
- node.left.value *= node.right.value;
218
- return node.left;
219
- } // value * (expr)
220
- else if (node.left.type === 'Value' && node.right.type === 'MathExpression') {
221
- if (isValueType(node.right.left.type) && isValueType(node.right.right.type)) {
222
- node.right.left.value *= node.left.value;
223
- node.right.right.value *= node.left.value;
224
- return node.right;
225
- }
226
- } // value * something
227
- else if (node.left.type === 'Value' && isValueType(node.right.type)) {
228
- node.right.value *= node.left.value;
229
- return node.right;
230
- }
218
+ // (expr) * number
219
+ if (node.right.type === 'Number') {
220
+ return applyNumberMultiplication(node.left, node.right.value);
221
+ } // number * (expr)
222
+
223
+
224
+ if (node.left.type === 'Number') {
225
+ return applyNumberMultiplication(node.right, node.left.value);
226
+ }
231
227
 
232
228
  return node;
229
+ } // apply (expr) / number
230
+
231
+
232
+ function applyNumberMultiplication(node, multiplier) {
233
+ if (isValueType(node.type)) {
234
+ node.value *= multiplier;
235
+ return node;
236
+ }
237
+
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
+ };
233
263
  }
234
264
 
235
- function reduceMathExpression(node, precision) {
236
- node = convertMathExpression(node, precision);
265
+ function covertNodesUnits(left, right, precision) {
266
+ switch (left.type) {
267
+ case 'LengthValue':
268
+ case 'AngleValue':
269
+ case 'TimeValue':
270
+ case 'FrequencyValue':
271
+ case 'ResolutionValue':
272
+ if (right.type === left.type && right.unit && left.unit) {
273
+ var converted = (0, _convertUnit.default)(right.value, right.unit, left.unit, precision);
274
+ right = {
275
+ type: left.type,
276
+ value: converted,
277
+ unit: left.unit
278
+ };
279
+ }
280
+
281
+ return {
282
+ left,
283
+ right
284
+ };
237
285
 
238
- switch (node.operator) {
239
- case "+":
240
- case "-":
286
+ default:
287
+ return {
288
+ left,
289
+ right
290
+ };
291
+ }
292
+ }
293
+
294
+ function reduce(node, precision) {
295
+ if (node.type === "MathExpression") {
296
+ if (isAddSubOperator(node.operator)) {
297
+ // reduceAddSubExpression will call reduce recursively
241
298
  return reduceAddSubExpression(node, precision);
299
+ }
300
+
301
+ node.left = reduce(node.left, precision);
302
+ node.right = reduce(node.right, precision);
242
303
 
243
- case "/":
244
- return reduceDivisionExpression(node, precision);
304
+ switch (node.operator) {
305
+ case "/":
306
+ return reduceDivisionExpression(node, precision);
307
+
308
+ case "*":
309
+ return reduceMultiplicationExpression(node, precision);
310
+ }
245
311
 
246
- case "*":
247
- return reduceMultiplicationExpression(node);
312
+ return node;
248
313
  }
249
314
 
250
315
  return node;
@@ -28,13 +28,25 @@ function stringify(node, prec) {
28
28
  right = node.right,
29
29
  op = node.operator;
30
30
  var str = "";
31
- if (left.type === 'MathExpression' && order[op] < order[left.operator]) str += `(${stringify(left, prec)})`;else str += stringify(left, prec);
31
+
32
+ if (left.type === 'MathExpression' && order[op] < order[left.operator]) {
33
+ str += `(${stringify(left, prec)})`;
34
+ } else {
35
+ str += stringify(left, prec);
36
+ }
37
+
32
38
  str += order[op] ? ` ${node.operator} ` : node.operator;
33
- if (right.type === 'MathExpression' && order[op] < order[right.operator]) str += `(${stringify(right, prec)})`;else str += stringify(right, prec);
39
+
40
+ if (right.type === 'MathExpression' && order[op] < order[right.operator]) {
41
+ str += `(${stringify(right, prec)})`;
42
+ } else {
43
+ str += stringify(right, prec);
44
+ }
45
+
34
46
  return str;
35
47
  }
36
48
 
37
- case "Value":
49
+ case 'Number':
38
50
  return round(node.value, prec);
39
51
 
40
52
  case 'Function':
@@ -47,8 +59,9 @@ function stringify(node, prec) {
47
59
 
48
60
  function _default(calc, node, originalValue, options, result, item) {
49
61
  var str = stringify(node, options.precision);
62
+ var shouldPrintCalc = node.type === "MathExpression" || node.type === "Function";
50
63
 
51
- if (node.type === "MathExpression") {
64
+ if (shouldPrintCalc) {
52
65
  // if calc expression couldn't be resolved to a single value, re-wrap it as
53
66
  // a calc()
54
67
  str = `${calc}(${str})`; // if the warnWhenCannotResolve option is on, inform the user that the calc
@@ -23,7 +23,10 @@ var MATCH_CALC = /((?:-(moz|webkit)-)?calc)/i;
23
23
  function transformValue(value, options, result, item) {
24
24
  return (0, _postcssValueParser.default)(value).walk(function (node) {
25
25
  // skip anything which isn't a calc() function
26
- if (node.type !== 'function' || !MATCH_CALC.test(node.value)) return node; // stringify calc expression and produce an AST
26
+ if (node.type !== 'function' || !MATCH_CALC.test(node.value)) {
27
+ return node;
28
+ } // stringify calc expression and produce an AST
29
+
27
30
 
28
31
  var contents = _postcssValueParser.default.stringify(node.nodes);
29
32
 
@@ -31,11 +34,12 @@ function transformValue(value, options, result, item) {
31
34
  // or a simplified calc expression
32
35
 
33
36
 
34
- var reducedAst = (0, _reducer.default)(ast, options.precision, item); // stringify AST and write it back
37
+ var reducedAst = (0, _reducer.default)(ast, options.precision); // stringify AST and write it back
35
38
 
36
39
  node.type = 'word';
37
40
  node.value = (0, _stringifier.default)(node.value, reducedAst, value, options, result, item);
38
- }, true).toString();
41
+ return false;
42
+ }).toString();
39
43
  }
40
44
 
41
45
  function transformSelector(value, options, result, item) {
@@ -49,7 +53,10 @@ function transformSelector(value, options, result, item) {
49
53
  // e.g. the "calc(3*3)" part of "div:nth-child(2n + calc(3*3))"
50
54
 
51
55
 
52
- if (node.type === 'tag') node.value = transformValue(node.value, options, result, item);
56
+ if (node.type === 'tag') {
57
+ node.value = transformValue(node.value, options, result, item);
58
+ }
59
+
53
60
  return;
54
61
  });
55
62
  }).processSync(value);
@@ -65,7 +72,9 @@ var _default = function _default(node, property, options, result) {
65
72
  var clone = node.clone();
66
73
  clone[property] = value;
67
74
  node.parent.insertBefore(node, clone);
68
- } else node[property] = value;
75
+ } else {
76
+ node[property] = value;
77
+ }
69
78
  };
70
79
 
71
80
  exports.default = _default;