eslint 9.34.0 → 9.36.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
@@ -277,6 +277,11 @@ Nitin Kumar
277
277
  The people who review and fix bugs and help triage issues.
278
278
 
279
279
  <table><tbody><tr><td align="center" valign="top" width="11%">
280
+ <a href="https://github.com/DMartens">
281
+ <img src="https://github.com/DMartens.png?s=75" width="75" height="75" alt="fnx's Avatar"><br />
282
+ fnx
283
+ </a>
284
+ </td><td align="center" valign="top" width="11%">
280
285
  <a href="https://github.com/JoshuaKGoldberg">
281
286
  <img src="https://github.com/JoshuaKGoldberg.png?s=75" width="75" height="75" alt="Josh Goldberg ✨'s Avatar"><br />
282
287
  Josh Goldberg ✨
@@ -328,7 +333,7 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
328
333
  <p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="128"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="128"></a></p><h3>Gold Sponsors</h3>
329
334
  <p><a href="https://qlty.sh/"><img src="https://images.opencollective.com/qltysh/33d157d/logo.png" alt="Qlty Software" height="96"></a> <a href="https://trunk.io/"><img src="https://images.opencollective.com/trunkio/fb92d60/avatar.png" alt="trunk.io" height="96"></a> <a href="https://shopify.engineering/"><img src="https://avatars.githubusercontent.com/u/8085" alt="Shopify" height="96"></a></p><h3>Silver Sponsors</h3>
330
335
  <p><a href="https://vite.dev/"><img src="https://images.opencollective.com/vite/e6d15e1/logo.png" alt="Vite" 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" alt="American Express" height="64"></a> <a href="https://stackblitz.com"><img src="https://avatars.githubusercontent.com/u/28635252" alt="StackBlitz" height="64"></a></p><h3>Bronze Sponsors</h3>
331
- <p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://sentry.io"><img src="https://github.com/getsentry.png" alt="Sentry" 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" 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://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104" alt="Nx" height="32"></a> <a href="https://opensource.mercedes-benz.com/"><img src="https://avatars.githubusercontent.com/u/34240465" alt="Mercedes-Benz Group" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="LambdaTest" height="32"></a></p>
336
+ <p><a href="https://syntax.fm"><img src="https://github.com/syntaxfm.png" alt="Syntax" height="32"></a> <a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://sentry.io"><img src="https://github.com/getsentry.png" alt="Sentry" 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://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104" alt="Nx" height="32"></a> <a href="https://opensource.mercedes-benz.com/"><img src="https://avatars.githubusercontent.com/u/34240465" alt="Mercedes-Benz Group" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="LambdaTest" height="32"></a></p>
332
337
  <h3>Technology Sponsors</h3>
333
338
  Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.
334
339
  <p><a href="https://netlify.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/netlify-icon.svg" alt="Netlify" height="32"></a> <a href="https://algolia.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/algolia-icon.svg" alt="Algolia" height="32"></a> <a href="https://1password.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/1password-icon.svg" alt="1Password" height="32"></a></p>
