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