| Leo Repp | 58b9f11 | 2021-11-22 11:57:47 +0100 | [diff] [blame^] | 1 | const os = require('os'); |
| 2 | const chalk = require('chalk'); |
| 3 | const imagemin = require('imagemin'); |
| 4 | const plur = require('plur'); |
| 5 | const prettyBytes = require('pretty-bytes'); |
| 6 | const pMap = require('p-map'); |
| 7 | |
| 8 | const defaultPlugins = ['gifsicle', 'jpegtran', 'optipng', 'svgo']; |
| 9 | |
| 10 | const loadPlugin = (grunt, plugin, opts) => { |
| 11 | try { |
| 12 | return require(`imagemin-${plugin}`)(opts); |
| 13 | } catch (error) { |
| 14 | grunt.warn(`Couldn't load default plugin "${plugin}"`); |
| 15 | } |
| 16 | }; |
| 17 | |
| 18 | const getDefaultPlugins = (grunt, opts) => defaultPlugins.reduce((plugins, plugin) => { |
| 19 | const instance = loadPlugin(grunt, plugin, opts); |
| 20 | |
| 21 | if (!instance) { |
| 22 | return plugins; |
| 23 | } |
| 24 | |
| 25 | return plugins.concat(instance); |
| 26 | }, []); |
| 27 | |
| 28 | module.exports = grunt => { |
| 29 | grunt.registerMultiTask('imagemin', 'Minify PNG, JPEG, GIF and SVG images', function () { |
| 30 | const done = this.async(); |
| 31 | const options = this.options({ |
| 32 | interlaced: true, |
| 33 | optimizationLevel: 3, |
| 34 | progressive: true, |
| 35 | concurrency: os.cpus().length |
| 36 | }); |
| 37 | |
| 38 | if (Array.isArray(options.svgoPlugins)) { |
| 39 | options.plugins = options.svgoPlugins; |
| 40 | } |
| 41 | |
| 42 | const plugins = options.use || getDefaultPlugins(grunt, options); |
| 43 | |
| 44 | let totalBytes = 0; |
| 45 | let totalSavedBytes = 0; |
| 46 | let totalFiles = 0; |
| 47 | |
| 48 | const processFile = file => Promise.resolve(grunt.file.read(file.src[0], {encoding: null})) |
| 49 | .then(buf => Promise.all([imagemin.buffer(buf, {plugins}), buf])) |
| 50 | .then(res => { |
| 51 | // TODO: Use destructuring when targeting Node.js 6 |
| 52 | const optimizedBuf = res[0]; |
| 53 | const originalBuf = res[1]; |
| 54 | const originalSize = originalBuf.length; |
| 55 | const optimizedSize = optimizedBuf.length; |
| 56 | const saved = originalSize - optimizedSize; |
| 57 | const percent = originalSize > 0 ? (saved / originalSize) * 100 : 0; |
| 58 | const savedMsg = `saved ${prettyBytes(saved)} - ${percent.toFixed(1).replace(/\.0$/, '')}%`; |
| 59 | const msg = saved > 0 ? savedMsg : 'already optimized'; |
| 60 | |
| 61 | if (saved > 0) { |
| 62 | totalBytes += originalSize; |
| 63 | totalSavedBytes += saved; |
| 64 | totalFiles++; |
| 65 | } |
| 66 | |
| 67 | grunt.file.write(file.dest, optimizedBuf); |
| 68 | grunt.verbose.writeln(chalk.green('✔ ') + file.src[0] + chalk.gray(` (${msg})`)); |
| 69 | }) |
| 70 | .catch(error => { |
| 71 | grunt.warn(`${error} in file ${file.src[0]}`); |
| 72 | }); |
| 73 | |
| 74 | pMap(this.files, processFile, {concurrency: options.concurrency}).then(() => { |
| 75 | const percent = totalBytes > 0 ? (totalSavedBytes / totalBytes) * 100 : 0; |
| 76 | let msg = `Minified ${totalFiles} ${plur('image', totalFiles)}`; |
| 77 | |
| 78 | if (totalFiles > 0) { |
| 79 | msg += chalk.gray(` (saved ${prettyBytes(totalSavedBytes)} - ${percent.toFixed(1).replace(/\.0$/, '')}%)`); |
| 80 | } |
| 81 | |
| 82 | grunt.log.writeln(msg); |
| 83 | done(); |
| 84 | }); |
| 85 | }); |
| 86 | }; |