UNPKG

@opichi/smartcode

Version:

Universal code intelligence MCP server - analyze any codebase with TypeScript excellence and multi-language support

198 lines 7.17 kB
import Parser from 'tree-sitter'; import JavaScript from 'tree-sitter-javascript'; import TypeScript from 'tree-sitter-typescript'; import Python from 'tree-sitter-python'; import Ruby from 'tree-sitter-ruby'; import { readFileSync } from 'fs'; export class CodeParser { parsers = new Map(); constructor() { this.initializeParsers(); } initializeParsers() { // JavaScript/JSX const jsParser = new Parser(); jsParser.setLanguage(JavaScript); this.parsers.set('js', jsParser); this.parsers.set('jsx', jsParser); this.parsers.set('mjs', jsParser); // TypeScript/TSX const tsParser = new Parser(); tsParser.setLanguage(TypeScript.typescript); this.parsers.set('ts', tsParser); const tsxParser = new Parser(); tsxParser.setLanguage(TypeScript.tsx); this.parsers.set('tsx', tsxParser); // Python const pyParser = new Parser(); pyParser.setLanguage(Python); this.parsers.set('py', pyParser); // Ruby const rbParser = new Parser(); rbParser.setLanguage(Ruby); this.parsers.set('rb', rbParser); } parseFile(filePath) { const extension = filePath.split('.').pop()?.toLowerCase(); if (!extension || !this.parsers.has(extension)) { return []; } try { const content = readFileSync(filePath, 'utf8'); const parser = this.parsers.get(extension); const tree = parser.parse(content); return this.extractNodes(tree.rootNode, filePath, content); } catch (error) { console.error(`Error parsing ${filePath}:`, error); return []; } } extractNodes(node, filePath, content) { const nodes = []; const lines = content.split('\n'); // Extract different types of nodes based on syntax this.traverseNode(node, (child) => { const extracted = this.extractNodeInfo(child, filePath, lines); if (extracted) { nodes.push(extracted); } }); return nodes; } extractNodeInfo(node, filePath, lines) { const nodeType = node.type; const startLine = node.startPosition.row + 1; const endLine = node.endPosition.row + 1; // Get the content of this node const nodeLines = lines.slice(node.startPosition.row, node.endPosition.row + 1); const content = nodeLines.join('\n'); let name = ''; let type = null; const metadata = {}; switch (nodeType) { case 'class_declaration': case 'class_definition': type = 'class'; name = this.extractName(node, 'class_name') || this.extractName(node, 'identifier') || 'UnnamedClass'; metadata.methods = this.extractMethods(node); break; case 'function_declaration': case 'function_definition': case 'method_definition': case 'arrow_function': type = 'function'; name = this.extractName(node, 'function_name') || this.extractName(node, 'identifier') || 'anonymous'; metadata.parameters = this.extractParameters(node); metadata.returnType = this.extractReturnType(node); break; case 'variable_declaration': case 'lexical_declaration': case 'assignment_expression': type = 'variable'; name = this.extractVariableName(node); if (!name) return null; break; case 'import_statement': case 'import_declaration': type = 'import'; name = this.extractImportName(node); metadata.source = this.extractImportSource(node); break; case 'export_statement': case 'export_declaration': type = 'export'; name = this.extractExportName(node); break; default: return null; } if (!type || !name) return null; return { id: `${filePath}:${startLine}:${name}`, type, name, filePath, startLine, endLine, content, metadata }; } traverseNode(node, callback) { callback(node); for (let i = 0; i < node.childCount; i++) { this.traverseNode(node.child(i), callback); } } extractName(node, nameType) { for (let i = 0; i < node.childCount; i++) { const child = node.child(i); if (child.type === nameType || child.type === 'identifier') { return child.text; } } return null; } extractMethods(classNode) { const methods = []; this.traverseNode(classNode, (node) => { if (node.type === 'method_definition' || node.type === 'function_declaration') { const name = this.extractName(node, 'identifier'); if (name) methods.push(name); } }); return methods; } extractParameters(funcNode) { const params = []; this.traverseNode(funcNode, (node) => { if (node.type === 'formal_parameters' || node.type === 'parameters') { this.traverseNode(node, (paramNode) => { if (paramNode.type === 'identifier') { params.push(paramNode.text); } }); } }); return params; } extractReturnType(funcNode) { // This is language-specific and would need more sophisticated parsing return null; } extractVariableName(node) { // Extract variable name from various declaration types for (let i = 0; i < node.childCount; i++) { const child = node.child(i); if (child.type === 'variable_declarator' || child.type === 'identifier') { return this.extractName(child, 'identifier') || child.text; } } return null; } extractImportName(node) { // Extract what's being imported const importClause = node.children.find(child => child.type === 'import_clause' || child.type === 'import_specifier'); if (importClause) { return importClause.text; } return node.text.split(' ')[1] || 'unknown'; } extractImportSource(node) { // Extract the source module const stringLiteral = node.children.find(child => child.type === 'string' || child.type === 'string_literal'); if (stringLiteral) { return stringLiteral.text.replace(/['"]/g, ''); } return null; } extractExportName(node) { // Extract what's being exported return node.text.split(' ')[1] || 'default'; } } //# sourceMappingURL=parser.js.map