postcss-calc
Advanced tools
Comparing version 7.0.4 to 7.0.5
@@ -0,1 +1,5 @@ | ||
# 7.0.5 | ||
- Fixed: reduction | ||
# 7.0.4 | ||
@@ -2,0 +6,0 @@ |
@@ -12,6 +12,2 @@ "use strict"; | ||
function isEqual(left, right) { | ||
return left.type === right.type && left.value === right.value; | ||
} | ||
function isValueType(type) { | ||
@@ -44,200 +40,128 @@ switch (type) { | ||
function flipValue(node) { | ||
if (isValueType(node.type)) { | ||
node.value = -node.value; | ||
} else if (node.type === 'MathExpression') { | ||
if (node.operator === '*' || node.operator === '/') { | ||
node.left = flipValue(node.left); | ||
} else { | ||
node.left = flipValue(node.left); | ||
node.right = flipValue(node.right); | ||
} | ||
} | ||
return node; | ||
function isAddSubOperator(operator) { | ||
return operator === '+' || operator === '-'; | ||
} | ||
function reduceAddSubExpression(node, precision) { | ||
// something + 0 => something | ||
// something - 0 => something | ||
if (isValueType(node.right.type) && node.right.value === 0) { | ||
return node.left; | ||
} // 0 + something => something | ||
function collectAddSubItems(preOperator, node, collected, precision) { | ||
if (!isAddSubOperator(preOperator)) { | ||
throw new Error(`invalid operator ${preOperator}`); | ||
} | ||
var type = node.type; | ||
if (isValueType(node.left.type) && node.left.value === 0 && node.operator === "+") { | ||
return node.right; | ||
} // 0 - something => -something | ||
if (isValueType(type)) { | ||
var itemIndex = collected.findIndex(function (x) { | ||
return x.node.type === type; | ||
}); | ||
if (itemIndex >= 0) { | ||
if (node.value === 0) { | ||
return; | ||
} | ||
if (isValueType(node.left.type) && node.left.value === 0 && node.operator === "-" && !isCSSFunction(node.right)) { | ||
return flipValue(node.right); | ||
} // value + value | ||
// value - value | ||
var _covertNodesUnits = covertNodesUnits(collected[itemIndex].node, node, precision), | ||
reducedNode = _covertNodesUnits.left, | ||
current = _covertNodesUnits.right; | ||
if (collected[itemIndex].preOperator === '-') { | ||
collected[itemIndex].preOperator = '+'; | ||
reducedNode.value *= -1; | ||
} | ||
if (isValueType(node.left.type) && node.left.type === node.right.type) { | ||
var operator = node.operator; | ||
if (preOperator === "+") { | ||
reducedNode.value += current.value; | ||
} else { | ||
reducedNode.value -= current.value; | ||
} // make sure reducedNode.value >= 0 | ||
var _covertNodesUnits = covertNodesUnits(node.left, node.right, precision), | ||
left = _covertNodesUnits.left, | ||
right = _covertNodesUnits.right; | ||
if (operator === "+") { | ||
left.value += right.value; | ||
if (reducedNode.value >= 0) { | ||
collected[itemIndex] = { | ||
node: reducedNode, | ||
preOperator: '+' | ||
}; | ||
} else { | ||
reducedNode.value *= -1; | ||
collected[itemIndex] = { | ||
node: reducedNode, | ||
preOperator: '-' | ||
}; | ||
} | ||
} else { | ||
left.value -= right.value; | ||
} | ||
return left; | ||
} // value <op> (expr) | ||
if (node.right.type === 'MathExpression' && (node.right.operator === '+' || node.right.operator === '-')) { | ||
// something - (something + something) => something - something - something | ||
// something - (something - something) => something - something + something | ||
if ((node.right.operator === '+' || node.right.operator === '-') && node.operator === '-') { | ||
node.right.operator = flip(node.right.operator); | ||
} | ||
if (isValueType(node.left.type)) { | ||
// value + (value + something) => value + something | ||
// value + (value - something) => value - something | ||
// value - (value + something) => value - something | ||
// value - (value - something) => value + something | ||
if (node.left.type === node.right.left.type) { | ||
var _left = node.left, | ||
_operator = node.operator, | ||
_right = node.right; | ||
node.left = reduce({ | ||
type: 'MathExpression', | ||
operator: _operator, | ||
left: _left, | ||
right: _right.left | ||
// make sure node.value >= 0 | ||
if (node.value >= 0) { | ||
collected.push({ | ||
node, | ||
preOperator | ||
}); | ||
node.operator = _right.operator; | ||
node.right = _right.right; | ||
return reduce(node, precision); | ||
} // something + (something + value) => dimension + something | ||
// something + (something - value) => dimension + something | ||
// something - (something + value) => dimension - something | ||
// something - (something - value) => dimension - something | ||
if (node.left.type === node.right.right.type) { | ||
var _left2 = node.left, | ||
_right2 = node.right; | ||
node.left = reduce({ | ||
type: 'MathExpression', | ||
operator: _right2.operator, | ||
left: _left2, | ||
right: _right2.right | ||
} else { | ||
node.value *= -1; | ||
collected.push({ | ||
node, | ||
preOperator: flip(preOperator) | ||
}); | ||
node.right = _right2.left; | ||
return reduce(node, precision); | ||
} | ||
} | ||
} // (expr) <op> value | ||
} else if (type === "MathExpression") { | ||
if (isAddSubOperator(node.operator)) { | ||
collectAddSubItems(preOperator, node.left, collected, precision); | ||
var collectRightOperator = preOperator === '-' ? flip(node.operator) : node.operator; | ||
collectAddSubItems(collectRightOperator, node.right, collected, precision); | ||
} else { | ||
// * or / | ||
var _reducedNode = reduce(node, precision); // prevent infinite recursive call | ||
if (node.left.type === 'MathExpression' && (node.left.operator === '+' || node.left.operator === '-') && isValueType(node.right.type)) { | ||
// (value + something) + value => value + something | ||
// (value - something) + value => value - something | ||
// (value + something) - value => value + something | ||
// (value - something) - value => value - something | ||
if (node.right.type === node.left.left.type) { | ||
var _left3 = node.left, | ||
_operator2 = node.operator, | ||
_right3 = node.right; | ||
_left3.left = reduce({ | ||
type: 'MathExpression', | ||
operator: _operator2, | ||
left: _left3.left, | ||
right: _right3 | ||
}, precision); | ||
return reduce(_left3, precision); | ||
} // (something + dimension) + dimension => something + dimension | ||
// (something - dimension) + dimension => something - dimension | ||
// (something + dimension) - dimension => something + dimension | ||
// (something - dimension) - dimension => something - dimension | ||
if (node.right.type === node.left.right.type) { | ||
var _left4 = node.left, | ||
_operator3 = node.operator, | ||
_right4 = node.right; | ||
if (_left4.operator === '-') { | ||
_left4.operator = _operator3 === '-' ? '-' : '+'; | ||
_left4.right = reduce({ | ||
type: 'MathExpression', | ||
operator: _operator3 === '-' ? '+' : '-', | ||
left: _right4, | ||
right: _left4.right | ||
}, precision); | ||
if (_reducedNode.type !== "MathExpression" || isAddSubOperator(_reducedNode.operator)) { | ||
collectAddSubItems(preOperator, _reducedNode, collected, precision); | ||
} else { | ||
_left4.right = reduce({ | ||
type: 'MathExpression', | ||
operator: _operator3, | ||
left: _left4.right, | ||
right: _right4 | ||
}, precision); | ||
collected.push({ | ||
node: _reducedNode, | ||
preOperator | ||
}); | ||
} | ||
if (_left4.right.value < 0) { | ||
_left4.right.value *= -1; | ||
_left4.operator = _left4.operator === '-' ? '+' : '-'; | ||
} | ||
_left4.parenthesized = node.parenthesized; | ||
return reduce(_left4, precision); | ||
} | ||
} // (expr) + (expr) => number | ||
// (expr) - (expr) => number | ||
} else { | ||
collected.push({ | ||
node, | ||
preOperator | ||
}); | ||
} | ||
} | ||
function reduceAddSubExpression(node, precision) { | ||
var collected = []; | ||
collectAddSubItems('+', node, collected, precision); | ||
var withoutZeroItem = collected.filter(function (item) { | ||
return !(isValueType(item.node.type) && item.node.value === 0); | ||
}); | ||
var firstNonZeroItem = withoutZeroItem[0]; // could be undefined | ||
// prevent producing "calc(-var(--a))" or "calc()" | ||
// which is invalid css | ||
if (node.right.type === 'MathExpression' && node.left.type === 'MathExpression') { | ||
if (node.left.operator === "-" && node.right.operator === "+" || node.right.operator === "-" && node.left.operator === "+") { | ||
// (expr1 - something) + (expr2 + something) => expr1 + expr2 | ||
// (expr1 + something) + (expr2 - something) => expr1 + expr2 | ||
// (expr1 - something) - (expr2 + something) => expr1 + expr2 | ||
// (expr1 + something) - (expr2 - something) => expr1 + expr2 | ||
if (isEqual(node.left.right, node.right.right)) { | ||
var newNodes = covertNodesUnits(node.left.left, node.right.left, precision); | ||
node.left = newNodes.left; | ||
node.right = newNodes.right; | ||
return reduce(node, precision); | ||
} // (expr1 - something) + (something + expr2) => expr1 + expr2 | ||
// (expr1 + something) + (something - expr2) => expr1 + expr2 | ||
// (expr1 - something) - (something + expr2) => expr1 + expr2 | ||
// (expr1 + something) - (something - expr2) => expr1 + expr2 | ||
if (!firstNonZeroItem || firstNonZeroItem.preOperator === '-' && !isValueType(firstNonZeroItem.node.type)) { | ||
var firstZeroItem = collected.find(function (item) { | ||
return isValueType(item.node.type) && item.node.value === 0; | ||
}); | ||
withoutZeroItem.unshift(firstZeroItem); | ||
} // make sure the preOperator of the first item is + | ||
if (isEqual(node.left.right, node.right.left)) { | ||
var _newNodes = covertNodesUnits(node.left.left, node.right.right, precision); | ||
if (withoutZeroItem[0].preOperator === '-' && isValueType(withoutZeroItem[0].node.type)) { | ||
withoutZeroItem[0].node.value *= -1; | ||
withoutZeroItem[0].preOperator = '+'; | ||
} | ||
node.left = _newNodes.left; | ||
node.right = _newNodes.right; | ||
return reduce(node, precision); | ||
} // (expr1 / something) + (expr2 / something) => (expr1 + expr2) / something | ||
// (expr1 * something) + (expr2 * something) => (expr1 + expr2) * something | ||
// (expr1 / something) - (expr2 / something) => (expr1 - expr2) / something | ||
// (expr1 * something) - (expr2 * something) => (expr1 - expr2) * something | ||
var root = withoutZeroItem[0].node; | ||
} else if ((node.left.operator === "/" || node.left.operator === "*") && node.left.operator === node.right.operator && isEqual(node.left.right, node.right.right)) { | ||
return reduce({ | ||
type: "MathExpression", | ||
operator: node.left.operator, | ||
left: { | ||
type: "MathExpression", | ||
operator: node.operator, | ||
left: node.left.left, | ||
right: node.right.left | ||
}, | ||
right: node.left.right | ||
}, precision); | ||
} | ||
for (var i = 1; i < withoutZeroItem.length; i++) { | ||
root = { | ||
type: 'MathExpression', | ||
operator: withoutZeroItem[i].preOperator, | ||
left: root, | ||
right: withoutZeroItem[i].node | ||
}; | ||
} | ||
return node; | ||
return root; | ||
} | ||
@@ -254,13 +178,41 @@ | ||
if (node.right.value === 0) { | ||
throw new Error('Cannot divide by zero'); | ||
} // something / value | ||
return applyNumberDivision(node.left, node.right.value); | ||
} // apply (expr) / number | ||
if (isValueType(node.left.type)) { | ||
node.left.value /= node.right.value; | ||
return node.left; | ||
function applyNumberDivision(node, divisor) { | ||
if (divisor === 0) { | ||
throw new Error('Cannot divide by zero'); | ||
} | ||
return node; | ||
if (isValueType(node.type)) { | ||
node.value /= divisor; | ||
return node; | ||
} | ||
if (node.type === "MathExpression" && isAddSubOperator(node.operator)) { | ||
// turn (a + b) / num into a/num + b/num | ||
// is good for further reduction | ||
// checkout the test case | ||
// "should reduce division before reducing additions" | ||
return { | ||
type: "MathExpression", | ||
operator: node.operator, | ||
left: applyNumberDivision(node.left, divisor), | ||
right: applyNumberDivision(node.right, divisor) | ||
}; | ||
} // it is impossible to reduce it into a single value | ||
// .e.g the node contains css variable | ||
// so we just preserve the division and let browser do it | ||
return { | ||
type: "MathExpression", | ||
operator: '/', | ||
left: node, | ||
right: { | ||
type: "Number", | ||
value: divisor | ||
} | ||
}; | ||
} | ||
@@ -270,32 +222,46 @@ | ||
// (expr) * number | ||
if (node.left.type === 'MathExpression' && node.right.type === 'Number') { | ||
if (isValueType(node.left.left.type) && isValueType(node.left.right.type)) { | ||
node.left.left.value *= node.right.value; | ||
node.left.right.value *= node.right.value; | ||
return node.left; | ||
} | ||
} // something * number | ||
if (node.right.type === 'Number') { | ||
return applyNumberMultiplication(node.left, node.right.value); | ||
} // number * (expr) | ||
if (isValueType(node.left.type) && node.right.type === 'Number') { | ||
node.left.value *= node.right.value; | ||
return node.left; | ||
} // number * (expr) | ||
if (node.left.type === 'Number') { | ||
return applyNumberMultiplication(node.right, node.left.value); | ||
} | ||
return node; | ||
} // apply (expr) / number | ||
if (node.left.type === 'Number' && node.right.type === 'MathExpression') { | ||
if (isValueType(node.right.left.type) && isValueType(node.right.right.type)) { | ||
node.right.left.value *= node.left.value; | ||
node.right.right.value *= node.left.value; | ||
return node.right; | ||
} | ||
} // number * something | ||
if (node.left.type === 'Number' && isValueType(node.right.type)) { | ||
node.right.value *= node.left.value; | ||
return node.right; | ||
function applyNumberMultiplication(node, multiplier) { | ||
if (isValueType(node.type)) { | ||
node.value *= multiplier; | ||
return node; | ||
} | ||
return node; | ||
if (node.type === "MathExpression" && isAddSubOperator(node.operator)) { | ||
// turn (a + b) * num into a*num + b*num | ||
// is good for further reduction | ||
// checkout the test case | ||
// "should reduce multiplication before reducing additions" | ||
return { | ||
type: "MathExpression", | ||
operator: node.operator, | ||
left: applyNumberMultiplication(node.left, multiplier), | ||
right: applyNumberMultiplication(node.right, multiplier) | ||
}; | ||
} // it is impossible to reduce it into a single value | ||
// .e.g the node contains css variable | ||
// so we just preserve the division and let browser do it | ||
return { | ||
type: "MathExpression", | ||
operator: '*', | ||
left: node, | ||
right: { | ||
type: "Number", | ||
value: multiplier | ||
} | ||
}; | ||
} | ||
@@ -334,2 +300,7 @@ | ||
if (node.type === "MathExpression") { | ||
if (isAddSubOperator(node.operator)) { | ||
// reduceAddSubExpression will call reduce recursively | ||
return reduceAddSubExpression(node, precision); | ||
} | ||
node.left = reduce(node.left, precision); | ||
@@ -339,6 +310,2 @@ node.right = reduce(node.right, precision); | ||
switch (node.operator) { | ||
case "+": | ||
case "-": | ||
return reduceAddSubExpression(node, precision); | ||
case "/": | ||
@@ -359,15 +326,2 @@ return reduceDivisionExpression(node, precision); | ||
exports.default = _default; | ||
function isCSSFunction(node) { | ||
if (node.type === "Function") { | ||
return true; | ||
} | ||
if (node.type === "MathExpression") { | ||
return isCSSFunction(node.left) || isCSSFunction(node.right); | ||
} | ||
return false; | ||
} | ||
module.exports = exports.default; |
{ | ||
"name": "postcss-calc", | ||
"version": "7.0.4", | ||
"version": "7.0.5", | ||
"description": "PostCSS plugin to reduce calc()", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
33
1
1
1
3
150688
3737