CSS Syntax Parser
The `css-syntax-parser` package provides a specialized JavaScript and TypeScript library for parsing and interpreting CSS value definition syntax as specified by MDN (Mozilla Developer Network). It generates an Abstract Syntax Tree (AST) that precisely details the structure, combinators, and multipliers present in a given CSS syntax string. Currently stable at version 1.5.1, the library does not specify a fixed release cadence but has seen ongoing development since its initial release. Its core differentiation lies in its precise adherence to the MDN syntax definition, offering programmatic access to the parsed structure including term types (e.g., `keyword`, `data-type`, `composed`), combinators (e.g., `|`, `&&`), and multipliers (e.g., `{1,4}`, `?`). This enables developers to validate, analyze, or generate CSS property values based on their defined grammar, which is a niche but critical use case for tooling and language servers.
Common errors
-
TypeError: resolveSyntax is not a function
cause The module was imported incorrectly, possibly using a default import or a CommonJS `require` call in an ESM-only context.fixEnsure you are using named imports for ESM: `import { resolveSyntax } from 'css-syntax-parser';` or the correct `require` syntax for CommonJS environments. -
Property 'name' does not exist on type 'Term'. Property 'name' does not exist on type 'BracketsTerm'.
cause Attempting to access a property specific to a derived `Term` type (like `DataTypeTerm`) directly from a generic `Term` or an incorrect `Term` type without proper type narrowing.fixUse TypeScript type guards to narrow the type before accessing specific properties, e.g., `if (term.type === TermType.DATA_TYPE) { console.log(term.name); }`. -
Syntax Error: Unexpected token '<' at position 0
cause The input string provided to `resolveSyntax` does not conform to the expected MDN CSS value definition syntax, causing the parser to fail at the first unexpected character.fixReview the input string for typos or incorrect grammar. The parser strictly adheres to the specified syntax and is not tolerant of general CSS syntax errors.
Warnings
- gotcha When working with the parsed AST in TypeScript, properties of a `Term` (the base interface) must often be accessed after type narrowing (e.g., checking `term.type === TermType.BRACKETS`) or using type assertions to access specific sub-interface properties.
- gotcha The `resolveSyntaxByName` method with `recursive: false` will not fully resolve nested data types (e.g., `<color>`). For comprehensive ASTs, always pass `true` as the second argument.
- gotcha Providing malformed or non-MDN-compliant CSS value definition syntax to `resolveSyntax` can lead to unexpected AST structures or parsing errors, as the parser is designed for a specific grammar.
Install
-
npm install css-syntax-parser -
yarn add css-syntax-parser -
pnpm add css-syntax-parser
Imports
- resolveSyntax
const resolveSyntax = require('css-syntax-parser')import { resolveSyntax } from 'css-syntax-parser' - resolveSyntaxByName
import { resolveSyntaxByName } from 'css-syntax-parser' - TermType
import { TermType } from 'css-syntax-parser'
Quickstart
import { resolveSyntax, Term, TermType, BracketsTerm, ComposedTerm, DataTypeTerm, TermMultiplier } from 'css-syntax-parser';
const syntax: Term = resolveSyntax('[ <length> | <percentage> | auto ]{1,4}');
console.log(`Parsed syntax type: ${syntax.type}`);
if (syntax.type === TermType.BRACKETS) {
const brackets: BracketsTerm = syntax as BracketsTerm;
console.log(`Bracket multiplier: ${brackets.multiplier}`);
if (brackets.multiplier === TermMultiplier.RANGE) {
console.log(`Range: min=${brackets.range.min}, max=${brackets.range.max}`);
}
if (brackets.content.type === TermType.COMPOSED) {
const content: ComposedTerm = brackets.content as ComposedTerm;
console.log(`Content type: ${content.type}, combinator: ${content.combinator}`);
const child1 = content.children[1];
if (child1.type === TermType.DATA_TYPE) {
const dataTypeChild: DataTypeTerm = child1 as DataTypeTerm;
console.log(`Second child: type=${dataTypeChild.type}, name=${dataTypeChild.name}, nonTerminal=${dataTypeChild.nonTerminal}`);
}
}
}