import eslint 7.18.0

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
Thomas Lamprecht 2021-01-19 16:04:13 +01:00
parent 07e2881e8e
commit 456be15ea9
78 changed files with 3290 additions and 13535 deletions

View File

@ -10,7 +10,7 @@ DSC=${PACKAGE}_${DEB_VERSION_UPSTREAM_REVISION}.dsc
SRCDIR=src SRCDIR=src
UPSTREAM=eslint UPSTREAM=eslint
UPSTREAMTAG=v7.12.1 UPSTREAMTAG=v7.18.0
BUILDSRC=${UPSTREAM}-${UPSTREAMTAG} BUILDSRC=${UPSTREAM}-${UPSTREAMTAG}
all: ${DEB} all: ${DEB}

View File

@ -26,7 +26,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest] os: [ubuntu-latest]
node: [14.x, 13.x, 12.x, 10.x, "10.12.0"] node: [15.x, 14.x, 13.x, 12.x, 10.x, "10.12.0"]
include: include:
- os: windows-latest - os: windows-latest
node: "12.x" node: "12.x"
@ -40,6 +40,10 @@ jobs:
node-version: ${{ matrix.node }} node-version: ${{ matrix.node }}
- name: Install Packages - name: Install Packages
run: npm install run: npm install
if: ${{ !startswith(matrix.node, '15') }}
- name: Install Packages
run: npm install --legacy-peer-deps
if: ${{ startswith(matrix.node, '15') }}
- name: Test - name: Test
run: node Makefile mocha run: node Makefile mocha
- name: Fuzz Test - name: Fuzz Test

View File

@ -0,0 +1,7 @@
- id: eslint
name: eslint
entry: eslint
description: "An AST-based pattern checker for JavaScript."
language: node
types: ['javascript']
require_serial: false

View File

