Getting started with webpack - Part 3: Bundling other file types
You will need Node 6.11.5+ installed on your machine.
In this part of the series, we will dig deeper into webpack and see what else is possible. We will specifically try to load other file types using webpack. As seen in the image below, webpack can handle other file types other than JavaScript.
In the previous part of the series, we learned how to configure webpack and define some defaults. We also learned how modules work in ES6 and we applied what we learned in code.
Let’s get started.
Source code of the application is available on GitHub.
Prerequisites
To follow along in this series, you need the following requirements:
- Completed all previous parts of the series.
- Basic knowledge of JavaScript.
- Basic knowledge of the CLI.
- A text editor. VS Code is recommended.
- Node.js (>=6.11.5) and npm installed locally.
Let’s continue with the series.
Introducing loaders in webpack
By default, webpack has support for loaders. This is how webpack knows what to do with specific file types.
Loaders are transformations that are applied on the source code of a module. They allow you to pre-process files as you
import
or “load” them. Thus, loaders are kind of like “tasks” in other build tools and provide a powerful way to handle front-end build steps. Loaders can transform files from a different language (like TypeScript) to JavaScript or inline images as data URLs. Loaders even allow you to do things likeimport
CSS files directly from your JavaScript modules! - webpack documentation
With loaders, we will be able to tell webpack how to handle some other file types other than JavaScript. We will explore that in a bit but right now let’s see what loaders look like in code.
To get started with loaders, you need to install them using npm as they don’t come bundled with webpack by default. To install the TypeScript loader, you need to run the command below for example:
$ npm install --save ts-loader
This will install the loader and you can then instruct webpack to use it now that it is available to your application.
Loaders in webpack configuration file
To use a loader, you need to update the webpack configuration file and tell webpack which loader to use and for which file type. For example:
module.exports = {
module: {
// [...]
rules: [
{
test: /\.ts$/,
use: 'ts-loader'
}
]
// [...]
}
};
In the code above, we see that we have a new rules
key. The value being an array of rules. Each rule is an object containing at least the test
and use
key:
{
test: /\.ts$/,
use: 'ts-loader'
}
The test
key is a regular expression that tells webpack to match TypeScript files, that is files that have the .ts
extension. The use
property has a string
value that tells webpack the loader to use for this file type. In this case, we are using the ts-loader
we installed earlier.
The use
property can also take an array
of loaders instead of the single loader passed as a string
. For example:
{
test: /\.ts$/,
use: [
'yet-another-loader',
'another-loader',
'ts-loader'
]
}
With this, webpack will know how to handle .ts
files and will bundle it when it comes across it. In the example above, webpack will use all three loaders, starting from the bottom one.
If you need to specify options for a specific loader, you can also do so using this syntax:
{
test: /\.ts$/,
use: [
{ loader: 'yet-another-loader' },
{
loader: 'another-loader',
options: {
// loader options...
}
},
{ loader: 'ts-loader' }
]
}
Loaders get called from last to first. This means if you have multiple loaders defined in an array, the last one on that list will be the first one executed and the first one will be the last one executed. As in the example above, the
ts-loader
will be called first, thenanother-loader
, and finally, theyet-another-loader
loader.
Inline loaders
In addition to adding your loaders to the configuration file, which is recommended, you can load them inline.
When importing your file, you can specify the loaders that will be used for the file. To add them, separate loaders from the resource with !
. Each part is resolved relative to the current directory:
import Styles from 'style-loader!css-loader?modules!./styles.css';
You can pass options to the loader with a query parameter, for example: ?foo=bar&bar=baz
. You can also pass a JSON object, for example ?{"key":"value","foo":"bar"}
.
While this method works, putting the loaders in your webpack configuration file is still the best way to handle webpack loaders.
CLI loaders
One final way to use loaders in webpack is through the command line. You can run the following command to bind the loaders
$ webpack --module-bind ts-loader --module-bind 'css=style-loader!css-loader'
This uses the ts-loader
for .ts
files, and the style-loader
and css-loader
for .css
files.
Writing your own loaders
If you have a specific use case and you cannot find a loader for it in the npm repository, then you can look at the documentation on how to create your own webpack loader.
Adding a loader to our existing project
If you still have the code from part two, we will be using that as a base for this part. If you do not have it locally, download the code for the project from GitHub here.
In this part, we will be using loaders to handle the styling of our project. To get started, we will install the loaders we would be using. Run the following command to install the appropriate loaders:
$ npm install sass-loader node-sass css-loader style-loader --save-dev
Above, we are installing quite a few packages:
-
sass-loader
- we need the sass-loader to load Sass files and convert them to CSS.The sass-loader…requires you to install either Node Sass or Dart Sass on your own. This allows you to control the versions of all your dependencies, and to choose which Sass implementation to use. - GitHub repository’s readme
-
node-sass
- allows you to natively compile .scss files to CSS. -
css-loader
- we need the css-loader to handle the CSS generated from the sass-loader. -
style-loader
- we need the style-loader to actually add the CSS loaded to the page.
After running the command, your package.json
file should now be automatically updated with the new dependencies:
// File: ./package.json
{
// [...]
"devDependencies": {
"css-loader": "^2.0.0",
"node-sass": "^4.11.0",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
// [...]
}
// [...]
}
Next, open your webpack configuration file and update the configuration with the rules
key as seen below:
// File: ./webpack.config.js
let webpack = require('webpack');
let path = require('path');
module.exports = {
mode: 'development',
entry: path.resolve(__dirname + '/src/index.js'),
output: {
path: path.resolve(__dirname + '/dist/assets'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'sass-loader'
]
}
]
}
};
As seen, we added the rules
key so we can specify the loaders we want to use with the project. In this case, we are using the three loaders we installed earlier. Remember, the loaders are called from last to first in this list.
This means that the sass loader will first compile the .scss
file to .css
. Then the css-loader
will be called to load the .css
file, then finally, the style-loader
will make sure it is injected into the final code.
Next, open the dist``/index.html
file and replace the contents with the following code:
<!-- File: ./dist/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Webpack Sample</title>
</head>
<body>
<div class="container">
<h1></h1>
</div>
<script src="./assets/bundle.js"></script>
</body>
</html>
Above, the only change we made is wrapping the h1
tag inside a .container
and removing the default content of the h1
tag.
Next, open the src/index.js
file which contains the script for our page that is to be bundled and replace the code with the following:
// File: ./src/index.js
import generateRandomString from './utilities/random';
import './style.scss';
document.addEventListener('DOMContentLoaded', function () {
var randomString = `Random String: <span>${generateRandomString()}</span>`;
window.setTimeout(function () {
document.getElementsByTagName('h1')[0].innerHTML = randomString
}, 0);
});
Above, we made a few minor changes to what the script was before:
- We
import
ed the.scss
file directly to the JavaScript file. When webpack sees this import, it’ll match the file extension.scss
and run the loaders that match the extension. - We added a
span
around the randomly generated string. This is so we can style it separately.
Finally, create a new file in the src
directory called style.scss
and paste the following code in:
/* File: ./src/style.scss */
body {
margin: 0;
padding: 0;
}
.container {
max-width: 900px;
padding: 0 16px;
margin: 50px auto 0;
h1 {
margin: 0;
text-align: center;
color: #272727;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Open Sans',
'Helvetica Neue', sans-serif;
span {
color: #999;
}
}
}
Above, we have the Sass file that we want to use as the style to the page. Save it and run the following command below to install all the dependencies and then bundle our assets:
$ npm install
$ npm run build
As seen above, the style.scss
file is built successfully without issues. When the build is complete, you can now run the Node.js web server with the following command:
$ node dist/server.js
You should see the application running as shown above in http://localhost:3000. If you Inspect Element and look at the source code, you’ll see that the style has been bundled and injected into the web page by webpack.
Conclusion
In this tutorial of the series, we have learned how to use loaders to handle other asset files especially stylesheets. We also learned how loaders work. However, Webpack is a lot more powerful than this. We will dive a little deeper in the next part.
The source code to this application is available on GitHub.
7 February 2019
by Neo Ighodaro