Skip to content
27 changes: 15 additions & 12 deletions crates/vite_task/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub enum CacheSubcommand {
}

/// Flags that control how a `run` command selects tasks.
#[derive(Debug, Clone, clap::Args)]
#[derive(Debug, Clone, PartialEq, Eq, clap::Args)]
pub struct RunFlags {
#[clap(flatten)]
pub package_query: PackageQueryArgs,
Expand All @@ -36,7 +36,7 @@ pub struct RunFlags {
///
/// Contains the `--last-details` flag which is resolved into a separate
/// `ResolvedCommand::RunLastDetails` variant internally.
#[derive(Debug, clap::Args)]
#[derive(Debug, clap::Parser)]
pub struct RunCommand {
/// `packageName#taskName` or `taskName`. If omitted, lists all available tasks.
pub(crate) task_specifier: Option<TaskSpecifier>,
Expand Down Expand Up @@ -109,7 +109,7 @@ pub enum ResolvedCommand {
///
/// Does not contain `last_details` — that case is represented by
/// [`ResolvedCommand::RunLastDetails`] instead.
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq)]
pub struct ResolvedRunCommand {
/// `packageName#taskName` or `taskName`. If omitted, lists all available tasks.
pub task_specifier: Option<TaskSpecifier>,
Expand Down Expand Up @@ -151,21 +151,24 @@ impl ResolvedRunCommand {
pub fn into_query_plan_request(
self,
cwd: &Arc<AbsolutePath>,
) -> Result<QueryPlanRequest, CLITaskQueryError> {
) -> Result<(QueryPlanRequest, bool), CLITaskQueryError> {
let task_specifier = self.task_specifier.ok_or(CLITaskQueryError::MissingTaskSpecifier)?;

let (package_query, _is_cwd_only) =
let (package_query, is_cwd_only) =
self.flags.package_query.into_package_query(task_specifier.package_name, cwd)?;

let include_explicit_deps = !self.flags.ignore_depends_on;

Ok(QueryPlanRequest {
query: TaskQuery {
package_query,
task_name: task_specifier.task_name,
include_explicit_deps,
Ok((
QueryPlanRequest {
query: TaskQuery {
package_query,
task_name: task_specifier.task_name,
include_explicit_deps,
},
plan_options: PlanOptions { extra_args: self.additional_args.into() },
},
plan_options: PlanOptions { extra_args: self.additional_args.into() },
})
is_cwd_only,
))
}
}
68 changes: 49 additions & 19 deletions crates/vite_task/src/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::{ffi::OsStr, fmt::Debug, io::IsTerminal, sync::Arc};

use cache::ExecutionCache;
pub use cache::{CacheMiss, FingerprintMismatch};
use clap::Parser as _;
use once_cell::sync::OnceCell;
pub use reporter::ExitStatus;
use reporter::{
Expand Down Expand Up @@ -109,7 +110,9 @@ impl vite_task_plan::PlanRequestParser for PlanRequestParser<'_> {
}
ResolvedCommand::Run(run_command) => {
match run_command.into_query_plan_request(&command.cwd) {
Ok(query_plan_request) => Ok(Some(PlanRequest::Query(query_plan_request))),
Ok((query_plan_request, _)) => {
Ok(Some(PlanRequest::Query(query_plan_request)))
}
Err(crate::cli::CLITaskQueryError::MissingTaskSpecifier) => {
Ok(Some(PlanRequest::Synthetic(
command.to_synthetic_plan_request(UserCacheConfig::disabled()),
Expand Down Expand Up @@ -240,24 +243,34 @@ impl<'a> Session<'a> {
let is_interactive =
std::io::stdin().is_terminal() && std::io::stdout().is_terminal();

// Detect bare `vp run` (no task, no flags, no extra args)
let bare = RunCommand::parse_from(["vp"]).into_resolved();
let is_bare = run_command == bare;

// Save task name and flags before consuming run_command
let task_name = run_command.task_specifier.as_ref().map(|s| s.task_name.clone());
let show_details = run_command.flags.verbose;
let flags = run_command.flags.clone();
let additional_args = run_command.additional_args.clone();

match self.plan_from_cli_run_resolved(cwd, run_command).await {
Ok(ref graph) if graph.node_count() == 0 => {
// No tasks matched the query — show task selector / "did you mean"
self.handle_no_task(
is_interactive,
task_name.as_deref(),
flags,
additional_args,
)
.await
Ok((ref graph, is_cwd_only)) if graph.node_count() == 0 => {
if is_cwd_only {
self.handle_no_task(
is_interactive,
task_name.as_deref(),
flags,
additional_args,
)
.await
} else {
return Err(vite_task_plan::Error::NoTasksMatched(
task_name.unwrap_or_default(),
)
.into());
}
}
Ok(graph) => {
Ok((graph, _)) => {
let builder = LabeledReporterBuilder::new(
self.workspace_path(),
Box::new(tokio::io::stdout()),
Expand All @@ -271,7 +284,11 @@ impl<'a> Session<'a> {
.unwrap_or(ExitStatus::SUCCESS))
}
Err(err) if err.is_missing_task_specifier() => {
self.handle_no_task(is_interactive, None, flags, additional_args).await
if is_bare {
self.handle_no_task(is_interactive, None, flags, additional_args).await
} else {
return Err(vite_task_plan::Error::MissingTaskSpecifier.into());
}
}
Err(err) => Err(err.into()),
}
Expand Down Expand Up @@ -387,8 +404,19 @@ impl<'a> Session<'a> {
};
};

// Interactive: run the selected task
// Interactive: print selected task and run it
let selected_label = &select_items[selected_index].label;
{
use std::io::Write as _;

use owo_colors::{OwoColorize as _, Stream};
writeln!(
stdout,
"{}{}",
"Selected task: ".if_supports_color(Stream::Stdout, |s| s.bold()),
selected_label,
)?;
}
let task_specifier = TaskSpecifier::parse_raw(selected_label);
let show_details = flags.verbose;
let run_command = RunCommand {
Expand Down Expand Up @@ -578,7 +606,8 @@ impl<'a> Session<'a> {
cwd: Arc<AbsolutePath>,
command: RunCommand,
) -> Result<ExecutionGraph, vite_task_plan::Error> {
self.plan_from_cli_run_resolved(cwd, command.into_resolved()).await
let (graph, _) = self.plan_from_cli_run_resolved(cwd, command.into_resolved()).await?;
Ok(graph)
}

/// Internal: plans execution from a resolved run command.
Expand All @@ -591,9 +620,9 @@ impl<'a> Session<'a> {
&mut self,
cwd: Arc<AbsolutePath>,
command: crate::cli::ResolvedRunCommand,
) -> Result<ExecutionGraph, vite_task_plan::Error> {
let query_plan_request = match command.into_query_plan_request(&cwd) {
Ok(query_plan_request) => query_plan_request,
) -> Result<(ExecutionGraph, bool), vite_task_plan::Error> {
let (query_plan_request, is_cwd_only) = match command.into_query_plan_request(&cwd) {
Ok(result) => result,
Err(crate::cli::CLITaskQueryError::MissingTaskSpecifier) => {
return Err(vite_task_plan::Error::MissingTaskSpecifier);
}
Expand All @@ -606,14 +635,15 @@ impl<'a> Session<'a> {
});
}
};
vite_task_plan::plan_query(
let graph = vite_task_plan::plan_query(
query_plan_request,
&self.workspace_path,
&cwd,
&self.envs,
&mut self.plan_request_parser,
&mut self.lazy_task_graph,
)
.await
.await?;
Ok((graph, is_cwd_only))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ Search task (↑/↓ to move, enter to select):
lib#build: echo build lib
@ write-key: enter
$ vp run ⊘ cache disabled
Selected task: hello
$ echo hello from root ⊘ cache disabled
hello from root
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,6 @@ Search task (↑/↓ to move, enter to select):
> long-cmd: echo aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa…
test: echo test app
@ write-key: enter
Selected task: long-cmd
~/packages/app$ echo aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ⊘ cache disabled
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ steps = [
"echo '' | vp run buid",
]

# Non-interactive: typo with -r flag
# Non-interactive: typo with -r flag errors (not cwd_only)
[[e2e]]
name = "non-interactive did you mean with recursive"
name = "non-interactive recursive typo errors"
steps = [
"echo '' | vp run -r buid",
]
Expand Down Expand Up @@ -51,20 +51,20 @@ steps = [
{ command = "vp run", interactions = [{ "expect-milestone" = "task-select::0" }, { "write" = "lin" }, { "expect-milestone" = "task-select:lin:0" }, { "write-key" = "escape" }, { "expect-milestone" = "task-select::0" }, { "write-key" = "enter" }] },
]

# Interactive: -r flag preserved
# -r flag without task errors (not bare)
[[e2e]]
name = "interactive select with recursive"
name = "recursive without task errors"
cwd = "packages/app"
steps = [
{ command = "vp run -r", interactions = [{ "expect-milestone" = "task-select::0" }, { "write-key" = "enter" }] },
"vp run -r",
]

# Interactive: -t flag + typo
# -t flag + typo errors (not cwd_only)
[[e2e]]
name = "interactive select with typo and transitive"
name = "transitive typo errors"
cwd = "packages/app"
steps = [
{ command = "vp run -t buid", interactions = [{ "expect-milestone" = "task-select:buid:0" }, { "write-key" = "enter" }] },
"vp run -t buid",
]

# Interactive: scroll down past visible page, then select a task beyond the initial viewport
Expand Down Expand Up @@ -131,3 +131,19 @@ name = "typo in task script fails without list"
steps = [
"vp run run-typo-task",
]

# --verbose without task: not bare, errors with "no task specifier provided"
[[e2e]]
name = "verbose without task errors"
cwd = "packages/app"
steps = [
"vp run --verbose",
]

# --verbose with typo: is_cwd_only is true, shows interactive selector
[[e2e]]
name = "verbose with typo enters selector"
cwd = "packages/app"
steps = [
{ command = "vp run buid --verbose", interactions = [{ "expect-milestone" = "task-select:buid:0" }, { "write-key" = "enter" }] },
]
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,6 @@ Search task (↑/↓ to move, enter to select):
task-select-test#format: echo format root
(…3 more)
@ write-key: enter
Selected task: build
~/packages/app$ echo build app ⊘ cache disabled
build app
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,6 @@ Search task (↑/↓ to move, enter to select):
task-select-test#format: echo format root
(…3 more)
@ write-key: enter
Selected task: build
~/packages/app$ echo build app ⊘ cache disabled
build app
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,6 @@ Search task (↑/↓ to move, enter to select):
task-select-test#format: echo format root
(…3 more)
@ write-key: enter
Selected task: build
~/packages/app$ echo build app ⊘ cache disabled
build app
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ Search task (↑/↓ to move, enter to select):
Search task (↑/↓ to move, enter to select): typec
> lib#typecheck: echo typecheck lib
@ write-key: enter
Selected task: lib#typecheck
~/packages/lib$ echo typecheck lib ⊘ cache disabled
typecheck lib
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@ Search task (↑/↓ to move, enter to select): t
app#test: echo test app
(…1 more)
@ write-key: enter
Selected task: test
~/packages/lib$ echo test lib ⊘ cache disabled
test lib
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ Search task (↑/↓ to move, enter to select): lin
> lint: echo lint app
lib#lint: echo lint lib
@ write-key: enter
Selected task: lint
~/packages/app$ echo lint app ⊘ cache disabled
lint app
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ Search task (↑/↓ to move, enter to select): lib#
lib#test: echo test lib
lib#typecheck: echo typecheck lib
@ write-key: enter
Selected task: lib#build
~/packages/lib$ echo build lib ⊘ cache disabled
build lib
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ Search task (↑/↓ to move, enter to select):
task-select-test#format: echo format root
(…3 more)
@ write-key: enter
Selected task: build
~/packages/lib$ echo build lib ⊘ cache disabled
build lib
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@ Search task (↑/↓ to move, enter to select):
task-select-test#format: echo format root
(…3 more)
@ write-key: enter
Selected task: lint
~/packages/app$ echo lint app ⊘ cache disabled
lint app

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ Search task (↑/↓ to move, enter to select): buid
> build: echo build app
lib#build: echo build lib
@ write-key: enter
Selected task: build
~/packages/app$ echo build app ⊘ cache disabled
build app
Loading