mirror of
https://github.com/hanzoai/react.git
synced 2026-09-24 17:02:27 +00:00
[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:
committed by
GitHub
co-authored by
Boshen
parent
496f32baab
commit
ed0367f4c0
Generated
+10
@@ -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) = ¶m.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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user