Repository navigation
Description
Filed the way the README asks for a language request: naming the grammar and the repo I would run it on.
Grammar
virchau13/tree-sitter-astro — MIT. It is what nvim-treesitter ships: pinned at revision 213f6e6973d9b456c6e50e86f19f66877e7ef0ee, maintainer @virchau13, tier 2.
Two things worth stating up front, because both bear on the cost:
- That pinned revision is from 2025-04-19, and it is still the head the ecosystem uses.
PRRPCHT/tree-sitter-astro-next(Apache-2.0, pushed 2026-02-13) is more recent but has no adoption to speak of. I have run neither against ripwire's query pipeline. - The grammar is not self-contained.
nvim-treesitterdeclaresrequires = { 'html', 'html_tags' }— the template half of an.astrofile parses through HTML injection. ripwire currently treats HTML as a document, not a grammar, so adopting the full grammar means bringing in an HTML parser too.
That second point is the main argument for the reduced scope below.
The repo I would run it on
Public: withastro/docs.
Locally: a private Astro + TypeScript monorepo — 1453 files indexed, 13648 symbols, 154 .astro files unindexed.
What it would buy
Astro splits a project the same way every time: the pages are .astro, the logic is .ts. The frontmatter of an .astro file — the block between --- fences — is TypeScript, and it is where the page calls into services.
Without the grammar the call graph is missing its entire top layer. Measured on the repo above: 114 of 172 exported service functions have callers only in .astro files, so --callers answers count="0" for two thirds of that app's service surface. --for still ranks the right symbols, and --callers is exact within the .tsx half — the gap is specifically the page layer.
Scope note
An .astro file is frontmatter (TypeScript) plus a template with JSX-like expressions. If the full grammar is more than it is worth, extracting only the frontmatter as TypeScript would already recover most call edges: the template mostly interpolates values the frontmatter computed. That would be a smaller change than a new language round, and it is where the calls are.
Related
The silent count="0" this gap produces today is filed separately as #66 — that one stands on its own regardless of whether Astro is ever added, since it also reproduces with Kotlin.
Disclosure
Investigated and written with AI assistance (Claude Code). The counts above come from real runs of the 0.5.0 release binary.
Activity
joyful-ii-V-I commentedon Sep 10, 2026
This is the most complete language request the project has had, and two things in it are worth naming before anything else.
You found the cost before we did. The grammar not being self-contained is the whole decision, and you flagged it in the second bullet.
nvim-treesitter'srequires = { 'html', 'html_tags' }meanstree-sitter-astroparses the.astroskeleton and delegates the template to HTML and the frontmatter to TypeScript. We vendor 23 grammars and none of them is HTML, so the full grammar is a two-grammar adoption plus injection plumbing.Your reduced scope is the right call, and it is cheaper than you pitched it. Extracting only the frontmatter as TypeScript is not a stripped-down version of the full job — it is a different, much smaller job:
ts_parser_set_included_ranges()is already in our vendored tree-sitter (api.h:263). ripwire has never called it — zero hits insrc/— so this would be the first use, but it is an API we already ship, not machinery to invent.---fences are findable without any parse at all.Your own measurement is the argument: 114 of 172 exported service functions have callers only in
.astrofiles, and the frontmatter is where those calls live. The template mostly interpolates what the frontmatter already computed.If you want to take it
prompts/add-a-language.mdis written for exactly this, aimed at a coding agent, and it is honest about where the afternoons go. Point Claude Code (or your agent of choice) at it from a checkout.Do STEP 0 first and be willing to stop there. It asks for a parse rate before any code, and it says plainly that a low rate is a finding rather than a failure. For Astro there is a version of STEP 0 that costs almost nothing and answers the real question: map
.astroto the TypeScript grammar wholesale and measure the parse rate. The TS grammar will choke on the template half, so the number will be poor — but how poor, and which nodes survive, tells you whether one-range injection recovers the call edges or whether the template carries more than we think. That is a one-line experiment againstwithastro/docsand your private monorepo, and it is worth more than any amount of reasoning about it.Astro-specific things that will save you time
src/ingest_crawl.hmaps extension → (Lang, grammar, label), and it is normal there for an extension to use a grammar that is not its namesake:.metal→tree_sitter_cpp,.cu/.cuh→tree_sitter_cuda,.mm→ ObjC..astro→ TypeScript is the same shape.Langenum is serialized into the cache — append only. Reordering invalidates every cache in existence. The prompt says this; it is the trap most likely to bite.kParserVerinsrc/ingest_cache.his at 85 and moves with any extraction change —kIngestParserVerMirrorinsrc/quality.hMUST move in the same commit (three separate collisions on that pair happened in one day recently). And PR Dart: a 23rd grammar, and the span arm its tree shape requires #75 adds Dart as a 23rd grammar; it also fixes six sites that sized per-language arrays withLang::Elixir + 1instead ofkLangCount, so if Astro lands after Dart that class of bug is already gone from under you. Rebase and re-derive those numbers rather than carrying them.One thing already resolved
You filed the silent
count="0"separately as #66 — that landed and is closed, along with #62 and #63, in the confident-zero fix. So the "answers zero when the callers live somewhere unindexed" half of your report is fixed independently of whether Astro is ever added, exactly as you argued it should be.And if you would rather not
Filing this issue, at this quality, was already a contribution — the cost analysis in it is the part we would have had to do anyway. If someone else picks it up, they will be starting from your numbers. If you do take it, say so here and ask whatever you need; the frontmatter-only route in particular is small enough to be a first PR, and we will review it properly rather than quickly.
joyful-ii-V-I commentedon Sep 11, 2026
Marking this
help wanted. Astro support would be a great contribution, and the path is well-trodden now.prompts/add-a-language.mdwalks an AI-assisted port end to end:The recent Dart and Kotlin ports are good templates to read alongside it.
The interesting design question for Astro is likely the component script: the
---frontmatter block holds JavaScript/TypeScript, and reusing the existing TypeScript extraction for it would be the natural route rather than re-deriving symbols.If you pick this up, say so here so nobody duplicates the work. Maintainers are happy to review early drafts.
snrmwg commentedon Sep 15, 2026
A follow-up that changes the size of the frontmatter-only route.
--callersdoes not see a call that has no named enclosing function. Astro frontmatter is module-level code. Parsing only the frontmatter would bring those calls into--uses, but not into--callers,--impactor--affected.Reproduced on 0.6.1 (release binary, linux-x64). Plain TypeScript, no Astro involved:
{"of":"getFair","defs":1,"count":2,"root":".","hop_tested":0,"hop_untested":2,"graph_ambiguous":0,"graph_unresolved":0,"counts_floor":true,"callers":[{"t":"fn","n":"load","p":"src/named.ts:2"},{"t":"fn","n":"GET","p":"src/route.ts:3"}]}--usesfinds all four.--callersreportscount=2withgraph_unresolved=0, so nothing in the answer hints at the two missing sites.A frontmatter-only port would therefore also have to attribute module-level call sites to something the caller walk reaches, for example a file-level symbol. That looks like the same design question as #60: a call inside a
node:testarrow callback has no enclosing symbol either.Measured on the monorepo from my original report, TypeScript side only. Ground truth was TypeScript's
findReferencesover the 178 exported service functions inapps/www, with Astro frontmatter loaded as virtual.tsfiles.--callersfound 58 of the 113 non-Astro files that reference one of them. The 55 misses are all 52 test files and 3 API route files; I have not looked into why the routes are missed.I won't take the frontmatter-only PR on for now. If someone builds a branch, I'm glad to run it against the monorepo.
joyful-ii-V-I commentedon Sep 16, 2026
Thank you, this measurement is very useful. Those misses have the same root cause as #60: a call outside any named function is dropped from the call graph. We're fixing that first under #60, which should make the frontmatter part much smaller. When there's a branch, we'd be glad to take you up on running it against your monorepo; we'll ping you here.
sclyde commentedon Sep 22, 2026
Taking this, frontmatter-only, along the route you and @snrmwg converged on. STEP 0 first, as
prompts/add-a-language.mdasks — here are the numbers before the code.STEP 0
Corpus: 1902 real
.astrofiles —withastro/docs,withastro/astro,withastro/starlight,onwidget/astrowind,satnaing/astro-paper, plus two private Astro sites. Shallow clones, not committed, in the spirit ofbench/recalleval/extcorpus.lock.You asked for the one-line experiment — map
.astroto the TypeScript grammar wholesale and see how poor it is. It is poor:Against
.metal's shipped 0.0081 and the 0.123 the C grammar was rejected at for CUDA, wholesale is 27×–120× worse than the option this project already turned down. So it is refused rather than error-recovered.The single degraded file under frontmatter-only is
packages/astro/e2e/fixtures/errors/src/pages/astro-frontmatter-syntax-error.astro— a file Astro ships on purpose to test its own error reporting.What it recovers
files=54 symbols=539 edges=88files=145 symbols=645 edges=137files=63 symbols=1949 edges=40files=90 symbols=1993 edges=75files=777 symbols=8905 edges=1351files=866 symbols=8981 edges=1381files=2729 symbols=24939 edges=2276files=2811 symbols=25011 edges=2309On the grammar
I checked
virchau13/tree-sitter-astroat the revision you pinned,213f6e69— still HEAD, so the ecosystem really has not moved. It does not help, for a reason worth recording:grammar.js:81isfrontmatter: $ => seq(token(prec(1,'---')), optional($.frontmatter_js_block), '---'), andfrontmatter_js_blockis an external token. The grammar lexes the whole frontmatter as one opaque blob and parses none of it, andqueries/ships onlyhighlights.scmandinjections.scm— notags.scm. Since ripwire extraction is tags-query driven, it would yield zero definitions. Every symbol still has to come from the TypeScript grammar over that range.PRRPCHT/tree-sitter-astro-nexthas the identical design and 0 stars / 0 forks.One small correction to the thread, since the cost analysis leaned on it: the grammar is self-contained as a build artifact —
grammar.jsdoesgrammar(require('tree-sitter-html/grammar'), …), which resolves at generation time, so the committedparser.calready has HTML merged in andinjections.scmnever injects HTML. Vendoring would not have needed a second grammar. It is still the wrong move, for the tags reason above.#60
Confirmed fixed on
mainbefore building anything:--callers=sum_of_squaresontest/jslangfixreturns the module-scope owner. So frontmatter top-level calls mint real--callersedges, and @snrmwg's 2026-09-15 objection no longer applies. They read as<s t="modscope" n="<file-scope>" p="src/pages/index.astro:1"/>rather than a named function, which the PR discloses.@snrmwg — I could not reproduce your 3 API-route misses. Every route spelling I could construct resolves on current
main:export async function GET,const GET: APIRoute = async (ctx) =>,satisfies APIRoute, a wrapper call, and a handler inside an object literal — as do~/*path aliases and workspace package imports. You measured on 0.6.1, and the object-literal shape in particular was a #60 case, so I think #60 took them. I would rather say that than file a speculative issue. The 52 test-file misses were certainly #60.Branch is up: #320.
@snrmwg — this is the branch you offered to run against the monorepo, and I would very much like that. Your ground truth was 114 of 172 exported service functions reachable only from
.astro; what I would most like to know is how many of those--callersfinds now, and whether the 3 API routes come back. Build iscmake -S . -B build && cmake --build build -j, and--callers=<a service fn>onapps/wwwis the whole test.feat(lang): Astro — .astro frontmatter on the TypeScript grammar, via…
snrmwg commentedon Sep 27, 2026
Ran 0.6.5 (release binary) against the monorepo from the original report. Ground truth: TypeScript
findReferencesover the 179 exported service functions inapps/www, with the Astro frontmatter loaded as virtual.tsfiles. That gives 349 (export, file) pairs.trackhas 6 same-named definitions, which the answer discloses asunproven_defs=3. Or a function is passed as a value in a table-driven test instead of being called.href={createFairWebsiteLink(...)}. For 5 exports those are the only callers, and--callers/--usesanswercount=0with nothing in the answer hinting at it.graph_unindexedstill carries the repo-wide total on every answer. Would a per-answer marker be feasible, e.g. when the symbol is imported by an.astrofile whose template is unread?On the 3 API-route misses: on 0.6.5, only 3 route files are missed, all because of the
trackambiguity. I can't tell whether they are the same 3 as in my earlier measurement.