blob: 6def97849f08d8a4745703a4aa5c976750e59e10 [file] [log] [blame]
const os = require('os');
const chalk = require('chalk');
const imagemin = require('imagemin');
const plur = require('plur');
const prettyBytes = require('pretty-bytes');
const pMap = require('p-map');
const defaultPlugins = ['gifsicle', 'jpegtran', 'optipng', 'svgo'];
const loadPlugin = (grunt, plugin, opts) => {
try {
return require(`imagemin-${plugin}`)(opts);
} catch (error) {
grunt.warn(`Couldn't load default plugin "${plugin}"`);
}
};
const getDefaultPlugins = (grunt, opts) => defaultPlugins.reduce((plugins, plugin) => {
const instance = loadPlugin(grunt, plugin, opts);
if (!instance) {
return plugins;
}
return plugins.concat(instance);
}, []);
module.exports = grunt => {
grunt.registerMultiTask('imagemin', 'Minify PNG, JPEG, GIF and SVG images', function () {
const done = this.async();
const options = this.options({
interlaced: true,
optimizationLevel: 3,
progressive: true,
concurrency: os.cpus().length
});
if (Array.isArray(options.svgoPlugins)) {
options.plugins = options.svgoPlugins;
}
const plugins = options.use || getDefaultPlugins(grunt, options);
let totalBytes = 0;
let totalSavedBytes = 0;
let totalFiles = 0;
const processFile = file => Promise.resolve(grunt.file.read(file.src[0], {encoding: null}))
.then(buf => Promise.all([imagemin.buffer(buf, {plugins}), buf]))
.then(res => {
// TODO: Use destructuring when targeting Node.js 6
const optimizedBuf = res[0];
const originalBuf = res[1];
const originalSize = originalBuf.length;
const optimizedSize = optimizedBuf.length;
const saved = originalSize - optimizedSize;
const percent = originalSize > 0 ? (saved / originalSize) * 100 : 0;
const savedMsg = `saved ${prettyBytes(saved)} - ${percent.toFixed(1).replace(/\.0$/, '')}%`;
const msg = saved > 0 ? savedMsg : 'already optimized';
if (saved > 0) {
totalBytes += originalSize;
totalSavedBytes += saved;
totalFiles++;
}
grunt.file.write(file.dest, optimizedBuf);
grunt.verbose.writeln(chalk.green('✔ ') + file.src[0] + chalk.gray(` (${msg})`));
})
.catch(error => {
grunt.warn(`${error} in file ${file.src[0]}`);
});
pMap(this.files, processFile, {concurrency: options.concurrency}).then(() => {
const percent = totalBytes > 0 ? (totalSavedBytes / totalBytes) * 100 : 0;
let msg = `Minified ${totalFiles} ${plur('image', totalFiles)}`;
if (totalFiles > 0) {
msg += chalk.gray(` (saved ${prettyBytes(totalSavedBytes)} - ${percent.toFixed(1).replace(/\.0$/, '')}%)`);
}
grunt.log.writeln(msg);
done();
});
});
};