Brief Description:
I am running into unexpected outputs/issues when working with .css files referenced inside <link> tag in the .html template provided to HtmlWebpackPlugin along with using MiniCssExtractPlugin and some loaders.
Directory structure:
| package.json
| webpack.prod.js
| src
| css
| main.css
| another.css
| ...
| images
| image1.png
...
| scripts
| script1.js
| script2.js
| templates
| file1.html
| file2.html
...
| ...
File contents for directory structure shown above:
script1.js contains reference to script2.js but does not have any reference to css files.
.css files are only referenced using <link> tags in .html files contained in ./src/templates folder.
webpack.prod.js contents:
const path = require('path');
const fs = require('fs');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const htmlTemplates = fs.readdirSync(path.resolve(__dirname, 'src/templates')).filter(file => /\.html$/i.test(file));
module.exports = {
mode: "production",
entry: {
index: "./src/scripts/script1.js",
},
output: {
filename: 'static/[name]-[contenthash].bundle.js',
path: path.resolve(__dirname, 'build'),
publicPath: '/',
assetModuleFilename: "static/[name]-markedByAssetModuleFilename-[contenthash].bundle[ext]",
clean: true,
},
optimization: {
minimizer: [
new CssMinimizerPlugin(),
new TerserPlugin({
extractComments: false, //omit the creation of a separate LICENSE file
terserOptions: {
format: {
comments: false, //remove the comments from the bundled output
},
}
}),
],
},
plugins: [
...htmlTemplates.map((file) => {
return new HtmlWebpackPlugin({
template: `./src/templates/${file}`,
filename: `htmlFiles/${file.replace(/\.html$/, '.bundle.html')}`,
inject: 'head',
scriptLoading: 'module',
minify: {
collapseWhitespace: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true,
minifyJS: true,
minifyCSS: true
}
});
}),
new MiniCssExtractPlugin({
filename: 'static/[name]-[contenthash].bundle.css',
}),
],
module: {
rules: [
{
test: /\.js$/i,
exclude: [/node_modules/],
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
},
},
},
{
test: /\.html$/i,
use: ["html-loader"]
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: "asset/resource",
},
{
test: /\.css$/i,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
],
}
],
},
};
package.json
"devDependencies": {
"@babel/preset-env": "^7.23.6",
"babel-loader": "^9.1.3",
"css-loader": "^6.8.1",
"css-minimizer-webpack-plugin": "^5.0.1",
"html-loader": "^4.2.0",
"html-webpack-plugin": "^5.6.0",
"mini-css-extract-plugin": "^2.7.6",
"terser-webpack-plugin": "^5.3.10",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
...
...
}
Output that I expected:
When I run webpack --config webpack.prod.js I should get ./build/htmlFiles folder containing all the "processed" html files. These html files should have script tag for bundled script1.js and should have link tags pointing to css files that were processed. Processed css files should be named according to filename specified for MiniCssExtractPlugin and not per output.assetModuleFilename. The css files should not be empty.
Output that I get:
The .html files inside ./build/htmlFiles folder have link tags and script tag pointing to css files and script that is output in ./build folder. So far so good.
I discover that inside ./build/static folder the .css files are all named according to name specified under ouput.assetModuleFilename. All .css files are also empty and look like this:
// extracted by mini-css-extract-plugin
export {};
I figured out that if I modify module.rules to have the include property such as shown below and pointing to a file that does not exist then all the .css files inside ./build/static have the css contents inside but no longer say // extracted by mini-css-extract-plugin Although, they are still named according to output.assetModuleFilename. If include does point to actual file that exists then that file inside ./build/static will not have css content and will be empty. Other processed .css files inside .build/static will have content then. I don't understand what is going on here.
{
test: /\.css$/i,
use: [
MiniCssExtractPlugin.loader, //2. Extract css into files
'css-loader' //1. Turns css into commonjs
],
include: path.resolve(__dirname, './src/css/nonExistentFile.css'),
}
How do I get the output that I expect? I want to use all css inside my html templates using <link> tags pointing to processed .css files inside build directory. I don't want to have css import statements inside shared script1.js file. Each .html template file points to its own .css file using <link> tags.
I have tried reading documentation for last 2 days but cant figure out webpack just yet. This is my first time using webpack.