Tutorial for webpack 4 - part 06 - SCSS

webpackAlthough we already know how to bundle CSS files, many projects use more advanced solutions like SASS with SCSS files. That is not a problem - a proper loader for webpack solves this issue.

 

 

Table of contents

If you want to jump to other parts of the tutorial, use table of contents.

 

What is SASS?

SASS (Syntatically Awesome Style Sheets) is a CSS language extension. It makes defining styles less painful than with CSS by using some features known from programming languages like variables, nesting, importing, code reusing. Some time ago, I wrote an article about setting up SASS with gulp. You may find some useful information there.

 

SASS in my sample application

I made some simple changes in the application to use SCSS instead of CSS. I replaced main.css file with main.scss and to make use of importing and variables I externalized some values to _variables.scss. The whole changeset is on GitHub.

After this, there are no CSS files in the project so webpack is unable to bundle the code (JS files have references to main.css). I have nothing to generate CSS files neither so the project is broken. But I will fix it easily.

 

SASS loader

There should be no surprise for you when I write that I need a loader for SASS. The one I need today is, surprise, surprise, sass-loader.

D:\Git\multi-page-webpack-example\src>npm install sass-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"}) + sass-loader@7.1.0
added 8 packages from 16 contributors and audited 4480 packages in 22.006s
found 0 vulnerabilities

Additionally, I need Node support for SASS:

D:\Git\multi-page-webpack-example\src>npm install node-sass --save-dev
> This email address is being protected from spambots. You need JavaScript enabled to view it..0 install D:\Git\multi-page-webpack-example\src\node_modules\node-sass
> node scripts/install.js Downloading binary from https://github.com/sass/node-sass/releases/download/v4.11.0/win32-x64-59_binding.node
Download complete ] - :
Binary saved to D:\Git\multi-page-webpack-example\src\node_modules\node-sass\vendor\win32-x64-59\binding.node
Caching binary to C:\Users\Arek\AppData\Roaming\npm-cache\node-sass\4.11.0\win32-x64-59_binding.node > This email address is being protected from spambots. You need JavaScript enabled to view it..0 postinstall D:\Git\multi-page-webpack-example\src\node_modules\node-sass
> node scripts/build.js Binary found at D:\Git\multi-page-webpack-example\src\node_modules\node-sass\vendor\win32-x64-59\binding.node
Testing binary
Binary is fine
npm WARN rollback Rolling back readable-stream@2.3.6 failed (this is probably harmless): EPERM: operation not permitted, lstat 'D:\Git\multi-page-webpack-example\src\node_m
odules\fsevents\node_modules'
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"}) + This email address is being protected from spambots. You need JavaScript enabled to view it..0
added 137 packages from 132 contributors and audited 5007 packages in 44.018s
found 0 vulnerabilities

Two new entries were added to package.json by the above two commands:

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": {
   
...
   
"node-sass": "^4.11.0",
   
"sass-loader": "^7.1.0",
   
"webpack": "^4.27.1",
   
"webpack-cli": "^3.1.2"
 
}
}

 

Do you remember webpack.config.js from the CSS part of the tutorial? There was a rule to process *.css files. As there are no CSS files now, I amend the rule to handle *.scss files instead. webpack.config.js after the changes look like below:

webpack.config.js
-----------------
const HtmlWebpackPlugin = require('html-webpack-plugin');
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 HtmlWebpackPlugin({
           
template: "./main/webapp/races.html",
           
filename: "races.html",
           
inject: 'head',
           
chunks: ['races']
        }),
       
new HtmlWebpackPlugin({
           
template: "./main/webapp/results.html",
           
filename: "results.html",
           
inject: 'head',
           
chunks: ['results']
        }),
       
new MiniCssExtractPlugin({
           
filename: "css/[name].css",
            
chunkFilename: "[id].css"
       
})
    ],
   
module: {
       
rules: [
            {
               
test: /\.scss$/,
               
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
            },
        ],
    },
};

The above configuration means that all SCSS files are first processed by sass-loader, then the result is sent over to css-loader to finally be processed by mini-css-extract-plugin loader.

There is only one more change that needs to be made - races.js and results.js have an import statement somewhere at the beginning. Currently it points to non-existent main.css. I change it to main.scss and that is it.

races.js
--------
"use strict";
import '../styles/main.scss';
(
function() {
   
...
})();

Of course, that same change has to be made in results.js.

 

Bundling

All should work now. Let's build the package.

D:\Git\multi-page-webpack-example\src>npx webpack --mode=development
npx: installed 1 in 4.53s
The "path" argument must be of type string
D:\Git\multi-page-webpack-example\src\node_modules\webpack\bin\webpack.js
Hash: 4f8defbc5e1e88032fe1
Version: webpack 4.27.1
Time: 1645ms
Built at: 2019-01-30 07:39:33
Asset Size Chunks Chunk Names
css/races.css 201 bytes races [emitted] races
css/results.css 201 bytes results [emitted] results
races.html 463 bytes [emitted]
results.html 691 bytes [emitted]
scripts/races.bundle.js 5.17 KiB races [emitted] races
scripts/results.bundle.js 6.08 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] 535 bytes {races} [built]
[./main/webapp/scripts/results.js] 1.36 KiB {results} [built]
[./main/webapp/styles/main.scss] 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!node_modules/sass-loader/lib/loader.js!main/webapp/styles/main.scss:
Entrypoint mini-css-extract-plugin = *
[./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/lib/loader.js!./main/webapp/styles/main.scss] 354 bytes {mini-css-extract-plugin} [built]
+ 1 hidden module

 

If you have a spare minute, let's spend it on understanding what has just happened. As always, the processing started at the entry points (races.js and results.js). Both those files have import statements to load main.scss. This SCSS file is processed by the rule from webpack.config.js which pushes it through the chain of loaders including sass-loader. The generated files are produced to the output directory.

bundle scss

 

When I open dist/races.html in the browser, it displays and works correctly. Styles are properly loaded and interpreted.

 

Summary

This time I extended the sample application with SASS - SCSS files instead of using CSS files.

Source code created in this part is on tutorial/part06-scss branch on GitHub.

 

Next part

Tutorial for webpack 4 - part 07 - multiple JS files for a page.

If you like what I do, consider buying me a coffee :)

Buy me a coffeeBuy me a coffee