UNPKG

tinify-client

Version:

A CLI to compress your images not only intelligently but also to the EXTREME!

250 lines (249 loc) 9.86 kB
#!/usr/bin/env node "use strict"; 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); }