Skip to main content

react_compiler_swc/
convert_ast_reverse.rs

1// Copyright (c) Meta Platforms, Inc. and affiliates.
2//
3// This source code is licensed under the MIT license found in the
4// LICENSE file in the root directory of this source tree.
5
6//! Reverse AST converter: react_compiler_ast (Babel format) → SWC AST.
7//!
8//! This is the inverse of `convert_ast.rs`. It takes a `react_compiler_ast::File`
9//! (which represents the compiler's Babel-compatible output) and produces SWC AST
10//! nodes suitable for code generation via `swc_codegen`.
11
12use react_compiler_ast::common::BaseNode;
13use react_compiler_ast::common::Comment as BabelComment;
14use react_compiler_ast::declarations::ExportAllDeclaration;
15use react_compiler_ast::declarations::ExportDefaultDecl as BabelExportDefaultDecl;
16use react_compiler_ast::declarations::ExportDefaultDeclaration;
17use react_compiler_ast::declarations::ExportKind;
18use react_compiler_ast::declarations::ExportNamedDeclaration;
19use react_compiler_ast::declarations::ImportDeclaration;
20use react_compiler_ast::declarations::ImportKind;
21use react_compiler_ast::expressions::Expression as BabelExpr;
22use react_compiler_ast::expressions::{self as babel_expr};
23use react_compiler_ast::operators::*;
24use react_compiler_ast::patterns::*;
25use react_compiler_ast::statements::Statement as BabelStmt;
26use react_compiler_ast::statements::{self as babel_stmt};
27use swc_atoms::Atom;
28use swc_atoms::Wtf8Atom;
29use swc_common::BytePos;
30use swc_common::DUMMY_SP;
31use swc_common::Span;
32use swc_common::Spanned;
33use swc_common::SyntaxContext;
34use swc_common::comments::Comment as SwcComment;
35use swc_common::comments::CommentKind;
36use swc_common::comments::Comments;
37use swc_common::comments::SingleThreadedComments;
38use swc_ecma_ast::*;
39
40/// Result of converting a Babel AST back to SWC, including extracted comments.
41pub struct SwcConversionResult {
42    pub module: Module,
43    pub comments: SingleThreadedComments,
44}
45
46/// Convert a `react_compiler_ast::File` into an SWC `Module` and extracted comments.
47pub fn convert_program_to_swc(file: &react_compiler_ast::File) -> SwcConversionResult {
48    convert_program_to_swc_with_source(file, None)
49}
50
51/// Convert a `react_compiler_ast::File` into an SWC `Module` and extracted comments.
52/// When `source_text` is provided, type declarations can be extracted from the
53/// original source for perfect fidelity.
54pub fn convert_program_to_swc_with_source(
55    file: &react_compiler_ast::File,
56    source_text: Option<&str>,
57) -> SwcConversionResult {
58    let ctx = ReverseCtx {
59        comments: SingleThreadedComments::default(),
60        source_text: source_text.map(|s| s.to_string()),
61    };
62    let module = ctx.convert_program(&file.program);
63    SwcConversionResult {
64        module,
65        comments: ctx.comments,
66    }
67}
68
69struct ReverseCtx {
70    comments: SingleThreadedComments,
71    source_text: Option<String>,
72}
73
74impl ReverseCtx {
75    /// Convert a BaseNode's start/end to an SWC Span, and extract any comments.
76    fn span(&self, base: &BaseNode) -> Span {
77        let span = match (base.start, base.end) {
78            (Some(start), Some(end)) => Span::new(BytePos(start), BytePos(end)),
79            _ => DUMMY_SP,
80        };
81        self.extract_comments(base, span);
82        span
83    }
84
85    /// Convert a BaseNode's start/end to an SWC Span without extracting comments.
86    /// Use this for sub-nodes where comments should not be duplicated.
87    fn span_no_comments(&self, base: &BaseNode) -> Span {
88        match (base.start, base.end) {
89            (Some(start), Some(end)) => Span::new(BytePos(start), BytePos(end)),
90            _ => DUMMY_SP,
91        }
92    }
93
94    /// Convert a Babel comment to an SWC comment.
95    fn convert_babel_comment(babel_comment: &BabelComment) -> SwcComment {
96        let (kind, text) = match babel_comment {
97            BabelComment::CommentBlock(data) => (CommentKind::Block, &data.value),
98            BabelComment::CommentLine(data) => (CommentKind::Line, &data.value),
99        };
100        SwcComment {
101            kind,
102            span: DUMMY_SP,
103            text: Atom::from(text.as_str()),
104        }
105    }
106
107    /// Extract comments from a BaseNode and register them with the SWC comments store.
108    fn extract_comments(&self, base: &BaseNode, span: Span) {
109        if let Some(ref leading) = base.leading_comments {
110            let pos = span.lo;
111            for c in leading {
112                self.comments
113                    .add_leading(pos, Self::convert_babel_comment(c));
114            }
115        }
116        if let Some(ref trailing) = base.trailing_comments {
117            let pos = span.hi;
118            for c in trailing {
119                self.comments
120                    .add_trailing(pos, Self::convert_babel_comment(c));
121            }
122        }
123        if let Some(ref inner) = base.inner_comments {
124            // Inner comments are typically leading comments of the next token
125            let pos = span.lo;
126            for c in inner {
127                self.comments
128                    .add_leading(pos, Self::convert_babel_comment(c));
129            }
130        }
131    }
132
133    fn atom(&self, s: &str) -> Atom {
134        Atom::from(s)
135    }
136
137    fn wtf8(&self, s: &str) -> Wtf8Atom {
138        Wtf8Atom::from(s)
139    }
140
141    /// Escape non-ASCII characters and special characters (like tab) in a string
142    /// value to \uXXXX or \xXX sequences, matching Babel's codegen output.
143    /// Returns the raw string representation wrapped in double quotes.
144    fn escape_string_raw(&self, value: &str) -> Option<Atom> {
145        let mut needs_escape = false;
146        for ch in value.chars() {
147            if !ch.is_ascii() || ch == '\t' || ch == '\'' || ch == '"' || ch == '\\' {
148                needs_escape = true;
149                break;
150            }
151        }
152        if !needs_escape {
153            return None;
154        }
155        let mut escaped = String::with_capacity(value.len() + 16);
156        escaped.push('"');
157        for ch in value.chars() {
158            match ch {
159                '"' => escaped.push_str("\\\""),
160                '\\' => escaped.push_str("\\\\"),
161                '\n' => escaped.push_str("\\n"),
162                '\r' => escaped.push_str("\\r"),
163                '\t' => escaped.push_str("\\t"),
164                c if !c.is_ascii() => {
165                    // Encode using \uXXXX (or surrogate pairs for chars > U+FFFF)
166                    let mut buf = [0u16; 2];
167                    let encoded = c.encode_utf16(&mut buf);
168                    for unit in encoded {
169                        escaped.push_str(&format!("\\u{:04X}", unit));
170                    }
171                }
172                c => escaped.push(c),
173            }
174        }
175        escaped.push('"');
176        Some(Atom::from(escaped.as_str()))
177    }
178
179    /// Extract the original source text for a node and re-parse it as a
180    /// statement using SWC's TypeScript parser. This is used for type
181    /// declarations (type aliases, interfaces, enums) that the compiler
182    /// preserves verbatim from the original source.
183    fn extract_source_stmt(&self, base: &react_compiler_ast::common::BaseNode) -> Option<Stmt> {
184        let source = self.source_text.as_deref()?;
185        let start = base.start? as usize;
186        let end = base.end? as usize;
187        // SWC BytePos is 1-based
188        let start_idx = start.saturating_sub(1);
189        let end_idx = end.saturating_sub(1);
190        if start_idx >= source.len() || end_idx > source.len() || start_idx >= end_idx {
191            return None;
192        }
193        let text = &source[start_idx..end_idx];
194        self.parse_ts_stmt(text, base)
195    }
196
197    /// Parse a string as a TypeScript statement using SWC's parser.
198    fn parse_ts_stmt(
199        &self,
200        text: &str,
201        base: &react_compiler_ast::common::BaseNode,
202    ) -> Option<Stmt> {
203        let cm = swc_common::sync::Lrc::new(swc_common::SourceMap::default());
204        let fm = cm.new_source_file(
205            swc_common::sync::Lrc::new(swc_common::FileName::Anon),
206            text.to_string(),
207        );
208        let mut errors = vec![];
209        let module = swc_ecma_parser::parse_file_as_module(
210            &fm,
211            swc_ecma_parser::Syntax::Typescript(swc_ecma_parser::TsSyntax {
212                tsx: true,
213                ..Default::default()
214            }),
215            swc_ecma_ast::EsVersion::latest(),
216            None,
217            &mut errors,
218        )
219        .ok()?;
220
221        if let Some(item) = module.body.into_iter().next() {
222            match item {
223                ModuleItem::Stmt(stmt) => {
224                    // Assign the original span so blank line computation works
225                    let span = self.span(base);
226                    return Some(self.assign_span_to_stmt(stmt, span));
227                }
228                ModuleItem::ModuleDecl(_) => {}
229            }
230        }
231        None
232    }
233
234    /// Assign a span to a statement's outermost node.
235    fn assign_span_to_stmt(&self, stmt: Stmt, span: Span) -> Stmt {
236        match stmt {
237            Stmt::Decl(Decl::TsTypeAlias(mut d)) => {
238                d.span = span;
239                Stmt::Decl(Decl::TsTypeAlias(d))
240            }
241            Stmt::Decl(Decl::TsInterface(mut d)) => {
242                d.span = span;
243                Stmt::Decl(Decl::TsInterface(d))
244            }
245            Stmt::Decl(Decl::TsEnum(mut d)) => {
246                d.span = span;
247                Stmt::Decl(Decl::TsEnum(d))
248            }
249            other => other,
250        }
251    }
252
253    fn ident(&self, name: &str, span: Span) -> Ident {
254        Ident {
255            sym: self.atom(name),
256            span,
257            ctxt: SyntaxContext::empty(),
258            optional: false,
259        }
260    }
261
262    fn ident_name(&self, name: &str, span: Span) -> IdentName {
263        IdentName {
264            sym: self.atom(name),
265            span,
266        }
267    }
268
269    fn binding_ident(&self, name: &str, span: Span) -> BindingIdent {
270        BindingIdent {
271            id: self.ident(name, span),
272            type_ann: None,
273        }
274    }
275
276    // ===== Program =====
277
278    fn convert_program(&self, program: &react_compiler_ast::Program) -> Module {
279        let mut body: Vec<ModuleItem> = Vec::new();
280
281        // Convert directives to expression statements at the beginning
282        for dir in &program.directives {
283            let span = self.span(&dir.base);
284            let str_span = self.span(&dir.value.base);
285            body.push(ModuleItem::Stmt(Stmt::Expr(ExprStmt {
286                span,
287                expr: Box::new(Expr::Lit(Lit::Str(Str {
288                    span: str_span,
289                    value: self.wtf8(&dir.value.value),
290                    raw: None,
291                }))),
292            })));
293        }
294
295        for s in &program.body {
296            body.push(self.convert_statement_to_module_item(s));
297        }
298
299        Module {
300            span: DUMMY_SP,
301            body,
302            shebang: None,
303        }
304    }
305
306    fn convert_statement_to_module_item(&self, stmt: &BabelStmt) -> ModuleItem {
307        match stmt {
308            BabelStmt::ImportDeclaration(d) => {
309                ModuleItem::ModuleDecl(ModuleDecl::Import(self.convert_import_declaration(d)))
310            }
311            BabelStmt::ExportNamedDeclaration(d) => self.convert_export_named_to_module_item(d),
312            BabelStmt::ExportDefaultDeclaration(d) => self.convert_export_default_to_module_item(d),
313            BabelStmt::ExportAllDeclaration(d) => ModuleItem::ModuleDecl(ModuleDecl::ExportAll(
314                self.convert_export_all_declaration(d),
315            )),
316            // The preserved TS module-interop statements are module
317            // declarations in swc, which `convert_statement` (returning
318            // `Stmt`) cannot represent, so they are rebuilt here.
319            BabelStmt::Unknown(unknown) => match self.convert_unknown_to_module_decl(unknown) {
320                Some(decl) => ModuleItem::ModuleDecl(decl),
321                None => ModuleItem::Stmt(self.convert_statement(stmt)),
322            },
323            _ => ModuleItem::Stmt(self.convert_statement(stmt)),
324        }
325    }
326
327    /// Rebuild the TS module-interop statements carried as raw
328    /// [`BabelStmt::Unknown`] nodes. `None` (other node types, malformed raw
329    /// JSON) routes the caller to the runtime-throw tripwire; silently
330    /// dropping the statement is the failure mode this path exists to avoid.
331    fn convert_unknown_to_module_decl(
332        &self,
333        unknown: &babel_stmt::UnknownStatement,
334    ) -> Option<ModuleDecl> {
335        let raw = unknown.raw().parse_value();
336        let raw = &raw;
337        match unknown.node_type() {
338            "TSImportEqualsDeclaration" => {
339                let id = self.ident_from_raw(raw.get("id")?)?;
340                let module_ref = self.ts_module_ref_from_raw(raw.get("moduleReference")?)?;
341                // Some Babel versions omit `importKind` for value imports,
342                // so an absent key defaults to value.
343                let is_type_only = match raw.get("importKind") {
344                    None => false,
345                    Some(kind) => match kind.as_str() {
346                        Some("type") => true,
347                        Some("value") => false,
348                        _ => return None,
349                    },
350                };
351                let is_export = match raw.get("isExport") {
352                    None => false,
353                    Some(value) => value.as_bool()?,
354                };
355                Some(ModuleDecl::TsImportEquals(Box::new(TsImportEqualsDecl {
356                    span: self.span(unknown.base()),
357                    is_export,
358                    is_type_only,
359                    id,
360                    module_ref,
361                })))
362            }
363            "TSExportAssignment" => {
364                let expr: BabelExpr =
365                    serde_json::from_value(raw.get("expression")?.clone()).ok()?;
366                Some(ModuleDecl::TsExportAssignment(TsExportAssignment {
367                    span: self.span(unknown.base()),
368                    expr: Box::new(self.convert_expression(&expr)),
369                }))
370            }
371            "TSNamespaceExportDeclaration" => {
372                let id = self.ident_from_raw(raw.get("id")?)?;
373                Some(ModuleDecl::TsNamespaceExport(TsNamespaceExportDecl {
374                    span: self.span(unknown.base()),
375                    id,
376                }))
377            }
378            _ => None,
379        }
380    }
381
382    fn ident_from_raw(&self, raw: &serde_json::Value) -> Option<Ident> {
383        if raw.get("type").and_then(serde_json::Value::as_str) != Some("Identifier") {
384            return None;
385        }
386        let id: babel_expr::Identifier = serde_json::from_value(raw.clone()).ok()?;
387        Some(self.ident(&id.name, self.span_no_comments(&id.base)))
388    }
389
390    fn ts_module_ref_from_raw(&self, raw: &serde_json::Value) -> Option<TsModuleRef> {
391        match raw.get("type").and_then(serde_json::Value::as_str)? {
392            "TSExternalModuleReference" => {
393                let expr = raw.get("expression")?;
394                if expr.get("type").and_then(serde_json::Value::as_str) != Some("StringLiteral") {
395                    return None;
396                }
397                let lit: react_compiler_ast::literals::StringLiteral =
398                    serde_json::from_value(expr.clone()).ok()?;
399                let ref_base: BaseNode = serde_json::from_value(raw.clone()).ok()?;
400                Some(TsModuleRef::TsExternalModuleRef(TsExternalModuleRef {
401                    span: self.span_no_comments(&ref_base),
402                    expr: Str {
403                        span: self.span_no_comments(&lit.base),
404                        value: self.wtf8(&lit.value),
405                        raw: None,
406                    },
407                }))
408            }
409            "TSQualifiedName" | "Identifier" => self
410                .ts_entity_name_from_raw(raw)
411                .map(TsModuleRef::TsEntityName),
412            _ => None,
413        }
414    }
415
416    fn ts_entity_name_from_raw(&self, raw: &serde_json::Value) -> Option<TsEntityName> {
417        match raw.get("type").and_then(serde_json::Value::as_str)? {
418            "Identifier" => self.ident_from_raw(raw).map(TsEntityName::Ident),
419            "TSQualifiedName" => {
420                let base: BaseNode = serde_json::from_value(raw.clone()).ok()?;
421                let left = self.ts_entity_name_from_raw(raw.get("left")?)?;
422                let right: babel_expr::Identifier =
423                    serde_json::from_value(raw.get("right")?.clone()).ok()?;
424                Some(TsEntityName::TsQualifiedName(Box::new(TsQualifiedName {
425                    span: self.span_no_comments(&base),
426                    left,
427                    right: self.ident_name(&right.name, self.span_no_comments(&right.base)),
428                })))
429            }
430            _ => None,
431        }
432    }
433
434    // ===== Statements =====
435
436    fn convert_statement(&self, stmt: &BabelStmt) -> Stmt {
437        match stmt {
438            BabelStmt::BlockStatement(s) => Stmt::Block(self.convert_block_statement(s)),
439            BabelStmt::ReturnStatement(s) => Stmt::Return(ReturnStmt {
440                span: self.span(&s.base),
441                arg: s
442                    .argument
443                    .as_ref()
444                    .map(|a| Box::new(self.convert_expression(a))),
445            }),
446            BabelStmt::ExpressionStatement(s) => {
447                let expr = self.convert_expression(&s.expression);
448                // Wrap in parens if the expression starts with `{` (object pattern
449                // in assignment) or `function` (IIFE), which would be ambiguous
450                // with a block statement or function declaration.
451                let needs_paren = match &expr {
452                    Expr::Assign(a) => {
453                        matches!(&a.left, AssignTarget::Pat(AssignTargetPat::Object(_)))
454                    }
455                    Expr::Call(c) => match &c.callee {
456                        Callee::Expr(e) => matches!(e.as_ref(), Expr::Fn(_)),
457                        _ => false,
458                    },
459                    _ => false,
460                };
461                let expr = if needs_paren {
462                    Expr::Paren(ParenExpr {
463                        span: self.span_no_comments(&s.base),
464                        expr: Box::new(expr),
465                    })
466                } else {
467                    expr
468                };
469                Stmt::Expr(ExprStmt {
470                    span: self.span(&s.base),
471                    expr: Box::new(expr),
472                })
473            }
474            BabelStmt::IfStatement(s) => Stmt::If(IfStmt {
475                span: self.span(&s.base),
476                test: Box::new(self.convert_expression(&s.test)),
477                cons: Box::new(self.convert_statement(&s.consequent)),
478                alt: s
479                    .alternate
480                    .as_ref()
481                    .map(|a| Box::new(self.convert_statement(a))),
482            }),
483            BabelStmt::ForStatement(s) => {
484                let init = s.init.as_ref().map(|i| self.convert_for_init(i));
485                let test = s
486                    .test
487                    .as_ref()
488                    .map(|t| Box::new(self.convert_expression(t)));
489                let update = s
490                    .update
491                    .as_ref()
492                    .map(|u| Box::new(self.convert_expression(u)));
493                let body = Box::new(self.convert_statement(&s.body));
494                Stmt::For(ForStmt {
495                    span: self.span(&s.base),
496                    init,
497                    test,
498                    update,
499                    body,
500                })
501            }
502            BabelStmt::WhileStatement(s) => Stmt::While(WhileStmt {
503                span: self.span(&s.base),
504                test: Box::new(self.convert_expression(&s.test)),
505                body: Box::new(self.convert_statement(&s.body)),
506            }),
507            BabelStmt::DoWhileStatement(s) => Stmt::DoWhile(DoWhileStmt {
508                span: self.span(&s.base),
509                test: Box::new(self.convert_expression(&s.test)),
510                body: Box::new(self.convert_statement(&s.body)),
511            }),
512            BabelStmt::ForInStatement(s) => Stmt::ForIn(ForInStmt {
513                span: self.span(&s.base),
514                left: self.convert_for_in_of_left(&s.left),
515                right: Box::new(self.convert_expression(&s.right)),
516                body: Box::new(self.convert_statement(&s.body)),
517            }),
518            BabelStmt::ForOfStatement(s) => Stmt::ForOf(ForOfStmt {
519                span: self.span(&s.base),
520                is_await: s.is_await,
521                left: self.convert_for_in_of_left(&s.left),
522                right: Box::new(self.convert_expression(&s.right)),
523                body: Box::new(self.convert_statement(&s.body)),
524            }),
525            BabelStmt::SwitchStatement(s) => {
526                let cases = s
527                    .cases
528                    .iter()
529                    .map(|c| SwitchCase {
530                        span: self.span(&c.base),
531                        test: c
532                            .test
533                            .as_ref()
534                            .map(|t| Box::new(self.convert_expression(t))),
535                        cons: c
536                            .consequent
537                            .iter()
538                            .map(|s| self.convert_statement(s))
539                            .collect(),
540                    })
541                    .collect();
542                Stmt::Switch(SwitchStmt {
543                    span: self.span(&s.base),
544                    discriminant: Box::new(self.convert_expression(&s.discriminant)),
545                    cases,
546                })
547            }
548            BabelStmt::ThrowStatement(s) => Stmt::Throw(ThrowStmt {
549                span: self.span(&s.base),
550                arg: Box::new(self.convert_expression(&s.argument)),
551            }),
552            BabelStmt::TryStatement(s) => {
553                let block = self.convert_block_statement(&s.block);
554                let handler = s.handler.as_ref().map(|h| self.convert_catch_clause(h));
555                let finalizer = s
556                    .finalizer
557                    .as_ref()
558                    .map(|f| self.convert_block_statement(f));
559                Stmt::Try(Box::new(TryStmt {
560                    span: self.span(&s.base),
561                    block,
562                    handler,
563                    finalizer,
564                }))
565            }
566            BabelStmt::BreakStatement(s) => Stmt::Break(BreakStmt {
567                span: self.span(&s.base),
568                label: s.label.as_ref().map(|l| self.ident(&l.name, DUMMY_SP)),
569            }),
570            BabelStmt::ContinueStatement(s) => Stmt::Continue(ContinueStmt {
571                span: self.span(&s.base),
572                label: s.label.as_ref().map(|l| self.ident(&l.name, DUMMY_SP)),
573            }),
574            BabelStmt::LabeledStatement(s) => Stmt::Labeled(LabeledStmt {
575                span: self.span(&s.base),
576                label: self.ident(&s.label.name, DUMMY_SP),
577                body: Box::new(self.convert_statement(&s.body)),
578            }),
579            BabelStmt::EmptyStatement(s) => Stmt::Empty(EmptyStmt {
580                span: self.span(&s.base),
581            }),
582            BabelStmt::DebuggerStatement(s) => Stmt::Debugger(DebuggerStmt {
583                span: self.span(&s.base),
584            }),
585            BabelStmt::WithStatement(s) => Stmt::With(WithStmt {
586                span: self.span(&s.base),
587                obj: Box::new(self.convert_expression(&s.object)),
588                body: Box::new(self.convert_statement(&s.body)),
589            }),
590            BabelStmt::VariableDeclaration(d) => {
591                Stmt::Decl(Decl::Var(Box::new(self.convert_variable_declaration(d))))
592            }
593            BabelStmt::FunctionDeclaration(f) => {
594                Stmt::Decl(Decl::Fn(self.convert_function_declaration(f)))
595            }
596            BabelStmt::ClassDeclaration(c) => {
597                let ident =
598                    c.id.as_ref()
599                        .map(|id| self.ident(&id.name, self.span(&id.base)))
600                        .unwrap_or_else(|| self.ident("_anonymous", DUMMY_SP));
601                let super_class = c
602                    .super_class
603                    .as_ref()
604                    .map(|s| Box::new(self.convert_expression(s)));
605                Stmt::Decl(Decl::Class(ClassDecl {
606                    ident,
607                    declare: c.declare.unwrap_or(false),
608                    class: Box::new(Class {
609                        span: self.span(&c.base),
610                        ctxt: SyntaxContext::empty(),
611                        decorators: vec![],
612                        body: vec![],
613                        super_class,
614                        is_abstract: false,
615                        type_params: None,
616                        super_type_params: None,
617                        implements: vec![],
618                    }),
619                }))
620            }
621            // Import/export handled in convert_statement_to_module_item
622            BabelStmt::ImportDeclaration(_)
623            | BabelStmt::ExportNamedDeclaration(_)
624            | BabelStmt::ExportDefaultDeclaration(_)
625            | BabelStmt::ExportAllDeclaration(_) => Stmt::Empty(EmptyStmt { span: DUMMY_SP }),
626            // TS declarations - extract from source text if available
627            BabelStmt::TSTypeAliasDeclaration(d) => self
628                .extract_source_stmt(&d.base)
629                .unwrap_or(Stmt::Empty(EmptyStmt { span: DUMMY_SP })),
630            BabelStmt::TSInterfaceDeclaration(d) => self
631                .extract_source_stmt(&d.base)
632                .unwrap_or(Stmt::Empty(EmptyStmt { span: DUMMY_SP })),
633            BabelStmt::TSEnumDeclaration(d) => self
634                .extract_source_stmt(&d.base)
635                .unwrap_or(Stmt::Empty(EmptyStmt { span: DUMMY_SP })),
636            // Flow type declarations - extract from source text if available
637            BabelStmt::TypeAlias(d) => self
638                .extract_source_stmt(&d.base)
639                .unwrap_or(Stmt::Empty(EmptyStmt { span: DUMMY_SP })),
640            BabelStmt::OpaqueType(d) => self
641                .extract_source_stmt(&d.base)
642                .unwrap_or(Stmt::Empty(EmptyStmt { span: DUMMY_SP })),
643            BabelStmt::InterfaceDeclaration(d) => self
644                .extract_source_stmt(&d.base)
645                .unwrap_or(Stmt::Empty(EmptyStmt { span: DUMMY_SP })),
646            BabelStmt::EnumDeclaration(d) => self
647                .extract_source_stmt(&d.base)
648                .unwrap_or(Stmt::Empty(EmptyStmt { span: DUMMY_SP })),
649            // Other TS/Flow declarations
650            BabelStmt::TSModuleDeclaration(_)
651            | BabelStmt::TSDeclareFunction(_)
652            | BabelStmt::DeclareVariable(_)
653            | BabelStmt::DeclareFunction(_)
654            | BabelStmt::DeclareClass(_)
655            | BabelStmt::DeclareModule(_)
656            | BabelStmt::DeclareModuleExports(_)
657            | BabelStmt::DeclareExportDeclaration(_)
658            | BabelStmt::DeclareExportAllDeclaration(_)
659            | BabelStmt::DeclareInterface(_)
660            | BabelStmt::DeclareTypeAlias(_)
661            | BabelStmt::DeclareOpaqueType(_) => Stmt::Empty(EmptyStmt { span: DUMMY_SP }),
662            // Only unknown node types without a reverse mapping reach this
663            // arm. Degrading to EmptyStatement would silently drop the node,
664            // so emit a deliberate runtime tripwire (a `throw` in generated
665            // code) instead.
666            BabelStmt::Unknown(unknown) => {
667                let message = format!(
668                    "[react-compiler] internal error: unmodeled statement `{}` reached the SWC reverse converter",
669                    unknown.node_type()
670                );
671                Stmt::Throw(ThrowStmt {
672                    span: DUMMY_SP,
673                    arg: Box::new(Expr::Lit(Lit::Str(Str {
674                        span: DUMMY_SP,
675                        value: self.wtf8(&message),
676                        raw: None,
677                    }))),
678                })
679            }
680        }
681    }
682
683    fn convert_block_statement(&self, block: &babel_stmt::BlockStatement) -> BlockStmt {
684        let mut stmts: Vec<Stmt> = Vec::new();
685
686        // Convert directives to expression statements at the beginning
687        for dir in &block.directives {
688            let span = self.span(&dir.base);
689            let str_span = self.span(&dir.value.base);
690            stmts.push(Stmt::Expr(ExprStmt {
691                span,
692                expr: Box::new(Expr::Lit(Lit::Str(Str {
693                    span: str_span,
694                    value: self.wtf8(&dir.value.value),
695                    raw: None,
696                }))),
697            }));
698        }
699
700        for s in &block.body {
701            stmts.push(self.convert_statement(s));
702        }
703
704        BlockStmt {
705            span: self.span(&block.base),
706            ctxt: SyntaxContext::empty(),
707            stmts,
708        }
709    }
710
711    fn convert_catch_clause(&self, clause: &babel_stmt::CatchClause) -> CatchClause {
712        let param = clause.param.as_ref().map(|p| self.convert_pattern(p));
713        CatchClause {
714            span: self.span(&clause.base),
715            param,
716            body: self.convert_block_statement(&clause.body),
717        }
718    }
719
720    fn convert_for_init(&self, init: &babel_stmt::ForInit) -> VarDeclOrExpr {
721        match init {
722            babel_stmt::ForInit::VariableDeclaration(v) => {
723                VarDeclOrExpr::VarDecl(Box::new(self.convert_variable_declaration(v)))
724            }
725            babel_stmt::ForInit::Expression(e) => {
726                VarDeclOrExpr::Expr(Box::new(self.convert_expression(e)))
727            }
728        }
729    }
730
731    fn convert_for_in_of_left(&self, left: &babel_stmt::ForInOfLeft) -> ForHead {
732        match left {
733            babel_stmt::ForInOfLeft::VariableDeclaration(v) => {
734                ForHead::VarDecl(Box::new(self.convert_variable_declaration(v)))
735            }
736            babel_stmt::ForInOfLeft::Pattern(p) => ForHead::Pat(Box::new(self.convert_pattern(p))),
737        }
738    }
739
740    fn convert_variable_declaration(&self, decl: &babel_stmt::VariableDeclaration) -> VarDecl {
741        let kind = match decl.kind {
742            babel_stmt::VariableDeclarationKind::Var => VarDeclKind::Var,
743            babel_stmt::VariableDeclarationKind::Let => VarDeclKind::Let,
744            babel_stmt::VariableDeclarationKind::Const => VarDeclKind::Const,
745            babel_stmt::VariableDeclarationKind::Using => VarDeclKind::Var, // SWC doesn't have Using
746        };
747        let decls = decl
748            .declarations
749            .iter()
750            .map(|d| self.convert_variable_declarator(d))
751            .collect();
752        let declare = decl.declare.unwrap_or(false);
753        VarDecl {
754            span: self.span(&decl.base),
755            ctxt: SyntaxContext::empty(),
756            kind,
757            declare,
758            decls,
759        }
760    }
761
762    fn convert_variable_declarator(&self, d: &babel_stmt::VariableDeclarator) -> VarDeclarator {
763        let name = self.convert_pattern(&d.id);
764        let init = d
765            .init
766            .as_ref()
767            .map(|e| Box::new(self.convert_expression(e)));
768        let definite = d.definite.unwrap_or(false);
769        VarDeclarator {
770            span: self.span(&d.base),
771            name,
772            init,
773            definite,
774        }
775    }
776
777    // ===== Expressions =====
778
779    fn convert_expression(&self, expr: &BabelExpr) -> Expr {
780        match expr {
781            BabelExpr::Identifier(id) => {
782                let span = self.span(&id.base);
783                Expr::Ident(self.ident(&id.name, span))
784            }
785            BabelExpr::StringLiteral(lit) => Expr::Lit(Lit::Str(Str {
786                span: self.span(&lit.base),
787                value: self.wtf8(&lit.value),
788                raw: self.escape_string_raw(&lit.value),
789            })),
790            BabelExpr::NumericLiteral(lit) => {
791                // Convert -0.0 to 0.0 to match Babel's codegen behavior.
792                // Babel outputs `0` for both `-0` and `0`.
793                let value = if lit.value == 0.0 && lit.value.is_sign_negative() {
794                    0.0
795                } else {
796                    lit.value
797                };
798                Expr::Lit(Lit::Num(Number {
799                    span: self.span(&lit.base),
800                    value,
801                    raw: None,
802                }))
803            }
804            BabelExpr::BooleanLiteral(lit) => Expr::Lit(Lit::Bool(Bool {
805                span: self.span(&lit.base),
806                value: lit.value,
807            })),
808            BabelExpr::NullLiteral(lit) => Expr::Lit(Lit::Null(Null {
809                span: self.span(&lit.base),
810            })),
811            BabelExpr::BigIntLiteral(lit) => Expr::Lit(Lit::BigInt(BigInt {
812                span: self.span(&lit.base),
813                value: Box::new(lit.value.parse().unwrap_or_default()),
814                raw: None,
815            })),
816            BabelExpr::RegExpLiteral(lit) => Expr::Lit(Lit::Regex(Regex {
817                span: self.span(&lit.base),
818                exp: self.atom(&lit.pattern),
819                flags: self.atom(&lit.flags),
820            })),
821            BabelExpr::CallExpression(call) => {
822                let callee = self.convert_expression(&call.callee);
823                let args = self.convert_arguments(&call.arguments);
824                // Wrap arrow/function expressions in parens when used as
825                // call targets (IIFEs). SWC codegen does not add parens for
826                // `(() => ...)()`, resulting in incorrect code.
827                let callee = match &callee {
828                    Expr::Arrow(_) | Expr::Fn(_) => Expr::Paren(ParenExpr {
829                        span: callee.span(),
830                        expr: Box::new(callee),
831                    }),
832                    _ => callee,
833                };
834                Expr::Call(CallExpr {
835                    span: self.span(&call.base),
836                    ctxt: SyntaxContext::empty(),
837                    callee: Callee::Expr(Box::new(callee)),
838                    args,
839                    type_args: None,
840                })
841            }
842            BabelExpr::MemberExpression(m) => self.convert_member_expression(m),
843            BabelExpr::OptionalCallExpression(call) => {
844                let callee = self.convert_expression_for_chain(&call.callee);
845                let args = self.convert_arguments(&call.arguments);
846                let base = OptChainBase::Call(OptCall {
847                    span: self.span(&call.base),
848                    ctxt: SyntaxContext::empty(),
849                    callee: Box::new(callee),
850                    args,
851                    type_args: None,
852                });
853                Expr::OptChain(OptChainExpr {
854                    span: self.span(&call.base),
855                    optional: call.optional,
856                    base: Box::new(base),
857                })
858            }
859            BabelExpr::OptionalMemberExpression(m) => {
860                let base = self.convert_optional_member_to_chain_base(m);
861                Expr::OptChain(OptChainExpr {
862                    span: self.span(&m.base),
863                    optional: m.optional,
864                    base: Box::new(base),
865                })
866            }
867            BabelExpr::BinaryExpression(bin) => {
868                let op = self.convert_binary_operator(&bin.operator);
869                Expr::Bin(BinExpr {
870                    span: self.span(&bin.base),
871                    op,
872                    left: Box::new(self.convert_expression(&bin.left)),
873                    right: Box::new(self.convert_expression(&bin.right)),
874                })
875            }
876            BabelExpr::LogicalExpression(log) => {
877                let op = self.convert_logical_operator(&log.operator);
878                let span = self.span(&log.base);
879                let bin = Expr::Bin(BinExpr {
880                    span,
881                    op,
882                    left: Box::new(self.convert_expression(&log.left)),
883                    right: Box::new(self.convert_expression(&log.right)),
884                });
885                // Wrap all logical expressions in parentheses. Logical
886                // operators (||, &&, ??) have lower precedence than most
887                // binary operators, but SWC's codegen does not always insert
888                // parens correctly (e.g., `a + b || c` vs `a + (b || c)`).
889                // Wrapping unconditionally is safe.
890                Expr::Paren(ParenExpr {
891                    span,
892                    expr: Box::new(bin),
893                })
894            }
895            BabelExpr::UnaryExpression(un) => {
896                let op = self.convert_unary_operator(&un.operator);
897                Expr::Unary(UnaryExpr {
898                    span: self.span(&un.base),
899                    op,
900                    arg: Box::new(self.convert_expression(&un.argument)),
901                })
902            }
903            BabelExpr::UpdateExpression(up) => {
904                let op = self.convert_update_operator(&up.operator);
905                Expr::Update(UpdateExpr {
906                    span: self.span(&up.base),
907                    op,
908                    prefix: up.prefix,
909                    arg: Box::new(self.convert_expression(&up.argument)),
910                })
911            }
912            BabelExpr::ConditionalExpression(cond) => {
913                let span = self.span(&cond.base);
914                // Wrap conditional expressions in parentheses. SWC's codegen
915                // does not always insert parens for ternaries inside binary
916                // or assignment expressions (e.g., `x + cond ? a : b` instead
917                // of `x + (cond ? a : b)`).
918                Expr::Paren(ParenExpr {
919                    span,
920                    expr: Box::new(Expr::Cond(CondExpr {
921                        span,
922                        test: Box::new(self.convert_expression(&cond.test)),
923                        cons: Box::new(self.convert_expression(&cond.consequent)),
924                        alt: Box::new(self.convert_expression(&cond.alternate)),
925                    })),
926                })
927            }
928            BabelExpr::AssignmentExpression(assign) => {
929                let op = self.convert_assignment_operator(&assign.operator);
930                let left = self.convert_pattern_to_assign_target(&assign.left);
931                let span = self.span(&assign.base);
932                let assign_expr = Expr::Assign(AssignExpr {
933                    span,
934                    op,
935                    left,
936                    right: Box::new(self.convert_expression(&assign.right)),
937                });
938                // Wrap assignment expressions in parentheses. SWC's codegen
939                // does not always insert necessary parens for assignments
940                // when they appear as operands of binary/logical expressions
941                // (e.g., `x + x = 2` instead of `x + (x = 2)`).
942                Expr::Paren(ParenExpr {
943                    span,
944                    expr: Box::new(assign_expr),
945                })
946            }
947            BabelExpr::SequenceExpression(seq) => {
948                let exprs = seq
949                    .expressions
950                    .iter()
951                    .map(|e| Box::new(self.convert_expression(e)))
952                    .collect();
953                let span = self.span(&seq.base);
954                // Wrap sequence expressions in parentheses. SWC's codegen
955                // does not always insert necessary parens for sequence
956                // expressions (e.g., in ternary consequent position), so
957                // wrapping unconditionally is safe and prevents parse errors.
958                Expr::Paren(ParenExpr {
959                    span,
960                    expr: Box::new(Expr::Seq(SeqExpr { span, exprs })),
961                })
962            }
963            BabelExpr::ArrowFunctionExpression(arrow) => self.convert_arrow_function(arrow),
964            BabelExpr::FunctionExpression(func) => {
965                let ident = func
966                    .id
967                    .as_ref()
968                    .map(|id| self.ident(&id.name, self.span(&id.base)));
969                let params = self.convert_params(&func.params);
970                let body = Some(self.convert_block_statement(&func.body));
971                Expr::Fn(FnExpr {
972                    ident,
973                    function: Box::new(Function {
974                        params,
975                        decorators: vec![],
976                        span: self.span(&func.base),
977                        ctxt: SyntaxContext::empty(),
978                        body,
979                        is_generator: func.generator,
980                        is_async: func.is_async,
981                        type_params: None,
982                        return_type: None,
983                    }),
984                })
985            }
986            BabelExpr::ObjectExpression(obj) => {
987                let props = obj
988                    .properties
989                    .iter()
990                    .map(|p| self.convert_object_expression_property(p))
991                    .collect();
992                Expr::Object(ObjectLit {
993                    span: self.span(&obj.base),
994                    props,
995                })
996            }
997            BabelExpr::ArrayExpression(arr) => {
998                let elems = arr
999                    .elements
1000                    .iter()
1001                    .map(|e| self.convert_array_element(e))
1002                    .collect();
1003                Expr::Array(ArrayLit {
1004                    span: self.span(&arr.base),
1005                    elems,
1006                })
1007            }
1008            BabelExpr::NewExpression(n) => {
1009                let callee = Box::new(self.convert_expression(&n.callee));
1010                let args = Some(self.convert_arguments(&n.arguments));
1011                Expr::New(NewExpr {
1012                    span: self.span(&n.base),
1013                    ctxt: SyntaxContext::empty(),
1014                    callee,
1015                    args,
1016                    type_args: None,
1017                })
1018            }
1019            BabelExpr::TemplateLiteral(tl) => {
1020                let template = self.convert_template_literal(tl);
1021                Expr::Tpl(template)
1022            }
1023            BabelExpr::TaggedTemplateExpression(tag) => {
1024                let t = Box::new(self.convert_expression(&tag.tag));
1025                let tpl = Box::new(self.convert_template_literal(&tag.quasi));
1026                Expr::TaggedTpl(TaggedTpl {
1027                    span: self.span(&tag.base),
1028                    ctxt: SyntaxContext::empty(),
1029                    tag: t,
1030                    type_params: None,
1031                    tpl,
1032                })
1033            }
1034            BabelExpr::AwaitExpression(a) => Expr::Await(AwaitExpr {
1035                span: self.span(&a.base),
1036                arg: Box::new(self.convert_expression(&a.argument)),
1037            }),
1038            BabelExpr::YieldExpression(y) => Expr::Yield(YieldExpr {
1039                span: self.span(&y.base),
1040                delegate: y.delegate,
1041                arg: y
1042                    .argument
1043                    .as_ref()
1044                    .map(|a| Box::new(self.convert_expression(a))),
1045            }),
1046            BabelExpr::SpreadElement(s) => {
1047                // SpreadElement can't be a standalone expression in SWC.
1048                // Return the argument directly as a fallback.
1049                self.convert_expression(&s.argument)
1050            }
1051            BabelExpr::MetaProperty(mp) => Expr::MetaProp(MetaPropExpr {
1052                span: self.span(&mp.base),
1053                kind: match (mp.meta.name.as_str(), mp.property.name.as_str()) {
1054                    ("new", "target") => MetaPropKind::NewTarget,
1055                    ("import", "meta") => MetaPropKind::ImportMeta,
1056                    _ => MetaPropKind::NewTarget,
1057                },
1058            }),
1059            BabelExpr::ClassExpression(c) => {
1060                let ident =
1061                    c.id.as_ref()
1062                        .map(|id| self.ident(&id.name, self.span(&id.base)));
1063                let super_class = c
1064                    .super_class
1065                    .as_ref()
1066                    .map(|s| Box::new(self.convert_expression(s)));
1067                Expr::Class(ClassExpr {
1068                    ident,
1069                    class: Box::new(Class {
1070                        span: self.span(&c.base),
1071                        ctxt: SyntaxContext::empty(),
1072                        decorators: vec![],
1073                        body: vec![],
1074                        super_class,
1075                        is_abstract: false,
1076                        type_params: None,
1077                        super_type_params: None,
1078                        implements: vec![],
1079                    }),
1080                })
1081            }
1082            BabelExpr::PrivateName(p) => Expr::PrivateName(PrivateName {
1083                span: self.span(&p.base),
1084                name: self.atom(&p.id.name),
1085            }),
1086            BabelExpr::Super(s) => Expr::Ident(self.ident("super", self.span(&s.base))),
1087            BabelExpr::Import(i) => Expr::Ident(self.ident("import", self.span(&i.base))),
1088            BabelExpr::ThisExpression(t) => Expr::This(ThisExpr {
1089                span: self.span(&t.base),
1090            }),
1091            BabelExpr::ParenthesizedExpression(p) => Expr::Paren(ParenExpr {
1092                span: self.span(&p.base),
1093                expr: Box::new(self.convert_expression(&p.expression)),
1094            }),
1095            BabelExpr::JSXElement(el) => {
1096                let element = self.convert_jsx_element(el.as_ref());
1097                Expr::JSXElement(Box::new(element))
1098            }
1099            BabelExpr::JSXFragment(frag) => {
1100                let fragment = self.convert_jsx_fragment(frag);
1101                Expr::JSXFragment(fragment)
1102            }
1103            // TS expressions - preserve as SWC TS nodes
1104            BabelExpr::TSAsExpression(e) => {
1105                let expr = Box::new(self.convert_expression(&e.expression));
1106                let span = self.span(&e.base);
1107                let annotation = e.type_annotation.parse_value();
1108                // Check if this is "as const" — Babel represents it as
1109                // TSAsExpression with typeAnnotation: TSTypeReference { typeName: Identifier { name: "const" } }
1110                let is_as_const = annotation.get("type").and_then(|v| v.as_str())
1111                    == Some("TSTypeReference")
1112                    && annotation
1113                        .get("typeName")
1114                        .and_then(|tn| tn.get("name"))
1115                        .and_then(|n| n.as_str())
1116                        == Some("const");
1117
1118                if is_as_const {
1119                    Expr::TsConstAssertion(TsConstAssertion { span, expr })
1120                } else {
1121                    let type_ann = self.convert_ts_type_from_json(&annotation, span);
1122                    Expr::TsAs(TsAsExpr {
1123                        span,
1124                        expr,
1125                        type_ann: Box::new(type_ann),
1126                    })
1127                }
1128            }
1129            BabelExpr::TSSatisfiesExpression(e) => self.convert_expression(&e.expression),
1130            BabelExpr::TSNonNullExpression(e) => Expr::TsNonNull(TsNonNullExpr {
1131                span: self.span(&e.base),
1132                expr: Box::new(self.convert_expression(&e.expression)),
1133            }),
1134            BabelExpr::TSTypeAssertion(e) => self.convert_expression(&e.expression),
1135            BabelExpr::TSInstantiationExpression(e) => self.convert_expression(&e.expression),
1136            BabelExpr::TypeCastExpression(e) => self.convert_expression(&e.expression),
1137            BabelExpr::AssignmentPattern(p) => {
1138                let left = self.convert_pattern_to_assign_target(&p.left);
1139                Expr::Assign(AssignExpr {
1140                    span: self.span(&p.base),
1141                    op: AssignOp::Assign,
1142                    left,
1143                    right: Box::new(self.convert_expression(&p.right)),
1144                })
1145            }
1146        }
1147    }
1148
1149    /// Convert an expression that may be used inside a chain (optional chaining).
1150    ///
1151    /// In Babel, a chain like `a?.b.c()` is represented as nested
1152    /// OptionalMemberExpression / OptionalCallExpression nodes. Each node
1153    /// has an `optional` flag indicating whether it uses `?.` at that point.
1154    ///
1155    /// In SWC, each `?.` point is wrapped in an `OptChainExpr`. Nodes in
1156    /// the chain that do NOT have `?.` are plain `MemberExpr` / `CallExpr`.
1157    ///
1158    /// So when `optional: true`, we still need to emit `OptChainExpr`.
1159    /// When `optional: false`, we emit a plain expr (part of the parent chain).
1160    fn convert_expression_for_chain(&self, expr: &BabelExpr) -> Expr {
1161        match expr {
1162            BabelExpr::OptionalMemberExpression(m) => {
1163                if m.optional {
1164                    // This node uses `?.`, wrap in OptChainExpr
1165                    let base = self.convert_optional_member_to_chain_base(m);
1166                    Expr::OptChain(OptChainExpr {
1167                        span: self.span(&m.base),
1168                        optional: true,
1169                        base: Box::new(base),
1170                    })
1171                } else {
1172                    // Part of a chain but no `?.` here — plain MemberExpr
1173                    self.convert_optional_member_to_member_expr(m)
1174                }
1175            }
1176            BabelExpr::OptionalCallExpression(call) => {
1177                let callee = self.convert_expression_for_chain(&call.callee);
1178                let args = self.convert_arguments(&call.arguments);
1179                if call.optional {
1180                    // This node uses `?.()`, wrap in OptChainExpr
1181                    let base = OptChainBase::Call(OptCall {
1182                        span: self.span(&call.base),
1183                        ctxt: SyntaxContext::empty(),
1184                        callee: Box::new(callee),
1185                        args,
1186                        type_args: None,
1187                    });
1188                    Expr::OptChain(OptChainExpr {
1189                        span: self.span(&call.base),
1190                        optional: true,
1191                        base: Box::new(base),
1192                    })
1193                } else {
1194                    // Part of a chain but no `?.` here — plain CallExpr
1195                    Expr::Call(CallExpr {
1196                        span: self.span(&call.base),
1197                        ctxt: SyntaxContext::empty(),
1198                        callee: Callee::Expr(Box::new(callee)),
1199                        args,
1200                        type_args: None,
1201                    })
1202                }
1203            }
1204            _ => self.convert_expression(expr),
1205        }
1206    }
1207
1208    fn convert_member_expression(&self, m: &babel_expr::MemberExpression) -> Expr {
1209        let object = self.convert_expression(&m.object);
1210        // When an optional chain expression is used as the object of a
1211        // non-optional member expression (e.g., `(props?.a).b`), wrap it
1212        // in parens to properly terminate the optional chain. Without
1213        // parens, SWC codegen emits `props?.a.b` which extends the chain.
1214        let object = match &object {
1215            Expr::OptChain(_) => Box::new(Expr::Paren(ParenExpr {
1216                span: object.span(),
1217                expr: Box::new(object),
1218            })),
1219            _ => Box::new(object),
1220        };
1221        if m.computed {
1222            let property = self.convert_expression(&m.property);
1223            Expr::Member(MemberExpr {
1224                span: self.span(&m.base),
1225                obj: object,
1226                prop: MemberProp::Computed(ComputedPropName {
1227                    span: DUMMY_SP,
1228                    expr: Box::new(property),
1229                }),
1230            })
1231        } else {
1232            let prop_name = self.expression_to_ident_name(&m.property);
1233            Expr::Member(MemberExpr {
1234                span: self.span(&m.base),
1235                obj: object,
1236                prop: MemberProp::Ident(prop_name),
1237            })
1238        }
1239    }
1240
1241    fn convert_optional_member_to_chain_base(
1242        &self,
1243        m: &babel_expr::OptionalMemberExpression,
1244    ) -> OptChainBase {
1245        let object = Box::new(self.convert_expression_for_chain(&m.object));
1246        if m.computed {
1247            let property = self.convert_expression(&m.property);
1248            OptChainBase::Member(MemberExpr {
1249                span: self.span(&m.base),
1250                obj: object,
1251                prop: MemberProp::Computed(ComputedPropName {
1252                    span: DUMMY_SP,
1253                    expr: Box::new(property),
1254                }),
1255            })
1256        } else {
1257            let prop_name = self.expression_to_ident_name(&m.property);
1258            OptChainBase::Member(MemberExpr {
1259                span: self.span(&m.base),
1260                obj: object,
1261                prop: MemberProp::Ident(prop_name),
1262            })
1263        }
1264    }
1265
1266    fn convert_optional_member_to_member_expr(
1267        &self,
1268        m: &babel_expr::OptionalMemberExpression,
1269    ) -> Expr {
1270        let object = Box::new(self.convert_expression_for_chain(&m.object));
1271        if m.computed {
1272            let property = self.convert_expression(&m.property);
1273            Expr::Member(MemberExpr {
1274                span: self.span(&m.base),
1275                obj: object,
1276                prop: MemberProp::Computed(ComputedPropName {
1277                    span: DUMMY_SP,
1278                    expr: Box::new(property),
1279                }),
1280            })
1281        } else {
1282            let prop_name = self.expression_to_ident_name(&m.property);
1283            Expr::Member(MemberExpr {
1284                span: self.span(&m.base),
1285                obj: object,
1286                prop: MemberProp::Ident(prop_name),
1287            })
1288        }
1289    }
1290
1291    fn expression_to_ident_name(&self, expr: &BabelExpr) -> IdentName {
1292        match expr {
1293            BabelExpr::Identifier(id) => self.ident_name(&id.name, self.span(&id.base)),
1294            _ => self.ident_name("__unknown__", DUMMY_SP),
1295        }
1296    }
1297
1298    fn convert_arguments(&self, args: &[BabelExpr]) -> Vec<ExprOrSpread> {
1299        args.iter().map(|a| self.convert_argument(a)).collect()
1300    }
1301
1302    fn convert_argument(&self, arg: &BabelExpr) -> ExprOrSpread {
1303        match arg {
1304            BabelExpr::SpreadElement(s) => ExprOrSpread {
1305                spread: Some(self.span(&s.base)),
1306                expr: Box::new(self.convert_expression(&s.argument)),
1307            },
1308            _ => ExprOrSpread {
1309                spread: None,
1310                expr: Box::new(self.convert_expression(arg)),
1311            },
1312        }
1313    }
1314
1315    fn convert_array_element(&self, elem: &Option<BabelExpr>) -> Option<ExprOrSpread> {
1316        match elem {
1317            None => None,
1318            Some(BabelExpr::SpreadElement(s)) => Some(ExprOrSpread {
1319                spread: Some(self.span(&s.base)),
1320                expr: Box::new(self.convert_expression(&s.argument)),
1321            }),
1322            Some(e) => Some(ExprOrSpread {
1323                spread: None,
1324                expr: Box::new(self.convert_expression(e)),
1325            }),
1326        }
1327    }
1328
1329    fn convert_object_expression_property(
1330        &self,
1331        prop: &babel_expr::ObjectExpressionProperty,
1332    ) -> PropOrSpread {
1333        match prop {
1334            babel_expr::ObjectExpressionProperty::ObjectProperty(p) => {
1335                let key = if p.computed {
1336                    // Computed property key: [expr]
1337                    PropName::Computed(ComputedPropName {
1338                        span: DUMMY_SP,
1339                        expr: Box::new(self.convert_expression(&p.key)),
1340                    })
1341                } else {
1342                    self.convert_expression_to_prop_name(&p.key)
1343                };
1344                let value = self.convert_expression(&p.value);
1345                let method = p.method.unwrap_or(false);
1346
1347                if p.shorthand {
1348                    PropOrSpread::Prop(Box::new(Prop::Shorthand(match &*p.key {
1349                        BabelExpr::Identifier(id) => self.ident(&id.name, self.span(&id.base)),
1350                        _ => self.ident("__unknown__", DUMMY_SP),
1351                    })))
1352                } else if method {
1353                    // Method shorthand: { foo() {} }
1354                    // The value should be a function expression
1355                    let func = match value {
1356                        Expr::Fn(fn_expr) => *fn_expr.function,
1357                        _ => {
1358                            // Fallback: wrap in a key-value
1359                            return PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
1360                                key,
1361                                value: Box::new(value),
1362                            })));
1363                        }
1364                    };
1365                    PropOrSpread::Prop(Box::new(Prop::Method(MethodProp {
1366                        key,
1367                        function: Box::new(func),
1368                    })))
1369                } else {
1370                    PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
1371                        key,
1372                        value: Box::new(value),
1373                    })))
1374                }
1375            }
1376            babel_expr::ObjectExpressionProperty::ObjectMethod(m) => {
1377                let key = if m.computed {
1378                    PropName::Computed(ComputedPropName {
1379                        span: DUMMY_SP,
1380                        expr: Box::new(self.convert_expression(&m.key)),
1381                    })
1382                } else {
1383                    self.convert_expression_to_prop_name(&m.key)
1384                };
1385                let func = self.convert_object_method_to_function(m);
1386                match m.kind {
1387                    babel_expr::ObjectMethodKind::Get => {
1388                        PropOrSpread::Prop(Box::new(Prop::Getter(GetterProp {
1389                            span: self.span(&m.base),
1390                            key,
1391                            type_ann: None,
1392                            body: func.body,
1393                        })))
1394                    }
1395                    babel_expr::ObjectMethodKind::Set => {
1396                        let param = func
1397                            .params
1398                            .into_iter()
1399                            .next()
1400                            .map(|p| Box::new(p.pat))
1401                            .unwrap_or_else(|| {
1402                                Box::new(Pat::Ident(self.binding_ident("_", DUMMY_SP)))
1403                            });
1404                        PropOrSpread::Prop(Box::new(Prop::Setter(SetterProp {
1405                            span: self.span(&m.base),
1406                            key,
1407                            this_param: None,
1408                            param,
1409                            body: func.body,
1410                        })))
1411                    }
1412                    babel_expr::ObjectMethodKind::Method => {
1413                        PropOrSpread::Prop(Box::new(Prop::Method(MethodProp {
1414                            key,
1415                            function: Box::new(func),
1416                        })))
1417                    }
1418                }
1419            }
1420            babel_expr::ObjectExpressionProperty::SpreadElement(s) => {
1421                PropOrSpread::Spread(SpreadElement {
1422                    dot3_token: self.span(&s.base),
1423                    expr: Box::new(self.convert_expression(&s.argument)),
1424                })
1425            }
1426        }
1427    }
1428
1429    fn convert_expression_to_prop_name(&self, expr: &BabelExpr) -> PropName {
1430        match expr {
1431            BabelExpr::Identifier(id) => {
1432                PropName::Ident(self.ident_name(&id.name, self.span(&id.base)))
1433            }
1434            BabelExpr::StringLiteral(s) => PropName::Str(Str {
1435                span: self.span(&s.base),
1436                value: self.wtf8(&s.value),
1437                raw: None,
1438            }),
1439            BabelExpr::NumericLiteral(n) => PropName::Num(Number {
1440                span: self.span(&n.base),
1441                value: n.value,
1442                raw: None,
1443            }),
1444            _ => PropName::Computed(ComputedPropName {
1445                span: DUMMY_SP,
1446                expr: Box::new(self.convert_expression(expr)),
1447            }),
1448        }
1449    }
1450
1451    fn convert_template_literal(&self, tl: &babel_expr::TemplateLiteral) -> Tpl {
1452        let quasis = tl
1453            .quasis
1454            .iter()
1455            .map(|q| {
1456                let cooked = q.value.cooked.as_ref().map(|c| self.wtf8(c));
1457                TplElement {
1458                    span: self.span(&q.base),
1459                    tail: q.tail,
1460                    cooked,
1461                    raw: self.atom(&q.value.raw),
1462                }
1463            })
1464            .collect();
1465        let exprs = tl
1466            .expressions
1467            .iter()
1468            .map(|e| Box::new(self.convert_expression(e)))
1469            .collect();
1470        Tpl {
1471            span: self.span(&tl.base),
1472            exprs,
1473            quasis,
1474        }
1475    }
1476
1477    // ===== Functions =====
1478
1479    fn convert_function_declaration(&self, f: &babel_stmt::FunctionDeclaration) -> FnDecl {
1480        let ident =
1481            f.id.as_ref()
1482                .map(|id| self.ident(&id.name, self.span(&id.base)))
1483                .unwrap_or_else(|| self.ident("_anonymous", DUMMY_SP));
1484        let params = self.convert_params(&f.params);
1485        let body = Some(self.convert_block_statement(&f.body));
1486        let declare = f.declare.unwrap_or(false);
1487        FnDecl {
1488            ident,
1489            declare,
1490            function: Box::new(Function {
1491                params,
1492                decorators: vec![],
1493                span: self.span(&f.base),
1494                ctxt: SyntaxContext::empty(),
1495                body,
1496                is_generator: f.generator,
1497                is_async: f.is_async,
1498                type_params: None,
1499                return_type: None,
1500            }),
1501        }
1502    }
1503
1504    fn convert_object_method_to_function(&self, m: &babel_expr::ObjectMethod) -> Function {
1505        let params = self.convert_params(&m.params);
1506        let body = Some(self.convert_block_statement(&m.body));
1507        Function {
1508            params,
1509            decorators: vec![],
1510            span: self.span(&m.base),
1511            ctxt: SyntaxContext::empty(),
1512            body,
1513            is_generator: m.generator,
1514            is_async: m.is_async,
1515            type_params: None,
1516            return_type: None,
1517        }
1518    }
1519
1520    fn convert_arrow_function(&self, arrow: &babel_expr::ArrowFunctionExpression) -> Expr {
1521        let is_expression = arrow.expression.unwrap_or(false);
1522        let params = arrow
1523            .params
1524            .iter()
1525            .map(|p| self.convert_pattern(p))
1526            .collect();
1527
1528        let body: Box<BlockStmtOrExpr> = match &*arrow.body {
1529            babel_expr::ArrowFunctionBody::BlockStatement(block) => Box::new(
1530                BlockStmtOrExpr::BlockStmt(self.convert_block_statement(block)),
1531            ),
1532            babel_expr::ArrowFunctionBody::Expression(expr) => {
1533                if is_expression {
1534                    let converted = self.convert_expression(expr);
1535                    // Wrap object expressions in parens to prevent ambiguity
1536                    // with block bodies: `() => ({...})` vs `() => {...}`
1537                    let converted = if matches!(&converted, Expr::Object(_)) {
1538                        Expr::Paren(ParenExpr {
1539                            span: converted.span(),
1540                            expr: Box::new(converted),
1541                        })
1542                    } else {
1543                        converted
1544                    };
1545                    Box::new(BlockStmtOrExpr::Expr(Box::new(converted)))
1546                } else {
1547                    // Wrap in block with return
1548                    let ret_stmt = Stmt::Return(ReturnStmt {
1549                        span: DUMMY_SP,
1550                        arg: Some(Box::new(self.convert_expression(expr))),
1551                    });
1552                    Box::new(BlockStmtOrExpr::BlockStmt(BlockStmt {
1553                        span: DUMMY_SP,
1554                        ctxt: SyntaxContext::empty(),
1555                        stmts: vec![ret_stmt],
1556                    }))
1557                }
1558            }
1559        };
1560
1561        Expr::Arrow(ArrowExpr {
1562            span: self.span(&arrow.base),
1563            ctxt: SyntaxContext::empty(),
1564            params,
1565            body,
1566            is_async: arrow.is_async,
1567            is_generator: arrow.generator,
1568            return_type: None,
1569            type_params: None,
1570        })
1571    }
1572
1573    fn convert_params(&self, params: &[PatternLike]) -> Vec<Param> {
1574        params
1575            .iter()
1576            .map(|p| Param {
1577                span: DUMMY_SP,
1578                decorators: vec![],
1579                pat: self.convert_pattern(p),
1580            })
1581            .collect()
1582    }
1583
1584    // ===== Patterns =====
1585
1586    fn convert_pattern(&self, pattern: &PatternLike) -> Pat {
1587        match pattern {
1588            PatternLike::Identifier(id) => {
1589                let mut bi = self.binding_ident(&id.name, self.span(&id.base));
1590                bi.id.optional = id.optional.unwrap_or(false);
1591                // Preserve type annotations if present
1592                if let Some(ref type_ann) = id.type_annotation {
1593                    bi.type_ann =
1594                        self.convert_ts_type_annotation_from_json(&type_ann.parse_value());
1595                }
1596                Pat::Ident(bi)
1597            }
1598            PatternLike::ObjectPattern(obj) => {
1599                let mut props: Vec<ObjectPatProp> = Vec::new();
1600
1601                for prop in &obj.properties {
1602                    match prop {
1603                        ObjectPatternProperty::ObjectProperty(p) => {
1604                            if p.shorthand {
1605                                // Shorthand: { x } or { x = default }
1606                                let value = self.convert_pattern(&p.value);
1607                                match &*p.key {
1608                                    BabelExpr::Identifier(id) => {
1609                                        let key_ident =
1610                                            self.binding_ident(&id.name, self.span(&id.base));
1611                                        match value {
1612                                            Pat::Assign(assign_pat) => {
1613                                                props.push(ObjectPatProp::Assign(AssignPatProp {
1614                                                    span: self.span(&p.base),
1615                                                    key: key_ident,
1616                                                    value: Some(assign_pat.right),
1617                                                }));
1618                                            }
1619                                            _ => {
1620                                                props.push(ObjectPatProp::Assign(AssignPatProp {
1621                                                    span: self.span(&p.base),
1622                                                    key: key_ident,
1623                                                    value: None,
1624                                                }));
1625                                            }
1626                                        }
1627                                    }
1628                                    _ => {
1629                                        // Fallback to key-value
1630                                        let key = self.convert_expression_to_prop_name(&p.key);
1631                                        props.push(ObjectPatProp::KeyValue(KeyValuePatProp {
1632                                            key,
1633                                            value: Box::new(value),
1634                                        }));
1635                                    }
1636                                }
1637                            } else {
1638                                let key = self.convert_expression_to_prop_name(&p.key);
1639                                let value = self.convert_pattern(&p.value);
1640                                props.push(ObjectPatProp::KeyValue(KeyValuePatProp {
1641                                    key,
1642                                    value: Box::new(value),
1643                                }));
1644                            }
1645                        }
1646                        ObjectPatternProperty::RestElement(r) => {
1647                            let arg = Box::new(self.convert_pattern(&r.argument));
1648                            props.push(ObjectPatProp::Rest(RestPat {
1649                                span: self.span(&r.base),
1650                                dot3_token: self.span(&r.base),
1651                                arg,
1652                                type_ann: None,
1653                            }));
1654                        }
1655                    }
1656                }
1657
1658                Pat::Object(ObjectPat {
1659                    span: self.span(&obj.base),
1660                    props,
1661                    optional: false,
1662                    type_ann: None,
1663                })
1664            }
1665            PatternLike::ArrayPattern(arr) => {
1666                let elems = arr
1667                    .elements
1668                    .iter()
1669                    .map(|e| e.as_ref().map(|p| self.convert_pattern(p)))
1670                    .collect();
1671                Pat::Array(ArrayPat {
1672                    span: self.span(&arr.base),
1673                    elems,
1674                    optional: false,
1675                    type_ann: None,
1676                })
1677            }
1678            PatternLike::AssignmentPattern(ap) => {
1679                let left = Box::new(self.convert_pattern(&ap.left));
1680                let right = Box::new(self.convert_expression(&ap.right));
1681                Pat::Assign(AssignPat {
1682                    span: self.span(&ap.base),
1683                    left,
1684                    right,
1685                })
1686            }
1687            PatternLike::RestElement(r) => {
1688                let arg = Box::new(self.convert_pattern(&r.argument));
1689                Pat::Rest(RestPat {
1690                    span: self.span(&r.base),
1691                    dot3_token: self.span(&r.base),
1692                    arg,
1693                    type_ann: None,
1694                })
1695            }
1696            PatternLike::MemberExpression(m) => {
1697                // MemberExpression in pattern position - convert to an expression pattern
1698                Pat::Expr(Box::new(self.convert_member_expression(m)))
1699            }
1700            // TS wrappers in pattern position: strip the type wrapper, keep the
1701            // inner expression (unreachable for unsupported targets; non-panicking).
1702            PatternLike::TSAsExpression(e) => {
1703                Pat::Expr(Box::new(self.convert_expression(&e.expression)))
1704            }
1705            PatternLike::TSSatisfiesExpression(e) => {
1706                Pat::Expr(Box::new(self.convert_expression(&e.expression)))
1707            }
1708            PatternLike::TSNonNullExpression(e) => {
1709                Pat::Expr(Box::new(self.convert_expression(&e.expression)))
1710            }
1711            PatternLike::TSTypeAssertion(e) => {
1712                Pat::Expr(Box::new(self.convert_expression(&e.expression)))
1713            }
1714            PatternLike::TypeCastExpression(e) => {
1715                Pat::Expr(Box::new(self.convert_expression(&e.expression)))
1716            }
1717        }
1718    }
1719
1720    // ===== Patterns → AssignmentTarget =====
1721
1722    fn convert_pattern_to_assign_target(&self, pattern: &PatternLike) -> AssignTarget {
1723        match pattern {
1724            PatternLike::Identifier(id) => AssignTarget::Simple(SimpleAssignTarget::Ident(
1725                self.binding_ident(&id.name, self.span(&id.base)),
1726            )),
1727            PatternLike::MemberExpression(m) => {
1728                let expr = self.convert_member_expression(m);
1729                match expr {
1730                    Expr::Member(member) => {
1731                        AssignTarget::Simple(SimpleAssignTarget::Member(member))
1732                    }
1733                    _ => AssignTarget::Simple(SimpleAssignTarget::Ident(
1734                        self.binding_ident("__unknown__", DUMMY_SP),
1735                    )),
1736                }
1737            }
1738            PatternLike::ObjectPattern(_obj) => {
1739                let pat = self.convert_pattern(pattern);
1740                match pat {
1741                    Pat::Object(obj_pat) => AssignTarget::Pat(AssignTargetPat::Object(obj_pat)),
1742                    _ => AssignTarget::Simple(SimpleAssignTarget::Ident(
1743                        self.binding_ident("__unknown__", DUMMY_SP),
1744                    )),
1745                }
1746            }
1747            PatternLike::ArrayPattern(_arr) => {
1748                let pat = self.convert_pattern(pattern);
1749                match pat {
1750                    Pat::Array(arr_pat) => AssignTarget::Pat(AssignTargetPat::Array(arr_pat)),
1751                    _ => AssignTarget::Simple(SimpleAssignTarget::Ident(
1752                        self.binding_ident("__unknown__", DUMMY_SP),
1753                    )),
1754                }
1755            }
1756            PatternLike::AssignmentPattern(ap) => {
1757                // For assignment LHS, use the left side
1758                self.convert_pattern_to_assign_target(&ap.left)
1759            }
1760            PatternLike::RestElement(r) => self.convert_pattern_to_assign_target(&r.argument),
1761            PatternLike::TSAsExpression(_)
1762            | PatternLike::TSSatisfiesExpression(_)
1763            | PatternLike::TSNonNullExpression(_)
1764            | PatternLike::TSTypeAssertion(_)
1765            | PatternLike::TypeCastExpression(_) => AssignTarget::Simple(
1766                SimpleAssignTarget::Ident(self.binding_ident("__unknown__", DUMMY_SP)),
1767            ),
1768        }
1769    }
1770
1771    // ===== JSX =====
1772
1773    fn convert_jsx_element(
1774        &self,
1775        el: &react_compiler_ast::jsx::JSXElement,
1776    ) -> swc_ecma_ast::JSXElement {
1777        let opening = self.convert_jsx_opening_element(&el.opening_element);
1778        let children: Vec<swc_ecma_ast::JSXElementChild> = el
1779            .children
1780            .iter()
1781            .map(|c| self.convert_jsx_child(c))
1782            .collect();
1783        let closing = el
1784            .closing_element
1785            .as_ref()
1786            .map(|c| self.convert_jsx_closing_element(c));
1787        swc_ecma_ast::JSXElement {
1788            span: self.span(&el.base),
1789            opening,
1790            children,
1791            closing,
1792        }
1793    }
1794
1795    fn convert_jsx_opening_element(
1796        &self,
1797        el: &react_compiler_ast::jsx::JSXOpeningElement,
1798    ) -> swc_ecma_ast::JSXOpeningElement {
1799        let name = self.convert_jsx_element_name(&el.name);
1800        let attrs = el
1801            .attributes
1802            .iter()
1803            .map(|a| self.convert_jsx_attribute_item(a))
1804            .collect();
1805        swc_ecma_ast::JSXOpeningElement {
1806            span: self.span(&el.base),
1807            name,
1808            attrs,
1809            self_closing: el.self_closing,
1810            type_args: None,
1811        }
1812    }
1813
1814    fn convert_jsx_closing_element(
1815        &self,
1816        el: &react_compiler_ast::jsx::JSXClosingElement,
1817    ) -> swc_ecma_ast::JSXClosingElement {
1818        let name = self.convert_jsx_element_name(&el.name);
1819        swc_ecma_ast::JSXClosingElement {
1820            span: self.span(&el.base),
1821            name,
1822        }
1823    }
1824
1825    fn convert_jsx_element_name(
1826        &self,
1827        name: &react_compiler_ast::jsx::JSXElementName,
1828    ) -> swc_ecma_ast::JSXElementName {
1829        match name {
1830            react_compiler_ast::jsx::JSXElementName::JSXIdentifier(id) => {
1831                swc_ecma_ast::JSXElementName::Ident(self.ident(&id.name, self.span(&id.base)))
1832            }
1833            react_compiler_ast::jsx::JSXElementName::JSXMemberExpression(m) => {
1834                let member = self.convert_jsx_member_expression(m);
1835                swc_ecma_ast::JSXElementName::JSXMemberExpr(member)
1836            }
1837            react_compiler_ast::jsx::JSXElementName::JSXNamespacedName(ns) => {
1838                let namespace = self.ident_name(&ns.namespace.name, self.span(&ns.namespace.base));
1839                let name = self.ident_name(&ns.name.name, self.span(&ns.name.base));
1840                swc_ecma_ast::JSXElementName::JSXNamespacedName(swc_ecma_ast::JSXNamespacedName {
1841                    span: DUMMY_SP,
1842                    ns: namespace,
1843                    name,
1844                })
1845            }
1846        }
1847    }
1848
1849    fn convert_jsx_member_expression(
1850        &self,
1851        m: &react_compiler_ast::jsx::JSXMemberExpression,
1852    ) -> swc_ecma_ast::JSXMemberExpr {
1853        let obj = self.convert_jsx_member_expression_object(&m.object);
1854        let prop = self.ident_name(&m.property.name, self.span(&m.property.base));
1855        swc_ecma_ast::JSXMemberExpr {
1856            span: DUMMY_SP,
1857            obj,
1858            prop,
1859        }
1860    }
1861
1862    fn convert_jsx_member_expression_object(
1863        &self,
1864        obj: &react_compiler_ast::jsx::JSXMemberExprObject,
1865    ) -> swc_ecma_ast::JSXObject {
1866        match obj {
1867            react_compiler_ast::jsx::JSXMemberExprObject::JSXIdentifier(id) => {
1868                swc_ecma_ast::JSXObject::Ident(self.ident(&id.name, self.span(&id.base)))
1869            }
1870            react_compiler_ast::jsx::JSXMemberExprObject::JSXMemberExpression(m) => {
1871                let member = self.convert_jsx_member_expression(m);
1872                swc_ecma_ast::JSXObject::JSXMemberExpr(Box::new(member))
1873            }
1874        }
1875    }
1876
1877    fn convert_jsx_attribute_item(
1878        &self,
1879        item: &react_compiler_ast::jsx::JSXAttributeItem,
1880    ) -> swc_ecma_ast::JSXAttrOrSpread {
1881        match item {
1882            react_compiler_ast::jsx::JSXAttributeItem::JSXAttribute(attr) => {
1883                let name = self.convert_jsx_attribute_name(&attr.name);
1884                let value = attr
1885                    .value
1886                    .as_ref()
1887                    .map(|v| self.convert_jsx_attribute_value(v));
1888                swc_ecma_ast::JSXAttrOrSpread::JSXAttr(swc_ecma_ast::JSXAttr {
1889                    span: self.span(&attr.base),
1890                    name,
1891                    value,
1892                })
1893            }
1894            react_compiler_ast::jsx::JSXAttributeItem::JSXSpreadAttribute(s) => {
1895                swc_ecma_ast::JSXAttrOrSpread::SpreadElement(SpreadElement {
1896                    dot3_token: self.span(&s.base),
1897                    expr: Box::new(self.convert_expression(&s.argument)),
1898                })
1899            }
1900        }
1901    }
1902
1903    fn convert_jsx_attribute_name(
1904        &self,
1905        name: &react_compiler_ast::jsx::JSXAttributeName,
1906    ) -> swc_ecma_ast::JSXAttrName {
1907        match name {
1908            react_compiler_ast::jsx::JSXAttributeName::JSXIdentifier(id) => {
1909                swc_ecma_ast::JSXAttrName::Ident(self.ident_name(&id.name, self.span(&id.base)))
1910            }
1911            react_compiler_ast::jsx::JSXAttributeName::JSXNamespacedName(ns) => {
1912                let namespace = self.ident_name(&ns.namespace.name, self.span(&ns.namespace.base));
1913                let name = self.ident_name(&ns.name.name, self.span(&ns.name.base));
1914                swc_ecma_ast::JSXAttrName::JSXNamespacedName(swc_ecma_ast::JSXNamespacedName {
1915                    span: DUMMY_SP,
1916                    ns: namespace,
1917                    name,
1918                })
1919            }
1920        }
1921    }
1922
1923    fn convert_jsx_attribute_value(
1924        &self,
1925        value: &react_compiler_ast::jsx::JSXAttributeValue,
1926    ) -> swc_ecma_ast::JSXAttrValue {
1927        match value {
1928            react_compiler_ast::jsx::JSXAttributeValue::StringLiteral(s) => {
1929                // For JSX attributes, if the value contains double quotes,
1930                // use single quotes to avoid escaping issues that prettier
1931                // can't parse (e.g., name="\"user\" name").
1932                let raw = if s.value.contains('"') {
1933                    Some(Atom::from(format!(
1934                        "'{}'",
1935                        s.value.replace('\\', "\\\\").replace('\'', "\\'")
1936                    )))
1937                } else {
1938                    self.escape_string_raw(&s.value)
1939                };
1940                swc_ecma_ast::JSXAttrValue::Str(Str {
1941                    span: self.span(&s.base),
1942                    value: self.wtf8(&s.value),
1943                    raw,
1944                })
1945            }
1946            react_compiler_ast::jsx::JSXAttributeValue::JSXExpressionContainer(ec) => {
1947                let expr = self.convert_jsx_expression_container_expr(&ec.expression);
1948                swc_ecma_ast::JSXAttrValue::JSXExprContainer(swc_ecma_ast::JSXExprContainer {
1949                    span: self.span(&ec.base),
1950                    expr,
1951                })
1952            }
1953            react_compiler_ast::jsx::JSXAttributeValue::JSXElement(el) => {
1954                let element = self.convert_jsx_element(el.as_ref());
1955                swc_ecma_ast::JSXAttrValue::JSXElement(Box::new(element))
1956            }
1957            react_compiler_ast::jsx::JSXAttributeValue::JSXFragment(frag) => {
1958                let fragment = self.convert_jsx_fragment(frag);
1959                swc_ecma_ast::JSXAttrValue::JSXFragment(fragment)
1960            }
1961        }
1962    }
1963
1964    fn convert_jsx_expression_container_expr(
1965        &self,
1966        expr: &react_compiler_ast::jsx::JSXExpressionContainerExpr,
1967    ) -> swc_ecma_ast::JSXExpr {
1968        match expr {
1969            react_compiler_ast::jsx::JSXExpressionContainerExpr::JSXEmptyExpression(e) => {
1970                swc_ecma_ast::JSXExpr::JSXEmptyExpr(swc_ecma_ast::JSXEmptyExpr {
1971                    span: self.span(&e.base),
1972                })
1973            }
1974            react_compiler_ast::jsx::JSXExpressionContainerExpr::Expression(e) => {
1975                swc_ecma_ast::JSXExpr::Expr(Box::new(self.convert_expression(e)))
1976            }
1977        }
1978    }
1979
1980    fn convert_jsx_child(
1981        &self,
1982        child: &react_compiler_ast::jsx::JSXChild,
1983    ) -> swc_ecma_ast::JSXElementChild {
1984        match child {
1985            react_compiler_ast::jsx::JSXChild::JSXText(t) => {
1986                swc_ecma_ast::JSXElementChild::JSXText(swc_ecma_ast::JSXText {
1987                    span: self.span(&t.base),
1988                    value: self.atom(&t.value),
1989                    raw: self.atom(&t.value),
1990                })
1991            }
1992            react_compiler_ast::jsx::JSXChild::JSXElement(el) => {
1993                let element = self.convert_jsx_element(el.as_ref());
1994                swc_ecma_ast::JSXElementChild::JSXElement(Box::new(element))
1995            }
1996            react_compiler_ast::jsx::JSXChild::JSXFragment(frag) => {
1997                let fragment = self.convert_jsx_fragment(frag);
1998                swc_ecma_ast::JSXElementChild::JSXFragment(fragment)
1999            }
2000            react_compiler_ast::jsx::JSXChild::JSXExpressionContainer(ec) => {
2001                let expr = self.convert_jsx_expression_container_expr(&ec.expression);
2002                swc_ecma_ast::JSXElementChild::JSXExprContainer(swc_ecma_ast::JSXExprContainer {
2003                    span: self.span(&ec.base),
2004                    expr,
2005                })
2006            }
2007            react_compiler_ast::jsx::JSXChild::JSXSpreadChild(s) => {
2008                swc_ecma_ast::JSXElementChild::JSXSpreadChild(swc_ecma_ast::JSXSpreadChild {
2009                    span: self.span(&s.base),
2010                    expr: Box::new(self.convert_expression(&s.expression)),
2011                })
2012            }
2013        }
2014    }
2015
2016    fn convert_jsx_fragment(
2017        &self,
2018        frag: &react_compiler_ast::jsx::JSXFragment,
2019    ) -> swc_ecma_ast::JSXFragment {
2020        let children = frag
2021            .children
2022            .iter()
2023            .map(|c| self.convert_jsx_child(c))
2024            .collect();
2025        swc_ecma_ast::JSXFragment {
2026            span: self.span(&frag.base),
2027            opening: swc_ecma_ast::JSXOpeningFragment {
2028                span: self.span(&frag.opening_fragment.base),
2029            },
2030            children,
2031            closing: swc_ecma_ast::JSXClosingFragment {
2032                span: self.span(&frag.closing_fragment.base),
2033            },
2034        }
2035    }
2036
2037    // ===== Import/Export =====
2038
2039    fn convert_import_declaration(&self, decl: &ImportDeclaration) -> swc_ecma_ast::ImportDecl {
2040        let specifiers = decl
2041            .specifiers
2042            .iter()
2043            .map(|s| self.convert_import_specifier(s))
2044            .collect();
2045        let src = Box::new(Str {
2046            span: self.span(&decl.source.base),
2047            value: self.wtf8(&decl.source.value),
2048            raw: None,
2049        });
2050        let type_only = matches!(decl.import_kind.as_ref(), Some(ImportKind::Type));
2051        swc_ecma_ast::ImportDecl {
2052            span: self.span(&decl.base),
2053            specifiers,
2054            src,
2055            type_only,
2056            with: None,
2057            phase: Default::default(),
2058        }
2059    }
2060
2061    fn convert_import_specifier(
2062        &self,
2063        spec: &react_compiler_ast::declarations::ImportSpecifier,
2064    ) -> swc_ecma_ast::ImportSpecifier {
2065        match spec {
2066            react_compiler_ast::declarations::ImportSpecifier::ImportSpecifier(s) => {
2067                let local = self.ident(&s.local.name, self.span(&s.local.base));
2068                // Only set `imported` if it differs from `local` — otherwise
2069                // SWC emits `foo as foo` instead of just `foo`.
2070                let imported_name = match &s.imported {
2071                    react_compiler_ast::declarations::ModuleExportName::Identifier(id) => {
2072                        Some(&id.name)
2073                    }
2074                    react_compiler_ast::declarations::ModuleExportName::StringLiteral(_) => None,
2075                };
2076                let imported = if imported_name == Some(&s.local.name) {
2077                    None
2078                } else {
2079                    Some(self.convert_module_export_name(&s.imported))
2080                };
2081                let is_type_only = matches!(s.import_kind.as_ref(), Some(ImportKind::Type));
2082                swc_ecma_ast::ImportSpecifier::Named(ImportNamedSpecifier {
2083                    span: self.span(&s.base),
2084                    local,
2085                    imported,
2086                    is_type_only,
2087                })
2088            }
2089            react_compiler_ast::declarations::ImportSpecifier::ImportDefaultSpecifier(s) => {
2090                let local = self.ident(&s.local.name, self.span(&s.local.base));
2091                swc_ecma_ast::ImportSpecifier::Default(ImportDefaultSpecifier {
2092                    span: self.span(&s.base),
2093                    local,
2094                })
2095            }
2096            react_compiler_ast::declarations::ImportSpecifier::ImportNamespaceSpecifier(s) => {
2097                let local = self.ident(&s.local.name, self.span(&s.local.base));
2098                swc_ecma_ast::ImportSpecifier::Namespace(ImportStarAsSpecifier {
2099                    span: self.span(&s.base),
2100                    local,
2101                })
2102            }
2103        }
2104    }
2105
2106    fn convert_module_export_name(
2107        &self,
2108        name: &react_compiler_ast::declarations::ModuleExportName,
2109    ) -> swc_ecma_ast::ModuleExportName {
2110        match name {
2111            react_compiler_ast::declarations::ModuleExportName::Identifier(id) => {
2112                swc_ecma_ast::ModuleExportName::Ident(self.ident(&id.name, self.span(&id.base)))
2113            }
2114            react_compiler_ast::declarations::ModuleExportName::StringLiteral(s) => {
2115                swc_ecma_ast::ModuleExportName::Str(Str {
2116                    span: self.span(&s.base),
2117                    value: self.wtf8(&s.value),
2118                    raw: None,
2119                })
2120            }
2121        }
2122    }
2123
2124    fn convert_export_named_to_module_item(&self, decl: &ExportNamedDeclaration) -> ModuleItem {
2125        // If there's a declaration, emit as ExportDecl
2126        if let Some(declaration) = &decl.declaration {
2127            let swc_decl = self.convert_declaration(declaration);
2128            return ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
2129                span: self.span(&decl.base),
2130                decl: swc_decl,
2131            }));
2132        }
2133        self.convert_export_named_specifiers(decl)
2134    }
2135
2136    fn convert_declaration(&self, decl: &react_compiler_ast::declarations::Declaration) -> Decl {
2137        match decl {
2138            react_compiler_ast::declarations::Declaration::FunctionDeclaration(f) => {
2139                Decl::Fn(self.convert_function_declaration(f))
2140            }
2141            react_compiler_ast::declarations::Declaration::VariableDeclaration(v) => {
2142                Decl::Var(Box::new(self.convert_variable_declaration(v)))
2143            }
2144            react_compiler_ast::declarations::Declaration::ClassDeclaration(c) => {
2145                let ident =
2146                    c.id.as_ref()
2147                        .map(|id| self.ident(&id.name, self.span(&id.base)))
2148                        .unwrap_or_else(|| self.ident("_anonymous", DUMMY_SP));
2149                let super_class = c
2150                    .super_class
2151                    .as_ref()
2152                    .map(|s| Box::new(self.convert_expression(s)));
2153                Decl::Class(ClassDecl {
2154                    ident,
2155                    declare: c.declare.unwrap_or(false),
2156                    class: Box::new(Class {
2157                        span: self.span(&c.base),
2158                        ctxt: SyntaxContext::empty(),
2159                        decorators: vec![],
2160                        body: vec![],
2161                        super_class,
2162                        is_abstract: false,
2163                        type_params: None,
2164                        super_type_params: None,
2165                        implements: vec![],
2166                    }),
2167                })
2168            }
2169            _ => Decl::Var(Box::new(VarDecl {
2170                span: DUMMY_SP,
2171                ctxt: SyntaxContext::empty(),
2172                kind: VarDeclKind::Const,
2173                declare: true,
2174                decls: vec![],
2175            })),
2176        }
2177    }
2178
2179    fn convert_export_named_specifiers(&self, decl: &ExportNamedDeclaration) -> ModuleItem {
2180        let specifiers = decl
2181            .specifiers
2182            .iter()
2183            .map(|s| self.convert_export_specifier(s))
2184            .collect();
2185        let src = decl.source.as_ref().map(|s| {
2186            Box::new(Str {
2187                span: self.span(&s.base),
2188                value: self.wtf8(&s.value),
2189                raw: None,
2190            })
2191        });
2192        let type_only = matches!(decl.export_kind.as_ref(), Some(ExportKind::Type));
2193
2194        ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(NamedExport {
2195            span: self.span(&decl.base),
2196            specifiers,
2197            src,
2198            type_only,
2199            with: None,
2200        }))
2201    }
2202
2203    fn convert_export_specifier(
2204        &self,
2205        spec: &react_compiler_ast::declarations::ExportSpecifier,
2206    ) -> swc_ecma_ast::ExportSpecifier {
2207        match spec {
2208            react_compiler_ast::declarations::ExportSpecifier::ExportSpecifier(s) => {
2209                let orig = self.convert_module_export_name(&s.local);
2210                // Only set `exported` if it differs from `local`
2211                let local_name = match &s.local {
2212                    react_compiler_ast::declarations::ModuleExportName::Identifier(id) => {
2213                        Some(&id.name)
2214                    }
2215                    _ => None,
2216                };
2217                let exported_name = match &s.exported {
2218                    react_compiler_ast::declarations::ModuleExportName::Identifier(id) => {
2219                        Some(&id.name)
2220                    }
2221                    _ => None,
2222                };
2223                let exported = if local_name.is_some() && local_name == exported_name {
2224                    None
2225                } else {
2226                    Some(self.convert_module_export_name(&s.exported))
2227                };
2228                let is_type_only = matches!(s.export_kind.as_ref(), Some(ExportKind::Type));
2229                swc_ecma_ast::ExportSpecifier::Named(ExportNamedSpecifier {
2230                    span: self.span(&s.base),
2231                    orig,
2232                    exported,
2233                    is_type_only,
2234                })
2235            }
2236            react_compiler_ast::declarations::ExportSpecifier::ExportDefaultSpecifier(s) => {
2237                swc_ecma_ast::ExportSpecifier::Default(swc_ecma_ast::ExportDefaultSpecifier {
2238                    exported: self.ident(&s.exported.name, self.span(&s.exported.base)),
2239                })
2240            }
2241            react_compiler_ast::declarations::ExportSpecifier::ExportNamespaceSpecifier(s) => {
2242                let name = self.convert_module_export_name(&s.exported);
2243                swc_ecma_ast::ExportSpecifier::Namespace(ExportNamespaceSpecifier {
2244                    span: self.span(&s.base),
2245                    name,
2246                })
2247            }
2248        }
2249    }
2250
2251    fn convert_export_default_to_module_item(&self, decl: &ExportDefaultDeclaration) -> ModuleItem {
2252        let span = self.span(&decl.base);
2253        match &*decl.declaration {
2254            BabelExportDefaultDecl::FunctionDeclaration(f) => {
2255                let fd = self.convert_function_declaration(f);
2256                ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(
2257                    swc_ecma_ast::ExportDefaultDecl {
2258                        span,
2259                        decl: swc_ecma_ast::DefaultDecl::Fn(FnExpr {
2260                            ident: Some(fd.ident),
2261                            function: fd.function,
2262                        }),
2263                    },
2264                ))
2265            }
2266            BabelExportDefaultDecl::ClassDeclaration(c) => {
2267                let ident =
2268                    c.id.as_ref()
2269                        .map(|id| self.ident(&id.name, self.span(&id.base)));
2270                let super_class = c
2271                    .super_class
2272                    .as_ref()
2273                    .map(|s| Box::new(self.convert_expression(s)));
2274                ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(
2275                    swc_ecma_ast::ExportDefaultDecl {
2276                        span,
2277                        decl: swc_ecma_ast::DefaultDecl::Class(ClassExpr {
2278                            ident,
2279                            class: Box::new(Class {
2280                                span,
2281                                ctxt: SyntaxContext::empty(),
2282                                decorators: vec![],
2283                                body: vec![],
2284                                super_class,
2285                                is_abstract: false,
2286                                type_params: None,
2287                                super_type_params: None,
2288                                implements: vec![],
2289                            }),
2290                        }),
2291                    },
2292                ))
2293            }
2294            BabelExportDefaultDecl::EnumDeclaration(_) => {
2295                // Flow enum declarations cannot be represented in SWC AST;
2296                // emit a null placeholder to preserve the export shape.
2297                ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr(ExportDefaultExpr {
2298                    span,
2299                    expr: Box::new(swc_ecma_ast::Expr::Lit(swc_ecma_ast::Lit::Null(
2300                        swc_ecma_ast::Null { span },
2301                    ))),
2302                }))
2303            }
2304            BabelExportDefaultDecl::Expression(e) => {
2305                ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr(ExportDefaultExpr {
2306                    span,
2307                    expr: Box::new(self.convert_expression(e)),
2308                }))
2309            }
2310        }
2311    }
2312
2313    fn convert_export_all_declaration(
2314        &self,
2315        decl: &ExportAllDeclaration,
2316    ) -> swc_ecma_ast::ExportAll {
2317        let src = Box::new(Str {
2318            span: self.span(&decl.source.base),
2319            value: self.wtf8(&decl.source.value),
2320            raw: None,
2321        });
2322        let type_only = matches!(decl.export_kind.as_ref(), Some(ExportKind::Type));
2323        swc_ecma_ast::ExportAll {
2324            span: self.span(&decl.base),
2325            src,
2326            type_only,
2327            with: None,
2328        }
2329    }
2330
2331    // ===== TS type helpers =====
2332
2333    /// Convert a Babel TSTypeAnnotation JSON to an SWC TsTypeAnnotation.
2334    /// Returns None if the JSON is not a valid type annotation.
2335    fn convert_ts_type_annotation_from_json(
2336        &self,
2337        json: &serde_json::Value,
2338    ) -> Option<Box<TsTypeAnn>> {
2339        let type_name = json.get("type")?.as_str()?;
2340        if type_name != "TSTypeAnnotation" && type_name != "TypeAnnotation" {
2341            return None;
2342        }
2343        let type_annotation = json.get("typeAnnotation")?;
2344        let ts_type = self.convert_ts_type_from_json(type_annotation, DUMMY_SP);
2345        Some(Box::new(TsTypeAnn {
2346            span: DUMMY_SP,
2347            type_ann: Box::new(ts_type),
2348        }))
2349    }
2350
2351    /// Convert a JSON-serialized TypeScript type annotation to an SWC TsType.
2352    /// This handles common cases from the compiler's output. For unrecognized
2353    /// types, it falls back to `any`.
2354    fn convert_ts_type_from_json(&self, json: &serde_json::Value, span: Span) -> TsType {
2355        let type_name = json.get("type").and_then(|v| v.as_str()).unwrap_or("");
2356        match type_name {
2357            "TSTypeReference" => {
2358                let name = json
2359                    .get("typeName")
2360                    .and_then(|tn| tn.get("name"))
2361                    .and_then(|n| n.as_str())
2362                    .unwrap_or("unknown");
2363                if name == "const" {
2364                    TsType::TsTypeRef(TsTypeRef {
2365                        span,
2366                        type_name: TsEntityName::Ident(self.ident("const", span)),
2367                        type_params: None,
2368                    })
2369                } else {
2370                    TsType::TsTypeRef(TsTypeRef {
2371                        span,
2372                        type_name: TsEntityName::Ident(self.ident(name, span)),
2373                        type_params: None,
2374                    })
2375                }
2376            }
2377            "TSNumberKeyword" => TsType::TsKeywordType(TsKeywordType {
2378                span,
2379                kind: TsKeywordTypeKind::TsNumberKeyword,
2380            }),
2381            "TSStringKeyword" => TsType::TsKeywordType(TsKeywordType {
2382                span,
2383                kind: TsKeywordTypeKind::TsStringKeyword,
2384            }),
2385            "TSBooleanKeyword" => TsType::TsKeywordType(TsKeywordType {
2386                span,
2387                kind: TsKeywordTypeKind::TsBooleanKeyword,
2388            }),
2389            "TSVoidKeyword" => TsType::TsKeywordType(TsKeywordType {
2390                span,
2391                kind: TsKeywordTypeKind::TsVoidKeyword,
2392            }),
2393            "TSNullKeyword" => TsType::TsKeywordType(TsKeywordType {
2394                span,
2395                kind: TsKeywordTypeKind::TsNullKeyword,
2396            }),
2397            "TSUndefinedKeyword" => TsType::TsKeywordType(TsKeywordType {
2398                span,
2399                kind: TsKeywordTypeKind::TsUndefinedKeyword,
2400            }),
2401            "TSAnyKeyword" => TsType::TsKeywordType(TsKeywordType {
2402                span,
2403                kind: TsKeywordTypeKind::TsAnyKeyword,
2404            }),
2405            "TSNeverKeyword" => TsType::TsKeywordType(TsKeywordType {
2406                span,
2407                kind: TsKeywordTypeKind::TsNeverKeyword,
2408            }),
2409            "TSUnionType" => {
2410                let types = json
2411                    .get("types")
2412                    .and_then(|t| t.as_array())
2413                    .map(|arr| {
2414                        arr.iter()
2415                            .map(|t| Box::new(self.convert_ts_type_from_json(t, span)))
2416                            .collect::<Vec<_>>()
2417                    })
2418                    .unwrap_or_default();
2419                TsType::TsUnionOrIntersectionType(TsUnionOrIntersectionType::TsUnionType(
2420                    TsUnionType { span, types },
2421                ))
2422            }
2423            "TSIntersectionType" => {
2424                let types = json
2425                    .get("types")
2426                    .and_then(|t| t.as_array())
2427                    .map(|arr| {
2428                        arr.iter()
2429                            .map(|t| Box::new(self.convert_ts_type_from_json(t, span)))
2430                            .collect::<Vec<_>>()
2431                    })
2432                    .unwrap_or_default();
2433                TsType::TsUnionOrIntersectionType(TsUnionOrIntersectionType::TsIntersectionType(
2434                    TsIntersectionType { span, types },
2435                ))
2436            }
2437            "TSLiteralType" => {
2438                if let Some(literal) = json.get("literal") {
2439                    let lit_type = literal.get("type").and_then(|t| t.as_str()).unwrap_or("");
2440                    match lit_type {
2441                        "StringLiteral" => {
2442                            let value = literal.get("value").and_then(|v| v.as_str()).unwrap_or("");
2443                            TsType::TsLitType(TsLitType {
2444                                span,
2445                                lit: TsLit::Str(Str {
2446                                    span,
2447                                    value: self.wtf8(value),
2448                                    raw: None,
2449                                }),
2450                            })
2451                        }
2452                        "NumericLiteral" => {
2453                            let value =
2454                                literal.get("value").and_then(|v| v.as_f64()).unwrap_or(0.0);
2455                            TsType::TsLitType(TsLitType {
2456                                span,
2457                                lit: TsLit::Number(Number {
2458                                    span,
2459                                    value,
2460                                    raw: None,
2461                                }),
2462                            })
2463                        }
2464                        "BooleanLiteral" => {
2465                            let value = literal
2466                                .get("value")
2467                                .and_then(|v| v.as_bool())
2468                                .unwrap_or(false);
2469                            TsType::TsLitType(TsLitType {
2470                                span,
2471                                lit: TsLit::Bool(Bool { span, value }),
2472                            })
2473                        }
2474                        _ => TsType::TsKeywordType(TsKeywordType {
2475                            span,
2476                            kind: TsKeywordTypeKind::TsAnyKeyword,
2477                        }),
2478                    }
2479                } else {
2480                    TsType::TsKeywordType(TsKeywordType {
2481                        span,
2482                        kind: TsKeywordTypeKind::TsAnyKeyword,
2483                    })
2484                }
2485            }
2486            "TSArrayType" => {
2487                let elem = json
2488                    .get("elementType")
2489                    .map(|t| self.convert_ts_type_from_json(t, span))
2490                    .unwrap_or(TsType::TsKeywordType(TsKeywordType {
2491                        span,
2492                        kind: TsKeywordTypeKind::TsAnyKeyword,
2493                    }));
2494                TsType::TsArrayType(TsArrayType {
2495                    span,
2496                    elem_type: Box::new(elem),
2497                })
2498            }
2499            "TSFunctionType"
2500            | "TSTypeLiteral"
2501            | "TSParenthesizedType"
2502            | "TSTupleType"
2503            | "TSOptionalType"
2504            | "TSRestType"
2505            | "TSConditionalType"
2506            | "TSInferType"
2507            | "TSMappedType"
2508            | "TSIndexedAccessType"
2509            | "TSTypeOperator"
2510            | "TSTypePredicate"
2511            | "TSImportType"
2512            | "TSQualifiedName" => {
2513                // For complex types, try to extract from source text
2514                if let (Some(source), Some(start), Some(end)) = (
2515                    self.source_text.as_deref(),
2516                    json.get("start").and_then(|v| v.as_u64()),
2517                    json.get("end").and_then(|v| v.as_u64()),
2518                ) {
2519                    let start_idx = (start as usize).saturating_sub(1);
2520                    let end_idx = (end as usize).saturating_sub(1);
2521                    if start_idx < source.len() && end_idx <= source.len() && start_idx < end_idx {
2522                        let text = &source[start_idx..end_idx];
2523                        // Parse the type using SWC
2524                        let wrapper = format!("type __T = {};", text);
2525                        let cm = swc_common::sync::Lrc::new(swc_common::SourceMap::default());
2526                        let fm = cm.new_source_file(
2527                            swc_common::sync::Lrc::new(swc_common::FileName::Anon),
2528                            wrapper,
2529                        );
2530                        let mut errors = vec![];
2531                        if let Ok(module) = swc_ecma_parser::parse_file_as_module(
2532                            &fm,
2533                            swc_ecma_parser::Syntax::Typescript(swc_ecma_parser::TsSyntax {
2534                                tsx: true,
2535                                ..Default::default()
2536                            }),
2537                            swc_ecma_ast::EsVersion::latest(),
2538                            None,
2539                            &mut errors,
2540                        ) {
2541                            if let Some(ModuleItem::Stmt(Stmt::Decl(Decl::TsTypeAlias(alias)))) =
2542                                module.body.into_iter().next()
2543                            {
2544                                return *alias.type_ann;
2545                            }
2546                        }
2547                    }
2548                }
2549                // Fallback
2550                TsType::TsKeywordType(TsKeywordType {
2551                    span,
2552                    kind: TsKeywordTypeKind::TsAnyKeyword,
2553                })
2554            }
2555            // Flow types
2556            "NumberTypeAnnotation"
2557            | "StringTypeAnnotation"
2558            | "BooleanTypeAnnotation"
2559            | "VoidTypeAnnotation"
2560            | "NullLiteralTypeAnnotation"
2561            | "AnyTypeAnnotation"
2562            | "GenericTypeAnnotation"
2563            | "UnionTypeAnnotation"
2564            | "IntersectionTypeAnnotation"
2565            | "NullableTypeAnnotation"
2566            | "FunctionTypeAnnotation"
2567            | "ObjectTypeAnnotation"
2568            | "ArrayTypeAnnotation"
2569            | "TupleTypeAnnotation"
2570            | "TypeofTypeAnnotation"
2571            | "NumberLiteralTypeAnnotation"
2572            | "StringLiteralTypeAnnotation"
2573            | "BooleanLiteralTypeAnnotation" => {
2574                // For Flow types, try to extract from source text
2575                if let (Some(source), Some(start), Some(end)) = (
2576                    self.source_text.as_deref(),
2577                    json.get("start").and_then(|v| v.as_u64()),
2578                    json.get("end").and_then(|v| v.as_u64()),
2579                ) {
2580                    let start_idx = (start as usize).saturating_sub(1);
2581                    let end_idx = (end as usize).saturating_sub(1);
2582                    if start_idx < source.len() && end_idx <= source.len() && start_idx < end_idx {
2583                        let text = &source[start_idx..end_idx];
2584                        // For Flow types, we can use TS parser as many simple types
2585                        // have the same syntax
2586                        let wrapper = format!("type __T = {};", text);
2587                        let cm = swc_common::sync::Lrc::new(swc_common::SourceMap::default());
2588                        let fm = cm.new_source_file(
2589                            swc_common::sync::Lrc::new(swc_common::FileName::Anon),
2590                            wrapper,
2591                        );
2592                        let mut errors = vec![];
2593                        if let Ok(module) = swc_ecma_parser::parse_file_as_module(
2594                            &fm,
2595                            swc_ecma_parser::Syntax::Typescript(swc_ecma_parser::TsSyntax {
2596                                tsx: true,
2597                                ..Default::default()
2598                            }),
2599                            swc_ecma_ast::EsVersion::latest(),
2600                            None,
2601                            &mut errors,
2602                        ) {
2603                            if let Some(ModuleItem::Stmt(Stmt::Decl(Decl::TsTypeAlias(alias)))) =
2604                                module.body.into_iter().next()
2605                            {
2606                                return *alias.type_ann;
2607                            }
2608                        }
2609                    }
2610                }
2611                // Fallback
2612                TsType::TsKeywordType(TsKeywordType {
2613                    span,
2614                    kind: TsKeywordTypeKind::TsAnyKeyword,
2615                })
2616            }
2617            _ => {
2618                // Fallback: emit `any` type
2619                TsType::TsKeywordType(TsKeywordType {
2620                    span,
2621                    kind: TsKeywordTypeKind::TsAnyKeyword,
2622                })
2623            }
2624        }
2625    }
2626
2627    // ===== Operators =====
2628
2629    fn convert_binary_operator(&self, op: &BinaryOperator) -> BinaryOp {
2630        match op {
2631            BinaryOperator::Add => BinaryOp::Add,
2632            BinaryOperator::Sub => BinaryOp::Sub,
2633            BinaryOperator::Mul => BinaryOp::Mul,
2634            BinaryOperator::Div => BinaryOp::Div,
2635            BinaryOperator::Rem => BinaryOp::Mod,
2636            BinaryOperator::Exp => BinaryOp::Exp,
2637            BinaryOperator::Eq => BinaryOp::EqEq,
2638            BinaryOperator::StrictEq => BinaryOp::EqEqEq,
2639            BinaryOperator::Neq => BinaryOp::NotEq,
2640            BinaryOperator::StrictNeq => BinaryOp::NotEqEq,
2641            BinaryOperator::Lt => BinaryOp::Lt,
2642            BinaryOperator::Lte => BinaryOp::LtEq,
2643            BinaryOperator::Gt => BinaryOp::Gt,
2644            BinaryOperator::Gte => BinaryOp::GtEq,
2645            BinaryOperator::Shl => BinaryOp::LShift,
2646            BinaryOperator::Shr => BinaryOp::RShift,
2647            BinaryOperator::UShr => BinaryOp::ZeroFillRShift,
2648            BinaryOperator::BitOr => BinaryOp::BitOr,
2649            BinaryOperator::BitXor => BinaryOp::BitXor,
2650            BinaryOperator::BitAnd => BinaryOp::BitAnd,
2651            BinaryOperator::In => BinaryOp::In,
2652            BinaryOperator::Instanceof => BinaryOp::InstanceOf,
2653            BinaryOperator::Pipeline => BinaryOp::BitOr, // no pipeline in SWC
2654        }
2655    }
2656
2657    fn convert_logical_operator(&self, op: &LogicalOperator) -> BinaryOp {
2658        match op {
2659            LogicalOperator::Or => BinaryOp::LogicalOr,
2660            LogicalOperator::And => BinaryOp::LogicalAnd,
2661            LogicalOperator::NullishCoalescing => BinaryOp::NullishCoalescing,
2662        }
2663    }
2664
2665    fn convert_unary_operator(&self, op: &UnaryOperator) -> UnaryOp {
2666        match op {
2667            UnaryOperator::Neg => UnaryOp::Minus,
2668            UnaryOperator::Plus => UnaryOp::Plus,
2669            UnaryOperator::Not => UnaryOp::Bang,
2670            UnaryOperator::BitNot => UnaryOp::Tilde,
2671            UnaryOperator::TypeOf => UnaryOp::TypeOf,
2672            UnaryOperator::Void => UnaryOp::Void,
2673            UnaryOperator::Delete => UnaryOp::Delete,
2674            UnaryOperator::Throw => UnaryOp::Void, // no throw-as-unary in SWC
2675        }
2676    }
2677
2678    fn convert_update_operator(&self, op: &UpdateOperator) -> UpdateOp {
2679        match op {
2680            UpdateOperator::Increment => UpdateOp::PlusPlus,
2681            UpdateOperator::Decrement => UpdateOp::MinusMinus,
2682        }
2683    }
2684
2685    fn convert_assignment_operator(&self, op: &AssignmentOperator) -> AssignOp {
2686        match op {
2687            AssignmentOperator::Assign => AssignOp::Assign,
2688            AssignmentOperator::AddAssign => AssignOp::AddAssign,
2689            AssignmentOperator::SubAssign => AssignOp::SubAssign,
2690            AssignmentOperator::MulAssign => AssignOp::MulAssign,
2691            AssignmentOperator::DivAssign => AssignOp::DivAssign,
2692            AssignmentOperator::RemAssign => AssignOp::ModAssign,
2693            AssignmentOperator::ExpAssign => AssignOp::ExpAssign,
2694            AssignmentOperator::ShlAssign => AssignOp::LShiftAssign,
2695            AssignmentOperator::ShrAssign => AssignOp::RShiftAssign,
2696            AssignmentOperator::UShrAssign => AssignOp::ZeroFillRShiftAssign,
2697            AssignmentOperator::BitOrAssign => AssignOp::BitOrAssign,
2698            AssignmentOperator::BitXorAssign => AssignOp::BitXorAssign,
2699            AssignmentOperator::BitAndAssign => AssignOp::BitAndAssign,
2700            AssignmentOperator::OrAssign => AssignOp::OrAssign,
2701            AssignmentOperator::AndAssign => AssignOp::AndAssign,
2702            AssignmentOperator::NullishAssign => AssignOp::NullishAssign,
2703        }
2704    }
2705}