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をデシリアライズするだけでいけると思いましたが、 moduleやpluginsのオブジェクトがうまく展開されずに撃沈しました。
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-bundle をcomposer removeで削除すると webpack.config.js も一緒に削除されてしまいます。 削除の前に必ずwebpack.config.jsのバックアップを行ってください。
まとめ
Webpack Encoreを使うと簡単に webpack.config.js を用意できます。 しかしあくまでもラッパーのため、本家バージョンアップへの追従に時間がかかる、複雑な設定になると結局本家webpackを調べないといけない、などのデメリットがあります。 もし特段の事情がなければ Webpack Encore ではなくピュアなwebpack構成でプロジェクトを進めたほうが後々つぶしが効くと思います。