[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 <[email protected]>
This commit is contained in:
lauren
2026-06-10 20:19:43 -07:00
committed by GitHub
co-authored by Boshen
parent 496f32baab
commit ed0367f4c0
17 changed files with 437 additions and 278 deletions
+10
View File
@@ -1199,6 +1199,7 @@ version = "0.1.0"
dependencies = [
"indexmap",
"serde",
"serde-transcode",
"serde_json",
"similar",
"walkdir",
@@ -1469,6 +1470,15 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde-transcode"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "590c0e25c2a5bb6e85bf5c1bce768ceb86b316e7a01bdf07d2cb4ec2271990e2"
dependencies = [
"serde",
]
[[package]]
name = "serde_core"
version = "1.0.228"
@@ -790,7 +790,7 @@ fn calls_hooks_or_creates_jsx_in_class_body(
) -> bool {
body.body
.iter()
.any(|member| calls_hooks_or_creates_jsx_in_json(member))
.any(|member| calls_hooks_or_creates_jsx_in_json(&member.parse_value()))
}
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 {
/// Returns false for primitive type annotations that indicate this is NOT a component.
fn is_valid_props_annotation(param: &PatternLike) -> bool {
let type_annotation = match param {
PatternLike::Identifier(id) => id.type_annotation.as_deref(),
PatternLike::ObjectPattern(op) => op.type_annotation.as_deref(),
PatternLike::ArrayPattern(ap) => ap.type_annotation.as_deref(),
PatternLike::AssignmentPattern(ap) => ap.type_annotation.as_deref(),
PatternLike::RestElement(re) => re.type_annotation.as_deref(),
PatternLike::Identifier(id) => id.type_annotation.as_ref(),
PatternLike::ObjectPattern(op) => op.type_annotation.as_ref(),
PatternLike::ArrayPattern(ap) => ap.type_annotation.as_ref(),
PatternLike::AssignmentPattern(ap) => ap.type_annotation.as_ref(),
PatternLike::RestElement(re) => re.type_annotation.as_ref(),
PatternLike::MemberExpression(_)
| PatternLike::TSAsExpression(_)
| PatternLike::TSSatisfiesExpression(_)
@@ -943,7 +943,7 @@ fn is_valid_props_annotation(param: &PatternLike) -> bool {
| PatternLike::TypeCastExpression(_) => None,
};
let annot = match type_annotation {
Some(val) => val,
Some(raw) => raw.parse_value(),
None => return true, // No annotation = valid
};
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
// Unmodeled statements (e.g. `export = X`) can reference top-level
// bindings; scan the raw node for a matching Identifier so the
// gating reference-before-declaration analysis does not miss them.
Statement::Unknown(unknown) => raw_node_references_identifier(unknown.raw(), name),
Statement::Unknown(unknown) => {
raw_node_references_identifier(&unknown.raw().parse_value(), name)
}
_ => false,
}
}
@@ -5,7 +5,8 @@ edition = "2024"
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_json = { version = "1", features = ["raw_value", "unbounded_depth"] }
serde-transcode = "1"
indexmap = { version = "2", features = ["serde"] }
[dev-dependencies]
@@ -1,18 +1,101 @@
use serde::Deserialize;
use serde::Serialize;
/// An AST subtree the compiler does not model with typed nodes (type
/// annotations, class bodies, parser extras). Wraps JSON text: serialization
/// is verbatim pass-through and deserialization streams the subtree into text
/// without retaining a `serde_json::Value` tree. Consumers that inspect these
/// subtrees parse on demand via [`RawNode::parse_value`]; paths that do so
/// repeatedly per traversal pay a parse each time, so cache the parsed Value
/// at the call site if it shows up in profiles.
///
/// Deserialize is hand-implemented with a transcode rather than capturing a
/// `RawValue` directly: most nodes sit under `#[serde(tag = "type")]` enums,
/// whose content buffering breaks `RawValue`'s text-borrowing capture.
#[derive(Debug, Clone, Serialize)]
#[serde(transparent)]
pub struct RawNode(pub Box<serde_json::value::RawValue>);
impl<'de> serde::Deserialize<'de> for RawNode {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let mut buf = Vec::new();
let mut ser = serde_json::Serializer::new(&mut buf);
serde_transcode::transcode(deserializer, &mut ser).map_err(serde::de::Error::custom)?;
let text = String::from_utf8(buf).map_err(serde::de::Error::custom)?;
serde_json::value::RawValue::from_string(text)
.map(RawNode)
.map_err(serde::de::Error::custom)
}
}
impl RawNode {
pub fn from_value(value: &serde_json::Value) -> Self {
RawNode(
serde_json::value::RawValue::from_string(value.to_string())
.expect("serde_json::Value always serializes to valid JSON"),
)
}
pub fn null() -> Self {
RawNode(
serde_json::value::RawValue::from_string("null".to_string())
.expect("null is valid JSON"),
)
}
/// The raw JSON text of this subtree.
pub fn get(&self) -> &str {
self.0.get()
}
/// Parse the subtree into a `serde_json::Value` for structural inspection.
/// RawNode text is valid JSON by construction, so failure here means a
/// broken invariant, not bad input; fail loudly rather than degrade.
pub fn parse_value(&self) -> serde_json::Value {
from_json_str_unbounded(self.0.get())
.expect("RawNode holds valid JSON by construction")
}
/// The node's `"type"` field, without parsing the whole subtree into a Value.
pub fn type_name(&self) -> Option<String> {
#[derive(Deserialize)]
struct TypeProbe {
#[serde(rename = "type")]
type_name: Option<String>,
}
from_json_str_unbounded::<TypeProbe>(self.0.get())
.ok()
.and_then(|p| p.type_name)
}
}
/// Parse JSON text with serde_json's recursion limit disabled. Every internal
/// reparse of [`RawNode`] text must go through this: the napi entrypoint
/// deserializes arbitrarily deep ASTs with the limit disabled (on a 64MB
/// stack), and the tolerant statement path's reparses must not quietly
/// reintroduce the default limit.
pub fn from_json_str_unbounded<'de, T: serde::Deserialize<'de>>(
s: &'de str,
) -> serde_json::Result<T> {
let mut deserializer = serde_json::Deserializer::from_str(s);
deserializer.disable_recursion_limit();
T::deserialize(&mut deserializer)
}
/// Custom deserializer that distinguishes "field absent" from "field: null".
/// - JSON field absent → `None` (via `#[serde(default)]`)
/// - JSON field `null` → `Some(Value::Null)`
/// - JSON field with value → `Some(value)`
/// - JSON field `null` → `Some(RawNode("null"))`
/// - JSON field with value → `Some(raw value)`
///
/// Use with `#[serde(default, skip_serializing_if = "Option::is_none", deserialize_with = "nullable_value")]`
pub fn nullable_value<'de, D>(deserializer: D) -> Result<Option<Box<serde_json::Value>>, D::Error>
pub fn nullable_value<'de, D>(deserializer: D) -> Result<Option<RawNode>, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = serde_json::Value::deserialize(deserializer)?;
Ok(Some(Box::new(value)))
RawNode::deserialize(deserializer).map(Some)
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -75,7 +158,7 @@ pub struct BaseNode {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub range: Option<(u32, u32)>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extra: Option<serde_json::Value>,
pub extra: Option<RawNode>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
@@ -2,6 +2,7 @@ use serde::Deserialize;
use serde::Serialize;
use crate::common::BaseNode;
use crate::common::RawNode;
use crate::expressions::Expression;
use crate::expressions::Identifier;
use crate::literals::StringLiteral;
@@ -197,20 +198,20 @@ pub struct ExportAllDeclaration {
pub attributes: Option<Vec<ImportAttribute>>,
}
// TypeScript declarations (pass-through via serde_json::Value for bodies)
// TypeScript declarations (pass-through via RawNode for bodies)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TSTypeAliasDeclaration {
#[serde(flatten)]
pub base: BaseNode,
pub id: Identifier,
#[serde(rename = "typeAnnotation")]
pub type_annotation: Box<serde_json::Value>,
pub type_annotation: RawNode,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "typeParameters"
)]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub declare: Option<bool>,
}
@@ -220,15 +221,15 @@ pub struct TSInterfaceDeclaration {
#[serde(flatten)]
pub base: BaseNode,
pub id: Identifier,
pub body: Box<serde_json::Value>,
pub body: RawNode,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "typeParameters"
)]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extends: Option<Vec<serde_json::Value>>,
pub extends: Option<Vec<RawNode>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub declare: Option<bool>,
}
@@ -238,7 +239,7 @@ pub struct TSEnumDeclaration {
#[serde(flatten)]
pub base: BaseNode,
pub id: Identifier,
pub members: Vec<serde_json::Value>,
pub members: Vec<RawNode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub declare: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none", rename = "const")]
@@ -249,8 +250,8 @@ pub struct TSEnumDeclaration {
pub struct TSModuleDeclaration {
#[serde(flatten)]
pub base: BaseNode,
pub id: Box<serde_json::Value>,
pub body: Box<serde_json::Value>,
pub id: RawNode,
pub body: RawNode,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub declare: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
@@ -262,7 +263,7 @@ pub struct TSDeclareFunction {
#[serde(flatten)]
pub base: BaseNode,
pub id: Option<Identifier>,
pub params: Vec<serde_json::Value>,
pub params: Vec<RawNode>,
#[serde(default, skip_serializing_if = "Option::is_none", rename = "async")]
pub is_async: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
@@ -274,13 +275,13 @@ pub struct TSDeclareFunction {
skip_serializing_if = "Option::is_none",
rename = "returnType"
)]
pub return_type: Option<Box<serde_json::Value>>,
pub return_type: Option<RawNode>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "typeParameters"
)]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
}
// Flow declarations (pass-through)
@@ -289,9 +290,9 @@ pub struct TypeAlias {
#[serde(flatten)]
pub base: BaseNode,
pub id: Identifier,
pub right: Box<serde_json::Value>,
pub right: RawNode,
#[serde(default, rename = "typeParameters")]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -300,14 +301,14 @@ pub struct OpaqueType {
pub base: BaseNode,
pub id: Identifier,
#[serde(rename = "supertype")]
pub supertype: Option<Box<serde_json::Value>>,
pub impltype: Box<serde_json::Value>,
pub supertype: Option<RawNode>,
pub impltype: RawNode,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "typeParameters"
)]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -315,19 +316,19 @@ pub struct InterfaceDeclaration {
#[serde(flatten)]
pub base: BaseNode,
pub id: Identifier,
pub body: Box<serde_json::Value>,
pub body: RawNode,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "typeParameters"
)]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extends: Option<Vec<serde_json::Value>>,
pub extends: Option<Vec<RawNode>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mixins: Option<Vec<serde_json::Value>>,
pub mixins: Option<Vec<RawNode>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub implements: Option<Vec<serde_json::Value>>,
pub implements: Option<Vec<RawNode>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -347,7 +348,7 @@ pub struct DeclareFunction {
skip_serializing_if = "Option::is_none",
deserialize_with = "crate::common::nullable_value"
)]
pub predicate: Option<Box<serde_json::Value>>,
pub predicate: Option<RawNode>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -355,27 +356,27 @@ pub struct DeclareClass {
#[serde(flatten)]
pub base: BaseNode,
pub id: Identifier,
pub body: Box<serde_json::Value>,
pub body: RawNode,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "typeParameters"
)]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extends: Option<Vec<serde_json::Value>>,
pub extends: Option<Vec<RawNode>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mixins: Option<Vec<serde_json::Value>>,
pub mixins: Option<Vec<RawNode>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub implements: Option<Vec<serde_json::Value>>,
pub implements: Option<Vec<RawNode>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeclareModule {
#[serde(flatten)]
pub base: BaseNode,
pub id: Box<serde_json::Value>,
pub body: Box<serde_json::Value>,
pub id: RawNode,
pub body: RawNode,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub kind: Option<String>,
}
@@ -385,7 +386,7 @@ pub struct DeclareModuleExports {
#[serde(flatten)]
pub base: BaseNode,
#[serde(rename = "typeAnnotation")]
pub type_annotation: Box<serde_json::Value>,
pub type_annotation: RawNode,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -393,9 +394,9 @@ pub struct DeclareExportDeclaration {
#[serde(flatten)]
pub base: BaseNode,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub declaration: Option<Box<serde_json::Value>>,
pub declaration: Option<RawNode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub specifiers: Option<Vec<serde_json::Value>>,
pub specifiers: Option<Vec<RawNode>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source: Option<StringLiteral>,
#[serde(default, skip_serializing_if = "Option::is_none")]
@@ -414,19 +415,19 @@ pub struct DeclareInterface {
#[serde(flatten)]
pub base: BaseNode,
pub id: Identifier,
pub body: Box<serde_json::Value>,
pub body: RawNode,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "typeParameters"
)]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extends: Option<Vec<serde_json::Value>>,
pub extends: Option<Vec<RawNode>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mixins: Option<Vec<serde_json::Value>>,
pub mixins: Option<Vec<RawNode>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub implements: Option<Vec<serde_json::Value>>,
pub implements: Option<Vec<RawNode>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -434,13 +435,13 @@ pub struct DeclareTypeAlias {
#[serde(flatten)]
pub base: BaseNode,
pub id: Identifier,
pub right: Box<serde_json::Value>,
pub right: RawNode,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "typeParameters"
)]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -449,15 +450,15 @@ pub struct DeclareOpaqueType {
pub base: BaseNode,
pub id: Identifier,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub supertype: Option<Box<serde_json::Value>>,
pub supertype: Option<RawNode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub impltype: Option<Box<serde_json::Value>>,
pub impltype: Option<RawNode>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "typeParameters"
)]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -465,5 +466,5 @@ pub struct EnumDeclaration {
#[serde(flatten)]
pub base: BaseNode,
pub id: Identifier,
pub body: Box<serde_json::Value>,
pub body: RawNode,
}
@@ -2,6 +2,7 @@ use serde::Deserialize;
use serde::Serialize;
use crate::common::BaseNode;
use crate::common::RawNode;
use crate::jsx::JSXElement;
use crate::jsx::JSXFragment;
use crate::literals::*;
@@ -20,11 +21,11 @@ pub struct Identifier {
skip_serializing_if = "Option::is_none",
rename = "typeAnnotation"
)]
pub type_annotation: Option<Box<serde_json::Value>>,
pub type_annotation: Option<RawNode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub optional: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub decorators: Option<Vec<serde_json::Value>>,
pub decorators: Option<Vec<RawNode>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -91,13 +92,13 @@ pub struct CallExpression {
skip_serializing_if = "Option::is_none",
rename = "typeParameters"
)]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "typeArguments"
)]
pub type_arguments: Option<Box<serde_json::Value>>,
pub type_arguments: Option<RawNode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub optional: Option<bool>,
}
@@ -123,13 +124,13 @@ pub struct OptionalCallExpression {
skip_serializing_if = "Option::is_none",
rename = "typeParameters"
)]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "typeArguments"
)]
pub type_arguments: Option<Box<serde_json::Value>>,
pub type_arguments: Option<RawNode>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -222,20 +223,20 @@ pub struct ArrowFunctionExpression {
skip_serializing_if = "Option::is_none",
rename = "returnType"
)]
pub return_type: Option<Box<serde_json::Value>>,
pub return_type: Option<RawNode>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "typeParameters"
)]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "predicate",
deserialize_with = "crate::common::nullable_value"
)]
pub predicate: Option<Box<serde_json::Value>>,
pub predicate: Option<RawNode>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -263,20 +264,20 @@ pub struct FunctionExpression {
skip_serializing_if = "Option::is_none",
rename = "returnType"
)]
pub return_type: Option<Box<serde_json::Value>>,
pub return_type: Option<RawNode>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "typeParameters"
)]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "predicate",
deserialize_with = "crate::common::nullable_value"
)]
pub predicate: Option<Box<serde_json::Value>>,
pub predicate: Option<RawNode>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -303,7 +304,7 @@ pub struct ObjectProperty {
pub computed: bool,
pub shorthand: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub decorators: Option<Vec<serde_json::Value>>,
pub decorators: Option<Vec<RawNode>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub method: Option<bool>,
}
@@ -325,26 +326,26 @@ pub struct ObjectMethod {
#[serde(default, rename = "async")]
pub is_async: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub decorators: Option<Vec<serde_json::Value>>,
pub decorators: Option<Vec<RawNode>>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "returnType"
)]
pub return_type: Option<Box<serde_json::Value>>,
pub return_type: Option<RawNode>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "typeParameters"
)]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "predicate",
deserialize_with = "crate::common::nullable_value"
)]
pub predicate: Option<Box<serde_json::Value>>,
pub predicate: Option<RawNode>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -373,14 +374,14 @@ pub struct NewExpression {
skip_serializing_if = "Option::is_none",
rename = "typeParameters"
)]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "crate::common::nullable_value",
rename = "typeArguments"
)]
pub type_arguments: Option<Box<serde_json::Value>>,
pub type_arguments: Option<RawNode>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -402,7 +403,7 @@ pub struct TaggedTemplateExpression {
skip_serializing_if = "Option::is_none",
rename = "typeParameters"
)]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -446,32 +447,32 @@ pub struct ClassExpression {
pub super_class: Option<Box<Expression>>,
pub body: ClassBody,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub decorators: Option<Vec<serde_json::Value>>,
pub decorators: Option<Vec<RawNode>>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "implements"
)]
pub implements: Option<Vec<serde_json::Value>>,
pub implements: Option<Vec<RawNode>>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "superTypeParameters"
)]
pub super_type_parameters: Option<Box<serde_json::Value>>,
pub super_type_parameters: Option<RawNode>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "typeParameters"
)]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClassBody {
#[serde(flatten)]
pub base: BaseNode,
pub body: Vec<serde_json::Value>,
pub body: Vec<RawNode>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -506,14 +507,14 @@ pub struct ParenthesizedExpression {
pub expression: Box<Expression>,
}
// TypeScript expression nodes (pass-through with serde_json::Value for type args)
// TypeScript expression nodes (pass-through with RawNode for type args)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TSAsExpression {
#[serde(flatten)]
pub base: BaseNode,
pub expression: Box<Expression>,
#[serde(rename = "typeAnnotation")]
pub type_annotation: Box<serde_json::Value>,
pub type_annotation: RawNode,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -522,7 +523,7 @@ pub struct TSSatisfiesExpression {
pub base: BaseNode,
pub expression: Box<Expression>,
#[serde(rename = "typeAnnotation")]
pub type_annotation: Box<serde_json::Value>,
pub type_annotation: RawNode,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -538,7 +539,7 @@ pub struct TSTypeAssertion {
pub base: BaseNode,
pub expression: Box<Expression>,
#[serde(rename = "typeAnnotation")]
pub type_annotation: Box<serde_json::Value>,
pub type_annotation: RawNode,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -547,7 +548,7 @@ pub struct TSInstantiationExpression {
pub base: BaseNode,
pub expression: Box<Expression>,
#[serde(rename = "typeParameters")]
pub type_parameters: Box<serde_json::Value>,
pub type_parameters: RawNode,
}
// Flow expression nodes
@@ -557,5 +558,5 @@ pub struct TypeCastExpression {
pub base: BaseNode,
pub expression: Box<Expression>,
#[serde(rename = "typeAnnotation")]
pub type_annotation: Box<serde_json::Value>,
pub type_annotation: RawNode,
}
@@ -1,6 +1,7 @@
use serde::{Deserialize, Serialize};
use crate::common::BaseNode;
use crate::common::RawNode;
use crate::expressions::Expression;
use crate::literals::StringLiteral;
@@ -41,7 +42,7 @@ pub struct JSXOpeningElement {
skip_serializing_if = "Option::is_none",
rename = "typeParameters"
)]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -1,6 +1,7 @@
use serde::{Deserialize, Serialize};
use crate::common::BaseNode;
use crate::common::RawNode;
use crate::expressions::{Expression, Identifier};
/// Covers assignment targets and patterns.
@@ -34,9 +35,9 @@ pub struct ObjectPattern {
skip_serializing_if = "Option::is_none",
rename = "typeAnnotation"
)]
pub type_annotation: Option<Box<serde_json::Value>>,
pub type_annotation: Option<RawNode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub decorators: Option<Vec<serde_json::Value>>,
pub decorators: Option<Vec<RawNode>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -55,7 +56,7 @@ pub struct ObjectPatternProp {
pub computed: bool,
pub shorthand: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub decorators: Option<Vec<serde_json::Value>>,
pub decorators: Option<Vec<RawNode>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub method: Option<bool>,
}
@@ -70,9 +71,9 @@ pub struct ArrayPattern {
skip_serializing_if = "Option::is_none",
rename = "typeAnnotation"
)]
pub type_annotation: Option<Box<serde_json::Value>>,
pub type_annotation: Option<RawNode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub decorators: Option<Vec<serde_json::Value>>,
pub decorators: Option<Vec<RawNode>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -86,9 +87,9 @@ pub struct AssignmentPattern {
skip_serializing_if = "Option::is_none",
rename = "typeAnnotation"
)]
pub type_annotation: Option<Box<serde_json::Value>>,
pub type_annotation: Option<RawNode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub decorators: Option<Vec<serde_json::Value>>,
pub decorators: Option<Vec<RawNode>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -101,7 +102,7 @@ pub struct RestElement {
skip_serializing_if = "Option::is_none",
rename = "typeAnnotation"
)]
pub type_annotation: Option<Box<serde_json::Value>>,
pub type_annotation: Option<RawNode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub decorators: Option<Vec<serde_json::Value>>,
pub decorators: Option<Vec<RawNode>>,
}
@@ -2,6 +2,7 @@ use serde::de::Error as _;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::common::BaseNode;
use crate::common::RawNode;
use crate::expressions::{Expression, Identifier};
use crate::patterns::PatternLike;
@@ -86,17 +87,17 @@ pub enum Statement {
#[derive(Debug, Clone)]
pub struct UnknownStatement {
raw: serde_json::Value,
raw: RawNode,
base: BaseNode,
}
impl UnknownStatement {
pub fn from_raw(raw: serde_json::Value) -> Result<Self, String> {
match raw.get("type").and_then(serde_json::Value::as_str) {
pub fn from_raw(raw: RawNode) -> Result<Self, String> {
match raw.type_name() {
Some(_) => {
// By-ref deserialization clones only the fields BaseNode
// reads, not the whole (arbitrarily large) unknown subtree.
let base = BaseNode::deserialize(&raw)
// Parsing into BaseNode reads only the fields BaseNode declares,
// not the whole (arbitrarily large) unknown subtree.
let base = crate::common::from_json_str_unbounded::<BaseNode>(raw.get())
.map_err(|err| format!("failed to read unknown statement base: {err}"))?;
Ok(Self { raw, base })
}
@@ -111,7 +112,7 @@ impl UnknownStatement {
self.base.node_type.as_deref().unwrap_or("Unknown")
}
pub fn raw(&self) -> &serde_json::Value {
pub fn raw(&self) -> &RawNode {
&self.raw
}
@@ -120,15 +121,15 @@ impl UnknownStatement {
/// string `type` field are rejected and rolled back.
pub fn with_raw_mut<R>(
&mut self,
f: impl FnOnce(&mut serde_json::Value) -> R,
f: impl FnOnce(&mut RawNode) -> R,
) -> Result<R, String> {
let saved = self.raw.clone();
let result = f(&mut self.raw);
if self.raw.get("type").and_then(serde_json::Value::as_str).is_none() {
if self.raw.type_name().is_none() {
self.raw = saved;
return Err("unknown statement mutation removed the string `type` field".to_string());
}
match BaseNode::deserialize(&self.raw) {
match crate::common::from_json_str_unbounded::<BaseNode>(self.raw.get()) {
Ok(base) => {
self.base = base;
Ok(result)
@@ -159,7 +160,7 @@ impl<'de> Deserialize<'de> for UnknownStatement {
where
D: Deserializer<'de>,
{
let raw = serde_json::Value::deserialize(deserializer)?;
let raw = RawNode::deserialize(deserializer)?;
Self::from_raw(raw).map_err(D::Error::custom)
}
}
@@ -169,15 +170,14 @@ impl<'de> Deserialize<'de> for Statement {
where
D: Deserializer<'de>,
{
let raw = serde_json::Value::deserialize(deserializer)?;
let raw = RawNode::deserialize(deserializer)?;
let node_type = raw
.get("type")
.and_then(serde_json::Value::as_str)
.ok_or_else(|| D::Error::custom("statement is missing a string `type` field"))?
.to_string();
.type_name()
.ok_or_else(|| D::Error::custom("statement is missing a string `type` field"))?;
if is_known_statement_type(&node_type) {
let known: KnownStatement = serde_json::from_value(raw).map_err(D::Error::custom)?;
let known: KnownStatement =
crate::common::from_json_str_unbounded(raw.get()).map_err(D::Error::custom)?;
Ok(known.into())
} else {
UnknownStatement::from_raw(raw)
@@ -508,20 +508,20 @@ pub struct FunctionDeclaration {
skip_serializing_if = "Option::is_none",
rename = "returnType"
)]
pub return_type: Option<Box<serde_json::Value>>,
pub return_type: Option<RawNode>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "typeParameters"
)]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "predicate",
deserialize_with = "crate::common::nullable_value"
)]
pub predicate: Option<Box<serde_json::Value>>,
pub predicate: Option<RawNode>,
/// Set by the Hermes parser for Flow `component Foo(...) { ... }` syntax
#[serde(
default,
@@ -547,7 +547,7 @@ pub struct ClassDeclaration {
pub super_class: Option<Box<Expression>>,
pub body: crate::expressions::ClassBody,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub decorators: Option<Vec<serde_json::Value>>,
pub decorators: Option<Vec<RawNode>>,
#[serde(default, skip_serializing_if = "Option::is_none", rename = "abstract")]
pub is_abstract: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
@@ -557,21 +557,21 @@ pub struct ClassDeclaration {
skip_serializing_if = "Option::is_none",
rename = "implements"
)]
pub implements: Option<Vec<serde_json::Value>>,
pub implements: Option<Vec<RawNode>>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "superTypeParameters"
)]
pub super_type_parameters: Option<Box<serde_json::Value>>,
pub super_type_parameters: Option<RawNode>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename = "typeParameters"
)]
pub type_parameters: Option<Box<serde_json::Value>>,
pub type_parameters: Option<RawNode>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mixins: Option<Vec<serde_json::Value>>,
pub mixins: Option<Vec<RawNode>>,
}
#[cfg(test)]
@@ -579,6 +579,7 @@ mod tests {
use serde_json::json;
use super::Statement;
use crate::common::RawNode;
#[test]
fn unknown_statement_round_trips_at_program_level() {
@@ -730,15 +731,22 @@ mod tests {
unknown
.with_raw_mut(|v| {
v["start"] = json!(9);
v["expression"]["name"] = json!("y");
let mut parsed = v.parse_value();
parsed["start"] = json!(9);
parsed["expression"]["name"] = json!("y");
*v = RawNode::from_value(&parsed);
})
.unwrap();
assert_eq!(unknown.base().start, Some(9));
assert_eq!(unknown.raw()["expression"]["name"], json!("y"));
assert_eq!(
unknown.raw().parse_value()["expression"]["name"],
json!("y")
);
let err = unknown.with_raw_mut(|v| {
v.as_object_mut().unwrap().remove("type");
let mut parsed = v.parse_value();
parsed.as_object_mut().unwrap().remove("type");
*v = RawNode::from_value(&parsed);
});
assert!(err.is_err(), "type removal must be rejected");
}
@@ -0,0 +1,37 @@
//! Deep ASTs must survive deserialization when the caller disables
//! serde_json's recursion limit, as the napi entrypoint does. The tolerant
//! statement deserializer reparses captured raw text internally; those
//! reparses must not reintroduce the default depth limit.
use react_compiler_ast::File;
use serde::Deserialize;
fn from_json_str_unbounded(s: &str) -> serde_json::Result<File> {
let mut deserializer = serde_json::Deserializer::from_str(s);
deserializer.disable_recursion_limit();
File::deserialize(&mut deserializer)
}
#[test]
fn statement_nested_beyond_default_recursion_limit_deserializes() {
let depth = 400;
let mut expr = r#"{"type":"Identifier","name":"x"}"#.to_string();
for _ in 0..depth {
expr = format!(
r#"{{"type":"CallExpression","callee":{expr},"arguments":[]}}"#
);
}
let json = format!(
r#"{{"type":"File","program":{{"type":"Program","sourceType":"module","body":[{{"type":"ExpressionStatement","expression":{expr}}}],"directives":[]}}}}"#
);
// Parse on a large stack like the napi entrypoint does; without the limit,
// depth is bounded by stack, not by serde_json's counter.
let file = std::thread::Builder::new()
.stack_size(64 * 1024 * 1024)
.spawn(move || from_json_str_unbounded(&json).expect("deep statement must deserialize"))
.expect("spawn")
.join()
.expect("join");
assert_eq!(file.program.body.len(), 1);
}
@@ -311,15 +311,21 @@ fn visit_json(val: &mut serde_json::Value, si: &ScopeInfo) {
}
}
fn visit_json_vec(vals: &mut [serde_json::Value], si: &ScopeInfo) {
fn visit_raw(val: &mut react_compiler_ast::common::RawNode, si: &ScopeInfo) {
let mut v = val.parse_value();
visit_json(&mut v, si);
*val = react_compiler_ast::common::RawNode::from_value(&v);
}
fn visit_json_vec(vals: &mut [react_compiler_ast::common::RawNode], si: &ScopeInfo) {
for val in vals.iter_mut() {
visit_json(val, si);
visit_raw(val, si);
}
}
fn visit_json_opt(val: &mut Option<Box<serde_json::Value>>, si: &ScopeInfo) {
fn visit_json_opt(val: &mut Option<react_compiler_ast::common::RawNode>, si: &ScopeInfo) {
if let Some(v) = val {
visit_json(v, si);
visit_raw(v, si);
}
}
@@ -425,12 +431,12 @@ fn visit_stmt(stmt: &mut Statement, si: &ScopeInfo) {
Statement::ExportDefaultDeclaration(d) => visit_export_default(d, si),
Statement::TSTypeAliasDeclaration(d) => {
rename_id(&mut d.id, si);
visit_json(&mut d.type_annotation, si);
visit_raw(&mut d.type_annotation, si);
visit_json_opt(&mut d.type_parameters, si);
}
Statement::TSInterfaceDeclaration(d) => {
rename_id(&mut d.id, si);
visit_json(&mut d.body, si);
visit_raw(&mut d.body, si);
visit_json_opt(&mut d.type_parameters, si);
if let Some(ext) = &mut d.extends {
visit_json_vec(ext, si);
@@ -441,8 +447,8 @@ fn visit_stmt(stmt: &mut Statement, si: &ScopeInfo) {
visit_json_vec(&mut d.members, si);
}
Statement::TSModuleDeclaration(d) => {
visit_json(&mut d.id, si);
visit_json(&mut d.body, si);
visit_raw(&mut d.id, si);
visit_raw(&mut d.body, si);
}
Statement::TSDeclareFunction(d) => {
if let Some(id) = &mut d.id {
@@ -454,20 +460,20 @@ fn visit_stmt(stmt: &mut Statement, si: &ScopeInfo) {
}
Statement::TypeAlias(d) => {
rename_id(&mut d.id, si);
visit_json(&mut d.right, si);
visit_raw(&mut d.right, si);
visit_json_opt(&mut d.type_parameters, si);
}
Statement::OpaqueType(d) => {
rename_id(&mut d.id, si);
if let Some(st) = &mut d.supertype {
visit_json(st, si);
visit_raw(st, si);
}
visit_json(&mut d.impltype, si);
visit_raw(&mut d.impltype, si);
visit_json_opt(&mut d.type_parameters, si);
}
Statement::InterfaceDeclaration(d) => {
rename_id(&mut d.id, si);
visit_json(&mut d.body, si);
visit_raw(&mut d.body, si);
visit_json_opt(&mut d.type_parameters, si);
if let Some(ext) = &mut d.extends {
visit_json_vec(ext, si);
@@ -477,25 +483,25 @@ fn visit_stmt(stmt: &mut Statement, si: &ScopeInfo) {
Statement::DeclareFunction(d) => {
rename_id(&mut d.id, si);
if let Some(pred) = &mut d.predicate {
visit_json(pred, si);
visit_raw(pred, si);
}
}
Statement::DeclareClass(d) => {
rename_id(&mut d.id, si);
visit_json(&mut d.body, si);
visit_raw(&mut d.body, si);
visit_json_opt(&mut d.type_parameters, si);
if let Some(ext) = &mut d.extends {
visit_json_vec(ext, si);
}
}
Statement::DeclareModule(d) => {
visit_json(&mut d.id, si);
visit_json(&mut d.body, si);
visit_raw(&mut d.id, si);
visit_raw(&mut d.body, si);
}
Statement::DeclareModuleExports(d) => visit_json(&mut d.type_annotation, si),
Statement::DeclareModuleExports(d) => visit_raw(&mut d.type_annotation, si),
Statement::DeclareExportDeclaration(d) => {
if let Some(decl) = &mut d.declaration {
visit_json(decl, si);
visit_raw(decl, si);
}
if let Some(specs) = &mut d.specifiers {
visit_json_vec(specs, si);
@@ -503,7 +509,7 @@ fn visit_stmt(stmt: &mut Statement, si: &ScopeInfo) {
}
Statement::DeclareInterface(d) => {
rename_id(&mut d.id, si);
visit_json(&mut d.body, si);
visit_raw(&mut d.body, si);
visit_json_opt(&mut d.type_parameters, si);
if let Some(ext) = &mut d.extends {
visit_json_vec(ext, si);
@@ -511,25 +517,25 @@ fn visit_stmt(stmt: &mut Statement, si: &ScopeInfo) {
}
Statement::DeclareTypeAlias(d) => {
rename_id(&mut d.id, si);
visit_json(&mut d.right, si);
visit_raw(&mut d.right, si);
visit_json_opt(&mut d.type_parameters, si);
}
Statement::DeclareOpaqueType(d) => {
rename_id(&mut d.id, si);
if let Some(st) = &mut d.supertype {
visit_json(st, si);
visit_raw(st, si);
}
if let Some(impl_) = &mut d.impltype {
visit_json(impl_, si);
visit_raw(impl_, si);
}
visit_json_opt(&mut d.type_parameters, si);
}
Statement::EnumDeclaration(d) => {
rename_id(&mut d.id, si);
visit_json(&mut d.body, si);
visit_raw(&mut d.body, si);
}
Statement::Unknown(d) => {
d.with_raw_mut(|raw| visit_json(raw, si))
d.with_raw_mut(|raw| visit_raw(raw, si))
.expect("identifier rename preserves the node `type`");
}
Statement::BreakStatement(_)
@@ -701,24 +707,24 @@ fn visit_expr(expr: &mut Expression, si: &ScopeInfo) {
}
Expression::TSAsExpression(e) => {
visit_expr(&mut e.expression, si);
visit_json(&mut e.type_annotation, si);
visit_raw(&mut e.type_annotation, si);
}
Expression::TSSatisfiesExpression(e) => {
visit_expr(&mut e.expression, si);
visit_json(&mut e.type_annotation, si);
visit_raw(&mut e.type_annotation, si);
}
Expression::TSNonNullExpression(e) => visit_expr(&mut e.expression, si),
Expression::TSTypeAssertion(e) => {
visit_expr(&mut e.expression, si);
visit_json(&mut e.type_annotation, si);
visit_raw(&mut e.type_annotation, si);
}
Expression::TSInstantiationExpression(e) => {
visit_expr(&mut e.expression, si);
visit_json(&mut e.type_parameters, si);
visit_raw(&mut e.type_parameters, si);
}
Expression::TypeCastExpression(e) => {
visit_expr(&mut e.expression, si);
visit_json(&mut e.type_annotation, si);
visit_raw(&mut e.type_annotation, si);
}
Expression::JSXElement(e) => visit_jsx_element(e, si),
Expression::JSXFragment(f) => {
@@ -779,22 +785,22 @@ fn visit_pat(pat: &mut PatternLike, si: &ScopeInfo) {
}
PatternLike::TSAsExpression(e) => {
visit_expr(&mut e.expression, si);
visit_json(&mut e.type_annotation, si);
visit_raw(&mut e.type_annotation, si);
}
PatternLike::TSSatisfiesExpression(e) => {
visit_expr(&mut e.expression, si);
visit_json(&mut e.type_annotation, si);
visit_raw(&mut e.type_annotation, si);
}
PatternLike::TSNonNullExpression(e) => {
visit_expr(&mut e.expression, si);
}
PatternLike::TSTypeAssertion(e) => {
visit_expr(&mut e.expression, si);
visit_json(&mut e.type_annotation, si);
visit_raw(&mut e.type_annotation, si);
}
PatternLike::TypeCastExpression(e) => {
visit_expr(&mut e.expression, si);
visit_json(&mut e.type_annotation, si);
visit_raw(&mut e.type_annotation, si);
}
}
}
@@ -893,12 +899,12 @@ fn visit_declaration(d: &mut Declaration, si: &ScopeInfo) {
Declaration::VariableDeclaration(v) => visit_var_decl(v, si),
Declaration::TSTypeAliasDeclaration(d) => {
rename_id(&mut d.id, si);
visit_json(&mut d.type_annotation, si);
visit_raw(&mut d.type_annotation, si);
visit_json_opt(&mut d.type_parameters, si);
}
Declaration::TSInterfaceDeclaration(d) => {
rename_id(&mut d.id, si);
visit_json(&mut d.body, si);
visit_raw(&mut d.body, si);
visit_json_opt(&mut d.type_parameters, si);
if let Some(ext) = &mut d.extends {
visit_json_vec(ext, si);
@@ -909,8 +915,8 @@ fn visit_declaration(d: &mut Declaration, si: &ScopeInfo) {
visit_json_vec(&mut d.members, si);
}
Declaration::TSModuleDeclaration(d) => {
visit_json(&mut d.id, si);
visit_json(&mut d.body, si);
visit_raw(&mut d.id, si);
visit_raw(&mut d.body, si);
}
Declaration::TSDeclareFunction(d) => {
if let Some(id) = &mut d.id {
@@ -922,20 +928,20 @@ fn visit_declaration(d: &mut Declaration, si: &ScopeInfo) {
}
Declaration::TypeAlias(d) => {
rename_id(&mut d.id, si);
visit_json(&mut d.right, si);
visit_raw(&mut d.right, si);
visit_json_opt(&mut d.type_parameters, si);
}
Declaration::OpaqueType(d) => {
rename_id(&mut d.id, si);
if let Some(st) = &mut d.supertype {
visit_json(st, si);
visit_raw(st, si);
}
visit_json(&mut d.impltype, si);
visit_raw(&mut d.impltype, si);
visit_json_opt(&mut d.type_parameters, si);
}
Declaration::InterfaceDeclaration(d) => {
rename_id(&mut d.id, si);
visit_json(&mut d.body, si);
visit_raw(&mut d.body, si);
visit_json_opt(&mut d.type_parameters, si);
if let Some(ext) = &mut d.extends {
visit_json_vec(ext, si);
@@ -943,7 +949,7 @@ fn visit_declaration(d: &mut Declaration, si: &ScopeInfo) {
}
Declaration::EnumDeclaration(d) => {
rename_id(&mut d.id, si);
visit_json(&mut d.body, si);
visit_raw(&mut d.body, si);
}
}
}
@@ -260,9 +260,9 @@ fn expression_type_name(expr: &react_compiler_ast::expressions::Expression) -> &
/// or { "type": "TypeAnnotation", "typeAnnotation": { "type": "GenericTypeAnnotation", ... } }
/// We extract the inner typeAnnotation's `type` field name.
fn extract_type_annotation_name(
type_annotation: &Option<Box<serde_json::Value>>,
type_annotation: &Option<react_compiler_ast::common::RawNode>,
) -> Option<String> {
let val = type_annotation.as_ref()?;
let val = type_annotation.as_ref()?.parse_value();
// Navigate: typeAnnotation.typeAnnotation.type
let inner = val.get("typeAnnotation")?;
let type_name = inner.get("type")?.as_str()?;
@@ -2205,30 +2205,30 @@ fn lower_expression(
Expression::TSAsExpression(ts) => {
let loc = convert_opt_loc(&ts.base.loc);
let value = lower_expression_to_temporary(builder, &ts.expression)?;
let type_annotation = &*ts.type_annotation;
let type_ = lower_type_annotation(type_annotation, builder);
let type_annotation_name = get_type_annotation_name(type_annotation);
let type_annotation = ts.type_annotation.parse_value();
let type_ = lower_type_annotation(&type_annotation, builder);
let type_annotation_name = get_type_annotation_name(&type_annotation);
Ok(InstructionValue::TypeCastExpression {
value,
type_,
type_annotation_name,
type_annotation_kind: Some("as".to_string()),
type_annotation: Some(ts.type_annotation.clone()),
type_annotation: Some(Box::new(type_annotation)),
loc,
})
}
Expression::TSSatisfiesExpression(ts) => {
let loc = convert_opt_loc(&ts.base.loc);
let value = lower_expression_to_temporary(builder, &ts.expression)?;
let type_annotation = &*ts.type_annotation;
let type_ = lower_type_annotation(type_annotation, builder);
let type_annotation_name = get_type_annotation_name(type_annotation);
let type_annotation = ts.type_annotation.parse_value();
let type_ = lower_type_annotation(&type_annotation, builder);
let type_annotation_name = get_type_annotation_name(&type_annotation);
Ok(InstructionValue::TypeCastExpression {
value,
type_,
type_annotation_name,
type_annotation_kind: Some("satisfies".to_string()),
type_annotation: Some(ts.type_annotation.clone()),
type_annotation: Some(Box::new(type_annotation)),
loc,
})
}
@@ -2236,15 +2236,15 @@ fn lower_expression(
Expression::TSTypeAssertion(ts) => {
let loc = convert_opt_loc(&ts.base.loc);
let value = lower_expression_to_temporary(builder, &ts.expression)?;
let type_annotation = &*ts.type_annotation;
let type_ = lower_type_annotation(type_annotation, builder);
let type_annotation_name = get_type_annotation_name(type_annotation);
let type_annotation = ts.type_annotation.parse_value();
let type_ = lower_type_annotation(&type_annotation, builder);
let type_annotation_name = get_type_annotation_name(&type_annotation);
Ok(InstructionValue::TypeCastExpression {
value,
type_,
type_annotation_name,
type_annotation_kind: Some("as".to_string()),
type_annotation: Some(ts.type_annotation.clone()),
type_annotation: Some(Box::new(type_annotation)),
loc,
})
}
@@ -2252,11 +2252,11 @@ fn lower_expression(
Expression::TypeCastExpression(tc) => {
let loc = convert_opt_loc(&tc.base.loc);
let value = lower_expression_to_temporary(builder, &tc.expression)?;
let annotation_value = tc.type_annotation.parse_value();
// Flow TypeCastExpression: typeAnnotation is a TypeAnnotation node wrapping the actual type
let inner_type = tc
.type_annotation
let inner_type = annotation_value
.get("typeAnnotation")
.unwrap_or(&*tc.type_annotation);
.unwrap_or(&annotation_value);
let type_ = lower_type_annotation(inner_type, builder);
let type_annotation_name = get_type_annotation_name(inner_type);
Ok(InstructionValue::TypeCastExpression {
@@ -2264,7 +2264,7 @@ fn lower_expression(
type_,
type_annotation_name,
type_annotation_kind: Some("cast".to_string()),
type_annotation: Some(tc.type_annotation.clone()),
type_annotation: Some(Box::new(annotation_value)),
loc,
})
}
@@ -4231,7 +4231,7 @@ fn lower_statement(
builder,
InstructionValue::UnsupportedNode {
node_type: Some(node_type),
original_node: Some(unknown.raw().clone()),
original_node: Some(unknown.raw().parse_value()),
loc,
},
)?;
@@ -239,7 +239,7 @@ impl<'ast> Visitor<'ast> for IdentifierLocVisitor {
// The typed AstWalker skips class bodies (stored as Vec<serde_json::Value>),
// but gatherCapturedContext in TS traverses them via Babel's traverse.
for member in &node.body.body {
self.walk_json_for_identifiers(member, false);
self.walk_json_for_identifiers(&member.parse_value(), false);
}
}
@@ -253,7 +253,7 @@ impl<'ast> Visitor<'ast> for IdentifierLocVisitor {
}
// Walk class body JSON to index identifiers inside class methods
for member in &node.body.body {
self.walk_json_for_identifiers(member, false);
self.walk_json_for_identifiers(&member.parse_value(), false);
}
}
}
@@ -10,6 +10,7 @@ use react_compiler_ast::File;
use react_compiler_ast::Program;
use react_compiler_ast::SourceType;
use react_compiler_ast::common::BaseNode;
use react_compiler_ast::common::RawNode;
use react_compiler_ast::common::Comment;
use react_compiler_ast::common::CommentData;
use react_compiler_ast::common::Position;
@@ -594,11 +595,11 @@ impl<'a> ConvertCtx<'a> {
return_type: func
.return_type
.as_ref()
.map(|_t| Box::new(serde_json::Value::Null)),
.map(|_t| RawNode::null()),
type_parameters: func
.type_parameters
.as_ref()
.map(|_t| Box::new(serde_json::Value::Null)),
.map(|_t| RawNode::null()),
predicate: None,
component_declaration: false,
hook_declaration: false,
@@ -622,7 +623,7 @@ impl<'a> ConvertCtx<'a> {
.body
.body
.iter()
.map(|_item| serde_json::Value::Null)
.map(|_item| RawNode::null())
.collect(),
},
decorators: if class.decorators.is_empty() {
@@ -632,7 +633,7 @@ impl<'a> ConvertCtx<'a> {
class
.decorators
.iter()
.map(|_d| serde_json::Value::Null)
.map(|_d| RawNode::null())
.collect(),
)
},
@@ -645,18 +646,18 @@ impl<'a> ConvertCtx<'a> {
class
.implements
.iter()
.map(|_i| serde_json::Value::Null)
.map(|_i| RawNode::null())
.collect(),
)
},
super_type_parameters: class
.super_type_arguments
.as_ref()
.map(|_t| Box::new(serde_json::Value::Null)),
.map(|_t| RawNode::null()),
type_parameters: class
.type_parameters
.as_ref()
.map(|_t| Box::new(serde_json::Value::Null)),
.map(|_t| RawNode::null()),
mixins: None,
}
}
@@ -942,11 +943,11 @@ impl<'a> ConvertCtx<'a> {
TSTypeAliasDeclaration {
base: self.make_base_node(type_alias.span),
id: self.convert_binding_identifier(&type_alias.id),
type_annotation: Box::new(serde_json::Value::Null),
type_annotation: RawNode::null(),
type_parameters: type_alias
.type_parameters
.as_ref()
.map(|_t| Box::new(serde_json::Value::Null)),
.map(|_t| RawNode::null()),
declare: if type_alias.declare { Some(true) } else { None },
}
}
@@ -958,11 +959,11 @@ impl<'a> ConvertCtx<'a> {
TSInterfaceDeclaration {
base: self.make_base_node(interface.span),
id: self.convert_binding_identifier(&interface.id),
body: Box::new(serde_json::Value::Null),
body: RawNode::null(),
type_parameters: interface
.type_parameters
.as_ref()
.map(|_t| Box::new(serde_json::Value::Null)),
.map(|_t| RawNode::null()),
extends: if interface.extends.is_empty() {
None
} else {
@@ -970,7 +971,7 @@ impl<'a> ConvertCtx<'a> {
interface
.extends
.iter()
.map(|_e| serde_json::Value::Null)
.map(|_e| RawNode::null())
.collect(),
)
},
@@ -986,7 +987,7 @@ impl<'a> ConvertCtx<'a> {
.body
.members
.iter()
.map(|_m| serde_json::Value::Null)
.map(|_m| RawNode::null())
.collect(),
declare: if ts_enum.declare { Some(true) } else { None },
is_const: if ts_enum.r#const { Some(true) } else { None },
@@ -999,8 +1000,8 @@ impl<'a> ConvertCtx<'a> {
) -> TSModuleDeclaration {
TSModuleDeclaration {
base: self.make_base_node(module.span),
id: Box::new(serde_json::Value::Null),
body: Box::new(serde_json::Value::Null),
id: RawNode::null(),
body: RawNode::null(),
declare: if module.declare { Some(true) } else { None },
global: None,
}
@@ -1267,11 +1268,11 @@ impl<'a> ConvertCtx<'a> {
return_type: arrow
.return_type
.as_ref()
.map(|_t| Box::new(serde_json::Value::Null)),
.map(|_t| RawNode::null()),
type_parameters: arrow
.type_parameters
.as_ref()
.map(|_t| Box::new(serde_json::Value::Null)),
.map(|_t| RawNode::null()),
predicate: None,
}
}
@@ -1607,7 +1608,7 @@ impl<'a> ConvertCtx<'a> {
type_parameters: call
.type_arguments
.as_ref()
.map(|_t| Box::new(serde_json::Value::Null)),
.map(|_t| RawNode::null()),
type_arguments: None,
optional: if call.optional { Some(true) } else { None },
}
@@ -1639,7 +1640,7 @@ impl<'a> ConvertCtx<'a> {
type_parameters: c
.type_arguments
.as_ref()
.map(|_t| Box::new(serde_json::Value::Null)),
.map(|_t| RawNode::null()),
type_arguments: None,
})
}
@@ -1728,7 +1729,7 @@ impl<'a> ConvertCtx<'a> {
type_parameters: c
.type_arguments
.as_ref()
.map(|_t| Box::new(serde_json::Value::Null)),
.map(|_t| RawNode::null()),
type_arguments: None,
})
}
@@ -1770,7 +1771,7 @@ impl<'a> ConvertCtx<'a> {
type_parameters: c
.type_arguments
.as_ref()
.map(|_t| Box::new(serde_json::Value::Null)),
.map(|_t| RawNode::null()),
type_arguments: None,
})
}
@@ -1821,7 +1822,7 @@ impl<'a> ConvertCtx<'a> {
.body
.body
.iter()
.map(|_item| serde_json::Value::Null)
.map(|_item| RawNode::null())
.collect(),
},
decorators: if class.decorators.is_empty() {
@@ -1831,7 +1832,7 @@ impl<'a> ConvertCtx<'a> {
class
.decorators
.iter()
.map(|_d| serde_json::Value::Null)
.map(|_d| RawNode::null())
.collect(),
)
},
@@ -1842,18 +1843,18 @@ impl<'a> ConvertCtx<'a> {
class
.implements
.iter()
.map(|_i| serde_json::Value::Null)
.map(|_i| RawNode::null())
.collect(),
)
},
super_type_parameters: class
.super_type_arguments
.as_ref()
.map(|_t| Box::new(serde_json::Value::Null)),
.map(|_t| RawNode::null()),
type_parameters: class
.type_parameters
.as_ref()
.map(|_t| Box::new(serde_json::Value::Null)),
.map(|_t| RawNode::null()),
}
}
@@ -1883,11 +1884,11 @@ impl<'a> ConvertCtx<'a> {
return_type: func
.return_type
.as_ref()
.map(|_t| Box::new(serde_json::Value::Null)),
.map(|_t| RawNode::null()),
type_parameters: func
.type_parameters
.as_ref()
.map(|_t| Box::new(serde_json::Value::Null)),
.map(|_t| RawNode::null()),
predicate: None,
}
}
@@ -1921,7 +1922,7 @@ impl<'a> ConvertCtx<'a> {
type_parameters: new
.type_arguments
.as_ref()
.map(|_t| Box::new(serde_json::Value::Null)),
.map(|_t| RawNode::null()),
type_arguments: None,
}
}
@@ -1980,11 +1981,11 @@ impl<'a> ConvertCtx<'a> {
return_type: func
.return_type
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
type_parameters: func
.type_parameters
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
predicate: None,
});
}
@@ -2063,7 +2064,7 @@ impl<'a> ConvertCtx<'a> {
type_parameters: tagged
.type_arguments
.as_ref()
.map(|_t| Box::new(serde_json::Value::Null)),
.map(|_t| RawNode::null()),
}
}
@@ -2185,7 +2186,7 @@ impl<'a> ConvertCtx<'a> {
TSAsExpression {
base: self.make_base_node(ts_as.span),
expression: Box::new(self.convert_expression(&ts_as.expression)),
type_annotation: Box::new(serde_json::Value::Null),
type_annotation: RawNode::null(),
}
}
@@ -2196,7 +2197,7 @@ impl<'a> ConvertCtx<'a> {
TSSatisfiesExpression {
base: self.make_base_node(ts_sat.span),
expression: Box::new(self.convert_expression(&ts_sat.expression)),
type_annotation: Box::new(serde_json::Value::Null),
type_annotation: RawNode::null(),
}
}
@@ -2204,7 +2205,7 @@ impl<'a> ConvertCtx<'a> {
TSTypeAssertion {
base: self.make_base_node(ts_assert.span),
expression: Box::new(self.convert_expression(&ts_assert.expression)),
type_annotation: Box::new(serde_json::Value::Null),
type_annotation: RawNode::null(),
}
}
@@ -2225,7 +2226,7 @@ impl<'a> ConvertCtx<'a> {
TSInstantiationExpression {
base: self.make_base_node(ts_inst.span),
expression: Box::new(self.convert_expression(&ts_inst.expression)),
type_parameters: Box::new(serde_json::Value::Null),
type_parameters: RawNode::null(),
}
}
@@ -2278,7 +2279,7 @@ impl<'a> ConvertCtx<'a> {
type_parameters: opening
.type_arguments
.as_ref()
.map(|_t| Box::new(serde_json::Value::Null)),
.map(|_t| RawNode::null()),
}
}
@@ -2620,7 +2621,7 @@ impl<'a> ConvertCtx<'a> {
type_annotation: rest
.type_annotation
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
decorators: None,
}));
}
@@ -2632,7 +2633,7 @@ impl<'a> ConvertCtx<'a> {
// Add type annotation if present (now on FormalParameter, not BindingPattern)
if let Some(_type_annotation) = &param.type_annotation {
let type_json = Box::new(serde_json::Value::Null);
let type_json = RawNode::null();
match &mut pattern {
PatternLike::Identifier(id) => {
id.type_annotation = Some(type_json);
@@ -15,6 +15,7 @@ use std::collections::HashSet;
use react_compiler_ast::common::BaseNode;
use react_compiler_ast::common::Position as AstPosition;
use react_compiler_ast::common::RawNode;
use react_compiler_ast::common::SourceLocation as AstSourceLocation;
use react_compiler_ast::expressions::ArrowFunctionBody;
use react_compiler_ast::expressions::Expression;
@@ -1593,7 +1594,7 @@ fn codegen_unsupported_original_node(
{
return Ok(UnsupportedOriginalNode::ExpressionCodegen);
}
let unknown = UnknownStatement::from_raw(node.clone()).map_err(|e| {
let unknown = UnknownStatement::from_raw(RawNode::from_value(node)).map_err(|e| {
invariant_err(
&format!("Failed to read unsupported original AST node: {}", e),
None,
@@ -2526,7 +2527,7 @@ fn codegen_base_instruction_value(
Expression::TSSatisfiesExpression(ast_expr::TSSatisfiesExpression {
base: BaseNode::typed("TSSatisfiesExpression"),
expression: Box::new(expr),
type_annotation: ta,
type_annotation: RawNode::from_value(&ta),
})
}
(Some("as"), Some(ta)) => {
@@ -2535,7 +2536,7 @@ fn codegen_base_instruction_value(
Expression::TSAsExpression(ast_expr::TSAsExpression {
base: BaseNode::typed("TSAsExpression"),
expression: Box::new(expr),
type_annotation: ta,
type_annotation: RawNode::from_value(&ta),
})
}
(Some("cast"), Some(ta)) => {
@@ -2544,7 +2545,7 @@ fn codegen_base_instruction_value(
Expression::TypeCastExpression(ast_expr::TypeCastExpression {
base: BaseNode::typed("TypeCastExpression"),
expression: Box::new(expr),
type_annotation: ta,
type_annotation: RawNode::from_value(&ta),
})
}
_ => expr,
@@ -4297,7 +4298,7 @@ mod tests {
match codegen_unsupported_original_node(&node).unwrap() {
UnsupportedOriginalNode::Statement(Statement::Unknown(unknown)) => {
assert_eq!(unknown.node_type(), "TSImportEqualsDeclaration");
assert_eq!(unknown.raw(), &node);
assert_eq!(unknown.raw().parse_value(), node);
}
UnsupportedOriginalNode::Statement(other) => {
panic!("expected Statement::Unknown, got {other:?}")
@@ -7,6 +7,7 @@ use react_compiler_ast::File;
use react_compiler_ast::Program;
use react_compiler_ast::SourceType;
use react_compiler_ast::common::BaseNode;
use react_compiler_ast::common::RawNode;
use react_compiler_ast::common::Position;
use react_compiler_ast::common::SourceLocation;
use react_compiler_ast::declarations::*;
@@ -29,7 +30,8 @@ fn wtf8_to_string(value: &swc_atoms::Wtf8Atom) -> String {
/// constructs `raw` with a `type` tag, so `from_raw` cannot fail.
fn unknown_statement(raw: serde_json::Value) -> Statement {
Statement::Unknown(
UnknownStatement::from_raw(raw).expect("raw unknown node is constructed with a `type` tag"),
UnknownStatement::from_raw(RawNode::from_value(&raw))
.expect("raw unknown node is constructed with a `type` tag"),
)
}
@@ -817,26 +819,26 @@ impl<'a> ConvertCtx<'a> {
swc::Expr::TsAs(e) => Expression::TSAsExpression(TSAsExpression {
base: self.make_base_node(e.span),
expression: Box::new(self.convert_expression(&e.expr)),
type_annotation: Box::new(
self.convert_ts_type_to_json(&e.type_ann)
type_annotation: RawNode::from_value(
&self.convert_ts_type_to_json(&e.type_ann)
.unwrap_or(serde_json::Value::Null),
),
),
}),
swc::Expr::TsSatisfies(e) => Expression::TSSatisfiesExpression(TSSatisfiesExpression {
base: self.make_base_node(e.span),
expression: Box::new(self.convert_expression(&e.expr)),
type_annotation: Box::new(
self.convert_ts_type_to_json(&e.type_ann)
type_annotation: RawNode::from_value(
&self.convert_ts_type_to_json(&e.type_ann)
.unwrap_or(serde_json::Value::Null),
),
),
}),
swc::Expr::TsTypeAssertion(e) => Expression::TSTypeAssertion(TSTypeAssertion {
base: self.make_base_node(e.span),
expression: Box::new(self.convert_expression(&e.expr)),
type_annotation: Box::new(
self.convert_ts_type_to_json(&e.type_ann)
type_annotation: RawNode::from_value(
&self.convert_ts_type_to_json(&e.type_ann)
.unwrap_or(serde_json::Value::Null),
),
),
}),
swc::Expr::TsNonNull(e) => Expression::TSNonNullExpression(TSNonNullExpression {
base: self.make_base_node(e.span),
@@ -846,7 +848,7 @@ impl<'a> ConvertCtx<'a> {
Expression::TSInstantiationExpression(TSInstantiationExpression {
base: self.make_base_node(e.span),
expression: Box::new(self.convert_expression(&e.expr)),
type_parameters: Box::new(serde_json::Value::Null),
type_parameters: RawNode::null(),
})
}
swc::Expr::TsConstAssertion(e) => {
@@ -862,7 +864,7 @@ impl<'a> ConvertCtx<'a> {
Expression::TSAsExpression(TSAsExpression {
base: self.make_base_node(e.span),
expression: Box::new(self.convert_expression(&e.expr)),
type_annotation: Box::new(type_ann),
type_annotation: RawNode::from_value(&type_ann),
})
}
swc::Expr::Invalid(i) => Expression::Identifier(Identifier {
@@ -1068,11 +1070,11 @@ impl<'a> ConvertCtx<'a> {
return_type: f
.return_type
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
type_parameters: f
.type_params
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
predicate: None,
component_declaration: false,
hook_declaration: false,
@@ -1103,11 +1105,11 @@ impl<'a> ConvertCtx<'a> {
return_type: f
.return_type
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
type_parameters: f
.type_params
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
predicate: None,
}
}
@@ -1133,11 +1135,11 @@ impl<'a> ConvertCtx<'a> {
return_type: arrow
.return_type
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
type_parameters: arrow
.type_params
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
predicate: None,
}
}
@@ -1249,7 +1251,7 @@ impl<'a> ConvertCtx<'a> {
type_annotation: obj
.type_ann
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
decorators: None,
}
}
@@ -1265,7 +1267,7 @@ impl<'a> ConvertCtx<'a> {
type_annotation: arr
.type_ann
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
decorators: None,
}
}
@@ -1423,7 +1425,7 @@ impl<'a> ConvertCtx<'a> {
return_type: g
.type_ann
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
type_parameters: None,
predicate: None,
}),
@@ -1476,12 +1478,12 @@ impl<'a> ConvertCtx<'a> {
.function
.return_type
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
type_parameters: m
.function
.type_params
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
predicate: None,
}),
swc::Prop::Assign(a) => {
@@ -1561,7 +1563,7 @@ impl<'a> ConvertCtx<'a> {
type_parameters: c
.type_params
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
mixins: None,
}
}
@@ -1588,7 +1590,7 @@ impl<'a> ConvertCtx<'a> {
type_parameters: c
.type_params
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
}
}
@@ -1629,7 +1631,7 @@ impl<'a> ConvertCtx<'a> {
type_parameters: el
.type_args
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
}
}
@@ -1983,11 +1985,11 @@ impl<'a> ConvertCtx<'a> {
return_type: func
.return_type
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
type_parameters: func
.type_params
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
predicate: None,
component_declaration: false,
hook_declaration: false,
@@ -2017,7 +2019,7 @@ impl<'a> ConvertCtx<'a> {
type_parameters: class
.type_params
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
mixins: None,
})
}
@@ -2139,11 +2141,11 @@ impl<'a> ConvertCtx<'a> {
TSTypeAliasDeclaration {
base: self.make_base_node(d.span),
id: self.convert_ident_to_identifier(&d.id),
type_annotation: Box::new(serde_json::Value::Null),
type_annotation: RawNode::null(),
type_parameters: d
.type_params
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
declare: if d.declare { Some(true) } else { None },
}
}
@@ -2152,11 +2154,11 @@ impl<'a> ConvertCtx<'a> {
TSInterfaceDeclaration {
base: self.make_base_node(d.span),
id: self.convert_ident_to_identifier(&d.id),
body: Box::new(serde_json::Value::Null),
body: RawNode::null(),
type_parameters: d
.type_params
.as_ref()
.map(|_| Box::new(serde_json::Value::Null)),
.map(|_| RawNode::null()),
extends: if d.extends.is_empty() {
None
} else {
@@ -2179,8 +2181,8 @@ impl<'a> ConvertCtx<'a> {
fn convert_ts_module(&self, d: &swc::TsModuleDecl) -> TSModuleDeclaration {
TSModuleDeclaration {
base: self.make_base_node(d.span),
id: Box::new(serde_json::Value::Null),
body: Box::new(serde_json::Value::Null),
id: RawNode::null(),
body: RawNode::null(),
declare: if d.declare { Some(true) } else { None },
global: if d.global { Some(true) } else { None },
}
@@ -2234,8 +2236,9 @@ impl<'a> ConvertCtx<'a> {
base: self.make_base_node(id.id.span),
name: id.id.sym.to_string(),
type_annotation: id.type_ann.as_ref().map(|ann| {
Box::new(
self.convert_ts_type_ann_to_json(ann)
RawNode::from_value(
&self
.convert_ts_type_ann_to_json(ann)
.unwrap_or(serde_json::Value::Null),
)
}),
@@ -332,7 +332,8 @@ impl ReverseCtx {
&self,
unknown: &babel_stmt::UnknownStatement,
) -> Option<ModuleDecl> {
let raw = unknown.raw();
let raw = unknown.raw().parse_value();
let raw = &raw;
match unknown.node_type() {
"TSImportEqualsDeclaration" => {
let id = self.ident_from_raw(raw.get("id")?)?;
@@ -1103,11 +1104,12 @@ impl ReverseCtx {
BabelExpr::TSAsExpression(e) => {
let expr = Box::new(self.convert_expression(&e.expression));
let span = self.span(&e.base);
let annotation = e.type_annotation.parse_value();
// Check if this is "as const" — Babel represents it as
// TSAsExpression with typeAnnotation: TSTypeReference { typeName: Identifier { name: "const" } }
let is_as_const = e.type_annotation.get("type").and_then(|v| v.as_str())
let is_as_const = annotation.get("type").and_then(|v| v.as_str())
== Some("TSTypeReference")
&& e.type_annotation
&& annotation
.get("typeName")
.and_then(|tn| tn.get("name"))
.and_then(|n| n.as_str())
@@ -1116,7 +1118,7 @@ impl ReverseCtx {
if is_as_const {
Expr::TsConstAssertion(TsConstAssertion { span, expr })
} else {
let type_ann = self.convert_ts_type_from_json(&e.type_annotation, span);
let type_ann = self.convert_ts_type_from_json(&annotation, span);
Expr::TsAs(TsAsExpr {
span,
expr,
@@ -1588,7 +1590,8 @@ impl ReverseCtx {
bi.id.optional = id.optional.unwrap_or(false);
// Preserve type annotations if present
if let Some(ref type_ann) = id.type_annotation {
bi.type_ann = self.convert_ts_type_annotation_from_json(type_ann);
bi.type_ann =
self.convert_ts_type_annotation_from_json(&type_ann.parse_value());
}
Pat::Ident(bi)
}