Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/vite_task/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ derive_more = { workspace = true, features = ["from"] }
diff-struct = { workspace = true }
fspy = { workspace = true }
futures-util = { workspace = true }
monostate = { workspace = true }
once_cell = { workspace = true }
owo-colors = { workspace = true }
petgraph = { workspace = true }
Expand Down
14 changes: 7 additions & 7 deletions crates/vite_task/docs/task-cache.md
Original file line number Diff line number Diff line change
Expand Up @@ -611,10 +611,10 @@ CommandFingerprint {
}
```

### Example: Built-in Task Cache Key
### Example: Synthetic Task Cache Key

```rust
// Built-in task: vite lint
// Synthetic task (e.g., "vite lint" in a task script)
TaskRunKey {
task_id: TaskId {
task_group_id: TaskGroupId {
Expand Down Expand Up @@ -830,20 +830,20 @@ Cache hit, replaying
b
```

### Example 3: Built-in Task Caching by Working Directory
### Example 3: Task Caching by Working Directory

```bash
# Different directories create separate caches for built-in tasks
> cd folder1 && vite lint
# Different directories create separate caches for tasks
> cd folder1 && vite run lint
Cache not found
Found 0 warnings and 0 errors.

> cd folder2 && vite lint
> cd folder2 && vite run lint
Cache not found # Different cwd = different cache
Found 0 warnings and 0 errors.

# Each directory maintains its own cache
> cd folder1 && vite lint
> cd folder1 && vite run lint
Cache hit, replaying
Found 0 warnings and 0 errors.
```
Expand Down
196 changes: 65 additions & 131 deletions crates/vite_task/src/cli/mod.rs
Original file line number Diff line number Diff line change
@@ -1,110 +1,49 @@
use std::{ffi::OsStr, sync::Arc};
use std::sync::Arc;

use clap::{Parser, Subcommand};
use clap::Parser;
use vite_path::AbsolutePath;
use vite_str::Str;
use vite_task_graph::{TaskSpecifier, query::TaskQueryKind};
use vite_task_plan::plan_request::{PlanOptions, PlanRequest, QueryPlanRequest};

/// Represents the CLI arguments handled by vite-task, including both built-in (like run) and custom subcommands (like lint).
#[derive(Debug)]
pub struct TaskCLIArgs<CustomSubcommand: Subcommand> {
pub(crate) original: Arc<[Str]>,
pub(crate) parsed: ParsedTaskCLIArgs<CustomSubcommand>,
#[derive(Debug, Clone, clap::Subcommand)]
pub enum CacheSubcommand {
/// Clean up all the cache
Clean,
}

impl<CustomSubcommand: Subcommand> TaskCLIArgs<CustomSubcommand> {
/// Inspect the custom subcommand (like lint/install). Returns `None` if it's built-in subcommand
/// The caller should not use this method to actually handle the custom subcommand. Instead, it should
/// private TaskSynthesizer to Session so that vite-task can handle custom subcommands consistently from
/// both direct CLI invocations and invocations in task scripts.
///
/// This method is provided only to make it possible for the caller to behave differently BEFORE and AFTER the session.
/// For example, vite+ needs this method to skip auto-install when the custom subcommand is already `install`.
pub fn custom_subcommand(&self) -> Option<&CustomSubcommand> {
match &self.parsed {
ParsedTaskCLIArgs::BuiltIn(_) => None,
ParsedTaskCLIArgs::Custom(custom) => Some(custom),
}
}
}

/// Represents the overall CLI arguments, containing three kinds of subcommands:
/// 1. Built-in subcommands handled by vite-task (like run)
/// 2. Custom subcommands handled by vite-task with the help of TaskSyntheizer (like lint)
/// 3. Custom subcommands not handled by vite-task (like vite+ commands without cache)
pub enum CLIArgs<CustomSubcommand: Subcommand, NonTaskSubcommand: Subcommand> {
/// Subcommands handled by vite task, including built-in (like run) and custom (like lint)
Task(TaskCLIArgs<CustomSubcommand>),
/// Custom subcommands not handled by vite task (like vite+ commands without cache)
NonTask(NonTaskSubcommand),
}
/// Arguments for the `run` subcommand.
#[derive(Debug, clap::Args)]
pub struct RunCommand {
/// `packageName#taskName` or `taskName`.
pub task_specifier: TaskSpecifier,

impl<CustomSubcommand: Subcommand, NonTaskSubcommand: Subcommand>
CLIArgs<CustomSubcommand, NonTaskSubcommand>
{
/// Get the original CLI arguments
pub fn try_parse_from(
args: impl Iterator<Item = impl AsRef<str>>,
) -> Result<Self, clap::Error> {
#[derive(Debug, clap::Parser)]
enum ParsedCLIArgs<CustomSubcommand: Subcommand, NonTaskSubcommand: Subcommand> {
/// subcommands handled by vite task
#[command(flatten)]
Task(ParsedTaskCLIArgs<CustomSubcommand>),
/// Run tasks found in all packages in the workspace, in topological order based on package dependencies.
#[clap(default_value = "false", short, long)]
pub recursive: bool,

/// subcommands that are not handled by vite task
#[command(flatten)]
NonTask(NonTaskSubcommand),
}
/// Run tasks found in the current package and all its transitive dependencies, in topological order based on package dependencies.
#[clap(default_value = "false", short, long)]
pub transitive: bool,

let args = args.map(|arg| Str::from(arg.as_ref())).collect::<Arc<[Str]>>();
let parsed_cli_args = ParsedCLIArgs::<CustomSubcommand, NonTaskSubcommand>::try_parse_from(
args.iter().map(|s| OsStr::new(s.as_str())),
)?;

Ok(match parsed_cli_args {
ParsedCLIArgs::Task(parsed_task_cli_args) => {
Self::Task(TaskCLIArgs { original: args, parsed: parsed_task_cli_args })
}
ParsedCLIArgs::NonTask(non_task_subcommand) => Self::NonTask(non_task_subcommand),
})
}
}
/// Do not run dependencies specified in `dependsOn` fields.
#[clap(default_value = "false", long)]
pub ignore_depends_on: bool,

#[derive(Debug, Parser)]
pub(crate) enum ParsedTaskCLIArgs<CustomSubcommand: Subcommand> {
/// subcommands provided by vite task, like `run`
#[clap(flatten)]
BuiltIn(BuiltInCommand),
/// custom subcommands provided by vite+, like `lint`
#[clap(flatten)]
Custom(CustomSubcommand),
/// Additional arguments to pass to the tasks
#[clap(trailing_var_arg = true, allow_hyphen_values = true)]
pub additional_args: Vec<Str>,
}

/// vite task CLI subcommands
#[derive(Debug, Subcommand)]
pub(crate) enum BuiltInCommand {
#[derive(Debug, Parser)]
pub enum Command {
/// Run tasks
Run {
/// `packageName#taskName` or `taskName`.
task_specifier: TaskSpecifier,

/// Run tasks found in all packages in the workspace, in topological order based on package dependencies.
#[clap(default_value = "false", short, long)]
recursive: bool,

/// Run tasks found in the current package and all its transitive dependencies, in topological order based on package dependencies.
#[clap(default_value = "false", short, long)]
transitive: bool,

/// Do not run dependencies specified in `dependsOn` fields.
#[clap(default_value = "false", long)]
ignore_depends_on: bool,

/// Additional arguments to pass to the tasks
#[clap(trailing_var_arg = true, allow_hyphen_values = true)]
additional_args: Vec<Str>,
Run(RunCommand),
/// Manage the task cache
Cache {
#[clap(subcommand)]
subcmd: CacheSubcommand,
},
}

Expand All @@ -117,50 +56,45 @@ pub enum CLITaskQueryError {
PackageNameSpecifiedWithRecursive { package_name: Str, task_name: Str },
}

impl BuiltInCommand {
/// Convert to `TaskQuery`, or return an error if invalid.
impl RunCommand {
/// Convert to `PlanRequest`, or return an error if invalid.
pub fn into_plan_request(
self,
cwd: &Arc<AbsolutePath>,
) -> Result<PlanRequest, CLITaskQueryError> {
match self {
Self::Run {
task_specifier,
recursive,
transitive,
ignore_depends_on,
additional_args,
} => {
let include_explicit_deps = !ignore_depends_on;

let query_kind = if recursive {
if transitive {
return Err(CLITaskQueryError::RecursiveTransitiveConflict);
}
let task_name = if let Some(package_name) = task_specifier.package_name {
return Err(CLITaskQueryError::PackageNameSpecifiedWithRecursive {
package_name,
task_name: task_specifier.task_name,
});
} else {
task_specifier.task_name
};
TaskQueryKind::Recursive { task_names: [task_name].into() }
} else {
TaskQueryKind::Normal {
task_specifiers: [task_specifier].into(),
cwd: Arc::clone(cwd),
include_topological_deps: transitive,
}
};
Ok(PlanRequest::Query(QueryPlanRequest {
query: vite_task_graph::query::TaskQuery {
kind: query_kind,
include_explicit_deps,
},
plan_options: PlanOptions { extra_args: additional_args.into() },
}))
let RunCommand {
task_specifier,
recursive,
transitive,
ignore_depends_on,
additional_args,
} = self;

let include_explicit_deps = !ignore_depends_on;

let query_kind = if recursive {
if transitive {
return Err(CLITaskQueryError::RecursiveTransitiveConflict);
}
let task_name = if let Some(package_name) = task_specifier.package_name {
return Err(CLITaskQueryError::PackageNameSpecifiedWithRecursive {
package_name,
task_name: task_specifier.task_name,
});
} else {
task_specifier.task_name
};
TaskQueryKind::Recursive { task_names: [task_name].into() }
} else {
TaskQueryKind::Normal {
task_specifiers: [task_specifier].into(),
cwd: Arc::clone(cwd),
include_topological_deps: transitive,
}
}
};
Ok(PlanRequest::Query(QueryPlanRequest {
query: vite_task_graph::query::TaskQuery { kind: query_kind, include_explicit_deps },
plan_options: PlanOptions { extra_args: additional_args.into() },
}))
}
}
8 changes: 4 additions & 4 deletions crates/vite_task/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ mod maybe_str;
pub mod session;

// Public exports for vite_task_bin
pub use cli::CLIArgs;
pub use session::{LabeledReporter, Reporter, Session, SessionCallbacks, TaskSynthesizer};
pub use cli::{CacheSubcommand, Command, RunCommand};
pub use session::{CommandHandler, ExitStatus, HandledCommand, Session, SessionCallbacks};
pub use vite_task_graph::{
config::{
self,
user::{EnabledCacheConfig, UserCacheConfig, UserTaskConfig, UserTaskOptions},
},
loader,
};
/// get_path_env is useful for TaskSynthesizer implementations. Re-export it here.
/// Re-exports useful for CommandHandler implementations.
pub use vite_task_plan::get_path_env;
pub use vite_task_plan::plan_request;
pub use vite_task_plan::{plan_request, plan_request::ScriptCommand};
8 changes: 4 additions & 4 deletions crates/vite_task/src/session/cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,16 @@ impl ExecutionCache {
"CREATE TABLE execution_key_to_fingerprint (key BLOB PRIMARY KEY, value BLOB);",
(),
)?;
conn.execute("PRAGMA user_version = 4", ())?;
conn.execute("PRAGMA user_version = 6", ())?;
}
1..=3 => {
1..=5 => {
// old internal db version. reset
conn.set_db_config(DbConfig::SQLITE_DBCONFIG_RESET_DATABASE, true)?;
conn.execute("VACUUM", ())?;
conn.set_db_config(DbConfig::SQLITE_DBCONFIG_RESET_DATABASE, false)?;
}
4 => break, // current version
5.. => {
6 => break, // current version
6.. => {
return Err(anyhow::anyhow!("Unrecognized database version: {}", user_version));
}
}
Expand Down
4 changes: 2 additions & 2 deletions crates/vite_task/src/session/execute/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,14 +348,14 @@ impl ExecutionContext<'_> {
}
}

impl<'a, CustomSubcommand> Session<'a, CustomSubcommand> {
impl<'a> Session<'a> {
/// Execute an execution plan, reporting events to the provided reporter.
///
/// Returns Err(ExitStatus) to suggest the caller to abort and exit the process with the given exit status.
///
/// The return type isn't just ExitStatus because we want to distinguish between normal successful execution,
/// and execution that failed and needs to exit with a specific code which can be zero.
pub async fn execute(
pub(crate) async fn execute(
&self,
plan: ExecutionPlan,
mut reporter: Box<dyn Reporter>,
Expand Down
Loading