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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,17 @@ Tasks are defined in `vite-task.json`:
}
```

## Internal vs Public Naming

This repo uses internal names that differ from the public-facing product. In code comments, docs, and user-facing strings, use the public names:

| Internal (this repo) | Public (Vite+) |
| -------------------- | --------------- |
| `vt` | `vp` |
| `vite-task.json` | `vite.config.*` |

`vite-task.json` and `vt` are fine in implementation code, test fixtures, and CLAUDE.md itself — just not in doc comments or user-facing messages.

## Code Constraints

### Required Patterns
Expand Down
2 changes: 1 addition & 1 deletion crates/vite_task_graph/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::display::TaskDisplay;

/// The type of a task dependency edge in the task graph.
///
/// Currently only `Explicit` is produced (from `dependsOn` in `vite-task.json`).
/// Currently only `Explicit` is produced (from `dependsOn` in the task config).
/// Topological ordering is handled at query time via the package subgraph rather
/// than by pre-computing edges in the task graph.
#[derive(Debug, Clone, Copy, Serialize)]
Expand Down
2 changes: 1 addition & 1 deletion crates/vite_task_graph/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub struct TaskQuery {
/// The task name to run within each selected package.
pub task_name: Str,

/// Whether to include explicit `dependsOn` dependencies from `vite-task.json`.
/// Whether to include explicit `dependsOn` dependencies from the task config.
pub include_explicit_deps: bool,
}

Expand Down
1 change: 1 addition & 0 deletions crates/vite_task_plan/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ pub fn plan_synthetic(
synthetic_plan_request,
Some(execution_cache_key),
cwd,
cwd,
ParentCacheConfig::None,
)
}
264 changes: 254 additions & 10 deletions crates/vite_task_plan/src/plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ use vite_str::Str;
use vite_task_graph::{
TaskNodeIndex, TaskSource,
config::{
CacheConfig, ResolvedGlobalCacheConfig, ResolvedTaskOptions,
CacheConfig, EnabledCacheConfig, ResolvedGlobalCacheConfig, ResolvedInputConfig,
ResolvedTaskOptions,
user::{UserCacheConfig, UserTaskOptions},
},
query::TaskQuery,
Expand Down Expand Up @@ -281,6 +282,7 @@ async fn plan_task_as_execution_node(
synthetic_plan_request,
Some(task_execution_cache_key),
&cwd,
package_path,
parent_cache_config,
)?;
ExecutionItemKind::Leaf(LeafExecutionKind::Spawn(spawn_execution))
Expand Down Expand Up @@ -433,7 +435,7 @@ pub enum ParentCacheConfig {
fn resolve_synthetic_cache_config(
parent: ParentCacheConfig,
synthetic_cache_config: UserCacheConfig,
cwd: &Arc<AbsolutePath>,
package_dir: &AbsolutePath,
workspace_path: &AbsolutePath,
) -> Result<Option<CacheConfig>, Error> {
match parent {
Expand All @@ -445,7 +447,7 @@ fn resolve_synthetic_cache_config(
cwd_relative_to_package: None,
depends_on: None,
},
cwd,
&package_dir.into(),
workspace_path,
)
.map_err(Error::ResolveTaskConfig)?
Expand All @@ -458,12 +460,33 @@ fn resolve_synthetic_cache_config(
Ok(match synthetic_cache_config {
UserCacheConfig::Disabled { .. } => Option::None,
UserCacheConfig::Enabled { enabled_cache_config, .. } => {
if let Some(extra_envs) = enabled_cache_config.env {
parent_config.env_config.fingerprinted_envs.extend(extra_envs.into_vec());
}
if let Some(extra_pts) = enabled_cache_config.untracked_env {
parent_config.env_config.untracked_env.extend(extra_pts);
let EnabledCacheConfig { env, untracked_env, input } = enabled_cache_config;
parent_config.env_config.fingerprinted_envs.extend(env.unwrap_or_default());
parent_config
.env_config
.untracked_env
.extend(untracked_env.unwrap_or_default());

if let Some(input) = input {
let synthetic_input = ResolvedInputConfig::from_user_config(
Some(&input),
package_dir,
workspace_path,
)
.map_err(Error::ResolveTaskConfig)?;
// Merge globs but preserve the parent's includes_auto —
// the user's explicit choice takes precedence
// over the synthetic handler's preference.
parent_config
.input_config
.positive_globs
.extend(synthetic_input.positive_globs);
parent_config
.input_config
.negative_globs
.extend(synthetic_input.negative_globs);
}

Some(parent_config)
}
})
Expand All @@ -478,13 +501,18 @@ pub fn plan_synthetic_request(
synthetic_plan_request: SyntheticPlanRequest,
execution_cache_key: Option<ExecutionCacheKey>,
cwd: &Arc<AbsolutePath>,
package_dir: &AbsolutePath,
parent_cache_config: ParentCacheConfig,
) -> Result<SpawnExecution, Error> {
let SyntheticPlanRequest { program, args, cache_config, envs } = synthetic_plan_request;

let program_path = which(&program, &envs, cwd)?;
let resolved_cache_config =
resolve_synthetic_cache_config(parent_cache_config, cache_config, cwd, workspace_path)?;
let resolved_cache_config = resolve_synthetic_cache_config(
parent_cache_config,
cache_config,
package_dir,
workspace_path,
)?;
let resolved_options =
ResolvedTaskOptions { cwd: Arc::clone(cwd), cache_config: resolved_cache_config };

Expand Down Expand Up @@ -749,3 +777,219 @@ pub async fn plan_query_request(
Error::CycleDependencyDetected(displays)
})
}

#[cfg(test)]
mod tests {
use std::collections::BTreeSet;

use rustc_hash::FxHashSet;
use vite_path::AbsolutePathBuf;
use vite_str::Str;
use vite_task_graph::config::{
CacheConfig, EnabledCacheConfig, EnvConfig, ResolvedInputConfig,
user::{UserCacheConfig, UserInputEntry},
};

use super::{ParentCacheConfig, resolve_synthetic_cache_config};

fn test_paths() -> (AbsolutePathBuf, AbsolutePathBuf) {
if cfg!(windows) {
(
AbsolutePathBuf::new("C:\\workspace\\packages\\my-pkg".into()).unwrap(),
AbsolutePathBuf::new("C:\\workspace".into()).unwrap(),
)
} else {
(
AbsolutePathBuf::new("/workspace/packages/my-pkg".into()).unwrap(),
AbsolutePathBuf::new("/workspace".into()).unwrap(),
)
}
}

fn parent_config(includes_auto: bool, positive_globs: &[&str]) -> CacheConfig {
CacheConfig {
env_config: EnvConfig {
fingerprinted_envs: FxHashSet::default(),
untracked_env: FxHashSet::default(),
},
input_config: ResolvedInputConfig {
includes_auto,
positive_globs: positive_globs.iter().map(|s| Str::from(*s)).collect(),
negative_globs: BTreeSet::new(),
},
}
}

fn globs(items: &[&str]) -> BTreeSet<Str> {
items.iter().map(|s| Str::from(*s)).collect()
}

#[test]
fn synthetic_input_none_preserves_parent() {
let (pkg, ws) = test_paths();
let parent = parent_config(true, &["src/**"]);
let result = resolve_synthetic_cache_config(
ParentCacheConfig::Inherited(parent),
UserCacheConfig::with_config(EnabledCacheConfig {
env: None,
untracked_env: None,
input: None,
}),
&pkg,
&ws,
)
.unwrap()
.unwrap();
assert!(result.input_config.includes_auto);
assert_eq!(result.input_config.positive_globs, globs(&["src/**"]));
assert_eq!(result.input_config.negative_globs, globs(&[]));
}

#[test]
fn synthetic_globs_merged_into_parent_default_auto() {
let (pkg, ws) = test_paths();
let parent = parent_config(true, &[]);
let result = resolve_synthetic_cache_config(
ParentCacheConfig::Inherited(parent),
UserCacheConfig::with_config(EnabledCacheConfig {
env: None,
untracked_env: None,
input: Some(vec![UserInputEntry::Glob("config/**".into())]),
}),
&pkg,
&ws,
)
.unwrap()
.unwrap();
assert!(result.input_config.includes_auto);
assert_eq!(result.input_config.positive_globs, globs(&["packages/my-pkg/config/**"]));
assert_eq!(result.input_config.negative_globs, globs(&[]));
}

#[test]
fn synthetic_globs_merged_into_parent_explicit_input() {
let (pkg, ws) = test_paths();
let parent = parent_config(false, &["src/**"]);
let result = resolve_synthetic_cache_config(
ParentCacheConfig::Inherited(parent),
UserCacheConfig::with_config(EnabledCacheConfig {
env: None,
untracked_env: None,
input: Some(vec![UserInputEntry::Glob("config/**".into())]),
}),
&pkg,
&ws,
)
.unwrap()
.unwrap();
assert!(!result.input_config.includes_auto);
assert_eq!(
result.input_config.positive_globs,
globs(&["packages/my-pkg/config/**", "src/**"])
);
assert_eq!(result.input_config.negative_globs, globs(&[]));
}

#[test]
fn synthetic_auto_ignored_when_parent_has_explicit_input() {
let (pkg, ws) = test_paths();
let parent = parent_config(false, &["src/**"]);
let result = resolve_synthetic_cache_config(
ParentCacheConfig::Inherited(parent),
UserCacheConfig::with_config(EnabledCacheConfig {
env: None,
untracked_env: None,
input: Some(vec![
UserInputEntry::Glob("config/**".into()),
UserInputEntry::Auto(vite_task_graph::config::user::AutoInput { auto: true }),
]),
}),
&pkg,
&ws,
)
.unwrap()
.unwrap();
// User's explicit choice (no auto) takes precedence
assert!(!result.input_config.includes_auto);
assert_eq!(
result.input_config.positive_globs,
globs(&["packages/my-pkg/config/**", "src/**"])
);
assert_eq!(result.input_config.negative_globs, globs(&[]));
}

#[test]
fn parent_auto_preserved_with_synthetic_globs() {
let (pkg, ws) = test_paths();
let parent = parent_config(true, &["tests/**"]);
let result = resolve_synthetic_cache_config(
ParentCacheConfig::Inherited(parent),
UserCacheConfig::with_config(EnabledCacheConfig {
env: None,
untracked_env: None,
input: Some(vec![UserInputEntry::Glob("config/**".into())]),
}),
&pkg,
&ws,
)
.unwrap()
.unwrap();
assert!(result.input_config.includes_auto);
assert_eq!(
result.input_config.positive_globs,
globs(&["packages/my-pkg/config/**", "tests/**"])
);
assert_eq!(result.input_config.negative_globs, globs(&[]));
}

#[test]
fn parent_cache_disabled_ignores_synthetic_input() {
let (pkg, ws) = test_paths();
let result = resolve_synthetic_cache_config(
ParentCacheConfig::Disabled,
UserCacheConfig::with_config(EnabledCacheConfig {
env: None,
untracked_env: None,
input: Some(vec![UserInputEntry::Glob("config/**".into())]),
}),
&pkg,
&ws,
)
.unwrap();
assert!(result.is_none());
}

#[test]
fn synthetic_disables_cache_despite_parent() {
let (pkg, ws) = test_paths();
let parent = parent_config(true, &["src/**"]);
let result = resolve_synthetic_cache_config(
ParentCacheConfig::Inherited(parent),
UserCacheConfig::disabled(),
&pkg,
&ws,
)
.unwrap();
assert!(result.is_none());
}

#[test]
fn synthetic_negative_globs_merged() {
let (pkg, ws) = test_paths();
let parent = parent_config(true, &["src/**"]);
let result = resolve_synthetic_cache_config(
ParentCacheConfig::Inherited(parent),
UserCacheConfig::with_config(EnabledCacheConfig {
env: None,
untracked_env: None,
input: Some(vec![UserInputEntry::Glob("!dist/**".into())]),
}),
&pkg,
&ws,
)
.unwrap()
.unwrap();
assert_eq!(result.input_config.positive_globs, globs(&["src/**"]));
assert_eq!(result.input_config.negative_globs, globs(&["packages/my-pkg/dist/**"]));
}
}
Loading