Skip to content

Blazor: .razor files are never discovered, and @page routes produce no Route nodes #1667

Description

@The1nk

What problem does this solve?

Two gaps stack up, and the second is invisible until the first is worked
around.

1. .razor has no entry in EXT_TABLE (src/discover/language.c), so
discovery skips Blazor component files entirely. Indexing a Blazor
application produces no graph nodes for any component. The only way in is an
undocumented extra_extensions entry in a per-project
.codebase-memory.json, which I found by reading src/discover/userconfig.c
rather than from any documentation.

2. Even with the files forced in, @page yields nothing. Blazor's route
directive lives in markup above the @code block:

@page "/counter"
@inject NavigationManager Nav

<h1>Counter</h1>

@code {
    private int currentCount = 0;
}

Tree-sitter's C# grammar recovers @code but never parses the directive, so
no Route node is created. On a real Blazor application here — 36 .razor
files with 11 @page directives — get_architecture(routes) returns the
aspect absent, not empty: the entry surface of the whole application is
missing from the graph, while Route is a first-class label with a BM25
boost of +8 and a dedicated routes aspect.

This is not #1041. That issue is ASP.NET Core attribute routing —
[HttpGet]/[Route] on controller methods, parsed C# attributes on a
Method node. Blazor's is a markup directive on a file whose class is
implicit, so there is no Method and no Class to hang a route on, and no
AST node containing the directive at all. Fixing #1041 would not produce a
single Blazor route. They are neighbours, not duplicates. (I checked all 778
issues; #1162 is closed as a duplicate of #1041, and nothing mentions Blazor
or .razor.)

Proposed solution

Three small changes, no new grammar and no new dependency:

  1. src/discover/language.c — map .razor to CBM_LANG_CSHARP. This is
    best-effort by design and worth saying plainly: the C# grammar recovers the
    @code block, while the surrounding markup lands in ERROR regions and the
    file is reported through parse_partial. That is still strictly more than
    the Module-and-imports currently extracted for the other markup-hosted
    languages, and it makes the files reachable without a per-project config
    key.

  2. internal/cbm/extract_defs.c — scan the raw source for @page and put
    the result on the file's module def as route_path, with
    route_method = "GET". The module QN already is the component's identity,
    and insert_def_into_gbuf is label-agnostic, so Route + HANDLES follow
    from the existing machinery. The match is deliberately strict — directive
    first on its line, then whitespace, then a double-quoted path starting
    / — so @pageSize and prose mentions cannot match.

  3. src/pipeline/pass_route_nodes.c — add "Module" to
    ensure_decorator_routes. Extraction alone covers a full index; this
    backstop is what runs on an incremental re-index, so without it a
    component's Route appears on a full index and disappears the next time
    that one file changes.

Test beds. Two public repos, both verified as I write this:

  • dotnet/eShop — 40 .razor files;
    src/WebApp/Components/Pages/Catalog/Catalog.razor opens with
    @page "/". A realistic application rather than a sample.
  • dotnet/blazor-samples — 3,058
    .razor files, the official sample set, good for breadth.
  • MudBlazor/MudBlazor (1,996) and
    radzenhq/radzen-blazor
    (1,476) are component libraries — heavy markup, useful as stress cases for
    the parse-recovery behaviour rather than for routes.

What I would not claim for it. @code methods are not reliably
extracted from .razor: real markup (class=, role= attributes) defeats
recovery and the file comes back parse_partial as a whole. Bare @code
fields are a separate gap. Two further gaps reproduce on the unmodified
binary and are out of scope here: routes report handler:"" although the
HANDLES edge exists, and the language census counts .razor as C# rather
than as its own surface. [Parameter], @inject and component composition
are all still invisible; @inject looks like the best next one, and
composition the worst economics, since most tags point at library components
with no definition in the repo.

Alternatives considered

Vendor a tree-sitter Razor grammar and register CBM_LANG_RAZOR. The
honest option, and it would fix @code recovery too. It is a new vendored
dependency — explicitly on CONTRIBUTING's "requires maintainer approval"
list — for a language whose C# half already extracts well. I did not think it
was mine to propose first.

CBMEmbeddedLangSpec (internal/cbm/lang_specs.c:863), the mechanism
Vue, Svelte, HTML and Astro use: outer grammar, named node slice re-parsed
with an inner grammar. Blazor is the same shape — markup outside, C# in
@code. It needs an outer grammar that can parse Razor markup, so it reduces
to the option above.

Leave it to extra_extensions. The status quo. It gets .razor files
indexed, but it is per-project, undocumented, and does nothing at all for
@page — which is the part that is categorically missing rather than merely
thin.

Do nothing. Defensible on volume: on the application I measured, .razor
is ~12% of files and the C# 88% already indexes fully. I think routes are
worth it anyway because the loss is categorical, not proportional — zero
routes for an entire application, in a tool that treats routes as a
first-class concept.

Confirmations

  • I searched existing issues and this is not a duplicate.

Disclosure, since CONTRIBUTING asks for the issue first: I have already
built and measured this — it is three files plus tests, ~190 added lines, on
a branch, full suite green with lint clean. I am not attaching it as a PR
because CONTRIBUTING says to open an issue and wait for feedback, and because
"new indexing algorithms" is on the approval-required list. Say the word and
I will open it against this issue; say no and that is a fine answer too.

Investigated with Claude Code; the counts and the parse behaviour above are
from real runs on a real application, not generated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    editor/integrationEditor compatibility and CLI integrationparsing/qualityGraph extraction bugs, false positives, missing edgesstability/performanceServer crashes, OOM, hangs, high CPU/memory

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions