Skip to content

JS: calls edge dropped for a @/ alias import when no tsconfig/jsconfig exists (0.8.11 emitted it, 0.9.53 does not) #3357

Description

@johnhu32

Summary

A JavaScript file that imports a named function through a path alias (import { fn } from "@/other.js") and calls it inside a method body gets a calls edge at graphify 0.8.11 and no calls edge at 0.9.53. The imports and imports_from edges for that file are gone at 0.9.53 as well. The nodes for both functions still exist, extraction reports success, and nothing warns.

The trigger is the alias specifier with no tsconfig.json or jsconfig.json on disk. Frameworks such as uni-app / HBuilderX resolve @/ to the project root natively and ship without either config file, so the alias never reaches _resolve_tsconfig_alias with a mapping and the specifier falls to the bare-import branch in extract.py (the "treat as external" path just below the #701 comment). 0.8.11 still emitted the calls edge in that situation, so this is a regression for those projects.

Versions and environment

  • graphifyy 0.8.11 (edge present) and graphifyy 0.9.53 (edge absent), each in its own clean venv, Python 3.14.6, macOS 26.6.2
  • Both runs: graphify update <dir>, no LLM key set, tool printed "no LLM needed"

Minimal reproduction

Two files in an otherwise empty folder. No package.json, no tsconfig.json, no jsconfig.json.

adapter.js

export function enableBackgroundBle(options = {}) {
  const timer = setInterval(() => {}, options.interval || 1000);
  return () => clearInterval(timer);
}

client.js

import { enableBackgroundBle } from "@/adapter.js";

export class BleClient {
  async createBLEConnection({ forceRefresh = false } = {}) {
    this._cleanup = enableBackgroundBle({ interval: forceRefresh ? 500 : 1000 });
    return true;
  }
}

Run:

graphify update "$PWD"
python3 -c "
import json; g=json.load(open('graphify-out/graph.json'))
for l in g['links']: print(l['source'], '--'+l['relation']+'-->', l['target'], l.get('confidence'))"

Actual

graphify 0.8.11 (5 nodes, 5 edges):

client_js --imports_from--> adapter_js EXTRACTED
client_js --contains--> ..._client_bleclient EXTRACTED
..._client_bleclient --method--> ..._client_bleclient_createbleconnection EXTRACTED
..._client_bleclient_createbleconnection --calls--> ..._adapter_enablebackgroundble EXTRACTED
adapter_js --contains--> ..._adapter_enablebackgroundble EXTRACTED

graphify 0.9.53 (5 nodes, 3 edges):

adapter --contains--> adapter_enablebackgroundble EXTRACTED
client --contains--> client_bleclient EXTRACTED
client_bleclient --method--> client_bleclient_createbleconnection EXTRACTED

Expected

At 0.9.53, at least the edge 0.8.11 produced:

client_bleclient_createbleconnection --calls--> adapter_enablebackgroundble

Ideally also client --imports--> adapter_enablebackgroundble and client --imports_from--> adapter, which 0.9.53 does emit when the import is relative (below).

What isolates it

Same two files, three variations, all at 0.9.53:

Variation calls edge imports edges
from "@/adapter.js", no config file absent absent
from "./adapter.js", no config file present present
from "@/adapter.js" plus a jsconfig.json with "paths": { "@/*": ["./*"] } present present

So the alias resolver works when it has a mapping. The regression is the fallback when it has none: 0.8.11 resolved the call by name and 0.9.53 does not.

Impact on a real corpus

On a JS/Vue project of roughly 17.5k nodes that uses @/ throughout and has no tsconfig/jsconfig, rebuilding from 0.8.11 to 0.9.53 took distinct cross-file calls pairs from 482 to 192; 404 of the old 482 have no counterpart in the new graph. Same-file calls pairs rose over the same rebuild (3,955 to 5,201), so this is specific to cross-file resolution. graphify path answers that routed through one of those edges at 0.8.11 now return "No directed path found", and --undirected finds nothing either.

Not a direction artifact

I checked this against the _src/_tgt handling from #563: export.py:347 in 0.9.53 pops the markers and writes source/target in the true order, and the written graph.json carries directed: false with no markers. The edge above is not flipped; it is absent from the link list entirely.

Related

A possible direction: when an alias specifier has no tsconfig/jsconfig mapping and the last path segment matches exactly one on-disk source file in the scanned tree, resolve to it instead of marking it external; or keep the 0.8.11 name-based calls fallback for unresolved bare specifiers, marked INFERRED.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions