Skip to content

Commit 9cce0f4

Browse files
branchseerclaude
andcommitted
fix(static-config): computed keys invalidate previously-seen fields
`{ a: 1, [key]: 2, b: 3 }` — `[key]` could resolve to `'a'` and override it, so `a` is now marked NonStatic. Same logic as spreads. Fields after the computed key are unaffected (they explicitly win). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 3d61f61 commit 9cce0f4

File tree

1 file changed

+39
-10
lines changed
  • crates/vite_static_config/src

1 file changed

+39
-10
lines changed

crates/vite_static_config/src/lib.rs

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -262,30 +262,43 @@ 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`]. Computed properties are silently skipped (key unknown).
265+
/// [`FieldValue::NonStatic`].
266266
///
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.
267+
/// Both spreads and computed-key properties invalidate all fields declared before
268+
/// them, because either may resolve to a key that overrides an earlier entry:
269+
///
270+
/// ```js
271+
/// { a: 1, ...x, b: 2 } // a → NonStatic, b → Json(2)
272+
/// { a: 1, [key]: 2, b: 3 } // a → NonStatic, b → Json(3)
273+
/// ```
274+
///
275+
/// Fields declared after such entries are safe (they explicitly override whatever
276+
/// the spread/computed-key produced). Unknown keys are never added to the map.
270277
fn extract_object_fields(
271278
obj: &oxc_ast::ast::ObjectExpression<'_>,
272279
) -> FxHashMap<Box<str>, FieldValue> {
273280
let mut map = FxHashMap::default();
274281

282+
/// Mark every field accumulated so far as NonStatic.
283+
fn invalidate_previous(map: &mut FxHashMap<Box<str>, FieldValue>) {
284+
for value in map.values_mut() {
285+
*value = FieldValue::NonStatic;
286+
}
287+
}
288+
275289
for prop in &obj.properties {
276290
if prop.is_spread() {
277291
// A spread may override any field declared before it.
278-
for value in map.values_mut() {
279-
*value = FieldValue::NonStatic;
280-
}
292+
invalidate_previous(&mut map);
281293
continue;
282294
}
283295
let ObjectPropertyKind::ObjectProperty(prop) = prop else {
284296
continue;
285297
};
286298

287299
let Some(key) = prop.key.static_name() else {
288-
// Computed properties — keys are unknown at static analysis time
300+
// A computed key may equal any previously-seen key name.
301+
invalidate_previous(&mut map);
289302
continue;
290303
};
291304

@@ -722,18 +735,34 @@ mod tests {
722735
}
723736

724737
#[test]
725-
fn computed_properties_skipped() {
738+
fn computed_key_unknown_not_in_map() {
739+
// The computed key's resolved name is unknown — not added to the map.
740+
// Fields declared after it are safe (they explicitly win).
726741
let result = parse(
727742
r"
728743
const key = 'dynamic';
729744
export default { [key]: 'value', plain: 'ok' }
730745
",
731746
);
732-
// Computed key — not in map at all (key is unknown)
733747
assert!(!result.contains_key("dynamic"));
734748
assert_json(&result, "plain", serde_json::json!("ok"));
735749
}
736750

751+
#[test]
752+
fn computed_key_invalidates_previous_fields() {
753+
// A computed key may resolve to any previously-seen name and override it.
754+
let result = parse(
755+
r"
756+
const key = 'run';
757+
export default { a: 1, run: { cacheScripts: true }, [key]: 'override', b: 2 }
758+
",
759+
);
760+
assert_non_static(&result, "a");
761+
assert_non_static(&result, "run");
762+
assert!(!result.contains_key("dynamic"));
763+
assert_json(&result, "b", serde_json::json!(2));
764+
}
765+
737766
#[test]
738767
fn non_static_array_with_spread() {
739768
let result = parse(

0 commit comments

Comments
 (0)