import chalk from 'chalk'; import { SingleBar, Presets } from 'cli-progress'; export type UploadStatus = 'success' | 'failed' | 'skipped'; export interface UploadFileResult { localPath: string; relativePath: string; bucket: string; key: string; status: UploadStatus; error?: Error; accessUrl?: string; } export interface UploadSummary { total: number; success: string[]; failed: string[]; skipped: string[]; context?: { bucket: string; suffix: string; localDir: string; cdnBaseUrl?: string; }; } export interface Reporter { onStart?(context: { env?: string; bucket: string; suffix: string; localDir: string; cdnBaseUrl?: string; }): void | Promise; onProgress?(current: number, total: number): void | Promise; onFileResult?(result: UploadFileResult): void | Promise; onComplete?(summary: UploadSummary): void | Promise; } export class ConsoleReporter implements Reporter { private progressBar: SingleBar | null = null; onStart(_context: { env?: string; bucket: string; suffix: string; localDir: string; cdnBaseUrl?: string; }): void {} onProgress(current: number, total: number): void { if (this.progressBar === null) { this.progressBar = new SingleBar( { format: ' Upload progress |{bar}| {percentage}% | {value}/{total} files', barCompleteChar: '\u2588', barIncompleteChar: '\u2591', hideCursor: true, }, Presets.shades_classic, ); this.progressBar.start(total, 0); } this.progressBar.update(current); if (current >= total) { this.progressBar.stop(); this.progressBar = null; } } onFileResult(_result: UploadFileResult): void {} onComplete(summary: UploadSummary): void { const { total, success, failed, skipped, context } = summary; console.log(chalk.bold.cyan('\n Upload Summary')); if (context) { console.log(chalk.bold(' Context')); console.log(` bucket: ${context.bucket}`); console.log(` suffix: ${context.suffix}`); console.log(` localDir: ${context.localDir}`); if (context.cdnBaseUrl) { console.log(` cdnBaseUrl: ${context.cdnBaseUrl}`); } console.log(''); } console.log( ` Total: ${chalk.bold(total)} ` + chalk.green(`Success: ${success.length}`) + ` ` + chalk.gray(`Skipped: ${skipped.length}`) + ` ` + (failed.length > 0 ? chalk.red.bold(`Failed: ${failed.length}`) : `Failed: ${failed.length}`), ); if (success.length > 0) { console.log(chalk.green(`\n Success (${success.length})`)); success.forEach((item) => console.log(chalk.green(` ${item}`))); } if (skipped.length > 0) { console.log(chalk.gray(`\n Skipped (${skipped.length})`)); skipped.forEach((item) => console.log(chalk.gray(` ${item}`))); } if (failed.length > 0) { console.log(chalk.red(`\n Failed (${failed.length})`)); failed.forEach((item) => console.log(chalk.red(` ${item}`))); } console.log(''); } } export class ScriptReporter implements Reporter { onStart(_context: { env?: string; bucket: string; suffix: string; localDir: string; cdnBaseUrl?: string; }): void {} onProgress(current: number, total: number): void { process.stderr.write(` Upload progress ${current}/${total} files\n`); } onFileResult(_result: UploadFileResult): void {} onComplete(summary: UploadSummary): void { const { total, success, failed, skipped } = summary; process.stderr.write( `\n Upload summary Total: ${total} Success: ${success.length} Skipped: ${skipped.length} Failed: ${failed.length}\n`, ); } }