Skip to content

Vue SFC <script setup> blocks are never parsed — silent empty graph for .vue files, causing false-positive dead code #1410

Description

@oscar6echo

Version

codebase-memory-mcp 0.9.0 (Linux x86_64, standalone binary)

Platform

Linux (x64)

Install channel

GitHub release archive / install.sh / install.ps1

Binary variant

standard

What happened, and what did you expect?

Summary

.vue files are discovered and registered as File and Module nodes, and
get_architecture reports them under languages. But their <script setup>
contents are never parsed: no Function/Method nodes are created for
functions defined in a .vue file, and no CALLS edges are created for calls
originating from one.

The index therefore looks healthy while being structurally empty for every
.vue file in the repo.

Impact

This makes search_graph(max_degree=0, exclude_entry_points=true) unsafe on any
Vue project. Every utility called only from components reports in_degree: 0
and is presented as dead code.

On a 9.2k-line Vue/TS codebase the dead-code query returned 29 functions.
6 of the 11 I verified were false positives — all called from .vue files:

Reported dead Actual call sites
showModal 5
hideModal 6
copyToClipboard 2
showToast 1
hideToast 1
now 1

Acting on that list deletes working code. fan_in in get_architecture
hotspots is understated for the same reason (reported clone at fan_in: 3;
actual is 6).

Reproduction

Minimal reproduction

Four files. The .ts caller and the .vue caller do the same thing.

repro/
  package.json
  tsconfig.json
  src/util.ts
  src/caller.ts
  src/components/Caller.vue

src/util.ts

export function fnRelTs(x: number): number { return x + 1; }
export function fnAliasTs(x: number): number { return x + 2; }
export function fnRelVue(x: number): number { return x + 3; }
export function fnAliasVue(x: number): number { return x + 4; }
export default { fnRelTs, fnAliasTs, fnRelVue, fnAliasVue };

src/caller.ts

import uRel from './util';
import uAlias from '~/util';

export function runTs(): number {
  return uRel.fnRelTs(1) + uAlias.fnAliasTs(2);
}

src/components/Caller.vue

<script setup lang="ts">
import uRel from '../util';
import uAlias from '~/util';

const runVue = (): number => {
  return uRel.fnRelVue(1) + uAlias.fnAliasVue(2);
};
</script>

<template>
  <div>{{ runVue() }}</div>
</template>

tsconfig.json

{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "bundler", "strict": true, "baseUrl": ".", "paths": { "~/*": ["src/*"] } } }

package.json

{ "name": "repro", "version": "1.0.0", "type": "module" }

Then:

index_repository(repo_path="…/repro", mode="full", name="repro")
query_graph(project="repro",
  query="MATCH (f) WHERE (f:Function OR f:Method) RETURN f.name, f.file_path, f.in_degree ORDER BY f.name")

Actual

fnAliasTs   src/util.ts   1
fnAliasVue  src/util.ts   0     ← called from Caller.vue
fnRelTs     src/util.ts   1
fnRelVue    src/util.ts   0     ← called from Caller.vue
runTs       src/caller.ts 0

runVue is absent from the graph entirely — no node is created for it.

MATCH (a)-[r:CALLS]->(b) RETURN a.name, a.file_path, b.name
→ runTs, src/caller.ts, fnRelTs
  runTs, src/caller.ts, fnAliasTs

Only two edges. Nothing originates from Caller.vue.

Expected

fnRelVue and fnAliasVue at in_degree: 1, a Function node for runVue
at src/components/Caller.vue, and two additional CALLS edges.

What this is not

I first suspected namespace default imports (import u from './util' then
u.fn()) or path-alias resolution (~/util). Both work correctly — the
2×2 above isolates it: caller.ts resolves relative and aliased namespace
calls fine. The only failing variable is the caller being a .vue file.

Why it is easy to miss

get_architecture reports the files as present, which reads as success:

"languages": [ { "language": "Vue", "file_count": 34 }, ]

Aggregate edge density is the tell — on the same machine:

Repo Nodes Edges Edges/node
Python, 3.7k LOC 409 1665 4.07
Vue+TS, 9.2k LOC 480 924 1.93

And on the Vue repo:

MATCH (f) WHERE (f:Function OR f:Method) AND f.file_path ENDS WITH '.vue'
RETURN count(f)                                            → 0

MATCH (a)-[r:CALLS]->(b) WHERE a.file_path ENDS WITH '.vue'
RETURN count(r)                                            → 0

MATCH (f:File) WHERE f.path ENDS WITH '.vue' RETURN count(f) → 34

34 file nodes, zero structural content.

Suggested direction

Extract the <script> / <script setup> block from the SFC before handing it
to the TS parser, preserving a line offset so file_path/line attribution stays
correct. Template-block references (@click="handler", {{ fn() }}) are a
larger job and could be a follow-up — script-block extraction alone would
recover the bulk of the missing edges.

Two smaller asks, if useful:

  1. Until this lands, consider having index_repository warn when a language is
    detected but yields zero function nodes — that single signal would have made
    this obvious immediately.
  2. exclude_entry_points=true in search_graph(max_degree=0, …) implies a
    confidence the index cannot support for partially-parsed languages. A
    per-language "structural coverage" figure in index_status would let callers
    know when not to trust a dead-code result.

Environment

  • codebase-memory-mcp 0.9.0, standalone binary
  • Linux 7.0.0-28-generic, x86_64
  • Repos: Vue 3 + TypeScript (Vite, ~/* path alias), and Python 3 for the
    comparison baseline
  • mode="full" in all runs; skipped_count: 0, expected_nodes matched
    nodes in every index (no reported extraction failures)

Logs


Diagnostics trajectory (memory / performance / leak issues)


Project scale (if relevant)

No response

Confirmations

  • I searched existing issues and this is not a duplicate.
  • My reproduction uses shareable code (a dummy snippet or a public OSS repository), not proprietary code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingparsing/qualityGraph extraction bugs, false positives, missing edgeswindowsWindows-specific issues

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions