UNPKG

ripple-ai-detector

Version:

🌊 Ripple AI Bug Detector - Built by an AI that knows its flaws. Catch AI-generated bugs before you commit.

286 lines • 10.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ASTParser = void 0; const tree_sitter_1 = __importDefault(require("tree-sitter")); const tree_sitter_javascript_1 = __importDefault(require("tree-sitter-javascript")); const tree_sitter_typescript_1 = __importDefault(require("tree-sitter-typescript")); class ASTParser { jsParser; tsParser; constructor() { // Initialize JavaScript parser this.jsParser = new tree_sitter_1.default(); this.jsParser.setLanguage(tree_sitter_javascript_1.default); // Initialize TypeScript parser this.tsParser = new tree_sitter_1.default(); this.tsParser.setLanguage(tree_sitter_typescript_1.default.typescript); } // Parse file and extract AST parseFile(content, isTypeScript = false) { const parser = isTypeScript ? this.tsParser : this.jsParser; return parser.parse(content); } // Extract all function definitions from AST extractFunctions(tree, filePath) { const functions = []; const traverse = (node) => { // Function declarations: function myFunc() {} if (node.type === 'function_declaration') { const func = this.parseFunctionDeclaration(node, filePath); if (func) functions.push(func); } // Arrow functions: const myFunc = () => {} if (node.type === 'variable_declarator') { const func = this.parseArrowFunction(node, filePath); if (func) functions.push(func); } // Method definitions: class methods if (node.type === 'method_definition') { const func = this.parseMethodDefinition(node, filePath); if (func) functions.push(func); } // Recursively traverse children for (const child of node.children) { traverse(child); } }; traverse(tree.rootNode); return functions; } // Extract function calls from AST extractFunctionCalls(tree, filePath) { const calls = []; const traverse = (node) => { if (node.type === 'call_expression') { const call = this.parseFunctionCall(node, filePath); if (call) calls.push(call); } for (const child of node.children) { traverse(child); } }; traverse(tree.rootNode); return calls; } // Extract imports from AST extractImports(tree, filePath) { const imports = []; const traverse = (node) => { if (node.type === 'import_statement') { const importInfo = this.parseImportStatement(node, filePath); if (importInfo) imports.push(...importInfo); } for (const child of node.children) { traverse(child); } }; traverse(tree.rootNode); return imports; } // Extract exports from AST extractExports(tree, filePath) { const exports = []; const traverse = (node) => { if (node.type === 'export_statement') { const exportInfo = this.parseExportStatement(node, filePath); if (exportInfo) exports.push(...exportInfo); } for (const child of node.children) { traverse(child); } }; traverse(tree.rootNode); return exports; } // Parse function declaration parseFunctionDeclaration(node, filePath) { const nameNode = node.childForFieldName('name'); const parametersNode = node.childForFieldName('parameters'); if (!nameNode || !parametersNode) return null; const name = nameNode.text; const parameters = this.parseParameters(parametersNode); const isAsync = node.text.startsWith('async '); return { name, parameters, file: filePath, line: node.startPosition.row + 1, column: node.startPosition.column + 1, isExported: this.isExported(node), isAsync, isArrow: false }; } // Parse arrow function parseArrowFunction(node, filePath) { const nameNode = node.childForFieldName('name'); const valueNode = node.childForFieldName('value'); if (!nameNode || !valueNode || valueNode.type !== 'arrow_function') return null; const name = nameNode.text; const parametersNode = valueNode.childForFieldName('parameters'); const parameters = parametersNode ? this.parseParameters(parametersNode) : []; const isAsync = valueNode.text.startsWith('async '); return { name, parameters, file: filePath, line: node.startPosition.row + 1, column: node.startPosition.column + 1, isExported: this.isExported(node.parent), isAsync, isArrow: true }; } // Parse method definition parseMethodDefinition(node, filePath) { const nameNode = node.childForFieldName('name'); const parametersNode = node.childForFieldName('parameters'); if (!nameNode || !parametersNode) return null; const name = nameNode.text; const parameters = this.parseParameters(parametersNode); const isAsync = node.text.includes('async '); return { name, parameters, file: filePath, line: node.startPosition.row + 1, column: node.startPosition.column + 1, isExported: true, // Methods are accessible if class is exported isAsync, isArrow: false }; } // Parse function parameters parseParameters(parametersNode) { const parameters = []; for (const child of parametersNode.children) { if (child.type === 'required_parameter' || child.type === 'optional_parameter') { const param = this.parseParameter(child); if (param) parameters.push(param); } } return parameters; } // Parse individual parameter parseParameter(node) { const nameNode = node.childForFieldName('pattern') || node.children[0]; if (!nameNode) return null; const name = nameNode.text; const optional = node.type === 'optional_parameter' || name.includes('?'); // Extract type annotation if present let type; const typeNode = node.childForFieldName('type'); if (typeNode) { type = typeNode.text; } // Extract default value if present let defaultValue; const defaultNode = node.childForFieldName('value'); if (defaultNode) { defaultValue = defaultNode.text; } return { name: name.replace('?', ''), // Remove optional marker type, optional, defaultValue }; } // Parse function call parseFunctionCall(node, filePath) { const functionNode = node.childForFieldName('function'); if (!functionNode) return null; let name = ''; // Handle different call patterns if (functionNode.type === 'identifier') { name = functionNode.text; } else if (functionNode.type === 'member_expression') { const propertyNode = functionNode.childForFieldName('property'); if (propertyNode) { name = propertyNode.text; } } if (!name) return null; // Get surrounding context (the line containing the call) const context = this.getLineContext(node, filePath); return { name, file: filePath, line: node.startPosition.row + 1, column: node.startPosition.column + 1, context }; } // Parse import statement parseImportStatement(node, filePath) { const imports = []; const sourceNode = node.childForFieldName('source'); if (!sourceNode) return imports; const modulePath = sourceNode.text.replace(/['"]/g, ''); // Handle different import patterns for (const child of node.children) { if (child.type === 'import_specifier') { const nameNode = child.childForFieldName('name'); if (nameNode) { imports.push({ importName: nameNode.text, modulePath, isDefault: false, isNamespace: false, file: filePath, line: node.startPosition.row + 1, column: node.startPosition.column + 1 }); } } } return imports; } // Parse export statement parseExportStatement(node, filePath) { const exports = []; // This is a simplified implementation // In a full implementation, we'd handle all export patterns return exports; } // Check if node is exported isExported(node) { if (!node) return false; // Check if parent is export statement let current = node.parent; while (current) { if (current.type === 'export_statement') { return true; } current = current.parent; } return false; } // Get line context for error reporting getLineContext(node, filePath) { // This would extract the full line containing the node // For now, return the node text return node.text; } } exports.ASTParser = ASTParser; //# sourceMappingURL=ast-parser.js.map