Package Configurations
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.
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
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:
For now, the only valid value for the extends
key is ["//"]
. //
is a
special name used to identify the root directory of the monorepo.
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
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:
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.
With Package Configurations, you can instead add custom
configuration in the SvelteKit package in apps/my-svelte-kit-app/turbo.json
:
and remove the SvelteKit-specific outputs
from the root configuration:
This not only makes each configuration easier to read, it puts the configuration closer to where it is used.
Specialized tasks
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:
With Package Configurations, you can move that compile
task into the
apps/my-custom-app/turbo.json
,
and remove it from the root:
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
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.
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:
In this example, my-sveltekit-app#build
completely overwrites build
for the
Sveltekit app, so outputLogs
and inputs
also need to be duplicated.
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.
Limitations
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.
Package Configurations cannot use the workspace#task
syntax as task entries
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
:
Note that the build
task can still depend on a package-specific task:
Package Configurations can only override values in the tasks
key
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 cannot use the extends
key
To avoid creating circular dependencies on packages, the root turbo.json
cannot extend from anything. The extends
key will be ignored.
Troubleshooting
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.
Was this helpful?