Provider resolution
Source: src/engine/graph/type-resolver.ts
Extracts dependency information from every @Injectable() class: constructor
dependencies, public method count, and file location. Rules like
no-unused-providers, no-missing-injectable, and
no-business-logic-in-controllers need to know which providers exist, what they
depend on, and how many methods they expose.
Input is the parsed project and the file list:
project: Project // ts-morph AST project
files: string[] // file paths to analyzeThe result maps each provider name to its info:
Map<string, ProviderInfo>
interface ProviderInfo {
name: string // class name
filePath: string
classDeclaration: ClassDeclaration
dependencies: string[] // constructor parameter type names
publicMethodCount: number
}How it works
For each file, finds all classes decorated with @Injectable():
- Extract constructor parameters: each parameter's type becomes a dependency name.
- Normalize type names:
extractSimpleTypeName()reduces the annotation to a bare class name.import("@prisma/client").PrismaService→PrismaServiceRepository<User>→RepositoryConfigService→ConfigService
- Count public methods: methods with no access modifier, or an explicit
public.
Example
Given this class:
@Injectable()
export class UserService {
constructor(
private readonly prisma: PrismaService,
private readonly config: ConfigService,
) {}
async findAll() { /* ... */ }
async findById(id: string) { /* ... */ }
private validate(user: User) { /* ... */ }
}The resolver produces:
{
name: "UserService",
filePath: "/src/user/user.service.ts",
dependencies: ["PrismaService", "ConfigService"],
publicMethodCount: 2, // findAll + findById (validate is private)
}Debugging tips
- Only classes with
@Injectable()are resolved. Controllers use@Controller(), so they are absent from the providers map and are handled separately during rule execution. - If a dependency name looks wrong, for example because it still contains an import path, check the type annotation on the constructor parameter.