# Optimise your Frontend CI/CD pipeline with Go and ESBuild

If you heard of the [Snowpack](https://www.snowpack.dev/), You may be familiar with [ESBuild](https://esbuild.github.io/). ESBuild is one of the fastest bundler/compilers for the Frontend libraries. ESBuild is not a bundler for Reactjs, It can bundle any JavaScript modules. ESBuild is written in  [Golang](https://golang.org/), which make is fast. ESBuild is not a complete solution for your all need. However, Implementing it in your pipeline significantly can improve your team productivity and deployment build time.

**Prerequisite: **
1. Go [v1.6 and above]
2. NPM (nodejs package manager)

**Note:**  [ESBuild](https://esbuild.github.io/getting-started/#install-esbuild)  is also available in nodejs. However, I find using Go and create binary CLI is much faster and reusable.

## Why should  I use ESBuild when I have the webpack?

*There is no reason that you should stop using [webpack](https://webpack.js.org/). But there are a lot of reasons you can at least start thinking of using ESBuild. Some of the best ones are given below. For more info, you can visit  [the official page.](https://esbuild.github.io/faq/#why-is-esbuild-fast) *

1. It's written in Go and compiles to **native code**.
2. **Parallelism** is used heavily.
3. **Memory** is used efficiently.
4. Loaders and plugins added by default

**[Compilers time comparison]**

![Compilers time comparison](https://cdn.hashnode.com/res/hashnode/image/upload/v1615723347496/3Z3868-a5.png)

## Where to start 

To build a pipeline, You need an application. You can start a project from scratch as it is documented  [here](https://esbuild.github.io/getting-started/#your-first-bundle). However, For demo purpose, I will use the  [Quote of the day](https://github.com/deepakshrma/es-build-react)  app that I have created in Github.

## Project Structure

![Project Structure](https://cdn.hashnode.com/res/hashnode/image/upload/v1615719935720/rluJlAeCN.png)

- **public: **A place to keep all public file like image
- **src: **It contains the source code written in TypeScript and ReactJS
- **package.json: **This will contain all the common scripts and dependencies for the nodejs app
- **runner.go: **The build script is written in Golang to use ESBuild to compile and bundle typescript
- **tsconfig.json:** Basic configuration for a TypeScript and react project.

The rest of the files and folder you can ignore for now.

**Note: **The purpose of the article to show how to user ESBuild. So I am not going into the details of react app. You can find source code here in [Quote of the day](https://github.com/deepakshrma/es-build-react) repo.


![React App Code](https://cdn.hashnode.com/res/hashnode/image/upload/v1615720230119/NMdCK3hUU.png)

[Screenshot of code]

## Write your first Go Script

**Open `runner.go` and add below code**

```go
package main

import (
	"os"

	"github.com/evanw/esbuild/pkg/api"
)

func main() {
	result := api.Build(api.BuildOptions{
		EntryPoints: []string{"src/app.tsx"},
		Outdir:      "dist",
		Bundle:      true,
		Write:       true,
		LogLevel:    api.LogLevelInfo,
	})
	if len(result.Errors) > 0 {
		os.Exit(1)
	}
}
```

**Create go module and get all go dependencies**

```bash
## Create/init a go module
go mod init quote-of-day 

## Create vendor module/directory for all dependencies(similar to **node_modules**)
go mod vendor 

## Get all go dependencies
go mod tidy
```
Once you are done with go modules, Let's update `package.json` to build go binary runner.

```json
/*package.json*/

"scripts": {
    "build:bin": "go build -o runner.exe runner.go",
    "build": "./runner.exe && cp public/* dist/",
    "serve": "http-server dist",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
```

** Explanation:** `npm run build:bin` will create an executable binary(runner.exe). This will we used to compile react source code. To compile react code next time you can use `npm run build`.

**Note:** We can use the `go run` command directly. However, Binary will be much faster and can be distributed among the teams.

**Run Build:** Once you run `npm run build`, You will see the output as below. You will see some warnings, We will fix these later. 

*Things to be noted, The **size of** the `app.js`. It will be around `1mb` which is too high for such a small app.*

![Build output](https://cdn.hashnode.com/res/hashnode/image/upload/v1615721976646/FGfuKGK5E.png)

## Optimise build settings and Fix warning messages

To optimize the es build settings, Let's add below `BuildOptions` in `runner.go`

```go
//... rest of the code

func main() {
	result := api.Build(api.BuildOptions{
		EntryPoints:       []string{"src/app.tsx"},
		Outdir:            "dist",
		Bundle:            true,
		Write:             true,
		LogLevel:          api.LogLevelInfo,
		ChunkNames:        "chunks/[name]-[hash]",
		MinifyWhitespace:  true,
		MinifyIdentifiers: true,
		MinifySyntax:      true,
		Splitting:         true,
		Format:            api.FormatESModule,
		Color:             api.ColorAlways,
		Define: map[string]string{
			"process.env.NODE_ENV": `"dev"`,
		},
		AssetNames: "assets/[name]-[hash]",
		Loader: map[string]api.Loader{
			".png": api.LoaderFile,
		},
		Engines: []api.Engine{
			{api.EngineChrome, "58"},
			{api.EngineFirefox, "57"},
			{api.EngineSafari, "11"},
			{api.EngineEdge, "16"},
		},
	})
	if len(result.Errors) > 0 {
		os.Exit(1)
	}
}
```
*Now re-build runner.exe and re-run the build with `npm run build:bin && npm run build` commands. This time you won't see any error or warning. Same time, The size of the app.js will be reduced to `283.4kb`.
*

## Quote Of the Day application
To see the output, I have added `http-server` in the `devDependencies`. You can use any other static server.

```bash
npm run serve
```

**[Quote Of the Day App Demo]**

![App Demo](https://cdn.hashnode.com/res/hashnode/image/upload/v1615722739316/QB420XVEZ.png)


## Comparison with Webpack

I tried to compare build time with Webpack with bare minimal configuration. For that we do need to add some dev dependencies and Webpack itself.

**Add dependencies:** Add webpack and some loaders

```bash
npm install --save-dev css-loader style-loader webpack webpack-cli typescript ts-loader
```

**Update package scripts:** Add new command to run build using webpack

```json
"scripts": {
    ...
    "webpack": "webpack && cp public/* dist/",
   ...
  },
```

**Create a webpack configuration file:**

```js
// webpack.config.js

const path = require("path");

module.exports = {
  entry: "./src/app.tsx",
  mode: "production",
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: "ts-loader",
        exclude: /node_modules/,
      },
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
  resolve: {
    extensions: [".tsx", ".ts", ".js"],
  },
  output: {
    filename: "app.js",
    path: path.resolve(__dirname, "dist"),
  },
};

```

**Let's run both commands and compare:**

- Run ESBuild: npm run build
- Run Webpack: npm run webpack


![ESBuild vs Webpack](https://cdn.hashnode.com/res/hashnode/image/upload/v1615732767053/0WetcGh_s.gif)

*The time taken by Webpack v/s ESbuild is `5.387s/0.329s`. As you can clearly see the difference between Webpack vs ESBuild time. ESBuild is 16x faster than Webpack.*


## Conclusion

ESBuild is not one solution for all your needs. In my opinion, ESBuild should not be used for development purpose. It is not a complete tool. You may struggle to enable a feature like live reload. For that, you can use [Snowpack](https://www.snowpack.dev/) which is built on top of ESBuild APIs. However, by Optimizing build configs and proper scripting pipeline, You can optimize your CI/CD pipeline significantly. 

There is a nice quote by ***Coco Chanel***

> “To be irreplaceable, one must always be different ” – Coco Chanel

**Code: ** You can all code in [es-build-react GitHub repo](https://github.com/deepakshrma/es-build-react). And webpack version of the code can be found in [esbuild_vs_webpack branch](https://github.com/deepakshrma/es-build-react/tree/esbuild_vs_webpack) of Github repo.

-------------

If you like and appreciate my hours of effort to **research**, **write code** and **write this article**. Please share your love and reaction ;) 😊. Thanks in advance.




