UNPKG

@wix/css-property-parser

Version:

A comprehensive TypeScript library for parsing and serializing CSS property values with full MDN specification compliance

153 lines (152 loc) 5.12 kB
"use strict"; // Border-color property parser // Handles parsing of CSS border-color shorthand property according to MDN specification // https://developer.mozilla.org/en-US/docs/Web/CSS/border-color Object.defineProperty(exports, "__esModule", { value: true }); exports.parse = parse; exports.toCSSValue = toCSSValue; const color_1 = require('./color.cjs'); const css_variable_1 = require('./css-variable.cjs'); const shared_utils_1 = require('../utils/shared-utils.cjs'); /** * Check if value is a border color keyword */ function isBorderColorKeyword(value) { return value.toLowerCase() === 'currentcolor'; } /** * Parse a single color value for border-color */ function parseSingleColor(value) { const trimmed = value.trim(); // CSS variables should be handled at the property level, not individual color level if ((0, shared_utils_1.isCssVariable)(trimmed)) { return null; // CSS variables apply to entire property } // Global keywords - return as BorderColorKeyword for individual color processing if ((0, shared_utils_1.isGlobalKeyword)(trimmed)) { return { type: 'keyword', keyword: trimmed.toLowerCase() }; } // Border color keyword (currentcolor) if (isBorderColorKeyword(trimmed)) { return { type: 'keyword', keyword: 'currentcolor' }; } // Color values - delegate to color evaluator const colorResult = (0, color_1.parse)(trimmed); if (colorResult && 'format' in colorResult) { // Only return actual color values, not CSS variables return colorResult; } return null; } /** * Parse a CSS border-color property string */ 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 ((0, shared_utils_1.isCssVariable)(trimmed)) { return (0, css_variable_1.parse)(trimmed); } // Tokenize the value (handles functions and quotes properly) const tokens = (0, shared_utils_1.tokenize)(trimmed); // Must have 1-4 values if (tokens.length === 0 || tokens.length > 4) { return null; } // Parse each token as a color const colors = []; for (const token of tokens) { const colorResult = parseSingleColor(token); if (!colorResult) { return null; // Invalid color } colors.push(colorResult); } // Expand shorthand values (1-4 values → 4 values) // Convert to strings for expandShorthandValues, then map back const colorStrings = colors.map(color => { if ('keyword' in color) { return color.keyword; } else { return (0, color_1.toCSSValue)(color) || ''; } }); const expandedStrings = (0, shared_utils_1.expandShorthandValues)(colorStrings); // Convert back to color objects const expandedColors = expandedStrings.map(colorStr => { const parsed = parseSingleColor(colorStr); if (!parsed) { throw new Error(`Failed to re-parse color: ${colorStr}`); } return parsed; }); return { borderTopColor: expandedColors[0], borderRightColor: expandedColors[1], borderBottomColor: expandedColors[2], borderLeftColor: expandedColors[3] }; } /** * Convert a single color value to CSS string */ function convertColorToCSSValue(color) { // Handle CSS variables if ('CSSvariable' in color) { return (0, css_variable_1.toCSSValue)(color); } if ('keyword' in color) { return color.keyword; } return (0, color_1.toCSSValue)(color); } /** * Convert BorderColorValue back to CSS string */ function toCSSValue(parsed) { if (!parsed) { return null; } // Handle CSS variables if ('CSSvariable' in parsed) { return (0, css_variable_1.toCSSValue)(parsed); } // Handle shorthand expansion if ('borderTopColor' in parsed) { const { borderTopColor, borderRightColor, borderBottomColor, borderLeftColor } = parsed; // Convert each color to CSS string const top = convertColorToCSSValue(borderTopColor); const right = convertColorToCSSValue(borderRightColor); const bottom = convertColorToCSSValue(borderBottomColor); const left = convertColorToCSSValue(borderLeftColor); if (!top || !right || !bottom || !left) { return null; } // Optimize shorthand output if (top === right && right === bottom && bottom === left) { // All four values are the same return top; } else if (top === bottom && right === left) { // Top/bottom same, left/right same return `${top} ${right}`; } else if (right === left) { // Left/right same return `${top} ${right} ${bottom}`; } else { // All four values different return `${top} ${right} ${bottom} ${left}`; } } return null; }