postcss-calc
Version:
PostCSS plugin to reduce calc()
93 lines (84 loc) • 2.37 kB
JavaScript
import { num, dim } from '../node.js';
import { baseOf, convert } from '../convertUnits.js';
/** @typedef {import('../node.js').Node} Node */
/** @typedef {import('../node.js').Num} Num */
/** @typedef {import('../node.js').Dim} Dim */
/** @typedef {import('../convertUnits.js').BaseType} BaseType */
/**
* Construct a Num or Dim node from a folded result.
* @param {{ unit: string }} fold
* @param {number} value
* @return {Num | Dim}
*/
function foldResult(fold, value) {
return fold.unit === '' ? num(value) : dim(value, fold.unit);
}
/**
* Return the argument that produced a folded min/max/clamp value, so the
* result keeps its own unit, spelling and zero sign. Falls back to a fresh
* node when no argument matches (NaN).
* @param {Node[]} args
* @param {{ values: number[], unit: string }} fold
* @param {number} value
* @return {Node}
*/
function chosenArg(args, fold, value) {
const index = fold.values.findIndex((v) => Object.is(v, value));
return index === -1 ? foldResult(fold, value) : args[index];
}
/**
* @param {Node[]} args
* @return {{ values: number[], unit: string } | null}
*/
function foldConstArgs(args) {
if (args.length === 0) {
return null;
}
const first = args[0];
if (first.type === 'Num') {
return foldNumberArgs(args);
}
if (first.type === 'Dim') {
const b = first.unit === '%' ? null : baseOf(first.unit);
if (!b) {
return null;
}
return foldDimArgs(args, first.unit, b);
}
return null;
}
/**
* @param {Node[]} args
* @return {{ values: number[], unit: '' } | null}
*/
function foldNumberArgs(args) {
/** @type {number[]} */ const values = [];
for (const a of args) {
if (a.type !== 'Num') {
return null;
}
values.push(a.value);
}
return { values, unit: '' };
}
/**
* @param {Node[]} args
* @param {string} unit
* @param {BaseType} base
* @return {{ values: number[], unit: string } | null}
*/
function foldDimArgs(args, unit, base) {
/** @type {number[]} */ const values = [];
for (const a of args) {
if (a.type !== 'Dim' || a.unit === '%' || baseOf(a.unit) !== base) {
return null;
}
const converted = convert(a.value, a.unit, unit);
if (converted === null) {
return null;
}
values.push(converted);
}
return { values, unit };
}
export { foldConstArgs, foldResult, chosenArg };