This project is a fork of g-plane/raffia for Oxfmt.
oxc-css-parser parses CSS, SCSS, Sass, and Less. It produces an AST and does not compile preprocessor syntax to CSS.
use oxc_css_parser::{Allocator, Parser, Syntax, ast::Stylesheet};
let allocator = Allocator::default();
let mut parser = Parser::new(&allocator, "a { color: green }", Syntax::Css);
let ast = parser.parse::<Stylesheet>().unwrap();
println!("{:#?}", ast);More examples are available in examples.
For detailed API documentation, see docs.rs.
Why there is a line to draw: css-syntax-3 alone rejects the postcss-plugin CSS real projects are full of (*zoom, $var, --color-*, x: { }),
while postcss itself is a tokenizer plus a statement splitter, so following its acceptance wholesale means an AST of strings.
Leniency never reaches below a statement: selectors, values and preludes are typed or raw, never re-parsed strings.
Each line has one grammar owner. postcss is never one, and neither is "Prettier prints it".
Changing acceptance is additive: it only turns errors into parses, with a comment citing the owner, a test pinning the strict shape, and the conformance snapshot flip in the PR. Changing how already-accepted input is represented is a bug fix or a refactor, and the PR says which.
Owner: css-syntax-3's syntax layer (tokenizer, rule / declaration / block structure).
- A declaration value is any component-value run: a value the typed grammar cannot read falls back to raw tokens
- Two postcss statement shapes on top, because the spec's syntax layer drops them and real projects run through postcss plugins:
- postcss property names: the glued token run up to the first
:/ whitespace / comment, not necessarily an<ident-token>(*zoom,+color,#x,2xl,background+,--color-*,$(var)-size) - raw-prelude rules:
x: { ... }is a rule, with or without a trailing;, and so is a numeric-led statement (50% { }outside@keyframes); the prelude is kept as raw tokens (UnknownQualifiedRule)
- postcss property names: the glued token run up to the first
$vargets a typed node (PostcssSimpleVar, postcss-simple-vars): an AST shape for the formatter's layout, not extra acceptance,$var: valueis already a postcss property name.$var: value;at the root is a statement, that is where postcss-simple-vars defines variables- Root declarations: the
TopLevelDeclarationrecoverable error. postcss keeps them; not followed - Everything else the spec's syntax layer discards stays rejected (
color red;,x: {a:b} more;), and so do shapes the spec keeps but nobody asked for ("foo" {},( ) {},, .a {}): the spec is a ceiling, not a floor - Errors: css-syntax-3 recovery (EOF closes blocks, bad strings) is kept even where postcss throws
- Oracles:
cssparser(Servo) for the spec ceiling, postcss at Prettier's pinned version for the two shapes
Owner: dart-sass. No postcss-scss leniency.
- No raw fallback for a normal property's value: the expression grammar owns it
- A custom property value is text, as dart-sass reads it (
//is no comment inside) - Root declarations: the
TopLevelDeclarationrecoverable error, as dart-sass rejects them - The IE
*colorhack is kept as a name prefix, as dart-sass accepts it
Owner: less.js. No postcss-less leniency.
- No raw fallback for a normal property's value
- Root declarations parse: less.js accepts them at parse time and fails only at eval
- The IE
*colorhack as a name prefix and digit-only names (5: x), as less.js accepts them
SCSS with template_placeholder set. It is the only option, and the only place a dialect line is relaxed.
- A backtick-delimited
`<prefix><digits>`is one typedToken::Placeholder - Root declarations are statements, without the error: a fragment is usually a declaration list (
css`display: flex;`) - Nothing from the CSS line comes along; postcss property names extend here only on demand
The benchmark suite compares parser performance against other CSS parsers.
Install cargo-criterion, then run the checked-in fixture benchmark:
cargo install cargo-criterion
cargo criterionTo benchmark custom inputs, add CSS, SCSS, Sass, or Less files to a local bench_data
directory. When bench_data contains supported files, it is used instead of the
checked-in fixtures.
Tests come from:
MIT License