@aaswe/codebase-ai
Version:
AI-Assisted Software Engineering (AASWE) - Rich codebase context for IDE LLMs
272 lines • 9.12 kB
JavaScript
"use strict";
/**
* Base AST Analyzer
* Abstract base class for language-specific AST analyzers
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseAnalyzer = void 0;
const fs_1 = require("fs");
const logger_1 = __importDefault(require("../../../utils/logger"));
class BaseAnalyzer {
language;
defaultOptions = {
includeComments: false,
calculateComplexity: true,
extractDependencies: true,
includePrivateMembers: true,
maxDepth: 10,
timeout: 30000
};
constructor(language) {
this.language = language;
}
/**
* Analyze a single file
*/
async analyzeFile(filePath, options) {
const startTime = Date.now();
const context = this.createContext(filePath, options);
try {
logger_1.default.debug(`Starting AST analysis for ${filePath}`);
// Read file content
const content = (0, fs_1.readFileSync)(filePath, 'utf-8');
// Parse AST
const ast = await this.parseAST(content, context);
// Extract information
const nodes = await this.extractNodes(ast, context);
const functions = await this.extractFunctions(ast, context);
const classes = await this.extractClasses(ast, context);
const imports = await this.extractImports(ast, context);
const exports = await this.extractExports(ast, context);
const dependencies = await this.extractDependencies(ast, context);
// Calculate complexity
const complexity = context.options.calculateComplexity
? await this.calculateComplexity(ast, context)
: this.getDefaultComplexity();
const result = {
filePath,
language: context.language,
nodes,
functions,
classes,
imports,
exports,
dependencies,
complexity,
errors: [],
timestamp: new Date()
};
const processingTime = Date.now() - startTime;
logger_1.default.debug(`AST analysis completed for ${filePath} in ${processingTime}ms`);
return result;
}
catch (error) {
logger_1.default.error(`AST analysis failed for ${filePath}:`, error);
return {
filePath,
language: context.language,
nodes: [],
functions: [],
classes: [],
imports: [],
exports: [],
dependencies: [],
complexity: this.getDefaultComplexity(),
errors: [{
type: 'analysis',
message: error instanceof Error ? error.message : 'Unknown analysis error',
severity: 'error'
}],
timestamp: new Date()
};
}
}
/**
* Analyze multiple files
*/
async analyzeFiles(filePaths, options) {
const results = [];
for (const filePath of filePaths) {
try {
const result = await this.analyzeFile(filePath, options);
results.push(result);
}
catch (error) {
logger_1.default.error(`Failed to analyze ${filePath}:`, error);
const context = this.createContext(filePath, options);
results.push({
filePath,
language: context.language,
nodes: [],
functions: [],
classes: [],
imports: [],
exports: [],
dependencies: [],
complexity: this.getDefaultComplexity(),
errors: [{
type: 'analysis',
message: error instanceof Error ? error.message : 'Unknown analysis error',
severity: 'error'
}],
timestamp: new Date()
});
}
}
return results;
}
/**
* Build dependency graph from analysis results
*/
buildDependencyGraph(results) {
const nodes = new Map();
const edges = [];
// Create nodes for each file, function, and class
for (const result of results) {
// File node
nodes.set(result.filePath, {
id: result.filePath,
name: result.filePath.split('/').pop() || result.filePath,
type: 'file',
filePath: result.filePath,
exported: result.exports.length > 0
});
// Function nodes
for (const func of result.functions) {
nodes.set(func.id, {
id: func.id,
name: func.name,
type: 'function',
filePath: result.filePath,
exported: func.isExported
});
}
// Class nodes
for (const cls of result.classes) {
nodes.set(cls.id, {
id: cls.id,
name: cls.name,
type: 'class',
filePath: result.filePath,
exported: cls.isExported
});
}
}
// Create edges for dependencies
for (const result of results) {
// Import edges
for (const imp of result.imports) {
edges.push({
from: result.filePath,
to: imp.source,
type: 'imports',
weight: imp.imports.length
});
}
// Function call edges
for (const func of result.functions) {
for (const call of func.calls) {
edges.push({
from: func.id,
to: call,
type: 'calls',
weight: 1
});
}
}
// Class inheritance edges
for (const cls of result.classes) {
if (cls.extends) {
edges.push({
from: cls.id,
to: cls.extends,
type: 'extends',
weight: 1
});
}
for (const impl of cls.implements) {
edges.push({
from: cls.id,
to: impl,
type: 'implements',
weight: 1
});
}
}
}
return {
nodes: Array.from(nodes.values()),
edges
};
}
/**
* Create analysis context
*/
createContext(filePath, options) {
// Detect language from file extension instead of using hardcoded language
const detectedLanguage = this.detectLanguageFromPath(filePath);
return {
filePath,
projectRoot: process.cwd(),
language: detectedLanguage,
options: { ...this.defaultOptions, ...options },
config: this.getParserConfig()
};
}
/**
* Detect language from file path
*/
detectLanguageFromPath(filePath) {
const extension = filePath.split('.').pop()?.toLowerCase();
switch (extension) {
case 'ts':
case 'tsx':
return 'typescript';
case 'js':
case 'jsx':
case 'mjs':
return 'javascript';
case 'py':
return 'python';
case 'java':
return 'java';
case 'go':
return 'go';
case 'rs':
return 'rust';
case 'cpp':
case 'cc':
case 'cxx':
case 'c++':
case 'hpp':
case 'h':
return 'cpp';
default:
// Fallback to analyzer's language if detection fails
return this.language;
}
}
/**
* Generate unique ID
*/
generateId() {
return Math.random().toString(36).substring(2) + Date.now().toString(36);
}
/**
* Get default complexity metrics
*/
getDefaultComplexity() {
return {
cyclomaticComplexity: 0,
cognitiveComplexity: 0,
linesOfCode: 0,
maintainabilityIndex: 100,
technicalDebt: 0
};
}
}
exports.BaseAnalyzer = BaseAnalyzer;
//# sourceMappingURL=BaseAnalyzer.js.map