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