mod config;
mod dependency;
mod file_scanner;
mod package_manager;
mod report;
mod uninstall;
mod utils;
#[cfg(test)]
mod tests;
use clap::{Arg, ArgAction, Command};
use colored::*;
use config::PACKAGE_JSON_PATH;
use dependency::read_package_json;
use file_scanner::scan_files;
use report::print_dependency_report;
use std::collections::HashSet;
use uninstall::handle_unused_dependencies;
fn main() {
let matches = Command::new("Dependency Analyzer")
.about("Analyze unused dependencies in a project")
.arg(
Arg::new("dry-run")
.long("dry-run")
.help("Simulate actions without making changes (e.g., no uninstalls)")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("interactive")
.short('i')
.long("interactive")
.help("Prompt the user before taking actions on unused dependencies")
.action(ArgAction::SetTrue),
)
.get_matches();
let dry_run: bool = *matches.get_one("dry-run").unwrap_or(&false);
let interactive: bool = *matches.get_one("interactive").unwrap_or(&false);
let pb = utils::create_spinner("Initializing...");
let package_json = read_package_json(PACKAGE_JSON_PATH).unwrap_or_else(|err| {
eprintln!("{}", err.red());
std::process::exit(1);
});
let dependencies: HashSet<String> = package_json
.get("dependencies")
.and_then(serde_json::Value::as_object)
.map_or_else(HashSet::new, |map| map.keys().cloned().collect());
pb.set_message("Scanning files...");
let (used_packages, explored_files, ignored_files) = scan_files(&dependencies, &pb);
pb.finish_with_message("Scanning complete!".green().to_string());
let required_deps = dependency::get_required_dependencies();
let ignored_deps = dependency::read_cnpignore();
let unused_dependencies: Vec<_> = dependencies
.difference(&used_packages)
.filter(|dep| !required_deps.contains(*dep) && !ignored_deps.contains(*dep))
.cloned()
.collect();
print_dependency_report(
&dependencies,
&used_packages,
&unused_dependencies,
&explored_files,
&ignored_files,
);
if !unused_dependencies.is_empty() {
handle_unused_dependencies(&unused_dependencies, dry_run, interactive);
}
}