Address CodeRabbit review on PR #48. The early `isExternalModule` check
in resolveImport was filtering out any import starting with `golang.org/`
before the Go case had a chance to match it against the local module
path. This blocked legitimate local imports for any project whose own
module path starts with `golang.org/` (the Go team's own packages like
golang.org/x/sync, golang.org/x/net, etc., where each one's go.mod
declares `module golang.org/x/<name>`).
Skip the early external check for Go specifically. The Go case in
resolveImport already does its own module-path-aware classification
and returns null for everything outside the local module, including
stdlib and third-party deps. No regression in those cases.
New regression test asserts that
`module golang.org/x/custom` + `import "golang.org/x/custom/internal"`
resolves to the local internal/ package. Confirmed the test fails
without the fix and passes with it. Total: 752 unit tests pass.
Co-authored-by: mrsuit92 <mrsuit92@users.noreply.github.com>
Resolves#45. Reported by @mrsuit92.
Go projects produced 0 dependency edges in codebase_graph_query and
codebase_graph_stats even though import extraction worked correctly.
The Go case in resolveImport returned null unconditionally, with a
comment that resolution required go.mod analysis. This patch adds
that analysis and wires it into the resolver, mirroring the existing
buildJvmSuffixMap and buildCsNamespaceMap patterns.
Mechanism:
- buildGoModuleInfo reads <projectPath>/go.mod once at graph-build
time, parses the `module <path>` directive, and walks the file set
to build a directory-to-representative-file map for every Go
package. _test.go files are excluded from representative selection
because Go does not allow them to be imported from non-test code in
other packages. Files are sorted lexicographically for
deterministic representative selection across machines and runs.
Returns null when go.mod is missing or has no parseable module
directive; the resolver treats null as "no Go resolution available"
and behaves exactly as before this patch in those cases.
- The Go case in resolveImport now strips the module path prefix
from the import (handling the bare-module-path root case as well
as subpackage paths) and looks up the resulting directory in the
package map. Imports outside the module path return null and are
treated as external dependencies (or stdlib already filtered
upstream by isExternalModule).
- Map keys are forward-slash paths, not OS-native, so resolution
works on Windows: Go imports are always forward-slash regardless
of host OS, but path.dirname produces backslashes on Windows for
nested directories. Normalising the key to forward slashes at
build time keeps the lookup correct across platforms.
Limitations (deferred to follow-up issues if any user reports them):
- The parenthesised module ( path ) form in go.mod is not parsed.
Not used by any mainstream Go project (verified against cobra,
gin-gonic/gin, uber-go/zap real-world go.mod files).
- vendor/ directory shadowing of external imports is not honoured.
- replace directives in go.mod are not honoured.
- go.work multi-module workspaces are not handled (each workspace
module would need its own go.mod read and prefix matching).
These are real Go features but each one widens the patch and
narrowly affects specific user populations. They can be added as
separate small PRs if a real user hits them.
Tests: 16 new cases in tests/unit/graph-resolution.test.ts covering
the new buildGoModuleInfo function (parses simple go.mod, handles
leading whitespace and trailing content, returns null on missing or
malformed go.mod, excludes _test.go from representative selection,
omits test-only directories, uses forward-slash keys for nested
packages) and the Go resolveImport case (back-compat null without
goModuleInfo, subpackage import resolves to lex-smallest non-test
.go file, root-package import resolves to a project-root .go file,
external imports return null, missing or malformed go.mod returns
null, _test.go excluded from representative selection, lexically
smallest file picked deterministically, similar-prefix imports do
not falsely resolve, nested-package imports work cross-platform).
Existing 735 unit tests continue to pass unchanged. Total: 751.
typecheck, biome, and CodeRabbit local review all clean. CodeRabbit
caught a real Windows path-separator bug in the first iteration; the
fix and a regression test for it are included.
Co-authored-by: mrsuit92 <mrsuit92@users.noreply.github.com>
Resolves#46. Reported by @mrsuit92.
Python projects where each top-level directory is a runnable application
root (a common service-style monorepo layout) had `import config` from
`service-a/main.py` produce 0 dependency edges, even when
`service-a/config.py` sits next to the importer. At runtime Python
resolves this correctly because the importer's directory is sys.path[0]
when the file is run as `python main.py` from inside its own directory.
The static resolver did not check that path.
The Python case in graph-resolution.ts only tried:
<projectPath>/<module>.py
<projectPath>/src/<module>.py
<projectPath>/lib/<module>.py
It did not try `<sourceDir>/<module>.py`, so non-relative sibling
imports never resolved. Relative imports (`from .config import ...`)
already used sourceDir and worked.
Fix: add `<sourceDir>/<module>.py` as the LAST fallback, after the
existing project-root and src/lib checks. Tried last to preserve
project-root precedence, so any layout that resolved before this PR
continues to resolve to the same file. resolveRelativePath also handles
the `<sourceDir>/<module>/__init__.py` package case via its built-in
Python init fallback, so package-style sibling imports work too.
Tests: 5 new cases in tests/unit/graph-resolution.test.ts covering
sibling-flat resolution, dotted module paths, package via __init__.py,
project-root precedence preservation, and the negative case (no match
anywhere). Existing 730 tests continue to pass; total now 735.
typecheck, biome, and CodeRabbit local review all clean.
Co-authored-by: mrsuit92 <mrsuit92@users.noreply.github.com>
The previous regex `^namespace\s+([\w.]+)` was anchored at column 0, so
nested namespace declarations (the `namespace Inner` block inside
`namespace Outer { ... }`) were silently missed. The capture group also
accepted invalid identifiers like `1Foo` and matched on stray
occurrences of the word `namespace` not followed by `;` or `{`.
The new regex `^\s*namespace\s+([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\s*(?=[;{])`:
- `^\s*` allows leading whitespace, so indented (nested) declarations
are captured.
- The dotted-identifier capture requires each segment to start with a
letter or underscore, matching C# identifier rules.
- `(?=[;{])` requires a real terminator, filtering out spurious matches.
Adds three regression tests: nested-namespace capture, rejection of
digit-leading identifiers, and the terminator-lookahead behaviour.
Found by CodeRabbit on PR #34 (nitpick).
`buildCsNamespaceMap` previously iterated `fileSet` in fs.readdir()
traversal order, which POSIX does not guarantee. Combined with the
`candidates[0]` selection in `resolveImport`, this meant a `using`
directive could resolve to different files on different machines or
runs.
Sort the `.cs` paths lexically before scanning so candidate lists are
stable. Adds a regression test that builds the map twice from sets
populated in different orders and asserts both produce the same
candidate sequence and the same first candidate.
Found by CodeRabbit on PR #34.
C# imports previously always returned null because there is no direct
filename mapping for `using X.Y.Z;`. As a result `codebase_graph_query`
reported zero edges for C# projects, and the symbol-level tools
(impact, flow, symbol) silently lost cross-file resolution because they
walk the file-import graph for Tier 2/3 candidates.
Build a namespace-to-files map once per graph build by regex-scanning
every `.cs` file for `^namespace X.Y.Z` (covers both block-scoped and
file-scoped C# 10+ syntax) and consult it from the csharp branch of
resolveImport. When a namespace spans multiple files the first
candidate is returned; multi-file fan-out is tracked as a follow-up.
External namespaces (System.*, Microsoft.*) are still filtered upstream
by isExternalModule, so the map is only consulted for project-internal
names.
Cost is O(n) reads at graph-build time, gated by a `hasCs` precheck so
non-C# projects pay nothing.
Adds 12 unit tests covering block-scoped, file-scoped, multi-file,
multi-namespace-per-file, commented-out lines, non-.cs files, empty
projects, unknown namespaces, and external (System.*) filtering.
Single-module resolution (src/main/java/…) already worked.
Multi-module layouts like:
module-a/sub/src/main/java/com/example/Foo.java
were silently unresolved because the three fixed src-dir prefixes
never matched. The dependency graph therefore always produced 0
edges for any Java/Kotlin/Scala project that follows the standard
Maven/Gradle multi-module layout.
Fix: introduce `buildJvmSuffixMap()` which scans `fileSet` once
(O(n)) and registers every JVM source file by its class-path key
(i.e. everything after src/main/<lang>/). `resolveImport` accepts
the map as an optional last argument and falls back to it when the
existing prefix-based approach yields no result.
- `buildJvmSuffixMap` exported for reuse / testing.
- Map is built once per `buildCodeGraph` call, only when the
project contains at least one JVM file — zero cost for other
language projects.
- Lookup is O(1) per import, replacing a worst-case O(n) linear
scan on every miss.
- Works on both Windows (backslash) and POSIX (forward-slash)
because the map key is built with `path.sep`.
- 7 new unit tests covering: map construction, test-source
exclusion, multi-module Java resolution, Kotlin resolution,
unresolvable class, stdlib guard.
Fixes: enterprise Java/Spring Boot codebases (30+ Maven modules)
reporting 0 edges in codebase_graph_stats.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Add CSS @import extraction from Svelte/Vue <style> blocks and standalone
CSS/SCSS/SASS/LESS files. Add path alias resolution from tsconfig.json /
jsconfig.json compilerOptions.paths with extends chain support.