diff --git a/crates/swc/src/builder.rs b/crates/swc/src/builder.rs index 9308c82e17da..d53de10403e5 100644 --- a/crates/swc/src/builder.rs +++ b/crates/swc/src/builder.rs @@ -186,7 +186,15 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> { should_enable(self.target, EsVersion::Es2021) ), Optional::new( - compat::es2020::es2020(), + compat::es2020::es2020(compat::es2020::Config { + nullish_coalescing: compat::es2020::nullish_coalescing::Config { + no_document_all: self.loose + }, + optional_chaining: compat::es2020::opt_chaining::Config { + no_document_all: self.loose, + pure_getter: self.loose + } + }), should_enable(self.target, EsVersion::Es2020) ), Optional::new( @@ -194,7 +202,12 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> { should_enable(self.target, EsVersion::Es2019) ), Optional::new( - compat::es2018(), + compat::es2018(compat::es2018::Config { + object_rest_spread: compat::es2018::object_rest_spread::Config { + no_symbol: self.loose, + set_property: self.loose + } + }), should_enable(self.target, EsVersion::Es2018) ), Optional::new( @@ -210,6 +223,9 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> { self.top_level_mark, comments.clone(), compat::es2015::Config { + computed_props: compat::es2015::computed_props::Config { + loose: self.loose + }, for_of: compat::es2015::for_of::Config { assume_array: self.loose }, @@ -218,6 +234,10 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> { loose: self.loose }, regenerator: self.regenerator, + template_literal: compat::es2015::template_literal::Config { + ignore_to_primitive: self.loose, + mutable_template: self.loose + } } ), should_enable(self.target, EsVersion::Es2015) diff --git a/crates/swc/tests/tsc-references/expressions/optionalChaining/callChain/callChain/input.ts/es2015.1.normal/output.js b/crates/swc/tests/tsc-references/expressions/optionalChaining/callChain/callChain/input.ts/es2015.1.normal/output.js index ac6486f64571..3c188fb03e48 100644 --- a/crates/swc/tests/tsc-references/expressions/optionalChaining/callChain/callChain/input.ts/es2015.1.normal/output.js +++ b/crates/swc/tests/tsc-references/expressions/optionalChaining/callChain/callChain/input.ts/es2015.1.normal/output.js @@ -1,4 +1,4 @@ -var ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, ref10, ref11, _obj, ref12; +var ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, ref10, ref11, ref12; o1 === null || o1 === void 0 ? void 0 : o1(); o1 === null || o1 === void 0 ? void 0 : o1(1); o1 === null || o1 === void 0 ? void 0 : o1(...[ @@ -60,7 +60,7 @@ o2 === null || o2 === void 0 ? void 0 : o2["b"](1, ...[ 3 ], 4).c; const v = o4 === null || o4 === void 0 ? void 0 : o4(incr); -(ref12 = _obj = o5()) === null || ref12 === void 0 ? void 0 : ref12.call(_obj); +(ref12 = o5()) === null || ref12 === void 0 ? void 0 : ref12(); // GH#36031 o2 === null || o2 === void 0 ? void 0 : o2.b().toString; o2 === null || o2 === void 0 ? void 0 : o2.b().toString; diff --git a/crates/swc/tests/tsc-references/expressions/optionalChaining/callChain/callChain/input.ts/es2015.2.minified/output.js b/crates/swc/tests/tsc-references/expressions/optionalChaining/callChain/callChain/input.ts/es2015.2.minified/output.js index e4a04f478908..0e9ea3b30e65 100644 --- a/crates/swc/tests/tsc-references/expressions/optionalChaining/callChain/callChain/input.ts/es2015.2.minified/output.js +++ b/crates/swc/tests/tsc-references/expressions/optionalChaining/callChain/callChain/input.ts/es2015.2.minified/output.js @@ -1,4 +1,4 @@ -var ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, ref10, ref11, _obj, ref12; +var ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, ref10, ref11, ref12; null == o1 || o1(), null == o1 || o1(1), null == o1 || o1(...[ 1, 2 @@ -35,4 +35,4 @@ null == o1 || o1(), null == o1 || o1(1), null == o1 || o1(...[ ]).c, null === (ref11 = o3.b) || void 0 === ref11 || ref11.call(o3, 1, ...[ 2, 3 -], 4).c, null == o4 || o4(incr), null === (ref12 = _obj = o5()) || void 0 === ref12 || ref12.call(_obj), null == o2 || o2.b().toString, null == o2 || o2.b().toString; +], 4).c, null == o4 || o4(incr), null === (ref12 = o5()) || void 0 === ref12 || ref12(), null == o2 || o2.b().toString, null == o2 || o2.b().toString; diff --git a/crates/swc/tests/tsc-references/expressions/optionalChaining/callChain/callChain/input.ts/es5.1.normal/output.js b/crates/swc/tests/tsc-references/expressions/optionalChaining/callChain/callChain/input.ts/es5.1.normal/output.js index 88a298c0fa95..135c4584b138 100644 --- a/crates/swc/tests/tsc-references/expressions/optionalChaining/callChain/callChain/input.ts/es5.1.normal/output.js +++ b/crates/swc/tests/tsc-references/expressions/optionalChaining/callChain/callChain/input.ts/es5.1.normal/output.js @@ -16,7 +16,7 @@ function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } var _o2, _o21, _o22, _o23, _ref, _ref1, _ref2, _ref3, _ref4, _ref5; -var ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, ref10, ref11, _obj, ref12; +var ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, ref10, ref11, ref12; o1 === null || o1 === void 0 ? void 0 : o1(); o1 === null || o1 === void 0 ? void 0 : o1(1); o1 === null || o1 === void 0 ? void 0 : o1.apply(void 0, [ @@ -102,7 +102,7 @@ o2 === null || o2 === void 0 ? void 0 : (_o23 = o2)["b"].apply(_o23, [ 4 ])).c; var v = o4 === null || o4 === void 0 ? void 0 : o4(incr); -(ref12 = _obj = o5()) === null || ref12 === void 0 ? void 0 : ref12.call(_obj); +(ref12 = o5()) === null || ref12 === void 0 ? void 0 : ref12(); // GH#36031 o2 === null || o2 === void 0 ? void 0 : o2.b().toString; o2 === null || o2 === void 0 ? void 0 : o2.b().toString; diff --git a/crates/swc/tests/tsc-references/expressions/optionalChaining/callChain/callChain/input.ts/es5.2.minified/output.js b/crates/swc/tests/tsc-references/expressions/optionalChaining/callChain/callChain/input.ts/es5.2.minified/output.js index 368ce98d925a..2b719854b413 100644 --- a/crates/swc/tests/tsc-references/expressions/optionalChaining/callChain/callChain/input.ts/es5.2.minified/output.js +++ b/crates/swc/tests/tsc-references/expressions/optionalChaining/callChain/callChain/input.ts/es5.2.minified/output.js @@ -1,4 +1,4 @@ -var _o2, _o21, _o22, _o23, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, ref10, ref11, _obj, ref12; +var _o2, _o21, _o22, _o23, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, ref10, ref11, ref12; function _toConsumableArray(arr) { return (function(arr) { if (Array.isArray(arr)) { @@ -71,4 +71,4 @@ null == o1 || o1(), null == o1 || o1(1), null == o1 || o1.apply(void 0, [ 3 ]), [ 4 -])).c, null == o4 || o4(incr), null === (ref12 = _obj = o5()) || void 0 === ref12 || ref12.call(_obj), null == o2 || o2.b().toString, null == o2 || o2.b().toString; +])).c, null == o4 || o4(incr), null === (ref12 = o5()) || void 0 === ref12 || ref12(), null == o2 || o2.b().toString, null == o2 || o2.b().toString; diff --git a/crates/swc_babel_compat/benches/babelify.rs b/crates/swc_babel_compat/benches/babelify.rs index e45699887663..6c43f4199516 100644 --- a/crates/swc_babel_compat/benches/babelify.rs +++ b/crates/swc_babel_compat/benches/babelify.rs @@ -55,7 +55,7 @@ fn babelify_only(b: &mut Bencher) { let module = c.run_transform(&handler, false, || { module .fold_with(&mut typescript::strip()) - .fold_with(&mut es2020()) + .fold_with(&mut es2020(Default::default())) }); b.iter(|| { diff --git a/crates/swc_ecma_preset_env/src/lib.rs b/crates/swc_ecma_preset_env/src/lib.rs index 90dce43f5512..5509506abe53 100644 --- a/crates/swc_ecma_preset_env/src/lib.rs +++ b/crates/swc_ecma_preset_env/src/lib.rs @@ -105,15 +105,35 @@ where // ES2020 let pass = add!(pass, ExportNamespaceFrom, es2020::export_namespace_from()); - let pass = add!(pass, NullishCoalescing, es2020::nullish_coalescing()); + let pass = add!( + pass, + NullishCoalescing, + es2020::nullish_coalescing(es2020::nullish_coalescing::Config { + no_document_all: loose + }) + ); - let pass = add!(pass, OptionalChaining, es2020::optional_chaining()); + let pass = add!( + pass, + OptionalChaining, + es2020::optional_chaining(es2020::opt_chaining::Config { + no_document_all: loose, + pure_getter: loose + }) + ); // ES2019 let pass = add!(pass, OptionalCatchBinding, es2019::optional_catch_binding()); // ES2018 - let pass = add!(pass, ObjectRestSpread, es2018::object_rest_spread()); + let pass = add!( + pass, + ObjectRestSpread, + es2018::object_rest_spread(es2018::object_rest_spread::Config { + no_symbol: loose, + set_property: loose + }) + ); // ES2017 let pass = add!(pass, AsyncToGenerator, es2017::async_to_generator()); @@ -123,7 +143,15 @@ where // ES2015 let pass = add!(pass, BlockScopedFunctions, es2015::block_scoped_functions()); - let pass = add!(pass, TemplateLiterals, es2015::template_literal(), true); + let pass = add!( + pass, + TemplateLiterals, + es2015::template_literal(es2015::template_literal::Config { + ignore_to_primitive: loose, + mutable_template: loose + }), + true + ); let pass = add!(pass, Classes, es2015::classes(comments)); let pass = add!( pass, @@ -150,7 +178,7 @@ where let pass = add!( pass, ComputedProperties, - es2015::computed_properties(), + es2015::computed_properties(es2015::computed_props::Config { loose }), true ); let pass = add!( diff --git a/crates/swc_ecma_transforms_compat/src/es2015/computed_props.rs b/crates/swc_ecma_transforms_compat/src/es2015/computed_props.rs index 258449a0f488..7d98f7b670be 100644 --- a/crates/swc_ecma_transforms_compat/src/es2015/computed_props.rs +++ b/crates/swc_ecma_transforms_compat/src/es2015/computed_props.rs @@ -1,3 +1,4 @@ +use serde::Deserialize; use swc_common::{Mark, Spanned, DUMMY_SP}; use swc_ecma_ast::*; use swc_ecma_transforms_base::helper; @@ -35,16 +36,26 @@ use swc_ecma_visit::{noop_fold_type, noop_visit_type, Fold, FoldWith, Node, Visi /// /// TODO(kdy1): cache reference like (_f = f, mutatorMap[_f].get = function(){}) /// instead of (mutatorMap[f].get = function(){} -pub fn computed_properties() -> impl Fold { - ComputedProps +pub fn computed_properties(c: Config) -> impl Fold { + ComputedProps { c } } -struct ComputedProps; +#[derive(Debug, Clone, Copy, Default, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Config { + #[serde(default)] + pub loose: bool, +} + +struct ComputedProps { + c: Config, +} #[derive(Default)] struct ObjectLitFolder { vars: Vec, used_define_enum_props: bool, + c: Config, } /// TODO: VisitMut @@ -75,7 +86,7 @@ impl Fold for ObjectLitFolder { let props_cnt = props.len(); - exprs.push(if props_cnt == 1 { + exprs.push(if !self.c.loose && props_cnt == 1 { Box::new(Expr::Object(ObjectLit { span: DUMMY_SP, props: obj_props, @@ -95,19 +106,26 @@ impl Fold for ObjectLitFolder { for prop in props { let span = prop.span(); - let (key, value) = match prop { + let ((key, is_compute), value) = match prop { PropOrSpread::Prop(prop) => match *prop { Prop::Shorthand(ident) => ( - Expr::Lit(Lit::Str(Str { - span: ident.span, - value: ident.sym.clone(), - has_escape: false, - kind: Default::default(), - })), + ( + if self.c.loose { + Expr::Ident(ident.clone()) + } else { + Expr::Lit(Lit::Str(Str { + span: ident.span, + value: ident.sym.clone(), + has_escape: false, + kind: Default::default(), + })) + }, + false, + ), Expr::Ident(ident), ), Prop::KeyValue(KeyValueProp { key, value }) => { - (prop_name_to_expr(key), *value) + (prop_name_to_expr(key, self.c.loose), *value) } Prop::Assign(..) => { unreachable!("assign property in object literal is invalid") @@ -167,8 +185,9 @@ impl Fold for ObjectLitFolder { }; // mutator[f] - let mutator_elem = - mutator_map.clone().computed_member(prop_name_to_expr(key)); + let mutator_elem = mutator_map + .clone() + .computed_member(prop_name_to_expr(key, false).0); // mutator[f] = mutator[f] || {} exprs.push(Box::new(Expr::Assign(AssignExpr { @@ -204,7 +223,7 @@ impl Fold for ObjectLitFolder { // unimplemented!("getter /setter property") } Prop::Method(MethodProp { key, function }) => ( - prop_name_to_expr(key), + prop_name_to_expr(key, self.c.loose), Expr::Fn(FnExpr { ident: None, function, @@ -214,7 +233,7 @@ impl Fold for ObjectLitFolder { PropOrSpread::Spread(..) => unimplemented!("computed spread property"), }; - if props_cnt == 1 { + if !self.c.loose && props_cnt == 1 { return Expr::Call(CallExpr { span, callee: helper!(define_property, "defineProperty"), @@ -222,12 +241,26 @@ impl Fold for ObjectLitFolder { type_args: Default::default(), }); } - exprs.push(Box::new(Expr::Call(CallExpr { - span, - callee: helper!(define_property, "defineProperty"), - args: vec![obj_ident.clone().as_arg(), key.as_arg(), value.as_arg()], - type_args: Default::default(), - }))); + exprs.push(if self.c.loose { + let left = if is_compute { + obj_ident.clone().computed_member(key) + } else { + obj_ident.clone().make_member(key) + }; + Box::new(Expr::Assign(AssignExpr { + span, + op: AssignOp::Assign, + left: PatOrExpr::Expr(Box::new(left)), + right: value.into(), + })) + } else { + Box::new(Expr::Call(CallExpr { + span, + callee: helper!(define_property, "defineProperty"), + args: vec![obj_ident.clone().as_arg(), key.as_arg(), value.as_arg()], + type_args: Default::default(), + })) + }); } self.vars.push(VarDeclarator { @@ -317,7 +350,10 @@ impl ComputedProps { continue; } - let mut folder = ObjectLitFolder::default(); + let mut folder = ObjectLitFolder { + c: self.c, + ..Default::default() + }; let stmt = stmt.fold_with(&mut folder); // Add variable declaration @@ -338,18 +374,25 @@ impl ComputedProps { } } -fn prop_name_to_expr(p: PropName) -> Expr { +fn prop_name_to_expr(p: PropName, loose: bool) -> (Expr, bool) { match p { - PropName::Ident(i) => Expr::Lit(Lit::Str(Str { - value: i.sym, - span: i.span, - has_escape: false, - kind: Default::default(), - })), - PropName::Str(s) => Expr::Lit(Lit::Str(s)), - PropName::Num(n) => Expr::Lit(Lit::Num(n)), - PropName::BigInt(b) => Expr::Lit(Lit::BigInt(b)), - PropName::Computed(c) => *c.expr, + PropName::Ident(i) => ( + if loose { + Expr::Ident(i) + } else { + Expr::Lit(Lit::Str(Str { + value: i.sym, + span: i.span, + has_escape: false, + kind: Default::default(), + })) + }, + false, + ), + PropName::Str(s) => (Expr::Lit(Lit::Str(s)), true), + PropName::Num(n) => (Expr::Lit(Lit::Num(n)), true), + PropName::BigInt(b) => (Expr::Lit(Lit::BigInt(b)), true), + PropName::Computed(c) => (*c.expr, true), } } diff --git a/crates/swc_ecma_transforms_compat/src/es2015/mod.rs b/crates/swc_ecma_transforms_compat/src/es2015/mod.rs index 84fce7f46a1b..7323b0dfdaea 100644 --- a/crates/swc_ecma_transforms_compat/src/es2015/mod.rs +++ b/crates/swc_ecma_transforms_compat/src/es2015/mod.rs @@ -14,7 +14,7 @@ mod arrow; mod block_scoped_fn; mod block_scoping; pub mod classes; -mod computed_props; +pub mod computed_props; pub mod destructuring; mod duplicate_keys; pub mod for_of; @@ -26,7 +26,7 @@ pub mod regenerator; mod shorthand_property; pub mod spread; mod sticky_regex; -mod template_literal; +pub mod template_literal; mod typeof_symbol; fn exprs() -> impl Fold { @@ -47,7 +47,7 @@ where { chain!( block_scoped_functions(), - template_literal(), + template_literal(c.template_literal), new_target(), classes(comments), spread(c.spread), @@ -57,7 +57,7 @@ where // Should come before parameters // See: https://github.com/swc-project/swc/issues/1036 parameters(), - computed_properties(), + computed_properties(c.computed_props), destructuring(c.destructuring), block_scoping(), regenerator(c.regenerator, global_mark), @@ -67,6 +67,9 @@ where #[derive(Debug, Clone, Default, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Config { + #[serde(flatten)] + pub computed_props: computed_props::Config, + #[serde(flatten)] pub for_of: for_of::Config, @@ -78,6 +81,9 @@ pub struct Config { #[serde(default)] pub regenerator: regenerator::Config, + + #[serde(default)] + pub template_literal: template_literal::Config, } #[cfg(test)] diff --git a/crates/swc_ecma_transforms_compat/src/es2015/template_literal.rs b/crates/swc_ecma_transforms_compat/src/es2015/template_literal.rs index 457bdeb0eedf..1fca7421b6be 100644 --- a/crates/swc_ecma_transforms_compat/src/es2015/template_literal.rs +++ b/crates/swc_ecma_transforms_compat/src/es2015/template_literal.rs @@ -1,3 +1,4 @@ +use serde::Deserialize; use std::{iter, mem}; use swc_atoms::js_word; use swc_common::{util::take::Take, BytePos, Spanned, DUMMY_SP}; @@ -9,13 +10,26 @@ use swc_ecma_utils::{ }; use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith}; -pub fn template_literal() -> impl Fold + VisitMut { - as_folder(TemplateLiteral::default()) +pub fn template_literal(c: Config) -> impl Fold + VisitMut { + as_folder(TemplateLiteral { + c, + ..Default::default() + }) } #[derive(Default)] struct TemplateLiteral { added: Vec, + c: Config, +} + +#[derive(Debug, Clone, Copy, Default, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Config { + #[serde(default)] + pub ignore_to_primitive: bool, + #[serde(default)] + pub mutable_template: bool, } impl Parallel for TemplateLiteral { @@ -44,8 +58,6 @@ impl VisitMut for TemplateLiteral { }) => { assert_eq!(quasis.len(), exprs.len() + 1); - // TODO: Optimize - // This makes result of addition string let mut obj: Box = Box::new( Lit::Str({ @@ -108,12 +120,6 @@ impl VisitMut for TemplateLiteral { exprs.next().unwrap() }; - // obj = box Expr::Bin(BinExpr { - // span: expr.span(), - // left: obj, - // op: op!(bin, "+"), - // right: expr.into(), - // }); let expr_span = expr.span(); // We can optimize if expression is a literal or ident @@ -164,66 +170,90 @@ impl VisitMut for TemplateLiteral { } if !is_empty { - args.push(ExprOrSpread { expr, spread: None }); + args.push(expr); } if last && !args.is_empty() { - obj = Box::new(Expr::Call(CallExpr { - span: span.with_hi(expr_span.hi() + BytePos(1)), - callee: ExprOrSuper::Expr(Box::new(Expr::Member(MemberExpr { - span: DUMMY_SP, - obj: ExprOrSuper::Expr(obj), - prop: Box::new(Expr::Ident(Ident::new( - js_word!("concat"), - expr_span, - ))), - - computed: false, - }))), - args: mem::replace(&mut args, vec![]), - type_args: Default::default(), - })); + obj = if self.c.ignore_to_primitive { + let args = mem::replace(&mut args, vec![]); + for arg in args { + obj = Box::new(Expr::Bin(BinExpr { + span: span.with_hi(expr_span.hi() + BytePos(1)), + op: BinaryOp::Add, + left: obj, + right: arg, + })) + } + obj + } else { + Box::new(Expr::Call(CallExpr { + span: span.with_hi(expr_span.hi() + BytePos(1)), + callee: ExprOrSuper::Expr(Box::new(Expr::Member(MemberExpr { + span: DUMMY_SP, + obj: ExprOrSuper::Expr(obj), + prop: Box::new(Expr::Ident(Ident::new( + js_word!("concat"), + expr_span, + ))), + + computed: false, + }))), + args: mem::replace(&mut args, vec![]) + .into_iter() + .map(|expr| ExprOrSpread { expr, spread: None }) + .collect(), + type_args: Default::default(), + })) + } } } else { if !args.is_empty() { - obj = Box::new(Expr::Call(CallExpr { - span: span.with_hi(expr_span.hi() + BytePos(1)), - callee: ExprOrSuper::Expr(Box::new(Expr::Member(MemberExpr { - span: DUMMY_SP, - obj: ExprOrSuper::Expr(obj), - prop: Box::new(Expr::Ident(Ident::new( - js_word!("concat"), - expr_span, - ))), - - computed: false, - }))), - args: mem::replace(&mut args, vec![]), - type_args: Default::default(), - })); + obj = if self.c.ignore_to_primitive { + let args = mem::replace(&mut args, vec![]); + let len = args.len(); + for arg in args { + // for `${asd}a` + if let Expr::Lit(Lit::Str(str)) = obj.as_ref() { + if str.value.len() == 0 && len == 2 { + obj = arg; + continue; + } + } + obj = Box::new(Expr::Bin(BinExpr { + span: span.with_hi(expr_span.hi() + BytePos(1)), + op: BinaryOp::Add, + left: obj, + right: arg, + })) + } + obj + } else { + Box::new(Expr::Call(CallExpr { + span: span.with_hi(expr_span.hi() + BytePos(1)), + callee: ExprOrSuper::Expr(Box::new(Expr::Member(MemberExpr { + span: DUMMY_SP, + obj: ExprOrSuper::Expr(obj), + prop: Box::new(Expr::Ident(Ident::new( + js_word!("concat"), + expr_span, + ))), + + computed: false, + }))), + args: mem::replace(&mut args, vec![]) + .into_iter() + .map(|expr| ExprOrSpread { expr, spread: None }) + .collect(), + type_args: Default::default(), + })) + }; } debug_assert!(args.is_empty()); - args.push(ExprOrSpread { expr, spread: None }); + args.push(expr); } } - // if !args.is_empty() { - // obj = box validate!(Expr::Call(CallExpr { - // span: span.with_hi(expr_span.hi() + BytePos(1)), - // callee: ExprOrSuper::Expr(box Expr::Member(MemberExpr - // { span: DUMMY_SP, - // obj: ExprOrSuper::Expr(validate!(obj)), - // prop: box - // Expr::Ident(Ident::new(js_word!("concat"), expr_span)), - // - // computed: false, - // })), - // args: mem::replace(&mut args, vec![]), - // type_args: Default::default(), - // })); - // } - *e = *obj } @@ -253,10 +283,14 @@ impl VisitMut for TemplateLiteral { definite: false, init: Some(Box::new(Expr::Call(CallExpr { span: DUMMY_SP, - callee: helper!( - tagged_template_literal, - "taggedTemplateLiteral" - ), + callee: if self.c.mutable_template { + helper!( + tagged_template_literal_loose, + "taggedTemplateLiteralLoose" + ) + } else { + helper!(tagged_template_literal, "taggedTemplateLiteral") + }, args: { let has_escape = quasis.iter().any(|s| { s.cooked.as_ref().map(|s| s.has_escape).unwrap_or(true) diff --git a/crates/swc_ecma_transforms_compat/src/es2018/mod.rs b/crates/swc_ecma_transforms_compat/src/es2018/mod.rs index d35a2bc1df0f..7482223a1d2e 100644 --- a/crates/swc_ecma_transforms_compat/src/es2018/mod.rs +++ b/crates/swc_ecma_transforms_compat/src/es2018/mod.rs @@ -1,8 +1,15 @@ pub use self::object_rest_spread::object_rest_spread; +use serde::Deserialize; use swc_ecma_visit::Fold; -mod object_rest_spread; +pub mod object_rest_spread; -pub fn es2018() -> impl Fold { - object_rest_spread() +pub fn es2018(c: Config) -> impl Fold { + object_rest_spread(c.object_rest_spread) +} + +#[derive(Debug, Clone, Copy, Default, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Config { + pub object_rest_spread: object_rest_spread::Config, } diff --git a/crates/swc_ecma_transforms_compat/src/es2018/object_rest_spread.rs b/crates/swc_ecma_transforms_compat/src/es2018/object_rest_spread.rs index 166011365774..ad2b564c7d74 100644 --- a/crates/swc_ecma_transforms_compat/src/es2018/object_rest_spread.rs +++ b/crates/swc_ecma_transforms_compat/src/es2018/object_rest_spread.rs @@ -1,3 +1,4 @@ +use serde::Deserialize; use std::{iter, mem}; use swc_common::{chain, util::take::Take, Mark, Spanned, DUMMY_SP}; use swc_ecma_ast::*; @@ -15,9 +16,18 @@ use swc_ecma_visit::{ VisitMut, VisitMutWith, VisitWith, }; +// TODO: currently swc behaves like babel with +// `ignoreFunctionLength` and `pureGetters` on + /// `@babel/plugin-proposal-object-rest-spread` -pub fn object_rest_spread() -> impl Fold { - chain!(ObjectRest::default(), as_folder(ObjectSpread)) +pub fn object_rest_spread(c: Config) -> impl Fold { + chain!( + ObjectRest { + c, + ..Default::default() + }, + as_folder(ObjectSpread { c }) + ) } #[derive(Default)] @@ -28,6 +38,16 @@ struct ObjectRest { mutable_vars: Vec, /// Assignment expressions. exprs: Vec>, + c: Config, +} + +#[derive(Debug, Clone, Copy, Default, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Config { + #[serde(default)] + pub no_symbol: bool, + #[serde(default)] + pub set_property: bool, } macro_rules! impl_for_for_stmt { @@ -441,7 +461,10 @@ impl ObjectRest { let mut buf = Vec::with_capacity(stmts.len()); for stmt in stmts { - let mut folder = ObjectRest::default(); + let mut folder = ObjectRest { + c: self.c, + ..Default::default() + }; let stmt = stmt.fold_with(&mut folder); // Add variable declaration @@ -843,14 +866,22 @@ impl ObjectRest { span: DUMMY_SP, left: PatOrExpr::Pat(last.arg), op: op!("="), - right: Box::new(object_without_properties(obj, excluded_props)), + right: Box::new(object_without_properties( + obj, + excluded_props, + self.c.no_symbol, + )), }))); } else { // println!("Var: rest = objectWithoutProperties()",); self.push_var_if_not_empty(VarDeclarator { span: DUMMY_SP, name: *last.arg, - init: Some(Box::new(object_without_properties(obj, excluded_props))), + init: Some(Box::new(object_without_properties( + obj, + excluded_props, + self.c.no_symbol, + ))), definite: false, }); } @@ -864,7 +895,11 @@ impl ObjectRest { } } -fn object_without_properties(obj: Box, excluded_props: Vec>) -> Expr { +fn object_without_properties( + obj: Box, + excluded_props: Vec>, + no_symbol: bool, +) -> Expr { if excluded_props.is_empty() { return Expr::Call(CallExpr { span: DUMMY_SP, @@ -901,7 +936,14 @@ fn object_without_properties(obj: Box, excluded_props: Vec Self { - ObjectSpread + ObjectSpread { c: self.c } } fn merge(&mut self, _: Self) {} @@ -1069,7 +1113,11 @@ impl VisitMut for ObjectSpread { *expr = Expr::Call(CallExpr { span: *span, - callee: helper!(object_spread, "objectSpread"), + callee: if self.c.set_property { + helper!(extends, "extends") + } else { + helper!(object_spread, "objectSpread") + }, args, type_args: Default::default(), }); diff --git a/crates/swc_ecma_transforms_compat/src/es2020/mod.rs b/crates/swc_ecma_transforms_compat/src/es2020/mod.rs index 18c9500dd1d2..aaf230110d44 100644 --- a/crates/swc_ecma_transforms_compat/src/es2020/mod.rs +++ b/crates/swc_ecma_transforms_compat/src/es2020/mod.rs @@ -2,17 +2,27 @@ pub use self::{ export_namespace_from::export_namespace_from, nullish_coalescing::nullish_coalescing, opt_chaining::optional_chaining, }; +use serde::Deserialize; use swc_common::chain; use swc_ecma_visit::Fold; mod export_namespace_from; -mod nullish_coalescing; -mod opt_chaining; +pub mod nullish_coalescing; +pub mod opt_chaining; -pub fn es2020() -> impl Fold { +pub fn es2020(config: Config) -> impl Fold { chain!( - nullish_coalescing(), - optional_chaining(), + nullish_coalescing(config.nullish_coalescing), + optional_chaining(config.optional_chaining), export_namespace_from(), ) } + +#[derive(Debug, Clone, Default, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Config { + #[serde(flatten)] + pub nullish_coalescing: nullish_coalescing::Config, + #[serde(flatten)] + pub optional_chaining: opt_chaining::Config, +} diff --git a/crates/swc_ecma_transforms_compat/src/es2020/nullish_coalescing/mod.rs b/crates/swc_ecma_transforms_compat/src/es2020/nullish_coalescing/mod.rs index fac9901fa443..73f87309822c 100644 --- a/crates/swc_ecma_transforms_compat/src/es2020/nullish_coalescing/mod.rs +++ b/crates/swc_ecma_transforms_compat/src/es2020/nullish_coalescing/mod.rs @@ -1,3 +1,4 @@ +use serde::Deserialize; use std::mem::take; use swc_common::{util::take::Take, Span, DUMMY_SP}; use swc_ecma_ast::*; @@ -7,13 +8,24 @@ use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWit #[cfg(test)] mod tests; -pub fn nullish_coalescing() -> impl Fold + VisitMut + 'static { - as_folder(NullishCoalescing::default()) +pub fn nullish_coalescing(c: Config) -> impl Fold + VisitMut + 'static { + as_folder(NullishCoalescing { + c, + ..Default::default() + }) } #[derive(Debug, Default)] struct NullishCoalescing { vars: Vec, + c: Config, +} + +#[derive(Debug, Clone, Copy, Default, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Config { + #[serde(default)] + pub no_document_all: bool, } impl NullishCoalescing { @@ -47,12 +59,16 @@ impl VisitMut for NullishCoalescing { /// Prevents #1123 fn visit_mut_block_stmt(&mut self, s: &mut BlockStmt) { - s.visit_mut_children_with(&mut NullishCoalescing::default()) + let old_vars = self.vars.take(); + s.visit_mut_children_with(self); + self.vars = old_vars; } /// Prevents #1123 fn visit_mut_switch_case(&mut self, s: &mut SwitchCase) { - s.visit_mut_children_with(&mut NullishCoalescing::default()) + let old_vars = self.vars.take(); + s.visit_mut_children_with(self); + self.vars = old_vars; } fn visit_mut_module_items(&mut self, n: &mut Vec) { @@ -96,7 +112,7 @@ impl VisitMut for NullishCoalescing { Expr::Ident(l.clone()) }; - *e = make_cond(*span, &l, var_expr, right.take()); + *e = make_cond(self.c, *span, &l, var_expr, right.take()); return; } @@ -140,7 +156,13 @@ impl VisitMut for NullishCoalescing { span: assign.span, op: op!("="), left: PatOrExpr::Pat(Box::new(Pat::Ident(alias.clone().into()))), - right: Box::new(make_cond(assign.span, &alias, var_expr, right_expr)), + right: Box::new(make_cond( + self.c, + assign.span, + &alias, + var_expr, + right_expr, + )), }); return; } @@ -151,6 +173,7 @@ impl VisitMut for NullishCoalescing { op: op!("="), left: PatOrExpr::Pat(Box::new(Pat::Ident(i.clone()))), right: Box::new(make_cond( + self.c, assign.span, &i.id, Expr::Ident(i.id.clone()), @@ -169,26 +192,40 @@ impl VisitMut for NullishCoalescing { } } -fn make_cond(span: Span, alias: &Ident, var_expr: Expr, init: Box) -> Expr { - Expr::Cond(CondExpr { - span, - test: Box::new(Expr::Bin(BinExpr { - span: DUMMY_SP, - left: Box::new(Expr::Bin(BinExpr { +fn make_cond(c: Config, span: Span, alias: &Ident, var_expr: Expr, init: Box) -> Expr { + Expr::Cond(if c.no_document_all { + CondExpr { + span, + test: Box::new(Expr::Bin(BinExpr { span: DUMMY_SP, left: Box::new(var_expr), - op: op!("!=="), + op: op!("!="), right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))), })), - op: op!("&&"), - right: Box::new(Expr::Bin(BinExpr { + cons: Box::new(Expr::Ident(alias.clone())), + alt: init, + } + } else { + CondExpr { + span, + test: Box::new(Expr::Bin(BinExpr { span: DUMMY_SP, - left: Box::new(Expr::Ident(alias.clone())), - op: op!("!=="), - right: undefined(DUMMY_SP), + left: Box::new(Expr::Bin(BinExpr { + span: DUMMY_SP, + left: Box::new(var_expr), + op: op!("!=="), + right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))), + })), + op: op!("&&"), + right: Box::new(Expr::Bin(BinExpr { + span: DUMMY_SP, + left: Box::new(Expr::Ident(alias.clone())), + op: op!("!=="), + right: undefined(DUMMY_SP), + })), })), - })), - cons: Box::new(Expr::Ident(alias.clone())), - alt: init, + cons: Box::new(Expr::Ident(alias.clone())), + alt: init, + } }) } diff --git a/crates/swc_ecma_transforms_compat/src/es2020/nullish_coalescing/tests.rs b/crates/swc_ecma_transforms_compat/src/es2020/nullish_coalescing/tests.rs index 4fdd0654376b..64de402f2db7 100644 --- a/crates/swc_ecma_transforms_compat/src/es2020/nullish_coalescing/tests.rs +++ b/crates/swc_ecma_transforms_compat/src/es2020/nullish_coalescing/tests.rs @@ -2,8 +2,8 @@ use super::*; use swc_ecma_parser::{EsConfig, Syntax}; use swc_ecma_transforms_testing::{test, test_exec}; -fn tr(_: ()) -> impl Fold { - nullish_coalescing() +fn tr(c: Config) -> impl Fold { + nullish_coalescing(c) } fn syntax() -> Syntax { @@ -15,7 +15,7 @@ fn syntax() -> Syntax { test_exec!( syntax(), - |_| tr(()), + |_| tr(Default::default()), runtime_semantics_exec, r#" expect(null ?? undefined).toBeUndefined(undefined); @@ -42,7 +42,7 @@ expect(obj2.foo ?? -1).toBe(0); test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), transform_in_default, r#" function foo(foo, qux = foo.bar ?? "qux") {} @@ -56,7 +56,7 @@ function foo(foo, qux = (_bar = foo.bar) !== null && _bar !== void 0 ? _bar : "q test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), transform_in_function, r#" @@ -75,7 +75,7 @@ function foo(opts) { test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), transform_static_refs_in_default, r#" @@ -90,7 +90,7 @@ function foo(foo, bar = foo !== null && foo !== void 0 ? foo : "bar") {} test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), transform_static_refs_in_function, r#" function foo() { @@ -110,7 +110,7 @@ function foo() { test!( Default::default(), - |_| tr(()), + |_| tr(Default::default()), assign_01, " a ??= b; @@ -122,7 +122,7 @@ test!( test!( Default::default(), - |_| tr(()), + |_| tr(Default::default()), issue_1570_1, " const a = {} @@ -138,7 +138,7 @@ test!( test_exec!( Default::default(), - |_| tr(()), + |_| tr(Default::default()), issue_1570_2, " const a = {} @@ -146,3 +146,23 @@ test_exec!( expect(a.b).toBe('1') " ); + +test!( + syntax(), + |_| tr(Config { + no_document_all: true + }), + loose, + r#" +function foo(opts) { + var foo = opts.foo ?? "default"; +} +"#, + r#" +function foo(opts) { + var _foo; + + var foo = (_foo = opts.foo) != null ? _foo : "default"; +} +"# +); diff --git a/crates/swc_ecma_transforms_compat/src/es2020/opt_chaining.rs b/crates/swc_ecma_transforms_compat/src/es2020/opt_chaining.rs index 46f79a7c9248..57add5dc3e3a 100644 --- a/crates/swc_ecma_transforms_compat/src/es2020/opt_chaining.rs +++ b/crates/swc_ecma_transforms_compat/src/es2020/opt_chaining.rs @@ -1,19 +1,33 @@ +use serde::Deserialize; use std::{iter::once, mem}; -use swc_common::{Spanned, DUMMY_SP}; +use swc_common::{util::take::Take, Spanned, DUMMY_SP}; use swc_ecma_ast::*; use swc_ecma_transforms_base::perf::Check; use swc_ecma_transforms_macros::fast_path; use swc_ecma_utils::{alias_if_required, prepend, private_ident, undefined, ExprFactory, StmtLike}; use swc_ecma_visit::{noop_fold_type, noop_visit_type, Fold, FoldWith, Node, Visit}; -pub fn optional_chaining() -> impl Fold { - OptChaining::default() +pub fn optional_chaining(c: Config) -> impl Fold { + OptChaining { + c, + ..Default::default() + } } #[derive(Default)] struct OptChaining { vars_without_init: Vec, vars_with_init: Vec, + c: Config, +} + +#[derive(Debug, Clone, Copy, Default, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Config { + #[serde(default)] + pub no_document_all: bool, + #[serde(default)] + pub pure_getter: bool, } /// TODO: VisitMut @@ -101,6 +115,7 @@ impl OptChaining { .map(|stmt| { GLOBALS.set(&globals, || { let mut visitor = Self::default(); + visitor.c = self.c; let mut stmts = Vec::with_capacity(3); visitor.fold_one_stmt_to(stmt, &mut stmts); @@ -131,23 +146,27 @@ impl OptChaining { let mut new: Vec = vec![]; - let mut v = Self::default(); + let init = self.vars_with_init.take(); + let uninit = self.vars_without_init.take(); for stmt in stmts { - v.fold_one_stmt_to(stmt, &mut new); + self.fold_one_stmt_to(stmt, &mut new); } - if !v.vars_without_init.is_empty() { + if !self.vars_without_init.is_empty() { prepend( &mut new, T::from_stmt(Stmt::Decl(Decl::Var(VarDecl { span: DUMMY_SP, declare: false, kind: VarDeclKind::Var, - decls: mem::replace(&mut v.vars_without_init, vec![]), + decls: mem::replace(&mut self.vars_without_init, vec![]), }))), ); } + self.vars_with_init = init; + self.vars_without_init = uninit; + new } } @@ -435,23 +454,30 @@ impl OptChaining { } }; - let right = Box::new(Expr::Bin(BinExpr { - span: DUMMY_SP, - left: right, - op: op!("==="), - right: undefined(span), - })); - - let test = Box::new(Expr::Bin(BinExpr { - span, - left: Box::new(Expr::Bin(BinExpr { + let test = Box::new(Expr::Bin(if self.c.no_document_all { + BinExpr { span: obj_span, left, - op: op!("==="), + op: op!("=="), right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))), - })), - op: op!("||"), - right, + } + } else { + BinExpr { + span, + left: Box::new(Expr::Bin(BinExpr { + span: obj_span, + left, + op: op!("==="), + right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))), + })), + op: op!("||"), + right: Box::new(Expr::Bin(BinExpr { + span: DUMMY_SP, + left: right, + op: op!("==="), + right: undefined(span), + })), + } })); CondExpr { @@ -479,21 +505,27 @@ impl OptChaining { let (left, right, alt) = match *obj { Expr::Ident(..) => (obj.clone(), obj, e.expr), + _ if is_simple_expr(&obj) && self.c.pure_getter => (obj.clone(), obj, e.expr), _ => { let this_as_super; - let (this_obj, aliased) = alias_if_required( - match &*obj { - Expr::Member(m) => match &m.obj { - ExprOrSuper::Super(s) => { - this_as_super = Expr::This(ThisExpr { span: s.span }); - &this_as_super - } - ExprOrSuper::Expr(obj) => &**obj, + let should_call = obj.is_member(); + let (this_obj, aliased) = if should_call { + alias_if_required( + match &*obj { + Expr::Member(m) => match &m.obj { + ExprOrSuper::Super(s) => { + this_as_super = Expr::This(ThisExpr { span: s.span }); + &this_as_super + } + ExprOrSuper::Expr(obj) => &**obj, + }, + _ => &*obj, }, - _ => &*obj, - }, - "_obj", - ); + "_obj", + ) + } else { + (Ident::dummy(), false) + }; let obj_expr = if !is_super_access && aliased { self.vars_without_init.push(VarDeclarator { span: obj_span, @@ -554,12 +586,19 @@ impl OptChaining { Box::new(Expr::Ident(tmp.clone())), Box::new(Expr::Call(CallExpr { span, - callee: ExprOrSuper::Expr(Box::new(Expr::Member(MemberExpr { - span: DUMMY_SP, - obj: ExprOrSuper::Expr(Box::new(Expr::Ident(tmp.clone()))), - prop: Box::new(Expr::Ident(Ident::new("call".into(), span))), - computed: false, - }))), + callee: ExprOrSuper::Expr(Box::new(if should_call { + Expr::Member(MemberExpr { + span: DUMMY_SP, + obj: ExprOrSuper::Expr(Box::new(Expr::Ident(tmp.clone()))), + prop: Box::new(Expr::Ident(Ident::new( + "call".into(), + span, + ))), + computed: false, + }) + } else { + Expr::Ident(tmp.clone()) + })), args: once(if is_super_access { ThisExpr { span }.as_arg() } else { @@ -573,21 +612,30 @@ impl OptChaining { } }; - let test = Box::new(Expr::Bin(BinExpr { - span, - left: Box::new(Expr::Bin(BinExpr { + let test = Box::new(Expr::Bin(if self.c.no_document_all { + BinExpr { span: DUMMY_SP, left, - op: op!("==="), + op: op!("=="), right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))), - })), - op: op!("||"), - right: Box::new(Expr::Bin(BinExpr { - span: DUMMY_SP, - left: right, - op: op!("==="), - right: undefined(span), - })), + } + } else { + BinExpr { + span, + left: Box::new(Expr::Bin(BinExpr { + span: DUMMY_SP, + left, + op: op!("==="), + right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))), + })), + op: op!("||"), + right: Box::new(Expr::Bin(BinExpr { + span: DUMMY_SP, + left: right, + op: op!("==="), + right: undefined(span), + })), + } })); CondExpr { @@ -620,3 +668,23 @@ impl Check for ShouldWork { self.found } } + +// TODO: skip transparent wrapper +fn is_simple_expr(expr: &Expr) -> bool { + match expr { + Expr::Ident(_) => true, + Expr::Member(MemberExpr { + obj, + computed: false, + .. + }) if match obj { + ExprOrSuper::Super(..) => true, + ExprOrSuper::Expr(expr) if is_simple_expr(expr) => true, + _ => false, + } => + { + true + } + _ => false, + } +} diff --git a/crates/swc_ecma_transforms_compat/tests/es2015_classes.rs b/crates/swc_ecma_transforms_compat/tests/es2015_classes.rs index a103fc1308c6..104d202262f5 100644 --- a/crates/swc_ecma_transforms_compat/tests/es2015_classes.rs +++ b/crates/swc_ecma_transforms_compat/tests/es2015_classes.rs @@ -6347,7 +6347,7 @@ test!( chain!( es2022::es2022(es2022::Config { loose: false }), - es2018::es2018(), + es2018::es2018(Default::default()), es2017::es2017(), es2016::es2016(), es2015::es2015( diff --git a/crates/swc_ecma_transforms_compat/tests/es2015_computed_props.rs b/crates/swc_ecma_transforms_compat/tests/es2015_computed_props.rs index 20d9cd7129d3..c9237813b893 100644 --- a/crates/swc_ecma_transforms_compat/tests/es2015_computed_props.rs +++ b/crates/swc_ecma_transforms_compat/tests/es2015_computed_props.rs @@ -1,5 +1,5 @@ use swc_ecma_parser::Syntax; -use swc_ecma_transforms_compat::es2015::computed_properties; +use swc_ecma_transforms_compat::es2015::computed_props::{computed_properties, Config}; use swc_ecma_transforms_testing::{test, test_exec}; use swc_ecma_visit::Fold; @@ -8,12 +8,12 @@ fn syntax() -> Syntax { } fn tr(_: ()) -> impl Fold { - computed_properties() + computed_properties(Default::default()) } test!( ::swc_ecma_parser::Syntax::default(), - |_| computed_properties(), + |_| computed_properties(Default::default()), issue_210, " const b = {[a]: 1} @@ -27,7 +27,7 @@ export const c = _defineProperty({ test!( ::swc_ecma_parser::Syntax::default(), - |_| computed_properties(), + |_| computed_properties(Default::default()), big_int, " const b = {1n: 1, [x]: 'x', 2n: 2} @@ -40,7 +40,7 @@ const b = (_obj = { test!( ::swc_ecma_parser::Syntax::default(), - |_| computed_properties(), + |_| computed_properties(Default::default()), accessors, r#"var obj = { get [foobar]() { @@ -77,7 +77,7 @@ var obj = ( _obj = { test!( ::swc_ecma_parser::Syntax::default(), - |_| computed_properties(), + |_| computed_properties(Default::default()), argument, r#"foo({ [bar]: "foobar" @@ -88,7 +88,7 @@ test!( test!( ::swc_ecma_parser::Syntax::default(), - |_| computed_properties(), + |_| computed_properties(Default::default()), assignment, r#"foo = { [bar]: "foobar" @@ -98,7 +98,7 @@ test!( test!( ::swc_ecma_parser::Syntax::default(), - |_| computed_properties(), + |_| computed_properties(Default::default()), method, r#"var obj = { [foobar]() { @@ -119,7 +119,7 @@ var obj = (_obj = {}, _defineProperty(_obj, foobar, function () { test!( ::swc_ecma_parser::Syntax::default(), - |_| computed_properties(), + |_| computed_properties(Default::default()), mixed, r#"var obj = { ["x" + foo]: "heh", @@ -135,7 +135,7 @@ var obj = (_obj = {}, _defineProperty(_obj, "x" + foo, "heh"), _defineProperty(_ test!( ::swc_ecma_parser::Syntax::default(), - |_| computed_properties(), + |_| computed_properties(Default::default()), multiple, r#"var obj = { ["x" + foo]: "heh", @@ -149,7 +149,7 @@ _defineProperty(_obj, "y" + bar, "noo"), _obj);"# test!( ::swc_ecma_parser::Syntax::default(), - |_| computed_properties(), + |_| computed_properties(Default::default()), single, r#"var obj = { ["x" + foo]: "heh" @@ -159,7 +159,7 @@ test!( test!( ::swc_ecma_parser::Syntax::default(), - |_| computed_properties(), + |_| computed_properties(Default::default()), symbol, r#"var k = Symbol(); var foo = { @@ -181,7 +181,7 @@ var foo = ( _obj = { test_exec!( ::swc_ecma_parser::Syntax::default(), - |_| computed_properties(), + |_| computed_properties(Default::default()), symbol_exec, r#" var k = Symbol(); @@ -198,7 +198,7 @@ expect(foo[k]).toBe(k)"# test!( ::swc_ecma_parser::Syntax::default(), - |_| computed_properties(), + |_| computed_properties(Default::default()), this, r#"var obj = { ["x" + foo.bar]: "heh" @@ -208,7 +208,7 @@ test!( test!( ::swc_ecma_parser::Syntax::default(), - |_| computed_properties(), + |_| computed_properties(Default::default()), issue_315_1, " ({ @@ -238,7 +238,7 @@ export function corge() { test!( ::swc_ecma_parser::Syntax::default(), - |_| computed_properties(), + |_| computed_properties(Default::default()), issue_315_2, " export function corge() {} @@ -250,7 +250,7 @@ export function corge() {} test!( ::swc_ecma_parser::Syntax::default(), - |_| computed_properties(), + |_| computed_properties(Default::default()), issue_315_3, " export function corge() {} @@ -281,7 +281,7 @@ _defineProperty({ test!( ::swc_ecma_parser::Syntax::default(), - |_| computed_properties(), + |_| computed_properties(Default::default()), issue_315_4, " export class Foo {} @@ -525,3 +525,217 @@ expect(foo[k]).toBe(k) "# ); + +test!( + syntax(), + |_| computed_properties(Config { loose: true }), + loose_assignment, + r#" +foo = { + [bar]: "foobar" +}; +"#, + r#" +var _obj; + +foo = (_obj = {}, _obj[bar] = "foobar", _obj); +"# +); + +test!( + syntax(), + |_| computed_properties(Config { loose: true }), + loose_argument, + r#" +foo({ + [bar]: "foobar" +}); +"#, + r#" +var _obj; + +foo((_obj = {}, _obj[bar] = "foobar", _obj)); +"# +); + +test!( + syntax(), + |_| computed_properties(Config { loose: true }), + loose_coerce, + r#" +var obj = { + foo: "bar", + [bar]: "foo" +}; +"#, + r#" +var _obj; + +var obj = (_obj = { + foo: "bar" +}, _obj[bar] = "foo", _obj); +"# +); + +test!( + syntax(), + |_| computed_properties(Config { loose: true }), + loose_method, + r#" +var obj = { + [foobar]() { + return "foobar"; + }, + test() { + return "regular method after computed property"; + } +}; +"#, + r#" +var _obj; + +var obj = (_obj = {}, _obj[foobar] = function () { + return "foobar"; +}, _obj.test = function () { + return "regular method after computed property"; +}, _obj); +"# +); + +test!( + syntax(), + |_| computed_properties(Config { loose: true }), + loose_mixed, + r#" +var obj = { + ["x" + foo]: "heh", + ["y" + bar]: "noo", + foo: "foo", + bar: "bar" +}; +"#, + r#" +var _obj; + +var obj = (_obj = {}, _obj["x" + foo] = "heh", _obj["y" + bar] = "noo", _obj.foo = "foo", _obj.bar = "bar", _obj); +"# +); + +test!( + syntax(), + |_| computed_properties(Config { loose: true }), + loose_multiple, + r#" +var obj = { + ["x" + foo]: "heh", + ["y" + bar]: "noo" +}; +"#, + r#" +var _obj; + +var obj = (_obj = {}, _obj["x" + foo] = "heh", _obj["y" + bar] = "noo", _obj); +"# +); + +test!( + syntax(), + |_| computed_properties(Config { loose: true }), + loose_single, + r#" +var obj = { + ["x" + foo]: "heh" +}; +"#, + r#" +var _obj; + +var obj = (_obj = {}, _obj["x" + foo] = "heh", _obj); +"# +); + +test!( + syntax(), + |_| computed_properties(Config { loose: true }), + loose_this, + r#" +var obj = { + ["x" + foo.bar]: "heh" +}; +"#, + r#" +var _obj; + +var obj = (_obj = {}, _obj["x" + foo.bar] = "heh", _obj); +"# +); + +test!( + syntax(), + |_| computed_properties(Config { loose: true }), + loose_two, + r#" +var obj = { + first: "first", + [second]: "second", +} +"#, + r#" +var _obj; + +var obj = (_obj = { + first: "first" +}, _obj[second] = "second", _obj); +"# +); + +test!( + syntax(), + |_| computed_properties(Config { loose: true }), + loose_variable, + r#" +var foo = { + [bar]: "foobar" +}; +"#, + r#" +var _obj; + +var foo = (_obj = {}, _obj[bar] = "foobar", _obj); +"# +); + +test!( + syntax(), + |_| computed_properties(Config { loose: true }), + loose_str_lit, + r#" +var foo = { +["213"]: "foobar", +}; +"#, + r#" +var _obj; +var foo = (_obj = { +}, _obj["213"] = "foobar", _obj); +"# +); + +test_exec!( + syntax(), + |_| computed_properties(Config { loose: true }), + loose_symbol, + r#" +var k = Symbol(); +var foo = { +[Symbol.iterator]: "foobar", +get [k]() { + return k; +} +}; + +expect(foo[Symbol.iterator]).toBe("foobar") +expect(foo[k]).toBe(k) + +"# +); diff --git a/crates/swc_ecma_transforms_compat/tests/es2015_destructuring.rs b/crates/swc_ecma_transforms_compat/tests/es2015_destructuring.rs index 48e94fd09dba..a2adda2b87e5 100644 --- a/crates/swc_ecma_transforms_compat/tests/es2015_destructuring.rs +++ b/crates/swc_ecma_transforms_compat/tests/es2015_destructuring.rs @@ -50,7 +50,7 @@ test!( parameters(), destructuring(Default::default()), block_scoping(), - object_rest_spread(), + object_rest_spread(Default::default()), ), issue_1948, " @@ -614,7 +614,10 @@ function foo(bar) { // destructuring_function_key_with_object_rest_spread test_exec!( syntax(), - |_| chain!(object_rest_spread(), destructuring(Default::default())), + |_| chain!( + object_rest_spread(Default::default()), + destructuring(Default::default()) + ), destructuring_function_key_with_object_rest_spread_exec, r#" const { [(() => 1)()]: a, ...rest } = { 1: "a" }; @@ -658,7 +661,7 @@ test!( parameters(), destructuring(Default::default()), block_scoping(), - object_rest_spread(), + object_rest_spread(Default::default()), ), destructuring_for_of, r#" @@ -693,7 +696,7 @@ test!( parameters(), destructuring(Default::default()), block_scoping(), - object_rest_spread(), + object_rest_spread(Default::default()), ), destructuring_object_basic, r#" @@ -719,7 +722,7 @@ test!( parameters(), destructuring(Default::default()), block_scoping(), - object_rest_spread(), + object_rest_spread(Default::default()), ), destructuring_assignment_arrow_function_block, r#" @@ -744,7 +747,7 @@ test_exec!( parameters(), destructuring(Default::default()), block_scoping(), - object_rest_spread(), + object_rest_spread(Default::default()), ), destructuring_non_iterable_exec, r#" @@ -771,7 +774,7 @@ test_exec!( parameters(), destructuring(Default::default()), block_scoping(), - object_rest_spread(), + object_rest_spread(Default::default()), ), destructuring_empty_object_pattern_exec, r#" @@ -792,7 +795,7 @@ test_exec!( parameters(), destructuring(Default::default()), block_scoping(), - object_rest_spread(), + object_rest_spread(Default::default()), ), destructuring_chained_exec, r#" @@ -810,7 +813,7 @@ expect(d).toBe(4); test_exec!( syntax(), |_| chain!( - object_rest_spread(), + object_rest_spread(Default::default()), spread(spread::Config { ..Default::default() }), @@ -868,7 +871,7 @@ test_exec!( parameters(), destructuring(Default::default()), block_scoping(), - object_rest_spread(), + object_rest_spread(Default::default()), ), destructuring_issue_5090_exec, r#" @@ -895,7 +898,7 @@ test_exec!( parameters(), destructuring(Default::default()), block_scoping(), - object_rest_spread(), + object_rest_spread(Default::default()), ), destructuring_default_precedence_exec, r#" @@ -930,7 +933,7 @@ expect(f2({a: 1})).toEqual([1, 1, 1]); // spread(spread::Config{..Default::default()}), // parameters(), // block_scoping(), -// object_rest_spread(), +// object_rest_spread(Default::default()), // ] //} //"#), @@ -980,7 +983,7 @@ test!( parameters(), destructuring(Default::default()), block_scoping(), - object_rest_spread(), + object_rest_spread(Default::default()), ), destructuring_parameters, r#" @@ -1047,7 +1050,7 @@ test!( parameters(), destructuring(Default::default()), block_scoping(), - object_rest_spread(), + object_rest_spread(Default::default()), ), destructuring_array_unpack_optimisation, r#" @@ -1128,7 +1131,7 @@ test!( parameters(), destructuring(Default::default()), block_scoping(), - object_rest_spread(), + object_rest_spread(Default::default()), ), destructuring_known_array, r#" @@ -1148,14 +1151,14 @@ var x = z[0], test!( syntax(), |_| chain!( - object_rest_spread(), + object_rest_spread(Default::default()), spread(spread::Config { ..Default::default() }), parameters(), destructuring(Default::default()), block_scoping(), - object_rest_spread(), + object_rest_spread(Default::default()), ), destructuring_es7_object_rest, r#" @@ -1209,7 +1212,7 @@ expect(oldCourses).toEqual([]); test!( syntax(), |_| chain!( - object_rest_spread(), + object_rest_spread(Default::default()), spread(spread::Config { ..Default::default() }), @@ -1233,7 +1236,7 @@ var tmp = z.x, y = (tmp === void 0 ? {} : tmp).y; test!( syntax(), |_| chain!( - object_rest_spread(), + object_rest_spread(Default::default()), spread(spread::Config { ..Default::default() }), @@ -1267,7 +1270,7 @@ var ref = [0, 1, 2, 3, 4, 5, 6], test!( syntax(), |_| chain!( - object_rest_spread(), + object_rest_spread(Default::default()), spread(spread::Config { ..Default::default() }), @@ -1303,7 +1306,7 @@ function isSorted(param) { test!( syntax(), |_| chain!( - object_rest_spread(), + object_rest_spread(Default::default()), spread(spread::Config { ..Default::default() }), @@ -1339,7 +1342,7 @@ test!( ..Default::default() }), block_scoping(), - object_rest_spread() + object_rest_spread(Default::default()) ), destructuring_assignment_statement, r#" @@ -1361,7 +1364,7 @@ test!( parameters(), destructuring(Default::default()), block_scoping(), - object_rest_spread(), + object_rest_spread(Default::default()), ), destructuring_array, r#" @@ -1386,7 +1389,7 @@ test!( parameters(), destructuring(Default::default()), block_scoping(), - object_rest_spread(), + object_rest_spread(Default::default()), ), destructuring_assignment_arrow_function_no_block, r#" @@ -1403,14 +1406,14 @@ var ref; test!( syntax(), |_| chain!( - object_rest_spread(), + object_rest_spread(Default::default()), spread(spread::Config { ..Default::default() }), parameters(), destructuring(Default::default()), block_scoping(), - object_rest_spread(), + object_rest_spread(Default::default()), ), destructuring_issue_9834, r#" @@ -1445,7 +1448,10 @@ var key = prefix + 'state', // destructuring_number_key_with_object_rest_spread test_exec!( syntax(), - |_| chain!(object_rest_spread(), destructuring(Default::default())), + |_| chain!( + object_rest_spread(Default::default()), + destructuring(Default::default()) + ), destructuring_number_key_with_object_rest_spread_exec, r#" const foo = { @@ -1472,7 +1478,7 @@ test!( parameters(), destructuring(Default::default()), block_scoping(), - object_rest_spread(), + object_rest_spread(Default::default()), ), destructuring_for_in, r#" @@ -1507,7 +1513,7 @@ test!( parameters(), destructuring(Default::default()), block_scoping(), - object_rest_spread(), + object_rest_spread(Default::default()), ), destructuring_issue_5744, r#" @@ -1531,7 +1537,7 @@ test_exec!( parameters(), destructuring(Default::default()), block_scoping(), - object_rest_spread(), + object_rest_spread(Default::default()), ), destructuring_spread_generator_exec, r#" diff --git a/crates/swc_ecma_transforms_compat/tests/es2015_regenerator.rs b/crates/swc_ecma_transforms_compat/tests/es2015_regenerator.rs index 1f87d5c3c419..8370f213cf28 100644 --- a/crates/swc_ecma_transforms_compat/tests/es2015_regenerator.rs +++ b/crates/swc_ecma_transforms_compat/tests/es2015_regenerator.rs @@ -1414,7 +1414,7 @@ test!( chain!( es2022(es2022::Config { loose: false }), es2021(), - es2018(), + es2018(Default::default()), es2017(), es2016(), es2015::(mark, None, Default::default()), diff --git a/crates/swc_ecma_transforms_compat/tests/es2015_template_literals.rs b/crates/swc_ecma_transforms_compat/tests/es2015_template_literals.rs index 301f80420e69..0d147a583068 100644 --- a/crates/swc_ecma_transforms_compat/tests/es2015_template_literals.rs +++ b/crates/swc_ecma_transforms_compat/tests/es2015_template_literals.rs @@ -7,8 +7,8 @@ fn syntax() -> Syntax { Default::default() } -fn tr(_: ()) -> impl Fold { - template_literal() +fn tr(config: template_literal::Config) -> impl Fold { + template_literal(config) } test_exec!( @@ -997,3 +997,398 @@ test_exec!( expect(typeof obj.foo`template`).toEqual('object') " ); + +test!( + syntax(), + |_| tr(template_literal::Config { + ignore_to_primitive: true, + mutable_template: false + }), + loose_escape_quotes, + r#"var t = `'${foo}' "${bar}"`;"#, + r#"var t = "'" + foo + "' \"" + bar + "\"";"# +); + +test!( + syntax(), + |_| tr(template_literal::Config { + ignore_to_primitive: true, + mutable_template: false + }), + loose_expression_first, + r#" +var foo = 5; +var bar = 10; +var baz = 15; + +var example = `${"a"}`; +var example2 = `${1}`; +var example3 = 1 + `${foo}${bar}${baz}`; +var example4 = 1 + `${foo}bar${baz}`; +var example5 = `${""}`;"#, + r#" +var foo = 5; +var bar = 10; +var baz = 15; +var example = "a"; +var example2 = "" + 1; +var example3 = 1 + ("" + foo + bar + baz); +var example4 = 1 + (foo + "bar" + baz); +var example5 = "";"# +); + +test!( + syntax(), + |_| tr(template_literal::Config { + ignore_to_primitive: true, + mutable_template: false + }), + loose_function, + r#"var foo = `test ${_.test(foo)} ${bar}`;"#, + r#"var foo = "test " + _.test(foo) + " " + bar;"# +); + +test!( + syntax(), + |_| tr(template_literal::Config { + ignore_to_primitive: true, + mutable_template: false + }), + loose_literals, + r#"var foo = `${1}${f}oo${true}${b}ar${0}${baz}`;"#, + r#"var foo = "" + 1 + f + "oo" + true + b + "ar" + 0 + baz;"# +); + +test!( + syntax(), + |_| tr(template_literal::Config { + ignore_to_primitive: true, + mutable_template: false + }), + loose_multi_line, + r#" +var o = `wow +this is +actually multiline!`; + "#, + r#"var o = "wow\nthis is\nactually multiline!";"# +); + +test!( + syntax(), + |_| tr(template_literal::Config { + ignore_to_primitive: true, + mutable_template: false + }), + loose_multiple, + r#"var foo = `test ${foo} ${bar}`;"#, + r#"var foo = "test " + foo + " " + bar;"# +); + +test!( + syntax(), + |_| tr(template_literal::Config { + ignore_to_primitive: true, + mutable_template: false + }), + loose_none, + r#"var foo = `test`;"#, + r#"var foo = "test";"# +); + +test!( + syntax(), + |_| tr(template_literal::Config { + ignore_to_primitive: true, + mutable_template: false + }), + loose_only, + r#"var foo = `${test}`;"#, + r#"var foo = "" + test;"# +); + +test!( + syntax(), + |_| tr(template_literal::Config { + ignore_to_primitive: true, + mutable_template: false + }), + loose_single, + r#"var foo = `test ${foo}`;"#, + r#"var foo = "test " + foo;"# +); + +test!( + syntax(), + |_| tr(template_literal::Config { + ignore_to_primitive: true, + mutable_template: false + }), + loose_statement, + r#"var foo = `test ${foo + bar}`;"#, + r#"var foo = "test " + (foo + bar);"# +); + +test_exec!( + syntax(), + |_| tr(template_literal::Config { + ignore_to_primitive: true, + mutable_template: false + }), + loose_order, + " + const calls = []; + + ` + ${ + calls.push(1), + { + [Symbol.toPrimitive](){ + calls.push(2); + return 'foo'; + } + } + } + ${ + calls.push(3), + { + [Symbol.toPrimitive](){ + calls.push(4); + return 'bar'; + } + } + } + `; + + expect(calls).toEqual([1, 2, 3, 4]); + " +); + +test_exec!( + syntax(), + |_| tr(template_literal::Config { + ignore_to_primitive: true, + mutable_template: false + }), + loose_order_2, + " + const calls = []; + + ` + ${{ + [Symbol.toPrimitive]() { + calls.push(1); + return 'foo'; + } + }} + ${1 + + { + valueOf() { + calls.push(2); + return 2; + } + }} + `; + + expect(calls).toEqual([1, 2]); + " +); + +test_exec!( + syntax(), + |_| tr(template_literal::Config { + ignore_to_primitive: true, + mutable_template: false + }), + loose_symbol, + "const fn = () => `${Symbol()}`; + + expect(fn).toThrow(TypeError);" +); + +test!( + syntax(), + |_| tr(template_literal::Config { + ignore_to_primitive: false, + mutable_template: true + }), + loose_no_tag, + "`foo ${bar} baz`;", + "'foo '.concat(bar, ' baz');" +); + +test!( + syntax(), + |_| tr(template_literal::Config { + ignore_to_primitive: false, + mutable_template: true + }), + loose_tag, + r#" +var foo = bar`wow\na${ 42 }b ${_.foobar()}`; +var bar = bar`wow\nab${ 42 } ${_.foobar()}`; +var bar = bar`wow\naB${ 42 } ${_.baz()}`; + "#, + r#" +function _templateObject() { + const data = _taggedTemplateLiteralLoose([ + "wow\na", + "b ", + "" + ], [ + "wow\\na", + "b ", + "" + ]); + _templateObject = function() { + return data; + }; + return data; +} +function _templateObject1() { + const data = _taggedTemplateLiteralLoose([ + "wow\nab", + " ", + "" + ], [ + "wow\\nab", + " ", + "" + ]); + _templateObject1 = function() { + return data; + }; + return data; +} +function _templateObject2() { + const data = _taggedTemplateLiteralLoose([ + "wow\naB", + " ", + "" + ], [ + "wow\\naB", + " ", + "" + ]); + _templateObject2 = function() { + return data; + }; + return data; +} +var foo = bar(_templateObject(), 42, _.foobar()); +var bar = bar(_templateObject1(), 42, _.foobar()); +var bar = bar(_templateObject2(), 42, _.baz()); + "# +); + +test!( + // TODO: Fix parser + ignore, + syntax(), + |_| tr(Default::default()), + loose_template_revision, + r#"tag`\unicode and \u{55}`; +tag`\01`; +tag`\xg${0}right`; +tag`left${0}\xg`; +tag`left${0}\xg${1}right`; +tag`left${0}\u000g${1}right`; +tag`left${0}\u{-0}${1}right`; +function a() { +var undefined = 4; +tag`\01`; +}"#, + r#" +function _templateObject8() { +const data = _taggedTemplateLiteralLoose([void 0], ["\\01"]); + +_templateObject8 = function () { + return data; +}; + +return data; +} + +function _templateObject7() { +const data = _taggedTemplateLiteralLoose(["left", void 0, "right"], ["left", "\\u{-0}", "right"]); + +_templateObject7 = function () { + return data; +}; + +return data; +} + +function _templateObject6() { +const data = _taggedTemplateLiteralLoose(["left", void 0, "right"], ["left", "\\u000g", "right"]); + +_templateObject6 = function () { + return data; +}; + +return data; +} + +function _templateObject5() { +const data = _taggedTemplateLiteralLoose(["left", void 0, "right"], ["left", "\\xg", "right"]); + +_templateObject5 = function () { + return data; +}; + +return data; +} + +function _templateObject4() { +const data = _taggedTemplateLiteralLoose(["left", void 0], ["left", "\\xg"]); + +_templateObject4 = function () { + return data; +}; + +return data; +} + +function _templateObject3() { +const data = _taggedTemplateLiteralLoose([void 0, "right"], ["\\xg", "right"]); + +_templateObject3 = function () { + return data; +}; + +return data; +} + +function _templateObject2() { +const data = _taggedTemplateLiteralLoose([void 0], ["\\01"]); + +_templateObject2 = function () { + return data; +}; + +return data; +} + +function _templateObject() { +const data = _taggedTemplateLiteralLoose([void 0], ["\\unicode and \\u{55}"]); + +_templateObject = function () { + return data; +}; + +return data; +} + +tag(_templateObject()); +tag(_templateObject2()); +tag(_templateObject3(), 0); +tag(_templateObject4(), 0); +tag(_templateObject5(), 0, 1); +tag(_templateObject6(), 0, 1); +tag(_templateObject7(), 0, 1); + +function a() { +var undefined = 4; +tag(_templateObject8()); +}"# +); diff --git a/crates/swc_ecma_transforms_compat/tests/es2018_object_rest_spread.rs b/crates/swc_ecma_transforms_compat/tests/es2018_object_rest_spread.rs index f1fd08e7d134..0ee730a89811 100644 --- a/crates/swc_ecma_transforms_compat/tests/es2018_object_rest_spread.rs +++ b/crates/swc_ecma_transforms_compat/tests/es2018_object_rest_spread.rs @@ -1,6 +1,9 @@ use swc_common::chain; use swc_ecma_parser::Syntax; -use swc_ecma_transforms_compat::{es2015::spread, es2018::object_rest_spread}; +use swc_ecma_transforms_compat::{ + es2015::spread, + es2018::{object_rest_spread, object_rest_spread::Config}, +}; use swc_ecma_transforms_testing::{test, test_exec}; use swc_ecma_visit::Fold; @@ -8,13 +11,13 @@ fn syntax() -> Syntax { Syntax::default() } -fn tr() -> impl Fold { - object_rest_spread() +fn tr(c: Config) -> impl Fold { + object_rest_spread(c) } test!( syntax(), - |_| tr(), + |_| tr(Default::default()), issue_233, "const foo = () => ({ x, ...y }) => y", "const foo = ()=>(_param)=>{ @@ -25,7 +28,7 @@ test!( test!( syntax(), - |_| tr(), + |_| tr(Default::default()), issue_239, "class Foo { constructor ({ ...bar }) {} @@ -42,7 +45,7 @@ test!( // args. test!( syntax(), - |_| tr(), + |_| tr(Default::default()), issue_227, "export default function fn1(...args) { fn2(...args); @@ -54,7 +57,7 @@ test!( test!( syntax(), - |_| tr(), + |_| tr(Default::default()), issue_162, r#" export const good = { @@ -75,7 +78,7 @@ export const good = { test!( syntax(), - |_| tr(), + |_| tr(Default::default()), issue_181, r#" const fn = ({ a, ...otherProps }) => otherProps; @@ -90,7 +93,7 @@ const fn = (_param)=>{ test!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_function_array, r#" function foo([{...bar}]) { @@ -106,7 +109,7 @@ function foo([_param]) { test!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_var_basic, r#" var { a , ...b } = _ref; @@ -118,7 +121,7 @@ var { a } = _ref, b = _objectWithoutProperties(_ref, ['a']); test_exec!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_assignment_exec, r#" let foo = { @@ -134,7 +137,7 @@ expect(c).toEqual({b: 2}); test_exec!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_catch_exec, r#" try { @@ -156,7 +159,7 @@ try { test!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_assignment_expression, r#"({ a1 } = c1); ({ a2, ...b2 } = c2); @@ -173,7 +176,7 @@ console.log(( _c3 = c3, b3 = _objectWithoutProperties(_c3, ['a3']), { a3 } = _c test!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_catch_clause, r#" try {} catch({ ...a34 }) {} @@ -213,7 +216,7 @@ try { test!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_export, r#" // ExportNamedDeclaration @@ -241,7 +244,7 @@ export var [dd, ee] = ads; test!( syntax(), |_| chain!( - tr(), + tr(Default::default()), spread(spread::Config { ..Default::default() }) @@ -321,7 +324,7 @@ async function a() { test_exec!( ignore, syntax(), - |_| tr(), + |_| tr(Default::default()), rest_impure_computed_exec, r#" var key, x, y, z; @@ -351,7 +354,7 @@ expect(z).toBe("zee"); test!( ignore, syntax(), - |_| tr(), + |_| tr(Default::default()), rest_impure_computed, r#" var key, x, y, z; @@ -432,7 +435,7 @@ expect(z).toBe("zee");"# test!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_nested_2, r#" const test = { @@ -481,7 +484,7 @@ const { test!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_nested_computed_key, r#" const { @@ -509,7 +512,7 @@ const _ref = { test_exec!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_nested_default_value_exec, r#" const { @@ -527,7 +530,7 @@ expect(d).toEqual({}) test!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_nested_default_value, r#" const { @@ -555,7 +558,7 @@ const _ref = { test_exec!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_nested_order_exec, r#" var result = ""; @@ -584,7 +587,7 @@ expect(result).toBe("barbazfoo"); test!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_nested_order, r#" const { a: { ...bar }, b: { ...baz }, ...foo } = obj; @@ -597,7 +600,7 @@ const bar = _extends({}, obj.a), baz = _extends({}, obj.b), foo = test!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_nested_1, r#" const defunct = { @@ -634,7 +637,7 @@ const { test_exec!( ignore, syntax(), - |_| tr(), + |_| tr(Default::default()), rest_non_string_computed_exec, r#" const a = { @@ -697,7 +700,7 @@ expect(dy).toBe("sy"); test!( ignore, syntax(), - |_| tr(), + |_| tr(Default::default()), rest_non_string_computed, r#" const a = { @@ -811,7 +814,7 @@ expect(dy).toBe("sy");"# test!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_parameters, r#" function a({ ...a34 }) {} @@ -906,7 +909,7 @@ function b3({ test_exec!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_symbol_exec, r#" const sym = Symbol("test"); @@ -934,7 +937,7 @@ expect(Object.getOwnPropertySymbols(noSym)).toEqual([]);"# test!( ignore, syntax(), - |_| tr(), + |_| tr(Default::default()), rest_symbol, r#" let { @@ -975,7 +978,7 @@ if (_ref3 = {}, _Symbol$for3 = Symbol.for("foo"), ({ test!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_variable_destructuring_1, r#" var z = {}; @@ -1030,7 +1033,7 @@ const { test!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_variable_destructuring_2, r#" let { @@ -1054,7 +1057,7 @@ let { test!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_variable_destructuring_3, r#" let { x4: { ...y4 } } = z; @@ -1066,7 +1069,7 @@ let y4 = _extends({}, z.x4); test_exec!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_with_array_rest_exec, r#" let [{ a, ...foo}, ...bar] = [{ a: 1, b:2 }, 2, 3, 4]; @@ -1078,7 +1081,7 @@ expect(bar).toEqual([2, 3, 4]); test_exec!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_with_array_rest_exec_2, r#" let { @@ -1098,7 +1101,7 @@ expect(objectRest).toEqual({d: 'oyez'}) test!( syntax(), - |_| tr(), + |_| tr(Default::default()), rest_with_array_rest, r#" let { @@ -1120,7 +1123,7 @@ let _ref = { test!( syntax(), - |_| tr(), + |_| tr(Default::default()), spread_assignment, r#" z = { x, ...y }; @@ -1140,7 +1143,7 @@ z = { test!( syntax(), - |_| tr(), + |_| tr(Default::default()), object_spread_expression, r#" ({ x, ...y, a, ...b, c }); @@ -1177,7 +1180,7 @@ _objectSpread({}, { test_exec!( syntax(), - |_| tr(), + |_| tr(Default::default()), spread_no_object_assign_exec, r#" Object.defineProperty(Object.prototype, 'NOSET', { @@ -1226,7 +1229,7 @@ expect(Array.isArray(Object.getPrototypeOf(o2))).toBe(false); test!( syntax(), - |_| tr(), + |_| tr(Default::default()), spread_variable_declaration, r#"var z = { ...x };"#, r#"var z = _objectSpread({}, x);"# @@ -1235,7 +1238,7 @@ test!( // object_spread_assignment test!( syntax(), - |_| tr(), + |_| tr(Default::default()), object_spread_assignment, r#" z = { x, ...y }; @@ -1345,7 +1348,7 @@ z = { // object_rest_symbol_exec_exec test_exec!( syntax(), - |_| tr(), + |_| tr(Default::default()), object_rest_symbol_exec_exec, r#" const sym = Symbol("test"); @@ -1632,7 +1635,7 @@ expect(Object.getOwnPropertySymbols(noSym)).toEqual([]); // regression_gh_5151 test!( syntax(), - |_| tr(), + |_| tr(Default::default()), regression_gh_5151, r#" const { x, ...y } = a, @@ -1892,7 +1895,7 @@ var l = foo(), // regression_gh_7388 test!( syntax(), - |_| tr(), + |_| tr(Default::default()), regression_gh_7388, r#" function fn0(obj0) { @@ -2040,7 +2043,7 @@ function fn0(obj0) { // object_rest_impure_computed_exec_exec test_exec!( syntax(), - |_| tr(), + |_| tr(Default::default()), object_rest_impure_computed_exec_exec, r#" var key, x, y, z; @@ -2111,7 +2114,7 @@ test_exec!( // WTF? babel's output is wrong ignore, syntax(), - |_| tr(), + |_| tr(Default::default()), object_spread_expression_exec, r#" var log = []; @@ -2308,7 +2311,7 @@ expect(log).toEqual([1]); // object_spread_variable_declaration test!( syntax(), - |_| tr(), + |_| tr(Default::default()), object_spread_variable_declaration, r#" var z = { ...x }; @@ -2358,7 +2361,7 @@ var z = _objectSpread({}, x); // object_spread_no_object_assign_exec_exec test_exec!( syntax(), - |_| tr(), + |_| tr(Default::default()), object_spread_no_object_assign_exec_exec, r#" Object.defineProperty(Object.prototype, 'NOSET', { @@ -2408,7 +2411,7 @@ expect(Array.isArray(Object.getPrototypeOf(o2))).toBe(false); // object_rest_non_string_computed_exec_exec test_exec!( syntax(), - |_| tr(), + |_| tr(Default::default()), object_rest_non_string_computed_exec_exec, r#" const a = { @@ -2474,7 +2477,7 @@ expect(dy).toBe("sy"); // object_rest_variable_exec_exec test_exec!( syntax(), - |_| tr(), + |_| tr(Default::default()), object_rest_variable_exec_exec, r#" // var { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; @@ -2560,7 +2563,7 @@ test_exec!( // object_spread_no_getOwnPropertyDescriptors_exec test_exec!( syntax(), - |_| tr(), + |_| tr(Default::default()), object_spread_no_get_own_property_descriptors_exec, r#" const oldGOPDs = Object.getOwnPropertyDescriptors; @@ -2720,7 +2723,7 @@ Object.getOwnPropertyDescriptors = oldGOPDs; // regression_gh_4904 test!( syntax(), - |_| tr(), + |_| tr(Default::default()), regression_gh_4904, r#" const { s, ...t } = foo(); @@ -2743,3 +2746,268 @@ const _ref2 = foo((_param)=>{ "# ); + +test!( + syntax(), + |_| tr(Config { + no_symbol: true, + set_property: true + }), + no_symbol_rest_assignment_expression, + r#"({ a, b, ...c } = obj);"#, + r#" +var _obj; +_obj = obj, c = _objectWithoutPropertiesLoose(_obj, [ + "a", + "b" +]), ({ a , b } = _obj), _obj; +"# +); + +test!( + syntax(), + |_| tr(Config { + no_symbol: true, + set_property: true + }), + no_symbol_computed, + r#"let { [a]: b, ...c } = obj;"#, + r#" + +let { + [a]: b +} = obj, + c = _objectWithoutPropertiesLoose(obj, [a].map(_toPropertyKey)); +"# +); + +test_exec!( + syntax(), + |_| tr(Config { + no_symbol: true, + set_property: true + }), + set_property_ignore_symbol_exec, + r#" +let sym = Symbol(); + +let { a, ...r } = { a: 1, b: 2, [sym]: 3 }; + +expect(a).toBe(1); +expect(r.b).toBe(2); +expect(sym in r).toBe(false); +"# +); + +test!( + syntax(), + |_| tr(Config { + no_symbol: true, + set_property: true + }), + no_symbol_rest_nested, + r#"let { a, nested: { b, c, ...d }, e } = obj;"#, + r#" +let { + a, + nested: { + b, + c + }, + e +} = obj, + d = _objectWithoutPropertiesLoose(obj.nested, ["b", "c"]); +"# +); + +test!( + syntax(), + |_| tr(Config { + no_symbol: true, + set_property: true + }), + no_symbol_var_declaration, + r#"var { a, b, ...c } = obj;"#, + r#" +var { + a, + b +} = obj, + c = _objectWithoutPropertiesLoose(obj, ["a", "b"]); +"# +); + +test!( + syntax(), + |_| tr(Config { + no_symbol: true, + set_property: true + }), + set_property_assignment, + r#" +var x; +var y; +var z; + +z = { x, ...y }; + +z = { x, w: { ...y } }; +"#, + r#" +var x; +var y; +var z; +z = _extends({ + x +}, y); +z = { + x, + w: _extends({}, y) +}; +"# +); + +test!( + syntax(), + |_| tr(Config { + no_symbol: true, + set_property: true + }), + set_property_expression, + r#" +var a; +var b; +var c; +var d; +var x; +var y; + +({ x, ...y, a, ...b, c }); + +({ ...Object.prototype }); + +({ ...{ foo: 'bar' } }); + +({ ...{ foo: 'bar' }, ...{ bar: 'baz' } }); + +({ ...{ get foo () { return 'foo' } } }); +"#, + r#" + +var a; +var b; +var c; +var d; +var x; +var y; +_extends({ x }, y, { a }, b, { c }); +_extends({}, Object.prototype); +_extends({}, { + foo: 'bar' +}); +_extends({}, { + foo: 'bar' +}, { + bar: 'baz' +}); +_extends({}, { + get foo() { + return 'foo'; + } + +}); +"# +); + +test_exec!( + syntax(), + |_| tr(Config { + no_symbol: true, + set_property: true + }), + set_property_expression_exec, + r#" +var log = []; + +var a = { + ...{ get foo() { log.push(1); } }, + get bar() { log.push(2); } +}; + +// Loose mode uses regular Get, not GetOwnProperty. +expect(log).toEqual([1, 2]); +"# +); + +test_exec!( + syntax(), + |_| tr(Config { + no_symbol: true, + set_property: true + }), + set_property_no_get_own_property_exec, + r#" +const oldGOPDs = Object.getOwnPropertyDescriptors; +Object.getOwnPropertyDescriptors = null; + +try { + ({ ...{ a: 1 }, b: 1, ...{} }); +} finally { + Object.getOwnPropertyDescriptors = oldGOPDs; +} +"# +); + +test_exec!( + syntax(), + |_| tr(Config { + no_symbol: true, + set_property: true + }), + set_property_no_object_assign_exec, + r#" +"use strict"; +Object.defineProperty(Object.prototype, 'NOSET', { + get(value) { + // noop + }, +}); + +Object.defineProperty(Object.prototype, 'NOWRITE', { + writable: false, + value: 'abc', +}); + +const obj = { 'NOSET': 123 }; +// this won't work as expected if transformed as Object.assign (or equivalent) +// because those trigger object setters (spread don't) +expect(() => { + const objSpread = { ...obj }; +}).toThrow(); + +const obj2 = { 'NOWRITE': 456 }; +// this throws `TypeError: Cannot assign to read only property 'NOWRITE'` +// if transformed as Object.assign (or equivalent) because those use *assignment* for creating properties +// (spread defines them) +expect(() => { + const obj2Spread = { ...obj2 }; +}).toThrow(); + +const KEY = Symbol('key'); +const obj3Spread = { ...{ get foo () { return 'bar' } }, [KEY]: 'symbol' }; +expect(Object.getOwnPropertyDescriptor(obj3Spread, 'foo').value).toBe('bar'); +expect(Object.getOwnPropertyDescriptor(obj3Spread, KEY).value).toBe('symbol'); + +const obj4Spread = { ...Object.prototype }; +expect(Object.getOwnPropertyDescriptor(obj4Spread, 'hasOwnProperty')).toBeUndefined(); + +expect(() => ({ ...null, ...undefined })).not.toThrow(); + +const o = Object.create(null); +o.a = 'foo'; +o.__proto__ = []; +const o2 = { ...o }; +// Loose will do o2.__proto__ = [] +expect(Array.isArray(Object.getPrototypeOf(o2))).toBe(true); +"# +); diff --git a/crates/swc_ecma_transforms_compat/tests/es2020_optional_chaining.rs b/crates/swc_ecma_transforms_compat/tests/es2020_optional_chaining.rs index fcba3362f6bc..7648f1df0039 100644 --- a/crates/swc_ecma_transforms_compat/tests/es2020_optional_chaining.rs +++ b/crates/swc_ecma_transforms_compat/tests/es2020_optional_chaining.rs @@ -1,11 +1,11 @@ use std::{fs::read_to_string, path::PathBuf}; use swc_ecma_parser::{Syntax, TsConfig}; -use swc_ecma_transforms_compat::es2020::optional_chaining; +use swc_ecma_transforms_compat::es2020::{opt_chaining::Config, optional_chaining}; use swc_ecma_transforms_testing::{compare_stdout, test, test_exec}; use swc_ecma_visit::Fold; -fn tr(_: ()) -> impl Fold { - optional_chaining() +fn tr(c: Config) -> impl Fold { + optional_chaining(c) } fn syntax() -> Syntax { @@ -15,21 +15,229 @@ fn syntax() -> Syntax { } // general_memoize_loose +test!( + syntax(), + |_| tr(Config { + no_document_all: true, + pure_getter: true + }), + general_memoize_loose, + r#" +"use strict"; + +function test(foo) { + foo?.bar; + + foo?.bar?.baz; + + foo?.(foo); + + foo?.bar() + + foo.get(bar)?.() + + foo.bar()?.() + foo[bar]()?.() + + foo.bar().baz?.() + foo[bar]().baz?.() + + foo.bar?.(foo.bar, false) + + foo?.bar?.(foo.bar, true) + + foo.bar?.baz(foo.bar, false) + + foo?.bar?.baz(foo.bar, true) + + foo.bar?.baz?.(foo.bar, false) + + foo?.bar?.baz?.(foo.bar, true) +} +"#, + r#" +"use strict"; + +function test(foo) { + var ref, ref1, ref2, ref3, _obj, ref4, _obj1, ref5, ref6, ref7, ref8, ref9; + + foo == null ? void 0 : foo.bar; + foo == null ? void 0 : (ref = foo.bar) == null ? void 0 : ref.baz; + foo == null ? void 0 : foo(foo); + foo == null ? void 0 : foo.bar(); + (ref1 = foo.get(bar)) == null ? void 0 : ref1(); + (ref2 = foo.bar()) == null ? void 0 : ref2(); + (ref3 = foo[bar]()) == null ? void 0 : ref3(); + (ref4 = (_obj = foo.bar()).baz) == null ? void 0 : ref4.call(_obj); + (ref5 = (_obj1 = foo[bar]()).baz) == null ? void 0 : ref5.call(_obj1); + foo.bar == null ? void 0 : foo.bar(foo.bar, false); + foo == null ? void 0 : foo.bar == null ? void 0 : foo.bar(foo.bar, true); + (ref6 = foo.bar) == null ? void 0 : ref6.baz(foo.bar, false); + foo == null ? void 0 : (ref7 = foo.bar) == null ? void 0 : ref7.baz(foo.bar, true); + (ref8 = foo.bar) == null ? void 0 : ref8.baz == null ? void 0 : ref8.baz(foo.bar, false); + foo == null ? void 0 : (ref9 = foo.bar) == null ? void 0 : ref9.baz == null ? void 0 : ref9.baz(foo.bar, true); +} +"# +); // general_lhs_assignment_read_and_update // general_function_call_loose +test!( + syntax(), + |_| tr(Config { + pure_getter: true, + no_document_all: true + }), + general_function_call_loose, + r#" +foo?.(foo); + +foo?.bar() + +foo.bar?.(foo.bar, false) + +foo?.bar?.(foo.bar, true) +"#, + r#" +foo == null ? void 0 : foo(foo); +foo == null ? void 0 : foo.bar(); +foo.bar == null ? void 0 : foo.bar(foo.bar, false); +foo == null ? void 0 : foo.bar == null ? void 0 : foo.bar(foo.bar, true); +"# +); + +// general_function_param_loose +test!( + ignore, + syntax(), + |_| tr(Config { + pure_getter: true, + no_document_all: true + }), + general_function_param_loose, + r#" +function f(a = x?.y) {} + +function g({ a, b = a?.c }) {} + +function h(a, { b = a.b?.c?.d.e }) {} + +function i(a, { b = (a.b?.c?.d).e }) {} + +function j(a, { b = a?.b?.c().d.e }) {} +"#, + r#" +function f(a = (() => { + var _x; + + return (_x = x) == null ? void 0 : _x.y; +})()) {} + +function g({ + a, + b = a == null ? void 0 : a.c +}) {} + +function h(a, { + b = (() => { + var _a$b, _a$b$c; + + return (_a$b = a.b) == null ? void 0 : (_a$b$c = _a$b.c) == null ? void 0 : _a$b$c.d.e; + })() +}) {} + +function i(a, { + b = (() => { + var _a$b2, _a$b2$c; + + return (_a$b2 = a.b) == null ? void 0 : (_a$b2$c = _a$b2.c) == null ? void 0 : _a$b2$c.d; + })().e +}) {} + +function j(a, { + b = (() => { + var _a$b3; + + return a == null ? void 0 : (_a$b3 = a.b) == null ? void 0 : _a$b3.c().d.e; + })() +}) {} +"# +); + +test!( + ignore, + syntax(), + |_| tr(Config { + pure_getter: true, + no_document_all: true + }), + general_method_key_loose, + r#" +let x; +const a = { + [x.y?.z]() {} +}; +"#, + r#" +var _x$y; + +let x; +const a = { + [(_x$y = x.y) == null ? void 0 : _x$y.z]() {} + +}; +"# +); // regression_7642 // general_super_method_call_loose +test!( + ignore, + syntax(), + |_| tr(Config { + pure_getter: true, + no_document_all: true + }), + general_super_method_call_loose, + r#" +"use strict"; +class Base { + method() { + return 'Hello!'; + } +} + +class Derived extends Base { + method() { + return super.method?.() + } +} +"#, + r#" +"use strict"; + +class Base { + method() { + return 'Hello!'; + } +} + +class Derived extends Base { + method() { + return super.method == null ? void 0 : super.method(); + } +} +"# +); // general_lhs_update // general_assignment test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), general_assignment, r#" "use strict"; @@ -80,7 +288,7 @@ val = obj === null || obj === void 0 ? void 0 : (ref2 = obj.a) === null || ref2 // general_memoize test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), general_memoize, r#" function test(foo) { @@ -126,7 +334,7 @@ function test(foo) { // general_containers test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), general_containers, r#" var street = user.address?.street @@ -155,7 +363,7 @@ a === null || a === void 0 ? void 0 : a.b, 2; // general_delete_exec test_exec!( syntax(), - |_| tr(()), + |_| tr(Default::default()), general_delete_exec, r#" "use strict"; @@ -187,7 +395,7 @@ expect(obj.a).toBeUndefined(); // general_member_access test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), general_member_access, r#" foo?.bar; @@ -229,7 +437,7 @@ orders[client === null || client === void 0 ? void 0 : client.key].price; // general_unary test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), general_unary, r#" "use strict"; @@ -272,7 +480,7 @@ test = +(obj === null || obj === void 0 ? void 0 : (ref2 = obj.b) === null || re // general_function_call test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), general_function_call, r#" foo?.(foo); @@ -299,7 +507,7 @@ foo?.bar()?.() "#, r#" -var ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, _obj, ref9; +var ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9; foo === null || foo === void 0 ? void 0 : foo(foo); foo === null || foo === void 0 ? void 0 : foo.bar(); (ref = foo.bar) === null || ref === void 0 ? void 0 : ref.call(foo, foo.bar, false); @@ -310,13 +518,13 @@ foo === null || foo === void 0 ? void 0 : (ref2 = foo()) === null || ref2 === vo (ref4 = foo.bar) === null || ref4 === void 0 ? void 0 : (ref5 = ref4.call(foo)) === null || ref5 === void 0 ? void 0 : ref5.baz; foo === null || foo === void 0 ? void 0 : (ref6 = foo.bar) === null || ref6 === void 0 ? void 0 : ref6.call(foo).baz; foo === null || foo === void 0 ? void 0 : (ref7 = foo.bar) === null || ref7 === void 0 ? void 0 : (ref8 = ref7.call(foo)) === null || ref8 === void 0 ? void 0 : ref8.baz; -(ref9 = _obj = foo === null || foo === void 0 ? void 0 : foo.bar()) === null || ref9 === void 0 ? void 0 : ref9.call(_obj);"# +(ref9 = foo === null || foo === void 0 ? void 0 : foo.bar()) === null || ref9 === void 0 ? void 0 : ref9();"# ); // general_unary_exec test_exec!( syntax(), - |_| tr(()), + |_| tr(Default::default()), general_unary_exec, r#" "use strict"; @@ -345,7 +553,7 @@ expect(test).toBe(NaN); // general_call_exec test_exec!( syntax(), - |_| tr(()), + |_| tr(Default::default()), general_call_exec, r#" @@ -355,7 +563,7 @@ test_exec!( // general_delete test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), general_delete, r#" "use strict"; @@ -396,7 +604,7 @@ obj === null || obj === void 0 ? void 0 : delete obj.a; // regression_8354_exec test_exec!( syntax(), - |_| tr(()), + |_| tr(Default::default()), regression_8354_exec, r#" const foo = undefined; @@ -410,7 +618,7 @@ expect(foobar).toBe(undefined); // general_assignment_exec test_exec!( syntax(), - |_| tr(()), + |_| tr(Default::default()), general_assignment_exec, r#" "use strict"; @@ -448,7 +656,7 @@ expect(() => { // general_super_method_call test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), general_super_method_call, r#" "use strict"; @@ -489,7 +697,7 @@ class Derived extends Base { test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), simple_1, "obj?.a", "obj === null || obj === void 0 ? void 0 : obj.a;" @@ -497,7 +705,7 @@ test!( test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), simple_2, "obj?.a?.b", "var ref; @@ -507,7 +715,7 @@ obj === null || obj === void 0 ? void 0 : (ref = obj.a) === null || ref === void test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), simple_3, "obj?.a?.b.c", "var ref; @@ -517,7 +725,7 @@ obj === null || obj === void 0 ? void 0 : (ref = obj.a) === null || ref === void test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), call_1, "obj?.a?.b()", "var ref; @@ -528,7 +736,7 @@ obj === null || obj === void 0 ? void 0 : (ref = obj.a) === null || ref === void test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), call_2, "a?.b?.c?.()", "var ref, ref1; @@ -544,7 +752,7 @@ a === null || a === void 0 test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), issue_732_1, "test.a?.b.c.d", "var ref; @@ -553,7 +761,7 @@ test!( test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), issue_732_2, "test.a?.b.c", "var ref; @@ -562,7 +770,7 @@ test!( test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), issue_732_3, "test.a?.b.c.d.e.f.g.h.i", "var ref; @@ -572,7 +780,7 @@ test!( // https://github.com/Brooooooklyn/swc-node/issues/62 test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), swc_node_issue_62, "a.focus?.()", "var ref; @@ -581,7 +789,7 @@ test!( test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), issue_1092_1, "a?.b.c()", "a === null || a === void 0 ? void 0 : a.b.c();" @@ -589,7 +797,7 @@ test!( test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), issue_1092_2, "a?.b.c.d.e.f.g.h()", "a === null || a === void 0 ? void 0 : a.b.c.d.e.f.g.h();" @@ -597,7 +805,7 @@ test!( test_exec!( syntax(), - |_| tr(()), + |_| tr(Default::default()), swc_node_95, " const obj = { @@ -618,7 +826,7 @@ test_exec!( test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), swc_node_95_2, " obj?.a?.b?.c() @@ -631,7 +839,7 @@ obj === null || obj === void 0 ? void 0 : (ref = obj.a) === null || ref === void test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), swc_node_95_3, " expect(obj?.a?.b?.c()).toBe(2) @@ -645,7 +853,7 @@ expect(obj === null || obj === void 0 ? void 0 : (ref = obj.a) === null || ref = test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), issue_1130_1, " const result = data?.filter(item => Math.random() > 0.5).map(item => JSON.stringify(item)); @@ -658,7 +866,7 @@ const result = data === null || data === void 0 ? void 0 : data.filter(item => M test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), issue_1130_2, " const r = d?.filter(i => Math.random() > 0.5).map(i => JSON.stringify(i)); @@ -671,7 +879,7 @@ const r = d === null || d === void 0 ? void 0 : d.filter(i => Math.random() > 0. test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), issue_1133_1, " async function foo() { @@ -687,26 +895,25 @@ async function foo() { test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), issue_1136_1, - " + r#" const PATCHES = new Map(); const ident = 'foo'; const patch = PATCHES.get(ident)?.(); - ", - " - var _obj, ref; + "#, + r#" + var ref; const PATCHES = new Map(); - const ident = \"foo\"; - const patch = (ref = _obj = PATCHES.get(ident)) === null || ref === void 0 ? void 0 : \ - ref.call(_obj); - " + const ident = "foo"; + const patch = (ref = PATCHES.get(ident)) === null || ref === void 0 ? void 0 : ref(); + "# ); test!( syntax(), - |_| tr(()), + |_| tr(Default::default()), issue_1836_1, " function bug() { @@ -727,9 +934,76 @@ test!( " ); +test_exec!( + ignore, + syntax(), + |_| tr(Config { + no_document_all: true, + pure_getter: true + }), + general_parenthesized_expression_member_call_loose, + r#" +class Foo { + constructor() { + this.x = 1; + this.self = this; + } + m() { return this.x; }; + getSelf() { return this } + + test() { + const Foo = this; + const o = { Foo: Foo }; + const fn = function () { + return o; + }; + + expect((Foo?.["m"])()).toEqual(1); + expect((Foo?.["m"])().toString).toEqual(1..toString); + expect((Foo?.["m"])().toString()).toEqual('1'); + + expect(((Foo?.["m"]))()).toEqual(1); + expect(((Foo?.["m"]))().toString).toEqual(1..toString); + expect(((Foo?.["m"]))().toString()).toEqual('1'); + + expect((o?.Foo.m)()).toEqual(1); + expect((o?.Foo.m)().toString).toEqual(1..toString); + expect((o?.Foo.m)().toString()).toEqual('1'); + + expect((((o.Foo?.self.getSelf)())?.m)()).toEqual(1); + expect((((o.Foo.self?.getSelf)())?.m)()).toEqual(1); + + expect((((fn()?.Foo?.self.getSelf)())?.m)()).toEqual(1); + expect((((fn?.().Foo.self?.getSelf)())?.m)()).toEqual(1); + } + + testNull() { + const o = null; + + expect(() => { (o?.Foo.m)() }).toThrow(); + expect(() => { (o?.Foo.m)().toString }).toThrow(); + expect(() => { (o?.Foo.m)().toString() }).toThrow(); + + expect(() => { (((o.Foo?.self.getSelf)())?.m)() }).toThrow(); + expect(() => { (((o.Foo.self?.getSelf)())?.m)() }).toThrow(); + + expect(() => (((fn()?.Foo?.self.getSelf)())?.m)()).toThrow(); + expect(() => (((fn?.().Foo.self?.getSelf)())?.m)()).toThrow(); + } +} + +(new Foo).test(); +(new Foo).testNull(); +"# +); + #[testing::fixture("tests/fixture/opt-chain/**/exec.js")] fn exec(input: PathBuf) { let src = read_to_string(&input).unwrap(); - compare_stdout(Default::default(), |_| optional_chaining(), &src); + compare_stdout( + Default::default(), + |_| optional_chaining(Default::default()), + &src, + ); } diff --git a/crates/swc_ecma_transforms_compat/tests/es2022_class_properties.rs b/crates/swc_ecma_transforms_compat/tests/es2022_class_properties.rs index 7b956c36d150..996933227a16 100644 --- a/crates/swc_ecma_transforms_compat/tests/es2022_class_properties.rs +++ b/crates/swc_ecma_transforms_compat/tests/es2022_class_properties.rs @@ -5390,7 +5390,7 @@ test_exec!( syntax(), |_| chain!( class_properties(class_properties::Config { loose: false }), - template_literal() + template_literal(Default::default()) ), issue_1742_2, " diff --git a/crates/swc_ecma_transforms_module/tests/common_js.rs b/crates/swc_ecma_transforms_module/tests/common_js.rs index b6bb92ef7d0a..5949b4ecaa8a 100644 --- a/crates/swc_ecma_transforms_module/tests/common_js.rs +++ b/crates/swc_ecma_transforms_module/tests/common_js.rs @@ -4336,7 +4336,7 @@ test!( syntax(), |_| chain!( resolver(), - object_rest_spread(), + object_rest_spread(Default::default()), destructuring(destructuring::Config { loose: false }), common_js(Mark::fresh(Mark::root()), Default::default(), None), ), diff --git a/crates/swc_ecma_transforms_optimization/tests/simplify.rs b/crates/swc_ecma_transforms_optimization/tests/simplify.rs index 285266f0ec23..3763e50f2ab0 100644 --- a/crates/swc_ecma_transforms_optimization/tests/simplify.rs +++ b/crates/swc_ecma_transforms_optimization/tests/simplify.rs @@ -542,7 +542,7 @@ test!( decorators(Default::default()), class_properties(class_properties::Config { loose: false }), simplifier(Default::default()), - es2018(), + es2018(Default::default()), es2017(), es2016(), es2015( diff --git a/crates/swc_ecma_transforms_typescript/benches/compat.rs b/crates/swc_ecma_transforms_typescript/benches/compat.rs index 24fedd4fca64..752926af8986 100644 --- a/crates/swc_ecma_transforms_typescript/benches/compat.rs +++ b/crates/swc_ecma_transforms_typescript/benches/compat.rs @@ -100,14 +100,18 @@ fn es2020(b: &mut Bencher) { #[bench] fn es2020_nullish_coalescing(b: &mut Bencher) { run(b, || { - swc_ecma_transforms_compat::es2020::nullish_coalescing() + swc_ecma_transforms_compat::es2020::nullish_coalescing( + swc_ecma_transforms_compat::es2020::nullish_coalescing::Config { + no_document_all: false, + }, + ) }); } #[bench] fn es2020_optional_chaining(b: &mut Bencher) { run(b, || { - swc_ecma_transforms_compat::es2020::optional_chaining() + swc_ecma_transforms_compat::es2020::optional_chaining(Default::default()) }); } @@ -120,13 +124,13 @@ fn es2022_class_properties(b: &mut Bencher) { #[bench] fn es2018(b: &mut Bencher) { - run(b, || swc_ecma_transforms_compat::es2018()); + run(b, || swc_ecma_transforms_compat::es2018(Default::default())); } #[bench] fn es2018_object_rest_spread(b: &mut Bencher) { run(b, || { - swc_ecma_transforms_compat::es2018::object_rest_spread() + swc_ecma_transforms_compat::es2018::object_rest_spread(Default::default()) }); } @@ -196,7 +200,9 @@ fn es2015_classes(b: &mut Bencher) { #[bench] fn es2015_computed_props(b: &mut Bencher) { - run(b, swc_ecma_transforms_compat::es2015::computed_properties); + run(b, || { + swc_ecma_transforms_compat::es2015::computed_properties(Default::default()) + }); } #[bench] @@ -270,7 +276,7 @@ fn full_es2016(b: &mut Bencher) { chain!( swc_ecma_transforms_compat::es2022(Default::default()), swc_ecma_transforms_compat::es2019(), - swc_ecma_transforms_compat::es2018(), + swc_ecma_transforms_compat::es2018(Default::default()), swc_ecma_transforms_compat::es2017(), swc_ecma_transforms_compat::es2016(), ) @@ -283,7 +289,7 @@ fn full_es2017(b: &mut Bencher) { chain!( swc_ecma_transforms_compat::es2022(Default::default()), swc_ecma_transforms_compat::es2019(), - swc_ecma_transforms_compat::es2018(), + swc_ecma_transforms_compat::es2018(Default::default()), swc_ecma_transforms_compat::es2017(), ) }); @@ -295,7 +301,7 @@ fn full_es2018(b: &mut Bencher) { chain!( swc_ecma_transforms_compat::es2022(Default::default()), swc_ecma_transforms_compat::es2019(), - swc_ecma_transforms_compat::es2018(), + swc_ecma_transforms_compat::es2018(Default::default()), ) }); } diff --git a/crates/swc_ecma_transforms_typescript/tests/strip.rs b/crates/swc_ecma_transforms_typescript/tests/strip.rs index 761eb09e4624..bd51b04db3d0 100644 --- a/crates/swc_ecma_transforms_typescript/tests/strip.rs +++ b/crates/swc_ecma_transforms_typescript/tests/strip.rs @@ -3184,7 +3184,7 @@ test!( decorators: true, ..Default::default() }), - |_| chain!(tr(), optional_chaining()), + |_| chain!(tr(), optional_chaining(Default::default())), issue_1149_1, " const tmp = tt?.map((t: any) => t).join((v: any) => v); @@ -3198,7 +3198,7 @@ test!( Syntax::Typescript(TsConfig { ..Default::default() }), - |_| chain!(tr(), nullish_coalescing()), + |_| chain!(tr(), nullish_coalescing(Default::default())), issue_1123_1, r#" interface SuperSubmission {