What would be a web application without styles? Even a small application used on production has defined colors, margins, fonts etc. I have to take them into consideration when bundling it with webpack. This time I will add CSS file to the package.
Table of contents
If you want to jump to other parts of the tutorial, use table of contents.
Styles in my sample application
Earlier, the application contained only HTML and JS files. This time I add a sample CSS file to show you how it can be handled by webpack. Code changes can be reviewed on GitHub. Here, I will focus only on the most important aspect - I created main.css with colors and other visual changes and added it to styles directory. Normally, I would also add a link tag to the HTML files to include the CSS file but I will not do that as I remember that the HTML files are only templates. The final files should be generated by webpack. I expect webpack to add proper dependencies as well.
Install css-loader and mini-css-extract-plugin
I need two frontend components for what I want to do. The first one is css-loader which is responsible for reading styles from CSS files. The second one is mini-css-extract-plugin which can create CSS file with styles that was read by the previous loader.
The installation is as easy as in the previous parts of the tutorial.
D:\Git\multi-page-webpack-example\src>npm install css-loader --save-dev npm WARN multi-page-webpack-example@1.0.0 No description npm WARN multi-page-webpack-example@1.0.0 No repository field. npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules\fsevents): npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"}) + css-loader@2.1.0 added 26 packages from 53 contributors and audited 4442 packages in 21.391s found 0 vulnerabilities
D:\Git\multi-page-webpack-example\src>npm install mini-css-extract-plugin --save-dev npm WARN multi-page-webpack-example@1.0.0 No description npm WARN multi-page-webpack-example@1.0.0 No repository field. npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules\fsevents): npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"}) + mini-css-extract-plugin@0.5.0 added 2 packages from 2 contributors and audited 4459 packages in 11.185s found 0 vulnerabilities
If everything goes well (as above), you should see two new entries in package.json.
package.json
------------
{
"name": "multi-page-webpack-example",
"version": "1.0.0",
"description": "",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"css-loader": "^2.1.0",
"html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.5.0",
"webpack": "^4.27.1",
"webpack-cli": "^3.1.2"
}
}
css-loader and mini-css-extract-plugin configuration
webpack.config.js
-----------------
...
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: {
races: "./main/webapp/scripts/races.js",
results: "./main/webapp/scripts/results.js",
},
output: {
path: __dirname + "/main/webapp/dist/",
filename: "scripts/[name].bundle.js"
},
plugins: [
...
new MiniCssExtractPlugin({
filename: "css/[name].css",
chunkFilename: "[id].css"
})
],
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
},
],
},
};
css-loader plugin does not require special configuration. The case is different with mini-css-extract-plugin. I use two options:
- filename - specifies the path and name of CSS files to be generated; in my case, it is css directory and the name is the name of the entry point; I have two entry points: races and results so there should be two CSS files generated,
- chunkFilename - defines a pattern for naming chunks; I have only one chunk so it does not do anything complex.
Bundling CSS
Everything is ready for a test. I build a package as usual.
D:\Git\multi-page-webpack-example\src>npx webpack --mode=development
npx: installed 1 in 13.494s
The "path" argument must be of type string
D:\Git\multi-page-webpack-example\src\node_modules\webpack\bin\webpack.js
Hash: b41f0a9b3d7bf61d1d51
Version: webpack 4.27.1
Time: 3442ms
Built at: 2019-01-26 20:47:02
Asset Size Chunks Chunk Names
css/races.css 205 bytes races [emitted] races
css/results.css 205 bytes results [emitted] results
races.html 463 bytes [emitted]
results.html 691 bytes [emitted]
scripts/races.bundle.js 5.16 KiB races [emitted] races
scripts/results.bundle.js 6.07 KiB results [emitted] results
Entrypoint races = css/races.css scripts/races.bundle.js
Entrypoint results = css/results.css scripts/results.bundle.js
[./main/webapp/scripts/races.js] 534 bytes {races} [built]
[./main/webapp/scripts/results.js] 1.36 KiB {results} [built]
[./main/webapp/styles/main.css] 39 bytes {races} {results} [built]
+ 1 hidden module
Child html-webpack-plugin for "races.html":
1 asset
Entrypoint undefined = races.html
[./node_modules/html-webpack-plugin/lib/loader.js!./main/webapp/races.html] 578 bytes {0} [built]
[./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 472 bytes {0} [built]
[./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 497 bytes {0} [built]
+ 1 hidden module
Child html-webpack-plugin for "results.html":
1 asset
Entrypoint undefined = results.html
[./node_modules/html-webpack-plugin/lib/loader.js!./main/webapp/results.html] 824 bytes {0} [built]
[./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 472 bytes {0} [built]
[./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 497 bytes {0} [built]
+ 1 hidden module
Child mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!main/webapp/styles/main.css:
Entrypoint mini-css-extract-plugin = *
[./node_modules/css-loader/dist/cjs.js!./main/webapp/styles/main.css] 375 bytes {mini-css-extract-plugin} [built]
+ 1 hidden module
Of course, I can also use the production mode:
npx webpack --mode=production
Whatever mode I use, two CSS files are generated: races.css and results.css.
It is worth to notice that one original file - main.css was transformed to two files. In this case might seem strange but it is a good approach when CSS files differ between pages.
One last item is left - do the generated HTML files have a reference to the generated CSS files?
races.html
----------
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Previous races</title>
<link href="css/races.css" rel="stylesheet"><script type="text/javascript" src="scripts/races.bundle.js"></script></head>
<body>
...
Yes, they do.
When I open races.html in the browser, the page is styled and everything works well.
Summary
In this article, I added a simple CSS files to the project and bundled it with webpack.
Source code created in this part is on tutorial/part05-css branch on GitHub.