Skip to content

fix(sync): a tsconfig, jsconfig or go.mod change re-resolves the imports it governs - #3057

Merged
colbymchenry merged 1 commit into
mainfrom
claude/pr-2984-module-config-sync
Oct 30, 2026
Merged

colbymchenry merged 1 commit into
mainfrom
claude/pr-2984-module-config-sync

Conversation

@colbymchenry

Copy link
Copy Markdown
Owner

Summary

codegraph sync now follows an edit to a tsconfig.json / jsconfig.json paths or baseUrl (the extends chain included) and to a go.mod module 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 extends chains, 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:

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 (one CodeGraph handle, as the MCP server):

    later process same process
    main bc9291a 7 of 11 diverge 9 of 11 diverge
    this branch 0 0

    The edits: paths retargeted (importer untouched, and touched as the issue did), a mapping in an extends target, jsconfig paths, 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:

    repo (sha) edit main this
    excalidraw e7b53466 @excalidraw/math paths retargeted to packages/utils 27 edges off identical
    caddy eb04ed52 go.mod module .../caddy/v2 renamed /v3 6,511 edges off identical
    excalidraw "strict": true → false identical identical, nothing re-opened
    caddy require patch bump identical identical, nothing re-opened

    Fresh 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_DAEMON unset, 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

…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sync: a tsconfig paths (or go.mod module) change keeps resolving imports with the old mapping

1 participant