eslint 8.45.0 → 8.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/lib/config/flat-config-schema.js +70 -0
- package/lib/eslint/flat-eslint.js +3 -5
- package/lib/rules/no-control-regex.js +15 -2
- package/lib/rules/no-empty-character-class.js +33 -12
- package/lib/rules/no-empty-pattern.js +38 -3
- package/lib/rules/no-invalid-regexp.js +22 -7
- package/lib/rules/no-loop-func.js +1 -1
- package/lib/rules/no-misleading-character-class.js +8 -2
- package/lib/rules/no-new-wrappers.js +19 -7
- package/lib/rules/no-regex-spaces.js +18 -3
- package/lib/rules/no-return-await.js +5 -0
- package/lib/rules/no-useless-backreference.js +1 -1
- package/lib/rules/no-useless-escape.js +155 -80
- package/lib/rules/prefer-named-capture-group.js +8 -5
- package/lib/rules/prefer-regex-literals.js +9 -3
- package/lib/rules/require-unicode-regexp.js +3 -3
- package/lib/rules/utils/ast-utils.js +4 -4
- package/lib/rules/utils/regular-expressions.js +2 -2
- package/messages/eslintrc-incompat.js +98 -0
- package/messages/eslintrc-plugins.js +24 -0
- package/package.json +8 -8
package/README.md
CHANGED
@@ -249,6 +249,11 @@ Bryan Mishkin
|
|
249
249
|
<img src="https://github.com/fasttime.png?s=75" width="75" height="75"><br />
|
250
250
|
Francesco Trotta
|
251
251
|
</a>
|
252
|
+
</td><td align="center" valign="top" width="11%">
|
253
|
+
<a href="https://github.com/ota-meshi">
|
254
|
+
<img src="https://github.com/ota-meshi.png?s=75" width="75" height="75"><br />
|
255
|
+
Yosuke Ota
|
256
|
+
</a>
|
252
257
|
</td></tr></tbody></table>
|
253
258
|
|
254
259
|
### Website Team
|
@@ -284,7 +289,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
|
|
284
289
|
<p><a href="#"><img src="https://images.opencollective.com/2021-frameworks-fund/logo.png" alt="Chrome Frameworks Fund" height="undefined"></a> <a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
|
285
290
|
<p><a href="https://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a></p><h3>Silver Sponsors</h3>
|
286
291
|
<p><a href="https://sentry.io"><img src="https://avatars.githubusercontent.com/u/1396951?v=4" alt="Sentry" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="64"></a></p><h3>Bronze Sponsors</h3>
|
287
|
-
<p><a href="https://
|
292
|
+
<p><a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://github.com/about"><img src="https://avatars.githubusercontent.com/u/9919?v=4" alt="GitHub" height="32"></a> <a href="https://transloadit.com/"><img src="https://avatars.githubusercontent.com/u/125754?v=4" alt="Transloadit" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://quickbookstoolhub.com"><img src="https://avatars.githubusercontent.com/u/95090305?u=e5bc398ef775c9ed19f955c675cdc1fb6abf01df&v=4" alt="QuickBooks Tool hub" height="32"></a></p>
|
288
293
|
<!--sponsorsend-->
|
289
294
|
|
290
295
|
## Technology Sponsors
|
@@ -212,6 +212,38 @@ function assertIsObject(value) {
|
|
212
212
|
}
|
213
213
|
}
|
214
214
|
|
215
|
+
/**
|
216
|
+
* The error type when there's an eslintrc-style options in a flat config.
|
217
|
+
*/
|
218
|
+
class IncompatibleKeyError extends Error {
|
219
|
+
|
220
|
+
/**
|
221
|
+
* @param {string} key The invalid key.
|
222
|
+
*/
|
223
|
+
constructor(key) {
|
224
|
+
super("This appears to be in eslintrc format rather than flat config format.");
|
225
|
+
this.messageTemplate = "eslintrc-incompat";
|
226
|
+
this.messageData = { key };
|
227
|
+
}
|
228
|
+
}
|
229
|
+
|
230
|
+
/**
|
231
|
+
* The error type when there's an eslintrc-style plugins array found.
|
232
|
+
*/
|
233
|
+
class IncompatiblePluginsError extends Error {
|
234
|
+
|
235
|
+
/**
|
236
|
+
* Creates a new instance.
|
237
|
+
* @param {Array<string>} plugins The plugins array.
|
238
|
+
*/
|
239
|
+
constructor(plugins) {
|
240
|
+
super("This appears to be in eslintrc format (array of strings) rather than flat config format (object).");
|
241
|
+
this.messageTemplate = "eslintrc-plugins";
|
242
|
+
this.messageData = { plugins };
|
243
|
+
}
|
244
|
+
}
|
245
|
+
|
246
|
+
|
215
247
|
//-----------------------------------------------------------------------------
|
216
248
|
// Low-Level Schemas
|
217
249
|
//-----------------------------------------------------------------------------
|
@@ -303,6 +335,11 @@ const pluginsSchema = {
|
|
303
335
|
throw new TypeError("Expected an object.");
|
304
336
|
}
|
305
337
|
|
338
|
+
// make sure it's not an array, which would mean eslintrc-style is used
|
339
|
+
if (Array.isArray(value)) {
|
340
|
+
throw new IncompatiblePluginsError(value);
|
341
|
+
}
|
342
|
+
|
306
343
|
// second check the keys to make sure they are objects
|
307
344
|
for (const key of Object.keys(value)) {
|
308
345
|
|
@@ -438,11 +475,44 @@ const sourceTypeSchema = {
|
|
438
475
|
}
|
439
476
|
};
|
440
477
|
|
478
|
+
/**
|
479
|
+
* Creates a schema that always throws an error. Useful for warning
|
480
|
+
* about eslintrc-style keys.
|
481
|
+
* @param {string} key The eslintrc key to create a schema for.
|
482
|
+
* @returns {ObjectPropertySchema} The schema.
|
483
|
+
*/
|
484
|
+
function createEslintrcErrorSchema(key) {
|
485
|
+
return {
|
486
|
+
merge: "replace",
|
487
|
+
validate() {
|
488
|
+
throw new IncompatibleKeyError(key);
|
489
|
+
}
|
490
|
+
};
|
491
|
+
}
|
492
|
+
|
493
|
+
const eslintrcKeys = [
|
494
|
+
"env",
|
495
|
+
"extends",
|
496
|
+
"globals",
|
497
|
+
"ignorePatterns",
|
498
|
+
"noInlineConfig",
|
499
|
+
"overrides",
|
500
|
+
"parser",
|
501
|
+
"parserOptions",
|
502
|
+
"reportUnusedDisableDirectives",
|
503
|
+
"root"
|
504
|
+
];
|
505
|
+
|
441
506
|
//-----------------------------------------------------------------------------
|
442
507
|
// Full schema
|
443
508
|
//-----------------------------------------------------------------------------
|
444
509
|
|
445
510
|
exports.flatConfigSchema = {
|
511
|
+
|
512
|
+
// eslintrc-style keys that should always error
|
513
|
+
...Object.fromEntries(eslintrcKeys.map(key => [key, createEslintrcErrorSchema(key)])),
|
514
|
+
|
515
|
+
// flat config keys
|
446
516
|
settings: deepObjectAssignSchema,
|
447
517
|
linterOptions: {
|
448
518
|
schema: {
|
@@ -714,12 +714,10 @@ class FlatESLint {
|
|
714
714
|
}
|
715
715
|
const rule = getRuleFromConfig(ruleId, config);
|
716
716
|
|
717
|
-
//
|
718
|
-
if (
|
719
|
-
|
717
|
+
// ignore unknown rules
|
718
|
+
if (rule) {
|
719
|
+
resultRules.set(ruleId, rule);
|
720
720
|
}
|
721
|
-
|
722
|
-
resultRules.set(ruleId, rule);
|
723
721
|
}
|
724
722
|
}
|
725
723
|
|
@@ -14,6 +14,16 @@ const collector = new (class {
|
|
14
14
|
}
|
15
15
|
|
16
16
|
onPatternEnter() {
|
17
|
+
|
18
|
+
/*
|
19
|
+
* `RegExpValidator` may parse the pattern twice in one `validatePattern`.
|
20
|
+
* So `this._controlChars` should be cleared here as well.
|
21
|
+
*
|
22
|
+
* For example, the `/(?<a>\x1f)/` regex will parse the pattern twice.
|
23
|
+
* This is based on the content described in Annex B.
|
24
|
+
* If the regex contains a `GroupName` and the `u` flag is not used, `ParseText` will be called twice.
|
25
|
+
* See https://tc39.es/ecma262/2023/multipage/additional-ecmascript-features-for-web-browsers.html#sec-parsepattern-annexb
|
26
|
+
*/
|
17
27
|
this._controlChars = [];
|
18
28
|
}
|
19
29
|
|
@@ -32,10 +42,13 @@ const collector = new (class {
|
|
32
42
|
|
33
43
|
collectControlChars(regexpStr, flags) {
|
34
44
|
const uFlag = typeof flags === "string" && flags.includes("u");
|
45
|
+
const vFlag = typeof flags === "string" && flags.includes("v");
|
46
|
+
|
47
|
+
this._controlChars = [];
|
48
|
+
this._source = regexpStr;
|
35
49
|
|
36
50
|
try {
|
37
|
-
this.
|
38
|
-
this._validator.validatePattern(regexpStr, void 0, void 0, uFlag); // Call onCharacter hook
|
51
|
+
this._validator.validatePattern(regexpStr, void 0, void 0, { unicode: uFlag, unicodeSets: vFlag }); // Call onCharacter hook
|
39
52
|
} catch {
|
40
53
|
|
41
54
|
// Ignore syntax errors in RegExp.
|
@@ -5,20 +5,18 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Requirements
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
const { RegExpParser, visitRegExpAST } = require("@eslint-community/regexpp");
|
13
|
+
|
8
14
|
//------------------------------------------------------------------------------
|
9
15
|
// Helpers
|
10
16
|
//------------------------------------------------------------------------------
|
11
17
|
|
12
|
-
|
13
|
-
|
14
|
-
* 0. `^` fix the match at the beginning of the string
|
15
|
-
* 1. `([^\\[]|\\.|\[([^\\\]]|\\.)+\])*`: regexp contents; 0 or more of the following
|
16
|
-
* 1.0. `[^\\[]`: any character that's not a `\` or a `[` (anything but escape sequences and character classes)
|
17
|
-
* 1.1. `\\.`: an escape sequence
|
18
|
-
* 1.2. `\[([^\\\]]|\\.)+\]`: a character class that isn't empty
|
19
|
-
* 2. `$`: fix the match at the end of the string
|
20
|
-
*/
|
21
|
-
const regex = /^([^\\[]|\\.|\[([^\\\]]|\\.)+\])*$/u;
|
18
|
+
const parser = new RegExpParser();
|
19
|
+
const QUICK_TEST_REGEX = /\[\]/u;
|
22
20
|
|
23
21
|
//------------------------------------------------------------------------------
|
24
22
|
// Rule Definition
|
@@ -45,9 +43,32 @@ module.exports = {
|
|
45
43
|
create(context) {
|
46
44
|
return {
|
47
45
|
"Literal[regex]"(node) {
|
48
|
-
|
49
|
-
|
46
|
+
const { pattern, flags } = node.regex;
|
47
|
+
|
48
|
+
if (!QUICK_TEST_REGEX.test(pattern)) {
|
49
|
+
return;
|
50
50
|
}
|
51
|
+
|
52
|
+
let regExpAST;
|
53
|
+
|
54
|
+
try {
|
55
|
+
regExpAST = parser.parsePattern(pattern, 0, pattern.length, {
|
56
|
+
unicode: flags.includes("u"),
|
57
|
+
unicodeSets: flags.includes("v")
|
58
|
+
});
|
59
|
+
} catch {
|
60
|
+
|
61
|
+
// Ignore regular expressions that regexpp cannot parse
|
62
|
+
return;
|
63
|
+
}
|
64
|
+
|
65
|
+
visitRegExpAST(regExpAST, {
|
66
|
+
onCharacterClassEnter(characterClass) {
|
67
|
+
if (!characterClass.negate && characterClass.elements.length === 0) {
|
68
|
+
context.report({ node, messageId: "unexpected" });
|
69
|
+
}
|
70
|
+
}
|
71
|
+
});
|
51
72
|
}
|
52
73
|
};
|
53
74
|
|
@@ -4,6 +4,8 @@
|
|
4
4
|
*/
|
5
5
|
"use strict";
|
6
6
|
|
7
|
+
const astUtils = require("./utils/ast-utils");
|
8
|
+
|
7
9
|
//------------------------------------------------------------------------------
|
8
10
|
// Rule Definition
|
9
11
|
//------------------------------------------------------------------------------
|
@@ -19,7 +21,18 @@ module.exports = {
|
|
19
21
|
url: "https://eslint.org/docs/latest/rules/no-empty-pattern"
|
20
22
|
},
|
21
23
|
|
22
|
-
schema: [
|
24
|
+
schema: [
|
25
|
+
{
|
26
|
+
type: "object",
|
27
|
+
properties: {
|
28
|
+
allowObjectPatternsAsParameters: {
|
29
|
+
type: "boolean",
|
30
|
+
default: false
|
31
|
+
}
|
32
|
+
},
|
33
|
+
additionalProperties: false
|
34
|
+
}
|
35
|
+
],
|
23
36
|
|
24
37
|
messages: {
|
25
38
|
unexpected: "Unexpected empty {{type}} pattern."
|
@@ -27,11 +40,33 @@ module.exports = {
|
|
27
40
|
},
|
28
41
|
|
29
42
|
create(context) {
|
43
|
+
const options = context.options[0] || {},
|
44
|
+
allowObjectPatternsAsParameters = options.allowObjectPatternsAsParameters || false;
|
45
|
+
|
30
46
|
return {
|
31
47
|
ObjectPattern(node) {
|
32
|
-
|
33
|
-
|
48
|
+
|
49
|
+
if (node.properties.length > 0) {
|
50
|
+
return;
|
34
51
|
}
|
52
|
+
|
53
|
+
// Allow {} and {} = {} empty object patterns as parameters when allowObjectPatternsAsParameters is true
|
54
|
+
if (
|
55
|
+
allowObjectPatternsAsParameters &&
|
56
|
+
(
|
57
|
+
astUtils.isFunction(node.parent) ||
|
58
|
+
(
|
59
|
+
node.parent.type === "AssignmentPattern" &&
|
60
|
+
astUtils.isFunction(node.parent.parent) &&
|
61
|
+
node.parent.right.type === "ObjectExpression" &&
|
62
|
+
node.parent.right.properties.length === 0
|
63
|
+
)
|
64
|
+
)
|
65
|
+
) {
|
66
|
+
return;
|
67
|
+
}
|
68
|
+
|
69
|
+
context.report({ node, messageId: "unexpected", data: { type: "object" } });
|
35
70
|
},
|
36
71
|
ArrayPattern(node) {
|
37
72
|
if (node.elements.length === 0) {
|
@@ -10,7 +10,7 @@
|
|
10
10
|
|
11
11
|
const RegExpValidator = require("@eslint-community/regexpp").RegExpValidator;
|
12
12
|
const validator = new RegExpValidator();
|
13
|
-
const validFlags = /[
|
13
|
+
const validFlags = /[dgimsuvy]/gu;
|
14
14
|
const undefined1 = void 0;
|
15
15
|
|
16
16
|
//------------------------------------------------------------------------------
|
@@ -108,12 +108,14 @@ module.exports = {
|
|
108
108
|
/**
|
109
109
|
* Check syntax error in a given pattern.
|
110
110
|
* @param {string} pattern The RegExp pattern to validate.
|
111
|
-
* @param {
|
111
|
+
* @param {Object} flags The RegExp flags to validate.
|
112
|
+
* @param {boolean} [flags.unicode] The Unicode flag.
|
113
|
+
* @param {boolean} [flags.unicodeSets] The UnicodeSets flag.
|
112
114
|
* @returns {string|null} The syntax error.
|
113
115
|
*/
|
114
|
-
function validateRegExpPattern(pattern,
|
116
|
+
function validateRegExpPattern(pattern, flags) {
|
115
117
|
try {
|
116
|
-
validator.validatePattern(pattern, undefined1, undefined1,
|
118
|
+
validator.validatePattern(pattern, undefined1, undefined1, flags);
|
117
119
|
return null;
|
118
120
|
} catch (err) {
|
119
121
|
return err.message;
|
@@ -131,10 +133,19 @@ module.exports = {
|
|
131
133
|
}
|
132
134
|
try {
|
133
135
|
validator.validateFlags(flags);
|
134
|
-
return null;
|
135
136
|
} catch {
|
136
137
|
return `Invalid flags supplied to RegExp constructor '${flags}'`;
|
137
138
|
}
|
139
|
+
|
140
|
+
/*
|
141
|
+
* `regexpp` checks the combination of `u` and `v` flags when parsing `Pattern` according to `ecma262`,
|
142
|
+
* but this rule may check only the flag when the pattern is unidentifiable, so check it here.
|
143
|
+
* https://tc39.es/ecma262/multipage/text-processing.html#sec-parsepattern
|
144
|
+
*/
|
145
|
+
if (flags.includes("u") && flags.includes("v")) {
|
146
|
+
return "Regex 'u' and 'v' flags cannot be used together";
|
147
|
+
}
|
148
|
+
return null;
|
138
149
|
}
|
139
150
|
|
140
151
|
return {
|
@@ -166,8 +177,12 @@ module.exports = {
|
|
166
177
|
|
167
178
|
// If flags are unknown, report the regex only if its pattern is invalid both with and without the "u" flag
|
168
179
|
flags === null
|
169
|
-
?
|
170
|
-
|
180
|
+
? (
|
181
|
+
validateRegExpPattern(pattern, { unicode: true, unicodeSets: false }) &&
|
182
|
+
validateRegExpPattern(pattern, { unicode: false, unicodeSets: true }) &&
|
183
|
+
validateRegExpPattern(pattern, { unicode: false, unicodeSets: false })
|
184
|
+
)
|
185
|
+
: validateRegExpPattern(pattern, { unicode: flags.includes("u"), unicodeSets: flags.includes("v") })
|
171
186
|
);
|
172
187
|
|
173
188
|
if (message) {
|
@@ -186,7 +186,7 @@ module.exports = {
|
|
186
186
|
}
|
187
187
|
|
188
188
|
const references = sourceCode.getScope(node).through;
|
189
|
-
const unsafeRefs = references.filter(r => !isSafe(loopNode, r)).map(r => r.identifier.name);
|
189
|
+
const unsafeRefs = references.filter(r => r.resolved && !isSafe(loopNode, r)).map(r => r.identifier.name);
|
190
190
|
|
191
191
|
if (unsafeRefs.length > 0) {
|
192
192
|
context.report({
|
@@ -18,7 +18,7 @@ const { isValidWithUnicodeFlag } = require("./utils/regular-expressions");
|
|
18
18
|
*
|
19
19
|
* CharacterClassRange syntax can steal a part of character sequence,
|
20
20
|
* so this function reverts CharacterClassRange syntax and restore the sequence.
|
21
|
-
* @param {regexpp.AST.CharacterClassElement[]} nodes The node list to iterate character sequences.
|
21
|
+
* @param {import('@eslint-community/regexpp').AST.CharacterClassElement[]} nodes The node list to iterate character sequences.
|
22
22
|
* @returns {IterableIterator<number[]>} The list of character sequences.
|
23
23
|
*/
|
24
24
|
function *iterateCharacterSequence(nodes) {
|
@@ -37,6 +37,9 @@ function *iterateCharacterSequence(nodes) {
|
|
37
37
|
break;
|
38
38
|
|
39
39
|
case "CharacterSet":
|
40
|
+
case "CharacterClass": // [[]] nesting character class
|
41
|
+
case "ClassStringDisjunction": // \q{...}
|
42
|
+
case "ExpressionCharacterClass": // [A--B]
|
40
43
|
if (seq.length > 0) {
|
41
44
|
yield seq;
|
42
45
|
seq = [];
|
@@ -144,7 +147,10 @@ module.exports = {
|
|
144
147
|
pattern,
|
145
148
|
0,
|
146
149
|
pattern.length,
|
147
|
-
|
150
|
+
{
|
151
|
+
unicode: flags.includes("u"),
|
152
|
+
unicodeSets: flags.includes("v")
|
153
|
+
}
|
148
154
|
);
|
149
155
|
} catch {
|
150
156
|
|
@@ -5,6 +5,12 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Requirements
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
const { getVariableByName } = require("./utils/ast-utils");
|
13
|
+
|
8
14
|
//------------------------------------------------------------------------------
|
9
15
|
// Rule Definition
|
10
16
|
//------------------------------------------------------------------------------
|
@@ -28,18 +34,24 @@ module.exports = {
|
|
28
34
|
},
|
29
35
|
|
30
36
|
create(context) {
|
37
|
+
const { sourceCode } = context;
|
31
38
|
|
32
39
|
return {
|
33
40
|
|
34
41
|
NewExpression(node) {
|
35
42
|
const wrapperObjects = ["String", "Number", "Boolean"];
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
+
const { name } = node.callee;
|
44
|
+
|
45
|
+
if (wrapperObjects.includes(name)) {
|
46
|
+
const variable = getVariableByName(sourceCode.getScope(node), name);
|
47
|
+
|
48
|
+
if (variable && variable.identifiers.length === 0) {
|
49
|
+
context.report({
|
50
|
+
node,
|
51
|
+
messageId: "noConstructor",
|
52
|
+
data: { fn: name }
|
53
|
+
});
|
54
|
+
}
|
43
55
|
}
|
44
56
|
}
|
45
57
|
};
|
@@ -77,7 +77,7 @@ module.exports = {
|
|
77
77
|
let regExpAST;
|
78
78
|
|
79
79
|
try {
|
80
|
-
regExpAST = regExpParser.parsePattern(pattern, 0, pattern.length, flags.includes("u"));
|
80
|
+
regExpAST = regExpParser.parsePattern(pattern, 0, pattern.length, { unicode: flags.includes("u"), unicodeSets: flags.includes("v") });
|
81
81
|
} catch {
|
82
82
|
|
83
83
|
// Ignore regular expressions with syntax errors
|
@@ -155,13 +155,28 @@ module.exports = {
|
|
155
155
|
const regExpVar = astUtils.getVariableByName(scope, "RegExp");
|
156
156
|
const shadowed = regExpVar && regExpVar.defs.length > 0;
|
157
157
|
const patternNode = node.arguments[0];
|
158
|
-
const flagsNode = node.arguments[1];
|
159
158
|
|
160
159
|
if (node.callee.type === "Identifier" && node.callee.name === "RegExp" && isString(patternNode) && !shadowed) {
|
161
160
|
const pattern = patternNode.value;
|
162
161
|
const rawPattern = patternNode.raw.slice(1, -1);
|
163
162
|
const rawPatternStartRange = patternNode.range[0] + 1;
|
164
|
-
|
163
|
+
let flags;
|
164
|
+
|
165
|
+
if (node.arguments.length < 2) {
|
166
|
+
|
167
|
+
// It has no flags.
|
168
|
+
flags = "";
|
169
|
+
} else {
|
170
|
+
const flagsNode = node.arguments[1];
|
171
|
+
|
172
|
+
if (isString(flagsNode)) {
|
173
|
+
flags = flagsNode.value;
|
174
|
+
} else {
|
175
|
+
|
176
|
+
// The flags cannot be determined.
|
177
|
+
return;
|
178
|
+
}
|
179
|
+
}
|
165
180
|
|
166
181
|
checkRegex(
|
167
182
|
node,
|
@@ -1,6 +1,7 @@
|
|
1
1
|
/**
|
2
2
|
* @fileoverview Disallows unnecessary `return await`
|
3
3
|
* @author Jordan Harband
|
4
|
+
* @deprecated in ESLint v8.46.0
|
4
5
|
*/
|
5
6
|
"use strict";
|
6
7
|
|
@@ -26,6 +27,10 @@ module.exports = {
|
|
26
27
|
|
27
28
|
fixable: null,
|
28
29
|
|
30
|
+
deprecated: true,
|
31
|
+
|
32
|
+
replacedBy: [],
|
33
|
+
|
29
34
|
schema: [
|
30
35
|
],
|
31
36
|
|
@@ -95,7 +95,7 @@ module.exports = {
|
|
95
95
|
let regExpAST;
|
96
96
|
|
97
97
|
try {
|
98
|
-
regExpAST = parser.parsePattern(pattern, 0, pattern.length, flags.includes("u"));
|
98
|
+
regExpAST = parser.parsePattern(pattern, 0, pattern.length, { unicode: flags.includes("u"), unicodeSets: flags.includes("v") });
|
99
99
|
} catch {
|
100
100
|
|
101
101
|
// Ignore regular expressions with syntax errors
|
@@ -6,7 +6,12 @@
|
|
6
6
|
"use strict";
|
7
7
|
|
8
8
|
const astUtils = require("./utils/ast-utils");
|
9
|
+
const { RegExpParser, visitRegExpAST } = require("@eslint-community/regexpp");
|
9
10
|
|
11
|
+
/**
|
12
|
+
* @typedef {import('@eslint-community/regexpp').AST.CharacterClass} CharacterClass
|
13
|
+
* @typedef {import('@eslint-community/regexpp').AST.ExpressionCharacterClass} ExpressionCharacterClass
|
14
|
+
*/
|
10
15
|
//------------------------------------------------------------------------------
|
11
16
|
// Rule Definition
|
12
17
|
//------------------------------------------------------------------------------
|
@@ -28,55 +33,17 @@ const VALID_STRING_ESCAPES = union(new Set("\\nrvtbfux"), astUtils.LINEBREAKS);
|
|
28
33
|
const REGEX_GENERAL_ESCAPES = new Set("\\bcdDfnpPrsStvwWxu0123456789]");
|
29
34
|
const REGEX_NON_CHARCLASS_ESCAPES = union(REGEX_GENERAL_ESCAPES, new Set("^/.$*+?[{}|()Bk"));
|
30
35
|
|
31
|
-
|
32
|
-
*
|
33
|
-
*
|
34
|
-
* @returns {Object[]} A list of characters, each with info on escaping and whether they're in a character class.
|
35
|
-
* @example
|
36
|
-
*
|
37
|
-
* parseRegExp("a\\b[cd-]");
|
38
|
-
*
|
39
|
-
* // returns:
|
40
|
-
* [
|
41
|
-
* { text: "a", index: 0, escaped: false, inCharClass: false, startsCharClass: false, endsCharClass: false },
|
42
|
-
* { text: "b", index: 2, escaped: true, inCharClass: false, startsCharClass: false, endsCharClass: false },
|
43
|
-
* { text: "c", index: 4, escaped: false, inCharClass: true, startsCharClass: true, endsCharClass: false },
|
44
|
-
* { text: "d", index: 5, escaped: false, inCharClass: true, startsCharClass: false, endsCharClass: false },
|
45
|
-
* { text: "-", index: 6, escaped: false, inCharClass: true, startsCharClass: false, endsCharClass: false }
|
46
|
-
* ];
|
47
|
-
*
|
36
|
+
/*
|
37
|
+
* Set of characters that require escaping in character classes in `unicodeSets` mode.
|
38
|
+
* ( ) [ ] { } / - \ | are ClassSetSyntaxCharacter
|
48
39
|
*/
|
49
|
-
|
50
|
-
const charList = [];
|
40
|
+
const REGEX_CLASSSET_CHARACTER_ESCAPES = union(REGEX_GENERAL_ESCAPES, new Set("q/[{}|()-"));
|
51
41
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
if (char === "[" && !state.inCharClass) {
|
58
|
-
return Object.assign(state, { inCharClass: true, startingCharClass: true });
|
59
|
-
}
|
60
|
-
if (char === "]" && state.inCharClass) {
|
61
|
-
if (charList.length && charList[charList.length - 1].inCharClass) {
|
62
|
-
charList[charList.length - 1].endsCharClass = true;
|
63
|
-
}
|
64
|
-
return Object.assign(state, { inCharClass: false, startingCharClass: false });
|
65
|
-
}
|
66
|
-
}
|
67
|
-
charList.push({
|
68
|
-
text: char,
|
69
|
-
index,
|
70
|
-
escaped: state.escapeNextChar,
|
71
|
-
inCharClass: state.inCharClass,
|
72
|
-
startsCharClass: state.startingCharClass,
|
73
|
-
endsCharClass: false
|
74
|
-
});
|
75
|
-
return Object.assign(state, { escapeNextChar: false, startingCharClass: false });
|
76
|
-
}, { escapeNextChar: false, inCharClass: false, startingCharClass: false });
|
77
|
-
|
78
|
-
return charList;
|
79
|
-
}
|
42
|
+
/*
|
43
|
+
* A single character set of ClassSetReservedDoublePunctuator.
|
44
|
+
* && !! ## $$ %% ** ++ ,, .. :: ;; << == >> ?? @@ ^^ `` ~~ are ClassSetReservedDoublePunctuator
|
45
|
+
*/
|
46
|
+
const REGEX_CLASS_SET_RESERVED_DOUBLE_PUNCTUATOR = new Set("!#$%&*+,.:;<=>?@^`~");
|
80
47
|
|
81
48
|
/** @type {import('../shared/types').Rule} */
|
82
49
|
module.exports = {
|
@@ -103,15 +70,17 @@ module.exports = {
|
|
103
70
|
|
104
71
|
create(context) {
|
105
72
|
const sourceCode = context.sourceCode;
|
73
|
+
const parser = new RegExpParser();
|
106
74
|
|
107
75
|
/**
|
108
76
|
* Reports a node
|
109
77
|
* @param {ASTNode} node The node to report
|
110
78
|
* @param {number} startOffset The backslash's offset from the start of the node
|
111
79
|
* @param {string} character The uselessly escaped character (not including the backslash)
|
80
|
+
* @param {boolean} [disableEscapeBackslashSuggest] `true` if escapeBackslash suggestion should be turned off.
|
112
81
|
* @returns {void}
|
113
82
|
*/
|
114
|
-
function report(node, startOffset, character) {
|
83
|
+
function report(node, startOffset, character, disableEscapeBackslashSuggest) {
|
115
84
|
const rangeStart = node.range[0] + startOffset;
|
116
85
|
const range = [rangeStart, rangeStart + 1];
|
117
86
|
const start = sourceCode.getLocFromIndex(rangeStart);
|
@@ -134,12 +103,16 @@ module.exports = {
|
|
134
103
|
return fixer.removeRange(range);
|
135
104
|
}
|
136
105
|
},
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
106
|
+
...disableEscapeBackslashSuggest
|
107
|
+
? []
|
108
|
+
: [
|
109
|
+
{
|
110
|
+
messageId: "escapeBackslash",
|
111
|
+
fix(fixer) {
|
112
|
+
return fixer.insertTextBeforeRange(range, "\\");
|
113
|
+
}
|
114
|
+
}
|
115
|
+
]
|
143
116
|
]
|
144
117
|
});
|
145
118
|
}
|
@@ -182,6 +155,133 @@ module.exports = {
|
|
182
155
|
}
|
183
156
|
}
|
184
157
|
|
158
|
+
/**
|
159
|
+
* Checks if the escape character in given regexp is unnecessary.
|
160
|
+
* @private
|
161
|
+
* @param {ASTNode} node node to validate.
|
162
|
+
* @returns {void}
|
163
|
+
*/
|
164
|
+
function validateRegExp(node) {
|
165
|
+
const { pattern, flags } = node.regex;
|
166
|
+
let patternNode;
|
167
|
+
const unicode = flags.includes("u");
|
168
|
+
const unicodeSets = flags.includes("v");
|
169
|
+
|
170
|
+
try {
|
171
|
+
patternNode = parser.parsePattern(pattern, 0, pattern.length, { unicode, unicodeSets });
|
172
|
+
} catch {
|
173
|
+
|
174
|
+
// Ignore regular expressions with syntax errors
|
175
|
+
return;
|
176
|
+
}
|
177
|
+
|
178
|
+
/** @type {(CharacterClass | ExpressionCharacterClass)[]} */
|
179
|
+
const characterClassStack = [];
|
180
|
+
|
181
|
+
visitRegExpAST(patternNode, {
|
182
|
+
onCharacterClassEnter: characterClassNode => characterClassStack.unshift(characterClassNode),
|
183
|
+
onCharacterClassLeave: () => characterClassStack.shift(),
|
184
|
+
onExpressionCharacterClassEnter: characterClassNode => characterClassStack.unshift(characterClassNode),
|
185
|
+
onExpressionCharacterClassLeave: () => characterClassStack.shift(),
|
186
|
+
onCharacterEnter(characterNode) {
|
187
|
+
if (!characterNode.raw.startsWith("\\")) {
|
188
|
+
|
189
|
+
// It's not an escaped character.
|
190
|
+
return;
|
191
|
+
}
|
192
|
+
|
193
|
+
const escapedChar = characterNode.raw.slice(1);
|
194
|
+
|
195
|
+
if (escapedChar !== String.fromCodePoint(characterNode.value)) {
|
196
|
+
|
197
|
+
// It's a valid escape.
|
198
|
+
return;
|
199
|
+
}
|
200
|
+
let allowedEscapes;
|
201
|
+
|
202
|
+
if (characterClassStack.length) {
|
203
|
+
allowedEscapes = unicodeSets ? REGEX_CLASSSET_CHARACTER_ESCAPES : REGEX_GENERAL_ESCAPES;
|
204
|
+
} else {
|
205
|
+
allowedEscapes = REGEX_NON_CHARCLASS_ESCAPES;
|
206
|
+
}
|
207
|
+
if (allowedEscapes.has(escapedChar)) {
|
208
|
+
return;
|
209
|
+
}
|
210
|
+
|
211
|
+
const reportedIndex = characterNode.start + 1;
|
212
|
+
let disableEscapeBackslashSuggest = false;
|
213
|
+
|
214
|
+
if (characterClassStack.length) {
|
215
|
+
const characterClassNode = characterClassStack[0];
|
216
|
+
|
217
|
+
if (escapedChar === "^") {
|
218
|
+
|
219
|
+
/*
|
220
|
+
* The '^' character is also a special case; it must always be escaped outside of character classes, but
|
221
|
+
* it only needs to be escaped in character classes if it's at the beginning of the character class. To
|
222
|
+
* account for this, consider it to be a valid escape character outside of character classes, and filter
|
223
|
+
* out '^' characters that appear at the start of a character class.
|
224
|
+
*/
|
225
|
+
if (characterClassNode.start + 1 === characterNode.start) {
|
226
|
+
|
227
|
+
return;
|
228
|
+
}
|
229
|
+
}
|
230
|
+
if (!unicodeSets) {
|
231
|
+
if (escapedChar === "-") {
|
232
|
+
|
233
|
+
/*
|
234
|
+
* The '-' character is a special case, because it's only valid to escape it if it's in a character
|
235
|
+
* class, and is not at either edge of the character class. To account for this, don't consider '-'
|
236
|
+
* characters to be valid in general, and filter out '-' characters that appear in the middle of a
|
237
|
+
* character class.
|
238
|
+
*/
|
239
|
+
if (characterClassNode.start + 1 !== characterNode.start && characterNode.end !== characterClassNode.end - 1) {
|
240
|
+
|
241
|
+
return;
|
242
|
+
}
|
243
|
+
}
|
244
|
+
} else { // unicodeSets mode
|
245
|
+
if (REGEX_CLASS_SET_RESERVED_DOUBLE_PUNCTUATOR.has(escapedChar)) {
|
246
|
+
|
247
|
+
// Escaping is valid if it is a ClassSetReservedDoublePunctuator.
|
248
|
+
if (pattern[characterNode.end] === escapedChar) {
|
249
|
+
return;
|
250
|
+
}
|
251
|
+
if (pattern[characterNode.start - 1] === escapedChar) {
|
252
|
+
if (escapedChar !== "^") {
|
253
|
+
return;
|
254
|
+
}
|
255
|
+
|
256
|
+
// If the previous character is a `negate` caret(`^`), escape to caret is unnecessary.
|
257
|
+
|
258
|
+
if (!characterClassNode.negate) {
|
259
|
+
return;
|
260
|
+
}
|
261
|
+
const negateCaretIndex = characterClassNode.start + 1;
|
262
|
+
|
263
|
+
if (negateCaretIndex < characterNode.start - 1) {
|
264
|
+
return;
|
265
|
+
}
|
266
|
+
}
|
267
|
+
}
|
268
|
+
|
269
|
+
if (characterNode.parent.type === "ClassIntersection" || characterNode.parent.type === "ClassSubtraction") {
|
270
|
+
disableEscapeBackslashSuggest = true;
|
271
|
+
}
|
272
|
+
}
|
273
|
+
}
|
274
|
+
|
275
|
+
report(
|
276
|
+
node,
|
277
|
+
reportedIndex,
|
278
|
+
escapedChar,
|
279
|
+
disableEscapeBackslashSuggest
|
280
|
+
);
|
281
|
+
}
|
282
|
+
});
|
283
|
+
}
|
284
|
+
|
185
285
|
/**
|
186
286
|
* Checks if a node has an escape.
|
187
287
|
* @param {ASTNode} node node to check.
|
@@ -220,32 +320,7 @@ module.exports = {
|
|
220
320
|
validateString(node, match);
|
221
321
|
}
|
222
322
|
} else if (node.regex) {
|
223
|
-
|
224
|
-
|
225
|
-
/*
|
226
|
-
* The '-' character is a special case, because it's only valid to escape it if it's in a character
|
227
|
-
* class, and is not at either edge of the character class. To account for this, don't consider '-'
|
228
|
-
* characters to be valid in general, and filter out '-' characters that appear in the middle of a
|
229
|
-
* character class.
|
230
|
-
*/
|
231
|
-
.filter(charInfo => !(charInfo.text === "-" && charInfo.inCharClass && !charInfo.startsCharClass && !charInfo.endsCharClass))
|
232
|
-
|
233
|
-
/*
|
234
|
-
* The '^' character is also a special case; it must always be escaped outside of character classes, but
|
235
|
-
* it only needs to be escaped in character classes if it's at the beginning of the character class. To
|
236
|
-
* account for this, consider it to be a valid escape character outside of character classes, and filter
|
237
|
-
* out '^' characters that appear at the start of a character class.
|
238
|
-
*/
|
239
|
-
.filter(charInfo => !(charInfo.text === "^" && charInfo.startsCharClass))
|
240
|
-
|
241
|
-
// Filter out characters that aren't escaped.
|
242
|
-
.filter(charInfo => charInfo.escaped)
|
243
|
-
|
244
|
-
// Filter out characters that are valid to escape, based on their position in the regular expression.
|
245
|
-
.filter(charInfo => !(charInfo.inCharClass ? REGEX_GENERAL_ESCAPES : REGEX_NON_CHARCLASS_ESCAPES).has(charInfo.text))
|
246
|
-
|
247
|
-
// Report all the remaining characters.
|
248
|
-
.forEach(charInfo => report(node, charInfo.index, charInfo.text));
|
323
|
+
validateRegExp(node);
|
249
324
|
}
|
250
325
|
|
251
326
|
}
|
@@ -112,14 +112,17 @@ module.exports = {
|
|
112
112
|
* @param {string} pattern The regular expression pattern to be checked.
|
113
113
|
* @param {ASTNode} node AST node which contains the regular expression or a call/new expression.
|
114
114
|
* @param {ASTNode} regexNode AST node which contains the regular expression.
|
115
|
-
* @param {
|
115
|
+
* @param {string|null} flags The regular expression flags to be checked.
|
116
116
|
* @returns {void}
|
117
117
|
*/
|
118
|
-
function checkRegex(pattern, node, regexNode,
|
118
|
+
function checkRegex(pattern, node, regexNode, flags) {
|
119
119
|
let ast;
|
120
120
|
|
121
121
|
try {
|
122
|
-
ast = parser.parsePattern(pattern, 0, pattern.length,
|
122
|
+
ast = parser.parsePattern(pattern, 0, pattern.length, {
|
123
|
+
unicode: Boolean(flags && flags.includes("u")),
|
124
|
+
unicodeSets: Boolean(flags && flags.includes("v"))
|
125
|
+
});
|
123
126
|
} catch {
|
124
127
|
|
125
128
|
// ignore regex syntax errors
|
@@ -148,7 +151,7 @@ module.exports = {
|
|
148
151
|
return {
|
149
152
|
Literal(node) {
|
150
153
|
if (node.regex) {
|
151
|
-
checkRegex(node.regex.pattern, node, node, node.regex.flags
|
154
|
+
checkRegex(node.regex.pattern, node, node, node.regex.flags);
|
152
155
|
}
|
153
156
|
},
|
154
157
|
Program(node) {
|
@@ -166,7 +169,7 @@ module.exports = {
|
|
166
169
|
const flags = getStringIfConstant(refNode.arguments[1]);
|
167
170
|
|
168
171
|
if (regex) {
|
169
|
-
checkRegex(regex, refNode, refNode.arguments[0], flags
|
172
|
+
checkRegex(regex, refNode, refNode.arguments[0], flags);
|
170
173
|
}
|
171
174
|
}
|
172
175
|
}
|
@@ -241,7 +241,7 @@ module.exports = {
|
|
241
241
|
/**
|
242
242
|
* Returns a ecmaVersion compatible for regexpp.
|
243
243
|
* @param {number} ecmaVersion The ecmaVersion to convert.
|
244
|
-
* @returns {import("regexpp/ecma-versions").EcmaVersion} The resulting ecmaVersion compatible for regexpp.
|
244
|
+
* @returns {import("@eslint-community/regexpp/ecma-versions").EcmaVersion} The resulting ecmaVersion compatible for regexpp.
|
245
245
|
*/
|
246
246
|
function getRegexppEcmaVersion(ecmaVersion) {
|
247
247
|
if (ecmaVersion <= 5) {
|
@@ -297,7 +297,10 @@ module.exports = {
|
|
297
297
|
const validator = new RegExpValidator({ ecmaVersion: regexppEcmaVersion });
|
298
298
|
|
299
299
|
try {
|
300
|
-
validator.validatePattern(pattern, 0, pattern.length,
|
300
|
+
validator.validatePattern(pattern, 0, pattern.length, {
|
301
|
+
unicode: flags ? flags.includes("u") : false,
|
302
|
+
unicodeSets: flags ? flags.includes("v") : false
|
303
|
+
});
|
301
304
|
if (flags) {
|
302
305
|
validator.validateFlags(flags);
|
303
306
|
}
|
@@ -461,7 +464,10 @@ module.exports = {
|
|
461
464
|
if (regexContent && !noFix) {
|
462
465
|
let charIncrease = 0;
|
463
466
|
|
464
|
-
const ast = new RegExpParser({ ecmaVersion: regexppEcmaVersion }).parsePattern(regexContent, 0, regexContent.length,
|
467
|
+
const ast = new RegExpParser({ ecmaVersion: regexppEcmaVersion }).parsePattern(regexContent, 0, regexContent.length, {
|
468
|
+
unicode: flags ? flags.includes("u") : false,
|
469
|
+
unicodeSets: flags ? flags.includes("v") : false
|
470
|
+
});
|
465
471
|
|
466
472
|
visitRegExpAST(ast, {
|
467
473
|
onCharacterEnter(characterNode) {
|
@@ -28,7 +28,7 @@ module.exports = {
|
|
28
28
|
type: "suggestion",
|
29
29
|
|
30
30
|
docs: {
|
31
|
-
description: "Enforce the use of `u` flag on RegExp",
|
31
|
+
description: "Enforce the use of `u` or `v` flag on RegExp",
|
32
32
|
recommended: false,
|
33
33
|
url: "https://eslint.org/docs/latest/rules/require-unicode-regexp"
|
34
34
|
},
|
@@ -51,7 +51,7 @@ module.exports = {
|
|
51
51
|
"Literal[regex]"(node) {
|
52
52
|
const flags = node.regex.flags || "";
|
53
53
|
|
54
|
-
if (!flags.includes("u")) {
|
54
|
+
if (!flags.includes("u") && !flags.includes("v")) {
|
55
55
|
context.report({
|
56
56
|
messageId: "requireUFlag",
|
57
57
|
node,
|
@@ -85,7 +85,7 @@ module.exports = {
|
|
85
85
|
const pattern = getStringIfConstant(patternNode, scope);
|
86
86
|
const flags = getStringIfConstant(flagsNode, scope);
|
87
87
|
|
88
|
-
if (!flagsNode || (typeof flags === "string" && !flags.includes("u"))) {
|
88
|
+
if (!flagsNode || (typeof flags === "string" && !flags.includes("u") && !flags.includes("v"))) {
|
89
89
|
context.report({
|
90
90
|
messageId: "requireUFlag",
|
91
91
|
node: refNode,
|
@@ -26,8 +26,8 @@ const {
|
|
26
26
|
|
27
27
|
const anyFunctionPattern = /^(?:Function(?:Declaration|Expression)|ArrowFunctionExpression)$/u;
|
28
28
|
const anyLoopPattern = /^(?:DoWhile|For|ForIn|ForOf|While)Statement$/u;
|
29
|
+
const arrayMethodWithThisArgPattern = /^(?:every|filter|find(?:Last)?(?:Index)?|flatMap|forEach|map|some)$/u;
|
29
30
|
const arrayOrTypedArrayPattern = /Array$/u;
|
30
|
-
const arrayMethodPattern = /^(?:every|filter|find|findIndex|forEach|map|some)$/u;
|
31
31
|
const bindOrCallOrApplyPattern = /^(?:bind|call|apply)$/u;
|
32
32
|
const thisTagPattern = /^[\s*]*@this/mu;
|
33
33
|
|
@@ -467,12 +467,12 @@ function isArrayFromMethod(node) {
|
|
467
467
|
}
|
468
468
|
|
469
469
|
/**
|
470
|
-
* Checks whether or not a node is a method which
|
470
|
+
* Checks whether or not a node is a method which expects a function as a first argument, and `thisArg` as a second argument.
|
471
471
|
* @param {ASTNode} node A node to check.
|
472
|
-
* @returns {boolean} Whether or not the node is a method which
|
472
|
+
* @returns {boolean} Whether or not the node is a method which expects a function as a first argument, and `thisArg` as a second argument.
|
473
473
|
*/
|
474
474
|
function isMethodWhichHasThisArg(node) {
|
475
|
-
return isSpecificMemberAccess(node, null,
|
475
|
+
return isSpecificMemberAccess(node, null, arrayMethodWithThisArgPattern);
|
476
476
|
}
|
477
477
|
|
478
478
|
/**
|
@@ -8,7 +8,7 @@
|
|
8
8
|
|
9
9
|
const { RegExpValidator } = require("@eslint-community/regexpp");
|
10
10
|
|
11
|
-
const REGEXPP_LATEST_ECMA_VERSION =
|
11
|
+
const REGEXPP_LATEST_ECMA_VERSION = 2024;
|
12
12
|
|
13
13
|
/**
|
14
14
|
* Checks if the given regular expression pattern would be valid with the `u` flag.
|
@@ -28,7 +28,7 @@ function isValidWithUnicodeFlag(ecmaVersion, pattern) {
|
|
28
28
|
});
|
29
29
|
|
30
30
|
try {
|
31
|
-
validator.validatePattern(pattern, void 0, void 0, /* uFlag = */ true);
|
31
|
+
validator.validatePattern(pattern, void 0, void 0, { unicode: /* uFlag = */ true });
|
32
32
|
} catch {
|
33
33
|
return false;
|
34
34
|
}
|
@@ -0,0 +1,98 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
/* eslint consistent-return: 0 -- no default case */
|
4
|
+
|
5
|
+
const messages = {
|
6
|
+
|
7
|
+
env: `
|
8
|
+
A config object is using the "env" key, which is not supported in flat config system.
|
9
|
+
|
10
|
+
Flat config uses "languageOptions.globals" to define global variables for your files.
|
11
|
+
|
12
|
+
Please see the following page for information on how to convert your config object into the correct format:
|
13
|
+
https://eslint.org/docs/latest/use/configure/migration-guide#configuring-language-options
|
14
|
+
`,
|
15
|
+
|
16
|
+
extends: `
|
17
|
+
A config object is using the "extends" key, which is not supported in flat config system.
|
18
|
+
|
19
|
+
Instead of "extends", you can include config objects that you'd like to extend from directly in the flat config array.
|
20
|
+
|
21
|
+
Please see the following page for more information:
|
22
|
+
https://eslint.org/docs/latest/use/configure/migration-guide#predefined-configs
|
23
|
+
`,
|
24
|
+
|
25
|
+
globals: `
|
26
|
+
A config object is using the "globals" key, which is not supported in flat config system.
|
27
|
+
|
28
|
+
Flat config uses "languageOptions.globals" to define global variables for your files.
|
29
|
+
|
30
|
+
Please see the following page for information on how to convert your config object into the correct format:
|
31
|
+
https://eslint.org/docs/latest/use/configure/migration-guide#configuring-language-options
|
32
|
+
`,
|
33
|
+
|
34
|
+
ignorePatterns: `
|
35
|
+
A config object is using the "ignorePatterns" key, which is not supported in flat config system.
|
36
|
+
|
37
|
+
Flat config uses "ignores" to specify files to ignore.
|
38
|
+
|
39
|
+
Please see the following page for information on how to convert your config object into the correct format:
|
40
|
+
https://eslint.org/docs/latest/use/configure/migration-guide#ignoring-files
|
41
|
+
`,
|
42
|
+
|
43
|
+
noInlineConfig: `
|
44
|
+
A config object is using the "noInlineConfig" key, which is not supported in flat config system.
|
45
|
+
|
46
|
+
Flat config uses "linterOptions.noInlineConfig" to specify files to ignore.
|
47
|
+
|
48
|
+
Please see the following page for information on how to convert your config object into the correct format:
|
49
|
+
https://eslint.org/docs/latest/use/configure/migration-guide#linter-options
|
50
|
+
`,
|
51
|
+
|
52
|
+
overrides: `
|
53
|
+
A config object is using the "overrides" key, which is not supported in flat config system.
|
54
|
+
|
55
|
+
Flat config is an array that acts like the eslintrc "overrides" array.
|
56
|
+
|
57
|
+
Please see the following page for information on how to convert your config object into the correct format:
|
58
|
+
https://eslint.org/docs/latest/use/configure/migration-guide#glob-based-configs
|
59
|
+
`,
|
60
|
+
|
61
|
+
parser: `
|
62
|
+
A config object is using the "parser" key, which is not supported in flat config system.
|
63
|
+
|
64
|
+
Flat config uses "languageOptions.parser" to override the default parser.
|
65
|
+
|
66
|
+
Please see the following page for information on how to convert your config object into the correct format:
|
67
|
+
https://eslint.org/docs/latest/use/configure/migration-guide#custom-parsers
|
68
|
+
`,
|
69
|
+
|
70
|
+
parserOptions: `
|
71
|
+
A config object is using the "parserOptions" key, which is not supported in flat config system.
|
72
|
+
|
73
|
+
Flat config uses "languageOptions.parserOptions" to specify parser options.
|
74
|
+
|
75
|
+
Please see the following page for information on how to convert your config object into the correct format:
|
76
|
+
https://eslint.org/docs/latest/use/configure/migration-guide#configuring-language-options
|
77
|
+
`,
|
78
|
+
|
79
|
+
reportUnusedDisableDirectives: `
|
80
|
+
A config object is using the "reportUnusedDisableDirectives" key, which is not supported in flat config system.
|
81
|
+
|
82
|
+
Flat config uses "linterOptions.reportUnusedDisableDirectives" to specify files to ignore.
|
83
|
+
|
84
|
+
Please see the following page for information on how to convert your config object into the correct format:
|
85
|
+
https://eslint.org/docs/latest/use/configure/migration-guide#linter-options
|
86
|
+
`,
|
87
|
+
|
88
|
+
root: `
|
89
|
+
A config object is using the "root" key, which is not supported in flat config system.
|
90
|
+
|
91
|
+
Flat configs always act as if they are the root config file, so this key can be safely removed.
|
92
|
+
`
|
93
|
+
};
|
94
|
+
|
95
|
+
module.exports = function({ key }) {
|
96
|
+
|
97
|
+
return messages[key].trim();
|
98
|
+
};
|
@@ -0,0 +1,24 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
module.exports = function({ plugins }) {
|
4
|
+
|
5
|
+
const isArrayOfStrings = typeof plugins[0] === "string";
|
6
|
+
|
7
|
+
return `
|
8
|
+
A config object has a "plugins" key defined as an array${isArrayOfStrings ? " of strings" : ""}.
|
9
|
+
|
10
|
+
Flat config requires "plugins" to be an object in this form:
|
11
|
+
|
12
|
+
{
|
13
|
+
plugins: {
|
14
|
+
${isArrayOfStrings && plugins[0] ? plugins[0] : "namespace"}: pluginObject
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
Please see the following page for information on how to convert your config object into the correct format:
|
19
|
+
https://eslint.org/docs/latest/use/configure/migration-guide#importing-plugins-and-custom-parsers
|
20
|
+
|
21
|
+
If you're using a shareable config that you cannot rewrite in flat config format, then use the compatibility utility:
|
22
|
+
https://eslint.org/docs/latest/use/configure/migration-guide#using-eslintrc-configs-in-flat-config
|
23
|
+
`;
|
24
|
+
};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "eslint",
|
3
|
-
"version": "8.
|
3
|
+
"version": "8.47.0",
|
4
4
|
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
|
5
5
|
"description": "An AST-based pattern checker for JavaScript.",
|
6
6
|
"bin": {
|
@@ -61,21 +61,21 @@
|
|
61
61
|
"bugs": "https://github.com/eslint/eslint/issues/",
|
62
62
|
"dependencies": {
|
63
63
|
"@eslint-community/eslint-utils": "^4.2.0",
|
64
|
-
"@eslint-community/regexpp": "^4.
|
65
|
-
"@eslint/eslintrc": "^2.1.
|
66
|
-
"@eslint/js": "8.
|
64
|
+
"@eslint-community/regexpp": "^4.6.1",
|
65
|
+
"@eslint/eslintrc": "^2.1.2",
|
66
|
+
"@eslint/js": "^8.47.0",
|
67
67
|
"@humanwhocodes/config-array": "^0.11.10",
|
68
68
|
"@humanwhocodes/module-importer": "^1.0.1",
|
69
69
|
"@nodelib/fs.walk": "^1.2.8",
|
70
|
-
"ajv": "^6.
|
70
|
+
"ajv": "^6.12.4",
|
71
71
|
"chalk": "^4.0.0",
|
72
72
|
"cross-spawn": "^7.0.2",
|
73
73
|
"debug": "^4.3.2",
|
74
74
|
"doctrine": "^3.0.0",
|
75
75
|
"escape-string-regexp": "^4.0.0",
|
76
|
-
"eslint-scope": "^7.2.
|
77
|
-
"eslint-visitor-keys": "^3.4.
|
78
|
-
"espree": "^9.6.
|
76
|
+
"eslint-scope": "^7.2.2",
|
77
|
+
"eslint-visitor-keys": "^3.4.3",
|
78
|
+
"espree": "^9.6.1",
|
79
79
|
"esquery": "^1.4.2",
|
80
80
|
"esutils": "^2.0.2",
|
81
81
|
"fast-deep-equal": "^3.1.3",
|