Repository: swc-project/swc (rust)

Issue:
'DotAll RegExp flag' is not Converted to ES5 Syntax
### Describe the bug

'DotAll RegExp flag' is only available after ES9. SWC does not convert the syntax.

### Input code

```typescript
const v0 = "Hello\nWorld";
console.info(`Without 's' flag:`, /Hello.World/.test(v0));
console.info(`With 's' flag:`, /Hello.World/s.test(v0));
```


### Config

```json
{
  "jsc": {
    "parser": {
      "syntax": "ecmascript",
      "jsx": false,
      "dynamicImport": false,
      "privateMethod": false,
      "functionBind": false,
      "exportDefaultFrom": false,
      "exportNamespaceFrom": false,
      "decorators": false,
      "decoratorsBeforeExport": false,
      "topLevelAwait": false,
      "importMeta": false
    },
    "transform": null,
    "target": "es5",
    "loose": false,
    "externalHelpers": false
  },
  "module": {
    "type": "commonjs",
    "strict": false,
    "strictMode": false,
    "lazy": false,
    "noInterop": false
  }
}
```


### Playground link

https://play.swc.rs/?version=1.2.161&code=H4sIAAAAAAAAA0vOzysuUSgzULBVUPJIzcnJj8kLzy%2FKSVGy5koGSuXnpOpl5qXlaySEZ5Zk5JeWKKgXqyuk5SSmWyXoKOiDdeiBNejrlaQWl2iUGWhqYtOKW18xkkYAT3n%2Fr44AAAA%3D&config=H4sIAAAAAAAAA0WNwQ6DQAhE%2F4Wz1178gvbgR5AtNmtY2AAmWuO%2Fd01tPcFMZt5sMHmCfoOK5mTH56sELtADpYKeLNeArsWaNSI77R2EofioVqCXmbkZaC%2BKo%2BO3FmZVpzPeAS1BJsh34krmF6boc2Y6NmOt7ULSUlQmbwgPyyn%2BjK8c9HlhGd%2FrX4g%2BpK1ovejZh5N%2FZkqWPP4q%2Bwfiymdk%2BgAAAA%3D%3D

### Expected behavior

```js
console.info("With 's' flag:", /Hello[\s\S]World/.test(v0));
```

### Actual behavior

_No response_

### Version

1.2.161

### Additional context

_No response_

Pull request:
feat(es/regex): support more regex expressions
<!-- Note: CI script will automatically rebase your PR so please do not rebase unless required -->

<!-- Thanks for submitting a pull request! Please provide enough information so that others can review your pull request. -->

**Description:**
- [x] `dot_all_regex`
- [ ] `lookbehind_assertion` | not supported by core-js, Lack of compat data 
- [x] `named_capturing_groups_regex`
- [x] `sticky_regex`
- [ ] `unicode_property_regex` | not supported by core-js
- [ ] `unicode_regex` | not supported by core-js

<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? -->

**BREAKING CHANGE:**

<!--
If this PR introduces a breaking change, it must contain a notice for it to be included in the CHANGELOG. Add description or remove entirely if not breaking.

You may need to update `MIGRATION.md` for the breaking changes.
-->

**Related issue (if exists):**
- close #4192

Patch:
diff --git a/crates/swc_ecma_ast/src/expr.rs b/crates/swc_ecma_ast/src/expr.rs
index d4cd7c3416..fc0e8b8490 100644
--- a/crates/swc_ecma_ast/src/expr.rs
+++ b/crates/swc_ecma_ast/src/expr.rs
@@ -888,6 +888,15 @@ impl Spanned for ExprOrSpread {
     }
 }
 
+impl From<Expr> for ExprOrSpread {
+    fn from(e: Expr) -> Self {
+        Self {
+            spread: None,
+            expr: Box::new(e),
+        }
+    }
+}
+
 #[ast_node]
 #[derive(Eq, Hash, Is, EqIgnoreSpan)]
 #[allow(variant_size_differences)]
