postcss-calc 8.2.0 → 8.2.4

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/src/index.js ADDED
@@ -0,0 +1,51 @@
1
+ 'use strict';
2
+ const transform = require('./lib/transform.js');
3
+
4
+ /**
5
+ * @typedef {{precision?: number | false,
6
+ * preserve?: boolean,
7
+ * warnWhenCannotResolve?: boolean,
8
+ * mediaQueries?: boolean,
9
+ * selectors?: boolean}} PostCssCalcOptions
10
+ */
11
+ /**
12
+ * @type {import('postcss').PluginCreator<PostCssCalcOptions>}
13
+ * @param {PostCssCalcOptions} opts
14
+ * @return {import('postcss').Plugin}
15
+ */
16
+ function pluginCreator(opts) {
17
+ const options = Object.assign(
18
+ {
19
+ precision: 5,
20
+ preserve: false,
21
+ warnWhenCannotResolve: false,
22
+ mediaQueries: false,
23
+ selectors: false,
24
+ },
25
+ opts
26
+ );
27
+
28
+ return {
29
+ postcssPlugin: 'postcss-calc',
30
+ OnceExit(css, { result }) {
31
+ css.walk((node) => {
32
+ const { type } = node;
33
+ if (type === 'decl') {
34
+ transform(node, 'value', options, result);
35
+ }
36
+
37
+ if (type === 'atrule' && options.mediaQueries) {
38
+ transform(node, 'params', options, result);
39
+ }
40
+
41
+ if (type === 'rule' && options.selectors) {
42
+ transform(node, 'selector', options, result);
43
+ }
44
+ });
45
+ },
46
+ };
47
+ }
48
+
49
+ pluginCreator.postcss = true;
50
+
51
+ module.exports = pluginCreator;
@@ -0,0 +1,160 @@
1
+ 'use strict';
2
+ /**
3
+ * @type {{[key:string]: {[key:string]: number}}}
4
+ */
5
+ const conversions = {
6
+ // Absolute length units
7
+ px: {
8
+ px: 1,
9
+ cm: 96 / 2.54,
10
+ mm: 96 / 25.4,
11
+ q: 96 / 101.6,
12
+ in: 96,
13
+ pt: 96 / 72,
14
+ pc: 16,
15
+ },
16
+ cm: {
17
+ px: 2.54 / 96,
18
+ cm: 1,
19
+ mm: 0.1,
20
+ q: 0.025,
21
+ in: 2.54,
22
+ pt: 2.54 / 72,
23
+ pc: 2.54 / 6,
24
+ },
25
+ mm: {
26
+ px: 25.4 / 96,
27
+ cm: 10,
28
+ mm: 1,
29
+ q: 0.25,
30
+ in: 25.4,
31
+ pt: 25.4 / 72,
32
+ pc: 25.4 / 6,
33
+ },
34
+ q: {
35
+ px: 101.6 / 96,
36
+ cm: 40,
37
+ mm: 4,
38
+ q: 1,
39
+ in: 101.6,
40
+ pt: 101.6 / 72,
41
+ pc: 101.6 / 6,
42
+ },
43
+ in: {
44
+ px: 1 / 96,
45
+ cm: 1 / 2.54,
46
+ mm: 1 / 25.4,
47
+ q: 1 / 101.6,
48
+ in: 1,
49
+ pt: 1 / 72,
50
+ pc: 1 / 6,
51
+ },
52
+ pt: {
53
+ px: 0.75,
54
+ cm: 72 / 2.54,
55
+ mm: 72 / 25.4,
56
+ q: 72 / 101.6,
57
+ in: 72,
58
+ pt: 1,
59
+ pc: 12,
60
+ },
61
+ pc: {
62
+ px: 0.0625,
63
+ cm: 6 / 2.54,
64
+ mm: 6 / 25.4,
65
+ q: 6 / 101.6,
66
+ in: 6,
67
+ pt: 6 / 72,
68
+ pc: 1,
69
+ },
70
+ // Angle units
71
+ deg: {
72
+ deg: 1,
73
+ grad: 0.9,
74
+ rad: 180 / Math.PI,
75
+ turn: 360,
76
+ },
77
+ grad: {
78
+ deg: 400 / 360,
79
+ grad: 1,
80
+ rad: 200 / Math.PI,
81
+ turn: 400,
82
+ },
83
+ rad: {
84
+ deg: Math.PI / 180,
85
+ grad: Math.PI / 200,
86
+ rad: 1,
87
+ turn: Math.PI * 2,
88
+ },
89
+ turn: {
90
+ deg: 1 / 360,
91
+ grad: 0.0025,
92
+ rad: 0.5 / Math.PI,
93
+ turn: 1,
94
+ },
95
+ // Duration units
96
+ s: {
97
+ s: 1,
98
+ ms: 0.001,
99
+ },
100
+ ms: {
101
+ s: 1000,
102
+ ms: 1,
103
+ },
104
+ // Frequency units
105
+ hz: {
106
+ hz: 1,
107
+ khz: 1000,
108
+ },
109
+ khz: {
110
+ hz: 0.001,
111
+ khz: 1,
112
+ },
113
+ // Resolution units
114
+ dpi: {
115
+ dpi: 1,
116
+ dpcm: 1 / 2.54,
117
+ dppx: 1 / 96,
118
+ },
119
+ dpcm: {
120
+ dpi: 2.54,
121
+ dpcm: 1,
122
+ dppx: 2.54 / 96,
123
+ },
124
+ dppx: {
125
+ dpi: 96,
126
+ dpcm: 96 / 2.54,
127
+ dppx: 1,
128
+ },
129
+ };
130
+ /**
131
+ * @param {number} value
132
+ * @param {string} sourceUnit
133
+ * @param {string} targetUnit
134
+ * @param {number|false} precision
135
+ */
136
+ function convertUnit(value, sourceUnit, targetUnit, precision) {
137
+ const sourceUnitNormalized = sourceUnit.toLowerCase();
138
+ const targetUnitNormalized = targetUnit.toLowerCase();
139
+
140
+ if (!conversions[targetUnitNormalized]) {
141
+ throw new Error('Cannot convert to ' + targetUnit);
142
+ }
143
+
144
+ if (!conversions[targetUnitNormalized][sourceUnitNormalized]) {
145
+ throw new Error('Cannot convert from ' + sourceUnit + ' to ' + targetUnit);
146
+ }
147
+
148
+ const converted =
149
+ conversions[targetUnitNormalized][sourceUnitNormalized] * value;
150
+
151
+ if (precision !== false) {
152
+ precision = Math.pow(10, Math.ceil(precision) || 5);
153
+
154
+ return Math.round(converted * precision) / precision;
155
+ }
156
+
157
+ return converted;
158
+ }
159
+
160
+ module.exports = convertUnit;
@@ -1,13 +1,5 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = void 0;
7
-
8
- var _convertUnit = _interopRequireDefault(require("./convertUnit"));
9
-
10
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
1
+ 'use strict';
2
+ const convertUnit = require('./convertUnit.js');
11
3
 
