Legacy decorator for class and class members (#531)

This commit is contained in:
강동윤 2019-12-25 19:30:16 +09:00 committed by GitHub
parent ac3f69acd0
commit 31a5bed497
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 787 additions and 125 deletions

View File

@ -284,6 +284,30 @@ where
}
}
impl<A, B, T> Fold<T> for Either<A, B>
where
T: FoldWith<A> + FoldWith<B> + FoldWith<Self>,
{
fn fold(&mut self, node: T) -> T {
match *self {
Either::Left(ref mut l) => node.fold_with(l),
Either::Right(ref mut r) => node.fold_with(r),
}
}
}
impl<A, B, T> Visit<T> for Either<A, B>
where
T: VisitWith<A> + VisitWith<B> + VisitWith<Self>,
{
fn visit(&mut self, node: &T) {
match *self {
Either::Left(ref mut l) => node.visit_with(l),
Either::Right(ref mut r) => node.visit_with(r),
}
}
}
impl<T, F> VisitWith<F> for Arc<T>
where
T: ?Sized,

View File

@ -499,3 +499,26 @@ impl<'a, I: Tokens> StmtLikeParser<'a, ModuleItem> for Parser<'a, I> {
Ok(decl)
}
}
#[cfg(test)]
mod tests {
use crate::{EsConfig, Syntax};
#[test]
fn test_legacy_decorator() {
crate::test_parser(
"@foo
export default class Foo {
bar() {
class Baz {}
}
}",
Syntax::Es(EsConfig {
decorators: true,
decorators_before_export: true,
..Default::default()
}),
|p| p.parse_module().map_err(|mut e| e.emit()),
);
}
}

View File

