grunt-sharp-optimize
180 строк · 5.1 Кб
1'use strict'
2
3const sharp = require('sharp'),4globParent = require('glob-parent'),5fs = require('fs-extra'),6path = require('path')7
8const
9ALLOWED_EXTENSIONS = [10'gif',11'png',12'jpg', 'jpeg',13'webp',14'avif',15'tiff',16'heif',17],18DEFAULT_CONVERSION_OPTIONS = {19quality: 90,20lossless: false,21chromaSubsampling: '4:2:0',22},23DEFAULT_SHARP_OPTIONS = {24animated: true,25limitInputPixels: false,26}27
28let logLevel, sharpOptions, destCwd, sourceCwd29var chalk30
31module.exports = function (grunt) {32grunt.task.registerMultiTask('sharp', 'Convert and optimize images with Sharp.',33async function () {34let35done = this.async(),36options = this.options()37
38chalk = await getChalkModule()39
40logLevel = options.logLevel ?? 'small'41sharpOptions = options.sharpOptions ?? {}42destCwd = this.files[0].dest43sourceCwd = globParent(this.data.src)44
45delete options.logLevel46delete options.sharpOptions47
48
49for (let fileSrc of this.filesSrc) {50let51parsedFile = path.parse(fileSrc),52fileExtname = parsedFile.ext.replace('.', ''),53fileName = parsedFile.name,54fileBase = parsedFile.base,55fileCwd = parsedFile.dir.replace(sourceCwd, ''),56
57pathToDist = createPathToNewFileInDist(fileName, fileExtname, fileCwd)58
59fs.ensureDirSync(pathToDist.replace(parsedFile.base, ''))60
61if (!extnamesIsCorrect(fileExtname)) {62if (fs.existsSync(pathToDist)) continue63
64fs.copySync(fileSrc, pathToDist)65
66logAboutSuccessfulCopy(fileBase)67continue68}69
70for (let [conversionRule, conversionRuleOptions] of Object.entries(options)) {71let [convertFrom, convertTo] = conversionRule.split('_to_')72
73if (!extnamesIsCorrect(convertFrom, convertTo)) {74console.error(chalk.red.bold('Invalid name of an conversion rule! Make sure you have spelled the extension names correctly.'))75
76return done(false)77}78
79
80// Checking that the file has not a suitable extension81if (convertTo && convertFrom != fileExtname) continue82
83//? For general conversion rules such as png: {}84if (!convertTo) {85let newFilePath = createPathToNewFileInDist(fileName, convertFrom, fileCwd)86
87// Checking that the file has already been created88if (fs.existsSync(newFilePath)) continue89
90await convert(fileSrc, convertFrom, conversionRuleOptions, newFilePath)91logAboutSuccessfulConversion(fileBase, fileName, convertFrom)92
93if (conversionRuleOptions.alsoProcessOriginal) {94let newFilePath = createPathToNewFileInDist(fileName, fileExtname, fileCwd)95
96if (fs.existsSync(newFilePath)) continue97
98await convert(fileSrc, fileExtname, conversionRuleOptions, newFilePath)99logAboutSuccessfulConversion(fileBase, fileName, fileExtname)100}101}102//? For specific conversion rules such as png_to_webp: {}103else {104let newFilePath = createPathToNewFileInDist(fileName, convertTo, fileCwd)105
106if (fs.existsSync(newFilePath)) continue107
108await convert(fileSrc, convertFrom, convertTo, conversionRuleOptions, newFilePath)109logAboutSuccessfulConversion(fileBase, fileName, convertTo)110}111}112}113
114return done(true)115}116)117
118async function convert(filePath, oldFileFormat, newFileFormat, options, newFilePath) {119if (newFileFormat == 'heif' && !options.compression)120options.compression = 'av1'121else if (oldFileFormat == 'heif' && !options.compression)122options.compression = 'jpeg'123
124let sharpInstance =125await sharp(filePath, Object.assign(DEFAULT_SHARP_OPTIONS, sharpOptions))126.toFormat(newFileFormat, Object.assign(DEFAULT_CONVERSION_OPTIONS, options))127
128fs.createFileSync(newFilePath)129
130await sharpInstance.toFile(newFilePath)131}132}
133
134function logAboutSuccessfulConversion(fileBase, fileName, newFileExtname) {135if (logLevel == 'full')136console.log(137'The file ' + chalk.green(sourceCwd + fileBase)138+ ' was processed to '139+ chalk.green(destCwd + fileName + '.' + chalk.bold(newFileExtname))140)141else if (logLevel == 'small')142console.log(143chalk.green(fileBase) + ' => ' + chalk.green.bold(newFileExtname)144)145}
146function logAboutSuccessfulCopy(fileBase) {147if (logLevel == 'full')148console.log(chalk.hex('#FF8800')149(150`The image ${chalk.bold(fileBase)} cannot be processed, so it is copied to dest.`151)152)153else if (logLevel == 'small')154console.log(chalk.hex('#FF8800')155(fileBase + ' => ' + chalk.bold('copied'))156)157}
158
159function createPathToNewFileInDist(fileName, newFileExt, fileCwd) {160return path.resolve(`${destCwd}/${fileCwd}/${fileName}.${newFileExt}`)161}
162
163function extnamesIsCorrect(...extnames) {164for (let extname of extnames) {165if (!ALLOWED_EXTENSIONS.includes(extname)) {166return false167}168}169
170return true171}
172
173async function getChalkModule() {174if (!chalk) {175return (await import('chalk')).default176}177else {178return chalk179}180}