Skip to content

feat(vue): put the Vue component contract in the graph — API, template bindings, cross-file types - #3135

Open
yanhongwang1017 wants to merge 1 commit into
colbymchenry:mainfrom
yanhongwang1017:vue-component-contract
Open

yanhongwang1017 wants to merge 1 commit into
colbymchenry:mainfrom
yanhongwang1017:vue-component-contract

Conversation

@yanhongwang1017

Copy link
Copy Markdown

New PR — body (paste into GitHub)

Base: colbymchenry:main ← head: yanhongwang1017:vue-component-contract
Title: feat(vue): put the Vue component contract in the graph — API, template bindings, cross-file types


This is a fresh take on #1858 (closed with direction from @colbymchenry), rebased onto current main and reworked around the three points raised there.

What it does. CodeGraph indexes Vue SFCs as generic source files, so the component contract — what an agent needs before building with or refactoring a component — was not in the graph. Questions like "what props does this component take?", "who handles this emit?", "which parents render it?" forced a Read of the .vue source (or a guess). This adds the missing layer:

  • Component API — props (name, type, required, default, leading JSDoc), emits (payload types), slots and exposed land on the component node as metadata.componentApi, for defineProps/withDefaults/defineEmits/defineSlots/defineExpose (type and runtime form) plus Options API props/emits.
  • Cross-file types — defineProps<ImportedType>() members are pulled from the declaring file: relative imports, tsconfig paths aliases, extends chains, import-rename, diamond dedup. A type it cannot follow keeps the written text; it never guesses.
  • Template wiring — every @event="handler" on a component tag becomes a references edge to the handler function (metadata.vueEvent / .vueComponent); <ChildComp> usage yields component-reference edges.
  • codegraph_component — one call returns the contract: API, children, and the parents that bind it. Opt-in via CODEGRAPH_MCP_TOOLS=component; the default tool surface is unchanged (explore only).
  • Storage — nodes.metadata with a guarded migration (NULL = "not extracted yet"; fills on re-index).

The three points from #1858:

  1. Root-checked reads. Every cross-file type read goes through lexicalPathWithinRoot (refuses ../, absolute paths outside the root, and a Windows //host/share with no syscall) and validatePathWithinRoot (realpath, so an in-root symlink pointing outside is never read). Without a root hint, cross-file expansion is skipped entirely. Tests cover relative/absolute/UNC escapes and the symlink case.
  2. Opt-in tool, no instructions text. codegraph_component is not in DEFAULT_MCP_TOOLS; it appears only when the operator lists it in CODEGRAPH_MCP_TOOLS. server-instructions.ts is untouched — no agent-facing guidance to run codegraph init.
  3. Lockfile from npmjs. package-lock.json resolves every package from registry.npmjs.org. The two new dependencies are @vue/compiler-sfc and @babel/parser (deliberately not vue-component-meta, which does not surface the per-member docs and written-type fidelity this keeps).

Validation. npm run build passes. The Vue suites (API / bindings / types) and the tool-allowlist suite are green (31 tests), including the new path-escape and opt-in tests. Full suite on this branch: 9,878 pass; the only failures are 30 installer-targets tests, which reproduce identically on a pristine main checkout in the same environment and are not caused by this diff.

Known limitations (pinned by tests): defineSlots types expand only same-file; complex TS (utility/generic/conditional types) keeps the written text.

Supersedes #1858.

…e bindings, cross-file types

CodeGraph indexed Vue SFCs as generic source files, but the component
contract — what an agent actually needs before building with or
refactoring a component — was not in the graph at all. "What props does
this component take?", "who handles this emit?", "which parents render
it?" could not be answered from the graph: the agent fell back to
reading .vue source (or hallucinated an interface), which is exactly
the Read-displacement codegraph exists to prevent. This adds the
missing semantic layer end to end:

Component API extraction (vue-extractor, rewritten):
- Script blocks are parsed with @vue/compiler-sfc parse plus a dedicated
  @babel/parser pass — compileScript strips withDefaults(defineProps<T>())
  type arguments, so macro types must be read pre-transform.
- props (name, type, required, default, leading JSDoc), emits with
  payload types, slots, and exposed members land on the component node
  as metadata.componentApi. defineProps/withDefaults/defineEmits/
  defineSlots/defineExpose in both type and runtime form, plus Options
  API props/emits.
- Cross-file types: defineProps<ImportedType>() members are pulled from
  the declaring file — relative paths, tsconfig paths aliases, extends
  chains, import-rename (lookup name vs declaration name), and diamond
  dedup so a base reached through two branches shows up once. Types the
  extractor cannot follow keep the written text; it never guesses.
- Cross-file reads go through validatePathWithinRoot: an import that
  escapes the root (`../`, an absolute path, a Windows //host/share) or
  an in-root symlink pointing outside it is skipped, not read.

Template wiring as graph edges:
- Every @event="handler" on a component tag becomes a references edge
  from the component to the handler function (metadata.vueEvent /
  .vueComponent), kebab-case tags included — callers, impact and the
  component view now see which functions a template drives.
- <ChildComp> template usage yields component-reference edges, so
  who-renders-whom is a graph query, surfaced by explore's
  dynamic-dispatch section.

MCP surface:
- New codegraph_component tool, opt-in via CODEGRAPH_MCP_TOOLS=component:
  the full contract in one call — API, child components it renders, and
  every parent @event binding attached to it, so changing an emit shows
  exactly which parent handlers break. It stays off the default face, so
  the single-tool server instructions are unchanged.

Storage and parse infrastructure:
- nodes.metadata column with a guarded migration (NULL means "not
  extracted yet"; fills as files re-index). All three node write paths
  and rowToNode carry it.
- Project-root hint threaded into parse workers (and set for in-process
  parses) so sibling-file type resolution works off-thread.

Tests: new vue-component-api / vue-component-bindings /
vue-component-types suites; mcp-tool-allowlist pins the default face to
explore and covers the component opt-in.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant