1use fallow_config::{ResolvedConfig, RulesConfig, Severity};
10
11use crate::effective_severity::{SeveritySource, any_finding_with_severity};
12
13pub fn has_error_severity_issues(
27 results: &crate::dead_code::AnalysisResults,
28 rules: &RulesConfig,
29 config: Option<&ResolvedConfig>,
30 promote_warns: bool,
31) -> bool {
32 let source = SeveritySource::new(rules, config, promote_warns);
33 any_finding_with_severity(results, &source, Severity::Error)
34}
35
36pub fn promote_warns_to_errors(rules: &mut RulesConfig) {
38 for rule in [
39 &mut rules.unused_files,
40 &mut rules.unused_exports,
41 &mut rules.unused_types,
42 &mut rules.private_type_leaks,
43 &mut rules.deprecated_exports_in_use,
44 &mut rules.unused_dependencies,
45 &mut rules.unused_dev_dependencies,
46 &mut rules.unused_optional_dependencies,
47 &mut rules.unused_enum_members,
48 &mut rules.unused_class_members,
49 &mut rules.unused_store_members,
50 &mut rules.unprovided_injects,
51 &mut rules.unrendered_components,
52 &mut rules.unused_component_props,
53 &mut rules.unused_component_emits,
54 &mut rules.unused_component_inputs,
55 &mut rules.unused_component_outputs,
56 &mut rules.unused_svelte_events,
57 &mut rules.unused_server_actions,
58 &mut rules.unused_load_data_keys,
59 &mut rules.unresolved_imports,
60 &mut rules.unlisted_dependencies,
61 &mut rules.duplicate_exports,
62 &mut rules.type_only_dependencies,
63 &mut rules.test_only_dependencies,
64 &mut rules.dev_dependencies_in_production,
65 &mut rules.circular_dependencies,
66 &mut rules.re_export_cycle,
67 &mut rules.package_cycle,
68 &mut rules.boundary_violation,
69 &mut rules.coverage_gaps,
70 &mut rules.stale_suppressions,
71 &mut rules.require_suppression_reason,
72 &mut rules.unused_catalog_entries,
73 &mut rules.empty_catalog_groups,
74 &mut rules.unresolved_catalog_references,
75 &mut rules.unused_dependency_overrides,
76 &mut rules.misconfigured_dependency_overrides,
77 &mut rules.policy_violation,
78 &mut rules.invalid_client_export,
79 &mut rules.mixed_client_server_barrel,
80 &mut rules.misplaced_directive,
81 &mut rules.route_collision,
82 &mut rules.dynamic_segment_name_conflict,
83 ] {
84 promote_warn_to_error(rule);
85 }
86}
87
88fn promote_warn_to_error(rule: &mut Severity) {
89 if *rule == Severity::Warn {
90 *rule = Severity::Error;
91 }
92}
93
94pub fn promote_policy_finding_warns(results: &mut crate::dead_code::AnalysisResults) {
100 use fallow_types::results::PolicyViolationSeverity;
101 for finding in &mut results.policy_violations {
102 if finding.violation.severity == PolicyViolationSeverity::Warn {
103 finding.violation.severity = PolicyViolationSeverity::Error;
104 }
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use std::path::PathBuf;
111
112 use fallow_config::{ConfigOverride, FallowConfig, PartialRulesConfig, RulesConfig, Severity};
113 use fallow_types::output_dead_code::{
114 EmptyCatalogGroupFinding, MisconfiguredDependencyOverrideFinding,
115 UnusedDependencyOverrideFinding,
116 };
117 use fallow_types::results::{
118 DependencyOverrideMisconfigReason, DependencyOverrideSource, EmptyCatalogGroup,
119 MisconfiguredDependencyOverride, UnusedDependencyOverride,
120 };
121
122 use super::has_error_severity_issues;
123 use crate::dead_code::AnalysisResults;
124
125 fn manifest_findings() -> [(&'static str, AnalysisResults); 3] {
129 let mut unused = AnalysisResults::default();
130 unused
131 .unused_dependency_overrides
132 .push(UnusedDependencyOverrideFinding::with_actions(
133 UnusedDependencyOverride {
134 raw_key: "old-dep".to_string(),
135 target_package: "old-dep".to_string(),
136 parent_package: None,
137 version_constraint: None,
138 version_range: "^1.0.0".to_string(),
139 source: DependencyOverrideSource::PnpmPackageJson,
140 path: PathBuf::from("/project/package.json"),
141 line: 7,
142 hint: None,
143 },
144 ));
145 let mut misconfigured = AnalysisResults::default();
146 misconfigured.misconfigured_dependency_overrides.push(
147 MisconfiguredDependencyOverrideFinding::with_actions(MisconfiguredDependencyOverride {
148 raw_key: "bad>".to_string(),
149 target_package: None,
150 raw_value: "1.0.0".to_string(),
151 reason: DependencyOverrideMisconfigReason::UnparsableKey,
152 source: DependencyOverrideSource::PnpmPackageJson,
153 path: PathBuf::from("/project/package.json"),
154 line: 4,
155 }),
156 );
157 let mut empty_group = AnalysisResults::default();
158 empty_group
159 .empty_catalog_groups
160 .push(EmptyCatalogGroupFinding::with_actions(EmptyCatalogGroup {
161 catalog_name: "legacy".to_string(),
162 path: PathBuf::from("/project/pnpm-workspace.yaml"),
163 line: 3,
164 }));
165 [
166 ("unused-dependency-overrides", unused),
167 ("misconfigured-dependency-overrides", misconfigured),
168 ("empty-catalog-groups", empty_group),
169 ]
170 }
171
172 fn config_with_manifest_override(
173 base: Severity,
174 manifest: Severity,
175 ) -> fallow_config::ResolvedConfig {
176 FallowConfig {
177 rules: RulesConfig {
178 unused_dependency_overrides: base,
179 misconfigured_dependency_overrides: base,
180 empty_catalog_groups: base,
181 ..RulesConfig::default()
182 },
183 overrides: vec![ConfigOverride {
184 files: vec![
185 "package.json".to_string(),
186 "pnpm-workspace.yaml".to_string(),
187 ],
188 rules: PartialRulesConfig {
189 unused_dependency_overrides: Some(manifest),
190 misconfigured_dependency_overrides: Some(manifest),
191 empty_catalog_groups: Some(manifest),
192 ..PartialRulesConfig::default()
193 },
194 }],
195 ..FallowConfig::default()
196 }
197 .resolve(
198 PathBuf::from("/project"),
199 fallow_config::OutputFormat::Human,
200 1,
201 true,
202 true,
203 None,
204 )
205 }
206
207 #[test]
208 fn a_manifest_override_to_warn_clears_a_base_error() {
209 let config = config_with_manifest_override(Severity::Error, Severity::Warn);
210 for (kind, results) in manifest_findings() {
211 assert!(
212 !has_error_severity_issues(&results, &config.rules, Some(&config), false),
213 "the `warn` override for the manifest must win over the base `error` for {kind}"
214 );
215 }
216 }
217
218 #[test]
219 fn a_manifest_override_to_error_raises_a_base_warn() {
220 let config = config_with_manifest_override(Severity::Warn, Severity::Error);
221 for (kind, results) in manifest_findings() {
222 assert!(
223 has_error_severity_issues(&results, &config.rules, Some(&config), false),
224 "the `error` override for the manifest must win over the base `warn` for {kind}"
225 );
226 }
227 }
228}