Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: sindresorhus/gulp-imagemin
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 8e731f0260e6ccf66f3d58db877d325cbad11012
Choose a base ref
...
head repository: sindresorhus/gulp-imagemin
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 2f7ecc948a30564ee728d8ae5400e8397b918eaf
Choose a head ref
  • 18 commits
  • 8 files changed
  • 5 contributors

Commits on May 28, 2019

  1. Create funding.yml

    sindresorhus authored May 28, 2019
    Copy the full SHA
    c1c3346 View commit details

Commits on May 30, 2019

  1. Require Node.js 8

    sindresorhus committed May 30, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    6e091ed View commit details
  2. 6.0.0

    sindresorhus committed May 30, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    97cd1c3 View commit details

Commits on Aug 18, 2019

  1. Update dependencies

    sindresorhus committed Aug 18, 2019
    Copy the full SHA
    92e224a View commit details
  2. 6.1.0

    sindresorhus committed Aug 18, 2019
    Copy the full SHA
    6c35e59 View commit details

Commits on Oct 4, 2019

  1. Copy the full SHA
    165bf8b View commit details
  2. 6.1.1

    sindresorhus committed Oct 4, 2019
    Copy the full SHA
    dfdba76 View commit details

Commits on Nov 12, 2019

  1. Add silent option (#331)

    MartijnCuppens authored and sindresorhus committed Nov 12, 2019
    Copy the full SHA
    0460c78 View commit details
  2. 6.2.0

    sindresorhus committed Nov 12, 2019
    Copy the full SHA
    f4a2255 View commit details

Commits on Jan 16, 2020

  1. Copy the full SHA
    279a91b View commit details
  2. Require Node.js 10

    sindresorhus committed Jan 16, 2020
    Copy the full SHA
    aacca91 View commit details
  3. 7.0.0

    sindresorhus committed Jan 16, 2020
    Copy the full SHA
    97432ea View commit details

Commits on Jan 21, 2020

  1. Copy the full SHA
    67cceb3 View commit details
  2. 7.1.0

    sindresorhus committed Jan 21, 2020
    Copy the full SHA
    1cbb7c5 View commit details

Commits on Aug 10, 2020

  1. Copy the full SHA
    8b40da0 View commit details

Commits on Jan 3, 2021

  1. Copy the full SHA
    ed39463 View commit details

Commits on Aug 12, 2021

  1. Copy the full SHA
    1b4baf6 View commit details
  2. 8.0.0

    sindresorhus committed Aug 12, 2021
    Copy the full SHA
    2f7ecc9 View commit details
Showing with 156 additions and 141 deletions.
  1. +20 −0 .github/workflows/main.yml
  2. +0 −5 .travis.yml
  3. BIN fixture.jpg
  4. +60 −57 index.js
  5. +1 −1 license
  6. +25 −18 package.json
  7. +22 −44 readme.md
  8. +28 −16 test.js
20 changes: 20 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: CI
on:
- push
- pull_request
jobs:
test:
name: Node.js ${{ matrix.node-version }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version:
- 16
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm test
5 changes: 0 additions & 5 deletions .travis.yml

This file was deleted.

Binary file added fixture.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
117 changes: 60 additions & 57 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,85 +1,85 @@
const path = require('path');
const log = require('fancy-log');
const PluginError = require('plugin-error');
const through = require('through2-concurrent');
const prettyBytes = require('pretty-bytes');
const chalk = require('chalk');
const imagemin = require('imagemin');
const plur = require('plur');
import {createRequire} from 'node:module';
import path from 'node:path';
import process from 'node:process';
import log from 'fancy-log';
import PluginError from 'plugin-error';
import through from 'through2-concurrent';
import prettyBytes from 'pretty-bytes';
import chalk from 'chalk';
import imagemin from 'imagemin';
import plur from 'plur';

const require = createRequire(import.meta.url);

const PLUGIN_NAME = 'gulp-imagemin';
const defaultPlugins = ['gifsicle', 'jpegtran', 'optipng', 'svgo'];
const defaultPlugins = ['gifsicle', 'mozjpeg', 'optipng', 'svgo'];

const loadPlugin = (plugin, ...args) => {
try {
return require(`imagemin-${plugin}`)(...args);
} catch (error) {
log(`${PLUGIN_NAME}: Couldn't load default plugin "${plugin}"`);
} catch {
log(`${PLUGIN_NAME}: Could not load default plugin \`${plugin}\``);
}
};

const exposePlugin = plugin => (...args) => loadPlugin(plugin, ...args);

const getDefaultPlugins = () =>
defaultPlugins.reduce((plugins, plugin) => {
const instance = loadPlugin(plugin);
const getDefaultPlugins = () => defaultPlugins.flatMap(plugin => loadPlugin(plugin));

if (!instance) {
return plugins;
}

return plugins.concat(instance);
}, []);

module.exports = (plugins, options) => {
export default function gulpImagemin(plugins, options) {
if (typeof plugins === 'object' && !Array.isArray(plugins)) {
options = plugins;
plugins = null;
plugins = undefined;
}

options = Object.assign({
options = {
// TODO: Remove this when Gulp gets a real logger with levels
verbose: process.argv.includes('--verbose')
}, options);
silent: process.argv.includes('--silent'),
verbose: process.argv.includes('--verbose'),
...options,
};

const validExts = ['.jpg', '.jpeg', '.png', '.gif', '.svg'];
const validExtensions = new Set(['.jpg', '.jpeg', '.png', '.gif', '.svg']);

let totalBytes = 0;
let totalSavedBytes = 0;
let totalFiles = 0;

return through.obj({
maxConcurrency: 8
}, (file, enc, cb) => {
maxConcurrency: 8,
}, (file, encoding, callback) => {
if (file.isNull()) {
cb(null, file);
callback(null, file);
return;
}

if (file.isStream()) {
cb(new PluginError(PLUGIN_NAME, 'Streaming not supported'));
callback(new PluginError(PLUGIN_NAME, 'Streaming not supported'));
return;
}

if (!validExts.includes(path.extname(file.path).toLowerCase())) {
if (!validExtensions.has(path.extname(file.path).toLowerCase())) {
if (options.verbose) {
log(`${PLUGIN_NAME}: Skipping unsupported image ${chalk.blue(file.relative)}`);
}

cb(null, file);
callback(null, file);
return;
}

const use = plugins || getDefaultPlugins();
const localPlugins = plugins || getDefaultPlugins();

imagemin.buffer(file.contents, {use})
.then(data => {
(async () => {
try {
const data = await imagemin.buffer(file.contents, {
plugins: localPlugins,
});
const originalSize = file.contents.length;
const optimizedSize = data.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';
const savedMessage = `saved ${prettyBytes(saved)} - ${percent.toFixed(1).replace(/\.0$/, '')}%`;
const message = saved > 0 ? savedMessage : 'already optimized';

if (saved > 0) {
totalBytes += originalSize;
@@ -88,29 +88,32 @@ module.exports = (plugins, options) => {
}

if (options.verbose) {
log(`${PLUGIN_NAME}:`, chalk.green('✔ ') + file.relative + chalk.gray(` (${msg})`));
log(`${PLUGIN_NAME}:`, chalk.green('✔ ') + file.relative + chalk.gray(` (${message})`));
}

file.contents = data;
cb(null, file);
})
.catch(error => {
cb(new PluginError(PLUGIN_NAME, error, {fileName: file.path}));
});
}, cb => {
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$/, '')}%)`);
callback(null, file);
} catch (error) {
callback(new PluginError(PLUGIN_NAME, error, {fileName: file.path}));
}
})();
}, callback => {
if (!options.silent) {
const percent = totalBytes > 0 ? (totalSavedBytes / totalBytes) * 100 : 0;
let message = `Minified ${totalFiles} ${plur('image', totalFiles)}`;

if (totalFiles > 0) {
message += chalk.gray(` (saved ${prettyBytes(totalSavedBytes)} - ${percent.toFixed(1).replace(/\.0$/, '')}%)`);
}

log(`${PLUGIN_NAME}:`, message);
}

log(`${PLUGIN_NAME}:`, msg);
cb();
callback();
});
};
}

module.exports.gifsicle = exposePlugin('gifsicle');
module.exports.jpegtran = exposePlugin('jpegtran');
module.exports.optipng = exposePlugin('optipng');
module.exports.svgo = exposePlugin('svgo');
export const gifsicle = exposePlugin('gifsicle');
export const mozjpeg = exposePlugin('mozjpeg');
export const optipng = exposePlugin('optipng');
export const svgo = exposePlugin('svgo');
2 changes: 1 addition & 1 deletion license
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

43 changes: 25 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
{
"name": "gulp-imagemin",
"version": "5.0.3",
"version": "8.0.0",
"description": "Minify PNG, JPEG, GIF and SVG images",
"license": "MIT",
"repository": "sindresorhus/gulp-imagemin",
"funding": "https://github.com/sponsors/sindresorhus",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "sindresorhus.com"
"url": "https://sindresorhus.com"
},
"type": "module",
"exports": "./index.js",
"engines": {
"node": ">=6"
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"scripts": {
"test": "xo && ava"
@@ -35,29 +38,33 @@
"svg"
],
"dependencies": {
"chalk": "^2.4.1",
"fancy-log": "^1.3.2",
"chalk": "^4.1.2",
"fancy-log": "^1.3.3",
"imagemin": "^8.0.1",
"plugin-error": "^1.0.1",
"imagemin": "^6.0.0",
"plur": "^3.0.1",
"pretty-bytes": "^5.1.0",
"plur": "^4.0.0",
"pretty-bytes": "^5.6.0",
"through2-concurrent": "^2.0.0"
},
"devDependencies": {
"ava": "^0.25.0",
"get-stream": "^4.1.0",
"imagemin-pngquant": "^6.0.0",
"pify": "^4.0.1",
"xo": "^0.23.0",
"vinyl": "^2.2.0"
"ava": "^3.15.0",
"get-stream": "^6.0.1",
"imagemin-pngquant": "^9.0.2",
"vinyl": "^2.2.1",
"xo": "^0.44.0"
},
"optionalDependencies": {
"imagemin-gifsicle": "^6.0.1",
"imagemin-jpegtran": "^6.0.0",
"imagemin-optipng": "^6.0.0",
"imagemin-svgo": "^7.0.0"
"imagemin-gifsicle": "^7.0.0",
"imagemin-mozjpeg": "^9.0.0",
"imagemin-optipng": "^8.0.0",
"imagemin-svgo": "^9.0.0"
},
"peerDependencies": {
"gulp": ">=4"
},
"peerDependenciesMeta": {
"gulp": {
"optional": true
}
}
}
66 changes: 22 additions & 44 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,24 @@
# gulp-imagemin [![Build Status](https://travis-ci.com/sindresorhus/gulp-imagemin.svg?branch=master)](https://travis-ci.com/sindresorhus/gulp-imagemin) [![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/xojs/xo)
# gulp-imagemin

> Minify PNG, JPEG, GIF and SVG images with [`imagemin`](https://github.com/imagemin/imagemin)
*Issues with the output should be reported on the [`imagemin` issue tracker](https://github.com/imagemin/imagemin/issues).*

---

<p align="center"><sup>🦄 Support <a href="https://github.com/sindresorhus">my open-source work</a> by buying this awesome video course:</sup><br><b><a href="https://learnnode.com/friend/AWESOME">Learn to build apps and APIs with Node.js</a> by Wes Bos</b><br><sub>Try his free <a href="https://javascript30.com/friend/AWESOME">JavaScript 30</a> course for a taste of what to expect & check out his <a href="https://ES6.io/friend/AWESOME">ES6</a>, <a href="https://ReactForBeginners.com/friend/AWESOME">React</a>, <a href="https://SublimeTextBook.com/friend/AWESOME">Sublime</a> courses.</sub></p>

---


## Install

```
$ npm install --save-dev gulp-imagemin
```


## Usage

### Basic

```js
const gulp = require('gulp');
const imagemin = require('gulp-imagemin');
import gulp from 'gulp';
import imagemin from 'gulp-imagemin';

gulp.task('default', () =>
export default () => (
gulp.src('src/images/*')
.pipe(imagemin())
.pipe(gulp.dest('dist/images'))
@@ -39,7 +31,7 @@ gulp.task('default', () =>
//
.pipe(imagemin([
imagemin.gifsicle({interlaced: true}),
imagemin.jpegtran({progressive: true}),
imagemin.mozjpeg({quality: 75, progressive: true}),
imagemin.optipng({optimizationLevel: 5}),
imagemin.svgo({
plugins: [
@@ -51,23 +43,6 @@ gulp.task('default', () =>
//
```

Note that you may come across an older, implicit syntax. In versions < 3, the same was written like this:

```js
//
.pipe(imagemin({
interlaced: true,
progressive: true,
optimizationLevel: 5,
svgoPlugins: [
{
removeViewBox: true
}
]
}))
//
```

### Custom plugin options and custom `gulp-imagemin` options

```js
@@ -86,36 +61,35 @@ Note that you may come across an older, implicit syntax. In versions < 3, the sa
//
```


## API

Comes bundled with the following **lossless** optimizers:
Comes bundled with the following optimizers:

- [gifsicle](https://github.com/imagemin/imagemin-gifsicle)*Compress GIF images*
- [jpegtran](https://github.com/imagemin/imagemin-jpegtran)*Compress JPEG images*
- [optipng](https://github.com/imagemin/imagemin-optipng)*Compress PNG images*
- [svgo](https://github.com/imagemin/imagemin-svgo)*Compress SVG images*
- [gifsicle](https://github.com/imagemin/imagemin-gifsicle)*Compress GIF images, lossless*
- [mozjpeg](https://github.com/imagemin/imagemin-mozjpeg)*Compress JPEG images, lossy*
- [optipng](https://github.com/imagemin/imagemin-optipng)*Compress PNG images, lossless*
- [svgo](https://github.com/imagemin/imagemin-svgo)*Compress SVG images, lossless*

These are bundled for convenience and most users will not need anything else.

### imagemin([plugins], [options])
### imagemin(plugins?, options?)

Unsupported files are ignored.

#### plugins

Type: `Array`<br>
Default: `[imagemin.gifsicle(), imagemin.jpegtran(), imagemin.optipng(), imagemin.svgo()]`
Type: `Array`\
Default: `[imagemin.gifsicle(), imagemin.mozjpeg(), imagemin.optipng(), imagemin.svgo()]`

[Plugins](https://www.npmjs.com/browse/keyword/imageminplugin) to use. This will overwrite the default plugins. Note that the default plugins comes with good defaults and should be sufficient in most cases. See the individual plugins for supported options.
[Plugins](https://www.npmjs.com/browse/keyword/imageminplugin) to use. This will completely overwrite all the default plugins. So, if you want to use custom plugins and you need some of defaults too, then you should pass default plugins as well. Note that the default plugins come with good defaults and should be sufficient in most cases. See the individual plugins for supported options.

#### options

Type: `Object`
Type: `object`

##### verbose

Type: `boolean`<br>
Type: `boolean`\
Default: `false`

Enabling this will log info on every image passed to `gulp-imagemin`:
@@ -125,7 +99,11 @@ gulp-imagemin: ✔ image1.png (already optimized)
gulp-imagemin: ✔ image2.png (saved 91 B - 0.4%)
```

##### silent

Type: `boolean`\
Default: `false`

## License
Don't log the number of images that have been minified.

MIT © [Sindre Sorhus](https://sindresorhus.com)
You can also enable this from the command-line with the `--silent` flag if the option is not already specified.
44 changes: 28 additions & 16 deletions test.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,43 @@
import fs from 'fs';
import path from 'path';
import {promises as fs} from 'node:fs';
import path from 'node:path';
import {fileURLToPath} from 'node:url';
import imageminPngquant from 'imagemin-pngquant';
import pify from 'pify';
import Vinyl from 'vinyl';
import getStream from 'get-stream';
import test from 'ava';
import m from '.';
import gulpImagemin, {mozjpeg, svgo} from './index.js';

const fsP = pify(fs);
const __dirname = path.dirname(fileURLToPath(import.meta.url));

const createFixture = async (plugins, file = 'fixture.png') => {
const buf = await fsP.readFile(path.join(__dirname, file));
const stream = m(plugins);
const buffer = await fs.readFile(path.join(__dirname, file));
const stream = gulpImagemin(plugins);

stream.end(new Vinyl({
path: path.join(__dirname, file),
contents: buf
contents: buffer,
}));

return {buf, stream};
return {buffer, stream};
};

test('minify images', async t => {
const {buf, stream} = await createFixture();
const {buffer, stream} = await createFixture();
const file = await getStream.array(stream);

t.true(file[0].contents.length < buf.length);
t.true(file[0].contents.length < buffer.length);
});

test('minify JPEG with custom settings', async t => {
const mozjpegOptions = {
quality: 30,
progressive: false,
smooth: 45,
};
const {buffer, stream} = await createFixture([mozjpeg(mozjpegOptions)], 'fixture.jpg');
const file = await getStream.array(stream);

t.true(file[0].contents.length < buffer.length);
});

test('use custom plugins', async t => {
@@ -38,13 +50,13 @@ test('use custom plugins', async t => {
});

test('use custom svgo settings', async t => {
const svgoOpts = {
const svgoOptions = {
js2svg: {
indent: 2,
pretty: true
}
pretty: true,
},
};
const {stream} = await createFixture([m.svgo(svgoOpts)], 'fixture-svg-logo.svg');
const {stream} = await createFixture([svgo(svgoOptions)], 'fixture-svg-logo.svg');
const compareStream = (await createFixture(null, 'fixture-svg-logo.svg')).stream;
const file = await getStream.array(stream);
const compareFile = await getStream.array(compareStream);
@@ -53,7 +65,7 @@ test('use custom svgo settings', async t => {
});

test('skip unsupported images', async t => {
const stream = m();
const stream = gulpImagemin();
stream.end(new Vinyl({path: path.join(__dirname, 'fixture.bmp')}));
const file = await getStream.array(stream);