Skip to content

Commit 431526c

Browse files
branchseerclaude
andcommitted
fix(static-config): spread invalidates previously-seen fields
`{ a: 1, ...x, b: 2 }` — the spread may override `a`, so `a` is now marked NonStatic. Fields declared after the spread (`b`) are unaffected since they win over any spread key. Unknown keys from the spread are still not added to the map. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 4592307 commit 431526c

File tree

1 file changed

+28
-5
lines changed
  • crates/vite_static_config/src

1 file changed

+28
-5
lines changed

crates/vite_static_config/src/lib.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -262,16 +262,22 @@ fn count_returns_in_stmt(stmt: &Statement<'_>) -> usize {
262262

263263
/// Extract fields from an object expression, converting each value to JSON.
264264
/// Fields whose values cannot be represented as pure JSON are recorded as
265-
/// [`FieldValue::NonStatic`]. Spread elements and computed properties
266-
/// are not representable so they are silently skipped (their keys are unknown).
265+
/// [`FieldValue::NonStatic`]. Computed properties are silently skipped (key unknown).
266+
///
267+
/// Spreads invalidate all fields declared before them: `{ a: 1, ...x, b: 2 }` yields
268+
/// `a: NonStatic` (spread may override it) and `b: Json(2)` (declared after, wins).
269+
/// Unknown keys introduced by the spread are not added to the map.
267270
fn extract_object_fields(
268271
obj: &oxc_ast::ast::ObjectExpression<'_>,
269272
) -> FxHashMap<Box<str>, FieldValue> {
270273
let mut map = FxHashMap::default();
271274

272275
for prop in &obj.properties {
273276
if prop.is_spread() {
274-
// Spread elements — keys are unknown at static analysis time
277+
// A spread may override any field declared before it.
278+
for value in map.values_mut() {
279+
*value = FieldValue::NonStatic;
280+
}
275281
continue;
276282
}
277283
let ObjectPropertyKind::ObjectProperty(prop) = prop else {
@@ -686,14 +692,31 @@ mod tests {
686692
}
687693

688694
#[test]
689-
fn spread_in_top_level_skipped() {
695+
fn spread_unknown_keys_not_in_map() {
696+
// Keys introduced by the spread are unknown — not added to the map.
697+
// Fields declared after the spread are safe (they win over the spread).
690698
let result = parse(
691699
r"
692700
const base = { x: 1 };
693701
export default { ...base, b: 'ok' }
694702
",
695703
);
696-
// Spread at top level — keys unknown, so not in map at all
704+
assert!(!result.contains_key("x"));
705+
assert_json(&result, "b", serde_json::json!("ok"));
706+
}
707+
708+
#[test]
709+
fn spread_invalidates_previous_fields() {
710+
// Fields declared before a spread become NonStatic — the spread may override them.
711+
// Fields declared after the spread are unaffected.
712+
let result = parse(
713+
r"
714+
const base = { x: 1 };
715+
export default { a: 1, run: { cacheScripts: true }, ...base, b: 'ok' }
716+
",
717+
);
718+
assert_non_static(&result, "a");
719+
assert_non_static(&result, "run");
697720
assert!(!result.contains_key("x"));
698721
assert_json(&result, "b", serde_json::json!("ok"));
699722
}

0 commit comments

Comments
 (0)