Node.js のビルドツール「esbuild」について！

![esbuild.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659561/514c85f1-f537-78e7-75ec-ab29b6058d23.png)

# はじめに

esbuild は、キャッシュなしで高速なビルドを可能とする Node.js のビルドツールです。

ビルドツールには、esbuild の他に、Webpack、Gulp、Parcel、Rollup、Browserify、FuseBox などがあります。

私自身が webpack を普段使っていて、ビルドに時間がかかりすぎているのが気になり、esbuild について調べてみようと思いました。

esbuild の特徴としては、

- キャッシュなしでの高速なビルド
- ES6 と CommonJS をサポート
- ES6 の Tree shaking 対応（利用されていないコードの除去）
- JavaScript と Go による API
- TypeScript と JSX をサポート
- ソースマップの生成
- ソースコードの最小化
- プラグイン（現在、experimental で v1.0.0 より前に対応予定）

が挙げられます。

この中でも注目すべきは、そのビルド速度にあると思います。

# なぜ早いのか？

1. esbuild は Go で書かれており、ネイティブコードへコンパイルしている

   ほとんどのビルドツールは、Javascript によって書かれており、JIT コンパイルを使用するため、速度が遅い。

   Go は、並列処理が得意で共有メモリをスレッド間で使用する。また、ヒープメモリも共有している。

   よって、CPU を効率的に使用して、並列処理を行える。

1. 並列で処理を行う

   昨今のPCは、複数のメモリを持っているため、効率的に並列処理を行える。

3. esbuild は、0 から速度を意識して作られた

   3rd パーティライブラリを使用するのに比べ、パフォーマンスに対して多くのメリットが得られる。

   例えば、多くのバンドラでは、公式の TypeScript コンパイラを使ってパースしているが、そのコンパイラは、パフォーマンスを最優先として作られていない。

   その点、0 からパフォーマンスを最優先として作ることで、高速にコンパイルが可能となる。

4. メモリを効率的に使用する

   esbuild は、JavaScript の抽象構文ツリーへは 3 回しかアクセスしない。

   また、Go は、コンパクトにしてメモリに保存するため、より効率的にメモリを使用できる。

# esbuild の制限

- TypeScript の型チェックは行われない
    - 別途 tsc を noEmit で実行し、チェックする必要があります
- ソースコードの変更監視が行われない
    - sane などの watch ツールを利用すると良い
- CSS モジュール非対応
    - 将来的に対応予定（https://github.com/evanw/esbuild/issues/20）
    - CSS のバンドルに PostCSS + postcss-import を利用すると良い
- コード分割
    - 将来的に対応予定（https://github.com/evanw/esbuild/issues/16）
- ES5へのトランスコンパイルができない
    - 将来的に対応予定（https://github.com/evanw/esbuild/issues/297）

# 簡単な使い方

```
$ echo "{}" > package.json
$ yarn add esbuild react react-dom
```

```javascript:app.jsx
import * as React from 'react'
import * as Server from 'react-dom/server'

let Greet = () => <h1>Hello, world!</h1>
console.log(Server.renderToString(<Greet />))
```

```
$ ./node_modules/.bin/esbuild app.jsx --bundle --outfile=out.js
$ node out.js
```

# 速度比較

今回は、create-react-app で作ったアプリケーションのビルド速度を比較します。

## 通常の React アプリケーション

```
$ npx create-react-app react-ts-app --template typescript
$ cd react-ts-app
$ vim tsconfig.json
target を es2016 に修正
$ vim src/App.tsx
svg 関連のコードをコメントアウト
$ sudo rm -fr build && /usr/bin/time yarn build
```

## esbuild 版 React アプリケーション

```
$ npx create-react-app react-ts-app --template typescript
$ cd react-ts-app
$ vim tsconfig.json
target を es2016 に修正
$ vim src/App.tsx
svg 関連のコードをコメントアウト
```

```typescript:build.ts
const { argv } = require('process')
const { build } = require('esbuild')
const path = require('path')

const options = {
  define: { 'process.env.NODE_ENV': process.env.NODE_ENV },
  entryPoints: [path.resolve(__dirname, 'src/index.tsx')],
  minify: argv[2] === 'production',
  bundle: true,
  target: 'es2016',
  platform: 'browser',
  outdir: path.resolve(__dirname, 'dist'),
  tsconfig: path.resolve(__dirname, 'tsconfig.json')
}

build(options).catch(err => {
  process.stderr.write(err.stderr)
  process.exit(1)
})
```

```
$ sudo rm -fr build && /usr/bin/time node build.ts
```

## 結果

```
通常の React アプリケーション
3.91 real         5.40 user         0.66 sys
3.43 real         5.53 user         0.60 sys
2.96 real         5.25 user         0.57 sys

esbuild 版 React アプリケーション
0.10 real         0.12 user         0.02 sys
0.08 real         0.12 user         0.01 sys
0.08 real         0.12 user         0.01 sys
```

# おわりに

結果を見てもらうと分かるようにかなり高速にビルド出来ました。

create-react-app で作られるアプリケーションには、svg をインポートしている部分がありますが、ここは別途プラグインでやる必要がありそうですが、

今回の目的とは違っていたので、コメントアウトで対応しました。

単純に置き換えるだけではうまくいかないところがあったり、まだ、メジャーバージョンも 1 になっていなかったりするので、様子見しつつ、タイミングを見て導入できればと思います。

開発当初は気にならないほどの速度でビルド出来ていても、開発が進むにつれてだんだんビルドが遅くなり、ストレスに感じることもあるかと思います。

そんな時にぜひ esbuild も検討してみてはいかがでしょうか？

# お知らせ

Webサイト・ツール・LP作成のご依頼は、

https://iteek.jp/contact/

こちらからお問い合わせいただけます。お気軽にご相談ください。


# 参考

https://github.com/evanw/esbuild

https://www.kabuku.co.jp/developers/ultrafast-tsx-build-tool-esbuild