diff --git a/crates/swc_ecma_transforms_typescript/src/transform.rs b/crates/swc_ecma_transforms_typescript/src/transform.rs index 2c39e2fd48e33..adbda74c4e30f 100644 --- a/crates/swc_ecma_transforms_typescript/src/transform.rs +++ b/crates/swc_ecma_transforms_typescript/src/transform.rs @@ -1,6 +1,6 @@ use std::{mem, vec}; -use swc_common::{collections::AHashSet, errors::HANDLER, util::take::Take, Mark, Span, DUMMY_SP}; +use swc_common::{collections::AHashSet, errors::HANDLER, util::take::Take, Mark, DUMMY_SP}; use swc_ecma_ast::*; use swc_ecma_utils::{ constructor::inject_after_super, member_expr, private_ident, quote_ident, quote_str, @@ -209,20 +209,23 @@ impl Transform { return; } - let mut decl_id = AHashSet::::default(); + let mut decl_id_record = AHashSet::::default(); for mut stmt in n.take().drain(..) { if let Some(id) = stmt.get_enum_or_module_id() { - let var_stmt = self - .add_var_for_enum_or_module_declaration(&id, &decl_id) - .map(Into::into); + let var_stmt = Self::add_var_for_enum_or_module_declaration( + &id, + &decl_id_record, + self.namespace_id.is_some(), + ) + .map(Into::into); n.extend(var_stmt); } let id = stmt.get_decl_id(); stmt = self.fold_stmt(stmt); - decl_id.extend(id); + decl_id_record.extend(id); n.push(stmt); } } @@ -244,11 +247,15 @@ impl Transform { return; } - let mut decl_id = AHashSet::::default(); + let mut decl_id_record = AHashSet::::default(); for mut module_item in n.take().drain(..) { if let Some(id) = &module_item.get_enum_or_module_id() { - let var_decl = self.add_var_for_enum_or_module_declaration(id, &decl_id); + let var_decl = Self::add_var_for_enum_or_module_declaration( + id, + &decl_id_record, + self.namespace_id.is_some(), + ); let var_stmt = if self.namespace_id.is_none() && matches!( @@ -275,7 +282,7 @@ impl Transform { let id = module_item.get_decl_id(); module_item = self.fold_module_item(module_item); - decl_id.extend(id); + decl_id_record.extend(id); n.push(module_item); } } @@ -295,141 +302,6 @@ impl Transform { } } - fn add_var_for_enum_or_module_declaration( - &self, - id: &Id, - decl_id: &AHashSet, - ) -> Option { - if decl_id.contains(id) { - return None; - } - - let kind = if self.namespace_id.is_none() { - VarDeclKind::Var - } else { - VarDeclKind::Let - }; - - let var_decl = VarDecl { - span: DUMMY_SP, - kind, - declare: false, - decls: vec![VarDeclarator { - span: DUMMY_SP, - name: id.clone().into(), - init: None, - definite: false, - }], - }; - - Some(var_decl) - } - - fn transform_ts_module(&self, ts_module: TsModuleDecl, is_export: bool) -> Stmt { - debug_assert!(!ts_module.declare); - debug_assert!(!ts_module.global); - - let TsModuleDecl { - span, - id: TsModuleName::Ident(module_ident), - body: Some(body), - .. - } = ts_module else { unreachable!() }; - - let body = Self::transform_ts_namespace_body(module_ident.to_id(), body); - - let expr = Self::wrap_namespace_or_enum_with_iife( - &self.namespace_id, - module_ident, - body, - is_export, - ); - - Stmt::Expr(ExprStmt { - span, - expr: expr.into(), - }) - } - - fn transform_ts_enum(&self, ts_enum: TsEnumDecl, is_export: bool) -> Stmt { - let TsEnumDecl { - span, - declare, - is_const: _, - id, - members: _, - } = ts_enum; - - debug_assert!(!declare); - - let body = BlockStmt { - span: DUMMY_SP, - stmts: vec![], - }; - - let expr = Self::wrap_namespace_or_enum_with_iife(&self.namespace_id, id, body, is_export); - - Stmt::Expr(ExprStmt { - span, - expr: expr.into(), - }) - } - - fn transform_ts_namespace_body( - container_name: Id, - ts_namespace_body: TsNamespaceBody, - ) -> BlockStmt { - let TsNamespaceDecl { - span, - declare, - global, - id: local_name, - body, - } = match ts_namespace_body { - TsNamespaceBody::TsModuleBlock(ts_module_block) => { - return Self::transform_ts_module_block(container_name, ts_module_block); - } - TsNamespaceBody::TsNamespaceDecl(ts_namespace_decl) => ts_namespace_decl, - }; - - debug_assert!(!declare); - debug_assert!(!global); - - let body = Self::transform_ts_namespace_body(local_name.to_id(), *body); - - let module_arg = - Self::get_namespace_or_enum_init_arg(&Some(container_name), local_name.clone(), true) - .into(); - - let expr = - Factory::function(vec![local_name.into()], body).as_call(DUMMY_SP, vec![module_arg]); - - BlockStmt { - span, - stmts: vec![expr.into_stmt()], - } - } - - fn transform_ts_module_block(id: Id, TsModuleBlock { span, body }: TsModuleBlock) -> BlockStmt { - let mut stmts = vec![]; - - for module_item in body { - match module_item { - ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(export_decl)) => { - let (export_stmt, decl_stmt) = - Self::transform_export_decl_in_ts_module_block(&id, export_decl); - - stmts.push(decl_stmt); - stmts.extend(export_stmt); - } - ModuleItem::Stmt(stmt) => stmts.push(stmt), - a => unreachable!("{a:?}"), - } - } - - BlockStmt { span, stmts } - } - fn visit_mut_module_for_import_require(&mut self, n: &mut Module) { if !n.body.iter().any(|item| matches!(item, ModuleItem::ModuleDecl(ModuleDecl::TsImportEquals(ts_import_equals)) if ts_import_equals.module_ref.is_ts_external_module_ref())) { return; @@ -528,7 +400,7 @@ impl Transform { unreachable!(); }; - let init = ts_entity_name_to_expr(ts_entity_name); + let init = Self::ts_entity_name_to_expr(ts_entity_name); let mut decl = init.into_var_decl(VarDeclKind::Const, id.into()); *module_item = if is_export { @@ -601,6 +473,105 @@ impl Transform { } } +impl Transform { + fn transform_ts_module(&self, ts_module: TsModuleDecl, is_export: bool) -> Stmt { + debug_assert!(!ts_module.declare); + debug_assert!(!ts_module.global); + + let TsModuleDecl { + span, + id: TsModuleName::Ident(module_ident), + body: Some(body), + .. + } = ts_module else { unreachable!() }; + + let body = Self::transform_ts_namespace_body(module_ident.to_id(), body); + + let expr = + Self::wrap_enum_or_module_with_iife(&self.namespace_id, module_ident, body, is_export); + + Stmt::Expr(ExprStmt { + span, + expr: expr.into(), + }) + } + + fn transform_ts_enum(&self, ts_enum: TsEnumDecl, is_export: bool) -> Stmt { + let TsEnumDecl { + span, + declare, + is_const: _, + id, + members: _, + } = ts_enum; + + debug_assert!(!declare); + + let body = BlockStmt { + span: DUMMY_SP, + stmts: vec![], + }; + + let expr = Self::wrap_enum_or_module_with_iife(&self.namespace_id, id, body, is_export); + + Stmt::Expr(ExprStmt { + span, + expr: expr.into(), + }) + } + + fn transform_ts_namespace_body(id: Id, body: TsNamespaceBody) -> BlockStmt { + let TsNamespaceDecl { + span, + declare, + global, + id: local_name, + body, + } = match body { + TsNamespaceBody::TsModuleBlock(ts_module_block) => { + return Self::transform_ts_module_block(id, ts_module_block); + } + TsNamespaceBody::TsNamespaceDecl(ts_namespace_decl) => ts_namespace_decl, + }; + + debug_assert!(!declare); + debug_assert!(!global); + + let body = Self::transform_ts_namespace_body(local_name.to_id(), *body); + + let init_arg = + Self::get_enum_or_module_init_arg(&Some(id), local_name.clone(), true).into(); + + let expr = + Factory::function(vec![local_name.into()], body).as_call(DUMMY_SP, vec![init_arg]); + + BlockStmt { + span, + stmts: vec![expr.into_stmt()], + } + } + + fn transform_ts_module_block(id: Id, TsModuleBlock { span, body }: TsModuleBlock) -> BlockStmt { + let mut stmts = vec![]; + + for module_item in body { + match module_item { + ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(export_decl)) => { + let (export_stmt, decl_stmt) = + Self::transform_export_decl_in_ts_module_block(&id, export_decl); + + stmts.push(decl_stmt); + stmts.extend(export_stmt); + } + ModuleItem::Stmt(stmt) => stmts.push(stmt), + a => unreachable!("{a:?}"), + } + } + + BlockStmt { span, stmts } + } +} + impl Transform { fn transform_export_decl_in_ts_module_block( id: &Id, @@ -623,11 +594,18 @@ impl Transform { debug_assert!(!var_decl.declare); if var_decl.kind == VarDeclKind::Const { + // Input: + // ```TypeScript + // export const foo = init; + // export const { foo: bar } = init; + // ``` + // Output: + // ```TypeScript // const foo = Foo.foo = init; // const { foo: bar } = { foo: Foo.bar } = init; - + // ``` var_decl.decls.iter_mut().for_each(|var_declarator| { - Self::visit_mut_const_var_declarator_with(var_declarator, id); + Self::export_const_var_with_init(var_declarator, id); }); return (None, var_decl.into()); @@ -642,7 +620,7 @@ impl Transform { (Some(visitor.build_export_stmt()), var_decl.into()) } - fn visit_mut_const_var_declarator_with(var_declarator: &mut VarDeclarator, id: &Id) { + fn export_const_var_with_init(var_declarator: &mut VarDeclarator, id: &Id) { let right = var_declarator .init .take() @@ -672,13 +650,43 @@ impl Transform { .into_stmt() } + fn add_var_for_enum_or_module_declaration( + id: &Id, + decl_id_record: &AHashSet, + in_namespace: bool, + ) -> Option { + if decl_id_record.contains(id) { + return None; + } + + let kind = if in_namespace { + VarDeclKind::Let + } else { + VarDeclKind::Var + }; + + let var_decl = VarDecl { + span: DUMMY_SP, + kind, + declare: false, + decls: vec![VarDeclarator { + span: DUMMY_SP, + name: id.clone().into(), + init: None, + definite: false, + }], + }; + + Some(var_decl) + } + /// Gets the argument used in a transformed enum or namespace call expr. /// /// Example: /// * `MyNamespace.MyEnum || (MyNamespace.MyEnum = {})` /// * or `MyEnum || (MyEnum = {})` - fn get_namespace_or_enum_init_arg( - namespace_id: &Option, + fn get_enum_or_module_init_arg( + container_name: &Option, ident: Ident, is_export: bool, ) -> Expr { @@ -686,8 +694,8 @@ impl Transform { let mut assign_left: Pat = ident.clone().into(); if is_export { - if let Some(namespace_id) = namespace_id.clone() { - left = Ident::from(namespace_id).make_member(ident); + if let Some(id) = container_name.clone() { + left = Ident::from(id).make_member(ident); assign_left = Pat::Expr(Box::new(left.clone())) } } @@ -715,26 +723,42 @@ impl Transform { .into() } - fn wrap_namespace_or_enum_with_iife( - namespace_id: &Option, + fn wrap_enum_or_module_with_iife( + container_name: &Option, ident: Ident, body: BlockStmt, is_export: bool, ) -> Expr { - let mut module_arg = - Self::get_namespace_or_enum_init_arg(namespace_id, ident.clone(), is_export).into(); + let mut init_arg = + Self::get_enum_or_module_init_arg(container_name, ident.clone(), is_export).into(); - if is_export && namespace_id.is_some() { - module_arg = AssignExpr { + if is_export && container_name.is_some() { + init_arg = AssignExpr { span: DUMMY_SP, op: op!("="), left: PatOrExpr::Pat(Box::new(Pat::Ident(ident.clone().into()))), - right: module_arg, + right: init_arg, } .into(); } - Factory::function(vec![ident.into()], body).as_call(DUMMY_SP, vec![module_arg.into()]) + Factory::function(vec![ident.into()], body).as_call(DUMMY_SP, vec![init_arg.into()]) + } + + fn ts_entity_name_to_expr(n: TsEntityName) -> Expr { + match n { + TsEntityName::Ident(i) => i.into(), + TsEntityName::TsQualifiedName(q) => { + let TsQualifiedName { left, right } = *q; + + MemberExpr { + span: DUMMY_SP, + obj: Box::new(Self::ts_entity_name_to_expr(left)), + prop: MemberProp::Ident(right), + } + .into() + } + } } } @@ -871,19 +895,3 @@ impl ExportedIdentCollector { } } } - -fn ts_entity_name_to_expr(n: TsEntityName) -> Expr { - match n { - TsEntityName::Ident(i) => i.into(), - TsEntityName::TsQualifiedName(q) => { - let TsQualifiedName { left, right } = *q; - - MemberExpr { - span: DUMMY_SP, - obj: Box::new(ts_entity_name_to_expr(left)), - prop: MemberProp::Ident(right), - } - .into() - } - } -}