前提
webpack5を使ってjs,scss,ejsファイルをバンドルする環境を構築しようとしております。
概要
開発の際にwebpack-dev-server(以下devserver)を実行しブラウザ上でファイルの変更を確認しているのですが、ejsのテンプレートファイルがwatch対象になっておらずリアルタイムで変更が反映されずに困っています。
原因または解決策のご教授をお願いしたいです。
事象
- devserverを実行してもtemplateディレクトリ内にあるejsファイルの変更が監視されない
- 一度devserverを停止し、再度実行すると差分は反映されている(リアルタイムで変更がなされない)
- templateディレクトリ以外のejsファイル、およびすべてのscss, jsファイルは問題なく監視対象となっている
該当のソースコード
ディレクトリ構成
webpack.config.js
js
1const path = require('path'); 2const globule = require('globule'); 3const {CleanWebpackPlugin} = require('clean-webpack-plugin'); 4const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 5const HtmlWebpackPlugin = require('html-webpack-plugin'); 6const HtmlWebpackHarddiskPlugin = require('html-webpack-harddisk-plugin'); 7const TerserPlugin = require("terser-webpack-plugin"); 8const CopyPlugin = require("copy-webpack-plugin"); 9 10const assignPlugins = (env_mode) => { 11 const srcFiles = globule.find(['**/*.ejs', '!**/_*.ejs'], {cwd: `${__dirname}/src/ejs`}); 12 const entriesList = {}; 13 const assignObject = {plugins: [ 14 new CleanWebpackPlugin({ 15 cleanOnceBeforeBuildPatterns: [ 16 'assets/stylesheet', 17 'assets/javascript', 18 ] 19 }), 20 new MiniCssExtractPlugin({ 21 filename: env_mode 22 ? 'assets/stylesheet/bundle.css' 23 : 'assets/stylesheet/bundle.[contenthash].css', 24 }), 25 new HtmlWebpackHarddiskPlugin({ 26 alwaysWriteToDisk: true 27 }), 28 new CopyPlugin({ 29 patterns: [ 30 {from: "src/images/", to: "assets/images/"} 31 ] 32 }) 33 ]}; 34 35 for(const ejsFileName of srcFiles) { 36 const htmlFileName = ejsFileName.replace(new RegExp(`.ejs`, 'i'), `.html`); 37 entriesList[htmlFileName] = `${__dirname}/src/ejs/${ejsFileName}`; 38 } 39 40 for(const [htmlFileName, ejsFileName] of Object.entries(entriesList)) { 41 assignObject.plugins.push(new HtmlWebpackPlugin({ 42 filename : htmlFileName, 43 template : ejsFileName, 44 inject: 'body', 45 minify: env_mode ? 46 false : 47 { 48 collapseWhitespace: false, 49 removeComments: true, 50 }, 51 })); 52 } 53 54 return assignObject; 55}; 56 57module.exports = (env, argv) => { 58 const is_DEVELOP = argv.mode === "development"; 59 60 return [ 61 Object.assign({ 62 entry: './src/js/main', 63 output: { 64 path: path.join(__dirname, 'public'), 65 filename: is_DEVELOP 66 ? 'assets/javascript/bundle.js' 67 : 'assets/javascript/bundle.[contenthash].js', 68 }, 69 devtool: is_DEVELOP ? 'source-map' : 'eval', 70 watchOptions: { 71 ignored: /node_modules/ 72 }, 73 resolve: { 74 extensions: ['.js', '.ts'], 75 }, 76 77 optimization: { 78 minimize: is_DEVELOP ? false : true, 79 minimizer: [ 80 new TerserPlugin({ 81 terserOptions: { 82 ecma: 2020, 83 } 84 }) 85 ] 86 }, 87 88 module: { 89 rules: [ 90 { 91 test: /.ejs$/, 92 use: 'ejs-compiled-loader', 93 }, 94 { 95 test: /.scss$/, 96 use: [ 97 MiniCssExtractPlugin.loader, 98 { 99 loader: 'css-loader', 100 options: { 101 url: false, 102 sourceMap: true, 103 }, 104 }, 105 { 106 loader: 'postcss-loader', 107 options: { 108 postcssOptions: { 109 plugins: [ 110 [ 111 require('autoprefixer')({grid: true}), 112 ], 113 ], 114 } 115 } 116 }, 117 { 118 loader: 'sass-loader', 119 options: { 120 implementation: require('sass'), 121 }, 122 }, 123 ] 124 }, 125 { 126 test: /.js$/, 127 exclude: /node_modules/, 128 use: [ 129 { 130 loader: 'babel-loader', 131 options: { 132 presets: ['@babel/preset-env', '@babel/preset-react'], 133 plugins: ['@babel/plugin-transform-runtime'], 134 }, 135 }, 136 ], 137 }, 138 { 139 enforce: 'pre', 140 test: /.js$/, 141 exclude: /node_modules/, 142 loader: 'eslint-loader', 143 }, 144 ], 145 }, 146 devServer: { 147 contentBase: path.resolve(__dirname, 'public'), 148 port: 8080, 149 }, 150 stats: { 151 children: true 152 }, 153 target: is_DEVELOP ? 154 "web" : 155 ["web", "es5"] 156 }, assignPlugins(is_DEVELOP)) 157 ] 158}
index.ejs
index.ejsでは以下のようにテンプレートファイルをincludeしています。
ejs
1<% var title = "TOP"; var description = "description"; %> 2<% var path = '/assets/images/'; %> 3 4<% include ./template/_head %> 5 6<% include ./template/_header %> 7 8<main class="l-main"> 9 ... 10</main> 11<!-- l-footer --> 12<% include ./template/_footer %>
package.json
{ ... "scripts": { "start": "webpack serve --mode=development", "dev": "webpack --mode=development", "prod": "webpack --mode=production", "watch": "webpack -w --mode=development" }, "devDependencies": { ... } }
devserver起動時のログ
$ yarn start yarn run v1.22.10 $ webpack serve --mode=development ℹ 「wds」: Project is running at http://localhost:8080/ ℹ 「wds」: webpack output is served from / ℹ 「wds」: Content not from webpack is served from /path/to/dir/public (node:44401) [DEP0148] DeprecationWarning: Use of deprecated folder mapping "./" in the "exports" field module resolution of the package at /path/to/dir/node_modules/css-loader/node_modules/postcss/package.json. Update this package.json to use a subpath pattern like "./*". (Use `node --trace-deprecation ...` to show where the warning was created) ℹ 「wdm」: assets by path assets/ 392 KiB asset assets/javascript/bundle.js 387 KiB [emitted] (name: main) 1 related asset asset assets/stylesheet/bundle.css 5.45 KiB [emitted] (name: main) 1 related asset asset index.html 4.98 KiB [emitted] asset about/index.html 1.75 KiB [emitted] Entrypoint main 392 KiB (449 KiB) = assets/stylesheet/bundle.css 5.45 KiB assets/javascript/bundle.js 387 KiB 2 auxiliary assets runtime modules 1.25 KiB 6 modules modules by path ./node_modules/ 361 KiB modules by path ./node_modules/webpack-dev-server/ 21.2 KiB 12 modules modules by path ./node_modules/html-entities/lib/.js 61 KiB 5 modules modules by path ./node_modules/@babel/runtime/ 1.55 KiB 4 modules modules by path ./node_modules/webpack/hot/ 1.58 KiB 3 modules modules by path ./node_modules/querystring/.js 4.51 KiB 3 modules modules by path ./node_modules/url/.js 23.1 KiB 2 modules modules by path ./src/ 4.31 KiB (javascript) 5.42 KiB (css/mini-extract) modules by path ./src/js/ 4.27 KiB 3 modules modules by path ./src/scss/ *.scss 50 bytes (javascript) 5.42 KiB (css/mini-extract) ./src/scss/app.scss 50 bytes [built] [code generated] css ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[1].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[1].use[3]!./src/scss/app.scss 5.42 KiB [code generated] Entrypoint HtmlWebpackPlugin_0-0 = Entrypoint HtmlWebpackPlugin_1-0 = runtime modules 1.43 KiB 8 modules cacheable modules 9.14 KiB data:text/javascript,__webpack_public_path__ = __webpack_base_uri__ = htmlWebpackPluginPublicPath; 77 bytes [built] [code generated] ./node_modules/html-webpack-plugin/lib/loader.js!./src/ejs/index.ejs 6.22 KiB [built] [code generated] ./node_modules/html-webpack-plugin/lib/loader.js!./src/ejs/about/index.ejs 2.85 KiB [built] [code generated] Child HtmlWebpackCompiler compiled successfully Entrypoint child = runtime modules 937 bytes 4 modules cacheable modules 21 KiB ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[1].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[1].use[3]!./src/scss/app.scss 17.3 KiB [built] [code generated] ./node_modules/css-loader/dist/runtime/cssWithMappingToString.js 2.21 KiB [built] [code generated] ./node_modules/css-loader/dist/runtime/api.js 1.57 KiB [built] [code generated] Child mini-css-extract-plugin /path/to/dir/node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[1].use[1]!/path/to/dir/node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!/path/to/dir/node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[1].use[3]!/path/to/dir/src/scss/app.scss compiled successfully webpack 5.28.0 compiled successfully in 1357 ms ℹ 「wdm」: Compiled successfully.
- npm startによってdevserverが実行されます
- ejsファイルはejs-compiled-loaderを通りhtml-webpack-pluginによって自動生成されます。
- modeによってjs,scssファイルのソースマップやminifyの切り替えをしています。htmlファイルはいずれのmodeでも非圧縮で出力します。
試したこと
- devserverの問題かと思いwebpackのwatchモードを実行してみましたが、同様にtamplateディレクトリ内のejsファイルのみ監視対象となりませんでした
ejs-html-loader
,ejs-plain-loader
などのその他のloaderも試してみましたが、同様の結果でした
マシン環境
- MacBook Air (M1, 2020) Big Sur v11.3.1
- node: v16.2.0
- npm: v7.13.0
- yarn: v1.22.10
よろしくお願いいたします。
あなたの回答
tips
プレビュー