Files
LivingDexTracker/scripts/optimize-sprites.mjs
T
Josh Creek 9ddca18f54 revert(sprites): keep the original 512px sprites
The resize to 192px isn't worthwhile now that sprites are only downloaded
when viewed or saved on request, and then kept forever. Restore the
original files and the script's no-resize default, drop the detail view
size cap, and regenerate the sprite manifest with the original sizes.
2026-09-14 16:53:31 +01:00

75 lines
2.3 KiB
JavaScript

#!/usr/bin/env node
import path from 'node:path';
import process from 'node:process';
import { mkdir, readdir, rename } from 'node:fs/promises';
import sharp from 'sharp';
import { writeSpriteManifest } from './sprite-manifest.mjs';
const inputDir = process.env.SPRITE_INPUT_DIR ?? path.join(process.cwd(), 'static', 'sprites');
const outputDir =
process.env.SPRITE_OUTPUT_DIR ?? path.join(process.cwd(), 'static', 'sprites-small');
const format = (process.env.SPRITE_FORMAT ?? 'webp').toLowerCase();
const quality = Number(process.env.SPRITE_QUALITY ?? 80);
const maxSize = Number(process.env.SPRITE_MAX_SIZE ?? 0);
if (!['png', 'webp'].includes(format)) {
console.error(`Unsupported SPRITE_FORMAT "${format}". Use "png" or "webp".`);
process.exit(1);
}
async function walk(dir, files = []) {
const entries = await readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
await walk(fullPath, files);
continue;
}
if (entry.isFile() && entry.name.toLowerCase().endsWith('.png')) {
files.push(fullPath);
}
}
return files;
}
const files = await walk(inputDir);
console.log(`Optimizing ${files.length} sprite images from ${inputDir}`);
for (const [index, file] of files.entries()) {
const relative = path.relative(inputDir, file);
const baseName = path.basename(file, path.extname(file));
const targetDir = path.join(outputDir, path.dirname(relative));
await mkdir(targetDir, { recursive: true });
const targetExt = format === 'png' ? '.png' : '.webp';
const targetPath = path.join(targetDir, `${baseName}${targetExt}`);
const tempPath = format === 'png' && outputDir === inputDir ? `${targetPath}.tmp` : targetPath;
const pipeline = sharp(file);
if (maxSize > 0) {
pipeline.resize({
width: maxSize,
height: maxSize,
fit: 'inside',
withoutEnlargement: true
});
}
if (format === 'png') {
await pipeline.png({ compressionLevel: 9, palette: true, quality }).toFile(tempPath);
} else {
await pipeline.webp({ quality }).toFile(tempPath);
}
if (tempPath !== targetPath) {
await rename(tempPath, targetPath);
}
if ((index + 1) % 250 === 0) {
console.log(`Processed ${index + 1}/${files.length}`);
}
}
const manifest = await writeSpriteManifest(outputDir);
console.log(`Sprite optimization complete. Manifest lists ${manifest.files.length} sprites.`);