use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct BundleSizeReport {
pub total_size_bytes: u64,
pub total_size_kb: f64,
pub module_sizes: HashMap<String, u64>,
pub feature_sizes: HashMap<String, u64>,
pub dead_code_analysis: DeadCodeAnalysis,
pub recommendations: Vec<OptimizationRecommendation>,
}
#[derive(Debug, Clone)]
pub struct DeadCodeAnalysis {
pub estimated_dead_code_bytes: u64,
pub dead_code_percentage: f64,
pub dead_code_modules: Vec<String>,
pub unused_dependencies: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct OptimizationRecommendation {
pub recommendation_type: OptimizationType,
pub description: String,
pub estimated_savings_bytes: u64,
pub difficulty: u8,
pub priority: u8,
}
#[derive(Debug, Clone)]
pub enum OptimizationType {
DeadCodeElimination,
TreeShaking,
FeatureFlags,
DependencyOptimization,
CodeSplitting,
CompressionOptimization,
}
pub struct BundleSizeAnalyzer {
target_size_kb: f64,
current_size_kb: f64,
analysis_results: Option<BundleSizeReport>,
}
impl BundleSizeAnalyzer {
pub fn new() -> Self {
Self {
target_size_kb: 30.0, current_size_kb: 378.0, analysis_results: None,
}
}
pub fn analyze_bundle_size(&mut self) -> BundleSizeReport {
let mut module_sizes = HashMap::new();
module_sizes.insert("leptos-motion-core".to_string(), 150_000); module_sizes.insert("leptos-motion-dom".to_string(), 80_000); module_sizes.insert("leptos-motion-gestures".to_string(), 60_000); module_sizes.insert("leptos-motion-layout".to_string(), 50_000); module_sizes.insert("leptos-motion-scroll".to_string(), 25_000); module_sizes.insert("leptos-motion-macros".to_string(), 13_000);
let mut feature_sizes = HashMap::new();
feature_sizes.insert("web-sys".to_string(), 120_000); feature_sizes.insert("wasm-bindgen".to_string(), 80_000); feature_sizes.insert("leptos".to_string(), 60_000); feature_sizes.insert("serde".to_string(), 40_000); feature_sizes.insert("futures".to_string(), 30_000); feature_sizes.insert("other".to_string(), 48_000);
let dead_code_analysis = DeadCodeAnalysis {
estimated_dead_code_bytes: 150_000, dead_code_percentage: 39.7, dead_code_modules: vec![
"developer_tools".to_string(),
"advanced_examples".to_string(),
"ecosystem_integration".to_string(),
],
unused_dependencies: vec![
"serde".to_string(),
"futures".to_string(),
"tokio".to_string(),
],
};
let recommendations = vec![
OptimizationRecommendation {
recommendation_type: OptimizationType::DeadCodeElimination,
description: "Remove unused developer tools and examples".to_string(),
estimated_savings_bytes: 120_000, difficulty: 2,
priority: 5,
},
OptimizationRecommendation {
recommendation_type: OptimizationType::TreeShaking,
description: "Implement proper tree shaking for dead code elimination".to_string(),
estimated_savings_bytes: 100_000, difficulty: 3,
priority: 5,
},
OptimizationRecommendation {
recommendation_type: OptimizationType::FeatureFlags,
description: "Add feature flags for optional components".to_string(),
estimated_savings_bytes: 80_000, difficulty: 2,
priority: 4,
},
OptimizationRecommendation {
recommendation_type: OptimizationType::DependencyOptimization,
description: "Remove unused dependencies and optimize imports".to_string(),
estimated_savings_bytes: 60_000, difficulty: 1,
priority: 3,
},
OptimizationRecommendation {
recommendation_type: OptimizationType::CodeSplitting,
description: "Implement code splitting for optional features".to_string(),
estimated_savings_bytes: 40_000, difficulty: 4,
priority: 2,
},
];
let report = BundleSizeReport {
total_size_bytes: 378_000, total_size_kb: 378.0,
module_sizes,
feature_sizes,
dead_code_analysis,
recommendations,
};
self.analysis_results = Some(report.clone());
report
}
pub fn get_optimization_potential(&self) -> f64 {
if let Some(ref analysis) = self.analysis_results {
let total_savings: u64 = analysis
.recommendations
.iter()
.map(|r| r.estimated_savings_bytes)
.sum();
let potential_size_bytes = if total_savings > analysis.total_size_bytes {
0
} else {
analysis.total_size_bytes - total_savings
};
potential_size_bytes as f64 / 1024.0
} else {
0.0
}
}
pub fn is_target_achievable(&self) -> bool {
self.get_optimization_potential() <= self.target_size_kb
}
pub fn get_reduction_percentage_needed(&self) -> f64 {
((self.current_size_kb - self.target_size_kb) / self.current_size_kb) * 100.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bundle_size_analysis() {
let mut analyzer = BundleSizeAnalyzer::new();
let report = analyzer.analyze_bundle_size();
assert_eq!(report.total_size_kb, 378.0);
assert_eq!(report.total_size_bytes, 378_000);
assert!(report.module_sizes.contains_key("leptos-motion-core"));
assert!(report.module_sizes.contains_key("leptos-motion-dom"));
assert!(report.module_sizes.contains_key("leptos-motion-gestures"));
assert!(report.feature_sizes.contains_key("web-sys"));
assert!(report.feature_sizes.contains_key("wasm-bindgen"));
assert!(report.dead_code_analysis.estimated_dead_code_bytes > 0);
assert!(report.dead_code_analysis.dead_code_percentage > 0.0);
assert!(!report.recommendations.is_empty());
assert!(report.recommendations.len() >= 5);
}
#[test]
fn test_optimization_potential() {
let mut analyzer = BundleSizeAnalyzer::new();
analyzer.analyze_bundle_size();
let potential_size = analyzer.get_optimization_potential();
assert!(potential_size >= 0.0); assert!(potential_size <= 378.0); }
#[test]
fn test_target_achievable() {
let mut analyzer = BundleSizeAnalyzer::new();
analyzer.analyze_bundle_size();
assert!(analyzer.is_target_achievable());
}
#[test]
fn test_reduction_percentage_needed() {
let analyzer = BundleSizeAnalyzer::new();
let reduction_percentage = analyzer.get_reduction_percentage_needed();
assert!(reduction_percentage > 90.0);
assert!(reduction_percentage < 95.0);
}
#[test]
fn test_dead_code_analysis() {
let mut analyzer = BundleSizeAnalyzer::new();
let report = analyzer.analyze_bundle_size();
let dead_code = &report.dead_code_analysis;
assert!(dead_code.estimated_dead_code_bytes > 100_000);
assert!(dead_code.dead_code_percentage > 30.0);
assert!(!dead_code.dead_code_modules.is_empty());
assert!(
dead_code
.dead_code_modules
.contains(&"developer_tools".to_string())
);
assert!(!dead_code.unused_dependencies.is_empty());
assert!(dead_code.unused_dependencies.contains(&"serde".to_string()));
}
#[test]
fn test_optimization_recommendations() {
let mut analyzer = BundleSizeAnalyzer::new();
let report = analyzer.analyze_bundle_size();
assert!(report.recommendations.len() >= 5);
let dead_code_rec = report
.recommendations
.iter()
.find(|r| matches!(r.recommendation_type, OptimizationType::DeadCodeElimination));
assert!(dead_code_rec.is_some());
let tree_shaking_rec = report
.recommendations
.iter()
.find(|r| matches!(r.recommendation_type, OptimizationType::TreeShaking));
assert!(tree_shaking_rec.is_some());
let feature_flags_rec = report
.recommendations
.iter()
.find(|r| matches!(r.recommendation_type, OptimizationType::FeatureFlags));
assert!(feature_flags_rec.is_some());
for rec in &report.recommendations {
assert!(rec.estimated_savings_bytes > 0);
assert!(rec.difficulty >= 1 && rec.difficulty <= 5);
assert!(rec.priority >= 1 && rec.priority <= 5);
}
}
#[test]
fn test_bundle_size_target_validation() {
let analyzer = BundleSizeAnalyzer::new();
assert!(analyzer.target_size_kb > 0.0);
assert!(analyzer.target_size_kb < 100.0);
assert!(analyzer.current_size_kb > analyzer.target_size_kb);
}
#[test]
fn test_module_size_breakdown() {
let mut analyzer = BundleSizeAnalyzer::new();
let report = analyzer.analyze_bundle_size();
let core_size = report.module_sizes.get("leptos-motion-core").unwrap();
assert!(*core_size > 100_000);
for (module, size) in &report.module_sizes {
assert!(*size > 0, "Module {} should have positive size", module);
}
let total_module_size: u64 = report.module_sizes.values().sum();
assert_eq!(total_module_size, report.total_size_bytes);
}
#[test]
fn test_feature_size_breakdown() {
let mut analyzer = BundleSizeAnalyzer::new();
let report = analyzer.analyze_bundle_size();
let web_sys_size = report.feature_sizes.get("web-sys").unwrap();
assert!(*web_sys_size > 50_000);
for (feature, size) in &report.feature_sizes {
assert!(*size > 0, "Feature {} should have positive size", feature);
}
let total_feature_size: u64 = report.feature_sizes.values().sum();
assert_eq!(total_feature_size, report.total_size_bytes);
}
}