https://www.codecademy.com/courses/learn-build-tools/lessons/building-apps-with-webpack/exercises/introduction-to-building-apps-with-webpack

Introduction to Building Apps with Webpack

What can Webpack bundle up?

  • JavaScript files
  • stylesheets
  • images
  • fonts
  • text files

Webpack Project Setup

Set Up package.json

Project metadata: https://docs.npmjs.com/cli/v7/configuring-npm/package-json

npm init -y  // create a package.json file (-y: using the default values for the metadata fields)

Install Webpack and Webpack CLI

npm install --save-dev webpack webpack-cli  //--save-dev: We want these tools to be developer dependencies because they will not be used when the final product is running

Make an entry point

A Webpack project requires an entry point where it will find the main file to bundle. The default Webpack entry point is index.js in a src folder, although this can be changed.

Define the Build Command

Scripts: https://docs.npmjs.com/cli/v7/using-npm/scripts

The webpack of the command just runs Webpack, --watch tells Webpack to automatically look for updates to our file and rebuild if any changes occur.

"scripts": {
  "build": "webpack --watch",
},

We run the build command in the terminal with:

npm run build

When we’ve set up the package.json, entry point, and build command, Webpack is ready to go!

Packing One JavaScript File

Default entry point: src/index.js

Default output: dist/main.js

Packing Multiple JavaScript Files

Build tools allow us to do just that. We can improve source code readability with functions, comments, and more. Webpack will remove much of this when creating the content served to the end-user.

In a typical static web project, we would have to put all three scripts into our HTML page and include them in the right order. Build tools like Webpack help make including resources easier.

Webpack will allow us to use import and export statements on all our front-end files, not just JavaScript. When we build Webpack stitches all our files together as if we wrote them as one.

Creating a Webpack Config

Webpack automatically looks for a configuration file named webpack.config.js.

'development' mode is used when we develop our app, producing a more readable version of the output. We switch to 'production' when we have a finished version, which makes the output less readable and more compact.

module.exports = {
  mode: 'development'
}

Defining Entry and Exit Points

The entry point allows us to define a path relative to where our webpack.config.js is located.

Unlike the entry point, the exit point requires an absolute path, which is best specified with the path Node module.

const path = require('path');
 
module.exports = {
  entry: './application/home.js',
  output: {
    filename: 'fast.js',
    path: path.resolve(__dirname, 'built'),
  },
  // ...
}

Viewing Our App with Webpack Dev Server

We would first install webpack-dev-server as a development dependency in a local environment:

npm install --save-dev webpack-dev-server

Next, we need an HTML document, and it should embed the JavaScript from our exit point:

<script src="./dist/main.js"></script>

To use webpack-dev-server, we’ll add start command to package.json inside of the scripts section.

"build": "webpack --watch",
"start": "webpack serve"

This is building our project and then serving it to the browser.

The build command is going to compile our project as well as update it when we make changes.

The serve command is going to serve our build and refresh when the build changes.

In order to have our project update any time we make changes, we would typically run each of these commands in a separate terminal.

We would first run the build command in one terminal, to create the bundle and wait for updates.

npm run build

We would next launch a second terminal and run the start command to serve the site.

npm run start

Introduction to Webpack Rules

Webpack uses rules to know what to do with different file types. Webpack expects an array of rules in a configuration option called module. The syntax looks like:

module.exports = 
{
  module: 
  {
    rules: []
  }
}

A rule has a test configuration option defined as a regular expression. If a file matches the regular expression, Webpack will use the rule on that file. For example, if we define test as \.txt$\i, the rule will apply to files ending in .txt.

The other part of the rule needs to tell Webpack what to do with files that match the test. That part of the rule varies by file type.

For .txt files, the rule inside of rules would look like:

rules: 
[
  {
    test: /\.txt$/i,
    type: 'asset/source'
  }
]

Here, type: 'asset/source' is telling Webpack that .txt files are an asset that can be added directly to the source code, not requiring much processing. Once we add a rule for a file type, we can import files of that type into our code. Here’s an example with a .txt file:

import Text from './example.txt';
document.querySelector('h1').innerHTML = Text;

Adding CSS to Our Builds

While we import .txt files as assets, other file types often need loaders to get bundled by Webpack. Instead of a type attribute, files that use one or several loaders require a use attribute.

CSS files use two loaders, css-loader and style-loader. css-loader takes the CSS out of a .css file and adds it to the JavaScript code. style-loader takes the output of css-loader and puts it in a style tag in the HTML. We need both loaders and to apply css-loader first. We specify multiple loaders with an array, and Webpack applies them in reverse order.

The rule for CSS files looks like:

{
  test: /\.css$/i,
  use: ['style-loader','css-loader']
}

We can add this rule anywhere within our rules array.

In a local environment, we’d also install the loaders as developer dependencies.

npm install --save-dev style-loader css-loader

We can then import CSS files directly into our JavaScript using another form of the import statement:

import './style.css';
import Text from './example.txt';
document.querySelector('h1').innerHTML = Text;

When we build and start our preview server, the CSS is applied to the HTML!

Adding Images to Our Builds

Since Webpack 5, images and fonts no longer need a loader, and can use Webpack’s asset system. Their rules are similar to that of .txt files.

{
  test: /\.(png|svg|jpg|jpeg|gif)$/i,
  type: 'asset/resource'
}

Notice we use type with asset/resource rather than asset/source. asset/resource creates a file in the build and imports it into the code as a URL. You may want to explore more about asset types.

When an image rule has been defined in webpack.config.js, we can import images into our JavaScript as if they were code:

import Square from '../square.png';
const img = document.createElement('img');
img.src = Square;
const body = document.querySelector('body');
body.appendChild(img);

When we build, the image is in the site!

Adding Fonts to Our Builds

{
  test: /\.(woff|woff2|eot|ttf|otf)$/i,
  type: 'asset/resource'
}

Unlike the other resources we’ve seen so far, font files are imported in CSS, not in JavaScript. We use a font in our CSS like so:

@font-face {
  font-family: 'Roboto-Black';
  src: url('../Roboto-Black.ttf');
}
 
h1 {
  font-family: 'Roboto-Black';
}

The url is the location of the font file. We use font-family to define a name for our font that we can use later in the CSS.

When we build the project, we can see our fonts!