swc_ecma_compat_es2015/
sticky_regex.rs

1use swc_ecma_ast::*;
2use swc_ecma_transforms_base::perf::Parallel;
3use swc_ecma_utils::{quote_ident, ExprFactory};
4use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};
5use swc_trace_macro::swc_trace;
6
7/// Compile ES2015 sticky regex to an ES5 RegExp constructor
8///
9///# Example
10///## In
11///
12/// ```js
13/// /o+/y;
14/// ```
15///
16///## Out
17///
18/// ```js
19/// new RegExp("o+", "y")
20/// ```
21pub fn sticky_regex() -> impl Pass {
22    visit_mut_pass(StickyRegex)
23}
24
25struct StickyRegex;
26
27impl Parallel for StickyRegex {
28    fn merge(&mut self, _: Self) {}
29
30    fn create(&self) -> Self {
31        StickyRegex
32    }
33}
34
35#[swc_trace]
36impl VisitMut for StickyRegex {
37    noop_visit_mut_type!(fail);
38
39    fn visit_mut_expr(&mut self, e: &mut Expr) {
40        e.visit_mut_children_with(self);
41
42        if let Expr::Lit(Lit::Regex(Regex { exp, flags, span })) = e {
43            if flags.contains('y') {
44                *e = NewExpr {
45                    span: *span,
46                    callee: Box::new(quote_ident!(Default::default(), *span, "RegExp").into()),
47                    args: Some(vec![exp.clone().as_arg(), flags.clone().as_arg()]),
48                    ..Default::default()
49                }
50                .into()
51            }
52        }
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use swc_ecma_transforms_testing::test;
59
60    use super::*;
61
62    test!(
63        ::swc_ecma_parser::Syntax::default(),
64        |_| sticky_regex(),
65        babel_basic,
66        "var re = /o\"'+/y;"
67    );
68
69    test!(
70        ::swc_ecma_parser::Syntax::default(),
71        |_| sticky_regex(),
72        babel_ignore_non_sticky,
73        "var re = /o+/;"
74    );
75}