UNPKG

agentsqripts

Version:

Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems

140 lines (119 loc) 4.4 kB
/** * @file AST-based code analyzer * @description Uses AST parsing for accurate code analysis */ /** * Count real exports using AST or fallback to regex * @param {string} content - Code content * @returns {number} Number of real exports */ function countRealExports(content) { try { // Try using acorn for JavaScript parsing const { parseAST } = require('../../utils/astHelpers'); const ast = parseAST(content, { allowImportExportEverywhere: true }); let exportCount = 0; // Walk the AST to count exports function walk(node) { if (!node) return; if (node.type === 'ExportNamedDeclaration' || node.type === 'ExportDefaultDeclaration' || node.type === 'ExportAllDeclaration') { exportCount++; } // Recursively walk child nodes for (const key in node) { if (node[key] && typeof node[key] === 'object') { if (Array.isArray(node[key])) { node[key].forEach(walk); } else if (node[key].type) { walk(node[key]); } } } } walk(ast); return exportCount; } catch (error) { // Fallback to regex if parsing fails - count all types of exports const exportMatches = content.match(/export\s+(default\s+|const\s+|let\s+|var\s+|function\s+|class\s+|async\s+function\s+|\{)/g) || []; return exportMatches.length; } } /** * Count real functions using AST or fallback to regex * @param {string} content - Code content * @returns {number} Number of real functions */ function countRealFunctions(content) { try { const acorn = require('acorn'); const ast = acorn.parse(content, { sourceType: 'module', ecmaVersion: 'latest', allowReturnOutsideFunction: true, allowImportExportEverywhere: true }); let functionCount = 0; let inCallExpression = false; function walk(node, parent) { if (!node) return; // Track if we're inside a call expression (like forEach, map, filter) const wasInCallExpression = inCallExpression; if (node.type === 'CallExpression') { inCallExpression = true; } // Count top-level and meaningful functions, not simple callbacks if (node.type === 'FunctionDeclaration') { functionCount++; } else if ((node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression')) { // Skip counting if this is a callback to common array methods const isArrayCallback = parent && parent.type === 'CallExpression' && parent.callee && parent.callee.property && ['forEach', 'map', 'filter', 'reduce', 'find', 'some', 'every', 'sort'].includes(parent.callee.property.name); // Skip counting if this is a simple one-liner arrow function in array methods const isSimpleCallback = node.type === 'ArrowFunctionExpression' && !node.body.body && // No block body (single expression) inCallExpression; if (!isArrayCallback && !isSimpleCallback) { functionCount++; } } // Count method definitions (e.g., in classes) if (node.type === 'MethodDefinition') { functionCount++; } // Count function properties in object expressions (like module.exports object) if (node.type === 'Property' && node.value && (node.value.type === 'FunctionExpression' || node.value.type === 'ArrowFunctionExpression')) { // Only count if not in a call expression context if (!inCallExpression) { functionCount++; } } for (const key in node) { if (node[key] && typeof node[key] === 'object') { if (Array.isArray(node[key])) { node[key].forEach(child => walk(child, node)); } else if (node[key].type) { walk(node[key], node); } } } inCallExpression = wasInCallExpression; } walk(ast, null); return functionCount; } catch (error) { // Fallback to regex const functionMatches = content.match(/function\s+\w+|const\s+\w+\s*=\s*\(|let\s+\w+\s*=\s*\(|var\s+\w+\s*=\s*\(/g) || []; return functionMatches.length; } } module.exports = { countRealExports, countRealFunctions };