软件包配置

许多 monorepos 可以在根目录中声明一个 turbo.json,并在所有包中声明一个 任务描述。但是,有时,monorepo 可能包含需要以不同方式配置其任务的包。

¥Many monorepos can declare a turbo.json in the root directory with a task description that applies to all packages. But, sometimes, a monorepo can contain packages that need to configure their tasks differently.

为了实现这一点,Turborepo 允许你在任何包中使用 turbo.json 扩展根配置。这种灵活性使得更多样化的应用和软件包能够在工作区中共存,并允许软件包所有者维护专门的任务和配置,而不会影响 monorepo 的其他应用和软件包。

¥To accommodate this, Turborepo enables you to extend the root configuration with a turbo.json in any package. This flexibility enables a more diverse set of apps and packages to co-exist in a Workspace, and allows package owners to maintain specialized tasks and configuration without affecting other apps and packages of the monorepo.

工作原理

¥How it works

要覆盖根 turbo.json 中定义的任何任务的配置,请在 monorepo 的任何软件包中添加一个带有顶层 extends 键的 turbo.json 文件:

¥To override the configuration for any task defined in the root turbo.json, add a turbo.json file in any package of your monorepo with a top-level extends key:

Turborepo logo
./apps/my-app/turbo.json
{
  "extends": ["//"],
  "tasks": {
    "build": {
      // Custom configuration for the build task in this package
    },
    "special-task": {} // New task specific to this package
  }
}

extends 数组必须以 ["//"] 开头。// 是一个用于标识 monorepo 根目录的特殊名称。你还可以通过在 // 之后添加其他包(例如 ["//", "shared-config"])来扩展它们。

¥The extends array must start with ["//"]. // is a special name used to identify the root directory of the monorepo. You can also extend from other packages by adding them after // (e.g., ["//", "shared-config"]).

继承行为

¥Inheritance behavior

当一个包配置继承自根 turbo.json 时,任务属性的继承方式取决于其类型。

¥When a Package Configuration extends the root turbo.json, task properties are inherited differently depending on their type.

标量字段会被继承

¥Scalar fields are inherited

标量字段(例如 outputLogscachepersistentinteractive)继承自根配置。只有当你想要覆盖它们时,才需要在包配置中指定它们。

¥Scalar fields like outputLogs, cache, persistent, and interactive are inherited from the root configuration. You only need to specify them in a Package Configuration if you want to override them.

例如,如果你的根 turbo.json 为某个任务设置了 "outputLogs": "hash-only",则所有包都会自动继承该设置。

¥For example, if your root turbo.json sets "outputLogs": "hash-only" for a task, all packages inherit that setting automatically.

数组字段默认替换

¥Array fields replace by default

默认情况下,数组字段(例如 outputsenvinputsdependsOnpassThroughEnv)会完全替换根配置的值。

¥Array fields like outputs, env, inputs, dependsOn, and passThroughEnv completely replace the root configuration's values by default.

Turborepo logo
./turbo.json
{
  "tasks": {
    "build": {
      "outputs": ["dist/**"],
      "env": ["NODE_ENV"]
    }
  }
}
Turborepo logo
./apps/my-app/turbo.json
{
  "extends": ["//"],
  "tasks": {
    "build": {
      // This REPLACES the root outputs - "dist/**" is NOT included
      "outputs": [".next/**"]
    }
  }
}

使用 $TURBO_EXTENDS$ 扩展数组

¥Extending arrays with $TURBO_EXTENDS$

要向继承的数组值添加内容而不是替换它们,请使用 $TURBO_EXTENDS$ 微语法

¥To add to inherited array values instead of replacing them, use the $TURBO_EXTENDS$ microsyntax:

Turborepo logo
./apps/my-app/turbo.json
{
  "extends": ["//"],
  "tasks": {
    "build": {
      // Inherits "dist/**" from root AND adds ".next/**"
      "outputs": ["$TURBO_EXTENDS$", ".next/**"]
    }
  }
}

$TURBO_EXTENDS$ 标记必须是数组中的第一个元素。它适用于 outputsenvinputsdependsOnpassThroughEnvwith

¥The $TURBO_EXTENDS$ marker must be the first element in the array. It works with outputs, env, inputs, dependsOn, passThroughEnv, and with.

从其他包扩展

¥Extending from other packages

包配置可以继承自其他包的 turbo.json 文件,而不仅仅是根包。这允许跨包组合共享任务配置。

¥Package Configurations can extend from other packages' turbo.json files, not just the root. This enables composing shared task configurations across packages.

