Skip to content

Commit 83becf4

Browse files
Allow rounding option
1 parent 1738250 commit 83becf4

File tree

6 files changed

+70
-46
lines changed

6 files changed

+70
-46
lines changed

README.md

+13
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,19 @@ var out = postcss()
116116
.css
117117
```
118118

119+
#### `allowRounding` (default: `true`)
120+
121+
With this variable set `true`, `calc(100% / 3)` will output `33.33333%` (with `precision: 5`). If it is set `false` it will remain `calc(100% / 3)`.
122+
123+
Another example with `allowRounding: false`: `calc(900% / 16)` will output `56.25%` with `precision: 5` (because `calc(900% / 16)` == `56.25%`), but `calc(900% / 16)` with `precision: 0` (because `calc(900% / 16)` != `56%`).
124+
125+
```js
126+
var out = postcss()
127+
.use(calc({allowRounding: false}))
128+
.process(css)
129+
.css
130+
```
131+
119132
#### `warnWhenCannotResolve` (default: `false`)
120133

121134
Adds warnings when calc() are not reduced to a single value.

src/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import transform from './lib/transform';
55
export default plugin('postcss-calc', (opts) => {
66
const options = Object.assign({
77
precision: 5,
8+
allowRounding: true,
89
preserve: false,
910
warnWhenCannotResolve: false,
1011
mediaQueries: false,

src/lib/convert.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
import convertUnits from 'css-unit-converter';
22

3-
function convertNodes(left, right, precision) {
3+
function convertNodes(left, right, options) {
44
switch (left.type) {
55
case 'LengthValue':
66
case 'AngleValue':
77
case 'TimeValue':
88
case 'FrequencyValue':
99
case 'ResolutionValue':
10-
return convertAbsoluteLength(left, right, precision);
10+
return convertAbsoluteLength(left, right, options);
1111
default:
1212
return { left, right };
1313
}
1414
}
1515

16-
function convertAbsoluteLength(left, right, precision) {
16+
function convertAbsoluteLength(left, right, options) {
1717
if (right.type === left.type) {
1818
right = {
1919
type: left.type,
20-
value: convertUnits(right.value, right.unit, left.unit, precision),
20+
value: convertUnits(right.value, right.unit, left.unit, options),
2121
unit: left.unit,
2222
};
2323
}

src/lib/reducer.js

+28-28
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import convert from './convert';
22

3-
function reduce(node, precision) {
4-
if (node.type === "MathExpression")
5-
return reduceMathExpression(node, precision);
3+
function reduce(node, options) {
4+
if (node.type === "MathExpression")
5+
return reduceMathExpression(node, options);
66

77
return node;
88
}
@@ -33,10 +33,10 @@ function isValueType(type) {
3333
return false;
3434
}
3535

36-
function convertMathExpression(node, precision) {
37-
let nodes = convert(node.left, node.right, precision);
38-
let left = reduce(nodes.left, precision);
39-
let right = reduce(nodes.right, precision);
36+
function convertMathExpression(node, options) {
37+
let nodes = convert(node.left, node.right, options);
38+
let left = reduce(nodes.left, options);
39+
let right = reduce(nodes.right, options);
4040

4141
if (left.type === "MathExpression" && right.type === "MathExpression") {
4242

@@ -46,13 +46,13 @@ function convertMathExpression(node, precision) {
4646
(left.operator === '+' && right.operator === '-'))) {
4747

4848
if (isEqual(left.right, right.right))
49-
nodes = convert(left.left, right.left, precision);
49+
nodes = convert(left.left, right.left, options);
5050

5151
else if (isEqual(left.right, right.left))
52-
nodes = convert(left.left, right.right, precision);
52+
nodes = convert(left.left, right.right, options);
5353

54-
left = reduce(nodes.left, precision);
55-
right = reduce(nodes.right, precision);
54+
left = reduce(nodes.left, options);
55+
right = reduce(nodes.right, options);
5656

5757
}
5858
}
@@ -69,14 +69,14 @@ function flip(operator) {
6969
function flipValue(node) {
7070
if (isValueType(node.type))
7171
node.value = -node.value;
72-
else if (node.type == 'MathExpression') {
72+
else if (node.type === 'MathExpression') {
7373
node.left = flipValue(node.left);
7474
node.right = flipValue(node.right);
7575
}
7676
return node;
7777
}
7878

79-
function reduceAddSubExpression(node, precision) {
79+
function reduceAddSubExpression(node, options) {
8080
const {left, right, operator: op} = node;
8181

8282
if (left.type === 'Function' || right.type === 'Function')
@@ -122,10 +122,10 @@ function reduceAddSubExpression(node, precision) {
122122
operator: op,
123123
left: left,
124124
right: right.left
125-
}, precision);
125+
}, options);
126126
node.right = right.right;
127127
node.operator = op === '-' ? flip(right.operator) : right.operator;
128-
return reduce(node, precision);
128+
return reduce(node, options);
129129
}
130130
// value + (something + value) => (value + value) + something
131131
// value + (something - value) => (value - value) + something
@@ -138,15 +138,15 @@ function reduceAddSubExpression(node, precision) {
138138
operator: op === '-' ? flip(right.operator) : right.operator,
139139
left: left,
140140
right: right.right
141-
}, precision);
141+
}, options);
142142
node.right = right.left;
143-
return reduce(node, precision);
143+
return reduce(node, options);
144144
}
145145
// value - (something + something) => value - something - something
146146
else if (op === '-' && right.operator === '+') {
147147
node = Object.assign({ }, node);
148148
node.right.operator = '-';
149-
return reduce(node, precision);
149+
return reduce(node, options);
150150
}
151151
}
152152

@@ -167,8 +167,8 @@ function reduceAddSubExpression(node, precision) {
167167
operator: op,
168168
left: left.left,
169169
right: right
170-
}, precision);
171-
return reduce(node, precision);
170+
}, options);
171+
return reduce(node, options);
172172
}
173173
// (something + value) + value => something + (value + value)
174174
// (something - value1) + value2 => something - (value2 - value1)
@@ -182,7 +182,7 @@ function reduceAddSubExpression(node, precision) {
182182
operator: flip(op),
183183
left: left.right,
184184
right: right
185-
}, precision);
185+
}, options);
186186
if (node.right.value && node.right.value < 0) {
187187
node.right.value = Math.abs(node.right.value);
188188
node.operator = '+';
@@ -196,16 +196,16 @@ function reduceAddSubExpression(node, precision) {
196196
operator: op,
197197
left: left.right,
198198
right: right
199-
}, precision);
199+
}, options);
200200
}
201201
if (node.right.value < 0) {
202202
node.right.value *= -1;
203203
node.operator = node.operator === '-' ? '+' : '-';
204204
}
205-
return reduce(node, precision);
205+
return reduce(node, options);
206206
}
207207
}
208-
208+
209209
if (
210210
left.type === 'MathExpression' && right.type === 'MathExpression' &&
211211
op === '-' && right.operator === '-'
@@ -269,15 +269,15 @@ function reduceMultiplicationExpression(node) {
269269
return node;
270270
}
271271

272-
function reduceMathExpression(node, precision) {
273-
node = convertMathExpression(node, precision);
272+
function reduceMathExpression(node, options) {
273+
node = convertMathExpression(node, options);
274274

275275
switch (node.operator) {
276276
case "+":
277277
case "-":
278-
return reduceAddSubExpression(node, precision);
278+
return reduceAddSubExpression(node, options);
279279
case "/":
280-
return reduceDivisionExpression(node, precision);
280+
return reduceDivisionExpression(node, options);
281281
case "*":
282282
return reduceMultiplicationExpression(node);
283283
}

src/lib/stringifier.js

+11-11
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,40 @@ const order = {
55
"-": 1,
66
};
77

8-
function round(value, prec) {
9-
if (prec !== false) {
10-
const precision = Math.pow(10, prec);
8+
function round(value, options) {
9+
if (options.precision !== false && options.allowRounding) {
10+
const precision = Math.pow(10, options.precision);
1111
return Math.round(value * precision) / precision;
1212
}
1313
return value;
1414
}
1515

16-
function stringify(node, prec) {
16+
function stringify(node, options) {
1717
switch (node.type) {
1818
case "MathExpression": {
1919
const {left, right, operator: op} = node;
2020
let str = "";
2121

2222
if (left.type === 'MathExpression' && order[op] < order[left.operator])
23-
str += `(${stringify(left, prec)})`;
23+
str += `(${stringify(left, options)})`;
2424
else
25-
str += stringify(left, prec);
25+
str += stringify(left, options);
2626

2727
str += order[op] ? ` ${node.operator} ` : node.operator;
2828

2929
if (right.type === 'MathExpression' && order[op] < order[right.operator])
30-
str += `(${stringify(right, prec)})`;
30+
str += `(${stringify(right, options)})`;
3131
else
32-
str += stringify(right, prec);
32+
str += stringify(right, options);
3333

3434
return str;
3535
}
3636
case "Value":
37-
return round(node.value, prec);
37+
return round(node.value, options);
3838
case 'Function':
3939
return node.value;
4040
default:
41-
return round(node.value, prec) + node.unit;
41+
return round(node.value, options) + node.unit;
4242
}
4343
}
4444

@@ -50,7 +50,7 @@ export default function (
5050
result,
5151
item
5252
) {
53-
let str = stringify(node, options.precision);
53+
let str = stringify(node, options);
5454

5555
if (node.type === "MathExpression") {
5656
// if calc expression couldn't be resolved to a single value, re-wrap it as

src/lib/transform.js

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import selectorParser from 'postcss-selector-parser';
2-
import valueParser from 'postcss-value-parser';
2+
import valueParser from 'postcss-value-parser';
33

44
// eslint-disable-next-line import/no-unresolved
55
import { parser } from '../parser';
@@ -18,10 +18,20 @@ function transformValue(value, options, result, item) {
1818
// stringify calc expression and produce an AST
1919
const contents = valueParser.stringify(node.nodes);
2020
const ast = parser.parse(contents);
21-
21+
2222
// reduce AST to its simplest form, that is, either to a single value
2323
// or a simplified calc expression
24-
const reducedAst = reducer(ast, options.precision, item);
24+
const reducedAst = reducer(ast, options);
25+
26+
// prevent rounding if not allowed
27+
if (!options.allowRounding && reducedAst.type !== "MathExpression") {
28+
const precision = Math.pow(10, options.precision);
29+
30+
// Check if the result is rounded
31+
if (Math.round(reducedAst.value * precision) / precision !== reducedAst.value){
32+
return node;
33+
}
34+
}
2535

2636
// stringify AST and write it back
2737
node.type = 'word';

0 commit comments

Comments
 (0)