1
头图

Preface

Back to the classic sentence: " it is, and then makes it ." I believe that many students know the esbuild, which is fast build speed well-known to the public. And, esbuild author Evan Wallace also the official website of the FAQ devoted esbuild why so fast? (Interested students can learn about https://esbuild.github.io/faq/)

So, back to today's article, I will start with the directory structure of esbuild source code, and walk into the bottom world of esbuild with you around the following 2 points:

  • First acquainted with the entrance of Esbuild build
  • What did the entrance of Esbuild build

1 Get to know the entrance of Esbuild

In Go, package (packages). Each Go application needs to include an entry package main , which is the main.go file. So, obviously esbuild itself is also a Go application, that is, its entry file is also the main.go file.

For esbuild, its directory structure:

|—— cmd
|—— docs
|—— images
|—— internal
|—— lib
|—— npm
|—— pkg
|—— require
|—— scripts
.gitignore
go.mod
go.sum
Makefile
README.md
version.txt

It seems that at a glance, there is no main.go file we want, so how do we find the entrance of the entire application?

Students who have studied C should know the Make build tool, which can be used to execute a series of commands we have defined to achieve a certain build goal. Moreover, it is not difficult to find that there is a Makefile in the above directory structure, which is used to register Make commands.

The basic syntax for registering rules in the Makefile file will look like this:

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

Here, let's understand the meaning of each parameter separately:

  • target , that is, the target using the Make command, for example, make a target name
  • prerequisites is usually the path corresponding to some files. Once these files are changed, they will be rebuilt when the Make command is executed, otherwise it will not
  • tab fixed grammatical format requirements, the beginning of the commands tab key
  • commands command is the command that will be executed when the 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 part of the rules in the Makefile file, interested students can check other rules by themselves~

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

According to the introduction of Makefile above and the content here, we can know that the core esbuild command is implemented by the three related files of cmd/esbuild/version.go cmd/esbuild/*.go and pkg/*/*.go and internal/*/*.go go.mod

Then, usually execute the make esbuild command, which essentially executes the command:

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

Below, let's take a look at what this command does (meaning):

CGO_ENABLED=0

CGO_ENABLED is one of Go's environment (env) information. We can use the go env command to view all the environment information supported by Go.

And here the CGO_ENABLED to 0 to disabled cgo , because by default, CGO_ENABLED as 1 , which is open cgo , but cgo will import files containing C code, then that is the final compilation The result will contain some external dynamic links instead of pure static links .

cgo allows you to use C grammar in .go files. We won’t introduce it in detail here. Interested students can learn about it on their own.

So, at this time, everyone may be thinking about the difference between external dynamic link and static link Why do you need purely statically linked compilation results?

This is because external dynamic linking will break you finally compiled to the platform . Because, there are certain uncertain factors in external dynamic links. Simply put, maybe the application you build now can be used, but the content of the external dynamic link changes on a certain day, then it is likely to cause your program to run. influences.

go build $(GO_FLAGS) ./cmd/esbuild

go build $(GO_FLAGS) ./cmd/esbuild core is go build command, which is used to compile the source files, code packages, dependencies and other operations, such as we have here is ./cmd/esbuild/main.go the compilation operation file.

At this point, we already know that the entry point for cmd/esbuild/main.go is the 060cc41d30faf3 file. So, let's take a look at what has been done to build the entrance?

2 What does the entrance of Esbuild build?

Although, the code of the entry cmd/esbuild/main.go only about 268 lines in total. However, in order to facilitate everyone's understanding, I will divide it into the following 3 points to explain step by step:

  • Import of basic dependent package
  • --help of the text prompt function of 060cc41d30fb53
  • main are the specific functions of 060cc41d30fb73?

2.1 Import of basic dependent package

First of all, the basic dependency of package imported. A total of 8 package :

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 package

  • fmt used to format the output I/O function
  • os provides system-related interfaces
  • runtime/debug provides the function of debugging at runtime
  • strings a simple function for manipulating UTF-8 encoded strings
  • time used to measure and show time
  • github.com/evanw/esbuild/internal/api_helpers used to detect whether the timer is in use
  • github.com/evanw/esbuild/internal/logger used to format the log output
  • github.com/evanw/esbuild/pkg/cli provides command line interface of esbuild

2.2 --help of text prompt function of 060cc41d30fcb2

Any tool will have an --help , which is used to inform the user of the specific commands that can be used. Therefore, the definition of the --help text prompt function of esbuild also has the same function, the corresponding code (pseudo code):

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
  ...
}

Here we will use the above mentioned logger this package of Colors structure, it is mainly used for beautification in a terminal output, e.g. 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
}

The variable created using the Colors structure will 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 main are the main functions of 060cc41d30fd56?

Earlier, we also mentioned that every Go application must have a main package , that is, the main.go file as the entrance of the application. main function must also be declared in the main.go file as the entry function of package

main function, which is the entry file of esbuild, mainly does these two things:

1. Get the input option (option) and process it

We use the above-mentioned os this package get input terminal option, that os.Args[1:] . Among them, [1:] means to obtain an array composed of all the elements from index 1 to the end of the array.

Then, the osArgs array will be looped, and each time switch will determine the specific case , and the different options will be processed accordingly. For example, the --version option will output the current version number of esbuild

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

The code corresponding to this whole process will be 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++
  }
}

And, it’s worth mentioning that the osArgs array will be reconstructed here, because multiple options can be entered at once.
But osArgs will start when the subsequent construction passed as a parameter , so here will be treated to remove the option in the array.

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

For users, we are really concerned about using esbuild to package an application, for example, using the esbuild xxx.js --bundle command. And this process is completed by the self-executing function at the end of the main

The core of this function is to call cli.Run() to start the build process and pass in the options that have been processed above.

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

Moreover, before the official start of the build, the logic related to the previous options will be processed according to the specific logic related to CPU trace, stack trace, etc., which will not be introduced here, and interested students will understand by themselves.

Concluding remarks

Okay, here we have roughly gone through the relevant source code of the entry file built by esbuild. From the perspective of students who have never been in touch with Go, it may be a little bit obscure, and there are some branching logics, which are not analyzed in the article, which will continue to be developed in subsequent articles. However, on the whole, opened a new window and saw a different scenery , which is what we as engineers hope to experience 😎. Finally, if there are improper or wrong expressions in the text, you are welcome to mention Issue~

Like 👍

After reading this article, if you have any gains, you can , this will become my motivation to continue to share, thank you~

I'm Wuliu, I like to innovate and tinker with the source code, focusing on the source code (Vue 3, Vite), front-end engineering, cross-end technology learning and sharing. In addition, all my articles will be included in https://github.com/WJCHumble/Blog , welcome to Watch Or Star!

五柳
1.1k 声望1.4k 粉丝

你好,我是五柳,希望能带给大家一些别样的知识和生活感悟,春华秋实,年年长茂。