# Turbo
> title="Set up Biome in a Turborepo"
---
# Source: https://turbo.build/guides/tools/biome.md
# Biome
[Biome](https://biomejs.dev/) is a fast formatter for JavaScript, TypeScript, JSX, and JSON that saves CI and developer time.
## Using Biome with Turborepo
Biome is a rare exception to most tools that are used with Turborepo because it is **so extraordinarily fast**. For this reason, we recommend using a [Root Task](/docs/crafting-your-repository/configuring-tasks#registering-root-tasks) rather than creating separate scripts in each of your packages.
Using Biome at the root of the project will result in cache misses for all
tasks when you upgrade Biome versions or change configuration. If you prefer
the tradeoff of higher cache hit ratios in these situations over less
configuration, you can still use Biome in separate scripts like the other
recommendations in our guides.
### Initialize Biome
First, [follow the installation documentation to set up Biome](https://biomejs.dev/guides/getting-started/) in your repository. You'll then be able to create a script to use Biome in the root of your repository:
```json title="./package.json"
{
"scripts": {
"format-and-lint": "biome check .",
"format-and-lint:fix": "biome check . --write"
}
}
```
### Create a root task
In practice, Biome is unlikely to be a bottleneck in the iteration speed of your repository. For this reason, we can have less configuration to manage in our repository by using Biome in a [Root Task](/docs/crafting-your-repository/configuring-tasks#registering-root-tasks).
If you believe Biome may be faster in your repository split up into tasks in
packages, you are free to do so. We encourage you to experiment with what's
best for your use case.
To create a [Root Task](/docs/crafting-your-repository/configuring-tasks#registering-root-tasks), register the scripts to Turborepo:
```json title="./turbo.json"
{
"tasks": {
"//#format-and-lint": {},
"//#format-and-lint:fix": {
"cache": false
}
}
}
```
You'll now be able to run these scripts using `turbo run format-and-lint` and `turbo run format-and-lint:fix`.
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/guides/ci-vendors/buildkite.md
# Buildkite
The following example shows how to use Turborepo with [Buildkite](https://buildkite.com/).
For a given root `package.json`:
```json title="./package.json"
{
"name": "my-turborepo",
"scripts": {
"build": "turbo run build",
"test": "turbo run test"
},
"devDependencies": {
"turbo": "latest"
}
}
```
And a `turbo.json`:
```json title="./turbo.json"
{
"$schema": "https://turborepo.dev/schema.json",
"tasks": {
"build": {
"outputs": [".next/**", "!.next/cache/**"],
"dependsOn": ["^build"]
},
"test": {
"dependsOn": ["^build"]
}
}
}
```
Create a file called `.buildkite/pipeline.yml` in your repository with the following contents:
```yaml title=".buildkite/pipeline.yml"
steps:
- label: ":test_tube: Test"
command: |
pnpm install
pnpm test
- label: ":hammer: Build"
command: |
pnpm install
pnpm build
```
```yaml title=".buildkite/pipeline.yml"
steps:
- label: ":test_tube: Test"
command: |
yarn
yarn test
- label: ":hammer: Build"
command: |
yarn
yarn build
```
```yaml title=".buildkite/pipeline.yml"
steps:
- label: ":test_tube: Test"
command: |
npm install
npm test
- label: ":hammer: Build"
command: |
npm install
npm run build
```
```yaml title=".buildkite/pipeline.yml"
steps:
- label: ":test_tube: Test"
command: |
bun install
bun run test
- label: ":hammer: Build"
command: |
bun install
bun run build
```
## Create a Pipeline
To create your pipeline in the Buildkite dashboard, you'll need to first upload the pipeline definition from your repository.
1. Select **Pipelines** to navigate to the Buildkite dashboard.
2. Select **New pipeline**.
3. Enter your pipeline's details in the respective **Name** and **Description** fields.
4. In the **Steps** editor, ensure there's a step to upload the definition from your repository:
```yaml title=".buildkite/pipeline.yml"
steps:
- label: ":pipeline:"
command: buildkite-agent pipeline upload
```
5. Select **Create Pipeline**, then click **New Build**, then select **Create Build**.
Run the pipeline whenever you make changes you want to verify.
## Remote Caching
To use Remote Caching, retrieve the team and token for the Remote Cache for your provider. In this example, we'll use [Vercel Remote Cache](https://vercel.com/docs/monorepos/remote-caching):
* `TURBO_TOKEN` - The Bearer token to access the Remote Cache
* `TURBO_TEAM` - The account to which the monorepo belongs
To use Vercel Remote Caching, you can get the value of these variables in a few steps:
1. Create a Scoped Access Token to your account in the [Vercel Dashboard](https://vercel.com/account/tokens). Copy the value to a safe place. You'll need it in a moment.
2. Obtain [your Team URL](https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fsettings\&title=Find+Team+URL) and copy its value as well. Both values will be used in the next step.
3. In the Buildkite dashboard, create two new [Buildkite secrets](https://buildkite.com/docs/pipelines/security/secrets/buildkite-secrets), one for each value. Name them `TURBO_TOKEN` and `TURBO_TEAM`.
4. Update `pipeline.yml` to fetch and apply `TURBO_TOKEN` and `TURBO_TEAM` as environment variables with the [Buildkite Secrets](https://github.com/buildkite-plugins/secrets-buildkite-plugin) plugin as shown. (For additional secret-management options, read [Managing pipeline secrets](https://buildkite.com/docs/pipelines/security/secrets/managing) in the Buildkite documentation.)
```yaml title=".buildkite/pipeline.yml"
steps:
- label: ":test_tube: Test"
command: |
npm install
npm test
plugins:
- secrets:
variables:
TURBO_TOKEN: TURBO_TOKEN
TURBO_TEAM: TURBO_TEAM
- label: ":hammer: Build"
command: |
npm install
npm run build
plugins:
- secrets:
variables:
TURBO_TOKEN: TURBO_TOKEN
TURBO_TEAM: TURBO_TEAM
```
Commit and push these changes to your repository, and on the next pipeline run, the secrets will be applied and Vercel Remote Caching will be active.
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/guides/ci-vendors/circleci.md
# CircleCI
The following example shows how to use Turborepo with [CircleCI](https://circleci.com/).
CircleCI [uses interactive terminals
(TTY)](https://discuss.circleci.com/t/reprise-allow-for-using-circle-ci-tooling-without-a-tty/23309)
that crash Turborepo's terminal UI. To workaround this, set the
`TURBO_UI=false` environment variable in your CircleCI configuration.
For a given root `package.json`:
```json title="./package.json"
{
"name": "my-turborepo",
"scripts": {
"build": "turbo run build",
"test": "turbo run test"
},
"devDependencies": {
"turbo": "latest"
}
}
```
And a `turbo.json`:
```json title="./turbo.json"
{
"$schema": "https://turborepo.dev/schema.json",
"tasks": {
"build": {
"outputs": [".next/**", "!.next/cache/**"],
"dependsOn": ["^build"]
},
"test": {
"dependsOn": ["^build"]
}
}
}
```
Create a file called `.circleci/config.yml` in your repository with the following contents:
```yaml title=".circleci/config.yml"
version: 2.1
orbs:
node: circleci/node@5.0.2
workflows:
test:
jobs:
- test
jobs:
test:
docker:
- image: cimg/node:lts
steps:
- checkout
- node/install-packages
- run:
command: npm i -g pnpm
environment:
TURBO_UI: "false"
- run:
command: pnpm build
environment:
TURBO_UI: "false"
- run:
command: pnpm test
environment:
TURBO_UI: "false"
```
```yaml title=".circleci/config.yml"
version: 2.1
orbs:
node: circleci/node@5.0.2
workflows:
test:
jobs:
- test
jobs:
test:
docker:
- image: cimg/node:lts
steps:
- checkout
- node/install-packages:
pkg-manager: yarn
- run:
command: yarn build
environment:
TURBO_UI: "false"
- run:
command: yarn test
environment:
TURBO_UI: "false"
```
```yaml title=".circleci/config.yml"
version: 2.1
orbs:
node: circleci/node@5.0.2
workflows:
test:
jobs:
- test
jobs:
test:
docker:
- image: cimg/node:lts
steps:
- checkout
- node/install-packages
- run:
command: npm run build
environment:
TURBO_UI: "false"
- run:
command: npm run test
environment:
TURBO_UI: "false"
```
```yaml title=".circleci/config.yml"
version: 2.1
orbs:
node: circleci/node@5.0.2
workflows:
test:
jobs:
- test
jobs:
test:
docker:
- image: cimg/node:lts
steps:
- checkout
- node/install-packages
- run:
command: npm i -g bun
environment:
TURBO_UI: "false"
- run:
command: bun run build
environment:
TURBO_UI: "false"
- run:
command: bun run test
environment:
TURBO_UI: "false"
```
## Remote Caching
To use Remote Caching, retrieve the team and token for the Remote Cache for your provider. In this example, we'll use [Vercel Remote Cache](https://vercel.com/docs/monorepos/remote-caching):
* `TURBO_TOKEN` - The Bearer token to access the Remote Cache
* `TURBO_TEAM` - The slug of the Vercel team to share the artifacts with
To use Vercel Remote Caching, you can get the value of these variables in a few steps:
1. Create a Scoped Access Token to your account in the [Vercel Dashboard](https://vercel.com/account/tokens)
Copy the value to a safe place. You'll need it in a moment.
2. Go to your CircleCI project settings and click on the **Environment Variables** tab. Create a new secret called `TURBO_TOKEN` and enter the value of your Scoped Access Token.
3. Make a second secret called `TURBO_TEAM` and set it to your team slug - the part after `vercel.com/` in [your Team URL](https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fsettings\&title=Find+Team+URL). For example, the slug for `vercel.com/acme` is `acme`.
4. CircleCI automatically loads environment variables stored in project settings into the CI environment. No modifications are necessary for the CI file.
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/guides/tools/docker.md
# Docker
Building a [Docker](https://www.docker.com/) image is a common way to deploy all sorts of applications. However, doing so from a monorepo has several challenges.
## The problem
**In a monorepo, unrelated changes can make Docker do unnecessary work when deploying your app.**
Let's imagine you have a monorepo that looks like this:
You want to deploy `apps/api` using Docker, so you create a Dockerfile:
```docker title="./apps/api/Dockerfile"
FROM node:16
WORKDIR /usr/src/app
# Copy root package.json and lockfile
COPY package.json ./
COPY package-lock.json ./
# Copy the api package.json
COPY apps/api/package.json ./apps/api/package.json
RUN npm install
# Copy app source
COPY . .
EXPOSE 8080
CMD [ "node", "apps/api/server.js" ]
```
This will copy the root `package.json` and the root lockfile to the Docker image. Then, it'll install dependencies, copy the app source and start the app.
You should also create a `.dockerignore` file to prevent node\_modules from being copied in with the app's source.
```txt title=".dockerignore"
node_modules
npm-debug.log
```
### The lockfile changes too often
Docker is pretty smart about how it deploys your apps. Just like Turborepo, it tries to do as [little work as possible](https://bitjudo.com/blog/2014/03/13/building-efficient-dockerfiles-node-dot-js/).
In our Dockerfile's case, it will only run `npm install` if the files it has in its image are *different* from the previous run. If not, it'll restore the `node_modules` directory it had before.
This means that whenever `package.json`, `apps/api/package.json` or `package-lock.json` change, the Docker image will run `npm install`.
This sounds great - until we realize something. The `package-lock.json` is *global* for the monorepo. That means that **if we install a new package inside `apps/web`, we'll cause `apps/api` to redeploy**.
In a large monorepo, this can result in a huge amount of lost time, as any change to a monorepo's lockfile cascades into tens or hundreds of deploys.
## The solution
The solution is to prune the inputs to the Dockerfile to only what is strictly necessary. Turborepo provides a simple solution - `turbo prune`.
```bash title="Terminal"
turbo prune api --docker
```
Running this command creates a **pruned version of your monorepo** inside an `./out` directory. It only includes workspaces which `api` depends on. It also **prunes the lockfile** so that only the relevant `node_modules` will be downloaded.
### The `--docker` flag
By default, `turbo prune` puts all relevant files inside `./out`. But to optimize caching with Docker, we ideally want to copy the files over in two stages.
First, we want to copy over only what we need to install the packages. When running `--docker`, you'll find this inside `./out/json`.
Afterwards, you can copy the files in `./out/full` to add the source files.
Splitting up **dependencies** and **source files** in this way lets us **only run `npm install` when dependencies change** - giving us a much larger speedup.
Without `--docker`, all pruned files are placed inside `./out`.
### Example
Our detailed [`with-docker` example](https://github.com/vercel/turborepo/tree/main/examples/with-docker) goes into depth on how to use `prune` to its full potential. Here's the Dockerfile, copied over for convenience.
Build the Dockerfile from the root of your monorepo:
```bash title="Terminal"
docker build -f apps/web/Dockerfile .
```
This Dockerfile is written for a [Next.js](https://nextjs.org/) app that is
using the `standalone` [output
mode](https://nextjs.org/docs/pages/api-reference/next-config-js/output).
```docker title="./apps/web/Dockerfile"
FROM node:18-alpine AS base
RUN apk update
RUN apk add --no-cache libc6-compat
# Set working directory
WORKDIR /app
# ---
FROM base AS prepare
# Replace with the major version installed in your repository. For example:
# RUN yarn global add turbo@^2
RUN yarn global add turbo@^
COPY . .
# Add lockfile and package.json's of isolated subworkspace
# Generate a partial monorepo with a pruned lockfile for a target workspace.
# Assuming "web" is the name entered in the project's package.json: { name: "web" }
RUN turbo prune web --docker
# ---
FROM base AS builder
# First install the dependencies (as they change less often)
COPY --from=prepare /app/out/json/ .
RUN yarn install
# Build the project
COPY --from=prepare /app/out/full/ .
# Uncomment and use build args to enable remote caching
# ARG TURBO_TEAM
# ENV TURBO_TEAM=$TURBO_TEAM
# ARG TURBO_TOKEN
# ENV TURBO_TOKEN=$TURBO_TOKEN
RUN yarn turbo build
# ---
FROM base AS runner
# Don't run production as root for security reasons
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
USER nextjs
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public
CMD node apps/web/server.js
```
## Remote Caching
To take advantage of remote caches during Docker builds, you will need to make sure your build container has credentials to access your [Remote Cache](/docs/core-concepts/remote-caching).
There are many ways to take care of secrets in a Docker image. We will use a simple strategy here with multi-stage builds using secrets as build arguments that will get hidden for the final image.
Assuming you are using a Dockerfile similar to the one above, we will bring in some environment variables from build arguments right before `turbo build`:
```docker title="./apps/api/Dockerfile"
ARG TURBO_TEAM
ENV TURBO_TEAM=$TURBO_TEAM
ARG TURBO_TOKEN
ENV TURBO_TOKEN=$TURBO_TOKEN
RUN yarn turbo run build
```
`turbo` will now be able to hit your Remote Cache. To see a Turborepo cache hit for a non-cached Docker build image, run a command like this one from your project root:
```bash title="Terminal"
docker build -f apps/web/Dockerfile . --build-arg TURBO_TEAM="your-team-name" --build-arg TURBO_TOKEN="your-token" --no-cache
```
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/guides/tools/eslint.md
# ESLint
ESLint is a static analysis tool for quickly finding and fixing problems in your JavaScript code.
In this guide, we'll cover:
* [ESLint v9 with Flat Configuration](#eslint-v9-flat-configs)
* [ESLint v8 with legacy configuration](#eslint-v8-legacy)
* [How to set up a `lint` task (applies to both versions)](#setting-up-a-lint-task)
We will share configurations across the monorepo's Workspace, ensuring configuration is consistent across packages and composable to maintain high cache hit ratios.
## ESLint v9 (Flat Configs)
Using ESLint v9's Flat Configs, we will end up with a file structure like this:
This structure includes:
* A package called `@repo/eslint-config` in `./packages/eslint-config` that holds all ESLint configuration
* Two applications, each with their own `eslint.config.js`
* A `ui` package that also has its own `eslint.config.js`
### About the configuration package
The `@repo/eslint-config` package has three configuration files, `base.js`, `next.js`, and `react-internal.js`. They are [exported from `package.json`](https://github.com/vercel/turborepo/blob/main/examples/basic/packages/eslint-config/package.json#L6) so that they can be used by other packages, according to needs. Examples of the configurations can be found [in the Turborepo GitHub repository](https://github.com/vercel/turborepo/tree/main/examples/basic/packages/eslint-config) and are available in `npx create-turbo@latest`.
Notably, the `next.js` and `react-internal.js` configurations use the `base.js` configuration for consistency, extending it with more configuration for their respective requirements. Additionally, notice that [the `package.json` for `eslint-config`](https://github.com/vercel/turborepo/blob/main/examples/basic/packages/eslint-config/package.json) has all of the ESLint dependencies for the repository. This is useful, since it means we don't need to re-specify the dependencies in the packages that import `@repo/eslint-config`.
### Using the configuration package
In our `web` app, we first need to add `@repo/eslint-config` as a dependency.
```jsonc title="./apps/web/package.json"
{
"devDependencies": {
"@repo/eslint-config": "workspace:*"
}
}
```
```jsonc title="./apps/web/package.json"
{
"devDependencies": {
"@repo/eslint-config": "*"
}
}
```
```jsonc title="./apps/web/package.json"
{
"devDependencies": {
"@repo/eslint-config": "*"
}
}
```
```jsonc title="./apps/web/package.json"
{
"devDependencies": {
"@repo/eslint-config": "workspace:*"
}
}
```
We can then import the configuration like this:
```js title="./apps/web/eslint.config.js"
/** @type {import("eslint").Linter.Config} */
export default nextJsConfig;
```
Additionally, you can add configuration specific to the package like this:
```js title="./apps/web/eslint.config.js"
/** @type {import("eslint").Linter.Config} */
export default [
...nextJsConfig,
// Other configurations
];
```
## ESLint v8 (Legacy)
ESLint v8 is end-of-life as of October 5, 2024. We encourage you to upgrade to
ESLint v9 or later. This documentation is here to help with existing projects
that have not yet upgraded.
Using legacy configuration from ESLint v8 and lower, we will end up with a file structure like this:
There's a package called `@repo/eslint-config`, and two applications, each with their own `.eslintrc.js`.
### The `@repo/eslint-config` package
The `@repo/eslint-config` file contains two files, `next.js`, and `library.js`. These are two different ESLint configurations, which we can use in different packages, depending on our needs.
A configuration for Next.js may look like this:
```js title="./packages/eslint-config/next.js"
/* Custom ESLint configuration for use with Next.js apps. */
module.exports = {
extends: [
"eslint-config-turbo",
"eslint-config-next",
// ...your other ESLint configurations
].map(require.resolve),
// ...your other configuration
};
```
The `package.json` looks like this:
```json title="./packages/eslint-config/package.json"
{
"name": "@repo/eslint-config",
"version": "0.0.0",
"private": true,
"devDependencies": {
"eslint": "^8",
"eslint-config-turbo": "latest",
"eslint-config-next": "latest"
}
}
```
Note that the ESLint dependencies are all listed here. This is useful, since it means we don't need to re-specify the dependencies inside the apps which import `@repo/eslint-config`.
### How to use the `@repo/eslint-config` package
In our `web` app, we first need to add `@repo/eslint-config` as a dependency.
```jsonc title="./apps/web/package.json"
{
"dependencies": {
"@repo/eslint-config": "workspace:*"
}
}
```
```jsonc title="./apps/web/package.json"
{
"dependencies": {
"@repo/eslint-config": "*"
}
}
```
```jsonc title="./apps/web/package.json"
{
"dependencies": {
"@repo/eslint-config": "*"
}
}
```
```jsonc title="./apps/web/package.json"
{
"dependencies": {
"@repo/eslint-config": "workspace:*"
}
}
```
We can then import the config like this:
```js title="./apps/web/.eslintrc.js"
module.exports = {
root: true,
extends: ["@repo/eslint-config/next.js"],
};
```
By adding `@repo/eslint-config/next.js` to our `extends` array, we're telling ESLint to look for a package called `@repo/eslint-config`, and reference the file `next.js`.
## Setting up a `lint` task
The `package.json` for each package where you'd like to run ESLint should look like this:
```json title="./packages/*/package.json"
{
"scripts": {
"lint": "eslint ."
}
}
```
With your scripts prepared, you can then create your Turborepo task:
```json title="./turbo.json"
{
"tasks": {
"lint": {
"dependsOn": ["^lint"]
}
}
}
```
Using `dependsOn` with `^lint` ensures that changes to dependencies like `@repo/eslint-config` will invalidate the cache for your `lint` task, even though the configuration package doesn't have a `lint` script itself.
You can now run `turbo lint` with [global `turbo`](/docs/getting-started/installation#global-installation) or create a script in your root `package.json`:
```json title="./package.json"
{
"scripts": {
"lint": "turbo run lint"
}
}
```
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/guides/ci-vendors/github-actions.md
# GitHub Actions
The following example shows how to use Turborepo with [GitHub Actions](https://github.com/features/actions).
For a given root `package.json`:
```json title="./package.json"
{
"name": "my-turborepo",
"scripts": {
"build": "turbo run build",
"test": "turbo run test"
},
"devDependencies": {
"turbo": "latest"
}
}
```
And a `turbo.json`:
```json title="./turbo.json"
{
"$schema": "https://turborepo.dev/schema.json",
"tasks": {
"build": {
"outputs": [".next/**", "!.next/cache/**", "other-output-dirs/**"],
"dependsOn": ["^build"]
},
"test": {
"dependsOn": ["^build"]
}
}
}
```
Create a file called `.github/workflows/ci.yml` in your repository with the following contents:
```yaml title=".github/workflows/ci.yml"
name: CI
on:
push:
branches: ["main"]
pull_request:
types: [opened, synchronize]
jobs:
build:
name: Build and Test
timeout-minutes: 15
runs-on: ubuntu-latest
# To use Remote Caching, uncomment the next lines and follow the steps below.
# env:
# TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
# TURBO_TEAM: ${{ vars.TURBO_TEAM }}
steps:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 2
- uses: pnpm/action-setup@v3
with:
version: 8
- name: Setup Node.js environment
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Build
run: pnpm build
- name: Test
run: pnpm test
```
```yaml title=".github/workflows/ci.yml"
name: CI
on:
push:
branches: ["main"]
pull_request:
types: [opened, synchronize]
jobs:
build:
name: Build and Test
timeout-minutes: 15
runs-on: ubuntu-latest
# To use Remote Caching, uncomment the next lines and follow the steps below.
# env:
# TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
# TURBO_TEAM: ${{ vars.TURBO_TEAM }}
steps:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Setup Node.js environment
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'yarn'
- name: Install dependencies
run: yarn
- name: Build
run: yarn build
- name: Test
run: yarn test
```
```yaml title=".github/workflows/ci.yml"
name: CI
on:
push:
branches: ["main"]
pull_request:
types: [opened, synchronize]
jobs:
build:
name: Build and Test
timeout-minutes: 15
runs-on: ubuntu-latest
# To use Remote Caching, uncomment the next lines and follow the steps below.
# env:
# TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
# TURBO_TEAM: ${{ vars.TURBO_TEAM }}
# TURBO_REMOTE_ONLY: true
steps:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Setup Node.js environment
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm install
- name: Build
run: npm run build
- name: Test
run: npm run test
```
```yaml title=".github/workflows/ci.yml"
name: CI
on:
push:
branches: ["main"]
pull_request:
types: [opened, synchronize]
jobs:
build:
name: Build and Test
timeout-minutes: 15
runs-on: ubuntu-latest
# To use Remote Caching, uncomment the next lines and follow the steps below.
# env:
# TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
# TURBO_TEAM: ${{ vars.TURBO_TEAM }}
steps:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 2
- uses: oven-sh/setup-bun@v2
- name: Setup Node.js environment
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: bun install
- name: Build
run: bun run build
- name: Test
run: bun run test
```
## Remote Caching with Vercel Remote Cache
To use Remote Caching with GitHub Actions, add the following environment variables to your GitHub Actions workflow
to make them available to your `turbo` commands.
* `TURBO_TOKEN` - The Bearer token to access the Remote Cache
* `TURBO_TEAM` - The slug of the Vercel team to share the artifacts with
To use Remote Caching, retrieve the team and token for the Remote Cache for your provider. In this example, we'll use [Vercel Remote Cache](https://vercel.com/docs/monorepos/remote-caching).
Create a Scoped Access Token to your account in the [Vercel Dashboard](https://vercel.com/account/tokens)
Copy the value to a safe place. You'll need it in a moment.
Go to your GitHub repository settings and click on the **Secrets** and then **Actions** tab. Create a new secret called `TURBO_TOKEN` and enter the value of your Scoped Access Token.
Create a new repository variable (click the **Variables** tab) called `TURBO_TEAM` and set it to your team slug - the part after `vercel.com/` in [your Team URL](https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fsettings\&title=Find+Team+URL). For example, the slug for `vercel.com/acme` is `acme`.
Using a repository variable rather than a secret will keep GitHub Actions from
censoring your team name in log output.
At the top of your GitHub Actions workflow, provide the following environment variables to jobs that use `turbo`:
```yaml title=".github/workflows/ci.yml"
# ...
jobs:
build:
name: Build and Test
timeout-minutes: 15
runs-on: ubuntu-latest
# To use Turborepo Remote Caching, set the following environment variables for the job.
env: # [!code highlight]
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} # [!code highlight]
TURBO_TEAM: ${{ vars.TURBO_TEAM }} # [!code highlight]
steps:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 2
# ...
```
## Using Remote Caching with Reusable Workflows
For information on passing secrets to reusable workflows, see [GitHub's documentation on passing secrets to nested workflows](https://docs.github.com/en/actions/how-tos/reuse-automations/reuse-workflows#passing-secrets-to-nested-workflows).
## Remote Caching with GitHub actions/cache
The following steps show how you could use [actions/cache](https://github.com/actions/cache) to cache your monorepo artifacts on GitHub.
Supply a package.json script that will run tasks using Turborepo.
Example `package.json` with a `build` script:
```json title="./package.json"
{
"name": "my-turborepo",
"scripts": {
"build": "turbo run build"
},
"devDependencies": {
"turbo": "1.2.5"
}
}
```
Configure your GitHub pipeline with a step which uses the `actions/cache@v4` action before the build steps of your CI file.
* Make sure that the `path` attribute set within the `actions/cache` action matches the output location above. In the example below, `path` was set to `.turbo`.
* State the cache key for the current run under the `key` attribute. In the example below, we used a combination of the runner os and GitHub sha as the cache key.
* State the desired cache prefix pattern under the `restore-keys` attribute. Make sure this pattern will remain valid for future ci runs. In the example below, we used the `${{ runner.os }}-turbo-` as the cache key prefix pattern to search against. This allows us to hit the cache on any subsequent ci runs despite `github.sha` changing.
Example `ci` yaml with `.turbo` as chosen cache folder:
```yaml title=".github/workflows/ci.yml"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Cache turbo build setup # [!code highlight]
uses: actions/cache@v4 # [!code highlight]
with: # [!code highlight]
path: .turbo # [!code highlight]
key: ${{ runner.os }}-turbo-${{ github.sha }} # [!code highlight]
restore-keys: | # [!code highlight]
${{ runner.os }}-turbo-
- name: Setup Node.js environment
uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- name: Install dependencies
run: npm install
- name: Build
run: npm run build
```
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/guides/ci-vendors/gitlab-ci.md
# GitLab CI
The following example shows how to use Turborepo with [GitLab CI](https://docs.gitlab.com/ee/ci/).
For a given root `package.json`:
```json title="./package.json"
{
"name": "my-turborepo",
"scripts": {
"build": "turbo run build",
"test": "turbo run test"
},
"devDependencies": {
"turbo": "latest"
}
}
```
And a `turbo.json`:
```json title="./turbo.json"
{
"$schema": "https://turborepo.dev/schema.json",
"tasks": {
"build": {
"outputs": [".svelte-kit/**"],
"dependsOn": ["^build"]
},
"test": {
"dependsOn": ["^build"]
}
}
}
```
Create a file called `.gitlab-ci.yml` in your repository with the following contents:
```yaml title=".gitlab-ci.yml"
image: node:latest
stages:
- build
build:
stage: build
before_script:
- curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@6.32.2
- pnpm config set store-dir .pnpm-store
script:
- pnpm install
- pnpm build
- pnpm test
cache:
key:
files:
- pnpm-lock.yaml
paths:
- .pnpm-store
```
> For more information visit the pnpm documentation section on GitLab CI integration, view it [here](https://pnpm.io/continuous-integration#gitlab)
```yaml title=".gitlab-ci.yml"
image: node:latest
stages:
- build
build:
stage: build
script:
- yarn install
- yarn build
- yarn test
cache:
paths:
- node_modules/
- .yarn
```
```yaml title=".gitlab-ci.yml"
image: node:latest
stages:
- build
build:
stage: build
script:
- npm install
- npm run build
- npm run test
```
```yaml title=".gitlab-ci.yml"
default:
image: oven/bun:1.2
cache:
key:
files:
- bun.lock
paths:
- node_modules/
before_script:
- bun install
build:
script: - bun run build
test:
script: - bun run test
```
## Remote Caching
To use Remote Caching, retrieve the team and token for the Remote Cache for your provider. In this example, we'll use [Vercel Remote Cache](https://vercel.com/docs/monorepos/remote-caching):
* `TURBO_TOKEN` - The Bearer token to access the Remote Cache
* `TURBO_TEAM` - The slug of the Vercel team to share the artifacts with
To use Vercel Remote Caching, you can get the value of these variables in a few steps:
1. Create a Scoped Access Token to your account in the [Vercel Dashboard](https://vercel.com/account/tokens)
Copy the value to a safe place. You'll need it in a moment.
2. Go to your GitLab repository settings and click on the **Settings** and then **CI/CD** tab. Create a new variable called `TURBO_TOKEN` and enter the value of your Scoped Access Token.
3. Make a second secret called `TURBO_TEAM` and set it to your team slug - the part after `vercel.com/` in [your Team URL](https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fsettings\&title=Find+Team+URL). For example, the slug for `vercel.com/acme` is `acme`.
Remote Caching will now be operational in your GitLab workflows.
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/messages/invalid-env-prefix.md
# Invalid environment variable prefix
## Why this error occurred
When declaring environment variables in your `turbo.json`, you cannot prefix them with `$`. This
was an old syntax for declaring a dependency on an environment variable that was deprecated in Turborepo 1.5.
```json title="./turbo.json"
{
"globalEnv": ["$MY_ENV_VAR"]
}
```
The environment variable declared above has the `$` prefix.
## Solution
Remove the `$` prefix from your environment variable declaration.
```json title="./turbo.json"
{
"globalEnv": ["MY_ENV_VAR"]
}
```
You can migrate to the `env` and `globalEnv` keys using `npx @turbo/codemod migrate-env-var-dependencies`.
Check out [the codemod's documentation for more details](/docs/reference/turbo-codemod#turborepo-1x).
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/guides/tools/jest.md
# Jest
[Jest](https://jestjs.io/) is a common test runner with a vast ecosystem. Integrating with Turborepo will lead to enormous speed-ups.
## Setting up
Let's say we have a monorepo that looks like this:
Install `jest` into the packages where you plan on having test suites. For this example, we will have tests in `web` and `@repo/ui`:
```bash title="Terminal"
pnpm add jest --save-dev --filter=@repo/ui --filter=web
```
```bash title="Terminal"
yarn workspace web add jest --dev
yarn workspace @repo/ui add jest --dev
```
```bash title="Terminal"
npm install jest --workspace=web --workspace=@repo/ui --save-dev
```
```bash title="Terminal"
cd apps/web && bun install jest --dev
cd packages/ui && bun install jest --dev
```
Both the `apps/web` and `packages/ui` have their own test suites, so we'll add a `test` script to their `package.json`:
```json title="./apps/web/package.json"
{
"name": "web",
"scripts": {
"test": "jest"
},
"devDependencies": {
"jest": "latest"
}
}
```
```json title="./packages/ui/package.json"
{
"name": "@repo/ui",
"scripts": {
"test": "jest"
},
"devDependencies": {
"jest": "latest"
}
}
```
Inside the root `turbo.json`, create a `test` task:
```json title="./turbo.json"
{
"tasks": {
"test": {}
}
}
```
Now, `turbo test` can parallelize and cache all of the test suites from each package, only testing code that has changed.
## Running tests in watch mode
When you run your test suite normally, it completes and outputs to `stdout`. This means you can [cache it](/docs/crafting-your-repository/caching) with Turborepo.
But when you run your tests in a watched mode, the process never exits. This makes a watch task more like a [development task](/docs/crafting-your-repository/developing-applications).
Because of this difference, we recommend specifying **two separate Turborepo tasks**: one for running your tests, and one for running them in Jest's watch mode. Inside your each `package.json` file for each workspace:
```json title="./apps/web/package.json"
{
"name": "web",
"scripts": {
"test": "jest",
"test:watch": "jest --watch" // [!code highlight]
},
"devDependencies": {
"jest": "latest"
}
}
```
```json title="./packages/ui/package.json"
{
"name": "@repo/ui",
"scripts": {
"test": "jest",
"test:watch": "jest --watch" // [!code highlight]
},
"devDependencies": {
"jest": "latest"
}
}
```
Inside the root `turbo.json`:
```json title="./turbo.json"
{
"tasks": {
"test": {},
"test:watch": {
"cache": false, // [!code highlight]
"persistent": true // [!code highlight]
}
}
}
```
You can now either run this task using [global `turbo`](/docs/getting-started/installation#global-installation) as `turbo test:watch` or from a script in your root `package.json`:
```bash title="Terminal"
turbo test
```
```bash title="Terminal"
turbo test:watch
```
```json title="./package.json"
{
"scripts": {
"test": "turbo run test",
"test:watch": "turbo run test:watch"
}
}
```
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/messages/missing-root-task-in-turbo-json.md
# Missing root task in turbo.json
## Why this error occurred
Root tasks are the scripts defined in the monorepo's root `package.json`. These tasks often call `turbo`. For example:
```json title="./package.json"
{
"scripts": {
"build": "turbo run build"
}
}
```
This creates a problem when we declare [topological dependencies](/docs/reference/configuration#dependson). Topological
dependencies specify that your package's dependencies should execute their tasks before your package executes its own task.
```json title="./turbo.json"
{
"tasks": {
"build": {
"dependsOn": ["^build"]
}
}
}
```
Because the root package is a dependency for all packages inside your workspace, its task would get executed first.
But since its task calls `turbo`, this would cause an infinite loop.
## Solution
As long as the root task does *not* call `turbo`, you can add it to the `tasks` field in `turbo.json`:
```json title="./turbo.json"
{
"tasks": {
"//#build": {}
}
}
```
This will permit tasks to depend on `//#build`.
However, if the root task does call `turbo`, this can cause infinite recursion. In this case, we don't recommend depending
on the root task. Instead, you can determine the tasks that this root task depends on, and depend on those directly.
For instance, if `//#build` depends on `app#lint` and `docs#lint`, then you can declare those as dependencies.
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/guides/tools/oxc.md
# Oxc (oxlint and oxfmt)
[Oxc](https://oxc.rs/) is a collection of high-performance JavaScript and TypeScript tools written in Rust, including [oxlint](https://oxc.rs/docs/guide/usage/linter) (a fast linter) and [oxfmt](https://oxc.rs/docs/guide/usage/formatter) (a fast formatter).
## Using Oxc tools with Turborepo
Similar to [Biome](/docs/guides/tools/biome), oxlint and oxfmt are **extraordinarily fast** tools. For this reason, we recommend using [Root Tasks](/docs/crafting-your-repository/configuring-tasks#registering-root-tasks) rather than creating separate scripts in each of your packages.
Using oxlint or oxfmt at the root of the project will result in cache misses
for all tasks when you upgrade versions or change configuration. If you prefer
the tradeoff of higher cache hit ratios in these situations over less
configuration, you can still run these tools in separate scripts like the
other recommendations in our guides.
## Setting up oxlint
### Install oxlint
First, install oxlint in your repository:
```bash title="Terminal"
pnpm add --save-dev oxlint
```
```bash title="Terminal"
yarn add --dev oxlint
```
```bash title="Terminal"
npm install --save-dev oxlint
```
```bash title="Terminal"
bun add --dev oxlint
```
### Create scripts
Add scripts to the root `package.json` of your repository:
```json title="./package.json"
{
"scripts": {
"lint": "oxlint .",
"lint:fix": "oxlint --fix ."
}
}
```
### Create root tasks
Register the scripts to Turborepo as [Root Tasks](/docs/crafting-your-repository/configuring-tasks#registering-root-tasks):
```json title="./turbo.json"
{
"tasks": {
"//#lint": {},
"//#lint:fix": {
"cache": false
}
}
}
```
You can now run `turbo run lint` to lint your entire repository.
## Setting up oxfmt
oxfmt is a fast code formatter for JavaScript and TypeScript, designed to be a drop-in replacement for Prettier.
oxfmt is currently in alpha and may not have full feature parity with
Prettier. Check the [oxfmt
documentation](https://oxc.rs/docs/guide/usage/formatter) for the latest
status and supported options.
### Install oxfmt
Install oxfmt as a dev dependency:
```bash title="Terminal"
pnpm add --save-dev oxfmt
```
```bash title="Terminal"
yarn add --dev oxfmt
```
```bash title="Terminal"
npm install --save-dev oxfmt
```
```bash title="Terminal"
bun add --dev oxfmt
```
### Create scripts
Add formatting scripts to the root `package.json`:
```json title="./package.json"
{
"scripts": {
"format": "oxfmt --check",
"format:fix": "oxfmt ."
}
}
```
### Create root tasks
Register the scripts to Turborepo:
```json title="./turbo.json"
{
"tasks": {
"//#format": {},
"//#format:fix": {
"cache": false
}
}
}
```
You can now run `turbo run format` to check formatting and `turbo run format:fix` to format your code.
## Using oxlint and oxfmt together
For repositories using both tools, you can orchestrate them with a unified quality task:
```json title="./package.json"
{
"scripts": {
"lint": "oxlint .",
"lint:fix": "oxlint --fix .",
"format": "oxfmt --check",
"format:fix": "oxfmt ."
}
}
```
```json title="./turbo.json"
{
"tasks": {
"//#quality": {
"dependsOn": ["//#lint", "//#format"]
},
"//#quality:fix": {
"dependsOn": ["//#lint:fix", "//#format:fix"]
},
"//#lint": {},
"//#lint:fix": {
"cache": false
},
"//#format": {},
"//#format:fix": {
"cache": false
}
}
}
```
With this configuration:
* Run `turbo run quality` to check both linting and formatting
* Run `turbo run quality:fix` to fix both linting and formatting issues
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/messages/package-task-in-single-package-workspace.md
# Package task in single-package workspace error
## Why this error occurred
In single package mode, there cannot be multiple packages in your repository. Therefore, declaring a task in the
`turbo.json` with a specified package name is not permitted.
```json title="./turbo.json"
{
"tasks": {
"app#build": {
"cache": true
}
}
}
```
## Solution
Remove the package name from the task declaration.
```json title="./turbo.json"
{
"tasks": {
"build": {
"cache": true
}
}
}
```
Alternatively, if you would like to have multiple packages, you can [specify the workspaces in your repository](/docs/getting-started/add-to-existing-repository).
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/guides/tools/playwright.md
# Playwright
[Playwright](https://playwright.dev/) enables reliable end-to-end testing for modern web apps.
We recommend creating a Playwright package for each test suite that you'd like to run in your monorepo. This may mean suites broken up per-application, per-domain, or some other scheme, according to your needs. If you are unsure, start with creating a Playwright package per-application.
## Handling Playwright's environment variables
Playwright requires several environment variables to run correctly. To ensure that these variables are available within your tasks in [Strict Mode](/docs/crafting-your-repository/using-environment-variables#strict-mode), you'll want to add them to your pass through variables using [`passThroughEnv`](/docs/reference/configuration#passthroughenv) in your end-to-end task or [`globalPassThroughEnv`](/docs/reference/configuration#globalpassthroughenv) globally, depending on your scoping preferences.
The configuration below using `passThroughEnv` will allow environment variables that start with `PLAYWRIGHT_` into **the `e2e` task** and **will not affect hashing**.
```json title="./turbo.json"
{
"tasks": {
"e2e": {
"passThroughEnv": ["PLAYWRIGHT_*"]
}
}
}
```
The configuration below using `globalPassThroughEnv` will allow environment variables that start with `PLAYWRIGHT_` into **all tasks** and **will not affect hashing**.
```json title="./turbo.json"
{
"globalPassThroughEnv": ["PLAYWRIGHT_*"]
}
```
Note that pass through variables are used since we don't want to miss cache in situations where these Playwright-internal variables change. For example,`PLAYWRIGHT_BROWSERS_PATH` is used to locate browser binaries used by Playwright, and we don't want to miss cache if this location changes.
## Designing the task graph
We want to ensure the proper caching behavior for our end-to-end suites. Specifically, we want to make sure of cache misses in a few key situations:
* If test suites change, cache gets missed.
* If code tested by the suites changes, cache gets missed.
The first requirement will be met naturally since the hash for the task will change when test code is changed. However, the second requirement means you need to ensure end-to-end tests depend on changes in application source code.
This relationship can be expressed in `turbo.json` and the end-to-end suite's `package.json`.
```json title="./turbo.json"
{
"tasks": {
"build": {
"dependsOn": ["^build"]
},
"e2e": {
"dependsOn": ["^build"] // [!code highlight]
}
}
}
```
```json title="./tooling/playwright-web/package.json"
{
"name": "@repo/playwright-web",
"dependencies": {
"web": "workspace:*" // [!code highlight]
}
}
```
Later on, when you want to run your end-to-end tests, use [the `--only` flag](/docs/reference/run#--only) to run end-to-end tests without running the application's build first. As an example, your command may look like `turbo run e2e --filter=@repo/playwright-myapp --only`.
## Sharing Playwright utilities
You can also create a common package for shared utilities that you need in your end-to-end test suites. We recommend using `peerDependencies` in this shared package so that you can get access to Playwright used in consumers without having to install Playwright into the shared package itself.
```json title="./packages/playwright-utilities/package.json"
{
"name": "@repo/playwright-utilities",
"peerDependencies": {
"playwright": "workspace:*"
}
}
```
```json title="./packages/playwright-utilities/package.json"
{
"name": "@repo/playwright-utilities",
"peerDependencies": {
"playwright": "*"
}
}
```
```json title="./packages/playwright-utilities/package.json"
{
"name": "@repo/playwright-utilities",
"peerDependencies": {
"playwright": "*"
}
}
```
```json title="./packages/playwright-utilities/package.json"
{
"name": "@repo/playwright-utilities",
"peerDependencies": {
"playwright": "workspace:*"
}
}
```
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/guides/tools/prisma.md
# Prisma
[Prisma](https://www.prisma.io/) unlocks a new level of developer experience when working with databases thanks to its intuitive data model, automated migrations, type-safety & auto-completion.
[Their official guide](https://www.prisma.io/docs/guides/turborepo) describes how to integrate Prisma into a Turborepo, including:
* Prisma client initialization
* Packaging the client as an [Internal Package](/docs/core-concepts/internal-packages)
* Performing migrations
* Working on your applications locally
* Deploying
## Example
To get started with our community-supported Prisma example, run:
```bash title="Terminal"
npx create-turbo@latest -e with-prisma
```
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/messages/recursive-turbo-invocations.md
# Recursive `turbo` invocations
## Why this error occurred
When a cycle of `turbo` invocations is detected in a [single-package workspace](https://turborepo.dev/docs/guides/single-package-workspaces), Turborepo will error to prevent the recursive calls to itself. Typically, this situation occurs for one of two reasons:
### Recursion in scripts and tasks
In a single-package workspace, a script in `package.json` that calls a Turborepo task with the same name causes a loop.
```json title="./package.json"
{
"scripts": {
"build": "turbo run build"
}
}
```
Calling the `build` script calls `turbo run build`. `turbo run build` then calls the `build` script, initiating the loop of recursive calls.
To resolve this, ensure that the name of the script in `package.json` is not the same as the Turborepo task. For example, to fix the snippet above, renaming the script would break the cycle:
```json title="./package.json"
{
"scripts": {
"build:app": "turbo run build"
}
}
```
### Package manager Workspace misconfiguration
A misconfigured workspace can make it appear that a [multi-package workspace](https://vercel.com/docs/vercel-platform/glossary#multi-package-workspace) is a single-package workspace. This causes Turborepo to infer that the repository is of the wrong type, causing it to see the script in `package.json` to be recursive.
Your repo can end up in this state in a few ways, with the most common being that the [packages are not defined according to your package manager](https://turborepo.dev/docs/crafting-your-repository/structuring-a-repository#specifying-packages-in-a-monorepo). An npm workspace that is missing the `workspaces` field in `package.json` or a pnpm workspace that is missing a `pnpm-workspace.yaml` file can result in this error message.
Check that your repository is complying with standards for multi-package workspaces and correct any issues.
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/guides/tools/shadcn-ui.md
# shadcn/ui
[shadcn/ui](https://ui.shadcn.com/docs/monorepo) is an open-source set of beautifully designed components made with Tailwind CSS that you can copy and paste into your apps.
To get started with shadcn/ui in a new monorepo, run:
```bash title="Terminal"
pnpm dlx shadcn@canary init
```
```bash title="Terminal"
npx shadcn@canary init
```
```bash title="Terminal"
npx shadcn@canary init
```
```bash title="Terminal"
bunx shadcn@canary init
```
When prompted, select the option for monorepos.
To add a component, run:
```bash title="Terminal"
pnpm dlx shadcn@canary add [COMPONENT]
```
```bash title="Terminal"
npx shadcn@canary add [COMPONENT]
```
```bash title="Terminal"
npx shadcn@canary add [COMPONENT]
```
```bash title="Terminal"
bunx shadcn@canary add [COMPONENT]
```
## More information
To learn more about using shadcn/ui in Turborepo, [visit the docs for shadcn/ui](https://ui.shadcn.com/docs/monorepo).
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/guides/tools/storybook.md
# Storybook
[Storybook](https://storybook.js.org/) is a popular way to build UI components in an isolated environment. By putting Storybook into your Turborepo, you can easily develop your design system right alongside your applications.
## Quickstart
If you'd rather use a template, this guide is walking through how to build [this Storybook/Turborepo template](https://vercel.com/templates/react/turborepo-design-system) on Vercel.
```bash title="Terminal"
pnpm dlx create-turbo@latest -e design-system
```
```bash title="Terminal"
yarn dlx create-turbo@latest -e design-system
```
```bash title="Terminal"
npx create-turbo@latest -e design-system
```
```bash title="Terminal"
bunx create-turbo@latest -e design-system
```
## Guide
### Create a monorepo
If you don't have an existing project, use [create-turbo](/docs/getting-started/installation) to create a new monorepo:
```bash title="Terminal"
pnpm dlx create-turbo@latest
```
```bash title="Terminal"
yarn dlx create-turbo@latest
```
```bash title="Terminal"
npx create-turbo@latest
```
```bash title="Terminal"
bunx create-turbo@latest
```
### Create a directory for the app
You'll need a directory for the Storybook application:
```bash title="Terminal"
mkdir apps/storybook
cd apps/storybook
```
### Add the Storybook application
In the `apps/storybook` directory, initialize a new Storybook application:
```bash title="Terminal"
pnpm create storybook@latest
```
```bash title="Terminal"
yarn create storybook@latest
```
```bash title="Terminal"
npm create storybook@latest
```
```bash title="Terminal"
bun create storybook@latest
```
Follow the prompts to create an application. For the rest of this guide, we'll assume React and TypeScript.
After going through Storybook's onboarding, you can [uninstall the onboarding
addon](https://github.com/storybookjs/addon-onboarding/blob/main/README.md).
### Add your UI kit to Storybook
Now, install your UI package into Storybook.
```bash title="Terminal"
pnpm add @repo/ui --filter=storybook
```
```bash title="Terminal"
yarn workspace storybook add @repo/ui
```
```bash title="Terminal"
npm install @repo/ui --workspace=storybook
```
```bash title="Terminal"
cd apps/storybook && bun install @repo/ui
```
### Set up a story for your Button component
Delete the stories and components found in `src/stories` that were created by the Storybook scaffolding tool. You will be making your own.
As an example, here is a story for the `Button` component from `@repo/ui/button`.
```tsx title="./apps/storybook/src/stories/Button.stories.tsx"
import type { Meta, StoryObj } from "@storybook/react";
import { Button } from "@repo/ui/button";
const meta = {
title: "Example/Button",
component: Button,
tags: ["autodocs"],
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const Primary: Story = {
args: {
appName: "Button",
children: "I am a primary button.",
},
};
```
### Align scripts to your tasks
Last, integrate the new Storybook application into your Turborepo:
```json title="apps/storybook/package.json"
{
"scripts": {
"dev": "storybook dev -p 6006", // [!code highlight]
"build": "storybook build" // [!code highlight]
}
}
```
These scripts will now run with the `turbo dev` and `turbo build` tasks in your `turbo.json`.
To ensure file outputs are cached when you run `build`, add `storybook-static` to the outputs of your `turbo.json` build task:
```diff title="turbo.json"
{
"tasks": {
"build": {
"outputs": [
".next/**",
"!.next/cache/**"
+ "storybook-static/**"
]
}
}
}
```
### Add Storybook build outputs to `.gitignore`
Ensure that the build outputs for Storybook are not committed to source control
```diff title=".gitignore"
+ storybook-static
```
### Verify your configuration
Run `turbo build` to build the Storybook application alongside the rest of your applications.
You can also run `turbo build` again to see cache hits for your builds.
## More tips
### Co-locating stories
If you'd prefer to co-locate your stories to their source code (rather than having them in the Storybook application), you'll need some extra configuration.
#### Re-configure Storybook sources
In `.storybook/main.ts`, change the `stories` paths in `config` to the directories you'd like to capture. For instance, if you'd like to write stories in the UI package:
```diff title="./apps/storybook/.storybook/main.ts"
const config = {
stories: [
- "../src/**/*.mdx",
- "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
+ "../../../packages/ui/src/**/*.stories.@(js|jsx|mjs|ts|tsx)",
};
```
#### Move story files to the UI package
Following along with [the guide above](/docs/guides/tools/storybook#set-up-a-story-for-your-button-component), move the `./apps/storybook/src/stories/Button.stories.tsx` file to `./packages/ui/src/Button.stories.tsx`.
Update components imports so that they reference the now co-located modules. For instance, in the story's imports:
```diff title="./packages/ui/src/Button.stories.tsx"
- import { Button } from "@repo/ui/button";
+ import { Button } from "./button";
```
You may also need to update [absolute
imports](/docs/guides/tools/typescript#use-nodejs-subpath-imports-instead-of-typescript-compiler-paths)
according to your changes and usage.
You'll also need to install any Storybook packages required for writing stories. For example, moving the story from above would require that you install `@storybook/react` into your `@repo/ui` package.
```bash title="Terminal"
pnpm add @storybook/react --filter=@repo/ui --save-dev
```
```bash title="Terminal"
yarn workspace @repo/ui add @storybook/react --dev
```
```bash title="Terminal"
npm install @storybook/react --workspace=@repo/ui --save-dev
```
```bash title="Terminal"
cd packages/ui && bun install @storybook/react --dev
```
#### Configure caching
Because stories are now in the UI package, changes to those stories can cause cache misses for any builds that depend on your UI package. However, changing a story doesn't mean your production applications should miss cache.
To prevent this, exclude stories from the inputs to your `build` task in your root `turbo.json`. You'll also need to create a `build:storybook` task, which you'll need in a moment:
```json title="./turbo.json"
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"inputs": ["$TURBO_DEFAULT$", "!**/*.stories.{tsx,jsx,mdx}"], // [!code highlight]
"outputs": [".next/**", "!.next/cache/**"]
},
"build:storybook": {} // [!code highlight]
}
}
```
Additionally, create a [Package Configuration](/docs/reference/package-configurations) in the `storybook` application so stories are **accounted for in building the Storybook application, specifically**:
```json title="./apps/storybook/turbo.json"
{
"extends": ["//"],
"tasks": {
"build:storybook": {
"dependsOn": ["^build:storybook"],
"outputs": ["storybook-static/**"]
}
}
}
```
If you are using the [Compiled Package
pattern](/docs/core-concepts/internal-packages#compiled-packages), you may
also need to add `^build` to your `dependsOn`.
#### Rename the build script
Last, make sure your script to build Storybook uses the configuration we just wrote by renaming it to the name of the task:
```json title="apps/storybook/package.json"
{
"scripts": {
"dev": "storybook dev -p 6006",
"build:storybook": "storybook build" // [!code highlight]
}
}
```
The script that was once `"build"` is now `"build:storybook"` to ensure the stories are included in hashes for caching.
#### Verify your configuration
To ensure your setup is correct:
1. Run `turbo build:storybook build`. You should see cache misses.
2. Run `turbo build:storybook build` again. You should see all cache hits.
3. Make a code change **to a story** in your `@repo/ui` package.
4. Run `turbo build:storybook build` again. You should **only** see a cache miss for the Storybook application. All others should hit cache.
### Adding CSS
If your UI package exports its own CSS, you'll need to add it to the renders in the Storybook app, similar to how you would add it to your applications. [The Storybook documentation](https://storybook.js.org/docs/configure/styling-and-css#css) recommends you add it to the `.storybook/preview.ts` file.
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/guides/tools/tailwind.md
# Tailwind CSS
[Tailwind CSS](https://tailwindcss.com/) is a CSS framework that allows you to rapidly build modern websites without ever leaving your HTML.
## Quickstart
If you'd rather use a template, this guide is walking through how to build [this Tailwind CSS + Turborepo template](https://github.com/vercel/turborepo/tree/main/examples/with-tailwind).
```bash title="Terminal"
pnpm dlx create-turbo@latest -e with-tailwind
```
```bash title="Terminal"
yarn dlx create-turbo@latest -e with-tailwind
```
```bash title="Terminal"
npx create-turbo@latest -e with-tailwind
```
```bash title="Terminal"
bunx create-turbo@latest -e with-tailwind
```
## Guide
This guide is for Tailwind CSS v4.
### Create a monorepo
If you don't have an existing project, use [create-turbo](/docs/getting-started/installation) to create a new monorepo:
```bash title="Terminal"
pnpm dlx create-turbo@latest
```
```bash title="Terminal"
yarn dlx create-turbo@latest
```
```bash title="Terminal"
npx create-turbo@latest
```
```bash title="Terminal"
bunx create-turbo@latest
```
### Add Tailwind CSS to your application
[Follow Tailwind CSS's guides](https://tailwindcss.com/docs/installation/using-vite) to set up Tailwind CSS for your frontend framework.
Once completed, you can start working on bringing your UI package into the applications.
### Create a shared Tailwind CSS configuration package
First, build an [Internal Package](https://turborepo.dev/docs/core-concepts/internal-packages) with four files:
This `package.json` installs Tailwind CSS so we can create the file shared styles and export for the rest of the repository.
```json title="./packages/tailwind-config/package.json"
{
"name": "@repo/tailwind-config",
"version": "0.0.0",
"type": "module",
"private": true,
"exports": {
".": "./shared-styles.css",
"./postcss": "./postcss.config.js"
},
"devDependencies": {
"postcss": "^8.5.3",
"tailwindcss": "^4.1.5"
}
}
```
This `shared-styles.css` file will be shared to the libraries and applications in the repository. The variables shown will be available anywhere that the file is included.
```css title="./packages/tailwind-config/shared-styles.css"
@import "tailwindcss";
@theme {
--blue-1000: #2a8af6;
--purple-1000: #a853ba;
--red-1000: #e92a67;
}
```
If your frontends need a PostCSS configuration file, you can create one to share.
```js title="./packages/tailwind-config/postcss.config.js"
export const postcssConfig = {
plugins: {
"@tailwindcss/postcss": {},
},
};
```
### Create the UI package
You can now build the components to share to your applications.
For a full example, [visit the source code for `@repo/ui` package in the Tailwind CSS example](https://github.com/vercel/turborepo/tree/main/examples/with-tailwind/packages/ui). The files required for your Tailwind CSS setup are below.
The `package.json` installs the dependencies for the package, sets up scripts for development and build environments, and marks the exports for the package.
```json title="./packages/ui/package.json"
{
"exports": {
"./styles.css": "./dist/index.css",
"./*": "./dist/*.js"
},
"scripts": {
"build:styles": "tailwindcss -i ./src/styles.css -o ./dist/index.css",
"build:components": "tsc",
"dev:styles": "tailwindcss -i ./src/styles.css -o ./dist/index.css --watch",
"dev:components": "tsc --watch"
},
"devDependencies": {
"@repo/tailwind-config": "workspace:*",
"@tailwindcss/cli": "^4.1.5",
"@tailwindcss/postcss": "^4.1.5",
"autoprefixer": "^10.4.20",
"tailwindcss": "^4.1.5"
}
}
```
Above, we've only included the code related to setting up Tailwind. The full
package.json is
[here](https://github.com/vercel/turborepo/tree/main/examples/with-tailwind/packages/ui/package.json).
Create a `build` and `dev` task that runs the scripts for building of components and style sheets in parallel.
```json title="./packages/ui/turbo.json"
{
"extends": ["//"],
"tasks": {
"build": {
"dependsOn": ["build:styles", "build:components"]
},
"build:styles": {
"outputs": ["dist/**"]
},
"build:components": {
"outputs": ["dist/**"]
},
"dev": {
"with": ["dev:styles", "dev:components"]
},
"dev:styles": {
"cache": false,
"persistent": true
},
"dev:components": {
"cache": false,
"persistent": true
}
}
}
```
This `styles.css` contains component-level styles for the shared UI library.
```css title="./packages/ui/src/styles.css"
/* Component-level styles for the UI package */
@import "tailwindcss" prefix(ui);
```
Tailwind classes used in this package should be prefixed with `ui:` to avoid
style specificity issues.
### Use the UI package in an application
Install the packages you've created into your application.
```bash title="Terminal"
pnpm add @repo/ui @repo/tailwind-config --save-dev --filter=@repo/ui --filter=web
```
```bash title="Terminal"
yarn workspace web add @repo/ui @repo/tailwind-config --dev
yarn workspace @repo/ui add @repo/ui @repo/tailwind-config --dev
```
```bash title="Terminal"
npm install @repo/ui @repo/tailwind-config --workspace=web --workspace=@repo/ui --save-dev
```
```bash title="Terminal"
cd apps/web && bun install @repo/ui @repo/tailwind-config --dev
cd packages/ui && bun install @repo/ui @repo/tailwind-config --dev
```
Then, configure the files in your application so the styles from the UI package are reflected in the application.
```css title="./apps/web/app/globals.css"
@import "tailwindcss";
@import "@repo/tailwind-config";
```
```tsx title="./apps/web/app/layout.tsx"
import "@repo/ui/styles.css";
import "./globals.css";
```
```js title="./apps/web/postcss.config.js"
import { postcssConfig } from "@repo/tailwind-config/postcss";
export default postcssConfig;
```
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/guides/ci-vendors/travis-ci.md
# Travis CI
The following example shows how to use Turborepo with [Travis CI](https://www.travis-ci.com/).
For a given root `package.json`:
```json title="./package.json"
{
"name": "my-turborepo",
"scripts": {
"build": "turbo run build",
"test": "turbo run test"
},
"devDependencies": {
"turbo": "latest"
}
}
```
And a `turbo.json`:
```json title="./turbo.json"
{
"$schema": "https://turborepo.dev/schema.json",
"tasks": {
"build": {
"outputs": [".svelte-kit/**"],
"dependsOn": ["^build"]
},
"test": {
"dependsOn": ["^build"]
}
}
}
```
Create a file called `.travis.yml` in your repository with the following contents:
```yaml title=".travis.yml"
language: node_js
node_js:
- lts/*
cache:
npm: false
directories:
- "~/.pnpm-store"
before_install:
- curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@6.32.2
- pnpm config set store-dir ~/.pnpm-store
install:
- pnpm install
script:
- pnpm build
script:
- pnpm test
```
> For more information visit the pnpm documentation section on Travis CI integration, view it [here](https://pnpm.io/continuous-integration#travis)
Travis CI detects the use of Yarn by the presence of `yarn.lock`. It will automatically ensure it is installed.
```yaml title=".travis.yml"
language: node_js
node_js:
- lts/*
install:
- yarn
script:
- yarn build
script:
- yarn test
```
```yaml title=".travis.yml"
language: node_js
node_js:
- lts/*
install:
- npm install
script:
- npm run build
script:
- npm run test
```
```yaml title=".travis.yml"
language: node_js
node_js:
- lts/*
cache:
npm: false
directories:
- "~/.pnpm-store"
before_install:
- curl -fsSL https://bun.sh/install | bash
install:
- bun install
script:
- bun run build
script:
- bun run test
```
## Remote Caching
To use Remote Caching, retrieve the team and token for the Remote Cache for your provider. In this example, we'll use [Vercel Remote Cache](https://vercel.com/docs/monorepos/remote-caching):
* `TURBO_TOKEN` - The Bearer token to access the Remote Cache
* `TURBO_TEAM` - The slug of the Vercel team to share the artifacts with
To use Vercel Remote Caching, you can get the value of these variables in a few steps:
1. Create a Scoped Access Token to your account in the [Vercel Dashboard](https://vercel.com/account/tokens)
Copy the value to a safe place. You'll need it in a moment.
2. Go to your Travis repository settings and scroll down to the *Environment Variables* section. Create a new variable called `TURBO_TOKEN` and enter the value of your Scoped Access Token.
3. Make a second secret called `TURBO_TEAM` and set it to your team slug - the part after `vercel.com/` in [your Team URL](https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fsettings\&title=Find+Team+URL). For example, the slug for `vercel.com/acme` is `acme`.
4. Travis CI automatically loads environment variables stored in project settings into the CI environment. No modifications are necessary for the CI file.
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/guides/tools/typescript.md
# TypeScript
TypeScript is an excellent tool in monorepos, allowing teams to safely add types to their JavaScript code. While there is some complexity to getting set up, this guide will walk you through the important parts of a TypeScript setup for most use cases.
* [Sharing TypeScript configuration](#sharing-tsconfigjson)
* [Building a TypeScript package](#building-a-typescript-package)
* [Making type checking faster across your workspace](/docs/guides/tools/typescript#linting-your-codebase)
This guide assumes you are using a recent version of TypeScript and uses some
features that are only available in those versions. You may need to adjust the
guidance on this page if you are unable to features from those versions.
## Sharing `tsconfig.json`
You want to build consistency into your TypeScript configurations so that your entire repo can use great defaults and your fellow developers can know what to expect when writing code in the Workspace.
TypeScript's `tsconfig.json` sets the configuration for the TypeScript compiler and features an [`extends` key](https://www.typescriptlang.org/tsconfig#extends) that you'll use to share configuration across your workspace.
This guide will use [`create-turbo`](/docs/reference/create-turbo) as an example.
```bash title="Terminal"
pnpm dlx create-turbo@latest
```
```bash title="Terminal"
yarn dlx create-turbo@latest
```
```bash title="Terminal"
npx create-turbo@latest
```
```bash title="Terminal"
bunx create-turbo@latest
```
### Use a base `tsconfig` file
Inside `packages/typescript-config`, you have a few `json` files which represent different ways you might want to configure TypeScript in various packages. The `base.json` file is extended by every other `tsconfig.json` in the workspace and looks like this:
```json title="./packages/typescript-config/base.json"
{
"compilerOptions": {
"esModuleInterop": true,
"skipLibCheck": true,
"target": "es2022",
"allowJs": true,
"resolveJsonModule": true,
"moduleDetection": "force",
"isolatedModules": true,
"strict": true,
"noUncheckedIndexedAccess": true,
"module": "NodeNext"
}
}
```
### Creating the rest of the package
The other `tsconfig` files in this package use the `extends` key to start with the base configuration and customize for specific types of projects, like for Next.js (`nextjs.json`) and a React library (`react-library.json`).
Inside `package.json`, name the package so it can be referenced in the rest of the Workspace:
```json title="packages/typescript-config/package.json"
{
"name": "@repo/typescript-config"
}
```
## Building a TypeScript package
### Using the configuration package
First, install the `@repo/typescript-config` package into your package:
```json title="./apps/web/package.json"
{
"devDependencies": {
"@repo/typescript-config": "workspace:*",
"typescript": "latest"
}
}
```
```json title="./apps/web/package.json"
{
"devDependencies": {
"@repo/typescript-config": "*",
"typescript": "latest"
}
}
```
```json title="./apps/web/package.json"
{
"devDependencies": {
"@repo/typescript-config": "*",
"typescript": "latest"
}
}
```
```json title="./apps/web/package.json"
{
"devDependencies": {
"@repo/typescript-config": "workspace:*",
"typescript": "latest"
}
}
```
Then, extend the `tsconfig.json` for the package from the `@repo/typescript-config` package. In this example, the `web` package is a Next.js application:
```json title="./apps/web/tsconfig.json"
{
"extends": "@repo/typescript-config/nextjs.json",
"compilerOptions": {
"outDir": "dist"
},
"include": ["src"],
"exclude": ["node_modules"]
}
```
### Creating entrypoints to the package
First, make sure your code gets compiled with `tsc` so there will be a `dist` directory. You'll need a `build` script as well as a `dev` script:
```json title="./packages/ui/package.json"
{
"scripts": {
"dev": "tsc --watch",
"build": "tsc"
}
}
```
Then, set up the entrypoints for your package in `package.json` so that other packages can use the compiled code:
```json title="./packages/ui/package.json"
{
"exports": {
"./*": {
"types": "./src/*.ts",
"default": "./dist/*.js"
}
}
}
```
Setting up `exports` this way has several advantages:
* Using the `types` field allows `tsserver` to use the code in `src` as the source of truth for your code's types. Your editor will always be up-to-date with the latest interfaces from your code.
* You can quickly add new entrypoints to your package without creating [dangerous barrel files](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js#what's-the-problem-with-barrel-files).
* You'll receive auto-importing suggestions for your imports across package boundaries in your editor.
If you're publishing the package, you cannot use references to source code in
`types` since only the compiled code will be published to npm. You'll need to
generate and reference declaration files and source maps.
## Linting your codebase
To use TypeScript as a linter, you can check the types across your workspace **fast** using Turborepo's caching and parallelization.
First, add a `check-types` script to any package that you want to check the types for:
```json title="./apps/web/package.json"
{
"scripts": {
"check-types": "tsc --noEmit"
}
}
```
Then, create a `check-types` task in `turbo.json`. From the [Configuring tasks guide](/docs/crafting-your-repository/configuring-tasks#dependent-tasks-that-can-be-run-in-parallel), we can make the task run in parallel while respecting source code changes from other packages using a [Transit Node](/docs/core-concepts/package-and-task-graph#transit-nodes):
```json title="./turbo.json"
{
"tasks": {
"topo": {
"dependsOn": ["^topo"]
},
"check-types": {
"dependsOn": ["topo"]
}
}
}
```
Then, run your task using `turbo check-types`.
## Best practices
### Use `tsc` to compile your packages
For [Internal Packages](/docs/core-concepts/internal-packages), we recommend that you use `tsc` to compile your TypeScript libraries whenever possible. While you can use a bundler, it's not necessary and adds extra complexity to your build process. Additionally, bundling a library can mangle the code before it makes it to your applications' bundlers, causing hard to debug issues.
### Enable go-to-definition across package boundaries
"Go-to-definition" is an editor feature for quickly navigating to the original declaration or definition of a symbol (like a variable or function) with a click or hotkey. Once TypeScript is configured correctly, you can navigate across [Internal Packages](/docs/core-concepts/internal-packages) with ease.
#### Just-in-Time Packages
Exports from [Just-in-Time Packages](/docs/core-concepts/internal-packages#just-in-time-packages) will automatically bring you to the original TypeScript source code. Go-to-definition will work as expected.
#### Compiled Packages
Exports from [Compiled Packages](/docs/core-concepts/internal-packages#compiled-packages) require the use of [`declaration`](https://www.typescriptlang.org/tsconfig/#declaration) and [`declarationMap`](https://www.typescriptlang.org/tsconfig/#declarationMap) configurations for go-to-definition to work. After you've enabled these two configurations for the package, compile the package with `tsc`, and open the output directory to find declaration files and source maps.
With these two files in place, your editor will now navigate to the original source code.
### Use Node.js subpath imports instead of TypeScript compiler `paths`
It's possible to create absolute imports in your packages using [the TypeScript compiler's `paths` option](https://www.typescriptlang.org/tsconfig#paths), but these paths can cause failed compilation when using [Just-in-Time Packages](https://turborepo.dev/docs/core-concepts/internal-packages#just-in-time-packages). [As of TypeScript 5.4](https://devblogs.microsoft.com/typescript/announcing-typescript-5-4/#auto-import-support-for-subpath-imports), you can use [Node.js subpath imports](https://nodejs.org/api/packages.html#imports) instead for a more robust solution.
#### Just-in-Time Packages
In [Just-in-Time packages](https://turborepo.dev/docs/core-concepts/internal-packages#just-in-time-packages), `imports` must target the source code in the package, since build outputs like `dist` won't be created.
```json title="./packages/ui/package.json"
{
"imports": {
"#*": "./src/*"
}
}
```
```tsx title="./packages/ui/button.tsx"
import { MY_STRING } from "#utils.ts" // Uses .ts extension // [!code highlight]
export const Button = () => {
return (
)
}
```
#### Compiled Packages
In [Compiled packages](https://turborepo.dev/docs/core-concepts/internal-packages#compiled-packages), `imports` target the built outputs for the package.
```json title="./packages/ui/package.json"
{
"imports": {
"#*": "./dist/*"
}
}
```
```tsx title="./packages/ui/button.tsx"
import { MY_STRING } from "#utils.js"; // Uses .js extension // [!code highlight]
export const Button = () => {
return ;
};
```
### You likely don't need a `tsconfig.json` file in the root of your project
As mentioned in the [Structuring your repository guide](/docs/crafting-your-repository/structuring-a-repository#anatomy-of-a-package), you want to treat each package in your tooling as its own unit. This means each package should have its own `tsconfig.json` to use instead of referencing a `tsconfig.json` in the root of your project. Following this practice will make it easier for Turborepo to cache your type checking tasks, simplifying your configuration.
The only case in which you may want to have a `tsconfig.json` in the Workspace root is to set configuration for TypeScript files that are not in packages. For example, if you have a script written with TypeScript that you need to run from the root, you may need a `tsconfig.json` for that file.
However, this practice is also discouraged since any changes in the Workspace root will cause all tasks to miss cache. Instead, move those scripts to a different directory in the repository.
### You likely don't need TypeScript Project References
We don't recommend using TypeScript Project References as they introduce both another point of configuration as well as another caching layer to your workspace. Both of these can cause problems in your repository with little benefit, so we suggest avoiding them when using Turborepo.
## Limitations
### Your editor won't use a package's TypeScript version
`tsserver` is not able to use different TypeScript versions for different packages in your code editor. Instead, it will discover a specific version and use that everywhere.
This can result in differences between the linting errors that show in your editor and when you run `tsc` scripts to check types. If this is an issue for you, consider [keeping the TypeScript dependency on the same version](/docs/crafting-your-repository/managing-dependencies#keeping-dependencies-on-the-same-version).
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/messages/unnecessary-package-task-syntax.md
# Unnecessary package task syntax error
## Why this error occurred
Turborepo supports adding additional `turbo.json` files in a package directory
to override the `turbo.json` file declared at the repository root, a feature called [Workspace Configurations](/docs/crafting-your-repository/structuring-a-repository#specifying-packages-in-a-monorepo).
In those additional `turbo.json` files, you can only configure tasks for that specific
package. Therefore, only the task name should be included in the task,
not the package and task name (`package#task`).
`turbo.json` file in `apps/web` directory:
```json title="./turbo.json"
{
"tasks": {
"web#build": {
"dependsOn": ["lint"]
}
}
}
```
Since this `turbo.json` file is inside a package directory, the `web` prefix is unnecessary.
## Solution
Remove the package prefix from the task name:
```json title="./turbo.json"
{
"tasks": {
"build": {
"dependsOn": ["lint"]
}
}
}
```
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/guides/ci-vendors/vercel.md
# Vercel
Vercel's zero-config integration with Turborepo automatically understands your monorepo.
To deploy your Turborepo on Vercel, [create a new project](https://vercel.com/new) and import your code. Your projects will be pre-configured with the correct settings to use the [Vercel Remote Cache](https://vercel.com/docs/monorepos/remote-caching).
For more information about deploying your Turborepo to Vercel, [visit the Vercel documentation](https://vercel.com/docs/concepts/monorepos/turborepo).
---
[View full sitemap](/sitemap.md)
---
# Source: https://turbo.build/guides/tools/vitest.md
# Vitest
[Vitest](https://vitest.dev/) is a test runner from the Vite ecosystem. Integrating it with Turborepo will lead to enormous speed-ups.
[The Vitest documentation](https://vitest.dev/guide/workspace) shows how to create a "Vitest Projects" configuration that runs all tests in the monorepo from one root command, enabling behavior like merged coverage reports out-of-the-box. This feature doesn't follow modern best practices for monorepos, since its designed for compatibility with Jest (whose Workspace feature was built before [package manager Workspaces](/docs/crafting-your-repository/structuring-a-repository)).
Vitest has deprecated workspaces in favor of projects. When using projects,
individual project vitest configs can't extend the root config anymore since
they would inherit the projects configuration. Instead, a separate shared file
like `vitest.shared.ts` is needed.
Because of this you have two options, each with their own tradeoffs:
* [Leveraging Turborepo for caching](#leveraging-turborepo-for-caching)
* [Using Vitest's Projects feature](#using-vitests-projects-feature)
### Leveraging Turborepo for caching
To improve on cache hit rates and only run tests with changes, you can choose to configure tasks per-package, splitting up the Vitest command into separate, cacheable scripts in each package. This speed comes with the tradeoff that you'll need to create merged coverage reports yourself.
For a complete example, run `npx create-turbo@latest --example with-vitest` or
[visit the example's source
code](https://github.com/vercel/turborepo/tree/main/examples/with-vitest).
#### Setting up
Let's say we have a simple [package manager Workspace](/docs/crafting-your-repository/structuring-a-repository) that looks like this:
Both `apps/web` and `packages/ui` have their own test suites, with `vitest` [installed into the packages that use them](/docs/crafting-your-repository/managing-dependencies#install-dependencies-where-theyre-used). Their `package.json` files include a `test` script that runs Vitest:
```json title="./apps/web/package.json"
{
"scripts": {
"test": "vitest run"
},
"devDependencies": {
"vitest": "latest"
}
}
```
Inside the root `turbo.json`, create a `test` task:
```json title="./turbo.json"
{
"tasks": {
"test": {
"dependsOn": ["transit"]
},
"transit": {
"dependsOn": ["^transit"]
}
}
}
```
Now, `turbo run test` can parallelize and cache all of the test suites from each package, only testing code that has changed.
#### Running tests in watch mode
When you run your test suite in CI, it logs results and eventually exits upon completion. This means you can [cache it with Turborepo](/docs/crafting-your-repository/caching). But when you run your tests using Vitest's watch mode during development, the process never exits. This makes a watch task more like a [long-running, development task](/docs/crafting-your-repository/developing-applications).
Because of this difference, we recommend specifying **two separate Turborepo tasks**: one for running your tests, and one for running them in watch mode.
This strategy below creates two tasks, one for local development and one for
CI. You could choose to make the `test` task for local development and create
some `test:ci` task instead.
For example, inside the `package.json` file for each workspace:
```json title="./apps/web/package.json"
{
"scripts": {
"test": "vitest run",
"test:watch": "vitest --watch"
}
}
```
And, inside the root `turbo.json`:
```json title="./turbo.json"
{
"tasks": {
"test": {
"dependsOn": ["^test"]
},
"test:watch": {
"cache": false,
"persistent": true
}
}
}
```
You can now run your tasks using [global `turbo`](/docs/getting-started/installation#global-installation) as `turbo run test:watch` or from a script in your root `package.json`:
```bash title="Terminal"
turbo run test
turbo run test:watch
```
```json title="./package.json"
{
"scripts": {
"test": "turbo run test",
"test:watch": "turbo run test:watch"
}
}
```
#### Creating merged coverage reports
[Vitest's Projects feature](#using-vitests-projects-feature) creates an out-of-the-box coverage report that merges all of your packages' tests coverage reports. Following the Turborepo strategy, though, you'll have to merge the coverage reports yourself.
The [`with-vitest`
example](https://github.com/vercel/turborepo/tree/main/examples/with-vitest)
shows a complete example that you may adapt for your needs. You can get
started with it quickly using `npx create-turbo@latest --example with-vitest`.
To do this, you'll follow a few general steps:
1. Run `turbo run test` to create the coverage reports.
2. Merge the coverage reports with [`nyc merge`](https://github.com/istanbuljs/nyc?tab=readme-ov-file#what-about-nyc-merge).
3. Create a report using `nyc report`.
Turborepo tasks to accomplish will look like:
```json title="./turbo.json"
{
"tasks": {
"test": {
"dependsOn": ["^test", "@repo/vitest-config#build"],
"outputs": ["coverage.json"]
},
"merge-json-reports": {
"inputs": ["coverage/raw/**"],
"outputs": ["coverage/merged/**"]
},
"report": {
"dependsOn": ["merge-json-reports"],
"inputs": ["coverage/merge"],
"outputs": ["coverage/report/**"]
}
}
}
```
With this in place, run `turbo test && turbo report` to create a merged coverage report.
The [`with-vitest`
example](https://github.com/vercel/turborepo/tree/main/examples/with-vitest)
shows a complete example that you may adapt for your needs. You can get
started with it quickly using `npx create-turbo@latest --example with-vitest`.
### Using Vitest's Projects feature
The Vitest Projects feature doesn't follow the same model as a [package manager Workspace](/docs/crafting-your-repository/structuring-a-repository). Instead, it uses a root script that then reaches out into each package in the repository to handle the tests in that respective package.
In this model, there aren't package boundaries, from a modern JavaScript ecosystem perspective. This means you can't rely on Turborepo's caching, since Turborepo leans on those package boundaries.
Because of this, you'll need to use [Root Tasks](/docs/crafting-your-repository/configuring-tasks#registering-root-tasks) if you want to run the tests using Turborepo. Once you've configured [a Vitest Projects setup](https://vitest.dev/guide/workspace), create the Root Tasks for Turborepo:
```json title="./turbo.json"
{
"tasks": {
"//#test": {
"outputs": ["coverage/**"]
},
"//#test:watch": {
"cache": false,
"persistent": true
}
}
}
```
**Notably, the file inputs for a Root Task include all packages by default, so any change in any package will result in a cache miss.** While this does make for a simplified configuration to create merged coverage reports, you'll be missing out on opportunities to cache repeated work.
### Using a hybrid approach
You can combine the benefits of both approaches by implementing a hybrid solution. This approach unifies local development using Vitest's Projects feature while preserving Turborepo's caching in CI. This comes at the tradeoff of slightly more configuration and a mixed task running model in the repository.
First, create a shared configuration package since individual projects can't extend the root config when using projects. Create a new package for your shared Vitest configuration:
```json title="./packages/vitest-config/package.json"
{
"name": "@repo/vitest-config",
"version": "0.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"dev": "tsc --watch"
},
"dependencies": {
"vitest": "latest"
},
"devDependencies": {
"@repo/typescript-config": "workspace:*",
"typescript": "latest"
}
}
```
```json title="./packages/vitest-config/tsconfig.json"
{
"extends": "@repo/typescript-config/base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"],
"exclude": ["dist", "node_modules"]
}
```
```ts title="./packages/vitest-config/src/index.ts"
export const sharedConfig = {
test: {
globals: true,
environment: "jsdom",
setupFiles: ["./src/test/setup.ts"],
// Other shared configuration
},
};
```
Then, create your root Vitest configuration using projects:
```ts title="./vitest.config.ts"
export default defineConfig({
...sharedConfig,
projects: [
{
name: "packages",
root: "./packages/*",
test: {
...sharedConfig.test,
// Project-specific configuration
},
},
],
});
```
In this setup, your packages maintain their individual Vitest configurations that import the shared config. First, install the shared config package:
```json title="./packages/ui/package.json"
{
"scripts": {
"test": "vitest run",
"test:watch": "vitest --watch"
},
"devDependencies": {
"@repo/vitest-config": "workspace:*",
"vitest": "latest"
}
}
```
Then create the Vitest configuration:
```ts title="./packages/ui/vitest.config.ts"
export default defineConfig({
...sharedConfig,
test: {
...sharedConfig.test,
// Package-specific overrides if needed
},
});
```
Make sure to update your `turbo.json` to include the new configuration package in the dependency graph:
```json title="./turbo.json"
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["^test", "@repo/vitest-config#build"]
},
"test:watch": {
"cache": false,
"persistent": true
}
}
}
```
While your root `package.json` includes scripts for running tests globally:
```json title="./package.json"
{
"scripts": {
"test:projects": "vitest run",
"test:projects:watch": "vitest --watch"
}
}
```
This configuration allows developers to run `pnpm test:projects` or `pnpm test:projects:watch` at the root for a seamless local development experience using Vitest projects, while CI continues to use `turbo run test` to leverage package-level caching. **You'll still need to handle merged coverage reports manually as described in the previous section**.
---
[View full sitemap](/sitemap.md)