yarn v1 → pnpm + monorepo への移行で出来るだけ依存関係を保ちたい
TL;DR
の2つでどうにかしました!😊
背景
いま関わっているプロジェクトで、yarn v1 の monorepo から pnpm monorepo への構成変更をする必要がありました。
悩みまくって丸1日費やしてしまったので、この記事をもって誰かのお役に立てれば嬉しい。
この記事の前提
pnpm や yarn の詳細は記載しません。公式docをどうぞ🙇♂️
タスクの前提
既存リポジトリは中途半端なTypeScript実装でかなり危うい状態。
また、自動テストも殆どなく、カバレッジも低い。
ただし、本番で動いているコードなので、実装内容へ出来る限り手は付けたくない。
現構成
構成を見ていきます。
ディレクトリツリー
├── hoge
│ ├── package.json
│ └── yarn.lock
├── fuga
│ ├── package.json
│ └── yarn.lock
├── hoo
│ ├── package.json
│ └── yarn.lock
├── package.json
└── yarn.lock
yarn.lockが散らばってる状態。
また、workspaceがカテゴライズされてなくて分かりづらい。
/package.json
"scripts": {
"hoge:build": "yarn --cwd ./hoge build",
"fuga:build": "yarn --cwd ./fuga build",
}
workspaces のプロパティ無し。
yarn --cwd でディレクトリだけ変更してスクリプトを通しているような状況。
現構成のメリデメ
良く言えば疎結合。
悪く言えばmonorepoのメリットはあんまりない。本当に git 一元管理のためだけ、という感じ。
構成変更によるメリット
ここで一旦、pnpm + workspace によるメリットをおさらい。
- パッケージのディスクスペース節約
- パッケージ依存関係の整理(不整合がなくなる)
- workspaceに対するscript実行の容易さ
- pnpm によるインストール時間の短縮
- コード共有の容易さ
- IDEへの統合
pnpm のメリットとworkspace対応のメリット両方含まれてます。
移行するメリットは大きいと思っています。
やり方1「単純なworkspace化」 ※ 失敗例
一番最初に思いつく方法として、単純なworkspaceへの移行。
祈りながら実施。
ステップ
- pnpm-workspace.yaml
packages:
- 'hoge'
- 'fuga'
- 'hoo'
- install
pnpm i
- build
pnpm -r build
結果
TypeScriptのエラーが出まくった。🥶
src/api/devices.ts:10:36 - error TS7016: Could not find a declaration file for module 'lodash'. '/root/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/lodash.js' implicitly has an 'any' type.
Try `npm i --save-dev @types/lodash` if it exists or add a new declaration (.d.ts) file containing `declare module 'lodash';`
10 import { isNaN, isUndefined } from 'lodash'
~~~~~~~~
src/hoge.ts:4:20 - error TS7016: Could not find a declaration file for module 'lodash'. '/root/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/lodash.js' implicitly has an 'any' type.
Try `npm i --save-dev @types/lodash` if it exists or add a new declaration (.d.ts) file containing `declare module 'lodash';`
4 import * as _ from 'lodash'
... 以下大量 ...
yarn だと出ないのにどうして...
ここで、全部のType定義を直していくという選択肢も考えましたが、多すぎる。
また、ロジックに手を付ける必要もあった。断念。
原因の推測
恐らく下記のあたりが原因だろうと予想しました。
- yarn.lockとpnpm-lock.yaml でパッケージ依存バージョンが変わったため
- pnpm と yarn でパッケージの依存解決手法が違うため
やり方2「pnpm import と node-linker: hoisted適用」
成功したやつ。
pnpm の下記2つを利用します。
上記の公式docが詳しい。
ステップ
- .npmrc
node-linker=hoisted
strict-peer-dependencies=false
※ 互換性を上げるため、セオリー通りstrict-peer-dependencies
も定義してます。インストール自体は完遂してたので本来は不要のはず。
- pnpm-workspace.yaml
packages:
- 'hoge'
- 'fuga'
- 'hoo'
- import
pnpm import
これで、project rootに pnpm-lock.yaml ができる
- install
pnpm i
- build
pnpm -r build
結果
通った!😊
hoge build$ tsc
└─ Done in 9.2s
fuga build$ tsc
└─ Done in 13.2s
hoo build$ react-app-rewired build
│ Find out more about deployment here:
│ https://cra.link/deployment
└─ Done in 16.7s
...
結論
今回は node-linker=hoisted
がいい動きをしてくれました。
pnpm import
は補佐的な役割。
もし yarn workspace を元々つかっていて、pnpm に移行するという場合は、この手順で問題は起きないはず。
補足
勘のいい人は気づいていると思いますが、結局それぞれのworkspaceにあった yarn.lock のバージョン設定は破棄された形になりました。
依存関係が正しく設定されていれば問題は起きないはずですが、障害になる確率もゼロではないです。
Discussion