embedia
Version:
Zero-configuration AI chatbot integration CLI - direct file copy with embedded API keys
122 lines (101 loc) • 3.36 kB
JavaScript
const fs = require('fs-extra');
const path = require('path');
const parser = require('@babel/parser');
const ASTModifier = require('./astModifier');
class SafeASTModifier {
constructor() {
this.astModifier = new ASTModifier();
}
async modifyWithRollback(filePath, modifier) {
// Create backup
const backup = await this.createBackup(filePath);
try {
// Read current content
const content = await fs.readFile(filePath, 'utf-8');
// Apply modification
const modified = await modifier(content);
// Validate the modified content
await this.validateModifiedCode(modified, filePath);
// Write the modified content
await fs.writeFile(filePath, modified);
// Test that the modification works
await this.testModification(filePath);
// Success - remove backup
await this.removeBackup(backup);
return { success: true, filePath };
} catch (error) {
// Rollback on any error
await this.rollback(filePath, backup);
return {
success: false,
error: error.message,
suggestion: this.getSuggestion(error)
};
}
}
async createBackup(filePath) {
const backupPath = `${filePath}.embedia-backup`;
await fs.copy(filePath, backupPath);
return backupPath;
}
async removeBackup(backupPath) {
try {
await fs.remove(backupPath);
} catch (error) {
// Ignore errors when removing backup
}
}
async rollback(filePath, backupPath) {
try {
await fs.copy(backupPath, filePath, { overwrite: true });
await fs.remove(backupPath);
} catch (error) {
console.error('Failed to rollback:', error);
}
}
async validateModifiedCode(code, filePath) {
const isTypeScript = filePath.endsWith('.ts') || filePath.endsWith('.tsx');
try {
// Parse to ensure valid syntax
parser.parse(code, {
sourceType: 'module',
plugins: [
'jsx',
isTypeScript && 'typescript',
'decorators-legacy',
'dynamicImport',
'classProperties',
'optionalChaining',
'nullishCoalescingOperator'
].filter(Boolean)
});
} catch (error) {
throw new Error(`Invalid syntax after modification: ${error.message}`);
}
}
async testModification(filePath) {
// Basic validation - check if file exists and is readable
try {
await fs.access(filePath, fs.constants.R_OK);
const stats = await fs.stat(filePath);
if (stats.size === 0) {
throw new Error('Modified file is empty');
}
} catch (error) {
throw new Error(`File validation failed: ${error.message}`);
}
}
getSuggestion(error) {
if (error.message.includes('Unexpected token')) {
return 'The file may have custom syntax. Consider manual integration.';
}
if (error.message.includes('body')) {
return 'Could not find body element. The layout structure may be non-standard.';
}
if (error.message.includes('Invalid syntax')) {
return 'The modified code has syntax errors. This may be due to complex JSX structure.';
}
return 'Automatic modification failed. See manual integration guide.';
}
}
module.exports = SafeASTModifier;