UNPKG

@gati-framework/cli

Version:

CLI tool for Gati framework - create, develop, build and deploy cloud-native applications

194 lines 8.34 kB
/** * @module cli/analyzer/file-watcher * @description Real-time file and manifest watcher */ import { watch } from 'chokidar'; import { resolve, relative } from 'path'; import { writeFileSync, readFileSync, existsSync, mkdirSync } from 'fs'; import chalk from 'chalk'; import { analyzeFile } from './simple-analyzer.js'; export class FileWatcher { srcDir; manifestsDir; fileWatcher; manifestWatcher; onUpdate; // private versionDetector?: VersionDetector; onVersionChange; constructor(projectRoot, onUpdate, options) { this.srcDir = resolve(projectRoot, 'src'); this.manifestsDir = resolve(projectRoot, '.gati', 'manifests'); this.onUpdate = onUpdate; this.onVersionChange = options?.onVersionChange; if (!existsSync(this.manifestsDir)) { mkdirSync(this.manifestsDir, { recursive: true }); } // Initialize version detector if enabled // if (options?.enableVersioning !== false) { // this.versionDetector = new VersionDetector(projectRoot, true); // } } start() { console.log('👁 Starting file watcher...'); // Watch source files this.fileWatcher = watch([ `${this.srcDir}/**/*.{ts,js}` ], { ignoreInitial: false }); this.fileWatcher.on('add', (filePath) => { console.log(`📄 File added: ${relative(this.srcDir, filePath)}`); this.processFile(filePath); }); this.fileWatcher.on('change', (filePath) => { console.log(`📝 File changed: ${relative(this.srcDir, filePath)}`); this.processFile(filePath); }); this.fileWatcher.on('unlink', (filePath) => { console.log(`🗑️ File removed: ${relative(this.srcDir, filePath)}`); this.removeManifest(filePath); }); // Watch manifest files this.manifestWatcher = watch(`${this.manifestsDir}/*.json`, { ignoreInitial: true }); this.manifestWatcher.on('add', () => this.updateAppManifest()); this.manifestWatcher.on('change', () => this.updateAppManifest()); this.manifestWatcher.on('unlink', () => this.updateAppManifest()); } async processFile(filePath) { try { const result = analyzeFile(filePath, this.srcDir); if (result) { // Create individual manifest const manifestName = this.getManifestName(filePath); const individualManifest = { filePath, type: result.route ? 'handler' : 'module', data: result, timestamp: Date.now() }; const manifestPath = resolve(this.manifestsDir, manifestName); writeFileSync(manifestPath, JSON.stringify(individualManifest, null, 2)); console.log(`✅ Updated manifest: ${manifestName}`); // Detect version changes for handlers // if ((result as any).route && this.versionDetector) { // const handlerCode = readFileSync(filePath, 'utf-8'); // const handlerPath = (result as any).route; // const versionChange = await this.versionDetector.detectChange(handlerPath, handlerCode); // if (versionChange) { // this.notifyVersionChange(versionChange); // } // } } } catch (error) { console.error(`❌ Failed to process ${filePath}:`, error); } } notifyVersionChange(change) { console.log('\n' + chalk.cyan('═'.repeat(70))); console.log(chalk.bold.cyan('🕰️ New Version Created')); console.log(chalk.cyan('═'.repeat(70))); console.log(chalk.bold('Handler:'), chalk.white(change.handlerPath)); if (change.oldVersion) { console.log(chalk.bold('Previous:'), chalk.gray(change.oldVersion)); } console.log(chalk.bold('New Version:'), chalk.green(change.newVersion)); console.log(chalk.bold('Timestamp:'), chalk.gray(new Date(change.timestamp).toISOString())); if (change.breaking) { console.log(chalk.bold('Type:'), chalk.red.bold('BREAKING CHANGE')); } else { console.log(chalk.bold('Type:'), chalk.yellow('Non-breaking change')); } if (change.changes.length > 0) { console.log(chalk.bold('\nChanges:')); for (const changeDesc of change.changes) { if (changeDesc.startsWith('BREAKING:')) { console.log(chalk.red(' • ' + changeDesc)); } else { console.log(chalk.yellow(' • ' + changeDesc)); } } } if (change.breaking) { console.log('\n' + chalk.yellow('⚠️ Breaking change detected!')); console.log(chalk.gray(' A transformer stub will be generated to maintain backward compatibility.')); console.log(chalk.gray(' Location: src/transformers/' + this.getTransformerName(change))); } console.log(chalk.cyan('═'.repeat(70)) + '\n'); // Call callback if provided if (this.onVersionChange) { this.onVersionChange(change); } } getTransformerName(change) { const handlerName = change.handlerPath.split('/').pop()?.replace(/^\//, '') || 'handler'; const oldNum = change.oldVersion?.match(/-(\d+)$/)?.[1] || '001'; const newNum = change.newVersion.match(/-(\d+)$/)?.[1] || '002'; return `${handlerName}-v${oldNum}-v${newNum}.ts`; } removeManifest(filePath) { const manifestName = this.getManifestName(filePath); const manifestPath = resolve(this.manifestsDir, manifestName); if (existsSync(manifestPath)) { require('fs').unlinkSync(manifestPath); console.log(`🗑️ Removed manifest: ${manifestName}`); } } getManifestName(filePath) { const relativePath = relative(this.srcDir, filePath).replace(/\\/g, '/'); if (relativePath.startsWith('handlers/')) { return relativePath .replace('handlers/', '') .replace(/\//g, '_') .replace(/\.(ts|js)$/, '.json'); } else if (relativePath.startsWith('modules/')) { return relativePath .replace('modules/', '') .replace(/\//g, '_') .replace(/\.(ts|js)$/, '.json'); } return relativePath .replace(/\//g, '_') .replace(/\.(ts|js)$/, '.json'); } updateAppManifest() { try { const handlers = []; const modules = []; // Read all individual manifests const manifestFiles = require('fs').readdirSync(this.manifestsDir); for (const file of manifestFiles) { if (file.endsWith('.json') && file !== '_app.json') { const manifestPath = resolve(this.manifestsDir, file); const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8')); if (manifest.type === 'handler') { handlers.push(manifest.data); } else if (manifest.type === 'module') { modules.push(manifest.data); } } } const appManifest = { handlers, modules, timestamp: Date.now() }; const appManifestPath = resolve(this.manifestsDir, '_app.json'); writeFileSync(appManifestPath, JSON.stringify(appManifest, null, 2)); console.log(`🔄 Updated app manifest: ${handlers.length} handlers, ${modules.length} modules`); if (this.onUpdate) { this.onUpdate(appManifest); } } catch (error) { console.error('❌ Failed to update app manifest:', error); } } stop() { if (this.fileWatcher) { this.fileWatcher.close(); } if (this.manifestWatcher) { this.manifestWatcher.close(); } } } //# sourceMappingURL=file-watcher.js.map