@aaswe/codebase-ai
Version:
AI-Assisted Software Engineering (AASWE) - Rich codebase context for IDE LLMs
642 lines • 24.4 kB
JavaScript
"use strict";
/**
* TypeScript/JavaScript AST Analyzer
* Implements AST analysis for TypeScript and JavaScript files
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TypeScriptAnalyzer = void 0;
const parser_1 = require("@babel/parser");
const traverse_1 = __importDefault(require("@babel/traverse"));
const t = __importStar(require("@babel/types"));
const BaseAnalyzer_1 = require("./BaseAnalyzer");
class TypeScriptAnalyzer extends BaseAnalyzer_1.BaseAnalyzer {
constructor() {
super('typescript');
}
async parseAST(content, _context) {
try {
return (0, parser_1.parse)(content, {
sourceType: 'module',
allowImportExportEverywhere: true,
allowReturnOutsideFunction: false,
plugins: [
'typescript',
'jsx',
'decorators-legacy',
'classProperties',
'objectRestSpread',
'functionBind',
'exportDefaultFrom',
'exportNamespaceFrom',
'dynamicImport',
'nullishCoalescingOperator',
'optionalChaining'
]
});
}
catch (error) {
throw new Error(`Failed to parse TypeScript/JavaScript: ${error}`);
}
}
async extractNodes(ast, context) {
const nodes = [];
(0, traverse_1.default)(ast, {
enter: (path) => {
const node = path.node;
if (!node.loc)
return;
const astNode = {
id: this.generateId(),
type: node.type,
name: this.getNodeName(node),
startLine: node.loc.start.line,
endLine: node.loc.end.line,
startColumn: node.loc.start.column,
endColumn: node.loc.end.column,
filePath: context.filePath,
parent: path.parent ? this.generateId() : undefined,
children: [],
properties: this.extractNodeProperties(node)
};
nodes.push(astNode);
}
});
return nodes;
}
async extractFunctions(ast, context) {
const functions = [];
(0, traverse_1.default)(ast, {
FunctionDeclaration: (path) => {
const node = path.node;
if (!node.loc)
return;
const func = {
id: this.generateId(),
name: node.id?.name || 'anonymous',
parameters: this.extractParameters(node.params),
returnType: this.extractReturnType(node) || undefined,
complexity: this.calculateFunctionComplexity(path),
startLine: node.loc.start.line,
endLine: node.loc.end.line,
filePath: context.filePath,
isAsync: node.async || false,
isExported: this.isExported(path),
visibility: 'public',
dependencies: this.extractFunctionDependencies(path),
calls: this.extractFunctionCalls(path)
};
functions.push(func);
},
ArrowFunctionExpression: (path) => {
const node = path.node;
if (!node.loc)
return;
// Only include named arrow functions or those assigned to variables
const name = this.getArrowFunctionName(path);
if (!name)
return;
const func = {
id: this.generateId(),
name,
parameters: this.extractParameters(node.params),
returnType: this.extractReturnType(node) || undefined,
complexity: this.calculateFunctionComplexity(path),
startLine: node.loc.start.line,
endLine: node.loc.end.line,
filePath: context.filePath,
isAsync: node.async || false,
isExported: this.isExported(path),
visibility: 'public',
dependencies: this.extractFunctionDependencies(path),
calls: this.extractFunctionCalls(path)
};
functions.push(func);
},
ClassMethod: (path) => {
const node = path.node;
if (!node.loc || !t.isIdentifier(node.key))
return;
const func = {
id: this.generateId(),
name: node.key.name,
parameters: this.extractParameters(node.params || []),
returnType: this.extractReturnType(node) || undefined,
complexity: this.calculateFunctionComplexity(path),
startLine: node.loc.start.line,
endLine: node.loc.end.line,
filePath: context.filePath,
isAsync: node.async || false,
isExported: false,
visibility: this.getMethodVisibility(node),
dependencies: this.extractFunctionDependencies(path),
calls: this.extractFunctionCalls(path)
};
functions.push(func);
}
});
return functions;
}
async extractClasses(ast, context) {
const classes = [];
(0, traverse_1.default)(ast, {
ClassDeclaration: (path) => {
const node = path.node;
if (!node.loc)
return;
const cls = {
id: this.generateId(),
name: node.id?.name || 'anonymous',
methods: [],
properties: this.extractClassProperties(node),
extends: node.superClass && t.isIdentifier(node.superClass) ? node.superClass.name : undefined,
implements: this.extractImplements(node),
startLine: node.loc.start.line,
endLine: node.loc.end.line,
filePath: context.filePath,
isExported: this.isExported(path),
visibility: 'public',
isAbstract: this.isAbstractClass(node)
};
// Extract methods (handled separately in extractFunctions)
classes.push(cls);
}
});
return classes;
}
async extractImports(ast, context) {
const imports = [];
(0, traverse_1.default)(ast, {
ImportDeclaration: (path) => {
const node = path.node;
if (!node.loc)
return;
const importDecl = {
id: this.generateId(),
source: node.source.value,
imports: this.extractImportSpecifiers(node.specifiers),
filePath: context.filePath,
startLine: node.loc.start.line,
endLine: node.loc.end.line
};
imports.push(importDecl);
}
});
return imports;
}
async extractExports(ast, context) {
const exports = [];
(0, traverse_1.default)(ast, {
ExportNamedDeclaration: (path) => {
const node = path.node;
if (!node.loc)
return;
if (node.declaration) {
const exportDecl = this.createExportFromDeclaration(node.declaration, context, node.loc);
if (exportDecl)
exports.push(exportDecl);
}
// Handle export { name } from 'module'
if (node.specifiers) {
for (const spec of node.specifiers) {
if (t.isExportSpecifier(spec) && t.isIdentifier(spec.exported)) {
exports.push({
id: this.generateId(),
name: spec.exported.name,
type: 'variable',
isDefault: false,
filePath: context.filePath,
startLine: node.loc.start.line,
endLine: node.loc.end.line
});
}
}
}
},
ExportDefaultDeclaration: (path) => {
const node = path.node;
if (!node.loc)
return;
const name = this.getDefaultExportName(node.declaration);
exports.push({
id: this.generateId(),
name,
type: this.getExportType(node.declaration),
isDefault: true,
filePath: context.filePath,
startLine: node.loc.start.line,
endLine: node.loc.end.line
});
}
});
return exports;
}
async extractDependencies(ast, _context) {
const dependencies = new Set();
(0, traverse_1.default)(ast, {
ImportDeclaration: (path) => {
dependencies.add(path.node.source.value);
},
CallExpression: (path) => {
// Handle require() calls
if (t.isIdentifier(path.node.callee) && path.node.callee.name === 'require') {
const arg = path.node.arguments[0];
if (t.isStringLiteral(arg)) {
dependencies.add(arg.value);
}
}
}
});
return Array.from(dependencies);
}
async calculateComplexity(ast, _context) {
let cyclomaticComplexity = 1; // Base complexity
let cognitiveComplexity = 0;
let linesOfCode = 0;
(0, traverse_1.default)(ast, {
enter: (path) => {
const node = path.node;
// Cyclomatic complexity
if (this.isComplexityNode(node)) {
cyclomaticComplexity++;
}
// Cognitive complexity (simplified)
if (this.isCognitiveComplexityNode(node)) {
cognitiveComplexity += this.getCognitiveComplexityWeight(node, path.getFunctionParent()?.node);
}
// Lines of code
if (node.loc) {
linesOfCode = Math.max(linesOfCode, node.loc.end.line);
}
}
});
const maintainabilityIndex = Math.max(0, 171 - 5.2 * Math.log(linesOfCode) - 0.23 * cyclomaticComplexity);
const technicalDebt = Math.max(0, (cyclomaticComplexity - 10) * 0.5 + (cognitiveComplexity - 15) * 0.3);
return {
cyclomaticComplexity,
cognitiveComplexity,
linesOfCode,
maintainabilityIndex,
technicalDebt
};
}
getParserConfig() {
return {
language: 'typescript',
sourceType: 'module',
allowImportExportEverywhere: true,
allowReturnOutsideFunction: false,
strictMode: false,
plugins: [
'typescript',
'jsx',
'decorators-legacy',
'classProperties',
'objectRestSpread',
'functionBind',
'exportDefaultFrom',
'exportNamespaceFrom',
'dynamicImport',
'nullishCoalescingOperator',
'optionalChaining'
]
};
}
// Helper methods
getNodeName(node) {
if (t.isIdentifier(node))
return node.name;
if (t.isFunctionDeclaration(node) && node.id)
return node.id.name;
if (t.isClassDeclaration(node) && node.id)
return node.id.name;
if (t.isVariableDeclarator(node) && t.isIdentifier(node.id))
return node.id.name;
return undefined;
}
extractNodeProperties(node) {
const properties = {};
if (t.isFunction(node)) {
properties.async = node.async;
properties.generator = node.generator;
}
if (t.isClass(node)) {
properties.abstract = node.abstract;
}
return properties;
}
extractParameters(params) {
return params.map(param => {
if (t.isIdentifier(param)) {
return {
name: param.name,
type: param.typeAnnotation ? this.extractTypeAnnotation(param.typeAnnotation) : undefined,
optional: false,
defaultValue: undefined
};
}
if (t.isAssignmentPattern(param) && t.isIdentifier(param.left)) {
return {
name: param.left.name,
type: param.left.typeAnnotation ? this.extractTypeAnnotation(param.left.typeAnnotation) : undefined,
optional: true,
defaultValue: this.extractDefaultValue(param.right)
};
}
return {
name: 'unknown',
type: undefined,
optional: false,
defaultValue: undefined
};
});
}
extractReturnType(node) {
if (node.returnType) {
return this.extractTypeAnnotation(node.returnType);
}
return undefined;
}
extractTypeAnnotation(typeAnnotation) {
// Extract TypeScript type annotations from AST nodes
if (typeAnnotation.typeAnnotation) {
const type = typeAnnotation.typeAnnotation;
if (t.isTSStringKeyword(type))
return 'string';
if (t.isTSNumberKeyword(type))
return 'number';
if (t.isTSBooleanKeyword(type))
return 'boolean';
if (t.isTSTypeReference(type) && t.isIdentifier(type.typeName)) {
return type.typeName.name;
}
}
return 'any';
}
extractDefaultValue(node) {
if (t.isStringLiteral(node))
return `"${node.value}"`;
if (t.isNumericLiteral(node))
return node.value.toString();
if (t.isBooleanLiteral(node))
return node.value.toString();
if (t.isNullLiteral(node))
return 'null';
return 'unknown';
}
calculateFunctionComplexity(path) {
let complexity = 1;
path.traverse({
IfStatement: () => { complexity++; },
ConditionalExpression: () => { complexity++; },
LogicalExpression: () => { complexity++; },
SwitchCase: () => { complexity++; },
WhileStatement: () => { complexity++; },
DoWhileStatement: () => { complexity++; },
ForStatement: () => { complexity++; },
ForInStatement: () => { complexity++; },
ForOfStatement: () => { complexity++; }
});
return complexity;
}
isExported(path) {
let currentPath = path;
const visited = new Set();
while (currentPath && currentPath.parent) {
// Prevent infinite loops
const pathKey = currentPath.toString();
if (visited.has(pathKey)) {
break;
}
visited.add(pathKey);
if (t.isExportNamedDeclaration(currentPath.parent) || t.isExportDefaultDeclaration(currentPath.parent)) {
return true;
}
currentPath = currentPath.parentPath;
}
return false;
}
getMethodVisibility(node) {
// TypeScript specific visibility
if (node.accessibility) {
return node.accessibility;
}
return 'public';
}
getArrowFunctionName(path) {
const parent = path.parent;
if (t.isVariableDeclarator(parent) && t.isIdentifier(parent.id)) {
return parent.id.name;
}
if (t.isAssignmentExpression(parent) && t.isIdentifier(parent.left)) {
return parent.left.name;
}
return undefined;
}
extractClassProperties(node) {
const properties = [];
if (node.body && node.body.body) {
for (const member of node.body.body) {
if (t.isClassProperty(member) && t.isIdentifier(member.key)) {
properties.push({
name: member.key.name,
type: member.typeAnnotation ? this.extractTypeAnnotation(member.typeAnnotation) : undefined,
visibility: member.accessibility || 'public',
isStatic: member.static || false,
isReadonly: member.readonly || false,
defaultValue: member.value ? this.extractDefaultValue(member.value) : undefined
});
}
}
}
return properties;
}
extractImplements(node) {
if (node.implements) {
return node.implements.map((impl) => {
if (t.isIdentifier(impl.id))
return impl.id.name;
return 'unknown';
});
}
return [];
}
isAbstractClass(node) {
return node.abstract || false;
}
extractImportSpecifiers(specifiers) {
return specifiers.map(spec => {
if (t.isImportDefaultSpecifier(spec)) {
return {
name: spec.local.name,
alias: undefined,
isDefault: true,
isNamespace: false
};
}
if (t.isImportNamespaceSpecifier(spec)) {
return {
name: spec.local.name,
alias: undefined,
isDefault: false,
isNamespace: true
};
}
if (t.isImportSpecifier(spec)) {
const importedName = t.isIdentifier(spec.imported) ? spec.imported.name : spec.imported.value;
return {
name: importedName,
alias: spec.local.name !== importedName ? spec.local.name : undefined,
isDefault: false,
isNamespace: false
};
}
return {
name: 'unknown',
alias: undefined,
isDefault: false,
isNamespace: false
};
});
}
createExportFromDeclaration(declaration, context, loc) {
if (t.isFunctionDeclaration(declaration) && declaration.id) {
return {
id: this.generateId(),
name: declaration.id.name,
type: 'function',
isDefault: false,
filePath: context.filePath,
startLine: loc.start.line,
endLine: loc.end.line
};
}
if (t.isClassDeclaration(declaration) && declaration.id) {
return {
id: this.generateId(),
name: declaration.id.name,
type: 'class',
isDefault: false,
filePath: context.filePath,
startLine: loc.start.line,
endLine: loc.end.line
};
}
return null;
}
getDefaultExportName(declaration) {
if (t.isIdentifier(declaration))
return declaration.name;
if (t.isFunctionDeclaration(declaration) && declaration.id)
return declaration.id.name;
if (t.isClassDeclaration(declaration) && declaration.id)
return declaration.id.name;
return 'default';
}
getExportType(declaration) {
if (t.isFunction(declaration))
return 'function';
if (t.isClass(declaration))
return 'class';
if (t.isTSInterfaceDeclaration(declaration))
return 'interface';
if (t.isTSTypeAliasDeclaration(declaration))
return 'type';
return 'variable';
}
extractFunctionDependencies(path) {
const dependencies = new Set();
path.traverse({
Identifier: (identifierPath) => {
if (identifierPath.isReferencedIdentifier()) {
dependencies.add(identifierPath.node.name);
}
}
});
return Array.from(dependencies);
}
extractFunctionCalls(path) {
const calls = new Set();
path.traverse({
CallExpression: (callPath) => {
const callee = callPath.node.callee;
if (t.isIdentifier(callee)) {
calls.add(callee.name);
}
else if (t.isMemberExpression(callee) && t.isIdentifier(callee.property)) {
calls.add(callee.property.name);
}
}
});
return Array.from(calls);
}
isComplexityNode(node) {
return t.isIfStatement(node) ||
t.isConditionalExpression(node) ||
t.isLogicalExpression(node) ||
t.isSwitchCase(node) ||
t.isWhileStatement(node) ||
t.isDoWhileStatement(node) ||
t.isForStatement(node) ||
t.isForInStatement(node) ||
t.isForOfStatement(node);
}
isCognitiveComplexityNode(node) {
return this.isComplexityNode(node) ||
t.isTryStatement(node) ||
t.isCatchClause(node);
}
getCognitiveComplexityWeight(node, _functionParent) {
// Calculate cognitive complexity weight based on node type
if (t.isIfStatement(node) || t.isConditionalExpression(node))
return 1;
if (t.isLogicalExpression(node))
return 1;
if (t.isSwitchCase(node))
return 1;
if (t.isWhileStatement(node) || t.isDoWhileStatement(node))
return 1;
if (t.isForStatement(node) || t.isForInStatement(node) || t.isForOfStatement(node))
return 1;
if (t.isTryStatement(node) || t.isCatchClause(node))
return 1;
return 0;
}
}
exports.TypeScriptAnalyzer = TypeScriptAnalyzer;
//# sourceMappingURL=TypeScriptAnalyzer.js.map