agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
217 lines (211 loc) • 8.47 kB
JavaScript
/**
* @file Core AST manipulation utilities for JavaScript code analysis
* @description Single responsibility: Provide fundamental AST operations across AgentSqripts analyzers
*
* This utility module provides essential Abstract Syntax Tree (AST) operations including
* robust parsing, node traversal, function extraction, and type checking. It serves as
* the foundation for all AST-based analysis in the platform, handling JavaScript parsing
* complexity while providing a simple, consistent interface.
*
* Design rationale:
* - Acorn parser provides fast, standards-compliant JavaScript parsing
* - Graceful error handling enables analysis to continue with partial results
* - Simple traversal utilities abstract away AST complexity
* - Type-safe node operations prevent runtime errors in analyzers
*/
const acorn = require('acorn');
const walk = require('acorn-walk');
/**
* Parse JavaScript content into AST with comprehensive error handling
*
* Technical function: Converts JavaScript source code into traversable AST structure
*
* Implementation rationale:
* - Latest ECMAScript version support enables modern JavaScript analysis
* - Module source type handles both ES6 modules and CommonJS patterns
* - allowReturnOutsideFunction accommodates real-world code variations
* - Silent error handling prevents analysis pipeline failures
*
* Parser configuration strategy:
* - ecmaVersion: 'latest' supports newest JavaScript features
* - sourceType: 'module' handles import/export statements
* - allowReturnOutsideFunction: Tolerates non-strict code patterns
* - Options spread enables specialized parser configuration
*
* Error handling approach:
* - Null return enables graceful degradation in analyzers
* - Silent errors prevent console noise during batch analysis
* - Partial analysis possible when some files fail to parse
* - Error patterns indicate syntax issues without breaking workflow
*
* AST parsing challenges:
* - Syntax errors in real-world codebases
* - Mixed module systems (ES6 + CommonJS)
* - Non-standard JavaScript extensions (JSX, TypeScript)
* - Dynamic code generation and eval patterns
*
* Performance considerations:
* - Acorn is optimized for parsing speed over error recovery
* - Single-pass parsing suitable for static analysis workflows
* - Memory usage scales linearly with source code size
* - Consider streaming parsers for extremely large files
*
* @param {string} content - JavaScript source code to parse into AST
* @param {Object} options - Additional parser options to override defaults
* @returns {Object|null} Parsed AST object, or null if parsing fails
* @example
* const ast = parseAST('function hello() { return "world"; }');
* if (ast) {
* // Process AST successfully
* } else {
* // Handle parsing failure gracefully
* }
*/
function parseAST(content, options = {}) {
try {
return acorn.parse(content, {
ecmaVersion: 'latest',
sourceType: 'module',
allowReturnOutsideFunction: true,
...options
});
} catch (error) {
// Silently handle parse errors
return null;
}
}
/**
* Collect all AST nodes of specific type with efficient traversal
*
* Technical function: Type-specific node collection using optimized AST walker
*
* Implementation rationale:
* - acorn-walk provides efficient tree traversal without manual recursion
* - Dynamic property access enables type-based node filtering
* - Array accumulation maintains discovery order for analysis
* - Simple interface abstracts traversal complexity from analyzers
*
* Traversal optimization:
* - walk.simple() optimized for single-purpose node collection
* - No unnecessary node visits for uninteresting types
* - Early termination possible with custom walker extensions
* - Memory-efficient accumulation without intermediate structures
*
* Node type applications:
* - Variable declarations: Identify data flow patterns
* - Function calls: Map dependency relationships
* - Import statements: Analyze module dependencies
* - Loop constructs: Detect performance patterns
*
* Collection strategy:
* - Dynamic object key enables runtime type specification
* - Array push maintains temporal order of node discovery
* - Shallow copy of nodes enables safe post-processing
* - No filtering applied - pure collection for flexibility
*
* @param {Object} ast - Root AST node to traverse for node collection
* @param {string} nodeType - AST node type to collect (e.g., 'FunctionDeclaration')
* @returns {Array<Object>} Array of collected AST nodes of specified type
* @example
* const functions = collectNodes(ast, 'FunctionDeclaration');
* const loops = collectNodes(ast, 'ForStatement');
* const imports = collectNodes(ast, 'ImportDeclaration');
*/
function collectNodes(ast, nodeType) {
const nodes = [];
walk.simple(ast, {
[nodeType]: (node) => nodes.push(node)
});
return nodes;
}
/**
* Extract all function definitions with comprehensive function type support
*
* Technical function: Specialized function node collection covering all JavaScript function forms
*
* Implementation rationale:
* - Multiple function types require comprehensive collection strategy
* - Single traversal more efficient than multiple type-specific calls
* - Function analysis foundational for complexity and dependency analysis
* - Unified collection simplifies function-based analysis workflows
*
* Function type coverage:
* - FunctionDeclaration: Traditional named functions
* - FunctionExpression: Anonymous and named function expressions
* - ArrowFunctionExpression: ES6 arrow functions
* - Method definitions and class methods handled by broader analysis
*
* Function analysis applications:
* - Cyclomatic complexity calculation
* - Function size and parameter analysis
* - Call graph construction for dependency mapping
* - Performance hotspot identification
*
* Collection completeness:
* - Covers all standard JavaScript function forms
* - Handles nested functions and closures
* - Maintains function hierarchy through AST structure
* - Enables both flat and hierarchical function analysis
*
* @param {Object} ast - Root AST node to extract functions from
* @returns {Array<Object>} Array of all function nodes found in AST
* @example
* const functions = extractFunctions(ast);
* functions.forEach(fn => {
* console.log(`Function: ${fn.id?.name || 'anonymous'}`);
* console.log(`Parameters: ${fn.params.length}`);
* });
*/
function extractFunctions(ast) {
const functions = [];
walk.simple(ast, {
FunctionDeclaration: (node) => functions.push(node),
FunctionExpression: (node) => functions.push(node),
ArrowFunctionExpression: (node) => functions.push(node)
});
return functions;
}
/**
* Type-safe AST node checking with multiple type support
*
* Technical function: Robust node type validation with flexible input handling
*
* Implementation rationale:
* - Null-safe checking prevents runtime errors on malformed ASTs
* - Multiple type support enables flexible node classification
* - Array normalization provides consistent interface
* - Type checking foundational for safe AST analysis
*
* Type checking strategy:
* - Single type passed as string for simple checks
* - Multiple types passed as array for union type checking
* - Array.includes() provides O(n) lookup sufficient for small type sets
* - Null checking prevents errors on undefined/null nodes
*
* Node type safety:
* - Prevents accessing properties on undefined nodes
* - Enables confident type-based property access
* - Supports conditional processing based on node types
* - Foundational for building type-safe AST analyzers
*
* @param {Object} node - AST node to check type of (may be null/undefined)
* @param {string|Array<string>} types - Single type or array of types to check against
* @returns {boolean} True if node exists and matches any of the specified types
* @example
* if (isNodeType(node, 'FunctionDeclaration')) {
* // Safe to access function-specific properties
* }
* if (isNodeType(node, ['CallExpression', 'NewExpression'])) {
* // Handle both call types
* }
*/
function isNodeType(node, types) {
const typeArray = Array.isArray(types) ? types : [types];
return node && typeArray.includes(node.type);
}
module.exports = {
parseAST,
collectNodes,
extractFunctions,
isNodeType
};