@ -1,28 +1,24 @@
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
var desc = {};
Object['ke' + 'ys'](descriptor).forEach(function (key) {
Object.keys(descriptor).forEach(function(key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
desc = decorators.slice().reverse().reduce(function(desc, decorator) {
return decorator(target, property, desc) || desc;
}, desc);
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object['define' + 'Property'](target, property, desc);
Object.defineProperty(target, property, desc);
desc = null;
}
return desc;
}
}

View File

@ -1,3 +1,4 @@
use self::legacy::Legacy;
use crate::{
pass::Pass,
util::{
@ -6,10 +7,14 @@ use crate::{
},
};
use ast::*;
use either::Either;
use serde::Deserialize;
use std::iter;
use swc_common::{Fold, FoldWith, Spanned, Visit, VisitWith, DUMMY_SP};
mod legacy;
mod usage;
/// ## Simple class decorator
///
/// ```js
@ -51,9 +56,12 @@ use swc_common::{Fold, FoldWith, Spanned, Visit, VisitWith, DUMMY_SP};
/// }
/// ```
pub fn decorators(c: Config) -> impl Pass {
Decorators {
c,
is_in_strict: false,
if c.legacy {
Either::Left(Legacy::default())
} else {
Either::Right(Decorators {
is_in_strict: false,
})
}
}
@ -62,18 +70,27 @@ pub struct Config {
pub legacy: bool,
}
#[derive(Debug, Default)]
struct Decorators {
c: Config,
is_in_strict: bool,
}
impl Fold<Vec<ModuleItem>> for Decorators {
fn fold(&mut self, items: Vec<ModuleItem>) -> Vec<ModuleItem> {
if !self::usage::has_decorator(&items) {
return items;
}
let old_strict = self.is_in_strict;
self.is_in_strict = true;
let mut buf = Vec::with_capacity(items.len() + 4);
items.into_iter().for_each(|item| {
if !self::usage::has_decorator(&item) {
buf.push(item);
return;
}
//
match item {
ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(ExportDefaultDecl {

View File

@ -0,0 +1,634 @@
use super::usage::DecoratorFinder;
use crate::util::{
alias_if_required, default_constructor, prepend, prop_name_to_expr_value, undefined,
ExprFactory, ModuleItemLike, StmtLike,
};
use ast::*;
use smallvec::SmallVec;
use std::mem::replace;
use swc_common::{util::move_map::MoveMap, Fold, FoldWith, VisitWith, DUMMY_SP};
#[derive(Debug, Default)]
pub(super) struct Legacy {
uninitialized_vars: Vec<VarDeclarator>,
initialized_vars: Vec<VarDeclarator>,
exports: Vec<ExportSpecifier>,
}
impl Fold<Module> for Legacy {
fn fold(&mut self, m: Module) -> Module {
let mut m = m.fold_children(self);
if !self.uninitialized_vars.is_empty() {
prepend(
&mut m.body,
Stmt::Decl(Decl::Var(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
decls: replace(&mut self.uninitialized_vars, Default::default()),
declare: false,
}))
.into(),
);
}
if !self.exports.is_empty() {
let decl = ModuleDecl::ExportNamed(NamedExport {
span: DUMMY_SP,
specifiers: replace(&mut self.exports, Default::default()),
src: None,
});
m.body.push(decl.into());
}
m
}
}
impl Fold<Script> for Legacy {
fn fold(&mut self, s: Script) -> Script {
let mut s = s.fold_children(self);
if !self.uninitialized_vars.is_empty() {
prepend(
&mut s.body,
Stmt::Decl(Decl::Var(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
decls: replace(&mut self.uninitialized_vars, Default::default()),
declare: false,
})),
);
}
s
}
}
impl<T> Fold<Vec<T>> for Legacy
where
T: FoldWith<Self> + VisitWith<DecoratorFinder> + StmtLike + ModuleItemLike,
Vec<T>: VisitWith<DecoratorFinder>,
{
fn fold(&mut self, stmts: Vec<T>) -> Vec<T> {
if !super::usage::has_decorator(&stmts) {
return stmts;
}
let mut buf = Vec::with_capacity(stmts.len() + 4);
for stmt in stmts {
if !super::usage::has_decorator(&stmt) {
buf.push(stmt);
continue;
}
let stmt = stmt.fold_with(self);
if !self.initialized_vars.is_empty() {
buf.push(T::from_stmt(Stmt::Decl(Decl::Var(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
decls: replace(&mut self.initialized_vars, Default::default()),
declare: false,
}))));
}
buf.push(stmt);
}
buf
}
}
impl Fold<ModuleItem> for Legacy {
fn fold(&mut self, item: ModuleItem) -> ModuleItem {
let item: ModuleItem = item.fold_children(self);
match item {
ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(ExportDefaultDecl {
decl: DefaultDecl::Class(c),
..
})) => {
let export_ident = c.ident.clone().unwrap_or_else(|| private_ident!("_class"));
let expr = self.handle(c);
self.exports
.push(ExportSpecifier::Named(NamedExportSpecifier {
span: DUMMY_SP,
orig: export_ident.clone(),
exported: Some(quote_ident!("default")),
}));
return ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Let,
declare: false,
decls: vec![VarDeclarator {
span: DUMMY_SP,
name: Pat::Ident(export_ident),
init: Some(expr),
definite: false,
}],
})));
}
_ => {}
}
item
}
}
impl Fold<Expr> for Legacy {
fn fold(&mut self, e: Expr) -> Expr {
let e: Expr = e.fold_children(self);
match e {
Expr::Class(e) => {
let expr = self.handle(e);
return *expr;
}
_ => {}
}
e
}
}
impl Fold<Decl> for Legacy {
fn fold(&mut self, decl: Decl) -> Decl {
let decl: Decl = decl.fold_children(self);
match decl {
Decl::Class(c) => {
let expr = self.handle(ClassExpr {
class: c.class,
ident: Some(c.ident.clone()),
});
return Decl::Var(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Let,
declare: false,
decls: vec![VarDeclarator {
span: DUMMY_SP,
name: Pat::Ident(c.ident),
init: Some(expr),
definite: false,
}],
});
}
_ => {}
}
decl
}
}
impl Legacy {
fn handle(&mut self, mut c: ClassExpr) -> Box<Expr> {
let cls_ident = private_ident!("_class");
self.uninitialized_vars.push(VarDeclarator {
span: DUMMY_SP,
name: Pat::Ident(cls_ident.clone()),
init: None,
definite: false,
});
let mut extra_exprs = vec![];
let mut constructor_stmts = SmallVec::<[_; 8]>::new();
let prototype = MemberExpr {
span: DUMMY_SP,
obj: ExprOrSuper::Expr(box Expr::Ident(cls_ident.clone())),
prop: box quote_ident!("prototype").into(),
computed: false,
};
c.class.body = c.class.body.move_flat_map(|m| match m {
ClassMember::Method(m) if !m.function.decorators.is_empty() => {
let prototype = if m.is_static {
cls_ident.clone().as_arg()
} else {
// _class2.prototype,
prototype.clone().as_arg()
};
// _applyDecoratedDescriptor(_class2.prototype, "method2", [_dec7, _dec8],
// Object.getOwnPropertyDescriptor(_class2.prototype, "method2"),
// _class2.prototype)
let mut dec_exprs = vec![];
for dec in m.function.decorators.into_iter() {
let (i, aliased) = alias_if_required(&dec.expr, "_dec");
if aliased {
self.initialized_vars.push(VarDeclarator {
span: DUMMY_SP,
name: Pat::Ident(i.clone()),
init: Some(dec.expr),
definite: false,
});
}
dec_exprs.push(Some(i.as_arg()))
}
let callee = helper!(apply_decorated_descriptor, "applyDecoratedDescriptor");
let name = match m.key {
PropName::Computed(..) => {
unimplemented!("decorators on methods with computed key")
}
_ => prop_name_to_expr_value(m.key.clone()),
};
extra_exprs.push(box Expr::Call(CallExpr {
span: DUMMY_SP,
callee,
// (_class2.prototype, "method2", [_dec7, _dec8],
// Object.getOwnPropertyDescriptor(_class2.prototype, "method2"),
// _class2.prototype)
args: vec![
prototype.clone(),
// "method2"
name.clone().as_arg(),
// [_dec7, _dec8],
ArrayLit {
span: DUMMY_SP,
elems: dec_exprs,
}
.as_arg(),
// Object.getOwnPropertyDescriptor(_class2.prototype, "method2"),
CallExpr {
span: DUMMY_SP,
callee: member_expr!(DUMMY_SP, Object.getOwnPropertyDescriptor)
.as_callee(),
args: vec![prototype.clone(), name.as_arg()],
type_args: None,
}
.as_arg(),
// _class2.prototype
prototype.clone(),
],
type_args: None,
}));
Some(ClassMember::Method(ClassMethod {
function: Function {
decorators: vec![],
..m.function
},
..m
}))
}
ClassMember::ClassProp(p) if !p.decorators.is_empty() => {
let prototype = if p.is_static {
cls_ident.clone().as_arg()
} else {
// _class2.prototype,
prototype.clone().as_arg()
};
//
let descriptor = private_ident!("_descriptor");
if !p.is_static {
self.uninitialized_vars.push(VarDeclarator {
span: DUMMY_SP,
name: Pat::Ident(descriptor.clone()),
init: None,
definite: false,
});
}
let mut value = Some(p.value);
let mut dec_exprs = vec![];
for dec in p.decorators.into_iter() {
let (i, aliased) = alias_if_required(&dec.expr, "_dec");
if aliased {
self.initialized_vars.push(VarDeclarator {
span: DUMMY_SP,
name: Pat::Ident(i.clone()),
init: Some(dec.expr),
definite: false,
});
}
dec_exprs.push(Some(i.as_arg()))
}
// TODO: Handle s prop name
let name = match *p.key {
Expr::Ident(ref i) => box Expr::Lit(Lit::Str(Str {
span: i.span,
value: i.sym.clone(),
has_escape: false,
})),
_ => p.key.clone(),
};
let init = private_ident!("_init");
if p.is_static {
self.uninitialized_vars.push(VarDeclarator {
span: DUMMY_SP,
name: Pat::Ident(init.clone()),
init: None,
definite: false,
});
}
let mut property_descriptor = Expr::Object(ObjectLit {
span: DUMMY_SP,
props: vec![
// configurable: true,
PropOrSpread::Prop(box Prop::KeyValue(KeyValueProp {
key: quote_ident!("configurable").into(),
value: box Expr::Lit(Lit::Bool(Bool {
span: DUMMY_SP,
value: true,
})),
})), // enumerable: true,
PropOrSpread::Prop(box Prop::KeyValue(KeyValueProp {
key: quote_ident!("enumerable").into(),
value: box Expr::Lit(Lit::Bool(Bool {
span: DUMMY_SP,
value: true,
})),
})),
// writable: true,
PropOrSpread::Prop(box Prop::KeyValue(KeyValueProp {
key: quote_ident!("writable").into(),
value: box Expr::Lit(Lit::Bool(Bool {
span: DUMMY_SP,
value: true,
})),
})),
// initializer: function () {
// return 2;
// }
PropOrSpread::Prop(box Prop::KeyValue(KeyValueProp {
key: quote_ident!("initializer").into(),
value: box Expr::Fn(FnExpr {
ident: None,
function: Function {
decorators: Default::default(),
is_generator: false,
is_async: false,
span: DUMMY_SP,
params: vec![],
body: Some(BlockStmt {
span: DUMMY_SP,
stmts: vec![ReturnStmt {
span: DUMMY_SP,
arg: if p.is_static {
Some(box Expr::Ident(init.clone()))
} else {
value.take().unwrap()
},
}
.into()],
}),
type_params: Default::default(),
return_type: Default::default(),
},
}),
})),
],
});
if p.is_static {
property_descriptor = Expr::Seq(SeqExpr {
span: DUMMY_SP,
exprs: vec![
box Expr::Assign(AssignExpr {
span: DUMMY_SP,
left: PatOrExpr::Pat(box Pat::Ident(init.clone())),
op: op!("="),
// Object.getOwnPropertyDescriptor(_class, "enumconfwrite")
right: box Expr::Call(CallExpr {
span: DUMMY_SP,
callee: member_expr!(DUMMY_SP, Object.getOwnPropertyDescriptor)
.as_callee(),
args: vec![cls_ident.clone().as_arg(), name.clone().as_arg()],
type_args: Default::default(),
}),
}),
// _init = _init ? _init.value : void 0
box Expr::Assign(AssignExpr {
span: DUMMY_SP,
left: PatOrExpr::Pat(box Pat::Ident(init.clone())),
op: op!("="),
right: box Expr::Cond(CondExpr {
span: DUMMY_SP,
test: box Expr::Ident(init.clone()),
cons: box init.clone().member(quote_ident!("value")),
alt: undefined(DUMMY_SP),
}),
}),
box property_descriptor,
],
});
}
// _applyDecoratedDescriptor(_class2.prototype, "prop2", [_dec9, _dec10], {
// configurable: true,
// enumerable: true,
// writable: true,
// initializer: function () {
// return 2;
// }
// }))
let call_expr = box Expr::Call(CallExpr {
span: DUMMY_SP,
callee: helper!(apply_decorated_descriptor, "applyDecoratedDescriptor"),
args: {
if p.is_static {
vec![
prototype,
name.clone().as_arg(),
ArrayLit {
span: DUMMY_SP,
elems: dec_exprs,
}
.as_arg(),
property_descriptor.as_arg(),
cls_ident.clone().as_arg(),
]
} else {
vec![
prototype,
name.clone().as_arg(),
ArrayLit {
span: DUMMY_SP,
elems: dec_exprs,
}
.as_arg(),
property_descriptor.as_arg(),
]
}
},
type_args: Default::default(),
});
if !p.is_static {
extra_exprs.push(box Expr::Assign(AssignExpr {
span: DUMMY_SP,
op: op!("="),
left: PatOrExpr::Pat(box Pat::Ident(descriptor.clone())),
right: call_expr,
}));
} else {
extra_exprs.push(call_expr);
}
if !p.is_static {
constructor_stmts.push(
CallExpr {
span: DUMMY_SP,
callee: helper!(
initializer_define_property,
"initializerDefineProperty"
),
args: vec![
ThisExpr { span: DUMMY_SP }.as_arg(),
name.as_arg(),
descriptor.as_arg(),
ThisExpr { span: DUMMY_SP }.as_arg(),
],
type_args: None,
}
.into_stmt(),
);
}
if p.is_static {
Some(
ClassProp {
decorators: vec![],
value: value.take().unwrap(),
..p
}
.into(),
)
} else {
None
}
}
_ => Some(m),
});
if !constructor_stmts.is_empty() {
{
// Create constructors as required
let has = c.class.body.iter().any(|m| match m {
ClassMember::Constructor(..) => true,
_ => false,
});
if !has {
c.class
.body
.push(ClassMember::Constructor(default_constructor(
c.class.super_class.is_some(),
)))
}
}
let constructor = c
.class
.body
.iter_mut()
.filter_map(|m| match m {
ClassMember::Constructor(c) => Some(c),
_ => None,
})
.next()
.unwrap();
if constructor.body.is_none() {
constructor.body = Some(BlockStmt {
span: DUMMY_SP,
stmts: vec![],
});
}
constructor
.body
.as_mut()
.unwrap()
.stmts
.extend(constructor_stmts)
}
let cls_assign = box Expr::Assign(AssignExpr {
span: DUMMY_SP,
op: op!("="),
left: PatOrExpr::Pat(box Pat::Ident(cls_ident.clone())),
right: box Expr::Class(ClassExpr {
ident: c.ident.clone(),
class: Class {
decorators: vec![],
..c.class
},
}),
});
let var_init = box Expr::Bin(BinExpr {
span: DUMMY_SP,
left: cls_assign,
op: op!("||"),
right: box Expr::Ident(cls_ident.clone()),
});
let expr = self.apply(
if extra_exprs.is_empty() {
var_init
} else {
extra_exprs.insert(0, var_init);
// Return value.
extra_exprs.push(box Expr::Ident(cls_ident));
box Expr::Seq(SeqExpr {
span: DUMMY_SP,
exprs: extra_exprs,
})
},
c.class.decorators,
);
expr
}
fn apply(&mut self, mut expr: Box<Expr>, decorators: Vec<Decorator>) -> Box<Expr> {
for dec in decorators.into_iter().rev() {
let (i, aliased) = alias_if_required(&dec.expr, "_dec");
if aliased {
self.initialized_vars.push(VarDeclarator {
span: DUMMY_SP,
name: Pat::Ident(i.clone()),
init: Some(dec.expr),
definite: false,
});
}
expr = box Expr::Call(CallExpr {
span: DUMMY_SP,
callee: i.as_callee(),
args: vec![expr.as_arg()],
type_args: None,
});
}
expr
}
}

View File

@ -0,0 +1,19 @@
use ast::Decorator;
use swc_common::{Visit, VisitWith};
pub(super) fn has_decorator<T: VisitWith<DecoratorFinder>>(node: &T) -> bool {
let mut v = DecoratorFinder { found: false };
node.visit_with(&mut v);
v.found
}
pub(super) struct DecoratorFinder {
found: bool,
}
impl Visit<Decorator> for DecoratorFinder {
fn visit(&mut self, node: &Decorator) {
self.found = true;
}
}

View File

@ -114,25 +114,6 @@ impl<'a> Tester<'a> {
})
}
pub fn parse_stmts(&mut self, file_name: &str, src: &str) -> Result<Vec<Stmt>, ()> {
let stmts = self.with_parser(file_name, Syntax::default(), src, |p| {
p.parse_script()
.map_err(|mut e| {
e.emit();
})
.map(|script| script.body)
})?;
Ok(stmts)
}
pub fn parse_stmt(&mut self, file_name: &str, src: &str) -> Result<Stmt, ()> {
let mut stmts = self.parse_stmts(file_name, src)?;
assert!(stmts.len() == 1);
Ok(stmts.pop().unwrap())
}
pub fn apply_transform<T: Fold<Module>>(
&mut self,
mut tr: T,

View File

@ -2030,9 +2030,7 @@ let A = _decorate([], function(_initialize) {
// legacy_class_constructors_return_new_constructor
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
ignore,
syntax(false),
syntax(true),
|_| chain!(
decorators(decorators::Config { legacy: true }),
class_properties(),
@ -2058,9 +2056,7 @@ expect(typeof Parent.prototype.child).toBe("function");
// legacy_regression_10264
test!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
ignore,
syntax(false),
syntax(true),
|_| chain!(
typescript::strip(),
decorators(decorators::Config { legacy: true })
@ -2074,13 +2070,13 @@ export default class {}
"#,
r#"
var _class2;
var _class;
function myDecorator(decoratee) {
}
let _class1 = myDecorator((_class = class{
}) || _class);
export { _class1 as default }
function myDecorator(decoratee) {}
let _class = myDecorator(_class2 = class {}) || _class2;
export { _class as default };
"#
);
@ -2467,8 +2463,6 @@ export { _class as default };
// legacy_decl_to_expression_class_decorators
test!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
ignore,
syntax(false),
|_| decorators(Config { legacy: true }),
legacy_decl_to_expression_class_decorators,
@ -2477,21 +2471,16 @@ export default @dec class A {}
@dec class B {}
"#,
r#"
var _class, _class2;
let A = dec(_class2 = class A {}) || _class2;
export { A as default };
let B = dec(_class = class B {}) || _class;
var _class, _class1;
export default dec((_class = class A{
}) || _class);
let B = dec((_class1 = class B{
}) || _class1);
"#
);
// legacy_class_prototype_methods_numeric_props
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
ignore,
syntax(false),
|_| chain!(
decorators(decorators::Config { legacy: true }),
@ -2515,8 +2504,6 @@ class Example {
// legacy_class_static_properties_mutate_descriptor
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
ignore,
syntax(false),
|_| chain!(
decorators(decorators::Config { legacy: true }),
@ -2629,8 +2616,6 @@ expect(Example._).toBe("__8__");
// legacy_class_static_methods_string_props
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
ignore,
syntax(false),
|_| chain!(
decorators(decorators::Config { legacy: true }),
@ -2654,8 +2639,6 @@ class Example {
// legacy_class_prototype_properties_string_literal_properties
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
ignore,
syntax(false),
|_| chain!(
decorators(decorators::Config { legacy: true }),
@ -2698,8 +2681,6 @@ expect(descs["a-prop"].configurable).toBeTruthy();
// legacy_class_prototype_methods_mutate_descriptor
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
ignore,
syntax(false),
|_| chain!(
decorators(decorators::Config { legacy: true }),
@ -2828,7 +2809,7 @@ expect(inst._()).toBe("__8__");
// legacy_object_properties_numeric_props
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
// Legacy decorator for object literals
ignore,
syntax(false),
|_| chain!(
@ -2853,8 +2834,6 @@ const inst = {
// legacy_decl_to_expression_method_decorators
test!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
ignore,
syntax(false),
|_| decorators(Config { legacy: true }),
legacy_decl_to_expression_method_decorators,
@ -2867,27 +2846,22 @@ class B {
}
"#,
r#"
var _class, _class2;
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; Object.keys(descriptor).forEach(function (key) { desc[key] = descriptor[key]; }); desc.enumerable = !!desc.enumerable; desc.configurable = !!desc.configurable; if ('value' in desc || desc.initializer) { desc.writable = true; } desc = decorators.slice().reverse().reduce(function (desc, decorator) { return decorator(target, property, desc) || desc; }, desc); if (context && desc.initializer !== void 0) { desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined; } if (desc.initializer === void 0) { Object.defineProperty(target, property, desc); desc = null; } return desc; }
let A = (_class2 = class A {
foo() {}
}, (_applyDecoratedDescriptor(_class2.prototype, "foo", [dec], Object.getOwnPropertyDescriptor(_class2.prototype, "foo"), _class2.prototype)), _class2);
export { A as default };
let B = (_class = class B {
foo() {}
}, (_applyDecoratedDescriptor(_class.prototype, "foo", [dec], Object.getOwnPropertyDescriptor(_class.prototype, "foo"), _class.prototype)), _class);
var _class, _class1;
let A = ((_class = class A{
foo() {
}
}) || _class, _applyDecoratedDescriptor(_class.prototype, 'foo', [dec], Object.getOwnPropertyDescriptor(_class.prototype, 'foo'), _class.prototype), _class);
let B = ((_class1 = class B{
foo() {
}
}) || _class1, _applyDecoratedDescriptor(_class1.prototype, 'foo', [dec], Object.getOwnPropertyDescriptor(_class1.prototype, 'foo'), _class1.prototype), _class1);
export { A as default }
"#
);
// legacy_class_prototype_properties_return_descriptor
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
ignore,
syntax(false),
|_| chain!(
decorators(decorators::Config { legacy: true }),
@ -3000,7 +2974,7 @@ expect(inst._).toBe("__8__");
// legacy_object_properties_string_props
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
// Legacy decorator for object literals
ignore,
syntax(false),
|_| chain!(
@ -3025,7 +2999,7 @@ const inst = {
// legacy_object_properties_return_descriptor
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
// Legacy decorator for object literals
ignore,
syntax(false),
|_| chain!(
@ -3137,8 +3111,6 @@ expect(inst._).toBe("__8__");
// legacy_class_prototype_methods_string_props
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
ignore,
syntax(false),
|_| chain!(
decorators(decorators::Config { legacy: true }),
@ -3162,8 +3134,6 @@ class Example {
// legacy_regression_8041
test!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
ignore,
syntax(false),
|_| chain!(
decorators(decorators::Config { legacy: true }),
@ -3178,24 +3148,20 @@ export default class {
"#,
r#"
var _class2;
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; Object.keys(descriptor).forEach(function (key) { desc[key] = descriptor[key]; }); desc.enumerable = !!desc.enumerable; desc.configurable = !!desc.configurable; if ('value' in desc || desc.initializer) { desc.writable = true; } desc = decorators.slice().reverse().reduce(function (desc, decorator) { return decorator(target, property, desc) || desc; }, desc); if (context && desc.initializer !== void 0) { desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined; } if (desc.initializer === void 0) { Object.defineProperty(target, property, desc); desc = null; } return desc; }
let _class = (_class2 = class {
bar() {}
}, (_applyDecoratedDescriptor(_class2.prototype, "bar", [foo], Object.getOwnPropertyDescriptor(_class2.prototype, "bar"), _class2.prototype)), _class2);
export { _class as default };
var _class;
let _class1 = ((_class = function() {
class _class2{
bar() {
}
}
return _class2;
}()) || _class, _applyDecoratedDescriptor(_class.prototype, 'bar', [foo], Object.getOwnPropertyDescriptor(_class.prototype, 'bar'), _class.prototype), _class);
export { _class1 as default }
"#
);
// legacy_class_prototype_methods_return_descriptor
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
ignore,
syntax(false),
|_| chain!(
decorators(decorators::Config { legacy: true }),
@ -3326,7 +3292,7 @@ expect(inst._()).toBe("__8__");
// legacy_object_ordering_reverse_order
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
// Legacy decorator for object literals
ignore,
syntax(false),
|_| chain!(
@ -3367,7 +3333,7 @@ expect(calls).toEqual([1, 2, 3, 4, 5, 6, 7, 8]);
// legacy_object_methods_numeric_props
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
// Legacy decorator for object literals
ignore,
syntax(false),
|_| chain!(
@ -3393,8 +3359,6 @@ const inst = {
// legacy_class_static_properties_return_descriptor
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
ignore,
syntax(false),
|_| chain!(
decorators(decorators::Config { legacy: true }),
@ -3507,9 +3471,12 @@ expect(Example._).toBe("__8__");
// legacy_class_export_default
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
// We wrap exec tests in a function like it('should work', function(){
// // .. code
// }), but it prevents swc_ecma_parser from parsing the code
// below correctly.
ignore,
syntax(false),
syntax(true),
|_| chain!(
decorators(decorators::Config { legacy: true }),
class_properties(),
@ -3536,9 +3503,7 @@ expect(calls).toEqual(["Foo"]);
// legacy_class_ordering_reverse_order
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
ignore,
syntax(false),
syntax(true),
|_| chain!(
decorators(decorators::Config { legacy: true }),
class_properties(),
@ -3580,9 +3545,9 @@ expect(calls).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
// legacy_object_methods_mutate_descriptor
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
// Legacy decorator for object literals
ignore,
syntax(false),
syntax(true),
|_| chain!(
decorators(decorators::Config { legacy: true }),
class_properties(),
@ -3708,8 +3673,6 @@ expect(inst._()).toBe("__8__");
// legacy_class_static_methods_return_descriptor
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
ignore,
syntax(false),
|_| chain!(
decorators(decorators::Config { legacy: true }),
@ -3837,7 +3800,7 @@ expect(Example._()).toBe("__8__");
// legacy_object_methods_return_descriptor
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
// Legacy decorator for object literals
ignore,
syntax(false),
|_| chain!(
@ -3965,7 +3928,7 @@ expect(inst._()).toBe("__8__");
// legacy_object_methods_string_props
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
// Legacy decorator for object literals
ignore,
syntax(false),
|_| chain!(
@ -3992,8 +3955,6 @@ const inst = {
// legacy_class_prototype_properties_child_classes_properties
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
ignore,
syntax(false),
|_| chain!(
decorators(decorators::Config { legacy: true }),
@ -4034,8 +3995,6 @@ expect(inst.prop2).toBe("__4__");
// legacy_class_static_methods_mutate_descriptor
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
ignore,
syntax(false),
|_| chain!(
decorators(decorators::Config { legacy: true }),
@ -4162,8 +4121,6 @@ expect(Example._()).toBe("__8__");
// legacy_regression_8512
test_exec!(
// legacy decorator: https://github.com/swc-project/swc/issues/421
ignore,
syntax(false),
|_| decorators(Config { legacy: true }),
legacy_regression_8512_exec,

View File

@ -144,7 +144,11 @@ impl Options {
} = config.jsc;
let syntax = syntax.unwrap_or_default();
let transform = transform.unwrap_or_default();
let mut transform = transform.unwrap_or_default();
if syntax.typescript() {
transform.legacy_decorator = true;
}
let const_modules = {
let enabled = transform.const_modules.is_some();
@ -183,11 +187,15 @@ impl Options {
Optional::new(typescript::strip(), syntax.typescript()),
Optional::new(nullish_coalescing(), syntax.nullish_coalescing()),
Optional::new(optional_chaining(), syntax.typescript()),
Optional::new(class_properties(), syntax.typescript()),
resolver(),
const_modules,
pass,
Optional::new(decorators(Default::default()), syntax.decorators()),
Optional::new(
decorators(decorators::Config {
legacy: transform.legacy_decorator
}),
syntax.decorators()
),
Optional::new(class_properties(), syntax.class_props()),
Optional::new(
export(),
@ -471,6 +479,9 @@ pub struct TransformConfig {
#[serde(default)]
pub optimizer: Option<OptimizerConfig>,
#[serde(default)]
pub legacy_decorator: bool,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]