UNPKG

@git.zone/cli

Version:

A comprehensive CLI tool for enhancing and managing local development workflows with gitzone utilities, focusing on project setup, version control, code formatting, and template management.

184 lines (159 loc) 6.21 kB
import * as plugins from './mod.plugins.js'; import { FormatContext } from './classes.formatcontext.js'; import { BaseFormatter } from './classes.baseformatter.js'; import type { IFormatPlan, IPlannedChange } from './interfaces.format.js'; import { logger } from '../gitzone.logging.js'; import { DependencyAnalyzer } from './classes.dependency-analyzer.js'; import { DiffReporter } from './classes.diffreporter.js'; export class FormatPlanner { private plannedChanges: Map<string, IPlannedChange[]> = new Map(); private dependencyAnalyzer = new DependencyAnalyzer(); private diffReporter = new DiffReporter(); async planFormat(modules: BaseFormatter[]): Promise<IFormatPlan> { const plan: IFormatPlan = { summary: { totalFiles: 0, filesAdded: 0, filesModified: 0, filesRemoved: 0, estimatedTime: 0 }, changes: [], warnings: [] }; for (const module of modules) { try { const changes = await module.analyze(); this.plannedChanges.set(module.name, changes); for (const change of changes) { plan.changes.push(change); // Update summary switch (change.type) { case 'create': plan.summary.filesAdded++; break; case 'modify': plan.summary.filesModified++; break; case 'delete': plan.summary.filesRemoved++; break; } } } catch (error) { plan.warnings.push({ level: 'error', message: `Failed to analyze module ${module.name}: ${error.message}`, module: module.name }); } } plan.summary.totalFiles = plan.summary.filesAdded + plan.summary.filesModified + plan.summary.filesRemoved; plan.summary.estimatedTime = plan.summary.totalFiles * 100; // 100ms per file estimate return plan; } async executePlan(plan: IFormatPlan, modules: BaseFormatter[], context: FormatContext, parallel: boolean = true): Promise<void> { await context.beginOperation(); const startTime = Date.now(); try { if (parallel) { // Get execution groups based on dependencies const executionGroups = this.dependencyAnalyzer.getExecutionGroups(modules); logger.log('info', `Executing formatters in ${executionGroups.length} groups...`); for (let i = 0; i < executionGroups.length; i++) { const group = executionGroups[i]; logger.log('info', `Executing group ${i + 1}: ${group.map(m => m.name).join(', ')}`); // Execute modules in this group in parallel const promises = group.map(async (module) => { const changes = this.plannedChanges.get(module.name) || []; if (changes.length > 0) { logger.log('info', `Executing ${module.name} formatter...`); await module.execute(changes); } }); await Promise.all(promises); } } else { // Sequential execution (original implementation) for (const module of modules) { const changes = this.plannedChanges.get(module.name) || []; if (changes.length > 0) { logger.log('info', `Executing ${module.name} formatter...`); await module.execute(changes); } } } const endTime = Date.now(); const duration = endTime - startTime; logger.log('info', `Format operations completed in ${duration}ms`); await context.commitOperation(); } catch (error) { await context.rollbackOperation(); throw error; } } async displayPlan(plan: IFormatPlan, detailed: boolean = false): Promise<void> { console.log('\nFormat Plan:'); console.log('━'.repeat(50)); console.log(`Summary: ${plan.summary.totalFiles} files will be changed`); console.log(` • ${plan.summary.filesAdded} new files`); console.log(` • ${plan.summary.filesModified} modified files`); console.log(` • ${plan.summary.filesRemoved} deleted files`); console.log(''); console.log('Changes by module:'); // Group changes by module const changesByModule = new Map<string, IPlannedChange[]>(); for (const change of plan.changes) { const moduleChanges = changesByModule.get(change.module) || []; moduleChanges.push(change); changesByModule.set(change.module, moduleChanges); } for (const [module, changes] of changesByModule) { console.log(`\n${this.getModuleIcon(module)} ${module} (${changes.length} ${changes.length === 1 ? 'file' : 'files'})`); for (const change of changes) { const icon = this.getChangeIcon(change.type); console.log(` ${icon} ${change.path} - ${change.description}`); // Show diff for modified files if detailed view is requested if (detailed && change.type === 'modify') { const diff = await this.diffReporter.generateDiffForChange(change); if (diff) { this.diffReporter.displayDiff(change.path, diff); } } } } if (plan.warnings.length > 0) { console.log('\nWarnings:'); for (const warning of plan.warnings) { const icon = warning.level === 'error' ? '❌' : '⚠️'; console.log(` ${icon} ${warning.message}`); } } console.log('\n' + '━'.repeat(50)); } private getModuleIcon(module: string): string { const icons: Record<string, string> = { 'packagejson': '📦', 'license': '📝', 'tsconfig': '🔧', 'cleanup': '🚮', 'gitignore': '🔒', 'prettier': '✨', 'readme': '📖', 'templates': '📄', 'npmextra': '⚙️', 'copy': '📋' }; return icons[module] || '📁'; } private getChangeIcon(type: 'create' | 'modify' | 'delete'): string { switch (type) { case 'create': return '✅'; case 'modify': return '✏️'; case 'delete': return '❌'; } } }