fix: resolve unmapped @/ imports in JS projects - #3362
Conversation
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 2 advisory finding(s) below merit a look before merge.
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Adds an unmapped @/ path-alias fallback for JS/TS so that, when no tsconfig.json/jsconfig.json is present, _resolve_js_module_path treats @/... as a project-root convention alias — resolving against <anchor>/src/<subpath> when a src/ dir exists, else <anchor>/<subpath>, with _find_js_project_anchor walking upward to the nearest package.json/pnpm-workspace.yaml, then a VCS root, then start_dir. Wires the same fallback into _resolve_rescued_specifier so rescued specifiers resolve to real file/symbol/call edges. Preserves the existing #3125 behaviour: any tsconfig.json/jsconfig.json (with or without paths) disables the convention, and explicit paths mappings still take precedence.
Worth a look
- _resolve_js_module_path @/ fallback in extract.py bypasses tsconfig guard used in resolution.py —
graphify/extract.py:1620· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Unresolved @/ fallback can dereference None —
graphify/extractors/resolution.py:568· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 1996 functions depend on the 373 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 546 callers, 43 callees - new:
_rebuild_code()— 113 callers, 50 callees - new:
_extract_generic()— 18 callers, 25 callees - new:
extract_js()— 85 callers, 4 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
dispatch_command()— 2 callers, 124 callees - new:
extract_objc()— 27 callers, 9 callees - new:
_resolve_js_module_path()— 28 callers, 8 callees - …and 44 more — each is listed as a finding
Verification — 1996 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 1831 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify \_resolve\_rescued\_specifier.
The verifier did not have enough to check \_resolve\_rescued\_specifier, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify \_resolve\_js\_module\_path.
The verifier did not have enough to check \_resolve\_js\_module\_path, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `start_dir` is annotated `Path | None` — outside the synthesizable primitive/collection set
· 2 grounded finding(s) anchored inline below; 50 more finding(s) on lines outside this diff (see the check run).
|
|
||
|
|
||
|
|
||
| def _resolve_js_module_path(raw: str | Path, start_dir: Path | None = None) -> Path | None: |
There was a problem hiding this comment.
_resolve_js_module_path()
fans out to 8 callees (efferent coupling); 28 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
|
|
||
| return None | ||
|
|
||
| def _resolve_js_import_target(raw: str, str_path: str) -> "tuple[str, Path | None] | None": |
There was a problem hiding this comment.
_resolve_js_import_target()
9 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
|
Shipped in v0.9.56 — landed on |
Summary
Fixes #3357 by resolving conventional
@/imports in JavaScript/TypeScript projects that do not have atsconfig.jsonorjsconfig.json.Previously, an import such as:
could remain unresolved, preventing Graphify from establishing import evidence and therefore preventing the corresponding cross-file
callsedge under the#1659safety gate.This change resolves
@/imports to concrete files on disk without restoring the historical name-based inference fallback.What changed
Added unmapped
@/convention resolution to the central JS/TS module resolver.Discover the project anchor using:
package.jsonorpnpm-workspace.yamlProbe conventional locations in this order:
<project>/src/<path><project>/<path>Reuse
_resolve_js_import_path()so normal JS/TS extensions and index resolution remain consistent.Applied the same resolution behavior to regex-rescued imports.
Preserve existing
tsconfig.json/jsconfig.jsonalias andbaseUrlbehavior.Safety / compatibility
This does not restore the old repository-wide name-based
INFERREDcall fallback.Instead, a
callsedge is produced only after the@/import resolves to a concrete file and provides legitimate import evidence, preserving the#1659safety boundary.Also preserves the
#3125behavior: when atsconfig.jsonexists without apathsmapping, the unmapped@/convention is not synthesized.Scoped packages such as
@scope/pkgremain unaffected.Tests
Added regression coverage for:
@/reproduction.src/layouts.tsconfig.jsonwithoutpaths(#3125).tsconfigalias precedence.@/targets.#1659).Verification: