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/README.md +8 -44
- package/package.json +22 -19
- package/src/__tests__/convertUnit.js +421 -0
- package/src/__tests__/index.js +903 -0
- package/src/index.js +51 -0
- package/src/lib/convertUnit.js +160 -0
- package/{dist → src}/lib/reducer.js +122 -139
- package/{dist → src}/lib/stringifier.js +40 -50
- package/src/lib/transform.js +109 -0
- package/src/parser.d.ts +51 -0
- package/src/parser.jison +111 -0
- package/{dist → src}/parser.js +6 -2
- package/types/index.d.ts +15 -20
- package/types/lib/convertUnit.d.ts +1 -1
- package/types/lib/reducer.d.ts +10 -6
- package/types/lib/stringifier.d.ts +2 -11
- package/types/lib/transform.d.ts +2 -2
- package/dist/index.js +0 -64
- package/dist/lib/convertUnit.js +0 -167
- package/dist/lib/transform.js +0 -119
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
|
-
|
|
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
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
}
|
|
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 ===
|
|
98
|
+
} else if (node.type === 'MathExpression') {
|
|
127
99
|
if (isAddSubOperator(node.operator)) {
|
|
128
100
|
collectAddSubItems(preOperator, node.left, collected, precision);
|
|
129
|
-
const collectRightOperator =
|
|
130
|
-
|
|
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);
|
|
134
|
-
|
|
135
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
167
|
-
|
|
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
|
-
}
|
|
173
|
-
|
|
156
|
+
}
|
|
174
157
|
|
|
175
|
-
|
|
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:
|
|
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
|
-
}
|
|
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:
|
|
225
|
+
type: 'MathExpression',
|
|
246
226
|
operator: '/',
|
|
247
227
|
left: node,
|
|
248
228
|
right: {
|
|
249
|
-
type:
|
|
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
|
-
}
|
|
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:
|
|
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
|
-
}
|
|
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:
|
|
276
|
+
type: 'MathExpression',
|
|
304
277
|
operator: '*',
|
|
305
278
|
left: node,
|
|
306
279
|
right: {
|
|
307
|
-
type:
|
|
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 = (
|
|
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 ===
|
|
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
|
-
|
|
378
|
-
exports.default = _default;
|
|
379
|
-
module.exports = exports.default;
|
|
362
|
+
module.exports = reduce;
|