软件包配置

许多 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 根目录的特殊名称。

¥For now, the only valid value for the extends key is ["//"]. // is a special name used to identify the root directory of the monorepo.

包中的配置可以覆盖任何 任务配置。任何未包含的键都将从扩展的 turbo.json 继承。

¥Configuration in a package can override any of the configurations for a task. Any keys that are not included are inherited from the extended turbo.json.

示例

¥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 语法。它们的功能相似,但有一个显著的区别:当你在根 turbo.json 中声明特定于包的任务时,它将完全覆盖基线任务配置。使用软件包配置,任务配置将被合并。

¥At first glance, Package Configurations may sound a lot like the package#task syntax in the root turbo.json. The features are similar, but have one significant difference: when you declare a package-specific task in the root turbo.json, it completely overwrites the baseline task configuration. With a Package Configuration, the task configuration is merged instead.

再次考虑包含多个 Next.js 应用和一个 Sveltekit 应用的 monorepo 示例。使用特定于软件包的任务,你可以像这样配置你的根 turbo.json

¥Consider the example of the monorepo with multiple Next.js apps and a Sveltekit app again. With a package-specific task, you might configure your root turbo.json like this:

Turborepo logo
./turbo.json
{
  "tasks": {
    "build": {
      "outputLogs": "hash-only",
      "inputs": ["src/**"],
      "outputs": [".next/**", "!.next/cache/**"]
    },
    "my-sveltekit-app#build": {
      "outputLogs": "hash-only", // must duplicate this
      "inputs": ["src/**"], // must duplicate this
      "outputs": [".svelte-kit/**"]
    }
  }
}

在此示例中,对于 Sveltekit 应用,my-sveltekit-app#build 完全覆盖了 build,因此 outputLogsinputs 也需要复制。

¥In this example, my-sveltekit-app#build completely overwrites build for the Sveltekit app, so outputLogs and inputs also need to be duplicated.

使用包配置,outputLogsinputs 是继承的,因此你无需复制它们。你只需在 my-sveltekit-app 配置中覆盖 outputs

¥With Package Configurations, outputLogs and inputs are inherited, so you don't need to duplicate them. You only need to override outputs in my-sveltekit-app config.

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

¥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.