プロジェクトを壊さず安全に npm から yarn4 へ移行する
これはなに
長らく node パッケージマネージャーとして npm が使われてきたプロジェクトを yarn v4 に移行する手順をまとめたものです。
単純に移行するだけなら何も考えず yarn を導入して終わりですが、依存する node パッケージのバージョンを一切変更することなく移行するとなれば一筋縄ではいかなくなります。立ち上げ時から増改築を繰り返して膨大な数の node パッケージに依存しているプロジェクトですと、ほんの少しバージョンがズレただけで壊れてしまうことも珍しくありません。本稿ではそのような類のプロジェクトでも可能な限り安全に npm から yarn4 へ移行する手順をご紹介します。
前提
- プロジェクトが依存するパッケージ情報は
^
付きでpackage.json
にバージョンが記載されているものとする。 - プロジェクトには
package-lock.json
があるものとする。 - プロジェクトに Renovate や dependabot の類は導入されていないものとする。
- プロジェクトの Node.js は
v18
以上であるものとする[1]。
移行手順
yarn.lock
を生成する
1. package.json
に ^
付きでバージョンが記載されているということは、依存するパッケージの本当のバージョンは package-lock.json
にのみ記載されていることになります。つまり各種依存パッケージのバージョンを一切変更することなく yarn に移行するには、 package-lock.json
の内容をそっくりそのまま yarn.lock
に移行せねばなりません。
yarn には package-lock.json
から yarn.lock
を生成する import
という機能があるため、これを利用します。
yarn import
「なんだ簡単じゃないか」と思われたでしょうが、この機能を使うには以下の要件をすべて満たす必要があります。
- yarn v1 である。
- yarn v2, v3, v4 は NG。
- yarn v1 は corepack 経由でインストールできないため、homebrew 等でグローバルインストールする必要がある。
- 使用する Node.js のバージョンが以下に該当する。
-
^14.18.0 || ^16.14.0 || >=18.0.0
[2]
-
上記の要件をすべて満たした上で yarn import
コマンドを実行すると package-lock.json
を元に yarn.lock
が生成されます。公式ドキュメントには「ロックファイルと既存の依存関係ツリーとの間の差異を可能な限り少なくするもの」と記載されているとおり 100% の精度ではないとのことですが、package.json
の記載内容から生成するよりは遥かに信頼できると言って良いでしょう。
yarn.lock
の生成に成功したら package-lock.json
はもう不要なので削除します。自動的に削除されないため、忘れずにやっておきましょう。
.
├── src/
-├── package-lock.json
├── package.json
+└── yarn.lock
2. yarn4 を導入する
yarn は corepack 経由でのインストールが推奨されているため、まずは corepack を有効化します。
corepack enable
次に yarn をインストールします。
yarn set version stable
上記コマンドを実行すると package.json
に packageManager
というフィールドが追加され、corepack 経由でインストールされた yarn のバージョンが表記されます。
{
"packageManager": "yarn@4.0.0"
// ...
}
これで yarn が利用可能となります。
yarn -v
4.0.0
3. yarn の設定を調整する
npm や yarn1 と違い、v2 以降の yarn は依存パッケージを node_modules
ディレクトリー配下に実態をダウンロードせず pnp.cjs
, pnp.locker.mjs
というファイルで管理する仕様となっています。ですが元々 npm を使っていたプロジェクトであることを考慮すると、引き続き node_modules
ディレクトリーを使う方式の方が適切です。そこで .yarnrc.yml
というファイルをプロジェクトディレクトリー直下に新規作成し、以下のように記述します。
.
├── src/
+├── .yarnrc.yml
├── package.json
└── yarn.lock
nodeLinker: node-modules
これで引き続き node_modules
ディレクトリー配下に依存パッケージがダウンロードされるようになります。
また、インストールした node パッケージのバージョンを完全に固定するための設定もしておくと良いでしょう。
nodeLinker: node-modules
+defaultSemverRangePrefix: ''
これで yarn add foo
とした際に "foo": "1.0.3"
のように exact version でインストールされます(^
が付かない)。つまり -E
オプションを毎回付ける必要がなくなります。
.gitignore
に yarn 用の設定を追記する
4. yarn は v2 から依存パッケージを .yarn
ディレクトリー配下にキャッシュします。これはパフォーマンス向上のための挙動ですが、それらは git 管理下に置くようなものではないため、以下を追記して git 管理下から除外しておくのが賢明です。
+.pnp.*
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/sdks
+!.yarn/versions
5. 依存パッケージを再インストールする
rm -rf node_modules # 念のため古い node_modules を掃除しておく。
yarn install
package-lock.json
が生成されないようにする
6. 誤って せっかく yarn に移行しても npm install
コマンドを実行すると再び package-lock.json
が生成されてしまいます。これを予防するために npm *
コマンドの使用を禁ずる設定をしておきます。
{
+ "engines": {
+ "npm": "use yarn instead",
+ "yarn": "4.0.0"
+ },
"packageManager": "yarn@4.0.0"
}
package.json
に engines
フィールドを追加し、その中に "npm": "use yarn instead"
と記述します。このフィールドは当該プロジェクトで使用可能な各種エンジンのバージョンを明記するためのものですが、ここに実在しないバージョンを指定することでそのエンジンを使うコマンドは必ず失敗するようになります。よって "use yarn instead"
でも "代わりに yarn を使ってください"
でも構いません。ついでに yarn
の適切なバージョンを指定しておくと良いでしょう。
次に .npmrc
というファイルを作成して以下のように設定します。
engine-strict=true
package.json
の設定だけでは不十分であり、このファイルと組み合わせることで初めて期待通りの動作が得られます。
移行作業は以上です。あとはプロジェクトに応じて CI/CD や README を書き直せば完了です。
参考文献
-
このバージョンに該当しないと
yarn import
が失敗します。 ↩︎
Discussion
とても参考になりました。ありがとうございます。