mirror of
https://github.com/swc-project/swc.git
synced 2024-11-24 02:06:08 +03:00
Fix decorators (#899)
swc_ecma_transforms: - typescript::strip: Preserve a class property if it has decorators - decorators::legacy: Implement parameter decorator.
This commit is contained in:
parent
b72901b5e0
commit
53b09aa356
@ -37,11 +37,9 @@ install:
|
|||||||
- npm install
|
- npm install
|
||||||
- npm install browserslist regenerator
|
- npm install browserslist regenerator
|
||||||
- npm install -g jest
|
- npm install -g jest
|
||||||
- travis_wait 50 RUST_BACKTRACE=0 cargo test --no-run --color always --all --all-features
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
# - RUST_BACKTRACE=0 cargo check --color always --all --all-targets
|
# - RUST_BACKTRACE=0 cargo check --color always --all --all-targets
|
||||||
# - RUST_BACKTRACE=full cargo test --color always --all --all-features
|
|
||||||
- true
|
- true
|
||||||
|
|
||||||
before_deploy:
|
before_deploy:
|
||||||
|
@ -207,7 +207,9 @@ impl Legacy {
|
|||||||
definite: false,
|
definite: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Injected to sequence expression which is wrapped with parenthesis.
|
||||||
let mut extra_exprs = vec![];
|
let mut extra_exprs = vec![];
|
||||||
|
// Injected to constructor
|
||||||
let mut constructor_stmts = SmallVec::<[_; 8]>::new();
|
let mut constructor_stmts = SmallVec::<[_; 8]>::new();
|
||||||
|
|
||||||
let prototype = MemberExpr {
|
let prototype = MemberExpr {
|
||||||
@ -218,7 +220,10 @@ impl Legacy {
|
|||||||
};
|
};
|
||||||
|
|
||||||
c.class.body = c.class.body.move_flat_map(|m| match m {
|
c.class.body = c.class.body.move_flat_map(|m| match m {
|
||||||
ClassMember::Method(m) if !m.function.decorators.is_empty() => {
|
ClassMember::Method(mut m)
|
||||||
|
if !m.function.decorators.is_empty()
|
||||||
|
|| m.function.params.iter().any(|p| !p.decorators.is_empty()) =>
|
||||||
|
{
|
||||||
let prototype = if m.is_static {
|
let prototype = if m.is_static {
|
||||||
cls_ident.clone().as_arg()
|
cls_ident.clone().as_arg()
|
||||||
} else {
|
} else {
|
||||||
@ -274,8 +279,6 @@ impl Legacy {
|
|||||||
dec_exprs.push(Some(i.as_arg()))
|
dec_exprs.push(Some(i.as_arg()))
|
||||||
}
|
}
|
||||||
|
|
||||||
let callee = helper!(apply_decorated_descriptor, "applyDecoratedDescriptor");
|
|
||||||
|
|
||||||
let name = match m.key {
|
let name = match m.key {
|
||||||
PropName::Computed(..) => {
|
PropName::Computed(..) => {
|
||||||
unimplemented!("decorators on methods with computed key")
|
unimplemented!("decorators on methods with computed key")
|
||||||
@ -283,6 +286,38 @@ impl Legacy {
|
|||||||
_ => prop_name_to_expr_value(m.key.clone()),
|
_ => prop_name_to_expr_value(m.key.clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
// https://github.com/swc-project/swc/issues/863
|
||||||
|
let mut new_params = Vec::with_capacity(m.function.params.len());
|
||||||
|
for (index, param) in m.function.params.into_iter().enumerate() {
|
||||||
|
for dec in param.decorators {
|
||||||
|
//
|
||||||
|
extra_exprs.push(Box::new(Expr::Call(CallExpr {
|
||||||
|
span: dec.span,
|
||||||
|
callee: dec.expr.as_callee(),
|
||||||
|
args: vec![
|
||||||
|
prototype.clone(),
|
||||||
|
name.clone().as_arg(),
|
||||||
|
Lit::Num(Number {
|
||||||
|
span: param.span,
|
||||||
|
value: index as _,
|
||||||
|
})
|
||||||
|
.as_arg(),
|
||||||
|
],
|
||||||
|
type_args: None,
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
new_params.push(Param {
|
||||||
|
decorators: Default::default(),
|
||||||
|
..param
|
||||||
|
});
|
||||||
|
}
|
||||||
|
m.function.params = new_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
let callee = helper!(apply_decorated_descriptor, "applyDecoratedDescriptor");
|
||||||
|
|
||||||
extra_exprs.extend(dec_inits);
|
extra_exprs.extend(dec_inits);
|
||||||
|
|
||||||
extra_exprs.push(Box::new(Expr::Call(CallExpr {
|
extra_exprs.push(Box::new(Expr::Call(CallExpr {
|
||||||
@ -646,6 +681,7 @@ impl Legacy {
|
|||||||
expr
|
expr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Apply class decorators.
|
||||||
fn apply(&mut self, mut expr: Box<Expr>, decorators: Vec<Decorator>) -> Box<Expr> {
|
fn apply(&mut self, mut expr: Box<Expr>, decorators: Vec<Decorator>) -> Box<Expr> {
|
||||||
for dec in decorators.into_iter().rev() {
|
for dec in decorators.into_iter().rev() {
|
||||||
let (i, aliased) = alias_if_required(&dec.expr, "_dec");
|
let (i, aliased) = alias_if_required(&dec.expr, "_dec");
|
||||||
|
@ -722,8 +722,12 @@ impl Fold for Strip {
|
|||||||
| ClassMember::Method(ClassMethod {
|
| ClassMember::Method(ClassMethod {
|
||||||
function: Function { body: None, .. },
|
function: Function { body: None, .. },
|
||||||
..
|
..
|
||||||
})
|
}) => None,
|
||||||
| ClassMember::ClassProp(ClassProp { value: None, .. }) => None,
|
ClassMember::ClassProp(ClassProp {
|
||||||
|
value: None,
|
||||||
|
ref decorators,
|
||||||
|
..
|
||||||
|
}) if decorators.is_empty() => None,
|
||||||
|
|
||||||
_ => Some(member),
|
_ => Some(member),
|
||||||
})
|
})
|
||||||
|
@ -5,12 +5,20 @@ use swc_ecma_transforms::{
|
|||||||
compat::{es2015::classes::Classes, es2020::class_properties},
|
compat::{es2015::classes::Classes, es2020::class_properties},
|
||||||
proposals::{decorators, decorators::Config},
|
proposals::{decorators, decorators::Config},
|
||||||
resolver, typescript,
|
resolver, typescript,
|
||||||
|
typescript::strip,
|
||||||
};
|
};
|
||||||
use swc_ecma_visit::Fold;
|
use swc_ecma_visit::Fold;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
|
fn ts() -> Syntax {
|
||||||
|
Syntax::Typescript(TsConfig {
|
||||||
|
decorators: true,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn syntax(decorators_before_export: bool) -> Syntax {
|
fn syntax(decorators_before_export: bool) -> Syntax {
|
||||||
Syntax::Es(EsConfig {
|
Syntax::Es(EsConfig {
|
||||||
decorators_before_export,
|
decorators_before_export,
|
||||||
@ -24,9 +32,20 @@ fn tr() -> impl Fold {
|
|||||||
chain!(decorators(Default::default()), class_properties(),)
|
chain!(decorators(Default::default()), class_properties(),)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ts_transform() -> impl Fold {
|
||||||
|
chain!(
|
||||||
|
strip(),
|
||||||
|
decorators(Config {
|
||||||
|
legacy: true,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
class_properties(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Folder for `transformation_*` tests
|
/// Folder for `transformation_*` tests
|
||||||
fn transformation() -> impl Fold {
|
fn transformation() -> impl Fold {
|
||||||
chain!(decorators(Default::default()), class_properties(),)
|
chain!(strip(), decorators(Default::default()), class_properties(),)
|
||||||
}
|
}
|
||||||
|
|
||||||
// transformation_declaration
|
// transformation_declaration
|
||||||
@ -4329,3 +4348,227 @@ let Person = ((_class = function() {
|
|||||||
const p = new Person();
|
const p = new Person();
|
||||||
p.save();"
|
p.save();"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test!(
|
||||||
|
ts(),
|
||||||
|
|_| ts_transform(),
|
||||||
|
issue_862_1,
|
||||||
|
"
|
||||||
|
@Entity()
|
||||||
|
export class Product extends TimestampedEntity {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
public id!: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
public price!: number;
|
||||||
|
|
||||||
|
@Column({ enum: ProductType })
|
||||||
|
public type!: ProductType;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
public productEntityId!: string;
|
||||||
|
|
||||||
|
/* ANCHOR: Relations ------------------------------------------------------ */
|
||||||
|
@OneToMany(() => Order, (order) => order.product)
|
||||||
|
public orders!: Order[];
|
||||||
|
|
||||||
|
@OneToMany(() => Discount, (discount) => discount.product)
|
||||||
|
public discounts!: Discount[];
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"var _class, _descriptor, _descriptor1, _descriptor2, _descriptor3, _descriptor4, \
|
||||||
|
_descriptor5;
|
||||||
|
var _dec = PrimaryGeneratedColumn('uuid'), _dec1 = Column(), _dec2 = Column({
|
||||||
|
enum: ProductType
|
||||||
|
}), _dec3 = Column(), _dec4 = OneToMany(()=>Order
|
||||||
|
, (order)=>order.product
|
||||||
|
), _dec5 = OneToMany(()=>Discount
|
||||||
|
, (discount)=>discount.product
|
||||||
|
), _dec6 = Entity();
|
||||||
|
export let Product = _dec6(((_class = function() {
|
||||||
|
class Product extends TimestampedEntity {
|
||||||
|
constructor(...args){
|
||||||
|
super(...args);
|
||||||
|
_initializerDefineProperty(this, 'id', _descriptor, this);
|
||||||
|
_initializerDefineProperty(this, 'price', _descriptor1, this);
|
||||||
|
_initializerDefineProperty(this, 'type', _descriptor2, this);
|
||||||
|
_initializerDefineProperty(this, 'productEntityId', _descriptor3, this);
|
||||||
|
_initializerDefineProperty(this, 'orders', _descriptor4, this);
|
||||||
|
_initializerDefineProperty(this, 'discounts', _descriptor5, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Product;
|
||||||
|
}()) || _class, _descriptor = _applyDecoratedDescriptor(_class.prototype, 'id', [
|
||||||
|
_dec
|
||||||
|
], {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
writable: true,
|
||||||
|
initializer: function() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}), _descriptor1 = _applyDecoratedDescriptor(_class.prototype, 'price', [
|
||||||
|
_dec1
|
||||||
|
], {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
writable: true,
|
||||||
|
initializer: function() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}), _descriptor2 = _applyDecoratedDescriptor(_class.prototype, 'type', [
|
||||||
|
_dec2
|
||||||
|
], {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
writable: true,
|
||||||
|
initializer: function() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}), _descriptor3 = _applyDecoratedDescriptor(_class.prototype, 'productEntityId', [
|
||||||
|
_dec3
|
||||||
|
], {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
writable: true,
|
||||||
|
initializer: function() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}), _descriptor4 = _applyDecoratedDescriptor(_class.prototype, 'orders', [
|
||||||
|
_dec4
|
||||||
|
], {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
writable: true,
|
||||||
|
initializer: function() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}), _descriptor5 = _applyDecoratedDescriptor(_class.prototype, 'discounts', [
|
||||||
|
_dec5
|
||||||
|
], {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
writable: true,
|
||||||
|
initializer: function() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}), _class));
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
test!(
|
||||||
|
ts(),
|
||||||
|
|_| ts_transform(),
|
||||||
|
issue_862_2,
|
||||||
|
"@Entity()
|
||||||
|
export class Product extends TimestampedEntity {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
public id!: string;
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"var _class, _descriptor;
|
||||||
|
var _dec = PrimaryGeneratedColumn('uuid'), _dec1 = Entity();
|
||||||
|
export let Product = _dec1(((_class = function() {
|
||||||
|
class Product extends TimestampedEntity {
|
||||||
|
constructor(...args){
|
||||||
|
super(...args);
|
||||||
|
_initializerDefineProperty(this, 'id', _descriptor, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Product;
|
||||||
|
}()) || _class, _descriptor = _applyDecoratedDescriptor(_class.prototype, 'id', [
|
||||||
|
_dec
|
||||||
|
], {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
writable: true,
|
||||||
|
initializer: function() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}), _class));
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
test_exec!(
|
||||||
|
ts(),
|
||||||
|
|_| ts_transform(),
|
||||||
|
issue_862_3,
|
||||||
|
"var log: number[] = [];
|
||||||
|
function push(x: number) { log.push(x); return x; }
|
||||||
|
|
||||||
|
function saveOrder(x: number) {
|
||||||
|
return function (el: any) {
|
||||||
|
log.push(x);
|
||||||
|
return el;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@saveOrder(1)
|
||||||
|
class Product {
|
||||||
|
@saveOrder(0)
|
||||||
|
public id!: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nums = Array.from({ length: 2 }, (_, i) => i);
|
||||||
|
expect(log).toEqual(nums)"
|
||||||
|
);
|
||||||
|
|
||||||
|
test!(
|
||||||
|
ts(),
|
||||||
|
|_| ts_transform(),
|
||||||
|
issue_863_1,
|
||||||
|
"class ProductController {
|
||||||
|
@bar()
|
||||||
|
findById(
|
||||||
|
@foo()
|
||||||
|
id: number
|
||||||
|
) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}",
|
||||||
|
" var _class, _dec;
|
||||||
|
let ProductController = ((_class = function() {
|
||||||
|
class ProductController {
|
||||||
|
findById(id) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ProductController;
|
||||||
|
}()) || _class, foo()(_class.prototype, 'findById', 0), _dec = bar(), \
|
||||||
|
_applyDecoratedDescriptor(_class.prototype, 'findById', [
|
||||||
|
_dec
|
||||||
|
], Object.getOwnPropertyDescriptor(_class.prototype, 'findById'), _class.prototype), _class);"
|
||||||
|
);
|
||||||
|
|
||||||
|
test_exec!(
|
||||||
|
ts(),
|
||||||
|
|_| ts_transform(),
|
||||||
|
issue_863_2,
|
||||||
|
"const logs: number[] = [];
|
||||||
|
|
||||||
|
function foo() {
|
||||||
|
return function (target: any, member: any, ix: any) {
|
||||||
|
logs.push(0);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function bar() {
|
||||||
|
return function (target: any, member: any, ix: any) {
|
||||||
|
logs.push(1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProductController {
|
||||||
|
findById(
|
||||||
|
@foo()
|
||||||
|
@bar()
|
||||||
|
id: number
|
||||||
|
) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(logs).toEqual([0, 1])
|
||||||
|
|
||||||
|
const c = new ProductController();
|
||||||
|
c.findById(100);
|
||||||
|
"
|
||||||
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user