UNPKG

@wix/css-property-parser

Version:

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

216 lines (215 loc) 7.43 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.parse = parse; exports.toCSSValue = toCSSValue; const shared_utils_1 = require('../utils/shared-utils.cjs'); const css_variable_1 = require('./css-variable.cjs'); const types_1 = require('../types.cjs'); /** * Parses a single grid line value (<grid-line>) * Syntax: auto | <custom-ident> | [ [ <integer> ] && <custom-ident>? ] | [ span && [ <integer> || <custom-ident> ] ] */ function parseGridLine(value) { if (!value || typeof value !== 'string') return null; const trimmed = value.trim(); if (trimmed === '') return null; // CSS variables if ((0, shared_utils_1.isCssVariable)(trimmed)) { return (0, css_variable_1.parse)(trimmed); } // Handle global keywords first if ((0, shared_utils_1.isGlobalKeyword)(trimmed)) { return { type: 'keyword', keyword: trimmed.toLowerCase() }; } // Handle auto keyword if (trimmed.toLowerCase() === 'auto') { return { type: 'keyword', keyword: 'auto' }; } // Handle span syntax if (trimmed.toLowerCase().includes('span')) { const tokens = (0, shared_utils_1.tokenize)(trimmed.toLowerCase()); // Find span keyword const spanIndex = tokens.findIndex(token => token === 'span'); if (spanIndex === -1) return null; let count; let name; // Process tokens around span const otherTokens = tokens.filter((_, index) => index !== spanIndex); for (const token of otherTokens) { const intValue = parseInt(token, 10); if (!isNaN(intValue) && intValue > 0) { if (count !== undefined) return null; // Multiple numbers not allowed count = intValue; } else if (/^[a-zA-Z_][\w-]*$/.test(token) && token !== 'auto') { if (name !== undefined) return null; // Multiple names not allowed name = token; } else { return null; // Invalid token } } // Span requires at least count OR name if (count === undefined && name === undefined) { count = 1; // Default span count } return { type: 'span', count: count || 1, name }; } // Handle multi-token values (number + identifier combinations) const tokens = (0, shared_utils_1.tokenize)(trimmed); if (tokens.length === 2) { const [first, second] = tokens; // <integer> <custom-ident> const intValue = parseInt(first, 10); if (!isNaN(intValue) && intValue !== 0 && first === intValue.toString()) { if (/^[a-zA-Z_][\w-]*$/.test(second) && second !== 'span' && second !== 'auto') { return { type: 'named-line', name: second, count: intValue }; } } // <custom-ident> <integer> const intValue2 = parseInt(second, 10); if (!isNaN(intValue2) && intValue2 !== 0 && second === intValue2.toString()) { if (/^[a-zA-Z_][\w-]*$/.test(first) && first !== 'span' && first !== 'auto') { return { type: 'named-line', name: first, count: intValue2 }; } } return null; } // Handle single token values if (tokens.length === 1) { const token = tokens[0]; // Integer line number const intValue = parseInt(token, 10); if (!isNaN(intValue) && intValue !== 0 && token === intValue.toString()) { return { type: 'integer', value: intValue }; } // Custom identifier (grid area name or line name) if (/^[a-zA-Z_][\w-]*$/.test(token) && token !== 'span' && token !== 'auto') { return { type: 'named-line', name: token }; } return null; } return null; } /** * Converts a grid line value back to CSS string */ function gridLineToCSSValue(gridLine) { if (!gridLine) return null; switch (gridLine.type) { case 'keyword': return gridLine.keyword; case 'integer': return gridLine.value.toString(); case 'named-line': { if (!gridLine.name) return null; const parts = []; if (gridLine.count !== undefined && gridLine.count !== 1) { parts.push(gridLine.count.toString()); } parts.push(gridLine.name); return parts.join(' '); } case 'span': { const parts = ['span']; if (gridLine.count !== undefined && gridLine.count !== 1) { parts.push(gridLine.count.toString()); } if (gridLine.name) { parts.push(gridLine.name); } return parts.join(' '); } case 'variable': return (0, css_variable_1.toCSSValue)(gridLine) || 'auto'; default: return null; } } /** * Parses a CSS grid-row property string into structured components * Syntax: <grid-line> [ / <grid-line> ]? */ 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); } // Handle global keywords first if ((0, shared_utils_1.isGlobalKeyword)(trimmed)) { return { type: 'keyword', keyword: trimmed.toLowerCase() }; } // Handle grid-row keywords const gridRowKeyword = (0, shared_utils_1.getValidKeyword)(trimmed, types_1.GRID_ROW_KEYWORDS); if (gridRowKeyword) { return { type: 'keyword', keyword: gridRowKeyword }; } // Handle shorthand syntax with slash if (trimmed.includes(' / ')) { const parts = trimmed.split(' / '); if (parts.length === 2) { const start = parseGridLine(parts[0].trim()); const end = parseGridLine(parts[1].trim()); if (start && end) { return { type: 'grid-placement', start, end }; } } return null; } // Handle single grid line value const gridLine = parseGridLine(trimmed); if (gridLine) { return { type: 'grid-placement', start: gridLine }; } return null; } /** * Converts a parsed grid-row back to a CSS value string */ function toCSSValue(parsed) { if (!parsed) return null; // Handle CSS variables if ('CSSvariable' in parsed) { return (0, css_variable_1.toCSSValue)(parsed); } // Handle keywords if (parsed.type === 'keyword') { return parsed.keyword; } // Handle grid placement if (parsed.type === 'grid-placement') { const start = gridLineToCSSValue(parsed.start || null); const end = gridLineToCSSValue(parsed.end || null); if (start && end) { // Don't output "auto" for end value if it's auto if (end === 'auto') { return start; } return `${start} / ${end}`; } else if (start) { return start; } } return null; }