Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .claude/skills/agent-eval/corpus.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,10 @@
{ "name": "react-native-segmented-control", "repo": "https://github.com/react-native-segmented-control/segmented-control", "size": "Small", "files": "~25", "question": "How does JSX `<SegmentedControl onChange={cb}/>` reach the native onChange handler on iOS/Android?" },
{ "name": "react-native-screens", "repo": "https://github.com/software-mansion/react-native-screens", "size": "Medium", "files": "~1200", "question": "How does JSX `<ScreenStack>` reach the native RNSScreenStackView component?" },
{ "name": "react-native-skia", "repo": "https://github.com/Shopify/react-native-skia", "size": "Large", "files": "~1000", "question": "How does a `<SkiaPictureView/>` JSX usage reach the iOS / Android native renderer?" }
],
"Salesforce (Apex + LWC + Aura + Visualforce)": [
{ "name": "dreamhouse-lwc", "repo": "https://github.com/trailheadapps/dreamhouse-lwc", "size": "Small", "files": "~176", "question": "How does the propertyTileList component reach the Apex that queries property records?" },
{ "name": "ebikes-lwc", "repo": "https://github.com/trailheadapps/ebikes-lwc", "size": "Small", "files": "~182", "question": "How does the orderBuilder LWC reach the Apex OrderController that updates order items?" },
{ "name": "apex-recipes", "repo": "https://github.com/trailheadapps/apex-recipes", "size": "Medium", "files": "~432", "question": "How does an LWC recipe invoke its Apex controller method, and what does that method call in turn?" }
]
}
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- ASP.NET Razor (`.cshtml`) and Blazor (`.razor`) markup are now parsed for code relationships. A `@model` / `@inherits` / `@inject` directive links the view to the C# view-model, base type, or service it names; a Blazor `<MyComponent/>` tag (plus `@typeof(...)` and generic `TItem="..."` arguments) links to the component class; and the C# inside `@code { }` / `@functions { }` / `@{ }` blocks is analyzed too, so services and types used in component logic are linked. A view-model, component, or service referenced only from markup is no longer reported as having no dependents, and editing it surfaces the views that use it. (ASP.NET, Blazor)
- A Razor/Blazor type reference now resolves through the component's `@using` namespaces — including the folder's cascading `_Imports.razor` — so a simple name that exists in several namespaces lands on the right one. A `@model` / `<MyComponent>` / `@code` reference to `CatalogBrand` resolves to the `@using`'d DTO (`BlazorShared.Models.CatalogBrand`) rather than a same-named domain entity. (ASP.NET, Blazor)
- `codegraph status --json` now also reports the running CLI `version`, the index directory (`indexPath`), and a `lastIndexed` timestamp (ISO-8601, or null when nothing's indexed yet), so CI and scripts can pin the CLI version and check index freshness from a single command. A matching `CodeGraph.getLastIndexedAt()` library method exposes the same freshness check without shelling out. Thanks @12122J and @eddieran. (#329)
- CodeGraph now indexes **Salesforce Apex** (`.cls`, `.trigger`) — classes, interfaces, enums, methods, constructors, properties, triggers, and inner classes, with call edges, `extends`/`implements`, and `@AuraEnabled`/`@RemoteAction` annotations. A class that another class or a trigger calls now shows its callers, and impact/blast-radius work across Apex.
- CodeGraph now connects the **Salesforce front end to its Apex back end** as one graph. A Lightning Web Component that imports an Apex method (`import getX from '@salesforce/apex/MyController.getX'`) links to that method; a Visualforce page links to its `controller=` and `extensions=` Apex classes; and an Aura component's controller/helper links to the Apex it calls via `cmp.get("c.method")`. So editing an Apex method now surfaces every LWC, Aura, and Visualforce file that depends on it. (Apex, LWC, Aura, Visualforce)
- CodeGraph now indexes **Salesforce UI markup** — Visualforce pages and components (`.page`, `.component`), Aura components (`.cmp`, `.app`, `.evt`, `.intf`), and Lightning Web Component templates (`.html` inside an `lwc` bundle). Custom component usage (`<c:child>` in Visualforce/Aura, `<c-child>` in LWC) links each parent to the child component it renders, so the component tree is navigable. A Visualforce `standardController` is left unlinked because it names a Salesforce object, not an Apex class. (LWC, Aura, Visualforce)

### Fixes

Expand All @@ -27,6 +30,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- React Native native→JS events now connect through the common `sendEvent(context, "X", body)` wrapper. Many libraries (react-native-device-info and others) wrap the event emitter behind a helper whose `.emit(eventName, …)` takes a *variable*, so the matcher — which looked for `.emit("literal", …)` — missed it; the literal event name actually lives in the wrapper call. Now a native method that fires `sendEvent(…, "batteryLevelChanged", …)` links to the JS `addListener('batteryLevelChanged', …)` handler, so editing the native emitter surfaces the JS subscriber. (React Native)
- React Native / Expo cross-language bridges are more complete and more precise. An Expo Module method declared with a generic type — Android's `AsyncFunction<Float>("getBatteryLevelAsync")` — is now indexed (the `<Float>` used to defeat the matcher, so every Android Expo method was dropped and a JS call resolved only to the iOS Swift impl). The iOS and Android implementations of the same JS-visible method — both Expo Modules and classic NativeModules (`@ReactMethod` on Android, the matching method on iOS) — are now linked to each other, so a JS call that resolves to one platform still reaches the other and editing either platform's native code surfaces the JS caller. And a `Type.member` static read in native code (e.g. Android's `BatteryManager.EXTRA_LEVEL`) no longer falsely links to a coincidentally same-named class in another language (a web `BatteryManager`) — type references stay within a language family, while genuine cross-language bridges (config→code, JS↔native calls) are unaffected. (React Native, Expo)
- A TypeScript/JavaScript reference or import no longer gets mis-linked to a same-named class in a native language. In a React Native / Expo repo that has both a TypeScript `TestRunner` type and a Kotlin `TestRunner` class, a TS reference to `TestRunner` — or an `import React` sitting next to a Swift `React` — used to resolve onto the native symbol (the component resolver matched any same-named class regardless of language, and import statements weren't language-checked at all). References and imports now stay within their language family, so they land on the right symbol while genuine cross-language bridges (JS↔native calls, config→code) are untouched. A C/C++ `#include "Foo.h"` likewise no longer resolves to a same-named header from another platform (an iOS Objective-C `Foo.h`). (React Native, Expo, TypeScript, C/C++)
- A method call in one programming language no longer gets mis-linked to an unrelated same-named method in another. A built-in or local call like `.replace(...)` or `.resolve(...)` — in JavaScript, or a Python `str.replace(...)` — used to bind to an Apex `CurrencyTokenReplacer::replace` or `…::resolve` just because the names matched, so a React or script file could look like it depended on Apex it never calls and an Apex method's dependents were inflated with false callers. Calls from a self-contained programming language (JS/TS, Java/Kotlin, Swift/Objective-C, C/C++, and Python, Go, Rust, PHP, Ruby, Dart, Lua) now stay within their language family, while genuine cross-layer calls still connect — a Salesforce Aura controller's `cmp.get("c.method")` reaches its Apex method, and JS↔native bridges are unaffected. (Apex, LWC, Aura, Visualforce, TypeScript/JavaScript, Python)
- Native includes and Kotlin Multiplatform imports now resolve to the correct file in multi-platform projects. A C/C++ `#include "Foo.h"` now resolves to the header in the including file's own directory first (the C quoted-include rule), so when a module ships a same-named header per platform (a Windows, an Apple, and an Android `Foo.h` side by side) the local one correctly shows its dependents instead of an arbitrary other-platform header looking like the dependency. And a Kotlin Multiplatform `expect` declaration is no longer reported as having no dependents: a `commonMain` import now resolves to the `commonMain` `expect` (matched within the importing source set) rather than being absorbed by one platform's `actual`. (C/C++, Kotlin)
- `codegraph affected` now reports the tests and files that actually depend on your changes. It used to follow only `import` statements — but those never cross file boundaries in CodeGraph's graph — so it returned **no affected tests for any change, in every language**. It now traces the real cross-file usage graph (calls, references, instantiations, and class `extends` / `implements`), so `git diff --name-only | codegraph affected` surfaces the test files that exercise the changed code. Circular-dependency detection, which had the same blind spot, now works too.
- Blast radius, callers, and `codegraph affected` now recognize far more of the dependencies that were already in your code. A symbol now counts as a dependency whether it's called, used only in a type annotation inside a function body (`const items: Foo[] = []`), imported and placed in a registry array or passed as an argument, used as a JSX component, simply re-exported from a barrel (`export { X } from './x'`), or pulled in as a namespace (`import * as ns from '@/x'`) — including through tsconfig path aliases like `@/`. Previously only called, instantiated, or signature-typed symbols created a cross-file link, so a file that used a dependency in any other way could look like it depended on nothing — and the file that defined a widely-used symbol could look like nothing depended on it. The graph still indexes exactly the same symbols; it just connects the ones that were already there. (TypeScript/JavaScript)
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ CodeGraph cuts **tokens, tool calls, and wall-clock time on every repo** — acr
| **Full-Text Search** | Find code by name instantly across your entire codebase, powered by FTS5 |
| **Impact Analysis** | Trace callers, callees, and the full impact radius of any symbol before making changes |
| **Always Fresh** | File watcher uses native OS events (FSEvents/inotify/ReadDirectoryChangesW) with debounced auto-sync — the graph stays current as you code, zero config |
| **20+ Languages** | TypeScript, JavaScript, Python, Go, Rust, Java, C#, PHP, Ruby, C, C++, Objective-C, Swift, Kotlin, Dart, Lua, Luau, Svelte, Liquid, Pascal/Delphi |
| **20+ Languages** | TypeScript, JavaScript, Python, Go, Rust, Java, C#, PHP, Ruby, C, C++, Objective-C, Swift, Kotlin, Dart, Lua, Luau, Svelte, Liquid, Pascal/Delphi, Apex, Visualforce, Aura, LWC |
| **Framework-aware Routes** | Recognizes web-framework routing files and links URL patterns to their handlers across 14 frameworks |
| **Mixed iOS / React Native / Expo** | Closes cross-language flows that static parsing misses: Swift ↔ ObjC bridging, React Native legacy bridge + TurboModules + Fabric view components, native → JS event emitters, Expo Modules |
| **100% Local** | No data leaves your machine. No API keys. No external services. SQLite database only |
Expand Down Expand Up @@ -638,6 +638,10 @@ is written):
| Pascal / Delphi | `.pas`, `.dpr`, `.dpk`, `.lpr` | Full support (classes, records, interfaces, enums, DFM/FMX form files) |
| Lua | `.lua` | Full support (functions, methods with receivers, local variables, `require` imports, call edges) |
| Luau | `.luau` | Full support (everything in Lua, plus `type`/`export type` aliases, typed signatures, and Roblox instance-path `require`) |
| Apex | `.cls`, `.trigger` | Full support (classes, interfaces, enums, methods, constructors, properties, triggers, inner classes; call/extends/implements edges; `@AuraEnabled`/`@RemoteAction` annotations) |
| Visualforce | `.page`, `.component` | Markup support (page/component → `controller=`/`extensions=` Apex classes and `<c:child>` components; `standardController` left unlinked) |
| Aura | `.cmp`, `.app`, `.evt`, `.intf` | Markup support (component → `<c:child>` components and `{!c.handler}` controller actions; controller/helper JS handlers → Apex via `cmp.get("c.x")`) |
| Lightning Web Components | `.js` + `.html` | Full support (the `.js` via JavaScript; the template links `<c-child>` components and the `@salesforce/apex/...` import links to the Apex method) |

## Measured cross-file coverage

Expand Down Expand Up @@ -666,9 +670,12 @@ Impact and blast-radius queries are only as good as the dependency graph behind
| Luau | dphfox/Fusion | 92.2% |
| Liquid | Shopify/dawn | 73.8% |
| Pascal / Delphi | PascalCoin | 75.7% |
| Apex (+ LWC / Aura / Visualforce → Apex) | trailheadapps/ebikes-lwc | 55.6% |

Framework routing is validated the same way, on a canonical app per framework: Express 100%, FastAPI 98%, Flask 100%, NestJS 96.8%, Gin 96.5%, Axum 100%, Rocket 93.8%, Vapor 100%, Laravel 92%, Rails 89.6%, React Router 100% — and the convention/reflection-heavy ones at their honest static-analysis ceiling: ASP.NET 83.9%, Spring 83.3%, Drupal 78.9%, Django 74.1%.

The **Salesforce** number reflects a quirk of the platform, not the extractor: every non-test Apex class in ebikes-lwc that something depends on is covered — the residual is the mandatory `*Test` classes (the platform requires a test class per class, and nothing in code depends on a test) plus one class referenced only from metadata XML. The novel coverage here is *cross-layer*: a Lightning Web Component's `@salesforce/apex/...` import resolves to the Apex method, a Visualforce page's `controller=`/`extensions=` resolve to their Apex classes, and an Aura controller's `cmp.get("c.x")` resolves to the Apex method — so editing Apex surfaces the LWC/Aura/Visualforce that depend on it.

## Troubleshooting

**"CodeGraph not initialized"** — Run `codegraph init` in your project directory first.
Expand Down
Loading