Files
codegraph/codegraph-kernel
Colby Mchenry 8998697f1d fix(vue,ts): link calls in Vue templates and in top-level destructuring (#2340) (#2357)
`codegraph callers <composable>` missed most of the components using it in
a Nuxt app (Issue #2340), through two extraction gaps:

- A destructuring declaration at module or `<script setup>` scope
  (`const { a } = useFoo(1)`, `const [b] = useFoo(1)`) mints no symbol,
  and extractVariable skipped its declarator with `continue` before
  walking the initializer, so the call was never recorded (inside a
  function the body walker reached it). The declarator is now walked the
  way a function body walks it, with the enclosing scope on the stack:
  the file for module code, which a `<script setup>` block folds into its
  component. Defaults in the pattern and calls inside callbacks in the
  initializer count too. Mirrored in the native kernel
  (tsjs/extractors.rs), which extracts TS/JS in released builds.
- The Vue extractor parsed only `<script>`, so calls in the template
  (`{{ useBar(link) }}`, `:to="useBar(link.location)"`, `v-if`, `v-for`
  sources, `@click="save(item)"`) made no reference. The new
  vue-template-calls scanner reads the root `<template>` quote-aware,
  collects interpolations and directive values, and names each callee
  the way a script call is named (`useBar`, `store.fetchUsers`). It skips
  `v-for` aliases and slot props in their element's subtree, arrow and
  function parameters, `$`-helpers, Vue's template globals, strings,
  regex literals, `new X()`, object-method shorthand, `v-pre` subtrees,
  custom blocks and non-HTML templates. Each call is a `calls` reference
  from the component at its own line and column; a bare call to the
  component's own Options API method is linked at extraction, since
  resolution only binds those through `this.`.

Two resolution follow-ons, found validating on elk:

- The vue-handler synthesizer resolved the leading identifier of every
  `@event` value, so `@click="save(item)"` got a second, line-1 edge, and
  `@click="query = ''"`, a slot prop's `hide()` or a `refresh()`
  destructured from useAsyncData were bound to any same-named function in
  another file. It now resolves only a bare handler name (`@click="save"`)
  and keeps its composable-destructure mapping.
- The Nuxt auto-import rule answered a call to a file's own `clearError`
  or `navigateTo` with an edge from the caller to itself. A same-file
  declaration of that name now shadows the auto-import.

Verification: new tests in vue-template-calls.test.ts (scanner shapes and
four indexed projects, incl. the issue's repro), extraction.test.ts
(module-scope destructuring in ts/tsx/js/jsx; script-setup + template
refs, LF and CRLF) and kernel-tsjs-parity.test.ts (destructuring, kernel
vs wasm). On main (src + kernel reverted) the 10 indexed/extraction tests
fail and the 4 parity cases fail with the main kernel; all pass with the
fix (CODEGRAPH_KERNEL_EXPECT=1). Issue repro on the built CLI with the kernel:
callers useFoo 2 -> 5 (A.vue, B.vue, F.vue, G.ts, D.ts), callers useBar
0 -> 2 (H.vue, I.vue). kernel-parity.mjs over elk, nuxt/movies and
fastify: 467/471 files byte-identical, 0 diffs, 4 deferred.

Validation (kernel loaded, main vs branch, edges by natural key):
elk 6282 -> 6633 (+443 / -92), vue-element-admin 2337 -> 2372 (+60 /
-25), nuxt/movies 939 -> 959 (+26 / -6), fastify (plain JS control) no
edge change. Every removed edge is a line-1 vue-handler edge whose pair
is now linked at the handler's line (101), a name-collision guess (8),
or an auto-import self-loop now resolved to the local function or route
(14). Self-loops: elk 44 -> 30, others unchanged.

Builds on #2341 by @drakeo338 (walk the destructuring initializer) and
the template scan in #2073 by @L0garithmic.

Co-authored-by: drakeo338 <paranoyouz@gmail.com>
Co-authored-by: L0garithmic <L0garithmic@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 20:17:51 +00:00
..