/** * Migration Reporter * Story 1.3: Content Migration Script * * Generates migration reports in JSON and Markdown formats */ import type { MigrationReport, CollectionMigrationResult } from './types' import { writeFile, mkdir } from 'fs/promises' import { join } from 'path' import { existsSync } from 'fs' // ============================================================ // REPORT GENERATION // ============================================================ /** * Create a new empty report */ export function createReport(dryRun: boolean = false): MigrationReport { return { timestamp: new Date().toISOString(), dryRun, summary: { total: 0, created: 0, skipped: 0, failed: 0, }, byCollection: {}, details: {}, } } /** * Update report with collection results */ export function updateReport( report: MigrationReport, collectionResult: CollectionMigrationResult, ): MigrationReport { const { collection, created, skipped, failed } = collectionResult // Update byCollection stats report.byCollection[collection] = { created, skipped, failed } // Store details report.details[collection] = collectionResult // Update summary report.summary.total += created + skipped + failed report.summary.created += created report.summary.skipped += skipped report.summary.failed += failed return report } /** * Generate markdown report */ export function generateMarkdownReport(report: MigrationReport): string { const lines: string[] = [] lines.push(`# Migration Report`) lines.push(``) lines.push(`**Generated:** ${new Date(report.timestamp).toLocaleString('zh-TW')}`) lines.push(`**Mode:** ${report.dryRun ? '๐Ÿงช Dry Run (no changes made)' : 'โœ… Live Migration'}`) lines.push(``) lines.push(`---`) lines.push(``) // Summary section lines.push(`## Summary`) lines.push(``) lines.push(`| Metric | Count |`) lines.push(`|--------|-------|`) lines.push(`| Total Items | ${report.summary.total} |`) lines.push(`| โœ… Created | ${report.summary.created} |`) lines.push(`| โญ๏ธ Skipped | ${report.summary.skipped} |`) lines.push(`| โŒ Failed | ${report.summary.failed} |`) lines.push(``) // By collection section lines.push(`## By Collection`) lines.push(``) for (const [collection, stats] of Object.entries(report.byCollection)) { lines.push(`### ${collection.charAt(0).toUpperCase() + collection.slice(1)}`) lines.push(``) lines.push(`| Metric | Count |`) lines.push(`|--------|-------|`) lines.push(`| Created | ${stats.created} |`) lines.push(`| Skipped | ${stats.skipped} |`) lines.push(`| Failed | ${stats.failed} |`) lines.push(``) } // Details section if (report.details) { lines.push(`## Details`) lines.push(``) for (const [collection, result] of Object.entries(report.details)) { lines.push(`### ${collection.charAt(0).toUpperCase() + collection.slice(1)}`) lines.push(``) // Created items if (result.results.some((r) => r.success)) { lines.push(`#### โœ… Created (${result.results.filter((r) => r.success).length})`) lines.push(``) for (const item of result.results.filter((r) => r.success)) { lines.push(`- \`${item.slug}\` (ID: ${item.id})`) } lines.push(``) } // Failed items if (result.results.some((r) => !r.success)) { lines.push(`#### โŒ Failed (${result.results.filter((r) => !r.success).length})`) lines.push(``) for (const item of result.results.filter((r) => !r.success)) { lines.push(`- \`${item.slug}\`: ${item.error}`) } lines.push(``) } } } return lines.join('\n') } /** * Save report to files */ export async function saveReport( report: MigrationReport, outputDir: string = './reports', ): Promise { // Ensure directory exists if (!existsSync(outputDir)) { await mkdir(outputDir, { recursive: true }) } const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0] const baseName = `migration-${timestamp}` // Save JSON report const jsonPath = join(outputDir, `${baseName}.json`) await writeFile(jsonPath, JSON.stringify(report, null, 2), 'utf-8') // Save Markdown report const mdPath = join(outputDir, `${baseName}.md`) await writeFile(mdPath, generateMarkdownReport(report), 'utf-8') console.log(`\n๐Ÿ“„ Reports saved:`) console.log(` - JSON: ${jsonPath}`) console.log(` - Markdown: ${mdPath}`) } /** * Print report summary to console */ export function printReportSummary(report: MigrationReport): void { console.log(`\n${'='.repeat(60)}`) console.log(`๐Ÿ“Š MIGRATION REPORT`) console.log(`${'='.repeat(60)}`) console.log(``) console.log(`Mode: ${report.dryRun ? '๐Ÿงช Dry Run' : 'โœ… Live'}`) console.log(``) console.log(`Summary:`) console.log(` Total: ${report.summary.total}`) console.log(` Created: ${report.summary.created} โœ…`) console.log(` Skipped: ${report.summary.skipped} โญ๏ธ`) console.log(` Failed: ${report.summary.failed} โŒ`) console.log(``) if (report.byCollection) { console.log(`By Collection:`) for (const [collection, stats] of Object.entries(report.byCollection)) { console.log( ` ${collection}: ${stats.created} created, ${stats.skipped} skipped, ${stats.failed} failed`, ) } } console.log(`${'='.repeat(60)}`) }