mirror of
https://github.com/swc-project/swc.git
synced 2024-10-05 20:58:35 +03:00
Legacy decorator for class and class members (#531)
This commit is contained in:
parent
ac3f69acd0
commit
31a5bed497
@ -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,
|
||||
|
@ -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()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
634
ecmascript/transforms/src/proposals/decorators/legacy.rs
Normal file
634
ecmascript/transforms/src/proposals/decorators/legacy.rs
Normal 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
|
||||
}
|
||||
}
|
19
ecmascript/transforms/src/proposals/decorators/usage.rs
Normal file
19
ecmascript/transforms/src/proposals/decorators/usage.rs
Normal 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;
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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)]
|
||||
|
Loading…
Reference in New Issue
Block a user