From d9f45d4d528d04f9a3c02a0544f186851667b159 Mon Sep 17 00:00:00 2001 From: VolodymyrBg Date: Thu, 16 Jan 2025 20:32:42 +0200 Subject: [PATCH] fix: add error handling and temp file cleanup in PNG optimization This PR improves the reliability of PNG image optimization by adding proper error handling and temporary file cleanup. Changes made: - Added try-catch block around PNG optimization logic - Implemented cleanup of temporary files in case of errors - Ensures temporary files are not left on disk even if optimization fails - Maintains original error propagation for proper error reporting This change prevents potential disk space leaks and improves the robustness of the image optimization process. --- _api/src/optimizeImages.ts | 58 ++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/_api/src/optimizeImages.ts b/_api/src/optimizeImages.ts index e6d1d31..5be69b4 100644 --- a/_api/src/optimizeImages.ts +++ b/_api/src/optimizeImages.ts @@ -1,9 +1,9 @@ -import * as fs from "fs" -import * as path from "path" -import chalk from "chalk" -import { v4 as uuidv4 } from "uuid" -import sharp from "sharp" -import { optimize } from "svgo" +import { readFileSync, writeFileSync, unlinkSync, renameSync } from 'node:fs' +import { extname, relative } from 'node:path' +import chalk from 'chalk' +import { v4 as uuidv4 } from 'uuid' +import sharp from 'sharp' +import { optimize } from 'svgo' import { getFilePathsInDirectory, getFileSize, isDirectory } from "./utils" // Constants for image size limits @@ -12,10 +12,10 @@ const PNG_SIZE_LIMIT = 50 * 1024 // 50KB const PNG_MAX_HEIGHT = 256 // 256px // Image file extensions to optimize -const imageExtensions = [".png", ".svg"] +const imageExtensions: readonly string[] = [".png", ".svg"] as const // Optimize images in the directory -export async function optimizeImages(dir: string) { +export async function optimizeImages(dir: string): Promise { const files = getFilePathsInDirectory(dir) for (const file of files) { @@ -24,7 +24,7 @@ export async function optimizeImages(dir: string) { continue } - const ext = path.extname(file).toLowerCase() + const ext = extname(file).toLowerCase() if (!imageExtensions.includes(ext)) { continue } @@ -42,43 +42,53 @@ export async function optimizeImages(dir: string) { } const newSize = getFileSize(file) - const relativePath = path.relative(dir, file) + const relativePath = relative(dir, file) console.log(`Optimized ${relativePath}: ${originalSize} bytes -> ${newSize} bytes`) if (ext === ".svg" && newSize > SVG_SIZE_LIMIT) { console.warn(chalk.yellow(`Deleting oversized SVG: ${relativePath}`)) - fs.unlinkSync(file) + unlinkSync(file) } if (ext === ".png" && newSize > PNG_SIZE_LIMIT) { console.warn(chalk.yellow(`Deleting oversized PNG: ${relativePath}`)) - fs.unlinkSync(file) + unlinkSync(file) } } } // Optimize PNG file -async function optimizePng(filePath: string) { +async function optimizePng(filePath: string): Promise { const image = sharp(filePath) const metadata = await image.metadata() // Use a temporary file for the output const tempFilePath = `${filePath}.${uuidv4()}.tmp` - if (metadata.height && metadata.height > PNG_MAX_HEIGHT) { - await image.resize({ height: PNG_MAX_HEIGHT }).toFile(tempFilePath) - } else { - const data = await image.toBuffer() - await sharp(data).toFile(tempFilePath) - } + try { + if (metadata.height && metadata.height > PNG_MAX_HEIGHT) { + await image.resize({ height: PNG_MAX_HEIGHT }).toFile(tempFilePath) + } else { + const data = await image.toBuffer() + await sharp(data).toFile(tempFilePath) + } - // Replace the original file with the optimized file - fs.renameSync(tempFilePath, filePath) + // Replace the original file with the optimized file + renameSync(tempFilePath, filePath) + } catch (error) { + // Clean up temporary file if it exists + try { + unlinkSync(tempFilePath) + } catch { + // Ignore error if temp file doesn't exist + } + throw error // Re-throw the original error + } } // Optimize SVG file -function optimizeSvg(filePath: string) { - const data = fs.readFileSync(filePath, "utf8") +function optimizeSvg(filePath: string): void { + const data = readFileSync(filePath, "utf8") const result = optimize(data, { path: filePath }) - fs.writeFileSync(filePath, result.data) + writeFileSync(filePath, result.data) }