通过在 extends 数组中使用其 package.json 中的 name 来继承任何包。例如,如果你有一个位于 ./apps/web 的 Next.js 应用,其 package.json 中包含 "name": "web"

¥Extend from any package by using its name from package.json in your extends array. For example, if you have a Next.js app at ./apps/web with "name": "web" in its package.json:

Turborepo logo
./apps/web/turbo.json
{
  "extends": ["//"],
  "tasks": {
    "build": {
      "outputs": [".next/**", "!.next/cache/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

其他 Next.js 应用可以继承此配置并共享相同的配置:

¥Another Next.js app can extend from it to share the same configuration:

Turborepo logo
./apps/docs/turbo.json
{
  "extends": ["//", "web"],
  "tasks": {
    "build": {
      // Additional customization specific to this package
      "env": ["NEXT_PUBLIC_DOCS_URL"]
    }
  }
}

当继承自多个配置时,根 ("//") 必须始终在 extends 数组中列在第一位。

¥When extending from multiple configurations, the root ("//") must always be listed first in the extends array.

继承顺序

¥Inheritance order

当继承自多个配置时,任务定义会按照它们在 extends 数组中出现的顺序合并:

¥When extending from multiple configurations, task definitions are merged in the order they appear in the extends array:

  1. 根包的 turbo.json ("//") 首先应用。

    ¥Root turbo.json ("//") is applied first

  2. 每个附加包配置按顺序应用。

    ¥Each additional package configuration is applied in order

  3. 当前包的配置最后应用。

    ¥The current package's configuration is applied last

对于标量字段,后续配置会覆盖早期配置。对于数组字段,请参阅“追加而不是替换 使用 $TURBO_EXTENDS$ 扩展数组”。

¥Later configurations override earlier ones for scalar fields. For array fields, see Extending arrays with $TURBO_EXTENDS$ to append instead of replace.

共享配置的模式

¥Patterns for sharing configuration

从现有包继承:如果你已经有一个包含要共享的配置的包,则其他包可以直接继承该配置。当一个包作为类似包的 "canonical" 示例时(例如,其他 Next.js 应用可以继承的主 Next.js 应用),此方法非常有效。

¥Extend from an existing package: If you already have a package with the configuration you want to share, other packages can extend from it directly. This works well when one package serves as the "canonical" example for similar packages (e.g., your main Next.js app that other Next.js apps can extend from).

创建专用配置包:对于较大的单体仓库,你可能需要创建专门用于共享配置的包。这使配置与应用代码分离,并明确表明其他包依赖于这些设置。这些包通常只包含 package.jsonturbo.json

¥Create a dedicated configuration package: For larger monorepos, you may want to create packages specifically for sharing configuration. This keeps configuration separate from application code and makes it clear that other packages depend on these settings. These packages typically only contain a package.json and turbo.json.

./packages/shared-config/package.json
{
  "name": "shared-config",
  "private": true
}

从继承中排除任务

¥Excluding tasks from inheritance

当继承自根或其他包时,你的包默认会继承它们的所有任务定义。你可以使用任务级别的 extends 字段来选择退出特定任务。

¥When extending from the root or other packages, your package inherits all their task definitions by default. You can use the task-level extends field to opt out of specific tasks.

完全排除任务

¥Excluding a task entirely

要从包中完全排除继承的任务,请设置 extends: false 而不进行任何其他配置:

¥To completely exclude an inherited task from your package, set extends: false with no other configuration:

Turborepo logo
./turbo.json
{
  "tasks": {
    "build": {},
    "lint": {},
    "test": {}
  }
}
Turborepo logo
./packages/ui/turbo.json
{
  "extends": ["//"],
  "tasks": {
    "lint": {
      "extends": false // This package does not have a lint task
    }
  }
}

运行 turbo run lint 时,对于 lint 任务,将完全跳过 ui 包。

¥When you run turbo run lint, the ui package will be skipped entirely for the lint task.

创建新的任务定义

¥Creating a fresh task definition

要创建不从 extends 链继承任何配置的新任务定义,请将 extends: false 与其他任务配置一起使用:

¥To create a new task definition that doesn't inherit any configuration from the extends chain, use extends: false along with other task configuration:

Turborepo logo
./packages/special-app/turbo.json
{
  "extends": ["//"],
  "tasks": {
    "build": {
      "extends": false, // Don't inherit from root
      "outputs": ["out/**"],
      "env": ["SPECIAL_VAR"]
    }
  }
}

当你需要完全不同的任务配置,且这些配置不应与继承的值合并时,此功能非常有用。

¥This is useful when you need completely different task configuration that shouldn't merge with inherited values.

排除项会沿着链传播

¥Exclusions propagate through the chain

当一个包排除某个任务时,该排除会传播到继承自它的包:

¥When a package excludes a task, that exclusion propagates to packages that extend from it:

Turborepo logo
./packages/base-config/turbo.json
{
  "extends": ["//"],
  "tasks": {
    "lint": {
      "extends": false // Exclude lint from this config
    }
  }
}
Turborepo logo
./apps/web/turbo.json
{
  "extends": ["//", "base-config"],
  "tasks": {
    // web does not inherit lint from root because base-config excluded it
  }
}

任务级别的 extends 仅在包配置中可用。在根目录 turbo.json 中的任务上使用 extends 将导致验证错误。

¥Task-level extends is only available in Package Configurations. Using extends on a task in the root turbo.json will result in a validation error.

示例

¥Examples

在一个应用中使用不同的框架工作区

¥Different frameworks in one Workspace

假设你的 monorepo 包含多个 Next.js 应用和一个 SvelteKit 应用。这两个框架都在各自的 package.json 中使用 build 脚本创建构建输出。你可以像这样配置 Turborepo,使其在根目录下使用单个 turbo.json 任务来运行这些任务:

¥Let's say your monorepo has multiple Next.js apps, and one SvelteKit app. Both frameworks create their build output with a build script in their respective package.json. You could configure Turborepo to run these tasks with a single turbo.json at the root like this:

Turborepo logo
./turbo.json
{
  "tasks": {
    "build": {
      "outputs": [".next/**", "!.next/cache/**", ".svelte-kit/**"]
    }
  }
}

请注意,即使 Next.js 应用不会生成 .svelte-kit 目录,.next/**.svelte-kit/** 都需要指定为 outputs,反之亦然。

¥Notice that both .next/** and .svelte-kit/** need to be specified as outputs, even though Next.js apps do not generate a .svelte-kit directory, and vice versa.

使用包配置,你可以在 apps/my-svelte-kit-app/turbo.json 中的 SvelteKit 包中添加自定义配置:

¥With Package Configurations, you can instead add custom configuration in the SvelteKit package in apps/my-svelte-kit-app/turbo.json:

Turborepo logo
./apps/my-svelte-kit-app/turbo.json
{
  "extends": ["//"],
  "tasks": {
    "build": {
      "outputs": [".svelte-kit/**"]
    }
  }
}

并从根配置中移除 SvelteKit 特定的 outputs

¥and remove the SvelteKit-specific outputs from the root configuration:

Turborepo logo
./turbo.json
{
  "tasks": {
    "build": {
-      "outputs": [".next/**", "!.next/cache/**", ".svelte-kit/**"]
+      "outputs": [".next/**", "!.next/cache/**"]
    }
  }
}

这不仅使每个配置更易于阅读,还使配置更接近其使用位置。

¥This not only makes each configuration easier to read, it puts the configuration closer to where it is used.

专门的任务

¥Specialized tasks

再举一个例子,假设一个软件包 dependsOn 中的 build 任务调用了一个 compile 任务。你可以将其声明为 dependsOn: ["compile"]。这意味着你的根 turbo.json 必须有一个空的 compile 任务条目:

¥In another example, say that the build task in one package dependsOn a compile task. You could universally declare it as dependsOn: ["compile"]. This means that your root turbo.json has to have an empty compile task entry:

Turborepo logo
./turbo.json
{
  "tasks": {
    "build": {
      "dependsOn": ["compile"]
    },
    "compile": {}
  }
}

使用包配置,你可以将该 compile 任务移动到 apps/my-custom-app/turbo.json

¥With Package Configurations, you can move that compile task into the apps/my-custom-app/turbo.json,

Turborepo logo
./apps/my-app/turbo.json
{
  "extends": ["//"],
  "tasks": {
    "build": {
      "dependsOn": ["compile"]
    },
    "compile": {}
  }
}

并将其从根目录中移除:

¥and remove it from the root:

Turborepo logo
./turbo.json
{
  "tasks": {
+    "build": {}
-    "build": {
-      "dependsOn": ["compile"]
-    },
-    "compile": {}
  }
}

现在,my-app 的所有者可以完全拥有其 build 任务的所有权,但可以继续继承根目录中定义的任何其他任务。

¥Now, the owners of my-app, can have full ownership over their build task, but continue to inherit any other tasks defined at the root.

与特定于软件包的任务的比较

¥Comparison to package-specific tasks

根包 turbo.json 中的 package#task 语法 会完全覆盖所有任务配置 - 不会继承任何配置。

¥The package#task syntax in the root turbo.json completely overwrites all task configuration—nothing is inherited.

对于包配置,标量字段会被继承,只有你指定的字段会被覆盖。这意味着当你只需要更改一两个属性时,可以减少重复代码。

¥With Package Configurations, scalar fields are inherited and only the fields you specify are overridden. This means less duplication when you only need to change one or two properties.

虽然没有计划移除特定于包的任务配置,但我们预计在大多数情况下可以使用包配置。

¥Although there are no plans to remove package-specific task configurations, we expect that Package Configurations can be used for most use cases instead.

Experimental 边界标签

¥Boundaries Tags Experimental

软件包配置也用于声明边界标签。为此,请在你的 turbo.json 中添加一个 tags 字段:

¥Package Configurations are also used to declare Tags for Boundaries. To do so, add a tags field to your turbo.json:

Turborepo logo
./apps/my-app/turbo.json
{
+ "tags": ["my-tag"],
  "extends": ["//"],
  "tasks": {
    "build": {
      "dependsOn": ["compile"]
    },
    "compile": {}
  }
}

从这里,你可以定义标签可以包含哪些依赖或被依赖的规则。查看 边界文档 了解更多详细信息。

¥From there, you can define rules for which dependencies or dependents a tag can have. Check out the Boundaries documentation for more details.

限制

¥Limitations

虽然总体思路与根 turbo.json 相同,但软件包配置附带一组防护措施,可以防止软件包造成潜在的混乱情况。

¥Although the general idea is the same as the root turbo.json, Package Configurations come with a set of guardrails that can prevent packages from creating potentially confusing situations.

软件包配置不能使用 workspace#task 语法 作为任务条目

¥Package Configurations cannot use the workspace#task syntax as task entries

package 是根据配置的位置推断出来的,并且无法更改其他包的配置。例如,在 my-nextjs-app 的包配置中:

¥The package is inferred based on the location of the configuration, and it is not possible to change configuration for another package. For example, in a Package Configuration for my-nextjs-app:

Turborepo logo
./apps/my-nextjs-app/turbo.json
{
  "tasks": {
    "my-nextjs-app#build": {
      // This is not allowed. Even though it's
      // referencing the correct package, "my-nextjs-app"
      // is inferred, and we don't need to specify it again.
      // This syntax also has different behavior, so we do not want to allow it.
      // (see "Comparison to package-specific tasks" section)
    },
    "my-sveltekit-app#build": {
      // Changing configuration for the "my-sveltekit-app" package
      // from Package Configuration in "my-nextjs-app" is not allowed.
    },
    "build": {
      // Just use the task name!
    }
  }
}

请注意,build 任务仍然可以依赖于特定于包的任务:

¥Note that the build task can still depend on a package-specific task:

Turborepo logo
./apps/my-nextjs-app/turbo.json
{
  "tasks": {
    "build": {
      "dependsOn": ["some-pkg#compile"] 
    }
  }
}

软件包配置只能覆盖 tasks 键中的值

¥Package Configurations can only override values in the tasks key

无法在包配置中像 globalEnvglobalDependencies 那样覆盖 全局配置。需要在包配置中更改的配置并非真正全局的,应该进行不同的配置。

¥It is not possible to override global configuration like globalEnv or globalDependencies in a Package Configuration. Configuration that would need to be altered in a Package Configuration is not truly global and should be configured differently.

Root turbo.json 无法使用 extends

¥Root turbo.json cannot use the extends key

为避免在包上创建循环依赖,根 turbo.json 不能从任何包扩展。extends 键将被忽略。

¥To avoid creating circular dependencies on packages, the root turbo.json cannot extend from anything. The extends key will be ignored.

故障排除

¥Troubleshooting

在大型单体仓库中,有时可能很难理解 Turborepo 如何解释你的配置。为了提供帮助,我们在 试运行 输出中添加了 resolvedTaskDefinition。例如,如果你运行 turbo run build --dry-run,输出将包含在运行 build 任务之前考虑的所有 turbo.json 配置的组合。

¥In large monorepos, it can sometimes be difficult to understand how Turborepo is interpreting your configuration. To help, we've added a resolvedTaskDefinition to the Dry Run output. If you run turbo run build --dry-run, for example, the output will include the combination of all turbo.json configurations that were considered before running the build task.