asdfでNode.jsを管理しつつ、グローバルツールのインストールにpnpmを使うときのメモ。
pnpm setup の既定設定だとグローバル領域が固定されるため、Node.jsのバージョンごとに分離できるように設定する。
TL;DR
.zshrc でasdfで現在使っているnodejsのバージョンを見て npm_config_global_dir と npm_config_global_bin_dir を切り替えることでNode.jsバージョンごとにpnpmのグローバル領域を分離できる。
前提
- Node.jsはasdfで管理している
- pnpmはCorepackで有効化する
# pnpm有効化
corepack enable
corepack prepare pnpm@latest --activate
pnpm --version
pnpmでのグローバルへのインストール
pnpm add -g <package-name>
asdfにおけるpnpm setup の問題点
pnpm setup を実行すると、.zshrc に次のような固定設定が追加される。
export PNPM_HOME="/Users/ken/Library/pnpm"
case ":$PATH:" in
*":$PNPM_HOME:"*) ;;
*) export PATH="$PNPM_HOME:$PATH" ;;
esac
この方式だとasdfでNode.jsのバージョンを切り替えてもpnpmのグローバル領域が共通化されてしまうため、不整合がおきると思われる。
対応方法
方針は以下の通り
PNPM_HOMEの固定方式は使わない- asdfの現在のNode.jsバージョンを取得し、バージョンごとに
global-dirとglobal-bin-dirを切り替える chpwdとprecmdの hook で追従させる
.zshrc にいれるブロック(pnpm setupがいれたブロックの代わりにいれる)
# pnpm
# Keep pnpm globals isolated per asdf-managed nodejs version.
set_pnpm_asdf_scope() {
command -v asdf > /dev/null 2>&1 || return
local asdf_dir node_ver pnpm_base
asdf_dir="${ASDF_DATA_DIR:-$HOME/.asdf}"
node_ver="$(asdf current nodejs 2>/dev/null | awk '$1=="nodejs"{print $2; exit}')"
if [ -z "$node_ver" ] || [ "$node_ver" = "system" ]; then
return
fi
# asdfでいれるnodejsのディレクトリの下にいれれば削除時に一緒にきえるのでよさそう
pnpm_base="$asdf_dir/installs/nodejs/$node_ver/.pnpm"
unset PNPM_HOME
export npm_config_global_dir="$pnpm_base/global"
export npm_config_global_bin_dir="$pnpm_base/bin"
mkdir -p "$npm_config_global_dir" "$npm_config_global_bin_dir"
case ":$PATH:" in
*":$npm_config_global_bin_dir:"*) ;;
*) export PATH="$npm_config_global_bin_dir:$PATH" ;;
esac
}
autoload -Uz add-zsh-hook
add-zsh-hook chpwd set_pnpm_asdf_scope
add-zsh-hook precmd set_pnpm_asdf_scope
set_pnpm_asdf_scope
# pnpm end
動作確認
source ~/.zshrc
asdf current nodejs
pnpm config get global-dir
pnpm config get global-bin-dir
global-dir / global-bin-dir に現在の Node.js バージョンを含むパスが表示されればOK。
例:
~/.asdf/installs/nodejs/24.14.1/.pnpm/...