fix(extract): resolve TS/JS barrel re-exports and extensionless imports#403
Conversation
|
Empirical signal that this fix matters in practice: running graphify on a TS codebase with path aliases gives The extensionless + barrel resolution logic here should recover a significant portion of the missing signal, especially in monorepos with barrel PR is large — if it would help review, consider splitting Gap 1 (re-export handling) and Gap 2 (extensionless resolution) into separate commits or PRs? Happy to help benchmark either path. |
|
@vhsantos26 the PR isn’t just the fixes, it’s a refactor plus the fixes bundled together. The bulk of the diff comes from the refactor. Specifically, it consolidates the per language extractors ( The actual fix logic is quite small roughly 25 lines total: Gap 1: Adds a Both fixes rely on the abstractions introduced in the refactor, which is why they ended up in the same PR. If you’d prefer a cleaner review flow, the proper split would be:
I’ve already prepared this as stacked PRs locally and verified that the final result is byte-identical to the current PR. Happy to push that version, or keep this as-is and expand the PR description to better explain the refactor scope, whatever you prefer. |
Problem
Two gaps in the JS/TS AST extractor caused barrel-exported symbols to appear as isolated nodes with no inbound edges in the graph, even when they were actively imported by other files.
Gap 1 — Re-export statements were invisible
export * from './X'andexport { A } from './X'produceexport_statementnodes in the tree-sitter AST. These were never inimport_types, so thewalk()function never processed them. In any TypeScript project using barrelindex.tsfiles, every component re-exported through a barrel appeared as a disconnected island.Gap 2 — Extensionless imports didn't resolve to real file IDs
import { X } from './components'(no extension) resolved to a target node ID for the bare path (e.g.…_components) which never matched any actual file node (e.g.…_components_index_ts). Same issue forimport { Y } from './UnverifiedEmailBanner'— the target ID lacked the_tsxsuffix.Fix
1.
LanguageConfig— newreexport_typesfield2.
walk()— re-export handlingAfter the
import_typesblock, check for re-export nodes. Only triggers when the node has afromchild keyword — this distinguishesexport * from './X'from regularexport const/function/classdeclarations, which must still walk their children for node extraction.3.
_import_js()— extensionless + index file resolution4. Enabled for
_JS_CONFIGand_TS_CONFIGVerification
Tested on a large TypeScript monorepo (~2,800 files, Next.js + React). The chain:
Previously: all three nodes appeared isolated (0 cross-file edges between them).
After fix:
NavigationSidebar.tsx --imports_from→ index.ts --imports_from→ UnverifiedEmailBanner.tsx✓The graph went from treating every barrel-exported component as a disconnected island to correctly tracing the full import chain through
index.tsfiles.🤖 Generated with Claude Code