tinify-client
Version:
A CLI to compress your images not only intelligently but also to the EXTREME!
138 lines (137 loc) • 5.47 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.summarize = exports.compress = exports.DELTA = void 0;
const path_1 = require("path");
const image_to_base64_1 = require("@legend80s/image-to-base64");
const colors_1 = require("../constants/colors");
const image_1 = require("../utils/image");
const lite_fs_1 = require("../utils/lite-fs");
// import { timeToReadable } from '../utils/lite-lodash';
const number_1 = require("../utils/number");
const lite_lodash_1 = require("../utils/lite-lodash");
const i18n_1 = require("../i18n");
const dictionary = i18n_1.i18n();
exports.DELTA = 1;
async function compress(src, { tinify, output, verbose, inPlace, maxCount, dryRun, }) {
const startAt = Date.now();
if (!output) {
output = await resolveOutput(src, { verbose, inPlace });
}
else if (lite_fs_1.isDirectory(output)) {
let filename = path_1.basename(src);
if (image_to_base64_1.isRemoteFile(src)) {
const ext = await image_to_base64_1.resolveExtFromRemote(src);
if (ext) {
filename = `${filename}.${ext}`;
}
}
output = path_1.join(output, filename);
// console.log('isDirectory output:', output);
}
// return;
// const tmpFiles = [];
const sizes = [];
let tmpSrc = src;
if (dryRun) {
return { sizes: new Array(maxCount).fill([0, 0]), dest: output, hasCompressedToExtreme: false, costs: Date.now() - startAt };
}
for (let i = 0; i < maxCount; ++i) {
// const tmpOutput = output.replace(/(-\d)*.png/, `-${idx+1}.png`);
const { sizeBefore, sizeAfter } = await minify(tmpSrc, { dest: output, verbose, tinify });
const diff = sizeBefore - sizeAfter;
tmpSrc = output;
sizes.push([sizeBefore, sizeAfter]);
if (diff <= exports.DELTA) {
if (verbose) {
console.log(colors_1.GREEN, `${diff} Bytes reduced in the last turn and it is less than the delta ${exports.DELTA} Bytes. Compressing is ready to abort.`, colors_1.EOS);
}
return { sizes, dest: output, hasCompressedToExtreme: true, costs: Date.now() - startAt };
}
}
return { sizes, dest: output, hasCompressedToExtreme: false, costs: Date.now() - startAt };
}
exports.compress = compress;
async function minify(src, { dest, verbose = false, tinify }) {
verbose && console.time(colors_1.GREEN + ' compress ' + src + ' costs' + colors_1.EOS);
const sizeBefore = await image_1.getImageSize(src);
try {
await (image_to_base64_1.isRemoteFile(src) ? tinify.fromUrl(src) : tinify.fromFile(src)).toFile(dest);
}
catch (error) {
console.error(colors_1.RED, 'tinify failed:', error);
console.log(colors_1.EOS);
throw error;
}
finally {
verbose && console.timeEnd(colors_1.GREEN + ' compress ' + src + ' costs' + colors_1.EOS);
}
// console.log(GREEN, 'compressed successfully');
// console.log('', src, 'has been optimized to', dest);
// console.log(EOS);
// console.time(' getImageSize costs')
try {
const sizeAfter = await image_1.getImageSize(dest);
const diff = sizeBefore - sizeAfter;
const percentage = `${number_1.getPercentageOff(sizeBefore, sizeAfter)}%`;
verbose && console.log(colors_1.GREEN, `Images compressed successfully: before ${sizeBefore} Bytes after ${sizeAfter} Bytes, ${diff} Bytes (${percentage}) off.`, colors_1.EOS);
// console.timeEnd(' getImageSize costs')
return { sizeBefore, sizeAfter };
}
catch (error) {
console.error(colors_1.RED, 'getImageSize failed:', error, colors_1.EOS);
throw error;
}
}
/**
* @param {string} endpoint local image path or
*/
async function resolveOutput(endpoint, { verbose = false, inPlace = false } = {}) {
const label = colors_1.GREEN + `no output resolve filename from url ` + endpoint + ' costs' + colors_1.EOS;
verbose && console.time(label);
let filename = '';
try {
filename = await resolveFilenameFromEndpoint(endpoint, { inPlace });
}
finally {
verbose && console.timeEnd(label);
}
const output = filename;
verbose && console.log('output after resolved:', output);
return output;
}
/**
* @param {string} endpoint
* @returns {Promise<string>}
*/
async function resolveFilenameFromEndpoint(endpoint, { inPlace = false } = {}) {
var _a;
if (!image_to_base64_1.isRemoteFile(endpoint)) {
return inPlace ?
endpoint :
// a => a-compressed
// a.png => a-compressed.png
endpoint.replace(/(\.\w+)?$/, (_m, $1) => `-compressed${$1 || ''}`);
}
const filename = ((_a = endpoint.split('/').pop()) === null || _a === void 0 ? void 0 : _a.replace(/\?.*/, '')) || endpoint;
if (/\.\w+$/.test(filename)) {
return filename;
}
const ext = await image_to_base64_1.resolveExtFromRemote(endpoint);
if (ext) {
return `${filename}.${ext}`;
}
return filename;
}
function summarize({ dest, sizes, costs }) {
const firstTurn = sizes[0];
const lastTurn = lite_lodash_1.last(sizes);
return dictionary.summarize({
dest,
beforeSizeInByte: firstTurn[0],
afterSizeInByte: lastTurn[1],
nTurns: sizes.length,
lastTurnDelta: lastTurn[0] - lastTurn[1],
costs,
});
}
exports.summarize = summarize;