UNPKG

agentsqripts

Version:

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

156 lines (138 loc) 3.88 kB
/** * @file AST Parser utility * @description Common AST parsing functionality for performance analyzers */ const { parseAST } = require('../../utils/astHelpers'); const walk = require('acorn-walk'); /** * Parse JavaScript content to AST * @param {string} content - JavaScript content * @param {string} filePath - File path for error reporting * @returns {Object|null} AST object or null if parsing fails */ function parseToAST(content, filePath) { try { // Try parsing as ES2020 first return parseAST(content, { ecmaVersion: 2020, allowHashBang: true, locations: true }); } catch (moduleError) { try { // Fallback to script mode for CommonJS return parseAST(content, { ecmaVersion: 2020, sourceType: 'script', allowHashBang: true, locations: true }); } catch (scriptError) { try { // Try with even more permissive settings return parseAST(content, { ecmaVersion: 'latest', sourceType: 'module', allowHashBang: true, allowReturnOutsideFunction: true, allowImportExportEverywhere: true, locations: true }); } catch (finalError) { console.error(`Failed to parse ${filePath}: ${finalError.message}`); throw new Error(`Unable to parse ${filePath} as JavaScript: ${finalError.message}`); } } } } /** * Check if file can be parsed as JavaScript * @param {string} filePath - File path * @returns {boolean} True if file can be parsed */ function canParseAsJS(filePath) { return /\.(js|jsx|ts|tsx|mjs|cjs)$/.test(filePath); } /** * Get the line number from an AST node * @param {Object} node - AST node * @returns {number} Line number or 0 if not found */ function getNodeLine(node) { if (!node) return 0; // Check for loc property (most common) if (node.loc && node.loc.start && typeof node.loc.start.line === 'number') { return node.loc.start.line; } // Check for line property directly if (typeof node.line === 'number') { return node.line; } // Check for start property if (typeof node.start === 'number') { // For acorn, we might need to calculate line from position // This is a fallback - ideally loc should be available return 1; // Default to line 1 if we only have position } return 0; } /** * Check if a node is inside a loop * @param {Object} node - Current AST node * @param {Array} ancestors - Array of ancestor nodes * @returns {boolean} True if node is inside a loop */ function isInsideLoop(node, ancestors = []) { if (!ancestors || !Array.isArray(ancestors)) { return false; } const loopTypes = [ 'ForStatement', 'ForInStatement', 'ForOfStatement', 'WhileStatement', 'DoWhileStatement' ]; // Check if any ancestor is a loop return ancestors.some(ancestor => ancestor && loopTypes.includes(ancestor.type) ); } /** * Get code snippet from a node * @param {Object} node - AST node * @param {string} source - Source code * @returns {string} Code snippet */ function getNodeSource(node, source) { if (!node || !source) return ''; if (typeof node.start === 'number' && typeof node.end === 'number') { return source.substring(node.start, node.end); } return ''; } /** * Collect nodes of specific types * @param {Object} ast - AST tree * @param {Array} nodeTypes - Array of node types to collect * @returns {Array} Array of collected nodes */ function collectNodes(ast, nodeTypes) { const nodes = []; walk.simple(ast, { ...nodeTypes.reduce((acc, type) => { acc[type] = (node) => nodes.push(node); return acc; }, {}) }); return nodes; } module.exports = { parseToAST, walk, canParseAsJS, getNodeLine, isInsideLoop, getNodeSource, collectNodes };