swc_ecma_compat_es2015/
lib.rs

1#![allow(clippy::vec_box)]
2#![allow(clippy::boxed_local)]
3
4use serde::Deserialize;
5use swc_common::{comments::Comments, Mark};
6use swc_ecma_ast::Pass;
7use swc_ecma_compat_common::regexp::{self, regexp};
8
9pub use self::{
10    arrow::arrow, block_scoped_fn::block_scoped_functions, block_scoping::block_scoping,
11    classes::classes, computed_props::computed_properties, destructuring::destructuring,
12    duplicate_keys::duplicate_keys, for_of::for_of, function_name::function_name,
13    instanceof::instance_of, new_target::new_target, object_super::object_super,
14    parameters::parameters, shorthand_property::shorthand, spread::spread,
15    sticky_regex::sticky_regex, template_literal::template_literal, typeof_symbol::typeof_symbol,
16};
17
18mod arrow;
19mod block_scoped_fn;
20mod block_scoping;
21pub mod classes;
22pub mod computed_props;
23pub mod destructuring;
24mod duplicate_keys;
25pub mod for_of;
26mod function_name;
27pub mod generator;
28mod instanceof;
29pub mod new_target;
30mod object_super;
31pub mod parameters;
32pub mod regenerator;
33mod shorthand_property;
34pub mod spread;
35mod sticky_regex;
36pub mod template_literal;
37pub mod typeof_symbol;
38
39fn exprs(unresolved_mark: Mark) -> impl Pass {
40    (
41        arrow(unresolved_mark),
42        duplicate_keys(),
43        sticky_regex(),
44        instance_of(),
45    )
46}
47
48/// Compiles es2015 to es5.
49///
50/// # Parameters
51///
52/// ## `unresolved_mark`
53///
54/// Used to generate `require` calls.
55/// See the documentation of [regenerator](self::regenerator::regenerator) for
56/// more details.
57pub fn es2015<C>(unresolved_mark: Mark, comments: Option<C>, c: Config) -> impl Pass
58where
59    C: Comments + Clone,
60{
61    (
62        (
63            regexp(regexp::Config {
64                dot_all_regex: false,
65                has_indices: false,
66                lookbehind_assertion: false,
67                named_capturing_groups_regex: false,
68                sticky_regex: true,
69                unicode_property_regex: false,
70                unicode_regex: true,
71                unicode_sets_regex: false,
72            }),
73            block_scoped_functions(),
74            template_literal(c.template_literal),
75            classes(c.classes),
76            new_target(),
77            spread(c.spread, unresolved_mark),
78        ),
79        // https://github.com/Microsoft/TypeScript/issues/5441
80        if !c.typescript {
81            Some(object_super())
82        } else {
83            None
84        },
85        shorthand(),
86        function_name(),
87        for_of(c.for_of),
88        // Should come before parameters
89        // See: https://github.com/swc-project/swc/issues/1036
90        parameters(c.parameters, unresolved_mark),
91        (
92            exprs(unresolved_mark),
93            typeof_symbol(c.typeof_symbol),
94            computed_properties(c.computed_props),
95            destructuring(c.destructuring),
96            block_scoping(unresolved_mark),
97            generator::generator(unresolved_mark, comments.clone()),
98        ),
99    )
100}
101
102#[derive(Debug, Clone, Default, Deserialize)]
103#[serde(rename_all = "camelCase")]
104pub struct Config {
105    #[serde(default)]
106    pub classes: classes::Config,
107
108    #[serde(flatten)]
109    pub computed_props: computed_props::Config,
110
111    #[serde(flatten)]
112    pub for_of: for_of::Config,
113
114    #[serde(flatten)]
115    pub destructuring: destructuring::Config,
116
117    #[serde(flatten)]
118    pub typeof_symbol: typeof_symbol::Config,
119
120    #[serde(flatten)]
121    pub spread: spread::Config,
122
123    #[serde(default)]
124    pub regenerator: regenerator::Config,
125
126    #[serde(default)]
127    pub template_literal: template_literal::Config,
128
129    #[serde(default)]
130    pub parameters: parameters::Config,
131
132    #[serde(default)]
133    pub typescript: bool,
134}
135
136#[cfg(test)]
137mod tests {
138    use swc_ecma_transforms_base::resolver;
139    use swc_ecma_transforms_testing::{test, test_exec};
140
141    use super::*;
142
143    test!(
144        ::swc_ecma_parser::Syntax::default(),
145        |t| es2015(
146            Mark::fresh(Mark::root()),
147            Some(t.comments.clone()),
148            Default::default()
149        ),
150        issue_169,
151        r#"
152export class Foo {
153	func(a, b = Date.now()) {
154		return {a};
155	}
156}
157"#
158    );
159
160    test!(
161        ::swc_ecma_parser::Syntax::default(),
162        |t| es2015(
163            Mark::fresh(Mark::root()),
164            Some(t.comments.clone()),
165            Default::default()
166        ),
167        issue_189,
168        r#"
169class HomePage extends React.Component {}
170"#
171    );
172
173    test!(
174        ::swc_ecma_parser::Syntax::default(),
175        |t| es2015(
176            Mark::fresh(Mark::root()),
177            Some(t.comments.clone()),
178            Default::default()
179        ),
180        issue_227,
181        "export default function fn1(...args) {
182  fn2(...args);
183}"
184    );
185
186    test!(
187        ::swc_ecma_parser::Syntax::default(),
188        |_| (
189            block_scoped_functions(),
190            resolver(Mark::new(), Mark::new(), false)
191        ),
192        issue_271,
193        "
194function foo(scope) {
195    scope.startOperation = startOperation;
196
197    function startOperation(operation) {
198        scope.agentOperation = operation;
199    }
200}
201"
202    );
203
204    //     test!(
205    //         ::swc_ecma_parser::Syntax::default(),
206    //         |_| (
207    //             resolver(),
208    //             class_properties(),
209    //             // Optional::new(compat::es2018(), target <= EsVersion::Es2018),
210    //             // Optional::new(compat::es2017(), target <= EsVersion::Es2017),
211    //             // Optional::new(compat::es2016(), target <= EsVersion::Es2016),
212    //             // Optional::new(compat::es2015(Mark::fresh(Mark::root()),
213    // Default::default()), target <= EsVersion::Es2015),             //
214    // Optional::new(compat::es3(), target <= EsVersion::Es3),
215    // hygiene(),             fixer(),
216    //         ),
217    //         issue_405,
218    //         "function Quadtree$1(x, y, x0, y0, x1, y1) {
219    //     this._x = x;
220    //     this._y = y;
221    //     this._x0 = x0;
222    //     this._y0 = y0;
223    //     this._x1 = x1;
224    //     this._y1 = y1;
225    //     this._root = undefined;
226    //   }
227    //   ",
228    //         ""
229    //     );
230
231    test!(
232        ::swc_ecma_parser::Syntax::default(),
233        |t| es2015(
234            Mark::fresh(Mark::root()),
235            Some(t.comments.clone()),
236            Default::default()
237        ),
238        issue_413,
239        r#"
240export const getBadgeBorderRadius = (text, color) => {
241  return (text && style) || {}
242}"#
243    );
244
245    test!(
246        ::swc_ecma_parser::Syntax::default(),
247        |t| es2015(
248            Mark::fresh(Mark::root()),
249            Some(t.comments.clone()),
250            Default::default()
251        ),
252        issue_400_1,
253        "class A {
254    constructor() {
255        this.a_num = 10;
256    }
257
258    print() {
259        expect(this.a_num).toBe(10);
260    }
261}
262
263class B extends A {
264    constructor(num) {
265        super();
266        this.b_num = num;
267    }
268
269    print() {
270        expect(this.b_num).toBe(20);
271        super.print();
272    }
273}
274"
275    );
276
277    test_exec!(
278        ::swc_ecma_parser::Syntax::default(),
279        |t| es2015(
280            Mark::fresh(Mark::root()),
281            Some(t.comments.clone()),
282            Default::default()
283        ),
284        issue_400_2,
285        "class A {
286    constructor() {
287        this.a_num = 10;
288    }
289
290    print() {
291        expect(this.a_num).toBe(10);
292    }
293}
294
295class B extends A {
296    constructor(num) {
297        super();
298        this.b_num = num;
299    }
300
301    print() {
302        expect(this.b_num).toBe(20);
303        super.print();
304    }
305}
306
307return new B(20).print()"
308    );
309
310    test!(
311        ::swc_ecma_parser::Syntax::default(),
312        |t| es2015(
313            Mark::fresh(Mark::root()),
314            Some(t.comments.clone()),
315            Default::default()
316        ),
317        issue_1660_1,
318        "
319        console.log(class {run(){}});
320        "
321    );
322
323    test_exec!(
324        ::swc_ecma_parser::Syntax::default(),
325        |t| es2015(
326            Mark::fresh(Mark::root()),
327            Some(t.comments.clone()),
328            Default::default()
329        ),
330        issue_2682,
331        "class MyObject extends null {
332            constructor() {
333              return Object.create(new.target.prototype);
334            }
335          }
336        var obj = new MyObject();
337        expect(obj.constructor).toBe(MyObject);
338        "
339    );
340
341    test!(
342        ::swc_ecma_parser::Syntax::default(),
343        |t| es2015(
344            Mark::fresh(Mark::root()),
345            Some(t.comments.clone()),
346            Config {
347                classes: classes::Config {
348                    set_class_methods: true,
349                    ..classes::Config::default()
350                },
351                ..Config::default()
352            }
353        ),
354        should_escape_keyword_in_method,
355        r#"
356export class Foo {
357	let() {}
358}
359"#
360    );
361
362    test!(
363        ::swc_ecma_parser::Syntax::default(),
364        |t| es2015(
365            Mark::fresh(Mark::root()),
366            Some(t.comments.clone()),
367            Config {
368                ..Default::default()
369            }
370        ),
371        issue_8871,
372        r#"
373        const x = "</" + "script>";
374        const y = "<\/script>";
375        const z = "\/\/   \\";
376        export { x, y, z };
377        "#
378    );
379}