@wix/css-property-parser
Version:
A comprehensive TypeScript library for parsing and serializing CSS property values with full MDN specification compliance
88 lines (87 loc) • 3.52 kB
JavaScript
// CSS Function Parser Utility
// Shared utility for parsing CSS math functions: calc(), min(), max(), clamp()
// Eliminates duplication across type evaluators
/**
* Parse a CSS math function (calc, min, max, clamp)
* @param value - The trimmed CSS value string
* @param allowedFunctions - Array of function names to accept (defaults to all)
* @returns Parsed function object or null if not a valid function
*/
export function parseCSSFunction(value, allowedFunctions = ['calc', 'min', 'max', 'clamp']) {
if (!value || typeof value !== 'string') {
return null;
}
const trimmed = value.trim();
// Check each allowed function
for (const fn of allowedFunctions) {
if (trimmed.startsWith(`${fn}(`) && trimmed.endsWith(')') && trimmed.length > fn.length + 2) {
const expression = trimmed.slice(fn.length + 1, -1).trim(); // Remove function name and parentheses
// Basic validation - reject obviously malformed expressions
if (expression === '' || // Empty expression
/[+\-*/]\s*$/.test(expression) || // Ends with operator
/^\s*[+*/]/.test(expression) || // Starts with operator (except unary +/-)
/[+\-*/]\s*[+\-*/]/.test(expression)) { // Adjacent operators
return null;
}
// Validate function-specific argument requirements
if (!validateFunctionArguments(fn, expression)) {
return null;
}
return {
type: 'function',
expression,
function: fn
};
}
}
return null;
}
/**
* Parse only calc() function (simpler version for basic type evaluators)
* @param value - The trimmed CSS value string
* @returns Parsed calc function object or null if not a valid calc
*/
export function parseCalcFunction(value) {
return parseCSSFunction(value, ['calc']);
}
/**
* Convert CSS function result back to CSS string
* @param parsed - The parsed CSS function object
* @returns CSS function string
*/
export function cssFunctionToCSSValue(parsed) {
return `${parsed.function}(${parsed.expression})`;
}
/**
* Check if a value is a CSS function call
* @param value - The CSS value string
* @param functionNames - Array of function names to check (defaults to all math functions)
* @returns True if the value is a CSS function call
*/
export function isCSSFunction(value, functionNames = ['calc', 'min', 'max', 'clamp']) {
if (!value || typeof value !== 'string') {
return false;
}
const trimmed = value.trim();
return functionNames.some(fn => trimmed.startsWith(`${fn}(`) && trimmed.endsWith(')'));
}
// Internal helper function to validate function arguments
function validateFunctionArguments(functionName, expression) {
switch (functionName) {
case 'min':
case 'max': {
// min() and max() require at least 2 arguments
const args = expression.split(',').map(arg => arg.trim());
return args.length >= 2 && args.every(arg => arg !== '');
}
case 'clamp': {
// clamp() requires exactly 3 arguments: min, preferred, max
const args = expression.split(',').map(arg => arg.trim());
return args.length === 3 && args.every(arg => arg !== '');
}
case 'calc':
default:
// calc() can have any valid expression, so no additional validation needed
return true;
}
}