image-asset-manager
Version:
A comprehensive image asset management tool for frontend projects
235 lines • 9.76 kB
JavaScript
;
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