Repository: eslint-community/eslint-plugin-es-x
Pull request #76 (2023-05-21 00:06:21)

Add `no-regexp-v-flag` rule


Patch:
diff --git a/docs/rules/index.md b/docs/rules/index.md
index f951309..67df829 100644
--- a/docs/rules/index.md
+++ b/docs/rules/index.md
@@ -11,6 +11,7 @@ There is a config that enables the rules in this category: `plugin:es-x/no-new-i
 | Rule ID | Description |    |
 |:--------|:------------|:--:|
 | [es-x/no-atomics-waitasync](./no-atomics-waitasync.md) | disallow the `Atomics.waitAsync` method. |  |
+| [es-x/no-regexp-v-flag](./no-regexp-v-flag.md) | disallow RegExp `v` flag. |  |
 | [es-x/no-string-prototype-iswellformed-towellformed](./no-string-prototype-iswellformed-towellformed.md) | disallow the `String.prototype.{isWellFormed,toWellFormed}` methods. |  |
 
 ## ES2023
diff --git a/docs/rules/no-regexp-v-flag.md b/docs/rules/no-regexp-v-flag.md
new file mode 100644
index 0000000..57e8049
--- /dev/null
+++ b/docs/rules/no-regexp-v-flag.md
@@ -0,0 +1,31 @@
+---
+title: "es-x/no-regexp-v-flag"
+description: "disallow RegExp `v` flag"
+---
+
+# es-x/no-regexp-v-flag
+> disallow RegExp `v` flag
+
+- ❗ <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
+- ✅ The following configurations enable this rule: `plugin:es-x/no-new-in-esnext`
+
+This rule reports ES2024 [RegExp `v` flag](https://github.com/tc39/proposal-regexp-v-flag) as errors.
+
+## 💡 Examples
+
+⛔ Examples of **incorrect** code for this rule:
+
+<eslint-playground type="bad">
+
+```js
+/*eslint es-x/no-regexp-v-flag: error */
+const r1 = /^\p{RGI_Emoji}$/v
+const r2 = /[\p{Decimal_Number}--[0-9]]/v
+```
+
+</eslint-playground>
+
+## 📚 References
+
+- [Rule source](https://github.com/eslint-community/eslint-plugin-es-x/blob/master/lib/rules/no-regexp-v-flag.js)
+- [Test source](https://github.com/eslint-community/eslint-plugin-es-x/blob/master/tests/lib/rules/no-regexp-v-flag.js)
diff --git a/lib/configs/no-new-in-esnext.js b/lib/configs/no-new-in-esnext.js
index c7c1b33..4e16ea2 100644
--- a/lib/configs/no-new-in-esnext.js
+++ b/lib/configs/no-new-in-esnext.js
@@ -8,6 +8,7 @@ module.exports = {
     plugins: ["es-x"],
     rules: {
         "es-x/no-atomics-waitasync": "error",
+        "es-x/no-regexp-v-flag": "error",
         "es-x/no-string-prototype-iswellformed-towellformed": "error",
     },
 }
diff --git a/lib/index.js b/lib/index.js
index 2dbb478..fff10d3 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -233,6 +233,7 @@ module.exports = {
         "no-regexp-unicode-property-escapes-2021": require("./rules/no-regexp-unicode-property-escapes-2021"),
         "no-regexp-unicode-property-escapes-2022": require("./rules/no-regexp-unicode-property-escapes-2022"),
         "no-regexp-unicode-property-escapes-2023": require("./rules/no-regexp-unicode-property-escapes-2023"),
+        "no-regexp-v-flag": require("./rules/no-regexp-v-flag"),
         "no-regexp-y-flag": require("./rules/no-regexp-y-flag"),
         "no-rest-parameters": require("./rules/no-rest-parameters"),
         "no-rest-spread-properties": require("./rules/no-rest-spread-properties"),
diff --git a/lib/rules/no-regexp-v-flag.js b/lib/rules/no-regexp-v-flag.js
new file mode 100644
index 0000000..85a03e9
--- /dev/null
+++ b/lib/rules/no-regexp-v-flag.js
@@ -0,0 +1,39 @@
+"use strict"
+
+const { getRegExpCalls } = require("../utils")
+
+module.exports = {
+    meta: {
+        docs: {
+            description: "disallow RegExp `v` flag.",
+            category: "ES2024",
+            recommended: false,
+            url: "http://eslint-community.github.io/eslint-plugin-es-x/rules/no-regexp-v-flag.html",
+        },
+        fixable: null,
+        messages: {
+            forbidden: "ES2024 RegExp 'v' flag is forbidden.",
+        },
+        schema: [],
+        type: "problem",
+    },
+    create(context) {
+        return {
+            "Literal[regex]"(node) {
+                if (node.regex.flags.includes("v")) {
+                    context.report({ node, messageId: "forbidden" })
+                }
+            },
+
+            "Program:exit"() {
+                const scope = context.getScope()
+
+                for (const { node, flags } of getRegExpCalls(scope)) {
+                    if (flags && flags.includes("v")) {
+                        context.report({ node, messageId: "forbidden" })
+                    }
+                }
+            },
+        }
+    },
+}
diff --git a/package.json b/package.json
index f3529ed..53b0880 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,7 @@
   },
   "dependencies": {
     "@eslint-community/eslint-utils": "^4.1.2",
-    "@eslint-community/regexpp": "^4.5.0"
+    "@eslint-community/regexpp": "^4.6.0"
   },
   "devDependencies": {
     "@typescript-eslint/parser": "^5.14.0",
diff --git a/scripts/new-rule.js b/scripts/new-rule.js
index 856063e..a8cbf95 100644
--- a/scripts/new-rule.js
+++ b/scripts/new-rule.js
@@ -7,8 +7,15 @@
 const cp = require("child_process")
 const fs = require("fs")
 const path = require("path")
+const { categories } = require("./rules")
 const logger = console
 
+const maxESVersion = Math.max(
+    ...Object.keys(categories).map((esVersion) =>
+        /^ES\d+$/u.test(esVersion) ? Number(esVersion.slice(2)) : 0,
+    ),
+)
+
 // main
 ;((ruleId) => {
     if (ruleId == null) {
@@ -33,8 +40,8 @@ const logger = console
 module.exports = {
     meta: {
         docs: {
-            description: "",
-            category: "",
+            description: "disallow ....",
+            category: "ES${maxESVersion}",
             recommended: false,
             url: "",
         },
@@ -58,7 +65,7 @@ module.exports = {
 const RuleTester = require("../../tester")
 const rule = require("../../../lib/rules/${ruleId}.js")
 
-if (!RuleTester.isSupported(2022)) {
+if (!RuleTester.isSupported(${maxESVersion})) {
     //eslint-disable-next-line no-console
     console.log("Skip the tests of ${ruleId}.")
     return


Test patch:
diff --git a/tests/lib/rules/no-regexp-v-flag.js b/tests/lib/rules/no-regexp-v-flag.js
new file mode 100644
index 0000000..7c1884f
--- /dev/null
+++ b/tests/lib/rules/no-regexp-v-flag.js
@@ -0,0 +1,43 @@
+"use strict"
+
+const RuleTester = require("../../tester")
+const rule = require("../../../lib/rules/no-regexp-v-flag.js")
+
+if (!RuleTester.isSupported(2024)) {
+    //eslint-disable-next-line no-console
+    console.log("Skip the tests of no-regexp-v-flag.")
+    return
+}
+
+new RuleTester().run("no-regexp-v-flag", rule, {
+    valid: [
+        "/foo/gimsu",
+        "a\n/b/y",
+        "new RegExp('foo')",
+        "new RegExp('foo', 'gimsu')",
+        "new RegExp('foo', flags)",
+        "const flags = 'gimsu'; new RegExp('foo', flags)",
+    ],
+    invalid: [
+        {
+            code: "/foo/v",
+            errors: ["ES2024 RegExp 'v' flag is forbidden."],
+        },
+        {
+            code: "/foo/gimsyv",
+            errors: ["ES2024 RegExp 'v' flag is forbidden."],
+        },
+        {
+            code: "new RegExp('foo', 'v')",
+            errors: ["ES2024 RegExp 'v' flag is forbidden."],
+        },
+        {
+            code: "new RegExp('foo', 'gimsyv')",
+            errors: ["ES2024 RegExp 'v' flag is forbidden."],
+        },
+        {
+            code: "const pattern = 'foo', flags = 'gimsyv', regex = new RegExp(pattern, flags)",
+            errors: ["ES2024 RegExp 'v' flag is forbidden."],
+        },
+    ],
+})
diff --git a/tests/tester.js b/tests/tester.js
index 196c5cc..a1c2c2c 100644
--- a/tests/tester.js
+++ b/tests/tester.js
@@ -11,6 +11,7 @@ const semver = require("semver")
 const eslintVersion = new Linter().version
 const ecmaVersion =
     /*eslint-disable prettier/prettier */
+    semver.gte(eslintVersion, "8.44.0") ? 2024 :
     semver.gte(eslintVersion, "8.23.0") ? 2023 :
     semver.gte(eslintVersion, "8.0.0") ? 2022 :
     semver.gte(eslintVersion, "7.8.0") ? 2021 :
@@ -37,5 +38,6 @@ RuleTester.setDefaultConfig({
 })
 RuleTester.isSupported = (targetEcmaVersion) => targetEcmaVersion <= ecmaVersion
 RuleTester.eslintVersion = eslintVersion
+RuleTester.supportedEcmaVersion = ecmaVersion
 
 module.exports = RuleTester