'use strict';
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}`).apply(null, opts);
	} catch (err) {
		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
		});

		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(err => {
				grunt.warn(`${err} in file ${file.src[0]}`);
			});

		pMap(this.files, processFile, {concurrency: os.cpus().length}).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();
		});
	});
};

'use strict';
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 (err) {
		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
		});

		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(err => {
				grunt.warn(`${err} in file ${file.src[0]}`);
			});

		pMap(this.files, processFile, {concurrency: os.cpus().length}).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();
		});
	});
};

Fix plugin options