# Language support

28 languages parsed to a full AST and 42 on the five-rung ladder, with framework awareness for Django, Spring Boot, Rails, Salesforce and more.

Repowise ships language-aware resolvers under
`packages/core/src/repowise/core/ingestion/resolvers/`. Every language
lands on one rung of a five-rung ladder, and the rung decides which
pipeline stages produce meaningful output. Languages below the parsing
rungs still appear in the documentation and still carry
[git history](https://docs.repowise.dev/intelligence/git-history).

## Coverage matrix

| Tier | Languages | What you get |
|---|---|---|
| **Full** (15) | Python · TypeScript · JavaScript · Svelte · Vue · Astro · Java · Kotlin · Go · Rust · C++ · C# · Scala · Ruby · Apex (Salesforce) | The whole pipeline: AST symbols, import resolution, a resolved [call graph](https://docs.repowise.dev/intelligence/dependency-graph), heritage, docstrings, framework edges, and [code-health](https://docs.repowise.dev/intelligence/code-health) markers |
| **Good** (11) | C · Swift · PHP · Dart · Object Pascal · COBOL · GDScript · VB.NET · Elixir · F# · Objective-C | Everything above except the full health suite. Dart, Object Pascal and PHP get health markers; C, Swift, COBOL, F# and Objective-C get the complexity-derived ones; GDScript, VB.NET and Elixir get none yet |
| **Partial** (2) | Luau / Roblox · Razor / Blazor | Luau: AST symbols and `require()` resolution, Rojo and `.luaurc` aware, no health markers yet. Razor: a component symbol per file, call edges from `@code` blocks and component tags, C# health markers, no import resolution yet |
| **Lightweight** (6) | Clojure · Haskell · Lean 4 · Erlang · HTML · QML | A real file-to-file import graph, no symbol-level claims |
| **Structural** (8) | R · Zig · Julia · Elm · OCaml · Crystal · Nim · D | Git history only: blame, hotspots, co-change. No AST parsing |

Tree-sitter parsing stops after the Partial rung. The first three rungs
are the 28 languages parsed to a full AST; all five rungs are the 42 on
the ladder. Both numbers are worth stating and neither is worth stating
alone, so the useful thing to take from this page is the rung your
language sits on rather than either count.

SQL and shell sit outside the ladder on purpose, because each has a
coverage shape the rungs cannot describe. SQL is parsed by sqlglot
rather than tree-sitter and gets symbols, wiki pages and health markers
but no call graph, with import edges only inside a dbt project. Shell
gets symbols, `source` edges and function-level complexity, but
reachability means little for a script invoked by name.

## What each tier gets from the pipeline

| Stage | Full | Good | Partial | Lightweight | Structural |
|---|:---:|:---:|:---:|:---:|:---:|
| File discovery and git history | yes | yes | yes | yes | yes |
| AST symbol extraction | yes | yes | yes | no | no |
| Import resolution | yes | yes | Luau only | file-level | no |
| Call graph edges | yes | yes | yes | no | no |
| Heritage (extends / implements) | yes | yes | no | no | no |
| Named bindings | yes | yes | no | no | no |
| Code-health markers | yes | Dart, Object Pascal, PHP; complexity only for C, Swift, COBOL, F#, Objective-C | Razor | no | no |
| Dead code detection | yes | yes, with COBOL limited to paragraphs | yes | yes | yes |
| Semantic search and wiki pages | yes | yes | yes | yes | yes |

Scala's import resolution is partial: it shares the JVM index with Java
and Kotlin and falls back to parsing SBT or Mill build files. Apex has no
imports: names resolve case-insensitively within each sfdx project, a
declared package dependency or a namespace prefix. Every other Full and
Good language resolves imports outright.

COBOL files and programs get no dead-code claims. JCL, schedulers and
dynamic program calls are external entry paths the repository graph cannot
observe. The one claim is a paragraph that nothing names and nothing falls
through into, at low confidence and never deletion-ready.

GDScript has a dedicated import resolver and Godot-specific framework
edges, but no named bindings. Scenes, autoloads and `class_name`
declarations connect scripts that no import statement ever linked.

Svelte, Vue and Astro share the TypeScript resolver: a single-file
component's `<script>` blocks (and an Astro file's frontmatter) and markup
expressions are projected to a TypeScript buffer at byte-identical offsets,
so the same queries, config and code-health dialects apply. Astro
frontmatter imports and exports become symbols and edges, PascalCase markup
tags become component calls, and pages and endpoints under `src/pages` are
entry points.

## COBOL

