A CSS parser, transformer, and minifier written in Rust with Node.js bindings
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Direct Rust library interface providing low-level CSS parsing, transformation, and serialization capabilities with zero-cost abstractions and memory safety.
Core stylesheet parsing, minification, and serialization with configurable options and error handling.
use lightningcss::{
stylesheet::{StyleSheet, MinifyOptions, ParserOptions, PrinterOptions, ToCssResult},
error::{Error, ParserError, MinifyErrorKind, PrinterErrorKind},
traits::{Parse, ToCss},
};
impl<'i, 'o, T> StyleSheet<'i, 'o, T>
where
T: crate::traits::AtRuleParser<'i>
{
/// Parse a CSS stylesheet from a string
pub fn parse(
css: &'i str,
options: ParserOptions<'o, 'i>
) -> Result<Self, Error<ParserError<'i>>>;
/// Minify the stylesheet in-place
pub fn minify(&mut self, options: MinifyOptions) -> Result<(), Error<MinifyErrorKind>>;
/// Serialize the stylesheet to CSS
pub fn to_css(&self, options: PrinterOptions) -> Result<ToCssResult, Error<PrinterErrorKind>>;
/// Get the rules in this stylesheet
pub fn rules(&self) -> &crate::rules::CssRuleList<'i, 'o, T>;
/// Get mutable access to the rules in this stylesheet
pub fn rules_mut(&mut self) -> &mut crate::rules::CssRuleList<'i, 'o, T>;
}
/// Configuration options for CSS parsing
pub struct ParserOptions<'o, 'i> {
/// The filename being parsed, used for error messages
pub filename: &'i str,
/// CSS Modules configuration
pub css_modules: Option<crate::css_modules::CssModulesConfig<'o>>,
/// Source index for source maps
pub source_index: u32,
/// Whether to recover from parse errors
pub error_recovery: bool,
/// Optional warnings collection
pub warnings: Option<&'o mut Vec<Error<ParserError<'i>>>>,
/// Parser feature flags
pub flags: crate::parser::ParserFlags,
}
/// Options for CSS minification
pub struct MinifyOptions {
/// Browser targets for optimization
pub targets: Option<crate::targets::Targets>,
/// List of unused symbols to remove
pub unused_symbols: std::collections::HashSet<String>,
}
/// Options for CSS serialization/printing
pub struct PrinterOptions<'a> {
/// Whether to minify the output
pub minify: bool,
/// Whether to include source map information
pub source_map: Option<&'a mut crate::sourcemap::SourceMap>,
/// Project root for source map paths
pub project_root: Option<&'a str>,
/// Browser targets for vendor prefixes
pub targets: Option<crate::targets::Targets>,
/// Whether to analyze dependencies
pub analyze_dependencies: Option<crate::dependencies::DependencyOptions<'a>>,
/// Pseudo-class replacements
pub pseudo_classes: Option<crate::properties::ui::PseudoClasses<'a>>,
}
/// Result of CSS serialization
pub struct ToCssResult {
/// The serialized CSS code
pub code: String,
/// Extracted dependencies, if enabled
pub dependencies: Option<Vec<crate::dependencies::Dependency>>,
/// CSS module exports, if enabled
pub exports: Option<crate::css_modules::CssModuleExports>,
/// CSS module references, if enabled
pub references: Option<crate::css_modules::CssModuleReferences>,
}Usage Examples:
use lightningcss::{
stylesheet::{StyleSheet, MinifyOptions, ParserOptions, PrinterOptions},
targets::Targets,
};
// Basic parsing and minification
fn process_css(css: &str) -> Result<String, Box<dyn std::error::Error>> {
let mut stylesheet = StyleSheet::parse(css, ParserOptions::default())?;
stylesheet.minify(MinifyOptions::default())?;
let result = stylesheet.to_css(PrinterOptions {
minify: true,
..PrinterOptions::default()
})?;
Ok(result.code)
}
// Advanced configuration
fn process_with_targets(css: &str) -> Result<String, Box<dyn std::error::Error>> {
let mut warnings = Vec::new();
let mut stylesheet = StyleSheet::parse(css, ParserOptions {
filename: "input.css",
error_recovery: true,
warnings: Some(&mut warnings),
..ParserOptions::default()
})?;
let targets = Targets {
chrome: Some(80 << 16),
firefox: Some(75 << 16),
safari: Some(13 << 16),
..Targets::default()
};
stylesheet.minify(MinifyOptions {
targets: Some(targets.clone()),
unused_symbols: ["unused-class", "old-animation"].iter().map(|s| s.to_string()).collect(),
})?;
let result = stylesheet.to_css(PrinterOptions {
minify: true,
targets: Some(targets),
..PrinterOptions::default()
})?;
// Handle warnings
for warning in warnings {
eprintln!("Warning: {:?}", warning);
}
Ok(result.code)
}Rust-native browser targeting for CSS transformation and vendor prefixing.
use lightningcss::targets::{Targets, Browsers};
/// Browser compatibility targets
#[derive(Debug, Clone, Default)]
pub struct Targets {
pub android: Option<u32>,
pub chrome: Option<u32>,
pub edge: Option<u32>,
pub firefox: Option<u32>,
pub ie: Option<u32>,
pub ios_saf: Option<u32>,
pub opera: Option<u32>,
pub safari: Option<u32>,
pub samsung: Option<u32>,
}
impl Targets {
/// Create targets from browser versions
pub fn new(browsers: &Browsers) -> Self;
/// Check if a specific feature is supported
pub fn is_compatible(&self, feature: crate::compat::Feature) -> bool;
/// Get the minimum browser versions
pub fn browsers(&self) -> Browsers;
}
/// Browser version specifications
#[derive(Debug, Clone, Default)]
pub struct Browsers {
pub android: Option<f32>,
pub chrome: Option<f32>,
pub edge: Option<f32>,
pub firefox: Option<f32>,
pub ie: Option<f32>,
pub ios_saf: Option<f32>,
pub opera: Option<f32>,
pub safari: Option<f32>,
pub samsung: Option<f32>,
}Usage Examples:
use lightningcss::targets::{Targets, Browsers};
// Direct target creation
let targets = Targets {
chrome: Some(90 << 16),
firefox: Some(88 << 16),
safari: Some(14 << 16),
..Targets::default()
};
// From browser versions
let browsers = Browsers {
chrome: Some(90.0),
firefox: Some(88.0),
safari: Some(14.0),
..Browsers::default()
};
let targets = Targets::new(&browsers);
// Feature compatibility checking
let supports_grid = targets.is_compatible(crate::compat::Feature::CssGrid);Rust API for bundling CSS files with dependency resolution and @import inlining.
#[cfg(feature = "bundler")]
use lightningcss::bundler::{Bundler, BundleErrorKind, FileProvider, SourceProvider};
/// CSS bundler for combining multiple files
pub struct Bundler<'a, 'o, 's, P, T>
where
P: SourceProvider,
T: crate::traits::AtRuleParser<'a>
{
/// Create a new bundler with a source provider
pub fn new(provider: &'s P, source_map: Option<&'s mut crate::sourcemap::SourceMap>) -> Self;
/// Bundle a CSS file and its dependencies
pub fn bundle(
&mut self,
fs_path: &std::path::Path,
options: crate::stylesheet::ParserOptions<'o, 'a>
) -> Result<crate::stylesheet::StyleSheet<'a, 'o, T>, BundleErrorKind<'a, std::io::Error>>;
}
/// Trait for providing CSS source content
pub trait SourceProvider: Send + Sync {
type Error: std::error::Error + Send + Sync + 'static;
/// Read CSS content from a file path
fn read<'a>(&'a self, file: &std::path::Path) -> Result<std::borrow::Cow<'a, str>, Self::Error>;
}
/// File system source provider
pub struct FileProvider {
/// Create a new file provider
pub fn new() -> Self;
}
impl SourceProvider for FileProvider {
type Error = std::io::Error;
fn read<'a>(&'a self, file: &std::path::Path) -> Result<std::borrow::Cow<'a, str>, Self::Error> {
std::fs::read_to_string(file).map(std::borrow::Cow::Owned)
}
}Usage Examples:
#[cfg(feature = "bundler")]
use lightningcss::bundler::{Bundler, FileProvider};
use lightningcss::stylesheet::{ParserOptions, PrinterOptions};
use std::path::Path;
#[cfg(feature = "bundler")]
fn bundle_css(entry_path: &Path) -> Result<String, Box<dyn std::error::Error>> {
let fs = FileProvider::new();
let mut bundler = Bundler::new(&fs, None);
let stylesheet = bundler.bundle(entry_path, ParserOptions::default())?;
let result = stylesheet.to_css(PrinterOptions {
minify: true,
..PrinterOptions::default()
})?;
Ok(result.code)
}
// Custom source provider
#[cfg(feature = "bundler")]
struct DatabaseProvider {
// Custom implementation
}
#[cfg(feature = "bundler")]
impl SourceProvider for DatabaseProvider {
type Error = Box<dyn std::error::Error>;
fn read<'a>(&'a self, file: &std::path::Path) -> Result<std::borrow::Cow<'a, str>, Self::Error> {
// Read from database, cache, etc.
let content = fetch_from_database(file)?;
Ok(std::borrow::Cow::Owned(content))
}
}Parse and transform individual CSS declaration lists for style attributes.
use lightningcss::stylesheet::StyleAttribute;
/// Parser for CSS declaration lists (style attributes)
pub struct StyleAttribute<'i> {
/// Parse a declaration list from a string
pub fn parse(
input: &'i str,
options: crate::stylesheet::ParserOptions<'_, 'i>
) -> Result<Self, crate::error::Error<crate::error::ParserError<'i>>>;
/// Minify the declarations in-place
pub fn minify(
&mut self,
options: crate::stylesheet::MinifyOptions
) -> Result<(), crate::error::Error<crate::error::MinifyErrorKind>>;
/// Serialize to CSS
pub fn to_css(
&self,
options: crate::stylesheet::PrinterOptions
) -> Result<crate::stylesheet::ToCssResult, crate::error::Error<crate::error::PrinterErrorKind>>;
/// Get the declarations
pub fn declarations(&self) -> &crate::declaration::DeclarationBlock<'i>;
/// Get mutable access to declarations
pub fn declarations_mut(&mut self) -> &mut crate::declaration::DeclarationBlock<'i>;
}Usage Examples:
use lightningcss::stylesheet::{StyleAttribute, ParserOptions, MinifyOptions, PrinterOptions};
fn process_style_attribute(style: &str) -> Result<String, Box<dyn std::error::Error>> {
let mut declarations = StyleAttribute::parse(style, ParserOptions::default())?;
declarations.minify(MinifyOptions::default())?;
let result = declarations.to_css(PrinterOptions {
minify: true,
..PrinterOptions::default()
})?;
Ok(result.code)
}
// Example: "color: red; background: blue; margin: 10px 20px 10px 20px"
// Result: "color:red;background:blue;margin:10px 20px"Comprehensive error types for different CSS processing stages.
use lightningcss::error::{Error, ErrorLocation, ParserError, MinifyErrorKind, PrinterErrorKind};
/// Generic error wrapper with location information
pub struct Error<T> {
/// The error kind
pub kind: T,
/// Source location where error occurred
pub loc: Option<ErrorLocation>,
}
/// Source location information
pub struct ErrorLocation {
/// Filename where error occurred
pub filename: String,
/// Line number (1-based)
pub line: u32,
/// Column number (1-based)
pub column: u32,
}
/// CSS parsing errors
pub enum ParserError<'i> {
/// Unexpected token during parsing
UnexpectedToken(cssparser::Token<'i>),
/// Unexpected EOF
UnexpectedEOF,
/// Invalid selector
SelectorError(crate::error::SelectorError<'i>),
/// Invalid at-rule
InvalidAtRule(std::borrow::Cow<'i, str>),
/// Invalid declaration
InvalidDeclaration,
/// Invalid value for property
InvalidValue,
/// Invalid media query
InvalidMediaQuery(cssparser::ParseError<'i, cssparser::BasicParseError<'i>>),
// ... more error variants
}
/// CSS minification errors
pub enum MinifyErrorKind {
/// Circular dependency in @import rules
CircularImport,
/// Invalid property value during minification
InvalidPropertyValue,
// ... more error variants
}
/// CSS printing/serialization errors
pub enum PrinterErrorKind {
/// I/O error during output
Io(std::io::Error),
/// Invalid UTF-8 in output
InvalidUtf8,
// ... more error variants
}Usage Examples:
use lightningcss::{
stylesheet::{StyleSheet, ParserOptions},
error::{Error, ParserError, ErrorLocation},
};
fn handle_css_errors(css: &str) {
match StyleSheet::parse(css, ParserOptions::default()) {
Ok(stylesheet) => {
println!("Parsed successfully");
}
Err(Error { kind, loc }) => {
match kind {
ParserError::UnexpectedToken(token) => {
println!("Unexpected token: {:?}", token);
}
ParserError::InvalidDeclaration => {
println!("Invalid CSS declaration");
}
ParserError::SelectorError(sel_err) => {
println!("Invalid selector: {:?}", sel_err);
}
_ => {
println!("Parse error: {:?}", kind);
}
}
if let Some(ErrorLocation { filename, line, column }) = loc {
println!(" at {}:{}:{}", filename, line, column);
}
}
}
}Core traits for parsing and serialization that can be implemented for custom types.
use lightningcss::traits::{Parse, ToCss};
/// Trait for parsing CSS values from tokens
pub trait Parse<'i>: Sized {
/// Parse this value from a CSS parser
fn parse<'t>(input: &mut cssparser::Parser<'i, 't>) -> Result<Self, cssparser::ParseError<'i, Self::Error>>;
/// Associated error type for parsing failures
type Error: 'i;
}
/// Trait for serializing values to CSS
pub trait ToCss {
/// Write this value as CSS to the given destination
fn to_css<W>(&self, dest: &mut crate::printer::Printer<W>) -> Result<(), crate::error::PrinterError>
where W: std::fmt::Write;
/// Convert to a CSS string
fn to_css_string(&self) -> Result<String, crate::error::PrinterError> {
let mut s = String::new();
let mut printer = crate::printer::Printer::new(&mut s, crate::printer::PrinterOptions::default());
self.to_css(&mut printer)?;
Ok(s)
}
}Usage Examples:
use lightningcss::traits::{Parse, ToCss};
use cssparser::{Parser, Token};
// Custom CSS value type
#[derive(Debug, Clone)]
struct CustomLength {
value: f32,
unit: String,
}
impl<'i> Parse<'i> for CustomLength {
type Error = ();
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, cssparser::ParseError<'i, Self::Error>> {
match input.next()? {
Token::Dimension { value, unit, .. } => {
Ok(CustomLength {
value: *value,
unit: unit.to_string(),
})
}
_ => Err(input.new_custom_error(())),
}
}
}
impl ToCss for CustomLength {
fn to_css<W>(&self, dest: &mut crate::printer::Printer<W>) -> Result<(), crate::error::PrinterError>
where W: std::fmt::Write {
write!(dest, "{}{}", self.value, self.unit)?;
Ok(())
}
}
// Usage
fn parse_custom_length(css: &str) -> Result<CustomLength, Box<dyn std::error::Error>> {
let mut input = cssparser::ParserInput::new(css);
let mut parser = cssparser::Parser::new(&mut input);
Ok(CustomLength::parse(&mut parser)?)
}