Optimise your Frontend CI/CD pipeline with Go and ESBuild
ESBuild is one of the fastest bundler/compilers for the Frontend libraries. Written in Golang, Built for front-end apps.
If you heard of the Snowpack, You may be familiar with ESBuild. 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, 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:
- Go [v1.6 and above]
- NPM (nodejs package manager)
Note: 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. 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.
- It's written in Go and compiles to native code.
- Parallelism is used heavily.
- Memory is used efficiently.
- Loaders and plugins added by default
[Compilers time comparison]
Where to start
To build a pipeline, You need an application. You can start a project from scratch as it is documented here. However, For demo purpose, I will use the Quote of the day app that I have created in Github.
Project Structure
- 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 repo.
[Screenshot of code]
Write your first Go Script
Open runner.go
and add below code
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
## 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.
/*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.
Optimise build settings and Fix warning messages
To optimize the es build settings, Let's add below BuildOptions
in runner.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.
npm run serve
[Quote Of the Day App Demo]
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
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
"scripts": {
...
"webpack": "webpack && cp public/* dist/",
...
},
Create a webpack configuration file:
// 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
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 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. And webpack version of the code can be found in esbuild_vs_webpack branch 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.