eslint 8.44.0 → 8.46.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 CHANGED
@@ -284,7 +284,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
284
284
  <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
285
  <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
286
  <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://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://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://opensource.mercedes-benz.com/"><img src="https://avatars.githubusercontent.com/u/34240465?v=4" alt="Mercedes-Benz Group" 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>
287
+ <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://iboysoft.com/"><img src="https://images.opencollective.com/iboysoft-software/7f9d60e/avatar.png" alt="iBoysoft" height="32"></a> <a href="https://www.bairesdev.com/sponsoring-open-source-projects/"><img src="https://images.opencollective.com/bairesdev/48bb773/logo.png" alt="BairesDev" 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
288
  <!--sponsorsend-->
289
289
 
290
290
  ## 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: {
@@ -576,7 +576,6 @@ class FlatESLint {
576
576
  cacheFilePath,
577
577
  lintResultCache,
578
578
  defaultConfigs,
579
- defaultIgnores: () => false,
580
579
  configs: null
581
580
  });
582
581
 
@@ -715,12 +714,10 @@ class FlatESLint {
715
714
  }
716
715
  const rule = getRuleFromConfig(ruleId, config);
717
716
 
718
- // ensure the rule exists
719
- if (!rule) {
720
- throw new TypeError(`Could not find the rule "${ruleId}".`);
717
+ // ignore unknown rules
718
+ if (rule) {
719
+ resultRules.set(ruleId, rule);
721
720
  }
722
-
723
- resultRules.set(ruleId, rule);
724
721
  }
725
722
  }
726
723
 
@@ -100,6 +100,22 @@ function normalizeReportLoc(descriptor) {
100
100
  return descriptor.node.loc;
101
101
  }
102
102
 
103
+ /**
104
+ * Clones the given fix object.
105
+ * @param {Fix|null} fix The fix to clone.
106
+ * @returns {Fix|null} Deep cloned fix object or `null` if `null` or `undefined` was passed in.
107
+ */
108
+ function cloneFix(fix) {
109
+ if (!fix) {
110
+ return null;
111
+ }
112
+
113
+ return {
114
+ range: [fix.range[0], fix.range[1]],
115
+ text: fix.text
116
+ };
117
+ }
118
+
103
119
  /**
104
120
  * Check that a fix has a valid range.
105
121
  * @param {Fix|null} fix The fix to validate.
@@ -137,7 +153,7 @@ function mergeFixes(fixes, sourceCode) {
137
153
  return null;
138
154
  }
139
155
  if (fixes.length === 1) {
140
- return fixes[0];
156
+ return cloneFix(fixes[0]);
141
157
  }
142
158
 
143
159
  fixes.sort(compareFixesByRange);
@@ -183,7 +199,7 @@ function normalizeFixes(descriptor, sourceCode) {
183
199
  }
184
200
 
185
201
  assertValidFix(fix);
186
- return fix;
202
+ return cloneFix(fix);
187
203
  }
188
204
 
189
205
  /**
@@ -1250,7 +1250,7 @@ module.exports = {
1250
1250
 
1251
1251
  IfStatement(node) {
1252
1252
  addBlocklessNodeIndent(node.consequent);
1253
- if (node.alternate && node.alternate.type !== "IfStatement") {
1253
+ if (node.alternate) {
1254
1254
  addBlocklessNodeIndent(node.alternate);
1255
1255
  }
1256
1256
  },
@@ -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._source = regexpStr;
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
- * plain-English description of the following regexp:
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
- if (!regex.test(node.regex.pattern)) {
49
- context.report({ node, messageId: "unexpected" });
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
- if (node.properties.length === 0) {
33
- context.report({ node, messageId: "unexpected", data: { type: "object" } });
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 = /[dgimsuy]/gu;
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 {boolean} uFlag The Unicode flag.
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, uFlag) {
116
+ function validateRegExpPattern(pattern, flags) {
115
117
  try {
116
- validator.validatePattern(pattern, undefined1, undefined1, uFlag);
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
- ? validateRegExpPattern(pattern, true) && validateRegExpPattern(pattern, false)
170
- : validateRegExpPattern(pattern, flags.includes("u"))
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
- flags.includes("u")
150
+ {
151
+ unicode: flags.includes("u"),
152
+ unicodeSets: flags.includes("v")
153
+ }
148
154
  );
149
155
  } catch {
150
156
 
@@ -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
- const flags = isString(flagsNode) ? flagsNode.value : "";
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
- * Parses a regular expression into a list of characters with character class info.
33
- * @param {string} regExpText The raw text used to create the regular expression
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
- function parseRegExp(regExpText) {
50
- const charList = [];
40
+ const REGEX_CLASSSET_CHARACTER_ESCAPES = union(REGEX_GENERAL_ESCAPES, new Set("q/[{}|()-"));
51
41
 
52
- regExpText.split("").reduce((state, char, index) => {
53
- if (!state.escapeNextChar) {
54
- if (char === "\\") {
55
- return Object.assign(state, { escapeNextChar: true });
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 = {
@@ -94,6 +61,7 @@ module.exports = {
94
61
  messages: {
95
62
  unnecessaryEscape: "Unnecessary escape character: \\{{character}}.",
96
63
  removeEscape: "Remove the `\\`. This maintains the current functionality.",
64
+ removeEscapeDoNotKeepSemantics: "Remove the `\\` if it was inserted by mistake.",
97
65
  escapeBackslash: "Replace the `\\` with `\\\\` to include the actual backslash character."
98
66
  },
99
67
 
@@ -102,15 +70,17 @@ module.exports = {
102
70
 
103
71
  create(context) {
104
72
  const sourceCode = context.sourceCode;
73
+ const parser = new RegExpParser();
105
74
 
106
75
  /**
107
76
  * Reports a node
108
77
  * @param {ASTNode} node The node to report
109
78
  * @param {number} startOffset The backslash's offset from the start of the node
110
79
  * @param {string} character The uselessly escaped character (not including the backslash)
80
+ * @param {boolean} [disableEscapeBackslashSuggest] `true` if escapeBackslash suggestion should be turned off.
111
81
  * @returns {void}
112
82
  */
