Skip to content

Commit

Permalink
feat(es/transforms/compat): Add more loose mode (#2611)
Browse files Browse the repository at this point in the history
swc_ecma_transforms_compat:
- `computed_properties`: Add loose mode.
- `template_literals`: Add loose mode.
- `object_spread`: Add loose mode.
- `optional_chaining`: Add loose mode.
- `nullish_coalescing`: Add loose mode.
- `optional_chaining`: Fix #2734.
  • Loading branch information
Austaras authored Nov 15, 2021
1 parent f2c67b8 commit dc58122
Show file tree
Hide file tree
Showing 28 changed files with 1,863 additions and 379 deletions.
24 changes: 22 additions & 2 deletions crates/swc/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,15 +186,28 @@ 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(
compat::es2019::es2019(),
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(
Expand All @@ -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
},
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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(...[
Expand Down Expand Up @@ -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;
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -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, [
Expand Down Expand Up @@ -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;
Original file line number Diff line number Diff line change
@@ -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)) {
Expand Down Expand Up @@ -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;
2 changes: 1 addition & 1 deletion crates/swc_babel_compat/benches/babelify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(|| {
Expand Down
38 changes: 33 additions & 5 deletions crates/swc_ecma_preset_env/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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,
Expand All @@ -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!(
Expand Down
111 changes: 77 additions & 34 deletions crates/swc_ecma_transforms_compat/src/es2015/computed_props.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use serde::Deserialize;
use swc_common::{Mark, Spanned, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_transforms_base::helper;
Expand Down Expand Up @@ -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<VarDeclarator>,
used_define_enum_props: bool,
c: Config,
}

/// TODO: VisitMut
Expand Down Expand Up @@ -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,
Expand All @@ -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")
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand All @@ -214,20 +233,34 @@ 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"),
args: vec![exprs.pop().unwrap().as_arg(), key.as_arg(), value.as_arg()],
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 {
Expand Down Expand Up @@ -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
Expand All @@ -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),
}
}

Expand Down
Loading

0 comments on commit dc58122

Please sign in to comment.