Summary
vp run resolves a lot before it executes anything: which packages the selection
picks, each package's task, the commands after compound splitting, the dependsOn
edges and their order, and whether a task would replay from cache. None of it can be
asked for. A --plan flag would print that resolution and exit, in text or JSON, the
way pip install --dry-run --report -, make -n, turbo run --dry=json and
bazel aquery do for their runs.
Motivation
Anything built around a task runner eventually needs to know what a run would do:
- a pre-tool hook for coding agents that decides whether a shell command is about to
start something expensive (a type checker, a browser suite) before letting it run;
- CI that picks jobs from the task graph instead of from path globs;
- a repository doctor or lint rule that checks task wiring ("does this
dependsOn
reach the task it means to?", "which packages drop out of -r for this task?");
- a person or an agent asking "what does
vp run -r test actually run, in what order?"
Today the only route is to load every vite.config.* yourself and re-implement the
selection rules, the dependsOn forms (task, pkg#task, { task, from }), compound
splitting and ordering, then keep that copy in step with Vite+. The existing surfaces
do not cover it: -v and --last-details describe a run after it ran, the interactive
selector is for a person choosing one task, and loadConfigFromFile resolves one
config with no selection, no edges and no cache status.
Prior art
pip install --dry-run --report -: resolve, print the JSON report, install nothing.
make -n / --dry-run: print the commands that would run.
just --dry-run, gradle --dry-run: list the recipes / tasks that would execute.
turbo run <task> --dry / --dry=json: per task, taskId, package, command,
hash, inputs, outputs, dependencies, dependents, cache status.
bazel aquery: the actions (commands) a build would execute.
nx run-many -t <task> --graph=stdout: the task graph the command would execute.
Proposal
vp run --plan [--json] <the usual selection: -r | -t | -w | --filter … | [pkg#]task> [--ignore-depends-on]
Same resolution as a real run, then print and exit 0; execute nothing, write no cache.
Text output: one line per task in execution order, package task: command, with
← dep markers for edges. JSON output:
{
"selection": { "packages": ["@acme/app", "@acme/core"], "task": "build" },
"tasks": [
{
"id": "@acme/core#build",
"package": "@acme/core",
"task": "build",
"commands": ["tsdown", "node scripts/emit-types.mjs"],
"dependsOn": [],
"cache": "replay"
},
{
"id": "@acme/app#build",
"package": "@acme/app",
"task": "build",
"commands": ["vp build"],
"dependsOn": [{ "id": "@acme/core#build", "kind": "topological" }],
"cache": "execute"
}
]
}
commands are the sub-tasks after compound splitting (&& or array form), since
those are what Vite Task runs and caches.
dependsOn entries carry kind: explicit (a dependsOn entry) or topological
(package-graph order), because the two are what a wiring check needs to tell apart.
cache is replay, execute or disabled when the plan already knows it; if input
hashing is too costly to do just to answer --plan, unknown is fine and an opt-in
--plan=hash can come later.
Non-goals: not a visualisation (#1182 asks for that; --plan --json is an input it
could consume), and not a change to --last-details.
How we hit it
In a 128-package workspace we wrote an agent hook that needed one bit: does this
vp run … start a TypeScript compiler? Answering it faithfully meant loading all 128
configs (about 1.8 s per call) and carrying ~500 lines that re-implemented selection
and dependsOn. We replaced that with a hard-coded list of task names, which is exactly
what --plan would retire; a lint rule in the same repo reads run.tasks out of the
config AST for the same reason. Happy to test a preview against that workspace.
Related
Summary
vp runresolves a lot before it executes anything: which packages the selectionpicks, each package's task, the commands after compound splitting, the
dependsOnedges and their order, and whether a task would replay from cache. None of it can be
asked for. A
--planflag would print that resolution and exit, in text or JSON, theway
pip install --dry-run --report -,make -n,turbo run --dry=jsonandbazel aquerydo for their runs.Motivation
Anything built around a task runner eventually needs to know what a run would do:
start something expensive (a type checker, a browser suite) before letting it run;
dependsOnreach the task it means to?", "which packages drop out of
-rfor this task?");vp run -r testactually run, in what order?"Today the only route is to load every
vite.config.*yourself and re-implement theselection rules, the
dependsOnforms (task,pkg#task,{ task, from }), compoundsplitting and ordering, then keep that copy in step with Vite+. The existing surfaces
do not cover it:
-vand--last-detailsdescribe a run after it ran, the interactiveselector is for a person choosing one task, and
loadConfigFromFileresolves oneconfig with no selection, no edges and no cache status.
Prior art
pip install --dry-run --report -: resolve, print the JSON report, install nothing.make -n/--dry-run: print the commands that would run.just --dry-run,gradle --dry-run: list the recipes / tasks that would execute.turbo run <task> --dry/--dry=json: per task,taskId,package,command,hash,inputs,outputs,dependencies,dependents, cache status.bazel aquery: the actions (commands) a build would execute.nx run-many -t <task> --graph=stdout: the task graph the command would execute.Proposal
Same resolution as a real run, then print and exit 0; execute nothing, write no cache.
Text output: one line per task in execution order,
package task: command, with← depmarkers for edges. JSON output:{ "selection": { "packages": ["@acme/app", "@acme/core"], "task": "build" }, "tasks": [ { "id": "@acme/core#build", "package": "@acme/core", "task": "build", "commands": ["tsdown", "node scripts/emit-types.mjs"], "dependsOn": [], "cache": "replay" }, { "id": "@acme/app#build", "package": "@acme/app", "task": "build", "commands": ["vp build"], "dependsOn": [{ "id": "@acme/core#build", "kind": "topological" }], "cache": "execute" } ] }commandsare the sub-tasks after compound splitting (&&or array form), sincethose are what Vite Task runs and caches.
dependsOnentries carrykind:explicit(adependsOnentry) ortopological(package-graph order), because the two are what a wiring check needs to tell apart.
cacheisreplay,executeordisabledwhen the plan already knows it; if inputhashing is too costly to do just to answer
--plan,unknownis fine and an opt-in--plan=hashcan come later.Non-goals: not a visualisation (#1182 asks for that;
--plan --jsonis an input itcould consume), and not a change to
--last-details.How we hit it
In a 128-package workspace we wrote an agent hook that needed one bit: does this
vp run …start a TypeScript compiler? Answering it faithfully meant loading all 128configs (about 1.8 s per call) and carrying ~500 lines that re-implemented selection
and
dependsOn. We replaced that with a hard-coded list of task names, which is exactlywhat
--planwould retire; a lint rule in the same repo readsrun.tasksout of theconfig AST for the same reason. Happy to test a preview against that workspace.
Related
need the same resolved list;
--planis its static, machine-readable form.dependsOnobject form cannot reach a task behind a package that lacks it; the query can vite-task#738 (dependsOnobject form cannot reach a task behind apackage that lacks it) is the kind of wiring fact a plan with labelled edges shows
directly.