12
4
  /**
13
5
  * @param {import('../parser').CalcNode} node
@@ -32,159 +24,153 @@ function isValueType(node) {
32
24
  case 'Number':
33
25
  return true;
34
26
  }
35
-
36
27
  return false;
37
28
  }
38
- /** @param {'-'|'+'} operator */
39
-
40
29
 
30
+ /** @param {'-'|'+'} operator */
41
31
  function flip(operator) {
42
32
  return operator === '+' ? '-' : '+';
43
33
  }
34
+
44
35
  /**
45
36
  * @param {string} operator
46
37
  * @returns {operator is '+'|'-'}
47
38
  */
48
-
49
-
50
39
  function isAddSubOperator(operator) {
51
40
  return operator === '+' || operator === '-';
52
41
  }
42
+
53
43
  /**
54
44
  * @typedef {{preOperator: '+'|'-', node: import('../parser').CalcNode}} Collectible
55
- */
45
+ */
56
46
 
57
47
  /**
58
48
  * @param {'+'|'-'} preOperator
59
49
  * @param {import('../parser').CalcNode} node
60
- * @param {Collectible[]} collected
50
+ * @param {Collectible[]} collected
61
51
  * @param {number} precision
62
52
  */
63
-
64
-
65
53
  function collectAddSubItems(preOperator, node, collected, precision) {
66
54
  if (!isAddSubOperator(preOperator)) {
67
55
  throw new Error(`invalid operator ${preOperator}`);
68
56
  }
69
-
70
57
  if (isValueType(node)) {
71
- const itemIndex = collected.findIndex(x => x.node.type === node.type);
72
-
58
+ const itemIndex = collected.findIndex((x) => x.node.type === node.type);
73
59
  if (itemIndex >= 0) {
74
60
  if (node.value === 0) {
75
61
  return;
76
- } // can cast because of the criterion used to find itemIndex
77
-
78
-
79
- const otherValueNode =
80
- /** @type import('../parser').ValueExpression*/
81
- collected[itemIndex].node;
82
- const {
83
- left: reducedNode,
84
- right: current
85
- } = convertNodesUnits(otherValueNode, node, precision);
62
+ }
63
+ // can cast because of the criterion used to find itemIndex
64
+ const otherValueNode = /** @type import('../parser').ValueExpression*/ (
65
+ collected[itemIndex].node
66
+ );
67
+ const { left: reducedNode, right: current } = convertNodesUnits(
68
+ otherValueNode,
69
+ node,
70
+ precision
71
+ );
86
72
 
87
73
  if (collected[itemIndex].preOperator === '-') {
88
74
  collected[itemIndex].preOperator = '+';
89
75
  reducedNode.value *= -1;
90
76
  }
91
-
92
- if (preOperator === "+") {
77
+ if (preOperator === '+') {
93
78
  reducedNode.value += current.value;
94
79
  } else {
95
80
  reducedNode.value -= current.value;
96
- } // make sure reducedNode.value >= 0
97
-
98
-
81
+ }
82
+ // make sure reducedNode.value >= 0
99
83
  if (reducedNode.value >= 0) {
100
- collected[itemIndex] = {
101
- node: reducedNode,
102
- preOperator: '+'
103
- };
84
+ collected[itemIndex] = { node: reducedNode, preOperator: '+' };
104
85
  } else {
105
86
  reducedNode.value *= -1;
106
- collected[itemIndex] = {
107
- node: reducedNode,
108
- preOperator: '-'
109
- };
87
+ collected[itemIndex] = { node: reducedNode, preOperator: '-' };
110
88
  }
111
89
  } else {
112
90
  // make sure node.value >= 0
113
91
  if (node.value >= 0) {
114
- collected.push({
115
- node,
116
- preOperator
117
- });
92
+ collected.push({ node, preOperator });
118
93
  } else {
119
94
  node.value *= -1;
120
- collected.push({
121
- node,
122
- preOperator: flip(preOperator)
123
- });
95
+ collected.push({ node, preOperator: flip(preOperator) });
124
96
  }
125
97
  }
126
- } else if (node.type === "MathExpression") {
98
+ } else if (node.type === 'MathExpression') {
127
99
  if (isAddSubOperator(node.operator)) {
128
100
  collectAddSubItems(preOperator, node.left, collected, precision);
129
- const collectRightOperator = preOperator === '-' ? flip(node.operator) : node.operator;
130
- collectAddSubItems(collectRightOperator, node.right, collected, precision);
101
+ const collectRightOperator =
102
+ preOperator === '-' ? flip(node.operator) : node.operator;
103
+ collectAddSubItems(
104
+ collectRightOperator,
105
+ node.right,
106
+ collected,
107
+ precision
108
+ );
131
109
  } else {
132
110
  // * or /
133
- const reducedNode = reduce(node, precision); // prevent infinite recursive call
134
-
135
- if (reducedNode.type !== "MathExpression" || isAddSubOperator(reducedNode.operator)) {
111
+ const reducedNode = reduce(node, precision);
112
+ // prevent infinite recursive call
113
+ if (
114
+ reducedNode.type !== 'MathExpression' ||
115
+ isAddSubOperator(reducedNode.operator)
116
+ ) {
136
117
  collectAddSubItems(preOperator, reducedNode, collected, precision);
137
118
  } else {
138
- collected.push({
139
- node: reducedNode,
140
- preOperator
141
- });
119
+ collected.push({ node: reducedNode, preOperator });
142
120
  }
143
121
  }
122
+ } else if (node.type === 'ParenthesizedExpression') {
123
+ collectAddSubItems(preOperator, node.content, collected, precision);
144
124
  } else {
145
- collected.push({
146
- node,
147
- preOperator
148
- });
125
+ collected.push({ node, preOperator });
149
126
  }
150
127
  }
