`gulp` + `imagemin-pngquant` で特定の画像を処理すると大量の文字化けが流れて最終的にエラーが発生する

gulp + imagemin-pngquant で特定の画像を処理すると、大量の文字化けが流れて最終的にエラーが発生する現象に遭遇したのでメモ。

現象再現

現象再現のため、最小限構成を作りました。

{
  "name": "pngquant_test",
  "version": "1.0.0",
  "description": "pngquant9.0.0のテストです",
  "main": "gulpfile.js",
  "scripts": {
    "start": "gulp"
  },
  "author": "アルム=バンド",
  "dependencies": {
    "gulp": "^4.0.2",
    "gulp-imagemin": "^7.1.0",
    "imagemin-pngquant": "^9.0.0",
    "imagemin": "^7.0.1"
  }
}

package.json はこのような形。検証のため、 gulp すら使わないパターンも考慮してプレーンの imagemin も入れています。

const gulp        = require('gulp');
const imagemin    = require('gulp-imagemin');
const imageminPng = require('imagemin-pngquant');

//画像圧縮
const imageminify = () => {
    return gulp.src('src/**/*.png', {
            since: gulp.lastRun(imageminify)
        })
        .pipe(imagemin([
            imageminPng({
                quality: [0.8, 0.9],
                speed: 1
            })
          ]))
        .pipe(gulp.dest('dist/'));
};

//コールタスク
exports.default = gulp.parallel(imageminify);

gulpfile.jssrc ディレクトリの画像を dist に出力するだけの簡単なタスクです。パラメータとしては qualityspeed を指定。

const imagemin = require('imagemin');
const imageminPngquant = require('imagemin-pngquant');

(async () => {
    await imagemin(['src/**/*.png'], {
        destination: 'dist/',
        plugins: [
            imageminPngquant({
                quality: [0.8, 0.9],
                speed: 1
            })
        ]
    });

    console.log('Images optimized');
})();

index.js 。 gulp 不使用のプレーンなもので、 imagemin-pngquant – npm のサンプルをコピーして上と同じパラメータを指定しただけです。

現象

これで以下の画像を処理します。

サンプル画像
サンプル画像

で、結果が以下。

gulp

gulp タスクのエラー
gulp タスクのエラー
> gulp

## 文字化け部分大量

    stderr:
    failed: true
    timedOut: false
    isCanceled: false
    killed: false
    fileName: PATH\TO\PROJECT\src\hoge.png
    domainEmitter: [object Object]
    domainThrown: false

[XX:XX:XX] 'default' errored after 28 s
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

gulp タスクの方は大量の文字化け文字が流れて、 ctrl + c の強制終了もしばらく効かない(応答なし)になるというハングっぷり。

しばらく待って止まると、最後に上述のエラーが表示されます。

nodeスクリプト

> node ./index.js
(node:2344) UnhandledPromiseRejectionWarning: Error
    at makeError (PATH\TO\PROJECT\node_modules\imagemin-pngquant\node_modules\execa\lib\error.js:59:11)
    at handlePromise (PATH\TO\PROJECT\node_modules\imagemin-pngquant\node_modules\execa\index.js:114:26)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async PATH\TO\PROJECT\node_modules\p-pipe\index.js:12:19
    at async handleFile (PATH\TO\PROJECT\node_modules\imagemin\index.js:21:9)
    at async PATH\TO\PROJECT\node_modules\imagemin\index.js:54:13
    at async Promise.all (index 0)
    at async PATH\TO\PROJECT\index.js:5:5
(node:2344) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing 
inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:2344) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

index.js の方はまだ有情で、さっと止まってくれます。

検証

現象についていくつか検証しました。

  • 他のpng画像で実験したところ、発生するもの(例: 上述サンプル)と発生しないものがあった
  • quality のパラメータを [0.8, 0.9] から [0.7, 0.9] に変更すると正常に処理が完了した
  • imagemin-pngquant のバージョンを 9.0.0 ではなく 8.0.0 で同じ gulp タスクを走らせてもエラーにはならない

以上より、 imagemin-pngquant のバージョン・圧縮率・画像の相性のような気はします。

各ツールのバージョン

Node.js

> node -v
v12.17.0

npm

>npm -v
6.14.4

yarn

>yarn -v
1.22.4

windows-build-tools

> npm list -g --depth 0
C:\Program Files (x86)\Nodist\bin
## 略
+-- windows-build-tools@1.3.2
## 略

Python

3.7.1150.0 (32bit)

対処療法

パッケージのバージョンを落とすのはやや気が引けたので、今回はquality のパラメータを [0.8, 0.9] から [0.7, 0.9] に変更することで対処しました。

備考

npm や yarn でグローバルインストールしているパッケージとバージョンの一覧表示方法について。

npm

npm の場合は npm list -g --depth 0

yarn

yarn の場合は yarn global list

参考

グローバルインストールの確認

この記事を書いた人

アルム=バンド

フロントエンド・バックエンド・サーバエンジニア。LAMPやNodeからWP、Gulpを使ってejs,Scss,JSのコーディングまで一通り。たまにRasPiで遊んだり、趣味で開発したり。