COBOL is read in fixed reference format, with `EXEC SQL`, `EXEC CICS` and
`EXEC DLI` blocks and `COPY` statements masked before parsing.

| Source | What the graph gets |
|---|---|
| Copybooks (`COPY`, `EXEC SQL INCLUDE`) | Resolve to the nearest copybook with that name; system copybooks such as `DFHAID` stay external. A copybook parses as a data or procedure fragment |
| DB2 DCLGEN (`.dcl`) | A copybook when it holds an `EXEC SQL DECLARE ... TABLE` and level-numbered data entries |
| JCL (`.jcl`, `.prc`, `.proc`) | Each job step links to the program or PROC it runs, which makes that program an entry point |
| BMS mapsets (`.bms`) | Linked from the programs that send or receive their maps |
| CICS | `LINK` and `XCTL` link to a literal or constant program name; any other target shows as a `cics-dynamic:` reference. CSD definitions and `TRANSID` links make programs entry points |
| Calls | `PERFORM` binds within its program and the copybooks it copies; `CALL`, `LINK` and `XCTL` bind to a unique PROGRAM-ID |

Code health measures each paragraph and section as a function: `IF`,
`WHEN` arms, `PERFORM UNTIL/VARYING` loops and `AND`/`OR` add to its
complexity. The advisory `cobol_unstructured_flow` marker lists paragraphs
with `ALTER`, `GO TO ... DEPENDING ON` or a `GO TO` that does not jump
forward to its own `...EXIT`, and never deducts from a score. Dynamic
`CALL` targets and free-format source produce no edges.

## Salesforce

