リポジトリ
ドキュメント
コアコンセプト
タスクの実行

モノレポでのタスクの実行

すべてのモノレポには、ワークスペースタスクの2つの主要な構成要素があります。3つのワークスペースがあり、それぞれに3つのタスクが含まれているモノレポがあると想像してみましょう。

ここでは、apps/webapps/docの両方がpackages/sharedのコードを使用します。実際、それらがビルドされるとき(build経由)、packages/shared最初にビルドされる必要があります

ほとんどのツールは速度を最適化しない

すべてのワークスペースで、すべてのタスクを実行したいとしましょう。yarnのようなツールでは、次のようなスクリプトを実行する可能性があります。

yarn workspaces run lint
yarn workspaces run test
yarn workspaces run build

これは、タスクがこのように実行されることを意味します。

ご覧のとおり、lintはすべてのワークスペースで実行されます。次に、buildが実行され、sharedが最初に実行されます。最後に、testが実行されます。

これは、これらのタスクを実行する最も遅い方法です。各タスクは、開始する前に前のタスクが完了するのを待つ必要があります。これを改善するには、マルチタスクが可能なツールが必要になります。

Turborepo はマルチタスクが可能

Turborepo は、タスク間の依存関係を理解することで、タスクを最大速度でスケジュールできます。

まず、turbo.json内にタスクを宣言します。

{
  "$schema": "https://turbo.dokyumento.jp/schema.json",
  "pipeline": {
    "build": {
      "outputs": [".next/**", "!.next/cache/**", ".svelte-kit/**"],
      // ^build means `build` must be run in dependencies
      // before it can be run in this workspace
      "dependsOn": ["^build"]
    },
    "test": {},
    "lint": {},
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

次に、yarn workspacesスクリプトを次のように置き換えることができます。

- yarn workspaces run lint
- yarn workspaces run test
- yarn workspaces run build
+ turbo run lint test build

これを実行すると、Turborepo は使用可能なすべての CPU で可能な限り多くのタスクをマルチタスクで実行するため、タスクは次のように実行されます。

linttest は、turbo.jsondependsOn が指定されていないため、すぐに実行されます。

shared 内の build タスクが最初に完了し、その後 webdocs のビルドが続きます。

パイプラインの定義

pipeline 設定は、モノレポ内でどのタスクが互いに依存しているかを宣言します。以下は包括的な例です。

{
  "$schema": "https://turbo.dokyumento.jp/schema.json",
  "pipeline": {
    "build": {
      // A workspace's `build` task depends on that workspace's
      // topological dependencies' and devDependencies'
      // `build` tasks  being completed first. The `^` symbol
      // indicates an upstream dependency.
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**", ".svelte-kit/**"]
    },
    "deploy": {
        // A workspace's `deploy` task depends on the `build`,
        // `test`, and `lint` tasks of the same workspace
        // being completed.
        "dependsOn": ["build", "test", "lint"]
    },
    "test": {
      // A workspace's `test` task depends on that workspace's
      // own `build` task being completed first.
      "dependsOn": ["build"],
      // A workspace's `test` task should only be rerun when
      // either a `.tsx` or `.ts` file has changed.
      "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"]
    },
    // A workspace's `lint` task has no dependencies and
    // can be run whenever.
    "lint": {},
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

タスクの設定方法の詳細については、次のセクション「タスクの依存関係」を参照してください。

ルートからのタスク実行

turbo は、モノレポのルートにある package.json ファイルに存在するタスクを実行できます。これらは、キー構文 "//#<task>" を使用して、パイプライン設定に明示的に追加する必要があります。これは、すでに独自のエントリを持つタスクの場合でも同様です。たとえば、パイプラインが "build" タスクを宣言しており、モノレポのルート package.json ファイルで定義されている build スクリプトを turbo run build で含めたい場合は、設定で "//#build": {...} を宣言して、ルートをオプトインする必要があります。逆に、必要なのが "//#my-task": {...} だけの場合は、一般的な "my-task": {...} エントリを定義する必要はありません。

ルートタスク format を定義し、ルートを test にオプトインするサンプルパイプラインは、次のようになります。

{
  "$schema": "https://turbo.dokyumento.jp/schema.json",
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**", ".svelte-kit/**"]
    },
    "test": {
      "dependsOn": ["^build"],
    },
    // This will cause the "test" script to be included when
    // "turbo run test" is run
    "//#test": {
      "dependsOn": [],
    },
    // This will cause the "format" script in the root package.json
    // to be run when "turbo run format" is run. Since the general
    // "format" task is not defined, only the root's "format" script
    // will be run.
    "//#format": {
      "dependsOn": [],
      "outputs": ["dist/**/*"],
      "inputs": ["version.txt"]
    }
  }
}

再帰に関する注意:モノレポのルート package.json で定義されたスクリプトは、多くの場合、turbo 自体を呼び出します。たとえば、build スクリプトは turbo run build である可能性があります。この状況では、turbo run build//#build を含めると、無限再帰が発生します。このため、モノレポのルートから実行されるタスクは、パイプライン設定に //#<task> を含めることによって、明示的にオプトインする必要があります。turbo には、再帰的な状況でエラーを生成するためのベストエフォートチェックがいくつか含まれていますが、再帰を引き起こす turbo の実行をトリガーしないタスクのみをオプトインするのはユーザーの責任です。

段階的な導入

turbo.json でタスクを宣言した後、package.json マニフェストでそれを実装するのはユーザーの責任です。スクリプトは一度にすべて追加することも、一度に1つのワークスペースずつ追加することもできます。Turborepoは、それぞれの package.json マニフェストにタスクが含まれていないワークスペースを適切にスキップします。

たとえば、リポジトリに3つのワークスペースがある場合(上記のワークスペースと同様)

apps/
  web/package.json
  docs/package.json
packages/
  shared/package.json
turbo.json
package.json

ここで、turbo.jsonbuild タスクを宣言しているが、2つの package.json のみがその build タスクを実装している場合

{
  "$schema": "https://turbo.dokyumento.jp/schema.json",
  "pipeline": {
    "build": {
      "outputs": [".next/**", "!.next/cache/**", "dist/**"]
    }
  }
}
turbo run build

turbo のビルドでは、web および docs ワークスペースに対してのみ build スクリプトが実行されます。shared パッケージは引き続きタスクグラフの一部になりますが、適切にスキップされます。

Turborepo のパイプライン API 設計とこのドキュメントのページは、Microsoft の Lage プロジェクト (新しいタブで開きます) に着想を得ています。このような簡潔でエレガントな方法でタスクを展開するというアイデアをくれた Kenneth Chau (新しいタブで開きます) に感謝します。