diff --git a/crates/swc_ecma_ast/src/lit.rs b/crates/swc_ecma_ast/src/lit.rs
index 668d4f89fe..39578738a7 100644
--- a/crates/swc_ecma_ast/src/lit.rs
+++ b/crates/swc_ecma_ast/src/lit.rs
@@ -233,6 +233,16 @@ pub struct Regex {
     pub flags: JsWord,
 }
 
+impl Take for Regex {
+    fn dummy() -> Self {
+        Self {
+            span: DUMMY_SP,
+            exp: Default::default(),
+            flags: Default::default(),
+        }
+    }
+}
+
 #[cfg(feature = "arbitrary")]
 #[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
 impl<'a> arbitrary::Arbitrary<'a> for Regex {
diff --git a/crates/swc_ecma_preset_env/src/corejs3/data.rs b/crates/swc_ecma_preset_env/src/corejs3/data.rs
index 8cc87cfaa1..9c3c44e9a7 100644
--- a/crates/swc_ecma_preset_env/src/corejs3/data.rs
+++ b/crates/swc_ecma_preset_env/src/corejs3/data.rs
@@ -138,6 +138,8 @@ static URL_SEARCH_PARAMS_DEPENDENCIES: &[&str] = &[
     "web.dom-collections.iterator",
 ];
 
+pub static REGEXP_DEPENDENCIES: &[&str] = &["es.regexp.constructor"];
+
 pub static BUILTINS: DataMap<&[&str]> = data_map!(Map {
     AggregateError: [
         "esnext.aggregate-error",
@@ -477,6 +479,7 @@ pub static INSTANCE_PROPERTIES: DataMap<&[&str]> = data_map!(Map {
     concat: ["es.array.concat"],
     copyWithin: ["es.array.copy-within"],
     description: ["es.symbol", "es.symbol.description"],
+    dotAll: [ "es.regexp.dot-all"],
     endsWith: ["es.string.ends-with"],
     entries: ARRAY_NATURE_ITERATORS_WITH_TAG,
     every: ["es.array.every"],
@@ -522,6 +525,7 @@ pub static INSTANCE_PROPERTIES: DataMap<&[&str]> = data_map!(Map {
     splice: ["es.array.splice"],
     split: ["es.string.split", "es.regexp.exec"],
     startsWith: ["es.string.starts-with"],
+    sticky:["es.regexp.sticky"],
     strike: ["es.string.strike"],
     sub: ["es.string.sub"],
     sup: ["es.string.sup"],
diff --git a/crates/swc_ecma_preset_env/src/corejs3/usage.rs b/crates/swc_ecma_preset_env/src/corejs3/usage.rs
index f3cece2587..209e7687ee 100644
--- a/crates/swc_ecma_preset_env/src/corejs3/usage.rs
+++ b/crates/swc_ecma_preset_env/src/corejs3/usage.rs
@@ -11,7 +11,7 @@ use crate::{
         compat::DATA as CORE_JS_COMPAT_DATA,
         data::{
             COMMON_ITERATORS, INSTANCE_PROPERTIES, POSSIBLE_GLOBAL_OBJECTS, PROMISE_DEPENDENCIES,
-            STATIC_PROPERTIES,
+            REGEXP_DEPENDENCIES, STATIC_PROPERTIES,
         },
     },
     util::DataMapExt,
@@ -173,12 +173,20 @@ impl Visit for UsageVisitor {
     }
 
     /// import('something').then(...)
+    /// RegExp(".", "us")
     fn visit_call_expr(&mut self, e: &CallExpr) {
         e.visit_children_with(self);
 
-        if let Callee::Import(_) = &e.callee {
-            self.add(PROMISE_DEPENDENCIES)
-        }
+        match &e.callee {
+            Callee::Import(_) => self.add(PROMISE_DEPENDENCIES),
+            Callee::Expr(expr) => match **expr {
+                Expr::Ident(ref ident) if ident.sym == js_word!("RegExp") => {
+                    self.add(REGEXP_DEPENDENCIES)
+                }
+                _ => {}
+            },
+            _ => {}
+        };
     }
 
     fn visit_expr(&mut self, e: &Expr) {
diff --git a/crates/swc_ecma_preset_env/src/lib.rs b/crates/swc_ecma_preset_env/src/lib.rs
index 8c99808c9e..647111246f 100644
--- a/crates/swc_ecma_preset_env/src/lib.rs
+++ b/crates/swc_ecma_preset_env/src/lib.rs
@@ -11,7 +11,10 @@ use swc_atoms::{js_word, JsWord};
 use swc_common::{chain, collections::AHashSet, comments::Comments, FromVariant, Mark, DUMMY_SP};
 use swc_ecma_ast::*;
 use swc_ecma_transforms::{
-    compat::{bugfixes, es2015, es2016, es2017, es2018, es2019, es2020, es2021, es2022, es3},
+    compat::{
+        bugfixes, es2015, es2016, es2017, es2018, es2019, es2020, es2021, es2022, es3,
+        regexp::{self, regexp},
+    },
     pass::{noop, Optional},
     Assumptions,
 };
@@ -89,6 +92,37 @@ where
         bugfixes::safari_id_destructuring_collision_in_function_expression()
     );
 
+    let pass = {
+        let enable_dot_all_regex = should_enable!(DotAllRegex, false);
+        let enable_named_capturing_groups_regex = should_enable!(NamedCapturingGroupsRegex, false);
+        let enable_sticky_regex = should_enable!(StickyRegex, false);
+        let enable_unicode_property_regex = should_enable!(UnicodePropertyRegex, false);
+        let enable_unicode_regex = should_enable!(UnicodeRegex, false);
+
+        let enable = enable_dot_all_regex
+            || enable_named_capturing_groups_regex
+            || enable_sticky_regex
+            || enable_unicode_property_regex
+            || enable_unicode_regex;
+
+        chain!(
+            pass,
+            Optional::new(
+                regexp(regexp::Config {
+                    dot_all_regex: enable_dot_all_regex,
+
+                    // TODO: add Feature::LookbehindAssertion
+                    lookbehind_assertion: false,
+                    named_capturing_groups_regex: enable_named_capturing_groups_regex,
+                    sticky_regex: enable_sticky_regex,
+                    unicode_property_regex: enable_unicode_property_regex,
+                    unicode_regex: enable_unicode_regex,
+                }),
+                enable
+            )
+        )
+    };
+
     // Proposals
 
     // ES2022
diff --git a/crates/swc_ecma_transforms_compat/src/es2015/mod.rs b/crates/swc_ecma_transforms_compat/src/es2015/mod.rs
index 5f0f4a0df6..64a0ca4039 100644
--- a/crates/swc_ecma_transforms_compat/src/es2015/mod.rs
+++ b/crates/swc_ecma_transforms_compat/src/es2015/mod.rs
@@ -11,6 +11,7 @@ pub use self::{
     spread::spread, sticky_regex::sticky_regex, template_literal::template_literal,
     typeof_symbol::typeof_symbol,
 };
+use crate::regexp::{self, regexp};
 
 mod arrow;
 mod block_scoped_fn;
@@ -50,6 +51,14 @@ where
     C: Comments,
 {
     chain!(
+        regexp(regexp::Config {
+            dot_all_regex: false,
+            lookbehind_assertion: false,
+            named_capturing_groups_regex: false,
+            sticky_regex: true,
+            unicode_property_regex: false,
+            unicode_regex: true,
+        }),
         block_scoped_functions(),
         template_literal(c.template_literal),
         classes(comments, c.classes),
diff --git a/crates/swc_ecma_transforms_compat/src/es2018/mod.rs b/crates/swc_ecma_transforms_compat/src/es2018/mod.rs
index dfe47ccfeb..421a32bf98 100644
--- a/crates/swc_ecma_transforms_compat/src/es2018/mod.rs
+++ b/crates/swc_ecma_transforms_compat/src/es2018/mod.rs
@@ -1,13 +1,25 @@
 use serde::Deserialize;
+use swc_common::chain;
 use swc_ecma_visit::Fold;
 
 pub use self::object_rest_spread::object_rest_spread;
+use crate::regexp::{self, regexp};
 
 pub mod object_rest_spread;
 
 #[tracing::instrument(level = "info", skip_all)]
 pub fn es2018(c: Config) -> impl Fold {
-    object_rest_spread(c.object_rest_spread)
+    chain!(
+        regexp(regexp::Config {
+            dot_all_regex: true,
+            lookbehind_assertion: true,
+            named_capturing_groups_regex: true,
+            sticky_regex: false,
+            unicode_property_regex: true,
+            unicode_regex: false,
+        }),
+        object_rest_spread(c.object_rest_spread)
+    )
 }
 
 #[derive(Debug, Clone, Copy, Default, Deserialize)]
diff --git a/crates/swc_ecma_transforms_compat/src/lib.rs b/crates/swc_ecma_transforms_compat/src/lib.rs
index 8df1c03458..431cb6a3ff 100644
--- a/crates/swc_ecma_transforms_compat/src/lib.rs
+++ b/crates/swc_ecma_transforms_compat/src/lib.rs
@@ -19,4 +19,5 @@ pub mod es2020;
 pub mod es2021;
 pub mod es2022;
 pub mod es3;
+pub mod regexp;
 pub mod reserved_words;
diff --git a/crates/swc_ecma_transforms_compat/src/regexp.rs b/crates/swc_ecma_transforms_compat/src/regexp.rs
new file mode 100644
index 0000000000..922ea1c096
--- /dev/null
+++ b/crates/swc_ecma_transforms_compat/src/regexp.rs
@@ -0,0 +1,66 @@
+use swc_common::util::take::Take;
+use swc_ecma_ast::{CallExpr, Expr, Lit, Regex};
+use swc_ecma_utils::{quote_ident, ExprFactory};
+use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};
+
+pub fn regexp(config: Config) -> impl Fold + VisitMut {
+    as_folder(RegExp { config })
+}
+
+#[derive(Default, Clone, Copy)]
+pub struct Config {
+    /// [s/dotAll flag for regular expressions](https://tc39.github.io/proposal-regexp-dotall-flag/)
+    pub dot_all_regex: bool,
+    /// [RegExp Lookbehind Assertions](https://tc39.es/proposal-regexp-lookbehind/)
+    pub lookbehind_assertion: bool,
+    /// [Named capture groups in regular expressions](https://tc39.es/proposal-regexp-named-groups/)
+    pub named_capturing_groups_regex: bool,
+    /// [RegExp.prototype.sticky](https://tc39.es/ecma262/multipage/text-processing.html#sec-get-regexp.prototype.sticky)
+    pub sticky_regex: bool,
+    /// [Unicode property escapes in regular expressions](https://tc39.es/proposal-regexp-unicode-property-escapes/)
+    pub unicode_property_regex: bool,
+    /// [RegExp.prototype.unicode](https://tc39.es/ecma262/multipage/text-processing.html#sec-get-regexp.prototype.unicode)
+    pub unicode_regex: bool,
+}
+
+struct RegExp {
+    config: Config,
+}
+
+impl VisitMut for RegExp {
+    noop_visit_mut_type!();
+
+    fn visit_mut_expr(&mut self, expr: &mut Expr) {
+        expr.visit_mut_children_with(self);
+
+        if let Expr::Lit(Lit::Regex(regex)) = expr {
+            if (self.config.dot_all_regex && regex.flags.contains('s'))
+                || (self.config.sticky_regex && regex.flags.contains('y'))
+                || (self.config.unicode_regex && regex.flags.contains('u'))
+                || (self.config.named_capturing_groups_regex && regex.exp.contains("(?<"))
+                || (self.config.lookbehind_assertion && regex.exp.contains("(?<=")
+                    || regex.exp.contains("(?<!"))
+                || (self.config.unicode_property_regex
+                    && (regex.exp.contains("\\p{") || regex.exp.contains("\\P{")))
+            {
+                let Regex { exp, flags, span } = regex.take();
+
+                let exp: Expr = exp.into();
+                let mut args = vec![exp.into()];
+
+                if !flags.is_empty() {
+                    let flags: Expr = flags.into();
+                    args.push(flags.into());
+                }
+
+                *expr = CallExpr {
+                    span,
+                    callee: quote_ident!("RegExp").as_callee(),
+                    args,
+                    type_args: None,
+                }
+                .into()
+            }
+        }
+    }
+}


Test patch:
diff --git a/crates/swc/tests/fixture/regexp/dotall/1/input/.swcrc b/crates/swc/tests/fixture/regexp/dotall/1/input/.swcrc
new file mode 100644
index 0000000000..c0e4178ffd
--- /dev/null
+++ b/crates/swc/tests/fixture/regexp/dotall/1/input/.swcrc
@@ -0,0 +1,5 @@
+{
+    "jsc": {
+        "target": "es2017"
+    }
+}
diff --git a/crates/swc/tests/fixture/regexp/dotall/1/input/index.js b/crates/swc/tests/fixture/regexp/dotall/1/input/index.js
new file mode 100644
index 0000000000..f42f89361e
--- /dev/null
+++ b/crates/swc/tests/fixture/regexp/dotall/1/input/index.js
@@ -0,0 +1,3 @@
+const v0 = "Hello\nWorld";
+/Hello.World/.test(v0);
+/Hello.World/s.test(v0);
diff --git a/crates/swc/tests/fixture/regexp/dotall/1/output/index.js b/crates/swc/tests/fixture/regexp/dotall/1/output/index.js
new file mode 100644
index 0000000000..606e579476
--- /dev/null
+++ b/crates/swc/tests/fixture/regexp/dotall/1/output/index.js
@@ -0,0 +1,3 @@
+const v0 = "Hello\nWorld";
+/Hello.World/.test(v0);
+RegExp("Hello.World", "s").test(v0);
diff --git a/crates/swc/tests/fixture/regexp/dotall/simple/input/.swcrc b/crates/swc/tests/fixture/regexp/dotall/simple/input/.swcrc
new file mode 100644
index 0000000000..c0e4178ffd
--- /dev/null
+++ b/crates/swc/tests/fixture/regexp/dotall/simple/input/.swcrc
@@ -0,0 +1,5 @@
+{
+    "jsc": {
+        "target": "es2017"
+    }
+}
diff --git a/crates/swc/tests/fixture/regexp/dotall/simple/input/index.js b/crates/swc/tests/fixture/regexp/dotall/simple/input/index.js
new file mode 100644
index 0000000000..bf1243961a
--- /dev/null
+++ b/crates/swc/tests/fixture/regexp/dotall/simple/input/index.js
@@ -0,0 +1,2 @@
+var a = /./;
+var b = /./s;
diff --git a/crates/swc/tests/fixture/regexp/dotall/simple/output/index.js b/crates/swc/tests/fixture/regexp/dotall/simple/output/index.js
new file mode 100644
index 0000000000..0ac9c86151
--- /dev/null
+++ b/crates/swc/tests/fixture/regexp/dotall/simple/output/index.js
@@ -0,0 +1,2 @@
+var a = /./;
+var b = RegExp(".", "s");
diff --git a/crates/swc/tests/fixture/regexp/dotall/with-unicode-flag/input/.swcrc b/crates/swc/tests/fixture/regexp/dotall/with-unicode-flag/input/.swcrc
new file mode 100644
index 0000000000..c0e4178ffd
--- /dev/null
+++ b/crates/swc/tests/fixture/regexp/dotall/with-unicode-flag/input/.swcrc
@@ -0,0 +1,5 @@
+{
+    "jsc": {
+        "target": "es2017"
+    }
+}
diff --git a/crates/swc/tests/fixture/regexp/dotall/with-unicode-flag/input/index.js b/crates/swc/tests/fixture/regexp/dotall/with-unicode-flag/input/index.js
new file mode 100644
index 0000000000..9ca7da3e38
--- /dev/null
+++ b/crates/swc/tests/fixture/regexp/dotall/with-unicode-flag/input/index.js
@@ -0,0 +1,2 @@
+var a = /./u;
+var b = /./su;
diff --git a/crates/swc/tests/fixture/regexp/dotall/with-unicode-flag/output/index.js b/crates/swc/tests/fixture/regexp/dotall/with-unicode-flag/output/index.js
new file mode 100644
index 0000000000..6371afe4ed
--- /dev/null
+++ b/crates/swc/tests/fixture/regexp/dotall/with-unicode-flag/output/index.js
@@ -0,0 +1,2 @@
+var a = /./u;
+var b = RegExp(".", "su");
diff --git a/crates/swc/tests/fixture/regexp/dotall/with-unicode-property-escape/input/.swcrc b/crates/swc/tests/fixture/regexp/dotall/with-unicode-property-escape/input/.swcrc
new file mode 100644
index 0000000000..c0e4178ffd
--- /dev/null
+++ b/crates/swc/tests/fixture/regexp/dotall/with-unicode-property-escape/input/.swcrc
@@ -0,0 +1,5 @@
+{
+    "jsc": {
+        "target": "es2017"
+    }
+}
diff --git a/crates/swc/tests/fixture/regexp/dotall/with-unicode-property-escape/input/index.js b/crates/swc/tests/fixture/regexp/dotall/with-unicode-property-escape/input/index.js
new file mode 100644
index 0000000000..dc74dbc0d6
--- /dev/null
+++ b/crates/swc/tests/fixture/regexp/dotall/with-unicode-property-escape/input/index.js
@@ -0,0 +1,2 @@
+var a = /\p{Unified_Ideograph}./u;
+var b = /\p{Unified_Ideograph}./su;
diff --git a/crates/swc/tests/fixture/regexp/dotall/with-unicode-property-escape/output/index.js b/crates/swc/tests/fixture/regexp/dotall/with-unicode-property-escape/output/index.js
new file mode 100644
index 0000000000..6d92aedfdc
--- /dev/null
+++ b/crates/swc/tests/fixture/regexp/dotall/with-unicode-property-escape/output/index.js
@@ -0,0 +1,2 @@
+var a = RegExp("\\p{Unified_Ideograph}.", "u");
+var b = RegExp("\\p{Unified_Ideograph}.", "su");
diff --git a/crates/swc/tests/tsc-references/useRegexpGroups_es2015.1.normal.js b/crates/swc/tests/tsc-references/useRegexpGroups_es2015.1.normal.js
index 20b797611e..edbce3f01a 100644
--- a/crates/swc/tests/tsc-references/useRegexpGroups_es2015.1.normal.js
+++ b/crates/swc/tests/tsc-references/useRegexpGroups_es2015.1.normal.js
@@ -1,6 +1,6 @@
 // @target: es5
 // @lib: es6,es2018
-let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
+let re = RegExp("(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})", "u");
 let result = re.exec("2015-01-02");
 let date = result[0];
 let year1 = result.groups.year;
@@ -9,4 +9,4 @@ let month1 = result.groups.month;
 let month2 = result[2];
 let day1 = result.groups.day;
 let day2 = result[3];
-let foo = "foo".match(/(?<bar>foo)/).groups.foo;
+let foo = "foo".match(RegExp("(?<bar>foo)")).groups.foo;
diff --git a/crates/swc/tests/tsc-references/useRegexpGroups_es2015.2.minified.js b/crates/swc/tests/tsc-references/useRegexpGroups_es2015.2.minified.js
index a6a3925d56..b44fa49ca2 100644
--- a/crates/swc/tests/tsc-references/useRegexpGroups_es2015.2.minified.js
+++ b/crates/swc/tests/tsc-references/useRegexpGroups_es2015.2.minified.js
@@ -1,2 +1,2 @@
-let result = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u.exec("2015-01-02");
-result[0], result.groups.year, result[1], result.groups.month, result[2], result.groups.day, result[3], "foo".match(/(?<bar>foo)/).groups.foo;
+let result = RegExp("(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})", "u").exec("2015-01-02");
+result[0], result.groups.year, result[1], result.groups.month, result[2], result.groups.day, result[3], "foo".match(RegExp("(?<bar>foo)")).groups.foo;
diff --git a/crates/swc/tests/tsc-references/useRegexpGroups_es5.1.normal.js b/crates/swc/tests/tsc-references/useRegexpGroups_es5.1.normal.js
index c012610f56..a4c3e6b777 100644
--- a/crates/swc/tests/tsc-references/useRegexpGroups_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/useRegexpGroups_es5.1.normal.js
@@ -1,6 +1,6 @@
 // @target: es5
 // @lib: es6,es2018
-var re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
+var re = RegExp("(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})", "u");
 var result = re.exec("2015-01-02");
 var date = result[0];
 var year1 = result.groups.year;
@@ -9,4 +9,4 @@ var month1 = result.groups.month;
 var month2 = result[2];
 var day1 = result.groups.day;
 var day2 = result[3];
-var foo = "foo".match(/(?<bar>foo)/).groups.foo;
+var foo = "foo".match(RegExp("(?<bar>foo)")).groups.foo;
diff --git a/crates/swc/tests/tsc-references/useRegexpGroups_es5.2.minified.js b/crates/swc/tests/tsc-references/useRegexpGroups_es5.2.minified.js
index e3582843e8..0eba333f1d 100644
--- a/crates/swc/tests/tsc-references/useRegexpGroups_es5.2.minified.js
+++ b/crates/swc/tests/tsc-references/useRegexpGroups_es5.2.minified.js
@@ -1,2 +1,2 @@
-var result = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u.exec("2015-01-02");
-result[0], result.groups.year, result[1], result.groups.month, result[2], result.groups.day, result[3], "foo".match(/(?<bar>foo)/).groups.foo;
+var result = RegExp("(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})", "u").exec("2015-01-02");
+result[0], result.groups.year, result[1], result.groups.month, result[2], result.groups.day, result[3], "foo".match(RegExp("(?<bar>foo)")).groups.foo;
diff --git a/crates/swc_ecma_preset_env/tests/fixtures/corejs3/usage-regexp/input.mjs b/crates/swc_ecma_preset_env/tests/fixtures/corejs3/usage-regexp/input.mjs
new file mode 100644
index 0000000000..bb558f8ec5
--- /dev/null
+++ b/crates/swc_ecma_preset_env/tests/fixtures/corejs3/usage-regexp/input.mjs
@@ -0,0 +1,8 @@
+const a = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
+const b = /./s;
+const c = /./imsuy;
+
+console.log(a.unicode);
+console.log(b.dotAll);
+console.log(c.sticky);
+console.log(c.ignoreCase);
diff --git a/crates/swc_ecma_preset_env/tests/fixtures/corejs3/usage-regexp/options.json b/crates/swc_ecma_preset_env/tests/fixtures/corejs3/usage-regexp/options.json
new file mode 100644
index 0000000000..0975cd86bf
--- /dev/null
+++ b/crates/swc_ecma_preset_env/tests/fixtures/corejs3/usage-regexp/options.json
@@ -0,0 +1,15 @@
+{
+  "presets": [
+    [
+      "../../../../lib",
+      {
+        "targets": {
+          "browsers": ["ie > 10"]
+        },
+        "useBuiltIns": "usage",
+        "corejs": 3,
+        "modules": false
+      }
+    ]
+  ]
+}
diff --git a/crates/swc_ecma_preset_env/tests/fixtures/corejs3/usage-regexp/output.mjs b/crates/swc_ecma_preset_env/tests/fixtures/corejs3/usage-regexp/output.mjs
new file mode 100644
index 0000000000..38d5c81ae8
--- /dev/null
+++ b/crates/swc_ecma_preset_env/tests/fixtures/corejs3/usage-regexp/output.mjs
@@ -0,0 +1,12 @@
+import "core-js/modules/es.regexp.constructor.js";
+import "core-js/modules/es.regexp.dot-all.js";
+import "core-js/modules/es.regexp.exec.js";
+import "core-js/modules/es.regexp.sticky.js";
+import "core-js/modules/es.regexp.to-string.js";
+var a = RegExp("(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})", "u");
+var b = RegExp(".", "s");
+var c = RegExp(".", "imsuy");
+console.log(a.unicode);
+console.log(b.dotAll);
+console.log(c.sticky);
+console.log(c.ignoreCase);