113
- function report(node, startOffset, character) {
83
+ function report(node, startOffset, character, disableEscapeBackslashSuggest) {
114
84
  const rangeStart = node.range[0] + startOffset;
115
85
  const range = [rangeStart, rangeStart + 1];
116
86
  const start = sourceCode.getLocFromIndex(rangeStart);
@@ -125,17 +95,24 @@ module.exports = {
125
95
  data: { character },
126
96
  suggest: [
127
97
  {
128
- messageId: "removeEscape",
98
+
99
+ // Removing unnecessary `\` characters in a directive is not guaranteed to maintain functionality.
100
+ messageId: astUtils.isDirective(node.parent)
101
+ ? "removeEscapeDoNotKeepSemantics" : "removeEscape",
129
102
  fix(fixer) {
130
103
  return fixer.removeRange(range);
131
104
  }
132
105
  },
133
- {
134
- messageId: "escapeBackslash",
135
- fix(fixer) {
136
- return fixer.insertTextBeforeRange(range, "\\");
137
- }
138
- }
106
+ ...disableEscapeBackslashSuggest
107
+ ? []
108
+ : [
109
+ {
110
+ messageId: "escapeBackslash",
111
+ fix(fixer) {
112
+ return fixer.insertTextBeforeRange(range, "\\");
113
+ }
114
+ }
115
+ ]
139
116
  ]
140
117
  });
141
118
  }
@@ -178,6 +155,133 @@ module.exports = {
178
155
  }
179
156
  }
