swc_ecma_transforms_typescript/
transform.rs

1use std::{iter, mem};
2
3use rustc_hash::{FxHashMap, FxHashSet};
4use swc_atoms::Atom;
5use swc_common::{
6    errors::HANDLER, source_map::PURE_SP, util::take::Take, Mark, Span, Spanned, SyntaxContext,
7    DUMMY_SP,
8};
9use swc_ecma_ast::*;
10use swc_ecma_utils::{
11    alias_ident_for, constructor::inject_after_super, find_pat_ids, ident::IdentLike, is_literal,
12    member_expr, private_ident, quote_ident, quote_str, stack_size::maybe_grow_default,
13    ExprFactory, QueryRef, RefRewriter, StmtLikeInjector,
14};
15use swc_ecma_visit::{
16    noop_visit_mut_type, noop_visit_type, visit_mut_pass, Visit, VisitMut, VisitMutWith, VisitWith,
17};
18
19use crate::{
20    config::TsImportExportAssignConfig,
21    ts_enum::{EnumValueComputer, TsEnumRecord, TsEnumRecordKey, TsEnumRecordValue},
22    utils::{assign_value_to_this_private_prop, assign_value_to_this_prop, Factory},
23};
24
25#[inline]
26fn enum_member_id_atom(id: &TsEnumMemberId) -> Atom {
27    match id {
28        TsEnumMemberId::Ident(ident) => ident.sym.clone(),
29        TsEnumMemberId::Str(s) => s.value.to_atom_lossy().into_owned(),
30        #[cfg(swc_ast_unknown)]
31        _ => panic!("unable to access unknown nodes"),
32    }
33}
34
35/// ## This Module will transform all TypeScript specific synatx
36///
37/// - ### [namespace]/[modules]/[enums]
38/// - ### class constructor [parameter properties]
39///
40///     ```TypeScript
41///     class Foo {
42///         constructor(public x: number) {
43///             // No body necessary
44///         }
45///     }
46///     ```
47/// - ### [export and import require]
48///
49///     ```TypeScript
50///     import foo = require("foo");
51///     export = foo;
52///     ```
53///
54/// [namespace]: https://www.typescriptlang.org/docs/handbook/namespaces.html
55/// [modules]: https://www.typescriptlang.org/docs/handbook/modules.html
56/// [enums]: https://www.typescriptlang.org/docs/handbook/enums.html
57/// [parameter properties]: https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties
58/// [export and import require]: https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require
59#[derive(Default)]
60pub(crate) struct Transform {
61    unresolved_ctxt: SyntaxContext,
62    top_level_ctxt: SyntaxContext,
63
64    import_export_assign_config: TsImportExportAssignConfig,
65    ts_enum_is_mutable: bool,
66    verbatim_module_syntax: bool,
67    native_class_properties: bool,
68
69    is_lhs: bool,
70
71    ref_rewriter: Option<RefRewriter<ExportQuery>>,
72
73    decl_id_record: FxHashSet<Id>,
74    namespace_id: Option<Id>,
75    exported_binding: FxHashMap<Id, Option<Id>>,
76
77    enum_record: TsEnumRecord,
78    const_enum: FxHashSet<Id>,
79
80    var_list: Vec<Id>,
81    export_var_list: Vec<Id>,
82
83    in_class_prop: Vec<Id>,
84    in_class_prop_init: Vec<Box<Expr>>,
85}
86
87pub fn transform(
88    unresolved_mark: Mark,
89    top_level_mark: Mark,
90    import_export_assign_config: TsImportExportAssignConfig,
91    ts_enum_is_mutable: bool,
92    verbatim_module_syntax: bool,
93    native_class_properties: bool,
94) -> impl Pass {
95    visit_mut_pass(Transform {
96        unresolved_ctxt: SyntaxContext::empty().apply_mark(unresolved_mark),
97        top_level_ctxt: SyntaxContext::empty().apply_mark(top_level_mark),
98        import_export_assign_config,
99        ts_enum_is_mutable,
100        verbatim_module_syntax,
101        native_class_properties,
102        ..Default::default()
103    })
104}
105
106impl Visit for Transform {
107    noop_visit_type!();
108
109    fn visit_ts_enum_decl(&mut self, node: &TsEnumDecl) {
110        node.visit_children_with(self);
111
112        let TsEnumDecl {
113            declare,
114            is_const,
115            id,
116            members,
117            ..
118        } = node;
119
120        if *is_const {
121            self.const_enum.insert(id.to_id());
122        }
123
124        debug_assert!(!declare);
125
126        let mut default_init = 0.0.into();
127
128        for m in members {
129            let value = Self::transform_ts_enum_member(
130                m.clone(),
131                &id.to_id(),
132                &default_init,
133                &self.enum_record,
134                self.unresolved_ctxt,
135            );
136
137            default_init = value.inc();
138
139            let member_name = enum_member_id_atom(&m.id);
140            let key = TsEnumRecordKey {
141                enum_id: id.to_id(),
142                member_name: member_name.clone(),
143            };
144
145            self.enum_record.insert(key, value);
146        }
147    }
148
149    fn visit_ts_namespace_decl(&mut self, n: &TsNamespaceDecl) {
150        let id = n.id.to_id();
151        let namespace_id = self.namespace_id.replace(id);
152
153        n.body.visit_with(self);
154
155        self.namespace_id = namespace_id;
156    }
157
158    fn visit_ts_module_decl(&mut self, n: &TsModuleDecl) {
159        let id = n.id.to_id();
160
161        let namespace_id = self.namespace_id.replace(id);
162
163        n.body.visit_with(self);
164
165        self.namespace_id = namespace_id;
166    }
167
168    fn visit_export_decl(&mut self, node: &ExportDecl) {
169        node.visit_children_with(self);
170
171        match &node.decl {
172            Decl::Var(var_decl) => {
173                self.exported_binding.extend({
174                    find_pat_ids(&var_decl.decls)
175                        .into_iter()
176                        .zip(iter::repeat(self.namespace_id.clone()))
177                });
178            }
179            Decl::TsEnum(ts_enum_decl) => {
180                self.exported_binding
181                    .insert(ts_enum_decl.id.to_id(), self.namespace_id.clone());
182            }
183            Decl::TsModule(ts_module_decl) => {
184                self.exported_binding
185                    .insert(ts_module_decl.id.to_id(), self.namespace_id.clone());
186            }
187            _ => {}
188        }
189    }
190
191    fn visit_export_named_specifier(&mut self, node: &ExportNamedSpecifier) {
192        if let ModuleExportName::Ident(ident) = &node.orig {
193            self.exported_binding
194                .insert(ident.to_id(), self.namespace_id.clone());
195        }
196    }
197
198    fn visit_export_default_expr(&mut self, node: &ExportDefaultExpr) {
199        node.visit_children_with(self);
200
201        if let Expr::Ident(ident) = &*node.expr {
202            self.exported_binding
203                .insert(ident.to_id(), self.namespace_id.clone());
204        }
205    }
206
207    fn visit_ts_import_equals_decl(&mut self, node: &TsImportEqualsDecl) {
208        node.visit_children_with(self);
209
210        if node.is_export {
211            self.exported_binding
212                .insert(node.id.to_id(), self.namespace_id.clone());
213        }
214    }
215
216    fn visit_expr(&mut self, node: &Expr) {
217        maybe_grow_default(|| node.visit_children_with(self));
218    }
219}
220
221impl VisitMut for Transform {
222    noop_visit_mut_type!();
223
224    fn visit_mut_program(&mut self, node: &mut Program) {
225        node.visit_with(self);
226
227        if !self.exported_binding.is_empty() {
228            self.ref_rewriter = Some(RefRewriter {
229                query: ExportQuery {
230                    export_name: self.exported_binding.clone(),
231                },
232            });
233        }
234        node.visit_mut_children_with(self);
235    }
236
237    fn visit_mut_module(&mut self, node: &mut Module) {
238        self.visit_mut_for_ts_import_export(node);
239
240        node.visit_mut_children_with(self);
241
242        if !self.export_var_list.is_empty() {
243            let decls = self
244                .export_var_list
245                .take()
246                .into_iter()
247                .map(id_to_var_declarator)
248                .collect();
249
250            node.body.push(
251                ExportDecl {
252                    decl: VarDecl {
253                        decls,
254                        ..Default::default()
255                    }
256                    .into(),
257                    span: DUMMY_SP,
258                }
259                .into(),
260            )
261        }
262    }
263
264    fn visit_mut_module_items(&mut self, node: &mut Vec<ModuleItem>) {
265        let var_list = self.var_list.take();
266        node.retain_mut(|item| {
267            let is_empty = item.as_stmt().map(Stmt::is_empty).unwrap_or(false);
268            item.visit_mut_with(self);
269            // Remove those folded into Empty
270            is_empty || !item.as_stmt().map(Stmt::is_empty).unwrap_or(false)
271        });
272        let var_list = mem::replace(&mut self.var_list, var_list);
273
274        if !var_list.is_empty() {
275            let decls = var_list.into_iter().map(id_to_var_declarator).collect();
276
277            node.push(
278                VarDecl {
279                    decls,
280                    ..Default::default()
281                }
282                .into(),
283            )
284        }
285    }
286
287    fn visit_mut_class_members(&mut self, node: &mut Vec<ClassMember>) {
288        let prop_list = self.in_class_prop.take();
289        let init_list = self.in_class_prop_init.take();
290
291        node.visit_mut_children_with(self);
292        let prop_list = mem::replace(&mut self.in_class_prop, prop_list);
293        let init_list = mem::replace(&mut self.in_class_prop_init, init_list);
294
295        if !prop_list.is_empty() {
296            if self.native_class_properties {
297                self.reorder_class_prop_decls(node, prop_list, init_list);
298            } else {
299                self.reorder_class_prop_decls_and_inits(node, prop_list, init_list);
300            }
301        }
302    }
303
304    fn visit_mut_constructor(&mut self, node: &mut Constructor) {
305        node.params
306            .iter_mut()
307            .for_each(|param_or_ts_param_prop| match param_or_ts_param_prop {
308                ParamOrTsParamProp::TsParamProp(ts_param_prop) => {
309                    let TsParamProp {
310                        span,
311                        decorators,
312                        param,
313                        ..
314                    } = ts_param_prop;
315
316                    let (pat, expr, id) = match param {
317                        TsParamPropParam::Ident(binding_ident) => {
318                            let id = binding_ident.to_id();
319                            let prop_name = PropName::Ident(IdentName::from(&*binding_ident));
320                            let value = Ident::from(&*binding_ident).into();
321
322                            (
323                                binding_ident.clone().into(),
324                                assign_value_to_this_prop(prop_name, value),
325                                id,
326                            )
327                        }
328                        TsParamPropParam::Assign(assign_pat) => {
329                            let AssignPat { left, .. } = &assign_pat;
330
331                            let Pat::Ident(binding_ident) = &**left else {
332                                unreachable!("destructuring pattern inside TsParameterProperty");
333                            };
334
335                            let id = binding_ident.id.to_id();
336                            let prop_name = PropName::Ident(binding_ident.id.clone().into());
337                            let value = binding_ident.id.clone().into();
338
339                            (
340                                assign_pat.clone().into(),
341                                assign_value_to_this_prop(prop_name, value),
342                                id,
343                            )
344                        }
345                        #[cfg(swc_ast_unknown)]
346                        _ => panic!("unable to access unknown nodes"),
347                    };
348
349                    self.in_class_prop.push(id);
350                    self.in_class_prop_init.push(expr);
351
352                    *param_or_ts_param_prop = Param {
353                        span: *span,
354                        decorators: decorators.take(),
355                        pat,
356                    }
357                    .into();
358                }
359                ParamOrTsParamProp::Param(..) => {}
360                #[cfg(swc_ast_unknown)]
361                _ => panic!("unable to access unknown nodes"),
362            });
363
364        node.params.visit_mut_children_with(self);
365        node.body.visit_mut_children_with(self);
366    }
367
368    fn visit_mut_stmts(&mut self, node: &mut Vec<Stmt>) {
369        let var_list = self.var_list.take();
370        node.retain_mut(|stmt| {
371            let is_empty = stmt.is_empty();
372            stmt.visit_mut_with(self);
373            // Remove those folded into Empty
374            is_empty || !stmt.is_empty()
375        });
376        let var_list = mem::replace(&mut self.var_list, var_list);
377        if !var_list.is_empty() {
378            let decls = var_list.into_iter().map(id_to_var_declarator).collect();
379            node.push(
380                VarDecl {
381                    decls,
382                    ..Default::default()
383                }
384                .into(),
385            )
386        }
387    }
388
389    fn visit_mut_ts_namespace_decl(&mut self, node: &mut TsNamespaceDecl) {
390        let id = node.id.to_id();
391        let namespace_id = self.namespace_id.replace(id);
392
393        node.body.visit_mut_with(self);
394
395        self.namespace_id = namespace_id;
396    }
397
398    fn visit_mut_ts_module_decl(&mut self, node: &mut TsModuleDecl) {
399        let id = node.id.to_id();
400
401        let namespace_id = self.namespace_id.replace(id);
402
403        node.body.visit_mut_with(self);
404
405        self.namespace_id = namespace_id;
406    }
407
408    fn visit_mut_stmt(&mut self, node: &mut Stmt) {
409        node.visit_mut_children_with(self);
410
411        let Stmt::Decl(decl) = node else {
412            return;
413        };
414
415        match self.fold_decl(decl.take(), false) {
416            FoldedDecl::Decl(var_decl) => *decl = var_decl,
417            FoldedDecl::Expr(stmt) => *node = stmt,
418            FoldedDecl::Empty => {
419                node.take();
420            }
421        }
422    }
423
424    fn visit_mut_module_item(&mut self, node: &mut ModuleItem) {
425        node.visit_mut_children_with(self);
426
427        if let Some(ExportDecl { decl, .. }) = node
428            .as_mut_module_decl()
429            .and_then(ModuleDecl::as_mut_export_decl)
430        {
431            match self.fold_decl(decl.take(), true) {
432                FoldedDecl::Decl(var_decl) => *decl = var_decl,
433                FoldedDecl::Expr(stmt) => *node = stmt.into(),
434                FoldedDecl::Empty => {
435                    node.take();
436                }
437            }
438        }
439    }
440
441    fn visit_mut_export_default_decl(&mut self, node: &mut ExportDefaultDecl) {
442        node.visit_mut_children_with(self);
443
444        if let DefaultDecl::Class(ClassExpr {
445            ident: Some(ref ident),
446            ..
447        })
448        | DefaultDecl::Fn(FnExpr {
449            ident: Some(ref ident),
450            ..
451        }) = node.decl
452        {
453            self.decl_id_record.insert(ident.to_id());
454        }
455    }
456
457    fn visit_mut_export_decl(&mut self, node: &mut ExportDecl) {
458        if self.ref_rewriter.is_some() {
459            if let Decl::Var(var_decl) = &mut node.decl {
460                // visit inner directly to bypass visit_mut_var_declarator
461                for decl in var_decl.decls.iter_mut() {
462                    decl.name.visit_mut_with(self);
463                    decl.init.visit_mut_with(self);
464                }
465                return;
466            }
467        }
468        node.visit_mut_children_with(self);
469    }
470
471    fn visit_mut_prop(&mut self, node: &mut Prop) {
472        node.visit_mut_children_with(self);
473
474        if let Some(ref_rewriter) = self.ref_rewriter.as_mut() {
475            ref_rewriter.exit_prop(node);
476        }
477    }
478
479    fn visit_mut_var_declarator(&mut self, n: &mut VarDeclarator) {
480        let ref_rewriter = self.ref_rewriter.take();
481        n.name.visit_mut_with(self);
482        self.ref_rewriter = ref_rewriter;
483        n.init.visit_mut_with(self);
484    }
485
486    fn visit_mut_pat(&mut self, node: &mut Pat) {
487        node.visit_mut_children_with(self);
488
489        if let Some(ref_rewriter) = self.ref_rewriter.as_mut() {
490            ref_rewriter.exit_pat(node);
491        }
492    }
493
494    fn visit_mut_expr(&mut self, node: &mut Expr) {
495        self.enter_expr_for_inline_enum(node);
496
497        maybe_grow_default(|| node.visit_mut_children_with(self));
498
499        if let Some(ref_rewriter) = self.ref_rewriter.as_mut() {
500            ref_rewriter.exit_expr(node);
501        }
502    }
503
504    fn visit_mut_assign_expr(&mut self, n: &mut AssignExpr) {
505        let is_lhs = mem::replace(&mut self.is_lhs, true);
506        n.left.visit_mut_with(self);
507        self.is_lhs = false;
508        n.right.visit_mut_with(self);
509        self.is_lhs = is_lhs;
510    }
511
512    fn visit_mut_assign_pat(&mut self, n: &mut AssignPat) {
513        let is_lhs = mem::replace(&mut self.is_lhs, true);
514        n.left.visit_mut_with(self);
515        self.is_lhs = false;
516        n.right.visit_mut_with(self);
517        self.is_lhs = is_lhs;
518    }
519
520    fn visit_mut_update_expr(&mut self, n: &mut UpdateExpr) {
521        let is_lhs = mem::replace(&mut self.is_lhs, true);
522        n.arg.visit_mut_with(self);
523        self.is_lhs = is_lhs;
524    }
525
526    fn visit_mut_assign_pat_prop(&mut self, n: &mut AssignPatProp) {
527        n.key.visit_mut_with(self);
528        let is_lhs = mem::replace(&mut self.is_lhs, false);
529        n.value.visit_mut_with(self);
530        self.is_lhs = is_lhs;
531    }
532
533    fn visit_mut_member_expr(&mut self, n: &mut MemberExpr) {
534        let is_lhs = mem::replace(&mut self.is_lhs, false);
535        n.visit_mut_children_with(self);
536        self.is_lhs = is_lhs;
537    }
538
539    fn visit_mut_simple_assign_target(&mut self, node: &mut SimpleAssignTarget) {
540        node.visit_mut_children_with(self);
541
542        if let Some(ref_rewriter) = self.ref_rewriter.as_mut() {
543            ref_rewriter.exit_simple_assign_target(node);
544        }
545    }
546
547    fn visit_mut_jsx_element_name(&mut self, node: &mut JSXElementName) {
548        node.visit_mut_children_with(self);
549
550        if let Some(ref_rewriter) = self.ref_rewriter.as_mut() {
551            ref_rewriter.exit_jsx_element_name(node);
552        }
553    }
554
555    fn visit_mut_jsx_object(&mut self, node: &mut JSXObject) {
556        node.visit_mut_children_with(self);
557
558        if let Some(ref_rewriter) = self.ref_rewriter.as_mut() {
559            ref_rewriter.exit_jsx_object(node);
560        }
561    }
562
563    fn visit_mut_object_pat_prop(&mut self, n: &mut ObjectPatProp) {
564        n.visit_mut_children_with(self);
565
566        if let Some(ref_rewriter) = self.ref_rewriter.as_mut() {
567            ref_rewriter.exit_object_pat_prop(n);
568        }
569    }
570}
571
572enum FoldedDecl {
573    Empty,
574    Decl(Decl),
575    Expr(Stmt),
576}
577
578impl Transform {
579    fn fold_decl(&mut self, node: Decl, is_export: bool) -> FoldedDecl {
580        match node {
581            Decl::TsModule(ts_module) => {
582                let id = ts_module.id.to_id();
583
584                if self.decl_id_record.insert(id.clone()) {
585                    if is_export {
586                        if self.namespace_id.is_none() {
587                            self.export_var_list.push(id);
588                        }
589                    } else {
590                        self.var_list.push(id);
591                    }
592                }
593
594                self.transform_ts_module(*ts_module, is_export)
595            }
596            Decl::TsEnum(ts_enum) => {
597                let id = ts_enum.id.to_id();
598
599                let is_first = self.decl_id_record.insert(id);
600
601                self.transform_ts_enum(*ts_enum, is_first, is_export)
602            }
603            Decl::Fn(FnDecl { ref ident, .. }) | Decl::Class(ClassDecl { ref ident, .. }) => {
604                self.decl_id_record.insert(ident.to_id());
605                FoldedDecl::Decl(node)
606            }
607            decl => FoldedDecl::Decl(decl),
608        }
609    }
610}
611
612struct InitArg<'a> {
613    id: &'a Ident,
614    namespace_id: Option<&'a Id>,
615}
616
617impl InitArg<'_> {
618    // {}
619    fn empty() -> ExprOrSpread {
620        ExprOrSpread {
621            spread: None,
622            expr: ObjectLit::default().into(),
623        }
624    }
625
626    // N
627    fn get(&self) -> ExprOrSpread {
628        self.namespace_id
629            .cloned()
630            .map_or_else(
631                || -> Expr { self.id.clone().into() },
632                |namespace_id| namespace_id.make_member(self.id.clone().into()).into(),
633            )
634            .into()
635    }
636
637    // N || {}
638    fn or_empty(&self) -> ExprOrSpread {
639        let expr = self.namespace_id.cloned().map_or_else(
640            || -> Expr { self.id.clone().into() },
641            |namespace_id| namespace_id.make_member(self.id.clone().into()).into(),
642        );
643
644        let bin = BinExpr {
645            op: op!("||"),
646            left: expr.into(),
647            right: ObjectLit::default().into(),
648            ..Default::default()
649        };
650
651        ExprOrSpread {
652            spread: None,
653            expr: bin.into(),
654        }
655    }
656
657    // N || (N = {})
658    fn or_assign_empty(&self) -> ExprOrSpread {
659        let expr = self.namespace_id.cloned().map_or_else(
660            || -> Expr { self.id.clone().into() },
661            |namespace_id| namespace_id.make_member(self.id.clone().into()).into(),
662        );
663
664        let assign = self.namespace_id.cloned().map_or_else(
665            || ObjectLit::default().make_assign_to(op!("="), self.id.clone().into()),
666            |namespace_id| {
667                ObjectLit::default().make_assign_to(
668                    op!("="),
669                    namespace_id.make_member(self.id.clone().into()).into(),
670                )
671            },
672        );
673
674        let bin = BinExpr {
675            op: op!("||"),
676            left: expr.into(),
677            right: assign.into(),
678            ..Default::default()
679        };
680
681        ExprOrSpread {
682            spread: None,
683            expr: bin.into(),
684        }
685    }
686}
687
688impl Transform {
689    fn transform_ts_enum(
690        &mut self,
691        ts_enum: TsEnumDecl,
692        is_first: bool,
693        is_export: bool,
694    ) -> FoldedDecl {
695        let TsEnumDecl {
696            span,
697            declare,
698            is_const,
699            id,
700            members,
701        } = ts_enum;
702
703        debug_assert!(!declare);
704
705        let ts_enum_safe_remove = !self.verbatim_module_syntax
706            && is_const
707            && !is_export
708            && !self.exported_binding.contains_key(&id.to_id());
709
710        let member_list: Vec<_> = members
711            .into_iter()
712            .map(|m| {
713                let span = m.span;
714                let name = enum_member_id_atom(&m.id);
715
716                let key = TsEnumRecordKey {
717                    enum_id: id.to_id(),
718                    member_name: name.clone(),
719                };
720
721                let value = self.enum_record.get(&key).unwrap().clone();
722
723                EnumMemberItem { span, name, value }
724            })
725            .filter(|m| !ts_enum_safe_remove || !m.is_const())
726            .collect();
727
728        if member_list.is_empty() && is_const {
729            return FoldedDecl::Empty;
730        }
731
732        let stmts = member_list
733            .into_iter()
734            .filter(|item| !ts_enum_safe_remove || !item.is_const())
735            .map(|item| item.build_assign(&id.to_id()));
736
737        let namespace_export = self.namespace_id.is_some() && is_export;
738        let iife = !is_first || namespace_export;
739
740        let body = if !iife {
741            let return_stmt: Stmt = ReturnStmt {
742                arg: Some(id.clone().into()),
743                ..Default::default()
744            }
745            .into();
746
747            let stmts = stmts.chain(iter::once(return_stmt)).collect();
748
749            BlockStmt {
750                stmts,
751                ..Default::default()
752            }
753        } else {
754            BlockStmt {
755                stmts: stmts.collect(),
756                ..Default::default()
757            }
758        };
759
760        let var_kind = if is_export || id.ctxt == self.top_level_ctxt {
761            VarDeclKind::Var
762        } else {
763            VarDeclKind::Let
764        };
765
766        let init_arg = 'init_arg: {
767            let init_arg = InitArg {
768                id: &id,
769                namespace_id: self.namespace_id.as_ref().filter(|_| is_export),
770            };
771            if !is_first {
772                break 'init_arg init_arg.get();
773            }
774
775            if namespace_export {
776                break 'init_arg init_arg.or_assign_empty();
777            }
778
779            if is_export || var_kind == VarDeclKind::Let {
780                InitArg::empty()
781            } else {
782                init_arg.or_empty()
783            }
784        };
785
786        let expr = Factory::function(vec![id.clone().into()], body)
787            .as_call(if iife { DUMMY_SP } else { PURE_SP }, vec![init_arg]);
788
789        if iife {
790            FoldedDecl::Expr(
791                ExprStmt {
792                    span,
793                    expr: expr.into(),
794                }
795                .into(),
796            )
797        } else {
798            let var_declarator = VarDeclarator {
799                span,
800                name: id.into(),
801                init: Some(expr.into()),
802                definite: false,
803            };
804
805            FoldedDecl::Decl(
806                VarDecl {
807                    span,
808                    kind: var_kind,
809                    decls: vec![var_declarator],
810                    ..Default::default()
811                }
812                .into(),
813            )
814        }
815    }
816
817    fn transform_ts_enum_member(
818        member: TsEnumMember,
819        enum_id: &Id,
820        default_init: &TsEnumRecordValue,
821        record: &TsEnumRecord,
822        unresolved_ctxt: SyntaxContext,
823    ) -> TsEnumRecordValue {
824        member
825            .init
826            .map(|expr| {
827                EnumValueComputer {
828                    enum_id,
829                    unresolved_ctxt,
830                    record,
831                }
832                .compute(expr)
833            })
834            .filter(TsEnumRecordValue::has_value)
835            .unwrap_or_else(|| default_init.clone())
836    }
837}
838
839impl Transform {
840    fn transform_ts_module(&self, ts_module: TsModuleDecl, is_export: bool) -> FoldedDecl {
841        debug_assert!(!ts_module.declare);
842        debug_assert!(!ts_module.global);
843
844        let TsModuleDecl {
845            span,
846            id: TsModuleName::Ident(module_ident),
847            body: Some(body),
848            ..
849        } = ts_module
850        else {
851            unreachable!();
852        };
853
854        let body = Self::transform_ts_namespace_body(module_ident.to_id(), body);
855
856        let init_arg = InitArg {
857            id: &module_ident,
858            namespace_id: self.namespace_id.as_ref().filter(|_| is_export),
859        }
860        .or_assign_empty();
861
862        let expr = Factory::function(vec![module_ident.clone().into()], body)
863            .as_call(DUMMY_SP, vec![init_arg])
864            .into();
865
866        FoldedDecl::Expr(ExprStmt { span, expr }.into())
867    }
868
869    fn transform_ts_namespace_body(id: Id, body: TsNamespaceBody) -> BlockStmt {
870        let TsNamespaceDecl {
871            span,
872            declare,
873            global,
874            id: local_name,
875            body,
876        } = match body {
877            TsNamespaceBody::TsModuleBlock(ts_module_block) => {
878                return Self::transform_ts_module_block(id, ts_module_block);
879            }
880            TsNamespaceBody::TsNamespaceDecl(ts_namespace_decl) => ts_namespace_decl,
881            #[cfg(swc_ast_unknown)]
882            _ => panic!("unable to access unknown nodes"),
883        };
884
885        debug_assert!(!declare);
886        debug_assert!(!global);
887
888        let body = Self::transform_ts_namespace_body(local_name.to_id(), *body);
889
890        let init_arg = InitArg {
891            id: &local_name,
892            namespace_id: Some(&id.to_id()),
893        }
894        .or_assign_empty();
895
896        let expr =
897            Factory::function(vec![local_name.into()], body).as_call(DUMMY_SP, vec![init_arg]);
898
899        BlockStmt {
900            span,
901            stmts: vec![expr.into_stmt()],
902            ..Default::default()
903        }
904    }
905
906    /// Note:
907    /// All exported variable declarations are transformed into assignment to
908    /// the namespace. All references to the exported binding will be
909    /// replaced with qualified access to the namespace property.
910    ///
911    /// Exported function and class will be treat as const exported which is in
912    /// line with how the TypeScript compiler handles exports.
913    ///
914    /// Inline exported syntax should not be used with function which will lead
915    /// to issues with function hoisting.
916    ///
917    /// Input:
918    /// ```TypeScript
919    /// export const foo = init, { bar: baz = init } = init;
920    ///
921    /// export function a() {}
922    ///
923    /// export let b = init;
924    /// ```
925    ///
926    /// Output:
927    /// ```TypeScript
928    /// NS.foo = init, { bar: NS.baz = init } = init;
929    ///
930    /// function a() {}
931    /// NS.a = a;
932    ///
933    /// NS.b = init;
934    /// ```
935    fn transform_ts_module_block(id: Id, TsModuleBlock { span, body }: TsModuleBlock) -> BlockStmt {
936        let mut stmts = Vec::new();
937
938        for module_item in body {
939            match module_item {
940                ModuleItem::Stmt(stmt) => stmts.push(stmt),
941                ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
942                    decl, span, ..
943                })) => match decl {
944                    Decl::Class(ClassDecl { ref ident, .. })
945                    | Decl::Fn(FnDecl { ref ident, .. }) => {
946                        let assign_stmt = Self::assign_prop(&id, ident, span);
947                        stmts.push(decl.into());
948                        stmts.push(assign_stmt);
949                    }
950                    Decl::Var(var_decl) => {
951                        let mut exprs: Vec<Box<_>> = var_decl
952                            .decls
953                            .into_iter()
954                            .flat_map(
955                                |VarDeclarator {
956                                     span, name, init, ..
957                                 }| {
958                                    let right = init?;
959                                    let left = name.try_into().unwrap();
960
961                                    Some(
962                                        AssignExpr {
963                                            span,
964                                            left,
965                                            op: op!("="),
966                                            right,
967                                        }
968                                        .into(),
969                                    )
970                                },
971                            )
972                            .collect();
973
974                        if exprs.is_empty() {
975                            continue;
976                        }
977
978                        let expr = if exprs.len() == 1 {
979                            exprs.pop().unwrap()
980                        } else {
981                            SeqExpr {
982                                span: DUMMY_SP,
983                                exprs,
984                            }
985                            .into()
986                        };
987
988                        stmts.push(
989                            ExprStmt {
990                                span: var_decl.span,
991                                expr,
992                            }
993                            .into(),
994                        );
995                    }
996                    decl => unreachable!("{decl:?}"),
997                },
998                ModuleItem::ModuleDecl(ModuleDecl::TsImportEquals(decl)) => {
999                    match decl.module_ref {
1000                        TsModuleRef::TsEntityName(ts_entity_name) => {
1001                            let init = Self::ts_entity_name_to_expr(ts_entity_name);
1002
1003                            // export impot foo = bar.baz
1004                            let stmt = if decl.is_export {
1005                                // Foo.foo = bar.baz
1006                                let left = id.clone().make_member(decl.id.clone().into());
1007                                let expr = init.make_assign_to(op!("="), left.into());
1008
1009                                ExprStmt {
1010                                    span: decl.span,
1011                                    expr: expr.into(),
1012                                }
1013                                .into()
1014                            } else {
1015                                // const foo = bar.baz
1016                                let mut var_decl =
1017                                    init.into_var_decl(VarDeclKind::Const, decl.id.clone().into());
1018
1019                                var_decl.span = decl.span;
1020
1021                                var_decl.into()
1022                            };
1023
1024                            stmts.push(stmt);
1025                        }
1026                        TsModuleRef::TsExternalModuleRef(..) => {
1027                            // TS1147
1028                            if HANDLER.is_set() {
1029                                HANDLER.with(|handler| {
1030                                    handler
1031                                    .struct_span_err(
1032                                        decl.span,
1033                                        r#"Import declarations in a namespace cannot reference a module."#,
1034                                    )
1035                                    .emit();
1036                                });
1037                            }
1038                        }
1039                        #[cfg(swc_ast_unknown)]
1040                        _ => panic!("unable to access unknown nodes"),
1041                    }
1042                }
1043                item => {
1044                    if HANDLER.is_set() {
1045                        HANDLER.with(|handler| {
1046                            handler
1047                                .struct_span_err(
1048                                    item.span(),
1049                                    r#"ESM-style module declarations are not permitted in a namespace."#,
1050                                )
1051                                .emit();
1052                        });
1053                    }
1054                }
1055            }
1056        }
1057
1058        BlockStmt {
1059            span,
1060            stmts,
1061            ..Default::default()
1062        }
1063    }
1064}
1065
1066impl Transform {
1067    fn reorder_class_prop_decls_and_inits(
1068        &mut self,
1069        class_member_list: &mut Vec<ClassMember>,
1070        prop_list: Vec<Id>,
1071        mut init_list: Vec<Box<Expr>>,
1072    ) {
1073        let mut constructor = None;
1074        let mut cons_index = 0;
1075        for (index, member) in class_member_list.iter_mut().enumerate() {
1076            match member {
1077                ClassMember::Constructor(..) => {
1078                    let empty = EmptyStmt {
1079                        span: member.span(),
1080                    }
1081                    .into();
1082                    constructor = mem::replace(member, empty).constructor();
1083                    cons_index = index;
1084                }
1085                ClassMember::ClassProp(ClassProp {
1086                    key,
1087                    value: value @ Some(..),
1088                    is_static: false,
1089                    span,
1090                    ..
1091                }) => {
1092                    let key = match &mut *key {
1093                        PropName::Computed(ComputedPropName { span, expr })
1094                            if !is_literal(expr) =>
1095                        {
1096                            let ident = alias_ident_for(expr, "_key");
1097
1098                            self.var_list.push(ident.to_id());
1099
1100                            **expr = expr.take().make_assign_to(op!("="), ident.clone().into());
1101
1102                            PropName::Computed(ComputedPropName {
1103                                span: *span,
1104                                expr: ident.into(),
1105                            })
1106                        }
1107                        _ => key.clone(),
1108                    };
1109
1110                    let mut init = assign_value_to_this_prop(key, *value.take().unwrap());
1111                    init.set_span(*span);
1112
1113                    init_list.push(init);
1114                }
1115                ClassMember::PrivateProp(PrivateProp {
1116                    key,
1117                    value: value @ Some(..),
1118                    is_static: false,
1119                    span,
1120                    ..
1121                }) => {
1122                    let mut init =
1123                        assign_value_to_this_private_prop(key.clone(), *value.take().unwrap());
1124                    init.set_span(*span);
1125                    init_list.push(init);
1126                }
1127                _ => {}
1128            }
1129        }
1130
1131        if let Some(mut constructor) = constructor {
1132            inject_after_super(&mut constructor, init_list);
1133
1134            if let Some(c) = class_member_list
1135                .get_mut(cons_index)
1136                .filter(|m| m.is_empty() && m.span() == constructor.span)
1137            {
1138                *c = constructor.into();
1139            } else {
1140                class_member_list.push(constructor.into());
1141            }
1142        }
1143
1144        class_member_list.splice(
1145            0..0,
1146            prop_list
1147                .into_iter()
1148                .map(Ident::from)
1149                .map(PropName::from)
1150                .map(|key| ClassProp {
1151                    key,
1152                    ..Default::default()
1153                })
1154                .map(ClassMember::ClassProp),
1155        );
1156    }
1157
1158    fn reorder_class_prop_decls(
1159        &mut self,
1160        class_member_list: &mut Vec<ClassMember>,
1161        prop_list: Vec<Id>,
1162        init_list: Vec<Box<Expr>>,
1163    ) {
1164        if let Some(constructor) = class_member_list
1165            .iter_mut()
1166            .find_map(|m| m.as_mut_constructor())
1167        {
1168            inject_after_super(constructor, init_list);
1169        }
1170
1171        class_member_list.splice(
1172            0..0,
1173            prop_list
1174                .into_iter()
1175                .map(Ident::from)
1176                .map(PropName::from)
1177                .map(|key| ClassProp {
1178                    key,
1179                    ..Default::default()
1180                })
1181                .map(ClassMember::ClassProp),
1182        );
1183    }
1184}
1185
1186impl Transform {
1187    // Foo.x = x;
1188    fn assign_prop(id: &Id, prop: &Ident, span: Span) -> Stmt {
1189        let expr = prop
1190            .clone()
1191            .make_assign_to(op!("="), id.clone().make_member(prop.clone().into()).into());
1192
1193        ExprStmt {
1194            span,
1195            expr: expr.into(),
1196        }
1197        .into()
1198    }
1199
1200    fn ts_entity_name_to_expr(n: TsEntityName) -> Expr {
1201        match n {
1202            TsEntityName::Ident(i) => i.into(),
1203            TsEntityName::TsQualifiedName(q) => {
1204                let TsQualifiedName { span, left, right } = *q;
1205
1206                MemberExpr {
1207                    span,
1208                    obj: Box::new(Self::ts_entity_name_to_expr(left)),
1209                    prop: MemberProp::Ident(right),
1210                }
1211                .into()
1212            }
1213            #[cfg(swc_ast_unknown)]
1214            _ => panic!("unable to access unknown nodes"),
1215        }
1216    }
1217}
1218
1219impl Transform {
1220    fn enter_expr_for_inline_enum(&mut self, node: &mut Expr) {
1221        if self.is_lhs {
1222            return;
1223        }
1224
1225        if let Expr::Member(MemberExpr { obj, prop, .. }) = node {
1226            let Some(enum_id) = get_enum_id(obj) else {
1227                return;
1228            };
1229
1230            if self.ts_enum_is_mutable && !self.const_enum.contains(&enum_id) {
1231                return;
1232            }
1233
1234            let Some(member_name) = get_member_key(prop) else {
1235                return;
1236            };
1237
1238            let key = TsEnumRecordKey {
1239                enum_id,
1240                member_name,
1241            };
1242
1243            let Some(value) = self.enum_record.get(&key) else {
1244                return;
1245            };
1246
1247            if value.is_const() {
1248                *node = value.clone().into();
1249            }
1250        }
1251    }
1252
1253    fn visit_mut_for_ts_import_export(&mut self, node: &mut Module) {
1254        let mut should_inject = false;
1255        let create_require = private_ident!("_createRequire");
1256        let require = private_ident!("__require");
1257
1258        // NOTE: This is not correct!
1259        // However, all unresolved_span are used in TsImportExportAssignConfig::Classic
1260        // which is deprecated and not used in real world.
1261        let unresolved_ctxt = self.unresolved_ctxt;
1262        let cjs_require = quote_ident!(unresolved_ctxt, "require");
1263        let cjs_exports = quote_ident!(unresolved_ctxt, "exports");
1264
1265        let mut cjs_export_assign = None;
1266
1267        for module_item in &mut node.body {
1268            match module_item {
1269                ModuleItem::ModuleDecl(ModuleDecl::TsImportEquals(decl)) if !decl.is_type_only => {
1270                    debug_assert_ne!(
1271                        decl.id.ctxt, self.unresolved_ctxt,
1272                        "TsImportEquals has top-level context and it should not be identical to \
1273                         the unresolved mark"
1274                    );
1275                    debug_assert_eq!(decl.id.ctxt, self.top_level_ctxt);
1276
1277                    match &mut decl.module_ref {
1278                        // import foo = bar.baz
1279                        TsModuleRef::TsEntityName(ts_entity_name) => {
1280                            let init = Self::ts_entity_name_to_expr(ts_entity_name.clone());
1281
1282                            let mut var_decl =
1283                                init.into_var_decl(VarDeclKind::Const, decl.id.take().into());
1284
1285                            *module_item = if decl.is_export {
1286                                ExportDecl {
1287                                    span: decl.span,
1288                                    decl: var_decl.into(),
1289                                }
1290                                .into()
1291                            } else {
1292                                var_decl.span = decl.span;
1293                                var_decl.into()
1294                            };
1295                        }
1296                        // import foo = require("foo")
1297                        TsModuleRef::TsExternalModuleRef(TsExternalModuleRef { expr, .. }) => {
1298                            match self.import_export_assign_config {
1299                                TsImportExportAssignConfig::Classic => {
1300                                    // require("foo");
1301                                    let mut init = cjs_require
1302                                        .clone()
1303                                        .as_call(DUMMY_SP, vec![expr.take().as_arg()]);
1304
1305                                    // exports.foo = require("foo");
1306                                    if decl.is_export {
1307                                        init = init.make_assign_to(
1308                                            op!("="),
1309                                            cjs_exports
1310                                                .clone()
1311                                                .make_member(decl.id.clone().into())
1312                                                .into(),
1313                                        )
1314                                    }
1315
1316                                    // const foo = require("foo");
1317                                    // const foo = exports.foo = require("foo");
1318                                    let mut var_decl = init
1319                                        .into_var_decl(VarDeclKind::Const, decl.id.take().into());
1320                                    var_decl.span = decl.span;
1321
1322                                    *module_item = var_decl.into();
1323                                }
1324                                TsImportExportAssignConfig::Preserve => {}
1325                                TsImportExportAssignConfig::NodeNext => {
1326                                    should_inject = true;
1327
1328                                    let mut var_decl = require
1329                                        .clone()
1330                                        .as_call(DUMMY_SP, vec![expr.take().as_arg()])
1331                                        .into_var_decl(VarDeclKind::Const, decl.id.take().into());
1332
1333                                    *module_item = if decl.is_export {
1334                                        ExportDecl {
1335                                            span: decl.span,
1336                                            decl: var_decl.into(),
1337                                        }
1338                                        .into()
1339                                    } else {
1340                                        var_decl.span = decl.span;
1341                                        var_decl.into()
1342                                    };
1343                                }
1344                                TsImportExportAssignConfig::EsNext => {
1345                                    // TS1202
1346                                    if HANDLER.is_set() {
1347                                        HANDLER.with(|handler| {
1348                                            handler.struct_span_err(
1349                                                decl.span,
1350                                                r#"Import assignment cannot be used when targeting ECMAScript modules. Consider using `import * as ns from "mod"`, `import {a} from "mod"`, `import d from "mod"`, or another module format instead."#,
1351                                            )
1352                                            .emit();
1353                                        });
1354                                    }
1355                                }
1356                            }
1357                        }
1358                        #[cfg(swc_ast_unknown)]
1359                        _ => panic!("unable to access unknown nodes"),
1360                    }
1361                }
1362                ModuleItem::ModuleDecl(ModuleDecl::TsExportAssignment(..)) => {
1363                    let ts_export_assign = module_item
1364                        .take()
1365                        .module_decl()
1366                        .unwrap()
1367                        .ts_export_assignment()
1368                        .unwrap();
1369
1370                    cjs_export_assign.get_or_insert(ts_export_assign);
1371                }
1372                _ => {}
1373            }
1374        }
1375
1376        if should_inject {
1377            node.body.prepend_stmts([
1378                // import { createRequire } from "module";
1379                ImportDecl {
1380                    span: DUMMY_SP,
1381                    specifiers: vec![ImportNamedSpecifier {
1382                        span: DUMMY_SP,
1383                        local: create_require.clone(),
1384                        imported: Some(quote_ident!("createRequire").into()),
1385                        is_type_only: false,
1386                    }
1387                    .into()],
1388                    src: Box::new(quote_str!("module")),
1389                    type_only: false,
1390                    with: None,
1391                    phase: Default::default(),
1392                }
1393                .into(),
1394                // const __require = _createRequire(import.meta.url);
1395                create_require
1396                    .as_call(
1397                        DUMMY_SP,
1398                        vec![MetaPropExpr {
1399                            span: DUMMY_SP,
1400                            kind: MetaPropKind::ImportMeta,
1401                        }
1402                        .make_member(quote_ident!("url"))
1403                        .as_arg()],
1404                    )
1405                    .into_var_decl(VarDeclKind::Const, require.clone().into())
1406                    .into(),
1407            ]);
1408        }
1409
1410        if let Some(cjs_export_assign) = cjs_export_assign {
1411            match self.import_export_assign_config {
1412                TsImportExportAssignConfig::Classic => {
1413                    let TsExportAssignment { expr, span } = cjs_export_assign;
1414
1415                    let stmt = ExprStmt {
1416                        span,
1417                        expr: Box::new(
1418                            expr.make_assign_to(
1419                                op!("="),
1420                                member_expr!(unresolved_ctxt, Default::default(), module.exports)
1421                                    .into(),
1422                            ),
1423                        ),
1424                    }
1425                    .into();
1426
1427                    if let Some(item) = node
1428                        .body
1429                        .last_mut()
1430                        .and_then(ModuleItem::as_mut_stmt)
1431                        .filter(|stmt| stmt.is_empty())
1432                    {
1433                        *item = stmt;
1434                    } else {
1435                        node.body.push(stmt.into());
1436                    }
1437                }
1438                TsImportExportAssignConfig::Preserve => {
1439                    node.body.push(cjs_export_assign.into());
1440                }
1441                TsImportExportAssignConfig::NodeNext | TsImportExportAssignConfig::EsNext => {
1442                    // TS1203
1443                    if HANDLER.is_set() {
1444                        HANDLER.with(|handler| {
1445                            handler
1446                                .struct_span_err(
1447                                    cjs_export_assign.span,
1448                                    r#"Export assignment cannot be used when targeting ECMAScript modules. Consider using `export default` or another module format instead."#,
1449                                )
1450                                .emit()
1451                        });
1452                    }
1453                }
1454            }
1455        }
1456    }
1457}
1458
1459struct ExportQuery {
1460    export_name: FxHashMap<Id, Option<Id>>,
1461}
1462
1463impl QueryRef for ExportQuery {
1464    fn query_ref(&self, export_name: &Ident) -> Option<Box<Expr>> {
1465        self.export_name
1466            .get(&export_name.to_id())?
1467            .clone()
1468            .map(|namespace_id| namespace_id.make_member(export_name.clone().into()).into())
1469    }
1470
1471    fn query_lhs(&self, ident: &Ident) -> Option<Box<Expr>> {
1472        self.query_ref(ident)
1473    }
1474
1475    fn query_jsx(&self, ident: &Ident) -> Option<JSXElementName> {
1476        self.export_name
1477            .get(&ident.to_id())?
1478            .clone()
1479            .map(|namespace_id| {
1480                JSXMemberExpr {
1481                    span: DUMMY_SP,
1482                    obj: JSXObject::Ident(namespace_id.into()),
1483                    prop: ident.clone().into(),
1484                }
1485                .into()
1486            })
1487    }
1488}
1489
1490struct EnumMemberItem {
1491    span: Span,
1492    name: Atom,
1493    value: TsEnumRecordValue,
1494}
1495
1496impl EnumMemberItem {
1497    fn is_const(&self) -> bool {
1498        self.value.is_const()
1499    }
1500
1501    fn build_assign(self, enum_id: &Id) -> Stmt {
1502        let is_string = self.value.is_string();
1503        let value: Expr = self.value.into();
1504
1505        let inner_assign = value.make_assign_to(
1506            op!("="),
1507            Ident::from(enum_id.clone())
1508                .computed_member(self.name.clone())
1509                .into(),
1510        );
1511
1512        let outer_assign = if is_string {
1513            inner_assign
1514        } else {
1515            let value: Expr = self.name.clone().into();
1516
1517            value.make_assign_to(
1518                op!("="),
1519                Ident::from(enum_id.clone())
1520                    .computed_member(inner_assign)
1521                    .into(),
1522            )
1523        };
1524
1525        ExprStmt {
1526            span: self.span,
1527            expr: outer_assign.into(),
1528        }
1529        .into()
1530    }
1531}
1532
1533trait ModuleId {
1534    fn to_id(&self) -> Id;
1535}
1536
1537impl ModuleId for TsModuleName {
1538    fn to_id(&self) -> Id {
1539        self.as_ident()
1540            .expect("Only ambient modules can use quoted names.")
1541            .to_id()
1542    }
1543}
1544
1545fn id_to_var_declarator(id: Id) -> VarDeclarator {
1546    VarDeclarator {
1547        span: DUMMY_SP,
1548        name: id.into(),
1549        init: None,
1550        definite: false,
1551    }
1552}
1553
1554fn get_enum_id(e: &Expr) -> Option<Id> {
1555    if let Expr::Ident(ident) = e {
1556        Some(ident.to_id())
1557    } else {
1558        None
1559    }
1560}
1561
1562fn get_member_key(prop: &MemberProp) -> Option<Atom> {
1563    match prop {
1564        MemberProp::Ident(ident) => Some(ident.sym.clone()),
1565        MemberProp::Computed(ComputedPropName { expr, .. }) => match &**expr {
1566            Expr::Lit(Lit::Str(Str { value, .. })) => Some(value.to_atom_lossy().into_owned()),
1567            Expr::Tpl(Tpl { exprs, quasis, .. }) => match (exprs.len(), quasis.len()) {
1568                (0, 1) => quasis[0]
1569                    .cooked
1570                    .as_ref()
1571                    .map(|cooked| cooked.to_atom_lossy().into_owned())
1572                    .or_else(|| Some(quasis[0].raw.clone())),
1573                _ => None,
1574            },
1575            _ => None,
1576        },
1577        MemberProp::PrivateName(_) => None,
1578        #[cfg(swc_ast_unknown)]
1579        _ => panic!("unable to access unknown nodes"),
1580    }
1581}