DEV Community

Cover image for πŸ”₯πŸ”₯πŸ”₯  Introducing ESBuild, compiling is straight up fast!!!
liu-jin-yi
liu-jin-yi

Posted on

πŸ”₯πŸ”₯πŸ”₯ Introducing ESBuild, compiling is straight up fast!!!

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
Enter fullscreen mode Exit fullscreen mode

Preview Folder

2021-10-16 15.17.42.gif

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

yarn add command-line-args --dev
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

We mainly use this package to perform deletion operations on packed files or folders.

yarn add get-port@5.1.1 --dev
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Plugin for esbuild that adds support for importing *.svg files as React components.

yarn add esbuild --dev
Enter fullscreen mode Exit fullscreen mode

The last thing is to install esbuild.

πŸ”₯ Modification package.json

    "scripts": {
        ...
+++     "dev": "node devBuild.js --dev"
      },
      ...
+++ "type": "module"
Enter fullscreen mode Exit fullscreen mode

πŸ”₯ 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();
  });
})();

Enter fullscreen mode Exit fullscreen mode

πŸ”₯ 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>
Enter fullscreen mode Exit fullscreen mode

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 };
Enter fullscreen mode Exit fullscreen mode

πŸ”₯ 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;
Enter fullscreen mode Exit fullscreen mode

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

Oldest comments (11)

Collapse
 
moosmas profile image
Sam Brands

What theme are you using for Visual Studio Code? And is the syntax theme included? It looks really good!

Collapse
 
abodactyl profile image
Abby Redwood

Looks like Tomorrow Night Blue to me - don't quote me on that though :)

Collapse
 
liujinyi profile image
liu-jin-yi

vscode-icons
One Dark Pro
Dracula Official

Collapse
 
hilleer profile image
Daniel Hillmann

You mention you install a specific version of a dependency, because you're running an earlier version of node than is required. Why don't you use nvm to use different versions of node? (And easily bump to more recent versions)

Collapse
 
sixman9 profile image
Richard Joseph

'tj/n' is an NVM-like Node version manager, I find it easier to use, personally.

Collapse
 
liujinyi profile image
liu-jin-yi

I don't use nvm because our projects are relatively new and I don't want to download packages on my computer that I don't use very often. Thanks for your message!

Collapse
 
malloc007 profile image
Ryota Murakami

Interesting!
But why not just use Vite?

Collapse
 
liujinyi profile image
liu-jin-yi

I know what you mean, it's easy to use vite! I think so too. But I still want to implement it all by myself.

Collapse
 
raibtoffoletto profile image
RaΓ­ B. Toffoletto

I'm sorry but I'm completely lost on what is your point with this... You say "Introducing ESBuild"... but you never introduce it, by this article I can only guess it is like webpak... but you don't explain what the project is, what is for and what problem does it solve. Also What company are you talking about? Yours? Please revise the text with someone πŸ˜‰

Collapse
 
liujinyi profile image
liu-jin-yi • Edited

First of all this article is my original, I published it half a month ago on the top of the Chinese website, DEV community's is my translation!
I think I have made it clear in the article that this article is used to solve the problem of slow HMR in our old company project.
As for your comment that my article wants to webpack, I don't dare to agree with it, I just introduce how to introduce esbuild in the project to enhance the experience in the development environment!

If you want to learn esbuild you can read the official documentation, it's clearer than me!

Collapse
 
liujinyi profile image
liu-jin-yi

Still, thanks for the suggestion!