ultimate-mcp-server
Version:
The definitive all-in-one Model Context Protocol server for AI-assisted coding across 30+ platforms
371 lines • 14.4 kB
JavaScript
/**
* Code Analysis for Cognitive Memory
* Analyzes code to extract symbols, dependencies, and patterns
*/
import { parse } from '@babel/parser';
import _traverse from '@babel/traverse';
const traverse = _traverse;
import * as t from '@babel/types';
import { Logger } from '../utils/logger.js';
const logger = new Logger('CodeAnalyzer');
export class CodeAnalyzer {
/**
* Analyze JavaScript/TypeScript code
*/
async analyzeJavaScript(code, filePath) {
const symbols = [];
const dependencies = [];
const patterns = new Map();
try {
// Parse code into AST
const ast = parse(code, {
sourceType: 'module',
plugins: ['typescript', 'jsx', 'decorators-legacy'],
sourceFilename: filePath
});
// Traverse AST to extract information
traverse(ast, {
// Functions
FunctionDeclaration(path) {
const node = path.node;
if (node.id) {
symbols.push({
name: node.id.name,
type: 'function',
location: {
file: filePath,
line: node.loc?.start.line || 0,
column: node.loc?.start.column || 0
},
signature: code.substring(node.start, node.end).split('\n')[0]
});
}
},
// Arrow functions assigned to variables
VariableDeclarator(path) {
if (t.isArrowFunctionExpression(path.node.init) ||
t.isFunctionExpression(path.node.init)) {
if (t.isIdentifier(path.node.id)) {
symbols.push({
name: path.node.id.name,
type: 'function',
location: {
file: filePath,
line: path.node.loc?.start.line || 0,
column: path.node.loc?.start.column || 0
}
});
}
}
},
// Classes
ClassDeclaration(path) {
const node = path.node;
if (node.id) {
symbols.push({
name: node.id.name,
type: 'class',
location: {
file: filePath,
line: node.loc?.start.line || 0,
column: node.loc?.start.column || 0
}
});
// Check for inheritance
if (node.superClass && t.isIdentifier(node.superClass)) {
dependencies.push({
source: node.id.name,
target: node.superClass.name,
type: 'extends'
});
}
}
},
// Interfaces (TypeScript)
TSInterfaceDeclaration(path) {
const node = path.node;
symbols.push({
name: node.id.name,
type: 'interface',
location: {
file: filePath,
line: node.loc?.start.line || 0,
column: node.loc?.start.column || 0
}
});
},
// Imports
ImportDeclaration(path) {
const node = path.node;
const source = node.source.value;
node.specifiers.forEach((spec) => {
if (t.isImportDefaultSpecifier(spec) ||
t.isImportSpecifier(spec) ||
t.isImportNamespaceSpecifier(spec)) {
const name = spec.local.name;
symbols.push({
name,
type: 'import',
location: {
file: filePath,
line: node.loc?.start.line || 0,
column: node.loc?.start.column || 0
}
});
dependencies.push({
source: filePath,
target: source,
type: 'import'
});
}
});
},
// Exports
ExportNamedDeclaration(path) {
const node = path.node;
if (node.declaration) {
if (t.isFunctionDeclaration(node.declaration) && node.declaration.id) {
symbols.push({
name: node.declaration.id.name,
type: 'export',
location: {
file: filePath,
line: node.loc?.start.line || 0,
column: node.loc?.start.column || 0
}
});
}
}
},
// Pattern detection: try-catch blocks
TryStatement(path) {
const patternType = 'error-handling';
if (!patterns.has(patternType)) {
patterns.set(patternType, {
type: patternType,
description: 'Try-catch error handling blocks',
occurrences: 0,
locations: []
});
}
const pattern = patterns.get(patternType);
pattern.occurrences++;
pattern.locations.push({
file: filePath,
line: path.node.loc?.start.line || 0
});
},
// Pattern detection: async/await
AwaitExpression(path) {
const patternType = 'async-await';
if (!patterns.has(patternType)) {
patterns.set(patternType, {
type: patternType,
description: 'Async/await usage',
occurrences: 0,
locations: []
});
}
const pattern = patterns.get(patternType);
pattern.occurrences++;
pattern.locations.push({
file: filePath,
line: path.node.loc?.start.line || 0
});
},
// Pattern detection: Promise usage
NewExpression(path) {
if (t.isIdentifier(path.node.callee) && path.node.callee.name === 'Promise') {
const patternType = 'promise';
if (!patterns.has(patternType)) {
patterns.set(patternType, {
type: patternType,
description: 'Promise usage',
occurrences: 0,
locations: []
});
}
const pattern = patterns.get(patternType);
pattern.occurrences++;
pattern.locations.push({
file: filePath,
line: path.node.loc?.start.line || 0
});
}
}
});
// Calculate complexity
const complexity = this.calculateComplexity(ast);
return {
symbols,
dependencies,
complexity,
patterns: Array.from(patterns.values())
};
}
catch (error) {
logger.error(`Failed to analyze JavaScript code: ${error}`);
return {
symbols: [],
dependencies: [],
complexity: 0,
patterns: []
};
}
}
/**
* Analyze Python code
*/
async analyzePython(code, filePath) {
const symbols = [];
const dependencies = [];
const patterns = [];
// Simple regex-based analysis for Python
const lines = code.split('\n');
lines.forEach((line, index) => {
// Function definitions
const funcMatch = line.match(/^\s*def\s+(\w+)\s*\(/);
if (funcMatch) {
symbols.push({
name: funcMatch[1],
type: 'function',
location: {
file: filePath,
line: index + 1,
column: line.indexOf('def')
},
signature: line.trim()
});
}
// Class definitions
const classMatch = line.match(/^\s*class\s+(\w+)(?:\s*\(([^)]+)\))?\s*:/);
if (classMatch) {
symbols.push({
name: classMatch[1],
type: 'class',
location: {
file: filePath,
line: index + 1,
column: line.indexOf('class')
}
});
// Check for inheritance
if (classMatch[2]) {
const baseClasses = classMatch[2].split(',').map(s => s.trim());
baseClasses.forEach(base => {
dependencies.push({
source: classMatch[1],
target: base,
type: 'extends'
});
});
}
}
// Import statements
const importMatch = line.match(/^\s*(?:from\s+(\S+)\s+)?import\s+(.+)/);
if (importMatch) {
const module = importMatch[1] || '';
const imports = importMatch[2].split(',').map(s => s.trim());
imports.forEach(imp => {
const name = imp.split(' as ')[0].trim();
symbols.push({
name,
type: 'import',
location: {
file: filePath,
line: index + 1,
column: 0
}
});
if (module) {
dependencies.push({
source: filePath,
target: module,
type: 'import'
});
}
});
}
});
// Simple complexity calculation
const complexity = this.calculatePythonComplexity(code);
return {
symbols,
dependencies,
complexity,
patterns
};
}
/**
* Calculate cyclomatic complexity for JavaScript/TypeScript
*/
calculateComplexity(ast) {
let complexity = 1; // Base complexity
traverse(ast, {
// Each decision point increases complexity
IfStatement() { complexity++; },
ConditionalExpression() { complexity++; },
SwitchCase() { complexity++; },
WhileStatement() { complexity++; },
ForStatement() { complexity++; },
ForInStatement() { complexity++; },
ForOfStatement() { complexity++; },
DoWhileStatement() { complexity++; },
CatchClause() { complexity++; },
LogicalExpression(path) {
if (path.node.operator === '&&' || path.node.operator === '||') {
complexity++;
}
}
});
return complexity;
}
/**
* Calculate cyclomatic complexity for Python
*/
calculatePythonComplexity(code) {
let complexity = 1;
const decisionKeywords = [
/\bif\b/,
/\belif\b/,
/\bwhile\b/,
/\bfor\b/,
/\bexcept\b/,
/\band\b/,
/\bor\b/
];
const lines = code.split('\n');
lines.forEach(line => {
decisionKeywords.forEach(keyword => {
if (keyword.test(line)) {
complexity++;
}
});
});
return complexity;
}
/**
* Analyze code based on file extension
*/
async analyzeCode(code, filePath) {
const extension = filePath.split('.').pop()?.toLowerCase();
switch (extension) {
case 'js':
case 'jsx':
case 'ts':
case 'tsx':
return this.analyzeJavaScript(code, filePath);
case 'py':
return this.analyzePython(code, filePath);
default:
logger.warn(`Unsupported file type for analysis: ${extension}`);
return {
symbols: [],
dependencies: [],
complexity: 0,
patterns: []
};
}
}
}
//# sourceMappingURL=code-analyzer.js.map