How it works
No monkey-patching, no runtime proxies: just the right timing and Reflect metadata.
The pipeline
DocfyModule.forRoot() runs synchronously during the NestFactory.create() phase, before SwaggerModule.createDocument() is called. It uses require() to load each companion docs file, which executes docs() and writes Reflect metadata directly onto the controller's methods, exactly as TypeScript's decorator syntax would at class-definition time.
By the time SwaggerModule.createDocument() scans for metadata, everything is already in place. No monkey-patching, no runtime proxies.
Timing
onModuleInit and other lifecycle hooks fire after SwaggerModule.createDocument() has already been called in main.ts. Any approach based on onModuleInit would produce empty Swagger documentation. That's why nestjs-docfy avoids that path and uses a synchronous require() inside forRoot(), which runs during NestFactory.create(), before any lifecycle hook or SwaggerModule call.
CLI: static analysis
The generate CLI uses ts-morph for static analysis: it reads the TypeScript AST without executing any project code. All user-supplied paths and glob patterns are validated and sanitized before use.