swc_ecma_compat_common/
regexp.rs1use 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 pub dot_all_regex: bool,
14 pub has_indices: bool,
16 pub lookbehind_assertion: bool,
18 pub named_capturing_groups_regex: bool,
20 pub sticky_regex: bool,
22 pub unicode_property_regex: bool,
24 pub unicode_regex: bool,
26 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}