# 如何使用 esbuild 打包 JavaScript 應用程式

前幾天我透過 [esbuild](https://esbuild.github.io/) 解決了兩個小專案(side project)的 Bundling 問題，執行起來不但速度快，其上手的難度也比 webpack 還低，這篇文章將會介紹 `esbuild` 的基本使用方式。

![image](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/c9bc525ef3115bf57492-277933414-128f24da-e951-4277-8868-1afd39ef565a.jpg)

### 安裝 esbuild 套件

```bash
npm install --save-exact --save-dev esbuild
```

查看版本

```bash
npx esbuild --version
```

### 替 Browser 應用程式打包

我之前替 [Tampermoneky](https://www.tampermonkey.net/) 寫了一個 ChatGPT 的 [Userscript](https://github.com/doggy8088/TampermonkeyUserscripts/#:~:text=%E8%87%AA%E5%8B%95%E9%80%81%E5%87%BA%E6%8F%90%E5%95%8F-,ChatGPT%3A%20%E8%AA%9E%E9%9F%B3%E8%BC%B8%E5%85%A5%E8%88%87%E8%AA%9E%E9%9F%B3%E5%90%88%E6%88%90%E5%8A%9F%E8%83%BD%20\(%E6%94%AF%E6%8F%B4%E4%B8%AD/%E8%8B%B1/%E6%97%A5/%E9%9F%93%E8%AA%9E%E8%A8%80\),-%E8%AE%93%E4%BD%A0%E5%8F%AF%E4%BB%A5)，可以替 ChatGPT 加上語音能力(語音合成與語音識別)，但是我有動態載入 `rxjs` 這個 npm 套件，可惜近期 ChatGPT 加入了 CSP (Content Security Policy) 的限制，導致我無法從 `cdn.jsdelivr.net` 動態載入 `rxjs`，因此我必須將整份 `rxjs` 打包進去 Userscript 中才行！

還好這個過程並不複雜，我五分鐘就搞定了，步驟如下：

1.  我主要的程式碼原本這樣寫
    
    ```javascript
    const {
        Observable,
        catchError,
        defer,
        filter,
        fromEvent,
        interval,
        map,
        of,
        retry,
        shareReplay,
        Subject,
        switchMap,
        take,
        tap,
        timer
    } = await import('https://cdn.jsdelivr.net/npm/@esm-bundle/rxjs/esm/es2015/rxjs.min.js');
    ```
    
2.  我改用 esbuild 打包的過程如下
    
    初始化 `package.json`
    
    ```bash
    npm init -y
    ```
    
    安裝 rxjs 套件
    
    ```bash
    npm install rxjs
    ```
    
    改寫我原本 JS 程式碼的 `import` 方式：
    
    ```javascript
    import {
        Observable,
        catchError,
        defer,
        filter,
        fromEvent,
        interval,
        map,
        of,
        retry,
        shareReplay,
        Subject,
        switchMap,
        take,
        tap,
        timer
    } from 'rxjs';
    ```
    
    執行 esbuild 打包程式碼
    
    ```bash
    npx esbuild app.js --bundle --outfile=out.js --platform=browser
    ```
    
    就這麼簡單！
    

你還可以透過 `--minify` 參數對輸出 JS 進行最小化，加上 `--sourcemap` 則會自動產生 [Source map](https://web.archive.org/web/20250318125637/https://blog.techbridge.cc/2021/03/28/how-source-map-works/) 檔案，例如：

```bash
npx esbuild app.js --bundle --minify --sourcemap --outfile=out.js
```

> 注意: `--platform` 的預設值就是 `browser`，預設可以忽略不寫。

如果你想要針對特定瀏覽器版本進行打包，esbuild 還能自動幫你打包出符合特定瀏覽器版本的程式碼，例如：

```bash
npx esbuild app.js --bundle --minify --sourcemap --outfile=out.js --target=chrome58,firefox57,safari11,edge16
```

### 替 Node 應用程式打包

如果要替 Node 應用程式打包，只需要將 `--platform` 參數改成 `node` 即可，例如：

```bash
npx esbuild app.js --bundle --platform=node --target=node
```

如果要針對特定 Node 版本進行打包，也是沒問題的，例如：

```bash
npx esbuild app.js --bundle --platform=node --target=node10.4
```

另外，你也可以指定輸出的格式，例如：

```bash
npx esbuild app.js --bundle --platform=node --target=node --format=esm
```

> `--format` 可設定的值有 `iife`, `cjs`, 與 `esm`！

如果你不想將 Node 的外部依賴項目跟 esbuild 打包在一起，esbuild 在打包時不支援許多特定於 Node.js 的功能，例如 \_\_dirname、import.meta.url、fs.readFileSync 和 \*.node 原生二進制模組。您可以通過將 `--packages` 設定為 `external` 來排除所有相依檔案：

```bash
npx esbuild app.jsx --bundle --platform=node --packages=external
```

### 相關連結

-   [esbuild - Getting Started](https://esbuild.github.io/getting-started/)
-   [esbuild - API](https://esbuild.github.io/api/)
-   [esbuild - Content Types](https://esbuild.github.io/content-types/)
-   [esbuild - Plugins](https://esbuild.github.io/plugins/)
-   [esbuild - FAQ](https://esbuild.github.io/faq/)
