Skip to content

Commit 7ccb9af

Browse files
committed
Address Copilot review: fix doc key name and add deny_unknown_fields
- Fix `"inputs"` → `"input"` in docs/inputs.md to match actual config field name - Extract GlobWithBase and AutoInput into dedicated structs with `#[serde(deny_unknown_fields)]` so ambiguous objects like `{ "auto": true, "pattern": "...", "base": "workspace" }` are rejected - Add test for mixed auto+glob fields rejection - Regenerate TypeScript type definitions https://claude.ai/code/session_01KiaZHtCW4hCsdJyPuBNPnw
1 parent b494a32 commit 7ccb9af

4 files changed

Lines changed: 99 additions & 71 deletions

File tree

crates/vite_task_graph/run-config.ts

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
// This file is auto-generated by `cargo test`. Do not edit manually.
22

3+
export type AutoInput = {
4+
/**
5+
* Automatically track which files the task reads
6+
*/
7+
auto: boolean;
8+
};
9+
10+
export type GlobWithBase = {
11+
/**
12+
* The glob pattern (positive or negative starting with `!`)
13+
*/
14+
pattern: string;
15+
/**
16+
* The base directory for resolving the pattern
17+
*/
18+
base: InputBase;
19+
};
20+
321
export type InputBase = 'package' | 'workspace';
422

