feat(framework): add Angular support — modules, selectors, templates, routes, control-flow blocks#526
Open
rj-majian3 wants to merge 1 commit into
Open
Conversation
… routes, control-flow blocks Closes the static-extraction holes for Angular projects so trace/explore/impact work without falling back to Read. All synthesized edges carry provenance:'heuristic' + metadata.synthesizedBy:'angular-*' + registeredAt. Phase 1 — NgModule + standalone @component metadata (synthesizer): - @NgModule({ declarations, imports, providers, exports, bootstrap }) - standalone @component({ imports }) - emits class→class edges from the @NgModule / @component class to each identifier in the metadata array Phase 2 — selector → component/directive (synthesizer): - <app-foo> tag match against the per-project selector index built from @component / @directive - [appHighlight] bracketed attribute selector - bare attribute form (<p appHighlight>) — Angular treats both as the same selector; the index lookup gates false positives Phase 3 — template binding → owner-class member (synthesizer): - (event)="handler($event)" → method - [prop]="expr" → property/field - [(banana)]="x" two-way binding - *ngFor / *ngIf / *ngSwitch structural directives - {{ interpolation }} - resolution: only members inside the owner class's line range in the same file, gated by NG_MEMBER_KINDS Phase 4 — router configs (per-file extract): - const routes: Routes = [...] / Route[] - RouterModule.forRoot([...]) / forChild([...]) - provideRouter([...]) standalone-bootstrap form - nested children: [...] with parent-path joining - loadChildren / loadComponent dynamic import forms — .then(m => m.X), destructured ({ X }) => X, async (await import()).X - route → component reference Phase 5 — Angular 17+ control-flow blocks (synthesizer): - @if (expr) / @else if (expr) - @for (item of expr; track …) - @switch (expr) / @case (expr) - @defer (expr) - @let name = expr; (v18+ template variables) - balanced-paren reader so nested fn calls inside the block expression aren't truncated Phase 6 — route guards / resolvers (per-file extract): - canActivate / canActivateChild / canDeactivate / canMatch / canLoad arrays - resolve: { key: Resolver } object — values extracted as references Cross-synthesizer dedup: - Merge in synthesizeCallbackEdges now keys on (source, target, synthesizedBy) instead of just (source, target). Same pair through different mechanisms (e.g. NgModule imports + template selector) now produces both edges with their distinct metadata channel. Within each synthesizer the existing per-channel dedup keeps duplicates out. Tests: 19 helper / detect unit tests + 11 end-to-end indexAll integration tests in __tests__/frameworks{,-integration}.test.ts. Verified against full suite: 882 passed / 0 failures. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds full Angular static extraction so
trace/explore/impact/callers/calleeswork on Angular projects without falling back to Read.
What's included
Six phases, all regex-over-source (same approach as other framework resolvers):
declarations,imports,providers,exports,bootstrap→ class→class edges<app-foo>tags,[appHighlight]attributes, bare attributes,and compound selectors (
a[href],i.anticon) matched against a per-project selector index(event),[prop],[(banana)],*ngFor/*ngIf,{{ interpolation }}.component.htmltemplates)— produced 22,672 Angular synthesized edges (12.4% of total graph):
Files changed
src/resolution/frameworks/angular.ts— new (1,744 lines)src/resolution/frameworks/index.ts— register angular resolversrc/resolution/callback-synthesizer.ts— integrate 3 angular synthesizers + dedup key fix__tests__/frameworks.test.ts— 124 unit tests__tests__/frameworks-integration.test.ts— 15 e2e testsCHANGELOG.md— [Unreleased] entry