Follow tsconfig.json extends + project-relative node IDs for JS/TS#867
Closed
ghaiat-yoobic wants to merge 54 commits into
Closed
Follow tsconfig.json extends + project-relative node IDs for JS/TS#867ghaiat-yoobic wants to merge 54 commits into
ghaiat-yoobic wants to merge 54 commits into
Conversation
* fix: git hooks fail when graphify is installed via pipx When installed via pipx, the graphify module is only available in pipx's isolated venv, not the system python3. The git hooks (post-commit, post-checkout) hardcoded `python3` which cannot import graphify in this case. Detect the correct Python interpreter from the graphify binary's shebang line, matching the approach already used in skill.md Step 1. Falls back to python3 for system installs. * fix: handle env-style shebangs and improve interpreter detection - Use POSIX `command -v` instead of non-standard `which` - Parse `#!/usr/bin/env python3` shebangs correctly (previous `tr -d ' '` would produce `/usr/bin/envpython3`) - Add import validation fallback to python3 if resolved interpreter cannot import graphify
… buffer * fix: suppress graspologic ANSI output that breaks PowerShell scrolling graspologic's leiden() emits ANSI escape sequences (progress bars, colored warnings) that corrupt PowerShell 5.1's scroll buffer on Windows, disabling vertical scrolling. Redirect stdout/stderr to StringIO during leiden() calls to prevent any escape codes from reaching the terminal. Add 2 tests verifying cluster() produces no stdout/stderr output. Fixes #19 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: add PowerShell troubleshooting section to Windows skill Document the PowerShell 5.1 scrolling issue and provide 4 workarounds: upgrade graphify, use Windows Terminal, reset terminal, or uninstall graspologic to use Louvain fallback. Fixes #19 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- Register 'trae' and 'trae-cn' in _PLATFORM_CONFIG (skill-trae.md, ~/.trae/skills/ and ~/.trae-cn/skills/, claude_md=False) - Add CLI subcommands: graphify trae install/uninstall, graphify trae-cn install/uninstall (routes to _agents_install/uninstall) - Update help text with new platform entries - Create skill-trae.md (Agent-tool based extraction, AGENTS.md integration, no PreToolUse hook support per Trae limitations) - Update README.md and README.zh-CN.md with Trae platform docs Co-authored-by: lijinshuan <lijinshuan@bytedance.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…utput Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ean README, save-result CLI
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…#71) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e relations in innerHTML (#sec) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tree-sitter resolves call targets directly from source — marking them INFERRED was incorrect. Cross-file class-level uses edges remain INFERRED. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…AST calls Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ooks (#140) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add load_tsconfig_paths() to detect.py that walks up the directory tree to find tsconfig.json and extracts compilerOptions.paths. Add resolve_ts_alias() to map alias prefixes (e.g. @/*) to their resolved filesystem paths. Update extract_js() to load aliases for the file being processed and pass them through a closure to _import_js(), so aliased imports like @/components/Button resolve to real module names in the edge graph.
…he, graphifyignore parent discovery, MCP fixes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…sor, Gemini in CLI section
…cription Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…port Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nstall Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…attribute Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…r prompt itself Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Real-world tsconfig.json files use JSONC syntax (// and /* */ comments,
trailing commas) by default. The plain json.loads() call silently returned
{} on any such file, making alias resolution a no-op in practice.
Replace with a string-aware JSONC stripper that handles line comments,
block comments (including multi-line), trailing commas, and string
literals containing comment-like sequences.
extract_js() now resolves aliases from tsconfig.json, but the cache was keyed only on source file contents. A tsconfig change would leave stale import edges in the cache until the source files themselves changed. Add an extra_key parameter to file_hash/load_cached/save_cached. In the extraction loop, JS/TS files with a non-empty alias map hash the serialised alias map and mix it into the cache key, so any change to compilerOptions.paths or baseUrl triggers a cache miss.
PR #155 added compilerOptions.paths resolution but only read the first tsconfig.json found while walking up from the source file. In Nx/Angular monorepos every project tsconfig.json typically looks like: { "extends": "../../tsconfig.base.json" } with no paths of its own — all aliases live in the shared base file. The previous implementation returned an empty alias map for these files, so every alias-style import silently fell back to basename matching. Refactor load_tsconfig_paths into a recursive helper that walks the extends chain. Each level's own paths override anything inherited from its parents; paths defined in a base file resolve relative to that base file's directory, matching TypeScript's actual behaviour. Also add find_project_root() (walks up looking for .git) which will be used by the import resolver in a follow-up commit to derive project-relative node IDs.
Even with alias resolution working, every node ID was derived from
path.stem (Path.stem strips one extension, so 'foo/test/data.ts' becomes
'data'). Files sharing a basename across the project all collapsed onto
the same node — a 7,700-file monorepo produced 'test/data' targets with
2,000+ incoming edges (every test/data.ts file in the codebase).
Add an optional LanguageConfig.id_for_path callable that converts an
absolute Path to a per-file identifier. When set, the generic extractor
rebinds 'stem' to its return value before deriving file/class/function/
method IDs, so all derived IDs inherit the project-relative prefix.
extract_js() now computes the project root once (via find_project_root)
and passes a closure that returns the path-relative stem.
_import_js() takes a matching project_root kwarg. When the resolved
import path is absolute (via alias resolution) or relative ('./',
'../'), it derives the target ID the same way — so source-file IDs and
import-target IDs always agree.
External imports (npm packages, bare module names like 'rxjs',
'@angular/core') and unresolved aliases continue to use the legacy
basename behaviour.
…ndex.*)
When an alias resolves to features/yobi/layout/src/flex but the actual
file is flex/index.ts (Node-style directory resolution), the import
edge needs to target the same node ID the file extractor produces for
flex/index.ts, otherwise the edge points at a phantom node.
Add _resolve_module_file() that tries the path as-is, then with each
common JS/TS extension, then as a directory with index.{ts,tsx,js,...}.
The import handler now uses the resolved file path (when found) to
derive the target node ID — keeping it in sync with what the file
extractor emits.
Falls back to the un-resolved path if nothing on disk matches, so broken
imports still get a stable id (just an orphan node) rather than skipping
the edge entirely.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Builds on #155 (by @nuthalapativarun) which added
compilerOptions.pathsresolution. Three issues that surface on a real-world Nx/Angular monorepo (~7,725 TS files):1.
extendschains are ignoredEvery project tsconfig in Nx/Angular monorepos typically looks like:
{ "extends": "../../tsconfig.base.json" }with no
pathsof its own — all aliases live in the shared base file. The previous implementation returned an empty alias map for these files, so every alias-style import silently fell back to basename matching.Fix:
load_tsconfig_pathsis refactored into a recursive helper that walks the extends chain. Each level's ownpathsoverride anything inherited; paths defined in a base file resolve relative to that base file's directory, matching TypeScript's behaviour.2. Node IDs collapse on basename
Even with alias resolution working, every node ID was derived from
path.stem(filename without extension). Across a 7,725-file project we found:datatest/data.tsfilebasebase.tsxcomponentstexttext.tsxcomponentsAll those files collapsed onto a single node.
Fix: new optional
LanguageConfig.id_for_pathcallable. When set,_extract_genericrebindsstemto its return value before deriving file/class/function/method IDs.extract_jscomputes the project root viafind_project_root(new helper that walks up looking for.git) and passes a closure that returns the project-relative stem._import_jstakes a matchingproject_rootkwarg. When the resolved import path is absolute (via alias resolution) or relative (./,../), it derives the target ID the same way — so source-file IDs and import-target IDs always agree.3. Directory-style imports point at phantom nodes
Resolving
@yobi/layout/flexto/.../src/flexand then computing a node ID misses that the actual file isflex/index.ts. The file extractor emitssrc_flex_index; the import resolver emitssrc_flex. Edge points at a phantom.Fix: new
_resolve_module_filehelper — tries the path as-is, then with.ts/.tsx/.d.ts/.js/.jsx/.mjs/.cjs, then as a directory withindex.{ts,tsx,js,...}. Import handler uses the resolved file path (when found) to derive the target ID. Falls back to the un-resolved path if nothing on disk matches, so broken imports still get a stable ID.Backwards-compat & scope
rxjs,@angular/core) and unresolved aliases continue to use the legacy basename behaviour.id_for_pathfield — Python/Java/Go/etc. extractors are untouched. Same fix could be applied per-language in follow-ups.load_tsconfig_pathsis contained — empty-paths case still returns{}.Measured impact on the test corpus
Same 7,725-file Nx/Angular repo, before and after, holding everything else constant:
Note on the head commit / split
This branch carries the 3 PR #155 commits at the base. Once #155 merges, this can be rebased to drop them. Happy to split into separate PRs — the three commits here are independent and could each land on their own.