AST Processing API β
Learning Path
This is Step 4 in the BaroCSS API learning path. Advanced concepts for understanding internal mechanisms.
Conceptual Importance
While you may not use these functions directly in your applications, understanding the AST processing pipeline is crucial for comprehending how BaroCSS transforms utility classes into optimized CSS. This knowledge helps you understand the internal workings and make informed decisions about utility design and CSS generation.
π― What You'll Learn β
- How ASTs are processed and optimized
- Internal functions used by the CSS generation process
- Complex examples of multi-value utilities
- Deep understanding of BaroCSS internals
π Prerequisites β
- Context API - Understanding contexts
- Engine API - Understanding CSS generation
The AST Processing API provides advanced functions for manipulating and optimizing Abstract Syntax Trees in BaroCSS. These functions are used internally by the CSS generation process but are also available for advanced use cases.
Core Functions β
collectDeclPaths() β
Purpose
Collects all paths from AST tree to declaration nodes (leaves), including variant chains.
import { collectDeclPaths } from '@barocss/kit';
const declPaths = collectDeclPaths(ast);Parameters:
nodes(AstNode[]): AST tree to processpath(PathNode[]): Recursive use (initial value omitted)
Returns:
DeclPath[]: Array of paths to each declaration
What it does:
- Extracts all declaration paths from complex AST structures
- Used for path extraction for AST optimization/merging
- Creates flat list of all declaration-to-root paths
Example:
const ast = [
{
type: "rule",
selector: "&:hover",
source: "pseudo",
nodes: [
{ type: "decl", prop: "background-color", value: "#3b82f6" }
]
}
];
const declPaths = collectDeclPaths(ast);
// Returns: [
// [
// { type: "rule", selector: "&:hover", source: "pseudo" },
// { type: "decl", prop: "background-color", value: "#3b82f6" }
// ]
// ]declPathToAst() β
Purpose
Converts a declaration-to-root path (variant chain) to actual nested AST structure.
import { declPathToAst } from '@barocss/kit';
const ast = declPathToAst(declPath);Parameters:
declPath(DeclPath): Declaration path fromcollectDeclPaths
Returns:
AstNode[]: Nested AST structure
Key Features
- Sorting: Variants are sorted by priority (at-rule > style-rule > rule > decl)
- Merging: Consecutive same variants are merged
- Nesting: Variants are nested in outsideβinside order
- Selector Merging: Multiple selectors are merged by source
Example:
const declPath = [
{ type: "rule", selector: "&:hover", source: "pseudo" },
{ type: "decl", prop: "background-color", value: "#3b82f6" }
];
const ast = declPathToAst(declPath);
// Returns: [
// {
// type: "rule",
// selector: "&:hover",
// source: "pseudo",
// nodes: [
// { type: "decl", prop: "background-color", value: "#3b82f6" }
// ]
// }
// ]mergeAstTreeList() β
Purpose
Takes a list of declPathToAst results and merges them into a single optimized AST tree.
import { mergeAstTreeList } from '@barocss/kit';
const merged = mergeAstTreeList(astList);Parameters:
astList(AstNode[][]): Array of AST trees fromdeclPathToAst
Returns:
AstNode[]: Merged and optimized AST tree
Optimization Features
- Deduplication: Removes duplicate rules and declarations
- Grouping: Groups similar rules together
- Merging: Merges compatible selectors and properties
- Structure: Maintains proper CSS nesting structure
Example:
const astList = [
[
{ type: "rule", selector: "&:hover", nodes: [...] }
],
[
{ type: "rule", selector: "&:focus", nodes: [...] }
]
];
const merged = mergeAstTreeList(astList);
// Returns: Merged AST with both hover and focus rulesoptimizeAst() β
Main Function
The main function that orchestrates the entire AST optimization process.
import { optimizeAst } from '@barocss/kit';
const optimized = optimizeAst(ast);Parameters:
ast(AstNode[]): Raw AST fromparseClassToAst
Returns:
AstNode[]: Optimized AST tree
Process Steps
- Calls
collectDeclPaths()to extract declaration paths - Calls
declPathToAst()for each path to create nested structures - Calls
mergeAstTreeList()to merge and optimize the final tree
Example:
const ast = parseClassToAst('hover:bg-blue-500 focus:ring-2', ctx);
const optimized = optimizeAst(ast);
// Returns: Optimized AST with merged hover and focus rulesComplex AST Processing Example β
Advanced Example
Let's walk through how optimizeAst() processes a complex AST with multiple variants and utilities. This example demonstrates the real-world complexity that BaroCSS handles internally.
Input: Complex Utility Classes β
// Input utility classes: "sm:dark:hover:bg-linear-to-r from-blue-500 to-red-500 focus:ring-2"
const ast = parseClassToAst('sm:dark:hover:bg-linear-to-r from-blue-500 to-red-500 focus:ring-2', ctx);Step 1: Raw AST from parseClassToAst β
// Raw AST structure - Multiple values exist simultaneously in a single utility
const rawAst = [
{
type: "rule",
selector: "&:hover",
source: "pseudo",
nodes: [
{
type: "at-rule",
name: "media",
params: "(min-width: 640px)",
source: "responsive",
nodes: [
{
type: "rule",
selector: ".dark &",
source: "dark",
nodes: [
// bg-linear-to-r utility - Multiple CSS properties and variable definitions
{
type: "at-root",
nodes: [
{ type: "property", prop: "--baro-gradient-position" },
{ type: "property", prop: "--baro-gradient-from", value: "#0000", syntax: "<color>" },
{ type: "property", prop: "--baro-gradient-via", value: "#0000", syntax: "<color>" },
{ type: "property", prop: "--baro-gradient-to", value: "#0000", syntax: "<color>" },
{ type: "property", prop: "--baro-gradient-stops", value: "transparent" },
{ type: "property", prop: "--baro-gradient-from-position", value: "0%", syntax: "<length-percentage>" },
{ type: "property", prop: "--baro-gradient-via-position", value: "50%", syntax: "<length-percentage>" },
{ type: "property", prop: "--baro-gradient-to-position", value: "100%", syntax: "<length-percentage>" }
]
},
{
type: "decl",
prop: "--baro-gradient-position",
value: "to right"
},
{
type: "style-rule",
selector: "@supports (background-image: linear-gradient(in lab, red, red))",
nodes: [
{
type: "decl",
prop: "--baro-gradient-position",
value: "to right in oklab"
}
]
},
{
type: "decl",
prop: "background-image",
value: "linear-gradient(to right, var(--baro-gradient-stops))"
}
]
}
]
}
]
},
// from-blue-500 utility - Gradient stop definition
{
type: "at-root",
nodes: [
{ type: "property", prop: "--baro-gradient-position" },
{ type: "property", prop: "--baro-gradient-from", value: "#0000", syntax: "<color>" },
{ type: "property", prop: "--baro-gradient-via", value: "#0000", syntax: "<color>" },
{ type: "property", prop: "--baro-gradient-to", value: "#0000", syntax: "<color>" },
{ type: "property", prop: "--baro-gradient-stops", value: "transparent" },
{ type: "property", prop: "--baro-gradient-from-position", value: "0%", syntax: "<length-percentage>" },
{ type: "property", prop: "--baro-gradient-via-position", value: "50%", syntax: "<length-percentage>" },
{ type: "property", prop: "--baro-gradient-to-position", value: "100%", syntax: "<length-percentage>" }
]
},
{
type: "decl",
prop: "--baro-gradient-from",
value: "#3b82f6"
},
{
type: "decl",
prop: "--baro-gradient-to",
value: "transparent"
},
{
type: "decl",
prop: "--baro-gradient-stops",
value: "var(--baro-gradient-from),var(--baro-gradient-to)"
},
// to-red-500 utility - Gradient stop definition
{
type: "at-root",
nodes: [
{ type: "property", prop: "--baro-gradient-position" },
{ type: "property", prop: "--baro-gradient-from", value: "#0000", syntax: "<color>" },
{ type: "property", prop: "--baro-gradient-via", value: "#0000", syntax: "<color>" },
{ type: "property", prop: "--baro-gradient-to", value: "#0000", syntax: "<color>" },
{ type: "property", prop: "--baro-gradient-stops", value: "transparent" },
{ type: "property", prop: "--baro-gradient-from-position", value: "0%", syntax: "<length-percentage>" },
{ type: "property", prop: "--baro-gradient-via-position", value: "50%", syntax: "<length-percentage>" },
{ type: "property", prop: "--baro-gradient-to-position", value: "100%", syntax: "<length-percentage>" }
]
},
{
type: "decl",
prop: "--baro-gradient-to",
value: "#ef4444"
},
// focus:ring-2 utility
{
type: "rule",
selector: "&:focus",
source: "pseudo",
nodes: [
{
type: "decl",
prop: "box-shadow",
value: "0 0 0 2px #3b82f6"
}
]
}
];Step 2: collectDeclPaths() - Extract Declaration Paths β
const declPaths = collectDeclPaths(rawAst);
// Returns: [
// // Multiple declarations from bg-linear-to-r
// [
// { type: "rule", selector: "&:hover", source: "pseudo" },
// { type: "at-rule", name: "media", params: "(min-width: 640px)", source: "responsive" },
// { type: "rule", selector: ".dark &", source: "dark" },
// { type: "at-root", nodes: [...] },
// { type: "decl", prop: "--baro-gradient-position", value: "to right" }
// ],
// [
// { type: "rule", selector: "&:hover", source: "pseudo" },
// { type: "at-rule", name: "media", params: "(min-width: 640px)", source: "responsive" },
// { type: "rule", selector: ".dark &", source: "dark" },
// { type: "style-rule", selector: "@supports (background-image: linear-gradient(in lab, red, red))" },
// { type: "decl", prop: "--baro-gradient-position", value: "to right in oklab" }
// ],
// [
// { type: "rule", selector: "&:hover", source: "pseudo" },
// { type: "at-rule", name: "media", params: "(min-width: 640px)", source: "responsive" },
// { type: "rule", selector: ".dark &", source: "dark" },
// { type: "decl", prop: "background-image", value: "linear-gradient(to right, var(--baro-gradient-stops))" }
// ],
// // Declarations from from-blue-500
// [
// { type: "at-root", nodes: [...] },
// { type: "decl", prop: "--baro-gradient-from", value: "#3b82f6" }
// ],
// [
// { type: "at-root", nodes: [...] },
// { type: "decl", prop: "--baro-gradient-to", value: "transparent" }
// ],
// [
// { type: "at-root", nodes: [...] },
// { type: "decl", prop: "--baro-gradient-stops", value: "var(--baro-gradient-from),var(--baro-gradient-to)" }
// ],
// // Declarations from to-red-500
// [
// { type: "at-root", nodes: [...] },
// { type: "decl", prop: "--baro-gradient-to", value: "#ef4444" }
// ],
// // Declaration from focus:ring-2
// [
// { type: "rule", selector: "&:focus", source: "pseudo" },
// { type: "decl", prop: "box-shadow", value: "0 0 0 2px #3b82f6" }
// ]
// ]Step 3: declPathToAst() - Convert to Nested Structures β
const astList = declPaths.map(declPathToAst);
// Returns: [
// // Nested structure from bg-linear-to-r
// [
// {
// type: "at-rule",
// name: "media",
// params: "(min-width: 640px)",
// source: "responsive",
// nodes: [
// {
// type: "rule",
// selector: ".dark &:hover",
// source: "dark",
// nodes: [
// {
// type: "at-root",
// nodes: [
// { type: "property", prop: "--baro-gradient-position" },
// { type: "property", prop: "--baro-gradient-from", value: "#0000", syntax: "<color>" },
// // ... other gradient variables
// ]
// },
// {
// type: "decl",
// prop: "--baro-gradient-position",
// value: "to right"
// },
// {
// type: "style-rule",
// selector: "@supports (background-image: linear-gradient(in lab, red, red))",
// nodes: [
// {
// type: "decl",
// prop: "--baro-gradient-position",
// value: "to right in oklab"
// }
// ]
// },
// {
// type: "decl",
// prop: "background-image",
// value: "linear-gradient(to right, var(--baro-gradient-stops))"
// }
// ]
// }
// ]
// }
// ],
// // Nested structure from from-blue-500
// [
// {
// type: "at-root",
// nodes: [
// { type: "property", prop: "--baro-gradient-position" },
// { type: "property", prop: "--baro-gradient-from", value: "#0000", syntax: "<color>" },
// // ... other gradient variables
// ]
// },
// {
// type: "decl",
// prop: "--baro-gradient-from",
// value: "#3b82f6"
// }
// ],
// // Nested structure from to-red-500
// [
// {
// type: "at-root",
// nodes: [
// { type: "property", prop: "--baro-gradient-position" },
// { type: "property", prop: "--baro-gradient-from", value: "#0000", syntax: "<color>" },
// // ... other gradient variables
// ]
// },
// {
// type: "decl",
// prop: "--baro-gradient-to",
// value: "#ef4444"
// }
// ],
// // Nested structure from focus:ring-2
// [
// {
// type: "rule",
// selector: "&:focus",
// source: "pseudo",
// nodes: [
// {
// type: "decl",
// prop: "box-shadow",
// value: "0 0 0 2px #3b82f6"
// }
// ]
// }
// ]
// ]Step 4: mergeAstTreeList() - Merge and Optimize β
const merged = mergeAstTreeList(astList);
// Returns: [
// // Gradient variable definitions (placed at top level with at-root)
// {
// type: "at-root",
// nodes: [
// { type: "property", prop: "--baro-gradient-position" },
// { type: "property", prop: "--baro-gradient-from", value: "#0000", syntax: "<color>" },
// { type: "property", prop: "--baro-gradient-via", value: "#0000", syntax: "<color>" },
// { type: "property", prop: "--baro-gradient-to", value: "#0000", syntax: "<color>" },
// { type: "property", prop: "--baro-gradient-stops", value: "transparent" },
// { type: "property", prop: "--baro-gradient-from-position", value: "0%", syntax: "<length-percentage>" },
// { type: "property", prop: "--baro-gradient-via-position", value: "50%", syntax: "<length-percentage>" },
// { type: "property", prop: "--baro-gradient-to-position", value: "100%", syntax: "<length-percentage>" }
// ]
// },
// // Gradient stops from from-blue-500
// {
// type: "decl",
// prop: "--baro-gradient-from",
// value: "#3b82f6"
// },
// {
// type: "decl",
// prop: "--baro-gradient-to",
// value: "transparent"
// },
// {
// type: "decl",
// prop: "--baro-gradient-stops",
// value: "var(--baro-gradient-from),var(--baro-gradient-to)"
// },
// // Gradient stops from to-red-500 (overwrites existing to value)
// {
// type: "decl",
// prop: "--baro-gradient-to",
// value: "#ef4444"
// },
// // Media query and gradient background
// {
// type: "at-rule",
// name: "media",
// params: "(min-width: 640px)",
// source: "responsive",
// nodes: [
// {
// type: "rule",
// selector: ".dark &:hover",
// source: "dark",
// nodes: [
// {
// type: "decl",
// prop: "--baro-gradient-position",
// value: "to right"
// },
// {
// type: "style-rule",
// selector: "@supports (background-image: linear-gradient(in lab, red, red))",
// nodes: [
// {
// type: "decl",
// prop: "--baro-gradient-position",
// value: "to right in oklab"
// }
// ]
// },
// {
// type: "decl",
// prop: "background-image",
// value: "linear-gradient(to right, var(--baro-gradient-stops))"
// }
// ]
// }
// ]
// },
// // focus:ring-2
// {
// type: "rule",
// selector: "&:focus",
// source: "pseudo",
// nodes: [
// {
// type: "decl",
// prop: "box-shadow",
// value: "0 0 0 2px #3b82f6"
// }
// ]
// }
// ]Step 5: Final CSS Output β
const css = astToCss(merged);
// Output:
// @property --baro-gradient-position;
// @property --baro-gradient-from {
// syntax: "<color>";
// initial-value: #0000;
// }
// @property --baro-gradient-via {
// syntax: "<color>";
// initial-value: #0000;
// }
// @property --baro-gradient-to {
// syntax: "<color>";
// initial-value: #0000;
// }
// @property --baro-gradient-stops {
// initial-value: transparent;
// }
// @property --baro-gradient-from-position {
// syntax: "<length-percentage>";
// initial-value: 0%;
// }
// @property --baro-gradient-via-position {
// syntax: "<length-percentage>";
// initial-value: 50%;
// }
// @property --baro-gradient-to-position {
// syntax: "<length-percentage>";
// initial-value: 100%;
// }
//
// --baro-gradient-from: #3b82f6;
// --baro-gradient-to: transparent;
// --baro-gradient-stops: var(--baro-gradient-from),var(--baro-gradient-to);
// --baro-gradient-to: #ef4444;
//
// @media (min-width: 640px) {
// .dark .sm\\:dark\\:hover\\:bg-linear-to-r:hover {
// --baro-gradient-position: to right;
// }
// .dark .sm\\:dark\\:hover\\:bg-linear-to-r:hover {
// --baro-gradient-position: to right in oklab;
// }
// .dark .sm\\:dark\\:hover\\:bg-linear-to-r:hover {
// background-image: linear-gradient(to right, var(--baro-gradient-stops));
// }
// }
// .focus\\:ring-2:focus {
// box-shadow: 0 0 0 2px #3b82f6;
// }Key Transformations
- Multi-Value Utilities:
bg-linear-to-rcreates multiple CSS properties and variables - CSS Custom Properties:
@propertydeclarations for gradient variables with syntax definitions - Variable Dependencies:
from-blue-500andto-red-500work together through shared variables - Variant Nesting:
sm:dark:hoverbecomes nested@mediaβ.dark &β&:hover - Selector Merging: Multiple selectors are properly merged (
.dark &:hover) - Source Priority: Variants are sorted by priority (at-rule > style-rule > rule > decl)
- Class Name Generation: Final class names are generated with proper escaping
- CSS Structure: Proper CSS nesting, media queries, and @supports rules
Why This Complexity is Needed
The @background.ts example shows why optimizeAst() is essential:
- Single Utility, Multiple Values:
bg-linear-to-rgenerates 8+ CSS properties and variables - Interdependent Variables:
from-*andto-*utilities share the same CSS custom properties - Progressive Enhancement:
@supportsrules for modern CSS features - Variable Overrides: Later utilities can override earlier variable values
- Structural Organization:
@propertydeclarations must be at the root level
This example demonstrates how optimizeAst() transforms complex utility classes with multiple interdependent values into well-structured, optimized CSS output.