儲存庫
文件
核心概念
執行工作

在單一儲存庫中執行任務

每個單一儲存庫都有兩個主要的建構區塊:工作區任務。想像一下,你有一個包含三個工作區的單一儲存庫,每個工作區有三個任務

這裡,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.dev.org.tw/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 上多工執行儘可能多的任務,表示我們的任務會像這樣執行

由於 linttestturbo.json 中未指定 dependsOn,因此會立即執行。

shared 內部的 build 任務會先完成,然後 webdocs 會在之後進行建置。

定義管線

pipeline 設定會宣告您的單一儲存庫中哪些任務彼此依賴。以下是一個範例

{
  "$schema": "https://turbo.dev.org.tw/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 can run tasks that exist in the package.json file at the root of the monorepo. These must be explicitly added to the pipeline configuration using the key syntax "//#<task>". This is true even for tasks that already have their own entry. For example, if your pipeline declares a "build" task, and you want to include the build script defined in the monorepo's root package.json file with turbo run build, you must opt the root into it by declaring "//#build": {...} in your configuration. Conversely, you do not need to define a generic "my-task": {...} entry if all you need is "//#my-task": {...}.

定義根目錄任務 format,並將根目錄選入 test 的範例管線如下

{
  "$schema": "https://turbo.dev.org.tw/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"]
    }
  }
}

關於遞迴的注意事項:定義在 monorepo 根目錄 package.json 中的腳本通常會呼叫 turbo 本身。例如,build 腳本可能是 turbo run build。在這種情況下,在 turbo run build 中包含 //#build 將導致無限遞迴。正因如此,從 monorepo 根目錄執行的任務必須透過在管道設定中包含 //#<task> 來明確選擇加入。 turbo 包含一些盡力而為的檢查,以便在遞迴情況下產生錯誤,但由您決定只選擇那些本身不會觸發遞迴的 turbo 執行的任務。

逐步採用

turbo.json 中宣告任務後,您必須在 package.json 清單中實作它。您可以一次新增所有腳本,或一次新增一個工作區。Turborepo 會優雅地略過未在其各自的 package.json 清單中包含該任務的工作區。

例如,如果您的儲存庫有三個工作區(類似於上面提到的那些)

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

其中 turbo.json 宣告了一個 build 任務,但只有兩個 package.json 實作了那個 build 任務

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

Turbo build 只會執行 build 指令碼給 webdocs 工作區。 shared 套件仍然會是任務圖表的一部分,但會優雅地略過。

Turborepo 的 Pipeline API 設計和此頁面文件是受到 Microsoft 的 Lage 專案(在新分頁中開啟) 的啟發。感謝 Kenneth Chau(在新分頁中開啟) 提出以如此簡潔優雅的方式展開任務的想法。