Skip to content

Instantly share code, notes, and snippets.

@11Firefox11
Created May 12, 2023 15:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 11Firefox11/b371c3f6ad3acc8213772260fd8973e9 to your computer and use it in GitHub Desktop.
Save 11Firefox11/b371c3f6ad3acc8213772260fd8973e9 to your computer and use it in GitHub Desktop.
Responsive image generation with an astro hook
import type { AstroIntegration } from "astro";
import sharp from 'sharp';
import path from 'path';
import fs from 'fs';
type imageSize = { width: number, height: number };
export default function createPlugin(): AstroIntegration {
return {
name: "ImageCompressorIntegration",
hooks: {
"astro:build:generated": async (options: { dir: URL }) => {
const distPath = options.dir.pathname;
const allFiles = readDirectoryRecursive(path.join(distPath, "thumbnail"));
for (const [i, imgPath] of allFiles.entries()) {
await ImageGenerator.processImage(imgPath, ImageGenerator.thumbnailMetaSizes, true, true);
console.log(`${path.basename(imgPath)} image sizes generated (${i+1}/${allFiles.length})`);
}
}
}
};
}
function readDirectoryRecursive(dir: string, filelist: string[] = []) {
const files = fs.readdirSync(dir);
files.forEach((file) => {
const filePath = path.join(dir, file);
if (fs.statSync(filePath).isDirectory()) {
filelist = readDirectoryRecursive(filePath, filelist);
} else {
filelist.push(filePath);
}
});
return filelist;
}
export class ImageGenerator {
public static readonly thumbnailMetaSizes = [
{ width: 1200, height: 630 },
{ width: 600, height: 315 },
{ width: 360, height: 189 },
{ width: 736, height: 414 },
];
static generateName(filePath: string, size: imageSize, isWebp: boolean = false) {
const extension = (isWebp ? ".webp" : path.extname(filePath));
const justName = path.join(path.dirname(filePath), path.basename(filePath, path.extname(filePath)));
return [justName, String(size.width), String(size.height)].join("-") + extension;
}
static async processImage(filepath: string, sizes: imageSize[], needWebp: boolean = false, removeOriginal: boolean = false) {
const image = sharp(filepath);
for (const size of sizes) {
const resizedImage = image.clone().resize(size.width, size.height, { fit: 'cover', withoutEnlargement: true });
for (const isWebp of (needWebp ? [true, false] : [false])) {
const outPath = ImageGenerator.generateName(filepath, size, isWebp);
await resizedImage.toFormat((isWebp ? "webp" : path.extname(filepath).slice(1) as any), { quality: 100, progressive: true }).withMetadata().toFile(outPath);
}
}
if (removeOriginal) {
fs.rm(filepath, () => { });
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment