UNPKG

vibe-coder-mcp

Version:

Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.

424 lines (423 loc) 19.2 kB
import { BaseLanguageHandler } from './base.js'; import { getNodeText } from '../astAnalyzer.js'; import logger from '../../../logger.js'; import path from 'path'; export class JsonHandler extends BaseLanguageHandler { options; getFunctionQueryPatterns() { return [ 'pair', 'object', 'array' ]; } getClassQueryPatterns() { return [ 'document', 'object' ]; } getImportQueryPatterns() { return [ 'pair' ]; } extractFunctionName(node, sourceCode, _options) { try { if (node.type === 'pair') { const keyNode = node.childForFieldName('key'); if (keyNode) { const key = getNodeText(keyNode, sourceCode).replace(/^["']|["']$/g, ''); if (['function', 'handler', 'callback', 'run', 'script', 'command', 'exec', 'test'].includes(key)) { return `${key}_function`; } if (this.isInOpenApiContext(node, sourceCode)) { const valueNode = node.childForFieldName('value'); if (valueNode && valueNode.type === 'object') { for (let i = 0; i < valueNode.childCount; i++) { const child = valueNode.child(i); if (child?.type === 'pair') { const methodKeyNode = child.childForFieldName('key'); if (methodKeyNode) { const method = getNodeText(methodKeyNode, sourceCode).replace(/^["']|["']$/g, ''); if (['get', 'post', 'put', 'delete', 'patch', 'options', 'head'].includes(method.toLowerCase())) { return `${method.toUpperCase()}_${key}`; } } } } } return `endpoint_${key}`; } if (this.isInCloudFormationContext(node, sourceCode)) { if (key === 'Type') { const valueNode = node.childForFieldName('value'); if (valueNode) { const resourceType = getNodeText(valueNode, sourceCode).replace(/^["']|["']$/g, ''); return `resource_${resourceType.split('::').pop()}`; } } if (key === 'Properties') { return 'resource_properties'; } } if (this.isInPackageJsonContext(node, sourceCode) && key === 'scripts') { return 'npm_scripts'; } if (this.isInTsConfigContext(node, sourceCode) && key === 'compilerOptions') { return 'compiler_options'; } return key; } } if (node.type === 'array') { if (node.parent?.type === 'pair') { const keyNode = node.parent.childForFieldName('key'); if (keyNode) { const key = getNodeText(keyNode, sourceCode).replace(/^["']|["']$/g, ''); return `${key}_array`; } } return 'array'; } if (node.type === 'object') { if (node.parent?.type === 'pair') { const keyNode = node.parent.childForFieldName('key'); if (keyNode) { const key = getNodeText(keyNode, sourceCode).replace(/^["']|["']$/g, ''); return `${key}_object`; } } return 'object'; } return 'json_element'; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting JSON function name'); return 'json_element'; } } isInOpenApiContext(node, sourceCode) { try { if (this.options?.filePath) { const filename = path.basename(this.options.filePath).toLowerCase(); if (filename.includes('swagger') || filename.includes('openapi') || filename.includes('api')) { return true; } } let current = node; while (current.parent) { current = current.parent; if (current.type === 'object') { for (let i = 0; i < current.childCount; i++) { const child = current.child(i); if (child?.type === 'pair') { const keyNode = child.childForFieldName('key'); if (keyNode) { const key = getNodeText(keyNode, sourceCode).replace(/^["']|["']$/g, ''); if (['swagger', 'openapi', 'paths', 'components', 'info'].includes(key)) { return true; } } } } } } return false; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error checking if JSON node is in OpenAPI context'); return false; } } isInCloudFormationContext(node, sourceCode) { try { if (this.options?.filePath) { const filename = path.basename(this.options.filePath).toLowerCase(); if (filename.includes('cloudformation') || filename.includes('template') || filename.includes('stack') || filename.includes('sam')) { return true; } } let current = node; while (current.parent) { current = current.parent; if (current.type === 'object') { for (let i = 0; i < current.childCount; i++) { const child = current.child(i); if (child?.type === 'pair') { const keyNode = child.childForFieldName('key'); if (keyNode) { const key = getNodeText(keyNode, sourceCode).replace(/^["']|["']$/g, ''); if (['AWSTemplateFormatVersion', 'Resources', 'Outputs', 'Parameters'].includes(key)) { return true; } } } } } } return false; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error checking if JSON node is in CloudFormation context'); return false; } } isInPackageJsonContext(node, sourceCode) { try { if (this.options?.filePath) { const filename = path.basename(this.options.filePath).toLowerCase(); if (filename === 'package.json') { return true; } } let current = node; while (current.parent) { current = current.parent; if (current.type === 'object') { let hasName = false; let hasVersion = false; let hasDependencies = false; for (let i = 0; i < current.childCount; i++) { const child = current.child(i); if (child?.type === 'pair') { const keyNode = child.childForFieldName('key'); if (keyNode) { const key = getNodeText(keyNode, sourceCode).replace(/^["']|["']$/g, ''); if (key === 'name') hasName = true; if (key === 'version') hasVersion = true; if (key === 'dependencies' || key === 'devDependencies') hasDependencies = true; } } } if (hasName && hasVersion && hasDependencies) { return true; } } } return false; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error checking if JSON node is in package.json context'); return false; } } isInTsConfigContext(node, sourceCode) { try { if (this.options?.filePath) { const filename = path.basename(this.options.filePath).toLowerCase(); if (filename === 'tsconfig.json' || filename.startsWith('tsconfig.')) { return true; } } let current = node; while (current.parent) { current = current.parent; if (current.type === 'object') { for (let i = 0; i < current.childCount; i++) { const child = current.child(i); if (child?.type === 'pair') { const keyNode = child.childForFieldName('key'); if (keyNode) { const key = getNodeText(keyNode, sourceCode).replace(/^["']|["']$/g, ''); if (key === 'compilerOptions') { return true; } } } } } } return false; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error checking if JSON node is in tsconfig.json context'); return false; } } extractClassName(node, sourceCode) { try { if (node.type === 'document') { if (this.isInOpenApiContext(node, sourceCode)) { return 'OpenAPI_Document'; } if (this.isInCloudFormationContext(node, sourceCode)) { return 'CloudFormation_Template'; } if (this.isInPackageJsonContext(node, sourceCode)) { const rootObject = node.firstChild; if (rootObject?.type === 'object') { for (let i = 0; i < rootObject.childCount; i++) { const child = rootObject.child(i); if (child?.type === 'pair') { const keyNode = child.childForFieldName('key'); if (keyNode && getNodeText(keyNode, sourceCode).replace(/^["']|["']$/g, '') === 'name') { const valueNode = child.childForFieldName('value'); if (valueNode) { return `Package_${getNodeText(valueNode, sourceCode).replace(/^["']|["']$/g, '')}`; } } } } } return 'Package_JSON'; } if (this.isInTsConfigContext(node, sourceCode)) { return 'TSConfig'; } if (this.options?.filePath) { return `JSON_${path.basename(this.options.filePath, path.extname(this.options.filePath))}`; } } else if (node.type === 'object') { if (node.parent?.type === 'pair') { const keyNode = node.parent.childForFieldName('key'); if (keyNode) { const key = getNodeText(keyNode, sourceCode).replace(/^["']|["']$/g, ''); return `Object_${key}`; } } } return 'JSON_Object'; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting JSON class name'); return 'JSON_Object'; } } extractImportPath(node, sourceCode) { try { if (node.type === 'pair') { const keyNode = node.childForFieldName('key'); if (keyNode) { const key = getNodeText(keyNode, sourceCode).replace(/^["']|["']$/g, ''); if (key === '$ref' || key === 'import' || key === 'include' || key === 'extends') { const valueNode = node.childForFieldName('value'); if (valueNode) { return getNodeText(valueNode, sourceCode).replace(/^["']|["']$/g, ''); } } } } return 'unknown'; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting JSON import path'); return 'unknown'; } } extractFunctionComment(node, sourceCode) { try { if (this.isInOpenApiContext(node, sourceCode) && node.type === 'pair') { const parent = node.parent; if (parent) { for (let i = 0; i < parent.childCount; i++) { const child = parent.child(i); if (child?.type === 'pair') { const keyNode = child.childForFieldName('key'); if (keyNode && getNodeText(keyNode, sourceCode).replace(/^["']|["']$/g, '') === 'description') { const valueNode = child.childForFieldName('value'); if (valueNode) { return getNodeText(valueNode, sourceCode).replace(/^["']|["']$/g, ''); } } } } } } return undefined; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting JSON function comment'); return undefined; } } extractClassComment(node, sourceCode) { try { if (this.isInOpenApiContext(node, sourceCode) && node.type === 'document') { const rootObject = node.firstChild; if (rootObject?.type === 'object') { for (let i = 0; i < rootObject.childCount; i++) { const child = rootObject.child(i); if (child?.type === 'pair') { const keyNode = child.childForFieldName('key'); if (keyNode && getNodeText(keyNode, sourceCode).replace(/^["']|["']$/g, '') === 'info') { const valueNode = child.childForFieldName('value'); if (valueNode && valueNode.type === 'object') { for (let j = 0; j < valueNode.childCount; j++) { const infoChild = valueNode.child(j); if (infoChild?.type === 'pair') { const infoKeyNode = infoChild.childForFieldName('key'); if (infoKeyNode && getNodeText(infoKeyNode, sourceCode).replace(/^["']|["']$/g, '') === 'description') { const infoValueNode = infoChild.childForFieldName('value'); if (infoValueNode) { return getNodeText(infoValueNode, sourceCode).replace(/^["']|["']$/g, ''); } } } } } } } } } } if (this.isInPackageJsonContext(node, sourceCode) && node.type === 'document') { const rootObject = node.firstChild; if (rootObject?.type === 'object') { for (let i = 0; i < rootObject.childCount; i++) { const child = rootObject.child(i); if (child?.type === 'pair') { const keyNode = child.childForFieldName('key'); if (keyNode && getNodeText(keyNode, sourceCode).replace(/^["']|["']$/g, '') === 'description') { const valueNode = child.childForFieldName('value'); if (valueNode) { return getNodeText(valueNode, sourceCode).replace(/^["']|["']$/g, ''); } } } } } } return undefined; } catch (error) { logger.warn({ err: error, nodeType: node.type }, 'Error extracting JSON class comment'); return undefined; } } detectFramework(sourceCode) { try { if ((sourceCode.includes('"swagger"') || sourceCode.includes('"openapi"')) && sourceCode.includes('"paths"')) { return 'openapi'; } if (sourceCode.includes('"AWSTemplateFormatVersion"') || (sourceCode.includes('"Resources"') && sourceCode.includes('"Type"'))) { return 'cloudformation'; } if (sourceCode.includes('"name"') && sourceCode.includes('"version"') && (sourceCode.includes('"dependencies"') || sourceCode.includes('"devDependencies"'))) { return 'npm'; } if (sourceCode.includes('"compilerOptions"') && (sourceCode.includes('"target"') || sourceCode.includes('"module"'))) { return 'typescript'; } return null; } catch (error) { logger.warn({ err: error }, 'Error detecting JSON framework'); return null; } } }