@wix/css-property-parser
Version:
A comprehensive TypeScript library for parsing and serializing CSS property values with full MDN specification compliance
133 lines (132 loc) • 4.33 kB
JavaScript
// Border-style property parser
// Handles parsing of CSS border-style property according to MDN specification
// https://developer.mozilla.org/en-US/docs/Web/CSS/border-style
import { parse as parseCSSVariable, toCSSValue as cssVariableToCSSValue } from './css-variable.js';
import { isCssVariable, isGlobalKeyword, tokenize, expandShorthandValues } from '../utils/shared-utils.js';
// Import centralized types
import { BORDER_STYLE_KEYWORDS } from '../types.js';
/**
* Check if value is a border style keyword
*/
function isBorderStyleKeyword(value) {
return BORDER_STYLE_KEYWORDS.includes(value.toLowerCase());
}
/**
* Parse a single border style value
*/
function parseSingleBorderStyle(value) {
if (!value || typeof value !== 'string') {
return null;
}
const trimmed = value.trim();
if (trimmed === '') {
return null;
}
// Global keywords
if (isGlobalKeyword(trimmed)) {
return { type: 'keyword', keyword: trimmed.toLowerCase() };
}
// Border style keywords
if (isBorderStyleKeyword(trimmed)) {
return { type: 'keyword', keyword: trimmed.toLowerCase() };
}
return null;
}
/**
* Serialize a single border style value back to CSS
*/
function serializeSingleBorderStyle(value) {
if (!value) {
return null;
}
// Handle CSS variables
if ('CSSvariable' in value) {
return cssVariableToCSSValue(value);
}
return value.keyword;
}
/**
* Parse a CSS border-style property string
*/
export function parse(value) {
if (!value || typeof value !== 'string') {
return null;
}
const trimmed = value.trim();
if (trimmed === '') {
return null;
}
// CSS variables - parse and return directly
if (isCssVariable(trimmed)) {
return parseCSSVariable(trimmed);
}
// Tokenize the value (handles function boundaries and quoted strings)
const tokens = tokenize(trimmed);
// Validate token count (CSS shorthand supports 1-4 values)
if (tokens.length === 0 || tokens.length > 4) {
return null;
}
// Validate each token can be parsed as a border style
for (const token of tokens) {
if (!parseSingleBorderStyle(token)) {
return null;
}
}
// Expand shorthand values (1-4 values → 4 values)
const expandedTokens = expandShorthandValues(tokens);
// Parse each expanded token
const borderTopStyle = parseSingleBorderStyle(expandedTokens[0]);
const borderRightStyle = parseSingleBorderStyle(expandedTokens[1]);
const borderBottomStyle = parseSingleBorderStyle(expandedTokens[2]);
const borderLeftStyle = parseSingleBorderStyle(expandedTokens[3]);
// All values must be valid
if (!borderTopStyle || !borderRightStyle || !borderBottomStyle || !borderLeftStyle) {
return null;
}
return {
borderTopStyle,
borderRightStyle,
borderBottomStyle,
borderLeftStyle
};
}
/**
* Convert BorderStyleValue back to CSS string
*/
export function toCSSValue(parsed) {
if (!parsed) {
return null;
}
// Handle CSS variables
if ('CSSvariable' in parsed) {
return cssVariableToCSSValue(parsed);
}
// Handle expanded border style
if ('borderTopStyle' in parsed) {
const top = serializeSingleBorderStyle(parsed.borderTopStyle);
const right = serializeSingleBorderStyle(parsed.borderRightStyle);
const bottom = serializeSingleBorderStyle(parsed.borderBottomStyle);
const left = serializeSingleBorderStyle(parsed.borderLeftStyle);
if (!top || !right || !bottom || !left) {
return null;
}
// Compress to shortest form
if (top === right && right === bottom && bottom === left) {
// All sides same: "solid"
return top;
}
else if (top === bottom && right === left) {
// Vertical/horizontal: "solid dashed"
return `${top} ${right}`;
}
else if (right === left) {
// Top, horizontal, bottom: "solid dashed dotted"
return `${top} ${right} ${bottom}`;
}
else {
// All different: "solid dashed dotted groove"
return `${top} ${right} ${bottom} ${left}`;
}
}
return null;
}