Creating A Build Tool Using Go

Part of Daniel Katungi’s talk during Daytona Developers Club Tour ‘24, Nairobi on Thurdsay July 18th

Understanding a fully-fledged web build tool(Vite)

Vite is a frontend build tool that has revolutionized the development experience by leveraging native ESM capabilities of modern browsers. To fully grasp its power. Core concepts:

  1. Native ESM
    • Leveraging Browser Capabilities: Vite takes advantage of the native ESM (EcmaScript Modules) support in modern browsers. It can serve you code directly as ESM modules without the need for bundling during development.
    • Improved Development Experience: This approach significantly speeds up development because the browser handles module loading efficiently.
    • Why Vite
    • Bundle Up? Vite or Webpack?
  2. ESBuild
    • High-Performance Builder: Vite utilizes ESBuild, a Go-based bundler, for production builds. ESBuild is renowned for its incredible speed and efficiency.
    • Optimized Output: It produces highly optimized bundles, resulting in faster load times for your application.
    • Getting started with Vite
  3. Dependency Pre-Bundling
    • Optimized Import Paths: Vite pre-bundles large dependency libraries into chunks to reduce the number of requests and improve load performance.
    • Faster Development: This optimization helps in faster development by avoiding unnecessary re-bundling during development.
    • Dependency Pre-Bundling
  4. Hot Module Replacement (HMR)
    • Instant Updates: Vite implements HMR to enable rapid development by updating only the changed modules in the browser without a full page reload.
    • Improved Developer Experience: This significantly enhances the development workflow by providing instant feedback on code changes.
  5. Plugin System

How It Works

  • Development Server: Vite starts a development server that serves your code directly as ESM modules. The browser handles module imports efficiently.
  • HMR: Vite watches for changes in your code and uses WebSockets to send updates to the browser.1 Only the affected modules are reloaded, providing a fast feedback loop. Vite In The Browser Vite Core Features
  • Production Build: When building for production, Vite uses ESBuild to bundle your code into optimized chunks. Dependency pre-bundling is applied to further improve performance.

“Vite isn’t just a fast bundler, it’s a development powerhouse. Native ESM support and Hot Module Replacement (HMR) dramatically accelerate your development workflow, while ESBuild and dependency pre-bundling ensure your production builds fly.”

Creating a Build Tool Using Go

  1. Set Up Your Go Environment:
    • Install Go from the official Go website.
    • Create a new Go project directory.
  2. Install Dependencies:
    • Run the following commands to install the minify package and its sub-packages for JavaScript and CSS minification:
      go get -u github.com/tdewolff/minify
      go get -u github.com/tdewolff/minify/js
      go get -u github.com/tdewolff/minify/css
      
  3. Read and Parse Files:
    • Use the os and io/ioutil packages to read files from the filesystem.
  4. Transpile, Bundle, and Minify Code:
    • Use the minify package to minify JavaScript and CSS files.
    • Bundle multiple JavaScript and CSS modules into a single file.
  5. Serve Files:
    • Use the net/http package to serve files during development.
    • Implement a simple server that watches for file changes and reloads the browser.
  6. Command-Line Interface (CLI):
    • Create a CLI using the flag package to provide options for building, serving, and watching files.

Here is the complete code for the build tool:

package main

import (
    "flag"
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
    "path/filepath"
    "strings"

    "github.com/tdewolff/minify"
    "github.com/tdewolff/minify/css"
    "github.com/tdewolff/minify/js"
)

func main() {
    // Define CLI flags
    build := flag.Bool("build", false, "Build the project")
    serve := flag.Bool("serve", false, "Serve the project")
    flag.Parse()

    if *build {
        buildProject()
    }

    if *serve {
        serveProject()
    }
}

func buildProject() {
    // Initialize minifier
    m := minify.New()
    m.AddFunc("text/javascript", js.Minify)
    m.AddFunc("text/css", css.Minify)

    // Read and concatenate JavaScript and CSS files
    files, err := ioutil.ReadDir("./src")
    if err != nil {
        fmt.Println("Error reading source directory:", err)
        return
    }

    var bundledCode strings.Builder
    for _, file := range files {
        content, err := ioutil.ReadFile("./src/" + file.Name())
        if err != nil {
            fmt.Println("Error reading file:", err)
            return
        }

        var minifiedContent string
        if strings.HasSuffix(file.Name(), ".js") {
            minifiedContent, err = m.String("text/javascript", string(content))
        } else if strings.HasSuffix(file.Name(), ".css") {
            minifiedContent, err = m.String("text/css", string(content))
        }

        if err != nil {
            fmt.Println("Error minifying file:", err)
            return
        }

        bundledCode.WriteString(minifiedContent)
    }

    // Ensure the dist directory exists
    err = os.MkdirAll("./dist", os.ModePerm)
    if err != nil {
        fmt.Println("Error creating dist directory:", err)
        return
    }

    // Write the bundled and minified code to a file
    err = ioutil.WriteFile("./dist/bundle.js", []byte(bundledCode.String()), 0644)
    if err != nil {
        fmt.Println("Error writing bundled file:", err)
    } else {
        fmt.Println("Project built successfully.")
    }
}

func serveProject() {
    http.Handle("/", http.FileServer(http.Dir("./dist")))
    fmt.Println("Serving project at http://localhost:8080")
    http.ListenAndServe(":8080", nil)
}

Steps to Use the Build Tool

  1. Build the Project:
    go run main.go -build
    
  2. Serve the Project:
    go run main.go -serve
    



Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • Using Tiled with Kaplay
  • Redis A High-Performance In-Memory Data Store
  • Dockerizing and Deploying Your App to Docker Hub