1pub mod arbitrary;
42pub mod ast_parser;
43pub mod class_scanner;
44pub mod classes;
45pub mod color;
46pub mod config;
47pub mod css_generator;
48pub mod css_optimizer;
49pub mod custom_variant;
50pub mod dark_mode;
51pub mod error;
52pub mod enhanced_variants;
54pub mod performance;
55pub mod plugin_system;
56#[cfg(feature = "postcss")]
57pub mod postcss_integration;
58pub mod responsive;
59
60pub mod theme;
61pub mod theme_new;
62pub mod tree_shaker;
63pub mod utilities;
64pub mod utils;
65pub mod validation;
66
67#[cfg(test)]
68mod api_stability;
69
70pub mod api_contracts;
72
73pub use arbitrary::{ArbitraryValue, ArbitraryValueError, ArbitraryValueUtilities};
75pub use ast_parser::AstParser;
76pub use class_scanner::{ClassScanner, ScanConfig, ScanResults, ScanStats};
77pub use classes::{ClassBuilder, ClassSet};
78pub use color::Color;
79pub use config::parser::ConfigParser;
80pub use config::{BuildConfig, TailwindConfig};
81pub use css_generator::{CssGenerationConfig, CssGenerator, CssProperty, CssRule};
83pub use css_optimizer::{OptimizationConfig, OptimizationResults, OptimizationStats};
84pub use custom_variant::{CustomVariant, CustomVariantManager, CustomVariantType};
85pub use dark_mode::{DarkModeVariant, DarkModeVariantError, DarkModeVariantUtilities};
86pub use error::{Result, TailwindError};
87pub use performance::{CacheStats, ClassCache, OptimizationLevel, PerformanceOptimizer};
89pub use plugin_system::{Plugin, PluginContext, PluginHook, PluginRegistry};
90pub use responsive::{
91 AlignItems, Breakpoint, FlexDirection, FlexWrap, JustifyContent, Responsive, ResponsiveBuilder,
92 ResponsiveFlex, ResponsiveGrid, ResponsiveValue, State,
93};
94pub use theme::{BorderRadius, BoxShadow, Spacing, Theme, ThemeValue};
95pub use theme_new::{
96 AnimationScale, BorderScale, FontFamily, FontSizeScale, FontWeightScale, LetterSpacingScale,
97 LineHeightScale, ShadowScale, SpacingScale, SpacingSize, Theme as NewTheme, ThemePreset,
98 ThemeVariant, ThemedComponent, TypographyScale,
99};
100pub use tree_shaker::{TreeShakeConfig, TreeShakeResults, TreeShakeStats, TreeShaker};
101pub use utilities::*;
102pub use validation::*;
103
104pub use enhanced_variants::{
105 CustomVariant as EnhancedCustomVariant, EnhancedVariantParser, ParsedVariant,
106 VariantCombination, VariantDefinition, VariantMetadata, VariantParseResult, VariantType,
107};
108
109#[cfg(feature = "postcss")]
110pub use postcss_integration::{EnhancedCssGenerator, EnhancedCssResult, PostCSSIntegrationConfig};
111
112pub fn generate_css_file(output_path: &str, classes: Option<&ClassSet>) -> Result<()> {
145 let mut generator = CssGenerator::new();
146
147 if let Some(class_set) = classes {
149 for class in &class_set.classes {
151 generator.add_class(class)?;
152 }
153
154 for (breakpoint, responsive_classes) in &class_set.responsive {
156 for class in responsive_classes {
157 generator.add_responsive_class(*breakpoint, class)?;
158 }
159 }
160
161 for (_condition, conditional_classes) in &class_set.conditional {
163 for class in conditional_classes {
164 generator.add_class(class)?;
167 }
168 }
169 } else {
170 let config = CssGenerationConfig::default();
172 generator.generate_comprehensive_css(&config)?;
173 }
174
175 let css = generator.generate_css();
177
178 if let Some(parent) = std::path::Path::new(output_path).parent() {
180 std::fs::create_dir_all(parent)?;
181 }
182
183 std::fs::write(output_path, css)?;
185
186 println!("β
CSS generated successfully at {}", output_path);
187 println!("π Generated {} CSS rules", generator.rule_count());
188
189 Ok(())
190}
191
192pub fn generate_comprehensive_css(output_path: &str, config: &CssGenerationConfig) -> Result<()> {
219 let mut generator = CssGenerator::new();
220
221 let css = generator.generate_comprehensive_css(config)?;
223
224 if let Some(parent) = std::path::Path::new(output_path).parent() {
226 std::fs::create_dir_all(parent)?;
227 }
228
229 std::fs::write(output_path, css)?;
231
232 println!(
233 "β
Comprehensive CSS generated successfully at {}",
234 output_path
235 );
236 println!("π Generated {} CSS rules", generator.rule_count());
237
238 Ok(())
239}
240
241#[cfg(test)]
242mod tests {
243 mod sync_api_tests;
244 use super::*;
247
248 #[test]
249 fn test_version_constant() {
250 assert!(!VERSION.is_empty());
251 assert!(VERSION.chars().any(|c| c.is_ascii_digit()));
252 }
253
254 #[test]
255 fn test_defaults() {
256 assert_eq!(defaults::DEFAULT_THEME, "default");
257 assert_eq!(defaults::DEFAULT_BREAKPOINT, Breakpoint::Base);
258 assert_eq!(defaults::default_color(), Color::Blue);
259 }
260}
261
262pub struct TailwindBuilder {
264 source_paths: Vec<std::path::PathBuf>,
265 output_path: Option<std::path::PathBuf>,
266 config_path: Option<std::path::PathBuf>,
267 tree_shaking: bool,
268 minification: bool,
269 source_maps: bool,
270}
271
272impl Default for TailwindBuilder {
273 fn default() -> Self {
274 Self::new()
275 }
276}
277
278impl TailwindBuilder {
279 pub fn new() -> Self {
280 Self {
281 source_paths: Vec::new(),
282 output_path: None,
283 config_path: None,
284 tree_shaking: false,
285 minification: false,
286 source_maps: false,
287 }
288 }
289
290 pub fn scan_source(mut self, path: &std::path::Path) -> Self {
291 self.source_paths.push(path.to_path_buf());
292 self
293 }
294
295 pub fn output_css(mut self, path: &std::path::Path) -> Self {
296 self.output_path = Some(path.to_path_buf());
297 self
298 }
299
300 pub fn config_file(mut self, path: &std::path::Path) -> Self {
301 self.config_path = Some(path.to_path_buf());
302 self
303 }
304
305 pub fn enable_tree_shaking(mut self) -> Self {
306 self.tree_shaking = true;
307 self
308 }
309
310 pub fn enable_minification(mut self) -> Self {
311 self.minification = true;
312 self
313 }
314
315 pub fn enable_source_maps(mut self) -> Self {
316 self.source_maps = true;
317 self
318 }
319
320 pub fn build(self) -> Result<()> {
321 let mut generator = CssGenerator::new();
323
324 if !self.source_paths.is_empty() {
326 for path in &self.source_paths {
327 if path.is_file() {
328 self.scan_file_for_classes(path, &mut generator)?;
329 } else if path.is_dir() {
330 self.scan_directory_for_classes(path, &mut generator)?;
331 }
332 }
333 } else {
334 generator.add_class("p-4")?;
336 generator.add_class("bg-blue-500")?;
337 generator.add_class("text-white")?;
338 generator.add_class("rounded-md")?;
339 }
340
341 let css = if self.minification {
343 generator.generate_minified_css()
344 } else {
345 generator.generate_css()
346 };
347
348 let output_path = self
350 .output_path
351 .unwrap_or_else(|| std::path::PathBuf::from("dist/styles.css"));
352
353 if let Some(parent) = output_path.parent() {
355 std::fs::create_dir_all(parent)?;
356 }
357
358 std::fs::write(&output_path, css)?;
360
361 println!("β
CSS generated successfully at {}", output_path.display());
362 println!("π Generated {} CSS rules", generator.rule_count());
363
364 if self.tree_shaking {
365 println!("π³ Tree shaking enabled");
366 }
367
368 if self.minification {
369 println!("ποΈ Minification enabled");
370 }
371
372 if self.source_maps {
373 println!("πΊοΈ Source maps enabled");
374 }
375
376 Ok(())
377 }
378
379 fn scan_file_for_classes(
381 &self,
382 path: &std::path::Path,
383 generator: &mut CssGenerator,
384 ) -> Result<()> {
385 let content = std::fs::read_to_string(path)?;
386
387 let class_pattern = regex::Regex::new(r#"class\s*=\s*["']([^"']+)["']"#)?;
389
390 for cap in class_pattern.captures_iter(&content) {
391 if let Some(class_attr) = cap.get(1) {
392 let classes = class_attr.as_str();
393 for class in classes.split_whitespace() {
394 if !class.is_empty() {
395 let _ = generator.add_class(class);
396 }
397 }
398 }
399 }
400
401 Ok(())
402 }
403
404 fn scan_directory_for_classes(
406 &self,
407 dir: &std::path::Path,
408 generator: &mut CssGenerator,
409 ) -> Result<()> {
410 for entry in std::fs::read_dir(dir)? {
411 let entry = entry?;
412 let path = entry.path();
413
414 if path.is_file() {
415 if let Some(ext) = path.extension() {
416 if ext == "rs"
417 || ext == "html"
418 || ext == "js"
419 || ext == "ts"
420 || ext == "jsx"
421 || ext == "tsx"
422 {
423 self.scan_file_for_classes(&path, generator)?;
424 }
425 }
426 } else if path.is_dir() {
427 self.scan_directory_for_classes(&path, generator)?;
428 }
429 }
430
431 Ok(())
432 }
433}
434
435pub const VERSION: &str = env!("CARGO_PKG_VERSION");
437
438pub mod defaults {
440 use super::*;
441
442 pub const DEFAULT_THEME: &str = "default";
443 pub const DEFAULT_BREAKPOINT: Breakpoint = Breakpoint::Base;
444 pub const DEFAULT_SPACING: Spacing = Spacing::Rem(1.0);
445
446 pub fn default_color() -> Color {
447 Color::Blue
448 }
449}