preface

It goes back to the classic saying: “First know what is, then let it be.” As many of you know, esbuild is known for its breakneck build speed. Evan Wallace, author of Esbuild, also explains why Esbuild is so fast in his FAQ on the website. (interested students can understand https://esbuild.github.io/faq/).

So, back to today’s article, I will start with the directory structure of the esbuild source code, and let you walk into the underlying world of esbuild around the following two points:

  • Get acquainted with the entrance to the Esbuild
  • What does the entry for the Esbuild do

1. Get acquainted with the entrance of Esbuild

In Go, modules are divided by package, and each Go application needs to include an entry package main, which is the main.go file. So, it’s clear that esbuild itself is a Go application, and its entry file is also a main.go file.

For esbuild, its directory structure is:

| | - CMD | - docs | - images - an internal | - lib | - NPM | - PKG | - the require | -- scripts. Gitignore. Mod. Sum Makefile README.md version.txt

At first glance, it doesn’t look like the main. Go file we want, so how do we find the entry point to the entire application?

For those of you who have taken C, Make is a build tool that can be used to execute a defined set of commands to achieve a build goal. What’s more, the directory structure above contains a Makefile, which is used to register the Make command.

The basic syntax for registering a rule in a Makefile would look like this:

<target> : <prerequisites> 
[tab]  <commands>

Here, let’s see what each parameter means:

  • targetThe target of the build, that is, the target that uses the Make command, for exampleMake a target name
  • prerequisitesPreconditions, usually paths to files that are changed, are rebuilt when the Make command is executed, and not vice versa
  • tabFixed syntax format required for commandscommandsThe start must be onetab
  • commandsCommand, which is the command that will be executed when a Make command is executed to build a target

So, let’s take a look at the contents of the Makefile in esbuild:

ESBUILD_VERSION = $(shell cat version.txt) # Strip debug info GO_FLAGS += "-ldflags=-s -w" # Avoid embedding the build path in the executable for more reproducible builds GO_FLAGS += -trimpath esbuild: cmd/esbuild/version.go cmd/esbuild/*.go pkg/*/*.go internal/*/*.go go.mod CGO_ENABLED=0 go build $(GO_FLAGS) ./cmd/esbuild test: make -j6 test-common # These tests are for development test-common: test-go vet-go no-filepath verify-source-map end-to-end-tests js-api-tests plugin-tests register-test node-unref-tests #  These tests are for release (the extra tests are not included in "test" because they are pretty slow) test-all: make -j6 test-common test-deno ts-type-tests test-wasm-node test-wasm-browser lib-typecheck ....

Note: this is only a partial list of the rules in the Makefile. If you are interested, you can check the other rules by yourself

As you can see, many rules are registered in the Makefile file. The esbuild command, which we often use, corresponds to the esbuild target here.

Based on the previous introduction to the Makefile and what’s going on here, Go CMD /esbuild/*. Go and PKG /*/*. Go, internal/*/*. Go.

So, the make esbuild command is usually executed, which essentially executes the command:

CGO_ENABLED=0 go build $(GO_FLAGS) ./cmd/esbuild

Let’s take a look at what this command does:

CGO_ENABLED=0

CGO_ENABLED is one of GO’s environment (env) information, and you can use the GO env command to view all the environment information that GO supports.

CGO is enabled by default, but CGO will import some files that contain C code, so the compiled result will contain some external dynamic links. Rather than just static linking.

cgoGo allows you to use C syntax in the.go file. I won’t go into the details here, but if you are interested, you can find out for yourself

Now, at this point you might be thinking what’s the difference between external dynamic linking and static linking? Why do you need compiled results for pure static links?

This is because external dynamic linking breaks the platform adaptability of the program you eventually compile. Because external dynamic linking has certain uncertainty factors, simply say you may build the application is usable now, but one day the content of external dynamic linking changes, then it is likely to affect your application running.

go build $(GO_FLAGS) ./cmd/esbuild

Go build $(GO_FLAGS)./ CMD /esbuild is the core of the go build command, which is used to compile source files, code packages, dependencies, etc. For example, in this case, the./ CMD /esbuild/main.go file.

At this point, we already know that the entry point to the esbuild is the CMD /esbuild/main.go file. So, let’s take a look at what the build entry does.

2 What does the entry for the Esbuild do?

Although, the CMD/Esbuild /main.go entry for Esbuild is only about 268 lines of code. However, to make it easier for you to understand, I will break it down into the following three steps:

  • base-dependentpackageThe import
  • --helpThe definition of the text prompt function
  • mainWhat does the function do

2.1 Base dependentpackageThe import

First, there is the underlying dependent Package import, with a total of eight Packages imported:

import (
    "fmt"
    "os"
    "runtime/debug"
    "strings"
    "time"

    "github.com/evanw/esbuild/internal/api_helpers"
    "github.com/evanw/esbuild/internal/logger"
    "github.com/evanw/esbuild/pkg/cli"
)

The corresponding functions of these 8 packages are as follows:

  • fmtA function used to format output I/O
  • osProvide system-specific interfaces
  • runtime/debugProvides the ability to debug at run time
  • stringsSimple function for manipulating UTF-8-encoded strings
  • timeUsed for measuring and showing time
  • github.com/evanw/esbuild/internal/api_helpersUsed to check if a timer is in use
  • github.com/evanw/esbuild/internal/loggerUsed to format the log output
  • github.com/evanw/esbuild/pkg/cliProvides a command line interface to esbuild

2.2 --helpThe definition of the text prompt function

Any tool will have a –help option that tells the user what specific commands they can use. So, the esbuild –help text prompt function definition has the same function, the corresponding code (pseudocode) :

var helpText = func(colors logger.Colors) string {
    return `
` + colors.Bold + `Usage:` + colors.Reset + `
  esbuild [options] [entry points]

` + colors.Bold + `Documentation:` + colors.Reset + `
  ` + colors.Underline + `https://esbuild.github.io/` + colors.Reset + `

` + colors.Bold
  ...
}

The Colors structure of the logger package we mentioned above is used here to beautify the output at the terminal, such as Bold (Bold), color (Red, Green) :

type Colors struct {
    Reset     string
    Bold      string
    Dim       string
    Underline string

    Red   string
    Green string
    Blue  string

    Cyan    string
    Magenta string
    Yellow  string
}

A variable created with the Colors structure would look like this:

var TerminalColors = Colors{
    Reset:     "\033[0m",
    Bold:      "\033[1m",
    Dim:       "\033[37m",
    Underline: "\033[4m",

    Red:   "\033[31m",
    Green: "\033[32m",
    Blue:  "\033[34m",

    Cyan:    "\033[36m",
    Magenta: "\033[35m",
    Yellow:  "\033[33m",
}

2.3 mainWhat the function basically does

Earlier, we also mentioned that every Go application must have a Main Package, the main. Go file, as the entry point to the application. The main function must also be declared in the main.go file as the entry function to the Package.

So the main function, which is the entry file to esbuild, does these two things:

1. Get the input option and process it

Get the option for terminal input using the OS Package we mentioned above, os.args [1:]. Where [1:] means to get the array of all elements from index 1 to the last.

Then, it will loop through the osArgs array, each time it will switch to determine the specific case, and handle the different options accordingly. For example, the –version option prints the version number of the current esbuild and exits:

fmt.Printf("%s\n", esbuildVersion)
os.Exit(0)

The code for this whole process would look like this:

osArgs := os.Args[1:] argsEnd := 0 for _, arg := range osArgs { switch { case arg == "-h", arg == "-help", arg == "--help", arg == "/?" : logger.PrintText(os.Stdout, logger.LevelSilent, os.Args, helpText) os.Exit(0) // Special-case the version flag here case arg == "--version": fmt.Printf("%s\n", esbuildVersion) os.Exit(0) ... default: osArgs[argsEnd] = arg argsEnd++ } }

Also, it is worth mentioning that the osArgs array is reconstructed. Since you can enter more than one option at a time, the osArgs will be passed in as a parameter during the subsequent start of the build, so the options processed here will be removed from the array.

2. Call cli.run () to start the build

For users, what we really care about is using esbuild to package an application, such as using the esbuild XXX. Js –bundle command. This is done by the self-executing function at the end of main.

The core of this function is to call cli.run () to start the build process, passing in the options already handled above.

func() {
  ...
  exitCode = cli.Run(osArgs)
}()

In addition, before the formal start of the build, we will continue to deal with the logic related to the previous options, which will specifically involve the CPU trace, stack trace, etc., here will not be introduced, students who are interested to understand it by themselves.

conclusion

OK, so here we will go over the ESBuild entry file related source code. From the perspective of students who have never been exposed to GO, it may be a little obscure, and some branch logic is not analyzed in this paper, which will be further explored in the following articles. But, on the whole, opening a new window and seeing a different landscape is what we as engineers want to experience 😎. Last but not least, if there is something wrong or wrong in the text, please mention it

Thumb up 👍

If you get anything from reading this article, you can like it. It will be my motivation to keep sharing. Thank you

I am Wu Liu, like innovation, tinkering with source code, focus on source code (VUE 3, VET), front-end engineering, cross-end technology learning and sharing. In addition, all my articles will be included in
https://github.com/WJCHumble/Blog, welcome Watch Or Star!