Skip to content

Commit 9656e8b

Browse files
branchseerclaude
andcommitted
fix: hooks from nested vt run calls within hook scripts are expanded
When a hook script's command is `vt run someScript`, `someScript`'s own pre/post hooks should still be expanded. Previously, the `expand_hooks` flag (which suppresses recursive hook expansion) incorrectly propagated into nested `plan_query_request` calls, preventing `prescriptInHook` from being found when `scriptInHook` was called via `vt run` from a hook. The flag is now a `with_hooks: bool` parameter of `plan_task_as_execution_node` rather than a field on `PlanContext`. `plan_query_request` always passes `true`, correctly scoping the one-level-deep restriction to the single hook call rather than its entire subtree. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 04b9b38 commit 9656e8b

File tree

6 files changed

+307
-24
lines changed

6 files changed

+307
-24
lines changed

crates/vite_task_plan/src/context.rs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,6 @@ pub struct PlanContext<'a> {
4848
/// The query that caused the current expansion.
4949
/// Used by the skip rule to detect and skip duplicate nested expansions.
5050
parent_query: Arc<TaskQuery>,
51-
52-
/// Whether pre/post hook scripts should be expanded for the current task.
53-
///
54-
/// Set to `false` when planning a hook script itself, so that hooks are
55-
/// never expanded more than one level deep (matching npm behavior).
56-
expand_hooks: bool,
5751
}
5852

5953
impl<'a> PlanContext<'a> {
@@ -76,7 +70,6 @@ impl<'a> PlanContext<'a> {
7670
extra_args: Arc::default(),
7771
resolved_global_cache,
7872
parent_query,
79-
expand_hooks: true,
8073
}
8174
}
8275

@@ -157,14 +150,6 @@ impl<'a> PlanContext<'a> {
157150
self.task_call_stack.last().map(|(idx, _)| *idx)
158151
}
159152

160-
pub const fn expand_hooks(&self) -> bool {
161-
self.expand_hooks
162-
}
163-
164-
pub const fn set_expand_hooks(&mut self, expand_hooks: bool) {
165-
self.expand_hooks = expand_hooks;
166-
}
167-
168153
pub fn duplicate(&mut self) -> PlanContext<'_> {
169154
PlanContext {
170155
workspace_path: self.workspace_path,
@@ -176,7 +161,6 @@ impl<'a> PlanContext<'a> {
176161
extra_args: Arc::clone(&self.extra_args),
177162
resolved_global_cache: self.resolved_global_cache,
178163
parent_query: Arc::clone(&self.parent_query),
179-
expand_hooks: self.expand_hooks,
180164
}
181165
}
182166
}

crates/vite_task_plan/src/plan.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,15 @@ fn effective_cache_config(
7979
if enabled { task_cache_config.cloned() } else { None }
8080
}
8181