@ -1,3 +1,83 @@
v7.18.0 - January 15, 2021
* [`e3264b2`](https://github.com/eslint/eslint/commit/e3264b26a625d926a1ea96df1c4b643af5c3797c) Upgrade: @eslint/eslintrc to improve error message for invalid extends (#14009) (Milos Djermanovic)
* [`f17c3c3`](https://github.com/eslint/eslint/commit/f17c3c371789ffa84f0cda57101e8193899adbe6) Update: check logical assignment operators in the complexity rule (#13979) (Milos Djermanovic)
* [`672deb0`](https://github.com/eslint/eslint/commit/672deb057a14a7acad8c669189870009f1edb8a6) Docs: fix no-invalid-regexp docs regarding ecmaVersion (#13991) (Milos Djermanovic)
* [`179a910`](https://github.com/eslint/eslint/commit/179a910b32e853bc12a9dd71f7c10e762cbeac44) Fix: --init crash on question to upgrade/downgrade ESLint (fixes #13978) (#13995) (Milos Djermanovic)
* [`292b1c0`](https://github.com/eslint/eslint/commit/292b1c0017bc442d399f67e01d699c59e6b71453) Fix: no-extra-parens false positive with `let` identifier in for-loop (#13981) (Milos Djermanovic)
* [`de61f94`](https://github.com/eslint/eslint/commit/de61f9444cf58a4d70e126ab3d10bf20851de7c9) Sponsors: Sync README with website (ESLint Jenkins)
* [`9250d16`](https://github.com/eslint/eslint/commit/9250d167ceb5684669eabe93dae326e33f0684f2) Upgrade: Bump lodash to fix security issue (#13993) (Frederik Prijck)
* [`75fea9b`](https://github.com/eslint/eslint/commit/75fea9bcdd3dde5a07e0089d9011a4df518cdbe3) Sponsors: Sync README with website (ESLint Jenkins)
* [`f2687e7`](https://github.com/eslint/eslint/commit/f2687e71f9e2a2773f821c4dc1a02abe95b97df4) Docs: update space-in-parens related rules (#13985) (Chris Brody)
* [`4a38bbe`](https://github.com/eslint/eslint/commit/4a38bbe81b4b29ca1a4e62d0a0cc8d525455b063) Docs: space-in-parens examples with no arguments etc. (#13987) (Chris Brody)
* [`3e49169`](https://github.com/eslint/eslint/commit/3e491698687aa08b3b798cee0931f0872ca1bc55) Sponsors: Sync README with website (ESLint Jenkins)
* [`c5bf1f2`](https://github.com/eslint/eslint/commit/c5bf1f2150a9fbbb9e74c04808dc3bfeda1ed321) Sponsors: Sync README with website (ESLint Jenkins)
* [`98a729c`](https://github.com/eslint/eslint/commit/98a729c9def54cee9e5478e75e8bd6f28167d5e8) Sponsors: Sync README with website (ESLint Jenkins)
* [`e83a696`](https://github.com/eslint/eslint/commit/e83a6962b51b05c2ddfe42b0748b405d515eeb9d) Sponsors: Sync README with website (ESLint Jenkins)
* [`78cb483`](https://github.com/eslint/eslint/commit/78cb48345c725e9f90fd0e631c476802244df4a4) Chore: test `foo( )` with space-in-parens option "always" (#13986) (Chris Brody)
* [`f6948f6`](https://github.com/eslint/eslint/commit/f6948f6bdc763dca0787bb2786bc9f6f9ed88f43) Docs: Update semantic versioning policy (#13970) (Nicholas C. Zakas)
* [`0688212`](https://github.com/eslint/eslint/commit/068821248e2d2eff11152f270102d537d8fa8126) Sponsors: Sync README with website (ESLint Jenkins)
* [`aeba5e5`](https://github.com/eslint/eslint/commit/aeba5e5e6062095a06d9b867d7e7ee75422f25b9) Chore: fix typo (#13975) (Nitin Kumar)
* [`4ee1134`](https://github.com/eslint/eslint/commit/4ee113414bdcbea240a5d9db27da6a10df472005) Sponsors: Sync README with website (ESLint Jenkins)
v7.17.0 - January 1, 2021
* [`e128e77`](https://github.com/eslint/eslint/commit/e128e775e9fa116a0ad68a071f1f0997589f8cd4) Update: check logical assignment in no-constant-condition (#13946) (Milos Djermanovic)
* [`cc48713`](https://github.com/eslint/eslint/commit/cc4871369645c3409dc56ded7a555af8a9f63d51) Chore: refactor calculating range and loc in no-useless-escape (#13964) (Milos Djermanovic)
* [`535fe47`](https://github.com/eslint/eslint/commit/535fe47fee6544b4957378f9408117c8318d4762) Update: use regexpp's default ecmaVersion in no-control-regex (#13969) (Milos Djermanovic)
* [`83e98cd`](https://github.com/eslint/eslint/commit/83e98cd48ce3d1acf729f4fb9be40cff332abd6e) Fix: use regexpp's default ecmaVersion in no-invalid-regexp (#13968) (Milos Djermanovic)
* [`7297363`](https://github.com/eslint/eslint/commit/7297363ea355d0e3b2a74aaec586126deb91fd93) Docs: fix examples for no-multi-str (#13966) (Milos Djermanovic)
* [`0649871`](https://github.com/eslint/eslint/commit/06498716bfba65ed8c7217917a29a07ad267193a) Update: add autofix to rule multiline-ternary (#13958) (薛定谔的猫)
* [`f6e7e32`](https://github.com/eslint/eslint/commit/f6e7e3231bc43c989f8c953de8e0d328bac5eea0) Fix: no-useless-escape wrong loc and fix with CRLF in template elements (#13953) (Milos Djermanovic)
* [`19c69c0`](https://github.com/eslint/eslint/commit/19c69c0293a98634ff0d4884a0cdabc1213ebcb4) Fix: one-var shouldn't split declaration if it isn't in a statement list (#13959) (Milos Djermanovic)
* [`e451b96`](https://github.com/eslint/eslint/commit/e451b9664aface32ad9321eaf5619c875dc76553) Docs: update build tool for webpack (#13962) (Sam Chen)
* [`c3e9acc`](https://github.com/eslint/eslint/commit/c3e9accce2f61b04ab699fd37c90703305281aa3) Chore: fix typos (#13960) (YeonJuan)
* [`7289ecf`](https://github.com/eslint/eslint/commit/7289ecf58ed0d2e7f0ad7f1e5004c8927a7bf805) Sponsors: Sync README with website (ESLint Jenkins)
v7.16.0 - December 18, 2020
* [`a62ad6f`](https://github.com/eslint/eslint/commit/a62ad6f03151358b93b5fede022a30d67310705c) Update: fix false negative of no-extra-parens with NewExpression (#13930) (Milos Djermanovic)
* [`f85b4c7`](https://github.com/eslint/eslint/commit/f85b4c72668c95c79fdb342b74dbd53d21baa93f) Fix: require-atomic-updates false positive across await (fixes #11954) (#13915) (buhi)
* [`301d0c0`](https://github.com/eslint/eslint/commit/301d0c05229dbd6cfb1045d716524e8ec46fa2c1) Fix: no-constant-condition false positives with unary expressions (#13927) (Milos Djermanovic)
* [`555c128`](https://github.com/eslint/eslint/commit/555c128b49ae6d9c100a9f8429416417edb40d13) Fix: false positive with await and ** in no-extra-parens (fixes #12739) (#13923) (Milos Djermanovic)
* [`d93c935`](https://github.com/eslint/eslint/commit/d93c9350361d2aa1a1976c553e47ab399e51e8c9) Docs: update JSON Schema links (#13936) (Milos Djermanovic)
* [`8d0c93a`](https://github.com/eslint/eslint/commit/8d0c93a7ef9449c7b7d082bbb4b7d8465b0d6bac) Upgrade: table@6.0.4 (#13920) (Rouven Weßling)
* [`9247683`](https://github.com/eslint/eslint/commit/924768377a4935a95a6ff3866f9545a5a6178b53) Docs: Remove for deleted npm run profile script (#13931) (Brandon Mills)
* [`ab240d4`](https://github.com/eslint/eslint/commit/ab240d49833b4e6e594667c1abe5b0caa8a9cf70) Fix: prefer-exponentiation-operator invalid autofix with await (#13924) (Milos Djermanovic)
* [`dc76911`](https://github.com/eslint/eslint/commit/dc7691103554a99bdb2142561cb507f50f547e3b) Chore: Add .pre-commit-hooks.yaml file (#13628) (Álvaro Mondéjar)
* [`2124e1b`](https://github.com/eslint/eslint/commit/2124e1b5dad30a905dc26bde9da472bf622d3f50) Docs: Fix wrong rule name (#13913) (noisyboy25)
* [`06b5809`](https://github.com/eslint/eslint/commit/06b58096975935ec016d96dd5f333f059c270f26) Sponsors: Sync README with website (ESLint Jenkins)
* [`26fc12f`](https://github.com/eslint/eslint/commit/26fc12f88109af9d4081bf0e16364c411bce3009) Docs: Update README team and sponsors (ESLint Jenkins)
v7.15.0 - December 5, 2020
* [`5c11aab`](https://github.com/eslint/eslint/commit/5c11aabbe8249aeb8cad29bc6a33fc20c8c683ef) Upgrade: @eslint/esintrc and espree for bug fixes (refs #13878) (#13908) (Brandon Mills)
* [`0eb7957`](https://github.com/eslint/eslint/commit/0eb7957e27fd521317bd5c8479ce7abc1399169c) Upgrade: file-entry-cache@6.0.0 (#13877) (Rouven Weßling)
* [`683ad00`](https://github.com/eslint/eslint/commit/683ad00c41e1ae4d889deff82b2a94318e8c2129) New: no-unsafe-optional-chaining rule (fixes #13431) (#13859) (YeonJuan)
* [`cbc57fb`](https://github.com/eslint/eslint/commit/cbc57fb7d07c00663ed5781f5e6bc8f534cc2d76) Fix: one-var autofixing for export (fixes #13834) (#13891) (Anix)
* [`110cf96`](https://github.com/eslint/eslint/commit/110cf962d05625a8a1bf7b5f4ec2194db150eb32) Docs: Fix a broken link in working-with-rules.md (#13875) (Anton Niklasson)
v7.14.0 - November 20, 2020
* [`5f09073`](https://github.com/eslint/eslint/commit/5f0907399a9666dec78c74384c8969c01483c30e) Update: fix 'skip' options in no-irregular-whitespace (fixes #13852) (#13853) (Milos Djermanovic)
* [`1861b40`](https://github.com/eslint/eslint/commit/1861b4086f1018f43ab19744d866d5da986c500d) Docs: correct the function-call-argument-newline 'default' descriptions (#13866) (Trevin Hofmann)
* [`98c00c4`](https://github.com/eslint/eslint/commit/98c00c41d2aecb3a990393d430694f4ce6b47de5) New: Add no-nonoctal-decimal-escape rule (fixes #13765) (#13845) (Milos Djermanovic)
* [`95d2fe6`](https://github.com/eslint/eslint/commit/95d2fe6057498fc1cc2193d28c8c2d1593224b33) Chore: remove eslint comment from no-octal-escape tests (#13846) (Milos Djermanovic)
* [`2004b7e`](https://github.com/eslint/eslint/commit/2004b7ecd3db0d4e7376cc3344246f7b9ada5801) Fix: enable debug logs for @eslint/eslintrc (fixes #13850) (#13861) (Milos Djermanovic)
* [`d2239a1`](https://github.com/eslint/eslint/commit/d2239a1fdec452e24ede04e990d16d42516fa538) Fix: no-useless-constructor crash on bodyless constructor (fixes #13830) (#13842) (Ari Perkkiö)
* [`eda0aa1`](https://github.com/eslint/eslint/commit/eda0aa18498dd85eb618873e8e0f4ac97032cfca) Docs: no-restricted-imports is only for static imports (#13863) (Robat Williams)
* [`042ae44`](https://github.com/eslint/eslint/commit/042ae44682a8a6c5037d920689124e2304056dd8) Docs: Fix JS syntax and doc URL in working-with-custom-formatters.md (#13828) (Raphael LANG)
* [`038dc73`](https://github.com/eslint/eslint/commit/038dc73c99ae68eae2035ef303f3a947053c8f05) Chore: Test on Node.js 15 (#13844) (Brandon Mills)
* [`37a06d6`](https://github.com/eslint/eslint/commit/37a06d633d3669f0f43236141dc43465b8bc7ec5) Sponsors: Sync README with website (ESLint Jenkins)
v7.13.0 - November 6, 2020
* [`254e00f`](https://github.com/eslint/eslint/commit/254e00fea8745ff5a8bcc8cb874fcfd02996d81b) New: Configurable List Size For Per-Rule Performance Metrics (#13812) (Bryan Mishkin)
* [`6c3c710`](https://github.com/eslint/eslint/commit/6c3c710ade7cd8654990f1adb55b58f038eab92d) Docs: fix broken url in docs (#13815) (SaintMalik)
* [`4a09149`](https://github.com/eslint/eslint/commit/4a091495a236d231a5065ece972719a0c4dd1b77) Sponsors: Sync README with website (ESLint Jenkins)
* [`fb6fcbf`](https://github.com/eslint/eslint/commit/fb6fcbfe0a8c41b92f0a33ab90f159037bd195e2) Docs: Fix reference to Code of Conduct (#13797) (Tobias Nießen)
* [`1b89ebe`](https://github.com/eslint/eslint/commit/1b89ebe1bdbef7de6001100945b8f71429df302c) Sponsors: Sync README with website (ESLint Jenkins)
v7.12.1 - October 26, 2020 v7.12.1 - October 26, 2020
* [`08f33e8`](https://github.com/eslint/eslint/commit/08f33e8b9a353c3183be6f937785db7a30fb90eb) Upgrade: @eslint/eslintrc to fix rule schema validation (fixes #13793) (#13794) (Brandon Mills) * [`08f33e8`](https://github.com/eslint/eslint/commit/08f33e8b9a353c3183be6f937785db7a30fb90eb) Upgrade: @eslint/eslintrc to fix rule schema validation (fixes #13793) (#13794) (Brandon Mills)

View File

@ -14,7 +14,7 @@
[Rules](https://eslint.org/docs/rules/) | [Rules](https://eslint.org/docs/rules/) |
[Contributing](https://eslint.org/docs/developer-guide/contributing) | [Contributing](https://eslint.org/docs/developer-guide/contributing) |
[Reporting Bugs](https://eslint.org/docs/developer-guide/contributing/reporting-bugs) | [Reporting Bugs](https://eslint.org/docs/developer-guide/contributing/reporting-bugs) |
[Code of Conduct](https://js.foundation/community/code-of-conduct) | [Code of Conduct](https://eslint.org/conduct) |
[Twitter](https://twitter.com/geteslint) | [Twitter](https://twitter.com/geteslint) |
[Mailing List](https://groups.google.com/group/eslint) | [Mailing List](https://groups.google.com/group/eslint) |
[Chat Room](https://eslint.org/chat) [Chat Room](https://eslint.org/chat)
@ -85,7 +85,7 @@ The three error levels allow you fine-grained control over how ESLint applies ru
## <a name="code-of-conduct"></a>Code of Conduct ## <a name="code-of-conduct"></a>Code of Conduct
ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct). ESLint adheres to the [JS Foundation Code of Conduct](https://eslint.org/conduct).
## <a name="filing-issues"></a>Filing Issues ## <a name="filing-issues"></a>Filing Issues
@ -158,6 +158,7 @@ ESLint follows [semantic versioning](https://semver.org). However, due to the na
* A bug fix in a rule that results in ESLint reporting more linting errors. * A bug fix in a rule that results in ESLint reporting more linting errors.
* A new rule is created. * A new rule is created.
* A new option to an existing rule that does not result in ESLint reporting more linting errors by default. * A new option to an existing rule that does not result in ESLint reporting more linting errors by default.
* A new addition to an existing rule to support a newly-added language feature (within the last 12 months) that will result in ESLint reporting more linting errors by default.
* An existing rule is deprecated. * An existing rule is deprecated.
* A new CLI capability is created. * A new CLI capability is created.
* New capabilities to the public API are added (new classes, new methods, new arguments to existing methods, etc.). * New capabilities to the public API are added (new classes, new methods, new arguments to existing methods, etc.).
@ -207,11 +208,6 @@ Brandon Mills
Toru Nagashima Toru Nagashima
</a> </a>
</td><td align="center" valign="top" width="11%"> </td><td align="center" valign="top" width="11%">
<a href="https://github.com/kaicataldo">
<img src="https://github.com/kaicataldo.png?s=75" width="75" height="75"><br />
Kai Cataldo
</a>
</td><td align="center" valign="top" width="11%">
<a href="https://github.com/mdjermanovic"> <a href="https://github.com/mdjermanovic">
<img src="https://github.com/mdjermanovic.png?s=75" width="75" height="75"><br /> <img src="https://github.com/mdjermanovic.png?s=75" width="75" height="75"><br />
Milos Djermanovic Milos Djermanovic
@ -265,9 +261,9 @@ The following companies, organizations, and individuals support ESLint's ongoing
<!--sponsorsstart--> <!--sponsorsstart-->
<h3>Platinum Sponsors</h3> <h3>Platinum Sponsors</h3>
<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> <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>
<p><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> <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>
<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> <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>
<p><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/printable-calendar"><img src="https://images.opencollective.com/betacalendars/9334b33/logo.png" alt="2021 calendar" height="32"></a> <a href="https://buy.fineproxy.org/eng/"><img src="https://images.opencollective.com/buy-fineproxy-org/2002c40/logo.png" alt="Buy.Fineproxy.Org" height="32"></a> <a href="https://www.veikkaajat.com"><img src="https://images.opencollective.com/veikkaajat/b92b427/logo.png" alt="Veikkaajat.com" 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.marfeel.com/"><img src="https://images.opencollective.com/marfeel/4b88e30/logo.png" alt="Marfeel" 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> <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>
<!--sponsorsend--> <!--sponsorsend-->
## <a name="technology-sponsors"></a>Technology Sponsors ## <a name="technology-sponsors"></a>Technology Sponsors

View File

@ -14,7 +14,7 @@ require("v8-compile-cache");
// must do this initialization *before* other requires in order to work // must do this initialization *before* other requires in order to work
if (process.argv.includes("--debug")) { if (process.argv.includes("--debug")) {
require("debug").enable("eslint:*,-eslint:code-path"); require("debug").enable("eslint:*,-eslint:code-path,eslintrc:*");
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@ -90,18 +90,3 @@ Generates `build/eslint.js`, a version of ESLint for use in the browser
#### npm run docs #### npm run docs
Generates JSDoc documentation and places it into `/jsdoc`. Generates JSDoc documentation and places it into `/jsdoc`.
#### npm run profile
This command is used for intensive profiling of ESLint using Chrome Developer Tools. It starts a development server that runs through three profiles:
* Large - Runs ESLint on JSHint
* Medium - Runs ESLint on jQuery
* Small - Runs ESLint on KnockoutJS
Your browser should automatically open to the page in question. When that happens:
1. Open up developer tools
1. Click on Profiles
You should start to see profiles for each run show up on the left side. If not, reload the page in the browser. Once all three profiles have completed, they will be available for inspection.

View File

@ -195,7 +195,7 @@ module.exports = function(results, data) {
var logMessage = { var logMessage = {
filePath: current.filePath, filePath: current.filePath,
ruleId: msg.ruleId, ruleId: msg.ruleId,
ruleUrl: data.rulesMeta[msg.ruleId].url, ruleUrl: data.rulesMeta[msg.ruleId].docs.url,
message: msg.message, message: msg.message,
line: msg.line, line: msg.line,
column: msg.column column: msg.column
@ -226,7 +226,7 @@ module.exports = function(results, data) {
"\n" + "\n" +
msg.type + msg.type +
" " + " " +
msg.ruleId + (msg.ruleUrl ? " (" + msg.ruleUrl + ")" : "" msg.ruleId + (msg.ruleUrl ? " (" + msg.ruleUrl + ")" : "") +
"\n " + "\n " +
msg.filePath + msg.filePath +
":" + ":" +

View File

@ -142,7 +142,7 @@ Additionally, the `context` object has the following methods:
* `getScope()` - returns the [scope](./scope-manager-interface.md#scope-interface) of the currently-traversed node. This information can be used to track references to variables. * `getScope()` - returns the [scope](./scope-manager-interface.md#scope-interface) of the currently-traversed node. This information can be used to track references to variables.
* `getSourceCode()` - returns a [`SourceCode`](#context-getsourcecode) object that you can use to work with the source that was passed to ESLint. * `getSourceCode()` - returns a [`SourceCode`](#context-getsourcecode) object that you can use to work with the source that was passed to ESLint.
* `markVariableAsUsed(name)` - marks a variable with the given name in the current scope as used. This affects the [no-unused-vars](../rules/no-unused-vars.md) rule. Returns `true` if a variable with the given name was found and marked as used, otherwise `false`. * `markVariableAsUsed(name)` - marks a variable with the given name in the current scope as used. This affects the [no-unused-vars](../rules/no-unused-vars.md) rule. Returns `true` if a variable with the given name was found and marked as used, otherwise `false`.
* `report(descriptor)` - reports a problem in the code (see the [dedicated section](#context-report)). * `report(descriptor)` - reports a problem in the code (see the [dedicated section](#contextreport)).
**Note:** Earlier versions of ESLint supported additional methods on the `context` object. Those methods were removed in the new format and should not be relied upon. **Note:** Earlier versions of ESLint supported additional methods on the `context` object. Those methods were removed in the new format and should not be relied upon.
@ -567,7 +567,7 @@ Please note that the following methods have been deprecated and will be removed
### Options Schemas ### Options Schemas
Rules may export a `schema` property, which is a [JSON schema](http://json-schema.org/) format description of a rule's options which will be used by ESLint to validate configuration options and prevent invalid or unexpected inputs before they are passed to the rule in `context.options`. Rules may export a `schema` property, which is a [JSON schema](https://json-schema.org/) format description of a rule's options which will be used by ESLint to validate configuration options and prevent invalid or unexpected inputs before they are passed to the rule in `context.options`.
There are two formats for a rule's exported `schema`. The first is a full JSON Schema object describing all possible options the rule accepts, including the rule's error level as the first argument and any optional arguments thereafter. There are two formats for a rule's exported `schema`. The first is a full JSON Schema object describing all possible options the rule accepts, including the rule's error level as the first argument and any optional arguments thereafter.
@ -597,7 +597,7 @@ module.exports = {
In the preceding example, the error level is assumed to be the first argument. It is followed by the first optional argument, a string which may be either `"always"` or `"never"`. The final optional argument is an object, which may have a Boolean property named `exceptRange`. In the preceding example, the error level is assumed to be the first argument. It is followed by the first optional argument, a string which may be either `"always"` or `"never"`. The final optional argument is an object, which may have a Boolean property named `exceptRange`.
To learn more about JSON Schema, we recommend looking at some examples in [website](http://json-schema.org/learn/) to start, and also reading [Understanding JSON Schema](http://spacetelescope.github.io/understanding-json-schema/) (a free ebook). To learn more about JSON Schema, we recommend looking at some examples in [website](https://json-schema.org/learn/) to start, and also reading [Understanding JSON Schema](https://json-schema.org/understanding-json-schema/) (a free ebook).
**Note:** Currently you need to use full JSON Schema object rather than array in case your schema has references ($ref), because in case of array format ESLint transforms this array into a single schema without updating references that makes them incorrect (they are ignored). **Note:** Currently you need to use full JSON Schema object rather than array in case your schema has references ($ref), because in case of array format ESLint transforms this array into a single schema without updating references that makes them incorrect (they are ignored).
@ -720,6 +720,8 @@ Rule | Time (ms) | Relative
quotes | 18.066 | 100.0% quotes | 18.066 | 100.0%
``` ```
To see a longer list of results (more than 10), set the environment variable to another value such as `TIMING=50` or `TIMING=all`.
## Rule Naming Conventions ## Rule Naming Conventions
The rule naming conventions for ESLint are fairly simple: The rule naming conventions for ESLint are fairly simple:

View File

@ -20,7 +20,7 @@ As Contributors gain experience and familiarity with the project, their profile
### Committers ### Committers
Committers are community members who have shown that they are committed to the continued development of the project through ongoing engagement with the community. Committers are given push access to the project's GitHub repos and must abide by the project's [Contribution Guidelines](contributing). Committers are community members who have shown that they are committed to the continued development of the project through ongoing engagement with the community. Committers are given push access to the project's GitHub repos and must abide by the project's [Contribution Guidelines](../developer-guide/contributing).
Committers: Committers:

View File

@ -330,7 +330,7 @@ j = function bar() {
Examples of **correct** code for this rule with the `{ "ArrayExpression": "always", "ArrayPattern": "never" }` options: Examples of **correct** code for this rule with the `{ "ArrayExpression": "always", "ArrayPattern": "never" }` options:
```js ```js
/*eslint object-curly-newline: ["error", { "ArrayExpression": "always", "ArrayPattern": "never" }]*/ /*eslint array-element-newline: ["error", { "ArrayExpression": "always", "ArrayPattern": "never" }]*/
var a = [1, var a = [1,
2]; 2];

View File

@ -32,6 +32,11 @@ function a(x) {
return 4; // 3rd path return 4; // 3rd path
} }
} }
function b() {
foo ||= 1;
bar &&= 1;
}
``` ```
Examples of **correct** code for a maximum of 2: Examples of **correct** code for a maximum of 2:
@ -46,6 +51,10 @@ function a(x) {
return 4; return 4;
} }
} }
function b() {
foo ||= 1;
}
``` ```
## Options ## Options

View File

@ -72,7 +72,7 @@ baz(
### never ### never
Examples of **incorrect** code for this rule with the default `"never"` option: Examples of **incorrect** code for this rule with the `"never"` option:
```js ```js
/*eslint function-call-argument-newline: ["error", "never"]*/ /*eslint function-call-argument-newline: ["error", "never"]*/
@ -123,7 +123,7 @@ baz("one", "two", (x) => {
### consistent ### consistent
Examples of **incorrect** code for this rule with the default `"consistent"` option: Examples of **incorrect** code for this rule with the `"consistent"` option:
```js ```js
/*eslint function-call-argument-newline: ["error", "consistent"]*/ /*eslint function-call-argument-newline: ["error", "consistent"]*/
@ -143,7 +143,7 @@ baz("one", "two",
); );
``` ```
Examples of **correct** code for this rule with the default `"consistent"` option: Examples of **correct** code for this rule with the `"consistent"` option:
```js ```js
/*eslint function-call-argument-newline: ["error", "consistent"]*/ /*eslint function-call-argument-newline: ["error", "consistent"]*/

View File

@ -28,6 +28,14 @@ if (void x) {
doSomethingUnfinished(); doSomethingUnfinished();
} }
if (x &&= false) {
doSomethingNever();
}
if (x ||= true) {
doSomethingAlways();
}
for (;-2;) { for (;-2;) {
doSomethingForever(); doSomethingForever();
} }

View File

@ -30,16 +30,9 @@ new RegExp
this.RegExp('[') this.RegExp('[')
``` ```
## Environments Please note that this rule validates regular expressions per the latest ECMAScript specification, regardless of your parser settings.
ECMAScript 6 adds the following flag arguments to the `RegExp` constructor: If you want to allow additional constructor flags for any reason, you can specify them using the `allowConstructorFlags` option. These flags will then be ignored by the rule.
* `"u"` ([unicode](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-get-regexp.prototype.unicode))
* `"y"` ([sticky](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-get-regexp.prototype.sticky))
You can enable these to be recognized as valid by setting the ECMAScript version to 6 in your [ESLint configuration](../user-guide/configuring).
If you want to allow additional constructor flags for any reason, you can specify them using an `allowConstructorFlags` option in `.eslintrc`. These flags will then be ignored by the rule regardless of the `ecmaVersion` setting.
## Options ## Options
@ -49,14 +42,14 @@ This rule has an object option for exceptions:
### allowConstructorFlags ### allowConstructorFlags
Examples of **correct** code for this rule with the `{ "allowConstructorFlags": ["u", "y"] }` option: Examples of **correct** code for this rule with the `{ "allowConstructorFlags": ["a", "z"] }` option:
```js ```js
/*eslint no-invalid-regexp: ["error", { "allowConstructorFlags": ["u", "y"] }]*/ /*eslint no-invalid-regexp: ["error", { "allowConstructorFlags": ["a", "z"] }]*/
new RegExp('.', 'y') new RegExp('.', 'a')
new RegExp('.', 'yu') new RegExp('.', 'az')
``` ```
## Further Reading ## Further Reading

View File

@ -17,8 +17,9 @@ Examples of **incorrect** code for this rule:
```js ```js
/*eslint no-multi-str: "error"*/ /*eslint no-multi-str: "error"*/
var x = "Line 1 \
Line 2"; var x = "some very \
long text";
``` ```
Examples of **correct** code for this rule: Examples of **correct** code for this rule:
@ -26,6 +27,8 @@ Examples of **correct** code for this rule:
```js ```js
/*eslint no-multi-str: "error"*/ /*eslint no-multi-str: "error"*/
var x = "Line 1\n" + var x = "some very long text";
"Line 2";
var x = "some very " +
"long text";
``` ```

View File

@ -0,0 +1,62 @@
# Disallow `\8` and `\9` escape sequences in string literals (no-nonoctal-decimal-escape)
Although not being specified in the language until ECMAScript 2021, `\8` and `\9` escape sequences in string literals were allowed in most JavaScript engines, and treated as "useless" escapes:
```js
"\8" === "8"; // true
"\9" === "9"; // true
```
Since ECMAScript 2021, these escape sequences are specified as [non-octal decimal escape sequences](https://tc39.es/ecma262/#prod-annexB-NonOctalDecimalEscapeSequence), retaining the same behavior.
Nevertheless, the ECMAScript specification treats `\8` and `\9` in string literals as a legacy feature. This syntax is optional if the ECMAScript host is not a web browser. Browsers still have to support it, but only in non-strict mode.
Regardless of your targeted environment, these escape sequences shouldn't be used when writing new code.
## Rule Details
This rule disallows `\8` and `\9` escape sequences in string literals.
Examples of **incorrect** code for this rule:
```js
/*eslint no-nonoctal-decimal-escape: "error"*/
"\8";
"\9";
var foo = "w\8less";
var bar = "December 1\9";
var baz = "Don't use \8 and \9 escapes.";
var quux = "\0\8";
```
Examples of **correct** code for this rule:
```js
/*eslint no-nonoctal-decimal-escape: "error"*/
"8";
"9";
var foo = "w8less";
var bar = "December 19";
var baz = "Don't use \\8 and \\9 escapes.";
var quux = "\0\u0038";
```
## Further Reading
* [NonOctalDecimalEscapeSequence](https://tc39.es/ecma262/#prod-annexB-NonOctalDecimalEscapeSequence) in ECMAScript specification
## Related Rules
* [no-octal-escape](no-octal-escape.md)

View File

@ -12,6 +12,8 @@ Why would you want to restrict imports?
This rule allows you to specify imports that you don't want to use in your application. This rule allows you to specify imports that you don't want to use in your application.
It applies to static imports only, not dynamic ones.
## Options ## Options
The syntax to specify restricted imports looks like this: The syntax to specify restricted imports looks like this:

View File

@ -0,0 +1,153 @@
# disallow use of optional chaining in contexts where the `undefined` value is not allowed (no-unsafe-optional-chaining)
The optional chaining (`?.`) expression can short-circuit with a return value of `undefined`. Therefore, treating an evaluated optional chaining expression as a function, object, number, etc., can cause TypeError or unexpected results. For example:
```js
var obj = undefined;
1 in obj?.foo; // TypeError
with (obj?.foo); // TypeError
for (bar of obj?.foo); // TypeError
bar instanceof obj?.foo; // TypeError
const { bar } = obj?.foo; // TypeError
```
Also, parentheses limit the scope of short-circuiting in chains. For example:
```js
var obj = undefined;
(obj?.foo)(); // TypeError
(obj?.foo).bar; // TypeError
```
## Rule Details
This rule aims to detect some cases where the use of optional chaining doesn't prevent runtime errors. In particular, it flags optional chaining expressions in positions where short-circuiting to `undefined` causes throwing a TypeError afterward.
Examples of **incorrect** code for this rule:
```js
/*eslint no-unsafe-optional-chaining: "error"*/
(obj?.foo)();
(obj?.foo).bar;
(foo?.()).bar;
(foo?.()).bar();
(obj?.foo ?? obj?.bar)();
(foo || obj?.foo)();
(obj?.foo && foo)();
(foo ? obj?.foo : bar)();
(foo, obj?.bar).baz;
(obj?.foo)`template`;
new (obj?.foo)();
[...obj?.foo];
bar(...obj?.foo);
1 in obj?.foo;
bar instanceof obj?.foo;
for (bar of obj?.foo);
const { bar } = obj?.foo;
[{ bar } = obj?.foo] = [];
with (obj?.foo);
class A extends obj?.foo {}
var a = class A extends obj?.foo {};
async function foo () {
const { bar } = await obj?.foo;
(await obj?.foo)();
(await obj?.foo).bar;
}
```
Examples of **correct** code for this rule:
```js
/*eslint no-unsafe-optional-chaining: "error"*/
(obj?.foo)?.();
obj?.foo();
(obj?.foo ?? bar)();
obj?.foo.bar;
foo?.()?.bar;
(obj?.foo ?? bar)`template`;
new (obj?.foo ?? bar)();
var baz = {...obj?.foo};
const { bar } = obj?.foo || baz;
async function foo () {
const { bar } = await obj?.foo || baz;
(await obj?.foo)?.();
(await obj?.foo)?.bar;
}
```
## Options
This rule has an object option:
- `disallowArithmeticOperators`: Disallow arithmetic operations on optional chaining expressions (Default `false`). If this is `true`, this rule warns arithmetic operations on optional chaining expressions, which possibly result in `NaN`.
### disallowArithmeticOperators
With this option set to `true` the rule is enforced for:
- Unary operators: `-`, `+`
- Arithmetic operators: `+`, `-`, `/`, `*`, `%`, `**`
- Assignment operators: `+=`, `-=`, `/=`, `*=`, `%=`, `**=`
Examples of additional **incorrect** code for this rule with the `{ "disallowArithmeticOperators": true }` option:
```js
/*eslint no-unsafe-optional-chaining: ["error", { "disallowArithmeticOperators": true }]*/
+obj?.foo;
-obj?.foo;
obj?.foo + bar;
obj?.foo - bar;
obj?.foo / bar;
obj?.foo * bar;
obj?.foo % bar;
obj?.foo ** bar;
baz += obj?.foo;
baz -= obj?.foo;
baz /= obj?.foo;
baz *= obj?.foo;
baz %= obj?.foo;
baz **= obj?.foo;
async function foo () {
+await obj?.foo;
await obj?.foo + bar;
baz += await obj?.foo;
}
```

View File

@ -36,10 +36,14 @@ Examples of **incorrect** code for this rule with the default `"never"` option:
```js ```js
/*eslint space-in-parens: ["error", "never"]*/ /*eslint space-in-parens: ["error", "never"]*/
foo( );
foo( 'bar'); foo( 'bar');
foo('bar' ); foo('bar' );
foo( 'bar' ); foo( 'bar' );
foo( /* bar */ );
var foo = ( 1 + 2 ) * 3; var foo = ( 1 + 2 ) * 3;
( function () { return 'bar'; }() ); ( function () { return 'bar'; }() );
``` ```
@ -53,6 +57,8 @@ foo();
foo('bar'); foo('bar');
foo(/* bar */);
var foo = (1 + 2) * 3; var foo = (1 + 2) * 3;
(function () { return 'bar'; }()); (function () { return 'bar'; }());
``` ```
@ -68,6 +74,8 @@ foo( 'bar');
foo('bar' ); foo('bar' );
foo('bar'); foo('bar');
foo(/* bar */);
var foo = (1 + 2) * 3; var foo = (1 + 2) * 3;
(function () { return 'bar'; }()); (function () { return 'bar'; }());
``` ```
@ -78,9 +86,12 @@ Examples of **correct** code for this rule with the `"always"` option:
/*eslint space-in-parens: ["error", "always"]*/ /*eslint space-in-parens: ["error", "always"]*/
foo(); foo();
foo( );
foo( 'bar' ); foo( 'bar' );
foo( /* bar */ );
var foo = ( 1 + 2 ) * 3; var foo = ( 1 + 2 ) * 3;
( function () { return 'bar'; }() ); ( function () { return 'bar'; }() );
``` ```
@ -276,4 +287,6 @@ You can turn this rule off if you are not concerned with the consistency of spac
## Related Rules ## Related Rules
* [space-in-brackets](space-in-brackets.md) (deprecated) * [array-bracket-spacing](array-bracket-spacing.md)
* [object-curly-spacing](object-curly-spacing.md)
* [computed-property-spacing](computed-property-spacing.md)

View File

@ -26,7 +26,7 @@
* Mimosa: [mimosa-eslint](https://www.npmjs.com/package/mimosa-eslint) * Mimosa: [mimosa-eslint](https://www.npmjs.com/package/mimosa-eslint)
* Broccoli: [broccoli-eslint](https://www.npmjs.com/package/broccoli-eslint) * Broccoli: [broccoli-eslint](https://www.npmjs.com/package/broccoli-eslint)
* Browserify: [eslintify](https://www.npmjs.com/package/eslintify) * Browserify: [eslintify](https://www.npmjs.com/package/eslintify)
* Webpack: [eslint-loader](https://www.npmjs.com/package/eslint-loader) * Webpack: [eslint-webpack-plugin](https://www.npmjs.com/package/eslint-webpack-plugin)
* Rollup: [rollup-plugin-eslint](https://www.npmjs.com/package/rollup-plugin-eslint) * Rollup: [rollup-plugin-eslint](https://www.npmjs.com/package/rollup-plugin-eslint)
* Ember-cli: [ember-cli-eslint](https://www.npmjs.com/package/ember-cli-eslint) * Ember-cli: [ember-cli-eslint](https://www.npmjs.com/package/ember-cli-eslint)
* Sails.js: [sails-hook-lint](https://www.npmjs.com/package/sails-hook-lint), [sails-eslint](https://www.npmjs.com/package/sails-eslint) * Sails.js: [sails-hook-lint](https://www.npmjs.com/package/sails-hook-lint), [sails-eslint](https://www.npmjs.com/package/sails-eslint)

View File

@ -272,7 +272,7 @@ function processOptions({
errors.push("'rulePaths' must be an array of non-empty strings."); errors.push("'rulePaths' must be an array of non-empty strings.");
} }
if (typeof useEslintrc !== "boolean") { if (typeof useEslintrc !== "boolean") {
errors.push("'useElintrc' must be a boolean."); errors.push("'useEslintrc' must be a boolean.");
} }
if (errors.length > 0) { if (errors.length > 0) {
@ -563,7 +563,7 @@ class ESLint {
/** /**
* Returns the formatter representing the given formatter name. * Returns the formatter representing the given formatter name.
* @param {string} [name] The name of the formattter to load. * @param {string} [name] The name of the formatter to load.
* The following values are allowed: * The following values are allowed:
* - `undefined` ... Load `stylish` builtin formatter. * - `undefined` ... Load `stylish` builtin formatter.
* - A builtin formatter name ... Load the builtin formatter. * - A builtin formatter name ... Load the builtin formatter.

View File

@ -565,7 +565,8 @@ function promptUser() {
{ {
type: "toggle", type: "toggle",
name: "installESLint", name: "installESLint",
message(answers) { message() {
const { answers } = this.state;
const verb = semver.ltr(answers.localESLintVersion, answers.requiredESLintVersionRange) const verb = semver.ltr(answers.localESLintVersion, answers.requiredESLintVersionRange)
? "upgrade" ? "upgrade"
: "downgrade"; : "downgrade";

View File

@ -44,6 +44,26 @@ const enabled = !!process.env.TIMING;
const HEADERS = ["Rule", "Time (ms)", "Relative"]; const HEADERS = ["Rule", "Time (ms)", "Relative"];
const ALIGN = [alignLeft, alignRight, alignRight]; const ALIGN = [alignLeft, alignRight, alignRight];
/**
* Decide how many rules to show in the output list.
* @returns {number} the number of rules to show
*/
function getListSize() {
const MINIMUM_SIZE = 10;
if (typeof process.env.TIMING !== "string") {
return MINIMUM_SIZE;
}
if (process.env.TIMING.toLowerCase() === "all") {
return Number.POSITIVE_INFINITY;
}
const TIMING_ENV_VAR_AS_INTEGER = Number.parseInt(process.env.TIMING, 10);
return TIMING_ENV_VAR_AS_INTEGER > 10 ? TIMING_ENV_VAR_AS_INTEGER : MINIMUM_SIZE;
}
/* istanbul ignore next */ /* istanbul ignore next */
/** /**
* display the data * display the data
@ -61,7 +81,7 @@ function display(data) {
return [key, time]; return [key, time];
}) })
.sort((a, b) => b[1] - a[1]) .sort((a, b) => b[1] - a[1])
.slice(0, 10); .slice(0, getListSize());
rows.forEach(row => { rows.forEach(row => {
row.push(`${(row[1] * 100 / total).toFixed(1)}%`); row.push(`${(row[1] * 100 / total).toFixed(1)}%`);
@ -133,7 +153,8 @@ module.exports = (function() {
return { return {
time, time,
enabled enabled,
getListSize
}; };
}()); }());

View File

@ -191,7 +191,7 @@ module.exports = {
} }
/* /*
* If the first token of the reutrn value is `{` or the return value is a sequence expression, * If the first token of the return value is `{` or the return value is a sequence expression,
* enclose the return value by parentheses to avoid syntax error. * enclose the return value by parentheses to avoid syntax error.
*/ */
if (astUtils.isOpeningBraceToken(firstValueToken) || blockBody[0].argument.type === "SequenceExpression" || (funcInfo.hasInOperator && isInsideForLoopInitializer(node))) { if (astUtils.isOpeningBraceToken(firstValueToken) || blockBody[0].argument.type === "SequenceExpression" || (funcInfo.hasInOperator && isInsideForLoopInitializer(node))) {

View File

@ -59,9 +59,9 @@ module.exports = {
} }
/** /**
* Check to see if a node contains only identifers * Check to see if a node contains only identifiers
* @param {ASTNode} node The node to check * @param {ASTNode} node The node to check
* @returns {boolean} Whether or not the node contains only identifers * @returns {boolean} Whether or not the node contains only identifiers
*/ */
function containsOnlyIdentifiers(node) { function containsOnlyIdentifiers(node) {
if (node.type === "Identifier") { if (node.type === "Identifier") {

View File

@ -153,7 +153,13 @@ module.exports = {
IfStatement: increaseComplexity, IfStatement: increaseComplexity,
SwitchCase: increaseSwitchComplexity, SwitchCase: increaseSwitchComplexity,
WhileStatement: increaseComplexity, WhileStatement: increaseComplexity,
DoWhileStatement: increaseComplexity DoWhileStatement: increaseComplexity,
AssignmentExpression(node) {
if (astUtils.isLogicalAssignmentOperator(node.operator)) {
increaseComplexity();
}
}
}; };
} }

View File

@ -46,7 +46,7 @@ module.exports = {
const sourceCode = context.getSourceCode(); const sourceCode = context.getSourceCode();
/** /**
* Reports if the dot between object and property is on the correct loccation. * Reports if the dot between object and property is on the correct location.
* @param {ASTNode} node The `MemberExpression` node. * @param {ASTNode} node The `MemberExpression` node.
* @returns {void} * @returns {void}
*/ */

View File

@ -131,7 +131,7 @@ module.exports = {
return null; return null;
} }
// If `?.` exsits, it doesn't hide no-undexpected-multiline errors // If `?.` exists, it doesn't hide no-unexpected-multiline errors
if (node.optional) { if (node.optional) {
return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], "?."); return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], "?.");
} }
@ -177,7 +177,7 @@ module.exports = {
/* /*
* Only autofix if there is no newline * Only autofix if there is no newline
* https://github.com/eslint/eslint/issues/7787 * https://github.com/eslint/eslint/issues/7787
* But if `?.` exsits, it doesn't hide no-undexpected-multiline errors * But if `?.` exists, it doesn't hide no-unexpected-multiline errors
*/ */
if (!node.optional) { if (!node.optional) {
return null; return null;

View File

@ -169,6 +169,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
"no-new-require": () => require("./no-new-require"), "no-new-require": () => require("./no-new-require"),
"no-new-symbol": () => require("./no-new-symbol"), "no-new-symbol": () => require("./no-new-symbol"),
"no-new-wrappers": () => require("./no-new-wrappers"), "no-new-wrappers": () => require("./no-new-wrappers"),
"no-nonoctal-decimal-escape": () => require("./no-nonoctal-decimal-escape"),
"no-obj-calls": () => require("./no-obj-calls"), "no-obj-calls": () => require("./no-obj-calls"),
"no-octal": () => require("./no-octal"), "no-octal": () => require("./no-octal"),
"no-octal-escape": () => require("./no-octal-escape"), "no-octal-escape": () => require("./no-octal-escape"),
@ -217,6 +218,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
"no-unreachable-loop": () => require("./no-unreachable-loop"), "no-unreachable-loop": () => require("./no-unreachable-loop"),
"no-unsafe-finally": () => require("./no-unsafe-finally"), "no-unsafe-finally": () => require("./no-unsafe-finally"),
"no-unsafe-negation": () => require("./no-unsafe-negation"), "no-unsafe-negation": () => require("./no-unsafe-negation"),
"no-unsafe-optional-chaining": () => require("./no-unsafe-optional-chaining"),
"no-unused-expressions": () => require("./no-unused-expressions"), "no-unused-expressions": () => require("./no-unused-expressions"),
"no-unused-labels": () => require("./no-unused-labels"), "no-unused-labels": () => require("./no-unused-labels"),
"no-unused-vars": () => require("./no-unused-vars"), "no-unused-vars": () => require("./no-unused-vars"),

View File

@ -27,19 +27,22 @@ module.exports = {
enum: ["always", "always-multiline", "never"] enum: ["always", "always-multiline", "never"]
} }
], ],
messages: { messages: {
expectedTestCons: "Expected newline between test and consequent of ternary expression.", expectedTestCons: "Expected newline between test and consequent of ternary expression.",
expectedConsAlt: "Expected newline between consequent and alternate of ternary expression.", expectedConsAlt: "Expected newline between consequent and alternate of ternary expression.",
unexpectedTestCons: "Unexpected newline between test and consequent of ternary expression.", unexpectedTestCons: "Unexpected newline between test and consequent of ternary expression.",
unexpectedConsAlt: "Unexpected newline between consequent and alternate of ternary expression." unexpectedConsAlt: "Unexpected newline between consequent and alternate of ternary expression."
} },
fixable: "whitespace"
}, },
create(context) { create(context) {
const sourceCode = context.getSourceCode();
const option = context.options[0]; const option = context.options[0];
const multiline = option !== "never"; const multiline = option !== "never";
const allowSingleLine = option === "always-multiline"; const allowSingleLine = option === "always-multiline";
const sourceCode = context.getSourceCode();
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
// Public // Public
@ -59,6 +62,8 @@ module.exports = {
const areTestAndConsequentOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfTest, firstTokenOfConsequent); const areTestAndConsequentOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfTest, firstTokenOfConsequent);
const areConsequentAndAlternateOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfConsequent, firstTokenOfAlternate); const areConsequentAndAlternateOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfConsequent, firstTokenOfAlternate);
const hasComments = !!sourceCode.getCommentsInside(node).length;
if (!multiline) { if (!multiline) {
if (!areTestAndConsequentOnSameLine) { if (!areTestAndConsequentOnSameLine) {
context.report({ context.report({
@ -67,7 +72,24 @@ module.exports = {
start: firstTokenOfTest.loc.start, start: firstTokenOfTest.loc.start,
end: lastTokenOfTest.loc.end end: lastTokenOfTest.loc.end
}, },
messageId: "unexpectedTestCons" messageId: "unexpectedTestCons",
fix: fixer => {
if (hasComments) {
return null;
}
const fixers = [];
const areTestAndQuestionOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfTest, questionToken);
const areQuestionAndConsOnSameLine = astUtils.isTokenOnSameLine(questionToken, firstTokenOfConsequent);
if (!areTestAndQuestionOnSameLine) {
fixers.push(fixer.removeRange([lastTokenOfTest.range[1], questionToken.range[0]]));
}
if (!areQuestionAndConsOnSameLine) {
fixers.push(fixer.removeRange([questionToken.range[1], firstTokenOfConsequent.range[0]]));
}
return fixers;
}
}); });
} }
@ -78,7 +100,24 @@ module.exports = {
start: firstTokenOfConsequent.loc.start, start: firstTokenOfConsequent.loc.start,
end: lastTokenOfConsequent.loc.end end: lastTokenOfConsequent.loc.end
}, },
messageId: "unexpectedConsAlt" messageId: "unexpectedConsAlt",
fix: fixer => {
if (hasComments) {
return null;
}
const fixers = [];
const areConsAndColonOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfConsequent, colonToken);
const areColonAndAltOnSameLine = astUtils.isTokenOnSameLine(colonToken, firstTokenOfAlternate);
if (!areConsAndColonOnSameLine) {
fixers.push(fixer.removeRange([lastTokenOfConsequent.range[1], colonToken.range[0]]));
}
if (!areColonAndAltOnSameLine) {
fixers.push(fixer.removeRange([colonToken.range[1], firstTokenOfAlternate.range[0]]));
}
return fixers;
}
}); });
} }
} else { } else {
@ -93,7 +132,16 @@ module.exports = {
start: firstTokenOfTest.loc.start, start: firstTokenOfTest.loc.start,
end: lastTokenOfTest.loc.end end: lastTokenOfTest.loc.end
}, },
messageId: "expectedTestCons" messageId: "expectedTestCons",
fix: fixer => (hasComments ? null : (
fixer.replaceTextRange(
[
lastTokenOfTest.range[1],
questionToken.range[0]
],
"\n"
)
))
}); });
} }
@ -104,7 +152,16 @@ module.exports = {
start: firstTokenOfConsequent.loc.start, start: firstTokenOfConsequent.loc.start,
end: lastTokenOfConsequent.loc.end end: lastTokenOfConsequent.loc.end
}, },
messageId: "expectedConsAlt" messageId: "expectedConsAlt",
fix: (fixer => (hasComments ? null : (
fixer.replaceTextRange(
[
lastTokenOfConsequent.range[1],
colonToken.range[0]
],
"\n"
)
)))
}); });
} }
} }

View File

@ -106,10 +106,15 @@ module.exports = {
*/ */
return operator === node.operator && return operator === node.operator &&
( (
isLogicalIdentity(node.left, node.operator) || isLogicalIdentity(node.left, operator) ||
isLogicalIdentity(node.right, node.operator) isLogicalIdentity(node.right, operator)
); );
case "AssignmentExpression":
return ["||=", "&&="].includes(node.operator) &&
operator === node.operator.slice(0, -1) &&
isLogicalIdentity(node.right, operator);
// no default // no default
} }
return false; return false;
@ -147,12 +152,18 @@ module.exports = {
} }
case "UnaryExpression": case "UnaryExpression":
if (node.operator === "void") { if (
node.operator === "void" ||
node.operator === "typeof" && inBooleanPosition
) {
return true; return true;
} }
return (node.operator === "typeof" && inBooleanPosition) || if (node.operator === "!") {
isConstant(node.argument, true); return isConstant(node.argument, true);
}
return isConstant(node.argument, false);
case "BinaryExpression": case "BinaryExpression":
return isConstant(node.left, false) && return isConstant(node.left, false) &&
@ -171,7 +182,15 @@ module.exports = {
} }
case "AssignmentExpression": case "AssignmentExpression":
return (node.operator === "=") && isConstant(node.right, inBooleanPosition); if (node.operator === "=") {
return isConstant(node.right, inBooleanPosition);
}
if (["||=", "&&="].includes(node.operator) && inBooleanPosition) {
return isLogicalIdentity(node.right, node.operator.slice(0, -1));
}
return false;
case "SequenceExpression": case "SequenceExpression":
return isConstant(node.expressions[node.expressions.length - 1], inBooleanPosition); return isConstant(node.expressions[node.expressions.length - 1], inBooleanPosition);

View File

@ -8,7 +8,6 @@
const RegExpValidator = require("regexpp").RegExpValidator; const RegExpValidator = require("regexpp").RegExpValidator;
const collector = new (class { const collector = new (class {
constructor() { constructor() {
this.ecmaVersion = 2018;
this._source = ""; this._source = "";
this._controlChars = []; this._controlChars = [];
this._validator = new RegExpValidator(this); this._validator = new RegExpValidator(this);

View File

@ -138,7 +138,7 @@ module.exports = {
} }
/* /*
* `identifierNode.parent` is a MamberExpression `*.prototype`. * `identifierNode.parent` is a MemberExpression `*.prototype`.
* If it's an optional member access, it may be wrapped by a `ChainExpression` node. * If it's an optional member access, it may be wrapped by a `ChainExpression` node.
*/ */
const prototypeNode = const prototypeNode =

View File

@ -472,20 +472,34 @@ module.exports = {
const callee = node.callee; const callee = node.callee;
if (hasExcessParensWithPrecedence(callee, precedence(node))) { if (hasExcessParensWithPrecedence(callee, precedence(node))) {
const hasNewParensException = callee.type === "NewExpression" && !isNewExpressionWithParens(callee);
if ( if (
hasDoubleExcessParens(callee) || hasDoubleExcessParens(callee) ||
!isIIFE(node) &&
!hasNewParensException &&
!( !(
isIIFE(node) ||
// Allow extra parens around a new expression if they are intervening parentheses. // (new A)(); new (new A)();
node.type === "NewExpression" && (
callee.type === "MemberExpression" && callee.type === "NewExpression" &&
doesMemberExpressionContainCallExpression(callee) !isNewExpressionWithParens(callee) &&
) && !(
!(!node.optional && callee.type === "ChainExpression") node.type === "NewExpression" &&
!isNewExpressionWithParens(node)
)
) ||
// new (a().b)(); new (a.b().c);
(
node.type === "NewExpression" &&
callee.type === "MemberExpression" &&
doesMemberExpressionContainCallExpression(callee)
) ||
// (a?.b)(); (a?.())();
(
!node.optional &&
callee.type === "ChainExpression"
)
)
) { ) {
report(node.callee); report(node.callee);
} }
@ -511,7 +525,7 @@ module.exports = {
if (!shouldSkipLeft && hasExcessParens(node.left)) { if (!shouldSkipLeft && hasExcessParens(node.left)) {
if ( if (
!(node.left.type === "UnaryExpression" && isExponentiation) && !(["AwaitExpression", "UnaryExpression"].includes(node.left.type) && isExponentiation) &&
!astUtils.isMixedLogicalAndCoalesceExpressions(node.left, node) && !astUtils.isMixedLogicalAndCoalesceExpressions(node.left, node) &&
(leftPrecedence > prec || (leftPrecedence === prec && !isExponentiation)) || (leftPrecedence > prec || (leftPrecedence === prec && !isExponentiation)) ||
isParenthesisedTwice(node.left) isParenthesisedTwice(node.left)
@ -881,6 +895,22 @@ module.exports = {
} }
if (node.init) { if (node.init) {
if (node.init.type !== "VariableDeclaration") {
const firstToken = sourceCode.getFirstToken(node.init, astUtils.isNotOpeningParenToken);
if (
firstToken.value === "let" &&
astUtils.isOpeningBracketToken(
sourceCode.getTokenAfter(firstToken, astUtils.isNotClosingParenToken)
)
) {
// ForStatement#init expression cannot start with `let[`.
tokensToIgnore.add(firstToken);
}
}
startNewReportsBuffering(); startNewReportsBuffering();
if (hasExcessParens(node.init)) { if (hasExcessParens(node.init)) {

View File

@ -97,10 +97,10 @@ function isIterationVariable(node) {
* - `Object.defineProperties` * - `Object.defineProperties`
* - `Object.freeze` * - `Object.freeze`
* - `Object.setPrototypeOf` * - `Object.setPrototypeOf`
* - `Refrect.defineProperty` * - `Reflect.defineProperty`
* - `Refrect.deleteProperty` * - `Reflect.deleteProperty`
* - `Refrect.set` * - `Reflect.set`
* - `Refrect.setPrototypeOf` * - `Reflect.setPrototypeOf`
* @param {ASTNode} node The node to check. * @param {ASTNode} node The node to check.
* @param {Scope} scope A `escope.Scope` object to find variable (whichever). * @param {Scope} scope A `escope.Scope` object to find variable (whichever).
* @returns {boolean} `true` if the node is at the first argument of a well-known mutation function. * @returns {boolean} `true` if the node is at the first argument of a well-known mutation function.

View File

@ -9,7 +9,7 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
const RegExpValidator = require("regexpp").RegExpValidator; const RegExpValidator = require("regexpp").RegExpValidator;
const validator = new RegExpValidator({ ecmaVersion: 2018 }); const validator = new RegExpValidator();
const validFlags = /[gimuys]/gu; const validFlags = /[gimuys]/gu;
const undefined1 = void 0; const undefined1 = void 0;

View File

@ -82,7 +82,7 @@ module.exports = {
const commentNodes = sourceCode.getAllComments(); const commentNodes = sourceCode.getAllComments();
/** /**
* Removes errors that occur inside a string node * Removes errors that occur inside the given node
* @param {ASTNode} node to check for matching errors. * @param {ASTNode} node to check for matching errors.
* @returns {void} * @returns {void}
* @private * @private
@ -91,14 +91,12 @@ module.exports = {
const locStart = node.loc.start; const locStart = node.loc.start;
const locEnd = node.loc.end; const locEnd = node.loc.end;
errors = errors.filter(({ loc: { start: errorLoc } }) => { errors = errors.filter(({ loc: { start: errorLocStart } }) => (
if (errorLoc.line >= locStart.line && errorLoc.line <= locEnd.line) { errorLocStart.line < locStart.line ||
if (errorLoc.column >= locStart.column && (errorLoc.column <= locEnd.column || errorLoc.line < locEnd.line)) { errorLocStart.line === locStart.line && errorLocStart.column < locStart.column ||
return false; errorLocStart.line === locEnd.line && errorLocStart.column >= locEnd.column ||
} errorLocStart.line > locEnd.line
} ));
return true;
});
} }
/** /**

View File

@ -0,0 +1,147 @@
/**
* @fileoverview Rule to disallow `\8` and `\9` escape sequences in string literals.
* @author Milos Djermanovic
*/
"use strict";
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
const QUICK_TEST_REGEX = /\\[89]/u;
/**
* Returns unicode escape sequence that represents the given character.
* @param {string} character A single code unit.
* @returns {string} "\uXXXX" sequence.
*/
function getUnicodeEscape(character) {
return `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`;
}
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "disallow `\\8` and `\\9` escape sequences in string literals",
category: "Best Practices",
recommended: false,
url: "https://eslint.org/docs/rules/no-nonoctal-decimal-escape",
suggestion: true
},
schema: [],
messages: {
decimalEscape: "Don't use '{{decimalEscape}}' escape sequence.",
// suggestions
refactor: "Replace '{{original}}' with '{{replacement}}'. This maintains the current functionality.",
escapeBackslash: "Replace '{{original}}' with '{{replacement}}' to include the actual backslash character."
}
},
create(context) {
const sourceCode = context.getSourceCode();
/**
* Creates a new Suggestion object.
* @param {string} messageId "refactor" or "escapeBackslash".
* @param {int[]} range The range to replace.
* @param {string} replacement New text for the range.
* @returns {Object} Suggestion
*/
function createSuggestion(messageId, range, replacement) {
return {
messageId,
data: {
original: sourceCode.getText().slice(...range),
replacement
},
fix(fixer) {
return fixer.replaceTextRange(range, replacement);
}
};
}
return {
Literal(node) {
if (typeof node.value !== "string") {
return;
}
if (!QUICK_TEST_REGEX.test(node.raw)) {
return;
}
const regex = /(?:[^\\]|(?<previousEscape>\\.))*?(?<decimalEscape>\\[89])/suy;
let match;
while ((match = regex.exec(node.raw))) {
const { previousEscape, decimalEscape } = match.groups;
const decimalEscapeRangeEnd = node.range[0] + match.index + match[0].length;
const decimalEscapeRangeStart = decimalEscapeRangeEnd - decimalEscape.length;
const decimalEscapeRange = [decimalEscapeRangeStart, decimalEscapeRangeEnd];
const suggest = [];
// When `regex` is matched, `previousEscape` can only capture characters adjacent to `decimalEscape`
if (previousEscape === "\\0") {
/*
* Now we have a NULL escape "\0" immediately followed by a decimal escape, e.g.: "\0\8".
* Fixing this to "\08" would turn "\0" into a legacy octal escape. To avoid producing
* an octal escape while fixing a decimal escape, we provide different suggestions.
*/
suggest.push(
createSuggestion( // "\0\8" -> "\u00008"
"refactor",
[decimalEscapeRangeStart - previousEscape.length, decimalEscapeRangeEnd],
`${getUnicodeEscape("\0")}${decimalEscape[1]}`
),
createSuggestion( // "\8" -> "\u0038"
"refactor",
decimalEscapeRange,
getUnicodeEscape(decimalEscape[1])
)
);
} else {
suggest.push(
createSuggestion( // "\8" -> "8"
"refactor",
decimalEscapeRange,
decimalEscape[1]
)
);
}
suggest.push(
createSuggestion( // "\8" -> "\\8"
"escapeBackslash",
decimalEscapeRange,
`\\${decimalEscape}`
)
);
context.report({
node,
loc: {
start: sourceCode.getLocFromIndex(decimalEscapeRangeStart),
end: sourceCode.getLocFromIndex(decimalEscapeRangeEnd)
},
messageId: "decimalEscape",
data: {
decimalEscape
},
suggest
});
}
}
};
}
};

View File

@ -45,7 +45,7 @@ module.exports = {
/** /**
* Checks and reports given exported identifier. * Checks and reports given exported identifier.
* @param {ASTNode} node exported `Identifer` node to check. * @param {ASTNode} node exported `Identifier` node to check.
* @returns {void} * @returns {void}
*/ */
function checkExportedName(node) { function checkExportedName(node) {

View File

@ -171,7 +171,7 @@ module.exports = {
/** /**
* Removes the top of stack item. * Removes the top of stack item.
* *
* And this treverses all segments of this code path then reports every * And this traverses all segments of this code path then reports every
* invalid node. * invalid node.
* @param {CodePath} codePath A code path which was ended. * @param {CodePath} codePath A code path which was ended.
* @returns {void} * @returns {void}

View File

@ -0,0 +1,205 @@
/**
* @fileoverview Rule to disallow unsafe optional chaining
* @author Yeon JuAn
*/
"use strict";
const UNSAFE_ARITHMETIC_OPERATORS = new Set(["+", "-", "/", "*", "%", "**"]);
const UNSAFE_ASSIGNMENT_OPERATORS = new Set(["+=", "-=", "/=", "*=", "%=", "**="]);
const UNSAFE_RELATIONAL_OPERATORS = new Set(["in", "instanceof"]);
/**
* Checks whether a node is a destructuring pattern or not
* @param {ASTNode} node node to check
* @returns {boolean} `true` if a node is a destructuring pattern, otherwise `false`
*/
function isDestructuringPattern(node) {
return node.type === "ObjectPattern" || node.type === "ArrayPattern";
}
module.exports = {
meta: {
type: "problem",
docs: {
description: "disallow use of optional chaining in contexts where the `undefined` value is not allowed",
category: "Possible Errors",
recommended: false,
url: "https://eslint.org/docs/rules/no-unsafe-optional-chaining"
},
schema: [{
type: "object",
properties: {
disallowArithmeticOperators: {
type: "boolean",
default: false
}
},
additionalProperties: false
}],
fixable: null,
messages: {
unsafeOptionalChain: "Unsafe usage of optional chaining. If it short-circuits with 'undefined' the evaluation will throw TypeError.",
unsafeArithmetic: "Unsafe arithmetic operation on optional chaining. It can result in NaN."
}
},
create(context) {
const options = context.options[0] || {};
const disallowArithmeticOperators = (options.disallowArithmeticOperators) || false;
/**
* Reports unsafe usage of optional chaining
* @param {ASTNode} node node to report
* @returns {void}
*/
function reportUnsafeUsage(node) {
context.report({
messageId: "unsafeOptionalChain",
node
});
}
/**
* Reports unsafe arithmetic operation on optional chaining
* @param {ASTNode} node node to report
* @returns {void}
*/
function reportUnsafeArithmetic(node) {
context.report({
messageId: "unsafeArithmetic",
node
});
}
/**
* Checks and reports if a node can short-circuit with `undefined` by optional chaining.
* @param {ASTNode} [node] node to check
* @param {Function} reportFunc report function
* @returns {void}
*/
function checkUndefinedShortCircuit(node, reportFunc) {
if (!node) {
return;
}
switch (node.type) {
case "LogicalExpression":
if (node.operator === "||" || node.operator === "??") {
checkUndefinedShortCircuit(node.right, reportFunc);
} else if (node.operator === "&&") {
checkUndefinedShortCircuit(node.left, reportFunc);
checkUndefinedShortCircuit(node.right, reportFunc);
}
break;
case "SequenceExpression":
checkUndefinedShortCircuit(
node.expressions[node.expressions.length - 1],
reportFunc
);
break;
case "ConditionalExpression":
checkUndefinedShortCircuit(node.consequent, reportFunc);
checkUndefinedShortCircuit(node.alternate, reportFunc);
break;
case "AwaitExpression":
checkUndefinedShortCircuit(node.argument, reportFunc);
break;
case "ChainExpression":
reportFunc(node);
break;
default:
break;
}
}
/**
* Checks unsafe usage of optional chaining
* @param {ASTNode} node node to check
* @returns {void}
*/
function checkUnsafeUsage(node) {
checkUndefinedShortCircuit(node, reportUnsafeUsage);
}
/**
* Checks unsafe arithmetic operations on optional chaining
* @param {ASTNode} node node to check
* @returns {void}
*/
function checkUnsafeArithmetic(node) {
checkUndefinedShortCircuit(node, reportUnsafeArithmetic);
}
return {
"AssignmentExpression, AssignmentPattern"(node) {
if (isDestructuringPattern(node.left)) {
checkUnsafeUsage(node.right);
}
},
"ClassDeclaration, ClassExpression"(node) {
checkUnsafeUsage(node.superClass);
},
CallExpression(node) {
if (!node.optional) {
checkUnsafeUsage(node.callee);
}
},
NewExpression(node) {
checkUnsafeUsage(node.callee);
},
VariableDeclarator(node) {
if (isDestructuringPattern(node.id)) {
checkUnsafeUsage(node.init);
}
},
MemberExpression(node) {
if (!node.optional) {
checkUnsafeUsage(node.object);
}
},
TaggedTemplateExpression(node) {
checkUnsafeUsage(node.tag);
},
ForOfStatement(node) {
checkUnsafeUsage(node.right);
},
SpreadElement(node) {
if (node.parent && node.parent.type !== "ObjectExpression") {
checkUnsafeUsage(node.argument);
}
},
BinaryExpression(node) {
if (UNSAFE_RELATIONAL_OPERATORS.has(node.operator)) {
checkUnsafeUsage(node.right);
}
if (
disallowArithmeticOperators &&
UNSAFE_ARITHMETIC_OPERATORS.has(node.operator)
) {
checkUnsafeArithmetic(node.right);
checkUnsafeArithmetic(node.left);
}
},
WithStatement(node) {
checkUnsafeUsage(node.object);
},
UnaryExpression(node) {
if (
disallowArithmeticOperators &&
UNSAFE_ARITHMETIC_OPERATORS.has(node.operator)
) {
checkUnsafeArithmetic(node.argument);
}
},
AssignmentExpression(node) {
if (
disallowArithmeticOperators &&
UNSAFE_ASSIGNMENT_OPERATORS.has(node.operator)
) {
checkUnsafeArithmetic(node.right);
}
}
};
}
};

View File

@ -162,6 +162,14 @@ module.exports = {
return; return;
} }
/*
* Prevent crashing on parsers which do not require class constructor
* to have a body, e.g. typescript and flow
*/
if (!node.value.body) {
return;
}
const body = node.value.body.body; const body = node.value.body.body;
const ctorParams = node.value.params; const ctorParams = node.value.params;
const superClass = node.parent.parent.superClass; const superClass = node.parent.parent.superClass;

View File

@ -109,9 +109,9 @@ module.exports = {
* @returns {void} * @returns {void}
*/ */
function report(node, startOffset, character) { function report(node, startOffset, character) {
const start = sourceCode.getLocFromIndex(sourceCode.getIndexFromLoc(node.loc.start) + startOffset); const rangeStart = node.range[0] + startOffset;
const rangeStart = sourceCode.getIndexFromLoc(node.loc.start) + startOffset;
const range = [rangeStart, rangeStart + 1]; const range = [rangeStart, rangeStart + 1];
const start = sourceCode.getLocFromIndex(rangeStart);
context.report({ context.report({
node, node,
@ -172,7 +172,7 @@ module.exports = {
} }
if (isUnnecessaryEscape && !isQuoteEscape) { if (isUnnecessaryEscape && !isQuoteEscape) {
report(node, match.index + 1, match[0].slice(1)); report(node, match.index, match[0].slice(1));
} }
} }
@ -206,7 +206,7 @@ module.exports = {
return; return;
} }
const value = isTemplateElement ? node.value.raw : node.raw.slice(1, -1); const value = isTemplateElement ? sourceCode.getText(node) : node.raw;
const pattern = /\\[^\d]/gu; const pattern = /\\[^\d]/gu;
let match; let match;

View File

@ -5,6 +5,25 @@
"use strict"; "use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/**
* Determines whether the given node is in a statement list.
* @param {ASTNode} node node to check
* @returns {boolean} `true` if the given node is in a statement list
*/
function isInStatementList(node) {
return astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type);
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Rule Definition // Rule Definition
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -268,8 +287,8 @@ module.exports = {
/** /**
* Fixer to join VariableDeclaration's into a single declaration * Fixer to join VariableDeclaration's into a single declaration
* @param {VariableDeclarator[]} declarations The `VariableDeclaration` to join * @param {VariableDeclarator[]} declarations The `VariableDeclaration` to join
* @returns {Function} The fixer function * @returns {Function} The fixer function
*/ */
function joinDeclarations(declarations) { function joinDeclarations(declarations) {
const declaration = declarations[0]; const declaration = declarations[0];
@ -297,10 +316,17 @@ module.exports = {
/** /**
* Fixer to split a VariableDeclaration into individual declarations * Fixer to split a VariableDeclaration into individual declarations
* @param {VariableDeclaration} declaration The `VariableDeclaration` to split * @param {VariableDeclaration} declaration The `VariableDeclaration` to split
* @returns {Function} The fixer function * @returns {Function|null} The fixer function
*/ */
function splitDeclarations(declaration) { function splitDeclarations(declaration) {
const { parent } = declaration;
// don't autofix code such as: if (foo) var x, y;
if (!isInStatementList(parent.type === "ExportNamedDeclaration" ? parent : declaration)) {
return null;
}
return fixer => declaration.declarations.map(declarator => { return fixer => declaration.declarations.map(declarator => {
const tokenAfterDeclarator = sourceCode.getTokenAfter(declarator); const tokenAfterDeclarator = sourceCode.getTokenAfter(declarator);
@ -314,12 +340,14 @@ module.exports = {
return null; return null;
} }
const exportPlacement = declaration.parent.type === "ExportNamedDeclaration" ? "export " : "";
/* /*
* `var x,y` * `var x,y`
* tokenAfterDeclarator ^^ afterComma * tokenAfterDeclarator ^^ afterComma
*/ */
if (afterComma.range[0] === tokenAfterDeclarator.range[1]) { if (afterComma.range[0] === tokenAfterDeclarator.range[1]) {
return fixer.replaceText(tokenAfterDeclarator, `; ${declaration.kind} `); return fixer.replaceText(tokenAfterDeclarator, `; ${exportPlacement}${declaration.kind} `);
} }
/* /*
@ -341,11 +369,11 @@ module.exports = {
return fixer.replaceTextRange( return fixer.replaceTextRange(
[tokenAfterDeclarator.range[0], lastComment.range[0]], [tokenAfterDeclarator.range[0], lastComment.range[0]],
`;${sourceCode.text.slice(tokenAfterDeclarator.range[1], lastComment.range[0])}${declaration.kind} ` `;${sourceCode.text.slice(tokenAfterDeclarator.range[1], lastComment.range[0])}${exportPlacement}${declaration.kind} `
); );
} }
return fixer.replaceText(tokenAfterDeclarator, `; ${declaration.kind}`); return fixer.replaceText(tokenAfterDeclarator, `; ${exportPlacement}${declaration.kind}`);
}).filter(x => x); }).filter(x => x);
} }

View File

@ -279,7 +279,7 @@ module.exports = {
* @param {ASTNode} node the AssignmentExpression node * @param {ASTNode} node the AssignmentExpression node
* @returns {void} * @returns {void}
*/ */
function checkAssigmentExpression(node) { function checkAssignmentExpression(node) {
if (node.operator === "=") { if (node.operator === "=") {
performCheck(node.left, node.right, node); performCheck(node.left, node.right, node);
} }
@ -291,7 +291,7 @@ module.exports = {
return { return {
VariableDeclarator: checkVariableDeclarator, VariableDeclarator: checkVariableDeclarator,
AssignmentExpression: checkAssigmentExpression AssignmentExpression: checkAssignmentExpression
}; };
} }
}; };

View File

@ -30,6 +30,7 @@ function doesBaseNeedParens(base) {
astUtils.getPrecedence(base) <= PRECEDENCE_OF_EXPONENTIATION_EXPR || astUtils.getPrecedence(base) <= PRECEDENCE_OF_EXPONENTIATION_EXPR ||
// An unary operator cannot be used immediately before an exponentiation expression // An unary operator cannot be used immediately before an exponentiation expression
base.type === "AwaitExpression" ||
base.type === "UnaryExpression" base.type === "UnaryExpression"
); );
} }

View File

@ -105,10 +105,10 @@ module.exports = {
CallExpression(node) { CallExpression(node) {
const methodName = (node.callee.property || {}).name; const methodName = (node.callee.property || {}).name;
const isReflectCall = (node.callee.object || {}).name === "Reflect"; const isReflectCall = (node.callee.object || {}).name === "Reflect";
const hasReflectSubsitute = Object.prototype.hasOwnProperty.call(reflectSubstitutes, methodName); const hasReflectSubstitute = Object.prototype.hasOwnProperty.call(reflectSubstitutes, methodName);
const userConfiguredException = exceptions.indexOf(methodName) !== -1; const userConfiguredException = exceptions.indexOf(methodName) !== -1;
if (hasReflectSubsitute && !isReflectCall && !userConfiguredException) { if (hasReflectSubstitute && !isReflectCall && !userConfiguredException) {
report(node, existingNames[methodName], reflectSubstitutes[methodName]); report(node, existingNames[methodName], reflectSubstitutes[methodName]);
} }
}, },

View File

@ -113,6 +113,9 @@ class SegmentInfo {
if (info) { if (info) {
info.freshReadVariableNames.add(variableName); info.freshReadVariableNames.add(variableName);
// If a variable is freshly read again, then it's no more out-dated.
info.outdatedReadVariableNames.delete(variableName);
} }
} }
} }

View File

@ -1,5 +1,5 @@
/** /**
* @fileoverview This rule shoud require or disallow spaces before or after unary operations. * @fileoverview This rule should require or disallow spaces before or after unary operations.
* @author Marcin Kumorek * @author Marcin Kumorek
*/ */
"use strict"; "use strict";

View File

@ -82,7 +82,7 @@ function startsWithUpperCase(s) {
/** /**
* Checks whether or not a node is a constructor. * Checks whether or not a node is a constructor.
* @param {ASTNode} node A function node to check. * @param {ASTNode} node A function node to check.
* @returns {boolean} Wehether or not a node is a constructor. * @returns {boolean} Whether or not a node is a constructor.
*/ */
function isES5Constructor(node) { function isES5Constructor(node) {
return (node.id && startsWithUpperCase(node.id.name)); return (node.id && startsWithUpperCase(node.id.name));
@ -1574,7 +1574,7 @@ module.exports = {
}, },
/* /*
* Determine if a node has a possiblity to be an Error object * Determine if a node has a possibility to be an Error object
* @param {ASTNode} node ASTNode to check * @param {ASTNode} node ASTNode to check
* @returns {boolean} True if there is a chance it contains an Error obj * @returns {boolean} True if there is a chance it contains an Error obj
*/ */

View File

@ -15,7 +15,7 @@ const lodash = require("lodash");
// Private // Private
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Defitions for deprecation warnings. // Definitions for deprecation warnings.
const deprecationWarningMessages = { const deprecationWarningMessages = {
ESLINT_LEGACY_ECMAFEATURES: ESLINT_LEGACY_ECMAFEATURES:
"The 'ecmaFeatures' config file property is deprecated and has no effect.", "The 'ecmaFeatures' config file property is deprecated and has no effect.",

View File

@ -46,9 +46,9 @@ module.exports = {};
/** /**
* @typedef {Object} OverrideConfigData * @typedef {Object} OverrideConfigData
* @property {Record<string, boolean>} [env] The environment settings. * @property {Record<string, boolean>} [env] The environment settings.
* @property {string | string[]} [excludedFiles] The glob pattarns for excluded files. * @property {string | string[]} [excludedFiles] The glob patterns for excluded files.
* @property {string | string[]} [extends] The path to other config files or the package name of shareable configs. * @property {string | string[]} [extends] The path to other config files or the package name of shareable configs.
* @property {string | string[]} files The glob pattarns for target files. * @property {string | string[]} files The glob patterns for target files.
* @property {Record<string, GlobalConf>} [globals] The global variable settings. * @property {Record<string, GlobalConf>} [globals] The global variable settings.
* @property {boolean} [noInlineConfig] The flag that disables directive comments. * @property {boolean} [noInlineConfig] The flag that disables directive comments.
* @property {OverrideConfigData[]} [overrides] The override settings per kind of files. * @property {OverrideConfigData[]} [overrides] The override settings per kind of files.

View File

@ -1,6 +1,6 @@
{ {
"name": "eslint", "name": "eslint",
"version": "7.12.1", "version": "7.18.0",
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>", "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
"description": "An AST-based pattern checker for JavaScript.", "description": "An AST-based pattern checker for JavaScript.",
"bin": { "bin": {
@ -47,7 +47,7 @@
"bugs": "https://github.com/eslint/eslint/issues/", "bugs": "https://github.com/eslint/eslint/issues/",
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.0.0", "@babel/code-frame": "^7.0.0",
"@eslint/eslintrc": "^0.2.1", "@eslint/eslintrc": "^0.3.0",
"ajv": "^6.10.0", "ajv": "^6.10.0",
"chalk": "^4.0.0", "chalk": "^4.0.0",
"cross-spawn": "^7.0.2", "cross-spawn": "^7.0.2",
@ -57,10 +57,10 @@
"eslint-scope": "^5.1.1", "eslint-scope": "^5.1.1",
"eslint-utils": "^2.1.0", "eslint-utils": "^2.1.0",
"eslint-visitor-keys": "^2.0.0", "eslint-visitor-keys": "^2.0.0",
"espree": "^7.3.0", "espree": "^7.3.1",
"esquery": "^1.2.0", "esquery": "^1.2.0",
"esutils": "^2.0.2", "esutils": "^2.0.2",
"file-entry-cache": "^5.0.1", "file-entry-cache": "^6.0.0",
"functional-red-black-tree": "^1.0.1", "functional-red-black-tree": "^1.0.1",
"glob-parent": "^5.0.0", "glob-parent": "^5.0.0",
"globals": "^12.1.0", "globals": "^12.1.0",
@ -71,7 +71,7 @@
"js-yaml": "^3.13.1", "js-yaml": "^3.13.1",
"json-stable-stringify-without-jsonify": "^1.0.1", "json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1", "levn": "^0.4.1",
"lodash": "^4.17.19", "lodash": "^4.17.20",
"minimatch": "^3.0.4", "minimatch": "^3.0.4",
"natural-compare": "^1.4.0", "natural-compare": "^1.4.0",
"optionator": "^0.9.1", "optionator": "^0.9.1",
@ -80,7 +80,7 @@
"semver": "^7.2.1", "semver": "^7.2.1",
"strip-ansi": "^6.0.0", "strip-ansi": "^6.0.0",
"strip-json-comments": "^3.1.0", "strip-json-comments": "^3.1.0",
"table": "^5.2.3", "table": "^6.0.4",
"text-table": "^0.2.0", "text-table": "^0.2.0",
"v8-compile-cache": "^2.0.3" "v8-compile-cache": "^2.0.3"
}, },

View File

@ -1,38 +0,0 @@
var Linter = require("../../lib/linter").Linter,
fs = require("fs");
var config = require("../../conf/eslint-recommended");
var large = fs.readFileSync(__dirname + "/large.js", "utf8"),
medium = fs.readFileSync(__dirname + "/medium.js", "utf8"),
small = fs.readFileSync(__dirname + "/small.js", "utf8");
var runs = {
large: large,
medium: medium,
small: small
};
var linter = new Linter();
benchmark.runs = runs;
benchmark(Boolean, 1);
function benchmark(grep, times) {
console.profile("all");
for(var key in runs) {
if(grep(key)) {
console.time(key);
console.profile(key);
run(runs[key], times);
console.profileEnd(key);
console.timeEnd(key);
}
}
console.profileEnd("all");
}
function run(content, times) {
while(times--) {
linter.verify(content, config);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,418 @@
"use strict";
/*
* Parsed on astexplorer.net using @typescript-eslint/parser@4.1.0
*
* Source:
* declare class A { constructor(options: any); }
*/
exports.parse = () => ({
"type": "Program",
"body": [
{
"type": "ClassDeclaration",
"id": {
"type": "Identifier",
"name": "A",
"range": [
14,
15
],
"loc": {
"start": {
"line": 1,
"column": 14
},
"end": {
"line": 1,
"column": 15
}
}
},
"body": {
"type": "ClassBody",
"body": [
{
"type": "MethodDefinition",
"key": {
"type": "Identifier",
"name": "constructor",
"range": [
18,
29
],
"loc": {
"start": {
"line": 1,
"column": 18
},
"end": {
"line": 1,
"column": 29
}
}
},
"value": {
"type": "TSEmptyBodyFunctionExpression",
"id": null,
"params": [
{
"type": "Identifier",
"name": "options",
"range": [
30,
42
],
"loc": {
"start": {
"line": 1,
"column": 30
},
"end": {
"line": 1,
"column": 42
}
},
"typeAnnotation": {
"type": "TSTypeAnnotation",
"loc": {
"start": {
"line": 1,
"column": 37
},
"end": {
"line": 1,
"column": 42
}
},
"range": [
37,
42
],
"typeAnnotation": {
"type": "TSAnyKeyword",
"range": [
39,
42
],
"loc": {
"start": {
"line": 1,
"column": 39
},
"end": {
"line": 1,
"column": 42
}
}
}
}
}
],
"generator": false,
"expression": false,
"async": false,
"body": null,
"range": [
29,
44
],
"loc": {
"start": {
"line": 1,
"column": 29
},
"end": {
"line": 1,
"column": 44
}
}
},
"computed": false,
"static": false,
"kind": "constructor",
"range": [
18,
44
],
"loc": {
"start": {
"line": 1,
"column": 18
},
"end": {
"line": 1,
"column": 44
}
}
}
],
"range": [
16,
46
],
"loc": {
"start": {
"line": 1,
"column": 16
},
"end": {
"line": 1,
"column": 46
}
}
},
"superClass": null,
"range": [
0,
46
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 46
}
},
"declare": true
}
],
"sourceType": "module",
"range": [
0,
46
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 46
}
},
"tokens": [
{
"type": "Identifier",
"value": "declare",
"range": [
0,
7
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 7
}
}
},
{
"type": "Keyword",
"value": "class",
"range": [
8,
13
],
"loc": {
"start": {
"line": 1,
"column": 8
},
"end": {
"line": 1,
"column": 13
}
}
},
{
"type": "Identifier",
"value": "A",
"range": [
14,
15
],
"loc": {
"start": {
"line": 1,
"column": 14
},
"end": {
"line": 1,
"column": 15
}
}
},
{
"type": "Punctuator",
"value": "{",
"range": [
16,
17
],
"loc": {
"start": {
"line": 1,
"column": 16
},
"end": {
"line": 1,
"column": 17
}
}
},
{
"type": "Identifier",
"value": "constructor",
"range": [
18,
29
],
"loc": {
"start": {
"line": 1,
"column": 18
},
"end": {
"line": 1,
"column": 29
}
}
},
{
"type": "Punctuator",
"value": "(",
"range": [
29,
30
],
"loc": {
"start": {
"line": 1,
"column": 29
},
"end": {
"line": 1,
"column": 30
}
}
},
{
"type": "Identifier",
"value": "options",
"range": [
30,
37
],
"loc": {
"start": {
"line": 1,
"column": 30
},
"end": {
"line": 1,
"column": 37
}
}
},
{
"type": "Punctuator",
"value": ":",
"range": [
37,
38
],
"loc": {
"start": {
"line": 1,
"column": 37
},
"end": {
"line": 1,
"column": 38
}
}
},
{
"type": "Identifier",
"value": "any",
"range": [
39,
42
],
"loc": {
"start": {
"line": 1,
"column": 39
},
"end": {
"line": 1,
"column": 42
}
}
},
{
"type": "Punctuator",
"value": ")",
"range": [
42,
43
],
"loc": {
"start": {
"line": 1,
"column": 42
},
"end": {
"line": 1,
"column": 43
}
}
},
{
"type": "Punctuator",
"value": ";",
"range": [
43,
44
],
"loc": {
"start": {
"line": 1,
"column": 43
},
"end": {
"line": 1,
"column": 44
}
}
},
{
"type": "Punctuator",
"value": "}",
"range": [
45,
46
],
"loc": {
"start": {
"line": 1,
"column": 45
},
"end": {
"line": 1,
"column": 46
}
}
}
],
"comments": []
});

View File

@ -208,7 +208,7 @@ describe("ESLint", () => {
"- 'reportUnusedDisableDirectives' must be any of \"error\", \"warn\", \"off\", and null.", "- 'reportUnusedDisableDirectives' must be any of \"error\", \"warn\", \"off\", and null.",
"- 'resolvePluginsRelativeTo' must be a non-empty string or null.", "- 'resolvePluginsRelativeTo' must be a non-empty string or null.",
"- 'rulePaths' must be an array of non-empty strings.", "- 'rulePaths' must be an array of non-empty strings.",
"- 'useElintrc' must be a boolean." "- 'useEslintrc' must be a boolean."
].join("\n")), "u") ].join("\n")), "u")
); );
}); });

View File

@ -626,7 +626,7 @@ describe("createReportTranslator", () => {
); );
}); });
// This isn't offically supported, but autofix works the same way // This isn't officially supported, but autofix works the same way
it("should remove the whole suggestion if 'fix' function didn't return anything.", () => { it("should remove the whole suggestion if 'fix' function didn't return anything.", () => {
const reportDescriptor = { const reportDescriptor = {
node, node,

View File

@ -0,0 +1,51 @@
"use strict";
const { getListSize } = require("../../../lib/linter/timing");
const assert = require("chai").assert;
describe("timing", () => {
describe("getListSize()", () => {
after(() => {
delete process.env.TIMING;
});
it("returns minimum list size with small environment variable value", () => {
delete process.env.TIMING; // With no value.
assert.strictEqual(getListSize(), 10);
process.env.TIMING = "true";
assert.strictEqual(getListSize(), 10);
process.env.TIMING = "foo";
assert.strictEqual(getListSize(), 10);
process.env.TIMING = "0";
assert.strictEqual(getListSize(), 10);
process.env.TIMING = "1";
assert.strictEqual(getListSize(), 10);
process.env.TIMING = "5";
assert.strictEqual(getListSize(), 10);
process.env.TIMING = "10";
assert.strictEqual(getListSize(), 10);
});
it("returns longer list size with larger environment variable value", () => {
process.env.TIMING = "11";
assert.strictEqual(getListSize(), 11);
process.env.TIMING = "100";
assert.strictEqual(getListSize(), 100);
});
it("returns maximum list size with environment variable value of 'all'", () => {
process.env.TIMING = "all";
assert.strictEqual(getListSize(), Number.POSITIVE_INFINITY);
process.env.TIMING = "ALL";
assert.strictEqual(getListSize(), Number.POSITIVE_INFINITY);
});
});
});

View File

@ -48,7 +48,7 @@ function makeError(name, complexity, max) {
}; };
} }
const ruleTester = new RuleTester(); const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2021 } });
ruleTester.run("complexity", rule, { ruleTester.run("complexity", rule, {
valid: [ valid: [
@ -66,6 +66,18 @@ ruleTester.run("complexity", rule, {
{ code: "function a(x) {return x === 4 ? 3 : (x === 3 ? 2 : 1);}", options: [3] }, { code: "function a(x) {return x === 4 ? 3 : (x === 3 ? 2 : 1);}", options: [3] },
{ code: "function a(x) {return x || 4;}", options: [2] }, { code: "function a(x) {return x || 4;}", options: [2] },
{ code: "function a(x) {x && 4;}", options: [2] }, { code: "function a(x) {x && 4;}", options: [2] },
{ code: "function a(x) {x ?? 4;}", options: [2] },
{ code: "function a(x) {x ||= 4;}", options: [2] },
{ code: "function a(x) {x &&= 4;}", options: [2] },
{ code: "function a(x) {x ??= 4;}", options: [2] },
{ code: "function a(x) {x = 4;}", options: [1] },
{ code: "function a(x) {x |= 4;}", options: [1] },
{ code: "function a(x) {x &= 4;}", options: [1] },
{ code: "function a(x) {x += 4;}", options: [1] },
{ code: "function a(x) {x >>= 4;}", options: [1] },
{ code: "function a(x) {x >>>= 4;}", options: [1] },
{ code: "function a(x) {x == 4;}", options: [1] },
{ code: "function a(x) {x === 4;}", options: [1] },
{ code: "function a(x) {switch(x){case 1: 1; break; case 2: 2; break; default: 3;}}", options: [3] }, { code: "function a(x) {switch(x){case 1: 1; break; case 2: 2; break; default: 3;}}", options: [3] },
{ code: "function a(x) {switch(x){case 1: 1; break; case 2: 2; break; default: if(x == 'foo') {5;};}}", options: [4] }, { code: "function a(x) {switch(x){case 1: 1; break; case 2: 2; break; default: if(x == 'foo') {5;};}}", options: [4] },
{ code: "function a(x) {while(true) {'foo';}}", options: [2] }, { code: "function a(x) {while(true) {'foo';}}", options: [2] },
@ -95,6 +107,10 @@ ruleTester.run("complexity", rule, {
{ code: "function a(x) {return x === 4 ? 3 : (x === 3 ? 2 : 1);}", options: [2], errors: 1 }, { code: "function a(x) {return x === 4 ? 3 : (x === 3 ? 2 : 1);}", options: [2], errors: 1 },
{ code: "function a(x) {return x || 4;}", options: [1], errors: 1 }, { code: "function a(x) {return x || 4;}", options: [1], errors: 1 },
{ code: "function a(x) {x && 4;}", options: [1], errors: 1 }, { code: "function a(x) {x && 4;}", options: [1], errors: 1 },
{ code: "function a(x) {x ?? 4;}", options: [1], errors: 1 },
{ code: "function a(x) {x ||= 4;}", options: [1], errors: 1 },
{ code: "function a(x) {x &&= 4;}", options: [1], errors: 1 },
{ code: "function a(x) {x ??= 4;}", options: [1], errors: 1 },
{ code: "function a(x) {switch(x){case 1: 1; break; case 2: 2; break; default: 3;}}", options: [2], errors: 1 }, { code: "function a(x) {switch(x){case 1: 1; break; case 2: 2; break; default: 3;}}", options: [2], errors: 1 },
{ code: "function a(x) {switch(x){case 1: 1; break; case 2: 2; break; default: if(x == 'foo') {5;};}}", options: [3], errors: 1 }, { code: "function a(x) {switch(x){case 1: 1; break; case 2: 2; break; default: if(x == 'foo') {5;};}}", options: [3], errors: 1 },
{ code: "function a(x) {while(true) {'foo';}}", options: [1], errors: 1 }, { code: "function a(x) {while(true) {'foo';}}", options: [1], errors: 1 },

View File

@ -116,6 +116,7 @@ ruleTester.run("multiline-ternary", rule, {
// default "always" // default "always"
{ {
code: "a ? b : c", code: "a ? b : c",
output: "a\n? b\n: c",
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
line: 1, line: 1,
@ -129,6 +130,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a\n? b : c", code: "a\n? b : c",
output: "a\n? b\n: c",
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
line: 2, line: 2,
@ -137,6 +139,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ? b\n: c", code: "a ? b\n: c",
output: "a\n? b\n: c",
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
line: 1, line: 1,
@ -145,6 +148,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ? (b ? c : d) : e", code: "a ? (b ? c : d) : e",
output: "a\n? (b\n? c\n: d)\n: e",
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
line: 1, line: 1,
@ -170,6 +174,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ?\n(b ? c : d) :\ne", code: "a ?\n(b ? c : d) :\ne",
output: "a ?\n(b\n? c\n: d) :\ne",
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
line: 2, line: 2,
@ -183,6 +188,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ? (b\n? c\n: d) : e", code: "a ? (b\n? c\n: d) : e",
output: "a\n? (b\n? c\n: d)\n: e",
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
line: 1, line: 1,
@ -198,6 +204,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ?\n(b? c\n: d) : e", code: "a ?\n(b? c\n: d) : e",
output: "a ?\n(b\n? c\n: d)\n: e",
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
line: 2, line: 2,
@ -213,6 +220,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ?\n(b\n? c : d) : e", code: "a ?\n(b\n? c : d) : e",
output: "a ?\n(b\n? c\n: d)\n: e",
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
line: 2, line: 2,
@ -228,6 +236,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ?\n(b\n? c\n : d) : e", code: "a ?\n(b\n? c\n : d) : e",
output: "a ?\n(b\n? c\n : d)\n: e",
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
line: 2, line: 2,
@ -240,6 +249,7 @@ ruleTester.run("multiline-ternary", rule, {
// "always" // "always"
{ {
code: "a ? b : c", code: "a ? b : c",
output: "a\n? b\n: c",
options: ["always"], options: ["always"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -258,6 +268,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "f() ? a + b : c", code: "f() ? a + b : c",
output: "f()\n? a + b\n: c",
options: ["always"], options: ["always"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -276,6 +287,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a\n? b : c", code: "a\n? b : c",
output: "a\n? b\n: c",
options: ["always"], options: ["always"],
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
@ -285,6 +297,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ? b\n: c", code: "a ? b\n: c",
output: "a\n? b\n: c",
options: ["always"], options: ["always"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -294,6 +307,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ? (b ? c : d) : e", code: "a ? (b ? c : d) : e",
output: "a\n? (b\n? c\n: d)\n: e",
options: ["always"], options: ["always"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -320,6 +334,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ?\n(b ? c : d) :\ne", code: "a ?\n(b ? c : d) :\ne",
output: "a ?\n(b\n? c\n: d) :\ne",
options: ["always"], options: ["always"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -334,6 +349,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ? (b\n? c\n: d) : e", code: "a ? (b\n? c\n: d) : e",
output: "a\n? (b\n? c\n: d)\n: e",
options: ["always"], options: ["always"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -350,6 +366,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ?\n(b? c\n: d) : e", code: "a ?\n(b? c\n: d) : e",
output: "a ?\n(b\n? c\n: d)\n: e",
options: ["always"], options: ["always"],
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
@ -366,6 +383,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ?\n(b\n? c : d) : e", code: "a ?\n(b\n? c : d) : e",
output: "a ?\n(b\n? c\n: d)\n: e",
options: ["always"], options: ["always"],
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
@ -382,6 +400,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ?\n(b\n? c\n : d) : e", code: "a ?\n(b\n? c\n : d) : e",
output: "a ?\n(b\n? c\n : d)\n: e",
options: ["always"], options: ["always"],
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
@ -393,6 +412,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "(a\n) ? b\n: c", code: "(a\n) ? b\n: c",
output: "(a\n)\n? b\n: c",
options: ["always"], options: ["always"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -404,6 +424,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "((a)\n) ? b\n: c", code: "((a)\n) ? b\n: c",
output: "((a)\n)\n? b\n: c",
options: ["always"], options: ["always"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -415,6 +436,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ? (\nb)\n: c", code: "a ? (\nb)\n: c",
output: "a\n? (\nb)\n: c",
options: ["always"], options: ["always"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -426,6 +448,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ? (\n(b))\n: c", code: "a ? (\n(b))\n: c",
output: "a\n? (\n(b))\n: c",
options: ["always"], options: ["always"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -437,6 +460,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a\n? (b\n): c", code: "a\n? (b\n): c",
output: "a\n? (b\n)\n: c",
options: ["always"], options: ["always"],
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
@ -448,6 +472,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a\n? ((b)\n): c", code: "a\n? ((b)\n): c",
output: "a\n? ((b)\n)\n: c",
options: ["always"], options: ["always"],
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
@ -459,6 +484,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a\n? b : (\nc)", code: "a\n? b : (\nc)",
output: "a\n? b\n: (\nc)",
options: ["always"], options: ["always"],
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
@ -470,6 +496,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a\n? b : (\n(c))", code: "a\n? b : (\n(c))",
output: "a\n? b\n: (\n(c))",
options: ["always"], options: ["always"],
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
@ -481,6 +508,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "(a\n) ? (\nb\n) : (\nc)", code: "(a\n) ? (\nb\n) : (\nc)",
output: "(a\n)\n? (\nb\n)\n: (\nc)",
options: ["always"], options: ["always"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -499,6 +527,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "((a)\n) ? (\n(b)\n) : (\n(c))", code: "((a)\n) ? (\n(b)\n) : (\n(c))",
output: "((a)\n)\n? (\n(b)\n)\n: (\n(c))",
options: ["always"], options: ["always"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -519,6 +548,7 @@ ruleTester.run("multiline-ternary", rule, {
// "always-multiline" // "always-multiline"
{ {
code: "a\n? b : c", code: "a\n? b : c",
output: "a\n? b\n: c",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
@ -528,6 +558,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ? b\n: c", code: "a ? b\n: c",
output: "a\n? b\n: c",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -537,6 +568,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a &&\nb ? c : d", code: "a &&\nb ? c : d",
output: "a &&\nb\n? c\n: d",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -553,6 +585,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ? b +\nc : d", code: "a ? b +\nc : d",
output: "a\n? b +\nc\n: d",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -567,6 +600,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ? b : c +\nd", code: "a ? b : c +\nd",
output: "a\n? b\n: c +\nd",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -581,6 +615,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ?\n(b ? c : d) : e", code: "a ?\n(b ? c : d) : e",
output: "a ?\n(b ? c : d)\n: e",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
@ -592,6 +627,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ? (b ? c : d) :\ne", code: "a ? (b ? c : d) :\ne",
output: "a\n? (b ? c : d) :\ne",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -601,6 +637,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ? (b\n? c\n: d) : e", code: "a ? (b\n? c\n: d) : e",
output: "a\n? (b\n? c\n: d)\n: e",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -617,6 +654,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ?\n(b ? c\n: d) : e", code: "a ?\n(b ? c\n: d) : e",
output: "a ?\n(b\n? c\n: d)\n: e",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
@ -633,6 +671,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ?\n(b\n? c : d) : e", code: "a ?\n(b\n? c : d) : e",
output: "a ?\n(b\n? c\n: d)\n: e",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
@ -649,6 +688,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ?\n(b\n? c\n : d) : e", code: "a ?\n(b\n? c\n : d) : e",
output: "a ?\n(b\n? c\n : d)\n: e",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
@ -660,6 +700,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "(a\n) ? b\n: c", code: "(a\n) ? b\n: c",
output: "(a\n)\n? b\n: c",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -671,6 +712,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "((a)\n) ? b\n: c", code: "((a)\n) ? b\n: c",
output: "((a)\n)\n? b\n: c",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -682,6 +724,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ? (\nb)\n: c", code: "a ? (\nb)\n: c",
output: "a\n? (\nb)\n: c",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -693,6 +736,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ? (\n(b))\n: c", code: "a ? (\n(b))\n: c",
output: "a\n? (\n(b))\n: c",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -704,6 +748,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a\n? (b\n): c", code: "a\n? (b\n): c",
output: "a\n? (b\n)\n: c",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
@ -715,6 +760,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a\n? ((b)\n): c", code: "a\n? ((b)\n): c",
output: "a\n? ((b)\n)\n: c",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
@ -726,6 +772,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a\n? b : (\nc)", code: "a\n? b : (\nc)",
output: "a\n? b\n: (\nc)",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
@ -737,6 +784,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a\n? b : (\n(c))", code: "a\n? b : (\n(c))",
output: "a\n? b\n: (\n(c))",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedConsAlt", messageId: "expectedConsAlt",
@ -748,6 +796,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "(a\n) ? (\nb\n) : (\nc)", code: "(a\n) ? (\nb\n) : (\nc)",
output: "(a\n)\n? (\nb\n)\n: (\nc)",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -766,6 +815,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "((a)\n) ? (\n(b)\n) : (\n(c))", code: "((a)\n) ? (\n(b)\n) : (\n(c))",
output: "((a)\n)\n? (\n(b)\n)\n: (\n(c))",
options: ["always-multiline"], options: ["always-multiline"],
errors: [{ errors: [{
messageId: "expectedTestCons", messageId: "expectedTestCons",
@ -786,6 +836,7 @@ ruleTester.run("multiline-ternary", rule, {
// "never" // "never"
{ {
code: "a\n? b : c", code: "a\n? b : c",
output: "a? b : c",
options: ["never"], options: ["never"],
errors: [{ errors: [{
messageId: "unexpectedTestCons", messageId: "unexpectedTestCons",
@ -795,6 +846,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ? b\n: c", code: "a ? b\n: c",
output: "a ? b: c",
options: ["never"], options: ["never"],
errors: [{ errors: [{
messageId: "unexpectedConsAlt", messageId: "unexpectedConsAlt",
@ -804,6 +856,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ?\n(b ? c : d) :\ne", code: "a ?\n(b ? c : d) :\ne",
output: "a ?(b ? c : d) :e",
options: ["never"], options: ["never"],
errors: [{ errors: [{
messageId: "unexpectedTestCons", messageId: "unexpectedTestCons",
@ -820,6 +873,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ? (b\n? c\n: d) : e", code: "a ? (b\n? c\n: d) : e",
output: "a ? (b? c: d) : e",
options: ["never"], options: ["never"],
errors: [{ errors: [{
messageId: "unexpectedTestCons", messageId: "unexpectedTestCons",
@ -834,6 +888,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ?\n(b? c\n: d) : e", code: "a ?\n(b? c\n: d) : e",
output: "a ?(b? c: d) : e",
options: ["never"], options: ["never"],
errors: [{ errors: [{
messageId: "unexpectedTestCons", messageId: "unexpectedTestCons",
@ -848,6 +903,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ?\n(b\n? c : d) : e", code: "a ?\n(b\n? c : d) : e",
output: "a ?(b? c : d) : e",
options: ["never"], options: ["never"],
errors: [{ errors: [{
messageId: "unexpectedTestCons", messageId: "unexpectedTestCons",
@ -862,6 +918,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ?\n(b\n? c\n : d) : e", code: "a ?\n(b\n? c\n : d) : e",
output: "a ?(b? c: d) : e",
options: ["never"], options: ["never"],
errors: [{ errors: [{
messageId: "unexpectedTestCons", messageId: "unexpectedTestCons",
@ -881,6 +938,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a ? (b\n? c\n: d)\n: e", code: "a ? (b\n? c\n: d)\n: e",
output: "a ? (b? c: d): e",
options: ["never"], options: ["never"],
errors: [{ errors: [{
messageId: "unexpectedConsAlt", messageId: "unexpectedConsAlt",
@ -902,6 +960,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "a\n?\n(b\n?\nc\n:\nd)\n:\ne", code: "a\n?\n(b\n?\nc\n:\nd)\n:\ne",
output: "a?(b?c:d):e",
options: ["never"], options: ["never"],
errors: [{ errors: [{
messageId: "unexpectedTestCons", messageId: "unexpectedTestCons",
@ -928,6 +987,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "(a)\n ? b \n : (c)", code: "(a)\n ? b \n : (c)",
output: "(a)? b: (c)",
options: ["never"], options: ["never"],
errors: [{ errors: [{
messageId: "unexpectedTestCons", messageId: "unexpectedTestCons",
@ -946,6 +1006,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "(a)\n ? (b) \n : (c)", code: "(a)\n ? (b) \n : (c)",
output: "(a)? (b): (c)",
options: ["never"], options: ["never"],
errors: [{ errors: [{
messageId: "unexpectedTestCons", messageId: "unexpectedTestCons",
@ -964,6 +1025,7 @@ ruleTester.run("multiline-ternary", rule, {
}, },
{ {
code: "((a))\n ? ((b)) \n : ((c))", code: "((a))\n ? ((b)) \n : ((c))",
output: "((a))? ((b)): ((c))",
options: ["never"], options: ["never"],
errors: [{ errors: [{
messageId: "unexpectedTestCons", messageId: "unexpectedTestCons",
@ -979,6 +1041,15 @@ ruleTester.run("multiline-ternary", rule, {
endLine: 2, endLine: 2,
endColumn: 9 endColumn: 9
}] }]
},
// https://github.com/eslint/eslint/pull/12982#discussion_r409120960
{
code: "a ? // comment\nb : c;",
output: null,
errors: [{
messageId: "expectedConsAlt"
}]
} }
] ]
}); });

View File

@ -16,13 +16,58 @@ const rule = require("../../../lib/rules/no-constant-condition"),
// Tests // Tests
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } }); const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2021 } });
ruleTester.run("no-constant-condition", rule, { ruleTester.run("no-constant-condition", rule, {
valid: [ valid: [
"if(a);", "if(a);",
"if(a == 0);", "if(a == 0);",
"if(a = f());", "if(a = f());",
"if(a += 1);",
"if(a |= 1);",
"if(a |= true);",
"if(a |= false);",
"if(a &= 1);",
"if(a &= true);",
"if(a &= false);",
"if(a >>= 1);",
"if(a >>= true);",
"if(a >>= false);",
"if(a >>>= 1);",
"if(a ??= 1);",
"if(a ??= true);",
"if(a ??= false);",
"if(a ||= b);",
"if(a ||= false);",
"if(a ||= 0);",
"if(a ||= void 0);",
"if(+(a ||= 1));",
"if(f(a ||= true));",
"if((a ||= 1) + 2);",
"if(1 + (a ||= true));",
"if(a ||= '' || false);",
"if(a ||= void 0 || null);",
"if((a ||= false) || b);",
"if(a || (b ||= false));",
"if((a ||= true) && b);",
"if(a && (b ||= true));",
"if(a &&= b);",
"if(a &&= true);",
"if(a &&= 1);",
"if(a &&= 'foo');",
"if((a &&= '') + false);",
"if('' + (a &&= null));",
"if(a &&= 1 && 2);",
"if((a &&= true) && b);",
"if(a && (b &&= true));",
"if((a &&= false) || b);",
"if(a || (b &&= false));",
"if(a ||= b ||= false);",
"if(a &&= b &&= true);",
"if(a ||= b &&= false);",
"if(a ||= b &&= true);",
"if(a &&= b ||= false);",
"if(a &&= b ||= true);",
"if(1, a);", "if(1, a);",
"if ('every' in []);", "if ('every' in []);",
"if (`\\\n${a}`) {}", "if (`\\\n${a}`) {}",
@ -32,6 +77,10 @@ ruleTester.run("no-constant-condition", rule, {
"if (`foo${a}` === 'fooa');", "if (`foo${a}` === 'fooa');",
"if (tag`a`);", "if (tag`a`);",
"if (tag`${a}`);", "if (tag`${a}`);",
"if (+(a || true));",
"if (-(a || true));",
"if (~(a || 1));",
"if (+(a && 0) === +(b && 0));",
"while(~!a);", "while(~!a);",
"while(a = b);", "while(a = b);",
"while(`${a}`);", "while(`${a}`);",
@ -162,7 +211,41 @@ ruleTester.run("no-constant-condition", rule, {
{ code: "if(`foo${0 || 1}`);", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] }, { code: "if(`foo${0 || 1}`);", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] },
{ code: "if(`foo${bar}`);", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] }, { code: "if(`foo${bar}`);", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] },
{ code: "if(`${bar}foo`);", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] }, { code: "if(`${bar}foo`);", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] },
{ code: "if(!(true || a));", errors: [{ messageId: "unexpected", type: "UnaryExpression" }] },
{ code: "if(!(a && void b && c));", errors: [{ messageId: "unexpected", type: "UnaryExpression" }] },
{ code: "if(0 || !(a && null));", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
{ code: "if(1 + !(a || true));", errors: [{ messageId: "unexpected", type: "BinaryExpression" }] },
{ code: "if(!(null && a) > 1);", errors: [{ messageId: "unexpected", type: "BinaryExpression" }] },
{ code: "if(+(!(a && 0)));", errors: [{ messageId: "unexpected", type: "UnaryExpression" }] },
{ code: "if(!typeof a === 'string');", errors: [{ messageId: "unexpected", type: "BinaryExpression" }] },
{ code: "if(-('foo' || a));", errors: [{ messageId: "unexpected", type: "UnaryExpression" }] },
{ code: "if(+(void a && b) === ~(1 || c));", errors: [{ messageId: "unexpected", type: "BinaryExpression" }] },
{ code: "if(a ||= true);", errors: [{ messageId: "unexpected", type: "AssignmentExpression" }] },
{ code: "if(a ||= 5);", errors: [{ messageId: "unexpected", type: "AssignmentExpression" }] },
{ code: "if(a ||= 'foo' || b);", errors: [{ messageId: "unexpected", type: "AssignmentExpression" }] },
{ code: "if(a ||= b || /regex/);", errors: [{ messageId: "unexpected", type: "AssignmentExpression" }] },
{ code: "if(a ||= b ||= true);", errors: [{ messageId: "unexpected", type: "AssignmentExpression" }] },
{ code: "if(a ||= b ||= c || 1);", errors: [{ messageId: "unexpected", type: "AssignmentExpression" }] },
{ code: "if(!(a ||= true));", errors: [{ messageId: "unexpected", type: "UnaryExpression" }] },
{ code: "if(!(a ||= 'foo') === true);", errors: [{ messageId: "unexpected", type: "BinaryExpression" }] },
{ code: "if(!(a ||= 'foo') === false);", errors: [{ messageId: "unexpected", type: "BinaryExpression" }] },
{ code: "if(a || (b ||= true));", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
{ code: "if((a ||= 1) || b);", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
{ code: "if((a ||= true) && true);", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
{ code: "if(true && (a ||= true));", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
{ code: "if(a &&= false);", errors: [{ messageId: "unexpected", type: "AssignmentExpression" }] },
{ code: "if(a &&= null);", errors: [{ messageId: "unexpected", type: "AssignmentExpression" }] },
{ code: "if(a &&= void b);", errors: [{ messageId: "unexpected", type: "AssignmentExpression" }] },
{ code: "if(a &&= 0 && b);", errors: [{ messageId: "unexpected", type: "AssignmentExpression" }] },
{ code: "if(a &&= b && '');", errors: [{ messageId: "unexpected", type: "AssignmentExpression" }] },
{ code: "if(a &&= b &&= false);", errors: [{ messageId: "unexpected", type: "AssignmentExpression" }] },
{ code: "if(a &&= b &&= c && false);", errors: [{ messageId: "unexpected", type: "AssignmentExpression" }] },
{ code: "if(!(a &&= false));", errors: [{ messageId: "unexpected", type: "UnaryExpression" }] },
{ code: "if(!(a &&= 0) + 1);", errors: [{ messageId: "unexpected", type: "BinaryExpression" }] },
{ code: "if(a && (b &&= false));", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
{ code: "if((a &&= null) && b);", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
{ code: "if(false || (a &&= false));", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
{ code: "if((a &&= false) || false);", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
{ code: "while([]);", errors: [{ messageId: "unexpected", type: "ArrayExpression" }] }, { code: "while([]);", errors: [{ messageId: "unexpected", type: "ArrayExpression" }] },
{ code: "while(~!0);", errors: [{ messageId: "unexpected", type: "UnaryExpression" }] }, { code: "while(~!0);", errors: [{ messageId: "unexpected", type: "UnaryExpression" }] },

View File

@ -41,6 +41,11 @@ ruleTester.run("no-control-regex", rule, {
code: "var regex = /(?<a>\\x1f)/", code: "var regex = /(?<a>\\x1f)/",
parserOptions: { ecmaVersion: 2018 }, parserOptions: { ecmaVersion: 2018 },
errors: [{ messageId: "unexpected", data: { controlChars: "\\x1f" }, type: "Literal" }] errors: [{ messageId: "unexpected", data: { controlChars: "\\x1f" }, type: "Literal" }]
},
{
code: String.raw`var regex = /(?<\u{1d49c}>.)\x1f/`,
parserOptions: { ecmaVersion: 2020 },
errors: [{ messageId: "unexpected", data: { controlChars: "\\x1f" }, type: "Literal" }]
} }
] ]
}); });

View File

@ -96,6 +96,8 @@ ruleTester.run("no-extra-parens", rule, {
"(new A)()", "(new A)()",
"(new (Foo || Bar))()", "(new (Foo || Bar))()",
"(new new foo())()", "(new new foo())()",
"new (new A)()",
"new (new a.b)()",
"new (new new foo())(bar)", "new (new new foo())(bar)",
"(new foo).bar", "(new foo).bar",
"(new foo)[bar]", "(new foo)[bar]",
@ -377,6 +379,9 @@ ruleTester.run("no-extra-parens", rule, {
"async function a() { await (a + await b) }", "async function a() { await (a + await b) }",
"async function a() { (await a)() }", "async function a() { (await a)() }",
"async function a() { new (await a) }", "async function a() { new (await a) }",
"async function a() { await (a ** b) }",
"async function a() { (await a) ** b }",
{ code: "(foo instanceof bar) instanceof baz", options: ["all", { nestedBinaryExpressions: false }] }, { code: "(foo instanceof bar) instanceof baz", options: ["all", { nestedBinaryExpressions: false }] },
{ code: "(foo in bar) in baz", options: ["all", { nestedBinaryExpressions: false }] }, { code: "(foo in bar) in baz", options: ["all", { nestedBinaryExpressions: false }] },
{ code: "(foo + bar) + baz", options: ["all", { nestedBinaryExpressions: false }] }, { code: "(foo + bar) + baz", options: ["all", { nestedBinaryExpressions: false }] },
@ -594,6 +599,22 @@ ruleTester.run("no-extra-parens", rule, {
options: ["functions"] options: ["functions"]
}, },
"(let)[foo]", "(let)[foo]",
// ForStatement#init expression cannot start with `let[`. It would be parsed as a `let` declaration with array pattern, or a syntax error.
"for ((let[a]);;);",
"for ((let)[a];;);",
"for ((let[a] = 1);;);",
"for ((let[a]) = 1;;);",
"for ((let)[a] = 1;;);",
"for ((let[a, b] = foo);;);",
"for ((let[a].b = 1);;);",
"for ((let[a].b) = 1;;);",
"for ((let[a]).b = 1;;);",
"for ((let)[a].b = 1;;);",
"for ((let[a])();;);",
"for ((let)[a]();;);",
"for ((let[a]) + b;;);",
"for ((let) in foo);", "for ((let) in foo);",
"for ((let[foo]) in bar);", "for ((let[foo]) in bar);",
"for ((let)[foo] in bar);", "for ((let)[foo] in bar);",
@ -844,6 +865,10 @@ ruleTester.run("no-extra-parens", rule, {
invalid("new ((a().b().d))", "new (a().b().d)", "MemberExpression"), invalid("new ((a().b().d))", "new (a().b().d)", "MemberExpression"),
invalid("new ((a())).b.d", "new (a()).b.d", "CallExpression"), invalid("new ((a())).b.d", "new (a()).b.d", "CallExpression"),
invalid("new (a.b).d;", "new a.b.d;", "MemberExpression"), invalid("new (a.b).d;", "new a.b.d;", "MemberExpression"),
invalid("new (new A())();", "new new A()();", "NewExpression"),
invalid("new (new A());", "new new A();", "NewExpression"),
invalid("new (new A);", "new new A;", "NewExpression"),
invalid("new (new a.b);", "new new a.b;", "NewExpression"),
invalid("(a().b).d;", "a().b.d;", "MemberExpression"), invalid("(a().b).d;", "a().b.d;", "MemberExpression"),
invalid("(a.b()).d;", "a.b().d;", "CallExpression"), invalid("(a.b()).d;", "a.b().d;", "CallExpression"),
invalid("(a.b).d;", "a.b.d;", "MemberExpression"), invalid("(a.b).d;", "a.b.d;", "MemberExpression"),
@ -1187,6 +1212,8 @@ ruleTester.run("no-extra-parens", rule, {
invalid("async function a() { await (+a); }", "async function a() { await +a; }", "UnaryExpression", null), invalid("async function a() { await (+a); }", "async function a() { await +a; }", "UnaryExpression", null),
invalid("async function a() { +(await a); }", "async function a() { +await a; }", "AwaitExpression", null), invalid("async function a() { +(await a); }", "async function a() { +await a; }", "AwaitExpression", null),
invalid("async function a() { await ((a,b)); }", "async function a() { await (a,b); }", "SequenceExpression", null), invalid("async function a() { await ((a,b)); }", "async function a() { await (a,b); }", "SequenceExpression", null),
invalid("async function a() { a ** (await b); }", "async function a() { a ** await b; }", "AwaitExpression", null),
invalid("(foo) instanceof bar", "foo instanceof bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] }), invalid("(foo) instanceof bar", "foo instanceof bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] }),
invalid("(foo) in bar", "foo in bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] }), invalid("(foo) in bar", "foo in bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] }),
invalid("(foo) + bar", "foo + bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] }), invalid("(foo) + bar", "foo + bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] }),
@ -1869,6 +1896,165 @@ ruleTester.run("no-extra-parens", rule, {
"Identifier", "Identifier",
1 1
), ),
// ForStatement#init expression cannot start with `let[`, but it can start with `let` if it isn't followed by `[`
invalid(
"for ((let);;);",
"for (let;;);",
"Identifier",
1
),
invalid(
"for ((let = 1);;);",
"for (let = 1;;);",
"AssignmentExpression",
1
),
invalid(
"for ((let) = 1;;);",
"for (let = 1;;);",
"Identifier",
1
),
invalid(
"for ((let = []);;);",
"for (let = [];;);",
"AssignmentExpression",
1
),
invalid(
"for ((let) = [];;);",
"for (let = [];;);",
"Identifier",
1
),
invalid(
"for ((let());;);",
"for (let();;);",
"CallExpression",
1
),
invalid(
"for ((let([]));;);",
"for (let([]);;);",
"CallExpression",
1
),
invalid(
"for ((let())[a];;);",
"for (let()[a];;);",
"CallExpression",
1
),
invalid(
"for ((let`[]`);;);",
"for (let`[]`;;);",
"TaggedTemplateExpression",
1
),
invalid(
"for ((let.a);;);",
"for (let.a;;);",
"MemberExpression",
1
),
invalid(
"for ((let).a;;);",
"for (let.a;;);",
"Identifier",
1
),
invalid(
"for ((let).a = 1;;);",
"for (let.a = 1;;);",
"Identifier",
1
),
invalid(
"for ((let).a[b];;);",
"for (let.a[b];;);",
"Identifier",
1
),
invalid(
"for ((let.a)[b];;);",
"for (let.a[b];;);",
"MemberExpression",
1
),
invalid(
"for ((let.a[b]);;);",
"for (let.a[b];;);",
"MemberExpression",
1
),
invalid(
"for ((let);[];);",
"for (let;[];);",
"Identifier",
1
),
invalid(
"for (((let[a]));;);",
"for ((let[a]);;);",
"MemberExpression",
1
),
invalid(
"for (((let))[a];;);",
"for ((let)[a];;);",
"Identifier",
1
),
invalid(
"for (((let[a])).b;;);",
"for ((let[a]).b;;);",
"MemberExpression",
1
),
invalid(
"for (((let))[a].b;;);",
"for ((let)[a].b;;);",
"Identifier",
1
),
invalid(
"for (((let)[a]).b;;);",
"for ((let)[a].b;;);",
"MemberExpression",
1
),
invalid(
"for (((let[a]) = b);;);",
"for ((let[a]) = b;;);",
"AssignmentExpression",
1
),
invalid(
"for (((let)[a]) = b;;);",
"for ((let)[a] = b;;);",
"MemberExpression",
1
),
invalid(
"for (((let)[a] = b);;);",
"for ((let)[a] = b;;);",
"AssignmentExpression",
1
),
invalid(
"for ((Let[a]);;);",
"for (Let[a];;);",
"MemberExpression",
1
),
invalid(
"for ((lett)[a];;);",
"for (lett[a];;);",
"Identifier",
1
),
invalid( invalid(
"for ((let.foo) in bar);", "for ((let.foo) in bar);",
"for (let.foo in bar);", "for (let.foo in bar);",

View File

@ -24,24 +24,68 @@ ruleTester.run("no-invalid-regexp", rule, {
"new RegExp('.', 'im')", "new RegExp('.', 'im')",
"global.RegExp('\\\\')", "global.RegExp('\\\\')",
"new RegExp('.', y)", "new RegExp('.', y)",
{ code: "new RegExp('.', 'y')", options: [{ allowConstructorFlags: ["y"] }] }, "new RegExp('.', 'y')",
{ code: "new RegExp('.', 'u')", options: [{ allowConstructorFlags: ["U"] }] }, "new RegExp('.', 'u')",
{ code: "new RegExp('.', 'yu')", options: [{ allowConstructorFlags: ["y", "u"] }] }, "new RegExp('.', 'yu')",
{ code: "new RegExp('/', 'yu')", options: [{ allowConstructorFlags: ["y", "u"] }] }, "new RegExp('/', 'yu')",
{ code: "new RegExp('\\/', 'yu')", options: [{ allowConstructorFlags: ["y", "u"] }] }, "new RegExp('\\/', 'yu')",
{ code: "new RegExp('.', 'y')", parserOptions: { ecmaVersion: 6 } }, "new RegExp('\\\\u{65}', 'u')",
{ code: "new RegExp('.', 'u')", parserOptions: { ecmaVersion: 6 } }, "new RegExp('\\\\u{65}*', 'u')",
{ code: "new RegExp('.', 'yu')", parserOptions: { ecmaVersion: 6 } }, "new RegExp('[\\\\u{0}-\\\\u{1F}]', 'u')",
{ code: "new RegExp('/', 'yu')", parserOptions: { ecmaVersion: 6 } }, "new RegExp('.', 's')",
{ code: "new RegExp('\\/', 'yu')", parserOptions: { ecmaVersion: 6 } }, "new RegExp('(?<=a)b')",
{ code: "new RegExp('\\\\u{65}', 'u')", parserOptions: { ecmaVersion: 2015 } }, "new RegExp('(?<!a)b')",
{ code: "new RegExp('[\\\\u{0}-\\\\u{1F}]', 'u')", parserOptions: { ecmaVersion: 2015 } }, "new RegExp('(?<a>b)\\k<a>')",
{ code: "new RegExp('.', 's')", parserOptions: { ecmaVersion: 2018 } }, "new RegExp('(?<a>b)\\k<a>', 'u')",
{ code: "new RegExp('(?<=a)b')", parserOptions: { ecmaVersion: 2018 } }, "new RegExp('\\\\p{Letter}', 'u')",
{ code: "new RegExp('(?<!a)b')", parserOptions: { ecmaVersion: 2018 } },
{ code: "new RegExp('(?<a>b)\\k<a>')", parserOptions: { ecmaVersion: 2018 } }, // ES2020
{ code: "new RegExp('(?<a>b)\\k<a>', 'u')", parserOptions: { ecmaVersion: 2018 } }, "new RegExp('(?<\\\\ud835\\\\udc9c>.)', 'g')",
{ code: "new RegExp('\\\\p{Letter}', 'u')", parserOptions: { ecmaVersion: 2018 } } "new RegExp('(?<\\\\u{1d49c}>.)', 'g')",
"new RegExp('(?<𝒜>.)', 'g');",
"new RegExp('\\\\p{Script=Nandinagari}', 'u');",
// allowConstructorFlags
{
code: "new RegExp('.', 'g')",
options: [{ allowConstructorFlags: [] }]
},
{
code: "new RegExp('.', 'g')",
options: [{ allowConstructorFlags: ["a"] }]
},
{
code: "new RegExp('.', 'a')",
options: [{ allowConstructorFlags: ["a"] }]
},
{
code: "new RegExp('.', 'ag')",
options: [{ allowConstructorFlags: ["a"] }]
},
{
code: "new RegExp('.', 'ga')",
options: [{ allowConstructorFlags: ["a"] }]
},
{
code: "new RegExp('.', 'a')",
options: [{ allowConstructorFlags: ["a", "z"] }]
},
{
code: "new RegExp('.', 'z')",
options: [{ allowConstructorFlags: ["a", "z"] }]
},
{
code: "new RegExp('.', 'az')",
options: [{ allowConstructorFlags: ["a", "z"] }]
},
{
code: "new RegExp('.', 'za')",
options: [{ allowConstructorFlags: ["a", "z"] }]
},
{
code: "new RegExp('.', 'agz')",
options: [{ allowConstructorFlags: ["a", "z"] }]
}
], ],
invalid: [ invalid: [
{ {
@ -60,6 +104,42 @@ ruleTester.run("no-invalid-regexp", rule, {
type: "CallExpression" type: "CallExpression"
}] }]
}, },
{
code: "RegExp('.', 'a');",
options: [{}],
errors: [{
messageId: "regexMessage",
data: { message: "Invalid flags supplied to RegExp constructor 'a'" },
type: "CallExpression"
}]
},
{
code: "new RegExp('.', 'a');",
options: [{ allowConstructorFlags: [] }],
errors: [{
messageId: "regexMessage",
data: { message: "Invalid flags supplied to RegExp constructor 'a'" },
type: "NewExpression"
}]
},
{
code: "new RegExp('.', 'z');",
options: [{ allowConstructorFlags: ["a"] }],
errors: [{
messageId: "regexMessage",
data: { message: "Invalid flags supplied to RegExp constructor 'z'" },
type: "NewExpression"
}]
},
{
code: "new RegExp('.', 'az');",
options: [{ allowConstructorFlags: ["z"] }],
errors: [{
messageId: "regexMessage",
data: { message: "Invalid flags supplied to RegExp constructor 'a'" },
type: "NewExpression"
}]
},
{ {
code: "new RegExp(')');", code: "new RegExp(')');",
errors: [{ errors: [{
@ -68,6 +148,23 @@ ruleTester.run("no-invalid-regexp", rule, {
type: "NewExpression" type: "NewExpression"
}] }]
}, },
{
code: String.raw`new RegExp('\\a', 'u');`,
errors: [{
messageId: "regexMessage",
data: { message: "Invalid regular expression: /\\a/u: Invalid escape" },
type: "NewExpression"
}]
},
{
code: String.raw`new RegExp('\\a', 'u');`,
options: [{ allowConstructorFlags: ["u"] }],
errors: [{
messageId: "regexMessage",
data: { message: "Invalid regular expression: /\\a/u: Invalid escape" },
type: "NewExpression"
}]
},
// https://github.com/eslint/eslint/issues/10861 // https://github.com/eslint/eslint/issues/10861
{ {

View File

@ -164,6 +164,13 @@ ruleTester.run("no-irregular-whitespace", rule, {
{ code: "`\u205f`", options: [{ skipTemplates: true }], parserOptions: { ecmaVersion: 6 } }, { code: "`\u205f`", options: [{ skipTemplates: true }], parserOptions: { ecmaVersion: 6 } },
{ code: "`\u3000`", options: [{ skipTemplates: true }], parserOptions: { ecmaVersion: 6 } }, { code: "`\u3000`", options: [{ skipTemplates: true }], parserOptions: { ecmaVersion: 6 } },
{ code: "`\u3000${foo}\u3000`", options: [{ skipTemplates: true }], parserOptions: { ecmaVersion: 6 } },
{ code: "const error = ` \u3000 `;", options: [{ skipTemplates: true }], parserOptions: { ecmaVersion: 6 } },
{ code: "const error = `\n\u3000`;", options: [{ skipTemplates: true }], parserOptions: { ecmaVersion: 6 } },
{ code: "const error = `\u3000\n`;", options: [{ skipTemplates: true }], parserOptions: { ecmaVersion: 6 } },
{ code: "const error = `\n\u3000\n`;", options: [{ skipTemplates: true }], parserOptions: { ecmaVersion: 6 } },
{ code: "const error = `foo\u3000bar\nfoo\u3000bar`;", options: [{ skipTemplates: true }], parserOptions: { ecmaVersion: 6 } },
// Unicode BOM. // Unicode BOM.
"\uFEFFconsole.log('hello BOM');" "\uFEFFconsole.log('hello BOM');"
], ],
@ -541,6 +548,123 @@ ruleTester.run("no-irregular-whitespace", rule, {
} }
] ]
}, },
{
code: "`something ${10\u3000} another thing`",
options: [{ skipTemplates: true }],
parserOptions: { ecmaVersion: 6 },
errors: [
{
messageId: "noIrregularWhitespace",
type: "Program",
line: 1,
column: 16
}
]
},
{
code: "\u3000\n`\u3000template`",
options: [{ skipTemplates: true }],
parserOptions: { ecmaVersion: 6 },
errors: [
{
messageId: "noIrregularWhitespace",
type: "Program",
line: 1,
column: 1
}
]
},
{
code: "\u3000\n`\u3000multiline\ntemplate`",
options: [{ skipTemplates: true }],
parserOptions: { ecmaVersion: 6 },
errors: [
{
messageId: "noIrregularWhitespace",
type: "Program",
line: 1,
column: 1
}
]
},
{
code: "\u3000`\u3000template`",
options: [{ skipTemplates: true }],
parserOptions: { ecmaVersion: 6 },
errors: [
{
messageId: "noIrregularWhitespace",
type: "Program",
line: 1,
column: 1
}
]
},
{
code: "\u3000`\u3000multiline\ntemplate`",
options: [{ skipTemplates: true }],
parserOptions: { ecmaVersion: 6 },
errors: [
{
messageId: "noIrregularWhitespace",
type: "Program",
line: 1,
column: 1
}
]
},
{
code: "`\u3000template`\u3000",
options: [{ skipTemplates: true }],
parserOptions: { ecmaVersion: 6 },
errors: [
{
messageId: "noIrregularWhitespace",
type: "Program",
line: 1,
column: 12
}
]
},
{
code: "`\u3000multiline\ntemplate`\u3000",
options: [{ skipTemplates: true }],
parserOptions: { ecmaVersion: 6 },
errors: [
{
messageId: "noIrregularWhitespace",
type: "Program",
line: 2,
column: 10
}
]
},
{
code: "`\u3000template`\n\u3000",
options: [{ skipTemplates: true }],
parserOptions: { ecmaVersion: 6 },
errors: [
{
messageId: "noIrregularWhitespace",
type: "Program",
line: 2,
column: 1
}
]
},
{
code: "`\u3000multiline\ntemplate`\n\u3000",
options: [{ skipTemplates: true }],
parserOptions: { ecmaVersion: 6 },
errors: [
{
messageId: "noIrregularWhitespace",
type: "Program",
line: 3,
column: 1
}
]
},
// full location tests // full location tests
{ {

View File

@ -0,0 +1,488 @@
/**
* @fileoverview Tests for the no-nonoctal-decimal-escape rule.
* @author Milos Djermanovic
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const rule = require("../../../lib/rules/no-nonoctal-decimal-escape"),
{ RuleTester } = require("../../../lib/rule-tester");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/**
* Creates an error object.
* @param {string} decimalEscape Reported escape sequence.
* @param {number} column Reported column.
* @param {string} refactorOutput Output for "refactor" suggestion.
* @param {string} escapeBackslashOutput Output for "escapeBackslash" suggestion.
* @returns {Object} The error object.
*/
function error(decimalEscape, column, refactorOutput, escapeBackslashOutput) {
return {
messageId: "decimalEscape",
data: { decimalEscape },
type: "Literal",
line: 1,
column,
endLine: 1,
endColumn: column + 2,
suggestions: [
{
messageId: "refactor",
data: {
original: decimalEscape,
replacement: decimalEscape[1]
},
output: refactorOutput
},
{
messageId: "escapeBackslash",
data: {
original: decimalEscape,
replacement: `\\${decimalEscape}`
},
output: escapeBackslashOutput
}
]
};
}
//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------
const ruleTester = new RuleTester();
ruleTester.run("no-nonoctal-decimal-escape", rule, {
valid: [
"8",
"var \\u8888",
"/\\8/",
"''",
"'foo'",
"'8'",
"'9'",
"'foo8'",
"'foo9bar'",
"'\\ '",
"'\\\\'",
"'\\a'",
"'\\n'",
"'\\0'",
"'\\1'",
"'\\7'",
"'\\01'",
"'\\08'",
"'\\19'",
"'\\t9'",
"'\\👍8'",
"'\\\\8'",
"'\\\\9'",
"'\\\\8\\\\9'",
"'\\\\ \\\\8'",
"'\\\\\\\\9'",
"'\\\\9bar'",
"'a\\\\8'",
"'foo\\\\8'",
"'foo\\\\8bar'",
"'9\\\\9'",
"'n\\n8'",
"'n\\nn\\n8'",
"'\\1.8'",
"'\\1\\28'",
"'\\x99'",
"'\\\\\\x38'",
"\\u99999",
"'\\\n8'",
"'\\\n\\\\9'"
],
invalid: [
{
code: "'\\8'",
errors: [error("\\8", 2, "'8'", "'\\\\8'")]
},
{
code: "'\\9'",
errors: [error("\\9", 2, "'9'", "'\\\\9'")]
},
{
code: '"\\8"',
errors: [error("\\8", 2, '"8"', '"\\\\8"')]
},
{
code: "'f\\9'",
errors: [error("\\9", 3, "'f9'", "'f\\\\9'")]
},
{
code: "'fo\\9'",
errors: [error("\\9", 4, "'fo9'", "'fo\\\\9'")]
},
{
code: "'foo\\9'",
errors: [error("\\9", 5, "'foo9'", "'foo\\\\9'")]
},
{
code: "'foo\\8bar'",
errors: [error("\\8", 5, "'foo8bar'", "'foo\\\\8bar'")]
},
{
code: "'👍\\8'",
errors: [error("\\8", 4, "'👍8'", "'👍\\\\8'")]
},
{
code: "'\\\\\\8'",
errors: [error("\\8", 4, "'\\\\8'", "'\\\\\\\\8'")]
},
{
code: "'\\\\\\\\\\9'",
errors: [error("\\9", 6, "'\\\\\\\\9'", "'\\\\\\\\\\\\9'")]
},
{
code: "'foo\\\\\\8'",
errors: [error("\\8", 7, "'foo\\\\8'", "'foo\\\\\\\\8'")]
},
{
code: "'\\ \\8'",
errors: [error("\\8", 4, "'\\ 8'", "'\\ \\\\8'")]
},
{
code: "'\\1\\9'",
errors: [error("\\9", 4, "'\\19'", "'\\1\\\\9'")]
},
{
code: "'foo\\1\\9'",
errors: [error("\\9", 7, "'foo\\19'", "'foo\\1\\\\9'")]
},
{
code: "'\\n\\n\\8\\n'",
errors: [error("\\8", 6, "'\\n\\n8\\n'", "'\\n\\n\\\\8\\n'")]
},
{
code: "'\\n.\\n\\8\\n'",
errors: [error("\\8", 7, "'\\n.\\n8\\n'", "'\\n.\\n\\\\8\\n'")]
},
{
code: "'\\n.\\nn\\8\\n'",
errors: [error("\\8", 8, "'\\n.\\nn8\\n'", "'\\n.\\nn\\\\8\\n'")]
},
{
code: "'\\👍\\8'",
errors: [error("\\8", 5, "'\\👍8'", "'\\👍\\\\8'")]
},
{
code: "'\\\\8\\9'",
errors: [error("\\9", 5, "'\\\\89'", "'\\\\8\\\\9'")]
},
{
code: "'\\8\\\\9'",
errors: [error("\\8", 2, "'8\\\\9'", "'\\\\8\\\\9'")]
},
{
code: "'\\8 \\\\9'",
errors: [error("\\8", 2, "'8 \\\\9'", "'\\\\8 \\\\9'")]
},
// multiple errors in the same string
{
code: "'\\8\\8'",
errors: [
error("\\8", 2, "'8\\8'", "'\\\\8\\8'"),
error("\\8", 4, "'\\88'", "'\\8\\\\8'")
]
},
{
code: "'\\9\\8'",
errors: [
error("\\9", 2, "'9\\8'", "'\\\\9\\8'"),
error("\\8", 4, "'\\98'", "'\\9\\\\8'")
]
},
{
code: "'foo\\8bar\\9baz'",
errors: [
error("\\8", 5, "'foo8bar\\9baz'", "'foo\\\\8bar\\9baz'"),
error("\\9", 10, "'foo\\8bar9baz'", "'foo\\8bar\\\\9baz'")
]
},
{
code: "'\\8\\1\\9'",
errors: [
error("\\8", 2, "'8\\1\\9'", "'\\\\8\\1\\9'"),
error("\\9", 6, "'\\8\\19'", "'\\8\\1\\\\9'")
]
},
{
code: "'\\9\\n9\\\\9\\9'",
errors: [
error("\\9", 2, "'9\\n9\\\\9\\9'", "'\\\\9\\n9\\\\9\\9'"),
error("\\9", 10, "'\\9\\n9\\\\99'", "'\\9\\n9\\\\9\\\\9'")
]
},
{
code: "'\\8\\\\\\9'",
errors: [
error("\\8", 2, "'8\\\\\\9'", "'\\\\8\\\\\\9'"),
error("\\9", 6, "'\\8\\\\9'", "'\\8\\\\\\\\9'")
]
},
// multiple strings
{
code: "var foo = '\\8'; bar('\\9')",
errors: [
error("\\8", 12, "var foo = '8'; bar('\\9')", "var foo = '\\\\8'; bar('\\9')"),
error("\\9", 22, "var foo = '\\8'; bar('9')", "var foo = '\\8'; bar('\\\\9')")
]
},
// test reported line
{
code: "var foo = '8'\n bar = '\\9'",
errors: [{
...error("\\9", 10, "var foo = '8'\n bar = '9'", "var foo = '8'\n bar = '\\\\9'"),
line: 2,
endLine: 2
}]
},
// multiline strings
{
code: "'\\\n\\8'",
errors: [{
...error("\\8", 1, "'\\\n8'", "'\\\n\\\\8'"),
line: 2,
endLine: 2
}]
},
{
code: "'\\\r\n\\9'",
errors: [{
...error("\\9", 1, "'\\\r\n9'", "'\\\r\n\\\\9'"),
line: 2,
endLine: 2
}]
},
{
code: "'\\\\\\\n\\8'",
errors: [{
...error("\\8", 1, "'\\\\\\\n8'", "'\\\\\\\n\\\\8'"),
line: 2,
endLine: 2
}]
},
{
code: "'foo\\\nbar\\9baz'",
errors: [{
...error("\\9", 4, "'foo\\\nbar9baz'", "'foo\\\nbar\\\\9baz'"),
line: 2,
endLine: 2
}]
},
// adjacent NULL escape
{
code: "'\\0\\8'",
errors: [{
...error("\\8", 4),
suggestions: [
{
messageId: "refactor",
data: {
original: "\\0\\8",
replacement: "\\u00008"
},
output: "'\\u00008'"
},
{
messageId: "refactor",
data: {
original: "\\8",
replacement: "\\u0038"
},
output: "'\\0\\u0038'"
},
{
messageId: "escapeBackslash",
data: {
original: "\\8",
replacement: "\\\\8"
},
output: "'\\0\\\\8'"
}
]
}]
},
{
code: "'foo\\0\\9bar'",
errors: [{
...error("\\9", 7),
suggestions: [
{
messageId: "refactor",
data: {
original: "\\0\\9",
replacement: "\\u00009"
},
output: "'foo\\u00009bar'"
},
{
messageId: "refactor",
data: {
original: "\\9",
replacement: "\\u0039"
},
output: "'foo\\0\\u0039bar'"
},
{
messageId: "escapeBackslash",
data: {
original: "\\9",
replacement: "\\\\9"
},
output: "'foo\\0\\\\9bar'"
}
]
}]
},
{
code: "'\\1\\0\\8'",
errors: [{
...error("\\8", 6),
suggestions: [
{
messageId: "refactor",
data: {
original: "\\0\\8",
replacement: "\\u00008"
},
output: "'\\1\\u00008'"
},
{
messageId: "refactor",
data: {
original: "\\8",
replacement: "\\u0038"
},
output: "'\\1\\0\\u0038'"
},
{
messageId: "escapeBackslash",
data: {
original: "\\8",
replacement: "\\\\8"
},
output: "'\\1\\0\\\\8'"
}
]
}]
},
{
code: "'\\0\\8\\9'",
errors: [
{
...error("\\8", 4),
suggestions: [
{
messageId: "refactor",
data: {
original: "\\0\\8",
replacement: "\\u00008"
},
output: "'\\u00008\\9'"
},
{
messageId: "refactor",
data: {
original: "\\8",
replacement: "\\u0038"
},
output: "'\\0\\u0038\\9'"
},
{
messageId: "escapeBackslash",
data: {
original: "\\8",
replacement: "\\\\8"
},
output: "'\\0\\\\8\\9'"
}
]
},
error("\\9", 6, "'\\0\\89'", "'\\0\\8\\\\9'")
]
},
{
code: "'\\8\\0\\9'",
errors: [
error("\\8", 2, "'8\\0\\9'", "'\\\\8\\0\\9'"),
{
...error("\\9", 6),
suggestions: [
{
messageId: "refactor",
data: {
original: "\\0\\9",
replacement: "\\u00009"
},
output: "'\\8\\u00009'"
},
{
messageId: "refactor",
data: {
original: "\\9",
replacement: "\\u0039"
},
output: "'\\8\\0\\u0039'"
},
{
messageId: "escapeBackslash",
data: {
original: "\\9",
replacement: "\\\\9"
},
output: "'\\8\\0\\\\9'"
}
]
}
]
},
// not an adjacent NULL escape
{
code: "'0\\8'",
errors: [error("\\8", 3, "'08'", "'0\\\\8'")]
},
{
code: "'\\\\0\\8'",
errors: [error("\\8", 5, "'\\\\08'", "'\\\\0\\\\8'")]
},
{
code: "'\\0 \\8'",
errors: [error("\\8", 5, "'\\0 8'", "'\\0 \\\\8'")]
},
{
code: "'\\01\\8'",
errors: [error("\\8", 5, "'\\018'", "'\\01\\\\8'")]
},
{
code: "'\\0\\1\\8'",
errors: [error("\\8", 6, "'\\0\\18'", "'\\0\\1\\\\8'")]
},
{
code: "'\\0\\\n\\8'",
errors: [{
...error("\\8", 1, "'\\0\\\n8'", "'\\0\\\n\\\\8'"),
line: 2,
endLine: 2
}]
}
]
});

View File

@ -1,4 +1,3 @@
/* eslint no-octal-escape: 0 */
/** /**
* @fileoverview Tests for no-octal-escape rule. * @fileoverview Tests for no-octal-escape rule.
* @author Ian Christian Myers * @author Ian Christian Myers

View File

@ -0,0 +1,378 @@
/**
* @fileoverview Tests for no-unsafe-optional-chaining rule.
* @author Yeon JuAn
*/
"use strict";
const rule = require("../../../lib/rules/no-unsafe-optional-chaining");
const { RuleTester } = require("../../../lib/rule-tester");
const parserOptions = {
ecmaVersion: 2021,
sourceType: "module"
};
const ruleTester = new RuleTester({ parserOptions });
ruleTester.run("no-unsafe-optional-chaining", rule, {
valid: [
"var foo;",
"class Foo {}",
"!!obj?.foo",
"obj?.foo();",
"obj?.foo?.();",
"(obj?.foo ?? bar)();",
"(obj?.foo)?.()",
"(obj?.foo ?? bar?.baz)?.()",
"(obj.foo)?.();",
"obj?.foo.bar;",
"obj?.foo?.bar;",
"(obj?.foo)?.bar;",
"(obj?.foo)?.bar.baz;",
"(obj?.foo)?.().bar",
"(obj?.foo ?? bar).baz;",
"(obj?.foo ?? val)`template`",
"new (obj?.foo ?? val)()",
"new bar();",
"obj?.foo?.()();",
"const {foo} = obj?.baz || {};",
"const foo = obj?.bar",
"foo = obj?.bar",
"foo.bar = obj?.bar",
"bar(...obj?.foo ?? []);",
"var bar = {...foo?.bar};",
"foo?.bar in {};",
"foo?.bar < foo?.baz;",
"foo?.bar <= foo?.baz;",
"foo?.bar > foo?.baz;",
"foo?.bar >= foo?.baz;",
"[foo = obj?.bar] = [];",
"[foo.bar = obj?.bar] = [];",
"({foo = obj?.bar} = obj);",
"({foo: obj.bar = obj?.baz} = obj);",
"(foo?.bar, bar)();",
"(foo?.bar ? baz : qux)();",
`
async function func() {
await obj?.foo();
await obj?.foo?.();
(await obj?.foo)?.();
(await obj?.foo)?.bar;
await bar?.baz;
await (foo ?? obj?.foo.baz);
(await bar?.baz ?? bar).baz;
(await bar?.baz ?? await bar).baz;
await (foo?.bar ? baz : qux);
}
`,
// logical operations
"(obj?.foo ?? bar?.baz ?? qux)();",
"((obj?.foo ?? bar?.baz) || qux)();",
"((obj?.foo || bar?.baz) || qux)();",
"((obj?.foo && bar?.baz) || qux)();",
// The default value option disallowArithmeticOperators is false
"obj?.foo - bar;",
"obj?.foo + bar;",
"obj?.foo * bar;",
"obj?.foo / bar;",
"obj?.foo % bar;",
"obj?.foo ** bar;",
"+obj?.foo;",
"-obj?.foo;",
"bar += obj?.foo;",
"bar -= obj?.foo;",
"bar %= obj?.foo;",
"bar **= obj?.foo;",
"bar *= obj?.boo",
"bar /= obj?.boo",
`async function func() {
await obj?.foo + await obj?.bar;
await obj?.foo - await obj?.bar;
await obj?.foo * await obj?.bar;
+await obj?.foo;
-await obj?.foo;
bar += await obj?.foo;
bar -= await obj?.foo;
bar %= await obj?.foo;
bar **= await obj?.foo;
bar *= await obj?.boo;
bar /= await obj?.boo;
}
`,
...[
"obj?.foo | bar",
"obj?.foo & bar",
"obj?.foo >> obj?.bar;",
"obj?.foo << obj?.bar;",
"obj?.foo >>> obj?.bar;",
"(obj?.foo || baz) + bar;",
"(obj?.foo ?? baz) + bar;",
"(obj?.foo ?? baz) - bar;",
"(obj?.foo ?? baz) * bar;",
"(obj?.foo ?? baz) / bar;",
"(obj?.foo ?? baz) % bar;",
"(obj?.foo ?? baz) ** bar;",
"void obj?.foo;",
"typeof obj?.foo;",
"!obj?.foo",
"~obj?.foo",
"+(obj?.foo ?? bar)",
"-(obj?.foo ?? bar)",
"bar |= obj?.foo;",
"bar &= obj?.foo;",
"bar ^= obj?.foo;",
"bar <<= obj?.foo;",
"bar >>= obj?.foo;",
"bar >>>= obj?.foo;",
"bar ||= obj?.foo",
"bar &&= obj?.foo",
"bar += (obj?.foo ?? baz);",
"bar -= (obj?.foo ?? baz)",
"bar *= (obj?.foo ?? baz)",
"bar /= (obj?.foo ?? baz)",
"bar %= (obj?.foo ?? baz);",
"bar **= (obj?.foo ?? baz)",
`async function foo() {
(await obj?.foo || baz) + bar;
(await obj?.foo ?? baz) + bar;
(await obj?.foo ?? baz) - bar;
(await obj?.foo ?? baz) * bar;
(await obj?.foo ?? baz) / bar;
(await obj?.foo ?? baz) % bar;
"(await obj?.foo ?? baz) ** bar;",
"void await obj?.foo;",
"typeof await obj?.foo;",
"!await obj?.foo",
"~await obj?.foo",
"+(await obj?.foo ?? bar)",
"-(await obj?.foo ?? bar)",
bar |= await obj?.foo;
bar &= await obj?.foo;
bar ^= await obj?.foo;
bar <<= await obj?.foo;
bar >>= await obj?.foo;
bar >>>= await obj?.foo
bar += ((await obj?.foo) ?? baz);
bar -= ((await obj?.foo) ?? baz);
bar /= ((await obj?.foo) ?? baz);
bar %= ((await obj?.foo) ?? baz);
bar **= ((await obj?.foo) ?? baz);
}`
].map(code => ({
code,
options: [{
disallowArithmeticOperators: true
}]
})),
{
code: "obj?.foo - bar;",
options: [{}]
},
{
code: "obj?.foo - bar;",
options: [{
disallowArithmeticOperators: false
}]
}
],
invalid: [
...[
"(obj?.foo)();",
"(obj.foo ?? bar?.baz)();",
"(obj.foo || bar?.baz)();",
"(obj?.foo && bar)();",
"(bar && obj?.foo)();",
"(obj?.foo?.())();",
"(obj?.foo).bar",
"(obj?.foo)[1];",
"(obj?.foo)`template`",
"new (obj?.foo)();",
"new (obj?.foo?.())()",
"new (obj?.foo?.() || obj?.bar)()",
`async function foo() {
(await obj?.foo)();
}`,
`async function foo() {
(await obj?.foo).bar;
}`,
`async function foo() {
(bar?.baz ?? await obj?.foo)();
}`,
`async function foo() {
(bar && await obj?.foo)();
}`,
`async function foo() {
(await (bar && obj?.foo))();
}`,
// spread
"[...obj?.foo];",
"bar(...obj?.foo);",
"new Bar(...obj?.foo);",
// destructuring
"const {foo} = obj?.bar;",
"const {foo} = obj?.bar();",
"const {foo: bar} = obj?.bar();",
"const [foo] = obj?.bar;",
"const [foo] = obj?.bar || obj?.foo;",
"([foo] = obj?.bar);",
"const [foo] = obj?.bar?.();",
"[{ foo } = obj?.bar] = [];",
"({bar: [ foo ] = obj?.prop} = {});",
"[[ foo ] = obj?.bar] = [];",
"async function foo() { const {foo} = await obj?.bar; }",
"async function foo() { const {foo} = await obj?.bar(); }",
"async function foo() { const [foo] = await obj?.bar || await obj?.foo; }",
"async function foo() { ([foo] = await obj?.bar); }",
// class declaration
"class A extends obj?.foo {}",
"async function foo() { class A extends (await obj?.foo) {}}",
// class expression
"var a = class A extends obj?.foo {}",
"async function foo() { var a = class A extends (await obj?.foo) {}}",
// relational operations
"foo instanceof obj?.prop",
"async function foo() { foo instanceof await obj?.prop }",
"1 in foo?.bar;",
"async function foo() { 1 in await foo?.bar; }",
// for...of
"for (foo of obj?.bar);",
"async function foo() { for (foo of await obj?.bar);}",
// sequence expression
"(foo, obj?.foo)();",
"(foo, obj?.foo)[1];",
"async function foo() { (await (foo, obj?.foo))(); }",
"async function foo() { ((foo, await obj?.foo))(); }",
"async function foo() { (foo, await obj?.foo)[1]; }",
"async function foo() { (await (foo, obj?.foo)) [1]; }",
// conditional expression
"(a ? obj?.foo : b)();",
"(a ? b : obj?.foo)();",
"(a ? obj?.foo : b)[1];",
"(a ? b : obj?.foo).bar;",
"async function foo() { (await (a ? obj?.foo : b))(); }",
"async function foo() { (a ? await obj?.foo : b)(); }",
"async function foo() { (await (a ? b : obj?.foo))(); }",
"async function foo() { (await (a ? obj?.foo : b))[1]; }",
"async function foo() { (await (a ? b : obj?.foo)).bar; }",
"async function foo() { (a ? b : await obj?.foo).bar; }"
].map(code => ({
code,
errors: [{ messageId: "unsafeOptionalChain", type: "ChainExpression" }]
})),
{
code: "(obj?.foo && obj?.baz).bar",
errors: [
{
messageId: "unsafeOptionalChain",
type: "ChainExpression",
line: 1,
column: 2
},
{
messageId: "unsafeOptionalChain",
type: "ChainExpression",
line: 1,
column: 14
}
]
},
{
code: "with (obj?.foo) {};",
parserOptions: {
sourceType: "script"
},
errors: [
{
messageId: "unsafeOptionalChain",
type: "ChainExpression",
line: 1,
column: 7
}
]
},
{
code: "async function foo() { with ( await obj?.foo) {}; }",
parserOptions: {
sourceType: "script"
},
errors: [
{
messageId: "unsafeOptionalChain",
type: "ChainExpression",
line: 1,
column: 37
}
]
},
{
code: "(foo ? obj?.foo : obj?.bar).bar",
errors: [
{
messageId: "unsafeOptionalChain",
type: "ChainExpression",
line: 1,
column: 8
},
{
messageId: "unsafeOptionalChain",
type: "ChainExpression",
line: 1,
column: 19
}
]
},
...[
"obj?.foo + bar;",
"(foo || obj?.foo) + bar;",
"bar + (foo || obj?.foo);",
"(a ? obj?.foo : b) + bar",
"(a ? b : obj?.foo) + bar",
"(foo, bar, baz?.qux) + bar",
"obj?.foo - bar;",
"obj?.foo * bar;",
"obj?.foo / bar;",
"obj?.foo % bar;",
"obj?.foo ** bar;",
"+obj?.foo;",
"-obj?.foo;",
"+(foo ?? obj?.foo);",
"+(foo || obj?.bar);",
"+(obj?.bar && foo);",
"+(foo ? obj?.foo : bar);",
"+(foo ? bar : obj?.foo);",
"bar += obj?.foo;",
"bar -= obj?.foo;",
"bar %= obj?.foo;",
"bar **= obj?.foo;",
"bar *= obj?.boo",
"bar /= obj?.boo",
"bar += (foo ?? obj?.foo);",
"bar += (foo || obj?.foo);",
"bar += (foo && obj?.foo);",
"bar += (foo ? obj?.foo : bar);",
"bar += (foo ? bar : obj?.foo);",
"async function foo() { await obj?.foo + bar; }",
"async function foo() { (foo || await obj?.foo) + bar;}",
"async function foo() { bar + (foo || await obj?.foo); }"
].map(code => ({
code,
options: [{ disallowArithmeticOperators: true }],
errors: [{ messageId: "unsafeArithmetic", type: "ChainExpression" }]
}))
]
});

View File

@ -36,7 +36,11 @@ ruleTester.run("no-useless-constructor", rule, {
"class A extends B { constructor(foo, bar){ super(foo); } }", "class A extends B { constructor(foo, bar){ super(foo); } }",
"class A extends B { constructor(test) { super(); } }", "class A extends B { constructor(test) { super(); } }",
"class A extends B { constructor() { foo; } }", "class A extends B { constructor() { foo; } }",
"class A extends B { constructor(foo, bar) { super(bar); } }" "class A extends B { constructor(foo, bar) { super(bar); } }",
{
code: "declare class A { constructor(options: any); }",
parser: require.resolve("../../fixtures/parsers/typescript-parsers/declare-class")
}
], ],
invalid: [ invalid: [
{ {

View File

@ -955,6 +955,96 @@ ruleTester.run("no-useless-escape", rule, {
}] }]
}] }]
}, },
{
code: "`multiline template\r\nliteral with useless \\escape`",
parserOptions: { ecmaVersion: 6 },
errors: [{
line: 2,
column: 22,
endColumn: 23,
message: "Unnecessary escape character: \\e.",
type: "TemplateElement",
suggestions: [{
messageId: "removeEscape",
output: "`multiline template\r\nliteral with useless escape`"
}, {
messageId: "escapeBackslash",
output: "`multiline template\r\nliteral with useless \\\\escape`"
}]
}]
},
{
code: "`template literal with line continuation \\\nand useless \\escape`",
parserOptions: { ecmaVersion: 6 },
errors: [{
line: 2,
column: 13,
endColumn: 14,
message: "Unnecessary escape character: \\e.",
type: "TemplateElement",
suggestions: [{
messageId: "removeEscape",
output: "`template literal with line continuation \\\nand useless escape`"
}, {
messageId: "escapeBackslash",
output: "`template literal with line continuation \\\nand useless \\\\escape`"
}]
}]
},
{
code: "`template literal with line continuation \\\r\nand useless \\escape`",
parserOptions: { ecmaVersion: 6 },
errors: [{
line: 2,
column: 13,
endColumn: 14,
message: "Unnecessary escape character: \\e.",
type: "TemplateElement",
suggestions: [{
messageId: "removeEscape",
output: "`template literal with line continuation \\\r\nand useless escape`"
}, {
messageId: "escapeBackslash",
output: "`template literal with line continuation \\\r\nand useless \\\\escape`"
}]
}]
},
{
code: "`template literal with mixed linebreaks \r\r\n\n\\and useless escape`",
parserOptions: { ecmaVersion: 6 },
errors: [{
line: 4,
column: 1,
endColumn: 2,
message: "Unnecessary escape character: \\a.",
type: "TemplateElement",
suggestions: [{
messageId: "removeEscape",
output: "`template literal with mixed linebreaks \r\r\n\nand useless escape`"
}, {
messageId: "escapeBackslash",
output: "`template literal with mixed linebreaks \r\r\n\n\\\\and useless escape`"
}]
}]
},
{
code: "`template literal with mixed linebreaks in line continuations \\\n\\\r\\\r\n\\and useless escape`",
parserOptions: { ecmaVersion: 6 },
errors: [{
line: 4,
column: 1,
endColumn: 2,
message: "Unnecessary escape character: \\a.",
type: "TemplateElement",
suggestions: [{
messageId: "removeEscape",
output: "`template literal with mixed linebreaks in line continuations \\\n\\\r\\\r\nand useless escape`"
}, {
messageId: "escapeBackslash",
output: "`template literal with mixed linebreaks in line continuations \\\n\\\r\\\r\n\\\\and useless escape`"
}]
}]
},
{ {
code: "`\\a```", code: "`\\a```",
parserOptions: { ecmaVersion: 6 }, parserOptions: { ecmaVersion: 6 },

View File

@ -492,6 +492,16 @@ ruleTester.run("one-var", rule, {
} }
], ],
invalid: [ invalid: [
{
code: "var bar = true, baz = false;",
output: "var bar = true; var baz = false;",
options: ["never"],
errors: [{
messageId: "split",
data: { type: "var" },
type: "VariableDeclaration"
}]
},
{ {
code: "function foo() { var bar = true, baz = false; }", code: "function foo() { var bar = true, baz = false; }",
output: "function foo() { var bar = true; var baz = false; }", output: "function foo() { var bar = true; var baz = false; }",
@ -502,6 +512,36 @@ ruleTester.run("one-var", rule, {
type: "VariableDeclaration" type: "VariableDeclaration"
}] }]
}, },
{
code: "if (foo) { var bar = true, baz = false; }",
output: "if (foo) { var bar = true; var baz = false; }",
options: ["never"],
errors: [{
messageId: "split",
data: { type: "var" },
type: "VariableDeclaration"
}]
},
{
code: "switch (foo) { case bar: var baz = true, quux = false; }",
output: "switch (foo) { case bar: var baz = true; var quux = false; }",
options: ["never"],
errors: [{
messageId: "split",
data: { type: "var" },
type: "VariableDeclaration"
}]
},
{
code: "switch (foo) { default: var baz = true, quux = false; }",
output: "switch (foo) { default: var baz = true; var quux = false; }",
options: ["never"],
errors: [{
messageId: "split",
data: { type: "var" },
type: "VariableDeclaration"
}]
},
{ {
code: "function foo() { var bar = true; var baz = false; }", code: "function foo() { var bar = true; var baz = false; }",
output: "function foo() { var bar = true, baz = false; }", output: "function foo() { var bar = true, baz = false; }",
@ -1854,6 +1894,226 @@ ruleTester.run("one-var", rule, {
line: 2, line: 2,
column: 1 column: 1
}] }]
},
{
code: "export const foo=1, bar=2;",
output: "export const foo=1; export const bar=2;",
options: ["never"],
parserOptions: { ecmaVersion: 2021, sourceType: "module" },
errors: [{
messageId: "split",
data: { type: "const" },
type: "VariableDeclaration"
}]
},
{
code: "const foo=1,\n bar=2;",
output: "const foo=1;\n const bar=2;",
options: ["never"],
parserOptions: { ecmaVersion: 2021, sourceType: "module" },
errors: [{
messageId: "split",
data: { type: "const" },
type: "VariableDeclaration"
}]
},
{
code: "export const foo=1,\n bar=2;",
output: "export const foo=1;\n export const bar=2;",
options: ["never"],
parserOptions: { ecmaVersion: 2021, sourceType: "module" },
errors: [{
messageId: "split",
data: { type: "const" },
type: "VariableDeclaration"
}]
},
{
code: "export const foo=1\n, bar=2;",
output: "export const foo=1\n; export const bar=2;",
options: ["never"],
parserOptions: { ecmaVersion: 2021, sourceType: "module" },
errors: [{
messageId: "split",
data: { type: "const" },
type: "VariableDeclaration"
}]
},
{
code: "export const foo= a, bar=2;",
output: "export const foo= a; export const bar=2;",
options: ["never"],
parserOptions: { ecmaVersion: 2021, sourceType: "module" },
errors: [{
messageId: "split",
data: { type: "const" },
type: "VariableDeclaration"
}]
},
{
code: "export const foo=() => a, bar=2;",
output: "export const foo=() => a; export const bar=2;",
options: ["never"],
parserOptions: { ecmaVersion: 2021, sourceType: "module" },
errors: [{
messageId: "split",
data: { type: "const" },
type: "VariableDeclaration"
}]
},
{
code: "export const foo= a, bar=2, bar2=2;",
output: "export const foo= a; export const bar=2; export const bar2=2;",
options: ["never"],
parserOptions: { ecmaVersion: 2021, sourceType: "module" },
errors: [{
messageId: "split",
data: { type: "const" },
type: "VariableDeclaration"
}]
},
{
code: "export const foo = 1,bar = 2;",
output: "export const foo = 1; export const bar = 2;",
options: ["never"],
parserOptions: { ecmaVersion: 2021, sourceType: "module" },
errors: [{
messageId: "split",
data: { type: "const" },
type: "VariableDeclaration"
}]
},
// "never" should not autofix declarations in a block position
{
code: "if (foo) var x, y;",
output: null,
options: ["never"],
errors: [{
messageId: "split",
data: { type: "var" },
type: "VariableDeclaration"
}]
},
{
code: "if (foo) var x, y;",
output: null,
options: [{ var: "never" }],
errors: [{
messageId: "split",
data: { type: "var" },
type: "VariableDeclaration"
}]
},
{
code: "if (foo) var x, y;",
output: null,
options: [{ uninitialized: "never" }],
errors: [{
messageId: "splitUninitialized",
data: { type: "var" },
type: "VariableDeclaration"
}]
},
{
code: "if (foo) var x = 1, y = 1;",
output: null,
options: [{ initialized: "never" }],
errors: [{
messageId: "splitInitialized",
data: { type: "var" },
type: "VariableDeclaration"
}]
},
{
code: "if (foo) {} else var x, y;",
output: null,
options: ["never"],
errors: [{
messageId: "split",
data: { type: "var" },
type: "VariableDeclaration"
}]
},
{
code: "while (foo) var x, y;",
output: null,
options: ["never"],
errors: [{
messageId: "split",
data: { type: "var" },
type: "VariableDeclaration"
}]
},
{
code: "do var x, y; while (foo);",
output: null,
options: ["never"],
errors: [{
messageId: "split",
data: { type: "var" },
type: "VariableDeclaration"
}]
},
{
code: "do var x = f(), y = b(); while (x < y);",
output: null,
options: ["never"],
errors: [{
messageId: "split",
data: { type: "var" },
type: "VariableDeclaration"
}]
},
{
code: "for (;;) var x, y;",
output: null,
options: ["never"],
errors: [{
messageId: "split",
data: { type: "var" },
type: "VariableDeclaration"
}]
},
{
code: "for (foo in bar) var x, y;",
output: null,
options: ["never"],
errors: [{
messageId: "split",
data: { type: "var" },
type: "VariableDeclaration"
}]
},
{
code: "for (foo of bar) var x, y;",
output: null,
options: ["never"],
errors: [{
messageId: "split",
data: { type: "var" },
type: "VariableDeclaration"
}]
},
{
code: "with (foo) var x, y;",
output: null,
options: ["never"],
errors: [{
messageId: "split",
data: { type: "var" },
type: "VariableDeclaration"
}]
},
{
code: "label: var x, y;",
output: null,
options: ["never"],
errors: [{
messageId: "split",
data: { type: "var" },
type: "VariableDeclaration"
}]
} }
] ]
}); });

View File

@ -218,6 +218,8 @@ ruleTester.run("prefer-exponentiation-operator", rule, {
invalid("Math.pow(a, -b)", "a**-b"), invalid("Math.pow(a, -b)", "a**-b"),
invalid("Math.pow(-2, 3)", "(-2)**3"), invalid("Math.pow(-2, 3)", "(-2)**3"),
invalid("Math.pow(2, -3)", "2**-3"), invalid("Math.pow(2, -3)", "2**-3"),
invalid("async () => Math.pow(await a, b)", "async () => (await a)**b"),
invalid("async () => Math.pow(a, await b)", "async () => a**await b"),
// base and exponent with a lower precedence // base and exponent with a lower precedence
invalid("Math.pow(a * b, c)", "(a * b)**c"), invalid("Math.pow(a * b, c)", "(a * b)**c"),

View File

@ -150,6 +150,18 @@ ruleTester.run("require-atomic-updates", rule, {
let bar = await get(foo.id); let bar = await get(foo.id);
foo.prop = bar.prop; foo.prop = bar.prop;
} }
`,
// https://github.com/eslint/eslint/issues/11954
`
let count = 0
let queue = []
async function A(...args) {
count += 1
await new Promise(resolve=>resolve())
count -= 1
return
}
` `
], ],

View File

@ -22,6 +22,7 @@ ruleTester.run("space-in-parens", rule, {
valid: [ valid: [
{ code: "foo()", options: ["never"] }, { code: "foo()", options: ["never"] },
{ code: "foo()", options: ["always"] }, { code: "foo()", options: ["always"] },
{ code: "foo( )", options: ["always"] },
{ code: "foo( bar )", options: ["always"] }, { code: "foo( bar )", options: ["always"] },
{ code: "foo\n(\nbar\n)\n", options: ["always"] }, { code: "foo\n(\nbar\n)\n", options: ["always"] },
{ code: "foo\n( \nbar\n )\n", options: ["always"] }, { code: "foo\n( \nbar\n )\n", options: ["always"] },

View File

@ -156,6 +156,7 @@
"no-new-require": "suggestion", "no-new-require": "suggestion",
"no-new-symbol": "problem", "no-new-symbol": "problem",
"no-new-wrappers": "suggestion", "no-new-wrappers": "suggestion",
"no-nonoctal-decimal-escape": "suggestion",
"no-obj-calls": "problem", "no-obj-calls": "problem",
"no-octal": "suggestion", "no-octal": "suggestion",
"no-octal-escape": "suggestion", "no-octal-escape": "suggestion",
@ -204,6 +205,7 @@
"no-unreachable-loop": "problem", "no-unreachable-loop": "problem",
"no-unsafe-finally": "problem", "no-unsafe-finally": "problem",
"no-unsafe-negation": "problem", "no-unsafe-negation": "problem",
"no-unsafe-optional-chaining": "problem",
"no-unused-expressions": "suggestion", "no-unused-expressions": "suggestion",
"no-unused-labels": "suggestion", "no-unused-labels": "suggestion",
"no-unused-vars": "problem", "no-unused-vars": "problem",