UNPKG

embedia

Version:

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

261 lines (225 loc) 8.4 kB
/** * Compliance Fixer - Phase 1 Implementation * * ARCHITECTURAL PRINCIPLE: Generated Code is Authority * * This module performs MINIMAL post-processing to fix critical compliance violations * that prevent builds from working. It does NOT modify the logic or functionality * of generated code - only fixes syntax/compliance issues that block builds. * * This is a TRANSITIONAL solution while server-side generation is updated. */ const chalk = require('chalk'); const logger = require('../utils/logger'); /** * Fix critical compliance violations in generated code * @param {Array} generatedFiles - Generated files from server * @param {Object} contract - Environment contract with compliance rules * @returns {Object} Fixed files and compliance report */ function fixComplianceViolations(generatedFiles, contract) { if (!contract || !contract.requirements || !contract.requirements.complianceRules) { return { success: true, files: generatedFiles, fixesApplied: [], message: 'No compliance rules to enforce' }; } console.log(chalk.cyan('🔧 Checking for critical compliance violations...')); const complianceRules = contract.requirements.complianceRules; const fixesApplied = []; const fixedFiles = []; generatedFiles.forEach(file => { let content = file.content; let modified = false; // Fix Next.js 15+ Script tag compliance if (complianceRules.includes('nextjs15-script-id-required')) { const scriptFix = fixNextJS15ScriptTags(content, file.path); if (scriptFix.modified) { content = scriptFix.content; modified = true; fixesApplied.push({ file: file.path, rule: 'nextjs15-script-id-required', description: 'Added required id attributes to Script tags' }); } } // Fix TypeScript import compliance if (complianceRules.includes('typescript-strict-imports')) { const importFix = fixTypeScriptImports(content, file.path, contract); if (importFix.modified) { content = importFix.content; modified = true; fixesApplied.push({ file: file.path, rule: 'typescript-strict-imports', description: 'Fixed TypeScript import statements' }); } } // Fix React 18+ createRoot API compliance if (complianceRules.includes('react18-createroot-api')) { const reactFix = fixReact18CreateRoot(content, file.path); if (reactFix.modified) { content = reactFix.content; modified = true; fixesApplied.push({ file: file.path, rule: 'react18-createroot-api', description: 'Updated to React 18 createRoot API' }); } } // Create new file object with fixed content fixedFiles.push({ ...file, content: content, complianceFixed: modified }); }); // Report fixes if (fixesApplied.length > 0) { console.log(chalk.yellow(`⚠️ Applied ${fixesApplied.length} compliance fixes:`)); fixesApplied.forEach(fix => { console.log(chalk.gray(` • ${fix.file}: ${fix.description}`)); }); console.log(chalk.gray(' These are minimal changes to ensure build compatibility')); } else { console.log(chalk.green('✅ No compliance violations found')); } return { success: true, files: fixedFiles, fixesApplied: fixesApplied, message: `Applied ${fixesApplied.length} compliance fixes` }; } /** * Fix Next.js 15+ Script tag compliance issues * Script tags must have id attributes in Next.js 15+ */ function fixNextJS15ScriptTags(content, filePath) { if (!content.includes('<Script')) { return { content, modified: false }; } let modified = false; let fixedContent = content; // Find Script tags without id attributes const scriptTagRegex = /<Script(?![^>]*\sid\s*=)[^>]*>/g; const matches = content.match(scriptTagRegex); if (matches) { matches.forEach((scriptTag, index) => { // Generate a unique id for each Script tag const uniqueId = `embedia-script-${Date.now()}-${index}`; // Add id attribute to the Script tag let fixedTag; if (scriptTag.includes('strategy=')) { // Add id after existing attributes fixedTag = scriptTag.replace('>', ` id="${uniqueId}">`); } else { // Add id as first attribute fixedTag = scriptTag.replace('<Script', `<Script id="${uniqueId}"`); } fixedContent = fixedContent.replace(scriptTag, fixedTag); modified = true; }); } return { content: fixedContent, modified }; } /** * Fix TypeScript import compliance issues * Ensure proper import statements for TypeScript projects */ function fixTypeScriptImports(content, filePath, contract) { if (!contract.language.typescript || !content.includes('import')) { return { content, modified: false }; } let modified = false; let fixedContent = content; // Fix common TypeScript import issues // 1. Add React import if JSX is used but React is not imported if (content.includes('<') && content.includes('>') && !content.includes('import React') && !content.includes("require('react')")) { fixedContent = "import React from 'react';\n" + fixedContent; modified = true; } // 2. Fix missing useEffect imports if (content.includes('useEffect') && !content.includes('useEffect')) { const reactImportMatch = fixedContent.match(/import React[^;]*from ['"]react['"];/); if (reactImportMatch) { const reactImport = reactImportMatch[0]; const newImport = reactImport.replace('import React', 'import React, { useEffect }'); fixedContent = fixedContent.replace(reactImport, newImport); modified = true; } } return { content: fixedContent, modified }; } /** * Fix React 18+ createRoot API compliance * Update // Legacy React patterns removed to // Legacy React patterns removed */ function fixReact18CreateRoot(content, filePath) { if (!content.includes('// Legacy React patterns removed') && !content.includes('window.// Legacy React patterns removed')) { return { content, modified: false }; } let modified = false; let fixedContent = content; // Replace // Legacy React patterns removed with createRoot API if (content.includes('// Legacy React patterns removed(')) { fixedContent = fixedContent.replace( /ReactDOM\.render\(\s*([^,]+)\s*,\s*([^)]+)\s*\)/g, '// Legacy React patterns removed($2).render($1)' ); modified = true; } // Replace window.// Legacy React patterns removed with createRoot API if (content.includes('window.// Legacy React patterns removed(')) { fixedContent = fixedContent.replace( /window\.ReactDOM\.render\(\s*([^,]+)\s*,\s*([^)]+)\s*\)/g, 'window.// Legacy React patterns removed($2).render($1)' ); modified = true; } return { content: fixedContent, modified }; } /** * Validate compliance rules are properly applied * @param {Array} files - Files to validate * @param {Object} contract - Environment contract * @returns {Object} Validation results */ function validateCompliance(files, contract) { const validationResult = { success: true, violations: [], warnings: [] }; if (!contract || !contract.requirements || !contract.requirements.complianceRules) { return validationResult; } const complianceRules = contract.requirements.complianceRules; files.forEach(file => { // Check Next.js 15+ Script compliance if (complianceRules.includes('nextjs15-script-id-required')) { if (file.content.includes('<Script') && !file.content.match(/<Script[^>]*\sid\s*=/)) { validationResult.violations.push(`${file.path}: Script tags missing required id attributes`); validationResult.success = false; } } // Check TypeScript compliance if (complianceRules.includes('typescript-strict-imports') && file.path.endsWith('.ts')) { if (file.content.includes('React.') && !file.content.includes('import React')) { validationResult.violations.push(`${file.path}: Missing React import for TypeScript`); validationResult.success = false; } } }); return validationResult; } module.exports = { fixComplianceViolations, validateCompliance };