An Introduction to Webpack Configs

Mar 2, 2020

Webpack configs allow you to configure and extend Webpack's basic functionality. A Webpack config is a JavaScript object that configures one of Webpack's options. Most projects define their Webpack config in a top-level webpack.config.js file, although you can also pass the config as a parameter to Webpack's Node.js API.

To understand Webpack configs, you need to understand what Webpack does. Webpack is first and foremost a bundler. Webpack's base functionality is to take a JavaScript file, resolve any dependencies (require(), import, etc.), and output a bundled JavaScript file that contains all those dependencies. You can then run the bundled file without having to load those dependencies again.

Do You Even Need a Webpack Config?

For basic use cases like bundling a Vue app or a Lambda function with Webpack, you might not even need a Webpack config. By default, Webpack bundles the src/index.js file and writes the output to the dist/main.js file.

Suppose you have the below file in src/index.js - it's a "Hello, World" app using Vue.

const Vue = require('vue');

const app = new Vue({
  template: '<h1>Hello, World</h1>'
});

app.$mount('#content');

If you run npm install vue webpack webpack-cli, and run ./node_modules/.bin/webpack, you'll see the below output:

$ ./node_modules/.bin/webpack
Hash: f19bd04db784f5de4438
Version: webpack 4.42.0
Time: 1152ms
Built at: 03/02/2020 10:18:13 AM
  Asset      Size  Chunks             Chunk Names
main.js  68.9 KiB       0  [emitted]  main
Entrypoint main = main.js
[0] (webpack)/buildin/global.js 472 bytes {0} [built]
[1] ./src/index.js 116 bytes {0} [built]
    + 4 hidden modules

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/

Webpack generated a bundled main.js file that you can then load in the browser:

<html>
  <head>
    <script src="dist/main.js"></script>
  </head>
  <body>
    <div id="content"></div>
  </body>
</html>

So you can get the core benefits of Webpack with zero configuration. For many apps Webpack's zero config option is enough. But one place where it starts to break down is if you have multiple files that you want to bundle - say you have one GitHub repo with multiple Lambda functions. Here's how you handle multiple files with a Webpack config.

Multiple Files

This section will use 3 Webpack options. 2 are for specifying which files to bundle:

There's one more option, the target option, which tells Webpack whether you're bundling for the browser ('web') or Node ('node'). For Vue apps you will typically use 'web', but for Lambda you should use 'node'.

Below is a Webpack file using those 3 options that bundles 2 files from the src directory and outputs them to the dist directory.

module.exports = {
  // You need to list out every file you want to bundle in `entry`
  entry: {
    express: `${process.cwd()}/src/express.js`,
    mongoose: `${process.cwd()}/src/mongoose.js`
  },
  output: {
    // Write to the '/dist' directory relative to the directory
    // where `webpack` is running
    path: `${process.cwd()}/dist`,
    // Webpack will bundle `src/foo.js` into `dist/foo.min.js`
    // because of `[name]`.
    filename: '[name].min.js'
  },
  target: 'node'
};

Note that Webpack configs are JavaScript files, not JSON or YAML.

Here's the contents of the express.js and mongoose.js files:

// express.js
const pkg = require('express/package');

console.log('Express version', pkg.version);
// mongoose.js
const mongoose = require('mongoose');

console.log('Mongoose version', mongoose.version);

Webpack bundles Express and Mongoose with each function, so you can still run express.min.js and mongoose.min.js even if you rm -rf ./node_modules.

More Sophisticated Configs

If Webpack configs are this simple, why do developers complain about Webpack being hard? Because Webpack is also a common entry point for transpilers - Babel, TypeScript, JSX, etc. If you don't need to transpile (and odds are you don't), Webpack is easy. But once you introduce transpilers, things can get tricky.

Here's the official guide for using Webpack to compile TypeScript. This section will provide an abridged version.

The key part of webpack.config.js for transpilers is the module.rules option. This is where you tell Webpack to use a special loader to compile a file before bundling. For TypeScript, you need the ts-loader npm module, in addition to the typescript npm module.

npm install typescript ts-loader

The module.rules option is an array of rules. The below webpack.config.js tells Webpack to use the ts-loader module to compile any files that end in '.ts'.

module.exports = {
  entry: './src/index.ts',
  module: {
    // Use `ts-loader` on any file that ends in '.ts'
    rules: [
      {
        test: /\.ts$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
    ],
  },
  // Bundle '.ts' files as well as '.js' files.
  resolve: {
    extensions: ['.ts', '.js'],
  },
  output: {
    filename: 'main.js',
    path: `${process.cwd()}/dist`,
  }
};

Below is the index.ts file:

const str: string = 'Hello, World';

console.log(str);

You also need to add a tsconfig.json file, otherwise TypeScript will error out. For the purposes of this tutorial, the below tsconfig.json is enough:

{"files":["src/index.ts"]}

Running ./node_modules/.bin/webpack should give you the below output:

$ ./node_modules/.bin/webpack
Hash: 63b83086be302b9d23c8
Version: webpack 4.42.0
Time: 1301ms
Built at: 03/02/2020 10:51:14 AM
  Asset       Size  Chunks             Chunk Names
main.js  957 bytes       0  [emitted]  main
Entrypoint main = main.js
[0] ./src/index.ts 44 bytes {0} [built]

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/

And then you can finally run node ./dist/main.js!


More Webpack Tutorials