swc_ecma_compat_common/
regexp.rs

1use swc_common::util::take::Take;
2use swc_ecma_ast::{CallExpr, Expr, Lit, Pass, Regex};
3use swc_ecma_utils::{quote_ident, ExprFactory};
4use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};
5
6pub fn regexp(config: Config) -> impl Pass {
7    visit_mut_pass(RegExp { config })
8}
9
10#[derive(Default, Clone, Copy)]
11pub struct Config {
12    /// [s/dotAll flag for regular expressions](https://tc39.github.io/proposal-regexp-dotall-flag/)
13    pub dot_all_regex: bool,
14    /// [RegExp.prototype.hasIndices](https://262.ecma-international.org/13.0/#sec-get-regexp.prototype.hasIndices)
15    pub has_indices: bool,
16    /// [RegExp Lookbehind Assertions](https://tc39.es/proposal-regexp-lookbehind/)
17    pub lookbehind_assertion: bool,
18    /// [Named capture groups in regular expressions](https://tc39.es/proposal-regexp-named-groups/)
19    pub named_capturing_groups_regex: bool,
20    /// [RegExp.prototype.sticky](https://tc39.es/ecma262/multipage/text-processing.html#sec-get-regexp.prototype.sticky)
21    pub sticky_regex: bool,
22    /// [Unicode property escapes in regular expressions](https://tc39.es/proposal-regexp-unicode-property-escapes/)
23    pub unicode_property_regex: bool,
24    /// [RegExp.prototype.unicode](https://tc39.es/ecma262/multipage/text-processing.html#sec-get-regexp.prototype.unicode)
25    pub unicode_regex: bool,
26    // [RegExp.prototype.unicodeSets](https://github.com/tc39/proposal-regexp-v-flag)
27    pub unicode_sets_regex: bool,
28}
29
30struct RegExp {
31    config: Config,
32}
33
34impl VisitMut for RegExp {
35    noop_visit_mut_type!(fail);
36
37    fn visit_mut_expr(&mut self, expr: &mut Expr) {
38        expr.visit_mut_children_with(self);
39
40        if let Expr::Lit(Lit::Regex(regex)) = expr {
41            if (self.config.dot_all_regex && regex.flags.contains('s'))
42                || (self.config.sticky_regex && regex.flags.contains('y'))
43                || (self.config.unicode_regex && regex.flags.contains('u'))
44                || (self.config.unicode_sets_regex && regex.flags.contains('v'))
45                || (self.config.has_indices && regex.flags.contains('d'))
46                || (self.config.named_capturing_groups_regex && regex.exp.contains("(?<"))
47                || (self.config.lookbehind_assertion && regex.exp.contains("(?<=")
48                    || regex.exp.contains("(?<!"))
49                || (self.config.unicode_property_regex
50                    && (regex.exp.contains("\\p{") || regex.exp.contains("\\P{")))
51            {
52                let Regex { exp, flags, span } = regex.take();
53
54                let exp: Expr = exp.into();
55                let mut args = vec![exp.into()];
56
57                if !flags.is_empty() {
58                    let flags: Expr = flags.into();
59                    args.push(flags.into());
60                }
61
62                *expr = CallExpr {
63                    span,
64                    callee: quote_ident!("RegExp").as_callee(),
65                    args,
66                    ..Default::default()
67                }
68                .into()
69            }
70        }
71    }
72}