新しいモノレポの作成
このガイドでは、グローバルインストールされたturbo
を使用します。このセットアップについては、インストールガイドに従ってください。または、下記のコマンドでローカルインストールされたturbo
をパッケージマネージャーを使用して実行することもできます。
クイックスタート
新しいモノレポを作成するには、create-turbo
(新しいタブで開きます) npmパッケージを使用します。
npx create-turbo@latest
また、Turborepoスターターリポジトリをクローンして、モノレポを始めることもできます。Turborepoのサンプルとスターターについては、GitHubのTurborepoのサンプルディレクトリ (新しいタブで開きます)を参照してください。
完全なチュートリアル
このチュートリアルでは、基本的なサンプルを設定する方法を説明します。終了する頃には、turbo
の使用に自信を持ち、すべての基本的な機能を理解できるようになります。
このチュートリアルでは、コードサンプルから一部のコード行が省略されています。たとえば、package.json
を表示する場合、すべてのキーを表示するのではなく、重要なキーのみを表示します。
1. create-turbo
の実行
最初に、次を実行します
npx create-turbo@latest
これにより、create-turbo
(新しいタブで開きます) CLIがインストールされ、実行されます。いくつかの質問に回答する必要があります。
turborepoを作成する場所は?
好きな場所を選択してください。デフォルトは./my-turborepo
です。
使用するパッケージマネージャーは?
Turborepoはパッケージのインストールを処理しないため、次のいずれかを選択する必要があります。
create-turbo
は、システムで使用可能なパッケージマネージャーを検出します。どれを選択すればよいかわからない場合は、Turborepoはpnpm
を推奨します。
インストール
パッケージマネージャーを選択したら、create-turbo
は、選択したフォルダー名の中にたくさんの新しいファイルを作成します。また、デフォルトで basic
サンプルに付属するすべての依存関係をインストールします。
2. 新しいリポジトリの探索
ターミナルに何かが表示されていることに気づいたかもしれません。create-turbo
は、追加したすべてのものの説明を提供しました。
>>> Creating a new turborepo with the following:
- apps/web: Next.js with TypeScript
- apps/docs: Next.js with TypeScript
- packages/ui: Shared React component library
- packages/eslint-config: Shared configuration (ESLint)
- packages/typescript-config: Shared TypeScript `tsconfig.json`
これらはそれぞれワークスペースです。つまり、package.json
を含むフォルダーです。各ワークスペースは、独自の依存関係を宣言し、独自のスクリプトを実行し、他のワークスペースが使用するコードをエクスポートできます。
お気に入りのコードエディターで、ルートフォルダー - ./my-turborepo
- を開いてください。
packages/ui
の理解
まず、./packages/ui/package.json
を開きます。パッケージの名前が "name": "@repo/ui"
であることに気付くでしょう。これはファイルの先頭にあります。
次に、./apps/web/package.json
を開きます。このパッケージの名前が "name": "web"
であることに気付くでしょう。ただし、その依存関係も確認してください。
"web"
が "@repo/ui"
という名前のパッケージに依存していることがわかります。
{
"dependencies": {
"@repo/ui": "*"
}
}
これは、Web アプリがローカルの @repo/ui
パッケージに依存していることを意味します。
apps/docs/package.json
の中を見ると、同じことがわかります。web
と docs
の両方が @repo/ui
に依存しています。これは共有コンポーネントライブラリです。
アプリケーション間でコードを共有するこのパターンは、モノレポでは非常に一般的であり、複数のアプリが単一のデザインシステムを共有できることを意味します。
インポートとエクスポートの理解
./apps/docs/app/page.tsx
の中を見てください。docs
と web
はどちらもNext.js(新しいタブで開きます)アプリケーションであり、どちらも同様の方法で @repo/ui
ライブラリを使用しています
import { Button } from "@repo/ui/button";
// ^^^^^^ ^^^^^^^^^^^^^^^
export default function Page() {
return (
<>
<Button appName="web" className={styles.button}>
Click me!
</Button>
<>
);
}
それらは Button
を @repo/ui/button
という依存関係から直接インポートしています。これはどのように機能するのでしょうか? Button
はどこから来ているのでしょうか?
packages/ui/package.json
を開きます。exports
フィールドが表示されます。
{
"exports": {
"./button": "./src/button.tsx",
"./card": "./src/card.tsx",
"./code": "./src/code.tsx"
},
}
ワークスペースが @repo/ui/button
からインポートするとき、exports
は、インポートしているコードにアクセスする場所を示します。
それでは、packages/ui/src/button.tsx
の中を見てみましょう
"use client";
import { ReactNode } from "react";
interface ButtonProps {
children: ReactNode;
className?: string;
appName: string;
}
export const Button = ({ children, className, appName }: ButtonProps) => {
return (
<button
className={className}
onClick={() => alert(`Hello from your ${appName} app!`)}
>
{children}
</button>
);
};
ボタンが見つかりました!
このファイル内のすべてのものは、@repo/ui/button
に依存するワークスペースで使用できるようになります。
このファイルで行う変更はすべて、web
および docs
で共有されます。とてもクールです!
このファイルから別の関数をエクスポートしてみてください。たとえば、2つの数値を足し合わせるための add(a, b)
など。
これは、web
と docs
でインポートできるようになります。
tsconfig
の理解
さらに、typescript-config
と eslint-config
の 2 つのワークスペースを見てみましょう。これらの各ワークスペースでは、モノレポ全体で構成を共有できます。typescript-config
の中を見てみましょう
{
"name": "@repo/typescript-config",
}
ここで、パッケージの名前が @repo/typescript-config
であることがわかります。
次に、web
アプリにある tsconfig.json
ファイルの中を見てみましょう。
{
"extends": "@repo/typescript-config/nextjs.json",
}
ご覧のとおり、@repo/typescript-config/nextjs.json
を tsconfig.json
ファイルに直接インポートしています。
このパターンにより、モノレポはすべてのワークスペースで単一の tsconfig.json
を共有でき、コードの重複を減らすことができます。
eslint-config
の理解
最後のワークスペースは eslint-config
です。
まず、packages/eslint-config/package.json
の中を見てみましょう
{
"name": "@repo/eslint-config",
"files": [
"library.js",
"next.js",
"react-internal.js"
],
}
ご覧のとおり、パッケージの名前は @repo/eslint-config
であり、library.js
、next.js
、react-internal.js
の3つのファイルを公開しています。
カスタム ESLint 設定をどのように使用できるかを理解するために、apps/docs/.eslintrc.js
の中を見てみましょう
module.exports = {
extends: ["@repo/eslint-config/next.js"],
};
ここでは、@repo/eslint-config/next.js
を .eslintrc.js
ファイルに直接インポートしていることがわかります。
typescript-config
と同様に、eslint-config
を使用すると、モノレポ全体で ESLint の設定を共有でき、どのプロジェクトで作業していても一貫性を保つことができます。
概要
これらのワークスペース間の依存関係を理解することが重要です。それらをマッピングしてみましょう。
web
-ui
、typescript-config
、およびeslint-config
に依存しますdocs
-ui
、typescript-config
、およびeslint-config
に依存しますui
-typescript-config
およびeslint-config
に依存しますtypescript-config
- 依存関係はありませんeslint-config
- 依存関係はありません
Turborepo CLI はこれらの依存関係を管理する責任がないことに注意してください。上記のすべてのことは、選択したパッケージ マネージャー (npm
、pnpm
、または yarn
) によって処理されます。
3. turbo.json
の理解
これで、リポジトリとその依存関係を理解できました。Turborepo はどのように役立つのでしょうか。
Turborepo は、タスクの実行をより簡単かつはるかに効率的にすることで役立ちます。
ルートにある turbo.json
の中を見てみましょう。
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**"]
},
"lint": {
"dependsOn": ["^lint"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
ここで確認しているのは、turbo
に 3 つのタスク (lint
、dev
、build
) を登録したということです。turbo.json
内に登録されたすべてのタスクは、turbo run <task>
(または短縮形の turbo <task>
) で実行できます。
先に進む前に、turbo.json
に登録されていない hello
というタスクを実行してみましょう。
turbo hello
ターミナルにエラーが表示されます。次のようなもの。
Could not find the following tasks in project: hello
これは覚えておく価値があります。turbo
がタスクを実行するには、turbo.json
に記述されている必要があります。
すでに用意されているスクリプトを調べてみましょう。
4. Turborepo を使用したリンティング
lint
スクリプトを実行してみましょう。
turbo lint
ターミナルでいくつかのことが発生します。
- いくつかのスクリプトが同時に実行され、それぞれに
docs:lint
、@repo/ui:lint
、またはweb:lint
のいずれかのプレフィックスが付いています。 - それぞれが成功し、ターミナルに
3 successful
と表示されます。 - また、
0 cached, 3 total
と表示されます。これが何を意味するのかは後で説明します。
実行される各スクリプトは、各ワークスペースの package.json
から取得されます。各ワークスペースは、独自の lint
スクリプトをオプションで指定できます。
{
"scripts": {
"lint": "next lint"
}
}
{
"scripts": {
"lint": "next lint"
}
}
{
"scripts": {
"lint": "eslint \"**/*.ts*\""
}
}
turbo lint
を実行すると、Turborepo は各ワークスペースの各 lint
スクリプトを確認し、実行します。詳細については、パイプラインに関するドキュメントを参照してください。
キャッシュの使用
lint
スクリプトをもう一度実行してみましょう。ターミナルにいくつかの新しいものが表示されます。
cache hit, replaying logs
がdocs:lint
、web:lint
、@repo/ui:lint
に対して表示されます。3 cached, 3 total
と表示されます。- 合計実行時間は
100ms
未満になり、>>> FULL TURBO
が表示されます。
興味深いことが起こりました。Turborepo は、前回のリンティング スクリプトの実行後、コードが変更されていないことに気づきました。
以前の実行からのログを保存していたため、それらを再生しただけです。
何が起こるかを確認するために、コードをいくつか変更してみましょう。apps/docs
内のファイルを変更します。
import { Button } from "@repo/ui/button";
// ^^^^^^ ^^^^^^^^^^^^^^^
export default function Page() {
return (
<>
<Button appName="web" className={styles.button}>
- Click me!
+ Click me now!
</Button>
<>
);
}
次に、lint
スクリプトを再度実行します。次のことに気づくでしょう。
docs:lint
には、cache miss, executing
というコメントがあります。これは、docs
がリンティングを実行していることを意味します。2 cached, 3 total
が下部に表示されます。
これは、以前のタスクの結果がまだキャッシュされていることを意味します。 docs
内のlint
スクリプトのみが実際に実行されました。これにより、処理が高速化されます。詳細については、キャッシュに関するドキュメントをご覧ください。
5. Turborepoを使ったビルド
build
スクリプトを実行してみましょう。
turbo build
リンティングスクリプトを実行したときと似た出力が表示されるでしょう。apps/docs
とapps/web
のみが、それぞれのpackage.json
でbuild
スクリプトを指定しているので、それらのみが実行されます。
turbo.json
内のbuild
の中を見てください。興味深い設定があります。
{
"pipeline": {
"build": {
"outputs": [".next/**", "!.next/cache/**"]
}
}
}
いくつかのoutputs
が指定されていることに気づくでしょう。出力を宣言すると、turbo
がタスクの実行を完了したときに、指定した出力がキャッシュに保存されることを意味します。
apps/docs
とapps/web
はどちらもNext.jsアプリであり、./.next
フォルダーにビルドを出力します。
試してみましょう。apps/docs/.next
ビルドフォルダーを削除してください。
もう一度build
スクリプトを実行します。気づくでしょう。
FULL TURBO
になりました。ビルドは100ms
未満で完了します。.next
フォルダーが再表示されます!
Turborepoは以前のビルドの結果をキャッシュしました。再度build
コマンドを実行したとき、キャッシュから.next/**
フォルダー全体が復元されました。詳細については、キャッシュ出力に関するドキュメントをご覧ください。
6. 開発スクリプトの実行
次に、dev
を実行してみましょう。
turbo dev
ターミナルにいくつかの情報が表示されるでしょう。
docs:dev
とweb:dev
の2つのスクリプトのみが実行されます。これらは、dev
を指定している唯一の2つのワークスペースです。- 両方の
dev
スクリプトが同時に実行され、ポート3000
と3001
でNext.jsアプリが開始されます。 - ターミナルには、
cache bypass, force executing
が表示されます。
スクリプトを終了して、再度実行してみてください。FULL TURBO
にならないことに気づくでしょう。それはなぜでしょうか?
turbo.json
を見てください。
{
"pipeline": {
"dev": {
"cache": false,
"persistent": true
}
}
}
dev
内で、"cache": false
を指定しました。これは、Turborepoにdev
スクリプトの結果をキャッシュしないように指示していることを意味します。dev
は永続的な開発サーバーを実行し、出力は生成しないため、キャッシュするものはありません。詳細については、キャッシュをオフにするに関するドキュメントをご覧ください。
さらに、"persistent": true
を設定して、これが長時間実行される開発サーバーであることをturboに知らせ、turboが他のタスクが依存しないようにすることを保証できるようにします。persistent
オプションのドキュメントで詳細を読むことができます。
一度に1つのワークスペースでのみdev
を実行する
デフォルトでは、turbo dev
はすべてのワークスペースでdev
を一度に実行します。しかし、1つのワークスペースのみを選択したい場合があります。
これを処理するために、コマンドに--filter
フラグを追加できます。
turbo dev --filter docs
これで、docs:dev
のみが実行されるようになったことに気づくでしょう。ワークスペースのフィルタリングについては、ドキュメントをご覧ください。
まとめ
よくできました!新しいモノレポと、Turborepoがタスクの処理をどのように簡単にするかについて学びました。
次のステップ
- タスクを追加する必要がありますか?パイプラインの使用について学びましょう。
- CIを高速化したいですか?リモートキャッシュを設定します。
- インスピレーションが必要ですか?examples (新しいタブで開きます)のディレクトリをご覧ください。