diff --git a/Cargo.lock b/Cargo.lock index 3e48f87e..3babd57a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3298,6 +3298,7 @@ dependencies = [ "diff-struct", "fspy", "futures-util", + "monostate", "nix 0.30.1", "once_cell", "owo-colors", @@ -3371,6 +3372,7 @@ dependencies = [ "anyhow", "async-trait", "bincode", + "clap", "copy_dir", "cow-utils", "futures-util", diff --git a/crates/vite_task/Cargo.toml b/crates/vite_task/Cargo.toml index e319b82a..f1529009 100644 --- a/crates/vite_task/Cargo.toml +++ b/crates/vite_task/Cargo.toml @@ -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 } diff --git a/crates/vite_task/docs/task-cache.md b/crates/vite_task/docs/task-cache.md index d21b7e42..db276b88 100644 --- a/crates/vite_task/docs/task-cache.md +++ b/crates/vite_task/docs/task-cache.md @@ -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 { @@ -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. ``` diff --git a/crates/vite_task/src/cli/mod.rs b/crates/vite_task/src/cli/mod.rs index e31c7d9a..5abdbc95 100644 --- a/crates/vite_task/src/cli/mod.rs +++ b/crates/vite_task/src/cli/mod.rs @@ -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 { - pub(crate) original: Arc<[Str]>, - pub(crate) parsed: ParsedTaskCLIArgs, +#[derive(Debug, Clone, clap::Subcommand)] +pub enum CacheSubcommand { + /// Clean up all the cache + Clean, } -impl TaskCLIArgs { - /// 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 { - /// Subcommands handled by vite task, including built-in (like run) and custom (like lint) - Task(TaskCLIArgs), - /// 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 - CLIArgs -{ - /// Get the original CLI arguments - pub fn try_parse_from( - args: impl Iterator>, - ) -> Result { - #[derive(Debug, clap::Parser)] - enum ParsedCLIArgs { - /// subcommands handled by vite task - #[command(flatten)] - Task(ParsedTaskCLIArgs), + /// 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::>(); - let parsed_cli_args = ParsedCLIArgs::::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 { - /// 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, } /// 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, + Run(RunCommand), + /// Manage the task cache + Cache { + #[clap(subcommand)] + subcmd: CacheSubcommand, }, } @@ -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, ) -> Result { - 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() }, + })) } } diff --git a/crates/vite_task/src/lib.rs b/crates/vite_task/src/lib.rs index 458483ae..0d86d9d2 100644 --- a/crates/vite_task/src/lib.rs +++ b/crates/vite_task/src/lib.rs @@ -4,8 +4,8 @@ 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, @@ -13,6 +13,6 @@ pub use vite_task_graph::{ }, 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}; diff --git a/crates/vite_task/src/session/cache/mod.rs b/crates/vite_task/src/session/cache/mod.rs index d43a01db..a44e8d53 100644 --- a/crates/vite_task/src/session/cache/mod.rs +++ b/crates/vite_task/src/session/cache/mod.rs @@ -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)); } } diff --git a/crates/vite_task/src/session/execute/mod.rs b/crates/vite_task/src/session/execute/mod.rs index 7c9cae83..494ec5b7 100644 --- a/crates/vite_task/src/session/execute/mod.rs +++ b/crates/vite_task/src/session/execute/mod.rs @@ -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, diff --git a/crates/vite_task/src/session/mod.rs b/crates/vite_task/src/session/mod.rs index c06ce44e..36a8203c 100644 --- a/crates/vite_task/src/session/mod.rs +++ b/crates/vite_task/src/session/mod.rs @@ -1,29 +1,34 @@ mod cache; mod event; mod execute; -pub mod reporter; +pub(crate) mod reporter; // Re-export types that are part of the public API use std::{ffi::OsStr, fmt::Debug, sync::Arc}; use cache::ExecutionCache; pub use cache::{CacheMiss, FingerprintMismatch}; -use clap::{Parser, Subcommand}; pub use event::ExecutionEvent; +use monostate::MustBe; use once_cell::sync::OnceCell; -pub use reporter::{LabeledReporter, Reporter}; +pub use reporter::ExitStatus; +use reporter::LabeledReporter; use vite_path::{AbsolutePath, AbsolutePathBuf}; use vite_str::Str; -use vite_task_graph::{IndexedTaskGraph, TaskGraph, TaskGraphLoadError, loader::UserConfigLoader}; +use vite_task_graph::{ + IndexedTaskGraph, TaskGraph, TaskGraphLoadError, + config::user::{UserCacheConfig, UserTaskOptions}, + loader::UserConfigLoader, +}; use vite_task_plan::{ ExecutionPlan, TaskGraphLoader, TaskPlanErrorKind, - plan_request::{PlanRequest, SyntheticPlanRequest}, + plan_request::{PlanRequest, ScriptCommand, SyntheticPlanRequest}, prepend_path_env, }; use vite_workspace::{WorkspaceRoot, find_workspace_root}; use crate::{ - cli::{ParsedTaskCLIArgs, TaskCLIArgs}, + cli::{CacheSubcommand, Command, RunCommand}, collections::HashMap, }; @@ -52,87 +57,71 @@ impl TaskGraphLoader for LazyTaskGraph<'_> { } } -pub struct SessionCallbacks<'a, CustomSubcommand> { - pub task_synthesizer: &'a mut (dyn TaskSynthesizer + 'a), +pub struct SessionCallbacks<'a> { + pub command_handler: &'a mut (dyn CommandHandler + 'a), pub user_config_loader: &'a mut (dyn UserConfigLoader + 'a), } -#[async_trait::async_trait(?Send)] -pub trait TaskSynthesizer: Debug { - fn should_synthesize_for_program(&self, program: &str) -> bool; +/// The result of a [`CommandHandler::handle_command`] call. +#[derive(Debug)] +pub enum HandledCommand { + /// The command was synthesized into a task (e.g., `vite lint` → `oxlint`). + Synthesized(SyntheticPlanRequest), + /// The command is a vite-task CLI command (e.g., `vite run build`). + ViteTaskCommand(Command), + /// The command should be executed verbatim as an external process. + Verbatim, +} - /// Synthesize a synthetic task plan request for the given parsed custom subcommand. - /// - /// - `envs` is the current environment variables where the task is being planned. - /// - `cwd` is the current working directory where the task is being planned. - /// - /// The implementor can return a different `envs` in `SyntheticPlanRequest` to customize - /// environment variables for the synthetic task. - async fn synthesize_task( +/// Handles commands found in task scripts to determine how they should be executed. +/// +/// The implementation should return: +/// - [`HandledCommand::Synthesized`] to replace the command with a synthetic task. +/// - [`HandledCommand::ViteTaskCommand`] when the command is a vite-task CLI invocation. +/// - [`HandledCommand::Verbatim`] to execute the command as-is as an external process. +#[async_trait::async_trait(?Send)] +pub trait CommandHandler: Debug { + /// Called for every command in task scripts to determine how it should be handled. + async fn handle_command( &mut self, - subcommand: CustomSubcommand, - envs: &Arc, Arc>>, - cwd: &Arc, - ) -> anyhow::Result; + command: &mut ScriptCommand, + ) -> anyhow::Result; } #[derive(derive_more::Debug)] -#[debug(bound())] // Avoid requiring CustomSubcommand: Debug -struct PlanRequestParser<'a, CustomSubcommand> { - task_synthesizer: &'a mut (dyn TaskSynthesizer + 'a), -} - -impl PlanRequestParser<'_, CustomSubcommand> { - async fn get_plan_request_from_cli_args( - &mut self, - cli_args: ParsedTaskCLIArgs, - envs: &Arc, Arc>>, - cwd: &Arc, - ) -> anyhow::Result { - match cli_args { - ParsedTaskCLIArgs::BuiltIn(vite_task_subcommand) => { - Ok(vite_task_subcommand.into_plan_request(cwd)?) - } - ParsedTaskCLIArgs::Custom(custom_subcommand) => { - let synthetic_plan_request = - self.task_synthesizer.synthesize_task(custom_subcommand, envs, cwd).await?; - Ok(PlanRequest::Synthetic(synthetic_plan_request)) - } - } - } +struct PlanRequestParser<'a> { + command_handler: &'a mut (dyn CommandHandler + 'a), } #[async_trait::async_trait(?Send)] -impl vite_task_plan::PlanRequestParser - for PlanRequestParser<'_, CustomSubcommand> -{ +impl vite_task_plan::PlanRequestParser for PlanRequestParser<'_> { async fn get_plan_request( &mut self, - program: &str, - args: &[Str], - envs: &Arc, Arc>>, - cwd: &Arc, + command: &mut ScriptCommand, ) -> anyhow::Result> { - Ok( - if self.task_synthesizer.should_synthesize_for_program(program) - && let Some(subcommand) = args.first() - && ParsedTaskCLIArgs::::has_subcommand(subcommand) - { - let cli_args = ParsedTaskCLIArgs::::try_parse_from( - std::iter::once(program).chain(args.iter().map(Str::as_str)), - )?; - Some(self.get_plan_request_from_cli_args(cli_args, envs, cwd).await?) - } else { - None + match self.command_handler.handle_command(command).await? { + HandledCommand::Synthesized(synthetic) => Ok(Some(PlanRequest::Synthetic(synthetic))), + HandledCommand::ViteTaskCommand(cli_command) => match cli_command { + Command::Cache { .. } => Ok(Some(PlanRequest::Synthetic(SyntheticPlanRequest { + program: Arc::from(OsStr::new(command.program.as_str())), + args: Arc::clone(&command.args), + task_options: UserTaskOptions { + cache_config: UserCacheConfig::Disabled { cache: MustBe!(false) }, + ..Default::default() + }, + envs: Arc::clone(&command.envs), + }))), + Command::Run(run_command) => Ok(Some(run_command.into_plan_request(&command.cwd)?)), }, - ) + HandledCommand::Verbatim => Ok(None), + } } } /// Represents a vite task session for planning and executing tasks. A process typically has one session. /// /// A session manages task graph loading internally and provides non-consuming methods to plan and/or execute tasks (allows multiple plans/executions per session). -pub struct Session<'a, CustomSubcommand> { +pub struct Session<'a> { workspace_path: Arc, /// A session doesn't necessarily load the task graph immediately. /// The task graph is loaded on-demand and cached for future use. @@ -141,7 +130,7 @@ pub struct Session<'a, CustomSubcommand> { envs: Arc, Arc>>, cwd: Arc, - plan_request_parser: PlanRequestParser<'a, CustomSubcommand>, + plan_request_parser: PlanRequestParser<'a>, /// Cache is lazily initialized to avoid SQLite race conditions when multiple /// processes (e.g., parallel `vite lib` commands) start simultaneously. @@ -157,9 +146,9 @@ fn get_cache_path_of_workspace(workspace_root: &AbsolutePath) -> AbsolutePathBuf } } -impl<'a, CustomSubcommand> Session<'a, CustomSubcommand> { +impl<'a> Session<'a> { /// Initialize a session with real environment variables and cwd - pub fn init(callbacks: SessionCallbacks<'a, CustomSubcommand>) -> anyhow::Result { + pub fn init(callbacks: SessionCallbacks<'a>) -> anyhow::Result { let envs = std::env::vars_os() .map(|(k, v)| (Arc::::from(k.as_os_str()), Arc::::from(v.as_os_str()))) .collect(); @@ -176,7 +165,7 @@ impl<'a, CustomSubcommand> Session<'a, CustomSubcommand> { pub fn init_with( mut envs: HashMap, Arc>, cwd: Arc, - callbacks: SessionCallbacks<'a, CustomSubcommand>, + callbacks: SessionCallbacks<'a>, ) -> anyhow::Result { let (workspace_root, _) = find_workspace_root(&cwd)?; let cache_path = get_cache_path_of_workspace(&workspace_root.path); @@ -194,12 +183,40 @@ impl<'a, CustomSubcommand> Session<'a, CustomSubcommand> { }, envs: Arc::new(envs), cwd, - plan_request_parser: PlanRequestParser { task_synthesizer: callbacks.task_synthesizer }, + plan_request_parser: PlanRequestParser { command_handler: callbacks.command_handler }, cache: OnceCell::new(), cache_path, }) } + /// Primary entry point for CLI usage. Plans and executes the given command. + pub async fn main(mut self, command: Command) -> anyhow::Result { + match command { + Command::Cache { subcmd } => self.handle_cache_command(subcmd), + Command::Run(run_command) => { + let cwd = Arc::clone(&self.cwd); + let plan = self.plan_from_cli(cwd, run_command).await?; + let reporter = LabeledReporter::new(std::io::stdout(), self.workspace_path()); + Ok(self + .execute(plan, Box::new(reporter)) + .await + .err() + .unwrap_or(ExitStatus::SUCCESS)) + } + } + } + + fn handle_cache_command(&self, subcmd: CacheSubcommand) -> anyhow::Result { + match subcmd { + CacheSubcommand::Clean => { + if self.cache_path.as_path().exists() { + std::fs::remove_dir_all(&self.cache_path)?; + } + Ok(ExitStatus::SUCCESS) + } + } + } + /// Lazily initializes and returns the execution cache. /// The cache is only created when first accessed to avoid SQLite race conditions /// when multiple processes start simultaneously. @@ -217,43 +234,51 @@ impl<'a, CustomSubcommand> Session<'a, CustomSubcommand> { _ => None, } } -} -impl<'a, CustomSubcommand: clap::Subcommand> Session<'a, CustomSubcommand> { - pub async fn plan_synthetic_task( - &mut self, + pub fn envs(&self) -> &Arc, Arc>> { + &self.envs + } + + pub fn cwd(&self) -> &Arc { + &self.cwd + } + + /// Execute a synthetic command with cache support. + /// + /// This is for executing a command with cache before/without the entrypoint [`Session::main`]. + /// In vite-plus, this is used for auto-install. + pub async fn execute_synthetic( + &self, synthetic_plan_request: SyntheticPlanRequest, - ) -> Result { - let plan = ExecutionPlan::plan( - PlanRequest::Synthetic(synthetic_plan_request), + cache_key: Arc<[Str]>, + silent_if_cache_hit: bool, + ) -> anyhow::Result { + let plan = ExecutionPlan::plan_synthetic( &self.workspace_path, &self.cwd, - &self.envs, - &mut self.plan_request_parser, - &mut self.lazy_task_graph, - ) - .await?; - Ok(plan) + synthetic_plan_request, + cache_key, + )?; + let mut reporter = LabeledReporter::new(std::io::stdout(), self.workspace_path()); + reporter.set_hide_summary(true); + reporter.set_silent_if_cache_hit(silent_if_cache_hit); + Ok(self.execute(plan, Box::new(reporter)).await.err().unwrap_or(ExitStatus::SUCCESS)) } pub async fn plan_from_cli( &mut self, cwd: Arc, - cli_args: TaskCLIArgs, + command: RunCommand, ) -> Result { - let plan_request = self - .plan_request_parser - .get_plan_request_from_cli_args(cli_args.parsed, &self.envs, &cwd) - .await - .map_err(|error| { - TaskPlanErrorKind::ParsePlanRequestError { - error, - program: cli_args.original[0].clone(), - args: cli_args.original.iter().skip(1).cloned().collect(), - cwd: Arc::clone(&cwd), - } - .with_empty_call_stack() - })?; + let plan_request = command.into_plan_request(&cwd).map_err(|error| { + TaskPlanErrorKind::ParsePlanRequestError { + error: error.into(), + program: Str::from("vite"), + args: Default::default(), + cwd: Arc::clone(&cwd), + } + .with_empty_call_stack() + })?; let plan = ExecutionPlan::plan( plan_request, &self.workspace_path, diff --git a/crates/vite_task/src/session/reporter.rs b/crates/vite_task/src/session/reporter.rs index 2d89fa3e..bc74a8bc 100644 --- a/crates/vite_task/src/session/reporter.rs +++ b/crates/vite_task/src/session/reporter.rs @@ -91,8 +91,8 @@ struct ExecutionStats { /// - Skips printing the execution summary entirely /// - Useful for programmatic usage or when summary is not needed /// -/// ## Simplified Summary for Built-in Commands -/// - When a single built-in command (e.g., `vite lint`) is executed: +/// ## Simplified Summary for Single Tasks +/// - When a single task is executed: /// - Skips full summary (no Statistics/Task Details sections) /// - Shows only cache status (except for "NotFound" which is hidden for clean first-run output) /// - Results in clean output showing just the command's stdout/stderr @@ -156,7 +156,7 @@ impl LabeledReporter { CacheStatus::Disabled(_) => self.stats.cache_disabled += 1, } - // Handle None display case - direct synthetic execution (e.g., `vite lint`) + // Handle None display case - direct synthetic execution (e.g., via plan_exec) // Don't print cache status here - will be printed at finish for cache hits only let Some(display) = display else { self.executions.push(ExecutionInfo { diff --git a/crates/vite_task_bin/src/lib.rs b/crates/vite_task_bin/src/lib.rs index 49fd6a06..d665358a 100644 --- a/crates/vite_task_bin/src/lib.rs +++ b/crates/vite_task_bin/src/lib.rs @@ -7,48 +7,18 @@ use std::{ sync::Arc, }; -use clap::Subcommand; +use clap::Parser; use vite_path::AbsolutePath; use vite_str::Str; use vite_task::{ - EnabledCacheConfig, SessionCallbacks, UserCacheConfig, UserTaskOptions, get_path_env, - plan_request::SyntheticPlanRequest, + Command, EnabledCacheConfig, HandledCommand, ScriptCommand, SessionCallbacks, UserCacheConfig, + UserTaskOptions, get_path_env, plan_request::SyntheticPlanRequest, }; -/// Theses are the custom subcommands that synthesize tasks for vite-task -#[derive(Debug, Subcommand)] -pub enum CustomTaskSubcommand { - /// oxlint - #[clap(disable_help_flag = true)] - Lint { - #[clap(allow_hyphen_values = true, trailing_var_arg = true)] - args: Vec, - }, - /// vitest - #[clap(disable_help_flag = true)] - Test { - #[clap(allow_hyphen_values = true, trailing_var_arg = true)] - args: Vec, - }, - /// Test command for testing additional_envs feature - EnvTest { - /// Environment variable name - name: Str, - /// Environment variable value - value: Str, - }, -} - -// These are the subcommands that is not handled by vite-task -#[derive(Debug, Subcommand)] -pub enum NonTaskSubcommand { - Version, -} - #[derive(Debug, Default)] -pub struct TaskSynthesizer(()); +pub struct CommandHandler(()); -fn find_executable( +pub fn find_executable( path_env: Option<&Arc>, cwd: &AbsolutePath, executable: &str, @@ -69,53 +39,74 @@ fn find_executable( Ok(executable_path.into_os_string().into()) } -#[async_trait::async_trait(?Send)] -impl vite_task::TaskSynthesizer for TaskSynthesizer { - fn should_synthesize_for_program(&self, program: &str) -> bool { - program == "vite" - } +fn synthesize_node_modules_bin_task( + executable_name: &str, + args: &[Str], + envs: &Arc, Arc>>, + cwd: &Arc, +) -> anyhow::Result { + Ok(SyntheticPlanRequest { + program: find_executable(get_path_env(envs), &*cwd, executable_name)?, + args: args.into(), + task_options: Default::default(), + envs: Arc::clone(envs), + }) +} - async fn synthesize_task( - &mut self, - subcommand: CustomTaskSubcommand, - envs: &Arc, Arc>>, - cwd: &Arc, - ) -> anyhow::Result { - let synthesize_node_modules_bin_task = |subcommand_name: &str, - executable_name: &str, - args: Vec| - -> anyhow::Result { - let direct_execution_cache_key: Arc<[Str]> = - iter::once(Str::from(subcommand_name)).chain(args.iter().cloned()).collect(); - Ok(SyntheticPlanRequest { - program: find_executable(get_path_env(envs), &*cwd, executable_name)?, - args: args.into(), - task_options: Default::default(), - direct_execution_cache_key, - envs: Arc::clone(envs), - }) - }; +#[derive(Debug, Parser)] +#[command(name = "vite", version)] +pub enum Args { + Lint { + #[clap(trailing_var_arg = true, allow_hyphen_values = true)] + args: Vec, + }, + Test { + #[clap(trailing_var_arg = true, allow_hyphen_values = true)] + args: Vec, + }, + EnvTest { + name: Str, + value: Str, + }, + #[command(flatten)] + Task(Command), +} - match subcommand { - CustomTaskSubcommand::Lint { args } => { - synthesize_node_modules_bin_task("lint", "oxlint", args) - } - CustomTaskSubcommand::Test { args } => { - synthesize_node_modules_bin_task("test", "vitest", args) +#[async_trait::async_trait(?Send)] +impl vite_task::CommandHandler for CommandHandler { + async fn handle_command( + &mut self, + command: &mut ScriptCommand, + ) -> anyhow::Result { + match command.program.as_str() { + "vite" => {} + // `vpr ` is shorthand for `vite run ` + "vpr" => { + command.program = Str::from("vite"); + command.args = + iter::once(Str::from("run")).chain(command.args.iter().cloned()).collect(); } - CustomTaskSubcommand::EnvTest { name, value } => { - let direct_execution_cache_key: Arc<[Str]> = - [Str::from("env-test"), name.clone(), value.clone()].into(); - - let mut envs = HashMap::clone(&envs); - // Update the env var for testing + _ => return Ok(HandledCommand::Verbatim), + } + let args = Args::try_parse_from( + std::iter::once(command.program.as_str()).chain(command.args.iter().map(Str::as_str)), + )?; + match args { + Args::Lint { args } => Ok(HandledCommand::Synthesized( + synthesize_node_modules_bin_task("oxlint", &args, &command.envs, &command.cwd)?, + )), + Args::Test { args } => Ok(HandledCommand::Synthesized( + synthesize_node_modules_bin_task("vitest", &args, &command.envs, &command.cwd)?, + )), + Args::EnvTest { name, value } => { + let mut envs = HashMap::clone(&command.envs); envs.insert( Arc::from(OsStr::new(name.as_str())), Arc::from(OsStr::new(value.as_str())), ); - Ok(SyntheticPlanRequest { - program: find_executable(get_path_env(&envs), &*cwd, "print-env")?, + Ok(HandledCommand::Synthesized(SyntheticPlanRequest { + program: find_executable(get_path_env(&envs), &*command.cwd, "print-env")?, args: [name.clone()].into(), task_options: UserTaskOptions { cache_config: UserCacheConfig::Enabled { @@ -127,10 +118,10 @@ impl vite_task::TaskSynthesizer for TaskSynthesizer { }, ..Default::default() }, - direct_execution_cache_key, envs: Arc::new(envs), - }) + })) } + Args::Task(cli_command) => Ok(HandledCommand::ViteTaskCommand(cli_command)), } } } @@ -164,14 +155,14 @@ impl vite_task::loader::UserConfigLoader for JsonUserConfigLoader { #[derive(Default)] pub struct OwnedSessionCallbacks { - task_synthesizer: TaskSynthesizer, + command_handler: CommandHandler, user_config_loader: JsonUserConfigLoader, } impl OwnedSessionCallbacks { - pub fn as_callbacks(&mut self) -> SessionCallbacks<'_, CustomTaskSubcommand> { + pub fn as_callbacks(&mut self) -> SessionCallbacks<'_> { SessionCallbacks { - task_synthesizer: &mut self.task_synthesizer, + command_handler: &mut self.command_handler, user_config_loader: &mut self.user_config_loader, } } diff --git a/crates/vite_task_bin/src/main.rs b/crates/vite_task_bin/src/main.rs index 43dc2a78..8fba7320 100644 --- a/crates/vite_task_bin/src/main.rs +++ b/crates/vite_task_bin/src/main.rs @@ -1,11 +1,12 @@ -use std::{env, process::ExitCode, sync::Arc}; +use std::{process::ExitCode, sync::Arc}; -use vite_path::{AbsolutePath, current_dir}; +use clap::Parser; +use vite_str::Str; use vite_task::{ - CLIArgs, Session, - session::reporter::{ExitStatus, LabeledReporter}, + EnabledCacheConfig, ExitStatus, Session, UserCacheConfig, UserTaskOptions, get_path_env, + plan_request::SyntheticPlanRequest, }; -use vite_task_bin::{CustomTaskSubcommand, NonTaskSubcommand, OwnedSessionCallbacks}; +use vite_task_bin::{Args, OwnedSessionCallbacks, find_executable}; #[tokio::main] async fn main() -> anyhow::Result { @@ -14,29 +15,40 @@ async fn main() -> anyhow::Result { } async fn run() -> anyhow::Result { - let cwd: Arc = current_dir()?.into(); - // Parse the CLI arguments and see if they are for vite-task or not - let args = match CLIArgs::::try_parse_from(env::args()) - { - Ok(ok) => ok, - Err(err) => { - err.exit(); - } - }; - let task_cli_args = match args { - CLIArgs::Task(task_cli_args) => task_cli_args, - CLIArgs::NonTask(NonTaskSubcommand::Version) => { - // Non-task subcommands are not handled by vite-task's session. - println!("{}", env!("CARGO_PKG_VERSION")); - return Ok(ExitStatus::SUCCESS); - } - }; - + let args = Args::parse(); let mut owned_callbacks = OwnedSessionCallbacks::default(); - let mut session = Session::init(owned_callbacks.as_callbacks())?; - let plan = session.plan_from_cli(cwd, task_cli_args).await?; - - // Create reporter and execute - let reporter = LabeledReporter::new(std::io::stdout(), session.workspace_path()); - Ok(session.execute(plan, Box::new(reporter)).await.err().unwrap_or(ExitStatus::SUCCESS)) + let session = Session::init(owned_callbacks.as_callbacks())?; + match args { + Args::Task(command) => session.main(command).await, + args => { + // If env FOO is set, run `print-env FOO` via Session::exec before proceeding. + // In vite-plus, Session::exec is used for auto-install. + let envs = session.envs(); + if envs.contains_key(std::ffi::OsStr::new("FOO")) { + let program = find_executable(get_path_env(envs), session.cwd(), "print-env")?; + let request = SyntheticPlanRequest { + program, + args: [Str::from("FOO")].into(), + task_options: UserTaskOptions { + cache_config: UserCacheConfig::Enabled { + cache: None, + enabled_cache_config: EnabledCacheConfig { + envs: Some(Box::from([Str::from("FOO")])), + pass_through_envs: None, + }, + }, + ..Default::default() + }, + envs: Arc::clone(envs), + }; + let cache_key: Arc<[Str]> = Arc::from([Str::from("print-env-foo")]); + let status = session.execute_synthetic(request, cache_key, true).await?; + if status != ExitStatus::SUCCESS { + return Ok(status); + } + } + println!("{:?}", args); + Ok(ExitStatus::SUCCESS) + } + } } diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-different-cwd/package.json b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-different-cwd/package.json index 0967ef42..3f45091c 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-different-cwd/package.json +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-different-cwd/package.json @@ -1 +1,5 @@ -{} +{ + "scripts": { + "lint": "vite lint" + } +} diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-different-cwd/snapshots.toml b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-different-cwd/snapshots.toml index b92b3f66..bb79544f 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-different-cwd/snapshots.toml +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-different-cwd/snapshots.toml @@ -1,11 +1,11 @@ -# Tests that builtin commands have separate cache per cwd +# Tests that tasks have separate cache per cwd [[e2e]] name = "builtin different cwd" steps = [ - "cd folder1 && vite lint # cache miss in folder1", - "cd folder2 && vite lint # cache miss in folder2", + "cd folder1 && vite run lint # cache miss in folder1", + "cd folder2 && vite run lint # cache miss in folder2", "echo 'console.log(1);' > folder2/a.js # modify folder2", - "cd folder1 && vite lint # cache hit", - "cd folder2 && vite lint # cache miss", + "cd folder1 && vite run lint # cache hit", + "cd folder2 && vite run lint # cache miss", ] diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-different-cwd/snapshots/builtin different cwd.snap b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-different-cwd/snapshots/builtin different cwd.snap index b40f307a..e86b760d 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-different-cwd/snapshots/builtin different cwd.snap +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-different-cwd/snapshots/builtin different cwd.snap @@ -1,51 +1,128 @@ --- source: crates/vite_task_bin/tests/e2e_snapshots/main.rs -assertion_line: 203 expression: e2e_outputs input_file: crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-different-cwd --- -> cd folder1 && vite lint # cache miss in folder1 +> cd folder1 && vite run lint # cache miss in folder1 +$ vite lint ! eslint-plugin-unicorn(no-empty-file): Empty files are not allowed. - ,-[a.js:1:1] + ,-[folder1/a.js:1:1] 1 | // Empty JS file for oxlint : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `---- help: Delete this file or add some code to it. -Found 1 warning and 0 errors. -Finished in on 1 file with 90 rules using threads. + ! eslint-plugin-unicorn(no-empty-file): Empty files are not allowed. + ,-[folder2/a.js:1:1] + 1 | // Empty JS file for oxlint + : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + `---- + help: Delete this file or add some code to it. + +Found 2 warnings and 0 errors. +Finished in on 2 files with 90 rules using threads. + + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Vite+ Task Runner • Execution Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Statistics: 1 tasks • 0 cache hits • 1 cache misses +Performance: 0% cache hit rate -> cd folder2 && vite lint # cache miss in folder2 +Task Details: +──────────────────────────────────────────────── + [1] lint: $ vite lint ✓ + → Cache miss: no previous cache entry found +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +> cd folder2 && vite run lint # cache miss in folder2 +$ vite lint ✓ cache hit, replaying ! eslint-plugin-unicorn(no-empty-file): Empty files are not allowed. - ,-[a.js:1:1] + ,-[folder1/a.js:1:1] 1 | // Empty JS file for oxlint : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `---- help: Delete this file or add some code to it. -Found 1 warning and 0 errors. -Finished in on 1 file with 90 rules using threads. + ! eslint-plugin-unicorn(no-empty-file): Empty files are not allowed. + ,-[folder2/a.js:1:1] + 1 | // Empty JS file for oxlint + : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + `---- + help: Delete this file or add some code to it. + +Found 2 warnings and 0 errors. +Finished in on 2 files with 90 rules using threads. + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Vite+ Task Runner • Execution Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Statistics: 1 tasks • 1 cache hits • 0 cache misses +Performance: 100% cache hit rate, saved in total + +Task Details: +──────────────────────────────────────────────── + [1] lint: $ vite lint ✓ + → Cache hit - output replayed - saved +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ > echo 'console.log(1);' > folder2/a.js # modify folder2 -> cd folder1 && vite lint # cache hit +> cd folder1 && vite run lint # cache hit +$ vite lint ✗ cache miss: content of input 'folder2/a.js' changed, executing ! eslint-plugin-unicorn(no-empty-file): Empty files are not allowed. - ,-[a.js:1:1] + ,-[folder1/a.js:1:1] 1 | // Empty JS file for oxlint : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `---- help: Delete this file or add some code to it. Found 1 warning and 0 errors. -Finished in on 1 file with 90 rules using threads. -✓ cache hit, logs replayed +Finished in on 2 files with 90 rules using threads. + + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Vite+ Task Runner • Execution Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Statistics: 1 tasks • 0 cache hits • 1 cache misses +Performance: 0% cache hit rate + +Task Details: +──────────────────────────────────────────────── + [1] lint: $ vite lint ✓ + → Cache miss: content of input 'folder2/a.js' changed +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +> cd folder2 && vite run lint # cache miss +$ vite lint ✓ cache hit, replaying + + ! eslint-plugin-unicorn(no-empty-file): Empty files are not allowed. + ,-[folder1/a.js:1:1] + 1 | // Empty JS file for oxlint + : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + `---- + help: Delete this file or add some code to it. + +Found 1 warning and 0 errors. +Finished in on 2 files with 90 rules using threads. + + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Vite+ Task Runner • Execution Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Statistics: 1 tasks • 1 cache hits • 0 cache misses +Performance: 100% cache hit rate, saved in total -> cd folder2 && vite lint # cache miss -Found 0 warnings and 0 errors. -Finished in on 1 file with 90 rules using threads. +Task Details: +──────────────────────────────────────────────── + [1] lint: $ vite lint ✓ + → Cache hit - output replayed - saved +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/package.json b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/package.json index cd8fc56e..484cf605 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/package.json +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/package.json @@ -1,3 +1,6 @@ { - "name": "builtin-non-zero-exit-test" + "name": "builtin-non-zero-exit-test", + "scripts": { + "lint": "vite lint" + } } diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/snapshots.toml b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/snapshots.toml index 28b3fb46..f50465ef 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/snapshots.toml +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/snapshots.toml @@ -1,6 +1,6 @@ [[e2e]] name = "builtin command with non-zero exit does not show cache not updated" steps = [ - "vite lint -D no-debugger", - "vite lint -D no-debugger", + "vite run lint -- -D no-debugger", + "vite run lint -- -D no-debugger", ] diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/snapshots/builtin command with non-zero exit does not show cache not updated.snap b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/snapshots/builtin command with non-zero exit does not show cache not updated.snap index 605f153a..06b73644 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/snapshots/builtin command with non-zero exit does not show cache not updated.snap +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/snapshots/builtin command with non-zero exit does not show cache not updated.snap @@ -3,7 +3,8 @@ source: crates/vite_task_bin/tests/e2e_snapshots/main.rs expression: e2e_outputs input_file: crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit --- -[1]> vite lint -D no-debugger +[1]> vite run lint -- -D no-debugger +$ vite lint -D no-debugger x eslint(no-debugger): `debugger` statement is not allowed ,-[bad.js:1:1] @@ -16,7 +17,21 @@ Found 0 warnings and 1 error. Finished in on 1 file with 90 rules using threads. -[1]> vite lint -D no-debugger +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Vite+ Task Runner • Execution Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Statistics: 1 tasks • 0 cache hits • 1 cache misses • 1 failed +Performance: 0% cache hit rate + +Task Details: +──────────────────────────────────────────────── + [1] builtin-non-zero-exit-test#lint: $ vite lint -D no-debugger ✗ (exit code: 1) + → Cache miss: no previous cache entry found +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +[1]> vite run lint -- -D no-debugger +$ vite lint -D no-debugger x eslint(no-debugger): `debugger` statement is not allowed ,-[bad.js:1:1] @@ -27,3 +42,17 @@ Finished in on 1 file with 90 rules using threads. Found 0 warnings and 1 error. Finished in on 1 file with 90 rules using threads. + + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Vite+ Task Runner • Execution Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Statistics: 1 tasks • 0 cache hits • 1 cache misses • 1 failed +Performance: 0% cache hit rate + +Task Details: +──────────────────────────────────────────────── + [1] builtin-non-zero-exit-test#lint: $ vite lint -D no-debugger ✗ (exit code: 1) + → Cache miss: no previous cache entry found +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/cache-subcommand/package.json b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/cache-subcommand/package.json new file mode 100644 index 00000000..538e7f4d --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/cache-subcommand/package.json @@ -0,0 +1,3 @@ +{ + "name": "@test/cache-subcommand" +} diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/cache-subcommand/snapshots.toml b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/cache-subcommand/snapshots.toml new file mode 100644 index 00000000..e647b042 --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/cache-subcommand/snapshots.toml @@ -0,0 +1,8 @@ +[[e2e]] +name = "cache clean" +steps = [ + "vite run cached-task # cache miss", + "vite run cached-task # cache hit", + "vite cache clean", + "vite run cached-task # cache miss after clean", +] diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/cache-subcommand/snapshots/cache clean.snap b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/cache-subcommand/snapshots/cache clean.snap new file mode 100644 index 00000000..9288581b --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/cache-subcommand/snapshots/cache clean.snap @@ -0,0 +1,60 @@ +--- +source: crates/vite_task_bin/tests/e2e_snapshots/main.rs +expression: e2e_outputs +input_file: crates/vite_task_bin/tests/e2e_snapshots/fixtures/cache-subcommand +--- +> vite run cached-task # cache miss +$ print-file test.txt +test content + + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Vite+ Task Runner • Execution Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Statistics: 1 tasks • 0 cache hits • 1 cache misses +Performance: 0% cache hit rate + +Task Details: +──────────────────────────────────────────────── + [1] @test/cache-subcommand#cached-task: $ print-file test.txt ✓ + → Cache miss: no previous cache entry found +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +> vite run cached-task # cache hit +$ print-file test.txt ✓ cache hit, replaying +test content + + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Vite+ Task Runner • Execution Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Statistics: 1 tasks • 1 cache hits • 0 cache misses +Performance: 100% cache hit rate, saved in total + +Task Details: +──────────────────────────────────────────────── + [1] @test/cache-subcommand#cached-task: $ print-file test.txt ✓ + → Cache hit - output replayed - saved +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +> vite cache clean + +> vite run cached-task # cache miss after clean +$ print-file test.txt +test content + + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Vite+ Task Runner • Execution Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Statistics: 1 tasks • 0 cache hits • 1 cache misses +Performance: 0% cache hit rate + +Task Details: +──────────────────────────────────────────────── + [1] @test/cache-subcommand#cached-task: $ print-file test.txt ✓ + → Cache miss: no previous cache entry found +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/cache-subcommand/test.txt b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/cache-subcommand/test.txt new file mode 100644 index 00000000..d670460b --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/cache-subcommand/test.txt @@ -0,0 +1 @@ +test content diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/cache-subcommand/vite-task.json b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/cache-subcommand/vite-task.json new file mode 100644 index 00000000..d1bedfe4 --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/cache-subcommand/vite-task.json @@ -0,0 +1,9 @@ +{ + "cacheScripts": true, + "tasks": { + "cached-task": { + "command": "print-file test.txt", + "cache": true + } + } +} diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-env-test/package.json b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-env-test/package.json index 9340d35d..ed6793f0 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-env-test/package.json +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-env-test/package.json @@ -1,4 +1,7 @@ { "name": "e2e-env-test", - "private": true + "private": true, + "scripts": { + "env-test": "vite env-test" + } } diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-env-test/snapshots.toml b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-env-test/snapshots.toml index 94a6dfdf..069aa8e5 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-env-test/snapshots.toml +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-env-test/snapshots.toml @@ -3,12 +3,12 @@ [[e2e]] name = "env-test prints value from additional_envs" steps = [ - "vite env-test SYNTHETIC_ENV_VAR test_value_from_synthesizer # prints env value", + "vite run env-test -- SYNTHETIC_ENV_VAR test_value_from_synthesizer # prints env value", ] [[e2e]] name = "env-test with different values" steps = [ - "vite env-test FOO bar # sets FOO=bar", - "vite env-test BAZ qux # sets BAZ=qux", + "vite run env-test -- FOO bar # sets FOO=bar", + "vite run env-test -- BAZ qux # sets BAZ=qux", ] diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-env-test/snapshots/env-test prints value from additional_envs.snap b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-env-test/snapshots/env-test prints value from additional_envs.snap index caf589c6..eb60e270 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-env-test/snapshots/env-test prints value from additional_envs.snap +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-env-test/snapshots/env-test prints value from additional_envs.snap @@ -1,8 +1,22 @@ --- source: crates/vite_task_bin/tests/e2e_snapshots/main.rs -assertion_line: 203 expression: e2e_outputs input_file: crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-env-test --- -> vite env-test SYNTHETIC_ENV_VAR test_value_from_synthesizer # prints env value +> vite run env-test -- SYNTHETIC_ENV_VAR test_value_from_synthesizer # prints env value +$ vite env-test SYNTHETIC_ENV_VAR test_value_from_synthesizer test_value_from_synthesizer + + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Vite+ Task Runner • Execution Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Statistics: 1 tasks • 0 cache hits • 1 cache misses +Performance: 0% cache hit rate + +Task Details: +──────────────────────────────────────────────── + [1] e2e-env-test#env-test: $ vite env-test SYNTHETIC_ENV_VAR test_value_from_synthesizer ✓ + → Cache miss: no previous cache entry found +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-env-test/snapshots/env-test with different values.snap b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-env-test/snapshots/env-test with different values.snap index 375b1f1f..43b582c2 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-env-test/snapshots/env-test with different values.snap +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-env-test/snapshots/env-test with different values.snap @@ -1,12 +1,40 @@ --- source: crates/vite_task_bin/tests/e2e_snapshots/main.rs -assertion_line: 203 expression: e2e_outputs input_file: crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-env-test --- -> vite env-test FOO bar # sets FOO=bar +> vite run env-test -- FOO bar # sets FOO=bar +$ vite env-test FOO bar bar -> vite env-test BAZ qux # sets BAZ=qux +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Vite+ Task Runner • Execution Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Statistics: 1 tasks • 0 cache hits • 1 cache misses +Performance: 0% cache hit rate + +Task Details: +──────────────────────────────────────────────── + [1] e2e-env-test#env-test: $ vite env-test FOO bar ✓ + → Cache miss: no previous cache entry found +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +> vite run env-test -- BAZ qux # sets BAZ=qux +$ vite env-test BAZ qux qux + + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Vite+ Task Runner • Execution Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Statistics: 1 tasks • 0 cache hits • 1 cache misses +Performance: 0% cache hit rate + +Task Details: +──────────────────────────────────────────────── + [1] e2e-env-test#env-test: $ vite env-test BAZ qux ✓ + → Cache miss: no previous cache entry found +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-lint-cache/package.json b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-lint-cache/package.json index c9faaf6d..8aeca1bc 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-lint-cache/package.json +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-lint-cache/package.json @@ -1,4 +1,7 @@ { "name": "e2e-lint-cache", - "private": true + "private": true, + "scripts": { + "lint": "vite lint" + } } diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-lint-cache/snapshots.toml b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-lint-cache/snapshots.toml index 1ad17a2d..ed0f46fb 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-lint-cache/snapshots.toml +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-lint-cache/snapshots.toml @@ -3,7 +3,7 @@ [[e2e]] name = "direct lint" steps = [ - "vite lint # cache miss", + "vite run lint # cache miss", "echo debugger > main.js # add lint error", - "vite lint # cache miss, lint fails", + "vite run lint # cache miss, lint fails", ] diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-lint-cache/snapshots/direct lint.snap b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-lint-cache/snapshots/direct lint.snap index 5fafaefe..66a27871 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-lint-cache/snapshots/direct lint.snap +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-lint-cache/snapshots/direct lint.snap @@ -1,17 +1,31 @@ --- source: crates/vite_task_bin/tests/e2e_snapshots/main.rs -assertion_line: 203 expression: e2e_outputs input_file: crates/vite_task_bin/tests/e2e_snapshots/fixtures/e2e-lint-cache --- -> vite lint # cache miss +> vite run lint # cache miss +$ vite lint Found 0 warnings and 0 errors. Finished in on 0 files with 90 rules using threads. +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Vite+ Task Runner • Execution Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Statistics: 1 tasks • 0 cache hits • 1 cache misses +Performance: 0% cache hit rate + +Task Details: +──────────────────────────────────────────────── + [1] e2e-lint-cache#lint: $ vite lint ✓ + → Cache miss: no previous cache entry found +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + > echo debugger > main.js # add lint error -> vite lint # cache miss, lint fails +> vite run lint # cache miss, lint fails +$ vite lint ✗ cache miss: content of input '' changed, executing ! eslint(no-debugger): `debugger` statement is not allowed ,-[main.js:1:1] @@ -22,3 +36,17 @@ Finished in on 0 files with 90 rules using threads. Found 1 warning and 0 errors. Finished in on 1 file with 90 rules using threads. + + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Vite+ Task Runner • Execution Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Statistics: 1 tasks • 0 cache hits • 1 cache misses +Performance: 0% cache hit rate + +Task Details: +──────────────────────────────────────────────── + [1] e2e-lint-cache#lint: $ vite lint ✓ + → Cache miss: content of input '' changed +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/exec-api/package.json b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/exec-api/package.json new file mode 100644 index 00000000..bd72b0f4 --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/exec-api/package.json @@ -0,0 +1,6 @@ +{ + "name": "@test/exec-api", + "scripts": { + "lint-task": "vite lint" + } +} diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/exec-api/snapshots.toml b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/exec-api/snapshots.toml new file mode 100644 index 00000000..884832c1 --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/exec-api/snapshots.toml @@ -0,0 +1,13 @@ +[[e2e]] +name = "exec not triggered from script" +steps = [ + "FOO=bar vite run lint-task # no print-env FOO output", +] + +[[e2e]] +name = "exec caching" +steps = [ + "FOO=bar vite lint # cache miss", + "FOO=bar vite lint # cache hit, silent", + "FOO=baz vite lint # env changed, cache miss", +] diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/exec-api/snapshots/exec caching.snap b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/exec-api/snapshots/exec caching.snap new file mode 100644 index 00000000..98f77ba3 --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/exec-api/snapshots/exec caching.snap @@ -0,0 +1,17 @@ +--- +source: crates/vite_task_bin/tests/e2e_snapshots/main.rs +expression: e2e_outputs +input_file: crates/vite_task_bin/tests/e2e_snapshots/fixtures/exec-api +--- +> FOO=bar vite lint # cache miss +bar + +Lint { args: [] } + +> FOO=bar vite lint # cache hit, silent +Lint { args: [] } + +> FOO=baz vite lint # env changed, cache miss +baz + +Lint { args: [] } diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/exec-api/snapshots/exec not triggered from script.snap b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/exec-api/snapshots/exec not triggered from script.snap new file mode 100644 index 00000000..3a822a4b --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/exec-api/snapshots/exec not triggered from script.snap @@ -0,0 +1,23 @@ +--- +source: crates/vite_task_bin/tests/e2e_snapshots/main.rs +expression: e2e_outputs +input_file: crates/vite_task_bin/tests/e2e_snapshots/fixtures/exec-api +--- +> FOO=bar vite run lint-task # no print-env FOO output +$ vite lint +Found 0 warnings and 0 errors. +Finished in on 0 files with 90 rules using threads. + + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Vite+ Task Runner • Execution Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Statistics: 1 tasks • 0 cache hits • 1 cache misses +Performance: 0% cache hit rate + +Task Details: +──────────────────────────────────────────────── + [1] @test/exec-api#lint-task: $ vite lint ✓ + → Cache miss: no previous cache entry found +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/same-name-as-builtin/vite-task.json b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/exec-api/vite-task.json similarity index 100% rename from crates/vite_task_bin/tests/e2e_snapshots/fixtures/same-name-as-builtin/vite-task.json rename to crates/vite_task_bin/tests/e2e_snapshots/fixtures/exec-api/vite-task.json diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/lint-dot-git/package.json b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/lint-dot-git/package.json index 0967ef42..3f45091c 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/lint-dot-git/package.json +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/lint-dot-git/package.json @@ -1 +1,5 @@ -{} +{ + "scripts": { + "lint": "vite lint" + } +} diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/lint-dot-git/snapshots.toml b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/lint-dot-git/snapshots.toml index 134dc2e5..6d0d9103 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/lint-dot-git/snapshots.toml +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/lint-dot-git/snapshots.toml @@ -4,7 +4,7 @@ name = "lint dot git" steps = [ "mkdir .git", - "vite lint # cache miss", + "vite run lint # cache miss", "echo hello > .git/foo.txt # add file inside .git", - "vite lint # cache hit, .git is ignored", + "vite run lint # cache hit, .git is ignored", ] diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/lint-dot-git/snapshots/lint dot git.snap b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/lint-dot-git/snapshots/lint dot git.snap index 5ced85d4..353eea7a 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/lint-dot-git/snapshots/lint dot git.snap +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/lint-dot-git/snapshots/lint dot git.snap @@ -1,12 +1,12 @@ --- source: crates/vite_task_bin/tests/e2e_snapshots/main.rs -assertion_line: 203 expression: e2e_outputs input_file: crates/vite_task_bin/tests/e2e_snapshots/fixtures/lint-dot-git --- > mkdir .git -> vite lint # cache miss +> vite run lint # cache miss +$ vite lint ! eslint-plugin-unicorn(no-empty-file): Empty files are not allowed. ,-[a.js:1:1] @@ -19,9 +19,23 @@ Found 1 warning and 0 errors. Finished in on 1 file with 90 rules using threads. +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Vite+ Task Runner • Execution Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Statistics: 1 tasks • 0 cache hits • 1 cache misses +Performance: 0% cache hit rate + +Task Details: +──────────────────────────────────────────────── + [1] lint: $ vite lint ✓ + → Cache miss: no previous cache entry found +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + > echo hello > .git/foo.txt # add file inside .git -> vite lint # cache hit, .git is ignored +> vite run lint # cache hit, .git is ignored +$ vite lint ✓ cache hit, replaying ! eslint-plugin-unicorn(no-empty-file): Empty files are not allowed. ,-[a.js:1:1] @@ -32,4 +46,17 @@ Finished in on 1 file with 90 rules using threads. Found 1 warning and 0 errors. Finished in on 1 file with 90 rules using threads. -✓ cache hit, logs replayed + + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Vite+ Task Runner • Execution Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Statistics: 1 tasks • 1 cache hits • 0 cache misses +Performance: 100% cache hit rate, saved in total + +Task Details: +──────────────────────────────────────────────── + [1] lint: $ vite lint ✓ + → Cache hit - output replayed - saved +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/same-name-as-builtin/a.js b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/same-name-as-builtin/a.js deleted file mode 100644 index dddcb9f2..00000000 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/same-name-as-builtin/a.js +++ /dev/null @@ -1 +0,0 @@ -// Empty JS file for oxlint diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/same-name-as-builtin/package.json b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/same-name-as-builtin/package.json deleted file mode 100644 index 008774dd..00000000 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/same-name-as-builtin/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "scripts": { - "lint": "echo custom lint script" - } -} diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/same-name-as-builtin/snapshots.toml b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/same-name-as-builtin/snapshots.toml deleted file mode 100644 index dc6a58f1..00000000 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/same-name-as-builtin/snapshots.toml +++ /dev/null @@ -1,11 +0,0 @@ -# Tests that user task named "lint" doesn't conflict with builtin lint - -[[e2e]] -name = "same name as builtin" -steps = [ - "vite lint # runs builtin oxlint", - "vite run lint # runs user-defined lint script", - "echo 'console.log(1);' > a.js # add valid JS file", - "vite lint # builtin lint: cache miss", - "vite run lint # user lint: cache miss", -] diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/same-name-as-builtin/snapshots/same name as builtin.snap b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/same-name-as-builtin/snapshots/same name as builtin.snap deleted file mode 100644 index 08376497..00000000 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/same-name-as-builtin/snapshots/same name as builtin.snap +++ /dev/null @@ -1,61 +0,0 @@ ---- -source: crates/vite_task_bin/tests/e2e_snapshots/main.rs -assertion_line: 203 -expression: e2e_outputs -input_file: crates/vite_task_bin/tests/e2e_snapshots/fixtures/same-name-as-builtin ---- -> vite lint # runs builtin oxlint - - ! eslint-plugin-unicorn(no-empty-file): Empty files are not allowed. - ,-[a.js:1:1] - 1 | // Empty JS file for oxlint - : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - `---- - help: Delete this file or add some code to it. - -Found 1 warning and 0 errors. -Finished in on 1 file with 90 rules using threads. - - -> vite run lint # runs user-defined lint script -$ echo custom lint script ⊘ cache disabled: built-in command -custom lint script - - -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - Vite+ Task Runner • Execution Summary -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - -Statistics: 1 tasks • 0 cache hits • 0 cache misses • 1 cache disabled -Performance: 0% cache hit rate - -Task Details: -──────────────────────────────────────────────── - [1] lint: $ echo custom lint script ✓ - → Cache disabled for built-in command -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - -> echo 'console.log(1);' > a.js # add valid JS file - -> vite lint # builtin lint: cache miss -Found 0 warnings and 0 errors. -Finished in on 1 file with 90 rules using threads. - - -> vite run lint # user lint: cache miss -$ echo custom lint script ⊘ cache disabled: built-in command -custom lint script - - -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - Vite+ Task Runner • Execution Summary -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - -Statistics: 1 tasks • 0 cache hits • 0 cache misses • 1 cache disabled -Performance: 0% cache hit rate - -Task Details: -──────────────────────────────────────────────── - [1] lint: $ echo custom lint script ✓ - → Cache disabled for built-in command -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/vite_task_bin/tests/e2e_snapshots/redact.rs b/crates/vite_task_bin/tests/e2e_snapshots/redact.rs index 4877b243..0e6d1de5 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/redact.rs +++ b/crates/vite_task_bin/tests/e2e_snapshots/redact.rs @@ -52,5 +52,53 @@ pub fn redact_e2e_output(mut output: String, workspace_root: &str) -> String { .unwrap(); output = node_trace_warning_regex.replace_all(&output, "").into_owned(); + // Sort consecutive diagnostic blocks to handle non-deterministic tool output + // (e.g., oxlint reports warnings in arbitrary order due to multi-threading). + // Each block starts with " ! " and ends at the next empty line. + output = sort_diagnostic_blocks(output); + output } + +fn sort_diagnostic_blocks(output: String) -> String { + let parts: Vec<&str> = output.split('\n').collect(); + let mut result: Vec<&str> = Vec::new(); + let mut i = 0; + + while i < parts.len() { + if parts[i].starts_with(" ! ") { + let mut blocks: Vec> = Vec::new(); + + loop { + if i >= parts.len() || !parts[i].starts_with(" ! ") { + break; + } + let mut block: Vec<&str> = Vec::new(); + while i < parts.len() && !parts[i].is_empty() { + block.push(parts[i]); + i += 1; + } + blocks.push(block); + // Skip the empty line separator between blocks + if i < parts.len() && parts[i].is_empty() { + i += 1; + } + } + + blocks.sort(); + + for (j, block) in blocks.iter().enumerate() { + result.extend_from_slice(block); + // Restore empty line separators (between blocks + trailing) + if j < blocks.len() - 1 || i <= parts.len() { + result.push(""); + } + } + } else { + result.push(parts[i]); + i += 1; + } + } + + result.join("\n") +} diff --git a/crates/vite_task_plan/Cargo.toml b/crates/vite_task_plan/Cargo.toml index 2cdd741f..b3585966 100644 --- a/crates/vite_task_plan/Cargo.toml +++ b/crates/vite_task_plan/Cargo.toml @@ -31,6 +31,7 @@ vite_task_graph = { workspace = true } which = { workspace = true } [dev-dependencies] +clap = { workspace = true, features = ["derive"] } copy_dir = { workspace = true } cow-utils = { workspace = true } insta = { workspace = true, features = ["glob", "json", "redactions", "filters", "ron"] } diff --git a/crates/vite_task_plan/README.md b/crates/vite_task_plan/README.md index 7a66daa7..8416b174 100644 --- a/crates/vite_task_plan/README.md +++ b/crates/vite_task_plan/README.md @@ -31,9 +31,9 @@ There are two types of execution requests: - Queries the task graph based on task patterns - Builds execution graph with dependency ordering -2. **Synthetic Request** - Execute on-the-fly tasks not in the graph (e.g., `vite lint`) - - Generated dynamically with provided configuration - - Used for built-in commands +2. **Synthetic Request** - Execute on-the-fly tasks not in the graph (e.g., `vite lint` in a task script) + - Generated dynamically by the TaskSynthesizer + - Used for synthesized commands within task scripts ### Execution Items diff --git a/crates/vite_task_plan/src/cache_metadata.rs b/crates/vite_task_plan/src/cache_metadata.rs index 83136f6b..46690f21 100644 --- a/crates/vite_task_plan/src/cache_metadata.rs +++ b/crates/vite_task_plan/src/cache_metadata.rs @@ -7,17 +7,9 @@ use vite_str::Str; use crate::envs::EnvFingerprints; -/// The kind of a key to identify an execution. -#[derive(Debug, Encode, bincode::Decode, Serialize)] -pub(crate) enum ExecutionCacheKeyKind { - /// This execution is directly from a custom syntactic vite-task subcommand (like `vite lint`). - /// - /// Note that this is only for the case where the subcommand is directly typed in the cli, - /// not from a task script (like `"lint-task": "vite lint"`), which is covered by the `UserTask` variant. - DirectSyntactic { - /// Provided in `SyntheticPlanRequest.direct_execution_cache_key` by task synthezier - direct_execution_cache_key: Arc<[Str]>, - }, +/// Key to identify an execution across sessions. +#[derive(Debug, Encode, Decode, Serialize)] +pub enum ExecutionCacheKey { /// This execution is from a script of a user-defined task. UserTask { /// The name of the user-defined task. @@ -28,21 +20,13 @@ pub(crate) enum ExecutionCacheKeyKind { /// Extra args provided when invoking the user-defined task (`vite [task_name] [extra_args...]`). /// These args are appended to the last and_item. Non-last and_items don't get extra args. extra_args: Arc<[Str]>, + /// The package path where the user-defined task is defined, relative to the workspace root. + package_path: RelativePathBuf, }, -} - -/// Key to identify an execution -#[derive(Debug, Encode, Decode, Serialize)] -pub struct ExecutionCacheKey { - /// The kind of the execution cache key (DirectSyntactic or UserTask) - pub(crate) kind: ExecutionCacheKeyKind, - /// The origin path where this execution is planned from. - /// It's relative to the workspace root. + /// This execution is from a synthetic task directly invoked from `Session::plan_exec` API. /// - /// - For DirectSyntactic, it's the cwd where the command `vite [custom subcommand] ...` is run. - /// It's not necessarily the actual cwd that the synthesized task runs in. - /// - For UserTask, it's the package path where the user-defined task is defined. - pub(crate) origin_path: RelativePathBuf, + /// The cache key is an opaque value provided by the caller. + ExecAPI(Arc<[Str]>), } /// Cache information for a spawn execution. diff --git a/crates/vite_task_plan/src/lib.rs b/crates/vite_task_plan/src/lib.rs index 346b73ba..d434ae14 100644 --- a/crates/vite_task_plan/src/lib.rs +++ b/crates/vite_task_plan/src/lib.rs @@ -22,7 +22,7 @@ use execution_graph::ExecutionGraph; use in_process::InProcessExecution; pub use path_env::{get_path_env, prepend_path_env}; use plan::{plan_query_request, plan_synthetic_request}; -use plan_request::PlanRequest; +use plan_request::{PlanRequest, SyntheticPlanRequest}; use serde::{Serialize, ser::SerializeMap as _}; use vite_graph_ser::serialize_by_key; use vite_path::AbsolutePath; @@ -141,13 +141,13 @@ pub enum LeafExecutionKind { /// An execution item, from a split subcommand in a task's command (`item1 && item2 && ...`). #[derive(Debug, Serialize)] pub enum ExecutionItemKind { - /// Expanded from a known vite subcommand, like `vite run ...` or `vite lint`. + /// Expanded from a known vite subcommand, like `vite run ...` or a synthesized task. Expanded(#[serde(serialize_with = "serialize_by_key")] ExecutionGraph), /// A normal execution that spawns a child process, like `tsc --noEmit`. Leaf(LeafExecutionKind), } -/// The callback trait for parsing plan requests from cli args. +/// The callback trait for parsing plan requests from script commands. /// See the method for details. #[async_trait::async_trait(?Send)] pub trait PlanRequestParser: Debug { @@ -155,16 +155,16 @@ pub trait PlanRequestParser: Debug { /// /// `vite_task_plan` doesn't have the knowledge of how cli args should be parsed. It relies on this callback. /// + /// The implementation can either mutate `command` or return a `PlanRequest`: /// - If it returns `Err`, the planning will abort with the returned error. - /// - If it returns `Ok(None)`, the command will be spawned as a normal process. - /// - If it returns `Ok(Some(PlanRequest::Query)`, the command will be expanded as a `ExpandedExecution` with a task graph queried from the returned `TaskQuery`. - /// - If it returns `Ok(Some(PlanRequest::Synthetic)`, the command will become a `SpawnExecution` with the synthetic task. + /// - If it returns `Ok(None)`, the (potentially mutated) `command` will be spawned as a normal process. + /// - If it returns `Ok(Some(PlanRequest::Query))`, the command will be expanded as a `ExpandedExecution` with a task graph queried from the returned `TaskQuery`. + /// - If it returns `Ok(Some(PlanRequest::Synthetic))`, the command will become a `SpawnExecution` with the synthetic task. + /// + /// When a `PlanRequest` is returned, any mutations to `command` are discarded. async fn get_plan_request( &mut self, - program: &str, - args: &[Str], - envs: &Arc, Arc>>, - cwd: &Arc, + command: &mut plan_request::ScriptCommand, ) -> anyhow::Result>; } @@ -225,4 +225,22 @@ impl ExecutionPlan { }; Ok(Self { root_node }) } + + pub fn plan_synthetic( + workspace_path: &Arc, + cwd: &Arc, + synthetic_plan_request: SyntheticPlanRequest, + cache_key: Arc<[Str]>, + ) -> Result { + let execution_cache_key = cache_metadata::ExecutionCacheKey::ExecAPI(cache_key); + let execution = plan_synthetic_request( + workspace_path, + &Default::default(), + synthetic_plan_request, + Some(execution_cache_key), + cwd, + ) + .with_empty_call_stack()?; + Ok(Self { root_node: ExecutionItemKind::Leaf(LeafExecutionKind::Spawn(execution)) }) + } } diff --git a/crates/vite_task_plan/src/plan.rs b/crates/vite_task_plan/src/plan.rs index 30e2dcaa..bf5a2547 100644 --- a/crates/vite_task_plan/src/plan.rs +++ b/crates/vite_task_plan/src/plan.rs @@ -16,10 +16,7 @@ use vite_task_graph::{TaskNodeIndex, config::ResolvedTaskOptions}; use crate::{ ExecutionItem, ExecutionItemDisplay, ExecutionItemKind, LeafExecutionKind, PlanContext, SpawnCommand, SpawnExecution, TaskExecution, - cache_metadata::{ - CacheMetadata, ExecutionCacheKey, ExecutionCacheKeyKind, ProgramFingerprint, - SpawnFingerprint, - }, + cache_metadata::{CacheMetadata, ExecutionCacheKey, ProgramFingerprint, SpawnFingerprint}, envs::EnvFingerprints, error::{ CdCommandError, Error, PathFingerprintError, PathFingerprintErrorKind, PathType, @@ -28,7 +25,7 @@ use crate::{ execution_graph::{ExecutionGraph, ExecutionNodeIndex}, in_process::InProcessExecution, path_env::get_path_env, - plan_request::{PlanRequest, QueryPlanRequest, SyntheticPlanRequest}, + plan_request::{PlanRequest, QueryPlanRequest, ScriptCommand, SyntheticPlanRequest}, }; /// Locate the executable path for a given program name in the provided envs and cwd. @@ -144,13 +141,11 @@ async fn plan_task_as_execution_node( } // Create execution cache key for this and_item - let task_execution_cache_key = ExecutionCacheKey { - kind: ExecutionCacheKeyKind::UserTask { - task_name: task_node.task_display.task_name.clone(), - and_item_index: index, - extra_args: Arc::clone(&extra_args), - }, - origin_path: strip_prefix_for_cache(package_path, context.workspace_path()) + let task_execution_cache_key = ExecutionCacheKey::UserTask { + task_name: task_node.task_display.task_name.clone(), + and_item_index: index, + extra_args: Arc::clone(&extra_args), + package_path: strip_prefix_for_cache(package_path, context.workspace_path()) .map_err(|kind| { TaskPlanErrorKind::PathFingerprintError(PathFingerprintError { kind, @@ -162,14 +157,20 @@ async fn plan_task_as_execution_node( // Try to parse the args of an and_item to a plan request like `run -r build` let envs: Arc, Arc>> = context.envs().clone().into(); + let mut script_command = ScriptCommand { + program: and_item.program.clone(), + args: args.into(), + envs, + cwd: Arc::clone(&cwd), + }; let plan_request = context .callbacks() - .get_plan_request(&and_item.program, &args, &envs, &cwd) + .get_plan_request(&mut script_command) .await .map_err(|error| TaskPlanErrorKind::ParsePlanRequestError { - program: and_item.program.clone(), - args: args.clone().into(), - cwd: Arc::clone(&cwd), + program: script_command.program.clone(), + args: Arc::clone(&script_command.args), + cwd: Arc::clone(&script_command.cwd), error, }) .with_plan_context(&context)?; @@ -182,7 +183,7 @@ async fn plan_task_as_execution_node( let execution_graph = plan_query_request(query_plan_request, context).await?; ExecutionItemKind::Expanded(execution_graph) } - // Synthetic task, like `vite lint` + // Synthetic task (from CommandHandler) Some(PlanRequest::Synthetic(synthetic_plan_request)) => { let spawn_execution = plan_synthetic_request( context.workspace_path(), @@ -194,20 +195,23 @@ async fn plan_task_as_execution_node( .with_plan_context(&context)?; ExecutionItemKind::Leaf(LeafExecutionKind::Spawn(spawn_execution)) } - // Normal 3rd party tool command (like `tsc --noEmit`) + // Normal 3rd party tool command (like `tsc --noEmit`), using potentially mutated script_command None => { - let program_path = - which(&OsStr::new(&and_item.program).into(), context.envs(), &cwd) - .map_err(TaskPlanErrorKind::ProgramNotFound) - .with_plan_context(&context)?; + let program_path = which( + &OsStr::new(&script_command.program).into(), + &script_command.envs, + &script_command.cwd, + ) + .map_err(TaskPlanErrorKind::ProgramNotFound) + .with_plan_context(&context)?; let spawn_execution = plan_spawn_execution( context.workspace_path(), - task_execution_cache_key, + Some(task_execution_cache_key), &and_item.envs, &task_node.resolved_config.resolved_options, - context.envs(), + &script_command.envs, program_path, - args.into(), + script_command.args, ) .with_plan_context(&context)?; ExecutionItemKind::Leaf(LeafExecutionKind::Spawn(spawn_execution)) @@ -250,13 +254,11 @@ async fn plan_task_as_execution_node( let spawn_execution = plan_spawn_execution( context.workspace_path(), - ExecutionCacheKey { - kind: ExecutionCacheKeyKind::UserTask { - task_name: task_node.task_display.task_name.clone(), - and_item_index: 0, - extra_args: Arc::clone(context.extra_args()), - }, - origin_path: strip_prefix_for_cache(package_path, context.workspace_path()) + Some(ExecutionCacheKey::UserTask { + task_name: task_node.task_display.task_name.clone(), + and_item_index: 0, + extra_args: Arc::clone(context.extra_args()), + package_path: strip_prefix_for_cache(package_path, context.workspace_path()) .map_err(|kind| { TaskPlanErrorKind::PathFingerprintError(PathFingerprintError { kind, @@ -264,7 +266,7 @@ async fn plan_task_as_execution_node( }) }) .with_plan_context(&context)?, - }, + }), &BTreeMap::new(), &task_node.resolved_config.resolved_options, context.envs(), @@ -285,28 +287,14 @@ pub fn plan_synthetic_request( workspace_path: &Arc, prefix_envs: &BTreeMap, synthetic_plan_request: SyntheticPlanRequest, - // generated from the task, overrides `synthetic_plan_request.direct_execution_cache_key` - task_execution_cache_key: Option, + execution_cache_key: Option, cwd: &Arc, ) -> Result { - let SyntheticPlanRequest { program, args, task_options, direct_execution_cache_key, envs } = - synthetic_plan_request; + let SyntheticPlanRequest { program, args, task_options, envs } = synthetic_plan_request; let program_path = which(&program, &envs, cwd).map_err(TaskPlanErrorKind::ProgramNotFound)?; let resolved_options = ResolvedTaskOptions::resolve(task_options, &cwd); - let execution_cache_key = if let Some(task_execution_cache_key) = task_execution_cache_key { - // Use task generated cache key if any - task_execution_cache_key - } else { - // Otherwise, use direct execution cache key - ExecutionCacheKey { - kind: ExecutionCacheKeyKind::DirectSyntactic { direct_execution_cache_key }, - origin_path: strip_prefix_for_cache(cwd, workspace_path) - .map_err(|kind| PathFingerprintError { kind, path_type: PathType::Cwd })?, - } - }; - plan_spawn_execution( workspace_path, execution_cache_key, @@ -337,7 +325,7 @@ fn strip_prefix_for_cache( fn plan_spawn_execution( workspace_path: &Arc, - execution_cache_key: ExecutionCacheKey, + execution_cache_key: Option, prefix_envs: &BTreeMap, resolved_task_options: &ResolvedTaskOptions, envs: &HashMap, Arc>, @@ -391,7 +379,10 @@ fn plan_spawn_execution( env_fingerprints, fingerprint_ignores: None, }; - resolved_cache_metadata = Some(CacheMetadata { execution_cache_key, spawn_fingerprint }); + if let Some(execution_cache_key) = execution_cache_key { + resolved_cache_metadata = + Some(CacheMetadata { execution_cache_key, spawn_fingerprint }); + } } // Add prefix envs to all envs diff --git a/crates/vite_task_plan/src/plan_request.rs b/crates/vite_task_plan/src/plan_request.rs index 6e4b9b08..d0c406a0 100644 --- a/crates/vite_task_plan/src/plan_request.rs +++ b/crates/vite_task_plan/src/plan_request.rs @@ -1,8 +1,22 @@ use std::{collections::HashMap, ffi::OsStr, sync::Arc}; +use vite_path::AbsolutePath; use vite_str::Str; use vite_task_graph::{config::user::UserTaskOptions, query::TaskQuery}; +/// A parsed command from a task script, passed to [`super::PlanRequestParser::get_plan_request`]. +/// +/// All fields use `Arc` for cheap reassignment. The implementation can mutate +/// these fields to modify how the command is executed when it falls through as a +/// normal process (i.e., when `get_plan_request` returns `None`). +#[derive(Debug)] +pub struct ScriptCommand { + pub program: Str, + pub args: Arc<[Str]>, + pub envs: Arc, Arc>>, + pub cwd: Arc, +} + #[derive(Debug)] pub struct PlanOptions { pub extra_args: Arc<[Str]>, @@ -19,7 +33,7 @@ pub struct QueryPlanRequest { pub plan_options: PlanOptions, } -/// The request to run a synthetic task, like `vite lint` or `vite exec ...` +/// The request to run a synthetic task (e.g., one generated by TaskSynthesizer from `vite lint` in a script). /// Synthetic tasks are not defined in the task graph, but are generated on-the-fly. #[derive(Debug)] pub struct SyntheticPlanRequest { @@ -32,10 +46,6 @@ pub struct SyntheticPlanRequest { /// The task options as if it's defined in `vite.config.*` pub task_options: UserTaskOptions, - /// The cache key for execution directly issued from user command line. - /// It typically includes the subcommand name and all args after it. (e.g. `["lint", "--fix"]` for `vite lint --fix`) - pub direct_execution_cache_key: Arc<[Str]>, - /// All environment variables to set for the synthetic task. /// /// This is set in the plan stage before resolving envs for caching. @@ -51,6 +61,6 @@ pub struct SyntheticPlanRequest { pub enum PlanRequest { /// The request to run tasks queried from the task graph, like `vite run ...` or `vite run-many ...`. Query(QueryPlanRequest), - /// The request to run a synthetic task (not defined in the task graph), like `vite lint` or `vite exec ...`. + /// The request to run a synthetic task (not defined in the task graph), e.g., from TaskSynthesizer. Synthetic(SyntheticPlanRequest), } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional-envs/package.json b/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional-envs/package.json index a3f69d92..670b1e46 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional-envs/package.json +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional-envs/package.json @@ -2,6 +2,7 @@ "name": "additional-envs", "private": true, "scripts": { - "hello": "echo hello" + "hello": "echo hello", + "env-test": "vite env-test TEST_VAR hello_world" } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional-envs/snapshots.toml b/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional-envs/snapshots.toml index ac268dc8..326567a0 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional-envs/snapshots.toml +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional-envs/snapshots.toml @@ -1,18 +1,5 @@ # Tests env-test synthetic command with additional_envs [[plan]] -name = "direct env-test synthetic task" -args = ["env-test", "TEST_VAR", "hello_world"] - -[[e2e]] -name = "env-test prints value from additional_envs" -steps = [ - "vite env-test SYNTHETIC_ENV_VAR test_value_from_synthesizer # prints env value", -] - -[[e2e]] -name = "env-test with different values" -steps = [ - "vite env-test FOO bar # sets FOO=bar", - "vite env-test BAZ qux # sets BAZ=qux", -] +name = "env-test synthetic task in user task" +args = ["run", "env-test"] diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional-envs/snapshots/query - direct env-test synthetic task.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional-envs/snapshots/query - direct env-test synthetic task.snap deleted file mode 100644 index 9f844c66..00000000 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional-envs/snapshots/query - direct env-test synthetic task.snap +++ /dev/null @@ -1,58 +0,0 @@ ---- -source: crates/vite_task_bin/tests/test_snapshots/main.rs -expression: "&plan_json" -input_file: crates/vite_task_bin/tests/test_snapshots/fixtures/additional-envs ---- -{ - "root_node": { - "Leaf": { - "Spawn": { - "cache_metadata": { - "spawn_fingerprint": { - "cwd": "", - "program_fingerprint": { - "OutsideWorkspace": { - "program_name": "print-env" - } - }, - "args": [ - "TEST_VAR" - ], - "env_fingerprints": { - "fingerprinted_envs": {}, - "pass_through_env_config": [ - "TEST_VAR", - "" - ] - }, - "fingerprint_ignores": null - }, - "execution_cache_key": { - "kind": { - "DirectSyntactic": { - "direct_execution_cache_key": [ - "env-test", - "TEST_VAR", - "hello_world" - ] - } - }, - "origin_path": "" - } - }, - "spawn_command": { - "program_path": "/node_modules/.bin/print-env", - "args": [ - "TEST_VAR" - ], - "all_envs": { - "NO_COLOR": "1", - "PATH": "/node_modules/.bin:/node_modules/.bin", - "TEST_VAR": "hello_world" - }, - "cwd": "/" - } - } - } - } -} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional-envs/snapshots/query - env-test synthetic task in user task.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional-envs/snapshots/query - env-test synthetic task in user task.snap new file mode 100644 index 00000000..2087308b --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional-envs/snapshots/query - env-test synthetic task in user task.snap @@ -0,0 +1,86 @@ +--- +source: crates/vite_task_plan/tests/plan_snapshots/main.rs +expression: "&plan_json" +input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/additional-envs +--- +{ + "root_node": { + "Expanded": [ + { + "key": [ + "/", + "env-test" + ], + "node": { + "task_display": { + "package_name": "additional-envs", + "task_name": "env-test", + "package_path": "/" + }, + "items": [ + { + "execution_item_display": { + "task_display": { + "package_name": "additional-envs", + "task_name": "env-test", + "package_path": "/" + }, + "command": "vite env-test TEST_VAR hello_world", + "and_item_index": null, + "cwd": "/" + }, + "kind": { + "Leaf": { + "Spawn": { + "cache_metadata": { + "spawn_fingerprint": { + "cwd": "", + "program_fingerprint": { + "OutsideWorkspace": { + "program_name": "print-env" + } + }, + "args": [ + "TEST_VAR" + ], + "env_fingerprints": { + "fingerprinted_envs": {}, + "pass_through_env_config": [ + "TEST_VAR", + "" + ] + }, + "fingerprint_ignores": null + }, + "execution_cache_key": { + "UserTask": { + "task_name": "env-test", + "and_item_index": 0, + "extra_args": [], + "package_path": "" + } + } + }, + "spawn_command": { + "program_path": "/node_modules/.bin/print-env", + "args": [ + "TEST_VAR" + ], + "all_envs": { + "NO_COLOR": "1", + "PATH": "/node_modules/.bin:/node_modules/.bin", + "TEST_VAR": "hello_world" + }, + "cwd": "/" + } + } + } + } + } + ] + }, + "neighbors": [] + } + ] + } +} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional-envs/snapshots/task graph.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional-envs/snapshots/task graph.snap index ad0b5839..72be1051 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional-envs/snapshots/task graph.snap +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional-envs/snapshots/task graph.snap @@ -1,9 +1,37 @@ --- -source: crates/vite_task_bin/tests/test_snapshots/main.rs +source: crates/vite_task_plan/tests/plan_snapshots/main.rs expression: task_graph_json -input_file: crates/vite_task_bin/tests/test_snapshots/fixtures/additional-envs +input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/additional-envs --- [ + { + "key": [ + "/", + "env-test" + ], + "node": { + "task_display": { + "package_name": "additional-envs", + "task_name": "env-test", + "package_path": "/" + }, + "resolved_config": { + "command": "vite env-test TEST_VAR hello_world", + "resolved_options": { + "cwd": "/", + "cache_config": { + "env_config": { + "fingerprinted_envs": [], + "pass_through_envs": [ + "" + ] + } + } + } + } + }, + "neighbors": [] + }, { "key": [ "/", diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots.toml b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots.toml index c4e8c96d..610b3d59 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots.toml +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots.toml @@ -13,17 +13,9 @@ name = "normal task with extra args" args = ["run", "hello", "a.txt"] [[plan]] -name = "direct synthetic task" -args = ["lint"] - -[[plan]] -name = "direct synthetic task with cwd" +name = "synthetic task in user task with cwd" cwd = "subdir" -args = ["lint"] - -[[plan]] -name = "direct synthetic task with extra args" -args = ["lint", "--fix"] +args = ["run", "lint"] [[plan]] name = "lint and echo with extra args" @@ -36,7 +28,7 @@ args = ["run", "echo-and-lint", "--fix"] [[e2e]] name = "direct lint" steps = [ - "vite lint # cache miss", + "vite run lint # cache miss", "echo debugger > main.js # add lint error", - "vite lint # cache miss, lint fails", + "vite run lint # cache miss, lint fails", ] diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - direct synthetic task with cwd.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - direct synthetic task with cwd.snap deleted file mode 100644 index 9c47d777..00000000 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - direct synthetic task with cwd.snap +++ /dev/null @@ -1,50 +0,0 @@ ---- -source: crates/vite_task_bin/tests/test_snapshots/main.rs -expression: "&plan_json" -input_file: crates/vite_task_bin/tests/test_snapshots/fixtures/cache-keys ---- -{ - "root_node": { - "Leaf": { - "Spawn": { - "cache_metadata": { - "spawn_fingerprint": { - "cwd": "subdir", - "program_fingerprint": { - "OutsideWorkspace": { - "program_name": "oxlint" - } - }, - "args": [], - "env_fingerprints": { - "fingerprinted_envs": {}, - "pass_through_env_config": [ - "" - ] - }, - "fingerprint_ignores": null - }, - "execution_cache_key": { - "kind": { - "DirectSyntactic": { - "direct_execution_cache_key": [ - "lint" - ] - } - }, - "origin_path": "subdir" - } - }, - "spawn_command": { - "program_path": "/node_modules/.bin/oxlint", - "args": [], - "all_envs": { - "NO_COLOR": "1", - "PATH": "/node_modules/.bin:/node_modules/.bin" - }, - "cwd": "/subdir" - } - } - } - } -} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - direct synthetic task with extra args.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - direct synthetic task with extra args.snap deleted file mode 100644 index 5beaeec6..00000000 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - direct synthetic task with extra args.snap +++ /dev/null @@ -1,55 +0,0 @@ ---- -source: crates/vite_task_bin/tests/test_snapshots/main.rs -expression: "&plan_json" -input_file: crates/vite_task_bin/tests/test_snapshots/fixtures/cache-keys ---- -{ - "root_node": { - "Leaf": { - "Spawn": { - "cache_metadata": { - "spawn_fingerprint": { - "cwd": "", - "program_fingerprint": { - "OutsideWorkspace": { - "program_name": "oxlint" - } - }, - "args": [ - "--fix" - ], - "env_fingerprints": { - "fingerprinted_envs": {}, - "pass_through_env_config": [ - "" - ] - }, - "fingerprint_ignores": null - }, - "execution_cache_key": { - "kind": { - "DirectSyntactic": { - "direct_execution_cache_key": [ - "lint", - "--fix" - ] - } - }, - "origin_path": "" - } - }, - "spawn_command": { - "program_path": "/node_modules/.bin/oxlint", - "args": [ - "--fix" - ], - "all_envs": { - "NO_COLOR": "1", - "PATH": "/node_modules/.bin:/node_modules/.bin" - }, - "cwd": "/" - } - } - } - } -} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - direct synthetic task.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - direct synthetic task.snap deleted file mode 100644 index b13900b4..00000000 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - direct synthetic task.snap +++ /dev/null @@ -1,50 +0,0 @@ ---- -source: crates/vite_task_bin/tests/test_snapshots/main.rs -expression: "&plan_json" -input_file: crates/vite_task_bin/tests/test_snapshots/fixtures/cache-keys ---- -{ - "root_node": { - "Leaf": { - "Spawn": { - "cache_metadata": { - "spawn_fingerprint": { - "cwd": "", - "program_fingerprint": { - "OutsideWorkspace": { - "program_name": "oxlint" - } - }, - "args": [], - "env_fingerprints": { - "fingerprinted_envs": {}, - "pass_through_env_config": [ - "" - ] - }, - "fingerprint_ignores": null - }, - "execution_cache_key": { - "kind": { - "DirectSyntactic": { - "direct_execution_cache_key": [ - "lint" - ] - } - }, - "origin_path": "" - } - }, - "spawn_command": { - "program_path": "/node_modules/.bin/oxlint", - "args": [], - "all_envs": { - "NO_COLOR": "1", - "PATH": "/node_modules/.bin:/node_modules/.bin" - }, - "cwd": "/" - } - } - } - } -} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - echo and lint with extra args.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - echo and lint with extra args.snap index 1643cb47..10f6851e 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - echo and lint with extra args.snap +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - echo and lint with extra args.snap @@ -1,7 +1,7 @@ --- -source: crates/vite_task_bin/tests/test_snapshots/main.rs +source: crates/vite_task_plan/tests/plan_snapshots/main.rs expression: "&plan_json" -input_file: crates/vite_task_bin/tests/test_snapshots/fixtures/cache-keys +input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys --- { "root_node": { @@ -78,16 +78,14 @@ input_file: crates/vite_task_bin/tests/test_snapshots/fixtures/cache-keys "fingerprint_ignores": null }, "execution_cache_key": { - "kind": { - "UserTask": { - "task_name": "echo-and-lint", - "and_item_index": 1, - "extra_args": [ - "--fix" - ] - } - }, - "origin_path": "" + "UserTask": { + "task_name": "echo-and-lint", + "and_item_index": 1, + "extra_args": [ + "--fix" + ], + "package_path": "" + } } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - lint and echo with extra args.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - lint and echo with extra args.snap index c95a028d..e5116b63 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - lint and echo with extra args.snap +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - lint and echo with extra args.snap @@ -1,7 +1,7 @@ --- -source: crates/vite_task_bin/tests/test_snapshots/main.rs +source: crates/vite_task_plan/tests/plan_snapshots/main.rs expression: "&plan_json" -input_file: crates/vite_task_bin/tests/test_snapshots/fixtures/cache-keys +input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys --- { "root_node": { @@ -50,14 +50,12 @@ input_file: crates/vite_task_bin/tests/test_snapshots/fixtures/cache-keys "fingerprint_ignores": null }, "execution_cache_key": { - "kind": { - "UserTask": { - "task_name": "lint-and-echo", - "and_item_index": 0, - "extra_args": [] - } - }, - "origin_path": "" + "UserTask": { + "task_name": "lint-and-echo", + "and_item_index": 0, + "extra_args": [], + "package_path": "" + } } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - normal task with extra args.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - normal task with extra args.snap index 8b4a858c..440abfa4 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - normal task with extra args.snap +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - normal task with extra args.snap @@ -1,7 +1,7 @@ --- -source: crates/vite_task_bin/tests/test_snapshots/main.rs +source: crates/vite_task_plan/tests/plan_snapshots/main.rs expression: "&plan_json" -input_file: crates/vite_task_bin/tests/test_snapshots/fixtures/cache-keys +input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys --- { "root_node": { @@ -52,16 +52,14 @@ input_file: crates/vite_task_bin/tests/test_snapshots/fixtures/cache-keys "fingerprint_ignores": null }, "execution_cache_key": { - "kind": { - "UserTask": { - "task_name": "hello", - "and_item_index": 0, - "extra_args": [ - "a.txt" - ] - } - }, - "origin_path": "" + "UserTask": { + "task_name": "hello", + "and_item_index": 0, + "extra_args": [ + "a.txt" + ], + "package_path": "" + } } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - synthetic task in user task with cwd.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - synthetic task in user task with cwd.snap new file mode 100644 index 00000000..a71b87e3 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - synthetic task in user task with cwd.snap @@ -0,0 +1,80 @@ +--- +source: crates/vite_task_plan/tests/plan_snapshots/main.rs +expression: "&plan_json" +input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys +--- +{ + "root_node": { + "Expanded": [ + { + "key": [ + "/", + "lint" + ], + "node": { + "task_display": { + "package_name": "", + "task_name": "lint", + "package_path": "/" + }, + "items": [ + { + "execution_item_display": { + "task_display": { + "package_name": "", + "task_name": "lint", + "package_path": "/" + }, + "command": "vite lint", + "and_item_index": null, + "cwd": "/" + }, + "kind": { + "Leaf": { + "Spawn": { + "cache_metadata": { + "spawn_fingerprint": { + "cwd": "", + "program_fingerprint": { + "OutsideWorkspace": { + "program_name": "oxlint" + } + }, + "args": [], + "env_fingerprints": { + "fingerprinted_envs": {}, + "pass_through_env_config": [ + "" + ] + }, + "fingerprint_ignores": null + }, + "execution_cache_key": { + "UserTask": { + "task_name": "lint", + "and_item_index": 0, + "extra_args": [], + "package_path": "" + } + } + }, + "spawn_command": { + "program_path": "/node_modules/.bin/oxlint", + "args": [], + "all_envs": { + "NO_COLOR": "1", + "PATH": "/node_modules/.bin:/node_modules/.bin" + }, + "cwd": "/" + } + } + } + } + } + ] + }, + "neighbors": [] + } + ] + } +} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - synthetic task in user task.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - synthetic task in user task.snap index 5d46256e..a71b87e3 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - synthetic task in user task.snap +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - synthetic task in user task.snap @@ -1,7 +1,7 @@ --- -source: crates/vite_task_bin/tests/test_snapshots/main.rs +source: crates/vite_task_plan/tests/plan_snapshots/main.rs expression: "&plan_json" -input_file: crates/vite_task_bin/tests/test_snapshots/fixtures/cache-keys +input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys --- { "root_node": { @@ -50,14 +50,12 @@ input_file: crates/vite_task_bin/tests/test_snapshots/fixtures/cache-keys "fingerprint_ignores": null }, "execution_cache_key": { - "kind": { - "UserTask": { - "task_name": "lint", - "and_item_index": 0, - "extra_args": [] - } - }, - "origin_path": "" + "UserTask": { + "task_name": "lint", + "and_item_index": 0, + "extra_args": [], + "package_path": "" + } } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - synthetic task with extra args in user task.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - synthetic task with extra args in user task.snap index bbea6ce6..91b9a980 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - synthetic task with extra args in user task.snap +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/query - synthetic task with extra args in user task.snap @@ -1,7 +1,7 @@ --- -source: crates/vite_task_bin/tests/test_snapshots/main.rs +source: crates/vite_task_plan/tests/plan_snapshots/main.rs expression: "&plan_json" -input_file: crates/vite_task_bin/tests/test_snapshots/fixtures/cache-keys +input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys --- { "root_node": { @@ -52,16 +52,14 @@ input_file: crates/vite_task_bin/tests/test_snapshots/fixtures/cache-keys "fingerprint_ignores": null }, "execution_cache_key": { - "kind": { - "UserTask": { - "task_name": "lint", - "and_item_index": 0, - "extra_args": [ - "--fix" - ] - } - }, - "origin_path": "" + "UserTask": { + "task_name": "lint", + "and_item_index": 0, + "extra_args": [ + "--fix" + ], + "package_path": "" + } } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/task graph.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/task graph.snap index e62a6bce..146b2bfd 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/task graph.snap +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys/snapshots/task graph.snap @@ -1,7 +1,7 @@ --- -source: crates/vite_task_bin/tests/test_snapshots/main.rs +source: crates/vite_task_plan/tests/plan_snapshots/main.rs expression: task_graph_json -input_file: crates/vite_task_bin/tests/test_snapshots/fixtures/cache-keys +input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-keys --- [ { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/node_modules/.bin/vite b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/node_modules/.bin/vite new file mode 100755 index 00000000..c635c2dc --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/node_modules/.bin/vite @@ -0,0 +1,2 @@ +# Dummy executable so `which("vite")` succeeds during plan resolution. +# This file is never actually executed. diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/node_modules/.bin/vite.cmd b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/node_modules/.bin/vite.cmd new file mode 100644 index 00000000..0efc832a --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/node_modules/.bin/vite.cmd @@ -0,0 +1,2 @@ +@REM Dummy executable so `which("vite")` succeeds during plan resolution on Windows. +@REM This file is never actually executed. diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/package.json b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/package.json new file mode 100644 index 00000000..225cb648 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/package.json @@ -0,0 +1,6 @@ +{ + "name": "@test/cache-subcommand", + "scripts": { + "clean-cache": "vite cache clean" + } +} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/snapshots.toml b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/snapshots.toml new file mode 100644 index 00000000..150dd755 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/snapshots.toml @@ -0,0 +1,3 @@ +[[plan]] +name = "cache clean in script" +args = ["run", "clean-cache"] diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/snapshots/query - cache clean in script.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/snapshots/query - cache clean in script.snap new file mode 100644 index 00000000..184aaf14 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/snapshots/query - cache clean in script.snap @@ -0,0 +1,58 @@ +--- +source: crates/vite_task_plan/tests/plan_snapshots/main.rs +expression: "&plan_json" +input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand +--- +{ + "root_node": { + "Expanded": [ + { + "key": [ + "/", + "clean-cache" + ], + "node": { + "task_display": { + "package_name": "@test/cache-subcommand", + "task_name": "clean-cache", + "package_path": "/" + }, + "items": [ + { + "execution_item_display": { + "task_display": { + "package_name": "@test/cache-subcommand", + "task_name": "clean-cache", + "package_path": "/" + }, + "command": "vite cache clean", + "and_item_index": null, + "cwd": "/" + }, + "kind": { + "Leaf": { + "Spawn": { + "cache_metadata": null, + "spawn_command": { + "program_path": "/node_modules/.bin/vite", + "args": [ + "cache", + "clean" + ], + "all_envs": { + "NO_COLOR": "1", + "PATH": "/node_modules/.bin:/node_modules/.bin" + }, + "cwd": "/" + } + } + } + } + } + ] + }, + "neighbors": [] + } + ] + } +} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/snapshots/task graph.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/snapshots/task graph.snap new file mode 100644 index 00000000..422476a8 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/snapshots/task graph.snap @@ -0,0 +1,35 @@ +--- +source: crates/vite_task_plan/tests/plan_snapshots/main.rs +expression: task_graph_json +input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand +--- +[ + { + "key": [ + "/", + "clean-cache" + ], + "node": { + "task_display": { + "package_name": "@test/cache-subcommand", + "task_name": "clean-cache", + "package_path": "/" + }, + "resolved_config": { + "command": "vite cache clean", + "resolved_options": { + "cwd": "/", + "cache_config": { + "env_config": { + "fingerprinted_envs": [], + "pass_through_envs": [ + "" + ] + } + } + } + } + }, + "neighbors": [] + } +] diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/vite-task.json b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/vite-task.json new file mode 100644 index 00000000..1d0fe9f2 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-subcommand/vite-task.json @@ -0,0 +1,3 @@ +{ + "cacheScripts": true +} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/shell-fallback/snapshots/query - shell fallback for pipe command.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/shell-fallback/snapshots/query - shell fallback for pipe command.snap index 159fb7d5..0e230877 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/shell-fallback/snapshots/query - shell fallback for pipe command.snap +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/shell-fallback/snapshots/query - shell fallback for pipe command.snap @@ -53,14 +53,12 @@ input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/shell-fallback "fingerprint_ignores": null }, "execution_cache_key": { - "kind": { - "UserTask": { - "task_name": "pipe-test", - "and_item_index": 0, - "extra_args": [] - } - }, - "origin_path": "" + "UserTask": { + "task_name": "pipe-test", + "and_item_index": 0, + "extra_args": [], + "package_path": "" + } } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic-in-subpackage/snapshots/query - synthetic-in-subpackage.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic-in-subpackage/snapshots/query - synthetic-in-subpackage.snap index c207584d..83b85689 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic-in-subpackage/snapshots/query - synthetic-in-subpackage.snap +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic-in-subpackage/snapshots/query - synthetic-in-subpackage.snap @@ -1,7 +1,7 @@ --- -source: crates/vite_task_bin/tests/test_snapshots/main.rs +source: crates/vite_task_plan/tests/plan_snapshots/main.rs expression: "&plan_json" -input_file: crates/vite_task_bin/tests/test_snapshots/fixtures/synthetic-in-subpackage +input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic-in-subpackage --- { "root_node": { @@ -75,14 +75,12 @@ input_file: crates/vite_task_bin/tests/test_snapshots/fixtures/synthetic-in-subp "fingerprint_ignores": null }, "execution_cache_key": { - "kind": { - "UserTask": { - "task_name": "lint", - "and_item_index": 0, - "extra_args": [] - } - }, - "origin_path": "packages/a" + "UserTask": { + "task_name": "lint", + "and_item_index": 0, + "extra_args": [], + "package_path": "packages/a" + } } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr-shorthand/package.json b/crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr-shorthand/package.json new file mode 100644 index 00000000..314d6388 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr-shorthand/package.json @@ -0,0 +1,6 @@ +{ + "scripts": { + "build": "echo building", + "all": "vpr build" + } +} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr-shorthand/snapshots.toml b/crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr-shorthand/snapshots.toml new file mode 100644 index 00000000..7cb91d86 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr-shorthand/snapshots.toml @@ -0,0 +1,3 @@ +[[plan]] +name = "vpr expands to vite run" +args = ["run", "all"] diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr-shorthand/snapshots/query - vpr expands to vite run.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr-shorthand/snapshots/query - vpr expands to vite run.snap new file mode 100644 index 00000000..5d31eed5 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr-shorthand/snapshots/query - vpr expands to vite run.snap @@ -0,0 +1,85 @@ +--- +source: crates/vite_task_plan/tests/plan_snapshots/main.rs +expression: "&plan_json" +input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr-shorthand +--- +{ + "root_node": { + "Expanded": [ + { + "key": [ + "/", + "all" + ], + "node": { + "task_display": { + "package_name": "", + "task_name": "all", + "package_path": "/" + }, + "items": [ + { + "execution_item_display": { + "task_display": { + "package_name": "", + "task_name": "all", + "package_path": "/" + }, + "command": "vpr build", + "and_item_index": null, + "cwd": "/" + }, + "kind": { + "Expanded": [ + { + "key": [ + "/", + "build" + ], + "node": { + "task_display": { + "package_name": "", + "task_name": "build", + "package_path": "/" + }, + "items": [ + { + "execution_item_display": { + "task_display": { + "package_name": "", + "task_name": "build", + "package_path": "/" + }, + "command": "echo building", + "and_item_index": null, + "cwd": "/" + }, + "kind": { + "Leaf": { + "InProcess": { + "kind": { + "Echo": { + "strings": [ + "building" + ], + "trailing_newline": true + } + } + } + } + } + } + ] + }, + "neighbors": [] + } + ] + } + } + ] + }, + "neighbors": [] + } + ] + } +} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr-shorthand/snapshots/task graph.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr-shorthand/snapshots/task graph.snap new file mode 100644 index 00000000..1dcf808c --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr-shorthand/snapshots/task graph.snap @@ -0,0 +1,63 @@ +--- +source: crates/vite_task_plan/tests/plan_snapshots/main.rs +expression: task_graph_json +input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr-shorthand +--- +[ + { + "key": [ + "/", + "all" + ], + "node": { + "task_display": { + "package_name": "", + "task_name": "all", + "package_path": "/" + }, + "resolved_config": { + "command": "vpr build", + "resolved_options": { + "cwd": "/", + "cache_config": { + "env_config": { + "fingerprinted_envs": [], + "pass_through_envs": [ + "" + ] + } + } + } + } + }, + "neighbors": [] + }, + { + "key": [ + "/", + "build" + ], + "node": { + "task_display": { + "package_name": "", + "task_name": "build", + "package_path": "/" + }, + "resolved_config": { + "command": "echo building", + "resolved_options": { + "cwd": "/", + "cache_config": { + "env_config": { + "fingerprinted_envs": [], + "pass_through_envs": [ + "" + ] + } + } + } + } + }, + "neighbors": [] + } +] diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr-shorthand/vite-task.json b/crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr-shorthand/vite-task.json new file mode 100644 index 00000000..1d0fe9f2 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr-shorthand/vite-task.json @@ -0,0 +1,3 @@ +{ + "cacheScripts": true +} diff --git a/crates/vite_task_plan/tests/plan_snapshots/main.rs b/crates/vite_task_plan/tests/plan_snapshots/main.rs index 0677eeac..77765162 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/main.rs +++ b/crates/vite_task_plan/tests/plan_snapshots/main.rs @@ -1,16 +1,24 @@ mod redact; -use std::{collections::HashMap, convert::Infallible, ffi::OsStr, path::Path, sync::Arc}; +use std::{collections::HashMap, ffi::OsStr, path::Path, sync::Arc}; +use clap::Parser; use copy_dir::copy_dir; use redact::redact_snapshot; use tokio::runtime::Runtime; use vite_path::{AbsolutePath, AbsolutePathBuf, RelativePathBuf}; use vite_str::Str; -use vite_task::{CLIArgs, Session}; -use vite_task_bin::CustomTaskSubcommand; +use vite_task::{Command, Session}; use vite_workspace::find_workspace_root; +/// Local parser wrapper for BuiltInCommand +#[derive(Parser)] +#[command(name = "vite")] +enum Cli { + #[clap(flatten)] + Command(Command), +} + #[derive(serde::Deserialize, Debug)] struct Plan { pub name: Str, @@ -118,8 +126,8 @@ fn run_case_inner( for plan in cases_file.plan_cases { let snapshot_name = format!("query - {}", plan.name); - let cli_args = match CLIArgs::::try_parse_from( - std::iter::once("vite") // dummy program name + let cli = match Cli::try_parse_from( + std::iter::once("vp") // dummy program name .chain(plan.args.iter().map(|s| s.as_str())), ) { Ok(ok) => ok, @@ -128,14 +136,14 @@ fn run_case_inner( continue; } }; - let task_cli_args = match cli_args { - CLIArgs::Task(task_cli_args) => task_cli_args, - CLIArgs::NonTask(never) => match never {}, + let Cli::Command(command) = cli; + let run_command = match command { + Command::Run(run_command) => run_command, + _ => panic!("only `run` commands supported in plan tests"), }; - let plan_result = session - .plan_from_cli(workspace_root.path.join(plan.cwd).into(), task_cli_args) - .await; + let plan_result = + session.plan_from_cli(workspace_root.path.join(plan.cwd).into(), run_command).await; let plan = match plan_result { Ok(plan) => plan,