UNPKG

embedia

Version:

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

288 lines (240 loc) 8.28 kB
const { execSync } = require('child_process'); const path = require('path'); const fs = require('fs-extra'); const chalk = require('chalk'); const logger = require('../utils/logger'); /** * BuildVerifier - Ensures Next.js build process remains intact after integration * Addresses the critical issue of build failures with "Cannot find module '../server/require-hook'" */ class BuildVerifier { constructor(projectPath) { this.projectPath = projectPath; this.buildIssues = []; } /** * Quick verification that doesn't require full build */ async quickVerify() { const verifications = { nextBinary: await this.verifyNextBinary(), nodeModulesIntegrity: await this.verifyNodeModulesIntegrity(), configIntegrity: await this.verifyConfigIntegrity(), packageJsonValid: await this.verifyPackageJson() }; const allPassed = Object.values(verifications).every(v => v === true); if (!allPassed) { logger.warn(chalk.yellow('\n⚠️ Build verification detected potential issues:')); this.buildIssues.forEach(issue => { logger.warn(` - ${issue}`); }); } return { passed: allPassed, verifications, issues: this.buildIssues }; } /** * Verify Next.js binary is accessible and not corrupted */ async verifyNextBinary() { try { const nextBinPath = path.join(this.projectPath, 'node_modules', '.bin', 'next'); // Check if next binary exists if (!await fs.pathExists(nextBinPath)) { this.buildIssues.push('Next.js binary not found in node_modules/.bin/'); return false; } // Try to run next --version const version = execSync('npx next --version', { cwd: this.projectPath, stdio: 'pipe', encoding: 'utf8' }).trim(); logger.info(chalk.gray(` Next.js version: ${version}`)); return true; } catch (error) { this.buildIssues.push(`Next.js binary check failed: ${error.message}`); return false; } } /** * Verify node_modules structure hasn't been corrupted */ async verifyNodeModulesIntegrity() { try { const criticalPaths = [ 'node_modules/next', 'node_modules/next/dist', 'node_modules/next/dist/server', 'node_modules/react', 'node_modules/react-dom' ]; for (const criticalPath of criticalPaths) { const fullPath = path.join(this.projectPath, criticalPath); if (!await fs.pathExists(fullPath)) { this.buildIssues.push(`Missing critical path: ${criticalPath}`); return false; } } // Check for the specific file that's failing const requireHookPath = path.join( this.projectPath, 'node_modules/next/dist/server/require-hook.js' ); if (!await fs.pathExists(requireHookPath)) { this.buildIssues.push('Missing next/dist/server/require-hook.js - Next.js installation may be corrupted'); return false; } return true; } catch (error) { this.buildIssues.push(`Node modules integrity check failed: ${error.message}`); return false; } } /** * Verify Next.js config files haven't been corrupted */ async verifyConfigIntegrity() { try { // Check for next.config.js or next.config.mjs const configFiles = ['next.config.js', 'next.config.mjs']; let configFound = false; for (const configFile of configFiles) { const configPath = path.join(this.projectPath, configFile); if (await fs.pathExists(configPath)) { configFound = true; // Read and check if it's valid JavaScript const content = await fs.readFile(configPath, 'utf8'); // Basic syntax check - ensure it's not corrupted if (content.includes('�') || content.length === 0) { this.buildIssues.push(`${configFile} appears to be corrupted`); return false; } } } return true; } catch (error) { this.buildIssues.push(`Config integrity check failed: ${error.message}`); return false; } } /** * Verify package.json scripts are intact */ async verifyPackageJson() { try { const packageJsonPath = path.join(this.projectPath, 'package.json'); const packageJson = await fs.readJson(packageJsonPath); // Check for required scripts if (!packageJson.scripts) { this.buildIssues.push('No scripts section in package.json'); return false; } // For Next.js projects, these scripts should exist const requiredScripts = ['dev', 'build']; const missingScripts = requiredScripts.filter(script => !packageJson.scripts[script]); if (missingScripts.length > 0) { this.buildIssues.push(`Missing required scripts: ${missingScripts.join(', ')}`); return false; } // Verify build script is correct if (packageJson.scripts.build && !packageJson.scripts.build.includes('next build')) { this.buildIssues.push('Build script does not use "next build"'); return false; } return true; } catch (error) { this.buildIssues.push(`Package.json verification failed: ${error.message}`); return false; } } /** * Attempt to fix common build issues */ async attemptAutoFix() { logger.info(chalk.blue('\n🔧 Attempting to auto-fix build issues...')); const fixes = []; // Fix 1: Reinstall Next.js if require-hook is missing if (this.buildIssues.some(issue => issue.includes('require-hook'))) { try { logger.info(' Reinstalling Next.js...'); execSync('npm uninstall next && npm install next', { cwd: this.projectPath, stdio: 'pipe' }); fixes.push('Reinstalled Next.js'); } catch (error) { logger.error(' Failed to reinstall Next.js'); } } // Fix 2: Restore package.json scripts if (this.buildIssues.some(issue => issue.includes('scripts'))) { try { const packageJsonPath = path.join(this.projectPath, 'package.json'); const packageJson = await fs.readJson(packageJsonPath); if (!packageJson.scripts) { packageJson.scripts = {}; } // Restore default Next.js scripts packageJson.scripts = { ...packageJson.scripts, dev: packageJson.scripts.dev || 'next dev', build: packageJson.scripts.build || 'next build', start: packageJson.scripts.start || 'next start' }; await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 }); fixes.push('Restored package.json scripts'); } catch (error) { logger.error(' Failed to restore scripts'); } } return fixes; } /** * Create a backup of critical files before integration */ static async createBackup(projectPath) { const backupDir = path.join(projectPath, '.embedia-backup'); await fs.ensureDir(backupDir); const filesToBackup = [ 'package.json', 'package-lock.json', 'next.config.js', 'next.config.mjs' ]; const backedUp = []; for (const file of filesToBackup) { const filePath = path.join(projectPath, file); if (await fs.pathExists(filePath)) { const backupPath = path.join(backupDir, file); await fs.copy(filePath, backupPath); backedUp.push(file); } } return { backupDir, backedUp }; } /** * Restore from backup if build is broken */ static async restoreFromBackup(projectPath) { const backupDir = path.join(projectPath, '.embedia-backup'); if (!await fs.pathExists(backupDir)) { throw new Error('No backup found'); } const files = await fs.readdir(backupDir); for (const file of files) { const backupPath = path.join(backupDir, file); const restorePath = path.join(projectPath, file); await fs.copy(backupPath, restorePath, { overwrite: true }); } // Clean up backup await fs.remove(backupDir); return files; } } module.exports = BuildVerifier;