eslint 9.0.0-alpha.0 → 9.0.0-alpha.1

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.
Files changed (85) hide show
  1. package/README.md +5 -0
  2. package/conf/ecma-version.js +16 -0
  3. package/lib/cli-engine/cli-engine.js +1 -1
  4. package/lib/cli-engine/lint-result-cache.js +2 -2
  5. package/lib/cli.js +14 -16
  6. package/lib/linter/apply-disable-directives.js +2 -2
  7. package/lib/linter/code-path-analysis/code-path.js +5 -19
  8. package/lib/linter/code-path-analysis/fork-context.js +1 -1
  9. package/lib/linter/config-comment-parser.js +7 -10
  10. package/lib/linter/linter.js +105 -4
  11. package/lib/linter/report-translator.js +2 -2
  12. package/lib/linter/source-code-fixer.js +1 -1
  13. package/lib/rule-tester/rule-tester.js +1 -26
  14. package/lib/rules/array-bracket-newline.js +1 -1
  15. package/lib/rules/array-bracket-spacing.js +1 -1
  16. package/lib/rules/block-scoped-var.js +1 -1
  17. package/lib/rules/callback-return.js +2 -2
  18. package/lib/rules/comma-dangle.js +1 -1
  19. package/lib/rules/comma-style.js +2 -2
  20. package/lib/rules/complexity.js +1 -1
  21. package/lib/rules/constructor-super.js +1 -1
  22. package/lib/rules/default-case.js +1 -1
  23. package/lib/rules/eol-last.js +2 -2
  24. package/lib/rules/function-paren-newline.js +2 -2
  25. package/lib/rules/indent-legacy.js +5 -5
  26. package/lib/rules/indent.js +5 -5
  27. package/lib/rules/index.js +1 -0
  28. package/lib/rules/key-spacing.js +2 -2
  29. package/lib/rules/line-comment-position.js +1 -1
  30. package/lib/rules/lines-around-directive.js +2 -2
  31. package/lib/rules/max-depth.js +1 -1
  32. package/lib/rules/max-len.js +3 -3
  33. package/lib/rules/max-lines.js +3 -3
  34. package/lib/rules/max-nested-callbacks.js +1 -1
  35. package/lib/rules/max-params.js +1 -1
  36. package/lib/rules/max-statements.js +1 -1
  37. package/lib/rules/multiline-comment-style.js +7 -7
  38. package/lib/rules/new-cap.js +1 -1
  39. package/lib/rules/newline-after-var.js +1 -1
  40. package/lib/rules/newline-before-return.js +1 -1
  41. package/lib/rules/no-constant-binary-expression.js +5 -5
  42. package/lib/rules/no-constructor-return.js +1 -1
  43. package/lib/rules/no-dupe-class-members.js +2 -2
  44. package/lib/rules/no-else-return.js +1 -1
  45. package/lib/rules/no-empty-function.js +2 -2
  46. package/lib/rules/no-fallthrough.js +1 -1
  47. package/lib/rules/no-inner-declarations.js +22 -1
  48. package/lib/rules/no-invalid-this.js +1 -1
  49. package/lib/rules/no-lone-blocks.js +2 -2
  50. package/lib/rules/no-loss-of-precision.js +1 -1
  51. package/lib/rules/no-misleading-character-class.js +174 -65
  52. package/lib/rules/no-multiple-empty-lines.js +1 -1
  53. package/lib/rules/no-restricted-globals.js +1 -1
  54. package/lib/rules/no-restricted-imports.js +2 -2
  55. package/lib/rules/no-restricted-modules.js +2 -2
  56. package/lib/rules/no-return-await.js +1 -1
  57. package/lib/rules/no-trailing-spaces.js +2 -3
  58. package/lib/rules/no-unneeded-ternary.js +1 -1
  59. package/lib/rules/no-unsafe-optional-chaining.js +1 -1
  60. package/lib/rules/no-unused-vars.js +6 -8
  61. package/lib/rules/no-useless-assignment.js +566 -0
  62. package/lib/rules/no-useless-backreference.js +1 -1
  63. package/lib/rules/object-curly-spacing.js +3 -3
  64. package/lib/rules/object-property-newline.js +1 -1
  65. package/lib/rules/one-var.js +5 -5
  66. package/lib/rules/padded-blocks.js +7 -7
  67. package/lib/rules/prefer-arrow-callback.js +3 -3
  68. package/lib/rules/prefer-reflect.js +1 -1
  69. package/lib/rules/prefer-regex-literals.js +1 -1
  70. package/lib/rules/prefer-template.js +1 -1
  71. package/lib/rules/radix.js +2 -2
  72. package/lib/rules/semi-style.js +1 -1
  73. package/lib/rules/sort-imports.js +1 -1
  74. package/lib/rules/sort-keys.js +1 -1
  75. package/lib/rules/sort-vars.js +1 -1
  76. package/lib/rules/space-unary-ops.js +1 -1
  77. package/lib/rules/strict.js +1 -1
  78. package/lib/rules/utils/ast-utils.js +7 -7
  79. package/lib/rules/yield-star-spacing.js +1 -1
  80. package/lib/source-code/source-code.js +4 -4
  81. package/lib/source-code/token-store/index.js +2 -2
  82. package/package.json +4 -4
  83. package/conf/config-schema.js +0 -93
  84. package/lib/shared/config-validator.js +0 -380
  85. package/lib/shared/relative-module-resolver.js +0 -50
