125 lines
4.4 KiB
TypeScript
125 lines
4.4 KiB
TypeScript
import type { OutputAsset, OutputBundle, OutputOptions } from "rollup";
|
|
import * as cliProgress from "cli-progress";
|
|
import fs from "fs";
|
|
import path from "path";
|
|
import sharp from "sharp";
|
|
|
|
interface Manifest {
|
|
[key: string]: {
|
|
file: string;
|
|
src: string;
|
|
};
|
|
}
|
|
|
|
type convertImagesToWebpType = {
|
|
name: string;
|
|
apply: string;
|
|
generateBundle: (_: OutputOptions, bundle: OutputBundle) => Promise<void>;
|
|
closeBundle: () => void;
|
|
}
|
|
|
|
/** @var string[] imageExtensions Formats accepted for the .webp convertion. */
|
|
const imageExtensions = [".jpg", ".jpeg", ".png"];
|
|
|
|
export default function convertImagesToWebp(): convertImagesToWebpType {
|
|
return {
|
|
name: "convert-images-to-webp",
|
|
apply: "build",
|
|
/**
|
|
* Generate all webp images during the compilation.
|
|
*
|
|
* @param _ OutputOptions
|
|
* @param bundle OutputBundle
|
|
* @return Promise<void>
|
|
*/
|
|
generateBundle: async (_: OutputOptions, bundle: OutputBundle): Promise<void> => {
|
|
const generatedFiles: string[] = [];
|
|
const imageFiles = Object.entries(bundle).filter(([fileName, asset]) => {
|
|
const ext = path.extname(fileName).toLowerCase();
|
|
return asset.type === "asset" && imageExtensions.includes(ext);
|
|
});
|
|
|
|
console.log("\ngenerate webp images:");
|
|
|
|
// Init progress bar.
|
|
const bar = new cliProgress.SingleBar({
|
|
format: "{bar} {percentage}%",
|
|
barCompleteChar: "\u2588",
|
|
barIncompleteChar: "\u2591",
|
|
}, cliProgress.Presets.shades_classic);
|
|
bar.start(imageFiles.length, 0);
|
|
|
|
// Convert each files into webp and save a copy to the same path.
|
|
for (const [fileName, asset] of imageFiles) {
|
|
const ext = path.extname(fileName).toLowerCase();
|
|
const assetPath = path.resolve("public/build", fileName);
|
|
const webpPath = assetPath.replace(ext, ".webp");
|
|
|
|
const dir = path.dirname(webpPath);
|
|
fs.mkdirSync(dir, { recursive: true });
|
|
|
|
try {
|
|
await sharp(Buffer.from((asset as OutputAsset).source))
|
|
.webp()
|
|
.toFile(webpPath);
|
|
|
|
generatedFiles.push(webpPath);
|
|
} catch (err) {
|
|
console.error(`error conversion WebP for ${fileName} :`, err);
|
|
}
|
|
|
|
// Increase the status progress bar.
|
|
bar.increment();
|
|
}
|
|
|
|
// Stop the progress bar and notify terminal user.
|
|
bar.stop();
|
|
console.log(`\x1b[32m✓ \x1b[37m${generatedFiles.length} webp images created.`);
|
|
},
|
|
/**
|
|
* Update the manifest.json after the compilation.
|
|
*
|
|
* @returns void
|
|
*/
|
|
closeBundle: (): void => {
|
|
// Get the manifest.json file.
|
|
const manifestPath = path.resolve("public/build/manifest.json");
|
|
|
|
if (!fs.existsSync(manifestPath)) {
|
|
console.error("manifest.json doesn't exist");
|
|
return;
|
|
}
|
|
|
|
// Get all entries from the manifest.json file.
|
|
const manifest: Manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
const entries = Object.entries(manifest);
|
|
const filesToUpdate: string[] = [];
|
|
|
|
// Duplicate each image entries for its equivalent in .webp format.
|
|
for (const [originalSrc, entry] of entries) {
|
|
const { file } = entry;
|
|
const ext = path.extname(file).toLowerCase();
|
|
|
|
if (!imageExtensions.includes(ext)) continue;
|
|
|
|
const webpFile = file.replace(ext, ".webp");
|
|
const webpPath = path.resolve("public/build", webpFile);
|
|
|
|
if (fs.existsSync(webpPath)) {
|
|
const webpKey = originalSrc.replace(ext, ".webp");
|
|
|
|
manifest[webpKey] = {
|
|
file: webpFile,
|
|
src: webpKey,
|
|
};
|
|
|
|
filesToUpdate.push(webpKey);
|
|
}
|
|
}
|
|
|
|
// Save the manifest.json updated and notify terminal user.
|
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
console.log("\x1b[32m✓ \x1b[37mmanifest.json updated");
|
|
},
|
|
};
|
|
}
|