Repository navigation
fix(sync): a tsconfig, jsconfig or go.mod change re-resolves the imports it governs - #3057
Merged
Merged
Conversation
…rts it governs
The resolver read tsconfig / jsconfig `paths` and `baseUrl` and go.mod's
module path once and kept the maps for its lifetime ("callers re-create the
resolver if config changes", which none did), and the watcher follows source
files only. So after an edit to one of them a long-running MCP server kept
resolving imports by the old mapping, and so did later processes: a sync
re-resolves only the changed files, and a config-only edit changes none.
The index now carries a snapshot of the config files resolution read: every
tsconfig / jsconfig the alias loaders opened, `extends` chains included,
every go.mod, and the root's candidates whether present or not. Every sync
reads them again and, when one differs, drops the resolver's alias and
module maps with the memos resolved through them, re-opens every import the
file governs (the ES-module family for a tsconfig / jsconfig, Go for a
go.mod) for the sweep, as a moved workspace map does, and marks synthesis
for a rebuild. An index an older engine stamped records a snapshot without
re-opening anything.
Review changes: each file's digest covers what resolution reads from it, a
go.mod's module path and a tsconfig / jsconfig's `extends`, `baseUrl` and
`paths`, not its bytes. A dependency bump in go.mod or a `strict` flag in
tsconfig had re-opened every import of the language for the same graph
(caddy: 48,945 refs, sync 3.8 s against 0.9 s; excalidraw: 88,860 refs,
9.2 s against 1.1 s). Plus a test for that, the ReferenceResolver class doc
comment back on its class, and the CHANGELOG entry beside the workspace-map
one.
Validation: 7 tests; on main's source the 5 behaviour tests fail, and the
new one fails on the byte digest. 11 fixture edits, each compared with a
fresh index of the same tree, in a later process and in the same process:
main diverged in 7 and 9, this in none. Real repos, sync against a fresh
index over nodes, edges, refs and files: excalidraw with `@excalidraw/math`
retargeted was 27 edges off on main and is identical here; caddy with its
module path renamed was 6,511 edges off and is identical here. Fresh indexes
are identical to main's. A one-file sync on excalidraw, median of 5
interleaved runs: main 3,834 ms, this 3,269 ms.
Original PR #2984 by @danusha2345.
Fixes #2977
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 30, 2026
Closed
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.
Summary
codegraph syncnow follows an edit to atsconfig.json/jsconfig.jsonpathsorbaseUrl(theextendschain included) and to ago.modmodule path: the imports they map are re-resolved by the new mapping, in a later process and in a long-running one alike.Root cause
The resolver reads those files once and keeps the alias and module maps for its lifetime (its comment says callers re-create it when the config changes; none does), and the watcher follows source files only. A sync re-resolves only the changed files, and a config-only edit changes none, so imports kept the mapping read when the project was indexed until a full re-index.
What came from the contributor, and what we changed
From @danusha2345 (PR #2984), unchanged in substance: the index records a snapshot of the config files resolution read (every tsconfig / jsconfig the alias loaders opened with their
extendschains, every go.mod, and the root's candidates whether present or not); every sync compares it, and when one differs the resolver drops its maps and memos, every import of the governed languages (the ES-module family for a tsconfig / jsconfig, Go for a go.mod) is re-opened for the sweep, as the workspace-map pass does, and synthesis is refreshed. Their 6 tests.Our changes on top:
modulepath; a tsconfig / jsconfig'sextends,compilerOptions.baseUrlandcompilerOptions.paths(newaliasConfigFieldsin path-aliases.ts). With the byte digest, a dependency bump in go.mod or astrictflag in tsconfig re-resolved every import of the language for the same graph: caddy'srequirebump re-opened 48,945 refs (sync 3.8 s against main's 0.9 s), excalidraw'sstricttoggle 88,860 refs (9.2 s against 1.1 s). Both now re-open nothing.ReferenceResolverclass doc comment moved back onto its class (the new declarations had been inserted between them).file:dependency on a sibling package is not used to resolve package-name imports when there is no rootworkspaces#2456/TypeScript/pnpm: workspace member imports fail when the entry is declared via package.json "main"/"exports" (e.g. ./src/index.ts) — a same-name private function then hides real callers #2502), in the house format, with credit.Validation
Tests: 7 in
__tests__/sync-module-config-change.test.ts. On main's source the 5 behaviour tests fail and the 2 controls pass; on the contributor's byte digest the new test fails (the re-open is called 9 times); all 7 pass here. The 28 sync, path-alias, Go-import and workspace test files: 334 passed.Sync against a fresh index (nodes and edges compared as sets) over 11 fixture edits, each run in a later process (
codegraph sync) and in the same process (oneCodeGraphhandle, as the MCP server):The edits:
pathsretargeted (importer untouched, and touched as the issue did), a mapping in anextendstarget, jsconfigpaths,baseUrl, a tsconfig appearing and removed, go.mod module renamed (with and without the imports rewritten); controls: a body edit and a tsconfig edit that moves no mapping.Real repos, sync against a fresh index of the same tree over nodes, edges, refs and files:
@excalidraw/mathpaths retargeted topackages/utils.../caddy/v2renamed/v3"strict": true→falserequirepatch bumpFresh indexes of both repos are identical to main's (sync-only change).
Cost: a plain one-file body-edit sync on excalidraw, median of 5 interleaved runs: main 3,834 ms, this 3,269 ms (noise). The added work on every sync is reading the handful of recorded config files; the full re-open happens only when one of them changes what resolution reads.
Full suite on the pushed commit (
CODEGRAPH_KERNEL_EXPECT=1,CODEGRAPH_NO_DAEMONunset, 4 workers): 801 files passed, 6 skipped; 9,559 tests passed, 86 skipped, 0 failed.Fixes #2977
Lands #2984 by @danusha2345.
🤖 Generated with Claude Code