preface

Esbuild is a very fast Javascript packaging tool written in GO by Evan Wallace, figma’s CTO, A 30 min Webpack project with esbuild can run in minutes. This article will document using esbuild to package a React library.

demand

Here I’m going to develop a react-checkbox as an example

<input type="checkbox" checked={true} / ><input type="checkbox" checked={false}/>
Copy the code

Since checkbox has only two values: checked (checked) or unchecked (unchecked), visually the checkbox has three states: unchecked Checked, unchecked, indeterminate, when implementing the full selection effect, You might want to use the indeterminate attribute, and for indeterminate you can’t set the checkbox to indeterminate in HTML. Since indeTerminate is not available in HTML, you can set it ina Javascript script

const checkbox = document.getElementById("checkbox");
checkbox.indeterminate = true;
Copy the code

The effect is as follows:

So we need to add an indeterminate attribute to the checkBox

Project initialization

First let’s create a folder and initialize NPM.

mkdir react-checkbox && cd react-checkbox && npm init --yes
Copy the code

We use typescript, and of course install React and react-dom

npm i esbuild typescript @types/react @types/react-dom --save-dev
Copy the code

Then we create the file./tsconfig.json in the root directory

{
  "compilerOptions": {
    "outDir": "./lib"."target": "es6"."module": "commonjs"."noImplicitAny": true."strictNullChecks": true."declaration": true."sourceMap": true."esModuleInterop": true."jsx": "react-jsx"."typeRoots": ["node_modules/@types"]},"include": ["./src/**/*.tsx"."./src/**/*.ts"."example/index.tsx"]."exclude": ["node_modules"]}Copy the code

Code implementation

Next we create SRC /checkbox.tsx, and here’s the code

import { ReactElement, CSSProperties, ChangeEvent } from "react";

export interfaceCheckboxProps { checked? :boolean; indeterminate? :boolean; className? :string; style? : CSSProperties; disabled? :boolean; onChange? :(e: ChangeEvent<HTMLInputElement>) = > void;
}

const Checkbox = ({
  checked = false,
  indeterminate = false,
  className = "",
  style = {},
  disabled = false,
  onChange,
}: CheckboxProps): ReactElement= > {
  return (
    <input
      type="checkbox"
      className={className}
      style={style}
      ref={(input)= > {
        if (input) {
          input.checked = checked;
          input.indeterminate = indeterminate as boolean;
          input.disabled = disabled as boolean;
        }
      }}
      onChange={(e) => {
        if (onChange) {
          onChange(e);
        }
      }}
    />
  );
};

export default Checkbox;

Copy the code

It’s as simple as using ref to set dom properties.

The code package

Next we create the./esbuild.js file in the project root directory

Write packaging configuration

const esbuild = require('esbuild');

esbuild
    .build({
        entryPoints: ['src/checkbox.tsx'].outdir: 'lib'.bundle: true.sourcemap: true.minify: true.splitting: true.format: 'esm'.target: ['esnext']
    })
    .catch(() = > process.exit(1));
Copy the code
  • EntryPoints and ourdir specify which files need to be entered and packaged into the output directory

  • Bundle indicates whether a package file is recursively referenced.

  • Sourcemap indicates whether the SourcemAP source mapping file is generated

  • Minify stands for whether to compress code or not

  • Splitting representative

    1. Whether to extract common code for multiple entries
    2. Whether to package import() asynchronous files separately
  • Target defines the type of javascript we want to output

  • Format sets the output format of the generated javascript file. There are three options: CJS, ESM, and IIFE

    • iifeThe format stands for “call function expression now” and can be run in a browser.
    • cjsThe format stands for “CommonJS” and runs in Node.
    • esmThe format stands for “ECMAScript module” and can be used in both browsers and Nodes

We can then use Node./esbuild.js to package it, but a typescript project ends up providing d.ts for external use, and esBuild ends up not providing D.ts, so we need to run TSC separately and modify the above code slightly.

Let’s add the following code to package.json

"scripts": {
    "ts-types": " tsc --emitDeclarationOnly --outDir lib"."build": "rm -rf lib && node ./esbuild.js && npm run ts-types"
 },
Copy the code

Again, use TSC’s EmitationOnly to generate D.ts

We then specify the entry file in package.json

"main": "lib/checkbox.js"."module": "lib/checkbox.js"."types": "lib/checkbox.d.ts"."files": [
    "lib"
 ]
Copy the code

Now that the package is complete, we need to add tests if we need to send packages.

Local preview

Of course we need to preview our project and create an example/index.tsx file

import React, { ReactElement } from "react";
import { render } from "react-dom";

import Checkbox from ".. /src/checkbox";

function App() :ReactElement {
  return (
    <div>
      <Checkbox></Checkbox>
      <Checkbox checked={true}></Checkbox>
      <Checkbox indeterminate={true}></Checkbox>
    </div>
  );
}

render(<App />.document.querySelector("#root"));

Copy the code

This file acts as a package entry for preview JS;

Then create a./example/index.html

<! DOCTYPEhtml>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <title>checkbox</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="./bandle.js"></script>
  </body>
</html>
Copy the code

This HTML references bandle.js. Next, we need to package up a bandle.js

Create a./example/esbuild.js file with the following code:

const esbuild = require("esbuild");
const path = require("path");

esbuild
  .build({
    entryPoints: [path.resolve(__dirname, "./index.tsx")].outfile: path.resolve(__dirname, "./bandle.js"),
    bundle: true.minify: true.target: ["esnext"].watch: {
      onRebuild(error, result) {
        if (error) console.error("watch build failed:", error);
        else console.log("watch build succeeded:", result); }},format: "esm",
  })
  .then((result) = > {
    console.log("watching...");
  });
Copy the code

Esbuild. js is a configuration for packaging preview files, with listening mode enabled so that changes to the JS are automatically packaged.

Then add to the scripts of package.json:

"start": " node ./example/esbuild.js"
Copy the code

The only drawback is that there is no hot update, we need to manually refresh.

summary

React uses esbuid as a packaging tool.

The disadvantage of esbuid

  • Es5 support is not very good and conversion from ES6 to ES5 is not supported.
  • Esbuild does not provide AST manipulation capabilities (e.g. Babel-plugin-import)

The advantages of esbuild

In addition to fast packaging, esbuild is also very friendly to TS and CSS files. It does not need to set various Loaders and is easy to configure. If your project doesn’t need es5 compatibility, you can migrate some of Monorepo’s JS libraries to esbuild.

Finally, preview the implementation method, you can also use the serve function of ESbuild, you can read the official document to explore. Or create a project using esbuild-Loader with WebPack, but vite is also a good choice.

I hope this article was helpful to you, and you can also refer to my previous articles or share your thoughts and insights in the comments section.