diff --git a/.changeset/lucky-panthers-cough.md b/.changeset/lucky-panthers-cough.md new file mode 100644 index 000000000000..a90c959039cd --- /dev/null +++ b/.changeset/lucky-panthers-cough.md @@ -0,0 +1,6 @@ +--- +swc_ecma_transforms_typescript: patch +swc_fast_ts_strip: patch +--- + +Fix (TypeScript): Handle single type statement in if/for/while diff --git a/crates/swc_ecma_transforms_typescript/src/strip_type.rs b/crates/swc_ecma_transforms_typescript/src/strip_type.rs index a6d0a6429263..95f0a51b62ed 100644 --- a/crates/swc_ecma_transforms_typescript/src/strip_type.rs +++ b/crates/swc_ecma_transforms_typescript/src/strip_type.rs @@ -205,8 +205,16 @@ impl VisitMut for StripType { } fn visit_mut_stmts(&mut self, n: &mut Vec) { - n.retain(should_retain_stmt); n.visit_mut_children_with(self); + n.retain(|s| !s.is_empty()); + } + + fn visit_mut_stmt(&mut self, n: &mut Stmt) { + if should_retain_stmt(n) { + n.visit_mut_children_with(self); + } else if !n.is_empty() { + n.take(); + } } fn visit_mut_ts_import_equals_decl(&mut self, _: &mut TsImportEqualsDecl) { diff --git a/crates/swc_fast_ts_strip/src/lib.rs b/crates/swc_fast_ts_strip/src/lib.rs index ac851cb54b00..52774f924e93 100644 --- a/crates/swc_fast_ts_strip/src/lib.rs +++ b/crates/swc_fast_ts_strip/src/lib.rs @@ -11,12 +11,13 @@ use swc_common::{ BytePos, FileName, Mark, SourceMap, Span, Spanned, }; use swc_ecma_ast::{ - ArrowExpr, BindingIdent, Class, ClassDecl, ClassMethod, ClassProp, EsVersion, ExportAll, - ExportDecl, ExportSpecifier, FnDecl, ImportDecl, ImportSpecifier, NamedExport, Param, Pat, - Program, TsAsExpr, TsConstAssertion, TsEnumDecl, TsExportAssignment, TsImportEqualsDecl, - TsIndexSignature, TsInstantiation, TsInterfaceDecl, TsModuleDecl, TsModuleName, - TsNamespaceDecl, TsNonNullExpr, TsParamPropParam, TsSatisfiesExpr, TsTypeAliasDecl, TsTypeAnn, - TsTypeAssertion, TsTypeParamDecl, TsTypeParamInstantiation, VarDecl, + ArrowExpr, BindingIdent, Class, ClassDecl, ClassMethod, ClassProp, Decl, DoWhileStmt, + EsVersion, ExportAll, ExportDecl, ExportSpecifier, FnDecl, ForInStmt, ForOfStmt, ForStmt, + IfStmt, ImportDecl, ImportSpecifier, NamedExport, Param, Pat, Program, Stmt, TsAsExpr, + TsConstAssertion, TsEnumDecl, TsExportAssignment, TsImportEqualsDecl, TsIndexSignature, + TsInstantiation, TsInterfaceDecl, TsModuleDecl, TsModuleName, TsNamespaceDecl, TsNonNullExpr, + TsParamPropParam, TsSatisfiesExpr, TsTypeAliasDecl, TsTypeAnn, TsTypeAssertion, + TsTypeParamDecl, TsTypeParamInstantiation, VarDecl, WhileStmt, }; use swc_ecma_parser::{ lexer::Lexer, @@ -910,8 +911,76 @@ impl Visit for TsStrip { n.visit_children_with(self); } + + fn visit_if_stmt(&mut self, n: &IfStmt) { + n.visit_children_with(self); + + if n.cons.is_ts_stmt() { + self.add_overwrite(n.cons.span_lo(), b';'); + } + + if let Some(alt) = &n.alt { + if alt.is_ts_stmt() { + self.add_overwrite(alt.span_lo(), b';'); + } + } + } + + fn visit_for_stmt(&mut self, n: &ForStmt) { + n.visit_children_with(self); + + if n.body.is_ts_stmt() { + self.add_overwrite(n.body.span_lo(), b';'); + } + } + + fn visit_for_in_stmt(&mut self, n: &ForInStmt) { + n.visit_children_with(self); + + if n.body.is_ts_stmt() { + self.add_overwrite(n.body.span_lo(), b';'); + } + } + + fn visit_for_of_stmt(&mut self, n: &ForOfStmt) { + n.visit_children_with(self); + + if n.body.is_ts_stmt() { + self.add_overwrite(n.body.span_lo(), b';'); + } + } + + fn visit_while_stmt(&mut self, n: &WhileStmt) { + n.visit_children_with(self); + + if n.body.is_ts_stmt() { + self.add_overwrite(n.body.span_lo(), b';'); + } + } + + fn visit_do_while_stmt(&mut self, n: &DoWhileStmt) { + n.visit_children_with(self); + + if n.body.is_ts_stmt() { + self.add_overwrite(n.body.span_lo(), b';'); + } + } } +trait IsTsStmt { + fn is_ts_stmt(&self) -> bool; +} + +impl IsTsStmt for Stmt { + fn is_ts_stmt(&self) -> bool { + match self { + Stmt::Decl(Decl::TsInterface { .. } | Decl::TsTypeAlias(..)) => true, + Stmt::Decl(Decl::TsModule(n)) => n.declare || matches!(n.id, TsModuleName::Str(..)), + Stmt::Decl(Decl::TsEnum(e)) => e.declare, + _ => false, + } + } +} trait U8Helper { fn is_utf8_char_boundary(&self) -> bool; } diff --git a/crates/swc_fast_ts_strip/tests/fixture/single-ts-stmt.js b/crates/swc_fast_ts_strip/tests/fixture/single-ts-stmt.js new file mode 100644 index 000000000000..581cd582af5c --- /dev/null +++ b/crates/swc_fast_ts_strip/tests/fixture/single-ts-stmt.js @@ -0,0 +1,22 @@ +if (false) ; +console.log("Hello, World!"); + +if (false) { } +else ; +console.log("Hello, World!"); + +for (; false;) + ; +console.log("Hello, World!"); + +for (; false;) + ; +console.log("Hello, World!"); + +while (false) + ; +console.log("Hello, World!"); + +do + ; +while (false); diff --git a/crates/swc_fast_ts_strip/tests/fixture/single-ts-stmt.transform.js b/crates/swc_fast_ts_strip/tests/fixture/single-ts-stmt.transform.js new file mode 100644 index 000000000000..4df06fa23843 --- /dev/null +++ b/crates/swc_fast_ts_strip/tests/fixture/single-ts-stmt.transform.js @@ -0,0 +1,12 @@ +if (false) ; +console.log("Hello, World!"); +if (false) {} else ; +console.log("Hello, World!"); +for(; false;); +console.log("Hello, World!"); +for(; false;); +console.log("Hello, World!"); +while(false); +console.log("Hello, World!"); +do ; +while (false) diff --git a/crates/swc_fast_ts_strip/tests/fixture/single-ts-stmt.ts b/crates/swc_fast_ts_strip/tests/fixture/single-ts-stmt.ts new file mode 100644 index 000000000000..6a1b50d9bd72 --- /dev/null +++ b/crates/swc_fast_ts_strip/tests/fixture/single-ts-stmt.ts @@ -0,0 +1,22 @@ +if (false) type Foo = string +console.log("Hello, World!"); + +if (false) { } +else type Bar = string +console.log("Hello, World!"); + +for (; false;) + interface X { } +console.log("Hello, World!"); + +for (; false;) + interface X { } +console.log("Hello, World!"); + +while (false) + type Baz = string +console.log("Hello, World!"); + +do + interface X { } +while (false);