171 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			171 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
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 = false,
 | 
						|
  ): Promise<void> {
 | 
						|
    const startTime = Date.now();
 | 
						|
 | 
						|
    try {
 | 
						|
      // Always use sequential execution to avoid race conditions
 | 
						|
      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`);
 | 
						|
    } catch (error) {
 | 
						|
      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 '❌';
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 |