diff --git a/src/lib.rs b/src/lib.rs index 012ef08..ab78c83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -297,6 +297,17 @@ testcase! { }"# } +testcase! { + handles_typescript_implied_export_default, + r#" satisfies MyType"#, + r#"import { template as template_UUID } from "@ember/template-compiler"; +export default template_UUID(`hello`, { + eval () { + return eval(arguments[0]); + } +}) satisfies MyType;"# +} + testcase! { handles_typescript_this, r#"function f(this: Context, ...args: unknown[]) { diff --git a/src/transform.rs b/src/transform.rs index 322d13e..d171af8 100644 --- a/src/transform.rs +++ b/src/transform.rs @@ -1,3 +1,4 @@ +use swc_common::Spanned; use swc_core::ecma::{ ast::{ BlockStmt, CallExpr, Callee, ClassMember, ContentTagExpression, ContentTagMember, Expr, @@ -10,6 +11,7 @@ use swc_core::ecma::{ use swc_ecma_ast::{ ContentTagContent, ExportDefaultExpr, ExprOrSpread, ModuleDecl, ModuleItem, Tpl, TplElement, + TsSatisfiesExpr, TsType, }; use swc_atoms::Atom; @@ -67,7 +69,11 @@ impl<'a> TransformVisitor<'a> { } fn escape_template_literal(input: &Atom) -> Atom { - input.replace("\\", "\\\\").replace("`", "\\`").replace("$", "\\$").into() + input + .replace("\\", "\\\\") + .replace("`", "\\`") + .replace("$", "\\$") + .into() } impl<'a> VisitMut for TransformVisitor<'a> { @@ -123,6 +129,18 @@ impl<'a> VisitMut for TransformVisitor<'a> { }, ))); self.set_found_it(); + } else if let Some(satisfies) = content_tag_satisfies_expression_statement(&item) { + items_updated.push(ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr( + ExportDefaultExpr { + span: satisfies.template.span, + expr: Box::new(Expr::TsSatisfies(TsSatisfiesExpr { + expr: Box::new(self.transform_tag_expression(&satisfies.template)), + type_ann: satisfies.ts_type.clone(), + span: satisfies.ts_type.span(), + })), + }, + ))); + self.set_found_it(); } else { items_updated.push(item); } @@ -145,6 +163,31 @@ fn content_tag_expression_statement(item: &ModuleItem) -> Option<&ContentTagExpr } } +struct TemplateSatisfies<'a> { + pub template: &'a ContentTagExpression, + pub ts_type: &'a Box, +} + +fn content_tag_satisfies_expression_statement(item: &ModuleItem) -> Option { + if let ModuleItem::Stmt(Stmt::Expr(ExprStmt { + expr: + box Expr::TsSatisfies(TsSatisfiesExpr { + expr: box Expr::ContentTagExpression(content_tag), + type_ann: ts_type, + .. + }), + .. + })) = item + { + Some(TemplateSatisfies { + template: content_tag, + ts_type, + }) + } else { + None + } +} + #[cfg(test)] use swc_core::ecma::visit::as_folder;