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.

248 lines (212 loc) 8.15 kB
import * as plugins from './mod.plugins.js'; import { Project } from '../classes.project.js'; import { FormatContext } from './classes.formatcontext.js'; import { FormatPlanner } from './classes.formatplanner.js'; import { logger, setVerboseMode } from '../gitzone.logging.js'; // Import wrapper classes for formatters import { CleanupFormatter } from './formatters/cleanup.formatter.js'; import { NpmextraFormatter } from './formatters/npmextra.formatter.js'; import { LicenseFormatter } from './formatters/license.formatter.js'; import { PackageJsonFormatter } from './formatters/packagejson.formatter.js'; import { TemplatesFormatter } from './formatters/templates.formatter.js'; import { GitignoreFormatter } from './formatters/gitignore.formatter.js'; import { TsconfigFormatter } from './formatters/tsconfig.formatter.js'; import { PrettierFormatter } from './formatters/prettier.formatter.js'; import { ReadmeFormatter } from './formatters/readme.formatter.js'; import { CopyFormatter } from './formatters/copy.formatter.js'; export let run = async (options: { dryRun?: boolean; yes?: boolean; planOnly?: boolean; savePlan?: string; fromPlan?: string; detailed?: boolean; interactive?: boolean; parallel?: boolean; verbose?: boolean; } = {}): Promise<any> => { // Set verbose mode if requested if (options.verbose) { setVerboseMode(true); } const project = await Project.fromCwd(); const context = new FormatContext(); await context.initializeCache(); // Initialize the cache system const planner = new FormatPlanner(); // Get configuration from npmextra const npmextraConfig = new plugins.npmextra.Npmextra(); const formatConfig = npmextraConfig.dataFor<any>('gitzone.format', { interactive: true, showDiffs: false, autoApprove: false, planTimeout: 30000, rollback: { enabled: true, autoRollbackOnError: true, backupRetentionDays: 7, maxBackupSize: '100MB', excludePatterns: ['node_modules/**', '.git/**'] }, modules: { skip: [], only: [], order: [] }, parallel: true, cache: { enabled: true, clean: true // Clean invalid entries from cache } }); // Clean cache if configured if (formatConfig.cache.clean) { await context.getChangeCache().clean(); } // Override config with command options const interactive = options.interactive ?? formatConfig.interactive; const autoApprove = options.yes ?? formatConfig.autoApprove; const parallel = options.parallel ?? formatConfig.parallel; try { // Initialize formatters const formatters = [ new CleanupFormatter(context, project), new NpmextraFormatter(context, project), new LicenseFormatter(context, project), new PackageJsonFormatter(context, project), new TemplatesFormatter(context, project), new GitignoreFormatter(context, project), new TsconfigFormatter(context, project), new PrettierFormatter(context, project), new ReadmeFormatter(context, project), new CopyFormatter(context, project), ]; // Filter formatters based on configuration const activeFormatters = formatters.filter(formatter => { if (formatConfig.modules.only.length > 0) { return formatConfig.modules.only.includes(formatter.name); } if (formatConfig.modules.skip.includes(formatter.name)) { return false; } return true; }); // Plan phase logger.log('info', 'Analyzing project for format operations...'); let plan = options.fromPlan ? JSON.parse(await plugins.smartfile.fs.toStringSync(options.fromPlan)) : await planner.planFormat(activeFormatters); // Display plan await planner.displayPlan(plan, options.detailed); // Save plan if requested if (options.savePlan) { await plugins.smartfile.memory.toFs(JSON.stringify(plan, null, 2), options.savePlan); logger.log('info', `Plan saved to ${options.savePlan}`); } // Exit if plan-only mode if (options.planOnly) { return; } // Dry-run mode if (options.dryRun) { logger.log('info', 'Dry-run mode - no changes will be made'); return; } // Interactive confirmation if (interactive && !autoApprove) { const interactInstance = new plugins.smartinteract.SmartInteract(); const response = await interactInstance.askQuestion({ type: 'confirm', name: 'proceed', message: 'Proceed with formatting?', default: true }); if (!(response as any).proceed) { logger.log('info', 'Format operation cancelled by user'); return; } } // Execute phase logger.log('info', `Executing format operations${parallel ? ' in parallel' : ' sequentially'}...`); await planner.executePlan(plan, activeFormatters, context, parallel); // Finish statistics tracking context.getFormatStats().finish(); // Display statistics const showStats = npmextraConfig.dataFor('gitzone.format.showStats', true); if (showStats) { context.getFormatStats().displayStats(); } // Save stats if requested if (options.detailed) { const statsPath = `.nogit/format-stats-${Date.now()}.json`; await context.getFormatStats().saveReport(statsPath); } logger.log('success', 'Format operations completed successfully!'); } catch (error) { logger.log('error', `Format operation failed: ${error.message}`); // Automatic rollback if enabled if (formatConfig.rollback.enabled && formatConfig.rollback.autoRollbackOnError) { logger.log('info', 'Attempting automatic rollback...'); try { await context.rollbackOperation(); logger.log('success', 'Rollback completed successfully'); } catch (rollbackError) { logger.log('error', `Rollback failed: ${rollbackError.message}`); } } throw error; } }; // Export CLI command handlers export const handleRollback = async (operationId?: string): Promise<void> => { const context = new FormatContext(); const rollbackManager = context.getRollbackManager(); if (!operationId) { // Rollback to last operation const backups = await rollbackManager.listBackups(); const lastOperation = backups .filter(op => op.status !== 'rolled-back') .sort((a, b) => b.timestamp - a.timestamp)[0]; if (!lastOperation) { logger.log('warn', 'No operations available for rollback'); return; } operationId = lastOperation.id; } try { await rollbackManager.rollback(operationId); logger.log('success', `Successfully rolled back operation ${operationId}`); } catch (error) { logger.log('error', `Rollback failed: ${error.message}`); throw error; } }; export const handleListBackups = async (): Promise<void> => { const context = new FormatContext(); const rollbackManager = context.getRollbackManager(); const backups = await rollbackManager.listBackups(); if (backups.length === 0) { logger.log('info', 'No backup operations found'); return; } console.log('\nAvailable backups:'); console.log('━'.repeat(50)); for (const backup of backups) { const date = new Date(backup.timestamp).toLocaleString(); const status = backup.status; const filesCount = backup.files.length; console.log(`ID: ${backup.id}`); console.log(`Date: ${date}`); console.log(`Status: ${status}`); console.log(`Files: ${filesCount}`); console.log('─'.repeat(50)); } }; export const handleCleanBackups = async (): Promise<void> => { const context = new FormatContext(); const rollbackManager = context.getRollbackManager(); // Get retention days from config const npmextraConfig = new plugins.npmextra.Npmextra(); const retentionDays = npmextraConfig.dataFor<any>('gitzone.format.rollback.backupRetentionDays', 7); await rollbackManager.cleanOldBackups(retentionDays); logger.log('success', `Cleaned backups older than ${retentionDays} days`); };