UNPKG

easy-tinypng-new-cli

Version:

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

202 lines (201 loc) 7.24 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'; import { getHashOfFile } from './util.js'; const recordName = 'tinyRecordList.json'; 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(), recordName); function defineTinyConfig(config) { return config; } // 根据文件内容获取hash const getFileHash = (filePath) => { const hash = getHashOfFile(filePath); // 只取前6位 return hash.slice(0, 6); // log(chalk.bgBlue.bold(`${hash}-${filePath} watching2~~~\n`)) // return hash }; 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: Object = fse.readJSONSync(RecordFilePath) // log(chalk.bgBlue.bold(`${RecordFilePath}-${pathDir} watching1~~~\n`)) // const isRecord = Object.prototype.hasOwnProperty.call(json, getFileHash(pathDir)) const list = fse.readJSONSync(RecordFilePath) || []; const isRecord = list.includes(getFileHash(pathDir)); return isRecord; } else { return false; } } export async function record(watchFileDir, pathDir) { const isExist = await isFileExist(RecordFilePath); if (isExist) { // const json: Object = fse.readJSONSync(RecordFilePath) // json[getFileHash(pathDir)] = getFileHash(pathDir) // fse.writeJSONSync(RecordFilePath, json, { spaces: 2 }) const list = fse.readJSONSync(RecordFilePath) || []; list.push(getFileHash(pathDir)); fse.writeJSONSync(RecordFilePath, list, { spaces: 2 }); } else { // fse.writeJSONSync(RecordFilePath, { [getFileHash(pathDir)]: getFileHash(pathDir) }, { spaces: 2 }) const list = [getFileHash(pathDir)]; fse.writeJSONSync(RecordFilePath, list, { spaces: 2 }); } } export async function removeRecord(watchFileDir, pathDir) { const RecordFilePath = path.resolve(cwd(), recordName); const isExist = await isFileExist(RecordFilePath); if (isExist) { // const json: Object = fse.readJSONSync(RecordFilePath) // // eslint-disable-next-line @typescript-eslint/ban-ts-comment // // @ts-expect-error // delete json[getFileHash(pathDir)] // fse.writeJSONSync(RecordFilePath, json, { spaces: 2 }) const list = fse.readJSONSync(RecordFilePath) || []; // 删除数组中的一项 const index = list.indexOf(getFileHash(pathDir)); // 找到index并删除 if (index > -1) { list.splice(index, 1); // 会改变原数组 fse.writeJSONSync(RecordFilePath, list, { 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; }