@opichi/smartcode
Version:
Universal code intelligence MCP server - analyze any codebase with TypeScript excellence and multi-language support
198 lines • 7.17 kB
JavaScript
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