UNPKG

image-asset-manager

Version:

A comprehensive image asset management tool for frontend projects

235 lines 9.76 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.BatchOptimizer = void 0; const ImageOptimizer_js_1 = require("./ImageOptimizer.js"); const fs = __importStar(require("fs/promises")); const path = __importStar(require("path")); class BatchOptimizer { constructor() { this.optimizer = new ImageOptimizer_js_1.ImageOptimizer(); } async batchOptimize(files, options) { const startTime = Date.now(); const concurrency = options.concurrency || 3; const results = []; const errors = []; let completed = 0; let failed = 0; const updateProgress = (currentFile) => { const progress = { total: files.length, completed, failed, percentage: Math.round(((completed + failed) / files.length) * 100), currentFile, }; options.onProgress?.(progress); }; // Process files in batches with concurrency control for (let i = 0; i < files.length; i += concurrency) { const batch = files.slice(i, i + concurrency); const batchPromises = batch.map(async (file) => { try { updateProgress(file.name); // Handle original file preservation if (options.keepOriginal && !options.outputDir) { // Create backup if keeping original and no output directory specified await this.createBackup(file); } const result = await this.optimizer.optimizeImage(file, options); if (result.success) { completed++; options.onFileComplete?.(result); } else { failed++; errors.push({ file, error: result.error || "Unknown error" }); } return result; } catch (error) { failed++; const errorMessage = error instanceof Error ? error.message : String(error); errors.push({ file, error: errorMessage }); return { originalFile: file, optimizedPath: "", originalSize: file.size, optimizedSize: file.size, compressionRatio: 0, success: false, error: errorMessage, }; } }); const batchResults = await Promise.all(batchPromises); results.push(...batchResults); updateProgress(); } const endTime = Date.now(); const processingTime = endTime - startTime; // Calculate statistics const successfulResults = results.filter((r) => r.success); const totalOriginalSize = results.reduce((sum, r) => sum + r.originalSize, 0); const totalOptimizedSize = successfulResults.reduce((sum, r) => sum + r.optimizedSize, 0); const totalSavedBytes = totalOriginalSize - totalOptimizedSize; const averageCompressionRatio = successfulResults.length > 0 ? successfulResults.reduce((sum, r) => sum + r.compressionRatio, 0) / successfulResults.length : 0; return { totalFiles: files.length, successfulFiles: completed, failedFiles: failed, totalOriginalSize, totalOptimizedSize, totalSavedBytes, averageCompressionRatio, processingTime, results, errors, }; } async previewBatchOptimization(files, options) { const previews = []; for (const file of files) { try { const preview = await this.optimizer.previewOptimization(file, options); previews.push({ file, preview }); } catch (error) { // Skip files that can't be previewed console.warn(`Failed to preview ${file.name}:`, error); } } const totalOriginalSize = files.reduce((sum, f) => sum + f.size, 0); const totalEstimatedSize = previews.reduce((sum, p) => sum + p.preview.estimatedSize, 0); const totalEstimatedSavings = totalOriginalSize - totalEstimatedSize; const totalEstimatedRatio = totalOriginalSize > 0 ? (totalEstimatedSavings / totalOriginalSize) * 100 : 0; // Estimate processing time based on file count and sizes const estimatedProcessingTime = this.estimateProcessingTime(files); return { files: previews, totalEstimatedSavings, totalEstimatedRatio, estimatedProcessingTime, }; } async generateOptimizationReport(report, outputPath) { const reportContent = this.formatReport(report); if (outputPath) { await fs.writeFile(outputPath, reportContent); return outputPath; } // Default report location const defaultPath = path.join(process.cwd(), "optimization-report.txt"); await fs.writeFile(defaultPath, reportContent); return defaultPath; } async createBackup(file) { const backupPath = this.getBackupPath(file.path); await this.ensureDirectoryExists(path.dirname(backupPath)); await fs.copyFile(file.path, backupPath); } getBackupPath(originalPath) { const ext = path.extname(originalPath); const nameWithoutExt = path.basename(originalPath, ext); const dir = path.dirname(originalPath); return path.join(dir, `${nameWithoutExt}.backup${ext}`); } estimateProcessingTime(files) { // Rough estimation: 100ms per MB of image data const totalSizeMB = files.reduce((sum, f) => sum + f.size, 0) / (1024 * 1024); // Ensure we return at least 1ms even for very small files return Math.max(1, Math.round(totalSizeMB * 100)); } formatReport(report) { const lines = [ "# Image Optimization Report", `Generated: ${new Date().toISOString()}`, "", "## Summary", `Total Files: ${report.totalFiles}`, `Successful: ${report.successfulFiles}`, `Failed: ${report.failedFiles}`, `Processing Time: ${(report.processingTime / 1000).toFixed(2)}s`, "", "## Size Statistics", `Original Total Size: ${this.formatBytes(report.totalOriginalSize)}`, `Optimized Total Size: ${this.formatBytes(report.totalOptimizedSize)}`, `Total Saved: ${this.formatBytes(report.totalSavedBytes)}`, `Average Compression: ${report.averageCompressionRatio.toFixed(2)}%`, "", "## File Results", ]; // Add successful optimizations const successful = report.results.filter((r) => r.success); if (successful.length > 0) { lines.push("", "### Successfully Optimized"); successful.forEach((result) => { lines.push(`- ${result.originalFile.name}: ${this.formatBytes(result.originalSize)}${this.formatBytes(result.optimizedSize)} (${result.compressionRatio.toFixed(1)}% saved)`); }); } // Add errors if (report.errors.length > 0) { lines.push("", "### Errors"); report.errors.forEach((error) => { lines.push(`- ${error.file.name}: ${error.error}`); }); } return lines.join("\n"); } formatBytes(bytes) { if (bytes === 0) return "0 B"; const k = 1024; const sizes = ["B", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`; } async ensureDirectoryExists(dirPath) { try { await fs.access(dirPath); } catch { await fs.mkdir(dirPath, { recursive: true }); } } } exports.BatchOptimizer = BatchOptimizer; //# sourceMappingURL=BatchOptimizer.js.map