Package tasks, not root tasks

Scripts live in each package; the root only calls turbo run.

From turborepo, “Package Tasks, Not Root Tasks”.

One root script vs five tasks

Run lint, edit ui, run it again.

Root scriptBad
1 task0 cachedtime –
"lint": "eslint apps/ packages/"
Package tasksGood
5 tasks0 cachedtime –
"lint": "turbo run lint"

// apps/web/package.json (same in every package)
{ "scripts": { "build": "next build", "lint": "eslint .", "test": "vitest" } }

// turbo.json: register the tasks
{
  "tasks": {
    "build": { "dependsOn": ["^build"], "outputs": ["dist/**"] },
    "lint": {},
    "test": { "dependsOn": ["build"] }
  }
}

// package.json at the root: only delegates, always "turbo run" in code
{ "scripts": { "build": "turbo run build", "lint": "turbo run lint" } }

Filter to one package

Run lint for web only.

Root scriptBad
no lint script in apps/web
Package tasksGood
"lint": "eslint ." in apps/web

Why it matters, pros and cons

Why it matters

Turbo schedules, caches and filters per package task. A root script is one opaque process.

Pros

  • Packages lint and build in parallel.
  • A one-package edit reruns one task.
  • --filter and --affected work.

Cons

  • Some tools only run once per repo; use a //#task.
  • More package.json scripts to keep in step.
  • turbo lint shorthand is for terminals, not code.