-ao- ramune blog

©2021 unio / GO2直営からふるラムネ
2018年12月18日 (更新:2019年07月28日)

Webpack Encoreをwebpack4に書き直す

Webpack Encoreを外す

この記事は Symfony Advent Calendar 2018 の18日目です。

今年2018年11月で、ようやく Webpack Encore がwebpack4に対応しました。 今回のようにWebpack Encoreは本家webpackの追従にタイムラグがありいろいろ歯痒い思いをしたので、 これを期に(?)ピュアな webpack.config.js に書き換えてみました。 今回書き換えるWebpack Encoreのバージョンは 0.22.2 です。

で、失敗したやつ

さっそくですが、一番最初に試して失敗した方法です。 Webpack Encoreはざっくり言うとwebpackコンフィグビルダーなので、 Encore.getWebpackConfig() の結果を webpack.config.js に書けばいけるはずです。

webpack.config.js
                
                    var Encore = require('@symfony/webpack-encore');

                    Encore
                        ...
                    ;

                    console.log(JSON.stringify(Encore.getWebpackConfig()));
                
            

Encoreで作成した設定のJSONをデシリアライズするだけでいけると思いましたが、 modulepluginsのオブジェクトがうまく展開されずに撃沈しました。

Webpack Encore vs. webpack4

仕方がないので、Webpack Encoreに用意されている関数を調べながら書き換えていきます。 下記はWebpack Encoreで書いた webpack.config.js のサンプルです。 たぶんよく使われるであろう設定をいくつか抜粋しました。

webpack.config.js
                
                    var Encore = require('@symfony/webpack-encore');

                    Encore
                        .setOutputPath('public/build/')
                        .setPublicPath('/build')
                        .cleanupOutputBeforeBuild()
                        .addEntry('hoge', './assets/hoge/index.js')
                        .enableSourceMaps(!Encore.isProduction())
                        .enableSingleRuntimeChunk()
                        .splitEntryChunks()
                        .autoProvidejQuery()
                        .enablePostCssLoader()
                        .enableSassLoader()
                        .enableVueLoader()
                    ;

                    module.exports = Encore.getWebpackConfig();
                
            

書き換え結果

先程のWebpack Encore設定を書き換えたものが以下です。 サンプルのEncoreのコードに比べると(当然ですが)記述量はかなり増えました。 実際は不要な設定もあるかと思いますが、一旦Webpack Encoreとほぼ同じ動作をする形に記述しています。 それぞれの設定の意味は、各パッケージサイトやwebpack本家サイトをご覧いただくとして、 いくつか注意点を抜粋して説明いたします。

