UNPKG

embedia

Version:

Zero-configuration AI chatbot integration CLI - direct file copy with embedded API keys

280 lines (238 loc) 8.56 kB
const fs = require('fs'); const path = require('path'); const chalk = require('chalk'); const logger = require('./logger'); /** * Validate generated code for common integration issues * @param {Array} generatedFiles - Array of generated files * @param {Object} projectContext - Project context * @param {string} targetDirectory - Target directory * @returns {Object} Validation results */ function validateGeneratedCode(generatedFiles, projectContext, targetDirectory) { const validationResults = { success: true, warnings: [], errors: [], checks: { nextjsCompliance: false, reactImports: false, apiRoutes: false, typeScriptCompatibility: false, importPaths: false } }; logger.step('🔍 Validating generated code...'); try { // Check Next.js compliance if (projectContext.isNextJs) { validationResults.checks.nextjsCompliance = validateNextjsCompliance( generatedFiles, projectContext, validationResults ); } // Check React imports validationResults.checks.reactImports = validateReactImports( generatedFiles, validationResults ); // Check API routes validationResults.checks.apiRoutes = validateApiRoutes( generatedFiles, projectContext, validationResults ); // Check TypeScript compatibility if (projectContext.isTypeScript) { validationResults.checks.typeScriptCompatibility = validateTypeScriptCompatibility( generatedFiles, validationResults ); } // Check import paths validationResults.checks.importPaths = validateImportPaths( generatedFiles, validationResults ); // Determine overall success validationResults.success = validationResults.errors.length === 0; // Log results if (validationResults.success) { logger.success('✅ Code validation passed'); } else { logger.warn(`⚠️ Code validation found ${validationResults.errors.length} errors`); } if (validationResults.warnings.length > 0) { logger.warn(`Found ${validationResults.warnings.length} warnings`); } } catch (error) { validationResults.success = false; validationResults.errors.push(`Validation failed: ${error.message}`); logger.error(`Validation error: ${error.message}`); } return validationResults; } /** * Validate Next.js specific compliance */ function validateNextjsCompliance(generatedFiles, projectContext, results) { let isValid = true; // Check for Script tags with id attributes (Next.js 15+) if (projectContext.nextVersion?.major >= 15) { const layoutFiles = generatedFiles.filter(f => f.path.includes('layout')); layoutFiles.forEach(file => { if (file.content.includes('<Script') && !file.content.includes('id=')) { results.errors.push(`Next.js 15+ requires Script tags to have id attributes in ${file.path}`); isValid = false; } }); } // Check for app router vs pages router consistency if (projectContext.hasAppDirectory) { const apiFiles = generatedFiles.filter(f => f.path.includes('/api/')); apiFiles.forEach(file => { if (!file.path.includes('/route.js') && !file.path.includes('/route.ts')) { results.warnings.push(`App router API routes should use route.js pattern: ${file.path}`); } }); } return isValid; } /** * Validate React imports */ function validateReactImports(generatedFiles, results) { let isValid = true; generatedFiles.forEach(file => { const content = file.content; // Check for window.React usage (should be avoided) if (content.includes('window.React')) { results.errors.push(`Global React reference found in ${file.path} - should use proper imports`); isValid = false; } // Check for React usage without import if (content.includes('React.') && !content.includes('import React') && !content.includes("require('react')")) { results.warnings.push(`React usage without import in ${file.path}`); } }); return isValid; } /** * Validate API routes */ function validateApiRoutes(generatedFiles, projectContext, results) { let isValid = true; const apiFiles = generatedFiles.filter(f => f.path.includes('/api/')); apiFiles.forEach(file => { const content = file.content; if (projectContext.hasAppDirectory) { // App router validation if (!content.includes('NextResponse') && content.includes('export async function')) { results.errors.push(`App router API route should use NextResponse in ${file.path}`); isValid = false; } } else { // Pages router validation if (!content.includes('export default') && content.includes('function handler')) { results.errors.push(`Pages router API route should export default handler in ${file.path}`); isValid = false; } } }); return isValid; } /** * Validate TypeScript compatibility */ function validateTypeScriptCompatibility(generatedFiles, results) { let isValid = true; generatedFiles.forEach(file => { // Check if JS files should be TS files if (file.path.endsWith('.js') || file.path.endsWith('.jsx')) { if (!file.path.includes('node_modules')) { results.warnings.push(`File should have TypeScript extension: ${file.path}`); } } // Check for type imports in JS files if ((file.path.endsWith('.js') || file.path.endsWith('.jsx')) && file.content.includes('import type')) { results.errors.push(`Type imports in JS file: ${file.path}`); isValid = false; } }); return isValid; } /** * Validate import paths */ function validateImportPaths(generatedFiles, results) { let isValid = true; generatedFiles.forEach(file => { const content = file.content; const imports = content.match(/import.*from\s+['"]([^'"]+)['"]/g) || []; imports.forEach(importLine => { const pathMatch = importLine.match(/from\s+['"]([^'"]+)['"]/); if (pathMatch) { const importPath = pathMatch[1]; // Check for incorrect relative paths if (importPath.startsWith('/components/') && !importPath.startsWith('./')) { results.warnings.push(`Absolute import path might not resolve: ${importPath} in ${file.path}`); } // Check for imports that don't match generated file structure if (importPath.includes('AutoChat/types') || importPath.includes('@/components/AutoChat')) { const fileExists = generatedFiles.some(f => f.path.includes('types') || f.path.includes('AutoChat')); if (!fileExists) { results.errors.push(`Import references non-existent file: ${importPath} in ${file.path}`); isValid = false; } } } }); }); return isValid; } /** * Verify files were written correctly * @param {Array} writeResults - Results from file writing * @param {string} targetDirectory - Target directory * @returns {Object} Verification results */ function verifyFilesWritten(writeResults, targetDirectory) { const verificationResults = { success: true, filesExist: [], filesMissing: [], filesCorrupted: [] }; logger.step('🔍 Verifying written files...'); writeResults.success.forEach(filePath => { const fullPath = path.join(targetDirectory, filePath); try { if (fs.existsSync(fullPath)) { const content = fs.readFileSync(fullPath, 'utf8'); if (content.length > 0) { verificationResults.filesExist.push(filePath); } else { verificationResults.filesCorrupted.push(filePath); verificationResults.success = false; } } else { verificationResults.filesMissing.push(filePath); verificationResults.success = false; } } catch (error) { verificationResults.filesCorrupted.push(filePath); verificationResults.success = false; } }); if (verificationResults.success) { logger.success(`✅ All ${verificationResults.filesExist.length} files verified`); } else { logger.error(`❌ File verification failed`); if (verificationResults.filesMissing.length > 0) { logger.error(`Missing files: ${verificationResults.filesMissing.join(', ')}`); } if (verificationResults.filesCorrupted.length > 0) { logger.error(`Corrupted files: ${verificationResults.filesCorrupted.join(', ')}`); } } return verificationResults; } module.exports = { validateGeneratedCode, verifyFilesWritten };