blob: 6def97849f08d8a4745703a4aa5c976750e59e10 [file] [log] [blame]
Leo Repp58b9f112021-11-22 11:57:47 +01001const os = require('os');
2const chalk = require('chalk');
3const imagemin = require('imagemin');
4const plur = require('plur');
5const prettyBytes = require('pretty-bytes');
6const pMap = require('p-map');
7
8const defaultPlugins = ['gifsicle', 'jpegtran', 'optipng', 'svgo'];
9
10const 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
18const 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
28module.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};