Webpack の clean-webpack-plugin のメモ

よんどころなき事情により Webpack で clean-webpack-plugin を使おうとしたのですが、過去の記事を見ていたら嵌まったのでメモしておきます。

経緯

処理中のフラグによって .js ファイルを複数出力(数はフラグにより増減)しようとしたのですが、減らすときは当然 output 先 (ここでは dist/js )は前の出力結果が残ってしまっているので、余計なゴミとして残ってしまうことに気付きました。

そこで Webpack のビルド直前に output 先をクリーニングしようと考え、検索すると clean-webpack-plugin がヒットしたので、これを試そうとしました。

なお、パッケージの指定は以下の通りです。

    "devDependencies": {
        "webpack": "^5.15.0",
        "webpack-stream": "^6.1.1",
        "terser-webpack-plugin": "^5.1.1",
        "clean-webpack-plugin": "^3.0.0"
    },
    "resolutions": {
        "webpack": "^5.15.0"
    },

Gulp から webpack-stream 経由で Webpack を使用、という状況です。

現象

clean-webpack-plugin の使い方の記事を見ると、 webpack.config.js に以下のように書くケースが散見されました。

const webpackTerser          = require('terser-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); // require
const path                   = require('path');
const glob                   = require('glob');

const entry = () => {
    const entries = glob
        .sync(
            '**/*.js',
            {
                cwd: 'src/js',
                ignore: [
                    '**/_*.js'
                ]
            }
        )
        .map(function (key) {
            return [key, path.resolve('src/js', key)];
        });
    return Object.fromEntries(entries);
};
const configs = {
    mode: 'development',
    entry: entry(),
    output: {
        filename: '[name]',
    },
    devtool: 'inline-source-map',
    plugins: [
        // plugin
        new CleanWebpackPlugin([
            'dist/js' // cleanup path
        ]),
    ],
    optimization: {
            minimizer: [
            new webpackTerser({
                extractComments: 'some',
                terserOptions: {
                    compress: {
                        drop_console: false,
                    },
                },
            }),
        ],
    },
};

module.exports = configs;

今回引っかかったのは CleanWebpackPlugin インスタンスへの引数で「クリーニングしたいディレクトリのパスの配列」を指定する部分。

Error: clean-webpack-plugin only accepts an options object. See:
            https://github.com/johnagan/clean-webpack-plugin#options-and-defaults-optiona

clean-webpack-plugin の引数はオブジェクトの形式ですって?

散見される記事は配列でした。

上記エラー文の通り、 Github を参照すると……確かにオブジェクトの形式で指定されています。

これ以上は突っ込んでいませんが、どうやらどこかのバージョン (今回インストールした clean-webpack-plugin3.0.0 だったので、2系と3系で乖離があるのかもしれません) で仕様が変わったのかもしれません。

これを受けて以下のように修正。

const webpackTerser = require('terser-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); // require
const path          = require('path');
const glob          = require('glob');

const entry = () => {
    const entries = glob
        .sync(
            '**/*.js',
            {
                cwd: 'src/js',
                ignore: [
                    '**/_*.js'
                ]
            }
        )
        .map(function (key) {
            return [key, path.resolve('src/js', key)];
        });
    return Object.fromEntries(entries);
};
const configs = {
    mode: 'development',
    entry: entry(),
    output: {
        filename: '[name]',
    },
    devtool: 'inline-source-map',
    plugins: [
        new CleanWebpackPlugin({
            cleanOnceBeforeBuildPatterns: [
                'dist/js/**/*.js' // cleanOnceBeforeBuildPatterns キーの中に配列指定。また、ディレクトリパスの指定ではなく、 glob での指定
            ],
        }),
    ],
    optimization: {
            minimizer: [
            new webpackTerser({
                extractComments: 'some',
                terserOptions: {
                    compress: {
                        drop_console: false,
                    },
                },
            }),
        ],
    },
};

module.exports = configs;

変更点は以下の2つ。

  1. 指定そのものを cleanOnceBeforeBuildPatterns キーに対する値として配列を記述するようにしたこと
  2. ディレクトリパスではなく glob 指定ということ (拡張子指定が不要な場合は dist/js/**/* というような形)

これで意図した挙動になりました。

余談

最初とりあえず試すだけ試そうとして

    plugins: [
        new CleanWebpackPlugin(),
    ],

途中試験のため cwd も抜いていたせいで、引数なしで CleanWebpackPlugin インスタンスを起こしたところ、プロジェクトルートからファイルを削除しようとして焦りました (デフォルトが dry: false(テストなし), verbose: false(削除するファイルをコンソールに出力しない), cleanOnceBeforeBuildPatterns: ['**/*'](ルートからの全てのファイル) という指定だったため)。

うっかり走らせないこと。

参考

(余談) エントリーポイントの ignore 指定

この記事を書いた人

アルム=バンド

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