リポジトリ
ドキュメント
はじめに
新しいモノレポを作成する

新しいモノレポの作成

このガイドでは、グローバルインストールされた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 の中を見ると、同じことがわかります。webdocs の両方が @repo/ui に依存しています。これは共有コンポーネントライブラリです。

アプリケーション間でコードを共有するこのパターンは、モノレポでは非常に一般的であり、複数のアプリが単一のデザインシステムを共有できることを意味します。

インポートとエクスポートの理解

./apps/docs/app/page.tsx の中を見てください。docsweb はどちらも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) など。

これは、webdocs でインポートできるようになります。

tsconfig の理解

さらに、typescript-configeslint-config の 2 つのワークスペースを見てみましょう。これらの各ワークスペースでは、モノレポ全体で構成を共有できます。typescript-config の中を見てみましょう

{
  "name": "@repo/typescript-config",
}

ここで、パッケージの名前が @repo/typescript-config であることがわかります。

次に、web アプリにある tsconfig.json ファイルの中を見てみましょう。

{
  "extends": "@repo/typescript-config/nextjs.json",
}

ご覧のとおり、@repo/typescript-config/nextjs.jsontsconfig.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.jsnext.jsreact-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 - uitypescript-config、および eslint-config に依存します
  • docs - uitypescript-config、および eslint-config に依存します
  • ui - typescript-config および eslint-config に依存します
  • typescript-config - 依存関係はありません
  • eslint-config - 依存関係はありません

Turborepo CLI はこれらの依存関係を管理する責任がないことに注意してください。上記のすべてのことは、選択したパッケージ マネージャー (npmpnpm、または 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 つのタスク (lintdevbuild) を登録したということです。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

ターミナルでいくつかのことが発生します。

  1. いくつかのスクリプトが同時に実行され、それぞれに docs:lint@repo/ui:lint、または web:lint のいずれかのプレフィックスが付いています。
  2. それぞれが成功し、ターミナルに 3 successful と表示されます。
  3. また、0 cached, 3 total と表示されます。これが何を意味するのかは後で説明します。

実行される各スクリプトは、各ワークスペースの package.json から取得されます。各ワークスペースは、独自の lint スクリプトをオプションで指定できます。

{
  "scripts": {
    "lint": "next lint"
  }
}
{
  "scripts": {
    "lint": "next lint"
  }
}
{
  "scripts": {
    "lint": "eslint \"**/*.ts*\""
  }
}

turbo lint を実行すると、Turborepo は各ワークスペースの各 lint スクリプトを確認し、実行します。詳細については、パイプラインに関するドキュメントを参照してください。

キャッシュの使用

lint スクリプトをもう一度実行してみましょう。ターミナルにいくつかの新しいものが表示されます。

  1. cache hit, replaying logsdocs:lintweb:lint@repo/ui:lint に対して表示されます。
  2. 3 cached, 3 total と表示されます。
  3. 合計実行時間は 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 スクリプトを再度実行します。次のことに気づくでしょう。

  1. docs:lintには、cache miss, executingというコメントがあります。これは、docsがリンティングを実行していることを意味します。
  2. 2 cached, 3 totalが下部に表示されます。

これは、以前のタスクの結果がまだキャッシュされていることを意味します。 docs内のlintスクリプトのみが実際に実行されました。これにより、処理が高速化されます。詳細については、キャッシュに関するドキュメントをご覧ください。

5. Turborepoを使ったビルド

buildスクリプトを実行してみましょう。

turbo build

リンティングスクリプトを実行したときと似た出力が表示されるでしょう。apps/docsapps/webのみが、それぞれのpackage.jsonbuildスクリプトを指定しているので、それらのみが実行されます。

turbo.json内のbuildの中を見てください。興味深い設定があります。

{
  "pipeline": {
    "build": {
      "outputs": [".next/**", "!.next/cache/**"]
    }
  }
}

いくつかのoutputsが指定されていることに気づくでしょう。出力を宣言すると、turboがタスクの実行を完了したときに、指定した出力がキャッシュに保存されることを意味します。

apps/docsapps/webはどちらもNext.jsアプリであり、./.nextフォルダーにビルドを出力します。

試してみましょう。apps/docs/.nextビルドフォルダーを削除してください。

もう一度buildスクリプトを実行します。気づくでしょう。

  1. FULL TURBOになりました。ビルドは100ms未満で完了します。
  2. .nextフォルダーが再表示されます!

Turborepoは以前のビルドの結果をキャッシュしました。再度buildコマンドを実行したとき、キャッシュから.next/**フォルダー全体が復元されました。詳細については、キャッシュ出力に関するドキュメントをご覧ください。

6. 開発スクリプトの実行

次に、devを実行してみましょう。

turbo dev

ターミナルにいくつかの情報が表示されるでしょう。

  1. docs:devweb:devの2つのスクリプトのみが実行されます。これらは、devを指定している唯一の2つのワークスペースです。
  2. 両方のdevスクリプトが同時に実行され、ポート30003001でNext.jsアプリが開始されます。
  3. ターミナルには、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がタスクの処理をどのように簡単にするかについて学びました。

次のステップ