www-new/scripts/optimize-images.ts

98 lines
3.0 KiB
TypeScript
Raw Normal View History

2021-10-05 01:10:52 -04:00
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
// TODO: upgrade libsquoosh once types are available: https://github.com/GoogleChromeLabs/squoosh/issues/1077
import { cpus } from "os";
2021-10-05 01:10:52 -04:00
import path from "path";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import { ImagePool } from "@squoosh/lib";
import fse from "fs-extra";
2021-10-14 23:59:45 -04:00
import { default as getImageDimensions } from "image-size";
2021-10-05 01:10:52 -04:00
const IMAGES_SOURCE_DIRECTORY = "images";
const IMAGES_DESTINATION_DIRECTORY = "public/images";
void optimizeImages();
2021-10-05 01:10:52 -04:00
2021-10-14 23:59:45 -04:00
const IMAGE_MINIMUM_SIZE = 300;
2021-10-14 23:32:38 -04:00
const IMAGE_ENCODE_OPTIONS = { mozjpeg: {} };
2021-10-14 23:59:45 -04:00
const GET_CODEC_FROM_EXTENSION: { [imageExtension: string]: string } = {
2021-10-14 23:32:38 -04:00
jpg: "mozjpeg",
jpeg: "mozjpeg",
};
2021-10-05 01:10:52 -04:00
export async function optimizeImages() {
const imagePaths = await getFilePathsInDirectory(IMAGES_SOURCE_DIRECTORY);
await fse.emptyDir(IMAGES_DESTINATION_DIRECTORY);
2021-10-05 01:10:52 -04:00
const imagePool = new ImagePool(cpus().length);
2021-10-05 01:10:52 -04:00
2021-10-14 22:44:57 -04:00
await Promise.all(
imagePaths.map(async (imagePath) => {
const sourcePath = path.join(IMAGES_SOURCE_DIRECTORY, imagePath);
const destinationPath = path.join(
IMAGES_DESTINATION_DIRECTORY,
imagePath
);
2021-10-14 23:32:38 -04:00
const fileExtension = imagePath.split(".").pop() ?? "";
2021-10-14 23:59:45 -04:00
if (!GET_CODEC_FROM_EXTENSION[fileExtension]) {
2021-10-14 22:44:57 -04:00
await fse.copy(sourcePath, destinationPath);
return;
}
2021-10-05 01:10:52 -04:00
2021-10-14 22:44:57 -04:00
const rawImageFile = await fse.readFile(sourcePath);
const ingestedImage = imagePool.ingestImage(rawImageFile);
2021-10-14 23:59:45 -04:00
const { width, height } = getImageDimensions(rawImageFile);
2021-10-05 01:10:52 -04:00
await ingestedImage.decoded;
2021-10-14 23:59:45 -04:00
if (width && height) {
const resizeEnabled =
width > IMAGE_MINIMUM_SIZE && height > IMAGE_MINIMUM_SIZE;
const smallerDimension = width < height ? "width" : "height";
// specifying only one dimension maintains the aspect ratio
const preprocessOptions = {
resize: {
enabled: resizeEnabled,
[smallerDimension]: IMAGE_MINIMUM_SIZE,
},
};
await ingestedImage.preprocess(preprocessOptions);
2021-10-14 23:59:45 -04:00
}
await ingestedImage.encode(IMAGE_ENCODE_OPTIONS);
2021-10-05 01:10:52 -04:00
const encodedImage = await ingestedImage.encodedWith[
GET_CODEC_FROM_EXTENSION[fileExtension]
];
await fse.outputFile(destinationPath, encodedImage.binary);
2021-10-14 22:44:57 -04:00
})
);
2021-10-05 01:10:52 -04:00
await imagePool.close();
2021-10-05 01:10:52 -04:00
}
async function getFilePathsInDirectory(directory: string): Promise<string[]> {
const entries = await fse.readdir(directory, { withFileTypes: true });
2021-10-05 01:10:52 -04:00
return (
await Promise.all(
entries.map(async (entry) => {
if (entry.isDirectory()) {
const subdirectory = path.join(directory, entry.name);
const subentries = await getFilePathsInDirectory(subdirectory);
return subentries.map((subentry) => path.join(entry.name, subentry));
2021-10-05 01:10:52 -04:00
}
return entry.name;
2021-10-05 01:10:52 -04:00
})
)
).flat();
}