@samitouri / QOS-React-2 / commits / b49e04151e

[rust-compiler] Carry uninspected AST subtrees as raw JSON text (#36730)

Stacked on #36729 (upstream rejects cross-fork base branches, so this targets main as a draft; the first commit belongs to the parent PR. Review the last three commits. Will rebase and mark ready when #36729 lands.) Unmodeled AST subtrees (type annotations, class bodies, unknown statements) were stored as `serde_json::Value` trees: every node allocated through a `Map<String, Value>`, and pass-through subtrees were repeatedly traversed by code that never looks inside them. They are now `RawNode`, a newtype over `Box<RawValue>` holding the original JSON text verbatim. Design notes, since two obvious alternatives fail: - Bare `RawValue` fields break under `#[serde(tag = "type")]` enums: internally-tagged deserialization buffers content into serde's private `Content` tree, which `RawValue` cannot read from. `RawNode::deserialize` instead streams whatever deserializer it is handed through `serde_transcode` into a fresh JSON string, which works behind tagged enums, `flatten`, and `from_value` alike. - Default-limit reparses break deep ASTs: internal `RawNode` reparse sites use `from_json_str_unbounded` (disables serde_json's 128-level recursion limit, matching how the top-level parse is configured); regression-tested with a 400-deep statement chain. `parse_value` fails loudly on malformed text rather than masking corruption with `Value::Null`; RawNode holds valid JSON by construction. Size-neutral in the shipped binary; the win is structural (no speculative `Value` trees on the hot path, pass-through subtrees stay untouched text). Verified on this exact tree: cargo workspace tests, both snap channels 1804/1804. --------- Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>

lauren committed Jun 10, 2026 at 20:19 UTC b49e04151e1c2cd34f4612e43c5e38edf5da56e2
17 files changed +437 -278
compiler/Cargo.lock
+10
@@ -1199,6 +1199,7 @@ version = "0.1.0"
1199 dependencies = [
1200 "indexmap",
1201 "serde",
1202 + "serde-transcode",
1203 "serde_json",
1204 "similar",
1205 "walkdir",
@@ -1469,6 +1470,15 @@ dependencies = [
1470 "serde_derive",
1471 ]
1472
1473 +[[package]]
1474 +name = "serde-transcode"
1475 +version = "1.1.1"
1476 +source = "registry+https://github.com/rust-lang/crates.io-index"
1477 +checksum = "590c0e25c2a5bb6e85bf5c1bce768ceb86b316e7a01bdf07d2cb4ec2271990e2"
1478 +dependencies = [
1479 + "serde",
1480 +]
1481 +
1482 [[package]]
1483 name = "serde_core"
1484 version = "1.0.228"
compiler/crates/react_compiler/src/entrypoint/program.rs
+10 -8
@@ -790,7 +790,7 @@ fn calls_hooks_or_creates_jsx_in_class_body(
790 ) -> bool {
791 body.body
792 .iter()
793 - .any(|member| calls_hooks_or_creates_jsx_in_json(member))
793 + .any(|member| calls_hooks_or_creates_jsx_in_json(&member.parse_value()))
794 }
795
796 fn calls_hooks_or_creates_jsx_in_json(value: &serde_json::Value) -> bool {
@@ -930,11 +930,11 @@ fn calls_hooks_or_creates_jsx_in_pattern(pattern: &PatternLike) -> bool {
930 /// Returns false for primitive type annotations that indicate this is NOT a component.
931 fn is_valid_props_annotation(param: &PatternLike) -> bool {
932 let type_annotation = match param {
933 - PatternLike::Identifier(id) => id.type_annotation.as_deref(),
934 - PatternLike::ObjectPattern(op) => op.type_annotation.as_deref(),
935 - PatternLike::ArrayPattern(ap) => ap.type_annotation.as_deref(),
936 - PatternLike::AssignmentPattern(ap) => ap.type_annotation.as_deref(),
937 - PatternLike::RestElement(re) => re.type_annotation.as_deref(),
933 + PatternLike::Identifier(id) => id.type_annotation.as_ref(),
934 + PatternLike::ObjectPattern(op) => op.type_annotation.as_ref(),
935 + PatternLike::ArrayPattern(ap) => ap.type_annotation.as_ref(),
936 + PatternLike::AssignmentPattern(ap) => ap.type_annotation.as_ref(),
937 + PatternLike::RestElement(re) => re.type_annotation.as_ref(),
938 PatternLike::MemberExpression(_)
939 | PatternLike::TSAsExpression(_)
940 | PatternLike::TSSatisfiesExpression(_)
@@ -943,7 +943,7 @@ fn is_valid_props_annotation(param: &PatternLike) -> bool {
943 | PatternLike::TypeCastExpression(_) => None,
944 };
945 let annot = match type_annotation {
946 - Some(val) => val,
946 + Some(raw) => raw.parse_value(),
947 None => return true, // No annotation = valid
948 };
949 let annot_type = match annot.get("type").and_then(|v| v.as_str()) {
@@ -2181,7 +2181,9 @@ fn stmt_references_identifier_at_top_level(stmt: &Statement, name: &str) -> bool
2181 // Unmodeled statements (e.g. `export = X`) can reference top-level
2182 // bindings; scan the raw node for a matching Identifier so the
2183 // gating reference-before-declaration analysis does not miss them.
2184 - Statement::Unknown(unknown) => raw_node_references_identifier(unknown.raw(), name),
2184 + Statement::Unknown(unknown) => {
2185 + raw_node_references_identifier(&unknown.raw().parse_value(), name)
2186 + }
2187 _ => false,
2188 }
2189 }
compiler/crates/react_compiler_ast/Cargo.toml
+2 -1
@@ -5,7 +5,8 @@ edition = "2024"
5
6 [dependencies]
7 serde = { version = "1", features = ["derive"] }
8 -serde_json = "1"
8 +serde_json = { version = "1", features = ["raw_value", "unbounded_depth"] }
9 +serde-transcode = "1"
10 indexmap = { version = "2", features = ["serde"] }
11
12 [dev-dependencies]
compiler/crates/react_compiler_ast/src/common.rs
+89 -6
@@ -1,18 +1,101 @@
1 use serde::Deserialize;
2 use serde::Serialize;
3
4 +/// An AST subtree the compiler does not model with typed nodes (type
5 +/// annotations, class bodies, parser extras). Wraps JSON text: serialization
6 +/// is verbatim pass-through and deserialization streams the subtree into text
7 +/// without retaining a `serde_json::Value` tree. Consumers that inspect these
8 +/// subtrees parse on demand via [`RawNode::parse_value`]; paths that do so
9 +/// repeatedly per traversal pay a parse each time, so cache the parsed Value
10 +/// at the call site if it shows up in profiles.
11 +///
12 +/// Deserialize is hand-implemented with a transcode rather than capturing a
13 +/// `RawValue` directly: most nodes sit under `#[serde(tag = "type")]` enums,
14 +/// whose content buffering breaks `RawValue`'s text-borrowing capture.
15 +#[derive(Debug, Clone, Serialize)]
16 +#[serde(transparent)]
17 +pub struct RawNode(pub Box<serde_json::value::RawValue>);
18 +
19 +impl<'de> serde::Deserialize<'de> for RawNode {
20 + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
21 + where
22 + D: serde::Deserializer<'de>,
23 + {
24 + let mut buf = Vec::new();
25 + let mut ser = serde_json::Serializer::new(&mut buf);
26 + serde_transcode::transcode(deserializer, &mut ser).map_err(serde::de::Error::custom)?;
27 + let text = String::from_utf8(buf).map_err(serde::de::Error::custom)?;
28 + serde_json::value::RawValue::from_string(text)
29 + .map(RawNode)
30 + .map_err(serde::de::Error::custom)
31 + }
32 +}
33 +
34 +impl RawNode {
35 + pub fn from_value(value: &serde_json::Value) -> Self {
36 + RawNode(
37 + serde_json::value::RawValue::from_string(value.to_string())
38 + .expect("serde_json::Value always serializes to valid JSON"),
39 + )
40 + }
41 +
42 + pub fn null() -> Self {
43 + RawNode(
44 + serde_json::value::RawValue::from_string("null".to_string())
45 + .expect("null is valid JSON"),
46 + )
47 + }
48 +
49 + /// The raw JSON text of this subtree.
50 + pub fn get(&self) -> &str {
51 + self.0.get()
52 + }
53 +
54 + /// Parse the subtree into a `serde_json::Value` for structural inspection.
55 + /// RawNode text is valid JSON by construction, so failure here means a
56 + /// broken invariant, not bad input; fail loudly rather than degrade.
57 + pub fn parse_value(&self) -> serde_json::Value {
58 + from_json_str_unbounded(self.0.get())
59 + .expect("RawNode holds valid JSON by construction")
60 + }
61 +
62 + /// The node's `"type"` field, without parsing the whole subtree into a Value.
63 + pub fn type_name(&self) -> Option<String> {
64 + #[derive(Deserialize)]
65 + struct TypeProbe {
66 + #[serde(rename = "type")]
67 + type_name: Option<String>,
68 + }
69 + from_json_str_unbounded::<TypeProbe>(self.0.get())
70 + .ok()
71 + .and_then(|p| p.type_name)
72 + }
73 +}
74 +
75 +/// Parse JSON text with serde_json's recursion limit disabled. Every internal
76 +/// reparse of [`RawNode`] text must go through this: the napi entrypoint
77 +/// deserializes arbitrarily deep ASTs with the limit disabled (on a 64MB
78 +/// stack), and the tolerant statement path's reparses must not quietly
79 +/// reintroduce the default limit.
80 +pub fn from_json_str_unbounded<'de, T: serde::Deserialize<'de>>(
81 + s: &'de str,
82 +) -> serde_json::Result<T> {
83 + let mut deserializer = serde_json::Deserializer::from_str(s);
84 + deserializer.disable_recursion_limit();
85 + T::deserialize(&mut deserializer)
86 +}
87 +
88 /// Custom deserializer that distinguishes "field absent" from "field: null".
89 /// - JSON field absent → `None` (via `#[serde(default)]`)
6 -/// - JSON field `null` → `Some(Value::Null)`
7 -/// - JSON field with value → `Some(value)`
90 +/// - JSON field `null` → `Some(RawNode("null"))`
91 +/// - JSON field with value → `Some(raw value)`
92 ///
93 /// Use with `#[serde(default, skip_serializing_if = "Option::is_none", deserialize_with = "nullable_value")]`
10 -pub fn nullable_value<'de, D>(deserializer: D) -> Result<Option<Box<serde_json::Value>>, D::Error>
94 +pub fn nullable_value<'de, D>(deserializer: D) -> Result<Option<RawNode>, D::Error>
95 where
96 D: serde::Deserializer<'de>,
97 {
14 - let value = serde_json::Value::deserialize(deserializer)?;
15 - Ok(Some(Box::new(value)))
98 + RawNode::deserialize(deserializer).map(Some)
99 }
100
101 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -75,7 +158,7 @@ pub struct BaseNode {
158 #[serde(default, skip_serializing_if = "Option::is_none")]
159 pub range: Option<(u32, u32)>,
160 #[serde(default, skip_serializing_if = "Option::is_none")]
78 - pub extra: Option<serde_json::Value>,
161 + pub extra: Option<RawNode>,
162 #[serde(
163 default,
164 skip_serializing_if = "Option::is_none",
compiler/crates/react_compiler_ast/src/declarations.rs
+45 -44
@@ -2,6 +2,7 @@ use serde::Deserialize;
2 use serde::Serialize;
3
4 use crate::common::BaseNode;
5 +use crate::common::RawNode;
6 use crate::expressions::Expression;
7 use crate::expressions::Identifier;
8 use crate::literals::StringLiteral;
@@ -197,20 +198,20 @@ pub struct ExportAllDeclaration {
198 pub attributes: Option<Vec<ImportAttribute>>,
199 }
200
200 -// TypeScript declarations (pass-through via serde_json::Value for bodies)
201 +// TypeScript declarations (pass-through via RawNode for bodies)
202 #[derive(Debug, Clone, Serialize, Deserialize)]
203 pub struct TSTypeAliasDeclaration {
204 #[serde(flatten)]
205 pub base: BaseNode,
206 pub id: Identifier,
207 #[serde(rename = "typeAnnotation")]
207 - pub type_annotation: Box<serde_json::Value>,
208 + pub type_annotation: RawNode,
209 #[serde(
210 default,
211 skip_serializing_if = "Option::is_none",
212 rename = "typeParameters"
213 )]
213 - pub type_parameters: Option<Box<serde_json::Value>>,
214 + pub type_parameters: Option<RawNode>,
215 #[serde(default, skip_serializing_if = "Option::is_none")]
216 pub declare: Option<bool>,
217 }
@@ -220,15 +221,15 @@ pub struct TSInterfaceDeclaration {
221 #[serde(flatten)]
222 pub base: BaseNode,
223 pub id: Identifier,
223 - pub body: Box<serde_json::Value>,
224 + pub body: RawNode,
225 #[serde(
226 default,
227 skip_serializing_if = "Option::is_none",
228 rename = "typeParameters"
229 )]
229 - pub type_parameters: Option<Box<serde_json::Value>>,
230 + pub type_parameters: Option<RawNode>,
231 #[serde(default, skip_serializing_if = "Option::is_none")]
231 - pub extends: Option<Vec<serde_json::Value>>,
232 + pub extends: Option<Vec<RawNode>>,
233 #[serde(default, skip_serializing_if = "Option::is_none")]
234 pub declare: Option<bool>,
235 }
@@ -238,7 +239,7 @@ pub struct TSEnumDeclaration {
239 #[serde(flatten)]
240 pub base: BaseNode,
241 pub id: Identifier,
241 - pub members: Vec<serde_json::Value>,
242 + pub members: Vec<RawNode>,
243 #[serde(default, skip_serializing_if = "Option::is_none")]
244 pub declare: Option<bool>,
245 #[serde(default, skip_serializing_if = "Option::is_none", rename = "const")]
@@ -249,8 +250,8 @@ pub struct TSEnumDeclaration {
250 pub struct TSModuleDeclaration {
251 #[serde(flatten)]
252 pub base: BaseNode,
252 - pub id: Box<serde_json::Value>,
253 - pub body: Box<serde_json::Value>,
253 + pub id: RawNode,
254 + pub body: RawNode,
255 #[serde(default, skip_serializing_if = "Option::is_none")]
256 pub declare: Option<bool>,
257 #[serde(default, skip_serializing_if = "Option::is_none")]
@@ -262,7 +263,7 @@ pub struct TSDeclareFunction {
263 #[serde(flatten)]
264 pub base: BaseNode,
265 pub id: Option<Identifier>,
265 - pub params: Vec<serde_json::Value>,
266 + pub params: Vec<RawNode>,
267 #[serde(default, skip_serializing_if = "Option::is_none", rename = "async")]
268 pub is_async: Option<bool>,
269 #[serde(default, skip_serializing_if = "Option::is_none")]
@@ -274,13 +275,13 @@ pub struct TSDeclareFunction {
275 skip_serializing_if = "Option::is_none",
276 rename = "returnType"
277 )]
277 - pub return_type: Option<Box<serde_json::Value>>,
278 + pub return_type: Option<RawNode>,
279 #[serde(
280 default,
281 skip_serializing_if = "Option::is_none",
282 rename = "typeParameters"
283 )]
283 - pub type_parameters: Option<Box<serde_json::Value>>,
284 + pub type_parameters: Option<RawNode>,
285 }
286
287 // Flow declarations (pass-through)
@@ -289,9 +290,9 @@ pub struct TypeAlias {
290 #[serde(flatten)]
291 pub base: BaseNode,
292 pub id: Identifier,
292 - pub right: Box<serde_json::Value>,
293 + pub right: RawNode,
294 #[serde(default, rename = "typeParameters")]
294 - pub type_parameters: Option<Box<serde_json::Value>>,
295 + pub type_parameters: Option<RawNode>,
296 }
297
298 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -300,14 +301,14 @@ pub struct OpaqueType {
301 pub base: BaseNode,
302 pub id: Identifier,
303 #[serde(rename = "supertype")]
303 - pub supertype: Option<Box<serde_json::Value>>,
304 - pub impltype: Box<serde_json::Value>,
304 + pub supertype: Option<RawNode>,
305 + pub impltype: RawNode,
306 #[serde(
307 default,
308 skip_serializing_if = "Option::is_none",
309 rename = "typeParameters"
310 )]
310 - pub type_parameters: Option<Box<serde_json::Value>>,
311 + pub type_parameters: Option<RawNode>,
312 }
313
314 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -315,19 +316,19 @@ pub struct InterfaceDeclaration {
316 #[serde(flatten)]
317 pub base: BaseNode,
318 pub id: Identifier,
318 - pub body: Box<serde_json::Value>,
319 + pub body: RawNode,
320 #[serde(
321 default,
322 skip_serializing_if = "Option::is_none",
323 rename = "typeParameters"
324 )]
324 - pub type_parameters: Option<Box<serde_json::Value>>,
325 + pub type_parameters: Option<RawNode>,
326 #[serde(default, skip_serializing_if = "Option::is_none")]
326 - pub extends: Option<Vec<serde_json::Value>>,
327 + pub extends: Option<Vec<RawNode>>,
328 #[serde(default, skip_serializing_if = "Option::is_none")]
328 - pub mixins: Option<Vec<serde_json::Value>>,
329 + pub mixins: Option<Vec<RawNode>>,
330 #[serde(default, skip_serializing_if = "Option::is_none")]
330 - pub implements: Option<Vec<serde_json::Value>>,
331 + pub implements: Option<Vec<RawNode>>,
332 }
333
334 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -347,7 +348,7 @@ pub struct DeclareFunction {
348 skip_serializing_if = "Option::is_none",
349 deserialize_with = "crate::common::nullable_value"
350 )]
350 - pub predicate: Option<Box<serde_json::Value>>,
351 + pub predicate: Option<RawNode>,
352 }
353
354 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -355,27 +356,27 @@ pub struct DeclareClass {
356 #[serde(flatten)]
357 pub base: BaseNode,
358 pub id: Identifier,
358 - pub body: Box<serde_json::Value>,
359 + pub body: RawNode,
360 #[serde(
361 default,
362 skip_serializing_if = "Option::is_none",
363 rename = "typeParameters"
364 )]
364 - pub type_parameters: Option<Box<serde_json::Value>>,
365 + pub type_parameters: Option<RawNode>,
366 #[serde(default, skip_serializing_if = "Option::is_none")]
366 - pub extends: Option<Vec<serde_json::Value>>,
367 + pub extends: Option<Vec<RawNode>>,
368 #[serde(default, skip_serializing_if = "Option::is_none")]
368 - pub mixins: Option<Vec<serde_json::Value>>,
369 + pub mixins: Option<Vec<RawNode>>,
370 #[serde(default, skip_serializing_if = "Option::is_none")]
370 - pub implements: Option<Vec<serde_json::Value>>,
371 + pub implements: Option<Vec<RawNode>>,
372 }
373
374 #[derive(Debug, Clone, Serialize, Deserialize)]
375 pub struct DeclareModule {
376 #[serde(flatten)]
377 pub base: BaseNode,
377 - pub id: Box<serde_json::Value>,
378 - pub body: Box<serde_json::Value>,
378 + pub id: RawNode,
379 + pub body: RawNode,
380 #[serde(default, skip_serializing_if = "Option::is_none")]
381 pub kind: Option<String>,
382 }
@@ -385,7 +386,7 @@ pub struct DeclareModuleExports {
386 #[serde(flatten)]
387 pub base: BaseNode,
388 #[serde(rename = "typeAnnotation")]
388 - pub type_annotation: Box<serde_json::Value>,
389 + pub type_annotation: RawNode,
390 }
391
392 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -393,9 +394,9 @@ pub struct DeclareExportDeclaration {
394 #[serde(flatten)]
395 pub base: BaseNode,
396 #[serde(default, skip_serializing_if = "Option::is_none")]
396 - pub declaration: Option<Box<serde_json::Value>>,
397 + pub declaration: Option<RawNode>,
398 #[serde(default, skip_serializing_if = "Option::is_none")]
398 - pub specifiers: Option<Vec<serde_json::Value>>,
399 + pub specifiers: Option<Vec<RawNode>>,
400 #[serde(default, skip_serializing_if = "Option::is_none")]
401 pub source: Option<StringLiteral>,
402 #[serde(default, skip_serializing_if = "Option::is_none")]
@@ -414,19 +415,19 @@ pub struct DeclareInterface {
415 #[serde(flatten)]
416 pub base: BaseNode,
417 pub id: Identifier,
417 - pub body: Box<serde_json::Value>,
418 + pub body: RawNode,
419 #[serde(
420 default,
421 skip_serializing_if = "Option::is_none",
422 rename = "typeParameters"
423 )]
423 - pub type_parameters: Option<Box<serde_json::Value>>,
424 + pub type_parameters: Option<RawNode>,
425 #[serde(default, skip_serializing_if = "Option::is_none")]
425 - pub extends: Option<Vec<serde_json::Value>>,
426 + pub extends: Option<Vec<RawNode>>,
427 #[serde(default, skip_serializing_if = "Option::is_none")]
427 - pub mixins: Option<Vec<serde_json::Value>>,
428 + pub mixins: Option<Vec<RawNode>>,
429 #[serde(default, skip_serializing_if = "Option::is_none")]
429 - pub implements: Option<Vec<serde_json::Value>>,
430 + pub implements: Option<Vec<RawNode>>,
431 }
432
433 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -434,13 +435,13 @@ pub struct DeclareTypeAlias {
435 #[serde(flatten)]
436 pub base: BaseNode,
437 pub id: Identifier,
437 - pub right: Box<serde_json::Value>,
438 + pub right: RawNode,
439 #[serde(
440 default,
441 skip_serializing_if = "Option::is_none",
442 rename = "typeParameters"
443 )]
443 - pub type_parameters: Option<Box<serde_json::Value>>,
444 + pub type_parameters: Option<RawNode>,
445 }
446
447 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -449,15 +450,15 @@ pub struct DeclareOpaqueType {
450 pub base: BaseNode,
451 pub id: Identifier,
452 #[serde(default, skip_serializing_if = "Option::is_none")]
452 - pub supertype: Option<Box<serde_json::Value>>,
453 + pub supertype: Option<RawNode>,
454 #[serde(default, skip_serializing_if = "Option::is_none")]
454 - pub impltype: Option<Box<serde_json::Value>>,
455 + pub impltype: Option<RawNode>,
456 #[serde(
457 default,
458 skip_serializing_if = "Option::is_none",
459 rename = "typeParameters"
460 )]
460 - pub type_parameters: Option<Box<serde_json::Value>>,
461 + pub type_parameters: Option<RawNode>,
462 }
463
464 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -465,5 +466,5 @@ pub struct EnumDeclaration {
466 #[serde(flatten)]
467 pub base: BaseNode,
468 pub id: Identifier,
468 - pub body: Box<serde_json::Value>,
469 + pub body: RawNode,
470 }
compiler/crates/react_compiler_ast/src/expressions.rs
+32 -31
@@ -2,6 +2,7 @@ use serde::Deserialize;
2 use serde::Serialize;
3
4 use crate::common::BaseNode;
5 +use crate::common::RawNode;
6 use crate::jsx::JSXElement;
7 use crate::jsx::JSXFragment;
8 use crate::literals::*;
@@ -20,11 +21,11 @@ pub struct Identifier {
21 skip_serializing_if = "Option::is_none",
22 rename = "typeAnnotation"
23 )]
23 - pub type_annotation: Option<Box<serde_json::Value>>,
24 + pub type_annotation: Option<RawNode>,
25 #[serde(default, skip_serializing_if = "Option::is_none")]
26 pub optional: Option<bool>,
27 #[serde(default, skip_serializing_if = "Option::is_none")]
27 - pub decorators: Option<Vec<serde_json::Value>>,
28 + pub decorators: Option<Vec<RawNode>>,
29 }
30
31 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -91,13 +92,13 @@ pub struct CallExpression {
92 skip_serializing_if = "Option::is_none",
93 rename = "typeParameters"
94 )]
94 - pub type_parameters: Option<Box<serde_json::Value>>,
95 + pub type_parameters: Option<RawNode>,
96 #[serde(
97 default,
98 skip_serializing_if = "Option::is_none",
99 rename = "typeArguments"
100 )]
100 - pub type_arguments: Option<Box<serde_json::Value>>,
101 + pub type_arguments: Option<RawNode>,
102 #[serde(default, skip_serializing_if = "Option::is_none")]
103 pub optional: Option<bool>,
104 }
@@ -123,13 +124,13 @@ pub struct OptionalCallExpression {
124 skip_serializing_if = "Option::is_none",
125 rename = "typeParameters"
126 )]
126 - pub type_parameters: Option<Box<serde_json::Value>>,
127 + pub type_parameters: Option<RawNode>,
128 #[serde(
129 default,
130 skip_serializing_if = "Option::is_none",
131 rename = "typeArguments"
132 )]
132 - pub type_arguments: Option<Box<serde_json::Value>>,
133 + pub type_arguments: Option<RawNode>,
134 }
135
136 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -222,20 +223,20 @@ pub struct ArrowFunctionExpression {
223 skip_serializing_if = "Option::is_none",
224 rename = "returnType"
225 )]
225 - pub return_type: Option<Box<serde_json::Value>>,
226 + pub return_type: Option<RawNode>,
227 #[serde(
228 default,
229 skip_serializing_if = "Option::is_none",
230 rename = "typeParameters"
231 )]
231 - pub type_parameters: Option<Box<serde_json::Value>>,
232 + pub type_parameters: Option<RawNode>,
233 #[serde(
234 default,
235 skip_serializing_if = "Option::is_none",
236 rename = "predicate",
237 deserialize_with = "crate::common::nullable_value"
238 )]
238 - pub predicate: Option<Box<serde_json::Value>>,
239 + pub predicate: Option<RawNode>,
240 }
241
242 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -263,20 +264,20 @@ pub struct FunctionExpression {
264 skip_serializing_if = "Option::is_none",
265 rename = "returnType"
266 )]
266 - pub return_type: Option<Box<serde_json::Value>>,
267 + pub return_type: Option<RawNode>,
268 #[serde(
269 default,
270 skip_serializing_if = "Option::is_none",
271 rename = "typeParameters"
272 )]
272 - pub type_parameters: Option<Box<serde_json::Value>>,
273 + pub type_parameters: Option<RawNode>,
274 #[serde(
275 default,
276 skip_serializing_if = "Option::is_none",
277 rename = "predicate",
278 deserialize_with = "crate::common::nullable_value"
279 )]
279 - pub predicate: Option<Box<serde_json::Value>>,
280 + pub predicate: Option<RawNode>,
281 }
282
283 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -303,7 +304,7 @@ pub struct ObjectProperty {
304 pub computed: bool,
305 pub shorthand: bool,
306 #[serde(default, skip_serializing_if = "Option::is_none")]
306 - pub decorators: Option<Vec<serde_json::Value>>,
307 + pub decorators: Option<Vec<RawNode>>,
308 #[serde(default, skip_serializing_if = "Option::is_none")]
309 pub method: Option<bool>,
310 }
@@ -325,26 +326,26 @@ pub struct ObjectMethod {
326 #[serde(default, rename = "async")]
327 pub is_async: bool,
328 #[serde(default, skip_serializing_if = "Option::is_none")]
328 - pub decorators: Option<Vec<serde_json::Value>>,
329 + pub decorators: Option<Vec<RawNode>>,
330 #[serde(
331 default,
332 skip_serializing_if = "Option::is_none",
333 rename = "returnType"
334 )]
334 - pub return_type: Option<Box<serde_json::Value>>,
335 + pub return_type: Option<RawNode>,
336 #[serde(
337 default,
338 skip_serializing_if = "Option::is_none",
339 rename = "typeParameters"
340 )]
340 - pub type_parameters: Option<Box<serde_json::Value>>,
341 + pub type_parameters: Option<RawNode>,
342 #[serde(
343 default,
344 skip_serializing_if = "Option::is_none",
345 rename = "predicate",
346 deserialize_with = "crate::common::nullable_value"
347 )]
347 - pub predicate: Option<Box<serde_json::Value>>,
348 + pub predicate: Option<RawNode>,
349 }
350
351 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -373,14 +374,14 @@ pub struct NewExpression {
374 skip_serializing_if = "Option::is_none",
375 rename = "typeParameters"
376 )]
376 - pub type_parameters: Option<Box<serde_json::Value>>,
377 + pub type_parameters: Option<RawNode>,
378 #[serde(
379 default,
380 skip_serializing_if = "Option::is_none",
381 deserialize_with = "crate::common::nullable_value",
382 rename = "typeArguments"
383 )]
383 - pub type_arguments: Option<Box<serde_json::Value>>,
384 + pub type_arguments: Option<RawNode>,
385 }
386
387 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -402,7 +403,7 @@ pub struct TaggedTemplateExpression {
403 skip_serializing_if = "Option::is_none",
404 rename = "typeParameters"
405 )]
405 - pub type_parameters: Option<Box<serde_json::Value>>,
406 + pub type_parameters: Option<RawNode>,
407 }
408
409 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -446,32 +447,32 @@ pub struct ClassExpression {
447 pub super_class: Option<Box<Expression>>,
448 pub body: ClassBody,
449 #[serde(default, skip_serializing_if = "Option::is_none")]
449 - pub decorators: Option<Vec<serde_json::Value>>,
450 + pub decorators: Option<Vec<RawNode>>,
451 #[serde(
452 default,
453 skip_serializing_if = "Option::is_none",
454 rename = "implements"
455 )]
455 - pub implements: Option<Vec<serde_json::Value>>,
456 + pub implements: Option<Vec<RawNode>>,
457 #[serde(
458 default,
459 skip_serializing_if = "Option::is_none",
460 rename = "superTypeParameters"
461 )]
461 - pub super_type_parameters: Option<Box<serde_json::Value>>,
462 + pub super_type_parameters: Option<RawNode>,
463 #[serde(
464 default,
465 skip_serializing_if = "Option::is_none",
466 rename = "typeParameters"
467 )]
467 - pub type_parameters: Option<Box<serde_json::Value>>,
468 + pub type_parameters: Option<RawNode>,
469 }
470
471 #[derive(Debug, Clone, Serialize, Deserialize)]
472 pub struct ClassBody {
473 #[serde(flatten)]
474 pub base: BaseNode,
474 - pub body: Vec<serde_json::Value>,
475 + pub body: Vec<RawNode>,
476 }
477
478 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -506,14 +507,14 @@ pub struct ParenthesizedExpression {
507 pub expression: Box<Expression>,
508 }
509
509 -// TypeScript expression nodes (pass-through with serde_json::Value for type args)
510 +// TypeScript expression nodes (pass-through with RawNode for type args)
511 #[derive(Debug, Clone, Serialize, Deserialize)]
512 pub struct TSAsExpression {
513 #[serde(flatten)]
514 pub base: BaseNode,
515 pub expression: Box<Expression>,
516 #[serde(rename = "typeAnnotation")]
516 - pub type_annotation: Box<serde_json::Value>,
517 + pub type_annotation: RawNode,
518 }
519
520 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -522,7 +523,7 @@ pub struct TSSatisfiesExpression {
523 pub base: BaseNode,
524 pub expression: Box<Expression>,
525 #[serde(rename = "typeAnnotation")]
525 - pub type_annotation: Box<serde_json::Value>,
526 + pub type_annotation: RawNode,
527 }
528
529 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -538,7 +539,7 @@ pub struct TSTypeAssertion {
539 pub base: BaseNode,
540 pub expression: Box<Expression>,
541 #[serde(rename = "typeAnnotation")]
541 - pub type_annotation: Box<serde_json::Value>,
542 + pub type_annotation: RawNode,
543 }
544
545 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -547,7 +548,7 @@ pub struct TSInstantiationExpression {
548 pub base: BaseNode,
549 pub expression: Box<Expression>,
550 #[serde(rename = "typeParameters")]
550 - pub type_parameters: Box<serde_json::Value>,
551 + pub type_parameters: RawNode,
552 }
553
554 // Flow expression nodes
@@ -557,5 +558,5 @@ pub struct TypeCastExpression {
558 pub base: BaseNode,
559 pub expression: Box<Expression>,
560 #[serde(rename = "typeAnnotation")]
560 - pub type_annotation: Box<serde_json::Value>,
561 + pub type_annotation: RawNode,
562 }
compiler/crates/react_compiler_ast/src/jsx.rs
+2 -1
@@ -1,6 +1,7 @@
1 use serde::{Deserialize, Serialize};
2
3 use crate::common::BaseNode;
4 +use crate::common::RawNode;
5 use crate::expressions::Expression;
6 use crate::literals::StringLiteral;
7
@@ -41,7 +42,7 @@ pub struct JSXOpeningElement {
42 skip_serializing_if = "Option::is_none",
43 rename = "typeParameters"
44 )]
44 - pub type_parameters: Option<Box<serde_json::Value>>,
45 + pub type_parameters: Option<RawNode>,
46 }
47
48 #[derive(Debug, Clone, Serialize, Deserialize)]
compiler/crates/react_compiler_ast/src/patterns.rs
+10 -9
@@ -1,6 +1,7 @@
1 use serde::{Deserialize, Serialize};
2
3 use crate::common::BaseNode;
4 +use crate::common::RawNode;
5 use crate::expressions::{Expression, Identifier};
6
7 /// Covers assignment targets and patterns.
@@ -34,9 +35,9 @@ pub struct ObjectPattern {
35 skip_serializing_if = "Option::is_none",
36 rename = "typeAnnotation"
37 )]
37 - pub type_annotation: Option<Box<serde_json::Value>>,
38 + pub type_annotation: Option<RawNode>,
39 #[serde(default, skip_serializing_if = "Option::is_none")]
39 - pub decorators: Option<Vec<serde_json::Value>>,
40 + pub decorators: Option<Vec<RawNode>>,
41 }
42
43 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -55,7 +56,7 @@ pub struct ObjectPatternProp {
56 pub computed: bool,
57 pub shorthand: bool,
58 #[serde(default, skip_serializing_if = "Option::is_none")]
58 - pub decorators: Option<Vec<serde_json::Value>>,
59 + pub decorators: Option<Vec<RawNode>>,
60 #[serde(default, skip_serializing_if = "Option::is_none")]
61 pub method: Option<bool>,
62 }
@@ -70,9 +71,9 @@ pub struct ArrayPattern {
71 skip_serializing_if = "Option::is_none",
72 rename = "typeAnnotation"
73 )]
73 - pub type_annotation: Option<Box<serde_json::Value>>,
74 + pub type_annotation: Option<RawNode>,
75 #[serde(default, skip_serializing_if = "Option::is_none")]
75 - pub decorators: Option<Vec<serde_json::Value>>,
76 + pub decorators: Option<Vec<RawNode>>,
77 }
78
79 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -86,9 +87,9 @@ pub struct AssignmentPattern {
87 skip_serializing_if = "Option::is_none",
88 rename = "typeAnnotation"
89 )]
89 - pub type_annotation: Option<Box<serde_json::Value>>,
90 + pub type_annotation: Option<RawNode>,
91 #[serde(default, skip_serializing_if = "Option::is_none")]
91 - pub decorators: Option<Vec<serde_json::Value>>,
92 + pub decorators: Option<Vec<RawNode>>,
93 }
94
95 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -101,7 +102,7 @@ pub struct RestElement {
102 skip_serializing_if = "Option::is_none",
103 rename = "typeAnnotation"
104 )]
104 - pub type_annotation: Option<Box<serde_json::Value>>,
105 + pub type_annotation: Option<RawNode>,
106 #[serde(default, skip_serializing_if = "Option::is_none")]
106 - pub decorators: Option<Vec<serde_json::Value>>,
107 + pub decorators: Option<Vec<RawNode>>,
108 }
compiler/crates/react_compiler_ast/src/statements.rs
+37 -29
@@ -2,6 +2,7 @@ use serde::de::Error as _;
2 use serde::{Deserialize, Deserializer, Serialize, Serializer};
3
4 use crate::common::BaseNode;
5 +use crate::common::RawNode;
6
7 use crate::expressions::{Expression, Identifier};
8 use crate::patterns::PatternLike;
@@ -86,17 +87,17 @@ pub enum Statement {
87
88 #[derive(Debug, Clone)]
89 pub struct UnknownStatement {
89 - raw: serde_json::Value,
90 + raw: RawNode,
91 base: BaseNode,
92 }
93
94 impl UnknownStatement {
94 - pub fn from_raw(raw: serde_json::Value) -> Result<Self, String> {
95 - match raw.get("type").and_then(serde_json::Value::as_str) {
95 + pub fn from_raw(raw: RawNode) -> Result<Self, String> {
96 + match raw.type_name() {
97 Some(_) => {
97 - // By-ref deserialization clones only the fields BaseNode
98 - // reads, not the whole (arbitrarily large) unknown subtree.
99 - let base = BaseNode::deserialize(&raw)
98 + // Parsing into BaseNode reads only the fields BaseNode declares,
99 + // not the whole (arbitrarily large) unknown subtree.
100 + let base = crate::common::from_json_str_unbounded::<BaseNode>(raw.get())
101 .map_err(|err| format!("failed to read unknown statement base: {err}"))?;
102 Ok(Self { raw, base })
103 }
@@ -111,7 +112,7 @@ impl UnknownStatement {
112 self.base.node_type.as_deref().unwrap_or("Unknown")
113 }
114
114 - pub fn raw(&self) -> &serde_json::Value {
115 + pub fn raw(&self) -> &RawNode {
116 &self.raw
117 }
118
@@ -120,15 +121,15 @@ impl UnknownStatement {
121 /// string `type` field are rejected and rolled back.
122 pub fn with_raw_mut<R>(
123 &mut self,
123 - f: impl FnOnce(&mut serde_json::Value) -> R,
124 + f: impl FnOnce(&mut RawNode) -> R,
125 ) -> Result<R, String> {
126 let saved = self.raw.clone();
127 let result = f(&mut self.raw);
127 - if self.raw.get("type").and_then(serde_json::Value::as_str).is_none() {
128 + if self.raw.type_name().is_none() {
129 self.raw = saved;
130 return Err("unknown statement mutation removed the string `type` field".to_string());
131 }
131 - match BaseNode::deserialize(&self.raw) {
132 + match crate::common::from_json_str_unbounded::<BaseNode>(self.raw.get()) {
133 Ok(base) => {
134 self.base = base;
135 Ok(result)
@@ -159,7 +160,7 @@ impl<'de> Deserialize<'de> for UnknownStatement {
160 where
161 D: Deserializer<'de>,
162 {
162 - let raw = serde_json::Value::deserialize(deserializer)?;
163 + let raw = RawNode::deserialize(deserializer)?;
164 Self::from_raw(raw).map_err(D::Error::custom)
165 }
166 }
@@ -169,15 +170,14 @@ impl<'de> Deserialize<'de> for Statement {
170 where
171 D: Deserializer<'de>,
172 {
172 - let raw = serde_json::Value::deserialize(deserializer)?;
173 + let raw = RawNode::deserialize(deserializer)?;
174 let node_type = raw
174 - .get("type")
175 - .and_then(serde_json::Value::as_str)
176 - .ok_or_else(|| D::Error::custom("statement is missing a string `type` field"))?
177 - .to_string();
175 + .type_name()
176 + .ok_or_else(|| D::Error::custom("statement is missing a string `type` field"))?;
177
178 if is_known_statement_type(&node_type) {
180 - let known: KnownStatement = serde_json::from_value(raw).map_err(D::Error::custom)?;
179 + let known: KnownStatement =
180 + crate::common::from_json_str_unbounded(raw.get()).map_err(D::Error::custom)?;
181 Ok(known.into())
182 } else {
183 UnknownStatement::from_raw(raw)
@@ -508,20 +508,20 @@ pub struct FunctionDeclaration {
508 skip_serializing_if = "Option::is_none",
509 rename = "returnType"
510 )]
511 - pub return_type: Option<Box<serde_json::Value>>,
511 + pub return_type: Option<RawNode>,
512 #[serde(
513 default,
514 skip_serializing_if = "Option::is_none",
515 rename = "typeParameters"
516 )]
517 - pub type_parameters: Option<Box<serde_json::Value>>,
517 + pub type_parameters: Option<RawNode>,
518 #[serde(
519 default,
520 skip_serializing_if = "Option::is_none",
521 rename = "predicate",
522 deserialize_with = "crate::common::nullable_value"
523 )]
524 - pub predicate: Option<Box<serde_json::Value>>,
524 + pub predicate: Option<RawNode>,
525 /// Set by the Hermes parser for Flow `component Foo(...) { ... }` syntax
526 #[serde(
527 default,
@@ -547,7 +547,7 @@ pub struct ClassDeclaration {
547 pub super_class: Option<Box<Expression>>,
548 pub body: crate::expressions::ClassBody,
549 #[serde(default, skip_serializing_if = "Option::is_none")]
550 - pub decorators: Option<Vec<serde_json::Value>>,
550 + pub decorators: Option<Vec<RawNode>>,
551 #[serde(default, skip_serializing_if = "Option::is_none", rename = "abstract")]
552 pub is_abstract: Option<bool>,
553 #[serde(default, skip_serializing_if = "Option::is_none")]
@@ -557,21 +557,21 @@ pub struct ClassDeclaration {
557 skip_serializing_if = "Option::is_none",
558 rename = "implements"
559 )]
560 - pub implements: Option<Vec<serde_json::Value>>,
560 + pub implements: Option<Vec<RawNode>>,
561 #[serde(
562 default,
563 skip_serializing_if = "Option::is_none",
564 rename = "superTypeParameters"
565 )]
566 - pub super_type_parameters: Option<Box<serde_json::Value>>,
566 + pub super_type_parameters: Option<RawNode>,
567 #[serde(
568 default,
569 skip_serializing_if = "Option::is_none",
570 rename = "typeParameters"
571 )]
572 - pub type_parameters: Option<Box<serde_json::Value>>,
572 + pub type_parameters: Option<RawNode>,
573 #[serde(default, skip_serializing_if = "Option::is_none")]
574 - pub mixins: Option<Vec<serde_json::Value>>,
574 + pub mixins: Option<Vec<RawNode>>,
575 }
576
577 #[cfg(test)]
@@ -579,6 +579,7 @@ mod tests {
579 use serde_json::json;
580
581 use super::Statement;
582 + use crate::common::RawNode;
583
584 #[test]
585 fn unknown_statement_round_trips_at_program_level() {
@@ -730,15 +731,22 @@ mod tests {
731
732 unknown
733 .with_raw_mut(|v| {
733 - v["start"] = json!(9);
734 - v["expression"]["name"] = json!("y");
734 + let mut parsed = v.parse_value();
735 + parsed["start"] = json!(9);
736 + parsed["expression"]["name"] = json!("y");
737 + *v = RawNode::from_value(&parsed);
738 })
739 .unwrap();
740 assert_eq!(unknown.base().start, Some(9));
738 - assert_eq!(unknown.raw()["expression"]["name"], json!("y"));
741 + assert_eq!(
742 + unknown.raw().parse_value()["expression"]["name"],
743 + json!("y")
744 + );
745
746 let err = unknown.with_raw_mut(|v| {
741 - v.as_object_mut().unwrap().remove("type");
747 + let mut parsed = v.parse_value();
748 + parsed.as_object_mut().unwrap().remove("type");
749 + *v = RawNode::from_value(&parsed);
750 });
751 assert!(err.is_err(), "type removal must be rejected");
752 }
compiler/crates/react_compiler_ast/tests/deep_nesting.rs new
+37
@@ -0,0 +1,37 @@
1 +//! Deep ASTs must survive deserialization when the caller disables
2 +//! serde_json's recursion limit, as the napi entrypoint does. The tolerant
3 +//! statement deserializer reparses captured raw text internally; those
4 +//! reparses must not reintroduce the default depth limit.
5 +
6 +use react_compiler_ast::File;
7 +use serde::Deserialize;
8 +
9 +fn from_json_str_unbounded(s: &str) -> serde_json::Result<File> {
10 + let mut deserializer = serde_json::Deserializer::from_str(s);
11 + deserializer.disable_recursion_limit();
12 + File::deserialize(&mut deserializer)
13 +}
14 +
15 +#[test]
16 +fn statement_nested_beyond_default_recursion_limit_deserializes() {
17 + let depth = 400;
18 + let mut expr = r#"{"type":"Identifier","name":"x"}"#.to_string();
19 + for _ in 0..depth {
20 + expr = format!(
21 + r#"{{"type":"CallExpression","callee":{expr},"arguments":[]}}"#
22 + );
23 + }
24 + let json = format!(
25 + r#"{{"type":"File","program":{{"type":"Program","sourceType":"module","body":[{{"type":"ExpressionStatement","expression":{expr}}}],"directives":[]}}}}"#
26 + );
27 +
28 + // Parse on a large stack like the napi entrypoint does; without the limit,
29 + // depth is bounded by stack, not by serde_json's counter.
30 + let file = std::thread::Builder::new()
31 + .stack_size(64 * 1024 * 1024)
32 + .spawn(move || from_json_str_unbounded(&json).expect("deep statement must deserialize"))
33 + .expect("spawn")
34 + .join()
35 + .expect("join");
36 + assert_eq!(file.program.body.len(), 1);
37 +}
compiler/crates/react_compiler_ast/tests/scope_resolution.rs
+48 -42
@@ -311,15 +311,21 @@ fn visit_json(val: &mut serde_json::Value, si: &ScopeInfo) {
311 }
312 }
313
314 -fn visit_json_vec(vals: &mut [serde_json::Value], si: &ScopeInfo) {
314 +fn visit_raw(val: &mut react_compiler_ast::common::RawNode, si: &ScopeInfo) {
315 + let mut v = val.parse_value();
316 + visit_json(&mut v, si);
317 + *val = react_compiler_ast::common::RawNode::from_value(&v);
318 +}
319 +
320 +fn visit_json_vec(vals: &mut [react_compiler_ast::common::RawNode], si: &ScopeInfo) {
321 for val in vals.iter_mut() {
316 - visit_json(val, si);
322 + visit_raw(val, si);
323 }
324 }
325
320 -fn visit_json_opt(val: &mut Option<Box<serde_json::Value>>, si: &ScopeInfo) {
326 +fn visit_json_opt(val: &mut Option<react_compiler_ast::common::RawNode>, si: &ScopeInfo) {
327 if let Some(v) = val {
322 - visit_json(v, si);
328 + visit_raw(v, si);
329 }
330 }
331
@@ -425,12 +431,12 @@ fn visit_stmt(stmt: &mut Statement, si: &ScopeInfo) {
431 Statement::ExportDefaultDeclaration(d) => visit_export_default(d, si),
432 Statement::TSTypeAliasDeclaration(d) => {
433 rename_id(&mut d.id, si);
428 - visit_json(&mut d.type_annotation, si);
434 + visit_raw(&mut d.type_annotation, si);
435 visit_json_opt(&mut d.type_parameters, si);
436 }
437 Statement::TSInterfaceDeclaration(d) => {
438 rename_id(&mut d.id, si);
433 - visit_json(&mut d.body, si);
439 + visit_raw(&mut d.body, si);
440 visit_json_opt(&mut d.type_parameters, si);
441 if let Some(ext) = &mut d.extends {
442 visit_json_vec(ext, si);
@@ -441,8 +447,8 @@ fn visit_stmt(stmt: &mut Statement, si: &ScopeInfo) {
447 visit_json_vec(&mut d.members, si);
448 }
449 Statement::TSModuleDeclaration(d) => {
444 - visit_json(&mut d.id, si);
445 - visit_json(&mut d.body, si);
450 + visit_raw(&mut d.id, si);
451 + visit_raw(&mut d.body, si);
452 }
453 Statement::TSDeclareFunction(d) => {
454 if let Some(id) = &mut d.id {
@@ -454,20 +460,20 @@ fn visit_stmt(stmt: &mut Statement, si: &ScopeInfo) {
460 }
461 Statement::TypeAlias(d) => {
462 rename_id(&mut d.id, si);
457 - visit_json(&mut d.right, si);
463 + visit_raw(&mut d.right, si);
464 visit_json_opt(&mut d.type_parameters, si);
465 }
466 Statement::OpaqueType(d) => {
467 rename_id(&mut d.id, si);
468 if let Some(st) = &mut d.supertype {
463 - visit_json(st, si);
469 + visit_raw(st, si);
470 }
465 - visit_json(&mut d.impltype, si);
471 + visit_raw(&mut d.impltype, si);
472 visit_json_opt(&mut d.type_parameters, si);
473 }
474 Statement::InterfaceDeclaration(d) => {
475 rename_id(&mut d.id, si);
470 - visit_json(&mut d.body, si);
476 + visit_raw(&mut d.body, si);
477 visit_json_opt(&mut d.type_parameters, si);
478 if let Some(ext) = &mut d.extends {
479 visit_json_vec(ext, si);
@@ -477,25 +483,25 @@ fn visit_stmt(stmt: &mut Statement, si: &ScopeInfo) {
483 Statement::DeclareFunction(d) => {
484 rename_id(&mut d.id, si);
485 if let Some(pred) = &mut d.predicate {
480 - visit_json(pred, si);
486 + visit_raw(pred, si);
487 }
488 }
489 Statement::DeclareClass(d) => {
490 rename_id(&mut d.id, si);
485 - visit_json(&mut d.body, si);
491 + visit_raw(&mut d.body, si);
492 visit_json_opt(&mut d.type_parameters, si);
493 if let Some(ext) = &mut d.extends {
494 visit_json_vec(ext, si);
495 }
496 }
497 Statement::DeclareModule(d) => {
492 - visit_json(&mut d.id, si);
493 - visit_json(&mut d.body, si);
498 + visit_raw(&mut d.id, si);
499 + visit_raw(&mut d.body, si);
500 }
495 - Statement::DeclareModuleExports(d) => visit_json(&mut d.type_annotation, si),
501 + Statement::DeclareModuleExports(d) => visit_raw(&mut d.type_annotation, si),
502 Statement::DeclareExportDeclaration(d) => {
503 if let Some(decl) = &mut d.declaration {
498 - visit_json(decl, si);
504 + visit_raw(decl, si);
505 }
506 if let Some(specs) = &mut d.specifiers {
507 visit_json_vec(specs, si);
@@ -503,7 +509,7 @@ fn visit_stmt(stmt: &mut Statement, si: &ScopeInfo) {
509 }
510 Statement::DeclareInterface(d) => {
511 rename_id(&mut d.id, si);
506 - visit_json(&mut d.body, si);
512 + visit_raw(&mut d.body, si);
513 visit_json_opt(&mut d.type_parameters, si);
514 if let Some(ext) = &mut d.extends {
515 visit_json_vec(ext, si);
@@ -511,25 +517,25 @@ fn visit_stmt(stmt: &mut Statement, si: &ScopeInfo) {
517 }
518 Statement::DeclareTypeAlias(d) => {
519 rename_id(&mut d.id, si);
514 - visit_json(&mut d.right, si);
520 + visit_raw(&mut d.right, si);
521 visit_json_opt(&mut d.type_parameters, si);
522 }
523 Statement::DeclareOpaqueType(d) => {
524 rename_id(&mut d.id, si);
525 if let Some(st) = &mut d.supertype {
520 - visit_json(st, si);
526 + visit_raw(st, si);
527 }
528 if let Some(impl_) = &mut d.impltype {
523 - visit_json(impl_, si);
529 + visit_raw(impl_, si);
530 }
531 visit_json_opt(&mut d.type_parameters, si);
532 }
533 Statement::EnumDeclaration(d) => {
534 rename_id(&mut d.id, si);
529 - visit_json(&mut d.body, si);
535 + visit_raw(&mut d.body, si);
536 }
537 Statement::Unknown(d) => {
532 - d.with_raw_mut(|raw| visit_json(raw, si))
538 + d.with_raw_mut(|raw| visit_raw(raw, si))
539 .expect("identifier rename preserves the node `type`");
540 }
541 Statement::BreakStatement(_)
@@ -701,24 +707,24 @@ fn visit_expr(expr: &mut Expression, si: &ScopeInfo) {
707 }
708 Expression::TSAsExpression(e) => {
709 visit_expr(&mut e.expression, si);
704 - visit_json(&mut e.type_annotation, si);
710 + visit_raw(&mut e.type_annotation, si);
711 }
712 Expression::TSSatisfiesExpression(e) => {
713 visit_expr(&mut e.expression, si);
708 - visit_json(&mut e.type_annotation, si);
714 + visit_raw(&mut e.type_annotation, si);
715 }
716 Expression::TSNonNullExpression(e) => visit_expr(&mut e.expression, si),
717 Expression::TSTypeAssertion(e) => {
718 visit_expr(&mut e.expression, si);
713 - visit_json(&mut e.type_annotation, si);
719 + visit_raw(&mut e.type_annotation, si);
720 }
721 Expression::TSInstantiationExpression(e) => {
722 visit_expr(&mut e.expression, si);
717 - visit_json(&mut e.type_parameters, si);
723 + visit_raw(&mut e.type_parameters, si);
724 }
725 Expression::TypeCastExpression(e) => {
726 visit_expr(&mut e.expression, si);
721 - visit_json(&mut e.type_annotation, si);
727 + visit_raw(&mut e.type_annotation, si);
728 }
729 Expression::JSXElement(e) => visit_jsx_element(e, si),
730 Expression::JSXFragment(f) => {
@@ -779,22 +785,22 @@ fn visit_pat(pat: &mut PatternLike, si: &ScopeInfo) {
785 }
786 PatternLike::TSAsExpression(e) => {
787 visit_expr(&mut e.expression, si);
782 - visit_json(&mut e.type_annotation, si);
788 + visit_raw(&mut e.type_annotation, si);
789 }
790 PatternLike::TSSatisfiesExpression(e) => {
791 visit_expr(&mut e.expression, si);
786 - visit_json(&mut e.type_annotation, si);
792 + visit_raw(&mut e.type_annotation, si);
793 }
794 PatternLike::TSNonNullExpression(e) => {
795 visit_expr(&mut e.expression, si);
796 }
797 PatternLike::TSTypeAssertion(e) => {
798 visit_expr(&mut e.expression, si);
793 - visit_json(&mut e.type_annotation, si);
799 + visit_raw(&mut e.type_annotation, si);
800 }
801 PatternLike::TypeCastExpression(e) => {
802 visit_expr(&mut e.expression, si);
797 - visit_json(&mut e.type_annotation, si);
803 + visit_raw(&mut e.type_annotation, si);
804 }
805 }
806 }
@@ -893,12 +899,12 @@ fn visit_declaration(d: &mut Declaration, si: &ScopeInfo) {
899 Declaration::VariableDeclaration(v) => visit_var_decl(v, si),
900 Declaration::TSTypeAliasDeclaration(d) => {
901 rename_id(&mut d.id, si);
896 - visit_json(&mut d.type_annotation, si);
902 + visit_raw(&mut d.type_annotation, si);
903 visit_json_opt(&mut d.type_parameters, si);
904 }
905 Declaration::TSInterfaceDeclaration(d) => {
906 rename_id(&mut d.id, si);
901 - visit_json(&mut d.body, si);
907 + visit_raw(&mut d.body, si);
908 visit_json_opt(&mut d.type_parameters, si);
909 if let Some(ext) = &mut d.extends {
910 visit_json_vec(ext, si);
@@ -909,8 +915,8 @@ fn visit_declaration(d: &mut Declaration, si: &ScopeInfo) {
915 visit_json_vec(&mut d.members, si);
916 }
917 Declaration::TSModuleDeclaration(d) => {
912 - visit_json(&mut d.id, si);
913 - visit_json(&mut d.body, si);
918 + visit_raw(&mut d.id, si);
919 + visit_raw(&mut d.body, si);
920 }
921 Declaration::TSDeclareFunction(d) => {
922 if let Some(id) = &mut d.id {
@@ -922,20 +928,20 @@ fn visit_declaration(d: &mut Declaration, si: &ScopeInfo) {
928 }
929 Declaration::TypeAlias(d) => {
930 rename_id(&mut d.id, si);
925 - visit_json(&mut d.right, si);
931 + visit_raw(&mut d.right, si);
932 visit_json_opt(&mut d.type_parameters, si);
933 }
934 Declaration::OpaqueType(d) => {
935 rename_id(&mut d.id, si);
936 if let Some(st) = &mut d.supertype {
931 - visit_json(st, si);
937 + visit_raw(st, si);
938 }
933 - visit_json(&mut d.impltype, si);
939 + visit_raw(&mut d.impltype, si);
940 visit_json_opt(&mut d.type_parameters, si);
941 }
942 Declaration::InterfaceDeclaration(d) => {
943 rename_id(&mut d.id, si);
938 - visit_json(&mut d.body, si);
944 + visit_raw(&mut d.body, si);
945 visit_json_opt(&mut d.type_parameters, si);
946 if let Some(ext) = &mut d.extends {
947 visit_json_vec(ext, si);
@@ -943,7 +949,7 @@ fn visit_declaration(d: &mut Declaration, si: &ScopeInfo) {
949 }
950 Declaration::EnumDeclaration(d) => {
951 rename_id(&mut d.id, si);
946 - visit_json(&mut d.body, si);
952 + visit_raw(&mut d.body, si);
953 }
954 }
955 }
compiler/crates/react_compiler_lowering/src/build_hir.rs
+19 -19
@@ -260,9 +260,9 @@ fn expression_type_name(expr: &react_compiler_ast::expressions::Expression) -> &
260 /// or { "type": "TypeAnnotation", "typeAnnotation": { "type": "GenericTypeAnnotation", ... } }
261 /// We extract the inner typeAnnotation's `type` field name.
262 fn extract_type_annotation_name(
263 - type_annotation: &Option<Box<serde_json::Value>>,
263 + type_annotation: &Option<react_compiler_ast::common::RawNode>,
264 ) -> Option<String> {
265 - let val = type_annotation.as_ref()?;
265 + let val = type_annotation.as_ref()?.parse_value();
266 // Navigate: typeAnnotation.typeAnnotation.type
267 let inner = val.get("typeAnnotation")?;
268 let type_name = inner.get("type")?.as_str()?;
@@ -2205,30 +2205,30 @@ fn lower_expression(
2205 Expression::TSAsExpression(ts) => {
2206 let loc = convert_opt_loc(&ts.base.loc);
2207 let value = lower_expression_to_temporary(builder, &ts.expression)?;
2208 - let type_annotation = &*ts.type_annotation;
2209 - let type_ = lower_type_annotation(type_annotation, builder);
2210 - let type_annotation_name = get_type_annotation_name(type_annotation);
2208 + let type_annotation = ts.type_annotation.parse_value();
2209 + let type_ = lower_type_annotation(&type_annotation, builder);
2210 + let type_annotation_name = get_type_annotation_name(&type_annotation);
2211 Ok(InstructionValue::TypeCastExpression {
2212 value,
2213 type_,
2214 type_annotation_name,
2215 type_annotation_kind: Some("as".to_string()),
2216 - type_annotation: Some(ts.type_annotation.clone()),
2216 + type_annotation: Some(Box::new(type_annotation)),
2217 loc,
2218 })
2219 }
2220 Expression::TSSatisfiesExpression(ts) => {
2221 let loc = convert_opt_loc(&ts.base.loc);
2222 let value = lower_expression_to_temporary(builder, &ts.expression)?;
2223 - let type_annotation = &*ts.type_annotation;
2224 - let type_ = lower_type_annotation(type_annotation, builder);
2225 - let type_annotation_name = get_type_annotation_name(type_annotation);
2223 + let type_annotation = ts.type_annotation.parse_value();
2224 + let type_ = lower_type_annotation(&type_annotation, builder);
2225 + let type_annotation_name = get_type_annotation_name(&type_annotation);
2226 Ok(InstructionValue::TypeCastExpression {
2227 value,
2228 type_,
2229 type_annotation_name,
2230 type_annotation_kind: Some("satisfies".to_string()),
2231 - type_annotation: Some(ts.type_annotation.clone()),
2231 + type_annotation: Some(Box::new(type_annotation)),
2232 loc,
2233 })
2234 }
@@ -2236,15 +2236,15 @@ fn lower_expression(
2236 Expression::TSTypeAssertion(ts) => {
2237 let loc = convert_opt_loc(&ts.base.loc);
2238 let value = lower_expression_to_temporary(builder, &ts.expression)?;
2239 - let type_annotation = &*ts.type_annotation;
2240 - let type_ = lower_type_annotation(type_annotation, builder);
2241 - let type_annotation_name = get_type_annotation_name(type_annotation);
2239 + let type_annotation = ts.type_annotation.parse_value();
2240 + let type_ = lower_type_annotation(&type_annotation, builder);
2241 + let type_annotation_name = get_type_annotation_name(&type_annotation);
2242 Ok(InstructionValue::TypeCastExpression {
2243 value,
2244 type_,
2245 type_annotation_name,
2246 type_annotation_kind: Some("as".to_string()),
2247 - type_annotation: Some(ts.type_annotation.clone()),
2247 + type_annotation: Some(Box::new(type_annotation)),
2248 loc,
2249 })
2250 }
@@ -2252,11 +2252,11 @@ fn lower_expression(
2252 Expression::TypeCastExpression(tc) => {
2253 let loc = convert_opt_loc(&tc.base.loc);
2254 let value = lower_expression_to_temporary(builder, &tc.expression)?;
2255 + let annotation_value = tc.type_annotation.parse_value();
2256 // Flow TypeCastExpression: typeAnnotation is a TypeAnnotation node wrapping the actual type
2256 - let inner_type = tc
2257 - .type_annotation
2257 + let inner_type = annotation_value
2258 .get("typeAnnotation")
2259 - .unwrap_or(&*tc.type_annotation);
2259 + .unwrap_or(&annotation_value);
2260 let type_ = lower_type_annotation(inner_type, builder);
2261 let type_annotation_name = get_type_annotation_name(inner_type);
2262 Ok(InstructionValue::TypeCastExpression {
@@ -2264,7 +2264,7 @@ fn lower_expression(
2264 type_,
2265 type_annotation_name,
2266 type_annotation_kind: Some("cast".to_string()),
2267 - type_annotation: Some(tc.type_annotation.clone()),
2267 + type_annotation: Some(Box::new(annotation_value)),
2268 loc,
2269 })
2270 }
@@ -4231,7 +4231,7 @@ fn lower_statement(
4231 builder,
4232 InstructionValue::UnsupportedNode {
4233 node_type: Some(node_type),
4234 - original_node: Some(unknown.raw().clone()),
4234 + original_node: Some(unknown.raw().parse_value()),
4235 loc,
4236 },
4237 )?;
compiler/crates/react_compiler_lowering/src/identifier_loc_index.rs
+2 -2
@@ -239,7 +239,7 @@ impl<'ast> Visitor<'ast> for IdentifierLocVisitor {
239 // The typed AstWalker skips class bodies (stored as Vec<serde_json::Value>),
240 // but gatherCapturedContext in TS traverses them via Babel's traverse.
241 for member in &node.body.body {
242 - self.walk_json_for_identifiers(member, false);
242 + self.walk_json_for_identifiers(&member.parse_value(), false);
243 }
244 }
245
@@ -253,7 +253,7 @@ impl<'ast> Visitor<'ast> for IdentifierLocVisitor {
253 }
254 // Walk class body JSON to index identifiers inside class methods
255 for member in &node.body.body {
256 - self.walk_json_for_identifiers(member, false);
256 + self.walk_json_for_identifiers(&member.parse_value(), false);
257 }
258 }
259 }
compiler/crates/react_compiler_oxc/src/convert_ast.rs
+40 -39
@@ -10,6 +10,7 @@ use react_compiler_ast::File;
10 use react_compiler_ast::Program;
11 use react_compiler_ast::SourceType;
12 use react_compiler_ast::common::BaseNode;
13 +use react_compiler_ast::common::RawNode;
14 use react_compiler_ast::common::Comment;
15 use react_compiler_ast::common::CommentData;
16 use react_compiler_ast::common::Position;
@@ -594,11 +595,11 @@ impl<'a> ConvertCtx<'a> {
595 return_type: func
596 .return_type
597 .as_ref()
597 - .map(|_t| Box::new(serde_json::Value::Null)),
598 + .map(|_t| RawNode::null()),
599 type_parameters: func
600 .type_parameters
601 .as_ref()
601 - .map(|_t| Box::new(serde_json::Value::Null)),
602 + .map(|_t| RawNode::null()),
603 predicate: None,
604 component_declaration: false,
605 hook_declaration: false,
@@ -622,7 +623,7 @@ impl<'a> ConvertCtx<'a> {
623 .body
624 .body
625 .iter()
625 - .map(|_item| serde_json::Value::Null)
626 + .map(|_item| RawNode::null())
627 .collect(),
628 },
629 decorators: if class.decorators.is_empty() {
@@ -632,7 +633,7 @@ impl<'a> ConvertCtx<'a> {
633 class
634 .decorators
635 .iter()
635 - .map(|_d| serde_json::Value::Null)
636 + .map(|_d| RawNode::null())
637 .collect(),
638 )
639 },
@@ -645,18 +646,18 @@ impl<'a> ConvertCtx<'a> {
646 class
647 .implements
648 .iter()
648 - .map(|_i| serde_json::Value::Null)
649 + .map(|_i| RawNode::null())
650 .collect(),
651 )
652 },
653 super_type_parameters: class
654 .super_type_arguments
655 .as_ref()
655 - .map(|_t| Box::new(serde_json::Value::Null)),
656 + .map(|_t| RawNode::null()),
657 type_parameters: class
658 .type_parameters
659 .as_ref()
659 - .map(|_t| Box::new(serde_json::Value::Null)),
660 + .map(|_t| RawNode::null()),
661 mixins: None,
662 }
663 }
@@ -942,11 +943,11 @@ impl<'a> ConvertCtx<'a> {
943 TSTypeAliasDeclaration {
944 base: self.make_base_node(type_alias.span),
945 id: self.convert_binding_identifier(&type_alias.id),
945 - type_annotation: Box::new(serde_json::Value::Null),
946 + type_annotation: RawNode::null(),
947 type_parameters: type_alias
948 .type_parameters
949 .as_ref()
949 - .map(|_t| Box::new(serde_json::Value::Null)),
950 + .map(|_t| RawNode::null()),
951 declare: if type_alias.declare { Some(true) } else { None },
952 }
953 }
@@ -958,11 +959,11 @@ impl<'a> ConvertCtx<'a> {
959 TSInterfaceDeclaration {
960 base: self.make_base_node(interface.span),
961 id: self.convert_binding_identifier(&interface.id),
961 - body: Box::new(serde_json::Value::Null),
962 + body: RawNode::null(),
963 type_parameters: interface
964 .type_parameters
965 .as_ref()
965 - .map(|_t| Box::new(serde_json::Value::Null)),
966 + .map(|_t| RawNode::null()),
967 extends: if interface.extends.is_empty() {
968 None
969 } else {
@@ -970,7 +971,7 @@ impl<'a> ConvertCtx<'a> {
971 interface
972 .extends
973 .iter()
973 - .map(|_e| serde_json::Value::Null)
974 + .map(|_e| RawNode::null())
975 .collect(),
976 )
977 },
@@ -986,7 +987,7 @@ impl<'a> ConvertCtx<'a> {
987 .body
988 .members
989 .iter()
989 - .map(|_m| serde_json::Value::Null)
990 + .map(|_m| RawNode::null())
991 .collect(),
992 declare: if ts_enum.declare { Some(true) } else { None },
993 is_const: if ts_enum.r#const { Some(true) } else { None },
@@ -999,8 +1000,8 @@ impl<'a> ConvertCtx<'a> {
1000 ) -> TSModuleDeclaration {
1001 TSModuleDeclaration {
1002 base: self.make_base_node(module.span),
1002 - id: Box::new(serde_json::Value::Null),
1003 - body: Box::new(serde_json::Value::Null),
1003 + id: RawNode::null(),
1004 + body: RawNode::null(),
1005 declare: if module.declare { Some(true) } else { None },
1006 global: None,
1007 }
@@ -1267,11 +1268,11 @@ impl<'a> ConvertCtx<'a> {
1268 return_type: arrow
1269 .return_type
1270 .as_ref()
1270 - .map(|_t| Box::new(serde_json::Value::Null)),
1271 + .map(|_t| RawNode::null()),
1272 type_parameters: arrow
1273 .type_parameters
1274 .as_ref()
1274 - .map(|_t| Box::new(serde_json::Value::Null)),
1275 + .map(|_t| RawNode::null()),
1276 predicate: None,
1277 }
1278 }
@@ -1607,7 +1608,7 @@ impl<'a> ConvertCtx<'a> {
1608 type_parameters: call
1609 .type_arguments
1610 .as_ref()
1610 - .map(|_t| Box::new(serde_json::Value::Null)),
1611 + .map(|_t| RawNode::null()),
1612 type_arguments: None,
1613 optional: if call.optional { Some(true) } else { None },
1614 }
@@ -1639,7 +1640,7 @@ impl<'a> ConvertCtx<'a> {
1640 type_parameters: c
1641 .type_arguments
1642 .as_ref()
1642 - .map(|_t| Box::new(serde_json::Value::Null)),
1643 + .map(|_t| RawNode::null()),
1644 type_arguments: None,
1645 })
1646 }
@@ -1728,7 +1729,7 @@ impl<'a> ConvertCtx<'a> {
1729 type_parameters: c
1730 .type_arguments
1731 .as_ref()
1731 - .map(|_t| Box::new(serde_json::Value::Null)),
1732 + .map(|_t| RawNode::null()),
1733 type_arguments: None,
1734 })
1735 }
@@ -1770,7 +1771,7 @@ impl<'a> ConvertCtx<'a> {
1771 type_parameters: c
1772 .type_arguments
1773 .as_ref()
1773 - .map(|_t| Box::new(serde_json::Value::Null)),
1774 + .map(|_t| RawNode::null()),
1775 type_arguments: None,
1776 })
1777 }
@@ -1821,7 +1822,7 @@ impl<'a> ConvertCtx<'a> {
1822 .body
1823 .body
1824 .iter()
1824 - .map(|_item| serde_json::Value::Null)
1825 + .map(|_item| RawNode::null())
1826 .collect(),
1827 },
1828 decorators: if class.decorators.is_empty() {
@@ -1831,7 +1832,7 @@ impl<'a> ConvertCtx<'a> {
1832 class
1833 .decorators
1834 .iter()
1834 - .map(|_d| serde_json::Value::Null)
1835 + .map(|_d| RawNode::null())
1836 .collect(),
1837 )
1838 },
@@ -1842,18 +1843,18 @@ impl<'a> ConvertCtx<'a> {
1843 class
1844 .implements
1845 .iter()
1845 - .map(|_i| serde_json::Value::Null)
1846 + .map(|_i| RawNode::null())
1847 .collect(),
1848 )
1849 },
1850 super_type_parameters: class
1851 .super_type_arguments
1852 .as_ref()
1852 - .map(|_t| Box::new(serde_json::Value::Null)),
1853 + .map(|_t| RawNode::null()),
1854 type_parameters: class
1855 .type_parameters
1856 .as_ref()
1856 - .map(|_t| Box::new(serde_json::Value::Null)),
1857 + .map(|_t| RawNode::null()),
1858 }
1859 }
1860
@@ -1883,11 +1884,11 @@ impl<'a> ConvertCtx<'a> {
1884 return_type: func
1885 .return_type
1886 .as_ref()
1886 - .map(|_t| Box::new(serde_json::Value::Null)),
1887 + .map(|_t| RawNode::null()),
1888 type_parameters: func
1889 .type_parameters
1890 .as_ref()
1890 - .map(|_t| Box::new(serde_json::Value::Null)),
1891 + .map(|_t| RawNode::null()),
1892 predicate: None,
1893 }
1894 }
@@ -1921,7 +1922,7 @@ impl<'a> ConvertCtx<'a> {
1922 type_parameters: new
1923 .type_arguments
1924 .as_ref()
1924 - .map(|_t| Box::new(serde_json::Value::Null)),
1925 + .map(|_t| RawNode::null()),
1926 type_arguments: None,
1927 }
1928 }
@@ -1980,11 +1981,11 @@ impl<'a> ConvertCtx<'a> {
1981 return_type: func
1982 .return_type
1983 .as_ref()
1983 - .map(|_| Box::new(serde_json::Value::Null)),
1984 + .map(|_| RawNode::null()),
1985 type_parameters: func
1986 .type_parameters
1987 .as_ref()
1987 - .map(|_| Box::new(serde_json::Value::Null)),
1988 + .map(|_| RawNode::null()),
1989 predicate: None,
1990 });
1991 }
@@ -2063,7 +2064,7 @@ impl<'a> ConvertCtx<'a> {
2064 type_parameters: tagged
2065 .type_arguments
2066 .as_ref()
2066 - .map(|_t| Box::new(serde_json::Value::Null)),
2067 + .map(|_t| RawNode::null()),
2068 }
2069 }
2070
@@ -2185,7 +2186,7 @@ impl<'a> ConvertCtx<'a> {
2186 TSAsExpression {
2187 base: self.make_base_node(ts_as.span),
2188 expression: Box::new(self.convert_expression(&ts_as.expression)),
2188 - type_annotation: Box::new(serde_json::Value::Null),
2189 + type_annotation: RawNode::null(),
2190 }
2191 }
2192
@@ -2196,7 +2197,7 @@ impl<'a> ConvertCtx<'a> {
2197 TSSatisfiesExpression {
2198 base: self.make_base_node(ts_sat.span),
2199 expression: Box::new(self.convert_expression(&ts_sat.expression)),
2199 - type_annotation: Box::new(serde_json::Value::Null),
2200 + type_annotation: RawNode::null(),
2201 }
2202 }
2203
@@ -2204,7 +2205,7 @@ impl<'a> ConvertCtx<'a> {
2205 TSTypeAssertion {
2206 base: self.make_base_node(ts_assert.span),
2207 expression: Box::new(self.convert_expression(&ts_assert.expression)),
2207 - type_annotation: Box::new(serde_json::Value::Null),
2208 + type_annotation: RawNode::null(),
2209 }
2210 }
2211
@@ -2225,7 +2226,7 @@ impl<'a> ConvertCtx<'a> {
2226 TSInstantiationExpression {
2227 base: self.make_base_node(ts_inst.span),
2228 expression: Box::new(self.convert_expression(&ts_inst.expression)),
2228 - type_parameters: Box::new(serde_json::Value::Null),
2229 + type_parameters: RawNode::null(),
2230 }
2231 }
2232
@@ -2278,7 +2279,7 @@ impl<'a> ConvertCtx<'a> {
2279 type_parameters: opening
2280 .type_arguments
2281 .as_ref()
2281 - .map(|_t| Box::new(serde_json::Value::Null)),
2282 + .map(|_t| RawNode::null()),
2283 }
2284 }
2285
@@ -2620,7 +2621,7 @@ impl<'a> ConvertCtx<'a> {
2621 type_annotation: rest
2622 .type_annotation
2623 .as_ref()
2623 - .map(|_| Box::new(serde_json::Value::Null)),
2624 + .map(|_| RawNode::null()),
2625 decorators: None,
2626 }));
2627 }
@@ -2632,7 +2633,7 @@ impl<'a> ConvertCtx<'a> {
2633
2634 // Add type annotation if present (now on FormalParameter, not BindingPattern)
2635 if let Some(_type_annotation) = &param.type_annotation {
2635 - let type_json = Box::new(serde_json::Value::Null);
2636 + let type_json = RawNode::null();
2637 match &mut pattern {
2638 PatternLike::Identifier(id) => {
2639 id.type_annotation = Some(type_json);
compiler/crates/react_compiler_reactive_scopes/src/codegen_reactive_function.rs
+6 -5
@@ -15,6 +15,7 @@ use std::collections::HashSet;
15
16 use react_compiler_ast::common::BaseNode;
17 use react_compiler_ast::common::Position as AstPosition;
18 +use react_compiler_ast::common::RawNode;
19 use react_compiler_ast::common::SourceLocation as AstSourceLocation;
20 use react_compiler_ast::expressions::ArrowFunctionBody;
21 use react_compiler_ast::expressions::Expression;
@@ -1593,7 +1594,7 @@ fn codegen_unsupported_original_node(
1594 {
1595 return Ok(UnsupportedOriginalNode::ExpressionCodegen);
1596 }
1596 - let unknown = UnknownStatement::from_raw(node.clone()).map_err(|e| {
1597 + let unknown = UnknownStatement::from_raw(RawNode::from_value(node)).map_err(|e| {
1598 invariant_err(
1599 &format!("Failed to read unsupported original AST node: {}", e),
1600 None,
@@ -2526,7 +2527,7 @@ fn codegen_base_instruction_value(
2527 Expression::TSSatisfiesExpression(ast_expr::TSSatisfiesExpression {
2528 base: BaseNode::typed("TSSatisfiesExpression"),
2529 expression: Box::new(expr),
2529 - type_annotation: ta,
2530 + type_annotation: RawNode::from_value(&ta),
2531 })
2532 }
2533 (Some("as"), Some(ta)) => {
@@ -2535,7 +2536,7 @@ fn codegen_base_instruction_value(
2536 Expression::TSAsExpression(ast_expr::TSAsExpression {
2537 base: BaseNode::typed("TSAsExpression"),
2538 expression: Box::new(expr),
2538 - type_annotation: ta,
2539 + type_annotation: RawNode::from_value(&ta),
2540 })
2541 }
2542 (Some("cast"), Some(ta)) => {
@@ -2544,7 +2545,7 @@ fn codegen_base_instruction_value(
2545 Expression::TypeCastExpression(ast_expr::TypeCastExpression {
2546 base: BaseNode::typed("TypeCastExpression"),
2547 expression: Box::new(expr),
2547 - type_annotation: ta,
2548 + type_annotation: RawNode::from_value(&ta),
2549 })
2550 }
2551 _ => expr,
@@ -4297,7 +4298,7 @@ mod tests {
4298 match codegen_unsupported_original_node(&node).unwrap() {
4299 UnsupportedOriginalNode::Statement(Statement::Unknown(unknown)) => {
4300 assert_eq!(unknown.node_type(), "TSImportEqualsDeclaration");
4300 - assert_eq!(unknown.raw(), &node);
4301 + assert_eq!(unknown.raw().parse_value(), node);
4302 }
4303 UnsupportedOriginalNode::Statement(other) => {
4304 panic!("expected Statement::Unknown, got {other:?}")
compiler/crates/react_compiler_swc/src/convert_ast.rs
+40 -37
@@ -7,6 +7,7 @@ use react_compiler_ast::File;
7 use react_compiler_ast::Program;
8 use react_compiler_ast::SourceType;
9 use react_compiler_ast::common::BaseNode;
10 +use react_compiler_ast::common::RawNode;
11 use react_compiler_ast::common::Position;
12 use react_compiler_ast::common::SourceLocation;
13 use react_compiler_ast::declarations::*;
@@ -29,7 +30,8 @@ fn wtf8_to_string(value: &swc_atoms::Wtf8Atom) -> String {
30 /// constructs `raw` with a `type` tag, so `from_raw` cannot fail.
31 fn unknown_statement(raw: serde_json::Value) -> Statement {
32 Statement::Unknown(
32 - UnknownStatement::from_raw(raw).expect("raw unknown node is constructed with a `type` tag"),
33 + UnknownStatement::from_raw(RawNode::from_value(&raw))
34 + .expect("raw unknown node is constructed with a `type` tag"),
35 )
36 }
37
@@ -817,26 +819,26 @@ impl<'a> ConvertCtx<'a> {
819 swc::Expr::TsAs(e) => Expression::TSAsExpression(TSAsExpression {
820 base: self.make_base_node(e.span),
821 expression: Box::new(self.convert_expression(&e.expr)),
820 - type_annotation: Box::new(
821 - self.convert_ts_type_to_json(&e.type_ann)
822 + type_annotation: RawNode::from_value(
823 + &self.convert_ts_type_to_json(&e.type_ann)
824 .unwrap_or(serde_json::Value::Null),
823 - ),
825 + ),
826 }),
827 swc::Expr::TsSatisfies(e) => Expression::TSSatisfiesExpression(TSSatisfiesExpression {
828 base: self.make_base_node(e.span),
829 expression: Box::new(self.convert_expression(&e.expr)),
828 - type_annotation: Box::new(
829 - self.convert_ts_type_to_json(&e.type_ann)
830 + type_annotation: RawNode::from_value(
831 + &self.convert_ts_type_to_json(&e.type_ann)
832 .unwrap_or(serde_json::Value::Null),
831 - ),
833 + ),
834 }),
835 swc::Expr::TsTypeAssertion(e) => Expression::TSTypeAssertion(TSTypeAssertion {
836 base: self.make_base_node(e.span),
837 expression: Box::new(self.convert_expression(&e.expr)),
836 - type_annotation: Box::new(
837 - self.convert_ts_type_to_json(&e.type_ann)
838 + type_annotation: RawNode::from_value(
839 + &self.convert_ts_type_to_json(&e.type_ann)
840 .unwrap_or(serde_json::Value::Null),
839 - ),
841 + ),
842 }),
843 swc::Expr::TsNonNull(e) => Expression::TSNonNullExpression(TSNonNullExpression {
844 base: self.make_base_node(e.span),
@@ -846,7 +848,7 @@ impl<'a> ConvertCtx<'a> {
848 Expression::TSInstantiationExpression(TSInstantiationExpression {
849 base: self.make_base_node(e.span),
850 expression: Box::new(self.convert_expression(&e.expr)),
849 - type_parameters: Box::new(serde_json::Value::Null),
851 + type_parameters: RawNode::null(),
852 })
853 }
854 swc::Expr::TsConstAssertion(e) => {
@@ -862,7 +864,7 @@ impl<'a> ConvertCtx<'a> {
864 Expression::TSAsExpression(TSAsExpression {
865 base: self.make_base_node(e.span),
866 expression: Box::new(self.convert_expression(&e.expr)),
865 - type_annotation: Box::new(type_ann),
867 + type_annotation: RawNode::from_value(&type_ann),
868 })
869 }
870 swc::Expr::Invalid(i) => Expression::Identifier(Identifier {
@@ -1068,11 +1070,11 @@ impl<'a> ConvertCtx<'a> {
1070 return_type: f
1071 .return_type
1072 .as_ref()
1071 - .map(|_| Box::new(serde_json::Value::Null)),
1073 + .map(|_| RawNode::null()),
1074 type_parameters: f
1075 .type_params
1076 .as_ref()
1075 - .map(|_| Box::new(serde_json::Value::Null)),
1077 + .map(|_| RawNode::null()),
1078 predicate: None,
1079 component_declaration: false,
1080 hook_declaration: false,
@@ -1103,11 +1105,11 @@ impl<'a> ConvertCtx<'a> {
1105 return_type: f
1106 .return_type
1107 .as_ref()
1106 - .map(|_| Box::new(serde_json::Value::Null)),
1108 + .map(|_| RawNode::null()),
1109 type_parameters: f
1110 .type_params
1111 .as_ref()
1110 - .map(|_| Box::new(serde_json::Value::Null)),
1112 + .map(|_| RawNode::null()),
1113 predicate: None,
1114 }
1115 }
@@ -1133,11 +1135,11 @@ impl<'a> ConvertCtx<'a> {
1135 return_type: arrow
1136 .return_type
1137 .as_ref()
1136 - .map(|_| Box::new(serde_json::Value::Null)),
1138 + .map(|_| RawNode::null()),
1139 type_parameters: arrow
1140 .type_params
1141 .as_ref()
1140 - .map(|_| Box::new(serde_json::Value::Null)),
1142 + .map(|_| RawNode::null()),
1143 predicate: None,
1144 }
1145 }
@@ -1249,7 +1251,7 @@ impl<'a> ConvertCtx<'a> {
1251 type_annotation: obj
1252 .type_ann
1253 .as_ref()
1252 - .map(|_| Box::new(serde_json::Value::Null)),
1254 + .map(|_| RawNode::null()),
1255 decorators: None,
1256 }
1257 }
@@ -1265,7 +1267,7 @@ impl<'a> ConvertCtx<'a> {
1267 type_annotation: arr
1268 .type_ann
1269 .as_ref()
1268 - .map(|_| Box::new(serde_json::Value::Null)),
1270 + .map(|_| RawNode::null()),
1271 decorators: None,
1272 }
1273 }
@@ -1423,7 +1425,7 @@ impl<'a> ConvertCtx<'a> {
1425 return_type: g
1426 .type_ann
1427 .as_ref()
1426 - .map(|_| Box::new(serde_json::Value::Null)),
1428 + .map(|_| RawNode::null()),
1429 type_parameters: None,
1430 predicate: None,
1431 }),
@@ -1476,12 +1478,12 @@ impl<'a> ConvertCtx<'a> {
1478 .function
1479 .return_type
1480 .as_ref()
1479 - .map(|_| Box::new(serde_json::Value::Null)),
1481 + .map(|_| RawNode::null()),
1482 type_parameters: m
1483 .function
1484 .type_params
1485 .as_ref()
1484 - .map(|_| Box::new(serde_json::Value::Null)),
1486 + .map(|_| RawNode::null()),
1487 predicate: None,
1488 }),
1489 swc::Prop::Assign(a) => {
@@ -1561,7 +1563,7 @@ impl<'a> ConvertCtx<'a> {
1563 type_parameters: c
1564 .type_params
1565 .as_ref()
1564 - .map(|_| Box::new(serde_json::Value::Null)),
1566 + .map(|_| RawNode::null()),
1567 mixins: None,
1568 }
1569 }
@@ -1588,7 +1590,7 @@ impl<'a> ConvertCtx<'a> {
1590 type_parameters: c
1591 .type_params
1592 .as_ref()
1591 - .map(|_| Box::new(serde_json::Value::Null)),
1593 + .map(|_| RawNode::null()),
1594 }
1595 }
1596
@@ -1629,7 +1631,7 @@ impl<'a> ConvertCtx<'a> {
1631 type_parameters: el
1632 .type_args
1633 .as_ref()
1632 - .map(|_| Box::new(serde_json::Value::Null)),
1634 + .map(|_| RawNode::null()),
1635 }
1636 }
1637
@@ -1983,11 +1985,11 @@ impl<'a> ConvertCtx<'a> {
1985 return_type: func
1986 .return_type
1987 .as_ref()
1986 - .map(|_| Box::new(serde_json::Value::Null)),
1988 + .map(|_| RawNode::null()),
1989 type_parameters: func
1990 .type_params
1991 .as_ref()
1990 - .map(|_| Box::new(serde_json::Value::Null)),
1992 + .map(|_| RawNode::null()),
1993 predicate: None,
1994 component_declaration: false,
1995 hook_declaration: false,
@@ -2017,7 +2019,7 @@ impl<'a> ConvertCtx<'a> {
2019 type_parameters: class
2020 .type_params
2021 .as_ref()
2020 - .map(|_| Box::new(serde_json::Value::Null)),
2022 + .map(|_| RawNode::null()),
2023 mixins: None,
2024 })
2025 }
@@ -2139,11 +2141,11 @@ impl<'a> ConvertCtx<'a> {
2141 TSTypeAliasDeclaration {
2142 base: self.make_base_node(d.span),
2143 id: self.convert_ident_to_identifier(&d.id),
2142 - type_annotation: Box::new(serde_json::Value::Null),
2144 + type_annotation: RawNode::null(),
2145 type_parameters: d
2146 .type_params
2147 .as_ref()
2146 - .map(|_| Box::new(serde_json::Value::Null)),
2148 + .map(|_| RawNode::null()),
2149 declare: if d.declare { Some(true) } else { None },
2150 }
2151 }
@@ -2152,11 +2154,11 @@ impl<'a> ConvertCtx<'a> {
2154 TSInterfaceDeclaration {
2155 base: self.make_base_node(d.span),
2156 id: self.convert_ident_to_identifier(&d.id),
2155 - body: Box::new(serde_json::Value::Null),
2157 + body: RawNode::null(),
2158 type_parameters: d
2159 .type_params
2160 .as_ref()
2159 - .map(|_| Box::new(serde_json::Value::Null)),
2161 + .map(|_| RawNode::null()),
2162 extends: if d.extends.is_empty() {
2163 None
2164 } else {
@@ -2179,8 +2181,8 @@ impl<'a> ConvertCtx<'a> {
2181 fn convert_ts_module(&self, d: &swc::TsModuleDecl) -> TSModuleDeclaration {
2182 TSModuleDeclaration {
2183 base: self.make_base_node(d.span),
2182 - id: Box::new(serde_json::Value::Null),
2183 - body: Box::new(serde_json::Value::Null),
2184 + id: RawNode::null(),
2185 + body: RawNode::null(),
2186 declare: if d.declare { Some(true) } else { None },
2187 global: if d.global { Some(true) } else { None },
2188 }
@@ -2234,8 +2236,9 @@ impl<'a> ConvertCtx<'a> {
2236 base: self.make_base_node(id.id.span),
2237 name: id.id.sym.to_string(),
2238 type_annotation: id.type_ann.as_ref().map(|ann| {
2237 - Box::new(
2238 - self.convert_ts_type_ann_to_json(ann)
2239 + RawNode::from_value(
2240 + &self
2241 + .convert_ts_type_ann_to_json(ann)
2242 .unwrap_or(serde_json::Value::Null),
2243 )
2244 }),
compiler/crates/react_compiler_swc/src/convert_ast_reverse.rs
+8 -5
@@ -332,7 +332,8 @@ impl ReverseCtx {
332 &self,
333 unknown: &babel_stmt::UnknownStatement,
334 ) -> Option<ModuleDecl> {
335 - let raw = unknown.raw();
335 + let raw = unknown.raw().parse_value();
336 + let raw = &raw;
337 match unknown.node_type() {
338 "TSImportEqualsDeclaration" => {
339 let id = self.ident_from_raw(raw.get("id")?)?;
@@ -1103,11 +1104,12 @@ impl ReverseCtx {
1104 BabelExpr::TSAsExpression(e) => {
1105 let expr = Box::new(self.convert_expression(&e.expression));
1106 let span = self.span(&e.base);
1107 + let annotation = e.type_annotation.parse_value();
1108 // Check if this is "as const" — Babel represents it as
1109 // TSAsExpression with typeAnnotation: TSTypeReference { typeName: Identifier { name: "const" } }
1108 - let is_as_const = e.type_annotation.get("type").and_then(|v| v.as_str())
1110 + let is_as_const = annotation.get("type").and_then(|v| v.as_str())
1111 == Some("TSTypeReference")
1110 - && e.type_annotation
1112 + && annotation
1113 .get("typeName")
1114 .and_then(|tn| tn.get("name"))
1115 .and_then(|n| n.as_str())
@@ -1116,7 +1118,7 @@ impl ReverseCtx {
1118 if is_as_const {
1119 Expr::TsConstAssertion(TsConstAssertion { span, expr })
1120 } else {
1119 - let type_ann = self.convert_ts_type_from_json(&e.type_annotation, span);
1121 + let type_ann = self.convert_ts_type_from_json(&annotation, span);
1122 Expr::TsAs(TsAsExpr {
1123 span,
1124 expr,
@@ -1588,7 +1590,8 @@ impl ReverseCtx {
1590 bi.id.optional = id.optional.unwrap_or(false);
1591 // Preserve type annotations if present
1592 if let Some(ref type_ann) = id.type_annotation {
1591 - bi.type_ann = self.convert_ts_type_annotation_from_json(type_ann);
1593 + bi.type_ann =
1594 + self.convert_ts_type_annotation_from_json(&type_ann.parse_value());
1595 }
1596 Pat::Ident(bi)
1597 }