Open5
Turborepo
Turborepoとはintelligent build systemである。
lint, build, testのようなコマンドを、キャッシュなどの活用により高速に行うことができる。
既存システムにすぐ組み込める
Installl Turborepo
pnpm install turbo --global
でグローバルインストールもできるが、チーム開発するならpnpm add turbo --save-dev --ignore-workspace-root-check
でリポジトリごとにインストール。
Add Turborepo to your existing monorepo
- turborepoは、既存のパッケージマネージャのworkspace機能とcompatible。
- npm, yarn, pnpmなどでworkspaceがすでに設定されているものとする。
以下導入手順。
- モノレポのルートに
turbo.json
を作る。この時、モノレポのタスクの依存関係グラフを構築するために、pipeline
を指定する。dependsOn
で依存関係を明示する
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
// A package's `build` script depends on that package's
// dependencies and devDependencies
// `build` tasks being completed first
// (the `^` symbol signifies `upstream`).
"dependsOn": ["^build"],
// note: output globs are relative to each package's `package.json`
// (and not the monorepo root)
"outputs": [".next/**", "!.next/cache/**"]
},
"test": {
// A package's `test` script depends on that package's
// own `build` script being completed first.
"dependsOn": ["build"],
// A package's `test` script should only be rerun when
// either a `.tsx` or `.ts` file has changed in `src` or `test` folders.
"inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"]
},
// A package's `lint` script has no dependencies and
// can be run whenever. It also has no filesystem outputs.
"lint": {},
"deploy": {
// A package's `deploy` script depends on the `build`,
// `test`, and `lint` scripts of the same package
// being completed. It also has no filesystem outputs.
"dependsOn": ["build", "test", "lint"]
}
}
}
- できたら、
npx turbo run deploy
などでタスクを走らせる
リモートキャッシュ
turborepoは、デフォルトではローカルマシンにキャッシュを置くが、リモートキャッシュを使うことで、CIや他の人のタスク実行結果のキャッシュを活用できる(dropboxみたいなもん)
以下手順。
-
turbo login
でvercelアカウントにログイン。 -
turbo link
でリモートキャッシュとturborepoを紐付け。そうすると、自動でremote cacheが作られる。この状態でlocal cacheを削除しても、ちゃんとremote cacheが使われるようになってる。
Core concepts
キャッシュ
- turborepoは、タスクのinput (e.g., ソースファイル, デフォルトでは、workspace内の全ファイル)のハッシュを計算、それに対応するキャッシュがあるかチェックし、あったらそれを使う。キャッシュがなかったらタスクを実行、outputに指定されたアーティファクトを保存
リモートキャッシュ
- 前述の通り
モノレポ
- 何百ものタスクが走るモノレポはスケールが難しい。
- これをturborepoは、1. キャッシュ、2. タスク間の依存関係に基づく実行スケジューリング によって解決
- turborepoは、npm, pnpm, yarnのようにパッケージのインストールは行わない
タスクの実行
- 3workspaceあるケースで普通に実行した場合が下記。
- turborepoでは、下記のように実行できる
ワークスペースのフィルタリング
- デフォルトでは、モノレポのルートで
turbo run test
とかやると、全パッケージでタスクが走る。--filter
オプションで、対象のワークスペースを絞れる
ワークスペースごとの設定
- version 1.8から導入
-
turbo.json
を、ルートディレクトリだけでなく、その配下の任意のワークスペースにおける。 - 例えば、Next.jsとSveleteKitが混在するケース。ルートだけに
turbo.json
を置くパターンだと、下記のようになり気持ち悪い。
{
"pipeline": {
"build": {
"outputs": [".next/**", "!.next/cache/**", ".svelte-kit/**"],
}
}
}
- ワークスペースごとにすれば、SvelteKit側を下記のように書ける。next.js側も同様。
{
"extends": ["//"],
"pipeline": {
"build": {
"outputs": [".svelte-kit/**"]
}
}
}
競合ツール
State of Javascript 2022を見ると、下記があげらている。
- Rush
- Yalc
- Lerna
- Nx
- npm
- yarn
- pnpm
awareness
usage
interest
retention
全体として、pnpm, npm, yarnなどのパッケージマネージャのworkspace機能か、もしくはNxが大きな競合になってそう。
Nxに関しては、turborepoより高機能でDXも良い、という意見がある。一方、turborepoはシンプル。
- https://medium.com/@knidarkness/nx-dev-vs-turborepo-for-a-green-field-projects-in-2022-c73dd858b687
- https://morioh.com/p/c735d1be3d70
- https://www.reddit.com/r/reactjs/comments/yhzf3f/nx_vs_turborepo_concerned_about_betting_on_either/
プロジェクトの規模によって、だいたい下記のような使い分けか。
- 大規模: Nx
- 中規模: Turborepo
- 小規模: pnpm, npm, yarn
パッケージごとに設定を分ける
- パッケージごとにturbo.jsonを書いて、タスクの内容をカスタムすることができる。その場合、各appやpackageごとに下記の内容にする
{
"extends": ["//"],
"tasks": {
"build": {
// Custom configuration for the build task in this package
},
"special-task": {} // New task specific to this package
}
}
- extendsでtop-levelのturbo.jsonを参照。tasksに、カスタムしたいタスクを書いていく。(何も書かなかったらtop-levelの内容がそのまま引き継がれる)
- 機能的には、ルートのturbo.jsonでpackageごとの設定をしていくのと同じに見える。しかし、ルートでこれをやると各設定が「merge」ではなく完全に「上書き」になってしまうのでちょっと煩雑。(e.g., 特定タスクのoutputだけカスタマイズしたいのに、inputも書かないといけなくなる)