use fallow_config::{ResolvedConfig, RulesConfig, Severity};
use crate::effective_severity::{SeveritySource, any_finding_with_severity};
pub fn has_error_severity_issues(
results: &crate::dead_code::AnalysisResults,
rules: &RulesConfig,
config: Option<&ResolvedConfig>,
promote_warns: bool,
) -> bool {
let source = SeveritySource::new(rules, config, promote_warns);
any_finding_with_severity(results, &source, Severity::Error)
}
pub fn promote_warns_to_errors(rules: &mut RulesConfig) {
for rule in [
&mut rules.unused_files,
&mut rules.unused_exports,
&mut rules.unused_types,
&mut rules.private_type_leaks,
&mut rules.deprecated_exports_in_use,
&mut rules.unused_dependencies,
&mut rules.unused_dev_dependencies,
&mut rules.unused_optional_dependencies,
&mut rules.unused_enum_members,
&mut rules.unused_class_members,
&mut rules.unused_store_members,
&mut rules.unprovided_injects,
&mut rules.unrendered_components,
&mut rules.unused_component_props,
&mut rules.unused_component_emits,
&mut rules.unused_component_inputs,
&mut rules.unused_component_outputs,
&mut rules.unused_svelte_events,
&mut rules.unused_server_actions,
&mut rules.unused_load_data_keys,
&mut rules.unresolved_imports,
&mut rules.unlisted_dependencies,
&mut rules.duplicate_exports,
&mut rules.type_only_dependencies,
&mut rules.test_only_dependencies,
&mut rules.dev_dependencies_in_production,
&mut rules.circular_dependencies,
&mut rules.re_export_cycle,
&mut rules.boundary_violation,
&mut rules.coverage_gaps,
&mut rules.stale_suppressions,
&mut rules.require_suppression_reason,
&mut rules.unused_catalog_entries,
&mut rules.empty_catalog_groups,
&mut rules.unresolved_catalog_references,
&mut rules.unused_dependency_overrides,
&mut rules.misconfigured_dependency_overrides,
&mut rules.policy_violation,
&mut rules.invalid_client_export,
&mut rules.mixed_client_server_barrel,
&mut rules.misplaced_directive,
&mut rules.route_collision,
&mut rules.dynamic_segment_name_conflict,
] {
promote_warn_to_error(rule);
}
}
fn promote_warn_to_error(rule: &mut Severity) {
if *rule == Severity::Warn {
*rule = Severity::Error;
}
}
pub fn promote_policy_finding_warns(results: &mut crate::dead_code::AnalysisResults) {
use fallow_types::results::PolicyViolationSeverity;
for finding in &mut results.policy_violations {
if finding.violation.severity == PolicyViolationSeverity::Warn {
finding.violation.severity = PolicyViolationSeverity::Error;
}
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use fallow_config::{ConfigOverride, FallowConfig, PartialRulesConfig, RulesConfig, Severity};
use fallow_types::output_dead_code::{
EmptyCatalogGroupFinding, MisconfiguredDependencyOverrideFinding,
UnusedDependencyOverrideFinding,
};
use fallow_types::results::{
DependencyOverrideMisconfigReason, DependencyOverrideSource, EmptyCatalogGroup,
MisconfiguredDependencyOverride, UnusedDependencyOverride,
};
use super::has_error_severity_issues;
use crate::dead_code::AnalysisResults;
fn manifest_findings() -> [(&'static str, AnalysisResults); 3] {
let mut unused = AnalysisResults::default();
unused
.unused_dependency_overrides
.push(UnusedDependencyOverrideFinding::with_actions(
UnusedDependencyOverride {
raw_key: "old-dep".to_string(),
target_package: "old-dep".to_string(),
parent_package: None,
version_constraint: None,
version_range: "^1.0.0".to_string(),
source: DependencyOverrideSource::PnpmPackageJson,
path: PathBuf::from("/project/package.json"),
line: 7,
hint: None,
},
));
let mut misconfigured = AnalysisResults::default();
misconfigured.misconfigured_dependency_overrides.push(
MisconfiguredDependencyOverrideFinding::with_actions(MisconfiguredDependencyOverride {
raw_key: "bad>".to_string(),
target_package: None,
raw_value: "1.0.0".to_string(),
reason: DependencyOverrideMisconfigReason::UnparsableKey,
source: DependencyOverrideSource::PnpmPackageJson,
path: PathBuf::from("/project/package.json"),
line: 4,
}),
);
let mut empty_group = AnalysisResults::default();
empty_group
.empty_catalog_groups
.push(EmptyCatalogGroupFinding::with_actions(EmptyCatalogGroup {
catalog_name: "legacy".to_string(),
path: PathBuf::from("/project/pnpm-workspace.yaml"),
line: 3,
}));
[
("unused-dependency-overrides", unused),
("misconfigured-dependency-overrides", misconfigured),
("empty-catalog-groups", empty_group),
]
}
fn config_with_manifest_override(
base: Severity,
manifest: Severity,
) -> fallow_config::ResolvedConfig {
FallowConfig {
rules: RulesConfig {
unused_dependency_overrides: base,
misconfigured_dependency_overrides: base,
empty_catalog_groups: base,
..RulesConfig::default()
},
overrides: vec![ConfigOverride {
files: vec![
"package.json".to_string(),
"pnpm-workspace.yaml".to_string(),
],
rules: PartialRulesConfig {
unused_dependency_overrides: Some(manifest),
misconfigured_dependency_overrides: Some(manifest),
empty_catalog_groups: Some(manifest),
..PartialRulesConfig::default()
},
}],
..FallowConfig::default()
}
.resolve(
PathBuf::from("/project"),
fallow_config::OutputFormat::Human,
1,
true,
true,
None,
)
}
#[test]
fn a_manifest_override_to_warn_clears_a_base_error() {
let config = config_with_manifest_override(Severity::Error, Severity::Warn);
for (kind, results) in manifest_findings() {
assert!(
!has_error_severity_issues(&results, &config.rules, Some(&config), false),
"the `warn` override for the manifest must win over the base `error` for {kind}"
);
}
}
#[test]
fn a_manifest_override_to_error_raises_a_base_warn() {
let config = config_with_manifest_override(Severity::Warn, Severity::Error);
for (kind, results) in manifest_findings() {
assert!(
has_error_severity_issues(&results, &config.rules, Some(&config), false),
"the `error` override for the manifest must win over the base `warn` for {kind}"
);
}
}
}