corepack is 何?
Node.js 本体に yarn を標準バンドルするかの議論が盛り上がったが、yarn を直接バンドルするのではなく corepack をバンドルする方向に決まったようだ。
ところで、corepack って何?
corepack とは
元は pmm (package manager manager) という名前だった。つまり npm, yarn などパッケージマネージャーを管理するツール。
利用者がグローバルに corepack をインストールしていて、かつプロジェクト側で package.json
にパッケージマネージャと利用バージョンを指定しておけば、指定したパッケージマネージャを自動的に使わせたり、他のパッケージマネージャを使えなくしたりできる。
もし Node.js に標準バンドルされれば、「利用者がインストールしておく」というステップが不要になり、プロジェクトの設定だけで適切なパッケージマネージャを使ってもらえることになる。
ちなみに作者の arcanis 氏は yarn のリードメンテナ。DESIGN.md によれば、npm だけが Node.js に標準バンドルされていて優遇されてるけど、他のパッケージマネージャも同等に first class citizen として扱いたい。そのために corepack によって、yarn などを使うために追加で必要なインストールなどの手間を削減するぞ、というのがモチベーションのようだ。
インストール
利用者側は npm で普通にグローバルインストールする。ただし、yarn などのバイナリを乗っ取る挙動になるので、コンフリクトしないよう yarn はあらかじめ削除しておけとのこと。
$ npm uninstall -g yarn pnpm
$ npm i -g corepack
nodenv を使っている場合は rehash しておくこと。ちなみに nodebrew とは相性が悪く、shim の相対パスがバグってうまく動かなかった。
プロジェクトでの利用
package.json
の packageManager
フィールドに利用するパッケージマネージャーを指定する。この最重要情報が README に書いてないのは謎。
{
"packageManager": "yarn@2.0.0-rc.29"
}
この状態で、該当プロジェクトディレクトリで yarn
コマンドを打つと、勝手に裏で yarn@2.0.0-rc.29
がインストールされてから実行される。基本はそれだけ。シンプル。
$ yarn -v
2.0.0-rc.29
このように透過的に動く仕組みなので、Node.js に corepack が標準バンドルされれば、確かに yarn を利用するために必要なセットアップの手間は無くなると言えそう。yarn の特定バージョン(v1 or v2 どっち?という問題もある)をバンドルするより使い勝手もメンテナンスコストも良さそうなので妥当な判断っぽい。
npm の扱い
今度は packageManager
に npm を指定する。
{
"packageManager": "npm@7.11.2"
}
このプロジェクト内で yarn
コマンドを打つと、正しくエラーになる。
$ yarn
Usage Error: This project is configured to use npm
では、npm
コマンドを打つと yarn のときと同様に指定したバージョンが自動でインストールされて実行されるよね?と思うけど、されない。グローバルにインストール済みのデフォルトの npm で普通に実行されるだけ。
なぜか?corepack はデフォルトでは yarn と pnpm だけしか管理下に置いていない。npm を管理下に置くには corepack enable
コマンドによってオプトインが必要(これはプロジェクト固有設定ではなく、一度実行するとグローバルに有効化される)。
$ corepack enable npm
すると、npm
コマンドで指定バージョンが自動インストールされて無事に実行されるようになる。
$ npm -v
7.11.2
同様に、npm を管理下に置いた状態であれば packageManager
に yarn が設定されているプロジェクトで npm
コマンドを使うと正しくエラーにできる。
$ npm
Usage Error: This project is configured to use yarn
逆に言うと、各ユーザーが corepack enable npm
を実行しない限り、yarn なプロジェクトで npm
コマンドを使ってもエラーにならないということ。この辺り、npm も管理下に置くことの副作用やデメリットがどこまであるか分からないが、Node.js の標準バンドルになるならデフォルト有効になってた方が便利そうな気はする。
corepack の仕組み
corepack enable
されたパッケージマネージャーは、グローバルのバイナリを置き換えることでフックする。例えば nodenv 環境の Node.js v16 で yarn を有効化すると ~/.nodenv/versions/16.0.0/bin/yarn
に以下のファイルが置かれる。
#!/usr/bin/env node
require("./corepack").runMain(["yarn", "yarn", ...process.argv.slice(2)]);
これでフックして、プロジェクトで指定されたバージョンを ~/.node/corepack/yarn/{version}
から探す。無ければその場でダウンロードして展開して実行する。仕組みはシンプル。
プロジェクトでの指定がない場合は?
npm i -g yarn@x.x.x
に相当するデフォルトバージョンみたいなものとして、"Known Good Releases" という概念のバージョンを corepack prepare --activate
によって指定できる。
$ corepack prepare yarn@1.22.9 --activate
ここで指定した yarn のバージョンは ~/.node/corepack/lastKnownGood.json
に書き込まれ、プロジェクトによる packageManager
の指定がない場所で yarn
コマンドを実行する場合に使われる。npm のバージョンも同様に管理できる。
ちなみに "Known Good Releases" が指定されていないデフォルト状態では、corepack にハードコードされた default バージョンが利用される。
まとめ
Node.js に標準バンドルされれば割と便利そうなので、一気にデファクトになっていくかもしれない。
Discussion