From d364683abdd8877f7a0bb370f940863bfe195c21 Mon Sep 17 00:00:00 2001 From: bompus Date: Tue, 27 Oct 2026 00:12:50 -0600 Subject: [PATCH] =?UTF-8?q?perf(resolve):=20memoize=20ts=5Fscope=5Fscan=20?= =?UTF-8?q?per=20file=20=E2=80=94=20ambient=20lookup=20rescans?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ts_ambient_lookup walks every .d.ts file in the project for each out-of-repo import, and each ts_scope_scan reparsed the file through js_binding_tree. On a monorepo the ~400-file working set blew the 32-entry tree cache, so resolution reparsed every declaration file thousands of times (~69k parses/minute on supabase; the run never finished). Upstream memoizes its equivalent scan per file (TS_GLOBAL_SCOPES); do the same — each file is scanned once per resolver and shared thereafter. supabase index: never completed -> 46.9s, 327,838 edges. --- codegraph-kernel/src/resolve/mod.rs | 6 ++++++ codegraph-kernel/src/resolve/ts_known.rs | 11 ++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/codegraph-kernel/src/resolve/mod.rs b/codegraph-kernel/src/resolve/mod.rs index f00a04b89..fb8401a6b 100644 --- a/codegraph-kernel/src/resolve/mod.rs +++ b/codegraph-kernel/src/resolve/mod.rs @@ -706,6 +706,11 @@ pub struct KernelResolver { ts_imported_type_memo: HashMap<(String, String), Option>, /// `(package, type)` → the project file declaring the package with that type in it. ts_ambient_memo: HashMap<(String, String), Option>, + /// `file` → its ambient/global scope scan (None when unreadable). The + /// scan parses the file and ambient-module lookup walks every `.d.ts` + /// once per import, so each file is scanned once and shared — upstream + /// memoizes the same way (TS_GLOBAL_SCOPES). + ts_scope_memo: HashMap>>, /// `(file, type as written)` → what the name names there. ts_named_type_memo: HashMap<(String, String), ts_known::TsNamedType>, /// Whether the project declares a `createInstance` returning an `InstanceType` of its constructor argument. @@ -935,6 +940,7 @@ impl KernelResolver { ts_field_reading: HashSet::new(), ts_imported_type_memo: HashMap::new(), ts_ambient_memo: HashMap::new(), + ts_scope_memo: HashMap::new(), ts_named_type_memo: HashMap::new(), ts_create_instance_declared: None, python_bound_memo: HashMap::new(), diff --git a/codegraph-kernel/src/resolve/ts_known.rs b/codegraph-kernel/src/resolve/ts_known.rs index a6cd234f0..92a257447 100644 --- a/codegraph-kernel/src/resolve/ts_known.rs +++ b/codegraph-kernel/src/resolve/ts_known.rs @@ -489,7 +489,16 @@ pub(super) struct TsScopeScan { } impl KernelResolver { - pub(super) fn ts_scope_scan(&mut self, file: &str, r: &ResolveRefIn) -> Option { + pub(super) fn ts_scope_scan(&mut self, file: &str, r: &ResolveRefIn) -> Option> { + if let Some(memo) = self.ts_scope_memo.get(file) { + return memo.clone(); + } + let scan = self.ts_scope_scan_uncached(file, r).map(Rc::new); + self.ts_scope_memo.insert(file.to_string(), scan.clone()); + scan + } + + fn ts_scope_scan_uncached(&mut self, file: &str, r: &ResolveRefIn) -> Option { let source = self.read_file(file)?; let mut site = r.clone(); site.file_path = file.to_string(); site.language = "typescript".to_string(); let tree = self.js_binding_tree(&source, &site)?;