eslint 7.18.0 → 7.22.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/CHANGELOG.md CHANGED
@@ -1,3 +1,68 @@
1
+ v7.22.0 - March 12, 2021
2
+
3
+ * [`3a432d8`](https://github.com/eslint/eslint/commit/3a432d82b3a5710aff7da20302fe0b94fedc46c2) Docs: Improve documentation for indent rule (#14168) (Serkan Özel)
4
+ * [`f62ec8d`](https://github.com/eslint/eslint/commit/f62ec8d30d925e70e4d0d40640857c587ac2e116) Update: throw error when fix range is invalid (#14142) (Jacob Bandes-Storch)
5
+ * [`0eecad2`](https://github.com/eslint/eslint/commit/0eecad271358f753730741fcfcb2f7cc915c1fa7) Upgrade: Update lodash in package.json to V 4.17.21 (#14159) (Basem Al-Nabulsi)
6
+ * [`5ad91aa`](https://github.com/eslint/eslint/commit/5ad91aa7df3d6bc185786e6eccd9e055fd951055) Update: report es2021 globals in no-extend-native (refs #13602) (#14177) (Milos Djermanovic)
7
+ * [`c295581`](https://github.com/eslint/eslint/commit/c295581aca4e08ec4ae8e5ee5726a6f454a3ee26) Chore: remove leftover JSDoc from lint-result-cache (#14176) (Milos Djermanovic)
8
+ * [`0d541f9`](https://github.com/eslint/eslint/commit/0d541f9d9d58966372e2055a8f69fb9483d56a4b) Chore: Reduce lodash usage (#14178) (Stephen Wade)
9
+ * [`27a67d7`](https://github.com/eslint/eslint/commit/27a67d71ffa9bbd7af02ae448844e127bcf956dc) Sponsors: Sync README with website (ESLint Jenkins)
10
+ * [`459d821`](https://github.com/eslint/eslint/commit/459d821f4a599501ceb002f9d7a5034fc45ffbb0) Chore: upgrade dependencies of browser test (#14127) (Pig Fang)
11
+ * [`ebfb63a`](https://github.com/eslint/eslint/commit/ebfb63a682004a008f2707dbad616e5ae1630b2c) Sponsors: Sync README with website (ESLint Jenkins)
12
+ * [`3ba029f`](https://github.com/eslint/eslint/commit/3ba029fbffd44068be93254890fc2aec3e92c212) Docs: Remove Extraneous Dash (#14164) (Danny Hurlburt)
13
+ * [`6f4540e`](https://github.com/eslint/eslint/commit/6f4540ea7ea39775906526506fd7abd7ea97610c) Sponsors: Sync README with website (ESLint Jenkins)
14
+ * [`ddf361c`](https://github.com/eslint/eslint/commit/ddf361ca2a2a01a9974f421e5f62270df282d0e8) Docs: Fix Formatting (#14154) (Danny Hurlburt)
15
+ * [`c0d2ac1`](https://github.com/eslint/eslint/commit/c0d2ac16f8f9c75c62c78e9fe6a24a25ba0d7828) Sponsors: Sync README with website (ESLint Jenkins)
16
+ * [`a8df03e`](https://github.com/eslint/eslint/commit/a8df03efe3bc47665d2112c2cdd5bead337d475d) Docs: Clarify triage process (#14117) (Nicholas C. Zakas)
17
+
18
+ v7.21.0 - February 27, 2021
19
+
20
+ * [`3cd5440`](https://github.com/eslint/eslint/commit/3cd5440b94d5fa4f11a09f50b685f6150f0c2d41) Upgrade: @eslint/eslintrc to 0.4.0 (#14147) (Brandon Mills)
21
+ * [`c0b8c71`](https://github.com/eslint/eslint/commit/c0b8c71df4d0b3f54b20587432d9133741985d5c) Upgrade: Puppeteer to 7.1.0 (#14122) (Tim van der Lippe)
22
+ * [`08ae31e`](https://github.com/eslint/eslint/commit/08ae31e539e381cd0eabf6393fa5c20f1d59125f) New: Implement cacheStrategy (refs eslint/rfcs#63) (#14119) (Manu Chambon)
23
+ * [`5e51fd2`](https://github.com/eslint/eslint/commit/5e51fd28dc773c11c924450d24088f97f2824f00) Update: do not ignore symbolic links (fixes #13551, fixes #13615) (#14126) (Pig Fang)
24
+ * [`87c43a5`](https://github.com/eslint/eslint/commit/87c43a5d7ea2018cffd6d9b5c431ecb60caaf0d6) Chore: improve a few comments and fix typos (#14125) (Tobias Nießen)
25
+ * [`e19c51e`](https://github.com/eslint/eslint/commit/e19c51ea2ef2cf666d94218c66f6b223bb2e9dae) Sponsors: Sync README with website (ESLint Jenkins)
26
+ * [`b8aea99`](https://github.com/eslint/eslint/commit/b8aea9988b6451b6a05af4f3ede8d6ed5c1d9926) Fix: pluralize 'line' to 'lines' in max-lines-per-function description (#14115) (Trevin Hofmann)
27
+ * [`f5b53e2`](https://github.com/eslint/eslint/commit/f5b53e285ab5286ea382d73b379b820f649c20d0) Sponsors: Sync README with website (ESLint Jenkins)
28
+ * [`eee1213`](https://github.com/eslint/eslint/commit/eee12134ce0956e9f825c1630776b1731551a948) Sponsors: Sync README with website (ESLint Jenkins)
29
+ * [`5c4d7ea`](https://github.com/eslint/eslint/commit/5c4d7ea8d2e8d137c42b918dc99a84b4a5fed9b3) Sponsors: Sync README with website (ESLint Jenkins)
30
+
31
+ v7.20.0 - February 12, 2021
32
+
33
+ * [`f4ac3b0`](https://github.com/eslint/eslint/commit/f4ac3b0e7072fbd3c14e9c64ff0c2c255a4eb730) Docs: fix sibling selector descriptions (#14099) (Milos Djermanovic)
34
+ * [`9d6063a`](https://github.com/eslint/eslint/commit/9d6063add931f0803cae1676d5df307baf114360) Fix: Crash with esquery when using JSX (fixes #13639) (#14072) (Yosuke Ota)
35
+ * [`a0871f1`](https://github.com/eslint/eslint/commit/a0871f1840060bd23cfe0952a096b107142db2f0) Docs: Triage process (#14014) (Nicholas C. Zakas)
36
+ * [`ad90761`](https://github.com/eslint/eslint/commit/ad9076183bc2c2029525edfc4596e403999348d1) Update: add enforceForJSX option to no-unused-expressions rule (#14012) (Duncan Beevers)
37
+ * [`d6c84af`](https://github.com/eslint/eslint/commit/d6c84af67318537177ffac0120a81af08e3e9df4) Fix: `--init` autoconfig shouldn't add deprecated rules (fixes #14017) (#14060) (Milos Djermanovic)
38
+ * [`9b277a1`](https://github.com/eslint/eslint/commit/9b277a16a7261e51b7ba36d6de7f996e9203a6a4) Fix: Support ENOTDIR error code in the folder existence checking utility (#13973) (Constantine Genchevsky)
39
+ * [`7aeb127`](https://github.com/eslint/eslint/commit/7aeb12798f2b9da706f3593f26a02e717929c9af) Upgrade: pin @babel/code-frame@7.12.11 (#14067) (Milos Djermanovic)
40
+ * [`b4e2af5`](https://github.com/eslint/eslint/commit/b4e2af5db1c29343ffec2cd104b04bf39b77ee56) Docs: Add more fields to bug report template (#14039) (Nicholas C. Zakas)
41
+ * [`96f1d49`](https://github.com/eslint/eslint/commit/96f1d49a4647e59f2fb918be096654e290513adc) Sponsors: Sync README with website (ESLint Jenkins)
42
+ * [`cb27b0a`](https://github.com/eslint/eslint/commit/cb27b0abeda6dfee55dd43b9cbe12afad321f55d) Build: package.json update for eslint-config-eslint release (ESLint Jenkins)
43
+ * [`4cab165`](https://github.com/eslint/eslint/commit/4cab165bf4e6e5e9f42a59a37a8ff2548c0af87d) Sponsors: Sync README with website (ESLint Jenkins)
44
+
45
+ v7.19.0 - January 30, 2021
46
+
47
+ * [`ce7f061`](https://github.com/eslint/eslint/commit/ce7f06121d9eb9cc2b3da24b4456b4d382e1413b) Update: add shadowed variable loc to message in no-shadow (fixes #13646) (#13841) (t-mangoe)
48
+ * [`c60e23f`](https://github.com/eslint/eslint/commit/c60e23ff306a14ca6eabcadb275ed27995fcc6e4) Update: fix `let` logic in for-in and for-of loops in no-extra-parens (#14011) (Milos Djermanovic)
49
+ * [`d76e8f6`](https://github.com/eslint/eslint/commit/d76e8f69bd791357c67ada7b5c55608acf29b622) Fix: no-useless-rename invalid autofix with parenthesized identifiers (#14032) (Milos Djermanovic)
50
+ * [`5800d92`](https://github.com/eslint/eslint/commit/5800d921144ec330b6ee7cd03364434007331354) Docs: Clarify stylistic rule update policy (#14052) (Brandon Mills)
51
+ * [`0ccf6d2`](https://github.com/eslint/eslint/commit/0ccf6d200147437b338cadb34546451972befd75) Docs: remove configuring.md (#14036) (Milos Djermanovic)
52
+ * [`65bb0ab`](https://github.com/eslint/eslint/commit/65bb0abde56f72586036fff151aa2d13f1b7be6c) Chore: Clean up new issue workflow (#14040) (Nicholas C. Zakas)
53
+ * [`e1da90f`](https://github.com/eslint/eslint/commit/e1da90fc414a3c9c16f52db4a5bd81bd4f9532a4) Fix: nested indenting for offsetTernaryExpressions: true (fixes #13971) (#13972) (Chris Brody)
54
+ * [`1a078b9`](https://github.com/eslint/eslint/commit/1a078b9166f29cb3760435ddbc1a0da4a0974d4a) Update: check ternary `:` even if `?` was reported in space-infix-ops (#13963) (Milos Djermanovic)
55
+ * [`fb27422`](https://github.com/eslint/eslint/commit/fb274226242eaebc1480fc9c901202986afc3c8a) Fix: extend prefer-const fixer range to whole declaration (fixes #13899) (#14033) (Nitin Kumar)
56
+ * [`e0b05c7`](https://github.com/eslint/eslint/commit/e0b05c704f3ce6f549d14718236d22fe49fcb611) Docs: add a correct example to no-unsafe-optional-chaining (refs #14029) (#14050) (armin yahya)
57
+ * [`46e836d`](https://github.com/eslint/eslint/commit/46e836d46442d2ec756038a2e12ba19b74394dbd) Sponsors: Sync README with website (ESLint Jenkins)
58
+ * [`3fc4fa4`](https://github.com/eslint/eslint/commit/3fc4fa485ca9ccd5e16dbc7e53ba31452d22dc4a) Docs: update configuring links (#14038) (Milos Djermanovic)
59
+ * [`8561c21`](https://github.com/eslint/eslint/commit/8561c2116ef89e53ebffb750066f1b00a4acdb76) Docs: fix broken links in configuring/README.md (#14046) (Milos Djermanovic)
60
+ * [`1c309eb`](https://github.com/eslint/eslint/commit/1c309ebca4a81a0faf397103dbc621019dea8c9c) Update: fix no-invalid-regexp false negatives with no flags specified (#14018) (Milos Djermanovic)
61
+ * [`f6602d5`](https://github.com/eslint/eslint/commit/f6602d569427e9e2a4f3b5ca3fc3a8bffb28d15e) Docs: Reorganize Configuration Documentation (#13837) (klkhan)
62
+ * [`c753b44`](https://github.com/eslint/eslint/commit/c753b442ef67867a178ffc2ad29b4e0534f72469) Sponsors: Sync README with website (ESLint Jenkins)
63
+ * [`a4fdb70`](https://github.com/eslint/eslint/commit/a4fdb7001aa41b9ad8bb92cc8a47b9135c94afc7) Docs: Fixed Typo (#14007) (Yash Singh)
64
+ * [`f7ca481`](https://github.com/eslint/eslint/commit/f7ca48165d025e01c38698352cff24d1de87cc8b) Docs: Explain why we disable lock files (refs eslint/tsc-meetings#234) (#14006) (Brandon Mills)
65
+
1
66
  v7.18.0 - January 15, 2021
2
67
 
3
68
  * [`e3264b2`](https://github.com/eslint/eslint/commit/e3264b26a625d926a1ea96df1c4b643af5c3797c) Upgrade: @eslint/eslintrc to improve error message for invalid extends (#14009) (Milos Djermanovic)
package/README.md CHANGED
@@ -35,10 +35,11 @@ ESLint is a tool for identifying and reporting on patterns found in ECMAScript/J
35
35
  6. [Releases](#releases)
36
36
  7. [Security Policy](#security-policy)
37
37
  8. [Semantic Versioning Policy](#semantic-versioning-policy)
38
- 9. [License](#license)
39
- 10. [Team](#team)
40
- 11. [Sponsors](#sponsors)
41
- 12. [Technology Sponsors](#technology-sponsors)
38
+ 9. [Stylistic Rule Updates](#stylistic-rule-updates)
39
+ 10. [License](#license)
40
+ 11. [Team](#team)
41
+ 12. [Sponsors](#sponsors)
42
+ 13. [Technology Sponsors](#technology-sponsors)
42
43
 
43
44
  ## <a name="installation-and-usage"></a>Installation and Usage
44
45
 
@@ -136,6 +137,16 @@ Once a language feature has been adopted into the ECMAScript standard (stage 4 a
136
137
 
137
138
  Join our [Mailing List](https://groups.google.com/group/eslint) or [Chatroom](https://eslint.org/chat).
138
139
 
140
+ ### Why doesn't ESLint lock dependency versions?
141
+
142
+ Lock files like `package-lock.json` are helpful for deployed applications. They ensure that dependencies are consistent between environments and across deployments.
143
+
144
+ Packages like `eslint` that get published to the npm registry do not include lock files. `npm install eslint` as a user will respect version constraints in ESLint's `package.json`. ESLint and its dependencies will be included in the user's lock file if one exists, but ESLint's own lock file would not be used.
145
+
146
+ We intentionally don't lock dependency versions so that we have the latest compatible dependency versions in development and CI that our users get when installing ESLint in a project.
147
+
148
+ The Twilio blog has a [deeper dive](https://www.twilio.com/blog/lockfiles-nodejs) to learn more.
149
+
139
150
  ## <a name="releases"></a>Releases
140
151
 
141
152
  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.
@@ -177,6 +188,15 @@ ESLint follows [semantic versioning](https://semver.org). However, due to the na
177
188
 
178
189
  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.
179
190
 
191
+ ## <a name="stylistic-rule-updates"></a>Stylistic Rule Updates
192
+
193
+ 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.
194
+ This means:
195
+
196
+ * **Bug fixes**: We will still fix bugs in stylistic rules.
197
+ * **New ECMAScript features**: We will also make sure stylistic rules are compatible with new ECMAScript features.
198
+ * **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.
199
+
180
200
  ## <a name="license"></a>License
181
201
 
182
202
  [![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)
@@ -260,10 +280,10 @@ The following companies, organizations, and individuals support ESLint's ongoing
260
280
  <!-- NOTE: This section is autogenerated. Do not manually edit.-->
261
281
  <!--sponsorsstart-->
262
282
  <h3>Platinum Sponsors</h3>
263
- <p><a href="https://automattic.com"><img src="https://images.opencollective.com/photomatt/ff91f0b/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
264
- <p><a href="https://google.com/chrome"><img src="https://images.opencollective.com/chrome/dc55bd4/logo.png" alt="Chrome's Web Framework & Tools Performance Fund" height="96"></a> <a href="https://www.shopify.com"><img src="https://images.opencollective.com/shopify/e780cd4/logo.png" alt="Shopify" height="96"></a> <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://aka.ms/microsoftfossfund"><img src="https://avatars1.githubusercontent.com/u/67931232?u=7fddc652a464d7151b97e8f108392af7d54fa3e8&v=4" alt="Microsoft FOSS Fund Sponsorships" height="96"></a></p><h3>Silver Sponsors</h3>
265
- <p><a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://www.ampproject.org/"><img src="https://images.opencollective.com/amp/c8a3b25/logo.png" alt="AMP Project" height="64"></a></p><h3>Bronze Sponsors</h3>
266
- <p><a href="https://streamat.se"><img src="https://images.opencollective.com/streamat/46890db/logo.png" alt="Streamat" height="32"></a> <a href="https://thestandarddaily.com/"><img src="https://images.opencollective.com/eric-watson/db4e598/avatar.png" alt="The Standard Daily" height="32"></a> <a href="https://writersperhour.com"><img src="https://images.opencollective.com/writersperhour/5787d4b/logo.png" alt="Writers Per Hour" height="32"></a> <a href="https://www.betacalendars.com/february-calendar.html"><img src="https://images.opencollective.com/betacalendars/9334b33/logo.png" alt="February 2021 calendar" height="32"></a> <a href="https://buy.fineproxy.org/eng/"><img src="https://images.opencollective.com/buy-fineproxy-org/b282e39/logo.png" alt="Buy.Fineproxy.Org" 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="null"><img src="https://images.opencollective.com/bugsnag-stability-monitoring/c2cef36/logo.png" alt="Bugsnag Stability Monitoring" height="32"></a> <a href="https://mixpanel.com"><img src="https://images.opencollective.com/mixpanel/cd682f7/logo.png" alt="Mixpanel" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" 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://discordapp.com"><img src="https://images.opencollective.com/discordapp/7e3d9a9/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.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a></p>
283
+ <p><a href="https://automattic.com"><img src="https://images.opencollective.com/photomatt/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
284
+ <p><a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="96"></a> <a href="https://google.com/chrome"><img src="https://images.opencollective.com/chrome/dc55bd4/logo.png" alt="Chrome's Web Framework & Tools Performance Fund" height="96"></a> <a href="https://www.shopify.com"><img src="https://images.opencollective.com/shopify/e780cd4/logo.png" alt="Shopify" height="96"></a> <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://substack.com/"><img src="https://avatars.githubusercontent.com/u/53023767?v=4" alt="Substack" height="96"></a> <a href="https://aka.ms/microsoftfossfund"><img src="https://avatars.githubusercontent.com/u/67931232?u=7fddc652a464d7151b97e8f108392af7d54fa3e8&v=4" alt="Microsoft FOSS Fund Sponsorships" height="96"></a></p><h3>Silver Sponsors</h3>
285
+ <p><a href="https://retool.com/"><img src="https://images.opencollective.com/retool/98ea68e/logo.png" alt="Retool" 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>
286
+ <p><a href="https://buy.fineproxy.org/eng/"><img src="https://images.opencollective.com/buy-fineproxy-org/b282e39/logo.png" alt="Buy.Fineproxy.Org" 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="null"><img src="https://images.opencollective.com/bugsnag-stability-monitoring/c2cef36/logo.png" alt="Bugsnag Stability Monitoring" height="32"></a> <a href="https://mixpanel.com"><img src="https://images.opencollective.com/mixpanel/cd682f7/logo.png" alt="Mixpanel" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" 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://discordapp.com"><img src="https://images.opencollective.com/discordapp/7e3d9a9/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.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a></p>
267
287
  <!--sponsorsend-->
268
288
 
269
289
  ## <a name="technology-sponsors"></a>Technology Sponsors
@@ -24,6 +24,7 @@ module.exports = {
24
24
  */
25
25
  cacheLocation: "",
26
26
  cacheFile: ".eslintcache",
27
+ cacheStrategy: "metadata",
27
28
  fix: false,
28
29
  allowInlineConfig: true,
29
30
  reportUnusedDisableDirectives: void 0,
@@ -531,7 +531,7 @@ function directoryExists(resolvedPath) {
531
531
  try {
532
532
  return fs.statSync(resolvedPath).isDirectory();
533
533
  } catch (error) {
534
- if (error && error.code === "ENOENT") {
534
+ if (error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
535
535
  return false;
536
536
  }
537
537
  throw error;
@@ -589,7 +589,7 @@ class CLIEngine {
589
589
  ignore: options.ignore
590
590
  });
591
591
  const lintResultCache =
592
- options.cache ? new LintResultCache(cacheFilePath) : null;
592
+ options.cache ? new LintResultCache(cacheFilePath, options.cacheStrategy) : null;
593
593
  const linter = new Linter({ cwd: options.cwd });
594
594
 
595
595
  /** @type {ConfigArray[]} */
@@ -433,9 +433,14 @@ class FileEnumerator {
433
433
  // Enumerate the files of this directory.
434
434
  for (const entry of readdirSafeSync(directoryPath)) {
435
435
  const filePath = path.join(directoryPath, entry.name);
436
+ const fileInfo = entry.isSymbolicLink() ? statSafeSync(filePath) : entry;
437
+
438
+ if (!fileInfo) {
439
+ continue;
440
+ }
436
441
 
437
442
  // Check if the file is matched.
438
- if (entry.isFile()) {
443
+ if (fileInfo.isFile()) {
439
444
  if (!config) {
440
445
  config = configArrayFactory.getConfigArrayForFile(
441
446
  filePath,
@@ -471,7 +476,7 @@ class FileEnumerator {
471
476
  }
472
477
 
473
478
  // Dive into the sub directory.
474
- } else if (options.recursive && entry.isDirectory()) {
479
+ } else if (options.recursive && fileInfo.isDirectory()) {
475
480
  if (!config) {
476
481
  config = configArrayFactory.getConfigArrayForFile(
477
482
  filePath,
@@ -72,7 +72,7 @@ function renderMessages(messages, parentIndex, rulesMeta) {
72
72
  * @param {Object} message Message.
73
73
  * @returns {string} HTML (table row) describing a message.
74
74
  */
75
- return lodash.map(messages, message => {
75
+ return messages.map(message => {
76
76
  const lineNumber = message.line || 0;
77
77
  const columnNumber = message.column || 0;
78
78
  let ruleUrl;
@@ -103,7 +103,7 @@ function renderMessages(messages, parentIndex, rulesMeta) {
103
103
  * @returns {string} HTML string describing the results.
104
104
  */
105
105
  function renderResults(results, rulesMeta) {
106
- return lodash.map(results, (result, index) => resultTemplate({
106
+ return results.map((result, index) => resultTemplate({
107
107
  index,
108
108
  color: renderColor(result.errorCount, result.warningCount),
109
109
  filePath: result.filePath,
@@ -15,6 +15,8 @@ const stringify = require("json-stable-stringify-without-jsonify");
15
15
  const pkg = require("../../package.json");
16
16
  const hash = require("./hash");
17
17
 
18
+ const debug = require("debug")("eslint:lint-result-cache");
19
+
18
20
  //-----------------------------------------------------------------------------
19
21
  // Helpers
20
22
  //-----------------------------------------------------------------------------
@@ -22,6 +24,22 @@ const hash = require("./hash");
22
24
  const configHashCache = new WeakMap();
23
25
  const nodeVersion = process && process.version;
24
26
 
27
+ const validCacheStrategies = ["metadata", "content"];
28
+ const invalidCacheStrategyErrorMessage = `Cache strategy must be one of: ${validCacheStrategies
29
+ .map(strategy => `"${strategy}"`)
30
+ .join(", ")}`;
31
+
32
+ /**
33
+ * Tests whether a provided cacheStrategy is valid
34
+ * @param {string} cacheStrategy The cache strategy to use
35
+ * @returns {boolean} true if `cacheStrategy` is one of `validCacheStrategies`; false otherwise
36
+ */
37
+ function isValidCacheStrategy(cacheStrategy) {
38
+ return (
39
+ validCacheStrategies.indexOf(cacheStrategy) !== -1
40
+ );
41
+ }
42
+
25
43
  /**
26
44
  * Calculates the hash of the config
27
45
  * @param {ConfigArray} config The config.
@@ -49,12 +67,30 @@ class LintResultCache {
49
67
  /**
50
68
  * Creates a new LintResultCache instance.
51
69
  * @param {string} cacheFileLocation The cache file location.
52
- * configuration lookup by file path).
70
+ * @param {"metadata" | "content"} cacheStrategy The cache strategy to use.
53
71
  */
54
- constructor(cacheFileLocation) {
72
+ constructor(cacheFileLocation, cacheStrategy) {
55
73
  assert(cacheFileLocation, "Cache file location is required");
56
-
57
- this.fileEntryCache = fileEntryCache.create(cacheFileLocation);
74
+ assert(cacheStrategy, "Cache strategy is required");
75
+ assert(
76
+ isValidCacheStrategy(cacheStrategy),
77
+ invalidCacheStrategyErrorMessage
78
+ );
79
+
80
+ debug(`Caching results to ${cacheFileLocation}`);
81
+
82
+ const useChecksum = cacheStrategy === "content";
83
+
84
+ debug(
85
+ `Using "${cacheStrategy}" strategy to detect changes`
86
+ );
87
+
88
+ this.fileEntryCache = fileEntryCache.create(
89
+ cacheFileLocation,
90
+ void 0,
91
+ useChecksum
92
+ );
93
+ this.cacheFileLocation = cacheFileLocation;
58
94
  }
59
95
 
60
96
  /**
@@ -76,17 +112,28 @@ class LintResultCache {
76
112
  * was previously linted
77
113
  * If any of these are not true, we will not reuse the lint results.
78
114
  */
79
-
80
115
  const fileDescriptor = this.fileEntryCache.getFileDescriptor(filePath);
81
116
  const hashOfConfig = hashOfConfigFor(config);
82
- const changed = fileDescriptor.changed || fileDescriptor.meta.hashOfConfig !== hashOfConfig;
117
+ const changed =
118
+ fileDescriptor.changed ||
119
+ fileDescriptor.meta.hashOfConfig !== hashOfConfig;
120
+
121
+ if (fileDescriptor.notFound) {
122
+ debug(`File not found on the file system: ${filePath}`);
123
+ return null;
124
+ }
83
125
 
84
- if (fileDescriptor.notFound || changed) {
126
+ if (changed) {
127
+ debug(`Cache entry not found or no longer valid: ${filePath}`);
85
128
  return null;
86
129
  }
87
130
 
88
131
  // If source is present but null, need to reread the file from the filesystem.
89
- if (fileDescriptor.meta.results && fileDescriptor.meta.results.source === null) {
132
+ if (
133
+ fileDescriptor.meta.results &&
134
+ fileDescriptor.meta.results.source === null
135
+ ) {
136
+ debug(`Rereading cached result source from filesystem: ${filePath}`);
90
137
  fileDescriptor.meta.results.source = fs.readFileSync(filePath, "utf-8");
91
138
  }
92
139
 
@@ -112,6 +159,7 @@ class LintResultCache {
112
159
  const fileDescriptor = this.fileEntryCache.getFileDescriptor(filePath);
113
160
 
114
161
  if (fileDescriptor && !fileDescriptor.notFound) {
162
+ debug(`Updating cached result: ${filePath}`);
115
163
 
116
164
  // Serialize the result, except that we want to remove the file source if present.
117
165
  const resultToSerialize = Object.assign({}, result);
@@ -135,6 +183,7 @@ class LintResultCache {
135
183
  * @returns {void}
136
184
  */
137
185
  reconcile() {
186
+ debug(`Persisting cached results: ${this.cacheFileLocation}`);
138
187
  this.fileEntryCache.reconcile();
139
188
  }
140
189
  }
package/lib/cli.js CHANGED
@@ -62,6 +62,7 @@ function translateOptions({
62
62
  cache,
63
63
  cacheFile,
64
64
  cacheLocation,
65
+ cacheStrategy,
65
66
  config,
66
67
  env,
67
68
  errorOnUnmatchedPattern,
@@ -88,6 +89,7 @@ function translateOptions({
88
89
  allowInlineConfig: inlineConfig,
89
90
  cache,
90
91
  cacheLocation: cacheLocation || cacheFile,
92
+ cacheStrategy,
91
93
  errorOnUnmatchedPattern,
92
94
  extensions: ext,
93
95
  fix: (fix || fixDryRun) && (quiet ? quietFixPredicate : true),
@@ -43,6 +43,7 @@ const { version } = require("../../package.json");
43
43
  * @property {ConfigData} [baseConfig] Base config object, extended by all configs used with this instance
44
44
  * @property {boolean} [cache] Enable result caching.
45
45
  * @property {string} [cacheLocation] The cache file to use instead of .eslintcache.
46
+ * @property {"metadata" | "content"} [cacheStrategy] The strategy used to detect changed files.
46
47
  * @property {string} [cwd] The value to use for the current working directory.
47
48
  * @property {boolean} [errorOnUnmatchedPattern] If `false` then `ESLint#lintFiles()` doesn't throw even if no target files found. Defaults to `true`.
48
49
  * @property {string[]} [extensions] An array of file extensions to check.
@@ -157,6 +158,7 @@ function processOptions({
157
158
  baseConfig = null,
158
159
  cache = false,
159
160
  cacheLocation = ".eslintcache",
161
+ cacheStrategy = "metadata",
160
162
  cwd = process.cwd(),
161
163
  errorOnUnmatchedPattern = true,
162
164
  extensions = null, // ← should be null by default because if it's an array then it suppresses RFC20 feature.
@@ -216,6 +218,12 @@ function processOptions({
216
218
  if (!isNonEmptyString(cacheLocation)) {
217
219
  errors.push("'cacheLocation' must be a non-empty string.");
218
220
  }
221
+ if (
222
+ cacheStrategy !== "metadata" &&
223
+ cacheStrategy !== "content"
224
+ ) {
225
+ errors.push("'cacheStrategy' must be any of \"metadata\", \"content\".");
226
+ }
219
227
  if (!isNonEmptyString(cwd) || !path.isAbsolute(cwd)) {
220
228
  errors.push("'cwd' must be an absolute path.");
221
229
  }
@@ -284,6 +292,7 @@ function processOptions({
284
292
  baseConfig,
285
293
  cache,
286
294
  cacheLocation,
295
+ cacheStrategy,
287
296
  configFile: overrideConfigFile,
288
297
  cwd,
289
298
  errorOnUnmatchedPattern,
@@ -85,7 +85,7 @@ class Registry {
85
85
  * @returns {void}
86
86
  */
87
87
  populateFromCoreRules() {
88
- const rulesConfig = configRule.createCoreRuleConfigs();
88
+ const rulesConfig = configRule.createCoreRuleConfigs(/* noDeprecated = */ true);
89
89
 
90
90
  this.rules = makeRegistryItems(rulesConfig);
91
91
  }
@@ -942,7 +942,9 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parser
942
942
  });
943
943
 
944
944
  // only run code path analyzer if the top level node is "Program", skip otherwise
945
- const eventGenerator = nodeQueue[0].node.type === "Program" ? new CodePathAnalyzer(new NodeEventGenerator(emitter)) : new NodeEventGenerator(emitter);
945
+ const eventGenerator = nodeQueue[0].node.type === "Program"
946
+ ? new CodePathAnalyzer(new NodeEventGenerator(emitter, { visitorKeys: sourceCode.visitorKeys, fallback: Traverser.getKeys }))
947
+ : new NodeEventGenerator(emitter, { visitorKeys: sourceCode.visitorKeys, fallback: Traverser.getKeys });
946
948
 
947
949
  nodeQueue.forEach(traversalInfo => {
948
950
  currentNode = traversalInfo.node;
@@ -208,10 +208,12 @@ class NodeEventGenerator {
208
208
  * An SafeEmitter which is the destination of events. This emitter must already
209
209
  * have registered listeners for all of the events that it needs to listen for.
210
210
  * (See lib/linter/safe-emitter.js for more details on `SafeEmitter`.)
211
+ * @param {ESQueryOptions} esqueryOptions `esquery` options for traversing custom nodes.
211
212
  * @returns {NodeEventGenerator} new instance
212
213
  */
213
- constructor(emitter) {
214
+ constructor(emitter, esqueryOptions) {
214
215
  this.emitter = emitter;
216
+ this.esqueryOptions = esqueryOptions;
215
217
  this.currentAncestry = [];
216
218
  this.enterSelectorsByNodeType = new Map();
217
219
  this.exitSelectorsByNodeType = new Map();
@@ -250,7 +252,7 @@ class NodeEventGenerator {
250
252
  * @returns {void}
251
253
  */
252
254
  applySelector(node, selector) {
253
- if (esquery.matches(node, selector.parsedSelector, this.currentAncestry)) {
255
+ if (esquery.matches(node, selector.parsedSelector, this.currentAncestry, this.esqueryOptions)) {
254
256
  this.emitter.emit(selector.rawSelector, node);
255
257
  }
256
258
  }
@@ -115,6 +115,17 @@ function normalizeReportLoc(descriptor) {
115
115
  return descriptor.node.loc;
116
116
  }
117
117
 
118
+ /**
119
+ * Check that a fix has a valid range.
120
+ * @param {Fix|null} fix The fix to validate.
121
+ * @returns {void}
122
+ */
123
+ function assertValidFix(fix) {
124
+ if (fix) {
125
+ assert(fix.range && typeof fix.range[0] === "number" && typeof fix.range[1] === "number", `Fix has invalid range: ${JSON.stringify(fix, null, 2)}`);
126
+ }
127
+ }
128
+
118
129
  /**
119
130
  * Compares items in a fixes array by range.
120
131
  * @param {Fix} a The first message.
@@ -133,6 +144,10 @@ function compareFixesByRange(a, b) {
133
144
  * @returns {{text: string, range: number[]}} The merged fixes
134
145
  */
135
146
  function mergeFixes(fixes, sourceCode) {
147
+ for (const fix of fixes) {
148
+ assertValidFix(fix);
149
+ }
150
+
136
151
  if (fixes.length === 0) {
137
152
  return null;
138
153
  }
@@ -181,6 +196,8 @@ function normalizeFixes(descriptor, sourceCode) {
181
196
  if (fix && Symbol.iterator in fix) {
182
197
  return mergeFixes(Array.from(fix), sourceCode);
183
198
  }
199
+
200
+ assertValidFix(fix);
184
201
  return fix;
185
202
  }
186
203
 
package/lib/options.js CHANGED
@@ -214,6 +214,14 @@ module.exports = optionator({
214
214
  type: "path::String",
215
215
  description: "Path to the cache file or directory"
216
216
  },
217
+ {
218
+ option: "cache-strategy",
219
+ dependsOn: ["cache"],
220
+ type: "String",
221
+ default: "metadata",
222
+ enum: ["metadata", "content"],
223
+ description: "Strategy to use for detecting changed files in the cache"
224
+ },
217
225
  {
218
226
  heading: "Miscellaneous"
219
227
  },
@@ -427,12 +427,12 @@ class RuleTester {
427
427
  scenarioErrors = [],
428
428
  linter = this.linter;
429
429
 
430
- if (lodash.isNil(test) || typeof test !== "object") {
430
+ if (!test || typeof test !== "object") {
431
431
  throw new TypeError(`Test Scenarios for rule ${ruleName} : Could not find test scenario object`);
432
432
  }
433
433
 
434
434
  requiredScenarios.forEach(scenarioType => {
435
- if (lodash.isNil(test[scenarioType])) {
435
+ if (!test[scenarioType]) {
436
436
  scenarioErrors.push(`Could not find any ${scenarioType} test scenarios`);
437
437
  }
438
438
  });
@@ -54,7 +54,7 @@ module.exports = {
54
54
  },
55
55
  LF = "\n",
56
56
  CRLF = `\r${LF}`,
57
- endsWithNewline = lodash.endsWith(src, LF);
57
+ endsWithNewline = src.endsWith(LF);
58
58
 
59
59
  /*
60
60
  * Empty source is always valid: No content in file so we don't
@@ -954,7 +954,7 @@ module.exports = {
954
954
  }
955
955
 
956
956
  /**
957
- * Checks wether a return statement is wrapped in ()
957
+ * Checks whether a return statement is wrapped in ()
958
958
  * @param {ASTNode} node node to examine
959
959
  * @returns {boolean} the result
960
960
  */
@@ -1178,6 +1178,7 @@ module.exports = {
1178
1178
  offsets.setDesiredOffset(colonToken, firstToken, 1);
1179
1179
 
1180
1180
  offsets.setDesiredOffset(firstConsequentToken, firstToken,
1181
+ firstConsequentToken.type === "Punctuator" &&
1181
1182
  options.offsetTernaryExpressions ? 2 : 1);
1182
1183
 
1183
1184
  /*
@@ -8,8 +8,7 @@
8
8
  // Requirements
9
9
  //------------------------------------------------------------------------------
10
10
 
11
- const lodash = require("lodash"),
12
- astUtils = require("./utils/ast-utils");
11
+ const astUtils = require("./utils/ast-utils");
13
12
 
14
13
  //------------------------------------------------------------------------------
15
14
  // Helpers
@@ -347,7 +346,7 @@ module.exports = {
347
346
  const nextTokenOrComment = sourceCode.getTokenAfter(token, { includeComments: true });
348
347
 
349
348
  // check for newline before
350
- if (!exceptionStartAllowed && before && !lodash.includes(commentAndEmptyLines, prevLineNum) &&
349
+ if (!exceptionStartAllowed && before && !commentAndEmptyLines.includes(prevLineNum) &&
351
350
  !(astUtils.isCommentToken(previousTokenOrComment) && astUtils.isTokenOnSameLine(previousTokenOrComment, token))) {
352
351
  const lineStart = token.range[0] - token.loc.start.column;
353
352
  const range = [lineStart, lineStart];
@@ -362,7 +361,7 @@ module.exports = {
362
361
  }
363
362
 
364
363
  // check for newline after
365
- if (!exceptionEndAllowed && after && !lodash.includes(commentAndEmptyLines, nextLineNum) &&
364
+ if (!exceptionEndAllowed && after && !commentAndEmptyLines.includes(nextLineNum) &&
366
365
  !(astUtils.isCommentToken(nextTokenOrComment) && astUtils.isTokenOnSameLine(token, nextTokenOrComment))) {
367
366
  context.report({
368
367
  node: token,
@@ -71,7 +71,7 @@ module.exports = {
71
71
  type: "suggestion",
72
72
 
73
73
  docs: {
74
- description: "enforce a maximum number of line of code in a function",
74
+ description: "enforce a maximum number of lines of code in a function",
75
75
  category: "Stylistic Issues",
76
76
  recommended: false,
77
77
  url: "https://eslint.org/docs/rules/max-lines-per-function"
@@ -151,7 +151,7 @@ module.exports = {
151
151
  );
152
152
 
153
153
  lines = lines.filter(
154
- l => !lodash.includes(commentLines, l.lineNumber)
154
+ l => !commentLines.includes(l.lineNumber)
155
155
  );
156
156
  }
157
157
 
@@ -844,45 +844,49 @@ module.exports = {
844
844
  ExportDefaultDeclaration: node => checkExpressionOrExportStatement(node.declaration),
845
845
  ExpressionStatement: node => checkExpressionOrExportStatement(node.expression),
846
846
 
847
- "ForInStatement, ForOfStatement"(node) {
848
- if (node.left.type !== "VariableDeclarator") {
847
+ ForInStatement(node) {
848
+ if (node.left.type !== "VariableDeclaration") {
849
849
  const firstLeftToken = sourceCode.getFirstToken(node.left, astUtils.isNotOpeningParenToken);
850
850
 
851
851
  if (
852
- firstLeftToken.value === "let" && (
853
-
854
- /*
855
- * If `let` is the only thing on the left side of the loop, it's the loop variable: `for ((let) of foo);`
856
- * Removing it will cause a syntax error, because it will be parsed as the start of a VariableDeclarator.
857
- */
858
- (firstLeftToken.range[1] === node.left.range[1] || /*
859
- * If `let` is followed by a `[` token, it's a property access on the `let` value: `for ((let[foo]) of bar);`
860
- * Removing it will cause the property access to be parsed as a destructuring declaration of `foo` instead.
861
- */
862
- astUtils.isOpeningBracketToken(
863
- sourceCode.getTokenAfter(firstLeftToken, astUtils.isNotClosingParenToken)
864
- ))
852
+ firstLeftToken.value === "let" &&
853
+ astUtils.isOpeningBracketToken(
854
+ sourceCode.getTokenAfter(firstLeftToken, astUtils.isNotClosingParenToken)
865
855
  )
866
856
  ) {
857
+
858
+ // ForInStatement#left expression cannot start with `let[`.
867
859
  tokensToIgnore.add(firstLeftToken);
868
860
  }
869
861
  }
870
862
 
871
- if (node.type === "ForOfStatement") {
872
- const hasExtraParens = node.right.type === "SequenceExpression"
873
- ? hasDoubleExcessParens(node.right)
874
- : hasExcessParens(node.right);
863
+ if (hasExcessParens(node.left)) {
864
+ report(node.left);
865
+ }
875
866
 
876
- if (hasExtraParens) {
877
- report(node.right);
878
- }
879
- } else if (hasExcessParens(node.right)) {
867
+ if (hasExcessParens(node.right)) {
880
868
  report(node.right);
881
869
  }
870
+ },
871
+
872
+ ForOfStatement(node) {
873
+ if (node.left.type !== "VariableDeclaration") {
874
+ const firstLeftToken = sourceCode.getFirstToken(node.left, astUtils.isNotOpeningParenToken);
875
+
876
+ if (firstLeftToken.value === "let") {
877
+
878
+ // ForOfStatement#left expression cannot start with `let`.
879
+ tokensToIgnore.add(firstLeftToken);
880
+ }
881
+ }
882
882
 
883
883
  if (hasExcessParens(node.left)) {
884
884
  report(node.left);
885
885
  }
886
+
887
+ if (hasExcessParensWithPrecedence(node.right, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
888
+ report(node.right);
889
+ }
886
890
  },
887
891
 
888
892
  ForStatement(node) {
@@ -69,6 +69,28 @@ module.exports = {
69
69
  return node && node.type === "Literal" && typeof node.value === "string";
70
70
  }
71
71
 
72
+ /**
73
+ * Gets flags of a regular expression created by the given `RegExp()` or `new RegExp()` call
74
+ * Examples:
75
+ * new RegExp(".") // => ""
76
+ * new RegExp(".", "gu") // => "gu"
77
+ * new RegExp(".", flags) // => null
78
+ * @param {ASTNode} node `CallExpression` or `NewExpression` node
79
+ * @returns {string|null} flags if they can be determined, `null` otherwise
80
+ * @private
81
+ */
82
+ function getFlags(node) {
83
+ if (node.arguments.length < 2) {
84
+ return "";
85
+ }
86
+
87
+ if (isString(node.arguments[1])) {
88
+ return node.arguments[1].value;
89
+ }
90
+
91
+ return null;
92
+ }
93
+
72
94
  /**
73
95
  * Check syntax error in a given pattern.
74
96
  * @param {string} pattern The RegExp pattern to validate.
@@ -104,18 +126,23 @@ module.exports = {
104
126
  return;
105
127
  }
106
128
  const pattern = node.arguments[0].value;
107
- let flags = isString(node.arguments[1]) ? node.arguments[1].value : "";
129
+ let flags = getFlags(node);
108
130
 
109
- if (allowedFlags) {
131
+ if (flags && allowedFlags) {
110
132
  flags = flags.replace(allowedFlags, "");
111
133
  }
112
134
 
113
- // If flags are unknown, check both are errored or not.
114
- const message = validateRegExpFlags(flags) || (
115
- flags
116
- ? validateRegExpPattern(pattern, flags.indexOf("u") !== -1)
117
- : validateRegExpPattern(pattern, true) && validateRegExpPattern(pattern, false)
118
- );
135
+ const message =
136
+ (
137
+ flags && validateRegExpFlags(flags)
138
+ ) ||
139
+ (
140
+
141
+ // If flags are unknown, report the regex only if its pattern is invalid both with and without the "u" flag
142
+ flags === null
143
+ ? validateRegExpPattern(pattern, true) && validateRegExpPattern(pattern, false)
144
+ : validateRegExpPattern(pattern, flags.includes("u"))
145
+ );
119
146
 
120
147
  if (message) {
121
148
  context.report({
@@ -105,9 +105,9 @@ module.exports = {
105
105
  }
106
106
 
107
107
  /**
108
- * Converts an integer to to an object containing the the integer's coefficient and order of magnitude
108
+ * Converts an integer to to an object containing the integer's coefficient and order of magnitude
109
109
  * @param {string} stringInteger the string representation of the integer being converted
110
- * @returns {Object} the object containing the the integer's coefficient and order of magnitude
110
+ * @returns {Object} the object containing the integer's coefficient and order of magnitude
111
111
  */
112
112
  function normalizeInteger(stringInteger) {
113
113
  const significantDigits = removeTrailingZeros(removeLeadingZeros(stringInteger));
@@ -120,9 +120,9 @@ module.exports = {
120
120
 
121
121
  /**
122
122
  *
123
- * Converts a float to to an object containing the the floats's coefficient and order of magnitude
123
+ * Converts a float to to an object containing the floats's coefficient and order of magnitude
124
124
  * @param {string} stringFloat the string representation of the float being converted
125
- * @returns {Object} the object containing the the integer's coefficient and order of magnitude
125
+ * @returns {Object} the object containing the integer's coefficient and order of magnitude
126
126
  */
127
127
  function normalizeFloat(stringFloat) {
128
128
  const trimmedFloat = removeLeadingZeros(stringFloat);
@@ -44,7 +44,8 @@ module.exports = {
44
44
  ],
45
45
 
46
46
  messages: {
47
- noShadow: "'{{name}}' is already declared in the upper scope."
47
+ noShadow: "'{{name}}' is already declared in the upper scope on line {{shadowedLine}} column {{shadowedColumn}}.",
48
+ noShadowGlobal: "'{{name}}' is already a global variable."
48
49
  }
49
50
  },
50
51
 
@@ -117,6 +118,29 @@ module.exports = {
117
118
  return def && def.name.range;
118
119
  }
119
120
 
121
+ /**
122
+ * Get declared line and column of a variable.
123
+ * @param {eslint-scope.Variable} variable The variable to get.
124
+ * @returns {Object} The declared line and column of the variable.
125
+ */
126
+ function getDeclaredLocation(variable) {
127
+ const identifier = variable.identifiers[0];
128
+ let obj;
129
+
130
+ if (identifier) {
131
+ obj = {
132
+ global: false,
133
+ line: identifier.loc.start.line,
134
+ column: identifier.loc.start.column + 1
135
+ };
136
+ } else {
137
+ obj = {
138
+ global: true
139
+ };
140
+ }
141
+ return obj;
142
+ }
143
+
120
144
  /**
121
145
  * Checks if a variable is in TDZ of scopeVar.
122
146
  * @param {Object} variable The variable to check.
@@ -165,10 +189,18 @@ module.exports = {
165
189
  !isOnInitializer(variable, shadowed) &&
166
190
  !(options.hoist !== "all" && isInTdz(variable, shadowed))
167
191
  ) {
192
+ const location = getDeclaredLocation(shadowed);
193
+ const messageId = location.global ? "noShadowGlobal" : "noShadow";
194
+ const data = { name: variable.name };
195
+
196
+ if (!location.global) {
197
+ data.shadowedLine = location.line;
198
+ data.shadowedColumn = location.column;
199
+ }
168
200
  context.report({
169
201
  node: variable.identifiers[0],
170
- messageId: "noShadow",
171
- data: variable
202
+ messageId,
203
+ data
172
204
  });
173
205
  }
174
206
  }
@@ -50,6 +50,10 @@ module.exports = {
50
50
  allowTaggedTemplates: {
51
51
  type: "boolean",
52
52
  default: false
53
+ },
54
+ enforceForJSX: {
55
+ type: "boolean",
56
+ default: false
53
57
  }
54
58
  },
55
59
  additionalProperties: false
@@ -65,7 +69,8 @@ module.exports = {
65
69
  const config = context.options[0] || {},
66
70
  allowShortCircuit = config.allowShortCircuit || false,
67
71
  allowTernary = config.allowTernary || false,
68
- allowTaggedTemplates = config.allowTaggedTemplates || false;
72
+ allowTaggedTemplates = config.allowTaggedTemplates || false,
73
+ enforceForJSX = config.enforceForJSX || false;
69
74
 
70
75
  // eslint-disable-next-line jsdoc/require-description
71
76
  /**
@@ -140,6 +145,12 @@ module.exports = {
140
145
  },
141
146
  FunctionExpression: alwaysTrue,
142
147
  Identifier: alwaysTrue,
148
+ JSXElement() {
149
+ return enforceForJSX;
150
+ },
151
+ JSXFragment() {
152
+ return enforceForJSX;
153
+ },
143
154
  Literal: alwaysTrue,
144
155
  LogicalExpression(node) {
145
156
  if (allowShortCircuit) {
@@ -5,6 +5,12 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const astUtils = require("./utils/ast-utils");
13
+
8
14
  //------------------------------------------------------------------------------
9
15
  // Rule Definition
10
16
  //------------------------------------------------------------------------------
@@ -54,11 +60,10 @@ module.exports = {
54
60
  * Reports error for unnecessarily renamed assignments
55
61
  * @param {ASTNode} node node to report
56
62
  * @param {ASTNode} initial node with initial name value
57
- * @param {ASTNode} result node with new name value
58
63
  * @param {string} type the type of the offending node
59
64
  * @returns {void}
60
65
  */
61
- function reportError(node, initial, result, type) {
66
+ function reportError(node, initial, type) {
62
67
  const name = initial.type === "Identifier" ? initial.name : initial.value;
63
68
 
64
69
  return context.report({
@@ -69,18 +74,21 @@ module.exports = {
69
74
  type
70
75
  },
71
76
  fix(fixer) {
72
- if (sourceCode.commentsExistBetween(initial, result)) {
77
+ const replacementNode = node.type === "Property" ? node.value : node.local;
78
+
79
+ if (sourceCode.getCommentsInside(node).length > sourceCode.getCommentsInside(replacementNode).length) {
73
80
  return null;
74
81
  }
75
82
 
76
- const replacementText = result.type === "AssignmentPattern"
77
- ? sourceCode.getText(result)
78
- : name;
83
+ // Don't autofix code such as `({foo: (foo) = a} = obj);`, parens are not allowed in shorthand properties.
84
+ if (
85
+ replacementNode.type === "AssignmentPattern" &&
86
+ astUtils.isParenthesised(sourceCode, replacementNode.left)
87
+ ) {
88
+ return null;
89
+ }
79
90
 
80
- return fixer.replaceTextRange([
81
- initial.range[0],
82
- result.range[1]
83
- ], replacementText);
91
+ return fixer.replaceText(node, sourceCode.getText(replacementNode));
84
92
  }
85
93
  });
86
94
  }
@@ -97,19 +105,11 @@ module.exports = {
97
105
 
98
106
  for (const property of node.properties) {
99
107
 
100
- /*
101
- * TODO: Remove after babel-eslint removes ExperimentalRestProperty
102
- * https://github.com/eslint/eslint/issues/12335
103
- */
104
- if (property.type === "ExperimentalRestProperty") {
105
- continue;
106
- }
107
-
108
108
  /**
109
109
  * Properties using shorthand syntax and rest elements can not be renamed.
110
110
  * If the property is computed, we have no idea if a rename is useless or not.
111
111
  */
112
- if (property.shorthand || property.type === "RestElement" || property.computed) {
112
+ if (property.type !== "Property" || property.shorthand || property.computed) {
113
113
  continue;
114
114
  }
115
115
 
@@ -117,7 +117,7 @@ module.exports = {
117
117
  const renamedKey = property.value.type === "AssignmentPattern" ? property.value.left.name : property.value.name;
118
118
 
119
119
  if (key === renamedKey) {
120
- reportError(property, property.key, property.value, "Destructuring assignment");
120
+ reportError(property, property.key, "Destructuring assignment");
121
121
  }
122
122
  }
123
123
  }
@@ -134,7 +134,7 @@ module.exports = {
134
134
 
135
135
  if (node.imported.name === node.local.name &&
136
136
  node.imported.range[0] !== node.local.range[0]) {
137
- reportError(node, node.imported, node.local, "Import");
137
+ reportError(node, node.imported, "Import");
138
138
  }
139
139
  }
140
140
 
@@ -150,7 +150,7 @@ module.exports = {
150
150
 
151
151
  if (node.local.name === node.exported.name &&
152
152
  node.local.range[0] !== node.exported.range[0]) {
153
- reportError(node, node.local, node.exported, "Export");
153
+ reportError(node, node.local, "Export");
154
154
  }
155
155
 
156
156
  }
@@ -82,7 +82,7 @@ function normalizeOptionValue(value) {
82
82
  function normalizeOptions(options) {
83
83
  const isNodeSpecificOption = lodash.overSome([lodash.isPlainObject, lodash.isString]);
84
84
 
85
- if (lodash.isPlainObject(options) && lodash.some(options, isNodeSpecificOption)) {
85
+ if (lodash.isPlainObject(options) && Object.values(options).some(isNodeSpecificOption)) {
86
86
  return {
87
87
  ObjectExpression: normalizeOptionValue(options.ObjectExpression),
88
88
  ObjectPattern: normalizeOptionValue(options.ObjectPattern),
@@ -5,6 +5,11 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const FixTracker = require("./utils/fix-tracker");
8
13
  const astUtils = require("./utils/ast-utils");
9
14
 
10
15
  //------------------------------------------------------------------------------
@@ -451,10 +456,18 @@ module.exports = {
451
456
  messageId: "useConst",
452
457
  data: node,
453
458
  fix: shouldFix
454
- ? fixer => fixer.replaceText(
455
- sourceCode.getFirstToken(varDeclParent, t => t.value === varDeclParent.kind),
456
- "const"
457
- )
459
+ ? fixer => {
460
+ const letKeywordToken = sourceCode.getFirstToken(varDeclParent, t => t.value === varDeclParent.kind);
461
+
462
+ /**
463
+ * Extend the replacement range to the whole declaration,
464
+ * in order to prevent other fixes in the same pass
465
+ * https://github.com/eslint/eslint/issues/13899
466
+ */
467
+ return new FixTracker(fixer, sourceCode)
468
+ .retainRange(varDeclParent.range)
469
+ .replaceTextRange(letKeywordToken.range, "const");
470
+ }
458
471
  : null
459
472
  });
460
473
  });
@@ -168,7 +168,7 @@ module.exports = {
168
168
  * Assignment expression is not fixed.
169
169
  * Array destructuring is not fixed.
170
170
  * Renamed property is not fixed.
171
- * @param {ASTNode} node the the node to evaluate
171
+ * @param {ASTNode} node the node to evaluate
172
172
  * @returns {boolean} whether or not the node should be fixed
173
173
  */
174
174
  function shouldFix(node) {
@@ -132,7 +132,9 @@ module.exports = {
132
132
 
133
133
  if (nonSpacedConsequentNode) {
134
134
  report(node, nonSpacedConsequentNode);
135
- } else if (nonSpacedAlternateNode) {
135
+ }
136
+
137
+ if (nonSpacedAlternateNode) {
136
138
  report(node, nonSpacedAlternateNode);
137
139
  }
138
140
  }
@@ -101,7 +101,7 @@ module.exports = {
101
101
  }
102
102
 
103
103
  /**
104
- * Checks the the given `CallExpression` node for `.indexOf(NaN)` and `.lastIndexOf(NaN)`.
104
+ * Checks the given `CallExpression` node for `.indexOf(NaN)` and `.lastIndexOf(NaN)`.
105
105
  * @param {ASTNode} node The node to check.
106
106
  * @returns {void}
107
107
  */
@@ -11,7 +11,6 @@
11
11
 
12
12
  const path = require("path");
13
13
  const spawn = require("cross-spawn");
14
- const { isEmpty } = require("lodash");
15
14
  const log = require("../shared/logging");
16
15
  const packageJson = require("../../package.json");
17
16
 
@@ -107,7 +106,7 @@ function environment() {
107
106
  * Checking globally returns an empty JSON object, while local checks
108
107
  * include the name and version of the local project.
109
108
  */
110
- if (isEmpty(parsedStdout) || !(parsedStdout.dependencies && parsedStdout.dependencies.eslint)) {
109
+ if (Object.keys(parsedStdout).length === 0 || !(parsedStdout.dependencies && parsedStdout.dependencies.eslint)) {
111
110
  return "Not found";
112
111
  }
113
112
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "7.18.0",
3
+ "version": "7.22.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "bin": {
@@ -46,8 +46,8 @@
46
46
  "homepage": "https://eslint.org",
47
47
  "bugs": "https://github.com/eslint/eslint/issues/",
48
48
  "dependencies": {
49
- "@babel/code-frame": "^7.0.0",
50
- "@eslint/eslintrc": "^0.3.0",
49
+ "@babel/code-frame": "7.12.11",
50
+ "@eslint/eslintrc": "^0.4.0",
51
51
  "ajv": "^6.10.0",
52
52
  "chalk": "^4.0.0",
53
53
  "cross-spawn": "^7.0.2",
@@ -58,12 +58,12 @@
58
58
  "eslint-utils": "^2.1.0",
59
59
  "eslint-visitor-keys": "^2.0.0",
60
60
  "espree": "^7.3.1",
61
- "esquery": "^1.2.0",
61
+ "esquery": "^1.4.0",
62
62
  "esutils": "^2.0.2",
63
- "file-entry-cache": "^6.0.0",
63
+ "file-entry-cache": "^6.0.1",
64
64
  "functional-red-black-tree": "^1.0.1",
65
65
  "glob-parent": "^5.0.0",
66
- "globals": "^12.1.0",
66
+ "globals": "^13.6.0",
67
67
  "ignore": "^4.0.6",
68
68
  "import-fresh": "^3.0.0",
69
69
  "imurmurhash": "^0.1.4",
@@ -71,7 +71,7 @@
71
71
  "js-yaml": "^3.13.1",
72
72
  "json-stable-stringify-without-jsonify": "^1.0.1",
73
73
  "levn": "^0.4.1",
74
- "lodash": "^4.17.20",
74
+ "lodash": "^4.17.21",
75
75
  "minimatch": "^3.0.4",
76
76
  "natural-compare": "^1.4.0",
77
77
  "optionator": "^0.9.1",
@@ -108,11 +108,11 @@
108
108
  "fs-teardown": "^0.1.0",
109
109
  "glob": "^7.1.6",
110
110
  "jsdoc": "^3.5.5",
111
- "karma": "^4.0.1",
111
+ "karma": "^6.1.1",
112
112
  "karma-chrome-launcher": "^3.1.0",
113
- "karma-mocha": "^1.3.0",
114
- "karma-mocha-reporter": "^2.2.3",
115
- "karma-webpack": "^4.0.0-rc.6",
113
+ "karma-mocha": "^2.0.1",
114
+ "karma-mocha-reporter": "^2.2.5",
115
+ "karma-webpack": "^5.0.0",
116
116
  "lint-staged": "^10.1.2",
117
117
  "load-perf": "^0.2.0",
118
118
  "markdownlint": "^0.19.0",
@@ -120,17 +120,18 @@
120
120
  "memfs": "^3.0.1",
121
121
  "mocha": "^7.1.1",
122
122
  "mocha-junit-reporter": "^1.23.0",
123
+ "node-polyfill-webpack-plugin": "^1.0.3",
123
124
  "npm-license": "^0.3.3",
124
125
  "nyc": "^15.0.1",
125
126
  "proxyquire": "^2.0.1",
126
- "puppeteer": "^4.0.0",
127
+ "puppeteer": "^7.1.0",
127
128
  "recast": "^0.19.0",
128
129
  "regenerator-runtime": "^0.13.2",
129
130
  "shelljs": "^0.8.2",
130
131
  "sinon": "^9.0.1",
131
132
  "temp": "^0.9.0",
132
- "webpack": "^4.35.0",
133
- "webpack-cli": "^3.3.5",
133
+ "webpack": "^5.23.0",
134
+ "webpack-cli": "^4.5.0",
134
135
  "yorkie": "^2.0.0"
135
136
  },
136
137
  "keywords": [