Skip to content

Commit

Permalink
feat: strip type only export
Browse files Browse the repository at this point in the history
  • Loading branch information
magic-akari committed May 18, 2023
1 parent c6a2c8c commit 35772ab
Show file tree
Hide file tree
Showing 4 changed files with 232 additions and 138 deletions.
2 changes: 1 addition & 1 deletion crates/swc_ecma_transforms_typescript/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ mod import_export_assign;
mod inline_enum;
mod macros;
pub mod strip;
mod strip_import;
mod strip_import_export;
mod strip_type;
mod transform;
pub mod typescript;
134 changes: 0 additions & 134 deletions crates/swc_ecma_transforms_typescript/src/strip_import.rs

This file was deleted.

228 changes: 228 additions & 0 deletions crates/swc_ecma_transforms_typescript/src/strip_import_export.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
use swc_common::collections::AHashSet;
use swc_ecma_ast::*;
use swc_ecma_visit::{noop_visit_type, Visit, VisitMut, VisitWith};

#[derive(Debug, Default)]
struct UsageCollect {
id_usage: AHashSet<Id>,
}

impl Visit for UsageCollect {
noop_visit_type!();

fn visit_ident(&mut self, n: &Ident) {
self.id_usage.insert(n.to_id());
}

fn visit_binding_ident(&mut self, _: &BindingIdent) {
// skip
}

fn visit_class(&mut self, n: &Class) {
// skip implements
n.decorators.visit_with(self);
n.body.visit_with(self);
n.super_class.visit_with(self);
}

fn visit_fn_decl(&mut self, n: &FnDecl) {
// skip function ident
n.function.visit_with(self);
}

fn visit_fn_expr(&mut self, n: &FnExpr) {
// skip function ident
n.function.visit_with(self);
}

fn visit_class_decl(&mut self, n: &ClassDecl) {
// skip class ident
n.class.visit_with(self);
}

fn visit_class_expr(&mut self, n: &ClassExpr) {
// skip class ident
n.class.visit_with(self);
}

fn visit_import_decl(&mut self, _: &ImportDecl) {
// skip
}

fn visit_ts_import_equals_decl(&mut self, n: &TsImportEqualsDecl) {
if n.is_type_only {
return;
}

// skip id visit

let TsModuleRef::TsEntityName(ts_entity_name) = &n.module_ref else {
return;
};

get_module_ident(ts_entity_name).visit_with(self);
}

fn visit_export_named_specifier(&mut self, n: &ExportNamedSpecifier) {
if n.is_type_only {
return;
}

n.orig.visit_with(self);
}

fn visit_named_export(&mut self, n: &NamedExport) {
if n.type_only || n.src.is_some() {
return;
}

n.visit_children_with(self);
}

fn visit_jsx_element_name(&mut self, n: &JSXElementName) {
if matches!(n, JSXElementName::Ident(i) if i.sym.starts_with(|c: char| c.is_ascii_lowercase()) )
{
return;
}

n.visit_children_with(self);
}
}

fn get_module_ident(ts_entity_name: &TsEntityName) -> &Ident {
match ts_entity_name {
TsEntityName::TsQualifiedName(ts_qualified_name) => {
get_module_ident(&ts_qualified_name.left)
}
TsEntityName::Ident(ident) => ident,
}
}

#[derive(Debug, Default)]
struct DeclareCollect {
id_declare: AHashSet<Id>,
}

// Only scan the top level of the module
impl Visit for DeclareCollect {
noop_visit_type!();

fn visit_binding_ident(&mut self, n: &BindingIdent) {
self.id_declare.insert(n.to_id());
}

fn visit_fn_decl(&mut self, n: &FnDecl) {
self.id_declare.insert(n.ident.to_id());
}

fn visit_class_decl(&mut self, n: &ClassDecl) {
self.id_declare.insert(n.ident.to_id());
}

fn visit_export_default_decl(&mut self, n: &ExportDefaultDecl) {
match &n.decl {
DefaultDecl::Class(ClassExpr {
ident: Some(ident), ..
}) => {
self.id_declare.insert(ident.to_id());
}
DefaultDecl::Fn(FnExpr {
ident: Some(ident), ..
}) => {
self.id_declare.insert(ident.to_id());
}
_ => {}
};
}

fn visit_ts_import_equals_decl(&mut self, n: &TsImportEqualsDecl) {
if n.is_type_only {
return;
}

self.id_declare.insert(n.id.to_id());
}

fn visit_ts_module_decl(&mut self, n: &TsModuleDecl) {
if n.global {
return;
}

if let TsModuleName::Ident(ident) = &n.id {
self.id_declare.insert(ident.to_id());
}
}

fn visit_stmts(&mut self, _: &[Stmt]) {
// skip
}

fn visit_block_stmt(&mut self, _: &BlockStmt) {
// skip
}
}

/// https://www.typescriptlang.org/tsconfig#importsNotUsedAsValues
///
/// The tsc drop import statements which only reference types by default.
pub(crate) struct StripImportExport;

impl VisitMut for StripImportExport {
fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
let mut usage_collect = UsageCollect::default();
let mut declare_collect = DeclareCollect::default();

n.visit_with(&mut usage_collect);
n.visit_with(&mut declare_collect);

let UsageCollect { id_usage } = usage_collect;
let DeclareCollect { id_declare } = declare_collect;

n.retain_mut(|module_item| match module_item {
ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl {
specifiers,
type_only: false,
..
})) if !specifiers.is_empty() => {
specifiers.retain(|import_specifier| match import_specifier {
ImportSpecifier::Named(named) => {
!named.is_type_only && id_usage.contains(&named.local.to_id())
}
ImportSpecifier::Default(default) => id_usage.contains(&default.local.to_id()),
ImportSpecifier::Namespace(namespace) => {
id_usage.contains(&namespace.local.to_id())
}
});

!specifiers.is_empty()
}
ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(NamedExport {
specifiers,
src: None,
type_only: false,
..
})) => {
specifiers.retain(|export_specifier| match export_specifier {
ExportSpecifier::Namespace(..) => true,
ExportSpecifier::Default(default) => {
id_declare.contains(&default.exported.to_id())
}
ExportSpecifier::Named(ExportNamedSpecifier {
orig: ModuleExportName::Ident(ident),
..
}) => id_declare.contains(&ident.to_id()),
ExportSpecifier::Named(ExportNamedSpecifier { is_type_only, .. }) => {
!is_type_only
}
});

!specifiers.is_empty()
}
_ => true,
});
}

fn visit_mut_script(&mut self, _: &mut Script) {
// skip
}
}
6 changes: 3 additions & 3 deletions crates/swc_ecma_transforms_typescript/src/typescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use swc_ecma_ast::*;
use swc_ecma_visit::{as_folder, Fold, VisitMut, VisitMutWith, VisitWith};

use crate::{
collect::Collect, strip_import::StripImport, strip_type::StripType, transform::transform,
TsImportExportAssignConfig,
collect::Collect, strip_import_export::StripImportExport, strip_type::StripType,
transform::transform, TsImportExportAssignConfig,
};

#[derive(Debug, Default, Serialize, Deserialize)]
Expand Down Expand Up @@ -51,7 +51,7 @@ pub(crate) struct TypeScript {
impl VisitMut for TypeScript {
fn visit_mut_program(&mut self, n: &mut Program) {
if !self.config.verbatim_module_syntax {
n.visit_mut_with(&mut StripImport::default());
n.visit_mut_with(&mut StripImportExport);
}

n.visit_mut_with(&mut StripType::default());
Expand Down

0 comments on commit 35772ab

Please sign in to comment.