eslint 8.29.0 → 8.31.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
@@ -31,7 +31,7 @@ ESLint is a tool for identifying and reporting on patterns found in ECMAScript/J
31
31
  2. [Configuration](#configuration)
32
32
  3. [Code of Conduct](#code-of-conduct)
33
33
  4. [Filing Issues](#filing-issues)
34
- 5. [Frequently Asked Questions](#faq)
34
+ 5. [Frequently Asked Questions](#frequently-asked-questions)
35
35
  6. [Releases](#releases)
36
36
  7. [Security Policy](#security-policy)
37
37
  8. [Semantic Versioning Policy](#semantic-versioning-policy)
@@ -41,7 +41,7 @@ ESLint is a tool for identifying and reporting on patterns found in ECMAScript/J
41
41
  12. [Sponsors](#sponsors)
42
42
  13. [Technology Sponsors](#technology-sponsors)
43
43
 
44
- ## <a name="installation-and-usage"></a>Installation and Usage
44
+ ## Installation and Usage
45
45
 
46
46
  Prerequisites: [Node.js](https://nodejs.org/) (`^12.22.0`, `^14.17.0`, or `>=16.0.0`) built with SSL support. (If you are using an official Node.js distribution, SSL is always built in.)
47
47
 
@@ -57,7 +57,7 @@ After that, you can run ESLint on any file or directory like this:
57
57
  ./node_modules/.bin/eslint yourfile.js
58
58
  ```
59
59
 
60
- ## <a name="configuration"></a>Configuration
60
+ ## Configuration
61
61
 
62
62
  After running `npm init @eslint/config`, you'll have a `.eslintrc` file in your directory. In it, you'll see some rules configured like this:
63
63
 
@@ -78,11 +78,11 @@ The names `"semi"` and `"quotes"` are the names of [rules](https://eslint.org/do
78
78
 
79
79
  The three error levels allow you fine-grained control over how ESLint applies rules (for more configuration options and details, see the [configuration docs](https://eslint.org/docs/user-guide/configuring)).
80
80
 
81
- ## <a name="code-of-conduct"></a>Code of Conduct
81
+ ## Code of Conduct
82
82
 
83
83
  ESLint adheres to the [JS Foundation Code of Conduct](https://eslint.org/conduct).
84
84
 
85
- ## <a name="filing-issues"></a>Filing Issues
85
+ ## Filing Issues
86
86
 
87
87
  Before filing an issue, please be sure to read the guidelines for what you're reporting:
88
88
 
@@ -91,7 +91,7 @@ Before filing an issue, please be sure to read the guidelines for what you're re
91
91
  * [Proposing a Rule Change](https://eslint.org/docs/developer-guide/contributing/rule-changes)
92
92
  * [Request a Change](https://eslint.org/docs/developer-guide/contributing/changes)
93
93
 
94
- ## <a name="faq"></a>Frequently Asked Questions
94
+ ## Frequently Asked Questions
95
95
 
96
96
  ### I'm using JSCS, should I migrate to ESLint?
97
97
 
@@ -141,15 +141,15 @@ We intentionally don't lock dependency versions so that we have the latest compa
141
141
 
142
142
  The Twilio blog has a [deeper dive](https://www.twilio.com/blog/lockfiles-nodejs) to learn more.
143
143
 
144
- ## <a name="releases"></a>Releases
144
+ ## Releases
145
145
 
146
146
  We have scheduled releases every two weeks on Friday or Saturday. You can follow a [release issue](https://github.com/eslint/eslint/issues?q=is%3Aopen+is%3Aissue+label%3Arelease) for updates about the scheduling of any particular release.
147
147
 
148
- ## <a name="security-policy"></a>Security Policy
148
+ ## Security Policy
149
149
 
150
150
  ESLint takes security seriously. We work hard to ensure that ESLint is safe for everyone and that security issues are addressed quickly and responsibly. Read the full [security policy](https://github.com/eslint/.github/blob/master/SECURITY.md).
151
151
 
152
- ## <a name="semantic-versioning-policy"></a>Semantic Versioning Policy
152
+ ## Semantic Versioning Policy
153
153
 
154
154
  ESLint follows [semantic versioning](https://semver.org). However, due to the nature of ESLint as a code quality tool, it's not always clear when a minor or major version bump occurs. To help clarify this for everyone, we've defined the following semantic versioning policy for ESLint:
155
155
 
@@ -182,7 +182,7 @@ ESLint follows [semantic versioning](https://semver.org). However, due to the na
182
182
 
183
183
  According to our policy, any minor update may report more linting errors than the previous release (ex: from a bug fix). As such, we recommend using the tilde (`~`) in `package.json` e.g. `"eslint": "~3.1.0"` to guarantee the results of your builds.
184
184
 
185
- ## <a name="stylistic-rule-updates"></a>Stylistic Rule Updates
185
+ ## Stylistic Rule Updates
186
186
 
187
187
  Stylistic rules are frozen according to [our policy](https://eslint.org/blog/2020/05/changes-to-rules-policies) on how we evaluate new rules and rule changes.
188
188
  This means:
@@ -191,11 +191,11 @@ This means:
191
191
  * **New ECMAScript features**: We will also make sure stylistic rules are compatible with new ECMAScript features.
192
192
  * **New options**: We will **not** add any new options to stylistic rules unless an option is the only way to fix a bug or support a newly-added ECMAScript feature.
193
193
 
194
- ## <a name="license"></a>License
194
+ ## License
195
195
 
196
196
  [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Feslint%2Feslint.svg?type=large)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Feslint%2Feslint?ref=badge_large)
197
197
 
198
- ## <a name="team"></a>Team
198
+ ## Team
199
199
 
200
200
  These folks keep the project moving and are resources for help.
201
201
 
@@ -245,11 +245,6 @@ Nitin Kumar
245
245
  The people who review and fix bugs and help triage issues.
246
246
 
247
247
  <table><tbody><tr><td align="center" valign="top" width="11%">
248
- <a href="https://github.com/brettz9">
249
- <img src="https://github.com/brettz9.png?s=75" width="75" height="75"><br />
250
- Brett Zamir
251
- </a>
252
- </td><td align="center" valign="top" width="11%">
253
248
  <a href="https://github.com/bmish">
254
249
  <img src="https://github.com/bmish.png?s=75" width="75" height="75"><br />
255
250
  Bryan Mishkin
@@ -260,38 +255,49 @@ Bryan Mishkin
260
255
  Sara Soueidan
261
256
  </a>
262
257
  </td><td align="center" valign="top" width="11%">
263
- <a href="https://github.com/g-plane">
264
- <img src="https://github.com/g-plane.png?s=75" width="75" height="75"><br />
265
- Pig Fang
258
+ <a href="https://github.com/yeonjuan">
259
+ <img src="https://github.com/yeonjuan.png?s=75" width="75" height="75"><br />
260
+ YeonJuan
261
+ </a>
262
+ </td></tr></tbody></table>
263
+
264
+ ### Website Team
265
+
266
+ Team members who focus specifically on eslint.org
267
+
268
+ <table><tbody><tr><td align="center" valign="top" width="11%">
269
+ <a href="https://github.com/amareshsm">
270
+ <img src="https://github.com/amareshsm.png?s=75" width="75" height="75"><br />
271
+ Amaresh S M
266
272
  </a>
267
273
  </td><td align="center" valign="top" width="11%">
268
- <a href="https://github.com/anikethsaha">
269
- <img src="https://github.com/anikethsaha.png?s=75" width="75" height="75"><br />
270
- Anix
274
+ <a href="https://github.com/harish-sethuraman">
275
+ <img src="https://github.com/harish-sethuraman.png?s=75" width="75" height="75"><br />
276
+ Strek
271
277
  </a>
272
278
  </td><td align="center" valign="top" width="11%">
273
- <a href="https://github.com/yeonjuan">
274
- <img src="https://github.com/yeonjuan.png?s=75" width="75" height="75"><br />
275
- YeonJuan
279
+ <a href="https://github.com/kecrily">
280
+ <img src="https://github.com/kecrily.png?s=75" width="75" height="75"><br />
281
+ Percy Ma
276
282
  </a>
277
283
  </td></tr></tbody></table>
278
284
 
279
285
  <!--teamend-->
280
286
 
281
- ## <a name="sponsors"></a>Sponsors
287
+ ## Sponsors
282
288
 
283
289
  The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://opencollective.com/eslint) to get your logo on our README and website.
284
290
 
285
291
  <!-- NOTE: This section is autogenerated. Do not manually edit.-->
286
292
  <!--sponsorsstart-->
287
293
  <h3>Platinum Sponsors</h3>
288
- <p><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>
289
- <p><a href="https://www.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> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="96"></a></p><h3>Silver Sponsors</h3>
290
- <p><a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a></p><h3>Bronze Sponsors</h3>
291
- <p><a href="https://launchdarkly.com"><img src="https://images.opencollective.com/launchdarkly/574bb9e/logo.png" alt="launchdarkly" 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://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS" 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://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" 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></p>
294
+ <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>
295
+ <p><a href="https://ridicorp.com/career/"><img src="https://images.opencollective.com/ridi-corporation/175dcf3/logo.png" alt="RIDI" height="96"></a> <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>
296
+ <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></p><h3>Bronze Sponsors</h3>
297
+ <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://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>
292
298
  <!--sponsorsend-->
293
299
 
294
- ## <a name="technology-sponsors"></a>Technology Sponsors
300
+ ## Technology Sponsors
295
301
 
296
302
  * Site search ([eslint.org](https://eslint.org)) is sponsored by [Algolia](https://www.algolia.com)
297
303
  * Hosting for ([eslint.org](https://eslint.org)) is sponsored by [Netlify](https://www.netlify.com)
@@ -0,0 +1,46 @@
1
+ [
2
+ {
3
+ "name": "checkstyle",
4
+ "description": "Outputs results to the [Checkstyle](https://checkstyle.sourceforge.io/) format."
5
+ },
6
+ {
7
+ "name": "compact",
8
+ "description": "Human-readable output format. Mimics the default output of JSHint."
9
+ },
10
+ {
11
+ "name": "html",
12
+ "description": "Outputs results to HTML. The `html` formatter is useful for visual presentation in the browser."
13
+ },
14
+ {
15
+ "name": "jslint-xml",
16
+ "description": "Outputs results to format compatible with the [JSLint Jenkins plugin](https://plugins.jenkins.io/jslint/)."
17
+ },
18
+ {
19
+ "name": "json-with-metadata",
20
+ "description": "Outputs JSON-serialized results. The `json-with-metadata` provides the same linting results as the [`json`](#json) formatter with additional metadata about the rules applied. The linting results are included in the `results` property and the rules metadata is included in the `metadata` property.\n\nAlternatively, you can use the [ESLint Node.js API](../../developer-guide/nodejs-api) to programmatically use ESLint."
21
+ },
22
+ {
23
+ "name": "json",
24
+ "description": "Outputs JSON-serialized results. The `json` formatter is useful when you want to programmatically work with the CLI's linting results.\n\nAlternatively, you can use the [ESLint Node.js API](../../developer-guide/nodejs-api) to programmatically use ESLint."
25
+ },
26
+ {
27
+ "name": "junit",
28
+ "description": "Outputs results to format compatible with the [JUnit Jenkins plugin](https://plugins.jenkins.io/junit/)."
29
+ },
30
+ {
31
+ "name": "stylish",
32
+ "description": "Human-readable output format. This is the default formatter."
33
+ },
34
+ {
35
+ "name": "tap",
36
+ "description": "Outputs results to the [Test Anything Protocol (TAP)](https://testanything.org/) specification format."
37
+ },
38
+ {
39
+ "name": "unix",
40
+ "description": "Outputs results to a format similar to many commands in UNIX-like systems. Parsable with tools such as [grep](https://www.gnu.org/software/grep/manual/grep.html), [sed](https://www.gnu.org/software/sed/manual/sed.html), and [awk](https://www.gnu.org/software/gawk/manual/gawk.html)."
41
+ },
42
+ {
43
+ "name": "visualstudio",
44
+ "description": "Outputs results to format compatible with the integrated terminal of the [Visual Studio](https://visualstudio.microsoft.com/) IDE. When using Visual Studio, you can click on the linting results in the integrated terminal to go to the issue in the source code."
45
+ }
46
+ ]
@@ -51,7 +51,7 @@ exports.defaultConfig = [
51
51
  // default ignores are listed here
52
52
  {
53
53
  ignores: [
54
- "**/node_modules/**",
54
+ "**/node_modules/*",
55
55
  ".git/"
56
56
  ]
57
57
  },
@@ -93,6 +93,7 @@ const FLAT_CONFIG_FILENAME = "eslint.config.js";
93
93
  const debug = require("debug")("eslint:flat-eslint");
94
94
  const removedFormatters = new Set(["table", "codeframe"]);
95
95
  const privateMembers = new WeakMap();
96
+ const importedConfigFileModificationTime = new Map();
96
97
 
97
98
  /**
98
99
  * It will calculate the error and warning count for collection of messages per file
@@ -281,7 +282,42 @@ async function loadFlatConfigFile(filePath) {
281
282
 
282
283
  debug(`Config file URL is ${fileURL}`);
283
284
 
284
- return (await import(fileURL)).default;
285
+ const mtime = (await fs.stat(filePath)).mtime.getTime();
286
+
287
+ /*
288
+ * Append a query with the config file's modification time (`mtime`) in order
289
+ * to import the current version of the config file. Without the query, `import()` would
290
+ * cache the config file module by the pathname only, and then always return
291
+ * the same version (the one that was actual when the module was imported for the first time).
292
+ *
293
+ * This ensures that the config file module is loaded and executed again
294
+ * if it has been changed since the last time it was imported.
295
+ * If it hasn't been changed, `import()` will just return the cached version.
296
+ *
297
+ * Note that we should not overuse queries (e.g., by appending the current time
298
+ * to always reload the config file module) as that could cause memory leaks
299
+ * because entries are never removed from the import cache.
300
+ */
301
+ fileURL.searchParams.append("mtime", mtime);
302
+
303
+ /*
304
+ * With queries, we can bypass the import cache. However, when import-ing a CJS module,
305
+ * Node.js uses the require infrastructure under the hood. That includes the require cache,
306
+ * which caches the config file module by its file path (queries have no effect).
307
+ * Therefore, we also need to clear the require cache before importing the config file module.
308
+ * In order to get the same behavior with ESM and CJS config files, in particular - to reload
309
+ * the config file only if it has been changed, we track file modification times and clear
310
+ * the require cache only if the file has been changed.
311
+ */
312
+ if (importedConfigFileModificationTime.get(filePath) !== mtime) {
313
+ delete require.cache[filePath];
314
+ }
315
+
316
+ const config = (await import(fileURL)).default;
317
+
318
+ importedConfigFileModificationTime.set(filePath, mtime);
319
+
320
+ return config;
285
321
  }
286
322
 
287
323
  /**
package/lib/options.js CHANGED
@@ -177,7 +177,7 @@ module.exports = function(usingFlatConfig) {
177
177
  },
178
178
  resolvePluginsFlag,
179
179
  {
180
- heading: "Specifying rules and plugins"
180
+ heading: "Specify Rules and Plugins"
181
181
  },
182
182
  {
183
183
  option: "plugin",
@@ -191,7 +191,7 @@ module.exports = function(usingFlatConfig) {
191
191
  },
192
192
  rulesDirFlag,
193
193
  {
194
- heading: "Fixing problems"
194
+ heading: "Fix Problems"
195
195
  },
196
196
  {
197
197
  option: "fix",
@@ -211,7 +211,7 @@ module.exports = function(usingFlatConfig) {
211
211
  description: "Specify the types of fixes to apply (directive, problem, suggestion, layout)"
212
212
  },
213
213
  {
214
- heading: "Ignoring files"
214
+ heading: "Ignore Files"
215
215
  },
216
216
  ignorePathFlag,
217
217
  {
@@ -229,7 +229,7 @@ module.exports = function(usingFlatConfig) {
229
229
  }]
230
230
  },
231
231
  {
232
- heading: "Using stdin"
232
+ heading: "Use stdin"
233
233
  },
234
234
  {
235
235
  option: "stdin",
@@ -243,7 +243,7 @@ module.exports = function(usingFlatConfig) {
243
243
  description: "Specify filename to process STDIN as"
244
244
  },
245
245
  {
246
- heading: "Handling warnings"
246
+ heading: "Handle Warnings"
247
247
  },
248
248
  {
249
249
  option: "quiet",
@@ -619,15 +619,17 @@ class FlatRuleTester {
619
619
  plugins: {
620
620
  "rule-tester": {
621
621
  rules: {
622
- "validate-ast"() {
623
- return {
624
- Program(node) {
625
- beforeAST = cloneDeeplyExcludesParent(node);
626
- },
627
- "Program:exit"(node) {
628
- afterAST = node;
629
- }
630
- };
622
+ "validate-ast": {
623
+ create() {
624
+ return {
625
+ Program(node) {
626
+ beforeAST = cloneDeeplyExcludesParent(node);
627
+ },
628
+ "Program:exit"(node) {
629
+ afterAST = node;
630
+ }
631
+ };
632
+ }
631
633
  }
632
634
  }
633
635
  }
@@ -632,14 +632,18 @@ class RuleTester {
632
632
  * The goal is to check whether or not AST was modified when
633
633
  * running the rule under test.
634
634
  */
635
- linter.defineRule("rule-tester/validate-ast", () => ({
636
- Program(node) {
637
- beforeAST = cloneDeeplyExcludesParent(node);
638
- },
639
- "Program:exit"(node) {
640
- afterAST = node;
635
+ linter.defineRule("rule-tester/validate-ast", {
636
+ create() {
637
+ return {
638
+ Program(node) {
639
+ beforeAST = cloneDeeplyExcludesParent(node);
640
+ },
641
+ "Program:exit"(node) {
642
+ afterAST = node;
643
+ }
644
+ };
641
645
  }
642
- }));
646
+ });
643
647
 
644
648
  if (typeof config.parser === "string") {
645
649
  assert(path.isAbsolute(config.parser), "Parsers provided as strings to RuleTester must be absolute paths");
@@ -346,7 +346,7 @@ module.exports = {
346
346
  "always-multiline": forceTrailingCommaIfMultiline,
347
347
  "only-multiline": allowTrailingCommaIfMultiline,
348
348
  never: forbidTrailingComma,
349
- ignore: () => {}
349
+ ignore() {}
350
350
  };
351
351
 
352
352
  return {
@@ -347,6 +347,40 @@ module.exports = {
347
347
  );
348
348
  }
349
349
 
350
+ /**
351
+ * Starting from the given node (a property.key node here) looks forward
352
+ * until it finds the colon punctuator and returns it.
353
+ * @param {ASTNode} node The node to start looking from.
354
+ * @returns {ASTNode} The colon punctuator.
355
+ */
356
+ function getNextColon(node) {
357
+ return sourceCode.getTokenAfter(node, astUtils.isColonToken);
358
+ }
359
+
360
+ /**
361
+ * Starting from the given node (a property.key node here) looks forward
362
+ * until it finds the last token before a colon punctuator and returns it.
363
+ * @param {ASTNode} node The node to start looking from.
364
+ * @returns {ASTNode} The last token before a colon punctuator.
365
+ */
366
+ function getLastTokenBeforeColon(node) {
367
+ const colonToken = getNextColon(node);
368
+
369
+ return sourceCode.getTokenBefore(colonToken);
370
+ }
371
+
372
+ /**
373
+ * Starting from the given node (a property.key node here) looks forward
374
+ * until it finds the first token after a colon punctuator and returns it.
375
+ * @param {ASTNode} node The node to start looking from.
376
+ * @returns {ASTNode} The first token after a colon punctuator.
377
+ */
378
+ function getFirstTokenAfterColon(node) {
379
+ const colonToken = getNextColon(node);
380
+
381
+ return sourceCode.getTokenAfter(colonToken);
382
+ }
383
+
350
384
  /**
351
385
  * Checks whether a property is a member of the property group it follows.
352
386
  * @param {ASTNode} lastMember The last Property known to be in the group.
@@ -355,7 +389,7 @@ module.exports = {
355
389
  */
356
390
  function continuesPropertyGroup(lastMember, candidate) {
357
391
  const groupEndLine = lastMember.loc.start.line,
358
- candidateValueStartLine = (isKeyValueProperty(candidate) ? candidate.value : candidate).loc.start.line;
392
+ candidateValueStartLine = (isKeyValueProperty(candidate) ? getFirstTokenAfterColon(candidate.key) : candidate).loc.start.line;
359
393
 
360
394
  if (candidateValueStartLine - groupEndLine <= 1) {
361
395
  return true;
@@ -384,28 +418,6 @@ module.exports = {
384
418
  return false;
385
419
  }
386
420
 
387
- /**
388
- * Starting from the given a node (a property.key node here) looks forward
389
- * until it finds the last token before a colon punctuator and returns it.
390
- * @param {ASTNode} node The node to start looking from.
391
- * @returns {ASTNode} The last token before a colon punctuator.
392
- */
393
- function getLastTokenBeforeColon(node) {
394
- const colonToken = sourceCode.getTokenAfter(node, astUtils.isColonToken);
395
-
396
- return sourceCode.getTokenBefore(colonToken);
397
- }
398
-
399
- /**
400
- * Starting from the given a node (a property.key node here) looks forward
401
- * until it finds the colon punctuator and returns it.
402
- * @param {ASTNode} node The node to start looking from.
403
- * @returns {ASTNode} The colon punctuator.
404
- */
405
- function getNextColon(node) {
406
- return sourceCode.getTokenAfter(node, astUtils.isColonToken);
407
- }
408
-
409
421
  /**
410
422
  * Gets an object literal property's key as the identifier name or string value.
411
423
  * @param {ASTNode} property Property node whose key to retrieve.
@@ -73,7 +73,7 @@ module.exports = {
73
73
  end: lastTokenOfTest.loc.end
74
74
  },
75
75
  messageId: "unexpectedTestCons",
76
- fix: fixer => {
76
+ fix(fixer) {
77
77
  if (hasComments) {
78
78
  return null;
79
79
  }
@@ -101,7 +101,7 @@ module.exports = {
101
101
  end: lastTokenOfConsequent.loc.end
102
102
  },
103
103
  messageId: "unexpectedConsAlt",
104
- fix: fixer => {
104
+ fix(fixer) {
105
105
  if (hasComments) {
106
106
  return null;
107
107
  }
@@ -178,7 +178,7 @@ module.exports = {
178
178
  context.report({
179
179
  node,
180
180
  messageId: "unexpected",
181
- fix: fixer => {
181
+ fix(fixer) {
182
182
 
183
183
  if (!isSafeFromNameCollisions(node, currentScope)) {
184
184
  return null;
@@ -13,6 +13,7 @@ const astUtils = require("./utils/ast-utils");
13
13
  /** @type {import('../shared/types').Rule} */
14
14
  module.exports = {
15
15
  meta: {
16
+ hasSuggestions: true,
16
17
  type: "suggestion",
17
18
 
18
19
  docs: {
@@ -29,6 +30,7 @@ module.exports = {
29
30
  ],
30
31
 
31
32
  messages: {
33
+ removeAwait: "Remove redundant `await`.",
32
34
  redundantUseOfAwait: "Redundant use of `await` on a return value."
33
35
  }
34
36
  },
@@ -44,7 +46,32 @@ module.exports = {
44
46
  context.report({
45
47
  node: context.getSourceCode().getFirstToken(node),
46
48
  loc: node.loc,
47
- messageId: "redundantUseOfAwait"
49
+ messageId: "redundantUseOfAwait",
50
+ suggest: [
51
+ {
52
+ messageId: "removeAwait",
53
+ fix(fixer) {
54
+ const sourceCode = context.getSourceCode();
55
+ const [awaitToken, tokenAfterAwait] = sourceCode.getFirstTokens(node, 2);
56
+
57
+ const areAwaitAndAwaitedExpressionOnTheSameLine = awaitToken.loc.start.line === tokenAfterAwait.loc.start.line;
58
+
59
+ if (!areAwaitAndAwaitedExpressionOnTheSameLine) {
60
+ return null;
61
+ }
62
+
63
+ const [startOfAwait, endOfAwait] = awaitToken.range;
64
+
65
+ const characterAfterAwait = sourceCode.text[endOfAwait];
66
+ const trimLength = characterAfterAwait === " " ? 1 : 0;
67
+
68
+ const range = [startOfAwait, endOfAwait + trimLength];
69
+
70
+ return fixer.removeRange(range);
71
+ }
72
+ }
73
+ ]
74
+
48
75
  });
49
76
  }
50
77
 
@@ -53,6 +53,14 @@ module.exports = {
53
53
  enforceInClassFields: {
54
54
  type: "boolean",
55
55
  default: false
56
+ },
57
+ allowInArrayDestructuring: {
58
+ type: "boolean",
59
+ default: true
60
+ },
61
+ allowInObjectDestructuring: {
62
+ type: "boolean",
63
+ default: true
56
64
  }
57
65
  },
58
66
  additionalProperties: false
@@ -74,6 +82,8 @@ module.exports = {
74
82
  const enforceInMethodNames = typeof options.enforceInMethodNames !== "undefined" ? options.enforceInMethodNames : false;
75
83
  const enforceInClassFields = typeof options.enforceInClassFields !== "undefined" ? options.enforceInClassFields : false;
76
84
  const allowFunctionParams = typeof options.allowFunctionParams !== "undefined" ? options.allowFunctionParams : true;
85
+ const allowInArrayDestructuring = typeof options.allowInArrayDestructuring !== "undefined" ? options.allowInArrayDestructuring : true;
86
+ const allowInObjectDestructuring = typeof options.allowInObjectDestructuring !== "undefined" ? options.allowInObjectDestructuring : true;
77
87
 
78
88
  //-------------------------------------------------------------------------
79
89
  // Helpers
@@ -195,6 +205,7 @@ module.exports = {
195
205
  checkForDanglingUnderscoreInFunctionParameters(node);
196
206
  }
197
207
 
208
+
198
209
  /**
199
210
  * Check if variable expression has a dangling underscore
200
211
  * @param {ASTNode} node node to evaluate
@@ -202,18 +213,32 @@ module.exports = {
202
213
  * @private
203
214
  */
204
215
  function checkForDanglingUnderscoreInVariableExpression(node) {
205
- const identifier = node.id.name;
216
+ context.getDeclaredVariables(node).forEach(variable => {
217
+ const definition = variable.defs.find(def => def.node === node);
218
+ const identifierNode = definition.name;
219
+ const identifier = identifierNode.name;
220
+ let parent = identifierNode.parent;
221
+
222
+ while (!["VariableDeclarator", "ArrayPattern", "ObjectPattern"].includes(parent.type)) {
223
+ parent = parent.parent;
224
+ }
206
225
 
207
- if (typeof identifier !== "undefined" && hasDanglingUnderscore(identifier) &&
208
- !isSpecialCaseIdentifierInVariableExpression(identifier) && !isAllowed(identifier)) {
209
- context.report({
210
- node,
211
- messageId: "unexpectedUnderscore",
212
- data: {
213
- identifier
214
- }
215
- });
216
- }
226
+ if (
227
+ hasDanglingUnderscore(identifier) &&
228
+ !isSpecialCaseIdentifierInVariableExpression(identifier) &&
229
+ !isAllowed(identifier) &&
230
+ !(allowInArrayDestructuring && parent.type === "ArrayPattern") &&
231
+ !(allowInObjectDestructuring && parent.type === "ObjectPattern")
232
+ ) {
233
+ context.report({
234
+ node,
235
+ messageId: "unexpectedUnderscore",
236
+ data: {
237
+ identifier
238
+ }
239
+ });
240
+ }
241
+ });
217
242
  }
218
243
 
219
244
  /**
@@ -144,7 +144,7 @@ module.exports = {
144
144
  context.report({
145
145
  node,
146
146
  messageId: "unnecessaryConditionalAssignment",
147
- fix: fixer => {
147
+ fix(fixer) {
148
148
  const shouldParenthesizeAlternate =
149
149
  (
150
150
  astUtils.getPrecedence(node.alternate) < OR_PRECEDENCE ||
@@ -159,7 +159,7 @@ function hasReferenceInTDZ(node) {
159
159
  return !reference.init && (
160
160
  start < idStart ||
161
161
  (defaultValue !== null && start >= defaultStart && end <= defaultEnd) ||
162
- (start >= initStart && end <= initEnd)
162
+ (!astUtils.isFunction(node) && start >= initStart && end <= initEnd)
163
163
  );
164
164
  });
165
165
  };
@@ -335,6 +335,7 @@ module.exports = {
335
335
  // Convert the function expression to an arrow function.
336
336
  const functionToken = sourceCode.getFirstToken(node, node.async ? 1 : 0);
337
337
  const leftParenToken = sourceCode.getTokenAfter(functionToken, astUtils.isOpeningParenToken);
338
+ const tokenBeforeBody = sourceCode.getTokenBefore(node.body);
338
339
 
339
340
  if (sourceCode.commentsExistBetween(functionToken, leftParenToken)) {
340
341
 
@@ -348,7 +349,7 @@ module.exports = {
348
349
  // Remove extra tokens and spaces.
349
350
  yield fixer.removeRange([functionToken.range[0], leftParenToken.range[0]]);
350
351
  }
351
- yield fixer.insertTextBefore(node.body, "=> ");
352
+ yield fixer.insertTextAfter(tokenBeforeBody, " =>");
352
353
 
353
354
  // Get the node that will become the new arrow function.
354
355
  let replacedNode = callbackInfo.isLexicalThis ? node.parent.parent : node;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "8.29.0",
3
+ "version": "8.31.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "bin": {
@@ -13,22 +13,23 @@
13
13
  "./use-at-your-own-risk": "./lib/unsupported-api.js"
14
14
  },
15
15
  "scripts": {
16
+ "build:docs:update-links": "node tools/fetch-docs-links.js",
17
+ "build:site": "node Makefile.js gensite",
18
+ "build:webpack": "node Makefile.js webpack",
19
+ "build:readme": "node tools/update-readme.js",
20
+ "lint": "node Makefile.js lint",
21
+ "lint:docs:js": "node Makefile.js lintDocsJS",
22
+ "lint:fix": "node Makefile.js lint -- fix",
23
+ "lint:fix:docs:js": "node Makefile.js lintDocsJS -- fix",
24
+ "release:generate:alpha": "node Makefile.js generatePrerelease -- alpha",
25
+ "release:generate:beta": "node Makefile.js generatePrerelease -- beta",
26
+ "release:generate:latest": "node Makefile.js generateRelease",
27
+ "release:generate:rc": "node Makefile.js generatePrerelease -- rc",
28
+ "release:publish": "node Makefile.js publishRelease",
16
29
  "test": "node Makefile.js test",
17
30
  "test:cli": "mocha",
18
- "lint": "node Makefile.js lint",
19
- "lint:docsjs": "node Makefile.js lintDocsJS",
20
- "fix": "node Makefile.js lint -- fix",
21
- "fix:docsjs": "node Makefile.js lintDocsJS -- fix",
22
- "fuzz": "node Makefile.js fuzz",
23
- "generate-release": "node Makefile.js generateRelease",
24
- "generate-alpharelease": "node Makefile.js generatePrerelease -- alpha",
25
- "generate-betarelease": "node Makefile.js generatePrerelease -- beta",
26
- "generate-rcrelease": "node Makefile.js generatePrerelease -- rc",
27
- "publish-release": "node Makefile.js publishRelease",
28
- "gensite": "node Makefile.js gensite",
29
- "webpack": "node Makefile.js webpack",
30
- "perf": "node Makefile.js perf",
31
- "docs:update-links": "node tools/fetch-docs-links.js"
31
+ "test:fuzz": "node Makefile.js fuzz",
32
+ "test:performance": "node Makefile.js perf"
32
33
  },
33
34
  "gitHooks": {
34
35
  "pre-commit": "lint-staged"
@@ -55,8 +56,8 @@
55
56
  "homepage": "https://eslint.org",
56
57
  "bugs": "https://github.com/eslint/eslint/issues/",
57
58
  "dependencies": {
58
- "@eslint/eslintrc": "^1.3.3",
59
- "@humanwhocodes/config-array": "^0.11.6",
59
+ "@eslint/eslintrc": "^1.4.1",
60
+ "@humanwhocodes/config-array": "^0.11.8",
60
61
  "@humanwhocodes/module-importer": "^1.0.1",
61
62
  "@nodelib/fs.walk": "^1.2.8",
62
63
  "ajv": "^6.10.0",
@@ -75,7 +76,7 @@
75
76
  "file-entry-cache": "^6.0.1",
76
77
  "find-up": "^5.0.0",
77
78
  "glob-parent": "^6.0.2",
78
- "globals": "^13.15.0",
79
+ "globals": "^13.19.0",
79
80
  "grapheme-splitter": "^1.0.4",
80
81
  "ignore": "^5.2.0",
81
82
  "import-fresh": "^3.0.0",