mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
refactor(linter/import/no_cycle): use ModuleGraphVisitor. (#3064)
Uses #3062 to avoid having multiple implementations for the same idea(shared with #3030).
This commit is contained in:
parent
5cf55c212e
commit
7e4beb0118
1 changed files with 41 additions and 59 deletions
|
|
@ -1,10 +1,6 @@
|
|||
#![allow(clippy::cast_possible_truncation)]
|
||||
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
ffi::OsStr,
|
||||
path::{Component, Path, PathBuf},
|
||||
};
|
||||
use std::{ffi::OsStr, path::Component, sync::Arc};
|
||||
|
||||
use oxc_diagnostics::{
|
||||
miette::{self, Diagnostic},
|
||||
|
|
@ -12,7 +8,10 @@ use oxc_diagnostics::{
|
|||
};
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::{CompactStr, Span};
|
||||
use oxc_syntax::module_record::ModuleRecord;
|
||||
use oxc_syntax::{
|
||||
module_graph_visitor::{ModuleGraphVisitorBuilder, ModuleGraphVisitorEvent, VisitFoldWhile},
|
||||
module_record::ModuleRecord,
|
||||
};
|
||||
|
||||
use crate::{context::LintContext, rule::Rule};
|
||||
|
||||
|
|
@ -106,9 +105,42 @@ impl Rule for NoCycle {
|
|||
let needle = &module_record.resolved_absolute_path;
|
||||
let cwd = std::env::current_dir().unwrap();
|
||||
|
||||
let mut state = State::default();
|
||||
if self.detect_cycle(&mut state, module_record, needle) {
|
||||
let stack = &state.stack;
|
||||
let mut stack = Vec::new();
|
||||
let ignore_types = self.ignore_types;
|
||||
let visitor_result = ModuleGraphVisitorBuilder::default()
|
||||
.max_depth(self.max_depth)
|
||||
.filter(move |(key, val): (&CompactStr, &Arc<ModuleRecord>), parent: &ModuleRecord| {
|
||||
let path = &val.resolved_absolute_path;
|
||||
let is_node_module = path
|
||||
.components()
|
||||
.any(|c| matches!(c, Component::Normal(p) if p == OsStr::new("node_modules")));
|
||||
let is_type_import = !ignore_types
|
||||
|| !parent
|
||||
.import_entries
|
||||
.iter()
|
||||
.filter(|entry| entry.module_request.name() == key)
|
||||
.all(|entry| entry.is_type);
|
||||
|
||||
is_node_module || is_type_import
|
||||
})
|
||||
.event(|event, (key, val), _| match event {
|
||||
ModuleGraphVisitorEvent::Enter => {
|
||||
stack.push((key.clone(), val.resolved_absolute_path.clone()));
|
||||
}
|
||||
ModuleGraphVisitorEvent::Leave => {
|
||||
stack.pop();
|
||||
}
|
||||
})
|
||||
.visit_fold(false, module_record, |_, (_, val), _| {
|
||||
let path = &val.resolved_absolute_path;
|
||||
if path == needle {
|
||||
VisitFoldWhile::Stop(true)
|
||||
} else {
|
||||
VisitFoldWhile::Next(false)
|
||||
}
|
||||
});
|
||||
|
||||
if visitor_result.result {
|
||||
let span = module_record.requested_modules.get(&stack[0].0).unwrap()[0].span();
|
||||
let help = stack
|
||||
.iter()
|
||||
|
|
@ -127,56 +159,6 @@ impl Rule for NoCycle {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct State {
|
||||
traversed: HashSet<PathBuf>,
|
||||
stack: Vec<(CompactStr, PathBuf)>,
|
||||
}
|
||||
|
||||
impl NoCycle {
|
||||
fn detect_cycle(&self, state: &mut State, module_record: &ModuleRecord, needle: &Path) -> bool {
|
||||
let path = &module_record.resolved_absolute_path;
|
||||
|
||||
if state.stack.len() as u32 > self.max_depth {
|
||||
return false;
|
||||
}
|
||||
|
||||
if path
|
||||
.components()
|
||||
.any(|c| matches!(c, Component::Normal(p) if p == OsStr::new("node_modules")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for module_record_ref in &module_record.loaded_modules {
|
||||
let resolved_absolute_path = &module_record_ref.resolved_absolute_path;
|
||||
if self.ignore_types {
|
||||
let was_imported_as_type = &module_record
|
||||
.import_entries
|
||||
.iter()
|
||||
.filter(|entry| entry.module_request.name() == module_record_ref.key())
|
||||
.all(|entry| entry.is_type);
|
||||
if *was_imported_as_type {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if !state.traversed.insert(resolved_absolute_path.clone()) {
|
||||
continue;
|
||||
}
|
||||
state.stack.push((module_record_ref.key().clone(), resolved_absolute_path.clone()));
|
||||
if needle == resolved_absolute_path {
|
||||
return true;
|
||||
}
|
||||
if self.detect_cycle(state, module_record_ref.value(), needle) {
|
||||
return true;
|
||||
}
|
||||
state.stack.pop();
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
use serde_json::json;
|
||||
|
|
|
|||
Loading…
Reference in a new issue