82+
/// - `with_hooks`: whether to look up `preX`/`postX` lifecycle hooks for this task.
83+
/// `false` when the task itself is being executed as a hook, so that hooks are
84+
/// never expanded more than one level deep (matching npm behavior).
8285
#[expect(clippy::too_many_lines, reason = "sequential planning steps are clearer in one function")]
8386
#[expect(clippy::future_not_send, reason = "PlanContext contains !Send dyn PlanRequestParser")]
8487
async fn plan_task_as_execution_node(
8588
task_node_index: TaskNodeIndex,
8689
mut context: PlanContext<'_>,
90+
with_hooks: bool,
8791
) -> Result<TaskExecution, Error> {
8892
// Check for recursions in the task call stack.
8993
context.check_recursion(task_node_index)?;
@@ -102,10 +106,10 @@ async fn plan_task_as_execution_node(
102106

103107
// Expand pre/post hooks (`preX`/`postX`) for package.json scripts.
104108
// Hooks are never expanded more than one level deep (matching npm behavior): when planning a
105-
// hook script, `expand_hooks` is false so it won't look for its own pre/post hooks.
109+
// hook script, `with_hooks` is false so it won't look for its own pre/post hooks.
106110
// Resolve the flag once before any mutable borrow of `context` (duplicate() needs &mut).
107111
let pre_post_scripts_enabled =
108-
context.expand_hooks() && context.indexed_task_graph().pre_post_scripts_enabled();
112+
with_hooks && context.indexed_task_graph().pre_post_scripts_enabled();
109113
let pre_hook_idx = if pre_post_scripts_enabled {
110114
context.indexed_task_graph().get_script_hook(task_node_index, "pre")
111115
} else {
@@ -115,9 +119,8 @@ async fn plan_task_as_execution_node(
115119
let mut pre_context = context.duplicate();
116120
// Extra args (e.g. `vt run test --coverage`) must not be forwarded to hooks.
117121
pre_context.set_extra_args(Arc::new([]));
118-
pre_context.set_expand_hooks(false);
119122
let pre_execution =
120-
Box::pin(plan_task_as_execution_node(pre_hook_idx, pre_context)).await?;
123+
Box::pin(plan_task_as_execution_node(pre_hook_idx, pre_context, false)).await?;
121124
items.extend(pre_execution.items);
122125
}
123126

@@ -388,9 +391,8 @@ async fn plan_task_as_execution_node(
388391
let mut post_context = context.duplicate();
389392
// Extra args must not be forwarded to hooks.
390393
post_context.set_extra_args(Arc::new([]));
391-
post_context.set_expand_hooks(false);
392394
let post_execution =
393-
Box::pin(plan_task_as_execution_node(post_hook_idx, post_context)).await?;
395+
Box::pin(plan_task_as_execution_node(post_hook_idx, post_context, false)).await?;
394396
items.extend(post_execution.items);
395397
}
396398

@@ -685,8 +687,9 @@ pub async fn plan_query_request(
685687
if Some(task_index) == pruned_task {
686688
continue;
687689
}
688-
let task_execution =
689-
plan_task_as_execution_node(task_index, context.duplicate()).boxed_local().await?;
690+
let task_execution = plan_task_as_execution_node(task_index, context.duplicate(), true)
691+
.boxed_local()
692+
.await?;
690693
execution_node_indices_by_task_index
691694
.insert(task_index, inner_graph.add_node(task_execution));
692695
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "@test/script-hooks-nested-run",
3+
"scripts": {
4+
"prescriptInHook": "echo prescriptInHook",
5+
"scriptInHook": "echo scriptInHook",
6+
"pretest": "vt run scriptInHook",
7+
"test": "echo test"
8+
}
9+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Tests that hooks of scripts called via `vt run` within a hook are properly expanded.
2+
# When `pretest` (a hook of `test`) runs `vt run scriptInHook`, the `prescriptInHook`
3+
# hook of `scriptInHook` should still be found and executed.
4+
5+
[[plan]]
6+
name = "prescriptInHook runs when scriptInHook is called from a hook"
7+
args = ["run", "test"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
---
2+
source: crates/vite_task_plan/tests/plan_snapshots/main.rs
3+
expression: "&plan_json"
4+
info:
5+
args:
6+
- run
7+
- test
8+
input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/script-hooks-nested-run
9+
---
10+
[
11+
{
12+
"key": [
13+
"<workspace>/",
14+
"test"
15+
],
16+
"node": {
17+
"task_display": {
18+
"package_name": "@test/script-hooks-nested-run",
19+
"task_name": "test",
20+
"package_path": "<workspace>/"
21+
},
22+
"items": [
23+
{
24+
"execution_item_display": {
25+
"task_display": {
26+
"package_name": "@test/script-hooks-nested-run",
27+
"task_name": "pretest",
28+
"package_path": "<workspace>/"
29+
},
30+
"command": "vt run scriptInHook",
31+
"and_item_index": null,
32+
"cwd": "<workspace>/"
33+
},
34+
"kind": {
35+
"Expanded": [
36+
{
37+
"key": [
38+
"<workspace>/",
39+
"scriptInHook"
40+
],
41+
"node": {
42+
"task_display": {
43+
"package_name": "@test/script-hooks-nested-run",
44+
"task_name": "scriptInHook",
45+
"package_path": "<workspace>/"
46+
},
47+
"items": [
48+
{
49+
"execution_item_display": {
50+
"task_display": {
51+
"package_name": "@test/script-hooks-nested-run",
52+
"task_name": "prescriptInHook",
53+
"package_path": "<workspace>/"
54+
},
55+
"command": "echo prescriptInHook",
56+
"and_item_index": null,
57+
"cwd": "<workspace>/"
58+
},
59+
"kind": {
60+
"Leaf": {
61+
"InProcess": {
62+
"kind": {
63+
"Echo": {
64+
"strings": [
65+
"prescriptInHook"
66+
],
67+
"trailing_newline": true
68+
}
69+
}
70+
}
71+
}
72+
}
73+
},
74+
{
75+
"execution_item_display": {
76+
"task_display": {
77+
"package_name": "@test/script-hooks-nested-run",
78+
"task_name": "scriptInHook",
79+
"package_path": "<workspace>/"
80+
},
81+
"command": "echo scriptInHook",
82+
"and_item_index": null,
83+
"cwd": "<workspace>/"
84+
},
85+
"kind": {
86+
"Leaf": {
87+
"InProcess": {
88+
"kind": {
89+
"Echo": {
90+
"strings": [
91+
"scriptInHook"
92+
],
93+
"trailing_newline": true
94+
}
95+
}
96+
}
97+
}
98+
}
99+
}
100+
]
101+
},
102+
"neighbors": []
103+
}
104+
]
105+
}
106+
},
107+
{
108+
"execution_item_display": {
109+
"task_display": {
110+
"package_name": "@test/script-hooks-nested-run",
111+
"task_name": "test",
112+
"package_path": "<workspace>/"
113+
},
114+
"command": "echo test",
115+
"and_item_index": null,
116+
"cwd": "<workspace>/"
117+
},
118+
"kind": {
119+
"Leaf": {
120+
"InProcess": {
121+
"kind": {
122+
"Echo": {
123+
"strings": [
124+
"test"
125+
],
126+
"trailing_newline": true
127+
}
128+
}
129+
}
130+
}
131+
}
132+
}
133+
]
134+
},
135+
"neighbors": []
136+
}
137+
]

0 commit comments

Comments
 (0)