Fix relative TS/JS imports never showing up in deps/imported_by#543
Fix relative TS/JS imports never showing up in deps/imported_by#543nsxdavid wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8c629070f5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "."; | ||
| const joined = std.fmt.allocPrint(allocator, "{s}/{s}", .{ dir, raw }) catch return null; | ||
| defer allocator.free(joined); | ||
| return normalizePath(joined, allocator); |
There was a problem hiding this comment.
Resolve extensionless TS imports before storing
When a TS/JS file uses the common extensionless form, e.g. import { foo } from "../bus/event-bus", this normalizes and stores the edge as daemon/src/bus/event-bus rather than the indexed file path daemon/src/bus/event-bus.ts. Since getImportedBy only checks the exact queried path and its basename, codedb_deps daemon/src/bus/event-bus.ts still returns no importer for the typical TS/JS case this change is meant to fix. Please resolve relative specs against indexed candidates or the language's extension list before interning the dependency key.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 83fddc0. Extensionless imports now take the importing file's extension, so import { foo } from "../bus/event-bus" from a .ts file resolves to daemon/src/bus/event-bus.ts and lines up with the indexed file. Added a test that indexes that exact extensionless form and checks both depends-on and imported_by resolve.
Went with the importer's extension rather than probing the index, since dep resolution runs per-file during indexing and the target may not be indexed yet, so an index lookup would be order-dependent. The same-language assumption (TS imports TS, JS imports JS) covers the common case without that hazard.
| if (isRelativeImport(spec)) { | ||
| const tmp = resolveRelativeImportPath(importer_path, spec, tmp_allocator) orelse return null; | ||
| defer tmp_allocator.free(tmp); | ||
| return try dep_graph.internString(tmp); |
There was a problem hiding this comment.
Avoid leaking interned dependency keys on reindex
Each relative import now gets duplicated into str_arena, but replacing deps during watcher reindex only deinitializes the old forward list and removes reverse membership; the arena allocations for the old resolved keys are never reclaimed until the entire Explorer shuts down. In a long-running MCP server, repeatedly editing a TS/JS file with relative imports will grow memory by one interned path per import per rebuild. Consider interning by value with reuse or owning/freeing resolved keys when setDeps replaces a file's deps.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 83fddc0. internString now dedups through a map, so re-resolving the same path returns the same arena slice instead of allocating a fresh one. Re-editing a file with relative imports reuses the existing keys, so growth is bounded by the number of distinct resolved paths, not by rebuilds. Added a test asserting two interns of the same string return the same pointer.
rebuildDepsFor (and its twin on the snapshot-restore path,
rebuildDepsFromOutline) dropped any import specifier containing "..",
so intra-repo relative imports never appeared in deps/imported_by; only
bare specifiers (node:*, package names) survived.
Add one shared resolveDependencyKey() used by both dep-build paths:
relative specifiers resolve against the importing file into repo-rooted
paths. Extensionless imports ("../foo") take the importer's extension so
they line up with the indexed file ("dir/foo.ts"). Resolved keys are
interned in a dep-graph-owned arena with a dedup map, so re-indexing a
file reuses one allocation per path instead of growing the arena. The
snapshot path dedups like the fresh path. outline.imports stays raw, so
issue-114 and the other import tests are unchanged. Also fixes a
pre-existing success-path leak in normalizePath.
Tests cover depends-on/imported_by for both explicit and extensionless
relative imports, and that interned keys are reused.
8c62907 to
83fddc0
Compare
|
Hi @nsxdavid — thanks again, another solid one. 🙏 The arena + intern-map ownership is correct (maps freed before the arena in Same as the sibling PR: instead of asking you to retarget off Tiny non-blocking note if you ever revisit: worth confirming Credited as author + in the 0.2.5825 release notes. Closing as landed. Thank you! 🙏 |
…ps/search/CLI + @nsxdavid credit Documents the work that landed in 0.2.5825 beyond the original #537/#538/#539 cut (codedb_callpath + PageRank #531, symbol filters, JSON/paths_only output, TS/JS dep-graph fixes #540/#541/#542/#543/#548, search word-index loading #547, CLI hardening #528) and credits @nsxdavid. Removes the pre-publish TODO. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes #541.
Every import with
..in it got thrown out before it hit the dep graph, so all the intra-repo../foodeps were invisible andimported_byon an internal file came back empty. This resolves relative specifiers against the importing file into real repo paths instead of dropping them.The skip lived in two places that had drifted:
rebuildDepsFor(fresh index) andrebuildDepsFromOutline(snapshot restore). The binary always reloads from a snapshot, so the restore copy is the one that broke it in real use. You could write an in-process test that passed and still have it broken from the CLI. Both now go through one sharedresolveDependencyKey, including the same dedup, so a file importing../fooas both a type and a value doesn't double up after reload.outline.importsstays raw, the resolved strings are interned in a dep-graph-owned arena, soissue-114and the other import tests don't change. Also fixes a pre-existing leak innormalizePaththat this newly hits under a leak-checking allocator (partswas errdefer-only, so it leaked on the success path).Red to green: the new test checks depends-on and imported_by for a
../import. Fails before (relative dep dropped), passes after. Verified through the built binary too, not just in-process. On a two-file repo whererole/driver.tsdoesimport type { Bus } from "../bus/event-bus.ts":Before:
After:
Files:
src/explore.zig(resolveDependencyKey, rebuildDepsFor, normalizePath),src/snapshot.zig(rebuildDepsFromOutline),src/test_parser.zig.Off current main, no generated files. Read CONTRIBUTING and followed it.