Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit45c18e1

Browse files
feat: addrequireFlag option torequire-unicode-regexp rule (#18836)
* feat: requireFlag option* docs: add option docs* docs: precise JSON schemaCo-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>* docs: remove extra exampleCo-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>* docs: format directiveCo-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>* docs: remove extra exampleCo-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>* docs: format directiveCo-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>* docs: remove extra exampleCo-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>* docs: format directiveCo-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>* docs: remove extra exampleCo-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>* docs: format directiveCo-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>* fix: remove extra quotesCo-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>* test: cover trailing comma* fix: only provide suggestion if flags node is string literal without escapes or template literal with no expressions and no escapes* avoid flag checks when flag-to-replace not present* check `flags` and append where flag safely not found* simplify fixerCo-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>* docs: rationale for choosing the flags* Update docs/src/rules/require-unicode-regexp.mdCo-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>* Update docs/src/rules/require-unicode-regexp.mdCo-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>---------Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>
1 parent5d80b59 commit45c18e1

File tree

4 files changed

+387
-18
lines changed

4 files changed

+387
-18
lines changed

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

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,87 @@ function i(flags) {
9898
9999
:::
100100
101+
## Options
102+
103+
This rule has one object option:
104+
105+
* `"requireFlag": "u"|"v"` requires a particular Unicode regex flag
106+
107+
### requireFlag: "u"
108+
109+
The `u` flag may be preferred in environments that do not support the `v` flag.
110+
111+
Examples of **incorrect** code for this rule with the `{ "requireFlag": "u" }` option:
112+
113+
:::incorrect
114+
115+
```js
116+
/*eslint require-unicode-regexp: ["error", { "requireFlag": "u" }] */
117+
118+
const fooEmpty = /foo/;
119+
120+
const fooEmptyRegexp = new RegExp('foo');
121+
122+
const foo = /foo/v;
123+
124+
const fooRegexp = new RegExp('foo','v');
125+
```
126+
127+
:::
128+
129+
Examples of **correct** code for this rule with the `{ "requireFlag": "u" }` option:
130+
131+
:::correct
132+
133+
```js
134+
/*eslint require-unicode-regexp: ["error", { "requireFlag": "u" }] */
135+
136+
const foo = /foo/u;
137+
138+
const fooRegexp = new RegExp('foo','u');
139+
```
140+
141+
:::
142+
143+
### requireFlag: "v"
144+
145+
The `v` flag may be a better choice when it is supported because it has more
146+
features than the `u` flag (e.g., the ability to test Unicode properties of strings). It
147+
does have a stricter syntax, however (e.g., the need to escape certain
148+
characters within character classes).
149+
150+
Examples of **incorrect** code for this rule with the `{ "requireFlag": "v" }` option:
151+
152+
:::incorrect
153+
154+
```js
155+
/*eslint require-unicode-regexp: ["error", { "requireFlag": "v" }] */
156+
157+
const fooEmpty = /foo/;
158+
159+
const fooEmptyRegexp = new RegExp('foo');
160+
161+
const foo = /foo/u;
162+
163+
const fooRegexp = new RegExp('foo','u');
164+
```
165+
166+
:::
167+
168+
Examples of **correct** code for this rule with the `{ "requireFlag": "v" }` option:
169+
170+
:::correct
171+
172+
```js
173+
/*eslint require-unicode-regexp: ["error", { "requireFlag": "v" }] */
174+
175+
const foo = /foo/v;
176+
177+
const fooRegexp = new RegExp('foo','v');
178+
```
179+
180+
:::
181+
101182
## When Not To Use It
102183
103184
If you don't want to warn on regular expressions without either a`u` or a`v` flag, then it's safe to disable this rule.

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

Lines changed: 95 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,26 @@ const {
1818
constastUtils=require("./utils/ast-utils.js");
1919
const{ isValidWithUnicodeFlag}=require("./utils/regular-expressions");
2020

21+
/**
22+
* Checks whether the flag configuration should be treated as a missing flag.
23+
*@param {"u"|"v"|undefined} requireFlag A particular flag to require
24+
*@param {string} flags The regex flags
25+
*@returns {boolean} Whether the flag configuration results in a missing flag.
26+
*/
27+
functioncheckFlags(requireFlag,flags){
28+
letmissingFlag;
29+
30+
if(requireFlag==="v"){
31+
missingFlag=!flags.includes("v");
32+
}elseif(requireFlag==="u"){
33+
missingFlag=!flags.includes("u");
34+
}else{
35+
missingFlag=!flags.includes("u")&&!flags.includes("v");
36+
}
37+
38+
returnmissingFlag;
39+
}
40+
2141
//------------------------------------------------------------------------------
2242
// Rule Definition
2343
//------------------------------------------------------------------------------
@@ -37,31 +57,65 @@ module.exports = {
3757

3858
messages:{
3959
addUFlag:"Add the 'u' flag.",
40-
requireUFlag:"Use the 'u' flag."
60+
addVFlag:"Add the 'v' flag.",
61+
requireUFlag:"Use the 'u' flag.",
62+
requireVFlag:"Use the 'v' flag."
4163
},
4264

43-
schema:[]
65+
schema:[
66+
{
67+
type:"object",
68+
properties:{
69+
requireFlag:{
70+
enum:["u","v"]
71+
}
72+
},
73+
additionalProperties:false
74+
}
75+
]
4476
},
4577

4678
create(context){
4779

4880
constsourceCode=context.sourceCode;
4981

82+
const{
83+
requireFlag
84+
}=context.options[0]??{};
85+
5086
return{
5187
"Literal[regex]"(node){
5288
constflags=node.regex.flags||"";
5389

54-
if(!flags.includes("u")&&!flags.includes("v")){
90+
constmissingFlag=checkFlags(requireFlag,flags);
91+
92+
if(missingFlag){
5593
context.report({
56-
messageId:"requireUFlag",
94+
messageId:requireFlag==="v" ?"requireVFlag" :"requireUFlag",
5795
node,
58-
suggest:isValidWithUnicodeFlag(context.languageOptions.ecmaVersion,node.regex.pattern)
96+
suggest:isValidWithUnicodeFlag(context.languageOptions.ecmaVersion,node.regex.pattern,requireFlag)
5997
?[
6098
{
6199
fix(fixer){
62-
returnfixer.insertTextAfter(node,"u");
100+
constreplaceFlag=requireFlag??"u";
101+
constregex=sourceCode.getText(node);
102+
constslashPos=regex.lastIndexOf("/");
103+
104+
if(requireFlag){
105+
constflag=requireFlag==="u" ?"v" :"u";
106+
107+
if(regex.includes(flag,slashPos)){
108+
returnfixer.replaceText(
109+
node,
110+
regex.slice(0,slashPos)+
111+
regex.slice(slashPos).replace(flag,requireFlag)
112+
);
113+
}
114+
}
115+
116+
returnfixer.insertTextAfter(node,replaceFlag);
63117
},
64-
messageId:"addUFlag"
118+
messageId:requireFlag==="v" ?"addVFlag" :"addUFlag"
65119
}
66120
]
67121
:null
@@ -85,22 +139,49 @@ module.exports = {
85139
constpattern=getStringIfConstant(patternNode,scope);
86140
constflags=getStringIfConstant(flagsNode,scope);
87141

88-
if(!flagsNode||(typeofflags==="string"&&!flags.includes("u")&&!flags.includes("v"))){
142+
letmissingFlag=!flagsNode;
143+
144+
if(typeofflags==="string"){
145+
missingFlag=checkFlags(requireFlag,flags);
146+
}
147+
148+
if(missingFlag){
89149
context.report({
90-
messageId:"requireUFlag",
150+
messageId:requireFlag==="v" ?"requireVFlag" :"requireUFlag",
91151
node:refNode,
92-
suggest:typeofpattern==="string"&&isValidWithUnicodeFlag(context.languageOptions.ecmaVersion,pattern)
152+
suggest:typeofpattern==="string"&&isValidWithUnicodeFlag(context.languageOptions.ecmaVersion,pattern,requireFlag)
93153
?[
94154
{
95155
fix(fixer){
156+
constreplaceFlag=requireFlag??"u";
157+
96158
if(flagsNode){
97159
if((flagsNode.type==="Literal"&&typeofflagsNode.value==="string")||flagsNode.type==="TemplateLiteral"){
98160
constflagsNodeText=sourceCode.getText(flagsNode);
161+
constflag=requireFlag==="u" ?"v" :"u";
162+
163+
if(flags.includes(flag)){
164+
165+
// Avoid replacing "u" in escapes like `\uXXXX`
166+
if(flagsNode.type==="Literal"&&flagsNode.raw.includes("\\")){
167+
returnnull;
168+
}
169+
170+
// Avoid replacing "u" in expressions like "`${regularFlags}g`"
171+
if(flagsNode.type==="TemplateLiteral"&&(
172+
flagsNode.expressions.length||
173+
flagsNode.quasis.some(({value:{ raw}})=>raw.includes("\\"))
174+
)){
175+
returnnull;
176+
}
177+
178+
returnfixer.replaceText(flagsNode,flagsNodeText.replace(flag,replaceFlag));
179+
}
99180

100181
returnfixer.replaceText(flagsNode,[
101182
flagsNodeText.slice(0,flagsNodeText.length-1),
102183
flagsNodeText.slice(flagsNodeText.length-1)
103-
].join("u"));
184+
].join(replaceFlag));
104185
}
105186

106187
// We intentionally don't suggest concatenating + "u" to non-literals
@@ -112,11 +193,11 @@ module.exports = {
112193
returnfixer.insertTextAfter(
113194
penultimateToken,
114195
astUtils.isCommaToken(penultimateToken)
115-
?' "u",'
116-
:', "u"'
196+
?` "${replaceFlag}",`
197+
:`, "${replaceFlag}"`
117198
);
118199
},
119-
messageId:"addUFlag"
200+
messageId:requireFlag==="v" ?"addVFlag" :"addUFlag"
120201
}
121202
]
122203
:null

‎lib/rules/utils/regular-expressions.js‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,16 @@ const REGEXPP_LATEST_ECMA_VERSION = 2025;
1414
* Checks if the given regular expression pattern would be valid with the `u` flag.
1515
*@param {number} ecmaVersion ECMAScript version to parse in.
1616
*@param {string} pattern The regular expression pattern to verify.
17+
*@param {"u"|"v"} flag The type of Unicode flag
1718
*@returns {boolean} `true` if the pattern would be valid with the `u` flag.
1819
* `false` if the pattern would be invalid with the `u` flag or the configured
1920
* ecmaVersion doesn't support the `u` flag.
2021
*/
21-
functionisValidWithUnicodeFlag(ecmaVersion,pattern){
22-
if(ecmaVersion<=5){// ecmaVersion <= 5 doesn't support the 'u' flag
22+
functionisValidWithUnicodeFlag(ecmaVersion,pattern,flag="u"){
23+
if(flag==="u"&&ecmaVersion<=5){// ecmaVersion <= 5 doesn't support the 'u' flag
24+
returnfalse;
25+
}
26+
if(flag==="v"&&ecmaVersion<=2023){
2327
returnfalse;
2428
}
2529

@@ -28,7 +32,11 @@ function isValidWithUnicodeFlag(ecmaVersion, pattern) {
2832
});
2933

3034
try{
31-
validator.validatePattern(pattern,void0,void0,{unicode:/* uFlag = */true});
35+
validator.validatePattern(pattern,void0,void0,flag==="u" ?{
36+
unicode:/* uFlag = */true
37+
} :{
38+
unicodeSets:true
39+
});
3240
}catch{
3341
returnfalse;
3442
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp