postcss-calc
Version:
PostCSS plugin to reduce calc()
58 lines (53 loc) • 1.46 kB
JavaScript
import { num, dim, call } from '../node.js';
import { foldConstArgs } from './fold.js';
/** @typedef {import('../node.js').Node} Node */
/**
* @param {'mod' | 'rem'} name
* @param {Node[]} args
* @return {Node}
*/
function simplifyModRem(name, args) {
if (args.length !== 2) {
return call(name, args);
}
const fold = foldConstArgs(args);
if (fold === null) {
return call(name, args);
}
const [a, b] = /** @type {[number, number]} */ (fold.values);
const result = applyModRem(name, a, b);
// NaN results drop the unit (`mod(5px, 0px)` → `calc(NaN)`, not
// `calc(NaN * 1px)`). §10.12 unit-preserving form is a known divergence.
if (Number.isNaN(result)) {
return num(Number.NaN);
}
return fold.unit === '' ? num(result) : dim(result, fold.unit);
}
/**
* @param {'mod' | 'rem'} name
* @param {number} a
* @param {number} b
* @return {number}
*/
function applyModRem(name, a, b) {
if (b === 0) {
return Number.NaN;
}
if (!Number.isFinite(a)) {
return Number.NaN;
}
if (!Number.isFinite(b)) {
// mod: result is NaN when A has opposite sign to B; otherwise A.
// rem: result is A regardless of signs.
if (name === 'mod' && a !== 0 && Math.sign(a) !== Math.sign(b)) {
return Number.NaN;
}
return a;
}
if (name === 'rem') {
return a % b; // sign follows dividend
}
// sign follows divisor
return a - b * Math.floor(a / b);
}
export { simplifyModRem };