@@ -263,11 +263,15 @@ async function locateConfigFileToUse({ configFile, cwd }) {
263
263
 
264
264
  /**
265
265
  * Creates an error to be thrown when an array of results passed to `getRulesMetaForResults` was not created by the current engine.
266
+ * @param {Error|undefined} cause The original error that led to this symptom error being thrown. Might not always be available.
266
267
  * @returns {TypeError} An error object.
267
268
  */
268
- function createExtraneousResultsError() {
269
+ function createExtraneousResultsError(cause) {
269
270
  return new TypeError(
270
271
  "Results object was not created from this ESLint instance.",
272
+ {
273
+ cause,
274
+ },
271
275
  );
272
276
  }
273
277
 
@@ -543,7 +547,7 @@ function validateOptionCloneability(options) {
543
547
  })
544
548
  .sort();
545
549
  const error = new TypeError(
546
- `The ${uncloneableOptionKeys.length === 1 ? "option" : "options"} ${new Intl.ListFormat("en-US").format(uncloneableOptionKeys.map(key => `"${key}"`))} cannot be cloned. When concurrency is enabled, all options must be cloneable. Remove uncloneable options or use an options module.`,
550
+ `The ${uncloneableOptionKeys.length === 1 ? "option" : "options"} ${new Intl.ListFormat("en-US").format(uncloneableOptionKeys.map(key => `"${key}"`))} cannot be cloned. When concurrency is enabled, all options must be cloneable values (JSON values). Remove uncloneable options or use an options module.`,
547
551
  );
548
552
  error.code = "ESLINT_UNCLONEABLE_OPTIONS";
549
553
  throw error;
@@ -773,8 +777,8 @@ class ESLint {
773
777
  try {
774
778
  configs =
775
779
  configLoader.getCachedConfigArrayForFile(filePath);
776
- } catch {
777
- throw createExtraneousResultsError();
780
+ } catch (err) {
781
+ throw createExtraneousResultsError(err);
778
782
  }
779
783
 
780
784
  const config = configs.getConfig(filePath);
@@ -271,6 +271,9 @@ function tryParseSelector(selector) {
271
271
  ) {
272
272
  throw new SyntaxError(
273
273
  `Syntax error in selector "${selector}" at position ${err.location.start.offset}: ${err.message}`,
274
+ {
275
+ cause: err,
276
+ },
274
277
  );
275
278
  }
276
279
  throw err;
@@ -851,6 +851,9 @@ class RuleTester {
851
851
  } catch (err) {
852
852
  throw new Error(
853
853
  `Schema for rule ${ruleName} is invalid: ${err.message}`,
854
+ {
855
+ cause: err,
856
+ },
854
857
  );
855
858
  }
856
859
  }
@@ -233,7 +233,6 @@ module.exports = {
233
233
  url: "https://eslint.org/docs/latest/rules/array-callback-return",
234
234
  },
235
235
 
236
- // eslint-disable-next-line eslint-plugin/require-meta-has-suggestions -- false positive
237
236
  hasSuggestions: true,
238
237
 
239
238
  schema: [
@@ -293,6 +293,7 @@ module.exports = new LazyLoadingRuleMap(
293
293
  "prefer-rest-params": () => require("./prefer-rest-params"),
294
294
  "prefer-spread": () => require("./prefer-spread"),
295
295
  "prefer-template": () => require("./prefer-template"),
296
+ "preserve-caught-error": () => require("./preserve-caught-error"),
296
297
  "quote-props": () => require("./quote-props"),
297
298
  quotes: () => require("./quotes"),
298
299
  radix: () => require("./radix"),
@@ -105,6 +105,7 @@ module.exports = {
105
105
  meta: {
106
106
  dialects: ["javascript", "typescript"],
107
107
  language: "javascript",
108
+ hasSuggestions: true,
108
109
  type: "suggestion",
109
110
 
110
111
  defaultOptions: [{ allow: [] }],
@@ -131,6 +132,7 @@ module.exports = {
131
132
 
132
133
  messages: {
133
134
  unexpected: "Unexpected empty {{name}}.",
135
+ suggestComment: "Add comment inside empty {{name}}.",
134
136
  },
135
137
  },
136
138
 
@@ -204,6 +206,23 @@ module.exports = {
204
206
  loc: node.body.loc,
205
207
  messageId: "unexpected",
206
208
  data: { name },
209
+ suggest: [
210
+ {
211
+ messageId: "suggestComment",
212
+ data: { name },
213
+ fix(fixer) {
214
+ const range = [
215
+ node.body.range[0] + 1,
216
+ node.body.range[1] - 1,
217
+ ];
218
+
219
+ return fixer.replaceTextRange(
220
+ range,
221
+ " /* empty */ ",
222
+ );
223
+ },
224
+ },
225
+ ],
207
226
  });
208
227
  }
209
228
  }
@@ -11,6 +11,7 @@
11
11
  /** @type {import('../types').Rule.RuleModule} */
12
12
  module.exports = {
13
13
  meta: {
14
+ hasSuggestions: true,
14
15
  type: "suggestion",
15
16
 
16
17
  docs: {
@@ -23,6 +24,7 @@ module.exports = {
23
24
 
24
25
  messages: {
25
26
  unexpected: "Unexpected empty static block.",
27
+ suggestComment: "Add comment inside empty static block.",
26
28
  },
27
29
  },
28
30
 
@@ -32,14 +34,36 @@ module.exports = {
32
34
  return {
33
35
  StaticBlock(node) {
34
36
  if (node.body.length === 0) {
37
+ const openingBrace = sourceCode.getFirstToken(node, {
38
+ skip: 1,
39
+ });
35
40
  const closingBrace = sourceCode.getLastToken(node);
36
41
 
37
42
  if (
38
43
  sourceCode.getCommentsBefore(closingBrace).length === 0
39
44
  ) {
40
45
  context.report({
41
- node,
46
+ loc: {
47
+ start: openingBrace.loc.start,
48
+ end: closingBrace.loc.end,
49
+ },
42
50
  messageId: "unexpected",
51
+ suggest: [
52
+ {
53
+ messageId: "suggestComment",
54
+ fix(fixer) {
55
+ const range = [
56
+ openingBrace.range[1],
57
+ closingBrace.range[0],
58
+ ];
59
+
60
+ return fixer.replaceTextRange(
61
+ range,
62
+ " /* empty */ ",
63
+ );
64
+ },
65
+ },
66
+ ],
43
67
  });
44
68
  }
45
69
  }
@@ -104,10 +104,47 @@ module.exports = {
104
104
  typeof node.cases === "undefined" ||
105
105
  node.cases.length === 0
106
106
  ) {
107
+ const openingBrace = sourceCode.getTokenAfter(
108
+ node.discriminant,
109
+ astUtils.isOpeningBraceToken,
110
+ );
111
+
112
+ const closingBrace = sourceCode.getLastToken(node);
113
+
114
+ if (
115
+ sourceCode.commentsExistBetween(
116
+ openingBrace,
117
+ closingBrace,
118
+ )
119
+ ) {
120
+ return;
121
+ }
122
+
107
123
  context.report({
108
124
  node,
125
+ loc: {
126
+ start: openingBrace.loc.start,
127
+ end: closingBrace.loc.end,
128
+ },
109
129
  messageId: "unexpected",
110
130
  data: { type: "switch" },
131
+ suggest: [
132
+ {
133
+ messageId: "suggestComment",
134
+ data: { type: "switch" },
135
+ fix(fixer) {
136
+ const range = [
137
+ openingBrace.range[1],
138
+ closingBrace.range[0],
139
+ ];
140
+
141
+ return fixer.replaceTextRange(
142
+ range,
143
+ " /* empty */ ",
144
+ );
145
+ },
146
+ },
147
+ ],
111
148
  });
112
149
  }
113
150
  },
@@ -226,7 +226,9 @@ module.exports = {
226
226
 
227
227
  Program(node) {
228
228
  const scope = sourceCode.getScope(node),
229
- features = context.parserOptions.ecmaFeatures || {},
229
+ features =
230
+ context.languageOptions.parserOptions.ecmaFeatures ||
231
+ {},
230
232
  strict =
231
233
  scope.isStrict ||
232
234
  node.sourceType === "module" ||
@@ -189,7 +189,7 @@ module.exports = {
189
189
  * @returns {boolean} true if they do not match
190
190
  */
191
191
  function baseTenLosesPrecision(node) {
192
- const rawNumber = getRaw(node);
192
+ const rawNumber = getRaw(node).toLowerCase();
193
193
 
194
194
  /*
195
195
  * If trailing zeros equal the exponent, this is a valid representation
@@ -278,6 +278,12 @@ module.exports = {
278
278
  meta: {
279
279
  type: "problem",
280
280
 
281
+ defaultOptions: [
282
+ {
283
+ allowEscape: false,
284
+ },
285
+ ],
286
+
281
287
  docs: {
282
288
  description:
283
289
  "Disallow characters which are made with multiple code points in character class syntax",
@@ -293,7 +299,6 @@ module.exports = {
293
299
  properties: {
294
300
  allowEscape: {
295
301
  type: "boolean",
296
- default: false,
297
302
  },
298
303
  },
299
304
  additionalProperties: false,
@@ -313,7 +318,7 @@ module.exports = {
313
318
  },
314
319
  },
315
320
  create(context) {
316
- const allowEscape = context.options[0]?.allowEscape;
321
+ const [{ allowEscape }] = context.options;
317
322
  const sourceCode = context.sourceCode;
318
323
  const parser = new RegExpParser();
319
324
  const checkedPatternNodes = new Set();