Skip to content
vxvx
GitHubBlueskydev.toRSS

Parity map for Turbo and Nx users

What a Turborepo or Nx user relies on, the vx spelling of it, and the test that proves it. Two end-to-end suites run the real CLI on one four-package fixture (tests/helpers/parity.ts: app → ui → lib, app → lib, docs alone) and name each case for the upstream contract: tests/parity-turbo.test.ts (turborepo.dev, 2.10) and tests/parity-nx.test.ts (nx.dev, 23). They are the scan surface; the right column names the suite that pins the behaviour in depth. A deliberate divergence is marked ≠ and explained.

Two more suites port the upstream projects’ own cache-invalidation tests, each case named for the Turbo or Nx test it stands in for: tests/parity-turbo-hashing.test.ts (crates turborepo-scm, turborepo-env, turborepo-task-hash) and tests/parity-nx-hashing.test.ts (the native hashers and hasher/*.spec.ts).

Turbo vx Deep pin
"dependsOn": ["^build"] dependsOn: ['^build'] tests/task-graph.test.ts
"dependsOn": ["build"] (same package) dependsOn: ['build'] tests/task-graph.test.ts
"dependsOn": ["pkg#task"] dependsOn: ['pkg#task'] tests/dependency-spec.test.ts
--only --exclude-dependencies[=<names>] tests/cli.test.ts
--dry, --dry=json --dry, --dry=json tests/plan-format.test.ts, tests/plan-predict.test.ts
--graph=file.dot --dry=json (each task with its deps; no DOT) tests/cli.test.ts
--filter=pkg, pkg..., ...pkg, ./dir, !pkg the same DSL (docs/cli.md § Filter DSL) tests/filter.test.ts
--filter=[ref], --affected --filter '[<ref>]', --affected[=<ref>]; ...[<ref>] adds dependents tests/affected.test.ts
--filter + --affected intersect ≠ they union (docs/comparison.md § Filter DSL) tests/filter.test.ts
a filter matching nothing exits 1 the same tests/task-selection.test.ts
second run hits; missing outputs restored; logs replayed the same, with outputs cleaned before exec and restore (≠ additive) tests/orchestrator.test.ts, tests/output-dirs.test.ts
"inputs": [...] narrows the hash inputs.files (required; nothing is inferred) tests/inputs.test.ts, tests/task-hash.test.ts
"inputs": ["src/"], "outputs": ["dist"] (a directory literal) the same: a literal is the file or its whole tree tests/inputs-resolution.test.ts
--filter=./apps/* (a path glob) --filter './apps/*', {apps/**} — over root-relative project dirs tests/filter.test.ts
--affected diffs from the merge base the same tests/affected.test.ts
TURBO_CACHE=remote:rw with no remote → “Remote caching disabled” --cache-policy=remote:rw with no remote layer → one status line tests/parity-turbo.test.ts
a dependency’s change re-hashes dependents upstream input keys are folded (never outputs) tests/task-hash-derive.test.ts, tests/upstream.test.ts
"env": ["MODE"] inputs.env: ['MODE'] tests/env.test.ts
"passThroughEnv" exec.env.passThrough (reaches the command, never the hash) tests/env.test.ts
"cache": false no cache: true (no-cache on its row) tests/no-cache-word.test.ts, tests/parity-turbo.test.ts
--force --force (reads off, writes on) tests/cli.test.ts
--cache=local:r,remote: / --no-cache (reads, no writes) --cache-policy=<spec> / --cache-policy=local:r,remote:r (≠ vx’s --no-cache: no reads either) tests/cli.test.ts, tests/download-policy.test.ts
turbo run test -- --watch vx run test -- --watch (forwarded args are in the hash) tests/cli.test.ts
a lockfile change re-keys the packages whose dependencies moved ≠ nothing without a plugin; @vzn/vx-lockfile keys the same tests/lockfile-claim.test.ts, packages/vx-lockfile/tests/discover.test.ts
a package’s package.json is in its hash ≠ under @vzn/vx-lockfile, an input of every cached task packages/vx-lockfile/tests/discover.test.ts
--continue=dependencies-successful (default) --continue=deps-ok (default) tests/continue-taint.test.ts
--continue=always --continue (a task built on a failure runs but is never saved) tests/continue-taint.test.ts
--continue=never --continue=never tests/scheduler.test.ts
graceful shutdown (graceful_shutdown_test.rs) the signal received, a 2 s grace, SIGKILL; a second one skips the grace tests/signal-handling.test.ts
recursive turbo run refused (recursive_turbo_test.rs) VX_RUN_WORKSPACE marker; vx run in its own workspace is refused tests/recursive-run.test.ts
failures are never cached the same tests/execute-task.test.ts
--output-logs=errors-only, hash-only, none the same (full too; the default follows the run’s flow) tests/output-flow.test.ts
--summarize vx last --format json (the run history) tests/parity-turbo.test.ts
--profile --profile[=<path>] tests/run-artifacts.test.ts
--concurrency --concurrency <n | n%> tests/scheduler.test.ts
"persistent": true exec.persistent: { readyWhen } (readiness gates dependents) tests/persistent.test.ts, tests/dev.test.ts
"interactive": true exec.interactive: true (on a TTY: the terminal, alone; never cached) tests/interactive.test.ts, tests/terminal.unsafe.test.ts
turbo watch vx watch <task> tests/watch-rules.test.ts, tests/watch-loop.test.ts, tests/watch-signals.test.ts
turbo prune no equivalent —
--summarize + Vercel Remote Cache turboCache() in @vzn/vx-migrate (same wire, self-hosted or Vercel) packages/vx-migrate/tests/turbo-cache.test.ts
$TURBO_ROOT$/tsconfig.json input inputs.workspaceFiles: ['tsconfig.json'] (outputs too) tests/workspace-files.test.ts
globalDependencies, globalEnv, extends ≠ rejected: configs are TypeScript and compose by import docs/comparison.md § Likely-worth-adding item 4 (global inputs and env), § Config schema comparison “Target defaults / inheritance” (extends)
Nx vx Deep pin
nx run app:build vx run app#build tests/task-selection.test.ts
nx run app:build:production vx run app#build:production — under nx() or the migration a configuration is a task of its own; the default configuration is the base task packages/vx-migrate/tests/nx.test.ts
nx run-many -t build test vx run build test --all tests/task-selection.test.ts
nx run-many -t lint -p app ui vx run lint --filter app --filter ui tests/filter.test.ts
nx run-many -t lint -p tag:scope:web, project.json tags vx run lint --filter tag:scope:web, vx.config tags tests/project-tags.test.ts, tests/filter.test.ts
--exclude docs --filter '!docs' tests/filter.test.ts
nx affected (changed and dependents) --affected follows task edges (Nx 23.3’s NX_LEGACY_AFFECTED=false); --filter '...[<ref>]' is changed and dependents, [<ref>] the changed set tests/affected-dependents.test.ts
nx affected --base=main --affected=main tests/affected.test.ts
packages: ['packages/*', '!packages/fixtures'] (a negated workspace glob) the same: negations subtract in discovery tests/workspace.test.ts
nx affected with nothing changed exits 0 the same (nothing affected since <ref>) tests/affected.test.ts
--parallel=3 --concurrency 3 tests/scheduler.test.ts
--skipNxCache --force (reads off, writes on)
--nxBail --continue=never tests/scheduler.test.ts
nx show projects, nx show project app --json vx show, vx show app --format json tests/show-info.test.ts
nx graph vx run <task> --dry=json (tasks and deps) tests/cli.test.ts
"dependsOn": ["^build"] ({ projects: "dependencies" } deprecated) dependsOn: ['^build'] tests/task-graph.test.ts
{ projects: ["ui"], target: "build" } dependsOn: ['ui#build'] tests/dependency-spec.test.ts
"dependsOn": ["build-*"] (wildcards) dependsOn: ['check.*'], ['^build.*'] tests/wildcard-depends.test.ts
a target that only aggregates a task with no exec — a group; its members run, it is not a task tests/task-graph.test.ts, tests/tally.test.ts
{ "runtime": "node -v" } input inputs.runtime: ['node -v'] (workspaceRuntime at the root) tests/runtime-inputs.test.ts
{ "env": "NODE_ENV" } input inputs.env: ['NODE_ENV'] tests/env.test.ts
{workspaceRoot}/tsconfig.base.json input inputs.workspaceFiles: ['tsconfig.base.json'] tests/workspace-files.test.ts
"outputs" restored on a hit outputs.files (cleaned before exec and restore, ≠ additive) tests/orchestrator.test.ts
project.json / package.json changes re-hash the resolved config and the project package.json are in every key tests/config-staleness.test.ts, tests/task-hash.test.ts
failures are never cached the same tests/execute-task.test.ts
"continuous": true exec.persistent: { readyWhen } tests/persistent.test.ts
nx watch vx watch <task> tests/watch-rules.test.ts, tests/watch-loop.test.ts
Nx Cloud’s flaky-task flag local: same key, failed after it passed → the footer, vx info tests/flaky.test.ts, tests/failure-mode.test.ts
lockfile-aware affected / hashing (the daemon’s pruned lockfile) @vzn/vx-lockfile: per-project dependency closures, --affected follows packages/vx-lockfile/tests, tests/lockfile-claim.test.ts
nx reset vx cache clean tests/cache.test.ts, tests/cli.test.ts
namedInputs, targetDefaults, {projectRoot} tokens, .env auto-loading ≠ rejected: TypeScript configs compose by import docs/comparison.md § Explicitly rejected (namedInputs, targetDefaults), § Deliberate divergences › Config schema (tokens), › Env handling (.env)
Nx Cloud (agents, dashboard, DTE) ≠ not in this repo: the executor / cache / telemetry seams, @vzn/vx-reapi packages/vx-reapi/tests
  • Every row’s vx spelling is documented in docs/cli.md or docs/schema.md; the parity suite asserts the row against the real CLI, the deep pin asserts the edge cases.
  • The two ≠ rows that change what a command selects or leaves on disk are decisions recorded in docs/comparison.md with the reasoning: union not intersection under § Deliberate divergences, cleaned-not-additive outputs under § Where vx is ahead.
  • What neither runner does and vx does — the resolved-config hash, vx lock (runs read only the lock), vx why, restore-ahead scheduling — is docs/comparison.md § Where vx is ahead; the parity map lists only what a user of the other runner would look for.
Turborepo, Nx and other product names are trademarks of their owners. vx is not affiliated with or endorsed by them.