diff --git a/CLAUDE.md b/CLAUDE.md index 17b4bd30..8d093a5a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -141,7 +141,7 @@ Tasks are defined in `vite-task.json`: ``` - `cache` (root): workspace-wide cache toggle. Default: `{ "scripts": false, "tasks": true }` -- `command`: shell command to run (falls back to package.json script if omitted) +- `command`: shell command to run (required) - `cwd`: working directory relative to the package root - `dependsOn`: explicit task dependencies (`taskName` or `package#task`) - `cache` (task): enable/disable caching for this task (default: `true`) diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task-list/packages/app/package.json b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task-list/packages/app/package.json index 45aeb51c..c8582bb8 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task-list/packages/app/package.json +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task-list/packages/app/package.json @@ -1,9 +1,5 @@ { "name": "app", - "scripts": { - "build": "echo build app", - "test": "echo test app" - }, "dependencies": { "lib": "workspace:*" } diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task-list/packages/app/vite-task.json b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task-list/packages/app/vite-task.json index 2814f38e..0ca9a1ed 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task-list/packages/app/vite-task.json +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task-list/packages/app/vite-task.json @@ -1,7 +1,11 @@ { "tasks": { - "build": {}, - "test": {}, + "build": { + "command": "echo build app" + }, + "test": { + "command": "echo test app" + }, "lint": { "command": "echo lint app" } diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task-list/packages/lib/package.json b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task-list/packages/lib/package.json index 5a2cc4d3..f5ff3585 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task-list/packages/lib/package.json +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task-list/packages/lib/package.json @@ -1,6 +1,3 @@ { - "name": "lib", - "scripts": { - "build": "echo build lib" - } + "name": "lib" } diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task-list/packages/lib/vite-task.json b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task-list/packages/lib/vite-task.json index 355525a7..bef5809c 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task-list/packages/lib/vite-task.json +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/task-list/packages/lib/vite-task.json @@ -1,5 +1,7 @@ { "tasks": { - "build": {} + "build": { + "command": "echo build lib" + } } } diff --git a/crates/vite_task_graph/run-config.ts b/crates/vite_task_graph/run-config.ts index b79190c9..d00dfff4 100644 --- a/crates/vite_task_graph/run-config.ts +++ b/crates/vite_task_graph/run-config.ts @@ -3,10 +3,8 @@ export type Task = { /** * The command to run for the task. - * - * If omitted, the script from `package.json` with the same name will be used */ - command?: string; + command: string; /** * The working directory for the task, relative to the package root (not workspace root). */ diff --git a/crates/vite_task_graph/src/config/mod.rs b/crates/vite_task_graph/src/config/mod.rs index 5a8fc11f..4ae4ecf1 100644 --- a/crates/vite_task_graph/src/config/mod.rs +++ b/crates/vite_task_graph/src/config/mod.rs @@ -243,14 +243,6 @@ pub struct EnvConfig { #[derive(Debug, thiserror::Error)] pub enum ResolveTaskConfigError { - /// Both package.json script and vite.config.* task define commands for the task - #[error("Both package.json script and vite.config.* task define commands for the task")] - CommandConflict, - - /// Neither package.json script nor vite.config.* task define a command for the task - #[error("Neither package.json script nor vite.config.* task define a command for the task")] - NoCommand, - /// A glob pattern resolves to a path outside the workspace root #[error("glob pattern '{pattern}' resolves outside the workspace root")] GlobOutsideWorkspace { pattern: Str }, @@ -288,26 +280,18 @@ impl ResolvedTaskConfig { }) } - /// Resolves from user config, package dir, and package.json script (if any). + /// Resolves from user config and package dir. /// /// # Errors /// - /// Returns [`ResolveTaskConfigError::CommandConflict`] if both the user config and - /// package.json define a command, or [`ResolveTaskConfigError::NoCommand`] if neither does. + /// Returns [`ResolveTaskConfigError`] if glob resolution fails. pub fn resolve( user_config: UserTaskConfig, package_dir: &Arc, - package_json_script: Option<&str>, workspace_root: &AbsolutePath, ) -> Result { - let command = match (&user_config.command, package_json_script) { - (Some(_), Some(_)) => return Err(ResolveTaskConfigError::CommandConflict), - (None, None) => return Err(ResolveTaskConfigError::NoCommand), - (Some(cmd), None) => cmd.as_ref(), - (None, Some(script)) => script, - }; Ok(Self { - command: command.into(), + command: Str::from(user_config.command.as_ref()), 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 6210fadf..8f46f67e 100644 --- a/crates/vite_task_graph/src/config/user.rs +++ b/crates/vite_task_graph/src/config/user.rs @@ -141,9 +141,7 @@ impl Default for UserTaskOptions { #[serde(rename_all = "camelCase")] pub struct UserTaskConfig { /// The command to run for the task. - /// - /// If omitted, the script from `package.json` with the same name will be used - pub command: Option>, + pub command: Box, /// Fields other than the command #[serde(flatten)] @@ -365,22 +363,30 @@ mod tests { use super::*; #[test] - fn test_defaults() { + fn test_command_required() { let user_config_json = json!({}); + assert!( + serde_json::from_value::(user_config_json).is_err(), + "task config without command should fail to deserialize" + ); + } + + #[test] + fn test_command_with_defaults() { + let user_config_json = json!({ + "command": "echo hello" + }); let user_config: UserTaskConfig = serde_json::from_value(user_config_json).unwrap(); assert_eq!( user_config, - UserTaskConfig { - command: None, - // A empty task config (`{}`) should be equivalent to not specifying any config at all (just package.json script) - options: UserTaskOptions::default(), - } + UserTaskConfig { command: "echo hello".into(), options: UserTaskOptions::default() } ); } #[test] fn test_cwd_rename() { let user_config_json = json!({ + "command": "echo test", "cwd": "src" }); let user_config: UserTaskConfig = serde_json::from_value(user_config_json).unwrap(); @@ -390,6 +396,7 @@ mod tests { #[test] fn test_cache_disabled() { let user_config_json = json!({ + "command": "echo test", "cache": false }); let user_config: UserTaskConfig = serde_json::from_value(user_config_json).unwrap(); diff --git a/crates/vite_task_graph/src/lib.rs b/crates/vite_task_graph/src/lib.rs index 91016619..9741c64a 100644 --- a/crates/vite_task_graph/src/lib.rs +++ b/crates/vite_task_graph/src/lib.rs @@ -95,6 +95,12 @@ pub enum TaskGraphLoadError { error: anyhow::Error, }, + #[error( + "Task {task_display} conflicts with a package.json script of the same name. \ + Remove the script from package.json or rename the task" + )] + ScriptConflict { task_display: TaskDisplay }, + #[error("Failed to resolve task config for task {task_display}")] ResolveConfigError { task_display: TaskDisplay, @@ -264,18 +270,25 @@ impl IndexedTaskGraph { .collect(); for (task_name, task_user_config) in user_config.tasks.unwrap_or_default() { - // For each task defined in the config, look up the corresponding package.json script (if any) - let package_json_script = package_json_scripts.remove(task_name.as_str()); + // Error if a package.json script with the same name exists + if package_json_scripts.remove(task_name.as_str()).is_some() { + return Err(TaskGraphLoadError::ScriptConflict { + task_display: TaskDisplay { + package_name: package.package_json.name.clone(), + task_name: task_name.clone(), + package_path: Arc::clone(&package_dir), + }, + }); + } let task_id = TaskId { task_name: task_name.clone(), package_index }; let dependency_specifiers = task_user_config.options.depends_on.clone(); - // Resolve the task configuration combining config and package.json script + // Resolve the task configuration from the user config let resolved_config = ResolvedTaskConfig::resolve( task_user_config, &package_dir, - package_json_script, &workspace_root.path, ) .map_err(|err| TaskGraphLoadError::ResolveConfigError { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-scripts-task-override/package.json b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-scripts-task-override/package.json index 7c4673a3..7bbf8fa6 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-scripts-task-override/package.json +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-scripts-task-override/package.json @@ -1,7 +1,6 @@ { "name": "@test/cache-scripts-task-override", "scripts": { - "build": "print-file package.json", "test": "print-file package.json" } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-scripts-task-override/snapshots.toml b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-scripts-task-override/snapshots.toml index 28ad4d0d..25915850 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-scripts-task-override/snapshots.toml +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-scripts-task-override/snapshots.toml @@ -5,12 +5,12 @@ name = "task cached by default" args = ["run", "build"] -# Task with explicit command should be cached +# Another task should also be cached [[plan]] -name = "task with command cached by default" +name = "another task cached by default" args = ["run", "deploy"] -# Script not wrapped by a task should not be cached +# Script not in the tasks map should not be cached [[plan]] name = "script not cached by default" args = ["run", "test"] diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-scripts-task-override/snapshots/query - task with command cached by default.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-scripts-task-override/snapshots/query - another task cached by default.snap similarity index 100% rename from crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-scripts-task-override/snapshots/query - task with command cached by default.snap rename to crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-scripts-task-override/snapshots/query - another task cached by default.snap diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-scripts-task-override/vite-task.json b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-scripts-task-override/vite-task.json index 8a8200da..507d14b5 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-scripts-task-override/vite-task.json +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-scripts-task-override/vite-task.json @@ -1,6 +1,8 @@ { "tasks": { - "build": {}, + "build": { + "command": "print-file package.json" + }, "deploy": { "command": "print-file package.json" } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-tasks-disabled/package.json b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-tasks-disabled/package.json index 96eab87e..1aadb62c 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-tasks-disabled/package.json +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-tasks-disabled/package.json @@ -1,7 +1,6 @@ { "name": "@test/cache-tasks-disabled", "scripts": { - "build": "print-file package.json", "test": "print-file package.json" } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-tasks-disabled/vite-task.json b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-tasks-disabled/vite-task.json index 31908df8..10d2e97d 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-tasks-disabled/vite-task.json +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-tasks-disabled/vite-task.json @@ -1,7 +1,9 @@ { "cache": { "tasks": false }, "tasks": { - "build": {}, + "build": { + "command": "print-file package.json" + }, "deploy": { "command": "print-file package.json", "cache": true diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-true-no-force-enable/package.json b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-true-no-force-enable/package.json index 8bace95e..74d15c2a 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-true-no-force-enable/package.json +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-true-no-force-enable/package.json @@ -1,7 +1,6 @@ { "name": "@test/cache-true-no-force-enable", "scripts": { - "build": "print-file package.json", "test": "print-file package.json" } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-true-no-force-enable/vite-task.json b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-true-no-force-enable/vite-task.json index 7f6f8a25..48139b37 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-true-no-force-enable/vite-task.json +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-true-no-force-enable/vite-task.json @@ -2,6 +2,7 @@ "cache": true, "tasks": { "build": { + "command": "print-file package.json", "cache": false }, "deploy": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/script-conflict/package.json b/crates/vite_task_plan/tests/plan_snapshots/fixtures/script-conflict/package.json new file mode 100644 index 00000000..fec247d2 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/script-conflict/package.json @@ -0,0 +1,6 @@ +{ + "name": "@test/script-conflict", + "scripts": { + "build": "echo from script" + } +} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/script-conflict/snapshots/task graph load error.snap b/crates/vite_task_plan/tests/plan_snapshots/fixtures/script-conflict/snapshots/task graph load error.snap new file mode 100644 index 00000000..4a7c6cd6 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/script-conflict/snapshots/task graph load error.snap @@ -0,0 +1,6 @@ +--- +source: crates/vite_task_plan/tests/plan_snapshots/main.rs +expression: err_str.as_ref() +input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/script-conflict +--- +Task @test/script-conflict#build conflicts with a package.json script of the same name. Remove the script from package.json or rename the task diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/script-conflict/vite-task.json b/crates/vite_task_plan/tests/plan_snapshots/fixtures/script-conflict/vite-task.json new file mode 100644 index 00000000..9fa380a8 --- /dev/null +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/script-conflict/vite-task.json @@ -0,0 +1,7 @@ +{ + "tasks": { + "build": { + "command": "echo from task" + } + } +} diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace-root-depends-on-passthrough/package.json b/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace-root-depends-on-passthrough/package.json index 72fb1dec..fc2ede25 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace-root-depends-on-passthrough/package.json +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace-root-depends-on-passthrough/package.json @@ -2,7 +2,6 @@ "name": "test-workspace", "private": true, "scripts": { - "build": "vp run -r build", "lint": "echo linting" } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace-root-depends-on-passthrough/vite-task.json b/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace-root-depends-on-passthrough/vite-task.json index f329d36f..38c35fb2 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace-root-depends-on-passthrough/vite-task.json +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace-root-depends-on-passthrough/vite-task.json @@ -2,6 +2,7 @@ "cache": true, "tasks": { "build": { + "command": "vp run -r build", "dependsOn": ["lint"] } }