@wix/css-property-parser
Version:
A comprehensive TypeScript library for parsing and serializing CSS property values with full MDN specification compliance
109 lines (108 loc) • 3.58 kB
JavaScript
// Text-decoration-line property parser
// https://developer.mozilla.org/en-US/docs/Web/CSS/text-decoration-line
import { isCssVariable, isGlobalKeyword, tokenize } from '../utils/shared-utils.js';
import { parse as parseCSSVariable, toCSSValue as cssVariableToCSSValue } from './css-variable.js';
import { TEXT_DECORATION_LINE_KEYWORDS } from '../types.js';
// Type guard for text-decoration-line values
function isTextDecorationLine(value) {
return TEXT_DECORATION_LINE_KEYWORDS.includes(value.toLowerCase());
}
/**
* Parses CSS text-decoration-line property values
*
* Syntax: none | [ underline || overline || line-through || blink ] | global-keywords
*
* @param value - The CSS text-decoration-line value to parse
* @returns Parsed TextDecorationLineValue or null if invalid
*
* @example
* ```typescript
* parse('underline') // { type: 'keyword', lines: ['underline'] }
* parse('underline overline') // { type: 'keyword', lines: ['underline', 'overline'] }
* parse('none') // { type: 'keyword', lines: ['none'] }
* parse('inherit') // { type: 'keyword', keyword: 'inherit' }
* parse('var(--decoration-line)') // { type: 'variable', CSSvariable: 'decoration-line' }
* ```
*/
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);
}
// Handle global keywords
if (isGlobalKeyword(trimmed)) {
return {
type: 'keyword',
keyword: trimmed.toLowerCase()
};
}
// Parse line decoration values using shared tokenizer
const tokens = tokenize(trimmed);
const lineValues = [];
// Check if 'none' is mixed with other values (invalid)
const hasNone = tokens.some(token => token.toLowerCase() === 'none');
const hasOthers = tokens.some(token => isTextDecorationLine(token) && token.toLowerCase() !== 'none');
if (hasNone && hasOthers) {
return null; // 'none' cannot be combined with other lines
}
// Parse each token
for (const token of tokens) {
if (isTextDecorationLine(token)) {
const lineValue = token.toLowerCase();
// Don't add duplicates
if (!lineValues.includes(lineValue)) {
lineValues.push(lineValue);
}
}
else {
// Invalid token
return null;
}
}
// Must have at least one line decoration specified
if (lineValues.length === 0) {
return null;
}
return {
type: 'keyword',
lines: lineValues
};
}
/**
* Converts a parsed TextDecorationLineValue back to its CSS string representation
*
* @param value - The parsed text-decoration-line value
* @returns CSS string representation or null if invalid
*/
export function toCSSValue(value) {
if (!value) {
return null;
}
// Handle CSS variables
if (value.type === 'variable') {
return cssVariableToCSSValue(value);
}
// Handle keyword values
if (value.type === 'keyword') {
// Handle global keywords
if ('keyword' in value) {
return value.keyword;
}
// Handle line keywords
if ('lines' in value) {
const lines = value.lines;
if (lines.length === 0) {
return null;
}
return lines.join(' ');
}
}
return null;
}