🔥🔥🔥  Introducing ESBuild, compiling is straight up fast!!!
Tags: react, webdev, javascript, vue
Published: 2021-11-05 08:17:28


The current hands of the project hot update is getting slower and slower, so there is the emergence of this piece of article, this is a tutorial article, the current set has come up the company's development environment, this example is the previous experiment to test and do. This piece of tutorial code and the real introduction of the project code or a certain difference, if the partners also want to introduce esbuild packaging for the company's project, you can leave a comment.

**Since the company's project is an old one, I mainly addressed the experience of working in a development environment.**

## 🔥 Creating a basic CRA project

Let's start by creating a basic react project.
```
yarn create react-app my-app
```
Preview Folder

![2021-10-16 15.17.42.gif](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/709749d2773b4468bed37e3cef8c3967~tplv-k3u1fbpfcp-watermark.image?)

After creating the test project let's see what problems we have to solve to introduce esbuild?

1. We need a **local server**, that will display the packaged files.
2. A library **for parsing command line arguments is also needed**, to pass variables for the development environment.
3. It is also necessary to delete the last packed file every time you start the project.
4. There is also a need to address the **port number**.
5. Solve **svg's icon**.
6. Introduce esbuild for packaging.

With the above problem solved, we can implement this demo.

## 🔥 Download Dependency Packages


```
yarn add browser-sync --dev
```

The main purpose of this package is to create the server, render the packaged files, and listen for file changes in the specified file for esbuild to repackage.

```
yarn add chalk --dev
```

The main purpose of this package is to beautify the character style of the terminal.

```
yarn add command-line-args --dev
```

This is a library mainly used for parsing command line arguments and we mainly use it to confirm if it is a development environment.


```
yarn add del --dev
```
We mainly use this package to perform deletion operations on packed files or folders.

```
yarn add get-port@5.1.1 --dev
```

We use this library mainly to get the current TCP port number available. I didn't install the latest version because the latest version has requirements for Node.js, my node version is v12.18.3, and it's expected node version is: "^12.20.0 || ^14.13.1 || >=16.0.0".

**We copy the public folder and rename it to public-dev, the index.html in this folder is the entry point of our application.**

```
yarn add --dev esbuild-plugin-svgr
```
Plugin for esbuild that adds support for importing `*.svg` files as React components.

```
yarn add esbuild --dev
```
The last thing is to install esbuild.

## 🔥 Modification package.json


```
    "scripts": {
        ...
+++     "dev": "node devBuild.js --dev"
      },
      ...
+++ "type": "module"
```

## 🔥 Create devBuild.js

After changing the package.json file, next create devBuild.js in the root folder.

```
import browserSync from "browser-sync";
import chalk from "chalk";
import commandLineArgs from "command-line-args";
import del from "del";
import esbuild from "esbuild";
import getPort from "get-port";
import svgrPlugin from "esbuild-plugin-svgr";
// Create the server.
const bs = browserSync.create();
// Deconstructing environment variables
const { dev } = commandLineArgs({ name: "dev", type: Boolean });
// Delete the package folder from the public-dev folder
del.sync("./public-dev/dist");

// Start esbuild to build the package
(async () => {
  const buildResult = await esbuild
    .build({
      format: "esm", // Sets the output format of the generated JavaScript file.
      target: "es2017", // Compile to convert version
      entryPoints: ["./src/index.jsx"], // Packed Entrance
      outdir: "./public-dev/dist", // Output Directory
      chunkNames: "chunks/[name].[hash]", // Packed out file name
      incremental: dev, // Because we are listening for file changes to repack, and we want the development environment to use esbuild, dev is true.
      loader: {
        // This option changes the way the given input file is interpreted.
        ".svg": "text",
        ".png": "dataurl",
      },
      bundle: true, // Bundling files means inlining any imported dependencies into the file itself.
      splitting: true, // Code splitting is currently only available for esm output format.
      plugins: [svgrPlugin()],
      inject: ["./public-dev/react-shim.js"], // Import React into esbuild as a global variable
    })
    .catch((err) => {
      console.error(chalk.red(err));
      process.exit(1);
    });
  console.log(chalk.green("The build has finished! 📦\n"));
  // Get the port number that can be used
  const port = await getPort({
    port: getPort.makeRange(4000, 4999),
  });

  console.log(
    chalk.cyan(
      `Launching the Shoelace dev server at http://localhost:${port}! 🥾\n`
    )
  );
  // Server initialization
  bs.init({
    startPath: "/", // Initial path
    port, // Port number
    logLevel: "silent", // Log level
    logFileChanges: true, // Log file changes
    notify: true, // Small pop-up notifications in the browser
    single: true, // Provide separate index.html
    server: {
      baseDir: "public-dev", // Base Folder
      index: "index.html", // Set the server's entry file
    },
    files: "src/", // Listening to files under src
  });

  // Listening for changes under the src folder
  bs.watch(["src/"]).on("change", async (filename) => {
    console.log(`Source file changed - ${filename}`);
    // Repackaging
    buildResult.rebuild();
  });
})();

```
## 🔥 index.html

Because I didn't want to change things directly under the public file, I directly copied the public folder and renamed it to public-dev. Why did I do that? Mainly because I didn't want to intersect with the webpack packaged files. So I simply copied a folder directly.

**In the index.html file, we have to introduce the packaged css and js. Here we have to be careful when introducing the js, we must use the ESM way to introduce it. Otherwise it will report an error!!!**

```
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <meta name="theme-color" content="#000000" />
  <meta name="description" content="Web site created using create-react-app" />
  <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
  <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
++  <link rel="stylesheet" type="text/css" href="./dist/index.css" />
  <title>React App</title>
</head>

<body>
  <noscript>You need to enable JavaScript to run this app.</noscript>
  <div id="root"></div>
++  <script type="module">
++    import './dist/index.js'
++  </script>
</body>

</html>
```

**Change the component suffix name to `.jsx`**

## 🔥 react-shim.js

The main purpose of creating this file is to import React into esbuild as a global variable, so that you don't need to introduce react in each component.

```
import * as React from "react";
export { React };
```

## 🔥 Modify App.jsx

The main thing here is that the usage of svg needs to be changed. This is because the usage of the plugin `esbuild-plugin-svgr` has to be conformed to.

It is also crucial to **change the suffix name of all components with the previous js to jsx.**

```
++ import Logo from "./logo.svg";
import "./App.css";

function App() {
  return (
    <div className="App">
      <header className="App-header">
++        <Logo className="App-logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;
```
At this point, introducing esbuild in CRA is Ok! If you are interested, go ahead and try it!

## 🔥  Preview demo

![2021-10-16 18.29.06.gif](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1a369b6a62b140e5b2fe280fda8eb693~tplv-k3u1fbpfcp-watermark.image?)