import { BaseFormatter } from '../classes.baseformatter.js'; import type { IPlannedChange } from '../interfaces.format.js'; import * as plugins from '../mod.plugins.js'; import * as paths from '../../paths.js'; import { logger, logVerbose } from '../../gitzone.logging.js'; export class TemplatesFormatter extends BaseFormatter { get name(): string { return 'templates'; } async analyze(): Promise { const changes: IPlannedChange[] = []; const project = this.project; const projectType = project.gitzoneConfig?.data?.projectType; // VSCode template - for all projects const vscodeChanges = await this.analyzeTemplate('vscode', [ { templatePath: '.vscode/settings.json', destPath: '.vscode/settings.json' }, { templatePath: '.vscode/launch.json', destPath: '.vscode/launch.json' }, ]); changes.push(...vscodeChanges); // CI and other templates based on projectType switch (projectType) { case 'npm': case 'wcc': const accessLevel = project.gitzoneConfig?.data?.npmciOptions?.npmAccessLevel; const ciTemplate = accessLevel === 'public' ? 'ci_default' : 'ci_default_private'; const ciChanges = await this.analyzeTemplate(ciTemplate, [ { templatePath: '.gitea/workflows/default_nottags.yaml', destPath: '.gitea/workflows/default_nottags.yaml' }, { templatePath: '.gitea/workflows/default_tags.yaml', destPath: '.gitea/workflows/default_tags.yaml' }, ]); changes.push(...ciChanges); break; case 'service': case 'website': const dockerCiChanges = await this.analyzeTemplate('ci_docker', [ { templatePath: '.gitea/workflows/docker_nottags.yaml', destPath: '.gitea/workflows/docker_nottags.yaml' }, { templatePath: '.gitea/workflows/docker_tags.yaml', destPath: '.gitea/workflows/docker_tags.yaml' }, ]); changes.push(...dockerCiChanges); const dockerfileChanges = await this.analyzeTemplate('dockerfile_service', [ { templatePath: 'Dockerfile', destPath: 'Dockerfile' }, { templatePath: 'dockerignore', destPath: '.dockerignore' }, ]); changes.push(...dockerfileChanges); const cliChanges = await this.analyzeTemplate('cli', [ { templatePath: 'cli.js', destPath: 'cli.js' }, { templatePath: 'cli.ts.js', destPath: 'cli.ts.js' }, ]); changes.push(...cliChanges); break; } // Update templates based on projectType if (projectType === 'website') { const websiteChanges = await this.analyzeTemplate('website_update', [ { templatePath: 'html/index.html', destPath: 'html/index.html' }, ]); changes.push(...websiteChanges); } else if (projectType === 'service') { const serviceChanges = await this.analyzeTemplate('service_update', []); changes.push(...serviceChanges); } else if (projectType === 'wcc') { const wccChanges = await this.analyzeTemplate('wcc_update', [ { templatePath: 'html/index.html', destPath: 'html/index.html' }, { templatePath: 'html/index.ts', destPath: 'html/index.ts' }, ]); changes.push(...wccChanges); } return changes; } private async analyzeTemplate( templateName: string, files: Array<{ templatePath: string; destPath: string }>, ): Promise { const changes: IPlannedChange[] = []; const templateDir = plugins.path.join(paths.templatesDir, templateName); // Check if template exists const templateExists = await plugins.smartfs.directory(templateDir).exists(); if (!templateExists) { logVerbose(`Template ${templateName} not found`); return changes; } for (const file of files) { const templateFilePath = plugins.path.join(templateDir, file.templatePath); const destFilePath = file.destPath; // Check if template file exists const fileExists = await plugins.smartfs.file(templateFilePath).exists(); if (!fileExists) { logVerbose(`Template file ${templateFilePath} not found`); continue; } try { // Read template content const templateContent = (await plugins.smartfs .file(templateFilePath) .encoding('utf8') .read()) as string; // Check if destination file exists const destExists = await plugins.smartfs.file(destFilePath).exists(); let currentContent = ''; if (destExists) { currentContent = (await plugins.smartfs .file(destFilePath) .encoding('utf8') .read()) as string; } // Only add change if content differs if (templateContent !== currentContent) { changes.push({ type: destExists ? 'modify' : 'create', path: destFilePath, module: this.name, description: `Apply template ${templateName}/${file.templatePath}`, content: templateContent, }); } } catch (error) { logVerbose(`Failed to read template ${templateFilePath}: ${error.message}`); } } return changes; } async applyChange(change: IPlannedChange): Promise { if (!change.content) return; // Ensure destination directory exists const destDir = plugins.path.dirname(change.path); if (destDir && destDir !== '.') { 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', `Applied template to ${change.path}`); } }