Files
leptos-shadcn-ui/docs/remediation/component-fixes/bundle-analysis-implementation.md
Peter Hanssens f6a72352c0 feat: Complete file size optimization - refactor 10 large files into 55 focused modules
- Refactored 6,741 lines across 10 large files into 55 focused modules
- All modules now under 300 lines for better LLM comprehension and maintainability
- Maintained full test coverage and functionality

Files refactored:
- packages/leptos/input/src/implementation_tests.rs (867 lines) → 6 modules
- packages/leptos/form/src/implementation_tests.rs (783 lines) → 5 modules
- packages/signal-management/src/signal_management_tests.rs (766 lines) → 7 modules
- packages/signal-management/src/simple_tests.rs (753 lines) → 7 modules
- packages/signal-management/src/lifecycle_tests.rs (648 lines) → 5 modules
- packages/leptos/input/src/tdd_tests.rs (663 lines) → 6 modules
- packages/leptos/command/src/tdd_tests.rs (607 lines) → 5 modules
- packages/signal-management/src/memory_management_tests.rs (554 lines) → 5 modules
- packages/signal-management/src/component_migration.rs (541 lines) → 4 modules
- packages/leptos/button/src/tdd_tests.rs (560 lines) → 5 modules

Added comprehensive remediation documentation in docs/remediation/
All tests passing - 132 tests for button component alone
2025-09-19 20:57:55 +10:00

9.2 KiB

🔧 Bundle Analysis Implementation Design

Component: leptos-shadcn-performance-audit
Priority: 🟡 HIGH
Issues: 3 todo! implementations in bundle analysis
Timeline: 3-4 days

🚨 Stub Code Issues

Missing Implementations

// File: performance-audit/src/bundle_analysis.rs
// Line 179:
todo!("Implement component bundle analysis")

// Line 185:
todo!("Implement single component analysis")

// Line 191:
todo!("Implement bundle size extraction")

🎯 Implementation Strategy

Phase 1: Component Bundle Analysis

1.1 Implement Bundle Analysis Core

// File: performance-audit/src/bundle_analysis.rs
use std::path::Path;
use std::fs;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComponentBundleInfo {
    pub component_name: String,
    pub bundle_size: u64,
    pub dependencies: Vec<String>,
    pub exports: Vec<String>,
    pub file_path: String,
}

impl BundleAnalyzer {
    pub fn analyze_component_bundles(&self, components_path: &Path) -> Result<Vec<ComponentBundleInfo>, BundleAnalysisError> {
        let mut bundle_info = Vec::new();
        
        // Scan for component directories
        let entries = fs::read_dir(components_path)?;
        
        for entry in entries {
            let entry = entry?;
            let path = entry.path();
            
            if path.is_dir() {
                if let Some(component_name) = path.file_name().and_then(|n| n.to_str()) {
                    if component_name.starts_with("leptos-shadcn-") {
                        let info = self.analyze_single_component(&path, component_name)?;
                        bundle_info.push(info);
                    }
                }
            }
        }
        
        Ok(bundle_info)
    }
}

1.2 Component Analysis Logic

impl BundleAnalyzer {
    fn analyze_single_component(&self, component_path: &Path, component_name: &str) -> Result<ComponentBundleInfo, BundleAnalysisError> {
        let mut bundle_size = 0;
        let mut dependencies = Vec::new();
        let mut exports = Vec::new();
        
        // Analyze Cargo.toml for dependencies
        let cargo_toml = component_path.join("Cargo.toml");
        if cargo_toml.exists() {
            let cargo_content = fs::read_to_string(&cargo_toml)?;
            dependencies = self.extract_dependencies(&cargo_content);
        }
        
        // Analyze source files for exports
        let src_path = component_path.join("src");
        if src_path.exists() {
            exports = self.extract_exports(&src_path)?;
            bundle_size = self.calculate_bundle_size(&src_path)?;
        }
        
        Ok(ComponentBundleInfo {
            component_name: component_name.to_string(),
            bundle_size,
            dependencies,
            exports,
            file_path: component_path.to_string_lossy().to_string(),
        })
    }
}

Phase 2: Single Component Analysis

2.1 Detailed Component Analysis

impl BundleAnalyzer {
    pub fn analyze_single_component_detailed(&self, component_path: &Path) -> Result<DetailedComponentAnalysis, BundleAnalysisError> {
        let mut analysis = DetailedComponentAnalysis::new();
        
        // Analyze source code structure
        analysis.source_files = self.analyze_source_files(component_path)?;
        analysis.dependencies = self.analyze_dependencies(component_path)?;
        analysis.exports = self.analyze_exports(component_path)?;
        analysis.imports = self.analyze_imports(component_path)?;
        
        // Calculate metrics
        analysis.total_lines = self.count_total_lines(&analysis.source_files);
        analysis.complexity_score = self.calculate_complexity(&analysis.source_files);
        analysis.bundle_size_estimate = self.estimate_bundle_size(&analysis);
        
        Ok(analysis)
    }
}

