UNPKG

@aaswe/codebase-ai

Version:

AI-Assisted Software Engineering (AASWE) - Rich codebase context for IDE LLMs

642 lines 24.4 kB
"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