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