Skip to main content

macroforge_ts_syn/
lib.rs

1//! # macroforge_ts_syn
2//!
3//! TypeScript syntax types for compile-time macro code generation.
4//!
5//! This crate provides a [`syn`](https://docs.rs/syn)-like API for parsing and manipulating
6//! TypeScript code, enabling macro authors to work with TypeScript AST in a familiar way.
7//! It is the core infrastructure crate for the Macroforge TypeScript macro system.
8//!
9//! ## Overview
10//!
11//! The crate is organized into several modules:
12//!
13//! - [`abi`] - Application Binary Interface types for stable macro communication
14//! - [`derive`] - Derive input types that mirror Rust's `syn::DeriveInput`
15//! - [`errors`] - Error types and diagnostics for macro expansion
16//! - [`lower`] - AST lowering from SWC types to IR representations
17//! - [`parse`] - TypeScript parsing utilities wrapping SWC
18//! - [`quote_helpers`] - Macros for ergonomic code generation
19//! - [`stream`] - Parsing stream abstraction similar to `syn::parse::ParseBuffer`
20//!
21//! ## Architecture
22//!
23//! The crate follows a layered architecture:
24//!
25//! ```text
26//! ┌─────────────────────────────────────────────────────────────┐
27//! │                    User-Facing API                          │
28//! │  (DeriveInput, TsStream, parse_ts_macro_input!)             │
29//! ├─────────────────────────────────────────────────────────────┤
30//! │                    Lowering Layer                           │
31//! │  (lower_classes, lower_interfaces, lower_enums, ...)        │
32//! ├─────────────────────────────────────────────────────────────┤
33//! │                    IR Types (ABI Stable)                    │
34//! │  (ClassIR, InterfaceIR, EnumIR, TypeAliasIR, ...)           │
35//! ├─────────────────────────────────────────────────────────────┤
36//! │                    SWC Parser                               │
37//! │  (swc_core for TypeScript/JavaScript parsing)               │
38//! └─────────────────────────────────────────────────────────────┘
39//! ```
40//!
41//! ## Usage Example
42//!
43//! Here's how to use this crate in a derive macro:
44//!
45//! ```rust,ignore
46//! use macroforge_ts_syn::{parse_ts_macro_input, DeriveInput, MacroResult, Patch, Data, MacroContextIR};
47//!
48//! // This function signature shows a typical derive macro entry point
49//! pub fn my_derive_macro(ctx: MacroContextIR) -> MacroResult {
50//!     // Parse the input using the syn-like API
51//!     let input = parse_ts_macro_input!(ctx);
52//!
53//!     // Access type information
54//!     println!("Processing type: {}", input.name());
55//!
56//!     // Match on the type kind
57//!     match &input.data {
58//!         Data::Class(class) => {
59//!             for field in class.fields() {
60//!                 println!("Field: {}", field.name);
61//!             }
62//!         }
63//!         Data::Interface(_iface) => {
64//!             // Handle interface...
65//!         }
66//!         Data::Enum(_enum_) => {
67//!             // Handle enum...
68//!         }
69//!         Data::TypeAlias(_alias) => {
70//!             // Handle type alias...
71//!         }
72//!     }
73//!
74//!     // Generate code and return patches
75//!     MacroResult::ok()
76//! }
77//! ```
78//!
79//! ## Helper Macros
80//!
81//! This crate provides several helper macros for working with SWC AST nodes:
82//!
83//! - [`ident!`] - Create an identifier with optional formatting
84//! - [`private_ident!`] - Create a private (marked) identifier
85//! - [`stmt_block!`] - Create a block statement from statements
86//! - [`fn_expr!`] - Create an anonymous function expression
87//! - [`member_expr!`] - Create a member access expression (obj.prop)
88//! - [`assign_stmt!`] - Create an assignment statement
89//! - [`fn_assign!`] - Create a function assignment (obj.prop = function() {...})
90//! - [`proto_method!`] - Create a prototype method assignment
91//!
92//! ## Feature Flags
93//!
94//! - `swc` - Enables SWC integration and the helper macros (enabled by default)
95//!
96//! ## Re-exports
97//!
98//! For convenience, the crate re-exports commonly used SWC types when the `swc` feature
99//! is enabled:
100//!
101//! - [`swc_core`] - The full SWC core crate
102//! - [`swc_common`] - Common SWC types (Span, SourceMap, etc.)
103//! - [`swc_ecma_ast`] - ECMAScript/TypeScript AST types
104//! - `quote!` - SWC's quote macro for AST generation
105
106pub mod abi;
107pub mod derive;
108pub mod errors;
109pub mod lower;
110pub mod parse;
111pub mod quote_helpers;
112pub mod stream;
113
114pub use abi::*;
115pub use derive::*;
116pub use errors::*;
117pub use lower::*;
118pub use stream::*;
119#[cfg(feature = "swc")]
120pub use swc_core::quote;
121
122// Re-export swc_core for convenience
123#[cfg(feature = "swc")]
124pub use swc_core;
125
126// Re-export common swc modules at top level for ergonomics
127#[cfg(feature = "swc")]
128pub use swc_core::common as swc_common;
129#[cfg(feature = "swc")]
130pub use swc_core::ecma::ast as swc_ecma_ast;
131
132// Helper macros for creating AST nodes
133
134// =============================================================================
135// Expression conversion helpers
136// =============================================================================
137
138/// Converts common Rust values into SWC [`Expr`](swc_core::ecma::ast::Expr) nodes.
139///
140/// This enables ergonomic interpolation in template literals and other AST
141/// construction contexts where a string or identifier should be treated as a
142/// TypeScript expression.
143#[cfg(feature = "swc")]
144pub trait ToTsExpr {
145    fn to_ts_expr(self) -> swc_core::ecma::ast::Expr;
146}
147
148#[cfg(feature = "swc")]
149impl ToTsExpr for swc_core::ecma::ast::Expr {
150    fn to_ts_expr(self) -> swc_core::ecma::ast::Expr {
151        self
152    }
153}
154
155#[cfg(feature = "swc")]
156impl ToTsExpr for Box<swc_core::ecma::ast::Expr> {
157    fn to_ts_expr(self) -> swc_core::ecma::ast::Expr {
158        *self
159    }
160}
161
162#[cfg(feature = "swc")]
163impl ToTsExpr for &swc_core::ecma::ast::Expr {
164    fn to_ts_expr(self) -> swc_core::ecma::ast::Expr {
165        self.clone()
166    }
167}
168
169#[cfg(feature = "swc")]
170impl ToTsExpr for swc_core::ecma::ast::Ident {
171    fn to_ts_expr(self) -> swc_core::ecma::ast::Expr {
172        swc_core::ecma::ast::Expr::Ident(self)
173    }
174}
175
176#[cfg(feature = "swc")]
177impl ToTsExpr for &swc_core::ecma::ast::Ident {
178    fn to_ts_expr(self) -> swc_core::ecma::ast::Expr {
179        swc_core::ecma::ast::Expr::Ident(self.clone())
180    }
181}
182
183#[cfg(feature = "swc")]
184impl ToTsExpr for String {
185    fn to_ts_expr(self) -> swc_core::ecma::ast::Expr {
186        swc_core::ecma::ast::Expr::Lit(swc_core::ecma::ast::Lit::Str(swc_core::ecma::ast::Str {
187            span: swc_core::common::DUMMY_SP,
188            value: self.into(),
189            raw: None,
190        }))
191    }
192}
193
194#[cfg(feature = "swc")]
195impl ToTsExpr for &String {
196    fn to_ts_expr(self) -> swc_core::ecma::ast::Expr {
197        self.clone().to_ts_expr()
198    }
199}
200
201#[cfg(feature = "swc")]
202impl ToTsExpr for &str {
203    fn to_ts_expr(self) -> swc_core::ecma::ast::Expr {
204        swc_core::ecma::ast::Expr::Lit(swc_core::ecma::ast::Lit::Str(swc_core::ecma::ast::Str {
205            span: swc_core::common::DUMMY_SP,
206            value: self.into(),
207            raw: None,
208        }))
209    }
210}
211
212#[cfg(feature = "swc")]
213impl ToTsExpr for bool {
214    fn to_ts_expr(self) -> swc_core::ecma::ast::Expr {
215        swc_core::ecma::ast::Expr::Lit(swc_core::ecma::ast::Lit::Bool(swc_core::ecma::ast::Bool {
216            span: swc_core::common::DUMMY_SP,
217            value: self,
218        }))
219    }
220}
221
222#[cfg(feature = "swc")]
223impl ToTsExpr for &bool {
224    fn to_ts_expr(self) -> swc_core::ecma::ast::Expr {
225        (*self).to_ts_expr()
226    }
227}
228
229/// Convert a value into a TypeScript [`Expr`](swc_core::ecma::ast::Expr).
230///
231/// This is a convenience wrapper for [`ToTsExpr`].
232#[cfg(feature = "swc")]
233pub fn to_ts_expr<T: ToTsExpr>(value: T) -> swc_core::ecma::ast::Expr {
234    value.to_ts_expr()
235}
236
237// =============================================================================
238// Type conversion helpers
239// =============================================================================
240
241/// Converts common Rust values into SWC [`TsType`](swc_core::ecma::ast::TsType) nodes.
242///
243/// This enables ergonomic interpolation in template literals where a type
244/// annotation is expected.
245#[cfg(feature = "swc")]
246pub trait ToTsType {
247    fn to_ts_type(self) -> swc_core::ecma::ast::TsType;
248}
249
250#[cfg(feature = "swc")]
251impl ToTsType for swc_core::ecma::ast::TsType {
252    fn to_ts_type(self) -> swc_core::ecma::ast::TsType {
253        self
254    }
255}
256
257#[cfg(feature = "swc")]
258impl ToTsType for Box<swc_core::ecma::ast::TsType> {
259    fn to_ts_type(self) -> swc_core::ecma::ast::TsType {
260        *self
261    }
262}
263
264#[cfg(feature = "swc")]
265impl ToTsType for &swc_core::ecma::ast::TsType {
266    fn to_ts_type(self) -> swc_core::ecma::ast::TsType {
267        self.clone()
268    }
269}
270
271/// Convert a string to a TsTypeRef (type reference by name).
272#[cfg(feature = "swc")]
273impl ToTsType for String {
274    fn to_ts_type(self) -> swc_core::ecma::ast::TsType {
275        swc_core::ecma::ast::TsType::TsTypeRef(swc_core::ecma::ast::TsTypeRef {
276            span: swc_core::common::DUMMY_SP,
277            type_name: swc_core::ecma::ast::TsEntityName::Ident(
278                swc_core::ecma::ast::Ident::new_no_ctxt(self.into(), swc_core::common::DUMMY_SP),
279            ),
280            type_params: None,
281        })
282    }
283}
284
285#[cfg(feature = "swc")]
286impl ToTsType for &String {
287    fn to_ts_type(self) -> swc_core::ecma::ast::TsType {
288        self.clone().to_ts_type()
289    }
290}
291
292#[cfg(feature = "swc")]
293impl ToTsType for &str {
294    fn to_ts_type(self) -> swc_core::ecma::ast::TsType {
295        self.to_string().to_ts_type()
296    }
297}
298
299/// Convert an Ident to a TsTypeRef.
300#[cfg(feature = "swc")]
301impl ToTsType for swc_core::ecma::ast::Ident {
302    fn to_ts_type(self) -> swc_core::ecma::ast::TsType {
303        swc_core::ecma::ast::TsType::TsTypeRef(swc_core::ecma::ast::TsTypeRef {
304            span: swc_core::common::DUMMY_SP,
305            type_name: swc_core::ecma::ast::TsEntityName::Ident(self),
306            type_params: None,
307        })
308    }
309}
310
311#[cfg(feature = "swc")]
312impl ToTsType for &swc_core::ecma::ast::Ident {
313    fn to_ts_type(self) -> swc_core::ecma::ast::TsType {
314        self.clone().to_ts_type()
315    }
316}
317
318/// Convert a bool to a TsLiteralType (true or false literal type).
319#[cfg(feature = "swc")]
320impl ToTsType for bool {
321    fn to_ts_type(self) -> swc_core::ecma::ast::TsType {
322        swc_core::ecma::ast::TsType::TsLitType(swc_core::ecma::ast::TsLitType {
323            span: swc_core::common::DUMMY_SP,
324            lit: swc_core::ecma::ast::TsLit::Bool(swc_core::ecma::ast::Bool {
325                span: swc_core::common::DUMMY_SP,
326                value: self,
327            }),
328        })
329    }
330}
331
332#[cfg(feature = "swc")]
333impl ToTsType for &bool {
334    fn to_ts_type(self) -> swc_core::ecma::ast::TsType {
335        (*self).to_ts_type()
336    }
337}
338
339/// Convert an Expr to a TsType. Only works for identifier expressions.
340#[cfg(feature = "swc")]
341impl ToTsType for swc_core::ecma::ast::Expr {
342    fn to_ts_type(self) -> swc_core::ecma::ast::TsType {
343        match self {
344            swc_core::ecma::ast::Expr::Ident(ident) => ident.to_ts_type(),
345            // For other expressions, create a typeof type
346            other => swc_core::ecma::ast::TsType::TsTypeQuery(swc_core::ecma::ast::TsTypeQuery {
347                span: swc_core::common::DUMMY_SP,
348                expr_name: swc_core::ecma::ast::TsTypeQueryExpr::TsEntityName(
349                    swc_core::ecma::ast::TsEntityName::Ident(
350                        swc_core::ecma::ast::Ident::new_no_ctxt(
351                            format!("{:?}", other).into(),
352                            swc_core::common::DUMMY_SP,
353                        ),
354                    ),
355                ),
356                type_args: None,
357            }),
358        }
359    }
360}
361
362#[cfg(feature = "swc")]
363impl ToTsType for &swc_core::ecma::ast::Expr {
364    fn to_ts_type(self) -> swc_core::ecma::ast::TsType {
365        self.clone().to_ts_type()
366    }
367}
368
369/// Convert a value into a TypeScript [`TsType`](swc_core::ecma::ast::TsType).
370///
371/// This is a convenience wrapper for [`ToTsType`].
372#[cfg(feature = "swc")]
373pub fn to_ts_type<T: ToTsType>(value: T) -> swc_core::ecma::ast::TsType {
374    value.to_ts_type()
375}
376
377// =============================================================================
378// Identifier conversion helpers
379// =============================================================================
380
381/// Converts common Rust values into SWC [`Ident`](swc_core::ecma::ast::Ident) nodes.
382///
383/// This enables ergonomic interpolation in template literals where an identifier
384/// is expected.
385#[cfg(feature = "swc")]
386pub trait ToTsIdent {
387    fn to_ts_ident(self) -> swc_core::ecma::ast::Ident;
388}
389
390#[cfg(feature = "swc")]
391impl ToTsIdent for swc_core::ecma::ast::Ident {
392    fn to_ts_ident(self) -> swc_core::ecma::ast::Ident {
393        self
394    }
395}
396
397#[cfg(feature = "swc")]
398impl ToTsIdent for &swc_core::ecma::ast::Ident {
399    fn to_ts_ident(self) -> swc_core::ecma::ast::Ident {
400        self.clone()
401    }
402}
403
404#[cfg(feature = "swc")]
405impl ToTsIdent for String {
406    fn to_ts_ident(self) -> swc_core::ecma::ast::Ident {
407        swc_core::ecma::ast::Ident::new_no_ctxt(self.into(), swc_core::common::DUMMY_SP)
408    }
409}
410
411#[cfg(feature = "swc")]
412impl ToTsIdent for &String {
413    fn to_ts_ident(self) -> swc_core::ecma::ast::Ident {
414        self.clone().to_ts_ident()
415    }
416}
417
418#[cfg(feature = "swc")]
419impl ToTsIdent for &str {
420    fn to_ts_ident(self) -> swc_core::ecma::ast::Ident {
421        swc_core::ecma::ast::Ident::new_no_ctxt(self.into(), swc_core::common::DUMMY_SP)
422    }
423}
424
425/// Convert an Expr to an Ident. Only works for identifier expressions.
426#[cfg(feature = "swc")]
427impl ToTsIdent for swc_core::ecma::ast::Expr {
428    fn to_ts_ident(self) -> swc_core::ecma::ast::Ident {
429        match self {
430            swc_core::ecma::ast::Expr::Ident(ident) => ident,
431            // For non-ident expressions, create a placeholder identifier
432            _ => swc_core::ecma::ast::Ident::new_no_ctxt(
433                "__expr__".into(),
434                swc_core::common::DUMMY_SP,
435            ),
436        }
437    }
438}
439
440#[cfg(feature = "swc")]
441impl ToTsIdent for &swc_core::ecma::ast::Expr {
442    fn to_ts_ident(self) -> swc_core::ecma::ast::Ident {
443        self.clone().to_ts_ident()
444    }
445}
446
447/// Convert a TsType to an Ident. Only works for type references with simple names.
448#[cfg(feature = "swc")]
449impl ToTsIdent for swc_core::ecma::ast::TsType {
450    fn to_ts_ident(self) -> swc_core::ecma::ast::Ident {
451        match self {
452            swc_core::ecma::ast::TsType::TsTypeRef(swc_core::ecma::ast::TsTypeRef {
453                type_name: swc_core::ecma::ast::TsEntityName::Ident(ident),
454                ..
455            }) => ident,
456            // For other types, create a placeholder
457            _ => swc_core::ecma::ast::Ident::new_no_ctxt(
458                "__type__".into(),
459                swc_core::common::DUMMY_SP,
460            ),
461        }
462    }
463}
464
465#[cfg(feature = "swc")]
466impl ToTsIdent for &swc_core::ecma::ast::TsType {
467    fn to_ts_ident(self) -> swc_core::ecma::ast::Ident {
468        self.clone().to_ts_ident()
469    }
470}
471
472#[cfg(feature = "swc")]
473impl ToTsIdent for Box<swc_core::ecma::ast::TsType> {
474    fn to_ts_ident(self) -> swc_core::ecma::ast::Ident {
475        (*self).to_ts_ident()
476    }
477}
478
479/// Convert a value into a TypeScript [`Ident`](swc_core::ecma::ast::Ident).
480///
481/// This is a convenience wrapper for [`ToTsIdent`].
482#[cfg(feature = "swc")]
483pub fn to_ts_ident<T: ToTsIdent>(value: T) -> swc_core::ecma::ast::Ident {
484    value.to_ts_ident()
485}
486
487// =============================================================================
488// Statement conversion helpers
489// =============================================================================
490
491/// Converts common Rust values into SWC [`Stmt`](swc_core::ecma::ast::Stmt) nodes.
492///
493/// This enables ergonomic interpolation in template literals where a statement
494/// is expected.
495#[cfg(feature = "swc")]
496pub trait ToTsStmt {
497    fn to_ts_stmt(self) -> swc_core::ecma::ast::Stmt;
498}
499
500#[cfg(feature = "swc")]
501impl ToTsStmt for swc_core::ecma::ast::Stmt {
502    fn to_ts_stmt(self) -> swc_core::ecma::ast::Stmt {
503        self
504    }
505}
506
507#[cfg(feature = "swc")]
508impl ToTsStmt for Box<swc_core::ecma::ast::Stmt> {
509    fn to_ts_stmt(self) -> swc_core::ecma::ast::Stmt {
510        *self
511    }
512}
513
514#[cfg(feature = "swc")]
515impl ToTsStmt for &swc_core::ecma::ast::Stmt {
516    fn to_ts_stmt(self) -> swc_core::ecma::ast::Stmt {
517        self.clone()
518    }
519}
520
521/// Convert an expression to an expression statement.
522#[cfg(feature = "swc")]
523impl ToTsStmt for swc_core::ecma::ast::Expr {
524    fn to_ts_stmt(self) -> swc_core::ecma::ast::Stmt {
525        swc_core::ecma::ast::Stmt::Expr(swc_core::ecma::ast::ExprStmt {
526            span: swc_core::common::DUMMY_SP,
527            expr: Box::new(self),
528        })
529    }
530}
531
532#[cfg(feature = "swc")]
533impl ToTsStmt for Box<swc_core::ecma::ast::Expr> {
534    fn to_ts_stmt(self) -> swc_core::ecma::ast::Stmt {
535        swc_core::ecma::ast::Stmt::Expr(swc_core::ecma::ast::ExprStmt {
536            span: swc_core::common::DUMMY_SP,
537            expr: self,
538        })
539    }
540}
541
542#[cfg(feature = "swc")]
543impl ToTsStmt for String {
544    fn to_ts_stmt(self) -> swc_core::ecma::ast::Stmt {
545        use swc_core::ecma::ast::{BlockStmt, EmptyStmt, ModuleItem, Stmt};
546
547        let mut stmts: Vec<Stmt> = parse_ts_to_module_items(&self)
548            .into_iter()
549            .filter_map(|item| match item {
550                ModuleItem::Stmt(stmt) => Some(stmt),
551                _ => None,
552            })
553            .collect();
554
555        match stmts.len() {
556            0 => Stmt::Empty(EmptyStmt {
557                span: swc_core::common::DUMMY_SP,
558            }),
559            1 => stmts.pop().expect("single statement missing"),
560            _ => Stmt::Block(BlockStmt {
561                span: swc_core::common::DUMMY_SP,
562                ctxt: swc_core::common::SyntaxContext::empty(),
563                stmts,
564            }),
565        }
566    }
567}
568
569#[cfg(feature = "swc")]
570impl ToTsStmt for &String {
571    fn to_ts_stmt(self) -> swc_core::ecma::ast::Stmt {
572        self.as_str().to_ts_stmt()
573    }
574}
575
576#[cfg(feature = "swc")]
577impl ToTsStmt for &str {
578    fn to_ts_stmt(self) -> swc_core::ecma::ast::Stmt {
579        self.to_string().to_ts_stmt()
580    }
581}
582
583/// Convert a value into a TypeScript [`Stmt`](swc_core::ecma::ast::Stmt).
584///
585/// This is a convenience wrapper for [`ToTsStmt`].
586#[cfg(feature = "swc")]
587pub fn to_ts_stmt<T: ToTsStmt>(value: T) -> swc_core::ecma::ast::Stmt {
588    value.to_ts_stmt()
589}
590
591/// Trait for converting values to type name strings.
592///
593/// This is used for type placeholder substitution in templates.
594/// The key difference from `ToString` is that for `Ident`, this uses
595/// the symbol directly (`.sym`) rather than the Display impl, which
596/// avoids including SyntaxContext markers like `#0`.
597#[cfg(feature = "swc")]
598pub trait ToTsTypeName {
599    fn to_ts_type_name(&self) -> String;
600}
601
602#[cfg(feature = "swc")]
603impl ToTsTypeName for swc_core::ecma::ast::Ident {
604    fn to_ts_type_name(&self) -> String {
605        self.sym.to_string()
606    }
607}
608
609#[cfg(feature = "swc")]
610impl ToTsTypeName for &swc_core::ecma::ast::Ident {
611    fn to_ts_type_name(&self) -> String {
612        self.sym.to_string()
613    }
614}
615
616#[cfg(feature = "swc")]
617impl ToTsTypeName for String {
618    fn to_ts_type_name(&self) -> String {
619        self.clone()
620    }
621}
622
623#[cfg(feature = "swc")]
624impl ToTsTypeName for &String {
625    fn to_ts_type_name(&self) -> String {
626        (*self).clone()
627    }
628}
629
630#[cfg(feature = "swc")]
631impl ToTsTypeName for &str {
632    fn to_ts_type_name(&self) -> String {
633        (*self).to_string()
634    }
635}
636
637/// Creates an SWC [`Ident`](swc_core::ecma::ast::Ident) with a dummy span.
638///
639/// This macro provides a convenient way to create identifier AST nodes for
640/// code generation. It supports both simple string names and format strings.
641///
642/// # Examples
643///
644/// Simple identifier:
645///
646/// ```rust,no_run
647/// use macroforge_ts_syn::ts_ident;
648///
649/// let id = ts_ident!("myVariable");
650/// assert_eq!(id.sym.as_str(), "myVariable");
651/// ```
652///
653/// Formatted identifier:
654///
655/// ```rust,no_run
656/// use macroforge_ts_syn::ts_ident;
657///
658/// let field_name = "age";
659/// let getter = ts_ident!("get{}", field_name.to_uppercase());
660/// assert_eq!(getter.sym.as_str(), "getAGE");
661/// ```
662///
663/// Using in code generation:
664///
665/// ```rust,no_run
666/// use macroforge_ts_syn::{ts_ident, quote};
667///
668/// let class_name = ts_ident!("MyClass");
669/// let code = quote!("class $class_name {}" as Stmt, class_name: Ident = class_name);
670/// ```
671#[cfg(feature = "swc")]
672#[macro_export]
673macro_rules! ts_ident {
674    // Single argument - direct string
675    // Use AsRef<str> to handle String, &String, &str, etc.
676    ($name:expr) => {
677        $crate::swc_core::ecma::ast::Ident::new_no_ctxt(
678            AsRef::<str>::as_ref(&$name).into(),
679            $crate::swc_core::common::DUMMY_SP,
680        )
681    };
682    // Format string with arguments
683    ($fmt:expr, $($args:expr),+ $(,)?) => {
684        $crate::swc_core::ecma::ast::Ident::new_no_ctxt(format!($fmt, $($args),+).into(), $crate::swc_core::common::DUMMY_SP)
685    };
686}
687
688/// Creates a private (marked) SWC [`Ident`](swc_core::ecma::ast::Ident).
689///
690/// Unlike [`ts_ident!`], this macro creates an identifier with a fresh hygiene mark,
691/// making it unique and preventing name collisions with user code. Use this when
692/// generating temporary variables or internal identifiers that shouldn't conflict
693/// with existing names in scope.
694///
695/// # Examples
696///
697/// Creating a unique temporary variable:
698///
699/// ```rust,no_run
700/// use macroforge_ts_syn::ts_private_ident;
701///
702/// // Each call creates a unique identifier that won't clash
703/// let temp1 = ts_private_ident!("temp");
704/// let temp2 = ts_private_ident!("temp");
705/// // temp1 and temp2 have different syntax contexts
706/// ```
707///
708/// Generating internal helper code:
709///
710/// ```rust,no_run
711/// use macroforge_ts_syn::{ts_private_ident, quote};
712///
713/// let internal_var = ts_private_ident!("__internal");
714/// // This won't conflict with any user-defined __internal variable
715/// let code = quote!("let $var = {};" as Stmt, var: Ident = internal_var);
716/// ```
717#[cfg(feature = "swc")]
718#[macro_export]
719macro_rules! ts_private_ident {
720    ($name:expr) => {{
721        let mark = $crate::swc_core::common::Mark::fresh($crate::swc_core::common::Mark::root());
722        $crate::swc_core::ecma::ast::Ident::new(
723            $name.into(),
724            $crate::swc_core::common::DUMMY_SP,
725            $crate::swc_core::common::SyntaxContext::empty().apply_mark(mark),
726        )
727    }};
728}
729
730/// Creates a block statement (`{ ... }`) from a vector of statements.
731///
732/// This macro wraps a `Vec<Stmt>` into a [`BlockStmt`](swc_core::ecma::ast::BlockStmt),
733/// which represents a `{ ... }` block in JavaScript/TypeScript. Useful when you need
734/// to group multiple statements into a single statement.
735///
736/// # Examples
737///
738/// Wrapping multiple statements in a block:
739///
740/// ```rust,ignore
741/// use macroforge_ts_syn::{stmt_block, parse_ts_str};
742/// use swc_core::ecma::ast::Stmt;
743///
744/// let stmt1: Stmt = parse_ts_str("console.log('hello');").unwrap();
745/// let stmt2: Stmt = parse_ts_str("console.log('world');").unwrap();
746///
747/// let block = stmt_block!(vec![stmt1, stmt2]);
748/// // Generates: { console.log('hello'); console.log('world'); }
749/// ```
750///
751/// Using with generated code:
752///
753/// ```rust
754/// use macroforge_ts_syn::stmt_block;
755/// use macroforge_ts_syn::swc_ecma_ast::Stmt;
756///
757/// fn wrap_in_block(statements: Vec<Stmt>) -> Stmt {
758///     stmt_block!(statements)
759/// }
760/// ```
761#[cfg(feature = "swc")]
762#[macro_export]
763macro_rules! stmt_block {
764    ($stmts:expr) => {
765        $crate::swc_core::ecma::ast::Stmt::Block($crate::swc_core::ecma::ast::BlockStmt {
766            span: $crate::swc_core::common::DUMMY_SP,
767            ctxt: $crate::swc_core::common::SyntaxContext::empty(),
768            stmts: $stmts,
769        })
770    };
771}
772
773/// A wrapper type for passing a `Vec<Stmt>` to be used inline in function bodies.
774///
775/// This is a marker type that `ts_quote!` can detect and handle specially,
776/// allowing you to interpolate multiple statements where the macro expects them.
777///
778/// # Examples
779///
780/// ```rust
781/// use macroforge_ts_syn::{StmtVec, stmt_vec};
782/// use macroforge_ts_syn::swc_ecma_ast::Stmt;
783///
784/// // Create directly with constructor
785/// let statements1: Vec<Stmt> = vec![];
786/// let _wrapped1 = StmtVec(statements1);
787///
788/// // Or using the macro
789/// let statements2: Vec<Stmt> = vec![];
790/// let _wrapped2 = stmt_vec!(statements2);
791/// ```
792#[cfg(feature = "swc")]
793pub struct StmtVec(pub Vec<swc_core::ecma::ast::Stmt>);
794
795/// Wraps a `Vec<Stmt>` in a [`StmtVec`] for use with `ts_quote!`.
796///
797/// This macro is a convenience wrapper around creating a `StmtVec` directly.
798/// Use it when you need to pass multiple statements to a quote macro that
799/// expects a statement vector.
800///
801/// # Examples
802///
803/// ```rust,no_run
804/// use macroforge_ts_syn::stmt_vec;
805/// use swc_core::ecma::ast::Stmt;
806///
807/// fn generate_method_body() -> Vec<Stmt> {
808///     // ... generate statements
809///     vec![]
810/// }
811///
812/// let body = stmt_vec!(generate_method_body());
813/// ```
814#[cfg(feature = "swc")]
815#[macro_export]
816macro_rules! stmt_vec {
817    ($stmts:expr) => {
818        macroforge_ts_syn::StmtVec($stmts)
819    };
820}
821
822/// Converts a `Vec<Stmt>` into a single block statement for use in `ts_quote!`.
823///
824/// This macro is useful when you need to interpolate multiple statements in a
825/// context where only a single statement is expected. The statements are wrapped
826/// in a block `{ ... }`.
827///
828/// This is an alias for [`stmt_block!`] and behaves identically.
829///
830/// # Examples
831///
832/// ```rust,no_run
833/// use macroforge_ts_syn::{stmt_block_from_vec, quote};
834/// use swc_core::ecma::ast::Stmt;
835///
836/// fn generate_body() -> Vec<Stmt> {
837///     // Generate multiple statements
838///     vec![]
839/// }
840///
841/// let body_block = stmt_block_from_vec!(generate_body());
842/// // Can now use body_block where a single Stmt is expected
843/// ```
844#[cfg(feature = "swc")]
845#[macro_export]
846macro_rules! stmt_block_from_vec {
847    ($stmts:expr) => {
848        $crate::swc_core::ecma::ast::Stmt::Block($crate::swc_core::ecma::ast::BlockStmt {
849            span: $crate::swc_core::common::DUMMY_SP,
850            ctxt: $crate::swc_core::common::SyntaxContext::empty(),
851            stmts: $stmts,
852        })
853    };
854}
855
856/// Creates an anonymous function expression with the given body statements.
857///
858/// This macro generates a [`FnExpr`](swc_core::ecma::ast::FnExpr) representing
859/// `function() { ... }` or `function(params) { ... }` in JavaScript/TypeScript.
860///
861/// # Variants
862///
863/// - `fn_expr!(body_stmts)` - Creates a function with no parameters
864/// - `fn_expr!(params, body_stmts)` - Creates a function with the specified parameters
865///
866/// # Examples
867///
868/// Function with no parameters:
869///
870/// ```rust,ignore
871/// use macroforge_ts_syn::{fn_expr, parse_ts_str};
872/// use swc_core::ecma::ast::Stmt;
873///
874/// let body: Vec<Stmt> = vec![
875///     parse_ts_str("return 42;").unwrap()
876/// ];
877///
878/// let func = fn_expr!(body);
879/// // Generates: function() { return 42; }
880/// ```
881///
882/// Function with parameters:
883///
884/// ```rust,ignore
885/// use macroforge_ts_syn::{fn_expr, ts_ident};
886/// use swc_core::ecma::ast::{Param, Pat, Stmt};
887/// use swc_core::common::DUMMY_SP;
888///
889/// let params = vec![
890///     Param {
891///         span: DUMMY_SP,
892///         decorators: vec![],
893///         pat: Pat::Ident(ts_ident!("x").into()),
894///     }
895/// ];
896/// let body: Vec<Stmt> = vec![];
897///
898/// let func = fn_expr!(params, body);
899/// // Generates: function(x) { ... }
900/// ```
901#[cfg(feature = "swc")]
902#[macro_export]
903macro_rules! fn_expr {
904    ($body_stmts:expr) => {
905        $crate::swc_core::ecma::ast::Expr::Fn($crate::swc_core::ecma::ast::FnExpr {
906            ident: None,
907            function: Box::new($crate::swc_core::ecma::ast::Function {
908                params: vec![],
909                decorators: vec![],
910                span: $crate::swc_core::common::DUMMY_SP,
911                ctxt: $crate::swc_core::common::SyntaxContext::empty(),
912                body: Some($crate::swc_core::ecma::ast::BlockStmt {
913                    span: $crate::swc_core::common::DUMMY_SP,
914                    ctxt: $crate::swc_core::common::SyntaxContext::empty(),
915                    stmts: $body_stmts,
916                }),
917                is_generator: false,
918                is_async: false,
919                type_params: None,
920                return_type: None,
921            }),
922        })
923    };
924    ($params:expr, $body_stmts:expr) => {
925        $crate::swc_core::ecma::ast::Expr::Fn($crate::swc_core::ecma::ast::FnExpr {
926            ident: None,
927            function: Box::new($crate::swc_core::ecma::ast::Function {
928                params: $params,
929                decorators: vec![],
930                span: $crate::swc_core::common::DUMMY_SP,
931                ctxt: $crate::swc_core::common::SyntaxContext::empty(),
932                body: Some($crate::swc_core::ecma::ast::BlockStmt {
933                    span: $crate::swc_core::common::DUMMY_SP,
934                    ctxt: $crate::swc_core::common::SyntaxContext::empty(),
935                    stmts: $body_stmts,
936                }),
937                is_generator: false,
938                is_async: false,
939                type_params: None,
940                return_type: None,
941            }),
942        })
943    };
944}
945
946/// Creates a member access expression (`obj.prop`).
947///
948/// This macro generates a [`MemberExpr`](swc_core::ecma::ast::MemberExpr) representing
949/// property access in JavaScript/TypeScript, such as `obj.property` or `this.field`.
950///
951/// # Arguments
952///
953/// - `$obj` - The object expression to access the property on
954/// - `$prop` - The property name as a string or something convertible to `JsWord`
955///
956/// # Examples
957///
958/// Accessing a property:
959///
960/// ```rust,ignore
961/// use macroforge_ts_syn::{member_expr, ts_ident};
962/// use swc_core::ecma::ast::{Expr, ThisExpr};
963/// use swc_core::common::DUMMY_SP;
964///
965/// let this_expr = Expr::This(ThisExpr { span: DUMMY_SP });
966/// let access = member_expr!(this_expr, "name");
967/// // Generates: this.name
968/// ```
969///
970/// Chaining member access:
971///
972/// ```rust
973/// use macroforge_ts_syn::{member_expr, ts_ident};
974/// use macroforge_ts_syn::swc_ecma_ast::Expr;
975///
976/// let obj = Expr::Ident(ts_ident!("obj"));
977/// let _nested = member_expr!(member_expr!(obj, "foo"), "bar");
978/// // Generates: obj.foo.bar
979/// ```
980///
981/// Accessing prototype:
982///
983/// ```rust
984/// use macroforge_ts_syn::{member_expr, ts_ident};
985/// use macroforge_ts_syn::swc_ecma_ast::Expr;
986///
987/// let class = Expr::Ident(ts_ident!("MyClass"));
988/// let _proto = member_expr!(class, "prototype");
989/// // Generates: MyClass.prototype
990/// ```
991#[cfg(feature = "swc")]
992#[macro_export]
993macro_rules! member_expr {
994    ($obj:expr, $prop:expr) => {
995        $crate::swc_core::ecma::ast::Expr::Member($crate::swc_core::ecma::ast::MemberExpr {
996            span: $crate::swc_core::common::DUMMY_SP,
997            obj: Box::new($obj),
998            prop: $crate::swc_core::ecma::ast::MemberProp::Ident(
999                $crate::swc_core::ecma::ast::IdentName {
1000                    span: $crate::swc_core::common::DUMMY_SP,
1001                    sym: $prop.into(),
1002                },
1003            ),
1004        })
1005    };
1006}
1007
1008/// Creates an assignment expression statement (`lhs = rhs;`).
1009///
1010/// This macro generates a statement that assigns a value to a target. The left-hand
1011/// side must be an [`AssignTarget`](swc_core::ecma::ast::AssignTarget) (typically a
1012/// variable, property access, or destructuring pattern), and the right-hand side
1013/// is any expression.
1014///
1015/// # Arguments
1016///
1017/// - `$lhs` - The assignment target (left-hand side)
1018/// - `$rhs` - The expression to assign (right-hand side)
1019///
1020/// # Examples
1021///
1022/// Simple variable assignment:
1023///
1024/// ```rust,ignore
1025/// use macroforge_ts_syn::{assign_stmt, ts_ident};
1026/// use swc_core::ecma::ast::{AssignTarget, SimpleAssignTarget, Expr, Lit, Number};
1027/// use swc_core::common::DUMMY_SP;
1028///
1029/// let target = AssignTarget::Simple(SimpleAssignTarget::Ident(ts_ident!("x").into()));
1030/// let value = Expr::Lit(Lit::Num(Number { span: DUMMY_SP, value: 42.0, raw: None }));
1031///
1032/// let stmt = assign_stmt!(target, value);
1033/// // Generates: x = 42;
1034/// ```
1035///
1036/// Property assignment:
1037///
1038/// ```rust,ignore
1039/// use macroforge_ts_syn::{assign_stmt, member_expr, ts_ident};
1040/// use swc_core::ecma::ast::{AssignTarget, SimpleAssignTarget, Expr, Lit, ThisExpr};
1041/// use swc_core::common::DUMMY_SP;
1042///
1043/// let this_name = member_expr!(Expr::This(ThisExpr { span: DUMMY_SP }), "name");
1044/// let target = /* convert to AssignTarget */;
1045/// let value = Expr::Lit(Lit::Str("John".into()));
1046///
1047/// let stmt = assign_stmt!(target, value);
1048/// // Generates: this.name = "John";
1049/// ```
1050#[cfg(feature = "swc")]
1051#[macro_export]
1052macro_rules! assign_stmt {
1053    ($lhs:expr, $rhs:expr) => {
1054        $crate::swc_core::ecma::ast::Stmt::Expr($crate::swc_core::ecma::ast::ExprStmt {
1055            span: $crate::swc_core::common::DUMMY_SP,
1056            expr: Box::new($crate::swc_core::ecma::ast::Expr::Assign(
1057                $crate::swc_core::ecma::ast::AssignExpr {
1058                    span: $crate::swc_core::common::DUMMY_SP,
1059                    op: $crate::swc_core::ecma::ast::AssignOp::Assign,
1060                    left: $lhs,
1061                    right: Box::new($rhs),
1062                },
1063            )),
1064        })
1065    };
1066}
1067
1068/// Creates a function assignment statement (`obj.prop = function(params) { body };`).
1069///
1070/// This macro is particularly useful for adding methods to prototypes or assigning
1071/// function implementations to object properties. It generates the complete assignment
1072/// statement including the anonymous function.
1073///
1074/// # Variants
1075///
1076/// - `fn_assign!(obj, prop, body_stmts)` - Function with no parameters
1077/// - `fn_assign!(obj, prop, params, body_stmts)` - Function with specified parameters
1078///
1079/// # Arguments
1080///
1081/// - `$obj` - The object expression to assign to
1082/// - `$prop` - The property name (as a string)
1083/// - `$params` - Optional: vector of function parameters
1084/// - `$body_stmts` - Vector of statements for the function body
1085///
1086/// # Examples
1087///
1088/// Adding a method to a prototype (no params):
1089///
1090/// ```rust
1091/// use macroforge_ts_syn::{fn_assign, ts_ident, member_expr};
1092/// use macroforge_ts_syn::swc_ecma_ast::{Expr, Stmt};
1093///
1094/// let class_expr = Expr::Ident(ts_ident!("MyClass"));
1095/// let proto = member_expr!(class_expr, "prototype");
1096///
1097/// let body: Vec<Stmt> = vec![];
1098/// let _stmt = fn_assign!(proto, "toString", body);
1099/// // Generates: MyClass.prototype.toString = function() { ... };
1100/// ```
1101///
1102/// Adding a method with parameters:
1103///
1104/// ```rust,ignore
1105/// use macroforge_ts_syn::{fn_assign, ts_ident, member_expr};
1106/// use swc_core::ecma::ast::{Expr, Param, Pat, Stmt};
1107/// use swc_core::common::DUMMY_SP;
1108///
1109/// let class_expr = Expr::Ident(ts_ident!("MyClass"));
1110/// let proto = member_expr!(class_expr, "prototype");
1111///
1112/// let params = vec![
1113///     Param { span: DUMMY_SP, decorators: vec![], pat: Pat::Ident(ts_ident!("value").into()) }
1114/// ];
1115/// let body: Vec<Stmt> = vec![];
1116///
1117/// let stmt = fn_assign!(proto, "setValue", params, body);
1118/// // Generates: MyClass.prototype.setValue = function(value) { ... };
1119/// ```
1120///
1121/// # See Also
1122///
1123/// - [`proto_method!`] - A higher-level macro for prototype methods
1124/// - [`fn_expr!`] - For creating standalone function expressions
1125#[cfg(feature = "swc")]
1126#[macro_export]
1127macro_rules! fn_assign {
1128    ($obj:expr, $prop:expr, $body_stmts:expr) => {{
1129        use $crate::swc_core::common::{DUMMY_SP, SyntaxContext};
1130        use $crate::swc_core::ecma::ast::*;
1131
1132        Stmt::Expr(ExprStmt {
1133            span: DUMMY_SP,
1134            expr: Box::new(Expr::Assign(AssignExpr {
1135                span: DUMMY_SP,
1136                op: AssignOp::Assign,
1137                left: AssignTarget::Simple(SimpleAssignTarget::Member(MemberExpr {
1138                    span: DUMMY_SP,
1139                    obj: Box::new($obj),
1140                    prop: MemberProp::Ident(IdentName {
1141                        span: DUMMY_SP,
1142                        sym: $prop.into(),
1143                    }),
1144                })),
1145                right: Box::new(Expr::Fn(FnExpr {
1146                    ident: None,
1147                    function: Box::new(Function {
1148                        params: vec![],
1149                        decorators: vec![],
1150                        span: DUMMY_SP,
1151                        ctxt: SyntaxContext::empty(),
1152                        body: Some(BlockStmt {
1153                            span: DUMMY_SP,
1154                            ctxt: SyntaxContext::empty(),
1155                            stmts: $body_stmts,
1156                        }),
1157                        is_generator: false,
1158                        is_async: false,
1159                        type_params: None,
1160                        return_type: None,
1161                    }),
1162                })),
1163            })),
1164        })
1165    }};
1166    ($obj:expr, $prop:expr, $params:expr, $body_stmts:expr) => {{
1167        use $crate::swc_core::common::{DUMMY_SP, SyntaxContext};
1168        use $crate::swc_core::ecma::ast::*;
1169
1170        Stmt::Expr(ExprStmt {
1171            span: DUMMY_SP,
1172            expr: Box::new(Expr::Assign(AssignExpr {
1173                span: DUMMY_SP,
1174                op: AssignOp::Assign,
1175                left: AssignTarget::Simple(SimpleAssignTarget::Member(MemberExpr {
1176                    span: DUMMY_SP,
1177                    obj: Box::new($obj),
1178                    prop: MemberProp::Ident(IdentName {
1179                        span: DUMMY_SP,
1180                        sym: $prop.into(),
1181                    }),
1182                })),
1183                right: Box::new(Expr::Fn(FnExpr {
1184                    ident: None,
1185                    function: Box::new(Function {
1186                        params: $params,
1187                        decorators: vec![],
1188                        span: DUMMY_SP,
1189                        ctxt: SyntaxContext::empty(),
1190                        body: Some(BlockStmt {
1191                            span: DUMMY_SP,
1192                            ctxt: SyntaxContext::empty(),
1193                            stmts: $body_stmts,
1194                        }),
1195                        is_generator: false,
1196                        is_async: false,
1197                        type_params: None,
1198                        return_type: None,
1199                    }),
1200                })),
1201            })),
1202        })
1203    }};
1204}
1205
1206// =============================================================================
1207// AST to string conversion helpers (for template codegen)
1208// =============================================================================
1209
1210/// Converts an expression to its TypeScript string representation.
1211///
1212/// This is used by the template compiler to convert AST nodes back to strings
1213/// for runtime parsing.
1214#[cfg(feature = "swc")]
1215pub fn expr_to_string(expr: &swc_core::ecma::ast::Expr) -> String {
1216    use swc_core::common::sync::Lrc;
1217    use swc_core::ecma::ast::{ExprStmt, Module, ModuleItem, Stmt};
1218    use swc_core::ecma::codegen::{Config, Emitter, text_writer::JsWriter};
1219
1220    // Wrap expression in a module as expression statement
1221    let module = Module {
1222        span: swc_core::common::DUMMY_SP,
1223        body: vec![ModuleItem::Stmt(Stmt::Expr(ExprStmt {
1224            span: swc_core::common::DUMMY_SP,
1225            expr: Box::new(expr.clone()),
1226        }))],
1227        shebang: None,
1228    };
1229
1230    let cm = Lrc::new(swc_core::common::SourceMap::default());
1231    let mut buf = Vec::new();
1232    {
1233        let mut emitter = Emitter {
1234            cfg: Config::default(),
1235            cm: cm.clone(),
1236            comments: None,
1237            wr: JsWriter::new(cm.clone(), "\n", &mut buf, None),
1238        };
1239        let _ = emitter.emit_module(&module);
1240    }
1241    // The output will be "expr;\n" - trim the semicolon and newline
1242    let s = String::from_utf8(buf).unwrap_or_default();
1243    s.trim_end().trim_end_matches(';').to_string()
1244}
1245
1246/// Converts a type to its TypeScript string representation.
1247#[cfg(feature = "swc")]
1248pub fn type_to_string(ty: &swc_core::ecma::ast::TsType) -> String {
1249    use swc_core::common::sync::Lrc;
1250    use swc_core::ecma::ast::*;
1251    use swc_core::ecma::codegen::{Config, Emitter, text_writer::JsWriter};
1252
1253    // Wrap type in a type alias declaration: type __T = <type>;
1254    let module = Module {
1255        span: swc_core::common::DUMMY_SP,
1256        body: vec![ModuleItem::Stmt(Stmt::Decl(Decl::TsTypeAlias(Box::new(
1257            TsTypeAliasDecl {
1258                span: swc_core::common::DUMMY_SP,
1259                declare: false,
1260                id: Ident::new_no_ctxt("__T".into(), swc_core::common::DUMMY_SP),
1261                type_params: None,
1262                type_ann: Box::new(ty.clone()),
1263            },
1264        ))))],
1265        shebang: None,
1266    };
1267
1268    let cm = Lrc::new(swc_core::common::SourceMap::default());
1269    let mut buf = Vec::new();
1270    {
1271        let mut emitter = Emitter {
1272            cfg: Config::default(),
1273            cm: cm.clone(),
1274            comments: None,
1275            wr: JsWriter::new(cm.clone(), "\n", &mut buf, None),
1276        };
1277        let _ = emitter.emit_module(&module);
1278    }
1279    // Output: "type __T = <type>;\n" - extract the type part
1280    let s = String::from_utf8(buf).unwrap_or_default();
1281    // Extract between "= " and ";"
1282    if let Some(eq_pos) = s.find("= ") {
1283        let after_eq = &s[eq_pos + 2..];
1284        if let Some(semi_pos) = after_eq.rfind(';') {
1285            return after_eq[..semi_pos].to_string();
1286        }
1287    }
1288    s.trim().to_string()
1289}
1290
1291/// Converts an identifier to its TypeScript string representation.
1292#[cfg(feature = "swc")]
1293pub fn ident_to_string(ident: &swc_core::ecma::ast::Ident) -> String {
1294    ident.sym.to_string()
1295}
1296
1297/// Emits a list of module items to a TypeScript source string.
1298///
1299/// This function takes AST nodes and optional comments, and produces
1300/// properly formatted TypeScript source code.
1301///
1302/// # Example
1303///
1304/// ```ignore
1305/// use macroforge_ts_syn::emit_module_items;
1306/// use swc_core::ecma::ast::ModuleItem;
1307/// use swc_core::common::comments::SingleThreadedComments;
1308///
1309/// let items: Vec<ModuleItem> = vec![/* ... */];
1310/// let comments = SingleThreadedComments::default();
1311/// let source = emit_module_items(&items, &comments);
1312/// ```
1313#[cfg(feature = "swc")]
1314pub fn emit_module_items(
1315    items: &[swc_core::ecma::ast::ModuleItem],
1316    comments: &swc_core::common::comments::SingleThreadedComments,
1317) -> String {
1318    use swc_core::common::sync::Lrc;
1319    use swc_core::ecma::ast::Module;
1320    use swc_core::ecma::codegen::{Config, Emitter, text_writer::JsWriter};
1321
1322    let module = Module {
1323        span: swc_core::common::DUMMY_SP,
1324        body: items.to_vec(),
1325        shebang: None,
1326    };
1327
1328    let cm = Lrc::new(swc_core::common::SourceMap::default());
1329    let mut buf = Vec::new();
1330    {
1331        let mut emitter = Emitter {
1332            cfg: Config::default().with_minify(false),
1333            cm: cm.clone(),
1334            comments: Some(comments),
1335            wr: JsWriter::new(cm.clone(), "\n", &mut buf, None),
1336        };
1337        let _ = emitter.emit_module(&module);
1338    }
1339    String::from_utf8(buf).unwrap_or_default()
1340}
1341
1342/// Emits an expression to a string representation.
1343///
1344/// Used by `ts_template!` when injecting expressions into raw source strings.
1345#[cfg(feature = "swc")]
1346pub fn emit_expr(expr: &swc_core::ecma::ast::Expr) -> String {
1347    use swc_core::common::comments::SingleThreadedComments;
1348    use swc_core::common::sync::Lrc;
1349    use swc_core::ecma::ast::{ExprStmt, Module, ModuleItem, Stmt};
1350    use swc_core::ecma::codegen::{Config, Emitter, text_writer::JsWriter};
1351
1352    // Wrap expression in a minimal module for emission
1353    let module = Module {
1354        span: swc_core::common::DUMMY_SP,
1355        body: vec![ModuleItem::Stmt(Stmt::Expr(ExprStmt {
1356            span: swc_core::common::DUMMY_SP,
1357            expr: Box::new(expr.clone()),
1358        }))],
1359        shebang: None,
1360    };
1361
1362    let cm = Lrc::new(swc_core::common::SourceMap::default());
1363    let comments = SingleThreadedComments::default();
1364    let mut buf = Vec::new();
1365    {
1366        let mut emitter = Emitter {
1367            cfg: Config::default().with_minify(false),
1368            cm: cm.clone(),
1369            comments: Some(&comments),
1370            wr: JsWriter::new(cm.clone(), "\n", &mut buf, None),
1371        };
1372        let _ = emitter.emit_module(&module);
1373    }
1374    // Remove the trailing semicolon added by the expression statement
1375    let s = String::from_utf8(buf).unwrap_or_default();
1376    s.trim_end().trim_end_matches(';').to_string()
1377}
1378
1379/// Emits a TypeScript type to a string representation.
1380///
1381/// Used by `ts_template!` when injecting types into raw source strings.
1382#[cfg(feature = "swc")]
1383pub fn emit_ts_type(ty: &swc_core::ecma::ast::TsType) -> String {
1384    use swc_core::common::comments::SingleThreadedComments;
1385    use swc_core::common::sync::Lrc;
1386    use swc_core::ecma::ast::{Decl, Ident, Module, ModuleItem, Stmt, TsTypeAliasDecl};
1387    use swc_core::ecma::codegen::{Config, Emitter, text_writer::JsWriter};
1388
1389    // Wrap type in a type alias for emission: `type __T = <type>;`
1390    let module = Module {
1391        span: swc_core::common::DUMMY_SP,
1392        body: vec![ModuleItem::Stmt(Stmt::Decl(Decl::TsTypeAlias(Box::new(
1393            TsTypeAliasDecl {
1394                span: swc_core::common::DUMMY_SP,
1395                declare: false,
1396                id: Ident::new("__T".into(), swc_core::common::DUMMY_SP, Default::default()),
1397                type_params: None,
1398                type_ann: Box::new(ty.clone()),
1399            },
1400        ))))],
1401        shebang: None,
1402    };
1403
1404    let cm = Lrc::new(swc_core::common::SourceMap::default());
1405    let comments = SingleThreadedComments::default();
1406    let mut buf = Vec::new();
1407    {
1408        let mut emitter = Emitter {
1409            cfg: Config::default().with_minify(false),
1410            cm: cm.clone(),
1411            comments: Some(&comments),
1412            wr: JsWriter::new(cm.clone(), "\n", &mut buf, None),
1413        };
1414        let _ = emitter.emit_module(&module);
1415    }
1416    let s = String::from_utf8(buf).unwrap_or_default();
1417    // Extract just the type part from `type __T = <type>;`
1418    if let Some(start) = s.find('=') {
1419        let after_eq = &s[start + 1..];
1420        after_eq.trim().trim_end_matches(';').to_string()
1421    } else {
1422        s
1423    }
1424}
1425
1426/// Emits a statement to a string representation.
1427///
1428/// Used by `ts_template!` when injecting statements into raw source strings.
1429#[cfg(feature = "swc")]
1430pub fn emit_stmt(stmt: &swc_core::ecma::ast::Stmt) -> String {
1431    use swc_core::common::comments::SingleThreadedComments;
1432    use swc_core::common::sync::Lrc;
1433    use swc_core::ecma::ast::{Module, ModuleItem};
1434    use swc_core::ecma::codegen::{Config, Emitter, text_writer::JsWriter};
1435
1436    let module = Module {
1437        span: swc_core::common::DUMMY_SP,
1438        body: vec![ModuleItem::Stmt(stmt.clone())],
1439        shebang: None,
1440    };
1441
1442    let cm = Lrc::new(swc_core::common::SourceMap::default());
1443    let comments = SingleThreadedComments::default();
1444    let mut buf = Vec::new();
1445    {
1446        let mut emitter = Emitter {
1447            cfg: Config::default().with_minify(false),
1448            cm: cm.clone(),
1449            comments: Some(&comments),
1450            wr: JsWriter::new(cm.clone(), "\n", &mut buf, None),
1451        };
1452        let _ = emitter.emit_module(&module);
1453    }
1454    String::from_utf8(buf).unwrap_or_default()
1455}
1456
1457// =============================================================================
1458// ToTsString trait for string-based template interpolation
1459// =============================================================================
1460
1461/// Trait for converting values to TypeScript string representations.
1462///
1463/// This trait is used by `ts_template!` macro for interpolating values into
1464/// string-based templates. It provides a unified way to convert both standard
1465/// Rust types and SWC AST types to their TypeScript string form.
1466///
1467/// # Implementations
1468///
1469/// - Primitive types (`String`, `&str`, numbers, `bool`) use their standard string representation
1470/// - SWC AST types (`Expr`, `Ident`, `TsType`, `Stmt`) use the appropriate emit functions
1471/// - Wrapper types (`TsExpr`, `TsIdent`, etc.) delegate to their inner type
1472pub trait ToTsString {
1473    /// Convert this value to a TypeScript string representation.
1474    fn to_ts_string(&self) -> String;
1475}
1476
1477// Implementations for common Rust types
1478// Note: Strings are output as-is (without quotes) for identifier concatenation.
1479// For string literals in expressions, use expr_str() or similar helper.
1480impl ToTsString for String {
1481    fn to_ts_string(&self) -> String {
1482        self.clone()
1483    }
1484}
1485
1486impl ToTsString for str {
1487    fn to_ts_string(&self) -> String {
1488        self.to_owned()
1489    }
1490}
1491
1492impl ToTsString for bool {
1493    fn to_ts_string(&self) -> String {
1494        self.to_string()
1495    }
1496}
1497
1498impl ToTsString for i8 {
1499    fn to_ts_string(&self) -> String {
1500        self.to_string()
1501    }
1502}
1503
1504impl ToTsString for i16 {
1505    fn to_ts_string(&self) -> String {
1506        self.to_string()
1507    }
1508}
1509
1510impl ToTsString for i32 {
1511    fn to_ts_string(&self) -> String {
1512        self.to_string()
1513    }
1514}
1515
1516impl ToTsString for i64 {
1517    fn to_ts_string(&self) -> String {
1518        self.to_string()
1519    }
1520}
1521
1522impl ToTsString for i128 {
1523    fn to_ts_string(&self) -> String {
1524        self.to_string()
1525    }
1526}
1527
1528impl ToTsString for isize {
1529    fn to_ts_string(&self) -> String {
1530        self.to_string()
1531    }
1532}
1533
1534impl ToTsString for u8 {
1535    fn to_ts_string(&self) -> String {
1536        self.to_string()
1537    }
1538}
1539
1540impl ToTsString for u16 {
1541    fn to_ts_string(&self) -> String {
1542        self.to_string()
1543    }
1544}
1545
1546impl ToTsString for u32 {
1547    fn to_ts_string(&self) -> String {
1548        self.to_string()
1549    }
1550}
1551
1552impl ToTsString for u64 {
1553    fn to_ts_string(&self) -> String {
1554        self.to_string()
1555    }
1556}
1557
1558impl ToTsString for u128 {
1559    fn to_ts_string(&self) -> String {
1560        self.to_string()
1561    }
1562}
1563
1564impl ToTsString for usize {
1565    fn to_ts_string(&self) -> String {
1566        self.to_string()
1567    }
1568}
1569
1570impl ToTsString for f32 {
1571    fn to_ts_string(&self) -> String {
1572        self.to_string()
1573    }
1574}
1575
1576impl ToTsString for f64 {
1577    fn to_ts_string(&self) -> String {
1578        self.to_string()
1579    }
1580}
1581
1582impl ToTsString for char {
1583    fn to_ts_string(&self) -> String {
1584        self.to_string()
1585    }
1586}
1587
1588// Implementation for SWC AST types
1589#[cfg(feature = "swc")]
1590impl ToTsString for swc_core::ecma::ast::Expr {
1591    fn to_ts_string(&self) -> String {
1592        emit_expr(self)
1593    }
1594}
1595
1596#[cfg(feature = "swc")]
1597impl ToTsString for swc_core::ecma::ast::Ident {
1598    fn to_ts_string(&self) -> String {
1599        self.sym.to_string()
1600    }
1601}
1602
1603#[cfg(feature = "swc")]
1604impl ToTsString for swc_core::ecma::ast::TsType {
1605    fn to_ts_string(&self) -> String {
1606        emit_ts_type(self)
1607    }
1608}
1609
1610#[cfg(feature = "swc")]
1611impl ToTsString for swc_core::ecma::ast::Stmt {
1612    fn to_ts_string(&self) -> String {
1613        emit_stmt(self)
1614    }
1615}
1616
1617#[cfg(feature = "swc")]
1618impl ToTsString for crate::TsStream {
1619    fn to_ts_string(&self) -> String {
1620        self.source().to_string()
1621    }
1622}
1623
1624// Reference implementations
1625impl<T: ToTsString + ?Sized> ToTsString for &T {
1626    fn to_ts_string(&self) -> String {
1627        (*self).to_ts_string()
1628    }
1629}
1630
1631impl<T: ToTsString + ?Sized> ToTsString for &mut T {
1632    fn to_ts_string(&self) -> String {
1633        (**self).to_ts_string()
1634    }
1635}
1636
1637impl<T: ToTsString + ?Sized> ToTsString for Box<T> {
1638    fn to_ts_string(&self) -> String {
1639        (**self).to_ts_string()
1640    }
1641}
1642
1643impl<T: ToTsString + Clone> ToTsString for std::borrow::Cow<'_, T> {
1644    fn to_ts_string(&self) -> String {
1645        self.as_ref().to_ts_string()
1646    }
1647}
1648
1649impl<T: ToTsString> ToTsString for std::rc::Rc<T> {
1650    fn to_ts_string(&self) -> String {
1651        (**self).to_ts_string()
1652    }
1653}
1654
1655impl<T: ToTsString> ToTsString for std::sync::Arc<T> {
1656    fn to_ts_string(&self) -> String {
1657        (**self).to_ts_string()
1658    }
1659}
1660
1661/// Wrapper for [`swc_core::ecma::ast::Expr`] that implements [`Display`] and [`ToTsString`].
1662///
1663/// This allows expressions to be interpolated into string-based templates
1664/// using `@{expr}` syntax, where the expression will be emitted as TypeScript code.
1665///
1666/// # Example
1667/// ```ignore
1668/// use macroforge_ts_syn::{TsExpr, swc_ecma_ast::Expr};
1669///
1670/// let expr: Expr = /* ... */;
1671/// let wrapped = TsExpr(expr);
1672/// println!("const x = {};", wrapped); // Emits: const x = <expr>;
1673/// ```
1674#[cfg(feature = "swc")]
1675#[derive(Debug, Clone)]
1676pub struct TsExpr(pub swc_core::ecma::ast::Expr);
1677
1678#[cfg(feature = "swc")]
1679impl std::fmt::Display for TsExpr {
1680    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1681        write!(f, "{}", emit_expr(&self.0))
1682    }
1683}
1684
1685#[cfg(feature = "swc")]
1686impl From<swc_core::ecma::ast::Expr> for TsExpr {
1687    fn from(expr: swc_core::ecma::ast::Expr) -> Self {
1688        TsExpr(expr)
1689    }
1690}
1691
1692#[cfg(feature = "swc")]
1693impl std::ops::Deref for TsExpr {
1694    type Target = swc_core::ecma::ast::Expr;
1695    fn deref(&self) -> &Self::Target {
1696        &self.0
1697    }
1698}
1699
1700/// Wrapper for [`swc_core::ecma::ast::Ident`] that implements [`Display`].
1701///
1702/// This allows identifiers to be interpolated into string-based templates.
1703/// The identifier's symbol (name) is emitted directly.
1704///
1705/// # Example
1706/// ```ignore
1707/// use macroforge_ts_syn::{TsIdent, ts_ident};
1708///
1709/// let id = TsIdent(ts_ident!("myVariable"));
1710/// println!("const {} = 42;", id); // Emits: const myVariable = 42;
1711/// ```
1712#[cfg(feature = "swc")]
1713#[derive(Debug, Clone)]
1714pub struct TsIdent(pub swc_core::ecma::ast::Ident);
1715
1716#[cfg(feature = "swc")]
1717impl std::fmt::Display for TsIdent {
1718    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1719        write!(f, "{}", self.0.sym)
1720    }
1721}
1722
1723#[cfg(feature = "swc")]
1724impl From<swc_core::ecma::ast::Ident> for TsIdent {
1725    fn from(ident: swc_core::ecma::ast::Ident) -> Self {
1726        TsIdent(ident)
1727    }
1728}
1729
1730#[cfg(feature = "swc")]
1731impl std::ops::Deref for TsIdent {
1732    type Target = swc_core::ecma::ast::Ident;
1733    fn deref(&self) -> &Self::Target {
1734        &self.0
1735    }
1736}
1737
1738/// Wrapper for [`swc_core::ecma::ast::TsType`] that implements [`Display`].
1739///
1740/// This allows TypeScript types to be interpolated into string-based templates.
1741///
1742/// # Example
1743/// ```ignore
1744/// use macroforge_ts_syn::TsTypeWrapper;
1745///
1746/// let ty: TsType = /* ... */;
1747/// let wrapped = TsTypeWrapper(ty);
1748/// println!("type Alias = {};", wrapped); // Emits: type Alias = <type>;
1749/// ```
1750#[cfg(feature = "swc")]
1751#[derive(Debug, Clone)]
1752pub struct TsTypeWrapper(pub swc_core::ecma::ast::TsType);
1753
1754#[cfg(feature = "swc")]
1755impl std::fmt::Display for TsTypeWrapper {
1756    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1757        write!(f, "{}", emit_ts_type(&self.0))
1758    }
1759}
1760
1761#[cfg(feature = "swc")]
1762impl From<swc_core::ecma::ast::TsType> for TsTypeWrapper {
1763    fn from(ty: swc_core::ecma::ast::TsType) -> Self {
1764        TsTypeWrapper(ty)
1765    }
1766}
1767
1768#[cfg(feature = "swc")]
1769impl std::ops::Deref for TsTypeWrapper {
1770    type Target = swc_core::ecma::ast::TsType;
1771    fn deref(&self) -> &Self::Target {
1772        &self.0
1773    }
1774}
1775
1776/// Wrapper for [`swc_core::ecma::ast::Stmt`] that implements [`Display`].
1777///
1778/// This allows statements to be interpolated into string-based templates.
1779///
1780/// # Example
1781/// ```ignore
1782/// use macroforge_ts_syn::TsStmt;
1783///
1784/// let stmt: Stmt = /* ... */;
1785/// let wrapped = TsStmt(stmt);
1786/// println!("{}", wrapped); // Emits the statement
1787/// ```
1788#[cfg(feature = "swc")]
1789#[derive(Debug, Clone)]
1790pub struct TsStmt(pub swc_core::ecma::ast::Stmt);
1791
1792#[cfg(feature = "swc")]
1793impl std::fmt::Display for TsStmt {
1794    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1795        write!(f, "{}", emit_stmt(&self.0))
1796    }
1797}
1798
1799#[cfg(feature = "swc")]
1800impl From<swc_core::ecma::ast::Stmt> for TsStmt {
1801    fn from(stmt: swc_core::ecma::ast::Stmt) -> Self {
1802        TsStmt(stmt)
1803    }
1804}
1805
1806#[cfg(feature = "swc")]
1807impl std::ops::Deref for TsStmt {
1808    type Target = swc_core::ecma::ast::Stmt;
1809    fn deref(&self) -> &Self::Target {
1810        &self.0
1811    }
1812}
1813
1814// =============================================================================
1815// Internal helpers for virtual completion (used by generated code)
1816// =============================================================================
1817
1818/// Internal module for virtual completion helpers.
1819///
1820/// These functions are used by the generated code from `ts_quote!` macro
1821/// when templates contain control flow inside function bodies. The template
1822/// gets split into incomplete chunks that are validated individually, and
1823/// these helpers reassemble the complete AST at runtime.
1824///
1825/// **DO NOT USE DIRECTLY** - These are implementation details of the macro system.
1826#[cfg(feature = "swc")]
1827pub mod __internal {
1828    use std::collections::HashMap;
1829    use swc_core::ecma::ast::*;
1830    use swc_core::ecma::visit::{VisitMut, VisitMutWith};
1831
1832    // =========================================================================
1833    // Context-aware collector system
1834    // =========================================================================
1835
1836    /// Collector for items generated inside control flow, parameterized by context.
1837    ///
1838    /// When a for-loop or if-block appears inside an object literal, array literal,
1839    /// class body, or type literal, the items generated need to be collected and
1840    /// later merged into the parent structure.
1841    #[derive(Debug, Clone)]
1842    pub enum ContextCollector {
1843        /// Object literal properties: `{ key: value, ... }`
1844        ObjectProps(Vec<PropOrSpread>),
1845        /// Array literal elements: `[elem, ...]`
1846        ArrayElems(Vec<ExprOrSpread>),
1847        /// Class body members: `class { member... }`
1848        ClassMembers(Vec<ClassMember>),
1849        /// Type literal properties: `{ prop: Type, ... }`
1850        TypeProps(Vec<TsTypeElement>),
1851        /// Statements (for function/block bodies) - handled separately
1852        Stmts(Vec<Stmt>),
1853    }
1854
1855    impl ContextCollector {
1856        /// Creates a new empty collector for the given context type.
1857        ///
1858        /// Context values: 0=ObjectLiteral, 1=ArrayLiteral, 2=ClassBody, 3=TypeObjectLiteral, _=Stmts
1859        pub fn new(context_type: u8) -> Self {
1860            match context_type {
1861                0 => Self::ObjectProps(vec![]),
1862                1 => Self::ArrayElems(vec![]),
1863                2 => Self::ClassMembers(vec![]),
1864                3 => Self::TypeProps(vec![]),
1865                _ => Self::Stmts(vec![]),
1866            }
1867        }
1868
1869        /// Creates a collector for object literal properties.
1870        pub fn for_object() -> Self {
1871            Self::ObjectProps(vec![])
1872        }
1873
1874        /// Creates a collector for array literal elements.
1875        pub fn for_array() -> Self {
1876            Self::ArrayElems(vec![])
1877        }
1878
1879        /// Creates a collector for class body members.
1880        pub fn for_class() -> Self {
1881            Self::ClassMembers(vec![])
1882        }
1883
1884        /// Creates a collector for type literal properties.
1885        pub fn for_type_object() -> Self {
1886            Self::TypeProps(vec![])
1887        }
1888    }
1889
1890    /// Pushes an object property to a collector.
1891    ///
1892    /// Panics if the collector is not an ObjectProps variant.
1893    pub fn push_object_prop(collector: &mut ContextCollector, prop: PropOrSpread) {
1894        match collector {
1895            ContextCollector::ObjectProps(props) => props.push(prop),
1896            _ => panic!("push_object_prop called on non-ObjectProps collector"),
1897        }
1898    }
1899
1900    /// Pushes an array element to a collector.
1901    ///
1902    /// Panics if the collector is not an ArrayElems variant.
1903    pub fn push_array_elem(collector: &mut ContextCollector, elem: ExprOrSpread) {
1904        match collector {
1905            ContextCollector::ArrayElems(elems) => elems.push(elem),
1906            _ => panic!("push_array_elem called on non-ArrayElems collector"),
1907        }
1908    }
1909
1910    /// Pushes a class member to a collector.
1911    ///
1912    /// Panics if the collector is not a ClassMembers variant.
1913    pub fn push_class_member(collector: &mut ContextCollector, member: ClassMember) {
1914        match collector {
1915            ContextCollector::ClassMembers(members) => members.push(member),
1916            _ => panic!("push_class_member called on non-ClassMembers collector"),
1917        }
1918    }
1919
1920    /// Pushes a type property to a collector.
1921    ///
1922    /// Panics if the collector is not a TypeProps variant.
1923    pub fn push_type_prop(collector: &mut ContextCollector, prop: TsTypeElement) {
1924        match collector {
1925            ContextCollector::TypeProps(props) => props.push(prop),
1926            _ => panic!("push_type_prop called on non-TypeProps collector"),
1927        }
1928    }
1929
1930    /// Extracts a PropOrSpread from a wrapped expression `({ prop })`.
1931    ///
1932    /// This is used when parsing object literal properties in a for-loop body.
1933    /// The property is wrapped in `({ ... })` to make it parseable, then we
1934    /// extract the actual property.
1935    pub fn extract_prop_from_wrapped_expr(expr: &Expr) -> Option<PropOrSpread> {
1936        // Expected structure: Paren(Object({ props: [prop] }))
1937        if let Expr::Paren(ParenExpr { expr: inner, .. }) = expr
1938            && let Expr::Object(ObjectLit { props, .. }) = inner.as_ref()
1939        {
1940            // Skip __mf_dummy property if present, return the real property
1941            for prop in props {
1942                match prop {
1943                    PropOrSpread::Prop(p) => {
1944                        if let Prop::KeyValue(KeyValueProp {
1945                            key: PropName::Ident(ident),
1946                            ..
1947                        }) = p.as_ref()
1948                            && ident.sym.as_ref() == "__mf_dummy"
1949                        {
1950                            continue;
1951                        }
1952                        return Some(prop.clone());
1953                    }
1954                    PropOrSpread::Spread(_) => return Some(prop.clone()),
1955                }
1956            }
1957        }
1958        None
1959    }
1960
1961    /// Extracts an ExprOrSpread from a wrapped array expression `[elem]`.
1962    ///
1963    /// This is used when parsing array literal elements in a for-loop body.
1964    pub fn extract_elem_from_wrapped_expr(expr: &Expr) -> Option<ExprOrSpread> {
1965        // Expected structure: Array({ elems: [Some(elem)] })
1966        if let Expr::Array(ArrayLit { elems, .. }) = expr
1967            && let Some(elem) = elems.iter().flatten().next()
1968        {
1969            return Some(elem.clone());
1970        }
1971        None
1972    }
1973
1974    /// Extracts class members from a wrapped class declaration `class __MF_DUMMY__ { member }`.
1975    ///
1976    /// This is used when parsing class body members in a for-loop body.
1977    pub fn extract_class_members_from_wrapped(item: &ModuleItem) -> Vec<ClassMember> {
1978        // Expected structure: ModuleItem::Stmt(Stmt::Decl(Decl::Class(ClassDecl { class: Class { body: [member] } })))
1979        if let ModuleItem::Stmt(Stmt::Decl(Decl::Class(ClassDecl { class, .. }))) = item {
1980            return class.body.clone();
1981        }
1982        vec![]
1983    }
1984
1985    /// Extracts type elements from a wrapped type alias `type __MF_WRAPPER__ = { prop: Type }`.
1986    ///
1987    /// This is used when parsing type literal members in a for-loop body.
1988    pub fn extract_type_elements_from_wrapped(item: &ModuleItem) -> Vec<TsTypeElement> {
1989        // Expected structure: ModuleItem::Stmt(Stmt::Decl(Decl::TsTypeAlias(TsTypeAliasDecl { type_ann: TsType::TsTypeLit(TsTypeLit { members }) })))
1990        if let ModuleItem::Stmt(Stmt::Decl(Decl::TsTypeAlias(type_alias))) = item
1991            && let TsType::TsTypeLit(type_lit) = type_alias.type_ann.as_ref()
1992        {
1993            return type_lit.members.clone();
1994        }
1995        vec![]
1996    }
1997
1998    /// Merges collected object properties into an opener's object literal.
1999    ///
2000    /// Finds the object literal in the opener (typically inside a return statement)
2001    /// and adds the collected properties to it, removing any dummy properties.
2002    pub fn merge_object_props(item: &mut ModuleItem, props: Vec<PropOrSpread>) {
2003        let mut merger = ObjectPropMerger {
2004            props_to_add: props,
2005        };
2006        item.visit_mut_with(&mut merger);
2007    }
2008
2009    /// Merges collected array elements into an opener's array literal.
2010    pub fn merge_array_elems(item: &mut ModuleItem, elems: Vec<ExprOrSpread>) {
2011        let mut merger = ArrayElemMerger {
2012            elems_to_add: elems,
2013        };
2014        item.visit_mut_with(&mut merger);
2015    }
2016
2017    /// Merges collected class members into an opener's class body.
2018    pub fn merge_class_members(item: &mut ModuleItem, members: Vec<ClassMember>) {
2019        let mut merger = ClassMemberMerger {
2020            members_to_add: members,
2021        };
2022        item.visit_mut_with(&mut merger);
2023    }
2024
2025    /// Merges collected type properties into an opener's type literal.
2026    pub fn merge_type_props(item: &mut ModuleItem, props: Vec<TsTypeElement>) {
2027        let mut merger = TypePropMerger {
2028            props_to_add: props,
2029        };
2030        item.visit_mut_with(&mut merger);
2031    }
2032
2033    /// Visitor that merges properties into the first object literal found.
2034    struct ObjectPropMerger {
2035        props_to_add: Vec<PropOrSpread>,
2036    }
2037
2038    impl VisitMut for ObjectPropMerger {
2039        fn visit_mut_object_lit(&mut self, obj: &mut ObjectLit) {
2040            if !self.props_to_add.is_empty() {
2041                // Remove __mf_dummy property if present
2042                obj.props.retain(|prop| {
2043                    if let PropOrSpread::Prop(p) = prop
2044                        && let Prop::KeyValue(KeyValueProp {
2045                            key: PropName::Ident(ident),
2046                            ..
2047                        }) = p.as_ref()
2048                        && ident.sym.as_ref() == "__mf_dummy"
2049                    {
2050                        return false;
2051                    }
2052                    true
2053                });
2054                // Add collected properties
2055                obj.props.extend(std::mem::take(&mut self.props_to_add));
2056            }
2057        }
2058    }
2059
2060    /// Visitor that merges elements into the first array literal found.
2061    struct ArrayElemMerger {
2062        elems_to_add: Vec<ExprOrSpread>,
2063    }
2064
2065    impl VisitMut for ArrayElemMerger {
2066        fn visit_mut_array_lit(&mut self, arr: &mut ArrayLit) {
2067            if !self.elems_to_add.is_empty() {
2068                // Remove dummy element (first element with value 0) if present
2069                if let Some(Some(ExprOrSpread { expr, .. })) = arr.elems.first()
2070                    && let Expr::Lit(Lit::Num(Number { value, .. })) = expr.as_ref()
2071                    && *value == 0.0
2072                {
2073                    arr.elems.remove(0);
2074                }
2075                // Add collected elements
2076                for elem in std::mem::take(&mut self.elems_to_add) {
2077                    arr.elems.push(Some(elem));
2078                }
2079            }
2080        }
2081    }
2082
2083    /// Visitor that merges members into the first class found.
2084    struct ClassMemberMerger {
2085        members_to_add: Vec<ClassMember>,
2086    }
2087
2088    impl VisitMut for ClassMemberMerger {
2089        fn visit_mut_class(&mut self, class: &mut Class) {
2090            if !self.members_to_add.is_empty() {
2091                class.body.extend(std::mem::take(&mut self.members_to_add));
2092            }
2093        }
2094    }
2095
2096    /// Visitor that merges type properties into the first type literal found.
2097    struct TypePropMerger {
2098        props_to_add: Vec<TsTypeElement>,
2099    }
2100
2101    impl VisitMut for TypePropMerger {
2102        fn visit_mut_ts_type_lit(&mut self, lit: &mut TsTypeLit) {
2103            if !self.props_to_add.is_empty() {
2104                lit.members.extend(std::mem::take(&mut self.props_to_add));
2105            }
2106        }
2107    }
2108
2109    // =========================================================================
2110    // Existing type replacement helpers
2111    // =========================================================================
2112
2113    /// Visitor that replaces marker type references with actual types.
2114    struct TypeReplacer {
2115        /// Mapping from placeholder names to actual types
2116        replacements: HashMap<String, TsType>,
2117    }
2118
2119    impl VisitMut for TypeReplacer {
2120        fn visit_mut_ts_type(&mut self, ty: &mut TsType) {
2121            // First, check if this is a type reference to one of our markers
2122            if let TsType::TsTypeRef(TsTypeRef {
2123                type_name: TsEntityName::Ident(ident),
2124                type_params: None,
2125                ..
2126            }) = ty
2127            {
2128                let name = ident.sym.to_string();
2129                if let Some(replacement) = self.replacements.get(&name) {
2130                    *ty = replacement.clone();
2131                    return;
2132                }
2133            }
2134            // Continue visiting children
2135            ty.visit_mut_children_with(self);
2136        }
2137    }
2138
2139    /// Replaces marker type references in a ModuleItem with actual types.
2140    ///
2141    /// This is used for type placeholder substitution. SWC quote! doesn't
2142    /// support `$placeholder` in type positions, so we use marker names like
2143    /// `__ph_0` and replace them after parsing.
2144    ///
2145    /// # Arguments
2146    /// * `item` - The ModuleItem to transform (modified in-place)
2147    /// * `replacements` - Map of placeholder names to actual TsType values
2148    pub fn replace_type_markers(item: &mut ModuleItem, replacements: HashMap<String, TsType>) {
2149        let mut replacer = TypeReplacer { replacements };
2150        item.visit_mut_with(&mut replacer);
2151    }
2152
2153    /// Replaces marker type references in a Statement with actual types.
2154    pub fn replace_type_markers_stmt(stmt: &mut Stmt, replacements: HashMap<String, TsType>) {
2155        let mut replacer = TypeReplacer { replacements };
2156        stmt.visit_mut_with(&mut replacer);
2157    }
2158
2159    /// Handle an opener chunk (template with unclosed braces).
2160    ///
2161    /// This is called when a template like `export function foo() { const x = 1;`
2162    /// was parsed with virtual closing braces. We push the item to output and
2163    /// return the index so the closer can find it.
2164    ///
2165    /// # Arguments
2166    /// * `item` - The parsed ModuleItem (function/class with virtual body)
2167    /// * `output` - The accumulator Vec<ModuleItem>
2168    /// * `depth` - Number of virtual closing braces that were added
2169    ///
2170    /// # Returns
2171    /// The index of the pushed item in output
2172    pub fn push_opener(item: ModuleItem, output: &mut Vec<ModuleItem>, _depth: usize) -> usize {
2173        let idx = output.len();
2174        output.push(item);
2175        idx
2176    }
2177
2178    /// Handle a closer chunk (template with unmatched closing braces).
2179    ///
2180    /// This is called when a template like `return x; }` was parsed with a
2181    /// virtual function wrapper. We extract the statements and add them to
2182    /// the opener's body.
2183    ///
2184    /// # Arguments
2185    /// * `item` - The parsed ModuleItem (virtual wrapper containing actual statements)
2186    /// * `output` - The accumulator Vec<ModuleItem>
2187    /// * `opener_idx` - The index of the opener item (from push_opener)
2188    /// * `_depth` - Number of virtual opening braces that were added
2189    pub fn finalize_closer(item: ModuleItem, output: &mut Vec<ModuleItem>, opener_idx: usize) {
2190        // Extract statements from the virtual wrapper (function __mf_virtual() { ... })
2191        let closer_stmts = extract_function_body_statements(&item);
2192
2193        // Collect all items pushed after the opener (these are statements from control flow)
2194        let intermediate_items: Vec<ModuleItem> = output.drain((opener_idx + 1)..).collect();
2195
2196        // Convert intermediate ModuleItems to Statements
2197        let intermediate_stmts: Vec<Stmt> = intermediate_items
2198            .into_iter()
2199            .filter_map(|mi| match mi {
2200                ModuleItem::Stmt(stmt) => Some(stmt),
2201                _ => None, // Skip module declarations (shouldn't happen in function body)
2202            })
2203            .collect();
2204
2205        // Extend the opener's body with intermediate + closer statements
2206        if let Some(opener) = output.get_mut(opener_idx) {
2207            let all_stmts: Vec<Stmt> = intermediate_stmts.into_iter().chain(closer_stmts).collect();
2208            extend_item_body(opener, all_stmts);
2209        }
2210    }
2211
2212    /// Handle a middle chunk (template with both unclosed and unmatched braces).
2213    ///
2214    /// This is for cases like `} else {` which close one block and open another.
2215    /// We extract the statements and add them to the opener's body.
2216    ///
2217    /// # Arguments
2218    /// * `item` - The parsed ModuleItem (virtual wrapper)
2219    /// * `output` - The accumulator Vec<ModuleItem>
2220    /// * `opener_idx` - The index of the opener item
2221    pub fn push_middle(item: ModuleItem, output: &mut Vec<ModuleItem>, opener_idx: usize) {
2222        // Extract statements from the virtual wrapper
2223        let stmts = extract_function_body_statements(&item);
2224
2225        // Collect intermediate items
2226        let intermediate_items: Vec<ModuleItem> = output.drain((opener_idx + 1)..).collect();
2227
2228        let intermediate_stmts: Vec<Stmt> = intermediate_items
2229            .into_iter()
2230            .filter_map(|mi| match mi {
2231                ModuleItem::Stmt(stmt) => Some(stmt),
2232                _ => None,
2233            })
2234            .collect();
2235
2236        // Extend the opener's body
2237        if let Some(opener) = output.get_mut(opener_idx) {
2238            let all_stmts: Vec<Stmt> = intermediate_stmts.into_iter().chain(stmts).collect();
2239            extend_item_body(opener, all_stmts);
2240        }
2241    }
2242
2243    /// Extract the body statements from a function declaration.
2244    pub fn extract_function_body_statements(item: &ModuleItem) -> Vec<Stmt> {
2245        match item {
2246            // function foo() { ... }
2247            ModuleItem::Stmt(Stmt::Decl(Decl::Fn(FnDecl { function, .. }))) => {
2248                if let Some(body) = &function.body {
2249                    body.stmts.clone()
2250                } else {
2251                    vec![]
2252                }
2253            }
2254            // export function foo() { ... }
2255            ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
2256                decl: Decl::Fn(FnDecl { function, .. }),
2257                ..
2258            })) => {
2259                if let Some(body) = &function.body {
2260                    body.stmts.clone()
2261                } else {
2262                    vec![]
2263                }
2264            }
2265            _ => vec![],
2266        }
2267    }
2268
2269    /// Extend the body of a function or class method with additional statements.
2270    fn extend_item_body(item: &mut ModuleItem, stmts: Vec<Stmt>) {
2271        match item {
2272            // export function foo() { ... }
2273            ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
2274                decl: Decl::Fn(FnDecl { function, .. }),
2275                ..
2276            })) => {
2277                if let Some(ref mut body) = function.body {
2278                    body.stmts.extend(stmts);
2279                }
2280            }
2281
2282            // export default function() { ... }
2283            ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(ExportDefaultDecl {
2284                decl: DefaultDecl::Fn(FnExpr { function, .. }),
2285                ..
2286            })) => {
2287                if let Some(ref mut body) = function.body {
2288                    body.stmts.extend(stmts);
2289                }
2290            }
2291
2292            // function foo() { ... }
2293            ModuleItem::Stmt(Stmt::Decl(Decl::Fn(FnDecl { function, .. }))) => {
2294                if let Some(ref mut body) = function.body {
2295                    body.stmts.extend(stmts);
2296                }
2297            }
2298
2299            // export class Foo { method() { ... } }
2300            ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
2301                decl: Decl::Class(ClassDecl { class, .. }),
2302                ..
2303            })) => {
2304                // Find the last method or constructor and extend its body
2305                for member in class.body.iter_mut().rev() {
2306                    match member {
2307                        ClassMember::Method(ClassMethod { function, .. }) => {
2308                            if let Some(ref mut body) = function.body {
2309                                body.stmts.extend(stmts);
2310                                return;
2311                            }
2312                        }
2313                        ClassMember::Constructor(Constructor {
2314                            body: Some(body), ..
2315                        }) => {
2316                            body.stmts.extend(stmts);
2317                            return;
2318                        }
2319                        _ => {}
2320                    }
2321                }
2322            }
2323
2324            // class Foo { ... }
2325            ModuleItem::Stmt(Stmt::Decl(Decl::Class(ClassDecl { class, .. }))) => {
2326                for member in class.body.iter_mut().rev() {
2327                    match member {
2328                        ClassMember::Method(ClassMethod { function, .. }) => {
2329                            if let Some(ref mut body) = function.body {
2330                                body.stmts.extend(stmts);
2331                                return;
2332                            }
2333                        }
2334                        ClassMember::Constructor(Constructor {
2335                            body: Some(body), ..
2336                        }) => {
2337                            body.stmts.extend(stmts);
2338                            return;
2339                        }
2340                        _ => {}
2341                    }
2342                }
2343            }
2344
2345            ModuleItem::Stmt(stmt) => {
2346                extend_stmt_body(stmt, stmts);
2347            }
2348
2349            _ => {
2350                // For other cases, we can't extend the body
2351            }
2352        }
2353    }
2354
2355    fn extend_stmt_body(stmt: &mut Stmt, stmts: Vec<Stmt>) {
2356        match stmt {
2357            Stmt::Block(block) => {
2358                block.stmts.extend(stmts);
2359            }
2360            Stmt::If(if_stmt) => {
2361                if let Some(alt) = if_stmt.alt.as_deref_mut() {
2362                    extend_stmt_body(alt, stmts);
2363                } else {
2364                    extend_stmt_body(if_stmt.cons.as_mut(), stmts);
2365                }
2366            }
2367            Stmt::For(for_stmt) => {
2368                extend_stmt_body(for_stmt.body.as_mut(), stmts);
2369            }
2370            Stmt::ForIn(for_in_stmt) => {
2371                extend_stmt_body(for_in_stmt.body.as_mut(), stmts);
2372            }
2373            Stmt::ForOf(for_of_stmt) => {
2374                extend_stmt_body(for_of_stmt.body.as_mut(), stmts);
2375            }
2376            Stmt::While(while_stmt) => {
2377                extend_stmt_body(while_stmt.body.as_mut(), stmts);
2378            }
2379            Stmt::DoWhile(do_while_stmt) => {
2380                extend_stmt_body(do_while_stmt.body.as_mut(), stmts);
2381            }
2382            Stmt::Labeled(labeled_stmt) => {
2383                extend_stmt_body(labeled_stmt.body.as_mut(), stmts);
2384            }
2385            Stmt::With(with_stmt) => {
2386                extend_stmt_body(with_stmt.body.as_mut(), stmts);
2387            }
2388            Stmt::Try(try_stmt) => {
2389                if let Some(finalizer) = &mut try_stmt.finalizer {
2390                    finalizer.stmts.extend(stmts);
2391                } else if let Some(handler) = &mut try_stmt.handler {
2392                    handler.body.stmts.extend(stmts);
2393                } else {
2394                    try_stmt.block.stmts.extend(stmts);
2395                }
2396            }
2397            _ => {}
2398        }
2399    }
2400}
2401
2402/// Converts a statement to its TypeScript string representation.
2403#[cfg(feature = "swc")]
2404pub fn stmt_to_string(stmt: &swc_core::ecma::ast::Stmt) -> String {
2405    use swc_core::common::sync::Lrc;
2406    use swc_core::ecma::ast::{Module, ModuleItem};
2407    use swc_core::ecma::codegen::{Config, Emitter, text_writer::JsWriter};
2408
2409    let module = Module {
2410        span: swc_core::common::DUMMY_SP,
2411        body: vec![ModuleItem::Stmt(stmt.clone())],
2412        shebang: None,
2413    };
2414
2415    let cm = Lrc::new(swc_core::common::SourceMap::default());
2416    let mut buf = Vec::new();
2417    {
2418        let mut emitter = Emitter {
2419            cfg: Config::default(),
2420            cm: cm.clone(),
2421            comments: None,
2422            wr: JsWriter::new(cm.clone(), "\n", &mut buf, None),
2423        };
2424        let _ = emitter.emit_module(&module);
2425    }
2426    String::from_utf8(buf)
2427        .unwrap_or_default()
2428        .trim()
2429        .to_string()
2430}
2431
2432/// Parses a TypeScript string into a Vec of ModuleItems.
2433///
2434/// This is used by the template compiler to parse generated code at runtime.
2435#[cfg(feature = "swc")]
2436pub fn parse_ts_to_module_items(source: &str) -> Vec<swc_core::ecma::ast::ModuleItem> {
2437    use swc_core::common::{FileName, SourceMap, sync::Lrc};
2438    use swc_core::ecma::parser::{Parser, StringInput, Syntax, TsSyntax, lexer::Lexer};
2439
2440    let cm: Lrc<SourceMap> = Lrc::new(SourceMap::default());
2441    let fm = cm.new_source_file(
2442        FileName::Custom("template.ts".into()).into(),
2443        source.to_string(),
2444    );
2445    let syntax = Syntax::Typescript(TsSyntax {
2446        tsx: true,
2447        decorators: true,
2448        ..Default::default()
2449    });
2450    let lexer = Lexer::new(
2451        syntax,
2452        swc_core::ecma::ast::EsVersion::latest(),
2453        StringInput::from(&*fm),
2454        None,
2455    );
2456    let mut parser = Parser::new_from(lexer);
2457    match parser.parse_module() {
2458        Ok(module) => module.body,
2459        Err(_) => vec![],
2460    }
2461}
2462
2463/// Extends the body of a function or class with additional statements.
2464///
2465/// This helper is used by the generated code for virtually-completed chunks.
2466/// When a template has control flow inside a function body, the function is
2467/// parsed with an empty body and this function extends it with the actual
2468/// statements collected at runtime.
2469///
2470/// # Arguments
2471///
2472/// * `item` - The `ModuleItem` containing a function or class declaration
2473/// * `stmts` - The statements to add to the function/class body
2474///
2475/// # Supported Structures
2476///
2477/// - `export function ...` - Extends the function body
2478/// - `export class ... { method() {} }` - Extends the last method's body
2479/// - `function ...` (non-export) - Extends the function body
2480#[cfg(feature = "swc")]
2481pub fn extend_module_item_body(
2482    item: &mut swc_core::ecma::ast::ModuleItem,
2483    stmts: Vec<swc_core::ecma::ast::Stmt>,
2484) {
2485    use swc_core::ecma::ast::*;
2486
2487    match item {
2488        // export function foo() { ... }
2489        ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
2490            decl: Decl::Fn(FnDecl { function, .. }),
2491            ..
2492        })) => {
2493            if let Some(ref mut body) = function.body {
2494                body.stmts.extend(stmts);
2495            }
2496        }
2497
2498        // export default function() { ... }
2499        ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(ExportDefaultDecl {
2500            decl: DefaultDecl::Fn(FnExpr { function, .. }),
2501            ..
2502        })) => {
2503            if let Some(ref mut body) = function.body {
2504                body.stmts.extend(stmts);
2505            }
2506        }
2507
2508        // function foo() { ... }
2509        ModuleItem::Stmt(Stmt::Decl(Decl::Fn(FnDecl { function, .. }))) => {
2510            if let Some(ref mut body) = function.body {
2511                body.stmts.extend(stmts);
2512            }
2513        }
2514
2515        // export class Foo { method() { ... } }
2516        ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
2517            decl: Decl::Class(ClassDecl { class, .. }),
2518            ..
2519        })) => {
2520            // Find the last method and extend its body
2521            for member in class.body.iter_mut().rev() {
2522                if let ClassMember::Method(ClassMethod { function, .. }) = member
2523                    && let Some(ref mut body) = function.body
2524                {
2525                    body.stmts.extend(stmts);
2526                    return;
2527                }
2528            }
2529        }
2530
2531        // class Foo { ... }
2532        ModuleItem::Stmt(Stmt::Decl(Decl::Class(ClassDecl { class, .. }))) => {
2533            // Find the last method and extend its body
2534            for member in class.body.iter_mut().rev() {
2535                if let ClassMember::Method(ClassMethod { function, .. }) = member
2536                    && let Some(ref mut body) = function.body
2537                {
2538                    body.stmts.extend(stmts);
2539                    return;
2540                }
2541            }
2542        }
2543
2544        // Arrow function expression or other constructs
2545        _ => {
2546            // For other cases, we can't easily extend the body
2547            // Just ignore - the statements won't be added
2548        }
2549    }
2550}