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 { CleanupFormatter } from './formatters/cleanup.formatter.js'; import { SmartconfigFormatter } from './formatters/smartconfig.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'; // Shared formatter class map used by both run() and runFormatter() const formatterMap: Record BaseFormatter> = { cleanup: CleanupFormatter, smartconfig: SmartconfigFormatter, license: LicenseFormatter, packagejson: PackageJsonFormatter, templates: TemplatesFormatter, gitignore: GitignoreFormatter, tsconfig: TsconfigFormatter, prettier: PrettierFormatter, readme: ReadmeFormatter, copy: CopyFormatter, }; // Formatters that don't require projectType to be set const formattersNotRequiringProjectType = ['smartconfig', 'prettier', 'cleanup', 'packagejson']; export let run = async ( options: { write?: boolean; dryRun?: boolean; // Deprecated, kept for compatibility yes?: boolean; planOnly?: boolean; savePlan?: string; fromPlan?: string; detailed?: boolean; interactive?: boolean; verbose?: boolean; diff?: boolean; } = {}, ): Promise => { if (options.verbose) { setVerboseMode(true); } const shouldWrite = options.write ?? (options.dryRun === false); const project = await Project.fromCwd({ requireProjectType: false }); const context = new FormatContext(); const planner = new FormatPlanner(); const smartconfigInstance = new plugins.smartconfig.Smartconfig(); const formatConfig = smartconfigInstance.dataFor('@git.zone/cli.format', { interactive: true, showDiffs: false, autoApprove: false, modules: { skip: [], only: [], }, }); const interactive = options.interactive ?? formatConfig.interactive; const autoApprove = options.yes ?? formatConfig.autoApprove; try { // Initialize formatters in execution order const formatters = Object.entries(formatterMap).map( ([, FormatterClass]) => new FormatterClass(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}`); } if (options.planOnly) { return; } // Show diffs if explicitly requested or before interactive write confirmation const showDiffs = options.diff || (shouldWrite && interactive && !autoApprove); if (showDiffs) { 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...'); await planner.executePlan(plan, activeFormatters, context); context.getFormatStats().finish(); const showStats = smartconfigInstance.dataFor('gitzone.format.showStats', true); if (showStats) { context.getFormatStats().displayStats(); } 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}`); throw error; } }; import type { ICheckResult } from './interfaces.format.js'; export type { ICheckResult }; /** * Run a single formatter by name (for use by other modules) */ export const runFormatter = async ( formatterName: string, options: { silent?: boolean; checkOnly?: boolean; showDiff?: boolean; } = {} ): Promise => { const requireProjectType = !formattersNotRequiringProjectType.includes(formatterName); const project = await Project.fromCwd({ requireProjectType }); const context = new FormatContext(); const FormatterClass = formatterMap[formatterName]; if (!FormatterClass) { throw new Error(`Unknown formatter: ${formatterName}`); } const formatter = new FormatterClass(context, project); if (options.checkOnly) { const result = await formatter.check(); if (result.hasDiff && options.showDiff) { formatter.displayAllDiffs(result); } return result; } const changes = await formatter.analyze(); for (const change of changes) { await formatter.applyChange(change); } if (!options.silent) { logger.log('success', `Formatter '${formatterName}' completed`); } };