webpack.config.js
                
                    // 以下はdevelopmentビルド時のwebpack設定です。
                    // 実際は NODE_ENV や、Symfony4で定義した .env を使って、設定を切り替える仕組みが必要です。

                    const path = require('path');

                    // 以下は@symfony/webpack-encoreをremoveした際に個別でaddが必要
                    const webpack = require('webpack');
                    const MiniCssExtractPlugin = require('mini-css-extract-plugin');
                    const ManifestPlugin = require('webpack-manifest-plugin');
                    const CleanWebpackPlugin = require('clean-webpack-plugin');
                    const VueLoaderPlugin = require('vue-loader/lib/plugin');
                    const AssetsWebpackPlugin = require('assets-webpack-plugin');

                    module.exports = {
                        mode: 'development',
                        context: __dirname,
                        // Encore.addEntryの設定
                        entry: {
                            hoge: './assets/hoge/index.js'
                        },
                        // Encore.setOutputPathとEncore.setPublicPathの設定
                        output: {
                            path: path.resolve(__dirname, 'public/build'),
                            filename: '[name].js',
                            publicPath: '/build/',
                            pathinfo: true
                        },
                        module: {
                            rules: [
                                // Babel7適用ルール
                                // v0.22.2ではデフォルト適用される
                                {
                                    test: /\.jsx?$/,
                                    exclude: /(node_modules|bower_components)/,
                                    use: {
                                        loader: 'babel-loader',
                                        options: {
                                            cacheDirectory: true,
                                            presets: ['@babel/preset-env'],
                                            plugins: ['@babel/plugin-syntax-dynamic-import']
                                        }
                                    }
                                },
                                // CSS適用ルール
                                // Encore.enablePostCssLoaderの設定
                                {
                                    test: /\.css$/,
                                    use: [
                                        path.resolve(__dirname, 'node_modules/mini-css-extract-plugin/dist/loader.js'),
                                        {loader: 'css-loader', options: {minimize: false, sourceMap: true, importLoaders: 1}},
                                        {loader: 'postcss-loader', options: {sourceMap: true}}
                                    ]
                                },
                                {
                                    test: /\.(png|jpg|jpeg|gif|ico|svg|webp)$/,
                                    loader: 'file-loader',
                                    options: {name: 'images/[name].[hash:8].[ext]', publicPath: '/build/'}
                                },
                                {
                                    test: /\.(woff|woff2|ttf|eot|otf)$/,
                                    loader: 'file-loader',
                                    options: {name: 'fonts/[name].[hash:8].[ext]', publicPath: '/build/'}
                                },
                                // Sass、Scss適用ルール
                                // Encore.enablePostCssLoaderとEncore.enableSassLoaderの設定
                                {
                                    test: /\.s[ac]ss$/,
                                    use: [
                                        path.resolve(__dirname, 'node_modules/mini-css-extract-plugin/dist/loader.js'),
                                        {loader: 'css-loader', options: {minimize: false, sourceMap: true, importLoaders: 1}},
                                        {loader: 'postcss-loader', options: {sourceMap: true}},
                                        {loader: 'resolve-url-loader', options: {sourceMap: true}},
                                        {loader: 'sass-loader', options: {sourceMap: true}}
                                    ]
                                },
                                // Vue.js適用ルール
                                // Encore.enableVueLoaderの設定
                                {
                                    test: /\.vue$/,
                                    loader: 'vue-loader'
                                }
                            ]
                        },
                        plugins: [
                            new MiniCssExtractPlugin({filename: '[name].css', chunkFilename: '[name].css'}),
                            new ManifestPlugin({
                                publicPath: null,
                                basePath: 'build/',
                                fileName: 'manifest.json',
                                transformExtensions: /^(gz|map)$/i,
                                writeToFileEmit: true,
                                seed: {},
                                filter: (file) => {
                                    return (!file.isChunk || !['_tmp_shared', '_tmp_copy'].includes(file.chunk.id));
                                },
                                map: null,
                                generate: null,
                                sort: null
                            }),
                            new webpack.LoaderOptionsPlugin({
                                debug: true,
                                options: {
                                    context: __dirname,
                                    output: {path: path.resolve(__dirname, 'public/build')}
                                }
                            }),
                            new webpack.ProvidePlugin({'$': 'jquery', jQuery: 'jquery', 'window.jQuery': 'jquery'}),
                            // Encore.cleanupOutputBeforeBuildの設定
                            new CleanWebpackPlugin(['**/*'], {
                                root: path.resolve(__dirname, 'public/build'),
                                verbose: false,
                                allowExternal: false,
                                dry: false
                            }),
                            new webpack.DefinePlugin({'process.env': {NODE_ENV: 'development'}}),
                            new VueLoaderPlugin(),
                            new AssetsWebpackPlugin({
                                path: path.resolve(__dirname, 'public/build'),
                                filename: 'entrypoints.json',
                                includeAllFileTypes: true,
                                entrypoints: true,
                                // Webpack Encoreで定義された関数をそのままコピペ
                                processOutput: (assets) => {
                                    for (const entry of ['_tmp_shared', '_tmp_copy']) {
                                        delete assets[entry];
                                    }
                                    delete assets.entrypoints;
                                    for (const asset in assets) {
                                        for (const fileType in assets[asset]) {
                                            if (!Array.isArray(assets[asset][fileType])) {
                                                assets[asset][fileType] = [assets[asset][fileType]];
                                            }
                                        }
                                    }
                                    return JSON.stringify({
                                        entrypoints: assets
                                    }, null, 2);
                                }
                            }),
                        ],
                        // splitChunks
                        // Encore.enableSingleRuntimeChunkの設定
                        optimization: {
                            namedModules: true,
                            runtimeChunk: 'single',
                            splitChunks: {chunks: 'all'}
                        },
                        devtool: 'inline-source-map',
                        performance: {hints: false},
                        stats: {
                            hash: false,
                            version: false,
                            timings: false,
                            assets: false,
                            chunks: false,
                            maxModules: 0,
                            modules: false,
                            reasons: false,
                            children: false,
                            source: false,
                            errors: false,
                            errorDetails: false,
                            warnings: false,
                            publicPath: false,
                            builtAt: false
                        },
                        resolve: {
                            extensions: ['.wasm', '.mjs', '.js', '.json', '.jsx', '.vue', '.ts', '.tsx'],
                            alias: {'vue$': 'vue/dist/vue.esm.js'}
                        },
                        externals: {}
                    };
                
            

@symfony/webpack-encore依存を切り離す

Webpack Encoreが独自に提供しているプラグインを切り離します。

クラス名 Encore独自機能 説明
FriendlyErrorsWebpackPlugin NO Encore独自のフィルタを使っているため除外
AssetOutputDisplayPlugin YES FriendlyErrorsWebpackPluginと連携して、asset情報を表示するプラグイン
DeleteUnusedEntriesJSPlugin YES addStyleEntryでcssを登録する際に、webpack本体で生成される同名のjsを削除するプラグイン

最後に@symfony/webpack-encoreを削除しますが、本家 package.json のdependenciesに記載されている各種パッケージを追加するのを忘れないでください。 loader名やpluginsの設定を参考に、必要なパッケージを個別にaddします。

Webpack Encoreを削除する

webpack-encore-bundlecomposer removeで削除すると webpack.config.js も一緒に削除されてしまいます。 削除の前に必ずwebpack.config.jsのバックアップを行ってください。

まとめ

Webpack Encoreを使うと簡単に webpack.config.js を用意できます。 しかしあくまでもラッパーのため、本家バージョンアップへの追従に時間がかかる、複雑な設定になると結局本家webpackを調べないといけない、などのデメリットがあります。 もし特段の事情がなければ Webpack Encore ではなくピュアなwebpack構成でプロジェクトを進めたほうが後々つぶしが効くと思います。

プロフィール画像
なかのひと:unio

数十年前の牧歌的なインターネッツが好きだった、永遠のモラトリアム人。 ただ、モラトリアムしててもお金は増えないので、しゃかいの厳しさを斜め後ろから眺めつつほそぼそと生活しています。

[広告]