├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── DEPENDENCIES.md ├── README.md ├── example ├── Gruntfile.js ├── package.json └── src │ └── images │ └── evolution_of_guybrush_threepwood.png ├── package.json └── tasks └── imageoptim.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: fold_left 2 | custom: ['https://www.paypal.me/foldleft'] 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Explain how to reproduce a Bug 4 | --- 5 | 6 | ## Description 7 | 8 | 13 | 14 | ## Suggested Solution 15 | 16 | 20 | 21 | ## Help Needed 22 | 23 | 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | --- 5 | 6 | ## Description 7 | 8 | 14 | 15 | ## Suggested Solution 16 | 17 | 21 | 22 | ## Help Needed 23 | 24 | 28 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description (What) 2 | 3 | 9 | 10 | ## Justification (Why) 11 | 12 | 18 | 19 | ## How Can This Be Tested? 20 | 21 | 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.log.* 3 | node_modules 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .git* 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ## [1.4.4](https://github.com/JamieMason/grunt-imageoptim/compare/1.4.3...v1.4.4) (2017-07-09) 3 | 4 | 5 | ### Bug Fixes 6 | 7 | * **npm:** update dependencies ([0ed033e](https://github.com/JamieMason/grunt-imageoptim/commit/0ed033e)) 8 | 9 | 10 | 11 | 12 | ## [1.4.3](https://github.com/JamieMason/grunt-imageoptim/compare/1.4.2...1.4.3) (2017-02-07) 13 | 14 | 15 | ### Bug Fixes 16 | 17 | * **cli:** update imageoptim-cli to 1.14.9 ([53ea333](https://github.com/JamieMason/grunt-imageoptim/commit/53ea333)) 18 | 19 | 20 | 21 | 22 | ## [1.4.2](https://github.com/JamieMason/grunt-imageoptim/compare/1.4.1...1.4.2) (2017-01-12) 23 | 24 | 25 | ### Bug Fixes 26 | 27 | * **npm:** update dependencies ([28bcfc4](https://github.com/JamieMason/grunt-imageoptim/commit/28bcfc4)) 28 | 29 | 30 | 31 | 32 | ## [1.4.1](https://github.com/JamieMason/grunt-imageoptim/compare/1.4.0...1.4.1) (2013-12-13) 33 | 34 | 35 | 36 | 37 | # [1.4.0](https://github.com/JamieMason/grunt-imageoptim/compare/1.3.13...1.4.0) (2013-10-12) 38 | 39 | 40 | 41 | 42 | ## [1.3.13](https://github.com/JamieMason/grunt-imageoptim/compare/1.2.12...1.3.13) (2013-09-22) 43 | 44 | 45 | 46 | 47 | ## [1.2.12](https://github.com/JamieMason/grunt-imageoptim/compare/1.2.11...1.2.12) (2013-08-28) 48 | 49 | 50 | 51 | 52 | ## [1.2.11](https://github.com/JamieMason/grunt-imageoptim/compare/1.2.10...1.2.11) (2013-08-10) 53 | 54 | 55 | 56 | 57 | ## [1.2.10](https://github.com/JamieMason/grunt-imageoptim/compare/1.2.6...1.2.10) (2013-08-04) 58 | 59 | 60 | 61 | 62 | ## [1.2.6](https://github.com/JamieMason/grunt-imageoptim/compare/1.2.4...1.2.6) (2013-06-13) 63 | 64 | 65 | 66 | 67 | ## [1.2.4](https://github.com/JamieMason/grunt-imageoptim/compare/1.2.2...1.2.4) (2013-06-10) 68 | 69 | 70 | 71 | 72 | ## [1.2.2](https://github.com/JamieMason/grunt-imageoptim/compare/1.0.0...1.2.2) (2013-06-10) 73 | 74 | 75 | 76 | 77 | # [1.0.0](https://github.com/JamieMason/grunt-imageoptim/compare/0.1.1...1.0.0) (2013-06-08) 78 | 79 | 80 | 81 | 82 | ## 0.1.1 (2013-05-26) 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /DEPENDENCIES.md: -------------------------------------------------------------------------------- 1 | # grunt-imageoptim 2 | 3 | Automate ImageOptim, ImageAlpha, and JPEGmini 4 | 5 | ## Installation 6 | 7 | Download node at [nodejs.org](http://nodejs.org) and install it, if you haven't already. 8 | 9 | ```sh 10 | npm install grunt-imageoptim --save 11 | ``` 12 | 13 | 14 | 15 | ## Dependencies 16 | 17 | - [imageoptim-cli](https://github.com/JamieMason/ImageOptim-CLI): Automates ImageOptim, ImageAlpha, and JPEGmini for Mac to make batch optimisation of images part of your automated build process. 18 | - [q](https://github.com/kriskowal/q): A library for promises (CommonJS/Promises/A,B,D) 19 | 20 | ## Dev Dependencies 21 | 22 | - [xo](https://github.com/sindresorhus/xo): JavaScript happiness style linter ❤️ 23 | 24 | 25 | ## License 26 | 27 | MIT 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # grunt-imageoptim 2 | 3 | > The companion [Grunt](http://gruntjs.com/) plugin for 4 | > [ImageOptim-CLI](http://jamiemason.github.io/ImageOptim-CLI/), which automates 5 | > batch optimisation of images with [ImageOptim](http://imageoptim.com), 6 | > [ImageAlpha](http://pngmini.com) and 7 | > [JPEGmini for Mac](http://jpegmini.com/mac). 8 | 9 | [![NPM version](http://img.shields.io/npm/v/grunt-imageoptim.svg?style=flat-square)](https://www.npmjs.com/package/grunt-imageoptim) 10 | [![NPM downloads](http://img.shields.io/npm/dm/grunt-imageoptim.svg?style=flat-square)](https://www.npmjs.com/package/grunt-imageoptim) 11 | [![Dependency Status](http://img.shields.io/david/JamieMason/grunt-imageoptim.svg?style=flat-square)](https://david-dm.org/JamieMason/grunt-imageoptim) 12 | [![Follow JamieMason on GitHub](https://img.shields.io/github/followers/JamieMason.svg?style=social&label=Follow)](https://github.com/JamieMason) 13 | [![Follow fold_left on Twitter](https://img.shields.io/twitter/follow/fold_left.svg?style=social&label=Follow)](https://twitter.com/fold_left) 14 | 15 | ## 🌩 Installation 16 | 17 | ``` 18 | npm install grunt-imageoptim --save-dev 19 | ``` 20 | 21 | ## 🔗 Dependencies 22 | 23 | Since this project automates three Mac Applications, you will need them to be 24 | installed on your machine for us to be able to reach them. 25 | 26 | - [ImageOptim](http://imageoptim.com) 27 | - [ImageAlpha](http://pngmini.com) 28 | - [JPEGmini for Mac](https://itunes.apple.com/us/app/jpegmini/id498944723) (App 29 | Store) 30 | 31 | A local copy of [ImageOptim-CLI](http://jamiemason.github.io/ImageOptim-CLI/) 32 | will be installed, you won't need to install that separately. 33 | 34 | ## ⚖️ Configuration 35 | 36 | As with all Grunt plugins, grunt-imageoptim is configured using a Gruntfile.js 37 | in the root of your project. 38 | 39 | Grunt provide a short 40 | [walkthrough of a sample Gruntfile](http://gruntjs.com/sample-gruntfile) which 41 | explains how they work, but the general structure is this; 42 | 43 | ```js 44 | module.exports = function(grunt) { 45 | grunt.initConfig({ 46 | /* your grunt-imageoptim configuration goes here */ 47 | }); 48 | grunt.loadNpmTasks("grunt-imageoptim"); 49 | }; 50 | ``` 51 | 52 | ### Use defaults 53 | 54 | Here we want to optimise two directories using default options. 55 | 56 | ```js 57 | imageoptim: { 58 | myTask: { 59 | src: ["www/images", "css/images"]; 60 | } 61 | } 62 | ``` 63 | 64 | ### Override defaults 65 | 66 | Here we want to optimise two directories using only ImageAlpha and ImageOptim, 67 | then close them once we're done. 68 | 69 | ```js 70 | imageoptim: { 71 | myTask: { 72 | options: { 73 | jpegMini: false, 74 | imageAlpha: true, 75 | quitAfter: true 76 | }, 77 | src: ['www/images', 'css/images'] 78 | } 79 | } 80 | ``` 81 | 82 | ### Custom options for each task 83 | 84 | Here we have a task for a folder of PNGs and another for JPGs. Since we use 85 | ImageAlpha to optimise PNGs but not JPGs and vice versa with JPEGmini, here we 86 | toggle their availability between the two tasks. 87 | 88 | ```js 89 | imageoptim: { 90 | myPngs: { 91 | options: { 92 | jpegMini: false, 93 | imageAlpha: true, 94 | quitAfter: true 95 | }, 96 | src: ['img/png'] 97 | }, 98 | myJpgs: { 99 | options: { 100 | jpegMini: true, 101 | imageAlpha: false, 102 | quitAfter: true 103 | }, 104 | src: ['img/jpg'] 105 | } 106 | } 107 | ``` 108 | 109 | ### Option inheritance 110 | 111 | This example is equivalent to the _custom options for each task_ example, except 112 | we're setting some base options then overriding those we want to change within 113 | each task. 114 | 115 | ```js 116 | imageoptim: { 117 | options: { 118 | quitAfter: true 119 | }, 120 | allPngs: { 121 | options: { 122 | imageAlpha: true, 123 | jpegMini: false 124 | }, 125 | src: ['img/png'] 126 | }, 127 | allJpgs: { 128 | options: { 129 | imageAlpha: false, 130 | jpegMini: true 131 | }, 132 | src: ['img/jpg'] 133 | } 134 | } 135 | ``` 136 | 137 | ## ⚖️ Configuration 138 | 139 | All options can be either `true` or `false` and default to `false`. 140 | 141 | - `quitAfter` Whether to exit each application after we're finished optimising 142 | your images. 143 | - `jpegMini` Whether to process your images using a copy of 144 | [JPEGmini.app](https://itunes.apple.com/us/app/jpegmini/id498944723) installed 145 | on your Mac. 146 | - `imageAlpha` Whether to process your images using a copy of 147 | [ImageAlpha.app](http://pngmini.com) installed on your Mac. 148 | 149 | ## 🙋🏾‍♀️ Getting Help 150 | 151 | - Get help with issues by creating a 152 | [Bug Report](https://github.com/JamieMason/grunt-imageoptim/issues/new?template=bug_report.md). 153 | - Discuss ideas by opening a 154 | [Feature Request](https://github.com/JamieMason/grunt-imageoptim/issues/new?template=feature_request.md). 155 | -------------------------------------------------------------------------------- /example/Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | grunt.initConfig({ 3 | imageoptim: { 4 | imageAssets: { 5 | options: { 6 | jpegMini: false, 7 | imageAlpha: true, 8 | quitAfter: true 9 | }, 10 | src: [ 11 | 'src/images' 12 | ] 13 | } 14 | } 15 | }); 16 | grunt.loadNpmTasks('grunt-imageoptim'); 17 | }; 18 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grunt-imageoptim-example", 3 | "description": "An example project using grunt-imageoptim", 4 | "version": "0.0.0", 5 | "license": "MIT", 6 | "main": "Gruntfile.js", 7 | "private": true, 8 | "devDependencies": { 9 | "grunt": "1.0.1", 10 | "grunt-cli": "1.2.0", 11 | "grunt-imageoptim": ".." 12 | }, 13 | "scripts": { 14 | "optimise-images": "grunt imageoptim" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /example/src/images/evolution_of_guybrush_threepwood.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamieMason/grunt-imageoptim/efe1d5d9e5c2b5256da4f8eac9dcedc594268842/example/src/images/evolution_of_guybrush_threepwood.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grunt-imageoptim", 3 | "description": "Automate ImageOptim, ImageAlpha, and JPEGmini", 4 | "version": "1.4.4", 5 | "author": "Jamie Mason (https://github.com/JamieMason)", 6 | "bugs": { 7 | "url": "https://github.com/JamieMason/grunt-imageoptim/issues" 8 | }, 9 | "contributors": [ 10 | "Jamie Mason (https://github.com/JamieMason)", 11 | "David Bushell (https://github.com/dbushell)", 12 | "Adam Simpson ((https://github.com/asimpson)" 13 | ], 14 | "dependencies": { 15 | "imageoptim-cli": "1.15.1", 16 | "q": "1.5.0" 17 | }, 18 | "devDependencies": { 19 | "xo": "0.18.2" 20 | }, 21 | "homepage": "https://github.com/JamieMason/grunt-imageoptim", 22 | "keywords": [ 23 | "ImageOptim", 24 | "JPEGmini", 25 | "advpng", 26 | "build", 27 | "compress", 28 | "compression", 29 | "gifsicle", 30 | "gruntplugin", 31 | "images", 32 | "jpegoptim", 33 | "jpegtran", 34 | "optimization", 35 | "optimize", 36 | "optipng", 37 | "pngcrush", 38 | "pngout" 39 | ], 40 | "license": "MIT", 41 | "main": "tasks/imageoptim.js", 42 | "repository": "JamieMason/grunt-imageoptim", 43 | "scripts": { 44 | "lint": "xo --fix" 45 | }, 46 | "xo": { 47 | "envs": [ 48 | "node" 49 | ], 50 | "esnext": false, 51 | "space": 2 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tasks/imageoptim.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-imageoptim 3 | * https://github.com/JamieMason/grunt-imageoptim 4 | * Copyright © 2013 Jamie Mason, @fold_left, 5 | * https://github.com/JamieMason 6 | * 7 | * Permission is hereby granted, free of charge, to any person 8 | * obtaining a copy of this software and associated documentation files 9 | * (the "Software"), to deal in the Software without restriction, 10 | * including without limitation the rights to use, copy, modify, merge, 11 | * publish, distribute, sublicense, and/or sell copies of the Software, 12 | * and to permit persons to whom the Software is furnished to do so, 13 | * subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 22 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 23 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 24 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | module.exports = function (grunt) { 29 | 'use strict'; 30 | 31 | var q = require('q'); 32 | var path = require('path'); 33 | var childProcess = require('child_process'); 34 | var exec = childProcess.exec; 35 | var spawn = childProcess.spawn; 36 | var gruntFile = path.resolve(); 37 | var taskName = 'imageoptim'; 38 | var issuesPage = 'https://github.com/JamieMason/grunt-imageoptim/issues/new'; 39 | var taskDescription = 'Losslessly compress images from the command line'; 40 | var cliPaths = [ 41 | '../node_modules/imageoptim-cli/bin', 42 | '../../imageoptim-cli/bin' 43 | ].map(function (dir) { 44 | return path.resolve(__dirname, dir); 45 | }); 46 | 47 | (function () { 48 | var config = grunt.config.data.imageoptim || {}; 49 | var tasks = config ? [config] : []; 50 | var hasDeprecatedConfig = false; 51 | var error = ''; 52 | 53 | Object.keys(config).forEach(function (key) { 54 | var value = config[key]; 55 | var isObject = value && typeof value === 'object'; 56 | var isTask = isObject && key !== 'options' && key !== 'files'; 57 | if (isTask) { 58 | tasks.push(value); 59 | } 60 | }); 61 | 62 | hasDeprecatedConfig = tasks.some(function (task) { 63 | return 'files' in task && task.files.some(function (member) { 64 | return typeof member === 'string'; 65 | }); 66 | }); 67 | 68 | if (hasDeprecatedConfig) { 69 | error += '\n'; 70 | error += 'grunt-imageoptim 1.4.0 brought a change to it\'s configuration to bring full '; 71 | error += 'support for Grunt\'s file pattern matching.'; 72 | error += '\n'; 73 | error += 'In most cases all this means is renaming the "files" property to "src", but '; 74 | error += 'updated examples can be found at https://github.com/JamieMason/grunt-imageoptim.'; 75 | grunt.fail.fatal(error); 76 | } 77 | })(); 78 | 79 | /** 80 | * Get the ImageOptim-CLI Terminal command to be run for a given directory and task options 81 | * @param {String} directory 82 | * @param {Object} options 83 | * @return {String} 84 | */ 85 | 86 | function getCommandByDirectory(directory, options) { 87 | var command = './imageOptim'; 88 | command += options.quitAfter ? ' --quit' : ''; 89 | command += options.imageAlpha ? ' --image-alpha' : ''; 90 | command += options.jpegMini ? ' --jpeg-mini' : ''; 91 | return command + ' --directory ' + directory.replace(/\s/g, '\\ '); 92 | } 93 | 94 | /** 95 | * @param {String} command 96 | * @param {String} cwd 97 | * @return {Promise} 98 | */ 99 | 100 | function executeDirectoryCommand(command, cwd) { 101 | var deferred = q.defer(); 102 | var errorMessage = 'ImageOptim-CLI exited with a failure status'; 103 | var imageOptimCli = exec(command, { 104 | cwd: cwd 105 | }); 106 | 107 | imageOptimCli.stdout.on('data', function (message) { 108 | console.log(String(message || '').replace(/\n+$/, '')); 109 | }); 110 | 111 | imageOptimCli.on('exit', function (code) { 112 | return code === 0 ? deferred.resolve(true) : deferred.reject(new Error(errorMessage)); 113 | }); 114 | 115 | return deferred.promise; 116 | } 117 | 118 | /** 119 | * @param {String[]} files Array of paths to directories from src: in config. 120 | * @param {Boolean} opts.jpegMini Whether to run JPEGmini.app. 121 | * @param {Boolean} opts.imageAlpha Whether to run ImageAlpha.app. 122 | * @param {Boolean} opts.quitAfter Whether to quit apps after running. 123 | * @return {Promise} 124 | */ 125 | 126 | function processDirectories(files, opts) { 127 | return files.map(function (directory) { 128 | return getCommandByDirectory(directory, opts); 129 | }).reduce(function (promise, command) { 130 | return promise.then(function () { 131 | return executeDirectoryCommand(command, opts.cliPath); 132 | }); 133 | }, q()); 134 | } 135 | 136 | /** 137 | * include necessary command line flags based on the current task's options 138 | * @param {Object} opts 139 | * @return {Array} 140 | */ 141 | 142 | function getCliFlags(opts) { 143 | var cliOptions = []; 144 | if (opts.quitAfter) { 145 | cliOptions.push('--quit'); 146 | } 147 | if (opts.imageAlpha) { 148 | cliOptions.push('--image-alpha'); 149 | } 150 | if (opts.jpegMini) { 151 | cliOptions.push('--jpeg-mini'); 152 | } 153 | return cliOptions; 154 | } 155 | 156 | /** 157 | * @param {String[]} files Array of paths to files from src: in config. 158 | * @param {Boolean} opts.jpegMini Whether to run JPEGmini.app. 159 | * @param {Boolean} opts.imageAlpha Whether to run ImageAlpha.app. 160 | * @param {Boolean} opts.quitAfter Whether to quit apps after running. 161 | * @return {Promise} 162 | */ 163 | 164 | function processFiles(files, opts) { 165 | var imageOptimCli; 166 | var deferred = q.defer(); 167 | var errorMessage = 'ImageOptim-CLI exited with a failure status'; 168 | 169 | imageOptimCli = spawn('./imageOptim', getCliFlags(opts), { 170 | cwd: opts.cliPath 171 | }); 172 | 173 | imageOptimCli.stdout.on('data', function (message) { 174 | console.log(String(message || '').replace(/\n+$/, '')); 175 | }); 176 | 177 | imageOptimCli.on('exit', function (code) { 178 | return code === 0 ? deferred.resolve(true) : deferred.reject(new Error(errorMessage)); 179 | }); 180 | 181 | imageOptimCli.stdin.setEncoding('utf8'); 182 | imageOptimCli.stdin.end(files.join('\n') + '\n'); 183 | 184 | return deferred.promise; 185 | } 186 | 187 | /** 188 | * @param {String} str "hello" 189 | * @return {String} "Hello" 190 | */ 191 | 192 | function firstUp(str) { 193 | return str.charAt(0).toUpperCase() + str.slice(1); 194 | } 195 | 196 | /** 197 | * @param {String} fileType "file" or "Dir" 198 | * @return {Function} 199 | */ 200 | 201 | function isFileType(fileType) { 202 | var methodName = 'is' + firstUp(fileType); 203 | return function (file) { 204 | return grunt.file[methodName](file); 205 | }; 206 | } 207 | 208 | /** 209 | * Ensure the ImageOptim-CLI binary is accessible 210 | * @return {String} 211 | */ 212 | 213 | function getPathToCli() { 214 | return cliPaths.filter(function (cliPath) { 215 | return grunt.file.exists(cliPath); 216 | })[0]; 217 | } 218 | 219 | /** 220 | * Convert a relative path to an absolute file system path 221 | * @param {String} relativePath 222 | * @return {String} 223 | */ 224 | 225 | function toAbsolute(relativePath) { 226 | return path.resolve(gruntFile, relativePath); 227 | } 228 | 229 | /** 230 | * Given a collection of files to be run in a task, seperate the files from the directories to 231 | * handle them in their own way. 232 | * @param {String} fileType "dir" or "file" 233 | * @param {String} cliPath 234 | * @param {Object} options 235 | * @param {Array} taskFiles 236 | * @param {Promise} promise 237 | * @return {Promise} 238 | */ 239 | 240 | function processBatch(fileType, cliPath, options, taskFiles, promise) { 241 | var files = taskFiles.filter(isFileType(fileType)).map(toAbsolute); 242 | var processor = fileType === 'dir' ? processDirectories : processFiles; 243 | return files.length === 0 ? promise : promise.then(function () { 244 | return processor(files, { 245 | cliPath: cliPath, 246 | jpegMini: options.jpegMini, 247 | imageAlpha: options.imageAlpha, 248 | quitAfter: options.quitAfter 249 | }); 250 | }); 251 | } 252 | 253 | grunt.registerMultiTask(taskName, taskDescription, function () { 254 | var task = this; 255 | var done = task.async(); 256 | var promise = q(); 257 | var cliPath = getPathToCli(); 258 | var options = task.options({ 259 | jpegMini: false, 260 | imageAlpha: false, 261 | quitAfter: false 262 | }); 263 | 264 | if (!cliPath) { 265 | throw new Error('Unable to locate ImageOptim-CLI. Please raise issue at ' + issuesPage); 266 | } 267 | 268 | task.files.forEach(function (file) { 269 | promise = processBatch('file', cliPath, options, file.src, promise); 270 | promise = processBatch('dir', cliPath, options, file.src, promise); 271 | }); 272 | 273 | promise.done(done); 274 | }); 275 | }; 276 | --------------------------------------------------------------------------------