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.

285 lines (246 loc) 9.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 { BaseFormatter } from './classes.baseformatter.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: { write?: boolean; // Explicitly write changes (default: false, dry-mode) dryRun?: boolean; // Deprecated, kept for compatibility yes?: boolean; planOnly?: boolean; savePlan?: string; fromPlan?: string; detailed?: boolean; interactive?: boolean; parallel?: boolean; verbose?: boolean; diff?: boolean; // Show file diffs } = {}, ): Promise<any> => { // Set verbose mode if requested if (options.verbose) { setVerboseMode(true); } // Determine if we should write changes // Default is dry-mode (no writing) unless --write/-w is specified const shouldWrite = options.write ?? (options.dryRun === false); const project = await Project.fromCwd({ requireProjectType: false }); const context = new FormatContext(); // Cache system removed - no longer needed const planner = new FormatPlanner(); // Get configuration from npmextra const npmextraConfig = new plugins.npmextra.Npmextra(); const formatConfig = npmextraConfig.dataFor<any>('@git.zone/cli.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 }, }); // Cache cleaning removed - no longer using cache system // 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.smartfs .file(options.fromPlan) .encoding('utf8') .read()) as string, ) : await planner.planFormat(activeFormatters); // Display plan await planner.displayPlan(plan, options.detailed); // Save plan if requested if (options.savePlan) { await plugins.smartfs .file(options.savePlan) .encoding('utf8') .write(JSON.stringify(plan, null, 2)); logger.log('info', `Plan saved to ${options.savePlan}`); } // Exit if plan-only mode if (options.planOnly) { return; } // Show diffs if requested (works in both dry-run and write modes) if (options.diff) { logger.log('info', 'Showing file diffs:'); console.log(''); for (const formatter of activeFormatters) { const checkResult = await formatter.check(); if (checkResult.hasDiff) { logger.log('info', `[${formatter.name}]`); formatter.displayAllDiffs(checkResult); console.log(''); } } } // Dry-run mode (default behavior) if (!shouldWrite) { logger.log('info', 'Dry-run mode - use --write (-w) to apply changes'); 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).value) { 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}`); // Rollback system has been removed for stability throw error; } }; // Export CLI command handlers export const handleRollback = async (operationId?: string): Promise<void> => { logger.log('info', 'Rollback system has been disabled for stability'); }; export const handleListBackups = async (): Promise<void> => { logger.log('info', 'Backup system has been disabled for stability'); }; export const handleCleanBackups = async (): Promise<void> => { logger.log( 'info', 'Backup cleaning has been disabled - backup system removed', ); }; // Import the ICheckResult type for external use import type { ICheckResult } from './interfaces.format.js'; export type { ICheckResult }; // Formatters that don't require projectType to be set const formattersNotRequiringProjectType = ['npmextra', 'prettier', 'cleanup', 'packagejson']; /** * Run a single formatter by name (for use by other modules) */ export const runFormatter = async ( formatterName: string, options: { silent?: boolean; checkOnly?: boolean; // Only check for diffs, don't apply showDiff?: boolean; // Show the diff output } = {} ): Promise<ICheckResult | void> => { // Determine if this formatter requires projectType const requireProjectType = !formattersNotRequiringProjectType.includes(formatterName); const project = await Project.fromCwd({ requireProjectType }); const context = new FormatContext(); // Map formatter names to classes const formatterMap: Record<string, new (ctx: FormatContext, proj: Project) => BaseFormatter> = { cleanup: CleanupFormatter, npmextra: NpmextraFormatter, license: LicenseFormatter, packagejson: PackageJsonFormatter, templates: TemplatesFormatter, gitignore: GitignoreFormatter, tsconfig: TsconfigFormatter, prettier: PrettierFormatter, readme: ReadmeFormatter, copy: CopyFormatter, }; const FormatterClass = formatterMap[formatterName]; if (!FormatterClass) { throw new Error(`Unknown formatter: ${formatterName}`); } const formatter = new FormatterClass(context, project); // Check-only mode: just check for diffs and optionally display them if (options.checkOnly) { const result = await formatter.check(); if (result.hasDiff && options.showDiff) { formatter.displayAllDiffs(result); } return result; } // Normal mode: analyze and apply changes const changes = await formatter.analyze(); for (const change of changes) { await formatter.applyChange(change); } if (!options.silent) { logger.log('success', `Formatter '${formatterName}' completed`); } };