11import convertUnit from "./convertUnit" ;
22
3- function isValueType ( type ) {
4- switch ( type ) {
3+ /**
4+ * @param {import('../parser').CalcNode } node
5+ * @return {node is import('../parser').ValueExpression }
6+ */
7+ function isValueType ( node ) {
8+ switch ( node . type ) {
59 case 'LengthValue' :
610 case 'AngleValue' :
711 case 'TimeValue' :
@@ -22,22 +26,38 @@ function isValueType(type) {
2226 return false ;
2327}
2428
29+ /** @param {'-'|'+' } operator */
2530function flip ( operator ) {
2631 return operator === '+' ? '-' : '+' ;
2732}
2833
34+ /**
35+ * @param {string } operator
36+ * @returns {operator is '+'|'-' }
37+ */
2938function isAddSubOperator ( operator ) {
3039 return operator === '+' || operator === '-' ;
3140}
3241
42+ /**
43+ * @typedef {{preOperator: '+'|'-', node: import('../parser').CalcNode} } Collectible
44+ */
45+
46+ /**
47+ * @param {'+'|'-' } preOperator
48+ * @param {import('../parser').CalcNode } node
49+ * @param {Collectible[] } collected
50+ * @param {number } precision
51+ */
3352function collectAddSubItems ( preOperator , node , collected , precision ) {
3453 if ( ! isAddSubOperator ( preOperator ) ) { throw new Error ( `invalid operator ${ preOperator } ` ) ; }
35- const type = node . type ;
36- if ( isValueType ( type ) ) {
37- const itemIndex = collected . findIndex ( x => x . node . type === type ) ;
54+ if ( isValueType ( node ) ) {
55+ const itemIndex = collected . findIndex ( x => x . node . type === node . type ) ;
3856 if ( itemIndex >= 0 ) {
3957 if ( node . value === 0 ) { return ; }
40- const { left : reducedNode , right : current } = convertNodesUnits ( collected [ itemIndex ] . node , node , precision )
58+ // can cast because of the criterion used to find itemIndex
59+ const otherValueNode = /** @type import('../parser').ValueExpression*/ ( collected [ itemIndex ] . node ) ;
60+ const { left : reducedNode , right : current } = convertNodesUnits ( otherValueNode , node , precision )
4161
4262 if ( collected [ itemIndex ] . preOperator === '-' ) {
4363 collected [ itemIndex ] . preOperator = '+' ;
@@ -64,7 +84,7 @@ function collectAddSubItems(preOperator, node, collected, precision) {
6484 collected . push ( { node, preOperator : flip ( preOperator ) } ) ;
6585 }
6686 }
67- } else if ( type === "MathExpression" ) {
87+ } else if ( node . type === "MathExpression" ) {
6888 if ( isAddSubOperator ( node . operator ) ) {
6989 collectAddSubItems ( preOperator , node . left , collected , precision ) ;
7090 const collectRightOperator = preOperator === '-' ? flip ( node . operator ) : node . operator ;
@@ -85,27 +105,33 @@ function collectAddSubItems(preOperator, node, collected, precision) {
85105 }
86106}
87107
88-
108+ /**
109+ * @param {import('../parser').CalcNode } node
110+ * @param {number } precision
111+ */
89112function reduceAddSubExpression ( node , precision ) {
113+ /** @type Collectible[] */
90114 const collected = [ ] ;
91115 collectAddSubItems ( '+' , node , collected , precision ) ;
92116
93- const withoutZeroItem = collected . filter ( ( item ) => ! ( isValueType ( item . node . type ) && item . node . value === 0 ) ) ;
117+ const withoutZeroItem = collected . filter ( ( item ) => ! ( isValueType ( item . node ) && item . node . value === 0 ) ) ;
94118 const firstNonZeroItem = withoutZeroItem [ 0 ] ; // could be undefined
95119
96120 // prevent producing "calc(-var(--a))" or "calc()"
97121 // which is invalid css
98122 if ( ! firstNonZeroItem ||
99123 firstNonZeroItem . preOperator === '-' &&
100- ! isValueType ( firstNonZeroItem . node . type ) ) {
124+ ! isValueType ( firstNonZeroItem . node ) ) {
101125 const firstZeroItem = collected . find ( ( item ) =>
102- isValueType ( item . node . type ) && item . node . value === 0 ) ;
103- withoutZeroItem . unshift ( firstZeroItem )
126+ isValueType ( item . node ) && item . node . value === 0 ) ;
127+ if ( firstZeroItem ) {
128+ withoutZeroItem . unshift ( firstZeroItem )
129+ }
104130 }
105131
106132 // make sure the preOperator of the first item is +
107133 if ( withoutZeroItem [ 0 ] . preOperator === '-' &&
108- isValueType ( withoutZeroItem [ 0 ] . node . type ) ) {
134+ isValueType ( withoutZeroItem [ 0 ] . node ) ) {
109135 withoutZeroItem [ 0 ] . node . value *= - 1 ;
110136 withoutZeroItem [ 0 ] . preOperator = '+' ;
111137 }
@@ -122,9 +148,11 @@ function reduceAddSubExpression(node, precision) {
122148
123149 return root ;
124150}
125-
151+ /**
152+ * @param {import('../parser').MathExpression } node
153+ */
126154function reduceDivisionExpression ( node ) {
127- if ( ! isValueType ( node . right . type ) ) {
155+ if ( ! isValueType ( node . right ) ) {
128156 return node ;
129157 }
130158
@@ -135,12 +163,18 @@ function reduceDivisionExpression(node) {
135163 return applyNumberDivision ( node . left , node . right . value )
136164}
137165
138- // apply (expr) / number
166+ /**
167+ * apply (expr) / number
168+ *
169+ * @param {import('../parser').CalcNode } node
170+ * @param {number } divisor
171+ * @return {import('../parser').CalcNode }
172+ */
139173function applyNumberDivision ( node , divisor ) {
140174 if ( divisor === 0 ) {
141175 throw new Error ( 'Cannot divide by zero' ) ;
142176 }
143- if ( isValueType ( node . type ) ) {
177+ if ( isValueType ( node ) ) {
144178 node . value /= divisor ;
145179 return node ;
146180 }
@@ -169,7 +203,9 @@ function applyNumberDivision(node, divisor) {
169203 }
170204 }
171205}
172-
206+ /**
207+ * @param {import('../parser').MathExpression } node
208+ */
173209function reduceMultiplicationExpression ( node ) {
174210 // (expr) * number
175211 if ( node . right . type === 'Number' ) {
@@ -182,9 +218,14 @@ function reduceMultiplicationExpression(node) {
182218 return node ;
183219}
184220
185- // apply (expr) / number
221+ /**
222+ * apply (expr) * number
223+ * @param {number } multiplier
224+ * @param {import('../parser').CalcNode } node
225+ * @return {import('../parser').CalcNode }
226+ */
186227function applyNumberMultiplication ( node , multiplier ) {
187- if ( isValueType ( node . type ) ) {
228+ if ( isValueType ( node ) ) {
188229 node . value *= multiplier ;
189230 return node ;
190231 }
@@ -214,6 +255,11 @@ function applyNumberMultiplication(node, multiplier) {
214255 }
215256}
216257
258+ /**
259+ * @param {import('../parser').ValueExpression } left
260+ * @param {import('../parser').ValueExpression } right
261+ * @param {number } precision
262+ */
217263function convertNodesUnits ( left , right , precision ) {
218264 switch ( left . type ) {
219265 case 'LengthValue' :
@@ -237,6 +283,10 @@ function convertNodesUnits(left, right, precision) {
237283 }
238284}
239285
286+ /**
287+ * @param {import('../parser').CalcNode } node
288+ * @param {number } precision
289+ */
240290function reduce ( node , precision ) {
241291 if ( node . type === "MathExpression" ) {
242292 if ( isAddSubOperator ( node . operator ) ) {
@@ -247,9 +297,9 @@ function reduce(node, precision) {
247297 node . right = reduce ( node . right , precision ) ;
248298 switch ( node . operator ) {
249299 case "/" :
250- return reduceDivisionExpression ( node , precision ) ;
300+ return reduceDivisionExpression ( node ) ;
251301 case "*" :
252- return reduceMultiplicationExpression ( node , precision ) ;
302+ return reduceMultiplicationExpression ( node ) ;
253303 }
254304
255305 return node ;
0 commit comments