fix(nestjs): propagate RouterModule.register prefixes to controller routes (#459) (#460)

NestJS's RouterModule lets apps compose modular route prefixes across files
(`RouterModule.register([{ path: 'admin', module: AdminModule, children: [...] }])`
in `app.module.ts` sets the prefix for controllers declared in another file's
`@Controller()`). The per-file `extract()` only sees one file at a time, so a
`UsersController` indexed in isolation showed up as `GET /` instead of
`GET /admin/users`.

Add an optional cross-file `postExtract(context)` hook to FrameworkResolver,
called by the orchestrator once after each `indexAll` and after every
incremental `sync` that touched files. The nestjs implementation:

  * walks every `*.module.{ts,js}` for `RouterModule.{register,forRoot,forChild}([...])`
    and recursively resolves `children` into `Module → /full/prefix`,
  * walks `@Module({ controllers: [...] })` for `Controller → Module`,
  * matches each route node against its controller's class line range
    (multi-controller files keep getting attributed correctly), and
  * rewrites `name` while preserving `id` (route→handler edges intact) and
    `qualifiedName` (still encodes the *original* in-file path, which keeps
    the pass idempotent on a re-sync — `app.module.ts` edits propagate to
    controllers in unchanged files without double-prefixing).

End-to-end validated against the exact reproduction in #459 (admin children
users) — all four routes (`GET /admin`, `GET /admin/users`,
`GET /admin/users/:id`, `POST /admin/users`) resolve correctly, edits to the
RouterModule tree re-propagate on the next sync, and route→handler edges in
`codegraph context` are preserved.

Closes #459

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-05-26 11:55:18 -05:00
committed by GitHub
co-authored by Claude Opus 4.7
parent 180ba785ce
commit 8876defbc1
6 changed files with 650 additions and 0 deletions
+11
View File
@@ -336,6 +336,9 @@ export class CodeGraph {
// chance to see the actual project before resolution runs.
if (result.success && result.filesIndexed > 0) {
this.resolver.initialize();
// Cross-file finalization (e.g. NestJS RouterModule prefixes). Runs
// before resolution so updated names show up in subsequent reads.
this.resolver.runPostExtract();
}
// Resolve references to create call/import/extends edges
@@ -406,6 +409,14 @@ export class CodeGraph {
try {
const result = await this.orchestrator.sync(options.onProgress);
// Cross-file finalization (e.g. NestJS RouterModule prefixes). Run on
// every sync that touched files so edits to `app.module.ts` propagate
// to controllers in unchanged files. The pass is idempotent and cheap
// (regex over *.module.ts only).
if (result.filesAdded > 0 || result.filesModified > 0) {
this.resolver.runPostExtract();
}
// Resolve references if files were updated
if (result.filesAdded > 0 || result.filesModified > 0) {
if (result.changedFilePaths) {