Skip to content

Commit ac17183

Browse files
authored
perf(linter): improve noImportCycles performance (#7454)
1 parent aa8cea3 commit ac17183

File tree

2 files changed

+20
-8
lines changed

2 files changed

+20
-8
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@biomejs/biome": patch
3+
---
4+
5+
Greatly improved performance of `noImportCycles` by eliminating allocations.
6+
7+
In one repository, the total runtime of Biome with only `noImportCycles` enabled went from ~23s down to ~4s.

crates/biome_js_analyze/src/lint/nursery/no_import_cycles.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use biome_console::markup;
66
use biome_diagnostics::Severity;
77
use biome_js_syntax::AnyJsImportLike;
88
use biome_module_graph::{JsImportPath, JsImportPhase, JsModuleInfo};
9+
use biome_resolver::ResolvedPath;
910
use biome_rowan::AstNode;
1011
use biome_rule_options::no_import_cycles::NoImportCyclesOptions;
1112
use camino::{Utf8Path, Utf8PathBuf};
@@ -154,7 +155,7 @@ declare_lint_rule! {
154155

155156
impl Rule for NoImportCycles {
156157
type Query = ResolvedImports<AnyJsImportLike>;
157-
type State = Box<[Box<str>]>;
158+
type State = Vec<String>;
158159
type Signals = Option<Self::State>;
159160
type Options = NoImportCyclesOptions;
160161

@@ -227,10 +228,10 @@ fn find_cycle(
227228
ctx: &RuleContext<NoImportCycles>,
228229
start_path: &Utf8Path,
229230
mut module_info: JsModuleInfo,
230-
) -> Option<Box<[Box<str>]>> {
231+
) -> Option<Vec<String>> {
231232
let options = ctx.options();
232233
let mut seen = FxHashSet::default();
233-
let mut stack: Vec<(Box<str>, JsModuleInfo)> = Vec::new();
234+
let mut stack: Vec<(ResolvedPath, JsModuleInfo)> = Vec::new();
234235

235236
'outer: loop {
236237
for JsImportPath {
@@ -252,17 +253,21 @@ fn find_cycle(
252253

253254
if path == ctx.file_path() {
254255
// Return all the paths from `start_path` to `resolved_path`:
255-
let paths = Some(start_path.as_str())
256+
let paths = Some(start_path.to_string())
256257
.into_iter()
257-
.map(Box::from)
258-
.chain(stack.into_iter().map(|(path, _)| path))
259-
.chain(Some(Box::from(path.as_str())))
258+
.chain(
259+
stack
260+
.iter()
261+
.filter_map(|(path, _)| path.as_path())
262+
.map(ToString::to_string),
263+
)
264+
.chain(Some(path.to_string()))
260265
.collect();
261266
return Some(paths);
262267
}
263268

264269
if let Some(next_module_info) = ctx.module_info_for_path(path) {
265-
stack.push((path.as_str().into(), module_info));
270+
stack.push((resolved_path.clone(), module_info));
266271
module_info = next_module_info;
267272
continue 'outer;
268273
}

0 commit comments

Comments
 (0)