Skip to content

Commit 785fac9

Browse files
authored
fix(cli): fall back to NAPI when plugins are present in vite config (#1213)
resolve #1211 When a Vite plugin adds `run.tasks` via the `config` hook, `VitePlusConfigLoader` was skipping the NAPI-based `resolveConfig()`, because the static analyzer found no `run` field and returned `None` immediately, without ever executing plugin hooks. Fix by checking for `plugins` before `run`: if plugins are present, always fall back to NAPI so that Vite's `resolveConfig()` runs plugin `config` hooks. When no plugins are present, static analysis results remain trustworthy and the fast path is preserved.
1 parent c682cb1 commit 785fac9

5 files changed

Lines changed: 66 additions & 19 deletions

File tree

packages/cli/binding/src/cli.rs

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -620,25 +620,37 @@ impl UserConfigLoader for VitePlusConfigLoader {
620620
) -> anyhow::Result<Option<UserRunConfig>> {
621621
// Try static config extraction first (no JS runtime needed)
622622
let static_fields = vite_static_config::resolve_static_config(package_path);
623-
match static_fields.get("run") {
624-
Some(vite_static_config::FieldValue::Json(run_value)) => {
625-
tracing::debug!(
626-
"Using statically extracted run config for {}",
627-
package_path.as_path().display()
628-
);
629-
let run_config: UserRunConfig = serde_json::from_value(run_value)?;
630-
return Ok(Some(run_config));
631-
}
632-
Some(vite_static_config::FieldValue::NonStatic) => {
633-
// `run` field exists (or may exist via a spread) — fall back to NAPI
634-
tracing::debug!(
635-
"run config is not statically analyzable for {}, falling back to NAPI",
636-
package_path.as_path().display()
637-
);
638-
}
639-
None => {
640-
// Config was analyzed successfully and `run` field is definitively absent
641-
return Ok(None);
623+
624+
// If plugins are present, a plugin's config hook may add/modify the `run`
625+
// field at runtime, so we must fall back to NAPI-based resolution which
626+
// calls Vite's resolveConfig() and executes plugin hooks.
627+
if static_fields.get("plugins").is_some() {
628+
tracing::debug!(
629+
"plugins detected for {}, falling back to NAPI to run config hooks",
630+
package_path.as_path().display()
631+
);
632+
} else {
633+
// No plugins — static analysis results are trustworthy
634+
match static_fields.get("run") {
635+
Some(vite_static_config::FieldValue::Json(run_value)) => {
636+
tracing::debug!(
637+
"Using statically extracted run config for {}",
638+
package_path.as_path().display()
639+
);
640+
let run_config: UserRunConfig = serde_json::from_value(run_value)?;
641+
return Ok(Some(run_config));
642+
}
643+
Some(vite_static_config::FieldValue::NonStatic) => {
644+
// `run` field exists (or may exist via a spread) — fall back to NAPI
645+
tracing::debug!(
646+
"run config is not statically analyzable for {}, falling back to NAPI",
647+
package_path.as_path().display()
648+
);
649+
}
650+
None => {
651+
// Config was analyzed successfully and `run` field is definitively absent
652+
return Ok(None);
653+
}
642654
}
643655
}
644656

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "vite-config-task-from-plugin-test",
3+
"private": true
4+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
> # Test that tasks added by a plugin config hook are recognized by vp run
2+
> vp run build:prod
3+
$ echo 'build:prod from plugin config hook' ⊘ cache disabled
4+
build:prod from plugin config hook
5+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"commands": [
3+
"# Test that tasks added by a plugin config hook are recognized by vp run",
4+
"vp run build:prod"
5+
]
6+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
function myTaskPlugin() {
2+
return {
3+
name: 'my-task-plugin',
4+
config() {
5+
return {
6+
run: {
7+
tasks: {
8+
'build:prod': {
9+
command: "echo 'build:prod from plugin config hook'",
10+
},
11+
},
12+
},
13+
};
14+
},
15+
};
16+
}
17+
18+
export default {
19+
plugins: [myTaskPlugin()],
20+
};

0 commit comments

Comments
 (0)