@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.01 kB
JavaScript
// CSS Variable (Custom Property) data type parser
// Handles parsing of CSS custom properties according to MDN specification
// https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties
// CSS variable pattern - var(--property-name) or var(--property-name, fallback)
const CSS_VARIABLE_REGEX = /^var\(\s*(--[a-zA-Z0-9_-]+)\s*(?:,\s*(.+?))?\s*\)$/i;
/**
* Normalizes a CSS variable fallback value by recursively parsing CSS variables
* @param fallback - The fallback value to normalize
* @returns Normalized fallback value
*/
function normalizeFallback(fallback) {
const trimmed = fallback.trim();
// If it looks like a CSS variable, parse and re-serialize it to normalize whitespace
if (trimmed.startsWith('var(')) {
const parsed = parse(trimmed);
if (parsed) {
const normalized = toCSSValue(parsed);
if (normalized) {
return normalized;
}
}
}
return trimmed;
}
/**
* Parses a CSS variable (custom property) value into structured components
* @param input - The CSS variable string
* @returns Parsed CSS variable object or null if invalid
*/
export function parse(input) {
if (!input || typeof input !== 'string') {
return null;
}
const trimmed = input.trim();
// Match CSS variable pattern
const match = trimmed.match(CSS_VARIABLE_REGEX);
if (!match) {
return null;
}
const [, propertyName, fallback] = match;
// Validate property name
const trimmedPropertyName = propertyName.trim();
if (!trimmedPropertyName.startsWith('--') || trimmedPropertyName.length <= 2) {
return null;
}
// If there's a fallback but it's empty or only whitespace, reject it
if (fallback !== undefined) {
const trimmedFallback = fallback.trim();
if (trimmedFallback === '') {
return null;
}
// Check if the fallback contains unbalanced parentheses that suggest
// it's part of multiple CSS variables (e.g., "10px) var(--other")
if (trimmedFallback.includes(') var(')) {
return null;
}
return {
type: 'variable',
CSSvariable: trimmedPropertyName,
defaultValue: normalizeFallback(trimmedFallback)
};
}
return {
type: 'variable',
CSSvariable: trimmedPropertyName
};
}
/**
* Converts a parsed CSS variable back to a CSS value string
* @param parsed - The parsed CSS variable object
* @returns CSS value string or null if invalid
*/
export function toCSSValue(parsed) {
if (!parsed) {
return null;
}
// Validate the parsed CSS variable
if (typeof parsed.CSSvariable !== 'string' ||
!parsed.CSSvariable.startsWith('--') ||
parsed.CSSvariable.length <= 2) {
return null;
}
if (parsed.defaultValue) {
return `var(${parsed.CSSvariable}, ${parsed.defaultValue})`;
}
return `var(${parsed.CSSvariable})`;
}