@wix/css-property-parser
Version:
A comprehensive TypeScript library for parsing and serializing CSS property values with full MDN specification compliance
171 lines (170 loc) • 6.02 kB
JavaScript
"use strict";
// Border-width property parser
// Handles parsing of CSS border-width shorthand property according to MDN specification
// https://developer.mozilla.org/en-US/docs/Web/CSS/border-width
Object.defineProperty(exports, "__esModule", { value: true });
exports.parse = parse;
exports.toCSSValue = toCSSValue;
const length_1 = require('./length.cjs');
const css_variable_1 = require('./css-variable.cjs');
const shared_utils_1 = require('../utils/shared-utils.cjs');
// Import centralized types
const types_1 = require('../types.cjs');
/**
* Check if value is a border width keyword
*/
function isBorderWidthKeyword(value) {
return types_1.BORDER_WIDTH_KEYWORDS.includes(value.toLowerCase());
}
/**
* Parse a single border width value (length or keyword)
*/
function parseSingleBorderWidth(value) {
const trimmed = value.trim();
// Handle CSS variables in individual components
if ((0, shared_utils_1.isCssVariable)(trimmed)) {
return (0, css_variable_1.parse)(trimmed);
}
// Global keywords
if ((0, shared_utils_1.isGlobalKeyword)(trimmed)) {
return { type: 'keyword', keyword: trimmed.toLowerCase() };
}
// Border width keywords
if (isBorderWidthKeyword(trimmed)) {
return { type: 'keyword', keyword: trimmed.toLowerCase() };
}
// Length values (non-negative only)
const lengthResult = (0, length_1.parse)(trimmed);
if (lengthResult &&
(('value' in lengthResult && lengthResult.value >= 0) ||
('expression' in lengthResult))) {
return lengthResult;
}
return null;
}
/**
* Parse a CSS border-width shorthand property string
* Supports 1-4 values: top [right [bottom [left]]]
*/
function parse(value) {
if (!value || typeof value !== 'string') {
return null;
}
const trimmed = value.trim();
if (trimmed === '') {
return null;
}
// CSS variables can be parsed directly for entire shorthand
const cssVariableResult = (0, css_variable_1.parse)(trimmed);
if (cssVariableResult) {
return cssVariableResult;
}
// Handle single global keyword
if ((0, shared_utils_1.isGlobalKeyword)(trimmed)) {
const keyword = { type: 'keyword', keyword: trimmed.toLowerCase() };
return {
borderTopWidth: keyword,
borderRightWidth: keyword,
borderBottomWidth: keyword,
borderLeftWidth: keyword
};
}
// Handle single border width keyword
if (isBorderWidthKeyword(trimmed)) {
const keyword = { type: 'keyword', keyword: trimmed.toLowerCase() };
return {
borderTopWidth: keyword,
borderRightWidth: keyword,
borderBottomWidth: keyword,
borderLeftWidth: keyword
};
}
// Tokenize the value to handle multiple values
const tokens = (0, shared_utils_1.tokenize)(trimmed);
// Validate we have 1-4 tokens
if (tokens.length === 0 || tokens.length > 4) {
return null;
}
// Validate each token can be parsed
for (const token of tokens) {
const parsed = parseSingleBorderWidth(token);
if (parsed === null) {
return null; // Invalid token
}
}
// Expand shorthand values to 4 values (top, right, bottom, left) using string tokens
const expandedStrings = (0, shared_utils_1.expandShorthandValues)(tokens);
const [topStr, rightStr, bottomStr, leftStr] = expandedStrings;
// Parse each expanded value
const borderTopWidth = parseSingleBorderWidth(topStr);
const borderRightWidth = parseSingleBorderWidth(rightStr);
const borderBottomWidth = parseSingleBorderWidth(bottomStr);
const borderLeftWidth = parseSingleBorderWidth(leftStr);
if (!borderTopWidth || !borderRightWidth || !borderBottomWidth || !borderLeftWidth) {
return null;
}
// Return the expanded structure
return {
borderTopWidth,
borderRightWidth,
borderBottomWidth,
borderLeftWidth
};
}
/**
* Convert BorderWidthValue back to CSS string
*/
function toCSSValue(parsed) {
if (!parsed) {
return null;
}
// Handle CSS variables for entire shorthand
if ('CSSvariable' in parsed) {
return (0, css_variable_1.toCSSValue)(parsed);
}
// Handle expanded shorthand structure
const borderWidthExpanded = parsed;
const topValue = serializeSingleBorderWidth(borderWidthExpanded.borderTopWidth);
const rightValue = serializeSingleBorderWidth(borderWidthExpanded.borderRightWidth);
const bottomValue = serializeSingleBorderWidth(borderWidthExpanded.borderBottomWidth);
const leftValue = serializeSingleBorderWidth(borderWidthExpanded.borderLeftWidth);
if (!topValue || !rightValue || !bottomValue || !leftValue) {
return null;
}
// Compress to shortest form
if (topValue === rightValue && rightValue === bottomValue && bottomValue === leftValue) {
// All same: "1px"
return topValue;
}
else if (topValue === bottomValue && rightValue === leftValue) {
// Top/bottom same, left/right same: "1px 2px"
return `${topValue} ${rightValue}`;
}
else if (rightValue === leftValue) {
// Left/right same: "1px 2px 3px"
return `${topValue} ${rightValue} ${bottomValue}`;
}
else {
// All different: "1px 2px 3px 4px"
return `${topValue} ${rightValue} ${bottomValue} ${leftValue}`;
}
}
/**
* Serialize a single border width value to CSS string
*/
function serializeSingleBorderWidth(value) {
// Handle CSS variables in components
if ('CSSvariable' in value) {
return (0, css_variable_1.toCSSValue)(value);
}
// Handle keywords
if ('keyword' in value) {
return value.keyword;
}
// Handle lengths by delegating to the length toCSSValue function
const lengthValue = (0, length_1.toCSSValue)(value);
if (lengthValue) {
return lengthValue;
}
return null;
}