fob_graph/memory/
package_json.rs1use rustc_hash::FxHashSet as HashSet;
4
5use super::super::package_json::{
6 DependencyCoverage, DependencyType, PackageJson, TypeCoverage, UnusedDependency,
7 extract_package_name,
8};
9use super::graph::ModuleGraph;
10use crate::Result;
11
12impl ModuleGraph {
13 pub fn unused_npm_dependencies(
24 &self,
25 package_json: &PackageJson,
26 include_dev: bool,
27 include_peer: bool,
28 ) -> Result<Vec<UnusedDependency>> {
29 let inner = self.inner.read();
30
31 let mut imported_packages = HashSet::default();
33 for module in inner.modules.values() {
34 for import in module.imports.iter() {
35 if import.is_external() {
36 let package_name = extract_package_name(&import.source);
37 imported_packages.insert(package_name.to_string());
38 }
39 }
40 }
41
42 let mut unused = Vec::new();
43
44 let dep_types = [
46 (DependencyType::Production, true),
47 (DependencyType::Development, include_dev),
48 (DependencyType::Peer, include_peer),
49 (DependencyType::Optional, true),
50 ];
51
52 for (dep_type, should_check) in dep_types {
53 if !should_check {
54 continue;
55 }
56
57 for (package, version) in package_json.get_dependencies(dep_type) {
58 if !imported_packages.contains(package) {
59 unused.push(UnusedDependency {
60 package: package.clone(),
61 version: version.clone(),
62 dep_type,
63 });
64 }
65 }
66 }
67
68 Ok(unused)
69 }
70
71 pub fn dependency_coverage(&self, package_json: &PackageJson) -> Result<DependencyCoverage> {
76 let inner = self.inner.read();
77
78 let mut imported_packages = HashSet::default();
80 for module in inner.modules.values() {
81 for import in module.imports.iter() {
82 if import.is_external() {
83 let package_name = extract_package_name(&import.source);
84 imported_packages.insert(package_name.to_string());
85 }
86 }
87 }
88
89 let mut by_type = std::collections::HashMap::new();
90 let mut total_declared = 0;
91 let mut total_used = 0;
92
93 for dep_type in [
94 DependencyType::Production,
95 DependencyType::Development,
96 DependencyType::Peer,
97 DependencyType::Optional,
98 ] {
99 let deps = package_json.get_dependencies(dep_type);
100 let declared = deps.len();
101 let used = deps
102 .keys()
103 .filter(|pkg| imported_packages.contains(*pkg))
104 .count();
105 let unused = declared - used;
106
107 total_declared += declared;
108 total_used += used;
109
110 by_type.insert(
111 dep_type,
112 TypeCoverage {
113 declared,
114 used,
115 unused,
116 },
117 );
118 }
119
120 Ok(DependencyCoverage {
121 total_declared,
122 total_used,
123 total_unused: total_declared - total_used,
124 by_type,
125 })
126 }
127}