Skip to main content
Glama
lininn
by lininn
code-analyzer.ts7.93 kB
import { CodeAnalysisResult, CodeIssue, AnalysisRule } from './types.js'; export class CodeAnalyzer { private rules: Map<string, AnalysisRule[]> = new Map(); constructor() { this.initializeRules(); } private initializeRules(): void { // JavaScript/TypeScript rules this.rules.set('javascript', [ { name: 'no-console-log', pattern: /console\.log\(/g, message: 'Console.log statement found', severity: 'warning', type: 'debug', }, { name: 'no-var', pattern: /\bvar\s+/g, message: 'Use let or const instead of var', severity: 'warning', type: 'best-practice', }, { name: 'no-eval', pattern: /\beval\(/g, message: 'eval() is dangerous and should be avoided', severity: 'error', type: 'security', }, { name: 'arrow-function-spacing', pattern: /=>\s*{/g, message: 'Consider using consistent arrow function spacing', severity: 'info', type: 'style', }, ]); this.rules.set('typescript', [ ...this.rules.get('javascript')!, { name: 'no-any', pattern: /:\s*any\b/g, message: 'Avoid using "any" type, use specific types', severity: 'warning', type: 'type-safety', }, { name: 'explicit-return-type', pattern: /function\s+\w+\([^)]*\)\s*{/g, message: 'Consider adding explicit return type', severity: 'info', type: 'type-safety', }, ]); // Python rules this.rules.set('python', [ { name: 'no-print', pattern: /\bprint\(/g, message: 'Consider using logging instead of print', severity: 'warning', type: 'debug', }, { name: 'pep8-line-length', pattern: /.{89,}/g, message: 'Line exceeds PEP 8 recommendation (88 characters)', severity: 'warning', type: 'style', }, { name: 'unused-import', pattern: /^import\s+\w+.*$/gm, message: 'Potential unused import (manual check required)', severity: 'info', type: 'cleanup', }, ]); // Java rules this.rules.set('java', [ { name: 'system-out-println', pattern: /System\.out\.println\(/g, message: 'Use proper logging instead of System.out.println', severity: 'warning', type: 'debug', }, { name: 'public-class-naming', pattern: /public\s+class\s+[a-z]/g, message: 'Class names should start with uppercase letter', severity: 'error', type: 'naming', }, ]); // Go rules this.rules.set('go', [ { name: 'no-fmt-println', pattern: /fmt\.Println\(/g, message: 'Consider using proper logging instead of fmt.Println', severity: 'warning', type: 'debug', }, { name: 'error-handling', pattern: /if\s+err\s*!=\s*nil\s*{/g, message: 'Good error handling practice', severity: 'info', type: 'best-practice', }, ]); // Common rules for all languages const commonRules: AnalysisRule[] = [ { name: 'todo-comment', pattern: /\b(TODO|FIXME|HACK|XXX)\b/gi, message: 'TODO/FIXME comment found', severity: 'info', type: 'maintenance', }, { name: 'long-line', pattern: /.{121,}/g, message: 'Line exceeds 120 characters', severity: 'warning', type: 'style', }, { name: 'trailing-whitespace', pattern: /\s+$/gm, message: 'Trailing whitespace found', severity: 'info', type: 'style', }, ]; // Add common rules to all languages for (const [language, rules] of this.rules) { this.rules.set(language, [...rules, ...commonRules]); } } analyze(code: string, language: string, customRules?: string[]): CodeAnalysisResult { const normalizedLanguage = language.toLowerCase(); const rules = this.rules.get(normalizedLanguage) || this.rules.get('javascript') || []; const lines = code.split('\n'); const issues: CodeIssue[] = []; const suggestions: string[] = []; // Apply rules for (const rule of rules) { if (customRules && customRules.length > 0 && !customRules.includes(rule.name)) { continue; } lines.forEach((line: string, index: number) => { const matches = line.match(rule.pattern); if (matches) { issues.push({ line: index + 1, column: line.search(rule.pattern) + 1, type: rule.type, message: rule.message, severity: rule.severity, rule: rule.name, }); } }); } // Generate suggestions based on analysis if (lines.length > 100) { suggestions.push('Consider breaking this file into smaller modules'); } if (issues.filter(i => i.severity === 'error').length > 5) { suggestions.push('Multiple errors found - consider reviewing code structure'); } const warningCount = issues.filter(i => i.severity === 'warning').length; if (warningCount > 10) { suggestions.push('High number of warnings - consider code refactoring'); } // Language-specific suggestions if (normalizedLanguage === 'javascript' || normalizedLanguage === 'typescript') { const hasConsoleLog = issues.some(i => i.rule === 'no-console-log'); if (hasConsoleLog) { suggestions.push('Replace console.log with proper logging framework'); } } // Calculate complexity metrics const cyclomaticComplexity = this.calculateCyclomaticComplexity(code, normalizedLanguage); const maintainabilityIndex = this.calculateMaintainabilityIndex(code, issues.length); return { language: normalizedLanguage, codeLength: code.length, lineCount: lines.length, issues, suggestions, metrics: { cyclomaticComplexity, maintainabilityIndex, issueCount: { error: issues.filter(i => i.severity === 'error').length, warning: issues.filter(i => i.severity === 'warning').length, info: issues.filter(i => i.severity === 'info').length, }, }, }; } private calculateCyclomaticComplexity(code: string, language: string): number { // Simple complexity calculation based on control flow statements const patterns = { javascript: /\b(if|else|while|for|switch|case|catch|&&|\|\||\?)\b/g, typescript: /\b(if|else|while|for|switch|case|catch|&&|\|\||\?)\b/g, python: /\b(if|elif|else|while|for|try|except|and|or)\b/g, java: /\b(if|else|while|for|switch|case|catch|&&|\|\||\?)\b/g, go: /\b(if|else|for|switch|case|&&|\|\|)\b/g, }; const pattern = patterns[language as keyof typeof patterns] || patterns.javascript; const matches = code.match(pattern); return (matches?.length || 0) + 1; // +1 for the main execution path } private calculateMaintainabilityIndex(code: string, issueCount: number): number { // Simplified maintainability index (0-100, higher is better) const lines = code.split('\n').length; const comments = (code.match(/\/\/|\/\*|\*\/|#/g) || []).length; const commentRatio = comments / lines; let score = 100; score -= Math.min(lines / 10, 30); // Penalty for length score -= Math.min(issueCount * 2, 40); // Penalty for issues score += Math.min(commentRatio * 20, 20); // Bonus for comments return Math.max(0, Math.round(score)); } getSupportedLanguages(): string[] { return Array.from(this.rules.keys()); } getLanguageRules(language: string): AnalysisRule[] { return this.rules.get(language.toLowerCase()) || []; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/lininn/gitlab-review-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server