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.boundary_violation,
68 &mut rules.coverage_gaps,
69 &mut rules.stale_suppressions,
70 &mut rules.require_suppression_reason,
71 &mut rules.unused_catalog_entries,
72 &mut rules.empty_catalog_groups,
73 &mut rules.unresolved_catalog_references,
74 &mut rules.unused_dependency_overrides,
75 &mut rules.misconfigured_dependency_overrides,
76 &mut rules.policy_violation,
77 &mut rules.invalid_client_export,
78 &mut rules.mixed_client_server_barrel,
79 &mut rules.misplaced_directive,
80 &mut rules.route_collision,
81 &mut rules.dynamic_segment_name_conflict,
82 ] {
83 promote_warn_to_error(rule);
84 }
85}
86
87fn promote_warn_to_error(rule: &mut Severity) {
88 if *rule == Severity::Warn {
89 *rule = Severity::Error;
90 }
91}
92
93pub fn promote_policy_finding_warns(results: &mut crate::dead_code::AnalysisResults) {
99 use fallow_types::results::PolicyViolationSeverity;
100 for finding in &mut results.policy_violations {
101 if finding.violation.severity == PolicyViolationSeverity::Warn {
102 finding.violation.severity = PolicyViolationSeverity::Error;
103 }
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use std::path::PathBuf;
110
111 use fallow_config::{ConfigOverride, FallowConfig, PartialRulesConfig, RulesConfig, Severity};
112 use fallow_types::output_dead_code::{
113 EmptyCatalogGroupFinding, MisconfiguredDependencyOverrideFinding,
114 UnusedDependencyOverrideFinding,
115 };
116 use fallow_types::results::{
117 DependencyOverrideMisconfigReason, DependencyOverrideSource, EmptyCatalogGroup,
118 MisconfiguredDependencyOverride, UnusedDependencyOverride,
119 };
120
121 use super::has_error_severity_issues;
122 use crate::dead_code::AnalysisResults;
123
124 fn manifest_findings() -> [(&'static str, AnalysisResults); 3] {
128 let mut unused = AnalysisResults::default();
129 unused
130 .unused_dependency_overrides
131 .push(UnusedDependencyOverrideFinding::with_actions(
132 UnusedDependencyOverride {
133 raw_key: "old-dep".to_string(),
134 target_package: "old-dep".to_string(),
135 parent_package: None,
136 version_constraint: None,
137 version_range: "^1.0.0".to_string(),
138 source: DependencyOverrideSource::PnpmPackageJson,
139 path: PathBuf::from("/project/package.json"),
140 line: 7,
141 hint: None,
142 },
143 ));
144 let mut misconfigured = AnalysisResults::default();
145 misconfigured.misconfigured_dependency_overrides.push(
146 MisconfiguredDependencyOverrideFinding::with_actions(MisconfiguredDependencyOverride {
147 raw_key: "bad>".to_string(),
148 target_package: None,
149 raw_value: "1.0.0".to_string(),
150 reason: DependencyOverrideMisconfigReason::UnparsableKey,
151 source: DependencyOverrideSource::PnpmPackageJson,
152 path: PathBuf::from("/project/package.json"),
153 line: 4,
154 }),
155 );
156 let mut empty_group = AnalysisResults::default();
157 empty_group
158 .empty_catalog_groups
159 .push(EmptyCatalogGroupFinding::with_actions(EmptyCatalogGroup {
160 catalog_name: "legacy".to_string(),
161 path: PathBuf::from("/project/pnpm-workspace.yaml"),
162 line: 3,
163 }));
164 [
165 ("unused-dependency-overrides", unused),
166 ("misconfigured-dependency-overrides", misconfigured),
167 ("empty-catalog-groups", empty_group),
168 ]
169 }
170
171 fn config_with_manifest_override(
172 base: Severity,
173 manifest: Severity,
174 ) -> fallow_config::ResolvedConfig {
175 FallowConfig {
176 rules: RulesConfig {
177 unused_dependency_overrides: base,
178 misconfigured_dependency_overrides: base,
179 empty_catalog_groups: base,
180 ..RulesConfig::default()
181 },
182 overrides: vec![ConfigOverride {
183 files: vec![
184 "package.json".to_string(),
185 "pnpm-workspace.yaml".to_string(),
186 ],
187 rules: PartialRulesConfig {
188 unused_dependency_overrides: Some(manifest),
189 misconfigured_dependency_overrides: Some(manifest),
190 empty_catalog_groups: Some(manifest),
191 ..PartialRulesConfig::default()
192 },
193 }],
194 ..FallowConfig::default()
195 }
196 .resolve(
197 PathBuf::from("/project"),
198 fallow_config::OutputFormat::Human,
199 1,
200 true,
201 true,
202 None,
203 )
204 }
205
206 #[test]
207 fn a_manifest_override_to_warn_clears_a_base_error() {
208 let config = config_with_manifest_override(Severity::Error, Severity::Warn);
209 for (kind, results) in manifest_findings() {
210 assert!(
211 !has_error_severity_issues(&results, &config.rules, Some(&config), false),
212 "the `warn` override for the manifest must win over the base `error` for {kind}"
213 );
214 }
215 }
216
217 #[test]
218 fn a_manifest_override_to_error_raises_a_base_warn() {
219 let config = config_with_manifest_override(Severity::Warn, Severity::Error);
220 for (kind, results) in manifest_findings() {
221 assert!(
222 has_error_severity_issues(&results, &config.rules, Some(&config), false),
223 "the `error` override for the manifest must win over the base `warn` for {kind}"
224 );
225 }
226 }
227}