From 1bcc8eb99f44bc1d9586d76a108604b2ebd83234 Mon Sep 17 00:00:00 2001 From: magic-akari Date: Thu, 6 Apr 2023 16:48:48 +0800 Subject: [PATCH] strip type --- .../swc_ecma_transforms_typescript/src/lib.rs | 2 +- .../src/strip_type.rs | 290 +++++++++++++++++- .../src/typescript.rs | 8 +- .../tests/typescript.rs | 51 +++ 4 files changed, 345 insertions(+), 6 deletions(-) create mode 100644 crates/swc_ecma_transforms_typescript/tests/typescript.rs diff --git a/crates/swc_ecma_transforms_typescript/src/lib.rs b/crates/swc_ecma_transforms_typescript/src/lib.rs index c7f1a0b9d1857..6b8353ffb4578 100644 --- a/crates/swc_ecma_transforms_typescript/src/lib.rs +++ b/crates/swc_ecma_transforms_typescript/src/lib.rs @@ -9,4 +9,4 @@ pub mod strip; mod strip_import; mod strip_type; mod transform; -mod typescript; +pub mod typescript; diff --git a/crates/swc_ecma_transforms_typescript/src/strip_type.rs b/crates/swc_ecma_transforms_typescript/src/strip_type.rs index 335a0462d6e9d..98e07c01c7130 100644 --- a/crates/swc_ecma_transforms_typescript/src/strip_type.rs +++ b/crates/swc_ecma_transforms_typescript/src/strip_type.rs @@ -1,7 +1,291 @@ -use swc_ecma_visit::VisitMut; +use swc_common::{collections::AHashSet, util::take::Take}; +use swc_ecma_ast::*; +use swc_ecma_visit::{VisitMut, VisitMutWith}; /// This Module will strip all types/generics/interface/declares /// and type import/export -pub(crate) struct StripType; +#[derive(Default)] +pub(crate) struct StripType { + type_id: AHashSet, +} -impl VisitMut for StripType {} +impl VisitMut for StripType { + fn visit_mut_accessibility(&mut self, _: &mut Accessibility) { + unreachable!() + } + + fn visit_mut_true_plus_minus(&mut self, _: &mut TruePlusMinus) { + unreachable!() + } + + fn visit_mut_ts_array_type(&mut self, _: &mut TsArrayType) { + unreachable!() + } + + fn visit_mut_ts_call_signature_decl(&mut self, _: &mut TsCallSignatureDecl) { + unreachable!() + } + + fn visit_mut_ts_conditional_type(&mut self, _: &mut TsConditionalType) { + unreachable!() + } + + fn visit_mut_ts_construct_signature_decl(&mut self, _: &mut TsConstructSignatureDecl) { + unreachable!() + } + + fn visit_mut_ts_constructor_type(&mut self, _: &mut TsConstructorType) { + unreachable!() + } + + fn visit_mut_ts_entity_name(&mut self, _: &mut TsEntityName) { + unreachable!() + } + + fn visit_mut_ts_external_module_ref(&mut self, _: &mut TsExternalModuleRef) { + unreachable!() + } + + fn visit_mut_ts_fn_or_constructor_type(&mut self, _: &mut TsFnOrConstructorType) { + unreachable!() + } + + fn visit_mut_ts_fn_param(&mut self, _: &mut TsFnParam) { + unreachable!() + } + + fn visit_mut_ts_fn_type(&mut self, _: &mut TsFnType) { + unreachable!() + } + + fn visit_mut_ts_import_type(&mut self, _: &mut TsImportType) { + unreachable!() + } + + fn visit_mut_ts_index_signature(&mut self, _: &mut TsIndexSignature) { + unreachable!() + } + + fn visit_mut_ts_indexed_access_type(&mut self, _: &mut TsIndexedAccessType) { + unreachable!() + } + + fn visit_mut_ts_infer_type(&mut self, _: &mut TsInferType) { + unreachable!() + } + + fn visit_mut_ts_interface_body(&mut self, _: &mut TsInterfaceBody) { + unreachable!() + } + + fn visit_mut_ts_interface_decl(&mut self, _: &mut TsInterfaceDecl) { + unreachable!() + } + + fn visit_mut_ts_intersection_type(&mut self, _: &mut TsIntersectionType) { + unreachable!() + } + + fn visit_mut_ts_keyword_type(&mut self, _: &mut TsKeywordType) { + unreachable!() + } + + fn visit_mut_ts_keyword_type_kind(&mut self, _: &mut TsKeywordTypeKind) { + unreachable!() + } + + fn visit_mut_ts_mapped_type(&mut self, _: &mut TsMappedType) { + unreachable!() + } + + fn visit_mut_ts_method_signature(&mut self, _: &mut TsMethodSignature) { + unreachable!() + } + + fn visit_mut_ts_module_ref(&mut self, _: &mut TsModuleRef) { + unreachable!() + } + + fn visit_mut_ts_optional_type(&mut self, _: &mut TsOptionalType) { + unreachable!() + } + + fn visit_mut_ts_parenthesized_type(&mut self, _: &mut TsParenthesizedType) { + unreachable!() + } + + fn visit_mut_ts_property_signature(&mut self, _: &mut TsPropertySignature) { + unreachable!() + } + + fn visit_mut_ts_qualified_name(&mut self, _: &mut TsQualifiedName) { + unreachable!() + } + + fn visit_mut_ts_rest_type(&mut self, _: &mut TsRestType) { + unreachable!() + } + + fn visit_mut_ts_this_type(&mut self, _: &mut TsThisType) { + unreachable!() + } + + fn visit_mut_ts_this_type_or_ident(&mut self, _: &mut TsThisTypeOrIdent) { + unreachable!() + } + + fn visit_mut_ts_tuple_type(&mut self, _: &mut TsTupleType) { + unreachable!() + } + + fn visit_mut_ts_type(&mut self, _: &mut TsType) { + unreachable!() + } + + fn visit_mut_ts_type_alias_decl(&mut self, _: &mut TsTypeAliasDecl) { + unreachable!() + } + + fn visit_mut_ts_type_ann(&mut self, _: &mut TsTypeAnn) { + unreachable!() + } + + fn visit_mut_ts_type_element(&mut self, _: &mut TsTypeElement) { + unreachable!() + } + + fn visit_mut_ts_type_lit(&mut self, _: &mut TsTypeLit) { + unreachable!() + } + + fn visit_mut_ts_type_operator(&mut self, _: &mut TsTypeOperator) { + unreachable!() + } + + fn visit_mut_ts_type_operator_op(&mut self, _: &mut TsTypeOperatorOp) { + unreachable!() + } + + fn visit_mut_ts_type_param(&mut self, _: &mut TsTypeParam) { + unreachable!() + } + + fn visit_mut_ts_type_param_decl(&mut self, _: &mut TsTypeParamDecl) { + unreachable!() + } + + fn visit_mut_ts_type_param_instantiation(&mut self, _: &mut TsTypeParamInstantiation) { + unreachable!() + } + + fn visit_mut_ts_type_predicate(&mut self, _: &mut TsTypePredicate) { + unreachable!() + } + + fn visit_mut_ts_type_query(&mut self, _: &mut TsTypeQuery) { + unreachable!() + } + + fn visit_mut_ts_type_query_expr(&mut self, _: &mut TsTypeQueryExpr) { + unreachable!() + } + + fn visit_mut_ts_type_ref(&mut self, _: &mut TsTypeRef) { + unreachable!() + } + + fn visit_mut_ts_union_or_intersection_type(&mut self, _: &mut TsUnionOrIntersectionType) { + unreachable!() + } + + fn visit_mut_ts_union_type(&mut self, _: &mut TsUnionType) { + unreachable!() + } + + fn visit_mut_module_items(&mut self, n: &mut Vec) { + n.retain(|module_item| match module_item { + ModuleItem::ModuleDecl(ModuleDecl::Import(import_decl)) => !import_decl.type_only, + ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(export_decl)) => { + match &export_decl.decl { + Decl::Class(class) => !class.declare, + Decl::Fn(r#fn) => !r#fn.declare, + Decl::Var(var) => !var.declare, + Decl::TsInterface(..) => false, + Decl::TsTypeAlias(..) => false, + Decl::TsEnum(ts_enum) => !ts_enum.declare, + Decl::TsModule(ts_module) => !ts_module.declare, + } + } + ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(export_named)) => { + !export_named.type_only + } + ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(export_default_decl)) => { + !export_default_decl.decl.is_ts_interface_decl() + } + ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr(..)) => true, + ModuleItem::ModuleDecl(ModuleDecl::ExportAll(export_all)) => !export_all.type_only, + ModuleItem::ModuleDecl(ModuleDecl::TsImportEquals(ts_import_equals)) => { + !ts_import_equals.is_type_only + } + ModuleItem::ModuleDecl(ModuleDecl::TsExportAssignment(..)) => true, + ModuleItem::ModuleDecl(ModuleDecl::TsNamespaceExport(..)) => { + // https://www.typescriptlang.org/docs/handbook/modules.html#umd-modules + // `TsNamespaceExport` is only used in UMD dts + false + } + ModuleItem::Stmt(..) => true, + }); + + n.visit_mut_children_with(self); + + n.retain(|module_item| !matches!(module_item, ModuleItem::Stmt(Stmt::Empty(empty)) if empty.span.is_dummy())) + } + + fn visit_mut_stmts(&mut self, n: &mut Vec) { + n.visit_mut_children_with(self); + + n.retain(|stmt| !matches!(stmt, Stmt::Empty(empty) if empty.span.is_dummy())); + } + + fn visit_mut_stmt(&mut self, n: &mut Stmt) { + let retain = match n { + Stmt::Block(..) + | Stmt::Empty(..) + | Stmt::Debugger(..) + | Stmt::With(..) + | Stmt::Return(..) + | Stmt::Labeled(..) + | Stmt::Break(..) + | Stmt::Continue(..) + | Stmt::If(..) + | Stmt::Switch(..) + | Stmt::Throw(..) + | Stmt::Try(..) + | Stmt::While(..) + | Stmt::DoWhile(..) + | Stmt::For(..) + | Stmt::ForIn(..) + | Stmt::ForOf(..) + | Stmt::Expr(..) => true, + Stmt::Decl(decl) => match decl { + Decl::Class(class) => !class.declare, + Decl::Fn(r#fn) => !r#fn.declare, + Decl::Var(var) => !var.declare, + Decl::TsInterface(ts_interface) => { + self.type_id.insert(ts_interface.id.to_id()); + false + } + Decl::TsTypeAlias(ts_type_alias) => { + self.type_id.insert(ts_type_alias.id.to_id()); + false + } + Decl::TsEnum(ts_enum) => !ts_enum.declare, + Decl::TsModule(ts_module) => !ts_module.declare, + }, + }; + + if !retain { + n.take(); + } + } +} diff --git a/crates/swc_ecma_transforms_typescript/src/typescript.rs b/crates/swc_ecma_transforms_typescript/src/typescript.rs index c224690084b7b..2fc2e7f0c78b9 100644 --- a/crates/swc_ecma_transforms_typescript/src/typescript.rs +++ b/crates/swc_ecma_transforms_typescript/src/typescript.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; use swc_ecma_ast::*; -use swc_ecma_visit::{VisitMut, VisitMutWith, VisitWith}; +use swc_ecma_visit::{as_folder, Fold, VisitMut, VisitMutWith, VisitWith}; use crate::{ collect::Collect, strip_import::StripImport, strip_type::StripType, transform::Transform, @@ -35,6 +35,10 @@ pub struct Config { pub import_export_assign_config: TsImportExportAssignConfig, } +pub fn typescript(config: Config) -> impl Fold + VisitMut { + as_folder(TypeScript { config }) +} + pub(crate) struct TypeScript { pub config: Config, } @@ -44,7 +48,7 @@ impl VisitMut for TypeScript { // https://tc39.es/proposal-type-annotations // It's possible to keep type in JavaScript. // We may skip this step or transform TS type to JS type in the future. - n.visit_mut_with(&mut StripType); + n.visit_mut_with(&mut StripType::default()); if !self.config.verbatim_module_syntax { n.visit_with(&mut Collect); diff --git a/crates/swc_ecma_transforms_typescript/tests/typescript.rs b/crates/swc_ecma_transforms_typescript/tests/typescript.rs new file mode 100644 index 0000000000000..a73a23aab50c3 --- /dev/null +++ b/crates/swc_ecma_transforms_typescript/tests/typescript.rs @@ -0,0 +1,51 @@ +use std::path::PathBuf; + +use swc_common::{chain, pass::Optional, Mark}; +use swc_ecma_parser::{Syntax, TsConfig}; +use swc_ecma_transforms_base::resolver; +use swc_ecma_transforms_proposal::decorators; +use swc_ecma_transforms_testing::test_fixture; +use swc_ecma_transforms_typescript::typescript; +use swc_ecma_visit::Fold; + +fn tr() -> impl Fold { + tr_config(None, None) +} + +fn tr_config( + config: Option, + decorators_config: Option, +) -> impl Fold { + let unresolved_mark = Mark::new(); + let top_level_mark = Mark::new(); + let has_decorators = decorators_config.is_some(); + let config = config.unwrap_or_else(|| typescript::Config { + no_empty_export: true, + ..Default::default() + }); + + chain!( + Optional::new( + decorators(decorators_config.unwrap_or_default()), + has_decorators, + ), + resolver(unresolved_mark, top_level_mark, true), + typescript::typescript(config), + ) +} + +#[testing::fixture("tests/fixture/**/input.ts")] +#[testing::fixture("tests/fixture/**/input.tsx")] +fn typescript_fixture_new(input: PathBuf) { + let output = input.with_file_name("output.js"); + test_fixture( + Syntax::Typescript(TsConfig { + tsx: input.to_string_lossy().ends_with(".tsx"), + ..Default::default() + }), + &|_t| tr(), + &input, + &output, + Default::default(), + ); +}