π₯π₯π₯ Introducing ESBuild, compiling is straight up fast!!!
liu-jin-yi
Posted on November 5, 2021
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
After creating the test project let's see what problems we have to solve to introduce esbuild?
- We need a local server, that will display the packaged files.
- A library for parsing command line arguments is also needed, to pass variables for the development environment.
- It is also necessary to delete the last packed file every time you start the project.
- There is also a need to address the port number.
- Solve svg's icon.
- 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
Posted on November 5, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.