Skip to content

Bug: Vue lang="tsx" scripts use the JavaScript parser; warnings depend on surviving symbols #2570

Closed

Description

@leadingGo

Summary

Vue SFC <script lang="tsx"> blocks are classified as JavaScript rather than TSX. Valid TypeScript inside them is therefore parsed with the JavaScript grammar.

The visible symptom depends on whether any script symbols survive: a macro-only withDefaults(defineProps<T>()) script emits parse produced no symbols, while adding an unrelated const suppresses that warning even though the underlying JavaScript parse still has errors.

Environment

  • CodeGraph reports 1.6.2, built from main at 8f1f842b5b828175992d2ce2cdf673b955b2cce9 (not just the v1.6.2 release tag).
  • Windows x64, bundled Node v24.16.0.
  • Vue compiler used for independent validation: @vue/compiler-sfc 3.4.27.

Minimal reproduction

Create an otherwise empty directory containing Title.vue:

<template><h1>{{ title }}</h1></template>
<script setup lang="tsx">
withDefaults(defineProps<{ title?: string }>(), { title: 'Hello' });
</script>

Run:

codegraph init --yes .
codegraph files --json --path .

Observed warning:

Title.vue: parse produced no symbols (tree has errors) — the file is indexed but contributes nothing to the graph

The file record has nodeCount: 2 and a parse_error warning. The two retained nodes are the file and component, so the diagnostic should not be read as the entire SFC being absent from the graph.

Controls verified in separate fresh indexes

  1. Original lang="tsx": 2 nodes, 1 edge, one parse_error warning.
  2. Change only the attribute to lang="ts": 2 nodes, 1 edge, no warning.
  3. Keep lang="tsx" and add const marker = 1; inside the script: 3 nodes, 2 edges, no warning.

Using CodeGraph's own parsers directly on the original script content, the JavaScript tree has hasError: true, while the TSX tree has hasError: false. Adding the constant leaves the JavaScript tree erroneous; it only changes the warning gate. These checks were run against the installed build, not inferred solely from the source.

The same behavior was reproduced on nine existing SFCs. All nine passed the Vue compiler's SFC parsing, compileScript, and template compilation. Three other existing Vue TSX components also produced erroneous JavaScript trees without a warning because symbols survived extraction.

Likely cause

vue-extractor.ts:212 recognizes only ts / typescript:

const isTypeScript = /lang\s*=\s*["'](ts|typescript)["']/.test(attrs);

processScriptBlock consequently selects javascript for tsx. The warning gate in tree-sitter.ts:741 explains why only the zero-symbol script reports it.

Expected behavior

Preserve the script block's declared language and select the TSX grammar for lang="tsx", including blocks containing actual JSX. Valid macro-only scripts should not produce a parser warning. A regression test should also cover a TSX script with another declaration, since a missing warning alone does not prove the right grammar was used.

Related: #1858 rewrites Vue API extraction, but its current diff still checks only ts / typescript when selecting the delegated script parser. #2336 concerns index-health reporting rather than this language selection bug.

Activity

  1. yanhongwang1017 commented on Oct 16, 2026

    @yanhongwang1017

    Fixed in #1858 (the Vue component-contract PR, which rewrites this extractor).

    Your diagnosis was exactly right — lang="tsx" fell through to the JavaScript
    grammar. The fix carries the declared lang through instead of an is-TS flag:
    tsx → the TSX grammar, jsx → JSX, and ts stays TypeScript. Two things your
    report surfaced that a single-line change would have missed:

    • collectComponentApi's delegated parse needed BOTH babel plugins
      (typescript + jsx) for tsx, not just jsx;
    • expandGrammarLanguages preloaded only typescript/javascript for Vue
      projects, so even with the right grammar name the parser was absent — and a
      parser_error makes the whole file store nothing, which is why the symptom
      was "the file contributes nothing" rather than a partial parse.

    Both of your suggested tests are in: the macro-only script (no warning) and a
    tsx script with real JSX, since — as you noted — a surviving declaration
    suppresses the warning even under the wrong grammar. Thanks for the detailed
    repro; the controls you ran made this quick to confirm.

  2. added a commit that references this issue on Oct 23, 2026
    7a156d3
  3. added a commit that references this issue on Oct 23, 2026
    9ee6718
  4. colbymchenry commented on Oct 23, 2026

    @colbymchenry
    Owner

    Thanks for the careful report and the controls; they made this quick to pin down.

    #2824 is merged to main. It reads <script lang="tsx"> with the TSX grammar. It also makes Vue projects load that grammar when they have no .tsx or .js file of their own; without that, those components would have dropped out of the index entirely.

    A macro-only script like your Title.vue no longer warns, and the interfaces and types a TSX script declares now show up in the graph. It ships in the next release; re-index Vue projects that use lang="tsx" after upgrading.

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions