Codemap statically analyzes C# solutions and JavaScript/TypeScript projects and renders an interactive dependency graph: classes, interfaces, enums, structs, functions and modules as nodes; inheritance, implementation, calls, references and cross-language HTTP invocations as typed edges. Graph snapshots persist to SQL Server so topologies can be diffed over time, git-style.
Point Codemap at a repository. It reads the code (it never runs it), builds a typed graph of
everything it finds, and shows it on a pannable/zoomable canvas β including the edges that
normally stay invisible: the fetch('/api/orders') in your frontend that lands on
OrdersController in your backend.
flowchart LR
repo["π Your repository<br/>C# + JS/TS"] --> scan["π Static analysis<br/>Roslyn + TypeScript API"]
scan --> model["πΈοΈ Typed graph<br/>nodes + edges"]
model --> canvas["π₯οΈ Interactive canvas<br/>pan Β· zoom Β· filter Β· inspect"]
model --> db[("πΎ SQL Server<br/>snapshots")]
db --> diff["π Diff two snapshots<br/>added / removed / changed"]
diff --> canvas
What the graph contains:
| Element | Meaning | Example |
|---|---|---|
| Node | Class, interface, enum, struct, JS/TS module or function | OrderService, api/client.ts |
Inherits edge |
Class inheritance | AdminUser β User |
Implements edge |
Interface implementation | EfGraphRepository β IGraphRepository |
Calls edge |
Method/function invocation | OrderService β EmailSender |
References edge |
Member types, imports, cross-module use | import { api } from './client' |
Invokes edge |
Cross-language HTTP call | JS fetch('/api/scan') β C# [HttpPost("/api/scan")] |
- Paste a path to a repo /
.sln/ project folder and hit Analyze (orCtrl+Enter). - The C# engine (Roslyn) and the JS/TS engine (a bundled Node script) walk the code and emit nodes and edges; progress streams live to the browser over SignalR.
- The cross-language resolver matches JS HTTP call sites against ASP.NET route attributes and
adds
Invokesedges. - The finished graph appears on the canvas. Publish snapshot persists it; History lets you diff any two snapshots, with additions/removals/changes highlighted on the canvas.
sequenceDiagram
actor U as User
participant W as Blazor UI (Home.razor)
participant D as IDispatcher
participant H as ScanRepositoryCommandHandler
participant CS as Roslyn analyzer (C#)
participant JS as Node analyzer (JS/TS)
participant X as Cross-language resolver
participant S as SignalR hub
U->>W: paste path, click Analyze
W->>D: Send(ScanRepositoryCommand)
D->>H: dispatch through pipeline behaviors
H->>CS: analyze .sln / .csproj
CS-->>S: progress % + current file
S-->>W: live progress bar
H->>JS: analyze .ts/.tsx/.jsx/.js
JS-->>S: progress % + current file
H->>X: match fetch/axios URLs β [Route]/[Http*] attributes
X-->>H: Invokes edges + warnings
H-->>W: TopologyGraph (nodes + edges)
W-->>U: interactive graph on the canvas
flowchart TB
subgraph Presentation
Web["<b>Codemap.Web</b><br/>Blazor Server UI Β· SignalR hub<br/>hand-rolled SVG canvas (no JS libs)"]
end
subgraph "Use cases"
App["<b>Codemap.Application</b><br/>commands / queries / handlers<br/>custom CQRS dispatcher (no MediatR)<br/>interfaces in Abstractions/"]
end
subgraph "Technical detail"
Infra["<b>Codemap.Infrastructure</b><br/>Roslyn analysis Β· Node bridge (TS API + Acorn)<br/>EF Core persistence Β· cross-language resolver"]
end
subgraph Core
Domain["<b>Codemap.Domain</b><br/>CodeNode Β· CodeEdge Β· TopologyGraph<br/>TopologyDiffer Β· CycleDetector<br/>zero dependencies"]
end
Web --> App --> Domain
Infra --> App
Infra --> Domain
Dependency direction: Web β Application β Domain; Infrastructure β Application β Domain.
Every use case is dispatched through the custom IDispatcher (no MediatR) β Codemap.Web never
calls a service directly. Application owns the interfaces; Infrastructure implements them.
src/Codemap.Domain entities, value objects, enums, pure graph algorithms β no dependencies
src/Codemap.Application commands/queries + handlers, custom CQRS dispatcher, infrastructure interfaces
src/Codemap.Infrastructure Roslyn analysis, JS/TS analysis (Node bridge), EF Core persistence,
cross-language edge resolver
src/Codemap.Web Blazor Server UI, SignalR scan-progress hub
tests/Codemap.Tests xUnit tests for all layers
Codemap connects a frontend to its backend by matching HTTP call sites to route attributes:
flowchart LR
subgraph "JavaScript / TypeScript"
js["orders.ts<br/><code>fetch('/api/orders/' + id)</code>"]
end
subgraph Matching
norm["normalize both sides<br/>case-insensitive Β·<br/>parameter names ignored<br/><code>/api/orders/{*}</code>"]
end
subgraph "C# (ASP.NET)"
cs["OrdersController.cs<br/><code>[HttpGet("/api/orders/{id}")]</code>"]
end
js --> norm --> cs
js -.->|"<b>Invokes</b> edge<br/>GET /api/orders/{id}"| cs
fetch/axios/$http call sites with literal (or simple template) URLs are matched against
[Route]/[Http*] attributes by normalized route pattern; unmatched calls surface as scan
warnings instead of silently disappearing.
- .NET 10 SDK (MSBuildLocator loads MSBuild from the installed SDK to open target solutions)
- SQL Server β defaults to
(localdb)\MSSQLLocalDB(seeConnectionStrings:Codemapinsrc/Codemap.Web/appsettings.json). If the database is unreachable the app still runs; scans stay in memory and only publish/history features are disabled. - Node.js β only needed for JS/TS analysis. On first JS scan, Codemap runs
npm installinside the bundled analyzer script folder (typescript,acorn); without Node the C# side still works and the scan reports a warning.
dotnet run --project src/Codemap.WebOpen the app, paste a path to a repository / .sln / project folder, hit Analyze
(or Ctrl+Enter). Progress streams live over SignalR. Publish snapshot persists the scan;
History lists snapshots, recent scans, and diffs two snapshots (added/removed/changed nodes
and edges, highlighted on the canvas).
Database schema is created automatically at startup via EF Core migrations. To add migrations:
dotnet dotnet-ef migrations add <Name> --project src/Codemap.Infrastructure --startup-project src/Codemap.Web --output-dir Persistence/Migrations? shows the in-app cheat-sheet. Highlights:
| Shortcut | Action |
|---|---|
Ctrl/β + K |
Quick-jump to any node |
Ctrl/β + Enter |
Analyze |
1 / 2 / 3 |
Language filter (All / C# / JS) |
F |
Zoom to fit |
+ / - |
Zoom in / out |
| Arrow keys | Nudge selected node (Shift = 10 px) |
Ctrl/β + F |
Focus the namespace filter |
Esc |
Clear selection / close panels |
- C# β
MSBuildWorkspaceloads solutions/projects;SymbolWalker(SyntaxWalker + SemanticModel) emits nodes, member signatures, and ASP.NET route attributes;CallGraphBuilderresolves invocations/base lists/member types intoCalls/Inherits/Implements/Referencesedges. Edges point at open generic definitions; partial classes collapse into one node; cross-project references resolve within a solution. - JS/TS β a bundled Node script (invoked via
Jering.Javascript.NodeJS) uses the TypeScript Compiler API for.ts/.tsx/.jsxand Acorn for plain.js. Modules, classes and top-level functions become nodes; imports becomeReferences; same-module calls becomeCalls, cross-module calls degrade to lower-confidenceReferences. - Cross-language β
fetch/axios/$httpcall sites with literal (or simple template) URLs are matched against[Route]/[Http*]attributes by normalized route pattern (case-insensitive, parameter names ignored) and emitInvokesedges; unmatched calls surface as scan warnings.
dotnet testCovers SymbolWalker node/endpoint extraction, CallGraphBuilder edge extraction (inheritance,
implements, calls, references, open generics), cross-language route matching, the dispatcher
(handler resolution, behavior ordering, notification fan-out), topology diffing, cycle detection,
and the keyboard shortcut mapping.
Codemap is released under the MIT License.