Skip to main content
Glama
MUSE-CODE-SPACE

Vibe Coding Documentation MCP (MUSE)

astParser.js12.1 kB
/** * AST 기반 코드 분석 유틸리티 * 함수, 클래스, 의존성을 자동으로 추출 */ import { memoize, hashCode } from '../core/cache.js'; // TypeScript/JavaScript 분석 export function analyzeTypeScript(code) { const functions = []; const classes = []; const imports = []; const exports = []; const dependencies = []; const lines = code.split('\n'); // Import 분석 const importRegex = /import\s+(?:(\w+)\s*,?\s*)?(?:\{([^}]+)\})?\s*(?:from\s+)?['"]([^'"]+)['"]/g; const importTypeRegex = /import\s+type\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/g; let match; while ((match = importRegex.exec(code)) !== null) { const defaultImport = match[1]; const namedImports = match[2]?.split(',').map(s => s.trim().split(' as ')[0].trim()).filter(Boolean) || []; const source = match[3]; imports.push({ source, imports: defaultImport ? [defaultImport, ...namedImports] : namedImports, isDefault: !!defaultImport, isNamespace: false }); if (!source.startsWith('.') && !source.startsWith('@/')) { dependencies.push(source.split('/')[0]); } } // 함수 분석 const functionRegex = /(?:(export)\s+)?(?:(async)\s+)?function\s+(\w+)\s*(?:<[^>]+>)?\s*\(([^)]*)\)(?:\s*:\s*([^\s{]+))?\s*\{/g; const arrowFunctionRegex = /(?:(export)\s+)?(?:const|let|var)\s+(\w+)\s*(?::\s*[^=]+)?\s*=\s*(?:(async)\s+)?\([^)]*\)\s*(?::\s*([^\s=]+))?\s*=>/g; while ((match = functionRegex.exec(code)) !== null) { const lineNumber = code.substring(0, match.index).split('\n').length; functions.push({ name: match[3], params: match[4].split(',').map(p => p.trim().split(':')[0].trim()).filter(Boolean), returnType: match[5], async: !!match[2], exported: !!match[1], lineNumber }); } while ((match = arrowFunctionRegex.exec(code)) !== null) { const lineNumber = code.substring(0, match.index).split('\n').length; functions.push({ name: match[2], params: [], returnType: match[4], async: !!match[3], exported: !!match[1], lineNumber }); } // 클래스 분석 const classRegex = /(?:(export)\s+)?class\s+(\w+)(?:\s+extends\s+(\w+))?(?:\s+implements\s+([^{]+))?\s*\{/g; while ((match = classRegex.exec(code)) !== null) { const className = match[2]; const lineNumber = code.substring(0, match.index).split('\n').length; // 클래스 본문 추출 const classStart = match.index + match[0].length; let braceCount = 1; let classEnd = classStart; for (let i = classStart; i < code.length && braceCount > 0; i++) { if (code[i] === '{') braceCount++; if (code[i] === '}') braceCount--; classEnd = i; } const classBody = code.substring(classStart, classEnd); const methods = []; const properties = []; // 메서드 추출 const methodRegex = /(?:(public|private|protected)\s+)?(?:(static)\s+)?(?:(async)\s+)?(\w+)\s*\([^)]*\)/g; let methodMatch; while ((methodMatch = methodRegex.exec(classBody)) !== null) { if (!['constructor', 'if', 'for', 'while', 'switch'].includes(methodMatch[4])) { methods.push({ name: methodMatch[4], params: [], async: !!methodMatch[3], exported: false, lineNumber: lineNumber + classBody.substring(0, methodMatch.index).split('\n').length }); } } classes.push({ name: className, methods, properties, extends: match[3], implements: match[4]?.split(',').map(s => s.trim()), exported: !!match[1], lineNumber }); } // Export 분석 const exportRegex = /export\s+(?:(default)\s+)?(?:const|let|var|function|class|type|interface)\s+(\w+)/g; while ((match = exportRegex.exec(code)) !== null) { const exportType = code.substring(match.index).match(/export\s+(?:default\s+)?(const|let|var|function|class|type|interface)/)?.[1]; exports.push({ name: match[2], type: exportType || 'variable', isDefault: !!match[1] }); } // 복잡도 계산 (간단한 휴리스틱) const complexity = calculateComplexity(code); return { language: 'typescript', functions, classes, imports, exports, dependencies: [...new Set(dependencies)], complexity, lineCount: lines.length }; } // Python 분석 export function analyzePython(code) { const functions = []; const classes = []; const imports = []; const exports = []; const dependencies = []; const lines = code.split('\n'); // Import 분석 const importRegex = /^(?:from\s+(\S+)\s+)?import\s+(.+)$/gm; let match; while ((match = importRegex.exec(code)) !== null) { const source = match[1] || match[2].split(',')[0].trim().split(' as ')[0]; const importNames = match[2].split(',').map(s => s.trim().split(' as ')[0].trim()); imports.push({ source, imports: importNames, isDefault: !match[1], isNamespace: false }); const pkgName = source.split('.')[0]; if (!pkgName.startsWith('.')) { dependencies.push(pkgName); } } // 함수 분석 const functionRegex = /^(\s*)(?:(async)\s+)?def\s+(\w+)\s*\(([^)]*)\)(?:\s*->\s*(\S+))?:/gm; while ((match = functionRegex.exec(code)) !== null) { const indent = match[1].length; const lineNumber = code.substring(0, match.index).split('\n').length; // 독스트링 추출 const afterDef = code.substring(match.index + match[0].length); const docstringMatch = afterDef.match(/^\s*[']{3}([\s\S]*?)[']{3}|^\s*["]{3}([\s\S]*?)["]{3}/); functions.push({ name: match[3], params: match[4].split(',').map(p => p.trim().split(':')[0].split('=')[0].trim()).filter(Boolean), returnType: match[5], async: !!match[2], exported: indent === 0 && !match[3].startsWith('_'), lineNumber, docstring: docstringMatch?.[1] || docstringMatch?.[2] }); } // 클래스 분석 const classRegex = /^class\s+(\w+)(?:\(([^)]*)\))?:/gm; while ((match = classRegex.exec(code)) !== null) { const className = match[1]; const lineNumber = code.substring(0, match.index).split('\n').length; const parentClasses = match[2]?.split(',').map(s => s.trim()) || []; classes.push({ name: className, methods: [], properties: [], extends: parentClasses[0], implements: parentClasses.slice(1), exported: !className.startsWith('_'), lineNumber }); } const complexity = calculateComplexity(code); return { language: 'python', functions, classes, imports, exports, dependencies: [...new Set(dependencies)], complexity, lineCount: lines.length }; } // Go 분석 export function analyzeGo(code) { const functions = []; const classes = []; const imports = []; const exports = []; const dependencies = []; const lines = code.split('\n'); // Import 분석 const singleImportRegex = /import\s+"([^"]+)"/g; const multiImportRegex = /import\s+\(([\s\S]*?)\)/g; let match; while ((match = singleImportRegex.exec(code)) !== null) { imports.push({ source: match[1], imports: [match[1].split('/').pop() || match[1]], isDefault: false, isNamespace: false }); dependencies.push(match[1].split('/')[0]); } while ((match = multiImportRegex.exec(code)) !== null) { const importBlock = match[1]; const importLines = importBlock.match(/"([^"]+)"/g) || []; for (const imp of importLines) { const source = imp.replace(/"/g, ''); imports.push({ source, imports: [source.split('/').pop() || source], isDefault: false, isNamespace: false }); dependencies.push(source.split('/')[0]); } } // 함수 분석 const functionRegex = /func\s+(?:\((\w+)\s+\*?(\w+)\)\s+)?(\w+)\s*\(([^)]*)\)(?:\s*\(([^)]*)\)|\s*(\w+))?\s*\{/g; while ((match = functionRegex.exec(code)) !== null) { const lineNumber = code.substring(0, match.index).split('\n').length; const isMethod = !!match[1]; const funcName = match[3]; functions.push({ name: funcName, params: match[4].split(',').map(p => p.trim().split(' ')[0]).filter(Boolean), returnType: match[5] || match[6], async: false, exported: funcName[0] === funcName[0].toUpperCase(), lineNumber }); } // Struct 분석 (Go의 클래스 역할) const structRegex = /type\s+(\w+)\s+struct\s*\{/g; while ((match = structRegex.exec(code)) !== null) { const structName = match[1]; const lineNumber = code.substring(0, match.index).split('\n').length; classes.push({ name: structName, methods: [], properties: [], exported: structName[0] === structName[0].toUpperCase(), lineNumber }); } const complexity = calculateComplexity(code); return { language: 'go', functions, classes, imports, exports, dependencies: [...new Set(dependencies)], complexity, lineCount: lines.length }; } // 복잡도 계산 (Cyclomatic Complexity 근사) function calculateComplexity(code) { let complexity = 1; // 조건문/반복문 카운트 const patterns = [ /\bif\b/g, /\belse\s+if\b/g, /\bfor\b/g, /\bwhile\b/g, /\bcase\b/g, /\bcatch\b/g, /\?\s*.*\s*:/g, // 삼항 연산자 /&&/g, /\|\|/g ]; for (const pattern of patterns) { const matches = code.match(pattern); if (matches) { complexity += matches.length; } } return complexity; } // 언어 감지 및 분석 (내부 구현) function analyzeCodeInternal(code, language) { const detectedLanguage = language || detectLanguageForAnalysis(code); switch (detectedLanguage) { case 'typescript': case 'javascript': return analyzeTypeScript(code); case 'python': return analyzePython(code); case 'go': return analyzeGo(code); default: return { language: detectedLanguage, functions: [], classes: [], imports: [], exports: [], dependencies: [], complexity: calculateComplexity(code), lineCount: code.split('\n').length }; } } // Memoized 버전 (캐시된 분석 결과 재사용) export const analyzeCode = memoize(analyzeCodeInternal, (code, language) => `${language || 'auto'}:${hashCode(code)}`); function detectLanguageForAnalysis(code) { if (/^import\s+.*from\s+['"]|:\s*(string|number|boolean)\b/.test(code)) { return 'typescript'; } if (/^def\s+\w+|^class\s+\w+.*:$/m.test(code)) { return 'python'; } if (/^package\s+\w+|^func\s+/.test(code)) { return 'go'; } if (/^const\s+|^let\s+|^function\s+/.test(code)) { return 'javascript'; } return 'unknown'; } //# sourceMappingURL=astParser.js.map

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/MUSE-CODE-SPACE/vibe-coding-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server