😋

corepack is 何?

2021/05/01に公開

Node.js 本体に yarn を標準バンドルするかの議論が盛り上がったが、yarn を直接バンドルするのではなく corepack をバンドルする方向に決まったようだ。

ところで、corepack って何?
https://github.com/nodejs/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.jsonpackageManager フィールドに利用するパッケージマネージャーを指定する。この最重要情報が 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