2.2 Source File Analysis

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SourceFileAnalysis {
    pub file_path: String,
    pub lines_of_code: usize,
    pub functions: Vec<String>,
    pub structs: Vec<String>,
    pub enums: Vec<String>,
    pub imports: Vec<String>,
    pub exports: Vec<String>,
}

impl BundleAnalyzer {
    fn analyze_source_files(&self, component_path: &Path) -> Result<Vec<SourceFileAnalysis>, BundleAnalysisError> {
        let mut analyses = Vec::new();
        let src_path = component_path.join("src");
        
        if src_path.exists() {
            self.analyze_directory_recursive(&src_path, &mut analyses)?;
        }
        
        Ok(analyses)
    }
    
    fn analyze_directory_recursive(&self, dir: &Path, analyses: &mut Vec<SourceFileAnalysis>) -> Result<(), BundleAnalysisError> {
        let entries = fs::read_dir(dir)?;
        
        for entry in entries {
            let entry = entry?;
            let path = entry.path();
            
            if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("rs") {
                let analysis = self.analyze_rust_file(&path)?;
                analyses.push(analysis);
            } else if path.is_dir() {
                self.analyze_directory_recursive(&path, analyses)?;
            }
        }
        
        Ok(())
    }
}

Phase 3: Bundle Size Extraction

3.1 Bundle Size Calculation

impl BundleAnalyzer {
    pub fn extract_bundle_sizes(&self, components_path: &Path) -> Result<BundleSizeReport, BundleAnalysisError> {
        let mut report = BundleSizeReport::new();
        
        // Analyze each component
        let components = self.analyze_component_bundles(components_path)?;
        
        for component in components {
            let size_info = BundleSizeInfo {
                component_name: component.component_name.clone(),
                estimated_size: component.bundle_size,
                dependencies_size: self.calculate_dependencies_size(&component.dependencies)?,
                total_size: component.bundle_size + self.calculate_dependencies_size(&component.dependencies)?,
                optimization_potential: self.assess_optimization_potential(&component),
            };
            
            report.components.push(size_info);
        }
        
        // Calculate totals
        report.total_bundle_size = report.components.iter().map(|c| c.total_size).sum();
        report.average_component_size = report.total_bundle_size / report.components.len() as u64;
        report.largest_component = report.components.iter().max_by_key(|c| c.total_size).cloned();
        
        Ok(report)
    }
}

3.2 Size Estimation Logic

impl BundleAnalyzer {
    fn calculate_bundle_size(&self, src_path: &Path) -> Result<u64, BundleAnalysisError> {
        let mut total_size = 0;
        
        // Count lines of code as proxy for bundle size
        let source_files = self.get_rust_files(src_path)?;
        
        for file in source_files {
            let content = fs::read_to_string(&file)?;
            let lines = content.lines().count();
            
            // Rough estimation: 1 line ≈ 50 bytes in compiled WASM
            total_size += lines as u64 * 50;
        }
        
        Ok(total_size)
    }
    
    fn calculate_dependencies_size(&self, dependencies: &[String]) -> Result<u64, BundleAnalysisError> {
        // Estimate dependency sizes based on known packages
        let mut total_size = 0;
        
        for dep in dependencies {
            let estimated_size = match dep.as_str() {
                "leptos" => 50000,  // ~50KB
                "leptos_router" => 20000,  // ~20KB
                "serde" => 15000,  // ~15KB
                "web-sys" => 10000,  // ~10KB
                _ => 5000,  // Default estimate
            };
            
            total_size += estimated_size;
        }
        
        Ok(total_size)
    }
}

📋 Implementation Steps

Step 1: Core Bundle Analysis (Day 1)

// 1. Implement analyze_component_bundles method
// 2. Add ComponentBundleInfo struct
// 3. Add error handling
// 4. Test with sample components

Step 2: Single Component Analysis (Day 2)

// 1. Implement analyze_single_component_detailed method
// 2. Add source file analysis
// 3. Add dependency analysis
// 4. Test detailed analysis

Step 3: Bundle Size Extraction (Day 3)

// 1. Implement extract_bundle_sizes method
// 2. Add size calculation logic
// 3. Add optimization assessment
// 4. Test size reporting

Step 4: Integration and Testing (Day 4)

// 1. Integrate all methods
// 2. Add comprehensive tests
// 3. Test CLI integration
// 4. Verify performance

🧪 Testing Strategy

Unit Tests

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_component_bundle_analysis() {
        let analyzer = BundleAnalyzer::new();
        let result = analyzer.analyze_component_bundles(Path::new("packages/leptos"));
        assert!(result.is_ok());
    }
    
    #[test]
    fn test_single_component_analysis() {
        let analyzer = BundleAnalyzer::new();
        let result = analyzer.analyze_single_component_detailed(Path::new("packages/leptos/button"));
        assert!(result.is_ok());
    }