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#[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 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 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 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 fn empty() -> ExprOrSpread {
620 ExprOrSpread {
621 spread: None,
622 expr: ObjectLit::default().into(),
623 }
624 }
625
626 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 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 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 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 let stmt = if decl.is_export {
1005 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 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 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 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 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 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 TsModuleRef::TsExternalModuleRef(TsExternalModuleRef { expr, .. }) => {
1298 match self.import_export_assign_config {
1299 TsImportExportAssignConfig::Classic => {
1300 let mut init = cjs_require
1302 .clone()
1303 .as_call(DUMMY_SP, vec![expr.take().as_arg()]);
1304
1305 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 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 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 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 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 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}