Apex (`.cls`, `.trigger`, `.apex`) is a Full language. Lightning Web
Components, Aura, Visualforce, flows, Lightning pages and quick actions are
entry points, and object and field metadata link to the Apex that names
them. Flows and SObject metadata become symbols; Aura and Visualforce
markup carries none. References are followed when the name is literal:
runtime-built names, and objects with no metadata file in the repository,
get no edge. Apex dead-code findings are review-only, since the platform
also reaches classes by name, and a third-party library in a static
resource counts as vendored. Apex code health carries advisory markers for SOQL injection and related
shapes (see [code health](https://docs.repowise.dev/intelligence/code-health#language-specific-advisory-markers)).

## Grammars on offline machines

The COBOL and Apex grammars download on first use, and only when the repo
holds that language. To index offline, stage them on a machine with
network access, copy the directory over and point `REPOWISE_GRAMMAR_DIR` at it.

```bash
repowise grammars fetch ./grammars
export REPOWISE_GRAMMAR_DIR=./grammars
```

`grammars fetch DIRECTORY` downloads every runtime grammar into
`DIRECTORY` and exits non-zero if any fails. `repowise doctor` reports
whether each grammar is cached, and `repowise init` warns once, with a file
count, when COBOL indexes without symbols because its grammar did not load.

## Config and build files

| Source | What the graph gets |
|---|---|
| Ansible YAML | Edges for `import_playbook`, `import_tasks`, `include_tasks`, `import_role`, `include_role`, `roles:` and role `meta` dependencies. Plain YAML config is untouched |
| `tsconfig.json` references | Each file resolves against the project that owns it |
| Gradle build scripts | Declared dependencies feed the package inventory |

## Framework awareness

Repowise recognises common framework conventions and emits edges for
them, even when the underlying call goes through a decorator or a
runtime registry.

| Language | Frameworks |
|---|---|
| **Python** | Django (URL patterns, models, signals), FastAPI (route handlers, dependency injection), Flask (route decorators) |
| **C#** | ASP.NET (controllers, attributes, minimal APIs), EF Core, gRPC-dotnet, dependency injection containers, MassTransit message contracts in C# and VB.NET (publishers and consumers linked across workspace repos); MassTransit, NServiceBus, Azure Functions and WCF handlers are dead-code roots |
| **Java / Kotlin** | Spring Boot (a `main` is a declared entry point, ranked above filename guesses; `@RestController`, `@Component`, `@Autowired`, and every `@GetMapping`-style route linked to its handler method), Spring Data and MyBatis calls flagged as database I/O, Jakarta / JPA, Quarkus, Micronaut |
| **JavaScript / TypeScript** | Next.js App Router, Express (route mounts keyed on the mount path), NestJS (`@Controller`, `@Module`, `@Injectable`), Hono, Fastify, Remix, SvelteKit, Astro, tRPC, Angular |
| **Go** | Gin, Echo, Chi router patterns |
| **Rust** | Axum, Actix routes |
| **Ruby** | Rails (Zeitwerk autoloading, route DSL); a gem's `lib` entry file and what it requires count as public API |
| **PHP** | Laravel routes, TYPO3 conventions |
| **Dart** | Flutter route tables, `runApp()` edges, and widget-tree edges from `build()` bodies; calls on a dotted receiver and prefixed named constructors bind, and a closure parameter takes its type from the `Iterable` call it is passed to |
| **Apex** | Lightning Web Components, Aura, Visualforce, flows, Lightning pages and quick actions as entry points, object and field metadata, Apex REST resources |
| **GDScript** | Godot scenes, autoloads and `class_name` registration |

## Dead-code awareness

The dead-code detector knows about dynamic dispatch patterns that look
unused to a static analyzer but aren't:

- Naming patterns: `*Plugin`, `*Handler`, `*Adapter`, `*Middleware`
- Dynamic imports: `importlib.import_module()`, `__import__()`, dynamic
  `require()`
- Framework convention files (Flask blueprints, FastAPI routers, Django
  apps, Rails controllers, Laravel providers, TYPO3 extensions)
- Deprecation annotations: `@Deprecated`, `[Obsolete]`, `@deprecated`
  and their siblings feed a finding's confidence
- Symbols a docs build or an API dump names, and symbols reached by a
  framework, a container, or a registration decorator

These are excluded from `safe_to_delete` findings to keep the
false-positive rate near zero.

## Code-health marker rollout

Health markers run off a per-language walker map that is independent of
`.scm` parsing, so a language can parse perfectly for the graph and
still need this map before markers fire. This table is most of the
reason a language is Full rather than Good.

| Language | Complexity / nesting | Class metrics | Assertion smells | Extract Method | Performance risk |
|---|:---:|:---:|:---:|:---:|:---:|
| Python | yes | yes | yes | yes | yes |
| TypeScript / JavaScript | yes | yes | yes | yes | yes |
| Svelte · Vue · Astro | yes | yes | yes | yes | yes |
| Java | yes | yes | yes | yes | yes |
| Go | yes | n/a | yes | yes | yes |
| Rust · C++ · C# | yes | yes | yes | yes | yes |
| Scala | yes | yes | yes | yes | yes |
| Ruby | yes | yes | yes | later | yes |
| Apex | yes | yes | yes | yes | yes |
| COBOL | yes | no | no | no | no |
| Kotlin | yes | yes | yes | blocked | yes |
| Dart | yes | yes | yes | later | yes |
| Object Pascal | yes | n/a | yes | later | yes |
| Razor | yes | n/a | n/a | later | yes |
| PHP | yes | yes | yes | yes | later |
| C | yes | later | later | yes | later |
| Swift | yes | yes | later | later | later |
| F# · Objective-C | yes | yes | later | later | later |
| Shell | yes | n/a | n/a | n/a | n/a |

COBOL measures paragraphs and sections as functions. Apex also gets four advisory markers: SOQL injection, a class with queries and no sharing keyword, hardcoded record Ids and `SeeAllData=true` tests. Scala has a dataflow def/use dialect, which is what Extract Method needs.

C, F# and Objective-C reach the complexity walker and carry real
cyclomatic complexity and maintainability numbers, without the rest of
the marker suite. Swift has a complexity map: function rows (CCN, nesting,
cognitive complexity, parameters) and class rows (methods, fields, LCOM4,
TCC).

Code Health serves findings on every language. Where a layer measured below
the precision bar on a language, its findings carry a **Preview** label with
the latest hand-judged measurement, in the web UI, REST, MCP and the CLI.

Every cell is a deliberate call rather than an oversight: a language
reaches a dialect or it stays silent, and an `n/a` records a metric the
language cannot carry rather than one nobody got to. Kotlin's Extract
Method is blocked on the grammar, not unscheduled.

## Adding a language

Adding new language support is five required steps: a `LanguageSpec`
module, a manual `LanguageTag` registration, a tree-sitter `.scm` query
file, a `LanguageConfig` entry, and the grammar dependency. Two of those
are one-line registrations, but the `LanguageTag` edit can't be derived
and has to be made by hand. No changes to `parser.py`, `graph.py`, or
any other analysis core file. See
[architecture/language-support.md](https://github.com/repowise-dev/repowise/blob/main/docs/architecture/language-support.md#adding-a-new-language)
in the OSS repo for the full recipe, including the optional steps that
buy heritage extraction, named bindings, and call resolution.

> **Note**
>
> Working in a language we don't list? Repowise still indexes the file
> tree, ownership, and churn; you just won't get import edges or
> framework-aware analysis. Open a Discussion and we'll prioritise.
