Skip to content

Commit 8a93438

Browse files
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 <[email protected]> * Update docs/src/rules/require-unicode-regexp.md Co-authored-by: Milos Djermanovic <[email protected]> * Update tests/lib/rules/require-unicode-regexp.js Co-authored-by: Milos Djermanovic <[email protected]> * Update tests/lib/rules/require-unicode-regexp.js Co-authored-by: Milos Djermanovic <[email protected]> --------- Co-authored-by: Milos Djermanovic <[email protected]>
1 parent 4d474e3 commit 8a93438

3 files changed

Lines changed: 53 additions & 5 deletions

File tree

docs/src/rules/require-unicode-regexp.md

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,37 @@ RegExp `u` flag has two effects:
2121
2222
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).
2323
24-
Therefore, the `u` flag lets us work better with regular expressions.
24+
The RegExp `v` flag, introduced in ECMAScript 2024, is a superset of the `u` flag, and offers two more features:
25+
26+
1. **Unicode properties of strings**
27+
28+
With the Unicode property escape, you can use properties of strings.
29+
30+
```js
31+
const re = /^\p{RGI_Emoji}$/v;
32+
33+
// Match an emoji that consists of just 1 code point:
34+
re.test(''); // '\u26BD'
35+
// → true ✅
36+
37+
// Match an emoji that consists of multiple code points:
38+
re.test('👨🏾‍⚕️'); // '\u{1F468}\u{1F3FE}\u200D\u2695\uFE0F'
39+
// → true ✅
40+
```
41+
42+
2. **Set notation**
43+
44+
It allows for set operations between character classes.
45+
46+
```js
47+
const re = /[\p{White_Space}&&\p{ASCII}]/v;
48+
re.test('\n'); // → true
49+
re.test('\u2028'); // → false
50+
```
51+
52+
Please see <https://github.com/tc39/proposal-regexp-v-flag> and <https://v8.dev/features/regexp-v-flag> for more details.
53+
54+
Therefore, the `u` and `v` flags let us work better with regular expressions.
2555
2656
## Rule Details
2757
@@ -54,6 +84,11 @@ const b = /bbb/giu
5484
const c = new RegExp("ccc", "u")
5585
const d = new RegExp("ddd", "giu")
5686
87+
const e = /aaa/v
88+
const f = /bbb/giv
89+
const g = new RegExp("ccc", "v")
90+
const h = new RegExp("ddd", "giv")
91+
5792
// This rule ignores RegExp calls if the flags could not be evaluated to a static value.
5893
function f(flags) {
5994
return new RegExp("eee", flags)

lib/rules/require-unicode-regexp.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ module.exports = {
2828
type: "suggestion",
2929

3030
docs: {
31-
description: "Enforce the use of `u` flag on RegExp",
31+
description: "Enforce the use of `u` or `v` flag on RegExp",
3232
recommended: false,
3333
url: "https://eslint.org/docs/latest/rules/require-unicode-regexp"
3434
},
@@ -51,7 +51,7 @@ module.exports = {
5151
"Literal[regex]"(node) {
5252
const flags = node.regex.flags || "";
5353

54-
if (!flags.includes("u")) {
54+
if (!flags.includes("u") && !flags.includes("v")) {
5555
context.report({
5656
messageId: "requireUFlag",
5757
node,
@@ -85,7 +85,7 @@ module.exports = {
8585
const pattern = getStringIfConstant(patternNode, scope);
8686
const flags = getStringIfConstant(flagsNode, scope);
8787

88-
if (!flagsNode || (typeof flags === "string" && !flags.includes("u"))) {
88+
if (!flagsNode || (typeof flags === "string" && !flags.includes("u") && !flags.includes("v"))) {
8989
context.report({
9090
messageId: "requireUFlag",
9191
node: refNode,

tests/lib/rules/require-unicode-regexp.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,20 @@ ruleTester.run("require-unicode-regexp", rule, {
4545
{ code: "const flags = 'u'; new globalThis.RegExp('', flags)", env: { es2020: true } },
4646
{ code: "const flags = 'g'; new globalThis.RegExp('', flags + 'u')", env: { es2020: true } },
4747
{ code: "const flags = 'gimu'; new globalThis.RegExp('foo', flags[3])", env: { es2020: true } },
48-
{ code: "class C { #RegExp; foo() { new globalThis.#RegExp('foo') } }", parserOptions: { ecmaVersion: 2022 }, env: { es2020: true } }
48+
{ code: "class C { #RegExp; foo() { new globalThis.#RegExp('foo') } }", parserOptions: { ecmaVersion: 2022 }, env: { es2020: true } },
49+
50+
// for v flag
51+
{ code: "/foo/v", parserOptions: { ecmaVersion: 2024 } },
52+
{ code: "/foo/gimvy", parserOptions: { ecmaVersion: 2024 } },
53+
{ code: "RegExp('', 'v')", parserOptions: { ecmaVersion: 2024 } },
54+
{ code: "RegExp('', `v`)", parserOptions: { ecmaVersion: 2024 } },
55+
{ code: "new RegExp('', 'v')", parserOptions: { ecmaVersion: 2024 } },
56+
{ code: "RegExp('', 'gimvy')", parserOptions: { ecmaVersion: 2024 } },
57+
{ code: "RegExp('', `gimvy`)", parserOptions: { ecmaVersion: 2024 } },
58+
{ code: "new RegExp('', 'gimvy')", parserOptions: { ecmaVersion: 2024 } },
59+
{ code: "const flags = 'v'; new RegExp('', flags)", parserOptions: { ecmaVersion: 2024 } },
60+
{ code: "const flags = 'g'; new RegExp('', flags + 'v')", parserOptions: { ecmaVersion: 2024 } },
61+
{ code: "const flags = 'gimv'; new RegExp('foo', flags[3])", parserOptions: { ecmaVersion: 2024 } }
4962
],
5063
invalid: [
5164
{

0 commit comments

Comments
 (0)