@@ -20,7 +20,8 @@ use vite_str::Str;
2020use vite_task_graph:: {
2121 TaskNodeIndex , TaskSource ,
2222 config:: {
23- CacheConfig , ResolvedGlobalCacheConfig , ResolvedTaskOptions ,
23+ CacheConfig , EnabledCacheConfig , ResolvedGlobalCacheConfig , ResolvedInputConfig ,
24+ ResolvedTaskOptions ,
2425 user:: { UserCacheConfig , UserTaskOptions } ,
2526 } ,
2627 query:: TaskQuery ,
@@ -281,6 +282,7 @@ async fn plan_task_as_execution_node(
281282 synthetic_plan_request,
282283 Some ( task_execution_cache_key) ,
283284 & cwd,
285+ package_path,
284286 parent_cache_config,
285287 ) ?;
286288 ExecutionItemKind :: Leaf ( LeafExecutionKind :: Spawn ( spawn_execution) )
@@ -433,7 +435,7 @@ pub enum ParentCacheConfig {
433435fn resolve_synthetic_cache_config (
434436 parent : ParentCacheConfig ,
435437 synthetic_cache_config : UserCacheConfig ,
436- cwd : & Arc < AbsolutePath > ,
438+ package_dir : & AbsolutePath ,
437439 workspace_path : & AbsolutePath ,
438440) -> Result < Option < CacheConfig > , Error > {
439441 match parent {
@@ -445,7 +447,7 @@ fn resolve_synthetic_cache_config(
445447 cwd_relative_to_package : None ,
446448 depends_on : None ,
447449 } ,
448- cwd ,
450+ & package_dir . into ( ) ,
449451 workspace_path,
450452 )
451453 . map_err ( Error :: ResolveTaskConfig ) ?
@@ -458,12 +460,33 @@ fn resolve_synthetic_cache_config(
458460 Ok ( match synthetic_cache_config {
459461 UserCacheConfig :: Disabled { .. } => Option :: None ,
460462 UserCacheConfig :: Enabled { enabled_cache_config, .. } => {
461- if let Some ( extra_envs) = enabled_cache_config. env {
462- parent_config. env_config . fingerprinted_envs . extend ( extra_envs. into_vec ( ) ) ;
463- }
464- if let Some ( extra_pts) = enabled_cache_config. untracked_env {
465- parent_config. env_config . untracked_env . extend ( extra_pts) ;
463+ let EnabledCacheConfig { env, untracked_env, input } = enabled_cache_config;
464+ parent_config. env_config . fingerprinted_envs . extend ( env. unwrap_or_default ( ) ) ;
465+ parent_config
466+ . env_config
467+ . untracked_env
468+ . extend ( untracked_env. unwrap_or_default ( ) ) ;
469+
470+ if let Some ( input) = input {
471+ let synthetic_input = ResolvedInputConfig :: from_user_config (
472+ Some ( & input) ,
473+ package_dir,
474+ workspace_path,
475+ )
476+ . map_err ( Error :: ResolveTaskConfig ) ?;
477+ // Merge globs but preserve the parent's includes_auto —
478+ // the user's explicit choice takes precedence
479+ // over the synthetic handler's preference.
480+ parent_config
481+ . input_config
482+ . positive_globs
483+ . extend ( synthetic_input. positive_globs ) ;
484+ parent_config
485+ . input_config
486+ . negative_globs
487+ . extend ( synthetic_input. negative_globs ) ;
466488 }
489+
467490 Some ( parent_config)
468491 }
469492 } )
@@ -478,13 +501,18 @@ pub fn plan_synthetic_request(
478501 synthetic_plan_request : SyntheticPlanRequest ,
479502 execution_cache_key : Option < ExecutionCacheKey > ,
480503 cwd : & Arc < AbsolutePath > ,
504+ package_dir : & AbsolutePath ,
481505 parent_cache_config : ParentCacheConfig ,
482506) -> Result < SpawnExecution , Error > {
483507 let SyntheticPlanRequest { program, args, cache_config, envs } = synthetic_plan_request;
484508
485509 let program_path = which ( & program, & envs, cwd) ?;
486- let resolved_cache_config =
487- resolve_synthetic_cache_config ( parent_cache_config, cache_config, cwd, workspace_path) ?;
510+ let resolved_cache_config = resolve_synthetic_cache_config (
511+ parent_cache_config,
512+ cache_config,
513+ package_dir,
514+ workspace_path,
515+ ) ?;
488516 let resolved_options =
489517 ResolvedTaskOptions { cwd : Arc :: clone ( cwd) , cache_config : resolved_cache_config } ;
490518
@@ -749,3 +777,219 @@ pub async fn plan_query_request(
749777 Error :: CycleDependencyDetected ( displays)
750778 } )
751779}
780+
781+ #[ cfg( test) ]
782+ mod tests {
783+ use std:: collections:: BTreeSet ;
784+
785+ use rustc_hash:: FxHashSet ;
786+ use vite_path:: AbsolutePathBuf ;
787+ use vite_str:: Str ;
788+ use vite_task_graph:: config:: {
789+ CacheConfig , EnabledCacheConfig , EnvConfig , ResolvedInputConfig ,
790+ user:: { UserCacheConfig , UserInputEntry } ,
791+ } ;
792+
793+ use super :: { ParentCacheConfig , resolve_synthetic_cache_config} ;
794+
795+ fn test_paths ( ) -> ( AbsolutePathBuf , AbsolutePathBuf ) {
796+ if cfg ! ( windows) {
797+ (
798+ AbsolutePathBuf :: new ( "C:\\ workspace\\ packages\\ my-pkg" . into ( ) ) . unwrap ( ) ,
799+ AbsolutePathBuf :: new ( "C:\\ workspace" . into ( ) ) . unwrap ( ) ,
800+ )
801+ } else {
802+ (
803+ AbsolutePathBuf :: new ( "/workspace/packages/my-pkg" . into ( ) ) . unwrap ( ) ,
804+ AbsolutePathBuf :: new ( "/workspace" . into ( ) ) . unwrap ( ) ,
805+ )
806+ }
807+ }
808+
809+ fn parent_config ( includes_auto : bool , positive_globs : & [ & str ] ) -> CacheConfig {
810+ CacheConfig {
811+ env_config : EnvConfig {
812+ fingerprinted_envs : FxHashSet :: default ( ) ,
813+ untracked_env : FxHashSet :: default ( ) ,
814+ } ,
815+ input_config : ResolvedInputConfig {
816+ includes_auto,
817+ positive_globs : positive_globs. iter ( ) . map ( |s| Str :: from ( * s) ) . collect ( ) ,
818+ negative_globs : BTreeSet :: new ( ) ,
819+ } ,
820+ }
821+ }
822+
823+ fn globs ( items : & [ & str ] ) -> BTreeSet < Str > {
824+ items. iter ( ) . map ( |s| Str :: from ( * s) ) . collect ( )
825+ }
826+
827+ #[ test]
828+ fn synthetic_input_none_preserves_parent ( ) {
829+ let ( pkg, ws) = test_paths ( ) ;
830+ let parent = parent_config ( true , & [ "src/**" ] ) ;
831+ let result = resolve_synthetic_cache_config (
832+ ParentCacheConfig :: Inherited ( parent) ,
833+ UserCacheConfig :: with_config ( EnabledCacheConfig {
834+ env : None ,
835+ untracked_env : None ,
836+ input : None ,
837+ } ) ,
838+ & pkg,
839+ & ws,
840+ )
841+ . unwrap ( )
842+ . unwrap ( ) ;
843+ assert ! ( result. input_config. includes_auto) ;
844+ assert_eq ! ( result. input_config. positive_globs, globs( & [ "src/**" ] ) ) ;
845+ assert_eq ! ( result. input_config. negative_globs, globs( & [ ] ) ) ;
846+ }
847+
848+ #[ test]
849+ fn synthetic_globs_merged_into_parent_default_auto ( ) {
850+ let ( pkg, ws) = test_paths ( ) ;
851+ let parent = parent_config ( true , & [ ] ) ;
852+ let result = resolve_synthetic_cache_config (
853+ ParentCacheConfig :: Inherited ( parent) ,
854+ UserCacheConfig :: with_config ( EnabledCacheConfig {
855+ env : None ,
856+ untracked_env : None ,
857+ input : Some ( vec ! [ UserInputEntry :: Glob ( "config/**" . into( ) ) ] ) ,
858+ } ) ,
859+ & pkg,
860+ & ws,
861+ )
862+ . unwrap ( )
863+ . unwrap ( ) ;
864+ assert ! ( result. input_config. includes_auto) ;
865+ assert_eq ! ( result. input_config. positive_globs, globs( & [ "packages/my-pkg/config/**" ] ) ) ;
866+ assert_eq ! ( result. input_config. negative_globs, globs( & [ ] ) ) ;
867+ }
868+
869+ #[ test]
870+ fn synthetic_globs_merged_into_parent_explicit_input ( ) {
871+ let ( pkg, ws) = test_paths ( ) ;
872+ let parent = parent_config ( false , & [ "src/**" ] ) ;
873+ let result = resolve_synthetic_cache_config (
874+ ParentCacheConfig :: Inherited ( parent) ,
875+ UserCacheConfig :: with_config ( EnabledCacheConfig {
876+ env : None ,
877+ untracked_env : None ,
878+ input : Some ( vec ! [ UserInputEntry :: Glob ( "config/**" . into( ) ) ] ) ,
879+ } ) ,
880+ & pkg,
881+ & ws,
882+ )
883+ . unwrap ( )
884+ . unwrap ( ) ;
885+ assert ! ( !result. input_config. includes_auto) ;
886+ assert_eq ! (
887+ result. input_config. positive_globs,
888+ globs( & [ "packages/my-pkg/config/**" , "src/**" ] )
889+ ) ;
890+ assert_eq ! ( result. input_config. negative_globs, globs( & [ ] ) ) ;
891+ }
892+
893+ #[ test]
894+ fn synthetic_auto_ignored_when_parent_has_explicit_input ( ) {
895+ let ( pkg, ws) = test_paths ( ) ;
896+ let parent = parent_config ( false , & [ "src/**" ] ) ;
897+ let result = resolve_synthetic_cache_config (
898+ ParentCacheConfig :: Inherited ( parent) ,
899+ UserCacheConfig :: with_config ( EnabledCacheConfig {
900+ env : None ,
901+ untracked_env : None ,
902+ input : Some ( vec ! [
903+ UserInputEntry :: Glob ( "config/**" . into( ) ) ,
904+ UserInputEntry :: Auto ( vite_task_graph:: config:: user:: AutoInput { auto: true } ) ,
905+ ] ) ,
906+ } ) ,
907+ & pkg,
908+ & ws,
909+ )
910+ . unwrap ( )
911+ . unwrap ( ) ;
912+ // User's explicit choice (no auto) takes precedence
913+ assert ! ( !result. input_config. includes_auto) ;
914+ assert_eq ! (
915+ result. input_config. positive_globs,
916+ globs( & [ "packages/my-pkg/config/**" , "src/**" ] )
917+ ) ;
918+ assert_eq ! ( result. input_config. negative_globs, globs( & [ ] ) ) ;
919+ }
920+
921+ #[ test]
922+ fn parent_auto_preserved_with_synthetic_globs ( ) {
923+ let ( pkg, ws) = test_paths ( ) ;
924+ let parent = parent_config ( true , & [ "tests/**" ] ) ;
925+ let result = resolve_synthetic_cache_config (
926+ ParentCacheConfig :: Inherited ( parent) ,
927+ UserCacheConfig :: with_config ( EnabledCacheConfig {
928+ env : None ,
929+ untracked_env : None ,
930+ input : Some ( vec ! [ UserInputEntry :: Glob ( "config/**" . into( ) ) ] ) ,
931+ } ) ,
932+ & pkg,
933+ & ws,
934+ )
935+ . unwrap ( )
936+ . unwrap ( ) ;
937+ assert ! ( result. input_config. includes_auto) ;
938+ assert_eq ! (
939+ result. input_config. positive_globs,
940+ globs( & [ "packages/my-pkg/config/**" , "tests/**" ] )
941+ ) ;
942+ assert_eq ! ( result. input_config. negative_globs, globs( & [ ] ) ) ;
943+ }
944+
945+ #[ test]
946+ fn parent_cache_disabled_ignores_synthetic_input ( ) {
947+ let ( pkg, ws) = test_paths ( ) ;
948+ let result = resolve_synthetic_cache_config (
949+ ParentCacheConfig :: Disabled ,
950+ UserCacheConfig :: with_config ( EnabledCacheConfig {
951+ env : None ,
952+ untracked_env : None ,
953+ input : Some ( vec ! [ UserInputEntry :: Glob ( "config/**" . into( ) ) ] ) ,
954+ } ) ,
955+ & pkg,
956+ & ws,
957+ )
958+ . unwrap ( ) ;
959+ assert ! ( result. is_none( ) ) ;
960+ }
961+
962+ #[ test]
963+ fn synthetic_disables_cache_despite_parent ( ) {
964+ let ( pkg, ws) = test_paths ( ) ;
965+ let parent = parent_config ( true , & [ "src/**" ] ) ;
966+ let result = resolve_synthetic_cache_config (
967+ ParentCacheConfig :: Inherited ( parent) ,
968+ UserCacheConfig :: disabled ( ) ,
969+ & pkg,
970+ & ws,
971+ )
972+ . unwrap ( ) ;
973+ assert ! ( result. is_none( ) ) ;
974+ }
975+
976+ #[ test]
977+ fn synthetic_negative_globs_merged ( ) {
978+ let ( pkg, ws) = test_paths ( ) ;
979+ let parent = parent_config ( true , & [ "src/**" ] ) ;
980+ let result = resolve_synthetic_cache_config (
981+ ParentCacheConfig :: Inherited ( parent) ,
982+ UserCacheConfig :: with_config ( EnabledCacheConfig {
983+ env : None ,
984+ untracked_env : None ,
985+ input : Some ( vec ! [ UserInputEntry :: Glob ( "!dist/**" . into( ) ) ] ) ,
986+ } ) ,
987+ & pkg,
988+ & ws,
989+ )
990+ . unwrap ( )
991+ . unwrap ( ) ;
992+ assert_eq ! ( result. input_config. positive_globs, globs( & [ "src/**" ] ) ) ;
993+ assert_eq ! ( result. input_config. negative_globs, globs( & [ "packages/my-pkg/dist/**" ] ) ) ;
994+ }
995+ }
0 commit comments