From 8a9343871f7dade19d910ca8e2a4177bfca28b64 Mon Sep 17 00:00:00 2001 From: SUZUKI Sosuke Date: Sat, 29 Jul 2023 00:38:24 +0900 Subject: [PATCH] feat: `require-unicode-regexp` support `v` flag (#17402) * feat: implement RegExp literal * test: add tests for RegExp literal * feat: implement for RegExp constructor * test: add tests for RegExp constructor * test: add invalid tests * feat: implement replacement for u and v * test: add tests * docs: update docs for `v` flag * fix: address review * Update lib/rules/require-unicode-regexp.js Co-authored-by: Milos Djermanovic * Update docs/src/rules/require-unicode-regexp.md Co-authored-by: Milos Djermanovic * Update tests/lib/rules/require-unicode-regexp.js Co-authored-by: Milos Djermanovic * Update tests/lib/rules/require-unicode-regexp.js Co-authored-by: Milos Djermanovic --------- Co-authored-by: Milos Djermanovic --- docs/src/rules/require-unicode-regexp.md | 37 ++++++++++++++++++++++- lib/rules/require-unicode-regexp.js | 6 ++-- tests/lib/rules/require-unicode-regexp.js | 15 ++++++++- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/docs/src/rules/require-unicode-regexp.md b/docs/src/rules/require-unicode-regexp.md index f3277066a5b..969818e2c0d 100644 --- a/docs/src/rules/require-unicode-regexp.md +++ b/docs/src/rules/require-unicode-regexp.md @@ -21,7 +21,37 @@ RegExp `u` flag has two effects: The `u` flag disables the recovering logic Annex B defined. As a result, you can find errors early. This is similar to [the strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode). -Therefore, the `u` flag lets us work better with regular expressions. +The RegExp `v` flag, introduced in ECMAScript 2024, is a superset of the `u` flag, and offers two more features: + +1. **Unicode properties of strings** + + With the Unicode property escape, you can use properties of strings. + + ```js + const re = /^\p{RGI_Emoji}$/v; + + // Match an emoji that consists of just 1 code point: + re.test('⚽'); // '\u26BD' + // → true ✅ + + // Match an emoji that consists of multiple code points: + re.test('👨🏾‍⚕️'); // '\u{1F468}\u{1F3FE}\u200D\u2695\uFE0F' + // → true ✅ + ``` + +2. **Set notation** + + It allows for set operations between character classes. + + ```js + const re = /[\p{White_Space}&&\p{ASCII}]/v; + re.test('\n'); // → true + re.test('\u2028'); // → false + ``` + +Please see and for more details. + +Therefore, the `u` and `v` flags let us work better with regular expressions. ## Rule Details @@ -54,6 +84,11 @@ const b = /bbb/giu const c = new RegExp("ccc", "u") const d = new RegExp("ddd", "giu") +const e = /aaa/v +const f = /bbb/giv +const g = new RegExp("ccc", "v") +const h = new RegExp("ddd", "giv") + // This rule ignores RegExp calls if the flags could not be evaluated to a static value. function f(flags) { return new RegExp("eee", flags) diff --git a/lib/rules/require-unicode-regexp.js b/lib/rules/require-unicode-regexp.js index f748753a2d1..dd687f8d796 100644 --- a/lib/rules/require-unicode-regexp.js +++ b/lib/rules/require-unicode-regexp.js @@ -28,7 +28,7 @@ module.exports = { type: "suggestion", docs: { - description: "Enforce the use of `u` flag on RegExp", + description: "Enforce the use of `u` or `v` flag on RegExp", recommended: false, url: "https://eslint.org/docs/latest/rules/require-unicode-regexp" }, @@ -51,7 +51,7 @@ module.exports = { "Literal[regex]"(node) { const flags = node.regex.flags || ""; - if (!flags.includes("u")) { + if (!flags.includes("u") && !flags.includes("v")) { context.report({ messageId: "requireUFlag", node, @@ -85,7 +85,7 @@ module.exports = { const pattern = getStringIfConstant(patternNode, scope); const flags = getStringIfConstant(flagsNode, scope); - if (!flagsNode || (typeof flags === "string" && !flags.includes("u"))) { + if (!flagsNode || (typeof flags === "string" && !flags.includes("u") && !flags.includes("v"))) { context.report({ messageId: "requireUFlag", node: refNode, diff --git a/tests/lib/rules/require-unicode-regexp.js b/tests/lib/rules/require-unicode-regexp.js index a75f6863168..eab11b70fed 100644 --- a/tests/lib/rules/require-unicode-regexp.js +++ b/tests/lib/rules/require-unicode-regexp.js @@ -45,7 +45,20 @@ ruleTester.run("require-unicode-regexp", rule, { { code: "const flags = 'u'; new globalThis.RegExp('', flags)", env: { es2020: true } }, { code: "const flags = 'g'; new globalThis.RegExp('', flags + 'u')", env: { es2020: true } }, { code: "const flags = 'gimu'; new globalThis.RegExp('foo', flags[3])", env: { es2020: true } }, - { code: "class C { #RegExp; foo() { new globalThis.#RegExp('foo') } }", parserOptions: { ecmaVersion: 2022 }, env: { es2020: true } } + { code: "class C { #RegExp; foo() { new globalThis.#RegExp('foo') } }", parserOptions: { ecmaVersion: 2022 }, env: { es2020: true } }, + + // for v flag + { code: "/foo/v", parserOptions: { ecmaVersion: 2024 } }, + { code: "/foo/gimvy", parserOptions: { ecmaVersion: 2024 } }, + { code: "RegExp('', 'v')", parserOptions: { ecmaVersion: 2024 } }, + { code: "RegExp('', `v`)", parserOptions: { ecmaVersion: 2024 } }, + { code: "new RegExp('', 'v')", parserOptions: { ecmaVersion: 2024 } }, + { code: "RegExp('', 'gimvy')", parserOptions: { ecmaVersion: 2024 } }, + { code: "RegExp('', `gimvy`)", parserOptions: { ecmaVersion: 2024 } }, + { code: "new RegExp('', 'gimvy')", parserOptions: { ecmaVersion: 2024 } }, + { code: "const flags = 'v'; new RegExp('', flags)", parserOptions: { ecmaVersion: 2024 } }, + { code: "const flags = 'g'; new RegExp('', flags + 'v')", parserOptions: { ecmaVersion: 2024 } }, + { code: "const flags = 'gimv'; new RegExp('foo', flags[3])", parserOptions: { ecmaVersion: 2024 } } ], invalid: [ {