Skip to content

Commit c81442b

Browse files
committed
feat: add types
1 parent 133260a commit c81442b

14 files changed

+279
-35
lines changed

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@
1010
"calc"
1111
],
1212
"main": "dist/index.js",
13+
"types": "types/index.d.ts",
1314
"files": [
1415
"dist",
16+
"types",
1517
"LICENSE"
1618
],
1719
"scripts": {
18-
"prepare": "pnpm run build",
20+
"prepare": "pnpm run build && tsc",
1921
"build": "rimraf dist && babel src --out-dir dist --ignore src/__tests__/**/*.js && jison src/parser.jison -o dist/parser.js",
20-
"lint": "eslint src",
22+
"lint": "eslint src && tsc",
2123
"pretest": "pnpm run build",
2224
"test": "uvu -r @babel/register src/__tests__"
2325
},
@@ -44,6 +46,7 @@
4446
"jison-gho": "^0.6.1-216",
4547
"postcss": "^8.2.2",
4648
"rimraf": "^3.0.2",
49+
"typescript": "^4.5.4",
4750
"uvu": "^0.5.2"
4851
},
4952
"dependencies": {

pnpm-lock.yaml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/index.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import transform from './lib/transform';
22

3+
/**
4+
* @param {{precision?: number | false,
5+
* preserve?: boolean,
6+
* warnWhenCannotResolve?: boolean,
7+
* mediaQueries?: boolean,
8+
* selectors?: boolean}} opts
9+
*/
310
function pluginCreator(opts) {
411
const options = Object.assign({
512
precision: 5,
@@ -11,10 +18,13 @@ function pluginCreator(opts) {
1118

1219
return {
1320
postcssPlugin: 'postcss-calc',
21+
/**
22+
* @param {import('postcss').Root} css
23+
* @param {{result: import('postcss').Result}} helpers
24+
*/
1425
OnceExit(css, { result }) {
1526
css.walk(node => {
1627
const { type } = node;
17-
1828
if (type === 'decl') {
1929
transform(node, "value", options, result);
2030
}

src/lib/convertUnit.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
/**
2+
* @type {{[key:string]: {[key:string]: number}}}
3+
*/
14
const conversions = {
25
// Absolute length units
36
'px': {
@@ -123,7 +126,12 @@ const conversions = {
123126
'dppx': 1
124127
}
125128
};
126-
129+
/**
130+
* @param {number} value
131+
* @param {string} sourceUnit
132+
* @param {string} targetUnit
133+
* @param {number|false} precision
134+
*/
127135
function convertUnit(value, sourceUnit, targetUnit, precision) {
128136
const sourceUnitNormalized = sourceUnit.toLowerCase();
129137
const targetUnitNormalized = targetUnit.toLowerCase();
@@ -139,7 +147,7 @@ function convertUnit(value, sourceUnit, targetUnit, precision) {
139147
const converted = conversions[targetUnitNormalized][sourceUnitNormalized] * value;
140148

141149
if (precision !== false) {
142-
precision = Math.pow(10, parseInt(precision) || 5);
150+
precision = Math.pow(10, precision || 5);
143151

144152
return Math.round(converted * precision) / precision;
145153
}

src/lib/reducer.js

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import 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 */
2530
function flip(operator) {
2631
return operator === '+' ? '-' : '+';
2732
}
2833

34+
/**
35+
* @param {string} operator
36+
* @returns {operator is '+'|'-'}
37+
*/
2938
function 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+
*/
3352
function 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,31 @@ function collectAddSubItems(preOperator, node, collected, precision) {
85105
}
86106
}
87107

88-
108+
/**
109+
* @param {import('../parser').CalcNode} node
110+
* @param {number} precision
111+
*/
89112
function 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+
withoutZeroItem.unshift(/** @type Collectible*/(firstZeroItem))
104128
}
105129

106130
// make sure the preOperator of the first item is +
107131
if (withoutZeroItem[0].preOperator === '-' &&
108-
isValueType(withoutZeroItem[0].node.type)) {
132+
isValueType(withoutZeroItem[0].node)) {
109133
withoutZeroItem[0].node.value *= -1;
110134
withoutZeroItem[0].preOperator = '+';
111135
}
@@ -122,9 +146,11 @@ function reduceAddSubExpression(node, precision) {
122146

123147
return root;
124148
}
125-
149+
/**
150+
* @param {import('../parser').MathExpression} node
151+
*/
126152
function reduceDivisionExpression(node) {
127-
if (!isValueType(node.right.type)) {
153+
if (!isValueType(node.right)) {
128154
return node;
129155
}
130156

@@ -135,12 +161,18 @@ function reduceDivisionExpression(node) {
135161
return applyNumberDivision(node.left, node.right.value)
136162
}
137163

138-
// apply (expr) / number
164+
/**
165+
* apply (expr) / number
166+
*
167+
* @param {import('../parser').CalcNode} node
168+
* @param {number} divisor
169+
* @return {import('../parser').CalcNode}
170+
*/
139171
function applyNumberDivision(node, divisor) {
140172
if (divisor === 0) {
141173
throw new Error('Cannot divide by zero');
142174
}
143-
if (isValueType(node.type)) {
175+
if (isValueType(node)) {
144176
node.value /= divisor;
145177
return node;
146178
}
@@ -169,7 +201,9 @@ function applyNumberDivision(node, divisor) {
169201
}
170202
}
171203
}
172-
204+
/**
205+
* @param {import('../parser').MathExpression} node
206+
*/
173207
function reduceMultiplicationExpression(node) {
174208
// (expr) * number
175209
if (node.right.type === 'Number') {
@@ -182,9 +216,14 @@ function reduceMultiplicationExpression(node) {
182216
return node;
183217
}
184218

185-
// apply (expr) / number
219+
/**
220+
* apply (expr) * number
221+
* @param {number} multiplier
222+
* @param {import('../parser').CalcNode} node
223+
* @return {import('../parser').CalcNode}
224+
*/
186225
function applyNumberMultiplication(node, multiplier) {
187-
if (isValueType(node.type)) {
226+
if (isValueType(node)) {
188227
node.value *= multiplier;
189228
return node;
190229
}
@@ -214,6 +253,11 @@ function applyNumberMultiplication(node, multiplier) {
214253
}
215254
}
216255

256+
/**
257+
* @param {import('../parser').ValueExpression} left
258+
* @param {import('../parser').ValueExpression} right
259+
* @param {number} precision
260+
*/
217261
function convertNodesUnits(left, right, precision) {
218262
switch (left.type) {
219263
case 'LengthValue':
@@ -237,6 +281,10 @@ function convertNodesUnits(left, right, precision) {
237281
}
238282
}
239283

284+
/**
285+
* @param {import('../parser').CalcNode} node
286+
* @param {number} precision
287+
*/
240288
function reduce(node, precision) {
241289
if (node.type === "MathExpression") {
242290
if (isAddSubOperator(node.operator)) {
@@ -247,9 +295,9 @@ function reduce(node, precision) {
247295
node.right = reduce(node.right, precision);
248296
switch (node.operator) {
249297
case "/":
250-
return reduceDivisionExpression(node, precision);
298+
return reduceDivisionExpression(node);
251299
case "*":
252-
return reduceMultiplicationExpression(node, precision);
300+
return reduceMultiplicationExpression(node);
253301
}
254302

255303
return node;

src/lib/stringifier.js

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ const order = {
55
"-": 1,
66
};
77

8+
/**
9+
* @param {number} value
10+
* @param {number | false} prec
11+
*/
812
function round(value, prec) {
913
if (prec !== false) {
1014
const precision = Math.pow(10, prec);
@@ -13,12 +17,15 @@ function round(value, prec) {
1317
return value;
1418
}
1519

20+
/**
21+
* @param {number | false} prec
22+
* @param {import('../parser').CalcNode} node
23+
*/
1624
function stringify(node, prec) {
1725
switch (node.type) {
1826
case "MathExpression": {
1927
const {left, right, operator: op} = node;
2028
let str = "";
21-
2229
if (left.type === 'MathExpression' && order[op] < order[left.operator]) {
2330
str += `(${stringify(left, prec)})`;
2431
} else {
@@ -36,14 +43,24 @@ function stringify(node, prec) {
3643
return str;
3744
}
3845
case 'Number':
39-
return round(node.value, prec);
46+
return round(node.value, prec).toString();
4047
case 'Function':
41-
return node.value;
48+
return node.value.toString();
4249
default:
4350
return round(node.value, prec) + node.unit;
4451
}
4552
}
4653

54+
/**
55+
* @param {string} calc
56+
* @param {import('../parser').CalcNode} node
57+
* @param {string} originalValue
58+
* @param {{precision: number | false, warnWhenCannotResolve: boolean}} options
59+
* @param {import("postcss").Result} result
60+
* @param {import("postcss").ChildNode} item
61+
*
62+
* @returns {string}
63+
*/
4764
export default function (
4865
calc,
4966
node,

0 commit comments

Comments
 (0)