@@ -62,7 +62,6 @@ function *iterateCharacterSequence(nodes) {
62
62
  }
63
63
  }
64
64
 
65
-
66
65
  /**
67
66
  * Checks whether the given character node is a Unicode code point escape or not.
68
67
  * @param {Character} char the character node to check.
@@ -73,80 +72,126 @@ function isUnicodeCodePointEscape(char) {
73
72
  }
74
73
 
75
74
  /**
76
- * Each function returns `true` if it detects that kind of problem.
77
- * @type {Record<string, (chars: Character[]) => boolean>}
75
+ * Each function returns matched characters if it detects that kind of problem.
76
+ * @type {Record<string, (chars: Character[]) => IterableIterator<Character[]>>}
78
77
  */
79
- const hasCharacterSequence = {
80
- surrogatePairWithoutUFlag(chars) {
81
- return chars.some((c, i) => {
82
- if (i === 0) {
83
- return false;
78
+ const findCharacterSequences = {
79
+ *surrogatePairWithoutUFlag(chars) {
80
+ for (const [index, char] of chars.entries()) {
81
+ if (index === 0) {
82
+ continue;
83
+ }
84
+ const previous = chars[index - 1];
85
+
86
+ if (
87
+ isSurrogatePair(previous.value, char.value) &&
88
+ !isUnicodeCodePointEscape(previous) &&
89
+ !isUnicodeCodePointEscape(char)
90
+ ) {
91
+ yield [previous, char];
84
92
  }
85
- const c1 = chars[i - 1];
86
-
87
- return (
88
- isSurrogatePair(c1.value, c.value) &&
89
- !isUnicodeCodePointEscape(c1) &&
90
- !isUnicodeCodePointEscape(c)
91
- );
92
- });
93
+ }
93
94
  },
94
95
 
95
- surrogatePair(chars) {
96
- return chars.some((c, i) => {
97
- if (i === 0) {
98
- return false;
96
+ *surrogatePair(chars) {
97
+ for (const [index, char] of chars.entries()) {
98
+ if (index === 0) {
99
+ continue;
99
100
  }
100
- const c1 = chars[i - 1];
101
+ const previous = chars[index - 1];
101
102
 
102
- return (
103
- isSurrogatePair(c1.value, c.value) &&
103
+ if (
104
+ isSurrogatePair(previous.value, char.value) &&
104
105
  (
105
- isUnicodeCodePointEscape(c1) ||
106
- isUnicodeCodePointEscape(c)
106
+ isUnicodeCodePointEscape(previous) ||
107
+ isUnicodeCodePointEscape(char)
107
108
  )
108
- );
109
- });
109
+ ) {
110
+ yield [previous, char];
111
+ }
112
+ }
110
113
  },
111
114
 
112
- combiningClass(chars) {
113
- return chars.some((c, i) => (
114
- i !== 0 &&
115
- isCombiningCharacter(c.value) &&
116
- !isCombiningCharacter(chars[i - 1].value)
117
- ));
115
+ *combiningClass(chars) {
116
+ for (const [index, char] of chars.entries()) {
117
+ if (index === 0) {
118
+ continue;
119
+ }
120
+ const previous = chars[index - 1];
121
+
122
+ if (
123
+ isCombiningCharacter(char.value) &&
124
+ !isCombiningCharacter(previous.value)
125
+ ) {
126
+ yield [previous, char];
127
+ }
128
+ }
118
129
  },
119
130
 
120
- emojiModifier(chars) {
121
- return chars.some((c, i) => (
122
- i !== 0 &&
123
- isEmojiModifier(c.value) &&
124
- !isEmojiModifier(chars[i - 1].value)
125
- ));
131
+ *emojiModifier(chars) {
132
+ for (const [index, char] of chars.entries()) {
133
+ if (index === 0) {
134
+ continue;
135
+ }
136
+ const previous = chars[index - 1];
137
+
138
+ if (
139
+ isEmojiModifier(char.value) &&
140
+ !isEmojiModifier(previous.value)
141
+ ) {
142
+ yield [previous, char];
143
+ }
144
+ }
126
145
  },
127
146
 
128
- regionalIndicatorSymbol(chars) {
129
- return chars.some((c, i) => (
130
- i !== 0 &&
131
- isRegionalIndicatorSymbol(c.value) &&
132
- isRegionalIndicatorSymbol(chars[i - 1].value)
133
- ));
147
+ *regionalIndicatorSymbol(chars) {
148
+ for (const [index, char] of chars.entries()) {
149
+ if (index === 0) {
150
+ continue;
151
+ }
152
+ const previous = chars[index - 1];
153
+
154
+ if (
155
+ isRegionalIndicatorSymbol(char.value) &&
156
+ isRegionalIndicatorSymbol(previous.value)
157
+ ) {
158
+ yield [previous, char];
159
+ }
160
+ }
134
161
  },
135
162
 
136
- zwj(chars) {
137
- const lastIndex = chars.length - 1;
163
+ *zwj(chars) {
164
+ let sequence = null;
138
165
 
139
- return chars.some((c, i) => (
140
- i !== 0 &&
141
- i !== lastIndex &&
142
- c.value === 0x200d &&
143
- chars[i - 1].value !== 0x200d &&
144
- chars[i + 1].value !== 0x200d
145
- ));
166
+ for (const [index, char] of chars.entries()) {
167
+ if (index === 0 || index === chars.length - 1) {
168
+ continue;
169
+ }
170
+ if (
171
+ char.value === 0x200d &&
172
+ chars[index - 1].value !== 0x200d &&
173
+ chars[index + 1].value !== 0x200d
174
+ ) {
175
+ if (sequence) {
176
+ if (sequence.at(-1) === chars[index - 1]) {
177
+ sequence.push(char, chars[index + 1]); // append to the sequence
178
+ } else {
179
+ yield sequence;
180
+ sequence = chars.slice(index - 1, index + 2);
181
+ }
182
+ } else {
183
+ sequence = chars.slice(index - 1, index + 2);
184
+ }
185
+ }
186
+ }
187
+
188
+ if (sequence) {
189
+ yield sequence;
190
+ }
146
191
  }
147
192
  };
148
193
 
149
- const kinds = Object.keys(hasCharacterSequence);
194
+ const kinds = Object.keys(findCharacterSequences);
150
195
 
151
196
  //------------------------------------------------------------------------------
152
197
  // Rule Definition
@@ -181,6 +226,62 @@ module.exports = {
181
226
  const sourceCode = context.sourceCode;
182
227
  const parser = new RegExpParser();
183
228
 
229
+ /**
230
+ * Generates a granular loc for context.report, if directly calculable.
231
+ * @param {Character[]} chars Individual characters being reported on.
232
+ * @param {Node} node Parent string node to report within.
233
+ * @returns {Object | null} Granular loc for context.report, if directly calculable.
234
+ * @see https://github.com/eslint/eslint/pull/17515
235
+ */
236
+ function generateReportLocation(chars, node) {
237
+
238
+ // Limit to to literals and expression-less templates with raw values === their value.
239
+ switch (node.type) {
240
+ case "TemplateLiteral":
241
+ if (node.expressions.length || sourceCode.getText(node).slice(1, -1) !== node.quasis[0].value.cooked) {
242
+ return null;
243
+ }
244
+ break;
245
+
246
+ case "Literal":
247
+ if (typeof node.value === "string" && node.value !== node.raw.slice(1, -1)) {
248
+ return null;
249
+ }
250
+ break;
251
+
252
+ default:
253
+ return null;
254
+ }
255
+
256
+ return {
257
+ start: sourceCode.getLocFromIndex(node.range[0] + 1 + chars[0].start),
258
+ end: sourceCode.getLocFromIndex(node.range[0] + 1 + chars.at(-1).end)
259
+ };
260
+ }
261
+
262
+ /**
263
+ * Finds the report loc(s) for a range of matches.
264
+ * @param {Character[][]} matches Characters that should trigger a report.
265
+ * @param {Node} node The node to report.
266
+ * @returns {Object | null} Node loc(s) for context.report.
267
+ */
268
+ function getNodeReportLocations(matches, node) {
269
+ const locs = [];
270
+
271
+ for (const chars of matches) {
272
+ const loc = generateReportLocation(chars, node);
273
+
274
+ // If a report can't match to a range, don't report any others
275
+ if (!loc) {
276
+ return [node.loc];
277
+ }
278
+
279
+ locs.push(loc);
280
+ }
281
+
282
+ return locs;
283
+ }
284
+
184
285
  /**
185
286
  * Verify a given regular expression.
186
287
  * @param {Node} node The node to report.
@@ -208,21 +309,24 @@ module.exports = {
208
309
  return;
209
310
  }
210
311
 
211
- const foundKinds = new Set();
312
+ const foundKindMatches = new Map();
212
313
 
213
314
  visitRegExpAST(patternNode, {
214
315
  onCharacterClassEnter(ccNode) {
215
316
  for (const chars of iterateCharacterSequence(ccNode.elements)) {
216
317
  for (const kind of kinds) {
217
- if (hasCharacterSequence[kind](chars)) {
218
- foundKinds.add(kind);
318
+ if (foundKindMatches.has(kind)) {
319
+ foundKindMatches.get(kind).push(...findCharacterSequences[kind](chars));
320
+ } else {
321
+ foundKindMatches.set(kind, [...findCharacterSequences[kind](chars)]);
219
322
  }
323
+
220
324
  }
221
325
  }
222
326
  }
223
327
  });
224
328
 
225
- for (const kind of foundKinds) {
329
+ for (const [kind, matches] of foundKindMatches) {
226
330
  let suggest;
227
331
 
228
332
  if (kind === "surrogatePairWithoutUFlag") {
@@ -232,11 +336,16 @@ module.exports = {
232
336
  }];
233
337
  }
234
338
 
235
- context.report({
236
- node,
237
- messageId: kind,
238
- suggest
239
- });
339
+ const locs = getNodeReportLocations(matches, node);
340
+
341
+ for (const loc of locs) {
342
+ context.report({
343
+ node,
344
+ loc,
345
+ messageId: kind,
346
+ suggest
347
+ });
348
+ }
240
349
  }
241
350
  }
242
351
 
@@ -267,7 +376,7 @@ module.exports = {
267
376
  const flags = getStringIfConstant(flagsNode, scope);
268
377
 
269
378
  if (typeof pattern === "string") {
270
- verify(refNode, pattern, flags || "", fixer => {
379
+ verify(patternNode, pattern, flags || "", fixer => {
271
380
 
272
381
  if (!isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, pattern)) {
273
382
  return null;
@@ -70,7 +70,7 @@ module.exports = {
70
70
  const sourceCode = context.sourceCode;
71
71
 
72
72
  // Swallow the final newline, as some editors add it automatically and we don't want it to cause an issue
73
- const allLines = sourceCode.lines[sourceCode.lines.length - 1] === "" ? sourceCode.lines.slice(0, -1) : sourceCode.lines;
73
+ const allLines = sourceCode.lines.at(-1) === "" ? sourceCode.lines.slice(0, -1) : sourceCode.lines;
74
74
  const templateLiteralLines = new Set();
75
75
 
76
76
  //--------------------------------------------------------------------------
@@ -97,7 +97,7 @@ module.exports = {
97
97
  * @private
98
98
  */
99
99
  function isRestricted(name) {
100
- return Object.prototype.hasOwnProperty.call(restrictedGlobalMessages, name);
100
+ return Object.hasOwn(restrictedGlobalMessages, name);
101
101
  }
102
102
 
103
103
  return {
@@ -158,7 +158,7 @@ module.exports = {
158
158
  const options = Array.isArray(context.options) ? context.options : [];
159
159
  const isPathAndPatternsObject =
160
160
  typeof options[0] === "object" &&
161
- (Object.prototype.hasOwnProperty.call(options[0], "paths") || Object.prototype.hasOwnProperty.call(options[0], "patterns"));
161
+ (Object.hasOwn(options[0], "paths") || Object.hasOwn(options[0], "patterns"));
162
162
 
163
163
  const restrictedPaths = (isPathAndPatternsObject ? options[0].paths : context.options) || [];
164
164
  const restrictedPathMessages = restrictedPaths.reduce((memo, importSource) => {
@@ -203,7 +203,7 @@ module.exports = {
203
203
  * @private
204
204
  */
205
205
  function checkRestrictedPathAndReport(importSource, importNames, node) {
206
- if (!Object.prototype.hasOwnProperty.call(restrictedPathMessages, importSource)) {
206
+ if (!Object.hasOwn(restrictedPathMessages, importSource)) {
207
207
  return;
208
208
  }
209
209
 
@@ -90,7 +90,7 @@ module.exports = {
90
90
  const options = Array.isArray(context.options) ? context.options : [];
91
91
  const isPathAndPatternsObject =
92
92
  typeof options[0] === "object" &&
93
- (Object.prototype.hasOwnProperty.call(options[0], "paths") || Object.prototype.hasOwnProperty.call(options[0], "patterns"));
93
+ (Object.hasOwn(options[0], "paths") || Object.hasOwn(options[0], "patterns"));
94
94
 
95
95
  const restrictedPaths = (isPathAndPatternsObject ? options[0].paths : context.options) || [];
96
96
  const restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
@@ -178,7 +178,7 @@ module.exports = {
178
178
  * @private
179
179
  */
180
180
  function isRestrictedPath(name) {
181
- return Object.prototype.hasOwnProperty.call(restrictedPathMessages, name);
181
+ return Object.hasOwn(restrictedPathMessages, name);
182
182
  }
183
183
 
184
184
  return {
@@ -118,7 +118,7 @@ module.exports = {
118
118
  if (node.parent.type === "LogicalExpression" && node === node.parent.right) {
119
119
  return isInTailCallPosition(node.parent);
120
120
  }
121
- if (node.parent.type === "SequenceExpression" && node === node.parent.expressions[node.parent.expressions.length - 1]) {
121
+ if (node.parent.type === "SequenceExpression" && node === node.parent.expressions.at(-1)) {
122
122
  return isInTailCallPosition(node.parent);
123
123
  }
124
124
  return false;
@@ -129,8 +129,7 @@ module.exports = {
129
129
  comments = sourceCode.getAllComments(),
130
130
  commentLineNumbers = getCommentLineNumbers(comments);
131
131
 
132
- let totalLength = 0,
133
- fixRange = [];
132
+ let totalLength = 0;
134
133
 
135
134
  for (let i = 0, ii = lines.length; i < ii; i++) {
136
135
  const lineNumber = i + 1;
@@ -177,7 +176,7 @@ module.exports = {
177
176
  continue;
178
177
  }
179
178
 
180
- fixRange = [rangeStart, rangeEnd];
179
+ const fixRange = [rangeStart, rangeEnd];
181
180
 
182
181
  if (!ignoreComments || !commentLineNumbers.has(lineNumber)) {
183
182
  report(node, location, fixRange);
@@ -76,7 +76,7 @@ module.exports = {
76
76
  * @returns {string} A string representing an inverted expression
77
77
  */
78
78
  function invertExpression(node) {
79
- if (node.type === "BinaryExpression" && Object.prototype.hasOwnProperty.call(OPERATOR_INVERSES, node.operator)) {
79
+ if (node.type === "BinaryExpression" && Object.hasOwn(OPERATOR_INVERSES, node.operator)) {
80
80
  const operatorToken = sourceCode.getFirstTokenBetween(
81
81
  node.left,
82
82
  node.right,
@@ -94,7 +94,7 @@ module.exports = {
94
94
  break;
95
95
  case "SequenceExpression":
96
96
  checkUndefinedShortCircuit(
97
- node.expressions[node.expressions.length - 1],
97
+ node.expressions.at(-1),
98
98
  reportFunc
99
99
  );
100
100
  break;
@@ -141,7 +141,7 @@ module.exports = {
141
141
  } else if (defType === "Parameter" && config.argsIgnorePattern) {
142
142
  type = "args";
143
143
  pattern = config.argsIgnorePattern.toString();
144
- } else if (defType !== "Parameter" && config.varsIgnorePattern) {
144
+ } else if (defType !== "Parameter" && defType !== "CatchClause" && config.varsIgnorePattern) {
145
145
  type = "vars";
146
146
  pattern = config.varsIgnorePattern.toString();
147
147
  }
@@ -218,7 +218,7 @@ module.exports = {
218
218
  function hasRestSibling(node) {
219
219
  return node.type === "Property" &&
220
220
  node.parent.type === "ObjectPattern" &&
221
- REST_PROPERTY_TYPE.test(node.parent.properties[node.parent.properties.length - 1].type);
221
+ REST_PROPERTY_TYPE.test(node.parent.properties.at(-1).type);
222
222
  }
223
223
 
224
224
  /**
@@ -323,7 +323,7 @@ module.exports = {
323
323
  }
324
324
 
325
325
  if (parent.type === "SequenceExpression") {
326
- const isLastExpression = parent.expressions[parent.expressions.length - 1] === node;
326
+ const isLastExpression = parent.expressions.at(-1) === node;
327
327
 
328
328
  if (!isLastExpression) {
329
329
  return true;
@@ -392,7 +392,7 @@ module.exports = {
392
392
  while (parent && isInside(parent, rhsNode)) {
393
393
  switch (parent.type) {
394
394
  case "SequenceExpression":
395
- if (parent.expressions[parent.expressions.length - 1] !== node) {
395
+ if (parent.expressions.at(-1) !== node) {
396
396
  return false;
397
397
  }
398
398
  break;
@@ -623,9 +623,7 @@ module.exports = {
623
623
  if (config.caughtErrorsIgnorePattern && config.caughtErrorsIgnorePattern.test(def.name.name)) {
624
624
  continue;
625
625
  }
626
- }
627
-
628
- if (type === "Parameter") {
626
+ } else if (type === "Parameter") {
629
627
 
630
628
  // skip any setter argument
631
629
  if ((def.node.parent.type === "Property" || def.node.parent.type === "MethodDefinition") && def.node.parent.kind === "set") {
@@ -688,7 +686,7 @@ module.exports = {
688
686
  let referenceToReport;
689
687
 
690
688
  if (writeReferences.length > 0) {
691
- referenceToReport = writeReferences[writeReferences.length - 1];
689
+ referenceToReport = writeReferences.at(-1);
692
690
  }
693
691
 
694
692
  context.report({