180
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
+
181
285
  /**
182
286
  * Checks if a node has an escape.
183
287
  * @param {ASTNode} node node to check.
@@ -216,32 +320,7 @@ module.exports = {
216
320
  validateString(node, match);
217
321
  }
218
322
  } else if (node.regex) {
219
- parseRegExp(node.regex.pattern)
220
-
221
- /*
222
- * The '-' character is a special case, because it's only valid to escape it if it's in a character
223
- * class, and is not at either edge of the character class. To account for this, don't consider '-'
224
- * characters to be valid in general, and filter out '-' characters that appear in the middle of a
225
- * character class.
226
- */
227
- .filter(charInfo => !(charInfo.text === "-" && charInfo.inCharClass && !charInfo.startsCharClass && !charInfo.endsCharClass))
228
-
229
- /*
230
- * The '^' character is also a special case; it must always be escaped outside of character classes, but
231
- * it only needs to be escaped in character classes if it's at the beginning of the character class. To
232
- * account for this, consider it to be a valid escape character outside of character classes, and filter
233
- * out '^' characters that appear at the start of a character class.
234
- */
235
- .filter(charInfo => !(charInfo.text === "^" && charInfo.startsCharClass))
236
-
237
- // Filter out characters that aren't escaped.
238
- .filter(charInfo => charInfo.escaped)
239
-
240
- // Filter out characters that are valid to escape, based on their position in the regular expression.
241
- .filter(charInfo => !(charInfo.inCharClass ? REGEX_GENERAL_ESCAPES : REGEX_NON_CHARCLASS_ESCAPES).has(charInfo.text))
242
-
243
- // Report all the remaining characters.
244
- .forEach(charInfo => report(node, charInfo.index, charInfo.text));
323
+ validateRegExp(node);
245
324
  }
246
325
 
247
326
  }
@@ -130,42 +130,6 @@ function isBlockLikeStatement(sourceCode, node) {
130
130
  );
131
131
  }
132
132
 
133
- /**
134
- * Check whether the given node is a directive or not.
135
- * @param {ASTNode} node The node to check.
136
- * @param {SourceCode} sourceCode The source code object to get tokens.
137
- * @returns {boolean} `true` if the node is a directive.
138
- */
139
- function isDirective(node, sourceCode) {
140
- return (
141
- astUtils.isTopLevelExpressionStatement(node) &&
142
- node.expression.type === "Literal" &&
143
- typeof node.expression.value === "string" &&
144
- !astUtils.isParenthesised(sourceCode, node.expression)
145
- );
146
- }
147
-
148
- /**
149
- * Check whether the given node is a part of directive prologue or not.
150
- * @param {ASTNode} node The node to check.
151
- * @param {SourceCode} sourceCode The source code object to get tokens.
152
- * @returns {boolean} `true` if the node is a part of directive prologue.
153
- */
154
- function isDirectivePrologue(node, sourceCode) {
155
- if (isDirective(node, sourceCode)) {
156
- for (const sibling of node.parent.body) {
157
- if (sibling === node) {
158
- break;
159
- }
160
- if (!isDirective(sibling, sourceCode)) {
161
- return false;
162
- }
163
- }
164
- return true;
165
- }
166
- return false;
167
- }
168
-
169
133
  /**
170
134
  * Gets the actual last token.
171
135
  *
@@ -359,12 +323,10 @@ const StatementTypes = {
359
323
  CJS_IMPORT.test(sourceCode.getText(node.declarations[0].init))
360
324
  },
361
325
  directive: {
362
- test: isDirectivePrologue
326
+ test: astUtils.isDirective
363
327
  },
364
328
  expression: {
365
- test: (node, sourceCode) =>
366
- node.type === "ExpressionStatement" &&
367
- !isDirectivePrologue(node, sourceCode)
329
+ test: node => node.type === "ExpressionStatement" && !astUtils.isDirective(node)
368
330
  },
369
331
  iife: {
370
332
  test: isIIFEStatement
@@ -375,10 +337,10 @@ const StatementTypes = {
375
337
  isBlockLikeStatement(sourceCode, node)
376
338
  },
377
339
  "multiline-expression": {
378
- test: (node, sourceCode) =>
340
+ test: node =>
379
341
  node.loc.start.line !== node.loc.end.line &&
380
342
  node.type === "ExpressionStatement" &&
381
- !isDirectivePrologue(node, sourceCode)
343
+ !astUtils.isDirective(node)
382
344
  },
383
345
 
384
346
  "multiline-const": newMultilineKeywordTester("const"),
@@ -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 {boolean} uFlag Flag indicates whether unicode mode is enabled or not.
115
+ * @param {string|null} flags The regular expression flags to be checked.
116
116
  * @returns {void}
117
117
  */
