import { BaseFormatter } from '../classes.baseformatter.js'; import type { IPlannedChange } from '../interfaces.format.js'; import * as plugins from '../mod.plugins.js'; import { logger, logVerbose } from '../../gitzone.logging.js'; interface ICopyPattern { from: string; to: string; preservePath?: boolean; } export class CopyFormatter extends BaseFormatter { get name(): string { return 'copy'; } async analyze(): Promise { const changes: IPlannedChange[] = []; // Get copy configuration from npmextra.json const npmextraConfig = new plugins.npmextra.Npmextra(); const copyConfig = npmextraConfig.dataFor<{ patterns: ICopyPattern[] }>( 'gitzone.format.copy', { patterns: [] }, ); if (!copyConfig.patterns || copyConfig.patterns.length === 0) { logVerbose('No copy patterns configured in npmextra.json'); return changes; } for (const pattern of copyConfig.patterns) { if (!pattern.from || !pattern.to) { logVerbose('Invalid copy pattern - missing "from" or "to" field'); continue; } try { // Handle glob patterns const entries = await plugins.smartfs .directory('.') .recursive() .filter(pattern.from) .list(); const files = entries.map((entry) => entry.path); for (const file of files) { const sourcePath = file; let destPath = pattern.to; // If destination is a directory, preserve filename if (pattern.to.endsWith('/')) { const filename = plugins.path.basename(file); destPath = plugins.path.join(pattern.to, filename); } // Handle template variables in destination path if (pattern.preservePath) { const relativePath = plugins.path.relative( plugins.path.dirname(pattern.from.replace(/\*/g, '')), file, ); destPath = plugins.path.join(pattern.to, relativePath); } // Read source content const content = (await plugins.smartfs .file(sourcePath) .encoding('utf8') .read()) as string; // Check if destination exists and has same content let needsCopy = true; const destExists = await plugins.smartfs.file(destPath).exists(); if (destExists) { const existingContent = (await plugins.smartfs .file(destPath) .encoding('utf8') .read()) as string; if (existingContent === content) { needsCopy = false; } } if (needsCopy) { changes.push({ type: destExists ? 'modify' : 'create', path: destPath, module: this.name, description: `Copy from ${sourcePath}`, content: content, }); } } } catch (error) { logVerbose(`Failed to process pattern ${pattern.from}: ${error.message}`); } } return changes; } async applyChange(change: IPlannedChange): Promise { if (!change.content) return; // Ensure destination directory exists const destDir = plugins.path.dirname(change.path); await plugins.smartfs.directory(destDir).recursive().create(); if (change.type === 'create') { await this.createFile(change.path, change.content); } else { await this.modifyFile(change.path, change.content); } logger.log('info', `Copied to ${change.path}`); } }