Skip to content

Commit 34bce5c

Browse files
branchseerclaude
andauthored
fix: make task command required (#224)
## Summary This change makes the `command` field mandatory in task configurations within `vite-task.json`. Previously, tasks could omit the command and fall back to a corresponding script defined in `package.json`. This change simplifies the configuration model by requiring explicit command definitions. ## Key Changes - **Made `command` field required**: Changed `UserTaskConfig.command` from `Option<Box<str>>` to `Box<str>`, making it a required field during deserialization - **Removed fallback logic**: Eliminated the fallback mechanism that would use `package.json` scripts when a task command was omitted - **Removed error variants**: Deleted `ResolveTaskConfigError::CommandConflict` and `ResolveTaskConfigError::NoCommand` as they are no longer needed - **Simplified task resolution**: Updated `ResolvedTaskConfig::resolve()` to only accept user config and package directory, removing the `package_json_script` parameter - **Updated test fixtures**: Modified all test fixtures to explicitly define commands in `vite-task.json` instead of relying on `package.json` scripts - **Updated documentation**: Modified TypeScript type definitions and CLAUDE.md to reflect that `command` is now required ## Implementation Details - Tasks defined in `vite-task.json` must now include an explicit `command` field - Package.json scripts are no longer used as fallbacks for task commands - The configuration is now more explicit and reduces ambiguity about which command will be executed - All existing test fixtures were updated to comply with the new requirement https://claude.ai/code/session_01LMFoqFGF6abfJ3WuSeEoRk --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 24434e8 commit 34bce5c

File tree

22 files changed

+78
-56
lines changed

22 files changed

+78
-56
lines changed

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ Tasks are defined in `vite-task.json`:
141141
```
142142

143143
- `cache` (root): workspace-wide cache toggle. Default: `{ "scripts": false, "tasks": true }`
144-
- `command`: shell command to run (falls back to package.json script if omitted)
144+
- `command`: shell command to run (required)
145145
- `cwd`: working directory relative to the package root
146146
- `dependsOn`: explicit task dependencies (`taskName` or `package#task`)
147147
- `cache` (task): enable/disable caching for this task (default: `true`)

crates/vite_task_bin/tests/e2e_snapshots/fixtures/task-list/packages/app/package.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
{
22
"name": "app",
3-
"scripts": {
4-
"build": "echo build app",
5-
"test": "echo test app"
6-
},
73
"dependencies": {
84
"lib": "workspace:*"
95
}

crates/vite_task_bin/tests/e2e_snapshots/fixtures/task-list/packages/app/vite-task.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
{
22
"tasks": {
3-
"build": {},
4-
"test": {},
3+
"build": {
4+
"command": "echo build app"
5+
},
6+
"test": {
7+
"command": "echo test app"
8+
},
59
"lint": {
610
"command": "echo lint app"
711
}
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
11
{
2-
"name": "lib",
3-
"scripts": {
4-
"build": "echo build lib"
5-
}
2+
"name": "lib"
63
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
{
22
"tasks": {
3-
"build": {}
3+
"build": {
4+
"command": "echo build lib"
5+
}
46
}
57
}

crates/vite_task_graph/run-config.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
export type Task = {
44
/**
55
* The command to run for the task.
6-
*
7-
* If omitted, the script from `package.json` with the same name will be used
86
*/
9-
command?: string;
7+
command: string;
108
/**
119
* The working directory for the task, relative to the package root (not workspace root).
1210
*/

crates/vite_task_graph/src/config/mod.rs

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -243,14 +243,6 @@ pub struct EnvConfig {
243243

244244
#[derive(Debug, thiserror::Error)]
245245
pub enum ResolveTaskConfigError {
246-
/// Both package.json script and vite.config.* task define commands for the task
247-
#[error("Both package.json script and vite.config.* task define commands for the task")]
248-
CommandConflict,
249-
250-
/// Neither package.json script nor vite.config.* task define a command for the task
251-
#[error("Neither package.json script nor vite.config.* task define a command for the task")]
252-
NoCommand,
253-
254246
/// A glob pattern resolves to a path outside the workspace root
255247
#[error("glob pattern '{pattern}' resolves outside the workspace root")]
256248
GlobOutsideWorkspace { pattern: Str },
@@ -288,26 +280,18 @@ impl ResolvedTaskConfig {
288280
})
289281
}
290282

291-
/// Resolves from user config, package dir, and package.json script (if any).
283+
/// Resolves from user config and package dir.
292284
///
293285
/// # Errors
294286
///
295-
/// Returns [`ResolveTaskConfigError::CommandConflict`] if both the user config and
296-
/// package.json define a command, or [`ResolveTaskConfigError::NoCommand`] if neither does.
287+
/// Returns [`ResolveTaskConfigError`] if glob resolution fails.
297288
pub fn resolve(
298289
user_config: UserTaskConfig,
299290
package_dir: &Arc<AbsolutePath>,
300-
package_json_script: Option<&str>,
301291
workspace_root: &AbsolutePath,
302292
) -> Result<Self, ResolveTaskConfigError> {
303-
let command = match (&user_config.command, package_json_script) {
304-
(Some(_), Some(_)) => return Err(ResolveTaskConfigError::CommandConflict),
305-
(None, None) => return Err(ResolveTaskConfigError::NoCommand),
306-
(Some(cmd), None) => cmd.as_ref(),
307-
(None, Some(script)) => script,
308-
};
309293
Ok(Self {
310-
command: command.into(),
294+
command: Str::from(user_config.command.as_ref()),
311295
resolved_options: ResolvedTaskOptions::resolve(
312296
user_config.options,
313297
package_dir,

crates/vite_task_graph/src/config/user.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,7 @@ impl Default for UserTaskOptions {
141141
#[serde(rename_all = "camelCase")]
142142
pub struct UserTaskConfig {
143143
/// The command to run for the task.
144-
///
145-
/// If omitted, the script from `package.json` with the same name will be used
146-
pub command: Option<Box<str>>,
144+
pub command: Box<str>,
147145

148146
/// Fields other than the command
149147
#[serde(flatten)]
@@ -365,22 +363,30 @@ mod tests {
365363
use super::*;
366364

367365
#[test]
368-
fn test_defaults() {
366+
fn test_command_required() {
369367
let user_config_json = json!({});
368+
assert!(
369+
serde_json::from_value::<UserTaskConfig>(user_config_json).is_err(),
370+
"task config without command should fail to deserialize"
371+
);
372+
}
373+
374+
#[test]
375+
fn test_command_with_defaults() {
376+
let user_config_json = json!({
377+
"command": "echo hello"
378+
});
370379
let user_config: UserTaskConfig = serde_json::from_value(user_config_json).unwrap();
371380
assert_eq!(
372381
user_config,
373-
UserTaskConfig {
374-
command: None,
375-
// A empty task config (`{}`) should be equivalent to not specifying any config at all (just package.json script)
376-
options: UserTaskOptions::default(),
377-
}
382+
UserTaskConfig { command: "echo hello".into(), options: UserTaskOptions::default() }
378383
);
379384
}
380385

381386
#[test]
382387
fn test_cwd_rename() {
383388
let user_config_json = json!({
389+
"command": "echo test",
384390
"cwd": "src"
385391
});
386392
let user_config: UserTaskConfig = serde_json::from_value(user_config_json).unwrap();
@@ -390,6 +396,7 @@ mod tests {
390396
#[test]
391397
fn test_cache_disabled() {
392398
let user_config_json = json!({
399+
"command": "echo test",
393400
"cache": false
394401
});
395402
let user_config: UserTaskConfig = serde_json::from_value(user_config_json).unwrap();

crates/vite_task_graph/src/lib.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ pub enum TaskGraphLoadError {
9595
error: anyhow::Error,
9696
},
9797

98+
#[error(
99+
"Task {task_display} conflicts with a package.json script of the same name. \
100+
Remove the script from package.json or rename the task"
101+
)]
102+
ScriptConflict { task_display: TaskDisplay },
103+
98104
#[error("Failed to resolve task config for task {task_display}")]
99105
ResolveConfigError {
100106
task_display: TaskDisplay,
@@ -264,18 +270,25 @@ impl IndexedTaskGraph {
264270
.collect();
265271

266272
for (task_name, task_user_config) in user_config.tasks.unwrap_or_default() {
267-
// For each task defined in the config, look up the corresponding package.json script (if any)
268-
let package_json_script = package_json_scripts.remove(task_name.as_str());
273+
// Error if a package.json script with the same name exists
274+
if package_json_scripts.remove(task_name.as_str()).is_some() {
275+
return Err(TaskGraphLoadError::ScriptConflict {
276+
task_display: TaskDisplay {
277+
package_name: package.package_json.name.clone(),
278+
task_name: task_name.clone(),
279+
package_path: Arc::clone(&package_dir),
280+
},
281+
});
282+
}
269283

270284
let task_id = TaskId { task_name: task_name.clone(), package_index };
271285

272286
let dependency_specifiers = task_user_config.options.depends_on.clone();
273287

274-
// Resolve the task configuration combining config and package.json script
288+
// Resolve the task configuration from the user config
275289
let resolved_config = ResolvedTaskConfig::resolve(
276290
task_user_config,
277291
&package_dir,
278-
package_json_script,
279292
&workspace_root.path,
280293
)
281294
.map_err(|err| TaskGraphLoadError::ResolveConfigError {
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
{
22
"name": "@test/cache-scripts-task-override",
33
"scripts": {
4-
"build": "print-file package.json",
54
"test": "print-file package.json"
65
}
76
}

0 commit comments

Comments
 (0)