dependsOn
Tasks that must complete successfully before this task runs. Turbo/Nx-style micro-syntax — a flat array of strings:
dependsOn?: readonly string[]| Form | Meaning |
|---|---|
'name' |
Same-project task name. |
'^name' |
The name task in the nearest project deps (a plugin’s edges) that declare it. |
'pkg#name' |
The name task in a specific other package (cross-project edge). |
'name.*' |
Every OTHER same-project task matching the pattern (* = any). |
'^name.*' |
All matching tasks of the nearest deps that declare any (pattern). |
Examples:
dependsOn: ['build'] // same-project build firstdependsOn: ['^build'] // build in every workspace dep firstdependsOn: ['codegen', '^build'] // bothdependsOn: ['lib#build', 'shared#test'] // cross-project edgesdependsOn: ['lint.*'] // every lint.<x> task in this projectdependsOn: ['^build.*'] // all build.<x> tasks of the nearest depsSemantics:
- Same-project (
'name') — name must exist in this project’stasksmap; missing is a hard error at graph-build time. When the config is authored throughdefineProject, a bare entry is also type-checked against this config’s task keys — a typo is a compile error ('^name'/'pkg#name'reference other projects and stay free strings). '^name'— nearest-holder frontier. Walk the project graph from this project’s direct deps: the edges a plugin’sdiscoverreturns asdeps(the lockfile pluginsbun()/pnpm()/npm()/yarn()return what the package manager installed; core reads no edge from apackage.json) and the cross-projectpkg#taskedges. With no plugin returningdepsthe workspace has no edge source and a'^name'is refused (VX_E_DEPENDS_ON: “^buildneeds project edges; add pnpm()/bun()/npm()/yarn() and install so its lockfile exists, or a plugin that returnsdeps”): it would resolve to nothing and fold no upstream into the key. With one, a project with no deps is a leaf; each path stops at the first dep that declares the task and an edge is added to it (Turbo/Nx direct-deps parity). The holder’s owndependsOnis responsible for anything deeper — chain'^name'in the holder to keep the cascade going (the universal pattern). Deps that don’t declare the task are passed through, so a sparse dep doesn’t break ordering to deeper packages that do (sparse tasks across a workspace are normal — not every package has alint). Finding no holder at all is legal too, as long as SOME project in the workspace declaresname: a preset spreads'^build'over packages whose deps have nobuild. A'^name'that no project in the whole workspace declares is a hard error at graph-build time, since it can only be a typo: “app#test: dependsOn ^biuld but no project in the workspace declares biuld. Did you mean ^build?” (a same-project name that is not declared names the project’s nearest task the same way). Until 2026-09-24 it resolved to no edges and the run went green (Nx#32779 is the same bug). A scoped run (an anchoredapp#test,--filter, a project directory) loads only its projects and their dependency closure; when a'^name'there finds no holder and nothing loaded declares it, vx evaluates the remaining configs to decide, and if one of them fails to load the name is let through rather than failing the scoped run. A'^name.*'pattern matching nothing anywhere stays legal, as'name.*'does.'pkg#name'— missing pkg or task is a hard error (you named them explicitly).'//#name', Turbo’s spelling of the root package, names the root project (D-39) here and ininputs.tasks; with no root project it is refused as such.- Patterns (
'name.*'/'^name.*') —*matches any run of characters in a task NAME; everything else is literal (the dotted namespace convention needs no escaping). A same-project pattern expands to every other matching task (the declaring task never matches itself); zero matches is legal — a preset-spread pattern needn’t match in every project. A'^pattern'walks the same nearest-holder frontier as'^name', where a holder is a dep declaring at least one matching task; the holder receives edges to ALL its matches and stops the walk. Patterns are not supported in the'pkg#task'form. (Nx 19.5build-*parity.) - No bare wildcards or negation here.
'*'/'^*'/'!form'belong ininputs.tasks(filtering which upstream hashes participate in this task’s cache key), not independsOn(declaring graph edges). The micro-syntax parser rejects them in this position. - Cycle detection runs across the resolved graph at the end of
buildTaskGraph. Cycles throw with a path-formatted message.