viteでNode向けパッケージをバンドルする方法

Tech
js/ts

状況

tsで書いたコードをnpm package向けにjsにコンパイルしたい
テストは、Vitestを使っている

Viteは基本的にブラウザ向けビルドツール

Viteはデフォルトでブラウザ向けに最適化されている(*1,*2)ため、Node向けパッケージとして利用する場合、いくつかの前提ズレが発生する

  • エントリーポイントは index.html
  • 依存関係は基本すべてバンドル対象
  • Node built-in(fs, pathなど)は警告対象
  • ブラウザ互換性優先でモジュールが外部化されることがある

そのためNode向けパッケージとして使う場合は追加設定が必要になる

ViteでNodeライブラリを作る場合

問題と解決手段

参考: https://github.com/vitejs/vite/issues/8910

vite.config.ts
import { defineConfig } from "vite";
import dts from "unplugin-dts/vite";

export default defineConfig({
  plugins: [dts()],
  build: {
    lib: {
      entry: "src/index.ts",
      formats: ["es"],
      fileName: "index",
    },
    ssr: true,
    sourcemap: true,
  },
});

Vite+を使うという選択肢

上記からわかるように、Viteはあくまでブラウザ向けのバンドラーなので
Nodeライブラリとしてバンドルする場合は、プラグインやSSRモードなどで無理矢理設定を作るしかないので
一般的には、tscやtsdownの利用が推奨されるが、tscはtsgoへの移行が安定化するまではあまり使いたくなくて
tsdownは、tsdown.config.tsを追加する必要があって、設定ファイルが増えるのはあまり気乗りしない

それを解決するのがVite+である

Vite+は内部でtsdownを利用しているが、その設定はvite.config.tsに書ける
すでにVitestのためにvite.config.tsを用意してあるため、設定ファイルが増えないのは大きな利点である

問題と解決手段

vite.config.ts
import { defineConfig } from "vite-plus";

export default defineConfig({
  pack: {
    deps: {
      skipNodeModulesBundle: true,
    },
    dts: {
      sourcemap: true,
    },
  },
});

選択肢の比較

問題VitetsdownVite+
エントリーポイントの設定
依存関係の外部化△ (SSR-mode or 外部plugin)
.d.ts生成△ (外部plugin)
Source Mapの生成
設定ファイルの増加○ (vite.config.ts)× (別config)○ (vite.config.ts統合)

Vite+は内部でtsdownを利用しているので、設定項目などはすべて同じで、設定ファイルを1つにまとめられるかどうかという点だけが異なる

まとめ

Nodeライブラリ用途ではVite単体は設計がブラウザ寄りで扱いづらい
tsdownを使うべきだけど、設定ファイルを増やしたくない
という状況にVite+が最適なツールだと判断した