UNPKG

@wix/css-property-parser

Version:

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

214 lines (213 loc) 7.83 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; // Handle auto keyword if (trimmed.toLowerCase() === 'auto') { return { type: 'keyword', keyword: 'auto' }; } // Tokenize to handle complex values const tokens = (0, shared_utils_1.tokenize)(trimmed); // Single token cases if (tokens.length === 1) { const token = tokens[0].toLowerCase(); // Auto keyword if (token === 'auto') { return { type: 'keyword', keyword: 'auto' }; } // Integer value const intValue = parseInt(token, 10); if (!isNaN(intValue) && intValue !== 0 && token === intValue.toString()) { return { type: 'integer', value: intValue }; } // Custom identifier (line name) if (/^[a-zA-Z_][\w-]*$/.test(token) && token !== 'span' && token !== 'auto') { return { type: 'named-line', name: token }; } return null; } // Two token cases if (tokens.length === 2) { const [first, second] = tokens.map(t => t.toLowerCase()); // span <integer> if (first === 'span') { const intValue = parseInt(second, 10); if (!isNaN(intValue) && intValue > 0 && second === intValue.toString()) { return { type: 'span', count: intValue }; } // span <custom-ident> if (/^[a-zA-Z_][\w-]*$/.test(second) && second !== 'span' && second !== 'auto') { return { type: 'span', count: 1, name: second }; } } // <integer> span if (second === 'span') { const intValue = parseInt(first, 10); if (!isNaN(intValue) && intValue !== 0 && first === intValue.toString()) { return { type: 'span', count: Math.abs(intValue) }; } } // <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; } // Three token cases if (tokens.length === 3) { const [first, second, third] = tokens.map(t => t.toLowerCase()); // span <integer> <custom-ident> if (first === 'span') { const intValue = parseInt(second, 10); if (!isNaN(intValue) && intValue > 0 && second === intValue.toString()) { if (/^[a-zA-Z_][\w-]*$/.test(third) && third !== 'span' && third !== 'auto') { return { type: 'span', count: intValue, name: third }; } } } // span <custom-ident> <integer> if (first === 'span') { const intValue = parseInt(third, 10); if (!isNaN(intValue) && intValue > 0 && third === intValue.toString()) { if (/^[a-zA-Z_][\w-]*$/.test(second) && second !== 'span' && second !== 'auto') { return { type: 'span', count: intValue, name: second }; } } } // <integer> <custom-ident> span if (third === 'span') { 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: 'span', count: Math.abs(intValue), name: second }; } } } return null; } return null; } /** * Converts a parsed grid line back to CSS string */ function gridLineToCSSValue(gridLine) { if (!gridLine) return 'auto'; switch (gridLine.type) { case 'keyword': return gridLine.keyword; case 'integer': return gridLine.value.toString(); case 'named-line': if (gridLine.count !== undefined) { return `${gridLine.count} ${gridLine.name}`; } return gridLine.name; 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 'auto'; } } 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); } // Global keywords if ((0, shared_utils_1.isGlobalKeyword)(trimmed)) { return { type: 'keyword', keyword: trimmed.toLowerCase() }; } // Handle grid-column keywords const gridColumnKeyword = (0, shared_utils_1.getValidKeyword)(trimmed, types_1.GRID_COLUMN_KEYWORDS); if (gridColumnKeyword) { return { type: 'keyword', keyword: gridColumnKeyword }; } // Check for slash separator (shorthand with start/end) if (trimmed.includes(' / ')) { const parts = trimmed.split(' / ').map(part => part.trim()); if (parts.length === 2) { const start = parseGridLine(parts[0]); const end = parseGridLine(parts[1]); if (start && end) { return { type: 'grid-placement', start: start, end: end }; } } return null; } // Single grid line value (applies to grid-column-start only) const gridLine = parseGridLine(trimmed); if (gridLine) { return { type: 'grid-placement', start: gridLine, end: { type: 'keyword', keyword: 'auto' } }; } return null; } 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 values if (parsed.type === 'grid-placement') { const start = gridLineToCSSValue(parsed.start || null); const end = gridLineToCSSValue(parsed.end || null); // If end is auto, we can omit it if (end === 'auto') { return start; } return `${start} / ${end}`; } return null; }