postcss-calc 10.1.0 → 11.0.0-rc.0
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 +56 -1
- package/package.json +19 -13
- package/src/index.js +122 -27
- package/src/lib/convertUnits.js +105 -0
- package/src/lib/node.js +189 -0
- package/src/lib/parser.js +314 -0
- package/src/lib/serialize.js +226 -0
- package/src/lib/simplify/abs.js +21 -0
- package/src/lib/simplify/atan2.js +25 -0
- package/src/lib/simplify/bucket.js +48 -0
- package/src/lib/simplify/call.js +59 -0
- package/src/lib/simplify/cancel.js +39 -0
- package/src/lib/simplify/clamp.js +26 -0
- package/src/lib/simplify/exp.js +18 -0
- package/src/lib/simplify/fold.js +63 -0
- package/src/lib/simplify/hypot.js +22 -0
- package/src/lib/simplify/inverse-trig.js +29 -0
- package/src/lib/simplify/log.js +25 -0
- package/src/lib/simplify/min-max.js +23 -0
- package/src/lib/simplify/mod-rem.js +47 -0
- package/src/lib/simplify/pow.js +20 -0
- package/src/lib/simplify/product.js +133 -0
- package/src/lib/simplify/round.js +113 -0
- package/src/lib/simplify/sign.js +22 -0
- package/src/lib/simplify/sqrt.js +18 -0
- package/src/lib/simplify/sum.js +83 -0
- package/src/lib/simplify/trig.js +40 -0
- package/src/lib/simplify.js +40 -0
- package/src/lib/tokenizer.js +115 -0
- package/types/index.d.ts +15 -17
- package/types/lib/convertUnits.d.ts +16 -0
- package/types/lib/node.d.ts +90 -0
- package/types/lib/parser.d.ts +39 -0
- package/types/lib/serialize.d.ts +20 -0
- package/types/lib/simplify/abs.d.ts +7 -0
- package/types/lib/simplify/atan2.d.ts +7 -0
- package/types/lib/simplify/bucket.d.ts +19 -0
- package/types/lib/simplify/call.d.ts +12 -0
- package/types/lib/simplify/cancel.d.ts +19 -0
- package/types/lib/simplify/clamp.d.ts +7 -0
- package/types/lib/simplify/exp.d.ts +7 -0
- package/types/lib/simplify/fold.d.ts +12 -0
- package/types/lib/simplify/hypot.d.ts +7 -0
- package/types/lib/simplify/inverse-trig.d.ts +7 -0
- package/types/lib/simplify/log.d.ts +7 -0
- package/types/lib/simplify/min-max.d.ts +8 -0
- package/types/lib/simplify/mod-rem.d.ts +8 -0
- package/types/lib/simplify/pow.d.ts +7 -0
- package/types/lib/simplify/product.d.ts +16 -0
- package/types/lib/simplify/round.d.ts +8 -0
- package/types/lib/simplify/sign.d.ts +7 -0
- package/types/lib/simplify/sqrt.d.ts +7 -0
- package/types/lib/simplify/sum.d.ts +18 -0
- package/types/lib/simplify/trig.d.ts +7 -0
- package/types/lib/simplify.d.ts +18 -0
- package/types/lib/tokenizer.d.ts +19 -0
- package/types/lib/type.d.ts +16 -0
- package/src/lib/convertUnit.js +0 -160
- package/src/lib/reducer.js +0 -390
- package/src/lib/stringifier.js +0 -98
- package/src/lib/transform.js +0 -109
- package/src/parser.d.ts +0 -79
- package/src/parser.js +0 -4200
- package/types/lib/convertUnit.d.ts +0 -8
- package/types/lib/reducer.d.ts +0 -14
- package/types/lib/stringifier.d.ts +0 -5
- package/types/lib/transform.d.ts +0 -6
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type TokenType = "number" | "dimension" | "ident" | "punct" | "eof";
|
|
2
|
+
export type Token = {
|
|
3
|
+
type: TokenType;
|
|
4
|
+
value: string;
|
|
5
|
+
/**
|
|
6
|
+
* Present on `dimension` tokens; `%` for percentages.
|
|
7
|
+
*/
|
|
8
|
+
unit?: string | undefined;
|
|
9
|
+
pos: number;
|
|
10
|
+
/**
|
|
11
|
+
* Whitespace immediately before — drives the §10.1 `+`/`-` rule.
|
|
12
|
+
*/
|
|
13
|
+
ws: boolean;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* @param {string} input
|
|
17
|
+
* @return {Token[]}
|
|
18
|
+
*/
|
|
19
|
+
export function tokenize(input: string): Token[];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type BaseType = "length" | "angle" | "time" | "frequency" | "resolution" | "flex" | "percentage";
|
|
2
|
+
/**
|
|
3
|
+
* @param {string} unit
|
|
4
|
+
* @return {BaseType | null}
|
|
5
|
+
*/
|
|
6
|
+
export function baseOf(unit: string): BaseType | null;
|
|
7
|
+
/**
|
|
8
|
+
* Convert a value within a single conversion family. Returns null when
|
|
9
|
+
* either unit is missing from the table (em/rem/vw need runtime context)
|
|
10
|
+
* or when the units belong to different base types.
|
|
11
|
+
* @param {number} value
|
|
12
|
+
* @param {string} from
|
|
13
|
+
* @param {string} to
|
|
14
|
+
* @return {number | null}
|
|
15
|
+
*/
|
|
16
|
+
export function convert(value: number, from: string, to: string): number | null;
|
package/src/lib/convertUnit.js
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
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;
|
package/src/lib/reducer.js
DELETED
|
@@ -1,390 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const convertUnit = require('./convertUnit.js');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* @param {import('../parser').CalcNode} node
|
|
6
|
-
* @return {node is import('../parser').ValueExpression}
|
|
7
|
-
*/
|
|
8
|
-
function isValueType(node) {
|
|
9
|
-
switch (node.type) {
|
|
10
|
-
case 'LengthValue':
|
|
11
|
-
case 'AngleValue':
|
|
12
|
-
case 'TimeValue':
|
|
13
|
-
case 'FrequencyValue':
|
|
14
|
-
case 'ResolutionValue':
|
|
15
|
-
case 'EmValue':
|
|
16
|
-
case 'ExValue':
|
|
17
|
-
case 'ChValue':
|
|
18
|
-
case 'RemValue':
|
|
19
|
-
case 'VhValue':
|
|
20
|
-
case 'SvhValue':
|
|
21
|
-
case 'LvhValue':
|
|
22
|
-
case 'DvhValue':
|
|
23
|
-
case 'VwValue':
|
|
24
|
-
case 'SvwValue':
|
|
25
|
-
case 'LvwValue':
|
|
26
|
-
case 'DvwValue':
|
|
27
|
-
case 'VminValue':
|
|
28
|
-
case 'SvminValue':
|
|
29
|
-
case 'LvminValue':
|
|
30
|
-
case 'DvminValue':
|
|
31
|
-
case 'VmaxValue':
|
|
32
|
-
case 'SvmaxValue':
|
|
33
|
-
case 'LvmaxValue':
|
|
34
|
-
case 'DvmaxValue':
|
|
35
|
-
case 'VbValue':
|
|
36
|
-
case 'SvbValue':
|
|
37
|
-
case 'LvbValue':
|
|
38
|
-
case 'DvbValue':
|
|
39
|
-
case 'ViValue':
|
|
40
|
-
case 'SviValue':
|
|
41
|
-
case 'LviValue':
|
|
42
|
-
case 'DviValue':
|
|
43
|
-
case 'CqwValue':
|
|
44
|
-
case 'CqhValue':
|
|
45
|
-
case 'CqiValue':
|
|
46
|
-
case 'CqbValue':
|
|
47
|
-
case 'CqminValue':
|
|
48
|
-
case 'CqmaxValue':
|
|
49
|
-
case 'PercentageValue':
|
|
50
|
-
case 'LhValue':
|
|
51
|
-
case 'RlhValue':
|
|
52
|
-
case 'Number':
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** @param {'-'|'+'} operator */
|
|
59
|
-
function flip(operator) {
|
|
60
|
-
return operator === '+' ? '-' : '+';
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* @param {string} operator
|
|
65
|
-
* @returns {operator is '+'|'-'}
|
|
66
|
-
*/
|
|
67
|
-
function isAddSubOperator(operator) {
|
|
68
|
-
return operator === '+' || operator === '-';
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* @typedef {{preOperator: '+'|'-', node: import('../parser').CalcNode}} Collectible
|
|
73
|
-
*/
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* @param {'+'|'-'} preOperator
|
|
77
|
-
* @param {import('../parser').CalcNode} node
|
|
78
|
-
* @param {Collectible[]} collected
|
|
79
|
-
* @param {number} precision
|
|
80
|
-
*/
|
|
81
|
-
function collectAddSubItems(preOperator, node, collected, precision) {
|
|
82
|
-
if (!isAddSubOperator(preOperator)) {
|
|
83
|
-
throw new Error(`invalid operator ${preOperator}`);
|
|
84
|
-
}
|
|
85
|
-
if (isValueType(node)) {
|
|
86
|
-
const itemIndex = collected.findIndex((x) => x.node.type === node.type);
|
|
87
|
-
if (itemIndex >= 0) {
|
|
88
|
-
if (node.value === 0) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
// can cast because of the criterion used to find itemIndex
|
|
92
|
-
const otherValueNode = /** @type import('../parser').ValueExpression*/ (
|
|
93
|
-
collected[itemIndex].node
|
|
94
|
-
);
|
|
95
|
-
const { left: reducedNode, right: current } = convertNodesUnits(
|
|
96
|
-
otherValueNode,
|
|
97
|
-
node,
|
|
98
|
-
precision
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
if (collected[itemIndex].preOperator === '-') {
|
|
102
|
-
collected[itemIndex].preOperator = '+';
|
|
103
|
-
reducedNode.value *= -1;
|
|
104
|
-
}
|
|
105
|
-
if (preOperator === '+') {
|
|
106
|
-
reducedNode.value += current.value;
|
|
107
|
-
} else {
|
|
108
|
-
reducedNode.value -= current.value;
|
|
109
|
-
}
|
|
110
|
-
// make sure reducedNode.value >= 0
|
|
111
|
-
if (reducedNode.value >= 0) {
|
|
112
|
-
collected[itemIndex] = { node: reducedNode, preOperator: '+' };
|
|
113
|
-
} else {
|
|
114
|
-
reducedNode.value *= -1;
|
|
115
|
-
collected[itemIndex] = { node: reducedNode, preOperator: '-' };
|
|
116
|
-
}
|
|
117
|
-
} else {
|
|
118
|
-
// make sure node.value >= 0
|
|
119
|
-
if (node.value >= 0) {
|
|
120
|
-
collected.push({ node, preOperator });
|
|
121
|
-
} else {
|
|
122
|
-
node.value *= -1;
|
|
123
|
-
collected.push({ node, preOperator: flip(preOperator) });
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
} else if (node.type === 'MathExpression') {
|
|
127
|
-
if (isAddSubOperator(node.operator)) {
|
|
128
|
-
collectAddSubItems(preOperator, node.left, collected, precision);
|
|
129
|
-
const collectRightOperator =
|
|
130
|
-
preOperator === '-' ? flip(node.operator) : node.operator;
|
|
131
|
-
collectAddSubItems(
|
|
132
|
-
collectRightOperator,
|
|
133
|
-
node.right,
|
|
134
|
-
collected,
|
|
135
|
-
precision
|
|
136
|
-
);
|
|
137
|
-
} else {
|
|
138
|
-
// * or /
|
|
139
|
-
const reducedNode = reduce(node, precision);
|
|
140
|
-
// prevent infinite recursive call
|
|
141
|
-
if (
|
|
142
|
-
reducedNode.type !== 'MathExpression' ||
|
|
143
|
-
isAddSubOperator(reducedNode.operator)
|
|
144
|
-
) {
|
|
145
|
-
collectAddSubItems(preOperator, reducedNode, collected, precision);
|
|
146
|
-
} else {
|
|
147
|
-
collected.push({ node: reducedNode, preOperator });
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
} else if (node.type === 'ParenthesizedExpression') {
|
|
151
|
-
collectAddSubItems(preOperator, node.content, collected, precision);
|
|
152
|
-
} else {
|
|
153
|
-
collected.push({ node, preOperator });
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* @param {import('../parser').CalcNode} node
|
|
159
|
-
* @param {number} precision
|
|
160
|
-
*/
|
|
161
|
-
function reduceAddSubExpression(node, precision) {
|
|
162
|
-
/** @type Collectible[] */
|
|
163
|
-
const collected = [];
|
|
164
|
-
collectAddSubItems('+', node, collected, precision);
|
|
165
|
-
|
|
166
|
-
const withoutZeroItem = collected.filter(
|
|
167
|
-
(item) => !(isValueType(item.node) && item.node.value === 0)
|
|
168
|
-
);
|
|
169
|
-
const firstNonZeroItem = withoutZeroItem[0]; // could be undefined
|
|
170
|
-
|
|
171
|
-
// prevent producing "calc(-var(--a))" or "calc()"
|
|
172
|
-
// which is invalid css
|
|
173
|
-
if (
|
|
174
|
-
!firstNonZeroItem ||
|
|
175
|
-
(firstNonZeroItem.preOperator === '-' &&
|
|
176
|
-
!isValueType(firstNonZeroItem.node))
|
|
177
|
-
) {
|
|
178
|
-
const firstZeroItem = collected.find(
|
|
179
|
-
(item) => isValueType(item.node) && item.node.value === 0
|
|
180
|
-
);
|
|
181
|
-
if (firstZeroItem) {
|
|
182
|
-
withoutZeroItem.unshift(firstZeroItem);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// make sure the preOperator of the first item is +
|
|
187
|
-
if (
|
|
188
|
-
withoutZeroItem[0].preOperator === '-' &&
|
|
189
|
-
isValueType(withoutZeroItem[0].node)
|
|
190
|
-
) {
|
|
191
|
-
withoutZeroItem[0].node.value *= -1;
|
|
192
|
-
withoutZeroItem[0].preOperator = '+';
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
let root = withoutZeroItem[0].node;
|
|
196
|
-
for (let i = 1; i < withoutZeroItem.length; i++) {
|
|
197
|
-
root = {
|
|
198
|
-
type: 'MathExpression',
|
|
199
|
-
operator: withoutZeroItem[i].preOperator,
|
|
200
|
-
left: root,
|
|
201
|
-
right: withoutZeroItem[i].node,
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return root;
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* @param {import('../parser').MathExpression} node
|
|
209
|
-
*/
|
|
210
|
-
function reduceDivisionExpression(node) {
|
|
211
|
-
if (!isValueType(node.right)) {
|
|
212
|
-
return node;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (node.right.type !== 'Number') {
|
|
216
|
-
throw new Error(`Cannot divide by "${node.right.unit}", number expected`);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
return applyNumberDivision(node.left, node.right.value);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* apply (expr) / number
|
|
224
|
-
*
|
|
225
|
-
* @param {import('../parser').CalcNode} node
|
|
226
|
-
* @param {number} divisor
|
|
227
|
-
* @return {import('../parser').CalcNode}
|
|
228
|
-
*/
|
|
229
|
-
function applyNumberDivision(node, divisor) {
|
|
230
|
-
if (divisor === 0) {
|
|
231
|
-
throw new Error('Cannot divide by zero');
|
|
232
|
-
}
|
|
233
|
-
if (isValueType(node)) {
|
|
234
|
-
node.value /= divisor;
|
|
235
|
-
return node;
|
|
236
|
-
}
|
|
237
|
-
if (node.type === 'MathExpression' && isAddSubOperator(node.operator)) {
|
|
238
|
-
// turn (a + b) / num into a/num + b/num
|
|
239
|
-
// is good for further reduction
|
|
240
|
-
// checkout the test case
|
|
241
|
-
// "should reduce division before reducing additions"
|
|
242
|
-
return {
|
|
243
|
-
type: 'MathExpression',
|
|
244
|
-
operator: node.operator,
|
|
245
|
-
left: applyNumberDivision(node.left, divisor),
|
|
246
|
-
right: applyNumberDivision(node.right, divisor),
|
|
247
|
-
};
|
|
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
|
-
return {
|
|
253
|
-
type: 'MathExpression',
|
|
254
|
-
operator: '/',
|
|
255
|
-
left: node,
|
|
256
|
-
right: {
|
|
257
|
-
type: 'Number',
|
|
258
|
-
value: divisor,
|
|
259
|
-
},
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* @param {import('../parser').MathExpression} node
|
|
264
|
-
*/
|
|
265
|
-
function reduceMultiplicationExpression(node) {
|
|
266
|
-
// (expr) * number
|
|
267
|
-
if (node.right.type === 'Number') {
|
|
268
|
-
return applyNumberMultiplication(node.left, node.right.value);
|
|
269
|
-
}
|
|
270
|
-
// number * (expr)
|
|
271
|
-
if (node.left.type === 'Number') {
|
|
272
|
-
return applyNumberMultiplication(node.right, node.left.value);
|
|
273
|
-
}
|
|
274
|
-
return node;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* apply (expr) * number
|
|
279
|
-
* @param {number} multiplier
|
|
280
|
-
* @param {import('../parser').CalcNode} node
|
|
281
|
-
* @return {import('../parser').CalcNode}
|
|
282
|
-
*/
|
|
283
|
-
function applyNumberMultiplication(node, multiplier) {
|
|
284
|
-
if (isValueType(node)) {
|
|
285
|
-
node.value *= multiplier;
|
|
286
|
-
return node;
|
|
287
|
-
}
|
|
288
|
-
if (node.type === 'MathExpression' && isAddSubOperator(node.operator)) {
|
|
289
|
-
// turn (a + b) * num into a*num + b*num
|
|
290
|
-
// is good for further reduction
|
|
291
|
-
// checkout the test case
|
|
292
|
-
// "should reduce multiplication before reducing additions"
|
|
293
|
-
return {
|
|
294
|
-
type: 'MathExpression',
|
|
295
|
-
operator: node.operator,
|
|
296
|
-
left: applyNumberMultiplication(node.left, multiplier),
|
|
297
|
-
right: applyNumberMultiplication(node.right, multiplier),
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
// it is impossible to reduce it into a single value
|
|
301
|
-
// .e.g the node contains css variable
|
|
302
|
-
// so we just preserve the division and let browser do it
|
|
303
|
-
return {
|
|
304
|
-
type: 'MathExpression',
|
|
305
|
-
operator: '*',
|
|
306
|
-
left: node,
|
|
307
|
-
right: {
|
|
308
|
-
type: 'Number',
|
|
309
|
-
value: multiplier,
|
|
310
|
-
},
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* @param {import('../parser').ValueExpression} left
|
|
316
|
-
* @param {import('../parser').ValueExpression} right
|
|
317
|
-
* @param {number} precision
|
|
318
|
-
*/
|
|
319
|
-
function convertNodesUnits(left, right, precision) {
|
|
320
|
-
switch (left.type) {
|
|
321
|
-
case 'LengthValue':
|
|
322
|
-
case 'AngleValue':
|
|
323
|
-
case 'TimeValue':
|
|
324
|
-
case 'FrequencyValue':
|
|
325
|
-
case 'ResolutionValue':
|
|
326
|
-
if (right.type === left.type && right.unit && left.unit) {
|
|
327
|
-
const converted = convertUnit(
|
|
328
|
-
right.value,
|
|
329
|
-
right.unit,
|
|
330
|
-
left.unit,
|
|
331
|
-
precision
|
|
332
|
-
);
|
|
333
|
-
|
|
334
|
-
right = {
|
|
335
|
-
type: left.type,
|
|
336
|
-
value: converted,
|
|
337
|
-
unit: left.unit,
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
return { left, right };
|
|
342
|
-
default:
|
|
343
|
-
return { left, right };
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* @param {import('../parser').ParenthesizedExpression} node
|
|
349
|
-
*/
|
|
350
|
-
function includesNoCssProperties(node) {
|
|
351
|
-
return (
|
|
352
|
-
node.content.type !== 'Function' &&
|
|
353
|
-
(node.content.type !== 'MathExpression' ||
|
|
354
|
-
(node.content.right.type !== 'Function' &&
|
|
355
|
-
node.content.left.type !== 'Function'))
|
|
356
|
-
);
|
|
357
|
-
}
|
|
358
|
-
/**
|
|
359
|
-
* @param {import('../parser').CalcNode} node
|
|
360
|
-
* @param {number} precision
|
|
361
|
-
* @return {import('../parser').CalcNode}
|
|
362
|
-
*/
|
|
363
|
-
function reduce(node, precision) {
|
|
364
|
-
if (node.type === 'MathExpression') {
|
|
365
|
-
if (isAddSubOperator(node.operator)) {
|
|
366
|
-
// reduceAddSubExpression will call reduce recursively
|
|
367
|
-
return reduceAddSubExpression(node, precision);
|
|
368
|
-
}
|
|
369
|
-
node.left = reduce(node.left, precision);
|
|
370
|
-
node.right = reduce(node.right, precision);
|
|
371
|
-
switch (node.operator) {
|
|
372
|
-
case '/':
|
|
373
|
-
return reduceDivisionExpression(node);
|
|
374
|
-
case '*':
|
|
375
|
-
return reduceMultiplicationExpression(node);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
return node;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
if (node.type === 'ParenthesizedExpression') {
|
|
382
|
-
if (includesNoCssProperties(node)) {
|
|
383
|
-
return reduce(node.content, precision);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
return node;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
module.exports = reduce;
|
package/src/lib/stringifier.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const order = {
|
|
3
|
-
'*': 0,
|
|
4
|
-
'/': 0,
|
|
5
|
-
'+': 1,
|
|
6
|
-
'-': 1,
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @param {number} value
|
|
11
|
-
* @param {number | false} prec
|
|
12
|
-
*/
|
|
13
|
-
function round(value, prec) {
|
|
14
|
-
if (prec !== false) {
|
|
15
|
-
const precision = Math.pow(10, prec);
|
|
16
|
-
return Math.round(value * precision) / precision;
|
|
17
|
-
}
|
|
18
|
-
return value;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* @param {number | false} prec
|
|
23
|
-
* @param {import('../parser').CalcNode} node
|
|
24
|
-
*
|
|
25
|
-
* @return {string}
|
|
26
|
-
*/
|
|
27
|
-
function stringify(node, prec) {
|
|
28
|
-
switch (node.type) {
|
|
29
|
-
case 'MathExpression': {
|
|
30
|
-
const { left, right, operator: op } = node;
|
|
31
|
-
let str = '';
|
|
32
|
-
if (left.type === 'MathExpression' && order[op] < order[left.operator]) {
|
|
33
|
-
str += `(${stringify(left, prec)})`;
|
|
34
|
-
} else {
|
|
35
|
-
str += stringify(left, prec);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
str += order[op] ? ` ${node.operator} ` : node.operator;
|
|
39
|
-
|
|
40
|
-
if (
|
|
41
|
-
right.type === 'MathExpression' &&
|
|
42
|
-
order[op] < order[right.operator]
|
|
43
|
-
) {
|
|
44
|
-
str += `(${stringify(right, prec)})`;
|
|
45
|
-
} else {
|
|
46
|
-
str += stringify(right, prec);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return str;
|
|
50
|
-
}
|
|
51
|
-
case 'Number':
|
|
52
|
-
return round(node.value, prec).toString();
|
|
53
|
-
case 'Function':
|
|
54
|
-
return node.value.toString();
|
|
55
|
-
case 'ParenthesizedExpression':
|
|
56
|
-
return `(${stringify(node.content, prec)})`;
|
|
57
|
-
default:
|
|
58
|
-
return round(node.value, prec) + node.unit;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* @param {string} calc
|
|
64
|
-
* @param {import('../parser').CalcNode} node
|
|
65
|
-
* @param {string} originalValue
|
|
66
|
-
* @param {{precision: number | false, warnWhenCannotResolve: boolean}} options
|
|
67
|
-
* @param {import("postcss").Result} result
|
|
68
|
-
* @param {import("postcss").ChildNode} item
|
|
69
|
-
*
|
|
70
|
-
* @returns {string}
|
|
71
|
-
*/
|
|
72
|
-
module.exports = function (calc, node, originalValue, options, result, item) {
|
|
73
|
-
let str = stringify(node, options.precision);
|
|
74
|
-
|
|
75
|
-
const shouldPrintCalc =
|
|
76
|
-
node.type === 'MathExpression' || node.type === 'Function' ||
|
|
77
|
-
node.type === 'ParenthesizedExpression';
|
|
78
|
-
|
|
79
|
-
if (shouldPrintCalc) {
|
|
80
|
-
// if calc expression couldn't be resolved to a single value, re-wrap it as
|
|
81
|
-
// a calc()
|
|
82
|
-
if (node.type === 'ParenthesizedExpression') {
|
|
83
|
-
str = `${calc}${str}`;
|
|
84
|
-
} else {
|
|
85
|
-
str = `${calc}(${str})`;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// if the warnWhenCannotResolve option is on, inform the user that the calc
|
|
89
|
-
// expression could not be resolved to a single value
|
|
90
|
-
if (options.warnWhenCannotResolve) {
|
|
91
|
-
result.warn('Could not reduce expression: ' + originalValue, {
|
|
92
|
-
plugin: 'postcss-calc',
|
|
93
|
-
node: item,
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return str;
|
|
98
|
-
};
|