From e1f5d681e3f679dda318de007d5eef9f8f91e46b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Mon, 10 Aug 2020 01:22:15 +0900 Subject: [PATCH] Fix typescript class properties pass (#951) --- ecmascript/transforms/Cargo.toml | 2 +- .../src/compat/es2020/class_properties.rs | 75 +++++++++++++++---- ecmascript/transforms/src/lib.rs | 1 + .../transforms/tests/typescript_strip.rs | 50 ++++++++++++- package.json | 2 +- src/builder.rs | 25 +++---- src/config.rs | 2 + 7 files changed, 119 insertions(+), 38 deletions(-) diff --git a/ecmascript/transforms/Cargo.toml b/ecmascript/transforms/Cargo.toml index 3264b26cdab..7d5c45dc6ea 100644 --- a/ecmascript/transforms/Cargo.toml +++ b/ecmascript/transforms/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "swc_ecma_transforms" -version = "0.19.1" +version = "0.19.2" authors = ["강동윤 "] license = "Apache-2.0/MIT" repository = "https://github.com/swc-project/swc.git" diff --git a/ecmascript/transforms/src/compat/es2020/class_properties.rs b/ecmascript/transforms/src/compat/es2020/class_properties.rs index 63ca3655951..b1ff27ede97 100644 --- a/ecmascript/transforms/src/compat/es2020/class_properties.rs +++ b/ecmascript/transforms/src/compat/es2020/class_properties.rs @@ -11,9 +11,9 @@ use crate::{ undefined, ExprFactory, ModuleItemLike, StmtLike, }, }; -use std::collections::HashSet; +use std::{collections::HashSet, mem::take}; use swc_atoms::JsWord; -use swc_common::{Mark, Spanned, DUMMY_SP}; +use swc_common::{util::move_map::MoveMap, Mark, Spanned, DUMMY_SP}; use swc_ecma_ast::*; use swc_ecma_visit::{Fold, FoldWith, VisitWith}; @@ -281,6 +281,8 @@ impl ClassProperties { let has_super = class.super_class.is_some(); + let mut typescript_constructor_properties = vec![]; + let (mut constructor_exprs, mut vars, mut extra_stmts, mut members, mut constructor) = (vec![], vec![], vec![], vec![], None); let mut used_names = vec![]; @@ -562,10 +564,61 @@ impl ClassProperties { }))); } - ClassMember::Constructor(c) => constructor = Some(c), + ClassMember::Constructor(mut c) => { + if self.typescript { + let store = |i: &Ident| { + Box::new( + AssignExpr { + span: DUMMY_SP, + left: PatOrExpr::Expr(Box::new(Expr::Member(MemberExpr { + span: DUMMY_SP, + obj: ThisExpr { span: DUMMY_SP }.as_obj(), + computed: false, + prop: Box::new(i.clone().into()), + }))), + op: op!("="), + right: Box::new(i.clone().into()), + } + .into(), + ) + }; + + c.params = c.params.move_map(|mut param| match &mut param { + ParamOrTsParamProp::TsParamProp(p) => match &p.param { + TsParamPropParam::Ident(i) => { + typescript_constructor_properties.push(store(i)); + ParamOrTsParamProp::Param(Param { + span: p.span, + decorators: take(&mut p.decorators), + pat: Pat::Ident(i.clone()), + }) + } + TsParamPropParam::Assign(pat) => match &*pat.left { + Pat::Ident(i) => { + typescript_constructor_properties.push(store(i)); + ParamOrTsParamProp::Param(Param { + span: p.span, + decorators: take(&mut p.decorators), + pat: Pat::Ident(i.clone()), + }) + } + _ => param, + }, + }, + ParamOrTsParamProp::Param(_) => param, + }); + } + + constructor = Some(c); + } } } + let constructor_exprs = { + typescript_constructor_properties.extend(constructor_exprs); + typescript_constructor_properties + }; + let constructor = self.process_constructor(constructor, has_super, &used_names, constructor_exprs); if let Some(c) = constructor { @@ -668,19 +721,9 @@ impl ClassProperties { } }); - if let Some(mut c) = constructor { - if self.typescript { - // Append properties - c.body - .as_mut() - .unwrap() - .stmts - .extend(constructor_exprs.into_iter().map(|v| v.into_stmt())); - Some(c) - } else { - // Prepend properties - Some(inject_after_super(c, constructor_exprs)) - } + if let Some(c) = constructor { + // Prepend properties + Some(inject_after_super(c, constructor_exprs)) } else { None } diff --git a/ecmascript/transforms/src/lib.rs b/ecmascript/transforms/src/lib.rs index 31db57b8cde..3d6635f8de9 100644 --- a/ecmascript/transforms/src/lib.rs +++ b/ecmascript/transforms/src/lib.rs @@ -1,5 +1,6 @@ #![cfg_attr(test, feature(test))] #![recursion_limit = "1024"] +#![deny(unused)] #[macro_use] extern crate swc_ecma_utils; diff --git a/ecmascript/transforms/tests/typescript_strip.rs b/ecmascript/transforms/tests/typescript_strip.rs index 604e8ba0adb..64aca7549ad 100644 --- a/ecmascript/transforms/tests/typescript_strip.rs +++ b/ecmascript/transforms/tests/typescript_strip.rs @@ -705,12 +705,11 @@ to!( test!( ::swc_ecma_parser::Syntax::Typescript(Default::default()), - |_| chain!(tr(), typescript_class_properties()), + |_| chain!(typescript_class_properties(), tr()), issue_930_instance, "class A { b = this.a; - constructor(a){ - this.a = a; + constructor(readonly a){ } }", "class A { @@ -723,7 +722,7 @@ test!( test!( ::swc_ecma_parser::Syntax::Typescript(Default::default()), - |_| chain!(tr(), typescript_class_properties()), + |_| chain!(typescript_class_properties(), tr()), issue_930_static, "class A { static b = 'foo'; @@ -736,3 +735,46 @@ test!( } A.b = 'foo';" ); + +test!( + ::swc_ecma_parser::Syntax::Typescript(Default::default()), + |_| chain!(typescript_class_properties(), tr()), + typescript_001, + "class A { + foo = new Subject() + + constructor() { + this.foo.subscribe() + } + }", + "class A { + constructor() { + this.foo = new Subject() + this.foo.subscribe() + } + }" +); + +test!( + ::swc_ecma_parser::Syntax::Typescript(Default::default()), + |_| chain!(typescript_class_properties(), tr()), + typescript_002, + "class A extends B { + foo = 'foo' + b = this.a; + + constructor(readonly a) { + super() + this.foo.subscribe() + } + }", + "class A extends B { + constructor(a) { + super(); + this.a = a; + this.foo = 'foo'; + this.b = this.a; + this.foo.subscribe(); + } + }" +); diff --git a/package.json b/package.json index 21d829e274a..c0570d93dc4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@swc/core", - "version": "1.2.15", + "version": "1.2.16", "description": "Super-fast alternative for babel", "main": "./index.js", "author": "강동윤 ", diff --git a/src/builder.rs b/src/builder.rs index 7c3e7212d5b..ebd902f61d6 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -88,10 +88,6 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> { self.then(pass) } - pub fn strip_typescript(self) -> PassBuilder<'a, 'b, impl swc_ecma_visit::Fold> { - self.then(typescript::strip()) - } - pub fn target(mut self, target: JscTarget) -> Self { self.target = target; self @@ -133,7 +129,10 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> { // compat let compat_pass = if let Some(env) = self.env { - Either::Left(swc_ecma_preset_env::preset_env(self.global_mark, env)) + Either::Left(chain!( + Optional::new(typescript::strip(), syntax.typescript()), + swc_ecma_preset_env::preset_env(self.global_mark, env) + )) } else { Either::Right(chain!( Optional::new( @@ -144,17 +143,11 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> { compat::es2020::optional_chaining(), self.target < JscTarget::Es2020 ), - if syntax.typescript() { - Either::Left(Optional::new( - compat::es2020::typescript_class_properties(), - self.target < JscTarget::Es2020, - )) - } else { - Either::Right(Optional::new( - compat::es2020::class_properties(), - self.target < JscTarget::Es2020, - )) - }, + Optional::new( + compat::es2020::class_properties(), + self.target < JscTarget::Es2020, + ), + Optional::new(typescript::strip(), syntax.typescript()), Optional::new(compat::es2018(), self.target <= JscTarget::Es2018), Optional::new(compat::es2017(), self.target <= JscTarget::Es2017), Optional::new(compat::es2016(), self.target <= JscTarget::Es2016), diff --git a/src/config.rs b/src/config.rs index 6825e8f5c80..dd634812a6f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -18,6 +18,7 @@ use swc_ecma_ast::{Expr, ExprStmt, ModuleItem, Stmt}; pub use swc_ecma_parser::JscTarget; use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax, TsConfig}; use swc_ecma_transforms::{ + compat::es2020::typescript_class_properties, const_modules, modules, optimization::{simplifier, InlineGlobals, JsonParse}, pass::{noop, Optional}, @@ -234,6 +235,7 @@ impl Options { }), syntax.decorators() ), + Optional::new(typescript_class_properties(), syntax.typescript()), Optional::new(typescript::strip(), syntax.typescript()), resolver_with_mark(root_mark), const_modules,