😃

【Bun v1.3】ワークスペース使用時のデフォルトインストールモードが Isolated(分離インストール)になっていた

に公開

はじめに

いつも通りにモノレポプロジェクトで bun i した時の挙動が変わっていたので調べてみました。
どうやら v1.3 からワークスペースを使用する場合はデフォルトで分離インストールされるようになったみたいです。

Isolated installs are now the default for workspaces
ワークスペースでは分離インストールがデフォルトになりました

詳細はリリースノートに書いてあります。

インストール手法の違い

Bun におけるインストール手法は2種類あります。参考

名前 説明
Hoist Install
(単一インストール)
ルートディレクトリの node_modules に全ての依存関係をインストールする手法
npm / yarn と同様のアプローチ
v1.3 の単一プロジェクトにおけるデフォルト
Isolated Install
(分離インストール)
各ワークスペースの node_modules に依存関係をインストールする手法
pnpmと同様のアプローチ
v1.3 のワークスペースにおけるデフォルト

Isolated Install とは

公式に詳細が載っています。
v1.2.19(2025/7/19リリース)で追加された機能です。
以下に主なものを抜粋します。

  • ファントム依存関係を防止できます。
    より具体的には、各ワークスペースで自身の依存関係に含まれていないパッケージは参照できなくなります。
    これにより、予期せぬパッケージへの依存(ファントム依存関係)を防ぐことができます。
  • 中央パッケージストア(ルートディレクトリの node_modules/.bun に実際のパッケージがインストールされ、各ワークスペースの node_modules には中央パッケージストアへのシンボリックリンクが貼られます。
    そのため、複数ワークスペースで重複している依存関係があったとしても、パッケージの実体は常に単一になります。
  • ワークスペース毎に環境が完全に分離されます。
    主にビルド時等に依存関係の予期せぬ不整合を未然に防ぐことができます。

構成図

Isolated Install を使用すると以下のような構成になります。

 /
 ├─ apps
 │   ├─ api
 │   │   ├─ src
+│   │   ├─ node_modules
 │   │   └─ package.json
 │   └─ ui
 │       ├─ src
+│       ├─ node_modules
 │       └─ package.json
 ├─ packages
 │   ├─ database
 │   │   ├─ src
+│   │   ├─ node_modules
 │   │   └─ package.json
 │   └─ components
 │       ├─ src
+│       ├─ node_modules
 │       └─ package.json
 ├─ node_modules
+│   ├─ .bun (中央パッケージストア)
 │   └─ ...
 └─ package.json

インストール手法の切り替え

インストール手法を切り替えるには CLI でオプションを都度指定する方法と、bunfig.toml で設定する方法の2種類があります。

CLI

# 単一インストール
bun i --linker hoisted

# 分離インストール
bun i --linker isolated

bunfig.toml

bunfig.toml
[install]
# linker = "hoisted" # 単一インストール
linker = "isolated" # 分離インストール

最後に

いきなり挙動が変わったので少しびっくりしましたが、Isolated Install 自体がモノレポととても相性のいい仕組みなので積極的に取り入れていきたいと思いました。

同じような事象が起きて驚いた方に向けて書き残しておきます。

追記

前提として筆者は Devcontainer を使用しており、Nodeは入れていません。

分離インストールを使用すると動かなくなるツールがいくつかありました。
Lefthook, Biome 等々...

例えば、Lefthookの場合は Node が見つからないと言われます。

/usr/bin/env: ‘node’: No such file or directory

入れてないので見つからないのはそうなんですが、なんで Node で実行しようとしている?と思って調べてみたところ分離インストールの場合はプラットフォーム別のバイナリへのシンボリックリンクは貼られないみたいです。

# Hoisted Install の場合: フラット化されるためルートの node_modules にプラットフォーム別のバイナリもインストールされている
ls node_modules/lefthook-*/bin/lefthook
node_modules/lefthook-linux-arm64/bin/lefthook

# Isolated Install の場合: フラット化されないためルートの node_modules にプラットフォーム別のバイナリはインストールされない
user ➜ ~/app (develop) $ ls node_modules/lefthook-*/bin/lefthook
ls: cannot access 'node_modules/lefthook-*/bin/lefthook': No such file or directory
user ➜ ~/app (develop) $ ls ../.bun/install/cache/lefthook-*/bin/lefthook
../.bun/install/cache/lefthook-linux-arm64@1.13.6@@@1/bin/lefthook

上記の動きは正しい気もしつつ、パッケージ側でプラットフォーム別のバイナリが node_modulesに あったら実行するみたいな実装がされていると、分離インストールの場合のプラットフォーム別のバイナリの実体は ~/.bun/install/cache にあるため常に見つからない状態となります。
Lefthook の場合も正にそうで、プラットフォーム別のバイナリが見つけられず、Node で実行する用のバイナリを実行しようとして Node が無いためエラーとなったようでした。

対策としては、パッケージ側の対応が一通り完了するまで Hoisted Install を使用するか。
Node.js を入れるか、どうしても Bun を使って Isolated Install を使いたい場合は、Node のパスに Bun へのシンボリックリンクを貼る等ができます。

$ sudo ln -s $(which bun) /usr/local/bin/node

これで Node を実行すると代わりに Bun が実行されるようになります。

$ node -p "1+1"
# 2
HCプロデューステックブログ

Discussion