UNPKG

easy-tinypng-cli

Version:

A cli can be used to compress images with tinypng.com

178 lines (177 loc) 6.23 kB
import path from 'node:path'; import { cwd } from 'node:process'; import fs from 'node:fs'; import tinify from 'tinify'; import chokidar from 'chokidar'; import fse from 'fs-extra'; import figlet from 'figlet'; import chalk from 'chalk'; import ora from 'ora'; import { Command } from 'commander'; import fg from 'fast-glob'; const program = new Command(); program .option('-o, --once', 'compress just once'); program.parse(process.argv); let compressedImagesNumber = 0; let needCompressImagesNumber = 0; const options = program.opts(); // eslint-disable-next-line no-console const log = console.log; const RecordFilePath = path.resolve(cwd(), 'record.json'); function defineTinyConfig(config) { return config; } export { defineTinyConfig, }; export function getAllConfigs(config) { return config.configs; } export async function startOptimize(configs, APIKey) { tinify.key = APIKey; await ConsoleFigFont('tinypng cli is running !!!'); for (let i = 0; i < configs.length; i++) { const { targetDir } = configs[i]; const count = await getTargetFileImagesCount(targetDir); needCompressImagesNumber += count; } if (!needCompressImagesNumber) { console.warn('There is no images need to compress!'); process.exit(0); } for (let i = 0; i < configs.length; i++) { const { targetDir } = configs[i]; log(chalk.bgBlue.bold(`${targetDir} watching~~~\n`)); chokidar.watch(path.resolve(cwd(), targetDir), { atomic: true, followSymlinks: true, }).on('all', async (event, pathDir) => { switch (event) { case 'add': await reduceImage(targetDir, pathDir, pathDir); break; case 'unlink': autoRecord(targetDir, event, pathDir); break; case 'change': default: break; } }); } } export function isPathDirectory(path) { return fs.statSync(path)?.isDirectory(); } export function isFileExist(pathDir) { return new Promise((resolve) => { fs.access(pathDir, fs.constants.F_OK, (err) => { resolve(!err); }); }); } export async function isRecord(watchFileDir, pathDir) { const isExist = await isFileExist(RecordFilePath); if (isExist) { const json = fse.readJSONSync(RecordFilePath); const isRecord = Object.prototype.hasOwnProperty.call(json, path.relative(watchFileDir, pathDir)); return isRecord; } else { return false; } } export async function record(watchFileDir, pathDir) { const isExist = await isFileExist(RecordFilePath); if (isExist) { const json = fse.readJSONSync(RecordFilePath); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error json[path.relative(watchFileDir, pathDir)] = path.relative(watchFileDir, pathDir); fse.writeJSONSync(RecordFilePath, json, { spaces: 2 }); } else { fse.writeJSONSync(RecordFilePath, { [path.relative(watchFileDir, pathDir)]: path.relative(watchFileDir, pathDir) }, { spaces: 2 }); } } export async function removeRecord(watchFileDir, pathDir) { const RecordFilePath = path.resolve(cwd(), 'record.json'); const isExist = await isFileExist(RecordFilePath); if (isExist) { const json = fse.readJSONSync(RecordFilePath); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error delete json[path.relative(watchFileDir, pathDir)]; fse.writeJSONSync(RecordFilePath, json, { spaces: 2 }); } } export function getFileName(pathDir) { return path.basename(pathDir); } export function getExtName(pathDir) { return path.extname(pathDir).slice(1); } export async function autoRecord(watchFileDir, action, pathDir) { if (!isImageFile(pathDir)) return; if (action === 'add') await record(watchFileDir, pathDir); if (action === 'unlink') await removeRecord(watchFileDir, pathDir); } export function isImageFile(pathDir) { const fileExtname = getExtName(pathDir); const supportFiles = ['webp', 'jpeg', 'png', 'jpg']; return supportFiles.includes(fileExtname); } export async function reduceImage(watchFileDir, fileDir, targetDir) { const recorded = await isRecord(watchFileDir, fileDir); if (recorded) { compressedImagesNumber++; if (compressedImagesNumber === needCompressImagesNumber && options.once) { console.warn(chalk.bgGreen.bold('\nAll images have been compressed!')); process.exit(0); } return; } if (!isImageFile(fileDir)) return; const spinner = ora('Loading').start(); try { spinner.color = 'blue'; spinner.text = chalk.bold.greenBright(`compressing ${fileDir}`); tinify.fromFile(fileDir).toFile(targetDir).then(async () => { await autoRecord(watchFileDir, 'add', fileDir); compressedImagesNumber++; spinner.succeed(); if (compressedImagesNumber === needCompressImagesNumber && options.once) { console.warn(chalk.bgGreen.bold('\nAll images have been compressed!')); process.exit(0); } }); } catch (err) { spinner.fail(); } } export function ConsoleFigFont(str) { return new Promise((resolve, reject) => { figlet(str, (err, data) => { if (err) { console.warn('Something went wrong...'); console.warn(err); reject(err); } console.warn(data); resolve(''); }); }); } export async function getTargetFileImagesCount(targetDir) { const patternArray = [ `${path.join(process.cwd(), targetDir, '/**/*.png')}`.replace(/\\/g, '/'), `${path.join(process.cwd(), targetDir, '/**/*.jpeg')}`.replace(/\\/g, '/'), `${path.join(process.cwd(), targetDir, '/**/*.jpg')}`.replace(/\\/g, '/'), ]; const entries = await fg(patternArray, { dot: true }); return entries.length; }