Skip to content

Commit 04b9b38

Browse files
branchseerclaude
andcommitted
fix: hooks are not expanded recursively (one level deep, matching npm)
When `vt run test` triggers `pretest` as a hook, `pretest` itself does not look for `prepretest`. This matches npm's behavior where lifecycle hooks are always one level deep. Implemented via an `expand_hooks: bool` flag on `PlanContext` that is set to `false` when planning any hook script. Added `prepretest` to the script-hooks test fixture to prove that `vt run test` correctly produces [pretest, test, posttest] without `prepretest`, while `vt run pretest` directly does expand `prepretest`. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 18fa8ff commit 04b9b38

File tree

6 files changed

+84
-2
lines changed

6 files changed

+84
-2
lines changed

crates/vite_task_plan/src/context.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ 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,
5157
}
5258

5359
impl<'a> PlanContext<'a> {
@@ -70,6 +76,7 @@ impl<'a> PlanContext<'a> {
7076
extra_args: Arc::default(),
7177
resolved_global_cache,
7278
parent_query,
79+
expand_hooks: true,
7380
}
7481
}
7582

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

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+
153168
pub fn duplicate(&mut self) -> PlanContext<'_> {
154169
PlanContext {
155170
workspace_path: self.workspace_path,
@@ -161,6 +176,7 @@ impl<'a> PlanContext<'a> {
161176
extra_args: Arc::clone(&self.extra_args),
162177
resolved_global_cache: self.resolved_global_cache,
163178
parent_query: Arc::clone(&self.parent_query),
179+
expand_hooks: self.expand_hooks,
164180
}
165181
}
166182
}

crates/vite_task_plan/src/plan.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,11 @@ async fn plan_task_as_execution_node(
101101
let mut items = Vec::<ExecutionItem>::new();
102102

103103
// Expand pre/post hooks (`preX`/`postX`) for package.json scripts.
104+
// 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.
104106
// Resolve the flag once before any mutable borrow of `context` (duplicate() needs &mut).
105-
let pre_post_scripts_enabled = context.indexed_task_graph().pre_post_scripts_enabled();
107+
let pre_post_scripts_enabled =
108+
context.expand_hooks() && context.indexed_task_graph().pre_post_scripts_enabled();
106109
let pre_hook_idx = if pre_post_scripts_enabled {
107110
context.indexed_task_graph().get_script_hook(task_node_index, "pre")
108111
} else {
@@ -112,6 +115,7 @@ async fn plan_task_as_execution_node(
112115
let mut pre_context = context.duplicate();
113116
// Extra args (e.g. `vt run test --coverage`) must not be forwarded to hooks.
114117
pre_context.set_extra_args(Arc::new([]));
118+
pre_context.set_expand_hooks(false);
115119
let pre_execution =
116120
Box::pin(plan_task_as_execution_node(pre_hook_idx, pre_context)).await?;
117121
items.extend(pre_execution.items);
@@ -384,6 +388,7 @@ async fn plan_task_as_execution_node(
384388
let mut post_context = context.duplicate();
385389
// Extra args must not be forwarded to hooks.
386390
post_context.set_extra_args(Arc::new([]));
391+
post_context.set_expand_hooks(false);
387392
let post_execution =
388393
Box::pin(plan_task_as_execution_node(post_hook_idx, post_context)).await?;
389394
items.extend(post_execution.items);

crates/vite_task_plan/tests/plan_snapshots/fixtures/script-hooks/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"name": "@test/script-hooks",
33
"scripts": {
4+
"prepretest": "echo prepretest",
45
"pretest": "echo pretest",
56
"test": "echo test",
67
"posttest": "echo posttest",

crates/vite_task_plan/tests/plan_snapshots/fixtures/script-hooks/snapshots.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ name = "build runs with pre hook only"
99
args = ["run", "build"]
1010

1111
[[plan]]
12-
name = "pretest directly has no hooks"
12+
name = "pretest directly expands prepretest but not when called as hook"
1313
args = ["run", "pretest"]
1414

1515
[[plan]]

crates/vite_task_plan/tests/plan_snapshots/fixtures/script-hooks/snapshots/query - pretest directly has no hooks.snap renamed to crates/vite_task_plan/tests/plan_snapshots/fixtures/script-hooks/snapshots/query - pretest directly expands prepretest but not when called as hook.snap

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,32 @@ input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/script-hooks
2020
"package_path": "<workspace>/"
2121
},
2222
"items": [
23+
{
24+
"execution_item_display": {
25+
"task_display": {
26+
"package_name": "@test/script-hooks",
27+
"task_name": "prepretest",
28+
"package_path": "<workspace>/"
29+
},
30+
"command": "echo prepretest",
31+
"and_item_index": null,
32+
"cwd": "<workspace>/"
33+
},
34+
"kind": {
35+
"Leaf": {
36+
"InProcess": {
37+
"kind": {
38+
"Echo": {
39+
"strings": [
40+
"prepretest"
41+
],
42+
"trailing_newline": true
43+
}
44+
}
45+
}
46+
}
47+
}
48+
},
2349
{
2450
"execution_item_display": {
2551
"task_display": {

crates/vite_task_plan/tests/plan_snapshots/fixtures/script-hooks/snapshots/task graph.snap

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,40 @@ input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/script-hooks
106106
},
107107
"neighbors": []
108108
},
109+
{
110+
"key": [
111+
"<workspace>/",
112+
"prepretest"
113+
],
114+
"node": {
115+
"task_display": {
116+
"package_name": "@test/script-hooks",
117+
"task_name": "prepretest",
118+
"package_path": "<workspace>/"
119+
},
120+
"resolved_config": {
121+
"command": "echo prepretest",
122+
"resolved_options": {
123+
"cwd": "<workspace>/",
124+
"cache_config": {
125+
"env_config": {
126+
"fingerprinted_envs": [],
127+
"untracked_env": [
128+
"<default untracked envs>"
129+
]
130+
},
131+
"input_config": {
132+
"includes_auto": true,
133+
"positive_globs": [],
134+
"negative_globs": []
135+
}
136+
}
137+
}
138+
},
139+
"source": "PackageJsonScript"
140+
},
141+
"neighbors": []
142+
},
109143
{
110144
"key": [
111145
"<workspace>/",

0 commit comments

Comments
 (0)