embedia
Version:
Zero-configuration AI chatbot integration CLI - direct file copy with embedded API keys
280 lines (238 loc) • 8.56 kB
JavaScript
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
};