Skip to content

Commit 1ea925d

Browse files
committed
refactor: deduplicate math function list
1 parent ec70cc4 commit 1ea925d

5 files changed

Lines changed: 105 additions & 71 deletions

File tree

‎src/index.js‎

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,11 @@ import valueParser from 'postcss-value-parser';
55
import { tokenize } from './lib/tokenizer.js';
66
import { parse } from './lib/parser.js';
77
import { simplify } from './lib/simplify.js';
8+
import { isSupportedMathFunction } from './lib/simplify/call.js';
89
import { serialize } from './lib/serialize.js';
910

1011
const MATCH_CALC = /^(?:-(?:moz|webkit)-)?calc$/i;
1112

12-
// Bare math-function calls (no calc() wrapper) — fed to the same pipeline.
13-
// Mirrors the dispatch in lib/simplify/call.js.
14-
const MATH_FUNCTIONS = new Set([
15-
'min',
16-
'max',
17-
'clamp',
18-
'abs',
19-
'sign',
20-
'mod',
21-
'rem',
22-
'round',
23-
'sin',
24-
'cos',
25-
'tan',
26-
'asin',
27-
'acos',
28-
'atan',
29-
'atan2',
30-
'pow',
31-
'sqrt',
32-
'hypot',
33-
'log',
34-
'exp',
35-
]);
36-
3713
/**
3814
* @typedef {object} PluginOptions
3915
* @property {number | false} [precision]
@@ -60,7 +36,7 @@ function transformValue(value, options, result, item) {
6036
return;
6137
}
6238
const isCalc = MATCH_CALC.test(node.value);
63-
const isMath = !isCalc && MATH_FUNCTIONS.has(node.value.toLowerCase());
39+
const isMath = !isCalc && isSupportedMathFunction(node.value);
6440
if (!isCalc && !isMath) {
6541
return;
6642
}

‎src/lib/simplify/call.js‎

Lines changed: 49 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,46 @@ import { simplifyHypot } from './hypot.js';
1919
/** @typedef {import('../node.js').Node} Node */
2020
/** @typedef {import('../simplify.js').SimplifyFn} SimplifyFn */
2121

