Files
puaros/packages/guardian/src/infrastructure/analyzers/NamingConventionDetector.ts

102 lines
3.8 KiB
TypeScript

import Parser from "tree-sitter"
import { INamingConventionDetector } from "../../domain/services/INamingConventionDetector"
import { NamingViolation } from "../../domain/value-objects/NamingViolation"
import { FILE_EXTENSIONS } from "../../shared/constants"
import { EXCLUDED_FILES } from "../constants/detectorPatterns"
import { CodeParser } from "../parsers/CodeParser"
import { AstClassNameAnalyzer } from "../strategies/naming/AstClassNameAnalyzer"
import { AstFunctionNameAnalyzer } from "../strategies/naming/AstFunctionNameAnalyzer"
import { AstInterfaceNameAnalyzer } from "../strategies/naming/AstInterfaceNameAnalyzer"
import { AstNamingTraverser } from "../strategies/naming/AstNamingTraverser"
import { AstVariableNameAnalyzer } from "../strategies/naming/AstVariableNameAnalyzer"
/**
* Detects naming convention violations using AST-based analysis
*
* This detector uses Abstract Syntax Tree (AST) analysis via tree-sitter to identify
* naming convention violations in classes, interfaces, functions, and variables
* according to Clean Architecture layer rules.
*
* The detector uses a modular architecture with specialized components:
* - AstClassNameAnalyzer: Analyzes class names
* - AstInterfaceNameAnalyzer: Analyzes interface names
* - AstFunctionNameAnalyzer: Analyzes function and method names
* - AstVariableNameAnalyzer: Analyzes variable and constant names
* - AstNamingTraverser: Traverses the AST and coordinates analyzers
*
* @example
* ```typescript
* const detector = new NamingConventionDetector()
* const code = `
* class userService { // Wrong: should be UserService
* GetUser() {} // Wrong: should be getUser
* }
* `
* const violations = detector.detectViolations(code, 'UserService.ts', 'domain', 'src/domain/UserService.ts')
* // Returns array of NamingViolation objects
* ```
*/
export class NamingConventionDetector implements INamingConventionDetector {
private readonly parser: CodeParser
private readonly traverser: AstNamingTraverser
constructor() {
this.parser = new CodeParser()
const classAnalyzer = new AstClassNameAnalyzer()
const interfaceAnalyzer = new AstInterfaceNameAnalyzer()
const functionAnalyzer = new AstFunctionNameAnalyzer()
const variableAnalyzer = new AstVariableNameAnalyzer()
this.traverser = new AstNamingTraverser(
classAnalyzer,
interfaceAnalyzer,
functionAnalyzer,
variableAnalyzer,
)
}
/**
* Detects naming convention violations in the given code
*
* @param content - Source code to analyze
* @param fileName - Name of the file being analyzed
* @param layer - Architectural layer (domain, application, infrastructure, shared)
* @param filePath - File path for context (used in violation reports)
* @returns Array of detected naming violations
*/
public detectViolations(
content: string,
fileName: string,
layer: string | undefined,
filePath: string,
): NamingViolation[] {
if (!layer) {
return []
}
if ((EXCLUDED_FILES as readonly string[]).includes(fileName)) {
return []
}
if (!content || content.trim().length === 0) {
return []
}
const tree = this.parseCode(content, filePath)
return this.traverser.traverse(tree, content, layer, filePath)
}
/**
* Parses code based on file extension
*/
private parseCode(code: string, filePath: string): Parser.Tree {
if (filePath.endsWith(FILE_EXTENSIONS.TYPESCRIPT_JSX)) {
return this.parser.parseTsx(code)
} else if (filePath.endsWith(FILE_EXTENSIONS.TYPESCRIPT)) {
return this.parser.parseTypeScript(code)
}
return this.parser.parseJavaScript(code)
}
}