11import { obj } from 'through2'
22import sharp from 'sharp'
33import Vinyl from 'vinyl'
4-
5- const ALLOWED_EXTENSIONS = [
6- '.gif' ,
7- '.png' ,
8- '.jpg' , '.jpeg' ,
9- '.webp' ,
10- '.avif' ,
11- '.tiff' ,
12- '.heif' ,
13- ]
14- const optionsByDefault = {
15- quality : 90 ,
16- lossless : false ,
17- chromaSubsampling : '4:2:0'
18- }
4+ import chalk from 'chalk'
5+
6+ const
7+ ALLOWED_EXTENSIONS = [
8+ '.gif' ,
9+ '.png' ,
10+ '.jpg' , '.jpeg' ,
11+ '.webp' ,
12+ '.avif' ,
13+ '.tiff' ,
14+ '.heif' ,
15+ ] ,
16+ DEFAULT_CONVERSION_OPTIONS = {
17+ quality : 90 ,
18+ lossless : false ,
19+ chromaSubsampling : '4:2:0' ,
20+ } ,
21+ DEFAULT_SHARP_OPTIONS = {
22+ animated : true ,
23+ limitInputPixels : false ,
24+ }
1925
2026
2127export default function sharpOptimizeImages ( options ) {
28+ let logLevel = options . logLevel ?? 'small'
29+ delete options . logLevel
30+ let optionObjects = Object . entries ( options )
31+
2232 return obj ( async function ( file , enc , callback ) {
23- if ( file . isNull ( ) ) {
24- return callback ( null , file )
25- }
26- if ( ALLOWED_EXTENSIONS . includes ( file . extname ) == false ) {
27- console . error ( `${ file . basename } not supported, just copy.` )
33+ if ( ! file ) return callback ( null , file )
2834
29- return callback ( null , file )
30- }
31- if ( typeof options !== 'object' ) {
35+ if ( typeof options !== 'object' )
3236 throw new Error ( 'Invalid parameters, they must be an object.' )
37+
38+ if ( ! ALLOWED_EXTENSIONS . includes ( file . extname ) ) {
39+ logAboutSuccessfulCopy ( file , logLevel )
40+ return callback ( null , file )
3341 }
3442
35- let convertedImages = [ ]
36- let optionObjects = Object . entries ( options )
43+ let originalFileExtname = file . extname . split ( '.' ) . at ( - 1 )
44+
3745
38- for ( let [ optionObjectFormat , optionObjectProps ] of optionObjects ) {
39- let splittedObjectName = optionObjectFormat . split ( '_to_' )
40- let convertFromOfGeneralExtname = splittedObjectName [ 0 ]
41- let convertToExtname = splittedObjectName [ 1 ]
46+ for ( let [ format , props ] of optionObjects ) {
47+ let [ convertFrom , convertTo ] = format . split ( '_to_' )
4248
43- if ( extnamesIsCorrect ( convertFromOfGeneralExtname , convertToExtname ) == false ) {
49+ if ( ! extnamesIsCorrect ( convertFrom , convertTo ) ) {
4450 throw new Error ( 'Invalid name of an object! Make sure you have spelled the extension names correctly.' )
4551 }
4652
47- if ( convertToExtname == undefined ) {
48- let optimizedFile = await convert ( file , file . extname . replace ( '.' , '' ) , optionObjectProps )
49- let convertedFile = await convert ( file , convertFromOfGeneralExtname , optionObjectProps )
53+ // Checking that the file has not a suitable extension
54+ if ( convertTo && `.${ convertFrom } ` != file . extname ) continue
5055
51- convertedImages . push ( optimizedFile , convertedFile )
52- }
53- else if ( file . extname == `.${ convertFromOfGeneralExtname } ` ) {
54- let convertedFile = await convert ( file , convertToExtname , optionObjectProps )
55- convertedImages . push ( convertedFile )
56+ //? For general conversion rules such as png: {}
57+ if ( ! convertTo ) {
58+ this . push ( await convert ( file , convertFrom , props ) )
59+ logAboutSuccessfulConversion ( file , convertFrom , logLevel )
60+
61+ if ( props . alsoProcessOriginal ) {
62+ this . push ( await convert ( file , originalFileExtname , props ) )
63+ logAboutSuccessfulConversion ( file , originalFileExtname , logLevel )
64+ }
5665 }
57- }
66+ //? For specific conversion rules such as png_to_webp: {}
67+ else {
68+ this . push ( await convert ( file , convertTo , props ) )
5869
59- for ( let convertedImage of convertedImages ) {
60- this . push ( convertedImage )
70+ logAboutSuccessfulConversion ( file , convertTo , logLevel )
71+ }
6172 }
6273
6374 return callback ( )
6475 } )
6576}
6677
6778async function convert ( file , newFileFormat , options ) {
68- let sharpInstance = sharp ( file . contents , { animated : true , limitInputPixels : false , } )
69-
70- switch ( newFileFormat ) {
71- case 'gif' :
72- sharpInstance = sharpInstance . gif ( Object . assign ( optionsByDefault , options ) )
73- break
74- case 'png' :
75- sharpInstance = sharpInstance . png ( Object . assign ( optionsByDefault , options ) )
76- break
77- case 'jpg' :
78- case 'jpeg' :
79- sharpInstance = sharpInstance . jpeg ( Object . assign ( optionsByDefault , options ) )
80- break
81- case 'webp' :
82- sharpInstance = sharpInstance . webp ( Object . assign ( optionsByDefault , options ) )
83- break
84- case 'tiff' :
85- sharpInstance = sharpInstance . tiff ( Object . assign ( optionsByDefault , options ) )
86- break
87- case 'avif' :
88- sharpInstance = sharpInstance . avif ( Object . assign ( optionsByDefault , options ) )
89- break
90- case 'heif' :
91- sharpInstance = sharpInstance . heif ( Object . assign ( optionsByDefault , options ) )
92- break
93- default :
94- return false
95- }
79+ let sharpInstance =
80+ sharp ( file . contents , DEFAULT_SHARP_OPTIONS )
81+ . toFormat ( newFileFormat , Object . assign ( DEFAULT_CONVERSION_OPTIONS , options ) )
9682
97- let buffer = await sharpInstance . toBuffer ( )
98- return toVinyl ( buffer , newFileFormat , file )
83+ return toVinyl ( await sharpInstance . toBuffer ( ) , newFileFormat , file )
9984}
10085
10186function toVinyl ( buffer , newFileFormat , file ) {
102- let newFileName = file . basename . substr ( 0 , file . basename . lastIndexOf ( "." ) ) + `. ${ newFileFormat } `
103- let newFilePath = ` ${ file . dirname } / ${ newFileName } `
87+ let newFileName = file . basename . substr ( 0 , file . basename . lastIndexOf ( '.' ) )
88+ + '.' + newFileFormat
10489
10590 return new Vinyl ( {
10691 cwd : file . cwd ,
10792 base : file . base ,
108- path : newFilePath ,
93+ path : ` ${ file . dirname } \\ ${ newFileName } ` ,
10994 contents : buffer ,
11095 } )
11196}
11297
11398
11499function extnamesIsCorrect ( ...extnames ) {
115100 for ( let extname of extnames ) {
116- if ( extname && ALLOWED_EXTENSIONS . includes ( `. ${ extname } ` ) == false ) {
101+ if ( extname && ! ALLOWED_EXTENSIONS . includes ( '.' + extname ) )
117102 return false
118- } else {
119- return true
120- }
121103 }
104+
105+ return true
106+ }
107+
108+ function logAboutSuccessfulConversion ( file , newFileExtname , logLevel ) {
109+ let filename = file . basename . split ( '.' ) [ 0 ]
110+
111+ if ( logLevel == 'full' )
112+ console . log (
113+ 'The file ' + chalk . green ( file . path )
114+ + ' was processed to '
115+ + chalk . green ( filename + '.' + chalk . bold ( newFileExtname ) )
116+ )
117+ else if ( logLevel == 'small' )
118+ console . log (
119+ chalk . green ( file . basename ) + ' => ' + chalk . green . bold ( newFileExtname )
120+ )
121+ }
122+ function logAboutSuccessfulCopy ( file , logLevel ) {
123+ if ( logLevel == 'full' )
124+ console . log ( chalk . hex ( '#FF8800' )
125+ (
126+ `The image ${ chalk . bold ( file . basename ) } cannot be processed, so it is copied.`
127+ )
128+ )
129+ else if ( logLevel == 'small' )
130+ console . log ( chalk . hex ( '#FF8800' )
131+ ( file . basename + ' => ' + chalk . bold ( 'copied' ) )
132+ )
122133}
0 commit comments