How to speed up webpack with esbuild-loader
Tags: webpack, webdev, esbuild
Published: 2021-07-17 08:08:58

If you are a webpack user and have heard about esbuild speed, you may start questioning your js-bundler choices. Luckily, you don't have to drop your hand-crafted webpack config just now. Thanks to [esbuild-loader](https://github.com/privatenumber/esbuild-loader), you can get part of the speed improvement without doing a whole migration.

# Benchmark repo
For checking the esbuild-loader with a non-trivial amount of code, I created a  [repository](https://github.com/marcin-wosinek/esbuild-loader-pdf-lib/). My code is minimal, `src/index.js`:
```js
import { PDFDocument } from "pdf-lib";

const pdfButton = document.getElementById("pdf-button");

pdfButton.addEventListener("click", async () => {
  const pdfDoc = await PDFDocument.create();
  const page = pdfDoc.addPage([350, 400]);
  page.moveTo(110, 200);
  page.drawText("Hello World!");
  const pdfDataUri = await pdfDoc.saveAsBase64({ dataUri: true });
  document.getElementById("pdf").src = pdfDataUri;
});
```

`index.html`:
```html
<!DOCTYPE html>

<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>esbuild-loader pdf-lib</title>
    <link rel="shortcut icon" href="#" />
  </head>
  <body>
    <button id="pdf-button">Generate pdf</button>

    <br />

    <iframe id="pdf" style="width: 350px; height: 600px"></iframe>

    <script type="text/javascript" src="dist/main.js"></script>
  </body>
</html>
```

and it does mainly 2 things:
* loads `pdf-lib` - pure js pdf library, so our build has something to spend time on
* provides some operation we can execute to see if the built code works as expected

# Standard webpack build
This code is meant to be built with the default webpack configuration. If you checkout the [webpack branch](https://github.com/marcin-wosinek/esbuild-loader-pdf-lib/tree/webpack), the build will go more or less like this:
```sh
$ npm run build

> esbuild-loader-pdf-lib@1.0.0 build
> webpack --mode=production

asset main.js 413 KiB [compared for emit] [minimized] [big] (name: main) 1 related asset
orphan modules 936 KiB [orphan] 156 modules
runtime modules 663 bytes 3 modules
cacheable modules 1.12 MiB
  modules by path ./node_modules/pako/lib/zlib/*.js 183 KiB 11 modules
  modules by path ./node_modules/pako/lib/utils/*.js 7.56 KiB
    ./node_modules/pako/lib/utils/common.js 2.39 KiB [built] [code generated]
    ./node_modules/pako/lib/utils/strings.js 5.17 KiB [built] [code generated]
  modules by path ./node_modules/pako/lib/*.js 22.9 KiB
    ./node_modules/pako/lib/deflate.js 10.8 KiB [built] [code generated]
    ./node_modules/pako/lib/inflate.js 12.1 KiB [built] [code generated]
  ./src/index.js + 155 modules 937 KiB [built] [code generated]
  ./node_modules/pako/index.js 347 bytes [built] [code generated]

WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets: 
  main.js (413 KiB)

WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
Entrypoints:
  main (413 KiB)
      main.js


WARNING in webpack performance recommendations: 
You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
For more info visit https://webpack.js.org/guides/code-splitting/

webpack 5.45.1 compiled with 3 warnings in 5983 ms
```

Key numbers to remember:
* complication time - almost 6s
* output size - 413 KiB

# esbuild-loader enhanced build
`esbuild-loader` provides 2 ways of using esbuild in webpack:

1. as a loader that can replace `babel-loader` or `ts-loader`
2. `ESBuildMinifyPlugin` that we can use to replace the default terser plugin

I haven't seen any improvements in my example repo by using the loader in my example repo - it even got a bit slower. Luckily, the minification improvements are interesting enough to recommend you to give it a try. You need `webpack.config.js` with:
```js
const { ESBuildMinifyPlugin } = require("esbuild-loader");

module.exports = {
  mode: "production",
  optimization: {
    minimizer: [
      new ESBuildMinifyPlugin({
        target: "es2015", // Syntax to compile to (see options below for possible values)
      }),
    ],
  },
};
```

install `esbuild-loader` with:
```sh
$ npm install --save-dev esbuild-loader
```

and your build will look like this:
```sh
$ npm run build          

> esbuild-loader-pdf-lib@1.0.0 build
> webpack --mode=production

asset main.js 426 KiB [compared for emit] [minimized] [big] (name: main)
orphan modules 936 KiB [orphan] 156 modules
runtime modules 663 bytes 3 modules
cacheable modules 1.12 MiB
  modules by path ./node_modules/pako/lib/zlib/*.js 183 KiB 11 modules
  modules by path ./node_modules/pako/lib/utils/*.js 7.56 KiB
    ./node_modules/pako/lib/utils/common.js 2.39 KiB [built] [code generated]
    ./node_modules/pako/lib/utils/strings.js 5.17 KiB [built] [code generated]
  modules by path ./node_modules/pako/lib/*.js 22.9 KiB
    ./node_modules/pako/lib/deflate.js 10.8 KiB [built] [code generated]
    ./node_modules/pako/lib/inflate.js 12.1 KiB [built] [code generated]
  ./src/index.js + 155 modules 937 KiB [built] [code generated]
  ./node_modules/pako/index.js 347 bytes [built] [code generated]

WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets: 
  main.js (426 KiB)

WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
Entrypoints:
  main (426 KiB)
      main.js


WARNING in webpack performance recommendations: 
You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
For more info visit https://webpack.js.org/guides/code-splitting/

webpack 5.45.1 compiled with 3 warnings in 1658 ms
```

Key values:
* complication time - below 2s
* output size - 426 KiB

So we got our build time about 3 times lower, with a 3% increase in build size.

# Links
* [the repository](https://github.com/marcin-wosinek/esbuild-loader-pdf-lib/)
* [webpack video course](https://bit.ly/WebpackCourse)

# Summary
If you are interested in speeding up your build time but have no time for replacing the whole bundler & the setup, `ESBuildMinifyPlugin` from `esbuild-loader` can be a good way to achieve that.