128
+
151
129
  /**
152
130
  * @param {import('../parser').CalcNode} node
153
131
  * @param {number} precision
154
132
  */
155
-
156
-
157
133
  function reduceAddSubExpression(node, precision) {
158
134
  /** @type Collectible[] */
159
135
  const collected = [];
160
136
  collectAddSubItems('+', node, collected, precision);
161
- const withoutZeroItem = collected.filter(item => !(isValueType(item.node) && item.node.value === 0));
137
+
138
+ const withoutZeroItem = collected.filter(
139
+ (item) => !(isValueType(item.node) && item.node.value === 0)
140
+ );
162
141
  const firstNonZeroItem = withoutZeroItem[0]; // could be undefined
142
+
163
143
  // prevent producing "calc(-var(--a))" or "calc()"
164
144
  // which is invalid css
165
-
166
- if (!firstNonZeroItem || firstNonZeroItem.preOperator === '-' && !isValueType(firstNonZeroItem.node)) {
167
- const firstZeroItem = collected.find(item => isValueType(item.node) && item.node.value === 0);
168
-
145
+ if (
146
+ !firstNonZeroItem ||
147
+ (firstNonZeroItem.preOperator === '-' &&
148
+ !isValueType(firstNonZeroItem.node))
149
+ ) {
150
+ const firstZeroItem = collected.find(
151
+ (item) => isValueType(item.node) && item.node.value === 0
152
+ );
169
153
  if (firstZeroItem) {
170
154
  withoutZeroItem.unshift(firstZeroItem);
171
155
  }
172
- } // make sure the preOperator of the first item is +
173
-
156
+ }
174
157
 
175
- if (withoutZeroItem[0].preOperator === '-' && isValueType(withoutZeroItem[0].node)) {
158
+ // make sure the preOperator of the first item is +
159
+ if (
160
+ withoutZeroItem[0].preOperator === '-' &&
161
+ isValueType(withoutZeroItem[0].node)
162
+ ) {
176
163
  withoutZeroItem[0].node.value *= -1;
177
164
  withoutZeroItem[0].preOperator = '+';
178
165
  }
179
166
 
180
167
  let root = withoutZeroItem[0].node;
181
-
182
168
  for (let i = 1; i < withoutZeroItem.length; i++) {
183
169
  root = {
184
170
  type: 'MathExpression',
185
171
  operator: withoutZeroItem[i].preOperator,
186
172
  left: root,
187
- right: withoutZeroItem[i].node
173
+ right: withoutZeroItem[i].node,
188
174
  };
189
175
  }
190
176
 
@@ -193,8 +179,6 @@ function reduceAddSubExpression(node, precision) {
193
179
  /**
194
180
  * @param {import('../parser').MathExpression} node
195
181
  */
196
-
197
-
198
182
  function reduceDivisionExpression(node) {
199
183
  if (!isValueType(node.right)) {
200
184
  return node;
@@ -206,116 +190,104 @@ function reduceDivisionExpression(node) {
206
190
 
207
191
  return applyNumberDivision(node.left, node.right.value);
208
192
  }
193
+
209
194
  /**
210
195
  * apply (expr) / number
211
196
  *
212
197
  * @param {import('../parser').CalcNode} node
213
198
  * @param {number} divisor
214
199
  * @return {import('../parser').CalcNode}
215
- */
216
-
217
-
200
+ */
218
201
  function applyNumberDivision(node, divisor) {
219
202
  if (divisor === 0) {
220
203
  throw new Error('Cannot divide by zero');
221
204
  }
222
-
223
205
  if (isValueType(node)) {
224
206
  node.value /= divisor;
225
207
  return node;
226
208
  }
227
-
228
- if (node.type === "MathExpression" && isAddSubOperator(node.operator)) {
209
+ if (node.type === 'MathExpression' && isAddSubOperator(node.operator)) {
229
210
  // turn (a + b) / num into a/num + b/num
230
211
  // is good for further reduction
231
212
  // checkout the test case
232
213
  // "should reduce division before reducing additions"
233
214
  return {
234
- type: "MathExpression",
215
+ type: 'MathExpression',
235
216
  operator: node.operator,
236
217
  left: applyNumberDivision(node.left, divisor),
237
- right: applyNumberDivision(node.right, divisor)
218
+ right: applyNumberDivision(node.right, divisor),
238
219
  };
239
- } // it is impossible to reduce it into a single value
220
+ }
221
+ // it is impossible to reduce it into a single value
240
222
  // .e.g the node contains css variable
241
223
  // so we just preserve the division and let browser do it
242
-
243
-
244
224
  return {
245
- type: "MathExpression",
225
+ type: 'MathExpression',
246
226
  operator: '/',
247
227
  left: node,
248
228
  right: {
249
- type: "Number",
250
- value: divisor
251
- }
229
+ type: 'Number',
230
+ value: divisor,
231
+ },
252
232
  };
253
233
  }
254
234
  /**
255
235
  * @param {import('../parser').MathExpression} node
256
236
  */
257
-
258
-
259
237
  function reduceMultiplicationExpression(node) {
260
238
  // (expr) * number
261
239
  if (node.right.type === 'Number') {
262
240
  return applyNumberMultiplication(node.left, node.right.value);
263
- } // number * (expr)
264
-
265
-
241
+ }
242
+ // number * (expr)
266
243
  if (node.left.type === 'Number') {
267
244
  return applyNumberMultiplication(node.right, node.left.value);
268
245
  }
269
-
270
246
  return node;
271
247
  }
248
+
272
249
  /**
273
250
  * apply (expr) * number
274
251
  * @param {number} multiplier
275
252
  * @param {import('../parser').CalcNode} node
276
253
  * @return {import('../parser').CalcNode}
277
254
  */
278
-
279
-
280
255
  function applyNumberMultiplication(node, multiplier) {
281
256
  if (isValueType(node)) {
282
257
  node.value *= multiplier;
283
258
  return node;
284
259
  }
285
-
286
- if (node.type === "MathExpression" && isAddSubOperator(node.operator)) {
260
+ if (node.type === 'MathExpression' && isAddSubOperator(node.operator)) {
287
261
  // turn (a + b) * num into a*num + b*num
288
262
  // is good for further reduction
289
263
  // checkout the test case
290
264
  // "should reduce multiplication before reducing additions"
291
265
  return {
292
- type: "MathExpression",
266
+ type: 'MathExpression',
293
267
  operator: node.operator,
294
268
  left: applyNumberMultiplication(node.left, multiplier),
295
- right: applyNumberMultiplication(node.right, multiplier)
269
+ right: applyNumberMultiplication(node.right, multiplier),
296
270
  };
297
- } // it is impossible to reduce it into a single value
271
+ }
272
+ // it is impossible to reduce it into a single value
298
273
  // .e.g the node contains css variable
299
274
  // so we just preserve the division and let browser do it
300
-
301
-
302
275
  return {
303
- type: "MathExpression",
276
+ type: 'MathExpression',
304
277
  operator: '*',
305
278
  left: node,
306
279
  right: {
307
- type: "Number",
308
- value: multiplier
309
- }
280
+ type: 'Number',
281
+ value: multiplier,
282
+ },
310
283
  };
311
284
  }
285
+
312
286
  /**
313
287
  * @param {import('../parser').ValueExpression} left
314
288
  * @param {import('../parser').ValueExpression} right
315
289
  * @param {number} precision
316
290
  */
317
-
318
-
319
291
  function convertNodesUnits(left, right, precision) {
320
292
  switch (left.type) {
321
293
  case 'LengthValue':
@@ -324,56 +296,67 @@ function convertNodesUnits(left, right, precision) {
324
296
  case 'FrequencyValue':
325
297
  case 'ResolutionValue':
326
298
  if (right.type === left.type && right.unit && left.unit) {
327
- const converted = (0, _convertUnit.default)(right.value, right.unit, left.unit, precision);
299
+ const converted = convertUnit(
300
+ right.value,
301
+ right.unit,
302
+ left.unit,
303
+ precision
304
+ );
305
+
328
306
  right = {
329
307
  type: left.type,
330
308
  value: converted,
331
- unit: left.unit
309
+ unit: left.unit,
332
310
  };
333
311
  }
334
312
 
335
- return {
336
- left,
337
- right
338
- };
339
-
313
+ return { left, right };
340
314
  default:
341
- return {
342
- left,
343
- right
344
- };
315
+ return { left, right };
345
316
  }
346
317
  }
318
+
319
+ /**
320
+ * @param {import('../parser').ParenthesizedExpression} node
321
+ */
322
+ function includesNoCssProperties(node) {
323
+ return (
324
+ node.content.type !== 'Function' &&
325
+ (node.content.type !== 'MathExpression' ||
326
+ (node.content.right.type !== 'Function' &&
327
+ node.content.left.type !== 'Function'))
328
+ );
329
+ }
347
330
  /**
348
331
  * @param {import('../parser').CalcNode} node
349
332
  * @param {number} precision
333
+ * @return {import('../parser').CalcNode}
350
334
  */
351
-
352
-
353
335
  function reduce(node, precision) {
354
- if (node.type === "MathExpression") {
336
+ if (node.type === 'MathExpression') {
355
337
  if (isAddSubOperator(node.operator)) {
356
338
  // reduceAddSubExpression will call reduce recursively
357
339
  return reduceAddSubExpression(node, precision);
358
340
  }
359
-
360
341
  node.left = reduce(node.left, precision);
361
342
  node.right = reduce(node.right, precision);
362
-
363
343
  switch (node.operator) {
364
- case "/":
344
+ case '/':
365
345
  return reduceDivisionExpression(node);
366
-
367
- case "*":
346
+ case '*':
368
347
  return reduceMultiplicationExpression(node);
369
348
  }
370
349
 
371
350
  return node;
372
351
  }
373
352
 
353
+ if (node.type === 'ParenthesizedExpression') {
354
+ if (includesNoCssProperties(node)) {
355
+ return reduce(node.content, precision);
356
+ }
357
+ }
358
+
374
359
  return node;
375
360
  }
376
361
 
377
- var _default = reduce;
378
- exports.default = _default;
379
- module.exports = exports.default;
362
+ module.exports = reduce;