118
- function checkRegex(pattern, node, regexNode, uFlag) {
118
+ function checkRegex(pattern, node, regexNode, flags) {
119
119
  let ast;
120
120
 
121
121
  try {
122
- ast = parser.parsePattern(pattern, 0, pattern.length, uFlag);
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.includes("u"));
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 && flags.includes("u"));
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, flags ? flags.includes("u") : false);
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, flags ? flags.includes("u") : false);
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,
@@ -1006,6 +1006,15 @@ function isTopLevelExpressionStatement(node) {
1006
1006
 
1007
1007
  }
1008
1008
 
1009
+ /**
1010
+ * Check whether the given node is a part of a directive prologue or not.
1011
+ * @param {ASTNode} node The node to check.
1012
+ * @returns {boolean} `true` if the node is a part of directive prologue.
1013
+ */
1014
+ function isDirective(node) {
1015
+ return node.type === "ExpressionStatement" && typeof node.directive === "string";
1016
+ }
1017
+
1009
1018
  //------------------------------------------------------------------------------
1010
1019
  // Public Interface
1011
1020
  //------------------------------------------------------------------------------
@@ -2158,5 +2167,6 @@ module.exports = {
2158
2167
  getSwitchCaseColonToken,
2159
2168
  getModuleExportName,
2160
2169
  isConstant,
2161
- isTopLevelExpressionStatement
2170
+ isTopLevelExpressionStatement,
2171
+ isDirective
2162
2172
  };
@@ -8,7 +8,7 @@
8
8
 
9
9
  const { RegExpValidator } = require("@eslint-community/regexpp");
10
10
 
11
- const REGEXPP_LATEST_ECMA_VERSION = 2022;
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
  }
@@ -14,6 +14,7 @@
14
14
  const { FileEnumerator } = require("./cli-engine/file-enumerator");
15
15
  const { FlatESLint, shouldUseFlatConfig } = require("./eslint/flat-eslint");
16
16
  const FlatRuleTester = require("./rule-tester/flat-rule-tester");
17
+ const { ESLint } = require("./eslint/eslint");
17
18
 
18
19
  //-----------------------------------------------------------------------------
19
20
  // Exports
@@ -24,5 +25,6 @@ module.exports = {
24
25
  FlatESLint,
25
26
  shouldUseFlatConfig,
26
27
  FlatRuleTester,
27
- FileEnumerator
28
+ FileEnumerator,
29
+ LegacyESLint: ESLint
28
30
  };
@@ -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.44.0",
3
+ "version": "8.46.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.4.0",
65
- "@eslint/eslintrc": "^2.1.0",
66
- "@eslint/js": "8.44.0",
64
+ "@eslint-community/regexpp": "^4.6.1",
65
+ "@eslint/eslintrc": "^2.1.1",
66
+ "@eslint/js": "^8.46.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.10.0",
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.0",
77
- "eslint-visitor-keys": "^3.4.1",
78
- "espree": "^9.6.0",
76
+ "eslint-scope": "^7.2.2",
77
+ "eslint-visitor-keys": "^3.4.2",
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",
@@ -85,7 +85,6 @@
85
85
  "globals": "^13.19.0",
86
86
  "graphemer": "^1.4.0",
87
87
  "ignore": "^5.2.0",
88
- "import-fresh": "^3.0.0",
89
88
  "imurmurhash": "^0.1.4",
90
89
  "is-glob": "^4.0.0",
91
90
  "is-path-inside": "^3.0.3",
@@ -97,7 +96,6 @@
97
96
  "natural-compare": "^1.4.0",
98
97
  "optionator": "^0.9.3",
99
98
  "strip-ansi": "^6.0.1",
100
- "strip-json-comments": "^3.1.0",
101
99
  "text-table": "^0.2.0"
102
100
  },
103
101
  "devDependencies": {
@@ -156,7 +154,6 @@
156
154
  "semver": "^7.5.3",
157
155
  "shelljs": "^0.8.2",
158
156
  "sinon": "^11.0.0",
159
- "temp": "^0.9.0",
160
157
  "webpack": "^5.23.0",
161
158
  "webpack-cli": "^4.5.0",
162
159
  "yorkie": "^2.0.0"