Intro to Module Bundlers and Webpack - getfutureproof/fp_guides_wiki GitHub Wiki

Module Bundlers

One of the key 'problems' we commonly solve with a module bundler is the fact that, unlike Node which has a built in module resolver, most browsers do not have the ability to handle require or import.

As our projects grow, we usually want to be splitting our code across many different files to aid organisation and human readability. Let's say we have 5 JavaScript files - usually we would just require or import them into each other as needed. However, if we want to be able to use them in the browser, we would need to give each one a script tag in the html file. This can get messy quickly so we can use a module bundler to create one large file with all the needed code in and just bring that file into the html.

But wait, there's more! Many bundler tools have been extended to include many other features such as handling more file types, automating additional tasks, generating helper files etc.

An example of this would be moving development code into a build folder which only contains the code you want to deploy and leaving out folders/files such as tests which could be a security risk.

Webpack

Webpack is a popular open-source JavaScript module bundler, able to copy static files, transpile ES6 to ES5 and more.

Setup

To use Webpack we need to go through some setup.

Begin by creating a project repository and running the standard git init and npm init.

Install Webpack

npm install webpack webpack-dev-server html-webpack-plugin --save-dev

We use the --save-dev flag as we only need these for development and not production. (NB: You can shorten npm install --save-dev to npm i -D)

Install Loaders

We use these to add support for various file types.

npm i -D babel-loader style-loader css-loader

Install Babel

We use this to transpile ES6 to ES5. Install the following dependencies:

  • @babel/core: the core babel library
  • @babel/preset-env: allows us to target specific environments
  • @babel/plugin-transform-runtime: if you get regenerator runtime errors you may have forgotten this one!
  • @babel/preset-react: only add if creating an app that uses React

To configure babel, we need to create a file called .babelrc and define any presets and plugins that you've installed. If you use all of the ones above, your .babelrc will look like this:

{
  "presets": ["@babel/preset-env", "@babel/preset-react"],
  "plugins": ["@babel/plugin-transform-runtime"]
}

Folder structure

Folder and file organisation is going to be extra important now. You can configure your setup to your taste but here is a very standard flow:

  • Create an src folder to work in, creating our source code which may or may not be bundled later
  • Create a public folder to store the publicly available assets
  • Create a config folder to store the configuration files stating how your app should be built

Webpack configuration

These will live in the config folder

  • Create a webpack.config.js Use the following as a starting point. Read through it and if there's anything you don't quite understand, use your search engine of choice to get more info!
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const ROOT_DIRECTORY = path.join(__dirname, '../'); // the root of your project
const PUBLIC_DIRECTORY = path.join(ROOT_DIRECTORY, 'public'); // the root of the frontend, i.e. html file

const config = {
  entry: [path.resolve(ROOT_DIRECTORY, 'src/index.js')], // the main JavaScript file of the project
  output: {
    // instructions for compiling the code
    path: path.resolve(ROOT_DIRECTORY, 'build'), // the file where the compiled code should go
    filename: 'bundle.js', // the file name of the compiled code
    publicPath: '/', // specifies the base path for all the assets within your application.
  },
  mode: 'development', // tells webpack to use its built-in optimizations according to the mode
  resolve: {
    // instructions on how to resolve modules
    modules: [path.resolve('node_modules'), 'node_modules'], // tells webpack where to look for node_modules
  },
  performance: {
    // notifies you if assets and entry points exceed a specific file limit
    hints: false,
  },
  plugins: [
    // plugins we are using to help with compiling
    new HtmlWebpackPlugin({
      // used to add the JavaScript code to the HTML
      template: path.join(PUBLIC_DIRECTORY, 'index.html'),
    }),
  ],
  module: {
    // helpers we want webpack to use
    rules: [
      {
        test: /\.(js|jsx)$/,
        resolve: {
          extensions: [".js", ".jsx"]
        },
        exclude: /nodeModules/,
        use: {
          loader: 'babel-loader'
        }
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      }, // transpile css files
      {
        test: /\.(png|svg|jpg|gif|pdf)$/,
        use: ['file-loader'],
      }, // transpile image files
    ],
  },
};

module.exports = config;

We also create a configuration file with extra instructions specifically for use in development environment.

  • Create a webpack.config.dev.js Use the following as a starting point. Read through it and if there's anything you don't quite understand, use your search engine of choice to get more info!
const config = require('./webpack.config.js');

config.devServer = {
  historyApiFallback: true, //serve a previous page on a 404 error
  port: 8080, // use this port for the server
  liveReload: true, // refresh the browser when changes are saved
  open: true, // open the project in the browser when the server starts
};

config.devtool = 'inline-source-map'; // a tool to find errors in the compiled code, but show them against the source code for easier debugging

module.exports = config;

Thinking ahead we will also need to let webpack know how to build a production ready application by creating a webpack.config.production.js file:

const config = require('./webpack.config.js');

config.mode = 'production';
module.exports = config;

Finally we should add some scripts to our package.json.

  "scripts": {
    "dev": "webpack serve --mode development --config config/webpack.config.dev.js",
    "build": "webpack --config config/webpack.config.production.js"
  },

And we're ready to go! Run the dev script with npm run dev and get coding!