Currently, the decorator code generated by swc has side effects, which will cause classes with decorators to
Unable to support Tree shaking (I use swc with rollup).
In fact, our decorators are side-effect-free in most cases.
Is it possible to add decorator's side effect configuration in swc?
I try to write a wasm plugin that converts decorated classes into closures with /* @__PURE__ */ annotations.
But I can't get the decorators of the class in the plugin (the decorators are transformed before the wasm plugin is executed, it only works properly when disableBuiltinTransformsForInternalTesting = true). Is there any way to make it work?
The following is the source code of the plugin:
pub struct TransformVisitor<C>
where
C: Comments,
{
config: Config,
comments: Option<C>,
}
impl<C> TransformVisitor<C>
where
C: Comments,
{
fn check_decorators(&mut self, decorators: &Vec<Decorator>, out: &mut usize) -> bool {
let len = decorators.len();
if len == 0 {
return true;
}
*out += len;
// TODO check side-effect decorators by config
return true;
}
fn check_class(&mut self, cls: &Class) -> bool {
let mut count: usize = 0;
if !self.check_decorators(&cls.decorators, &mut count) {
return false;
}
for item in cls.body.iter() {
if let Some(prop) = item.as_class_prop() {
if !self.check_decorators(&prop.decorators, &mut count) {
return false;
}
} else if let Some(prop) = item.as_private_prop() {
if !self.check_decorators(&prop.decorators, &mut count) {
return false;
}
} else if let Some(method) = item.as_method() {
if !self.check_decorators(&method.function.decorators, &mut count) {
return false;
}
} else if let Some(method) = item.as_private_method() {
if !self.check_decorators(&method.function.decorators, &mut count) {
return false;
}
} else if let Some(accessor) = item.as_auto_accessor() {
if !self.check_decorators(&accessor.decorators, &mut count) {
return false;
}
}
}
return count > 0;
}
/**
* Input:
* cls ident
* | |
* class [name] {}
* Output: const name = (function(){ cls_decl })()
*/
fn wrap_class_expr(&mut self, cls: &Class, ident: &Ident) -> Box<Expr> {
let span = &cls.span;
self.comments.add_pure_comment(span.lo);
return Box::new(Expr::Call(CallExpr {
span: *span,
args: Vec::new(),
type_args: None,
callee: Callee::Expr(Box::new(Expr::Paren(ParenExpr {
span: *span,
expr: Box::new(Expr::Fn(FnExpr {
ident: None,
function: Box::new(Function {
params: Vec::new(),
decorators: Vec::new(),
span: *span,
body: Some(BlockStmt {
span: *span,
stmts: vec![
Stmt::Decl(Decl::Class(ClassDecl {
ident: ident.clone(),
declare: false,
class: Box::new(cls.clone()),
})),
Stmt::Return(ReturnStmt {
span: *span,
arg: Some(Box::new(Expr::Ident(ident.clone()))),
}),
],
}),
is_generator: false,
is_async: false,
type_params: None,
return_type: None,
}),
})),
}))),
}));
}
/**
* Input:
* cls_decl ident
* | |
* class name {}
* Output: const name = (function(){ cls_decl })()
*/
fn wrap_var_class(&mut self, cls_decl: &ClassDecl) -> Box<VarDecl> {
let class = &cls_decl.class;
let ident = &cls_decl.ident;
return Box::new(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Const,
declare: false,
decls: vec![VarDeclarator {
span: DUMMY_SP,
definite: false,
name: Pat::Ident(BindingIdent {
id: ident.clone(),
type_ann: None,
}),
init: Some(self.wrap_class_expr(class, ident)),
}],
});
}
fn create_class_ident(&mut self, span: &Span) -> Ident {
return Ident {
span: span.clone(),
sym: Atom::new("Anonymous"),
optional: false,
};
}
}
impl<C> VisitMut for TransformVisitor<C>
where
C: Comments,
{
fn visit_mut_var_declarator(&mut self, var: &mut VarDeclarator) {
if let Some(cls_expr) = var.init.as_ref().and_then(|expr| expr.as_class()) {
if self.check_class(&cls_expr.class) {
/*
* Input:
* var_decl cls_expr ident
* | | |
* var name = @x class [name] {}
* Output: var name = (function(){ cls_expr })()
*
* Input:
* var_decl cls_expr ident
* | | |
* export var name = @x class [name] {}
* Output: export var name = (function(){ cls_expr })()
*/
let ident = &cls_expr
.ident
.clone()
.or(var.name.as_ident().and_then(|id| Some(id.id.clone())))
.or(Some(self.create_class_ident(&cls_expr.span())))
.unwrap();
var.init = Some(self.wrap_class_expr(&cls_expr.class, ident));
}
}
}
fn visit_mut_module_item(&mut self, item: &mut ModuleItem) {
if let Some(decl) = item.as_mut_module_decl() {
if let Some(exp_decl) = decl.as_export_default_decl() {
if let Some(cls_expr) = exp_decl.decl.as_class() {
if self.check_class(&cls_expr.class) {
/*
* Input:
* exp_decl cls_expr ident
* | | |
* export default class [name] {}
* Output: export default (function(){ cls_expr })()
*/
let ident = &cls_expr
.ident
.clone()
.or(Some(self.create_class_ident(&cls_expr.span())))
.unwrap();
*decl = ModuleDecl::ExportDefaultExpr(ExportDefaultExpr {
span: exp_decl.span().clone(),
expr: self.wrap_class_expr(&cls_expr.class, ident),
});
}
}
} else if let Some(exp_decl) = decl.as_export_decl() {
if let Some(cls_decl) = exp_decl.decl.as_class() {
if self.check_class(&cls_decl.class) {
/*
* Input:
* exp_decl cls_decl ident
* | | |
* export class name {}
* Output: export const name = (function(){ cls_decl })()
*/
*decl = ModuleDecl::ExportDecl(ExportDecl {
span: exp_decl.span().clone(),
decl: Decl::Var(self.wrap_var_class(cls_decl)),
});
}
} else {
item.visit_mut_children_with(self);
}
}
} else if let Some(stmt) = item.as_stmt() {
if let Some(decl) = stmt.as_decl() {
if let Some(cls_decl) = decl.as_class() {
if self.check_class(&cls_decl.class) {
/*
* Input:
* cls_decl ident
* | |
* class name {}
* Output: const name = (function(){ cls_decl })()
*/
*item =
ModuleItem::Stmt(Stmt::Decl(Decl::Var(self.wrap_var_class(cls_decl))));
}
} else {
item.visit_mut_children_with(self);
}
}
}
}
fn visit_mut_block_stmt(&mut self, _stmt: &mut BlockStmt) {}
}
Describe the feature
1. side-effect-free decorators
Currently, the decorator code generated by swc has side effects, which will cause classes with decorators to Unable to support Tree shaking (I use swc with rollup). In fact, our decorators are side-effect-free in most cases. Is it possible to add decorator's side effect configuration in swc?
For example, I have this source code:
compiled code:
side-effect-free code:
2. wasm plugin for decorators
I try to write a wasm plugin that converts decorated classes into closures with
/* @__PURE__ */
annotations. But I can't get thedecorators
of theclass
in the plugin (the decorators are transformed before the wasm plugin is executed, it only works properly whendisableBuiltinTransformsForInternalTesting = true
). Is there any way to make it work? The following is the source code of the plugin:Babel plugin or link to the feature description
No response
Additional context
No response