diff --git a/CHANGELOG.md b/CHANGELOG.md index add778222..0edabcde4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog +- **Added** task command shorthands for defining tasks as command strings or command string arrays ([#391](https://github.com/voidzero-dev/vite-task/pull/391)) - **Changed** Cached logs are stored with colors intact (`FORCE_COLOR=1` is auto-injected into spawned tasks). Colors are then stripped at display time when the terminal does not support them. Other color-related env vars (`NO_COLOR`, `COLORTERM`, `TERM`, `TERM_PROGRAM`) are no longer passed through by default. Opt in via a task's `env`/`untrackedEnv` ([#378](https://github.com/voidzero-dev/vite-task/pull/378)) - **Added** `output` field for cached tasks: archives matching files after a successful run and restores them on cache hit ([#375](https://github.com/voidzero-dev/vite-task/pull/375)) - **Fixed** Windows cached tasks can now run package shims rewritten through PowerShell; default env passthrough now preserves `PATHEXT` ([#366](https://github.com/voidzero-dev/vite-task/pull/366)) diff --git a/crates/vite_task/docs/task-cache.md b/crates/vite_task/docs/task-cache.md index 440901ccd..bd71db2f9 100644 --- a/crates/vite_task/docs/task-cache.md +++ b/crates/vite_task/docs/task-cache.md @@ -550,13 +550,13 @@ Ensure commands produce identical outputs for identical inputs: ```json { - "scripts": { - "build": "tsc && rollup -c && terser dist/bundle.js" + "tasks": { + "build": ["tsc", "rollup -c", "terser dist/bundle.js"] } } ``` -Each `&&` separated command is cached independently. If only terser config changes, TypeScript and rollup will hit cache. +Each `&&` separated command is cached independently. Task command arrays use the same granular caching semantics. If only terser config changes, TypeScript and rollup will hit cache. ## Implementation Reference diff --git a/crates/vite_task/docs/terminologies.md b/crates/vite_task/docs/terminologies.md index 6118fc52e..62e3ebb71 100644 --- a/crates/vite_task/docs/terminologies.md +++ b/crates/vite_task/docs/terminologies.md @@ -7,8 +7,8 @@ { "name": "app", "scripts": { - "build": "echo build1 && echo build2", - }, + "build": "echo build1 && echo build2" + } } ``` @@ -16,19 +16,24 @@ // vite-task.json { "tasks": { - "lint": { - "command": "echo lint" + "lint": "echo lint", + "check": ["eslint .", "tsc --noEmit", "prettier --check ."] } } ``` -In the example above, `build` and `lint` are **task group names**. A task group may define one task, or multiple tasks separated by `&&`. +In the example above, `build`, `lint`, and `check` are **task group names**. A task group may define one task, or multiple tasks separated by `&&`. + +In `tasks`, command-only task groups can be written as a string or as an array. Object form with `command` and options is also supported. -The two task groups generates 3 tasks: +The three task groups generate these tasks: - `app#build(subcommand 0)` (runs `echo build1`) - `app#build` (runs `echo build2`) - `app#lint` (runs `echo lint`) +- `app#check(subcommand 0)` (runs `eslint .`) +- `app#check(subcommand 1)` (runs `tsc --noEmit`) +- `app#check` (runs `prettier --check .`) These are **task names**. They are for displaying and filtering. diff --git a/crates/vite_task_graph/run-config.ts b/crates/vite_task_graph/run-config.ts index 1fa4ee868..258f0ff7e 100644 --- a/crates/vite_task_graph/run-config.ts +++ b/crates/vite_task_graph/run-config.ts @@ -20,9 +20,9 @@ export type InputBase = "package" | "workspace"; export type Task = { /** - * The command to run for the task. + * Command string or sequence of command strings to run for the task. */ -command: string, +command: TaskCommand, /** * The working directory for the task, relative to the package root (not workspace root). */ @@ -68,6 +68,10 @@ output?: Array, } | { */ cache: false, }); +export type TaskCommand = string | Array; + +export type TaskDefinition = Task | TaskCommand; + export type UserGlobalCacheConfig = boolean | { /** * Enable caching for package.json scripts not defined in the `tasks` map. @@ -98,9 +102,9 @@ export type RunConfig = { */ cache?: UserGlobalCacheConfig, /** - * Task definitions + * Task definitions: full task objects, command strings, or command string arrays. */ -tasks?: { [key in string]: Task }, +tasks?: { [key in string]: TaskDefinition }, /** * Whether to automatically run `preX`/`postX` package.json scripts as * lifecycle hooks when script `X` is executed. diff --git a/crates/vite_task_graph/src/config/mod.rs b/crates/vite_task_graph/src/config/mod.rs index 38e1db736..2f483f1d4 100644 --- a/crates/vite_task_graph/src/config/mod.rs +++ b/crates/vite_task_graph/src/config/mod.rs @@ -6,9 +6,9 @@ use monostate::MustBe; use rustc_hash::FxHashSet; use serde::Serialize; pub use user::{ - AutoInput, EnabledCacheConfig, GlobWithBase, InputBase, ResolvedGlobalCacheConfig, + AutoInput, EnabledCacheConfig, GlobWithBase, InputBase, ResolvedGlobalCacheConfig, TaskCommand, UserCacheConfig, UserGlobalCacheConfig, UserInputEntry, UserInputsConfig, UserOutputEntry, - UserRunConfig, UserTaskConfig, + UserRunConfig, UserTaskConfig, UserTaskDefinition, }; use vite_path::AbsolutePath; use vite_str::Str; @@ -28,10 +28,10 @@ use crate::config::user::UserTaskOptions; /// `depends_on` is not included here because it's represented by the edges of the task graph. #[derive(Debug, Serialize)] pub struct ResolvedTaskConfig { - /// The command to run for this task, as a raw string. + /// The command or commands to run for this task. /// - /// The command may contain environment variables that need to be expanded later. - pub command: Str, + /// Commands may contain environment variables that need to be expanded later. + pub command: TaskCommand, pub resolved_options: ResolvedTaskOptions, } @@ -360,7 +360,7 @@ impl ResolvedTaskConfig { workspace_root: &AbsolutePath, ) -> Result { Ok(Self { - command: package_json_script.into(), + command: TaskCommand::String(package_json_script.into()), resolved_options: ResolvedTaskOptions::resolve( UserTaskOptions::default(), package_dir, @@ -380,7 +380,7 @@ impl ResolvedTaskConfig { workspace_root: &AbsolutePath, ) -> Result { Ok(Self { - command: Str::from(user_config.command.as_ref()), + command: user_config.command, resolved_options: ResolvedTaskOptions::resolve( user_config.options, package_dir, diff --git a/crates/vite_task_graph/src/config/user.rs b/crates/vite_task_graph/src/config/user.rs index aeee38608..5c801313b 100644 --- a/crates/vite_task_graph/src/config/user.rs +++ b/crates/vite_task_graph/src/config/user.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use monostate::MustBe; use rustc_hash::FxHashMap; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; #[cfg(all(test, not(clippy)))] use ts_rs::TS; use vite_path::RelativePathBuf; @@ -193,20 +193,56 @@ impl Default for UserTaskOptions { } } +/// Task command: a command string or a sequence of command strings. +#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] +// TS derive macro generates code using std types that clippy disallows; skip derive during linting +#[cfg_attr(all(test, not(clippy)), derive(TS))] +#[serde(untagged)] +pub enum TaskCommand { + /// A single command string. + String(Str), + /// Command strings to run in order. + Array(Vec), +} + +impl From<&str> for TaskCommand { + fn from(value: &str) -> Self { + Self::String(value.into()) + } +} + +impl From for TaskCommand { + fn from(value: Str) -> Self { + Self::String(value) + } +} + /// Full user-defined task configuration in `vite.config.*`, including the command and options. #[derive(Debug, Deserialize, PartialEq, Eq)] // TS derive macro generates code using std types that clippy disallows; skip derive during linting #[cfg_attr(all(test, not(clippy)), derive(TS), ts(optional_fields, rename = "Task"))] #[serde(rename_all = "camelCase")] pub struct UserTaskConfig { - /// The command to run for the task. - pub command: Box, + /// Command string or sequence of command strings to run for the task. + pub command: TaskCommand, /// Fields other than the command #[serde(flatten)] pub options: UserTaskOptions, } +/// User-defined task configuration or command-only shorthand in `vite.config.*`. +#[derive(Debug, Deserialize, PartialEq, Eq)] +// TS derive macro generates code using std types that clippy disallows; skip derive during linting +#[cfg_attr(all(test, not(clippy)), derive(TS), ts(rename = "TaskDefinition"))] +#[serde(untagged)] +pub enum UserTaskDefinition { + /// Full task object form. + Config(UserTaskConfig), + /// Command-only shorthand form using default task options. + Command(TaskCommand), +} + /// Root-level cache configuration. /// /// Controls caching behavior for the entire workspace. @@ -281,8 +317,8 @@ pub struct UserRunConfig { /// Setting it in a package's config will result in an error. pub cache: Option, - /// Task definitions - pub tasks: Option>, + /// Task definitions: full task objects, command strings, or command string arrays. + pub tasks: Option>, /// Whether to automatically run `preX`/`postX` package.json scripts as /// lifecycle hooks when script `X` is executed. @@ -417,6 +453,89 @@ mod tests { ); } + #[test] + fn test_command_array() { + let user_config_json = json!({ + "command": ["echo one", "echo two", "echo three"] + }); + let user_config: UserTaskConfig = serde_json::from_value(user_config_json).unwrap(); + assert_eq!( + user_config.command, + TaskCommand::Array(vec!["echo one".into(), "echo two".into(), "echo three".into()]) + ); + assert_eq!(user_config.options, UserTaskOptions::default()); + } + + #[test] + fn test_task_string_shorthand() { + let user_config_json = json!({ + "tasks": { + "build": "echo build" + } + }); + let mut user_config: UserRunConfig = serde_json::from_value(user_config_json).unwrap(); + let task = user_config.tasks.as_mut().unwrap().remove("build").unwrap(); + assert_eq!(task, UserTaskDefinition::Command(TaskCommand::String("echo build".into()))); + } + + #[test] + fn test_task_array_shorthand() { + let user_config_json = json!({ + "tasks": { + "build": ["echo one", "echo two", "echo three"] + } + }); + let mut user_config: UserRunConfig = serde_json::from_value(user_config_json).unwrap(); + let task = user_config.tasks.as_mut().unwrap().remove("build").unwrap(); + assert_eq!( + task, + UserTaskDefinition::Command(TaskCommand::Array(vec![ + "echo one".into(), + "echo two".into(), + "echo three".into() + ])) + ); + } + + #[test] + fn test_command_array_with_options() { + let user_config_json = json!({ + "command": ["echo one", "echo two"], + "cwd": "src", + "dependsOn": ["build"], + "cache": false + }); + let user_config: UserTaskConfig = serde_json::from_value(user_config_json).unwrap(); + assert_eq!( + user_config.command, + TaskCommand::Array(vec!["echo one".into(), "echo two".into()]) + ); + assert_eq!(user_config.options.cwd_relative_to_package.as_ref().unwrap().as_str(), "src"); + assert_eq!(user_config.options.depends_on.as_ref().unwrap().as_ref(), [Str::from("build")]); + assert_eq!( + user_config.options.cache_config, + UserCacheConfig::Disabled { cache: MustBe!(false) } + ); + } + + #[test] + fn test_task_invalid_shorthand_error() { + let user_config_json = json!({ + "tasks": { + "build": 123 + } + }); + assert!(serde_json::from_value::(user_config_json).is_err()); + } + + #[test] + fn test_command_array_invalid_item_error() { + let user_config_json = json!({ + "command": ["echo one", 123] + }); + assert!(serde_json::from_value::(user_config_json).is_err()); + } + #[test] fn test_cwd_rename() { let user_config_json = json!({ diff --git a/crates/vite_task_graph/src/display.rs b/crates/vite_task_graph/src/display.rs index fbc06dd29..99051f010 100644 --- a/crates/vite_task_graph/src/display.rs +++ b/crates/vite_task_graph/src/display.rs @@ -6,7 +6,7 @@ use serde::Serialize; use vite_path::AbsolutePath; use vite_str::Str; -use crate::{IndexedTaskGraph, TaskNodeIndex}; +use crate::{IndexedTaskGraph, TaskNodeIndex, config::TaskCommand}; /// struct for printing a task in a human-readable way. #[derive(Debug, Clone, Serialize)] @@ -50,9 +50,27 @@ impl IndexedTaskGraph { let node = &self.task_graph()[idx]; TaskListEntry { task_display: node.task_display.clone(), - command: node.resolved_config.command.clone(), + command: format_command_for_task_list(&node.resolved_config.command), } }) .collect() } } + +// Display-only formatting for task list/selector descriptions. Execution planning keeps +// `TaskCommand` structured and must not depend on this joined string. +fn format_command_for_task_list(command: &TaskCommand) -> Str { + match command { + TaskCommand::String(command) => command.clone(), + TaskCommand::Array(commands) => { + let mut display = Str::default(); + for (index, command) in commands.iter().enumerate() { + if index > 0 { + display.push_str(" && "); + } + display.push_str(command.as_str()); + } + display + } + } +} diff --git a/crates/vite_task_graph/src/lib.rs b/crates/vite_task_graph/src/lib.rs index 98a7c3d62..feb488b1c 100644 --- a/crates/vite_task_graph/src/lib.rs +++ b/crates/vite_task_graph/src/lib.rs @@ -6,7 +6,10 @@ mod specifier; use std::{convert::Infallible, sync::Arc}; -use config::{ResolvedGlobalCacheConfig, ResolvedTaskConfig, UserRunConfig}; +use config::{ + ResolvedGlobalCacheConfig, ResolvedTaskConfig, UserRunConfig, UserTaskConfig, + UserTaskDefinition, +}; use petgraph::graph::{DefaultIx, DiGraph, EdgeIndex, IndexType, NodeIndex}; use rustc_hash::{FxBuildHasher, FxHashMap}; use serde::Serialize; @@ -15,7 +18,7 @@ use vite_path::AbsolutePath; use vite_str::Str; use vite_workspace::{PackageNodeIndex, WorkspaceRoot, package_graph::IndexedPackageGraph}; -use crate::display::TaskDisplay; +use crate::{config::user::UserTaskOptions, display::TaskDisplay}; /// The type of a task dependency edge in the task graph. /// @@ -303,6 +306,12 @@ impl IndexedTaskGraph { let task_id = TaskId { task_name: task_name.clone(), package_index }; + let task_user_config = match task_user_config { + UserTaskDefinition::Config(config) => config, + UserTaskDefinition::Command(command) => { + UserTaskConfig { command, options: UserTaskOptions::default() } + } + }; let dependency_specifiers = task_user_config.options.depends_on.clone(); // Resolve the task configuration from the user config diff --git a/crates/vite_task_plan/src/error.rs b/crates/vite_task_plan/src/error.rs index 7a255b8df..d7014697b 100644 --- a/crates/vite_task_plan/src/error.rs +++ b/crates/vite_task_plan/src/error.rs @@ -119,6 +119,9 @@ pub enum Error { #[error(transparent)] TaskRecursionDetected(#[from] TaskRecursionError), + #[error("Invalid task command: {0}")] + InvalidTaskCommand(Str), + #[error("Invalid vite task command: {program} with args {args:?} under cwd {cwd:?}")] ParsePlanRequest { program: Str, diff --git a/crates/vite_task_plan/src/plan.rs b/crates/vite_task_plan/src/plan.rs index 89e892b48..189f1e100 100644 --- a/crates/vite_task_plan/src/plan.rs +++ b/crates/vite_task_plan/src/plan.rs @@ -8,6 +8,7 @@ use std::{ collections::BTreeMap, env::home_dir, ffi::OsStr, + ops::Range, sync::{Arc, LazyLock}, }; @@ -15,13 +16,13 @@ use futures_util::FutureExt; use petgraph::Direction; use rustc_hash::FxHashMap; use vite_path::{AbsolutePath, AbsolutePathBuf, RelativePathBuf, relative::InvalidPathDataError}; -use vite_shell::try_parse_as_and_list; +use vite_shell::{TaskParsedCommand, try_parse_as_and_list}; use vite_str::Str; use vite_task_graph::{ TaskNodeIndex, TaskSource, config::{ CacheConfig, EnabledCacheConfig, ResolvedGlobConfig, ResolvedGlobalCacheConfig, - ResolvedTaskOptions, + ResolvedTaskOptions, TaskCommand, user::{UserCacheConfig, UserTaskOptions}, }, query::TaskQuery, @@ -80,6 +81,54 @@ fn effective_cache_config( if enabled { task_cache_config.cloned() } else { None } } +enum PlannedCommand { + Parsed { and_item: TaskParsedCommand, display: Str, stack_frame: Range }, + Shell(Str), +} + +#[expect(clippy::result_large_err, reason = "Error is large for diagnostics")] +fn command_source(command: &TaskCommand) -> Result { + match command { + TaskCommand::String(command) => Ok(command.clone()), + TaskCommand::Array(commands) => { + if commands.is_empty() { + return Err(Error::InvalidTaskCommand("command array must not be empty".into())); + } + if commands.iter().any(|command| command.as_str().trim().is_empty()) { + return Err(Error::InvalidTaskCommand( + "command array entries must not be empty".into(), + )); + } + + let mut source = Str::default(); + for (index, command) in commands.iter().enumerate() { + if index > 0 { + source.push_str(" && "); + } + source.push_str(command.as_str()); + } + Ok(source) + } + } +} + +#[expect(clippy::result_large_err, reason = "Error is large for diagnostics")] +fn planned_commands(command: &TaskCommand) -> Result, Error> { + let source = command_source(command)?; + if let Some(parsed) = try_parse_as_and_list(source.as_str()) { + Ok(parsed + .into_iter() + .map(|(and_item, range)| PlannedCommand::Parsed { + display: Str::from(&source.as_str()[range.clone()]), + and_item, + stack_frame: range, + }) + .collect()) + } else { + Ok(vec![PlannedCommand::Shell(source)]) + } +} + /// - `with_hooks`: whether to look up `preX`/`postX` lifecycle hooks for this task. /// `false` when the task itself is being executed as a hook, so that hooks are /// never expanded more than one level deep (matching npm behavior). @@ -93,7 +142,6 @@ async fn plan_task_as_execution_node( context.check_recursion(task_node_index)?; let task_node = &context.indexed_task_graph().task_graph()[task_node_index]; - let command_str = task_node.resolved_config.command.as_str(); let package_path = context.indexed_task_graph().get_package_path_for_task(task_node_index); // Prepend {package_path}/node_modules/.bin to PATH @@ -128,264 +176,271 @@ async fn plan_task_as_execution_node( let mut cwd = Arc::clone(&task_node.resolved_config.resolved_options.cwd); // TODO: variable expansion (https://crates.io/crates/shellexpand) BEFORE parsing - // Try to parse the command string as a list of subcommands separated by `&&` - if let Some(parsed_subcommands) = try_parse_as_and_list(command_str) { - let and_item_count = parsed_subcommands.len(); - for (index, (and_item, add_item_span)) in parsed_subcommands.into_iter().enumerate() { - // Duplicate the context before modifying it for each and_item - let mut context = context.duplicate(); - context.push_stack_frame(task_node_index, add_item_span.clone()); - - let mut args = and_item.args; - let extra_args = if index == and_item_count - 1 { - // For the last and_item, append extra args from the plan context - Arc::clone(context.extra_args()) - } else { - Arc::new([]) - }; - args.extend(extra_args.iter().cloned()); + let planned_commands = planned_commands(&task_node.resolved_config.command)?; + let and_item_count = planned_commands.len(); + for (index, planned_command) in planned_commands.into_iter().enumerate() { + match planned_command { + PlannedCommand::Parsed { and_item, display, stack_frame } => { + // Duplicate the context before modifying it for each and_item + let mut context = context.duplicate(); + context.push_stack_frame(task_node_index, stack_frame); + + let mut args = and_item.args; + let extra_args = if index == and_item_count - 1 { + // For the last and_item, append extra args from the plan context + Arc::clone(context.extra_args()) + } else { + Arc::new([]) + }; + args.extend(extra_args.iter().cloned()); + + // Handle `cd` builtin command + if and_item.program == "cd" { + #[expect( + clippy::disallowed_types, + reason = "Path is needed for std::env::home_dir return type and AbsolutePath::join" + )] + let cd_target: Cow<'_, Path> = match args.as_slice() { + // No args, go to home directory + [] => home_dir() + .ok_or(Error::CdCommand(CdCommandError::NoHomeDirectory))? + .into(), + [dir] => Path::new(dir.as_str()).into(), + _ => { + return Err(Error::CdCommand(CdCommandError::TooManyArgs)); + } + }; + cwd = cwd.join(cd_target.as_ref()).into(); + continue; + } - // Handle `cd` builtin command - if and_item.program == "cd" { - #[expect( - clippy::disallowed_types, - reason = "Path is needed for std::env::home_dir return type and AbsolutePath::join" - )] - let cd_target: Cow<'_, Path> = match args.as_slice() { - // No args, go to home directory - [] => { - home_dir().ok_or(Error::CdCommand(CdCommandError::NoHomeDirectory))?.into() - } - [dir] => Path::new(dir.as_str()).into(), - _ => { - return Err(Error::CdCommand(CdCommandError::TooManyArgs)); - } + // Build execution display + let execution_item_display = ExecutionItemDisplay { + command: { + let mut command = display.clone(); + for arg in extra_args.iter() { + command.push(' '); + command.push_str(shell_escape::escape(arg.as_str().into()).as_ref()); + } + command + }, + and_item_index: if and_item_count > 1 { Some(index) } else { None }, + cwd: Arc::clone(&cwd), + task_display: task_node.task_display.clone(), }; - cwd = cwd.join(cd_target.as_ref()).into(); - continue; - } - // Build execution display - let execution_item_display = ExecutionItemDisplay { - command: { - let mut command = Str::from(&command_str[add_item_span.clone()]); - for arg in extra_args.iter() { - command.push(' '); - command.push_str(shell_escape::escape(arg.as_str().into()).as_ref()); - } - command - }, - and_item_index: if and_item_count > 1 { Some(index) } else { None }, - cwd: Arc::clone(&cwd), - task_display: task_node.task_display.clone(), - }; - - // Check for builtin commands like `echo ...` - if let Some(builtin_execution) = - InProcessExecution::get_builtin_execution(&and_item.program, args.iter(), &cwd) - { - items.push(ExecutionItem { - execution_item_display, - kind: ExecutionItemKind::Leaf(LeafExecutionKind::InProcess(builtin_execution)), - }); - continue; - } + // Check for builtin commands like `echo ...` + if let Some(builtin_execution) = + InProcessExecution::get_builtin_execution(&and_item.program, args.iter(), &cwd) + { + items.push(ExecutionItem { + execution_item_display, + kind: ExecutionItemKind::Leaf(LeafExecutionKind::InProcess( + builtin_execution, + )), + }); + continue; + } - // Create execution cache key for this and_item - 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| PathFingerprintError { - kind, - path_type: PathType::PackagePath, - })?, - }; - - // 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(&mut script_command).await.map_err( - |error| Error::ParsePlanRequest { - program: script_command.program.clone(), - args: Arc::clone(&script_command.args), - cwd: Arc::clone(&script_command.cwd), - error, - }, - )?; + // Create execution cache key for this and_item + 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| PathFingerprintError { + kind, + path_type: PathType::PackagePath, + })?, + }; - let execution_item_kind: ExecutionItemKind = match plan_request { - // Expand task query like `vp run -r build` - Some(PlanRequest::Query(query_plan_request)) => { - // Skip rule: skip if this nested query is the same as the parent expansion. - // This handles workspace root tasks like `"build": "vp run -r build"` — - // re-entering the same query would just re-expand the same tasks. - // - // The comparison is on TaskQuery only (package_query + task_name + - // include_explicit_deps). Extra args live in PlanOptions, so - // `vp run -r build extra_arg` still matches `vp run -r build`. - // Conversely, `cd packages/a && vp run build` does NOT match a - // parent `vp run build` from root because `cd` changes the cwd, - // producing a different ContainingPackage in the PackageQuery. - if query_plan_request.query == *context.parent_query() { - continue; - } + // 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(&mut script_command).await.map_err( + |error| Error::ParsePlanRequest { + program: script_command.program.clone(), + args: Arc::clone(&script_command.args), + cwd: Arc::clone(&script_command.cwd), + error, + }, + )?; + + let execution_item_kind: ExecutionItemKind = match plan_request { + // Expand task query like `vp run -r build` + Some(PlanRequest::Query(query_plan_request)) => { + // Skip rule: skip if this nested query is the same as the parent expansion. + if query_plan_request.query == *context.parent_query() { + continue; + } - // Save task name before consuming the request - let task_name = query_plan_request.query.task_name.clone(); - // Add prefix envs to the context - context.add_envs(and_item.envs.iter()); - let QueryPlanRequest { query, plan_options } = query_plan_request; - let query = Arc::new(query); - let execution_graph = - plan_query_request(Arc::clone(&query), plan_options, context) - .await - .map_err(|error| Error::NestPlan { + let task_name = query_plan_request.query.task_name.clone(); + context.add_envs(and_item.envs.iter()); + let QueryPlanRequest { query, plan_options } = query_plan_request; + let query = Arc::new(query); + let execution_graph = + plan_query_request(Arc::clone(&query), plan_options, context) + .await + .map_err(|error| Error::NestPlan { + task_display: task_node.task_display.clone(), + command: display.clone(), + error: Box::new(error), + })?; + if execution_graph.graph.node_count() == 0 { + return Err(Error::NestPlan { task_display: task_node.task_display.clone(), - command: Str::from(&command_str[add_item_span.clone()]), - error: Box::new(error), - })?; - // An empty execution graph means no tasks matched the query. - // At the top level the session shows the task selector UI, - // but in a nested context there is no UI — propagate as an error. - if execution_graph.graph.node_count() == 0 { - return Err(Error::NestPlan { - task_display: task_node.task_display.clone(), - command: Str::from(&command_str[add_item_span]), - error: Box::new(Error::NoTasksMatched(task_name)), - }); + command: display, + error: Box::new(Error::NoTasksMatched(task_name)), + }); + } + ExecutionItemKind::Expanded(execution_graph) } - ExecutionItemKind::Expanded(execution_graph) - } - // Synthetic task (from CommandHandler) - Some(PlanRequest::Synthetic(synthetic_plan_request)) => { - let task_effective_cache = effective_cache_config( - task_node.resolved_config.resolved_options.cache_config.as_ref(), - task_node.source, - *context.resolved_global_cache(), - ); - let parent_cache_config = task_effective_cache - .as_ref() - .map_or(ParentCacheConfig::Disabled, |config| { - ParentCacheConfig::Inherited(config.clone()) - }); - let spawn_execution = plan_synthetic_request( - context.workspace_path(), - &and_item.envs, - synthetic_plan_request, - Some(task_execution_cache_key), - &cwd, - package_path, - parent_cache_config, - )?; - ExecutionItemKind::Leaf(LeafExecutionKind::Spawn(spawn_execution)) - } - // Normal 3rd party tool command (like `tsc --noEmit`), using potentially mutated script_command - None => { - let program_path = which( - &OsStr::new(&script_command.program).into(), - &script_command.envs, - &script_command.cwd, - )?; - let (program_path, spawn_args) = crate::ps1_shim::rewrite_cmd_shim_with_args( - program_path, - script_command.args, - &task_node.resolved_config.resolved_options.cwd, - context.workspace_path(), - ); - let resolved_options = ResolvedTaskOptions { - cwd: Arc::clone(&task_node.resolved_config.resolved_options.cwd), - cache_config: effective_cache_config( + // Synthetic task (from CommandHandler) + Some(PlanRequest::Synthetic(synthetic_plan_request)) => { + let task_effective_cache = effective_cache_config( task_node.resolved_config.resolved_options.cache_config.as_ref(), task_node.source, *context.resolved_global_cache(), - ), - }; - let spawn_execution = plan_spawn_execution( - context.workspace_path(), - Some(task_execution_cache_key), - &and_item.envs, - &resolved_options, - &script_command.envs, - program_path, - spawn_args, - )?; - ExecutionItemKind::Leaf(LeafExecutionKind::Spawn(spawn_execution)) - } - }; - items.push(ExecutionItem { execution_item_display, kind: execution_item_kind }); - } - } else { - #[expect(clippy::disallowed_types, reason = "PathBuf needed for which fallback path")] - static SHELL_PROGRAM_PATH: LazyLock> = - LazyLock::new(|| { - if cfg!(target_os = "windows") { - AbsolutePathBuf::new(which::which("cmd.exe").unwrap_or_else(|_| { - std::path::PathBuf::from("C:\\Windows\\System32\\cmd.exe") - })) - .unwrap() - .into() - } else { - AbsolutePath::new("/bin/sh").unwrap().into() - } - }); + ); + let parent_cache_config = task_effective_cache + .as_ref() + .map_or(ParentCacheConfig::Disabled, |config| { + ParentCacheConfig::Inherited(config.clone()) + }); + let spawn_execution = plan_synthetic_request( + context.workspace_path(), + &and_item.envs, + synthetic_plan_request, + Some(task_execution_cache_key), + &cwd, + package_path, + parent_cache_config, + )?; + ExecutionItemKind::Leaf(LeafExecutionKind::Spawn(spawn_execution)) + } + // Normal 3rd party tool command (like `tsc --noEmit`), using potentially mutated script_command + None => { + let program_path = which( + &OsStr::new(&script_command.program).into(), + &script_command.envs, + &script_command.cwd, + )?; + let (program_path, spawn_args) = + crate::ps1_shim::rewrite_cmd_shim_with_args( + program_path, + script_command.args, + &script_command.cwd, + context.workspace_path(), + ); + let resolved_options = ResolvedTaskOptions { + cwd: Arc::clone(&script_command.cwd), + cache_config: effective_cache_config( + task_node.resolved_config.resolved_options.cache_config.as_ref(), + task_node.source, + *context.resolved_global_cache(), + ), + }; + let spawn_execution = plan_spawn_execution( + context.workspace_path(), + Some(task_execution_cache_key), + &and_item.envs, + &resolved_options, + &script_command.envs, + program_path, + spawn_args, + )?; + ExecutionItemKind::Leaf(LeafExecutionKind::Spawn(spawn_execution)) + } + }; + items.push(ExecutionItem { execution_item_display, kind: execution_item_kind }); + } + PlannedCommand::Shell(script) => { + #[expect( + clippy::disallowed_types, + reason = "PathBuf needed for which fallback path" + )] + static SHELL_PROGRAM_PATH: LazyLock> = LazyLock::new(|| { + if cfg!(target_os = "windows") { + AbsolutePathBuf::new(which::which("cmd.exe").unwrap_or_else(|_| { + std::path::PathBuf::from("C:\\Windows\\System32\\cmd.exe") + })) + .unwrap() + .into() + } else { + AbsolutePath::new("/bin/sh").unwrap().into() + } + }); - static SHELL_ARGS: &[&str] = - if cfg!(target_os = "windows") { &["/d", "/s", "/c"] } else { &["-c"] }; + static SHELL_ARGS: &[&str] = + if cfg!(target_os = "windows") { &["/d", "/s", "/c"] } else { &["-c"] }; - let mut context = context.duplicate(); - context.push_stack_frame(task_node_index, 0..command_str.len()); + let mut context = context.duplicate(); + context.push_stack_frame(task_node_index, 0..script.len()); - let execution_item_display = ExecutionItemDisplay { - command: command_str.into(), - and_item_index: None, - cwd, - task_display: task_node.task_display.clone(), - }; + let extra_args = if index == and_item_count - 1 { + Arc::clone(context.extra_args()) + } else { + Arc::new([]) + }; - let mut script = Str::from(command_str); - for arg in context.extra_args().iter() { - script.push(' '); - script.push_str(shell_escape::escape(arg.as_str().into()).as_ref()); - } + let execution_item_display = ExecutionItemDisplay { + command: script.clone(), + and_item_index: if and_item_count > 1 { Some(index) } else { None }, + cwd: Arc::clone(&cwd), + task_display: task_node.task_display.clone(), + }; - let resolved_options = ResolvedTaskOptions { - cwd: Arc::clone(&task_node.resolved_config.resolved_options.cwd), - cache_config: effective_cache_config( - task_node.resolved_config.resolved_options.cache_config.as_ref(), - task_node.source, - *context.resolved_global_cache(), - ), - }; - let spawn_execution = plan_spawn_execution( - 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| PathFingerprintError { - kind, - path_type: PathType::PackagePath, - })?, - }), - &BTreeMap::new(), - &resolved_options, - context.envs(), - Arc::clone(&*SHELL_PROGRAM_PATH), - SHELL_ARGS.iter().map(|s| Str::from(*s)).chain(std::iter::once(script)).collect(), - )?; - items.push(ExecutionItem { - execution_item_display, - kind: ExecutionItemKind::Leaf(LeafExecutionKind::Spawn(spawn_execution)), - }); + let mut script_with_args = script; + for arg in extra_args.iter() { + script_with_args.push(' '); + script_with_args.push_str(shell_escape::escape(arg.as_str().into()).as_ref()); + } + + let resolved_options = ResolvedTaskOptions { + cwd: Arc::clone(&cwd), + cache_config: effective_cache_config( + task_node.resolved_config.resolved_options.cache_config.as_ref(), + task_node.source, + *context.resolved_global_cache(), + ), + }; + let spawn_execution = plan_spawn_execution( + context.workspace_path(), + Some(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| PathFingerprintError { + kind, + path_type: PathType::PackagePath, + })?, + }), + &BTreeMap::new(), + &resolved_options, + context.envs(), + Arc::clone(&*SHELL_PROGRAM_PATH), + SHELL_ARGS + .iter() + .map(|s| Str::from(*s)) + .chain(std::iter::once(script_with_args)) + .collect(), + )?; + items.push(ExecutionItem { + execution_item_display, + kind: ExecutionItemKind::Leaf(LeafExecutionKind::Spawn(spawn_execution)), + }); + } + } } // Expand post-hook (`postX`) for package.json scripts. diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/package.json b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/package.json new file mode 100644 index 000000000..a2538e229 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/package.json @@ -0,0 +1,4 @@ +{ + "name": "@test/task-command-shorthands", + "private": true +} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots.toml b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots.toml new file mode 100644 index 000000000..14243a2ab --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots.toml @@ -0,0 +1,43 @@ +[[plan]] +name = "string_shorthand" +args = ["run", "string_shorthand"] + +[[plan]] +name = "array_shorthand" +args = ["run", "array_shorthand"] + +[[plan]] +name = "array_with_and" +args = ["run", "array_with_and"] + +[[plan]] +name = "nested_vt_array" +args = ["run", "nested_vt_array"] + +[[plan]] +name = "array_cd_spawn" +args = ["run", "array_cd_spawn"] + +[[plan]] +name = "array_cd_shell" +args = ["run", "array_cd_shell"] + +[[plan]] +name = "array_shell_cd_before_next" +args = ["run", "array_shell_cd_before_next"] + +[[plan]] +name = "array_compound_cd_before_next" +args = ["run", "array_compound_cd_before_next"] + +[[plan]] +name = "object_array_depends_on" +args = ["run", "object_array_depends_on"] + +[[plan]] +name = "empty_array_error" +args = ["run", "empty_array"] + +[[plan]] +name = "empty_item_error" +args = ["run", "empty_item"] diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_array_cd_shell.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_array_cd_shell.jsonc new file mode 100644 index 000000000..5beddf00f --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_array_cd_shell.jsonc @@ -0,0 +1,90 @@ +// run array_cd_shell +{ + "graph": [ + { + "key": [ + "/", + "array_cd_shell" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_cd_shell", + "package_path": "/" + }, + "items": [ + { + "execution_item_display": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_cd_shell", + "package_path": "/" + }, + "command": "cd snapshots && echo $PWD", + "and_item_index": null, + "cwd": "/" + }, + "kind": { + "Leaf": { + "Spawn": { + "cache_metadata": { + "spawn_fingerprint": { + "cwd": "", + "program_fingerprint": { + "OutsideWorkspace": { + "program_name": "" + } + }, + "args": [ + "", + "cd snapshots && echo $PWD" + ], + "env_fingerprints": { + "fingerprinted_envs": {}, + "untracked_env_config": [ + "" + ] + } + }, + "execution_cache_key": { + "UserTask": { + "task_name": "array_cd_shell", + "and_item_index": 0, + "extra_args": [], + "package_path": "" + } + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + }, + "spawn_command": { + "program_path": "", + "args": [ + "", + "cd snapshots && echo $PWD" + ], + "all_envs": { + "FORCE_COLOR": "1", + "PATH": "/node_modules/.bin:" + }, + "cwd": "/" + } + } + } + } + } + ] + }, + "neighbors": [] + } + ], + "concurrency_limit": 4 +} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_array_cd_spawn.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_array_cd_spawn.jsonc new file mode 100644 index 000000000..570a21775 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_array_cd_spawn.jsonc @@ -0,0 +1,90 @@ +// run array_cd_spawn +{ + "graph": [ + { + "key": [ + "/", + "array_cd_spawn" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_cd_spawn", + "package_path": "/" + }, + "items": [ + { + "execution_item_display": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_cd_spawn", + "package_path": "/" + }, + "command": "vtt print-file package.json", + "and_item_index": 1, + "cwd": "/snapshots" + }, + "kind": { + "Leaf": { + "Spawn": { + "cache_metadata": { + "spawn_fingerprint": { + "cwd": "snapshots", + "program_fingerprint": { + "OutsideWorkspace": { + "program_name": "vtt" + } + }, + "args": [ + "print-file", + "package.json" + ], + "env_fingerprints": { + "fingerprinted_envs": {}, + "untracked_env_config": [ + "" + ] + } + }, + "execution_cache_key": { + "UserTask": { + "task_name": "array_cd_spawn", + "and_item_index": 1, + "extra_args": [], + "package_path": "" + } + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + }, + "spawn_command": { + "program_path": "/vtt", + "args": [ + "print-file", + "package.json" + ], + "all_envs": { + "FORCE_COLOR": "1", + "PATH": "/node_modules/.bin:" + }, + "cwd": "/snapshots" + } + } + } + } + } + ] + }, + "neighbors": [] + } + ], + "concurrency_limit": 4 +} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_array_compound_cd_before_next.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_array_compound_cd_before_next.jsonc new file mode 100644 index 000000000..eb34730e1 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_array_compound_cd_before_next.jsonc @@ -0,0 +1,90 @@ +// run array_compound_cd_before_next +{ + "graph": [ + { + "key": [ + "/", + "array_compound_cd_before_next" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_compound_cd_before_next", + "package_path": "/" + }, + "items": [ + { + "execution_item_display": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_compound_cd_before_next", + "package_path": "/" + }, + "command": "if true; then cd snapshots; fi && vtt print-file package.json", + "and_item_index": null, + "cwd": "/" + }, + "kind": { + "Leaf": { + "Spawn": { + "cache_metadata": { + "spawn_fingerprint": { + "cwd": "", + "program_fingerprint": { + "OutsideWorkspace": { + "program_name": "" + } + }, + "args": [ + "", + "if true; then cd snapshots; fi && vtt print-file package.json" + ], + "env_fingerprints": { + "fingerprinted_envs": {}, + "untracked_env_config": [ + "" + ] + } + }, + "execution_cache_key": { + "UserTask": { + "task_name": "array_compound_cd_before_next", + "and_item_index": 0, + "extra_args": [], + "package_path": "" + } + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + }, + "spawn_command": { + "program_path": "", + "args": [ + "", + "if true; then cd snapshots; fi && vtt print-file package.json" + ], + "all_envs": { + "FORCE_COLOR": "1", + "PATH": "/node_modules/.bin:" + }, + "cwd": "/" + } + } + } + } + } + ] + }, + "neighbors": [] + } + ], + "concurrency_limit": 4 +} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_array_shell_cd_before_next.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_array_shell_cd_before_next.jsonc new file mode 100644 index 000000000..4f3c36c18 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_array_shell_cd_before_next.jsonc @@ -0,0 +1,90 @@ +// run array_shell_cd_before_next +{ + "graph": [ + { + "key": [ + "/", + "array_shell_cd_before_next" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_shell_cd_before_next", + "package_path": "/" + }, + "items": [ + { + "execution_item_display": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_shell_cd_before_next", + "package_path": "/" + }, + "command": "cd \"$APP_DIR\" && vtt print-file package.json", + "and_item_index": null, + "cwd": "/" + }, + "kind": { + "Leaf": { + "Spawn": { + "cache_metadata": { + "spawn_fingerprint": { + "cwd": "", + "program_fingerprint": { + "OutsideWorkspace": { + "program_name": "" + } + }, + "args": [ + "", + "cd \"$APP_DIR\" && vtt print-file package.json" + ], + "env_fingerprints": { + "fingerprinted_envs": {}, + "untracked_env_config": [ + "" + ] + } + }, + "execution_cache_key": { + "UserTask": { + "task_name": "array_shell_cd_before_next", + "and_item_index": 0, + "extra_args": [], + "package_path": "" + } + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + }, + "spawn_command": { + "program_path": "", + "args": [ + "", + "cd \"$APP_DIR\" && vtt print-file package.json" + ], + "all_envs": { + "FORCE_COLOR": "1", + "PATH": "/node_modules/.bin:" + }, + "cwd": "/" + } + } + } + } + } + ] + }, + "neighbors": [] + } + ], + "concurrency_limit": 4 +} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_array_shorthand.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_array_shorthand.jsonc new file mode 100644 index 000000000..6fc10ef65 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_array_shorthand.jsonc @@ -0,0 +1,226 @@ +// run array_shorthand +{ + "graph": [ + { + "key": [ + "/", + "array_shorthand" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_shorthand", + "package_path": "/" + }, + "items": [ + { + "execution_item_display": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_shorthand", + "package_path": "/" + }, + "command": "vtt print-file package.json", + "and_item_index": 0, + "cwd": "/" + }, + "kind": { + "Leaf": { + "Spawn": { + "cache_metadata": { + "spawn_fingerprint": { + "cwd": "", + "program_fingerprint": { + "OutsideWorkspace": { + "program_name": "vtt" + } + }, + "args": [ + "print-file", + "package.json" + ], + "env_fingerprints": { + "fingerprinted_envs": {}, + "untracked_env_config": [ + "" + ] + } + }, + "execution_cache_key": { + "UserTask": { + "task_name": "array_shorthand", + "and_item_index": 0, + "extra_args": [], + "package_path": "" + } + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + }, + "spawn_command": { + "program_path": "/vtt", + "args": [ + "print-file", + "package.json" + ], + "all_envs": { + "FORCE_COLOR": "1", + "PATH": "/node_modules/.bin:" + }, + "cwd": "/" + } + } + } + } + }, + { + "execution_item_display": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_shorthand", + "package_path": "/" + }, + "command": "vtt print-file vite-task.json", + "and_item_index": 1, + "cwd": "/" + }, + "kind": { + "Leaf": { + "Spawn": { + "cache_metadata": { + "spawn_fingerprint": { + "cwd": "", + "program_fingerprint": { + "OutsideWorkspace": { + "program_name": "vtt" + } + }, + "args": [ + "print-file", + "vite-task.json" + ], + "env_fingerprints": { + "fingerprinted_envs": {}, + "untracked_env_config": [ + "" + ] + } + }, + "execution_cache_key": { + "UserTask": { + "task_name": "array_shorthand", + "and_item_index": 1, + "extra_args": [], + "package_path": "" + } + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + }, + "spawn_command": { + "program_path": "/vtt", + "args": [ + "print-file", + "vite-task.json" + ], + "all_envs": { + "FORCE_COLOR": "1", + "PATH": "/node_modules/.bin:" + }, + "cwd": "/" + } + } + } + } + }, + { + "execution_item_display": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_shorthand", + "package_path": "/" + }, + "command": "vtt print-file package.json", + "and_item_index": 2, + "cwd": "/" + }, + "kind": { + "Leaf": { + "Spawn": { + "cache_metadata": { + "spawn_fingerprint": { + "cwd": "", + "program_fingerprint": { + "OutsideWorkspace": { + "program_name": "vtt" + } + }, + "args": [ + "print-file", + "package.json" + ], + "env_fingerprints": { + "fingerprinted_envs": {}, + "untracked_env_config": [ + "" + ] + } + }, + "execution_cache_key": { + "UserTask": { + "task_name": "array_shorthand", + "and_item_index": 2, + "extra_args": [], + "package_path": "" + } + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + }, + "spawn_command": { + "program_path": "/vtt", + "args": [ + "print-file", + "package.json" + ], + "all_envs": { + "FORCE_COLOR": "1", + "PATH": "/node_modules/.bin:" + }, + "cwd": "/" + } + } + } + } + } + ] + }, + "neighbors": [] + } + ], + "concurrency_limit": 4 +} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_array_with_and.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_array_with_and.jsonc new file mode 100644 index 000000000..dff16256d --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_array_with_and.jsonc @@ -0,0 +1,226 @@ +// run array_with_and +{ + "graph": [ + { + "key": [ + "/", + "array_with_and" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_with_and", + "package_path": "/" + }, + "items": [ + { + "execution_item_display": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_with_and", + "package_path": "/" + }, + "command": "vtt print-file package.json", + "and_item_index": 0, + "cwd": "/" + }, + "kind": { + "Leaf": { + "Spawn": { + "cache_metadata": { + "spawn_fingerprint": { + "cwd": "", + "program_fingerprint": { + "OutsideWorkspace": { + "program_name": "vtt" + } + }, + "args": [ + "print-file", + "package.json" + ], + "env_fingerprints": { + "fingerprinted_envs": {}, + "untracked_env_config": [ + "" + ] + } + }, + "execution_cache_key": { + "UserTask": { + "task_name": "array_with_and", + "and_item_index": 0, + "extra_args": [], + "package_path": "" + } + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + }, + "spawn_command": { + "program_path": "/vtt", + "args": [ + "print-file", + "package.json" + ], + "all_envs": { + "FORCE_COLOR": "1", + "PATH": "/node_modules/.bin:" + }, + "cwd": "/" + } + } + } + } + }, + { + "execution_item_display": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_with_and", + "package_path": "/" + }, + "command": "vtt print-file vite-task.json", + "and_item_index": 1, + "cwd": "/" + }, + "kind": { + "Leaf": { + "Spawn": { + "cache_metadata": { + "spawn_fingerprint": { + "cwd": "", + "program_fingerprint": { + "OutsideWorkspace": { + "program_name": "vtt" + } + }, + "args": [ + "print-file", + "vite-task.json" + ], + "env_fingerprints": { + "fingerprinted_envs": {}, + "untracked_env_config": [ + "" + ] + } + }, + "execution_cache_key": { + "UserTask": { + "task_name": "array_with_and", + "and_item_index": 1, + "extra_args": [], + "package_path": "" + } + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + }, + "spawn_command": { + "program_path": "/vtt", + "args": [ + "print-file", + "vite-task.json" + ], + "all_envs": { + "FORCE_COLOR": "1", + "PATH": "/node_modules/.bin:" + }, + "cwd": "/" + } + } + } + } + }, + { + "execution_item_display": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_with_and", + "package_path": "/" + }, + "command": "vtt print-file package.json", + "and_item_index": 2, + "cwd": "/" + }, + "kind": { + "Leaf": { + "Spawn": { + "cache_metadata": { + "spawn_fingerprint": { + "cwd": "", + "program_fingerprint": { + "OutsideWorkspace": { + "program_name": "vtt" + } + }, + "args": [ + "print-file", + "package.json" + ], + "env_fingerprints": { + "fingerprinted_envs": {}, + "untracked_env_config": [ + "" + ] + } + }, + "execution_cache_key": { + "UserTask": { + "task_name": "array_with_and", + "and_item_index": 2, + "extra_args": [], + "package_path": "" + } + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + }, + "spawn_command": { + "program_path": "/vtt", + "args": [ + "print-file", + "package.json" + ], + "all_envs": { + "FORCE_COLOR": "1", + "PATH": "/node_modules/.bin:" + }, + "cwd": "/" + } + } + } + } + } + ] + }, + "neighbors": [] + } + ], + "concurrency_limit": 4 +} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_empty_array_error.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_empty_array_error.snap new file mode 100644 index 000000000..071fcf32d --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_empty_array_error.snap @@ -0,0 +1 @@ +Invalid task command: command array must not be empty \ No newline at end of file diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_empty_item_error.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_empty_item_error.snap new file mode 100644 index 000000000..db31e66b7 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_empty_item_error.snap @@ -0,0 +1 @@ +Invalid task command: command array entries must not be empty \ No newline at end of file diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_nested_vt_array.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_nested_vt_array.jsonc new file mode 100644 index 000000000..a8d1109c1 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_nested_vt_array.jsonc @@ -0,0 +1,193 @@ +// run nested_vt_array +{ + "graph": [ + { + "key": [ + "/", + "nested_vt_array" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "nested_vt_array", + "package_path": "/" + }, + "items": [ + { + "execution_item_display": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "nested_vt_array", + "package_path": "/" + }, + "command": "vtt print-file package.json", + "and_item_index": 0, + "cwd": "/" + }, + "kind": { + "Leaf": { + "Spawn": { + "cache_metadata": { + "spawn_fingerprint": { + "cwd": "", + "program_fingerprint": { + "OutsideWorkspace": { + "program_name": "vtt" + } + }, + "args": [ + "print-file", + "package.json" + ], + "env_fingerprints": { + "fingerprinted_envs": {}, + "untracked_env_config": [ + "" + ] + } + }, + "execution_cache_key": { + "UserTask": { + "task_name": "nested_vt_array", + "and_item_index": 0, + "extra_args": [], + "package_path": "" + } + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + }, + "spawn_command": { + "program_path": "/vtt", + "args": [ + "print-file", + "package.json" + ], + "all_envs": { + "FORCE_COLOR": "1", + "PATH": "/node_modules/.bin:" + }, + "cwd": "/" + } + } + } + } + }, + { + "execution_item_display": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "nested_vt_array", + "package_path": "/" + }, + "command": "vt run string_shorthand", + "and_item_index": 1, + "cwd": "/" + }, + "kind": { + "Expanded": { + "graph": [ + { + "key": [ + "/", + "string_shorthand" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "string_shorthand", + "package_path": "/" + }, + "items": [ + { + "execution_item_display": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "string_shorthand", + "package_path": "/" + }, + "command": "vtt print-file package.json", + "and_item_index": null, + "cwd": "/" + }, + "kind": { + "Leaf": { + "Spawn": { + "cache_metadata": { + "spawn_fingerprint": { + "cwd": "", + "program_fingerprint": { + "OutsideWorkspace": { + "program_name": "vtt" + } + }, + "args": [ + "print-file", + "package.json" + ], + "env_fingerprints": { + "fingerprinted_envs": {}, + "untracked_env_config": [ + "" + ] + } + }, + "execution_cache_key": { + "UserTask": { + "task_name": "string_shorthand", + "and_item_index": 0, + "extra_args": [], + "package_path": "" + } + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + }, + "spawn_command": { + "program_path": "/vtt", + "args": [ + "print-file", + "package.json" + ], + "all_envs": { + "FORCE_COLOR": "1", + "PATH": "/node_modules/.bin:" + }, + "cwd": "/" + } + } + } + } + } + ] + }, + "neighbors": [] + } + ], + "concurrency_limit": 4 + } + } + } + ] + }, + "neighbors": [] + } + ], + "concurrency_limit": 4 +} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_object_array_depends_on.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_object_array_depends_on.jsonc new file mode 100644 index 000000000..9f14954cd --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_object_array_depends_on.jsonc @@ -0,0 +1,315 @@ +// run object_array_depends_on +{ + "graph": [ + { + "key": [ + "/", + "object_array_depends_on" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "object_array_depends_on", + "package_path": "/" + }, + "items": [ + { + "execution_item_display": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "object_array_depends_on", + "package_path": "/" + }, + "command": "vtt print-file package.json", + "and_item_index": 0, + "cwd": "/" + }, + "kind": { + "Leaf": { + "Spawn": { + "cache_metadata": { + "spawn_fingerprint": { + "cwd": "", + "program_fingerprint": { + "OutsideWorkspace": { + "program_name": "vtt" + } + }, + "args": [ + "print-file", + "package.json" + ], + "env_fingerprints": { + "fingerprinted_envs": {}, + "untracked_env_config": [ + "" + ] + } + }, + "execution_cache_key": { + "UserTask": { + "task_name": "object_array_depends_on", + "and_item_index": 0, + "extra_args": [], + "package_path": "" + } + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + }, + "spawn_command": { + "program_path": "/vtt", + "args": [ + "print-file", + "package.json" + ], + "all_envs": { + "FORCE_COLOR": "1", + "PATH": "/node_modules/.bin:" + }, + "cwd": "/" + } + } + } + } + }, + { + "execution_item_display": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "object_array_depends_on", + "package_path": "/" + }, + "command": "vtt print-file vite-task.json", + "and_item_index": 1, + "cwd": "/" + }, + "kind": { + "Leaf": { + "Spawn": { + "cache_metadata": { + "spawn_fingerprint": { + "cwd": "", + "program_fingerprint": { + "OutsideWorkspace": { + "program_name": "vtt" + } + }, + "args": [ + "print-file", + "vite-task.json" + ], + "env_fingerprints": { + "fingerprinted_envs": {}, + "untracked_env_config": [ + "" + ] + } + }, + "execution_cache_key": { + "UserTask": { + "task_name": "object_array_depends_on", + "and_item_index": 1, + "extra_args": [], + "package_path": "" + } + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + }, + "spawn_command": { + "program_path": "/vtt", + "args": [ + "print-file", + "vite-task.json" + ], + "all_envs": { + "FORCE_COLOR": "1", + "PATH": "/node_modules/.bin:" + }, + "cwd": "/" + } + } + } + } + }, + { + "execution_item_display": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "object_array_depends_on", + "package_path": "/" + }, + "command": "vtt print-file package.json", + "and_item_index": 2, + "cwd": "/" + }, + "kind": { + "Leaf": { + "Spawn": { + "cache_metadata": { + "spawn_fingerprint": { + "cwd": "", + "program_fingerprint": { + "OutsideWorkspace": { + "program_name": "vtt" + } + }, + "args": [ + "print-file", + "package.json" + ], + "env_fingerprints": { + "fingerprinted_envs": {}, + "untracked_env_config": [ + "" + ] + } + }, + "execution_cache_key": { + "UserTask": { + "task_name": "object_array_depends_on", + "and_item_index": 2, + "extra_args": [], + "package_path": "" + } + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + }, + "spawn_command": { + "program_path": "/vtt", + "args": [ + "print-file", + "package.json" + ], + "all_envs": { + "FORCE_COLOR": "1", + "PATH": "/node_modules/.bin:" + }, + "cwd": "/" + } + } + } + } + } + ] + }, + "neighbors": [ + [ + "/", + "string_shorthand" + ] + ] + }, + { + "key": [ + "/", + "string_shorthand" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "string_shorthand", + "package_path": "/" + }, + "items": [ + { + "execution_item_display": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "string_shorthand", + "package_path": "/" + }, + "command": "vtt print-file package.json", + "and_item_index": null, + "cwd": "/" + }, + "kind": { + "Leaf": { + "Spawn": { + "cache_metadata": { + "spawn_fingerprint": { + "cwd": "", + "program_fingerprint": { + "OutsideWorkspace": { + "program_name": "vtt" + } + }, + "args": [ + "print-file", + "package.json" + ], + "env_fingerprints": { + "fingerprinted_envs": {}, + "untracked_env_config": [ + "" + ] + } + }, + "execution_cache_key": { + "UserTask": { + "task_name": "string_shorthand", + "and_item_index": 0, + "extra_args": [], + "package_path": "" + } + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + }, + "spawn_command": { + "program_path": "/vtt", + "args": [ + "print-file", + "package.json" + ], + "all_envs": { + "FORCE_COLOR": "1", + "PATH": "/node_modules/.bin:" + }, + "cwd": "/" + } + } + } + } + } + ] + }, + "neighbors": [] + } + ], + "concurrency_limit": 4 +} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_string_shorthand.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_string_shorthand.jsonc new file mode 100644 index 000000000..eb265c51c --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/query_string_shorthand.jsonc @@ -0,0 +1,90 @@ +// run string_shorthand +{ + "graph": [ + { + "key": [ + "/", + "string_shorthand" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "string_shorthand", + "package_path": "/" + }, + "items": [ + { + "execution_item_display": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "string_shorthand", + "package_path": "/" + }, + "command": "vtt print-file package.json", + "and_item_index": null, + "cwd": "/" + }, + "kind": { + "Leaf": { + "Spawn": { + "cache_metadata": { + "spawn_fingerprint": { + "cwd": "", + "program_fingerprint": { + "OutsideWorkspace": { + "program_name": "vtt" + } + }, + "args": [ + "print-file", + "package.json" + ], + "env_fingerprints": { + "fingerprinted_envs": {}, + "untracked_env_config": [ + "" + ] + } + }, + "execution_cache_key": { + "UserTask": { + "task_name": "string_shorthand", + "and_item_index": 0, + "extra_args": [], + "package_path": "" + } + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + }, + "spawn_command": { + "program_path": "/vtt", + "args": [ + "print-file", + "package.json" + ], + "all_envs": { + "FORCE_COLOR": "1", + "PATH": "/node_modules/.bin:" + }, + "cwd": "/" + } + } + } + } + } + ] + }, + "neighbors": [] + } + ], + "concurrency_limit": 4 +} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/task_graph.jsonc new file mode 100644 index 000000000..c7ad2eab1 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/snapshots/task_graph.jsonc @@ -0,0 +1,466 @@ +// task graph +[ + { + "key": [ + "/", + "array_cd_shell" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_cd_shell", + "package_path": "/" + }, + "resolved_config": { + "command": [ + "cd snapshots", + "echo $PWD" + ], + "resolved_options": { + "cwd": "/", + "cache_config": { + "env_config": { + "fingerprinted_envs": [], + "untracked_env": [ + "" + ] + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + } + } + }, + "source": "TaskConfig" + }, + "neighbors": [] + }, + { + "key": [ + "/", + "array_cd_spawn" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_cd_spawn", + "package_path": "/" + }, + "resolved_config": { + "command": [ + "cd snapshots", + "vtt print-file package.json" + ], + "resolved_options": { + "cwd": "/", + "cache_config": { + "env_config": { + "fingerprinted_envs": [], + "untracked_env": [ + "" + ] + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + } + } + }, + "source": "TaskConfig" + }, + "neighbors": [] + }, + { + "key": [ + "/", + "array_compound_cd_before_next" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_compound_cd_before_next", + "package_path": "/" + }, + "resolved_config": { + "command": [ + "if true; then cd snapshots; fi", + "vtt print-file package.json" + ], + "resolved_options": { + "cwd": "/", + "cache_config": { + "env_config": { + "fingerprinted_envs": [], + "untracked_env": [ + "" + ] + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + } + } + }, + "source": "TaskConfig" + }, + "neighbors": [] + }, + { + "key": [ + "/", + "array_shell_cd_before_next" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_shell_cd_before_next", + "package_path": "/" + }, + "resolved_config": { + "command": [ + "cd \"$APP_DIR\"", + "vtt print-file package.json" + ], + "resolved_options": { + "cwd": "/", + "cache_config": { + "env_config": { + "fingerprinted_envs": [], + "untracked_env": [ + "" + ] + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + } + } + }, + "source": "TaskConfig" + }, + "neighbors": [] + }, + { + "key": [ + "/", + "array_shorthand" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_shorthand", + "package_path": "/" + }, + "resolved_config": { + "command": [ + "vtt print-file package.json", + "vtt print-file vite-task.json", + "vtt print-file package.json" + ], + "resolved_options": { + "cwd": "/", + "cache_config": { + "env_config": { + "fingerprinted_envs": [], + "untracked_env": [ + "" + ] + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + } + } + }, + "source": "TaskConfig" + }, + "neighbors": [] + }, + { + "key": [ + "/", + "array_with_and" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "array_with_and", + "package_path": "/" + }, + "resolved_config": { + "command": [ + "vtt print-file package.json", + "vtt print-file vite-task.json && vtt print-file package.json" + ], + "resolved_options": { + "cwd": "/", + "cache_config": { + "env_config": { + "fingerprinted_envs": [], + "untracked_env": [ + "" + ] + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + } + } + }, + "source": "TaskConfig" + }, + "neighbors": [] + }, + { + "key": [ + "/", + "empty_array" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "empty_array", + "package_path": "/" + }, + "resolved_config": { + "command": [], + "resolved_options": { + "cwd": "/", + "cache_config": { + "env_config": { + "fingerprinted_envs": [], + "untracked_env": [ + "" + ] + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + } + } + }, + "source": "TaskConfig" + }, + "neighbors": [] + }, + { + "key": [ + "/", + "empty_item" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "empty_item", + "package_path": "/" + }, + "resolved_config": { + "command": [ + "vtt print-file package.json", + "" + ], + "resolved_options": { + "cwd": "/", + "cache_config": { + "env_config": { + "fingerprinted_envs": [], + "untracked_env": [ + "" + ] + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + } + } + }, + "source": "TaskConfig" + }, + "neighbors": [] + }, + { + "key": [ + "/", + "nested_vt_array" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "nested_vt_array", + "package_path": "/" + }, + "resolved_config": { + "command": [ + "vtt print-file package.json", + "vt run string_shorthand" + ], + "resolved_options": { + "cwd": "/", + "cache_config": { + "env_config": { + "fingerprinted_envs": [], + "untracked_env": [ + "" + ] + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + } + } + }, + "source": "TaskConfig" + }, + "neighbors": [] + }, + { + "key": [ + "/", + "object_array_depends_on" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "object_array_depends_on", + "package_path": "/" + }, + "resolved_config": { + "command": [ + "vtt print-file package.json", + "vtt print-file vite-task.json", + "vtt print-file package.json" + ], + "resolved_options": { + "cwd": "/", + "cache_config": { + "env_config": { + "fingerprinted_envs": [], + "untracked_env": [ + "" + ] + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + } + } + }, + "source": "TaskConfig" + }, + "neighbors": [ + [ + "/", + "string_shorthand" + ] + ] + }, + { + "key": [ + "/", + "string_shorthand" + ], + "node": { + "task_display": { + "package_name": "@test/task-command-shorthands", + "task_name": "string_shorthand", + "package_path": "/" + }, + "resolved_config": { + "command": "vtt print-file package.json", + "resolved_options": { + "cwd": "/", + "cache_config": { + "env_config": { + "fingerprinted_envs": [], + "untracked_env": [ + "" + ] + }, + "input_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] + }, + "output_config": { + "includes_auto": false, + "positive_globs": [], + "negative_globs": [] + } + } + } + }, + "source": "TaskConfig" + }, + "neighbors": [] + } +] diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/vite-task.json b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/vite-task.json new file mode 100644 index 000000000..68e06f6a4 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/task_command_shorthands/vite-task.json @@ -0,0 +1,18 @@ +{ + "tasks": { + "string_shorthand": "vtt print-file package.json", + "array_shorthand": ["vtt print-file package.json", "vtt print-file vite-task.json", "vtt print-file package.json"], + "array_with_and": ["vtt print-file package.json", "vtt print-file vite-task.json && vtt print-file package.json"], + "nested_vt_array": ["vtt print-file package.json", "vt run string_shorthand"], + "array_cd_spawn": ["cd snapshots", "vtt print-file package.json"], + "array_cd_shell": ["cd snapshots", "echo $PWD"], + "array_shell_cd_before_next": ["cd \"$APP_DIR\"", "vtt print-file package.json"], + "array_compound_cd_before_next": ["if true; then cd snapshots; fi", "vtt print-file package.json"], + "object_array_depends_on": { + "command": ["vtt print-file package.json", "vtt print-file vite-task.json", "vtt print-file package.json"], + "dependsOn": ["string_shorthand"] + }, + "empty_array": [], + "empty_item": ["vtt print-file package.json", ""] + } +}