👌

yarn v3 の独自機能を避けつつ yarn v1 から v3 へのアップグレードをする

2021/08/12に公開

yarn v3 が出ました。詳しい解説は譲るとして、esbuild integration や パフォーマンス向上が目玉です。

Yarn 3.0 🚀🤖 Performances, ESBuild, Better Patches, ... - DEV Community

流石に v1 はもう古いが、 v2 からの独自路線は受け付けがたい…という立場なのですが(yarn オリジナル作者の sebmck も難色を示しています)、今回は yarn 特有の機能をできるだけ避けて、できるだけ npm や pnpm 等と互換な部分だけで yarn v3 を使います。なので pnp も使いません。eslint や vscode の typescript 等でハマりどころが多すぎます。

ゼロインストールも否定派です。git blob objects のサイズが爆発して仕事にならなくなったことがあります。具体的には git lfs を使わずバイナリをコミットする Unity Project みたいになる予感しかしません。あれは辛かった…。

既にある yarn v1 プロジェクトからの移行

$ rm -rf yarn.lock node_modules **/node_modules
$ yarn set version berry
$ touch yarn.lock
$ yarn set version 3.0.0

# .yarnrc.yml を編集
# yarnPath: .yarn/releases/yarn-3.0.0.cjs
# nodeLinker: node-modules

# .gitignore を編集
# .yarn/
# !.yarn/releases/*

$ yarn install

意味はあとで解説します。yarn.lock が v1 と互換があるかわからないので、一旦消して更地から構成しています。

解説: yarn v3 プロジェクトの新規作成

最初に、新規だとどういう振る舞いをするか知っておく必要があります。これは既にある v1 プロジェクトを移行する際に必要になる知識が身につきます。

$ mkdir yarn-v3-test
$ cd yarn-v3-test
$ yarn init -y # この時点では yarn v1.22
$ yarn set version berry # berry
$ touch yarn.lock
$ yarn set version 3.0.0 # 3.0.0

既にややこしい問題が複数あって、その回避策が混ざったセットアップです。

まず、現在の yarn v1.22.x から直接 v3 へアップグレードする機能がなく、一旦 berry というコードネームの実質 v2 を経由します。もうややこしいですね。

ここからは環境依存なのですが、 v2 以降では、親ディレクトリのいずれかに package.json があると、そこを workspace の親として認識しようとして、親に管理されていない子 package.json では yarn install が失敗します。自分は巨大なリポジトリの一部でこれをやろうとしてハマりました。

package.json # ここに husky と lint-staged の設定があった
app/
  package.json
  yarn.lock # これが必要

これを解決するために、自分が独立した package.json のロックの管理単位であることを宣言するために空の yarn.lock を作成しておきます。

yarn.lock があるディレクトリで、 yarn set version 3.0.0 をすると、.yarn/releases/yarn-3.0.0.cjs に yarn v3 の実体が生成されます。これがないと yarn コマンドの実行に失敗するので、 git にコミットしておく必要があります。

また、 .yarnrc.yml もコミットします。

.yarnrc.yml の設定

まだ yarn install しません。最初に pnp を無効化します。

.yarnrc.yml

yarnPath: .yarn/releases/yarn-3.0.0.cjs
nodeLinker: node-modules

今の yarn コマンドの振る舞いは、.yarnrc.yml があればその yarnPath を使います。何もなければ安定版である v1 を使います。

これで yarn install ができるはずです。

$ yarn install

.gitignore

yarn install すると .yarn/cache に node_modules の実体の zip が保存されます。これをコミットするのがゼロインストールモードで、 yarn install が不要になります。ただ、これをやると前述のように git blob objects が爆発するので避けます。

.yarn/
!.yarn/release/*

たしかにゼロインストールで便利なところもあって、 docker に転送する際に docker 内で yarn install をする必要がない、という点です。このためだけに 一時的に .gitignore を書き換えるスクリプトを用意するのもありかもしれないと考えてますが、まだ検証してません。

Workspace の設定

機能自体は v1 と同一ですが、v3 のほうが安定しています。

packages/
  foo/
    package.json
  bar/
    package.json
package.json
yarn.lock

こういう構成の時, package.json に次の設定をすることで、可能な限り親の node_modules に巻き上げられます。また、foo や bar がそれぞれの name で相互に require/import できるようになります。

  "workspaces": ["packages/*"]

巻き上げのポリシーがいくつかあり、 nmHoistingLimits で設定できます。デフォルトの全部巻き上げる none, workspace 単位で独立する workspaces, それぞれが独立する dependencies です。

# ...
nmHoistingLimits: workspaces

ここで今自分が困ってるのが、 今まで package.json の workspaces.nohoist で出来た、特定パッケージで hoist を無効化する機能がなくなっています。 react-native などでは詰むかもしれません。また、試した感じ dependenciesnodeLinker: node-modules だと機能してない気がしてます。 pnp だと yarn install 自体は動きましたが正しい状態かは未確認です。

GitHub Package Registry を使う設定

yarn v3 は .npmrc を認識しません。なので認識するための設定を .yarnrc.yml に書く必要があります。

例えば @mizchi/myutil という private な npm パッケージがあった場合、それを解決するためには次の設定が必要です。

# ...
npmScopes:
  mizchi:
    npmAlwaysAuth: true
    npmRegistryServer: 'https://npm.pkg.github.com'

npmRegistries:
  //npm.pkg.github.com:
    npmAlwaysAuth: true
    npmAuthToken: <your-token>
shared/
  hoge/
    package.json
app-a/
  package.json
  yarn.lock
app-b/
  package.json
  yarn.lock

app-a や app-b から shared を引きたいとき、dependencies として link: を使います。

  "dependencies": {
    "hoge": "link:../shared/hoge"
  }

自分はここでハマったのですが、yarn v3 は link: を省いたデフォルトだと portal: というプロコトルが使われています。これは hoisting 単位が app-a や app-b と同一になり、同名のパッケージでバージョンのコンフリクトがあると hoisting がおきない…ではなく、Cannot Link のエラーがおきて yarn install が停止します。つまり、ここで使われているパッケージ内で全部揃える必要が発生して、例えば typescript コンパイラの patch バージョンすら揃える必要が出ます。

link: だと単に simulink になるので、この問題は発生しません。ただし、自分で対象の (yarn/npm) install を済ませておく必要があります。これは npmpnpm と同一の機能のはず…です。

感想

動きだせば安定したように見えてるんですが、またハマったりしそうなので、しばらく運用したら追記します。

参考

Migration | Yarn - Package Manager

Discussion