Cloud Run functions × TypeScript 開発をしよう！【ビルド：esbuild を使ったTypeScriptのビルド】

# はじめに
この記事では以下のことについて解説しています。
- esbuildについて
- esbuildを使ってTypeScriptで書かれた Node.js関数をビルドする方法
- esbuildの注意点

---
前回の記事でTSのビルドツール Parcel を使って TypeScriptで書かれた Node.js関数をビルドする方法を紹介しました。本記事では esbuild というTSのビルドツールを使った手法を紹介します。

# esbuidとは
Parcelと同様にフロントエンドのビルドツールです。公式に挙げられている特徴として

- キャッシュなしでの高速なビルド
- ES6 と CommonJS をサポート
- Tree shaking（利用されていないコードの除去）、minify, ソースマップの生成に対応
- JavaScript, CSSだけでなく TypeScript と JSX をサポート

とTSのビルドでやれて欲しいことは一通りできます。
使っていて思うのは**とにかく実行が速い。** 公式が自ら「*An extremely fast bundler for the web*」と推すだけあります。

![スクリーンショット 2024-12-10 2.50.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374872/17c83dc8-70b2-f822-f1bc-2b103d920501.png)
↑ 公式TOPにある比較表。本当に体感これくらい早い。

そして後述しますが、**`tsconfig.json` の設定をベースにモジュール解決もよしなにやってくれるので、esbuild側で設定することはほとんどありません。ほぼ Zero Config で使えます。** ~~Parcelの意義とは~~

早速使ってまいりましょう。

# esbuild のインストール
```bash
npm install --save-exact --save-dev esbuild
```

# esbuildでTypeScriptで書かれた Node.js関数をビルドする
以下のbuildコマンドを `package.json` に追記します。
```js:package.json / build
  "scripts": {
    "build": "esbuild ./src/index.ts --bundle --platform=node --outfile=./dist/index.js"
  }
```

```bash:ビルド実行
$ npm run build

> advent-calendar-2024@1.0.0 build
> esbuild ./src/index.ts --bundle --platform=node --outfile=./dist/index.js


  dist/index.js  5.9kb

⚡ Done in 17ms
```

秒でビルドできました。`--bundle` オプションを指定している場合、勝手にTree shakingをしてくれます。
追加で以下のオプションも指定できます。

### --minify
その名の通り指定すると `minify` してくれます。

### --sourcemap
sourcemapを生成してくれます。

### --platform=node
Node.jsのビルドの際には指定します。今回では指定必須です。

### --format
出力されるJSのフォーマットをCommonJS形式で出力するか、ESModule形式で出力するか。
package.json で `"type": module` としているので、`--format=esm` としましょう。

# esbuild使用時の注意点
### `tsconfig.json`の `outDir` 設定を読んでくれない
下記のリンクを見ても分かるように、`outDir` は`tsconfig.json` の読んでくれるオプションの対象外のようです。
（なんでやねんっ！）ってなりますが、`--outfile` オプションで出力先を指定してあげましょう。

https://esbuild.github.io/content-types/#tsconfig-json


### 型チェックは行われない
esbuildもParcelと同様にTSの型チェックが行われません。公式も「*うちはビルドを超速でやることが仕事だ。型チェックはtscにでもやらしておけ*」的なことを~~開き直っています~~おっしゃっています。
> However, esbuild does not do any type checking so you will still need to run tsc -noEmit in parallel with esbuild to check types. This is not something esbuild does itself.

https://esbuild.github.io/content-types/#typescript

試してみましょう。
```ts: 型をあえて間違えてみる
import * as ff from '@google-cloud/functions-framework'
import { hoge } from '@/hoge/hoge.js'

export const helloGET: HttpFunction =  (req: ff.Request, res: ff.Response) => {
    let hogehoge: string = hoge();
    hogehoge = 1; // strgin型にnumberを代入
    console.log(hogehoge)
    res.send(`Hello World!`);
});
```

```bash: buildが通ってしまう
$ npm run build

> advent-calendar-2024@1.0.0 build
> esbuild ./src/index.ts --bundle --minify --sourcemap --platform=node --outfile=./dist/index.js


  dist/index.js       2.6kb
  dist/index.js.map  10.6kb

⚡ Done in 13ms
```
型チェックで余計な時間を割くよりもビルドの速さを追い求めた結果なのでしょう。

VSCodeなどのIDEを使っていれば型の誤りがあると赤波線が出たりするのでそれで全然カバーできますし、CI/CDなどで `tsc --noEmit` のステップを挟めば無問題でしょう。

# esbuildを使った、TypeScriptで書かれた Node.js関数のビルドコマンドの完成系
ということで最終的なビルドコマンドは以下になります。この章以降でも引き続き esbuild を使っていきます。

```js:最終的なビルドコマンド
{
  // ...
  "scripts": {
    "build": "esbuild ./src/index.ts --bundle --minify --sourcemap --platform=node --format=esm --outfile=./dist/index.js"
  }
  // ...
}
```

ビルド結果として以下のようなものが出力されていれば、無事実行できたと判断していいでしょう。

```js: ./dist/index.js
var o=()=>"hogehoge";var g=(r,e)=>{let t=o();console.log(t),e.send("Hello World!")};export{g as helloGET};
//# sourceMappingURL=index.js.map
```

# おわりに
2章にわたってTypeScriptで書かれたNode.js関数のビルド方法（というよりビルドツールの紹介）をしてきました。

くれぐれも、流行っているからだとか周りがイケてると言っているから闇雲にビルドツールを選定するのではなく、きちんと目的や規模にあったものを選択してきましょうね。