22+
/** @typedef {(name: string, args: Node[]) => Node} MathSimplifier */
23+
24+
// Bare CSS math functions with implemented simplification semantics, keyed
25+
// by lowercase name. calc() and its vendor-prefixed forms are handled
26+
// separately as wrappers in simplifyCall. This map is the single source of
27+
// truth for both dispatch and `isSupportedMathFunction`.
28+
/** @type {Map<string, MathSimplifier>} */
29+
const MATH_SIMPLIFIERS = new Map([
30+
['min', simplifyMinMax],
31+
['max', simplifyMinMax],
32+
['clamp', (_name, args) => simplifyClamp(args)],
33+
['abs', (_name, args) => simplifyAbs(args)],
34+
['sign', (_name, args) => simplifySign(args)],
35+
['mod', (_name, args) => simplifyModRem('mod', args)],
36+
['rem', (_name, args) => simplifyModRem('rem', args)],
37+
['round', (_name, args) => simplifyRound(args)],
38+
['sin', (_name, args) => simplifyTrig('sin', args)],
39+
['cos', (_name, args) => simplifyTrig('cos', args)],
40+
['tan', (_name, args) => simplifyTrig('tan', args)],
41+
['asin', (_name, args) => simplifyInverseTrig('asin', args)],
42+
['acos', (_name, args) => simplifyInverseTrig('acos', args)],
43+
['atan', (_name, args) => simplifyInverseTrig('atan', args)],
44+
['atan2', (_name, args) => simplifyAtan2(args)],
45+
['pow', (_name, args) => simplifyPow(args)],
46+
['sqrt', (_name, args) => simplifySqrt(args)],
47+
['hypot', (_name, args) => simplifyHypot(args)],
48+
['log', (_name, args) => simplifyLog(args)],
49+
['exp', (_name, args) => simplifyExp(args)],
50+
]);
51+
52+
/**
53+
* Whether a bare CSS math function has an implemented simplifier.
54+
*
55+
* @param {string} name
56+
* @return {boolean}
57+
*/
58+
function isSupportedMathFunction(name) {
59+
return MATH_SIMPLIFIERS.has(name.toLowerCase());
60+
}
61+
2262
/**
2363
* @param {Extract<Node, { type: 'Call' }>} node
2464
* @param {SimplifyFn} simplify
@@ -36,50 +76,17 @@ function simplifyCall(node, simplify) {
3676

3777
const args = node.args.map((a) => simplify(a));
3878

39-
if (name === 'min' || name === 'max') {
40-
return simplifyMinMax(node.name, args);
41-
}
42-
if (name === 'clamp') {
43-
return simplifyClamp(args);
44-
}
45-
if (name === 'abs') {
46-
return simplifyAbs(args);
47-
}
48-
if (name === 'sign') {
49-
return simplifySign(args);
50-
}
51-
if (name === 'mod' || name === 'rem') {
52-
return simplifyModRem(name, args);
53-
}
54-
if (name === 'round') {
55-
return simplifyRound(args);
56-
}
57-
if (name === 'sin' || name === 'cos' || name === 'tan') {
58-
return simplifyTrig(name, args);
59-
}
60-
if (name === 'asin' || name === 'acos' || name === 'atan') {
61-
return simplifyInverseTrig(name, args);
62-
}
63-
if (name === 'atan2') {
64-
return simplifyAtan2(args);
65-
}
66-
if (name === 'pow') {
67-
return simplifyPow(args);
68-
}
69-
if (name === 'sqrt') {
70-
return simplifySqrt(args);
71-
}
72-
if (name === 'hypot') {
73-
return simplifyHypot(args);
74-
}
75-
if (name === 'log') {
76-
return simplifyLog(args);
77-
}
78-
if (name === 'exp') {
79-
return simplifyExp(args);
79+
const simplifier = MATH_SIMPLIFIERS.get(name);
80+
if (simplifier) {
81+
// min/max preserve the call's original casing in their opaque-args
82+
// fallback; the rest normalize to lowercase internally.
83+
return simplifier(
84+
name === 'min' || name === 'max' ? node.name : name,
85+
args
86+
);
8087
}
8188

8289
return { type: 'Call', name: node.name, args };
8390
}
8491

85-
export { simplifyCall };
92+
export { isSupportedMathFunction, simplifyCall };

‎test/unit/plugin.test.mjs‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,14 @@ test('plugin: simplifies bare math functions case-insensitively', async () => {
215215
const { css } = await process('a{ width: MIN(1px, 2px) }');
216216
assert.equal(css, 'a{ width: 1px }');
217217
});
218+
test('plugin: simplifies a supported bare function from the dispatcher', async () => {
219+
const { css } = await process('a{ width: pow(2, 3) }');
220+
assert.equal(css, 'a{ width: 8 }');
221+
});
222+
test('plugin: leaves unsupported bare functions untouched', async () => {
223+
const { css } = await process('a{ width: unknown(1px + 2px) }');
224+
assert.equal(css, 'a{ width: unknown(1px + 2px) }');
225+
});
218226
test('plugin: leaves opaque-arg bare min() preserved', async () => {
219227
const { css } = await process('a{ width: min(1px, var(--x)) }');
220228
assert.equal(css, 'a{ width: min(1px, var(--x)) }');

‎test/unit/simplify/call.test.mjs‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { test } from 'node:test';
2+
import assert from 'node:assert/strict';
3+
import { isSupportedMathFunction } from '../../../src/lib/simplify/call.js';
4+
5+
test('isSupportedMathFunction: recognizes every implemented bare math function', () => {
6+
const supported = [
7+
'min',
8+
'max',
9+
'clamp',
10+
'abs',
11+
'sign',
12+
'mod',
13+
'rem',
14+
'round',
15+
'sin',
16+
'cos',
17+
'tan',
18+
'asin',
19+
'acos',
20+
'atan',
21+
'atan2',
22+
'pow',
23+
'sqrt',
24+
'hypot',
25+
'log',
26+
'exp',
27+
];
28+
29+
for (const name of supported) {
30+
assert.equal(isSupportedMathFunction(name), true, name);
31+
assert.equal(isSupportedMathFunction(name.toUpperCase()), true, name);
32+
}
33+
34+
for (const name of ['calc', '-webkit-calc', '-moz-calc', 'var', 'unknown']) {
35+
assert.equal(isSupportedMathFunction(name), false, name);
36+
}
37+
});

‎types/lib/simplify/call.d.ts‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
export type Node = import('../node.js').Node;
22
export type SimplifyFn = import('../simplify.js').SimplifyFn;
3-
/** @typedef {import('../node.js').Node} Node */
4-
/** @typedef {import('../simplify.js').SimplifyFn} SimplifyFn */
3+
export type MathSimplifier = (name: string, args: Node[]) => Node;
4+
/**
5+
* Whether a bare CSS math function has an implemented simplifier.
6+
*
7+
* @param {string} name
8+
* @return {boolean}
9+
*/
10+
declare function isSupportedMathFunction(name: string): boolean;
511
/**
612
* @param {Extract<Node, { type: 'Call' }>} node
713
* @param {SimplifyFn} simplify
@@ -10,4 +16,4 @@ export type SimplifyFn = import('../simplify.js').SimplifyFn;
1016
declare function simplifyCall(node: Extract<Node, {
1117
type: 'Call';
1218
}>, simplify: SimplifyFn): Node;
13-
export { simplifyCall };
19+
export { isSupportedMathFunction, simplifyCall };

0 commit comments

Comments
 (0)