@moikas/code-audit-mcp
Version:
AI-powered code auditing via MCP using local Ollama models for security, performance, and quality analysis
484 lines • 14.6 kB
JavaScript
/**
* Code parsing utilities for _language detection and analysis
*/
/**
* Language detection based on code patterns and syntax
*/
export class LanguageDetector {
static patterns = {
javascript: [
/function\s+\w+/,
/const\s+\w+\s*=/,
/let\s+\w+\s*=/,
/var\s+\w+\s*=/,
/=>\s*{?/,
/require\s*\(/,
/module\.exports/,
],
typescript: [
/interface\s+\w+/,
/type\s+\w+\s*=/,
/enum\s+\w+/,
/:\s*\w+\s*[=;]/,
/import.*from/,
/export.*{/,
/as\s+\w+/,
],
python: [
/def\s+\w+\s*\(/,
/class\s+\w+/,
/import\s+\w+/,
/from\s+\w+\s+import/,
/if\s+__name__\s*==\s*['""]__main__['""]:/,
/#\s*.+$/m,
],
java: [
/public\s+class\s+\w+/,
/private\s+\w+\s+\w+/,
/public\s+static\s+void\s+main/,
/@\w+/,
/import\s+\w+\.\w+/,
/package\s+\w+/,
],
csharp: [
/using\s+\w+/,
/namespace\s+\w+/,
/public\s+class\s+\w+/,
/\[.+\]/,
/Console\.WriteLine/,
/var\s+\w+\s*=/,
],
go: [
/package\s+\w+/,
/import\s*\(/,
/func\s+\w+/,
/type\s+\w+\s+struct/,
/fmt\.Print/,
/go\s+\w+/,
],
rust: [
/fn\s+\w+/,
/let\s+mut\s+\w+/,
/struct\s+\w+/,
/impl\s+\w+/,
/use\s+\w+::/,
/println!/,
],
php: [
/<\?php/,
/\$\w+/,
/function\s+\w+/,
/class\s+\w+/,
/echo\s+/,
/include\s+/,
],
ruby: [
/def\s+\w+/,
/class\s+\w+/,
/module\s+\w+/,
/require\s+/,
/puts\s+/,
/@\w+/,
],
swift: [
/func\s+\w+/,
/class\s+\w+/,
/struct\s+\w+/,
/var\s+\w+:/,
/let\s+\w+:/,
/import\s+\w+/,
],
kotlin: [
/fun\s+\w+/,
/class\s+\w+/,
/val\s+\w+/,
/var\s+\w+/,
/import\s+\w+/,
/package\s+\w+/,
],
html: [/<html/i, /<head/i, /<body/i, /<div/i, /<script/i, /<style/i],
css: [
/\.\w+\s*{/,
/#\w+\s*{/,
/@media/,
/@import/,
/:\s*\w+;/,
/font-family:/,
],
sql: [
/SELECT\s+/i,
/FROM\s+/i,
/WHERE\s+/i,
/INSERT\s+INTO/i,
/UPDATE\s+/i,
/DELETE\s+FROM/i,
],
yaml: [/^\s*\w+:\s*$/m, /^\s*-\s+/m, /^\s*\|\s*$/m, /^\s*>\s*$/m],
json: [/^\s*{/, /^\s*\[/, /"\w+":\s*/, /"\w+"\s*,/],
dockerfile: [
/^FROM\s+/m,
/^RUN\s+/m,
/^COPY\s+/m,
/^ADD\s+/m,
/^WORKDIR\s+/m,
/^EXPOSE\s+/m,
],
};
/**
* Detect programming _language from code content
*/
static detectLanguage(code, filename) {
// First try to detect from filename extension
if (filename) {
const langFromExt = this.detectFromExtension(filename);
if (langFromExt) {
return langFromExt;
}
}
// Then analyze code patterns
const scores = {};
for (const [_language, patterns] of Object.entries(this.patterns)) {
let score = 0;
for (const pattern of patterns) {
const matches = code.match(pattern);
if (matches) {
score += matches.length;
}
}
scores[_language] = score;
}
// Return _language with highest score
const bestMatch = Object.entries(scores).reduce((a, b) => scores[a[0]] > scores[b[0]] ? a : b);
return bestMatch[1] > 0 ? bestMatch[0] : 'unknown';
}
/**
* Detect _language from file extension
*/
static detectFromExtension(filename) {
const ext = filename.toLowerCase().split('.').pop();
const extensionMap = {
js: 'javascript',
jsx: 'javascript',
ts: 'typescript',
tsx: 'typescript',
py: 'python',
java: 'java',
cs: 'csharp',
go: 'go',
rs: 'rust',
php: 'php',
rb: 'ruby',
swift: 'swift',
kt: 'kotlin',
html: 'html',
htm: 'html',
css: 'css',
scss: 'css',
sass: 'css',
sql: 'sql',
yaml: 'yaml',
yml: 'yaml',
json: 'json',
dockerfile: 'dockerfile',
};
return ext ? extensionMap[ext] || null : null;
}
/**
* Check if detected _language is supported
*/
static isSupported(_language) {
return Object.keys(this.patterns).includes(_language);
}
}
/**
* Generic code parser for extracting structure and metrics
*/
export class CodeParser {
/**
* Parse code structure and extract functions, classes, etc.
*/
static parseCode(code, _language) {
const lines = code.split('\n');
return {
_language,
functions: this.extractFunctions(code, _language),
classes: this.extractClasses(code, _language),
imports: this.extractImports(code, _language),
exports: this.extractExports(code, _language),
comments: this.extractComments(code, _language),
lineCount: lines.length,
complexity: this.calculateComplexity(code, _language),
};
}
/**
* Extract function definitions
*/
static extractFunctions(code, _language) {
const functions = [];
const lines = code.split('\n');
const functionPatterns = this.getFunctionPatterns(_language);
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
for (const pattern of functionPatterns) {
const match = line.match(pattern);
if (match) {
const func = this.parseFunctionDetails(lines, i, match, _language);
if (func) {
functions.push(func);
}
}
}
}
return functions;
}
/**
* Extract class definitions
*/
static extractClasses(code, _language) {
const classes = [];
const lines = code.split('\n');
const classPatterns = this.getClassPatterns(_language);
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
for (const pattern of classPatterns) {
const match = line.match(pattern);
if (match) {
const cls = this.parseClassDetails(lines, i, match, _language);
if (cls) {
classes.push(cls);
}
}
}
}
return classes;
}
/**
* Extract import statements
*/
static extractImports(code, _language) {
const imports = [];
const importPatterns = this.getImportPatterns(_language);
for (const pattern of importPatterns) {
const matches = code.match(new RegExp(pattern.source, 'gm'));
if (matches) {
imports.push(...matches);
}
}
return imports;
}
/**
* Extract export statements
*/
static extractExports(code, _language) {
const exports = [];
const exportPatterns = this.getExportPatterns(_language);
for (const pattern of exportPatterns) {
const matches = code.match(new RegExp(pattern.source, 'gm'));
if (matches) {
exports.push(...matches);
}
}
return exports;
}
/**
* Extract comments
*/
static extractComments(code, _language) {
const comments = [];
const lines = code.split('\n');
const commentPatterns = this.getCommentPatterns(_language);
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
// Single line comments
for (const pattern of commentPatterns.single) {
const match = line.match(pattern);
if (match) {
comments.push({
line: i + 1,
type: 'single',
content: match[0],
});
}
}
}
// Multi-line comments (simplified)
for (const pattern of commentPatterns.multi) {
const matches = code.match(new RegExp(pattern.source, 'gms'));
if (matches) {
for (const match of matches) {
const lineNum = code
.substring(0, code.indexOf(match))
.split('\n').length;
comments.push({
line: lineNum,
type: 'multi',
content: match,
});
}
}
}
return comments;
}
/**
* Calculate code complexity
*/
static calculateComplexity(code, _language) {
let complexity = 1; // Base complexity
const complexityPatterns = this.getComplexityPatterns(_language);
for (const pattern of complexityPatterns) {
const matches = code.match(new RegExp(pattern.source, 'g'));
if (matches) {
complexity += matches.length;
}
}
return complexity;
}
/**
* Get function patterns for specific _language
*/
static getFunctionPatterns(_language) {
const patterns = {
javascript: [
/function\s+(\w+)\s*\([^)]*\)/,
/(\w+)\s*:\s*function\s*\([^)]*\)/,
/(\w+)\s*=>\s*/,
/(\w+)\s*=\s*function\s*\([^)]*\)/,
],
typescript: [
/function\s+(\w+)\s*\([^)]*\)/,
/(\w+)\s*\([^)]*\)\s*:\s*\w+/,
/(\w+)\s*=>\s*/,
],
python: [/def\s+(\w+)\s*\([^)]*\)/],
java: [/\w+\s+(\w+)\s*\([^)]*\)\s*{/],
go: [/func\s+(\w+)\s*\([^)]*\)/],
rust: [/fn\s+(\w+)\s*\([^)]*\)/],
};
return patterns[_language] || [];
}
/**
* Get class patterns for specific _language
*/
static getClassPatterns(_language) {
const patterns = {
javascript: [/class\s+(\w+)/],
typescript: [/class\s+(\w+)/, /interface\s+(\w+)/],
python: [/class\s+(\w+)/],
java: [/class\s+(\w+)/, /interface\s+(\w+)/],
csharp: [/class\s+(\w+)/, /interface\s+(\w+)/],
go: [/type\s+(\w+)\s+struct/],
rust: [/struct\s+(\w+)/, /enum\s+(\w+)/, /trait\s+(\w+)/],
};
return patterns[_language] || [];
}
/**
* Get import patterns for specific _language
*/
static getImportPatterns(_language) {
const patterns = {
javascript: [/import.*from.*/, /require\s*\(.*/],
typescript: [/import.*from.*/],
python: [/import\s+.*/, /from\s+.*\s+import.*/],
java: [/import\s+.*;/],
go: [/import\s+.*/],
};
return patterns[_language] || [];
}
/**
* Get export patterns for specific _language
*/
static getExportPatterns(_language) {
const patterns = {
javascript: [/export\s+.*/, /module\.exports\s*=.*/],
typescript: [/export\s+.*/],
};
return patterns[_language] || [];
}
/**
* Get comment patterns for specific _language
*/
static getCommentPatterns(_language) {
const patterns = {
javascript: {
single: [/\/\/.*/],
multi: [/\/\*[\s\S]*?\*\//],
},
typescript: {
single: [/\/\/.*/],
multi: [/\/\*[\s\S]*?\*\//],
},
python: {
single: [/#.*/],
multi: [/"""[\s\S]*?"""/],
},
java: {
single: [/\/\/.*/],
multi: [/\/\*[\s\S]*?\*\//],
},
};
return patterns[_language] || { single: [], multi: [] };
}
/**
* Get complexity patterns for specific _language
*/
static getComplexityPatterns(_language) {
return [
/\bif\b/,
/\belse\b/,
/\bwhile\b/,
/\bfor\b/,
/\bswitch\b/,
/\bcase\b/,
/\btry\b/,
/\bcatch\b/,
/\band\b/,
/\bor\b/,
/&&/,
/\|\|/,
/\?.*:/,
];
}
/**
* Parse function details from matched line
*/
static parseFunctionDetails(lines, startLine, match, _language) {
const name = match[1] || 'anonymous';
const line = lines[startLine];
return {
name,
startLine: startLine + 1,
endLine: startLine + 1, // Simplified - would need better parsing
parameters: this.extractParameters(line),
complexity: 1,
isAsync: line.includes('async'),
isExported: line.includes('export'),
};
}
/**
* Parse class details from matched line
*/
static parseClassDetails(lines, startLine, match, _language) {
const name = match[1] || 'anonymous';
return {
name,
startLine: startLine + 1,
endLine: startLine + 1, // Simplified
methods: [],
properties: [],
};
}
/**
* Extract function parameters
*/
static extractParameters(line) {
const paramMatch = line.match(/\(([^)]*)\)/);
if (!paramMatch || !paramMatch[1]) {
return [];
}
return paramMatch[1]
.split(',')
.map((param) => param.trim())
.filter((param) => param.length > 0);
}
}
//# sourceMappingURL=codeParser.js.map