tinify-client
Version:
A CLI to compress your images not only intelligently but also to the EXTREME!
250 lines (249 loc) • 9.86 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const ora_1 = __importDefault(require("ora"));
const path_1 = require("path");
const child_process_1 = require("child_process");
// @ts-ignore
const cli_aid_1 = require("cli-aid");
// If converted to import
// build error will emit 'Cannot find module '../package.json'. Consider using '--resolveJsonModule' to import module with '.json' extension'
// after resolveJsonModule and rooDir set to src in tsconfig.json
// new error emitted
// > 'rootDir' is expected to contain all source files.
// import pkg from '../package.json';
//
// so I kept the require way 😓 though pkg will be typed if import in the `import` way
// Not good solution found in https://github.com/microsoft/TypeScript/issues/9858
const pkg = require('../package.json');
const constants_1 = require("./constants");
const colors_1 = require("./constants/colors");
const image_1 = require("./utils/image");
const lite_lodash_1 = require("./utils/lite-lodash");
const lite_fs_1 = require("./utils/lite-fs");
const executeBase64Command_1 = require("./commands/executeBase64Command");
const copyBase64_1 = require("./utils/copyBase64");
const decorated_console_1 = require("./utils/decorated-console");
const i18n_1 = require("./i18n");
const compressBatchWrapper_1 = require("./lib/compressBatchWrapper");
const compress_1 = require("./lib/compress");
const tinify_1 = __importDefault(require("tinify"));
const setKey_1 = require("./commands/setKey");
const colorize_1 = require("./utils/colorize");
// console.log('process.argv.slice(2):', process.argv.slice(2));
// process.exit(0)
let cmdExecuting = false;
const params = new cli_aid_1.CLI()
.package(pkg)
.usage('npx tinify-client IMG_URL_OR_LOCAL_IMG_PATH [OPTIONS]')
.option('key', 'k', { help: 'The Tinify key. Accessible at https://tinypng.com/developers.' })
.option('src', { help: 'Image url or local image path to compress.' })
.option('output', 'o', { help: 'The compressed image file path.' })
.option('max-count', 'm', { default: 15, help: 'The max compressing turns. Default 15.' })
.option('in-place', 'i', { default: false, help: 'Overwrite the original image. Default false' })
.option('verbose', { default: false, help: 'Show more information about each compressing turn.' })
.option('no-base64', { default: false, help: 'Not output the base64 of the compressed image. base64 encoded by default.' })
.option('dry-run', { default: false, help: 'Does everything compress would do except actually compressing. Reports the details of what would have been compressed' })
.option('open-dir-after-compressed', { default: true, help: 'Should open the compressed image\'s directory after compressed.' })
.option('debug', 'd', { help: 'Show the parsed CLI params.' })
.option('show-quota', 'quota', 'q', { help: 'Show compressions you have made this month.' })
.command('base64', {
usage: constants_1.BASE64_USAGE,
help: 'Output base64-encoded string of the input image.',
}, async (options) => {
cmdExecuting = true;
try {
await executeBase64Command_1.executeBase64Command(options);
}
finally {
process.exit(0);
}
})
.command('set-key', {
usage: constants_1.USAGE_SET_KEY,
help: 'Output base64-encoded string of the input image.',
}, async (options) => {
cmdExecuting = true;
try {
await setKey_1.setKey(options);
}
catch (error) {
console.error('setKey failed', error);
}
finally {
process.exit(0);
}
})
.parse(process.argv.slice(2));
if (params.debug) {
console.log('params:', params);
process.exit(0);
}
const srcList = params._;
const verbose = params.verbose;
const noBase64 = params['no-base64'];
const inPlace = params['in-place'];
const maxCount = params['max-count'];
const dryRun = params['dry-run'];
const shouldShowQuota = params['show-quota'];
const batch = process.env.BATCH === 'true';
verbose && console.log('batch:', batch);
let output = params.output;
if (verbose) {
console.log('process.argv.slice(2):', process.argv.slice(2));
console.log('params:', params);
}
const dictionary = i18n_1.i18n();
async function main() {
const keyFromCli = params.key;
const TINIFY_KEY = process.env.TINIFY_KEY;
let key = keyFromCli || (TINIFY_KEY && TINIFY_KEY.length > 1 ? TINIFY_KEY : '');
if (key || cmdExecuting) {
// no need to check the key
}
else {
try {
const config = require(constants_1.configFile);
key = config.key;
}
catch (error) {
// do nothing
}
if (!key) {
printHowToGetKey();
return;
}
}
verbose && console.log('key:', key);
tinify_1.default.key = key;
if (shouldShowQuota) {
await showQuota(tinify_1.default);
return;
}
const src = params.src || srcList[0];
if (!src) {
console.log(colors_1.YELLOW);
console.error('image src required. Example: tinify-client IMG_URL_OR_LOCAL_IMG');
console.log(colors_1.EOS);
return;
}
if (lite_fs_1.isDirectory(src)) {
return await compressBatchWrapper_1.compressBatchWrapper(src, Object.assign(Object.assign({}, params), { tinify: tinify_1.default }));
}
else {
const isImage = (endpoint) => /(?:png|jpg)$/.test(endpoint) || image_1.isRemoteFile(endpoint);
const images = srcList.filter(isImage);
if (images.length > 1) {
return await compressBatchWrapper_1.compressBatchWrapper(images, Object.assign(Object.assign({}, params), { tinify: tinify_1.default }));
}
}
let timer;
let milliseconds = 0;
let spinner;
if (!batch) {
console.log();
spinner = ora_1.default(`${dictionary.compressing}... ${lite_lodash_1.timeToReadable(milliseconds)} 🚀`).start();
const GAP = 100;
timer = setInterval(() => {
milliseconds += GAP;
spinner.text = `${dictionary.compressing}... ${lite_lodash_1.timeToReadable(milliseconds)} 🚀`;
}, 1 * GAP);
}
verbose && console.time(colors_1.GREEN + ` ${dictionary.genTotalTimeCostsTips(src)}` + colors_1.EOS);
let result;
try {
result = await compress_1.compress(src, {
tinify: tinify_1.default,
output,
verbose,
inPlace,
maxCount,
dryRun,
});
}
catch (error) {
// @ts-ignore
spinner === null || spinner === void 0 ? void 0 : spinner.fail(dictionary.compressFailed);
console.error(colors_1.RED, 'compress failed:', error);
return;
}
finally {
if (batch) {
return;
}
// @ts-ignore
timer && clearInterval(timer);
if (verbose) {
console.timeEnd(colors_1.GREEN + ` ${dictionary.genTotalTimeCostsTips(src)}` + colors_1.EOS);
console.log();
}
}
// @ts-ignore
spinner === null || spinner === void 0 ? void 0 : spinner.succeed(dictionary.compressed + ` ${lite_lodash_1.timeToReadable(milliseconds)} ✨`);
const { sizes, hasCompressedToExtreme, dest } = result;
const [sizeBefore, sizeAfter] = lite_lodash_1.last(sizes);
const lastDiff = sizeBefore - sizeAfter;
if (verbose && !hasCompressedToExtreme) {
console.log();
console.log(colors_1.YELLOW, `${lastDiff} Bytes reduced in the last turn though it's no less than the delta ${compress_1.DELTA} Bytes,`, `but the loop has reached it's limit ${maxCount}.`, 'Compress Aborted.', colors_1.EOS);
}
report(result);
if (params['open-dir-after-compressed']) {
open(path_1.dirname(dest));
}
}
function open(path) {
try {
child_process_1.execSync(`open ${path}`);
}
catch (error) {
console.log(error);
}
}
// console.log('base64CmdExecuting:', base64CmdExecuting);
if (!cmdExecuting) {
main();
}
async function report({ dest, sizes, costs }) {
console.log(colors_1.YELLOW);
console.table([compress_1.summarize({ dest, sizes, costs })]);
console.log();
if (noBase64) {
return;
}
const base64 = await image_1.imageToBase64(dest);
if (!base64) {
return;
}
// console.log('sizes:', sizes);
copyBase64_1.copyBase64(base64, { verbose: verbose || lite_lodash_1.last(sizes)[1] < 1024 });
decorated_console_1.decorated.success('The compressed image\'s base64 has been copied to your clipboard.');
}
async function showQuota(tinifyWithKey) {
try {
await tinifyWithKey.validate();
}
catch (error) {
console.error('show quota failed:', error);
return;
}
const compressionsThisMonth = tinifyWithKey.compressionCount || 0;
console.log();
console.log(colorize_1.chalk.green('Quota is'), 500, colorize_1.chalk.green('per month. Compressions you have made this month:'), compressionsThisMonth, ',', 500 - compressionsThisMonth, 'left');
console.log(colorize_1.chalk.italic('Read more at https://tinypng.com/developers'));
console.log();
return;
}
function printHowToGetKey() {
console.error(`${colors_1.YELLOW}key required. Get your key at`, `${colors_1.GREEN}https://tinypng.com/developers${colors_1.EOS}\n`);
console.log('Then run the one line code below to set it\n');
console.log('```sh');
console.log(colorize_1.chalk.green(constants_1.USAGE_SET_KEY));
console.log('```');
console.log();
console.log(`Or set key in the cli params for once: $ ${colors_1.GREEN}tinify --key YOUR_API_KEY IMG_URL_OR_LOCAL_IMG`);
console.log(colors_1.EOS);
}