Commit Graph

3 Commits

Author SHA1 Message Date
Colby Mchenry c3efbbf4e7 feat(angular): a template's event bindings call the component's methods (#2114)
An Angular handler's only caller is its template: `(click)="toggleFavorite()"`,
`(ngSubmit)="submitForm()"`. With no edge, the method showed no callers,
impact stopped at it, and the Steps picture of a screen reached its
handlers only through class containment, with nothing to say what fires
them.

The template pass now reads each `(event)="…"` binding — not
`[(ngModel)]`'s two-way half — and links the component to every method of
its own the statement calls (`save()`, `this.toggle(x)`; not
`closed.emit()` or `form.reset()`), as a `calls` edge
(`synthesizedBy: 'angular-event'`) carrying the binding as
`metadata.trigger`: `{ kind: 'prop', name: '(click)', of: 'button' }`.
The binding is in the template, so it cannot be read back from the source
at the edge's line; Steps now takes a trigger an edge carries before
reading one at the site.

angular-realworld: 21 binding sites → 17 edges (3 repeats of a method and
event, 1 `delete.emit(true)`); Steps for /article/:slug draws
deleteArticle on (click) · <button> → DELETE and back to /, addComment on
(ngSubmit) · <form>, the favorite button's toggle → POST/DELETE or
/register. Ghostfolio 225 edges of 302 sites, ngx-admin 75 of 100 — the
rest are `$event.stopPropagation()`, output emits, assignments and
repeats. Controls (koel, proshop) byte-identical.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 02:51:30 +00:00
Colby Mchenry 761611c18e feat(angular): routes, navigation and the component tree for Angular apps (#2113)
An Angular app had no route nodes, no navigates edges and no link from a
template to what it renders — the viewer's Steps tab said "No screens or
endpoints" and there was no Screens tab.

Routes (frameworks/angular-router.ts): every `Routes` array — typed
`Routes` / `Route[]`, `RouterModule.forRoot/forChild`, `provideRouter`, a
routes file's `export default [...]` / `as|satisfies Routes` — walked as
objects. `component` binds by `references` (route-roots takes a class as
a named handler); a lazy `loadComponent` binds to the file's `@Component`
class. `children` join their parent's path; a lazily loaded file's routes
are put under the path that loads them in postExtract (following an
NgModule `loadChildren` to the routing module it imports), idempotently
from the in-file path kept in the qualified name. A route with children
is a layout: a screen only where no child claims its address, and linked
from each screen inside it (`references`, `layout: true`). Paths written
as route constants (`internalRoutes.account.path`, through destructured
locals and `const x + '/:id'`) and `$localize` strings are read from the
constant object. `redirectTo` aliases an address to the screen it sends
the user to; a root `**` redirect answers `/` only. A `matcher` route and
a `**` catch-all are not screens.

Navigation: `router.navigate([...])`, `navigateByUrl`, a guard's
`createUrlTree` / `parseUrl` — a command array (non-literal elements are
holes), a static string, a route constant, a component property holding
one, a function constant called with arguments, `.concat(id)`. A
`relativeTo` navigation, a query-only `navigate([])` and a destination
made only of holes (it would match any route of its length) are left
unresolved.

Templates (angular-template-synthesizer.ts): a component's `templateUrl`
file or inline `template:` is read at synthesis time. `<app-foo>` by
element selector (same app first; two candidates = none) is a `calls`
edge from parent to child (`angular-template`), the edge a navigation in
a child component rides to its screen. `routerLink` / `[routerLink]` and
a `routerLink:` field written in the class (a tab bar's config) are
navigates edges. Gated on an `@angular/core` dependency.

Measured:
- angular-realworld: 10 routes all bound; 11/11 navigation sites
  accounted for (10 resolved, 1 query-only); 31/31 routerLink sites
  resolved; 18 renders.
- Ghostfolio: 84 routes all bound, 0 unresolved constant paths; 27
  navigate resolved, 23 correctly not (8 relative, 9 query-only, 2
  computed, 4 aimed at a matcher route); 162 routerLink edges, every one
  checked against its site; 170 renders.
- ngx-admin (NgModule): 55 routes, 47 bound (8 are @nebular/auth's); no
  navigation calls in the source.
- Controls byte-identical vs main (nodes and edges incl. metadata):
  koel, IceCubesApp, spring-petclinic, proshop. Ghostfolio index time at
  parity.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 02:43:58 +00:00
Colby McHenry 6f4887db80 feat(steps): draw all arms of conditional navigations as separate edges
Adds multi-arm navigation support: when a destination is produced by a conditional, every arm is now drawn as its own edge. Introduces helpers (hrefArms, destinationsForHref) and updates framework resolvers and edge creation to emit multiple navigates edges (via alsoTargets) instead of a single one. Also introduces per-app rooted route tables to avoid cross-app crossings, and updates various resolvers (React Router, TanStack Router, Vue Router, SvelteKit, Vue, and SvelteKit’s linker) and the UI to reflect multiple possible destinations. Tests and docs updated to reflect the new behavior, ensuring the Screens tab shows all possible navigation paths from conditional destinations. This makes navigation visualization more accurate for forked destinations.
2026-08-31 11:34:26 -05:00