Allow using properties with legacy decorators (#824)

This commit is contained in:
강동윤 2020-06-06 15:30:40 +09:00 committed by GitHub
parent d82e6ab69d
commit 3b1ebdd2e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 208 additions and 6 deletions

View File

@ -761,7 +761,12 @@ impl<'a> Emitter<'a> {
fn emit_ts_const_assertion(&mut self, n: &TsConstAssertion) -> Result {
self.emit_leading_comments_of_pos(n.span().lo())?;
unimplemented!("emit_ts_const_assertion")
emit!(n.expr);
space!();
keyword!("as");
space!();
keyword!("const");
}
#[emitter]

View File

@ -196,6 +196,7 @@ impl Fold<Decl> for Legacy {
impl Legacy {
fn handle(&mut self, mut c: ClassExpr) -> Box<Expr> {
let cls_ident = private_ident!("_class");
let cls_name = c.ident.clone();
self.uninitialized_vars.push(VarDeclarator {
span: DUMMY_SP,
@ -228,15 +229,44 @@ impl Legacy {
// _class2.prototype)
let mut dec_exprs = vec![];
let mut dec_inits = 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 {
self.uninitialized_vars.push(VarDeclarator {
span: DUMMY_SP,
name: Pat::Ident(i.clone()),
init: Some(dec.expr),
init: None,
definite: false,
});
// We use _class.staticField instead of Person.staticField because while
// initializing the class,
//
// _dec = Debounce(Person.debounceTime)
//
// fails while
//
// _dec = Debounce(_class.debounceTime)
//
// works.
//
// See: https://github.com/swc-project/swc/issues/823
let right = if let Some(cls_name) = cls_name.clone() {
dec.expr.fold_with(&mut ClassFieldAccessConverter {
cls_name,
alias: cls_ident.clone(),
})
} else {
dec.expr
};
dec_inits.push(box Expr::Assign(AssignExpr {
span: dec.span,
op: op!("="),
left: PatOrExpr::Pat(box Pat::Ident(i.clone())),
right,
}));
}
dec_exprs.push(Some(i.as_arg()))
@ -251,6 +281,8 @@ impl Legacy {
_ => prop_name_to_expr_value(m.key.clone()),
};
extra_exprs.extend(dec_inits);
extra_exprs.push(box Expr::Call(CallExpr {
span: DUMMY_SP,
callee,
@ -635,3 +667,38 @@ impl Legacy {
expr
}
}
struct ClassFieldAccessConverter {
cls_name: Ident,
/// `_class`
alias: Ident,
}
noop_fold_type!(ClassFieldAccessConverter);
impl Fold<MemberExpr> for ClassFieldAccessConverter {
fn fold(&mut self, node: MemberExpr) -> MemberExpr {
if node.computed {
MemberExpr {
obj: node.obj.fold_with(self),
prop: node.prop.fold_with(self),
..node
}
} else {
MemberExpr {
obj: node.obj.fold_with(self),
..node
}
}
}
}
impl Fold<Ident> for ClassFieldAccessConverter {
fn fold(&mut self, node: Ident) -> Ident {
if node.sym == self.cls_name.sym && node.span.ctxt() == self.cls_name.span.ctxt() {
return self.alias.clone();
}
node
}
}

View File

@ -434,7 +434,7 @@ impl Strip {
node
}
fn handle_enum(&mut self, mut e: TsEnumDecl, stmts: &mut Vec<ModuleItem>) {
fn handle_enum(&mut self, e: TsEnumDecl, stmts: &mut Vec<ModuleItem>) {
/// Value does not contain TsLit::Bool
type EnumValues = FxHashMap<Id, TsLit>;
@ -637,7 +637,7 @@ impl Strip {
.collect::<Result<Vec<_>, _>>()
.unwrap_or_else(|_| panic!("invalid value for enum is detected"));
let is_all_str = members.iter().all(|(m, v)| match v {
let is_all_str = members.iter().all(|(_, v)| match v {
Expr::Lit(Lit::Str(..)) => true,
_ => false,
});
@ -671,7 +671,7 @@ impl Strip {
stmts: members
.into_iter()
.enumerate()
.map(|(i, (m, val))| {
.map(|(_, (m, val))| {
let value = match m.id {
TsEnumMemberId::Str(s) => s,
TsEnumMemberId::Ident(i) => Str {

View File

@ -6,6 +6,7 @@
use swc_common::chain;
use swc_ecma_parser::{EsConfig, Syntax, TsConfig};
use swc_ecma_transforms::{
compat::es2015::classes::Classes,
pass::Pass,
proposals::{class_properties, decorators, decorators::Config},
resolver, typescript,
@ -4203,3 +4204,132 @@ let Example = ((_class = class Example{
}), _class);
"
);
test!(
Syntax::Typescript(TsConfig {
decorators: true,
..Default::default()
}),
|_| chain!(typescript::strip(), decorators(Config { legacy: true })),
issue_823_1,
"import {Debounce} from 'lodash-decorators';
class Person {
private static debounceTime: number = 500 as const;
@Debounce(Person.debounceTime)
save() {
console.log('Hello World!');
}
}
const p = new Person();
p.save();",
"var _class, _dec;
import { Debounce } from 'lodash-decorators';
let Person = ((_class = class Person {
static debounceTime = 500;
save() {
console.log('Hello World!');
}
}) || _class, _dec = Debounce(_class.debounceTime), _applyDecoratedDescriptor(_class.prototype, \
'save', [
_dec
], Object.getOwnPropertyDescriptor(_class.prototype, 'save'), _class.prototype), _class);
const p = new Person();
p.save();"
);
test!(
Syntax::Typescript(TsConfig {
decorators: true,
..Default::default()
}),
|_| chain!(
typescript::strip(),
decorators(Config { legacy: true }),
class_properties(),
// Classes::default(),
),
issue_823_2,
"import {Debounce} from 'lodash-decorators';
class Person {
private static debounceTime: number = 500 as const;
@Debounce(Person.debounceTime)
save() {
console.log('Hello World!');
}
}
const p = new Person();
p.save();",
"var _class, _dec;
import { Debounce } from 'lodash-decorators';
let Person = ((_class = function() {
class Person {
save() {
console.log('Hello World!');
}
}
_defineProperty(Person, 'debounceTime', 500);
return Person;
}()) || _class, _dec = Debounce(_class.debounceTime), _applyDecoratedDescriptor(_class.prototype, \
'save', [
_dec
], Object.getOwnPropertyDescriptor(_class.prototype, 'save'), _class.prototype), _class);
const p = new Person();
p.save();
"
);
test!(
Syntax::Typescript(TsConfig {
decorators: true,
..Default::default()
}),
|_| chain!(
typescript::strip(),
decorators(Config { legacy: true }),
class_properties(),
Classes::default(),
),
issue_823_3,
"import {Debounce} from 'lodash-decorators';
class Person {
private static debounceTime: number = 500 as const;
@Debounce(Person.debounceTime)
save() {
console.log('Hello World!');
}
}
const p = new Person();
p.save();",
"var _class, _dec;
import { Debounce } from 'lodash-decorators';
let Person = ((_class = function() {
let Person = function() {
'use strict';
function Person() {
_classCallCheck(this, Person);
}
_createClass(Person, [
{
key: 'save',
value: function save() {
console.log('Hello World!');
}
}
]);
return Person;
}();
_defineProperty(Person, 'debounceTime', 500);
return Person;
}()) || _class, _dec = Debounce(_class.debounceTime), _applyDecoratedDescriptor(_class.prototype, \
'save', [
_dec
], Object.getOwnPropertyDescriptor(_class.prototype, 'save'), _class.prototype), _class);
const p = new Person();
p.save();"
);