523
export type Task = {
@@ -39,25 +57,7 @@ export type Task = {
3957
* - `{auto: true}` enables automatic file tracking
4058
* - Negative patterns (e.g. `"!dist/**"`) exclude matched files
4159
*/
42-
input?: Array<
43-
| string
44-
| {
45-
/**
46-
* The glob pattern (positive or negative starting with `!`)
47-
*/
48-
pattern: string;
49-
/**
50-
* The base directory for resolving the pattern
51-
*/
52-
base: InputBase;
53-
}
54-
| {
55-
/**
56-
* Automatically track which files the task reads
57-
*/
58-
auto: boolean;
59-
}
60-
>;
60+
input?: Array<string | GlobWithBase | AutoInput>;
6161
}
6262
| {
6363
/**

crates/vite_task_graph/src/config/mod.rs

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ use monostate::MustBe;
77
use rustc_hash::FxHashSet;
88
use serde::Serialize;
99
pub use user::{
10-
EnabledCacheConfig, InputBase, ResolvedGlobalCacheConfig, UserCacheConfig,
11-
UserGlobalCacheConfig, UserInputEntry, UserInputsConfig, UserRunConfig, UserTaskConfig,
10+
AutoInput, EnabledCacheConfig, GlobWithBase, InputBase, ResolvedGlobalCacheConfig,
11+
UserCacheConfig, UserGlobalCacheConfig, UserInputEntry, UserInputsConfig, UserRunConfig,
12+
UserTaskConfig,
1213
};
1314
use vite_path::AbsolutePath;
1415
use vite_str::Str;
@@ -153,8 +154,8 @@ impl ResolvedInputConfig {
153154

154155
for entry in entries {
155156
match entry {
156-
UserInputEntry::Auto { auto: true } => includes_auto = true,
157-
UserInputEntry::Auto { auto: false } => {} // Ignore {auto: false}
157+
UserInputEntry::Auto(AutoInput { auto: true }) => includes_auto = true,
158+
UserInputEntry::Auto(AutoInput { auto: false }) => {} // Ignore {auto: false}
158159
UserInputEntry::Glob(pattern) => {
159160
Self::insert_glob(
160161
pattern.as_str(),
@@ -164,7 +165,7 @@ impl ResolvedInputConfig {
164165
&mut negative_globs,
165166
)?;
166167
}
167-
UserInputEntry::GlobWithBase { pattern, base } => {
168+
UserInputEntry::GlobWithBase(GlobWithBase { pattern, base }) => {
168169
let base_dir = match base {
169170
InputBase::Package => package_dir,
170171
InputBase::Workspace => workspace_root,
@@ -452,7 +453,7 @@ mod tests {
452453
#[test]
453454
fn test_resolved_input_config_auto_only() {
454455
let (pkg, ws) = test_paths();
455-
let user_inputs = vec![UserInputEntry::Auto { auto: true }];
456+
let user_inputs = vec![UserInputEntry::Auto(AutoInput { auto: true })];
456457
let config = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap();
457458
assert!(config.includes_auto);
458459
assert!(config.positive_globs.is_empty());
@@ -462,7 +463,7 @@ mod tests {
462463
#[test]
463464
fn test_resolved_input_config_auto_false_ignored() {
464465
let (pkg, ws) = test_paths();
465-
let user_inputs = vec![UserInputEntry::Auto { auto: false }];
466+
let user_inputs = vec![UserInputEntry::Auto(AutoInput { auto: false })];
466467
let config = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap();
467468
assert!(!config.includes_auto);
468469
assert!(config.positive_globs.is_empty());
@@ -506,7 +507,7 @@ mod tests {
506507
let (pkg, ws) = test_paths();
507508
let user_inputs = vec![
508509
UserInputEntry::Glob("package.json".into()),
509-
UserInputEntry::Auto { auto: true },
510+
UserInputEntry::Auto(AutoInput { auto: true }),
510511
UserInputEntry::Glob("!node_modules/**".into()),
511512
];
512513
let config = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap();
@@ -521,8 +522,10 @@ mod tests {
521522
fn test_resolved_input_config_globs_with_auto() {
522523
let (pkg, ws) = test_paths();
523524
// Globs with auto keeps inference enabled
524-
let user_inputs =
525-
vec![UserInputEntry::Glob("src/**/*.ts".into()), UserInputEntry::Auto { auto: true }];
525+
let user_inputs = vec![
526+
UserInputEntry::Glob("src/**/*.ts".into()),
527+
UserInputEntry::Auto(AutoInput { auto: true }),
528+
];
526529
let config = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap();
527530
assert!(config.includes_auto);
528531
}
@@ -552,10 +555,10 @@ mod tests {
552555
#[test]
553556
fn test_resolved_input_config_glob_with_workspace_base() {
554557
let (pkg, ws) = test_paths();
555-
let user_inputs = vec![UserInputEntry::GlobWithBase {
558+
let user_inputs = vec![UserInputEntry::GlobWithBase(GlobWithBase {
556559
pattern: "configs/tsconfig.json".into(),
557560
base: InputBase::Workspace,
558-
}];
561+
})];
559562
let config = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap();
560563
assert!(!config.includes_auto);
561564
assert_eq!(config.positive_globs.len(), 1);
@@ -570,10 +573,10 @@ mod tests {
570573
#[test]
571574
fn test_resolved_input_config_negative_glob_with_workspace_base() {
572575
let (pkg, ws) = test_paths();
573-
let user_inputs = vec![UserInputEntry::GlobWithBase {
576+
let user_inputs = vec![UserInputEntry::GlobWithBase(GlobWithBase {
574577
pattern: "!dist/**".into(),
575578
base: InputBase::Workspace,
576-
}];
579+
})];
577580
let config = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap();
578581
assert_eq!(config.negative_globs.len(), 1);
579582
assert!(
@@ -587,10 +590,10 @@ mod tests {
587590
fn test_resolved_input_config_glob_with_package_base_explicit() {
588591
let (pkg, ws) = test_paths();
589592
// Explicit "package" base should behave same as bare string
590-
let user_inputs = vec![UserInputEntry::GlobWithBase {
593+
let user_inputs = vec![UserInputEntry::GlobWithBase(GlobWithBase {
591594
pattern: "src/**/*.ts".into(),
592595
base: InputBase::Package,
593-
}];
596+
})];
594597
let config = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap();
595598
assert_eq!(config.positive_globs.len(), 1);
596599
assert!(
@@ -605,12 +608,15 @@ mod tests {
605608
let (pkg, ws) = test_paths();
606609
let user_inputs = vec![
607610
UserInputEntry::Glob("src/**".into()),
608-
UserInputEntry::GlobWithBase {
611+
UserInputEntry::GlobWithBase(GlobWithBase {
609612
pattern: "configs/**".into(),
610613
base: InputBase::Workspace,
611-
},
612-
UserInputEntry::Auto { auto: true },
613-
UserInputEntry::GlobWithBase { pattern: "!dist/**".into(), base: InputBase::Workspace },
614+
}),
615+
UserInputEntry::Auto(AutoInput { auto: true }),
616+
UserInputEntry::GlobWithBase(GlobWithBase {
617+
pattern: "!dist/**".into(),
618+
base: InputBase::Workspace,
619+
}),
614620
];
615621
let config = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap();
616622
assert!(config.includes_auto);

crates/vite_task_graph/src/config/user.rs

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,26 @@ pub enum InputBase {
2121
Workspace,
2222
}
2323

24+
/// Glob pattern with explicit base directory for resolution.
25+
#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
26+
#[cfg_attr(all(test, not(clippy)), derive(TS))]
27+
#[serde(deny_unknown_fields)]
28+
pub struct GlobWithBase {
29+
/// The glob pattern (positive or negative starting with `!`)
30+
pub pattern: Str,
31+
/// The base directory for resolving the pattern
32+
pub base: InputBase,
33+
}
34+
35+
/// Auto-inference directive for input tracking.
36+
#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
37+
#[cfg_attr(all(test, not(clippy)), derive(TS))]
38+
#[serde(deny_unknown_fields)]
39+
pub struct AutoInput {
40+
/// Automatically track which files the task reads
41+
pub auto: bool,
42+
}
43+
2444
/// A single input entry in the `input` array.
2545
///
2646
/// Inputs can be:
@@ -35,17 +55,9 @@ pub enum UserInputEntry {
3555
/// Glob pattern (positive or negative starting with `!`), resolved relative to package dir
3656
Glob(Str),
3757
/// Glob pattern with explicit base directory
38-
GlobWithBase {
39-
/// The glob pattern (positive or negative starting with `!`)
40-
pattern: Str,
41-
/// The base directory for resolving the pattern
42-
base: InputBase,
43-
},
58+
GlobWithBase(GlobWithBase),
4459
/// Auto-inference directive
45-
Auto {
46-
/// Automatically track which files the task reads
47-
auto: bool,
48-
},
60+
Auto(AutoInput),
4961
}
5062

5163
/// The inputs configuration for cache fingerprinting.
@@ -471,7 +483,7 @@ mod tests {
471483
"input": [{ "auto": true }]
472484
});
473485
let config: EnabledCacheConfig = serde_json::from_value(user_config_json).unwrap();
474-
assert_eq!(config.input, Some(vec![UserInputEntry::Auto { auto: true }]));
486+
assert_eq!(config.input, Some(vec![UserInputEntry::Auto(AutoInput { auto: true })]));
475487
}
476488

477489
#[test]
@@ -480,7 +492,7 @@ mod tests {
480492
"input": [{ "auto": false }]
481493
});
482494
let config: EnabledCacheConfig = serde_json::from_value(user_config_json).unwrap();
483-
assert_eq!(config.input, Some(vec![UserInputEntry::Auto { auto: false }]));
495+
assert_eq!(config.input, Some(vec![UserInputEntry::Auto(AutoInput { auto: false })]));
484496
}
485497

486498
#[test]
@@ -523,7 +535,7 @@ mod tests {
523535
config.input,
524536
Some(vec![
525537
UserInputEntry::Glob("package.json".into()),
526-
UserInputEntry::Auto { auto: true },
538+
UserInputEntry::Auto(AutoInput { auto: true }),
527539
UserInputEntry::Glob("!node_modules/**".into()),
528540
])
529541
);
@@ -537,10 +549,10 @@ mod tests {
537549
let config: EnabledCacheConfig = serde_json::from_value(user_config_json).unwrap();
538550
assert_eq!(
539551
config.input,
540-
Some(vec![UserInputEntry::GlobWithBase {
552+
Some(vec![UserInputEntry::GlobWithBase(GlobWithBase {
541553
pattern: "configs/tsconfig.json".into(),
542554
base: InputBase::Workspace,
543-
}])
555+
})])
544556
);
545557
}
546558

@@ -552,10 +564,10 @@ mod tests {
552564
let config: EnabledCacheConfig = serde_json::from_value(user_config_json).unwrap();
553565
assert_eq!(
554566
config.input,
555-
Some(vec![UserInputEntry::GlobWithBase {
567+
Some(vec![UserInputEntry::GlobWithBase(GlobWithBase {
556568
pattern: "src/**".into(),
557569
base: InputBase::Package,
558-
}])
570+
})])
559571
);
560572
}
561573

@@ -567,10 +579,10 @@ mod tests {
567579
let config: EnabledCacheConfig = serde_json::from_value(user_config_json).unwrap();
568580
assert_eq!(
569581
config.input,
570-
Some(vec![UserInputEntry::GlobWithBase {
582+
Some(vec![UserInputEntry::GlobWithBase(GlobWithBase {
571583
pattern: "!dist/**".into(),
572584
base: InputBase::Workspace,
573-
}])
585+
})])
574586
);
575587
}
576588

@@ -593,6 +605,16 @@ mod tests {
593605
assert!(result.is_err(), "invalid 'base' value should produce an error");
594606
}
595607

608+
#[test]
609+
fn test_input_mixed_auto_and_glob_with_base_error() {
610+
// An object with both "auto" and "pattern"/"base" should fail due to deny_unknown_fields
611+
let user_config_json = json!({
612+
"input": [{ "auto": true, "pattern": "src/**", "base": "workspace" }]
613+
});
614+
let result = serde_json::from_value::<EnabledCacheConfig>(user_config_json);
615+
assert!(result.is_err(), "mixing auto and pattern/base fields should produce an error");
616+
}
617+
596618
#[test]
597619
fn test_input_mixed_with_glob_base() {
598620
let user_config_json = json!({
@@ -608,11 +630,11 @@ mod tests {
608630
config.input,
609631
Some(vec![
610632
UserInputEntry::Glob("package.json".into()),
611-
UserInputEntry::GlobWithBase {
633+
UserInputEntry::GlobWithBase(GlobWithBase {
612634
pattern: "configs/**".into(),
613635
base: InputBase::Workspace,
614-
},
615-
UserInputEntry::Auto { auto: true },
636+
}),
637+
UserInputEntry::Auto(AutoInput { auto: true }),
616638
UserInputEntry::Glob("!node_modules/**".into()),
617639
])
618640
);

docs/inputs.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ By default, glob patterns are resolved relative to the package directory. Use th
6868

6969
```json
7070
{
71-
"inputs": [
71+
"input": [
7272
"src/**",
7373
{ "pattern": "configs/tsconfig.json", "base": "workspace" },
7474
{ "pattern": "!dist/**", "base": "workspace" }
@@ -159,15 +159,15 @@ The cache will only invalidate when the command itself or environment variables
159159

160160
## Behavior Summary
161161

162-
| Configuration | Auto-Inference | File Tracking |
163-
| --------------------------------------------------------------- | -------------- | ---------------------------------- |
164-
| `inputs` omitted | Enabled | Inferred files |
165-
| `inputs: [{ "auto": true }]` | Enabled | Inferred files |
166-
| `inputs: ["src/**"]` | Disabled | Matched files only |
167-
| `inputs: [{ "auto": true }, "!dist/**"]` | Enabled | Inferred files except `dist/` |
168-
| `inputs: ["pkg.json", { "auto": true }]` | Enabled | `pkg.json` + inferred files |
169-
| `inputs: [{ "pattern": "tsconfig.json", "base": "workspace" }]` | Disabled | Matched files (workspace-relative) |
170-
| `inputs: []` | Disabled | No files tracked |
162+
| Configuration | Auto-Inference | File Tracking |
163+
| -------------------------------------------------------------- | -------------- | ---------------------------------- |
164+
| `inputs` omitted | Enabled | Inferred files |
165+
| `inputs: [{ "auto": true }]` | Enabled | Inferred files |
166+
| `inputs: ["src/**"]` | Disabled | Matched files only |
167+
| `inputs: [{ "auto": true }, "!dist/**"]` | Enabled | Inferred files except `dist/` |
168+
| `inputs: ["pkg.json", { "auto": true }]` | Enabled | `pkg.json` + inferred files |
169+
| `input: [{ "pattern": "tsconfig.json", "base": "workspace" }]` | Disabled | Matched files (workspace-relative) |
170+
| `inputs: []` | Disabled | No files tracked |
171171

172172
## Important Notes
173173

0 commit comments

Comments
 (0)