mirror of
https://git.proxmox.com/git/pve-eslint
synced 2025-10-04 16:10:05 +00:00
import 7.12.1 upstream release
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
parent
a4a2572412
commit
6f03646270
2
Makefile
2
Makefile
@ -10,7 +10,7 @@ DSC=${PACKAGE}_${DEB_VERSION_UPSTREAM_REVISION}.dsc
|
||||
|
||||
SRCDIR=src
|
||||
UPSTREAM=eslint
|
||||
UPSTREAMTAG=v7.2.0
|
||||
UPSTREAMTAG=v7.12.1
|
||||
BUILDSRC=${UPSTREAM}-${UPSTREAMTAG}
|
||||
|
||||
all: ${DEB}
|
||||
|
@ -95,12 +95,8 @@ module.exports = {
|
||||
files: ["lib/rules/*", "tools/internal-rules/*"],
|
||||
excludedFiles: ["index.js"],
|
||||
rules: {
|
||||
"internal-rules/no-invalid-meta": "error"
|
||||
|
||||
/*
|
||||
* TODO: enable it when all the rules using meta.messages
|
||||
* "internal-rules/consistent-meta-messages": "error"
|
||||
*/
|
||||
"internal-rules/no-invalid-meta": "error",
|
||||
"internal-rules/consistent-meta-messages": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
17
eslint/.github/CODEOWNERS.md
vendored
Normal file
17
eslint/.github/CODEOWNERS.md
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
# Config-related files
|
||||
|
||||
lib/conf/config-schema.js @nzakas
|
||||
lib/cli-engine/config-array/* @nzakas
|
||||
lib/cli-engine/config-array-factory.js @nzakas
|
||||
lib/cli-engine/cascading-config-array-factory.js @nzakas
|
||||
lib/shared/config-* @nzakas
|
||||
lib/shared/naming.js @nzakas
|
||||
lib/shared/relative-module-resolver.js @nzakas
|
||||
|
||||
tests/lib/conf/config-schema.js @nzakas
|
||||
tests/lib/cli-engine/config-array/* @nzakas
|
||||
tests/lib/cli-engine/config-array-factory.js @nzakas
|
||||
tests/lib/cli-engine/cascading-config-array-factory.js @nzakas
|
||||
tests/lib/shared/config-* @nzakas
|
||||
tests/lib/shared/naming.js @nzakas
|
||||
tests/lib/shared/relative-module-resolver.js @nzakas
|
2
eslint/.github/ISSUE_TEMPLATE.md
vendored
2
eslint/.github/ISSUE_TEMPLATE.md
vendored
@ -22,7 +22,7 @@
|
||||
* **Node Version:**
|
||||
* **npm Version:**
|
||||
|
||||
**What parser (default, Babel-ESLint, etc.) are you using?**
|
||||
**What parser (default, `@babel/eslint-parser`, `@typescript-eslint/parser`, etc.) are you using?**
|
||||
|
||||
**Please show your full configuration:**
|
||||
|
||||
|
2
eslint/.github/ISSUE_TEMPLATE/BUG_REPORT.md
vendored
2
eslint/.github/ISSUE_TEMPLATE/BUG_REPORT.md
vendored
@ -32,7 +32,7 @@ assignees: ''
|
||||
* **Node Version:**
|
||||
* **npm Version:**
|
||||
|
||||
**What parser (default, Babel-ESLint, etc.) are you using?**
|
||||
**What parser (default, `@babel/eslint-parser`, `@typescript-eslint/parser`, etc.) are you using?**
|
||||
|
||||
**Please show your full configuration:**
|
||||
|
||||
|
@ -1,3 +1,226 @@
|
||||
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)
|
||||
* [`aeef485`](https://github.com/eslint/eslint/commit/aeef485dc790571b1a82ac09904329e0226b66a9) Fix: Pass internal config paths in FileEnumerator default (fixes #13789) (#13792) (Brandon Mills)
|
||||
* [`631ae8b`](https://github.com/eslint/eslint/commit/631ae8b50e5f7975f10860e9e763b70b4f25182e) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
|
||||
v7.12.0 - October 23, 2020
|
||||
|
||||
* [`cbf3585`](https://github.com/eslint/eslint/commit/cbf3585f1d6c60414c07380367a8b4505ee3538d) Update: skip keyword check for fns in space-before-blocks (fixes #13553) (#13712) (Milos Djermanovic)
|
||||
* [`256f656`](https://github.com/eslint/eslint/commit/256f656455b47bcf9ed3fc30fbf72532678f97da) Fix: autofix shouldn't produce template literals with `\8` or `\9` (#13737) (Milos Djermanovic)
|
||||
* [`b165aa5`](https://github.com/eslint/eslint/commit/b165aa5f4d4d19328f13ab80e5f058cbce94c3a6) Fix: yoda rule autofix produces syntax errors with adjacent tokens (#13760) (Milos Djermanovic)
|
||||
* [`3175316`](https://github.com/eslint/eslint/commit/3175316db26aebef4b19e269aca90c8ce3955363) Fix: prefer-destructuring invalid autofix with comma operator (#13761) (Milos Djermanovic)
|
||||
* [`1a9f171`](https://github.com/eslint/eslint/commit/1a9f17151a4e93eb17c8a2bf4f0a5320cce616de) Chore: Remove more ESLintRC-related files (refs #13481) (#13762) (Nicholas C. Zakas)
|
||||
* [`bfddced`](https://github.com/eslint/eslint/commit/bfddcedace5587d662c840c2edf33062b54a178e) Update: remove suggestion if it didn't provide a fix (fixes #13723) (#13772) (Milos Djermanovic)
|
||||
* [`5183b14`](https://github.com/eslint/eslint/commit/5183b14a2420b42b4089fb134a61ae57142f31fd) Update: check template literal in no-script-url (#13775) (YeonJuan)
|
||||
* [`bfe97d2`](https://github.com/eslint/eslint/commit/bfe97d2332e711ca76b1fd2e7f8548b0cc84cb1c) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`6c51ade`](https://github.com/eslint/eslint/commit/6c51adeb86f1de292cd02d2ee19f7b56182e358b) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`603de04`](https://github.com/eslint/eslint/commit/603de04cab5e700df12999af2918decd4da9d11b) Update: treat all literals like boolean literal in no-constant-condition (#13245) (Zen)
|
||||
* [`289aa6f`](https://github.com/eslint/eslint/commit/289aa6fcef3874ba5f86455f9302dc4209ea83e5) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`9a1f669`](https://github.com/eslint/eslint/commit/9a1f6694e59eb3e584d4c5a98b98675c895a9783) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`637f818`](https://github.com/eslint/eslint/commit/637f8187404ded600fb3d4013b3cd495d5ae675b) Docs: add more examples for no-func-assign (fixes #13705) (#13777) (Nitin Kumar)
|
||||
* [`17cc0dd`](https://github.com/eslint/eslint/commit/17cc0dd9b5d2d500359c36881cd3e5637443c133) Chore: add test case for no-func-assign (refs #13705) (#13783) (Nitin Kumar)
|
||||
* [`dee0f77`](https://github.com/eslint/eslint/commit/dee0f7764a1d5a323c89b22c4db94acee2b3c718) Docs: add TOC to user-guide/configuring.md (#13727) (metasean)
|
||||
* [`0510621`](https://github.com/eslint/eslint/commit/05106212985cb1ffa1e6fa996a57f6fd2fc3c970) Update: Fix && vs || short-circuiting false negatives (fixes #13634) (#13769) (Brandon Mills)
|
||||
* [`8b6ed69`](https://github.com/eslint/eslint/commit/8b6ed691c48189b7d096339441a78cb5874d4137) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`1457509`](https://github.com/eslint/eslint/commit/145750991b04fd4cfb3fff3c5d4211a4428e011c) Docs: fix broken links in Node.js API docs (#13771) (Laura Barluzzi)
|
||||
* [`7c813d4`](https://github.com/eslint/eslint/commit/7c813d458f9aedf7a94351d137728a4647542879) Docs: Fix typo in v7 migration page (#13778) (Yusuke Sasaki)
|
||||
* [`b025795`](https://github.com/eslint/eslint/commit/b0257953be704d0bb387fc15afd7859fd6f19ba5) Docs: Fix the format option name in the document (#13770) (Hideki Igarashi)
|
||||
* [`84fd591`](https://github.com/eslint/eslint/commit/84fd591c234accc41bb5af555f178825012fd35d) Chore: Increase Mocha timeout for copying fixtures (#13768) (Brandon Mills)
|
||||
* [`1faeb84`](https://github.com/eslint/eslint/commit/1faeb84e663d88c5d85a3cb3f15cd224cc552c2d) Docs: clarify that space-unary-ops doesn't apply when space is required (#13767) (Taylor Morgan)
|
||||
* [`67c0605`](https://github.com/eslint/eslint/commit/67c06059dd1ddcee6f369c650ce71220da1510c3) Update: check computed keys in no-prototype-builtins (fixes #13088) (#13755) (Milos Djermanovic)
|
||||
* [`b5e011c`](https://github.com/eslint/eslint/commit/b5e011c865e95d700d29cb9a4ba71c671d99e423) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
|
||||
v7.11.0 - October 9, 2020
|
||||
|
||||
* [`23e966f`](https://github.com/eslint/eslint/commit/23e966f6cf2a6c6b699dff5d6950ece3cc396498) Chore: Refactor CLIEngine tests (refs #13481) (#13709) (Nicholas C. Zakas)
|
||||
* [`fa9429a`](https://github.com/eslint/eslint/commit/fa9429aac0ffed505f3f02e8fc75f646c69f5c61) Fix: don't count line after EOF in max-lines (#13735) (Milos Djermanovic)
|
||||
* [`d973675`](https://github.com/eslint/eslint/commit/d973675a5c06a2bd4f8ce640c78b67842cfebfd4) Docs: Update anchor links to use existing linkrefs (refs #13715) (#13741) (Brandon Mills)
|
||||
* [`2c6d774`](https://github.com/eslint/eslint/commit/2c6d774c89dcd14f386bd9d73d451fa2a892c3ef) Docs: Fix typos (#13730) (Frieder Bluemle)
|
||||
* [`cc468c0`](https://github.com/eslint/eslint/commit/cc468c01021385a028de727eefcd442e7f34875c) Upgrade: eslint-visitor-keys@2.0.0 (#13732) (Milos Djermanovic)
|
||||
* [`ab0ac6c`](https://github.com/eslint/eslint/commit/ab0ac6c532fb7b7d49779c8913146244d680743b) Docs: Fix anchor links (#13715) (Gary Moore)
|
||||
* [`27f0de6`](https://github.com/eslint/eslint/commit/27f0de62e6281c28043be38ef051818c9edc15cd) Fix: account for linebreaks before postfix `++`/`--` in no-extra-parens (#13731) (Milos Djermanovic)
|
||||
* [`da78fa1`](https://github.com/eslint/eslint/commit/da78fa11632a2908db4ac494012a16f5d5a88a64) Update: support async arrow fn in function-paren-newline (fixes #13728) (#13729) (Michal Dziekonski)
|
||||
* [`fe301b8`](https://github.com/eslint/eslint/commit/fe301b8cc0762d7f4edd59603ca51ed0ec0c2a43) Docs: Add configuration comments in examples (#13738) (YeonJuan)
|
||||
* [`504408c`](https://github.com/eslint/eslint/commit/504408cd65e9d8827b2b8bbeb8f589df90eee523) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`3900659`](https://github.com/eslint/eslint/commit/390065985b2289ad4412a83598e3e833c382d27e) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`c1974b3`](https://github.com/eslint/eslint/commit/c1974b3f7169a8e5fab7007df92d02d8c1a8d5a3) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`6f4abe5`](https://github.com/eslint/eslint/commit/6f4abe5d5ade2711cc4c21bc8485af952763c2d3) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
|
||||
v7.10.0 - September 26, 2020
|
||||
|
||||
* [`6919fbb`](https://github.com/eslint/eslint/commit/6919fbb83f86552b0f49ae749da866e4edc7c46a) Docs: Clarify that ignorePattern should be a string (refs #13029) (#13718) (Brandon Mills)
|
||||
* [`07d9bea`](https://github.com/eslint/eslint/commit/07d9bea7c6f953e8f754afffc9752edcee799431) Update: Add ignorePattern to no-inline-comments (#13029) (Edie Lemoine)
|
||||
* [`d79bbe9`](https://github.com/eslint/eslint/commit/d79bbe982930b53358d34ad91cc6e5eaac8ddede) Docs: fix typo (#13717) (Alexander Liu)
|
||||
* [`9b8490e`](https://github.com/eslint/eslint/commit/9b8490ee6391c986b1314540a92b71d8c1e0efc4) Docs: grammatical error (#13687) (rajdeep)
|
||||
* [`cb44e93`](https://github.com/eslint/eslint/commit/cb44e93f4780e925a75a68ce2f7f6d065b5f756c) Fix: prefer-destructuring invalid autofix with computed property access (#13704) (Milos Djermanovic)
|
||||
* [`46c73b1`](https://github.com/eslint/eslint/commit/46c73b159a5ceed2f7f26f254fd97e459fb0e81a) Upgrade: eslint-scope@5.1.1 (#13716) (Milos Djermanovic)
|
||||
* [`b7b12ba`](https://github.com/eslint/eslint/commit/b7b12ba0bd4e9c66883f11e97de8ed84b600cdaa) Chore: Move comment to make tests more organized (#13707) (Yusuke Tanaka)
|
||||
* [`51674a4`](https://github.com/eslint/eslint/commit/51674a4113a1ca877094606bbf4938ab06cc1aad) Docs: Add missing quotes (#13714) (Lucio Paiva)
|
||||
* [`7c34a98`](https://github.com/eslint/eslint/commit/7c34a982aaf93a02348f56c9ce887c7dcf51b5bd) Chore: remove mistakenly added file (#13710) (Milos Djermanovic)
|
||||
* [`30b76c9`](https://github.com/eslint/eslint/commit/30b76c9a13fae3dff59f7db406d6c66f11152973) Docs: Clarify package.json requirement in Getting Started (refs #13549) (#13696) (Nicholas C. Zakas)
|
||||
* [`044560d`](https://github.com/eslint/eslint/commit/044560dcc74db98b28e293da2e2f3b41ecbf5884) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`54000d1`](https://github.com/eslint/eslint/commit/54000d13f27d5255851b5ac0606ad027e2b8d331) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
|
||||
v7.9.0 - September 12, 2020
|
||||
|
||||
* [`3ca2700`](https://github.com/eslint/eslint/commit/3ca27004ece5016ba7aed775f01ad13bc9282296) Fix: Corrected notice for invalid (:) plugin names (#13473) (Josh Goldberg)
|
||||
* [`fc5783d`](https://github.com/eslint/eslint/commit/fc5783d2ff9e3b0d7a1f9664928d49270b4a6c01) Docs: Fix leaky anchors in v4 migration page (#13635) (Timo Tijhof)
|
||||
* [`f1d07f1`](https://github.com/eslint/eslint/commit/f1d07f112be96c64dfdaa154aa9ac81985b16238) Docs: Provide install commands for Yarn (#13661) (Nikita Baksalyar)
|
||||
* [`29d1cdc`](https://github.com/eslint/eslint/commit/29d1cdceedd6c056a39149723cf9ff2fbb260cbf) Fix: prefer-destructuring removes comments (refs #13678) (#13682) (Milos Djermanovic)
|
||||
* [`b4da0a7`](https://github.com/eslint/eslint/commit/b4da0a7ca7995435bdfc116fd374eb0649470131) Docs: fix typo in working with plugins docs (#13683) (啸生)
|
||||
* [`6f87db7`](https://github.com/eslint/eslint/commit/6f87db7c318225e48ccbbf0bec8b3758ea839b82) Update: fix id-length false negatives on Object.prototype property names (#13670) (Milos Djermanovic)
|
||||
* [`361ac4d`](https://github.com/eslint/eslint/commit/361ac4d895c15086fb4351d4dca1405b2fdc4bd5) Fix: NonOctalDecimalIntegerLiteral is decimal integer (fixes #13588) (#13664) (Milos Djermanovic)
|
||||
* [`f260716`](https://github.com/eslint/eslint/commit/f260716695064e4b4193337107b60401bd4b3f20) Docs: update outdated link (#13677) (klkhan)
|
||||
* [`5138c91`](https://github.com/eslint/eslint/commit/5138c913c256e4266ffb68278783af45bf70af84) Docs: add missing eslint directive comments in no-await-in-loop (#13673) (Milos Djermanovic)
|
||||
* [`17b58b5`](https://github.com/eslint/eslint/commit/17b58b528df62bf96813d50c087cafdf83306810) Docs: clarify correct example in no-return-await (fixes #13656) (#13657) (Milos Djermanovic)
|
||||
* [`9171f0a`](https://github.com/eslint/eslint/commit/9171f0a99bb4d7c53f109b1c2b215004a7c27713) Chore: fix typo (#13660) (Nitin Kumar)
|
||||
* [`6d9f8fb`](https://github.com/eslint/eslint/commit/6d9f8fbb7ed4361b475fb50d04e6d25744d5b1a2) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`97b0dd9`](https://github.com/eslint/eslint/commit/97b0dd9a1af1ae4ae3857adcfe6eeac7837101ed) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`deab125`](https://github.com/eslint/eslint/commit/deab125fc9220dab43baeb32c6cf78942ad25a83) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`bf2e367`](https://github.com/eslint/eslint/commit/bf2e367bf4f6fde9930af9de8b8d8bc3d8b5782f) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`8929208`](https://github.com/eslint/eslint/commit/89292084bf91ba5ae5bf966c6c56fa3da139ce57) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
|
||||
v7.8.1 - September 1, 2020
|
||||
|
||||
* [`f542b5d`](https://github.com/eslint/eslint/commit/f542b5d0679b73326ad249fc44a54c3f848bd3e6) Fix: Update broken @eslint/eslintrc version (fixes #13641) (#13647) (Nicholas C. Zakas)
|
||||
* [`c1b5696`](https://github.com/eslint/eslint/commit/c1b56966c2354e12d16e8394443de49fa54f4290) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`8ddeda0`](https://github.com/eslint/eslint/commit/8ddeda01afdb1e9656a43853b8e25c9c4582e6ad) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`e02e2fe`](https://github.com/eslint/eslint/commit/e02e2fe019a1ed9a34a7b96e4c8961c35093b0ce) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
|
||||
v7.8.0 - August 31, 2020
|
||||
|
||||
* [`58abd93`](https://github.com/eslint/eslint/commit/58abd9311900a8af5a3c0963daaf64675bdd8383) Update: support logical assignments in code path analysis (refs #13569) (#13612) (Milos Djermanovic)
|
||||
* [`db7488e`](https://github.com/eslint/eslint/commit/db7488e6326fd1b7ea04c5062beb1c5f75fc15ed) Update: support logical assignments in core rules (refs #13569) (#13618) (Milos Djermanovic)
|
||||
* [`3729219`](https://github.com/eslint/eslint/commit/372921924778f2e525535985e17c97b988546210) Docs: Update Step 1 of Development Environment documentation (klkhan)
|
||||
* [`a320324`](https://github.com/eslint/eslint/commit/a32032430a0779a4e3b2d137d4d0682844084b82) Chore: Test formatted integers in no-dupe-keys (refs #13568) (#13626) (Brandon Mills)
|
||||
* [`88a9ade`](https://github.com/eslint/eslint/commit/88a9ade7643bb166efbab45cee15f3269496f4be) Update: add es2021 environment (refs #13602) (#13603) (Milos Djermanovic)
|
||||
* [`0003dc0`](https://github.com/eslint/eslint/commit/0003dc0f966f2b47555595586f84eb3163cb0179) Update: support numeric separators (refs #13568) (#13581) (Milos Djermanovic)
|
||||
* [`96b11a0`](https://github.com/eslint/eslint/commit/96b11a0717bf32b94ec768611574372320fb774b) Update: Add exceptionPatterns to id-length rule (fixes #13094) (#13576) (sodam)
|
||||
* [`3439fea`](https://github.com/eslint/eslint/commit/3439fea5c0ed330d01d874b0c9df51dd51ae792c) Update: support numeric-separator in no-loss-of-precision (refs #13568) (#13574) (Anix)
|
||||
* [`ed64767`](https://github.com/eslint/eslint/commit/ed64767859d776145d68145419a61f5379b4dd63) Update: add comment to message in no-warning-comments (fixes #12327) (#13522) (Anix)
|
||||
* [`e60ec07`](https://github.com/eslint/eslint/commit/e60ec07fad0c1d4c966f28d214c5379da753ff4e) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`483bf7f`](https://github.com/eslint/eslint/commit/483bf7f3cc40e0d866798d6ca9ee1c19aa77ddd2) Docs: fix examples in object-curly-newline (#13605) (Soobin Bak)
|
||||
* [`1c35d57`](https://github.com/eslint/eslint/commit/1c35d57b0a5f374cc55f1727a7561bcab1962e83) Docs: Remove stale Keybase 2FA instructions (#13622) (Brandon Mills)
|
||||
* [`82669fa`](https://github.com/eslint/eslint/commit/82669fa66670a00988db5b1d10fe8f3bf30be84e) Chore: Extract some functionality to eslintrc (refs #13481) (#13613) (Nicholas C. Zakas)
|
||||
* [`4111d21`](https://github.com/eslint/eslint/commit/4111d21a046b73892e2c84f92815a21ef4db63e1) Docs: Fix typo and missing article before noun in docs (#13611) (Patrice Sandhu)
|
||||
* [`091e52a`](https://github.com/eslint/eslint/commit/091e52ae1ca408f3e668f394c14d214c9ce806e6) Upgrade: espree@7.3.0 (refs #13568) (#13609) (Kai Cataldo)
|
||||
* [`05074fb`](https://github.com/eslint/eslint/commit/05074fb2c243e904e8c09d714ad9d084acdd80d2) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`bdb65ec`](https://github.com/eslint/eslint/commit/bdb65ec2e672c9815bee356b61d1cd60a1072152) Chore: add 3rd party parsers in BUG_REPORT template (#13606) (YeonJuan)
|
||||
* [`f954476`](https://github.com/eslint/eslint/commit/f954476fb6b0664679c73babd5e8a0647572b81f) Chore: add common 3rd party parsers to issue template (#13596) (Kai Cataldo)
|
||||
* [`2bee6d2`](https://github.com/eslint/eslint/commit/2bee6d256ae0516c9a9003bb3fdca24ff93253b5) Chore: Mark config-related files (refs #13481) (#13597) (Nicholas C. Zakas)
|
||||
* [`66442a9`](https://github.com/eslint/eslint/commit/66442a9faf9872db4a40f56dde28c48f4d02fc7b) Update: Add no-magic-numbers 'ignoreDefaultValues' option (#12611) (Dieter Luypaert)
|
||||
* [`b487164`](https://github.com/eslint/eslint/commit/b487164d01dd0bf66fdf2df0e374ce1c3bdb0339) Docs: add exponentiation operators to operator-assignment documentation (#13577) (Milos Djermanovic)
|
||||
* [`2f27836`](https://github.com/eslint/eslint/commit/2f27836e989f3dfe236e34054b490febc359bc48) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`60eafc1`](https://github.com/eslint/eslint/commit/60eafc15075f38955cb6816bf1f0bcf6e6e6d3a6) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
|
||||
v7.7.0 - August 14, 2020
|
||||
|
||||
* [`b46f3ee`](https://github.com/eslint/eslint/commit/b46f3ee0dae4add9df99cae940b641ad8de58b9e) Update: allowFunctionParams option in no-underscore-dangle (fixes 12579) (#13545) (Sunghyun Cho)
|
||||
* [`26aa245`](https://github.com/eslint/eslint/commit/26aa2452b5f407fabc25dad21182180e4d3be532) Docs: clarify "case" specifier in padding-line-between-statements (#13562) (Milos Djermanovic)
|
||||
* [`082891c`](https://github.com/eslint/eslint/commit/082891c042d72953fe86cd3ce9c96e661760793d) Docs: Update semantic versioning policy (#13563) (Nicholas C. Zakas)
|
||||
* [`4e0b672`](https://github.com/eslint/eslint/commit/4e0b672eb4bf39f7502a550b08b25a56a196f19f) Fix: revert "Update: disallow multiple options in comma-dangle schema" (#13564) (Kai Cataldo)
|
||||
* [`254990e`](https://github.com/eslint/eslint/commit/254990e87914457ca25ea2d7ee012964e56fc9e5) Fix: indent for async arrow functions (fixes #13497) (#13544) (Anix)
|
||||
* [`28ca339`](https://github.com/eslint/eslint/commit/28ca339259b07c96c73f2ef28cbf112b96395855) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`2e4158d`](https://github.com/eslint/eslint/commit/2e4158d3ec9cfed6400bf70795fd7171e96ff9b3) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`488d159`](https://github.com/eslint/eslint/commit/488d1595aef43c4d52cccdb2c97977884f0375a8) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`c44306e`](https://github.com/eslint/eslint/commit/c44306e52778309a79232ceab8b55a9aa0f2dfda) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`6677180`](https://github.com/eslint/eslint/commit/6677180495e16a02d150d0552e7e5d5f6b77fcc5) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`07db7b8`](https://github.com/eslint/eslint/commit/07db7b8080c2f68ee28e7d447db356c33e6fddce) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`d4ce4d3`](https://github.com/eslint/eslint/commit/d4ce4d3b8492c3e4654ed1f51f2c48e6c0ad272f) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`284e954`](https://github.com/eslint/eslint/commit/284e954f93126c50e0aa9b88f42afb03a47ad967) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`ae9b54e`](https://github.com/eslint/eslint/commit/ae9b54e59b01aa9f50ee31f5b6787d86e6b59de6) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`9124a15`](https://github.com/eslint/eslint/commit/9124a1599638a1caf4b7e252d1cb66abdc5e51c6) Chore: remove leche (fixes #13287) (#13533) (Mark de Dios)
|
||||
* [`5c4c7f5`](https://github.com/eslint/eslint/commit/5c4c7f515c2e8e83f2186a66ddce75d6477abeb0) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`48d8ec8`](https://github.com/eslint/eslint/commit/48d8ec8cf320c69aed17c6b6c78f19e7c1e587ca) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
|
||||
v7.6.0 - July 31, 2020
|
||||
|
||||
* [`ecb2b73`](https://github.com/eslint/eslint/commit/ecb2b7343a0d14fb57d297a16be6c1b176fb3dbf) Update: require `meta` for fixable rules in RuleTester (refs #13349) (#13489) (Milos Djermanovic)
|
||||
* [`6fb4edd`](https://github.com/eslint/eslint/commit/6fb4edde3b7a7ae2faf8ac956a7342fbf80865fc) Docs: fix broken links in developer guide (#13518) (Sam Chen)
|
||||
* [`318fe10`](https://github.com/eslint/eslint/commit/318fe103dbf2548eee293ff456ef0b829dbe3db3) Fix: Do not output `undefined` as line and column when it's unavailable (#13519) (haya14busa)
|
||||
* [`493b5b4`](https://github.com/eslint/eslint/commit/493b5b40cae7a076fdeb19740f8c88fb4ae9c1fb) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`f100143`](https://github.com/eslint/eslint/commit/f100143fa5f529aacb2b50e650a00d2697ca4c54) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`16b10fe`](https://github.com/eslint/eslint/commit/16b10fe8ba3c78939d5ada4a25caf2f0c9e6a058) Fix: Update the chatroom link to go directly to help channel (#13536) (Nicholas C. Zakas)
|
||||
* [`f937eb9`](https://github.com/eslint/eslint/commit/f937eb95407f60d3772bcb956e227aaf99e48777) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`e71e298`](https://github.com/eslint/eslint/commit/e71e2980cd2e319afc70d8c859c7ffd59cf4157b) Update: Change no-duplicate-case to comparing tokens (fixes #13485) (#13494) (Yosuke Ota)
|
||||
* [`6c4aea4`](https://github.com/eslint/eslint/commit/6c4aea44fd78e1eecea5fe3c37e1921e3b1e98a6) Docs: add ECMAScript 2020 to README (#13510) (Milos Djermanovic)
|
||||
|
||||
v7.5.0 - July 18, 2020
|
||||
|
||||
* [`6ea3178`](https://github.com/eslint/eslint/commit/6ea3178776eae0e40c3f5498893e8aab0e23686b) Update: optional chaining support (fixes #12642) (#13416) (Toru Nagashima)
|
||||
* [`540b1af`](https://github.com/eslint/eslint/commit/540b1af77278ae649b621aa8d4bf8d6de03c3155) Chore: enable consistent-meta-messages internal rule (#13487) (Milos Djermanovic)
|
||||
* [`885a145`](https://github.com/eslint/eslint/commit/885a1455691265db88dc0befe9b48a69d69e8b9c) Docs: clarify behavior if `meta.fixable` is omitted (refs #13349) (#13493) (Milos Djermanovic)
|
||||
* [`1a01b42`](https://github.com/eslint/eslint/commit/1a01b420eaab0de03dab5cc190a9f2a860c21a84) Docs: Update technology sponsors in README (#13478) (Nicholas C. Zakas)
|
||||
* [`6ed9e8e`](https://github.com/eslint/eslint/commit/6ed9e8e4ff038c0259b0e7fe7ab7f4fd4ec55801) Upgrade: lodash@4.17.19 (#13499) (Yohan Siguret)
|
||||
* [`45cdf00`](https://github.com/eslint/eslint/commit/45cdf00da6aeff3d584d37b0710fc8d6ad9456d6) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`f1cc725`](https://github.com/eslint/eslint/commit/f1cc725ba1b8646dcf06a83716d96ad9bb726172) Docs: fix linebreaks between versions in changelog (#13488) (Milos Djermanovic)
|
||||
* [`f4d7b9e`](https://github.com/eslint/eslint/commit/f4d7b9e1a599346b2f21ff9de003b311b51411e6) Update: deprecate id-blacklist rule (#13465) (Dimitri Mitropoulos)
|
||||
* [`e14a645`](https://github.com/eslint/eslint/commit/e14a645aa495558081490f990ba221e21aa6b27c) Chore: use espree.latestEcmaVersion in fuzzer (#13484) (Milos Djermanovic)
|
||||
* [`61097fe`](https://github.com/eslint/eslint/commit/61097fe5cc275d414a0c8e19b31c6060cb5568b7) Docs: Update int rule level to string (#13483) (Brandon Mills)
|
||||
* [`c8f9c82`](https://github.com/eslint/eslint/commit/c8f9c8210cf4b9da8f07922093d7b219abad9f10) Update: Improve report location no-irregular-whitespace (refs #12334) (#13462) (Milos Djermanovic)
|
||||
* [`f2e68ec`](https://github.com/eslint/eslint/commit/f2e68ec1d6cee6299e8a5cdf76c522c11d3008dd) Build: update webpack resolve.mainFields to match website config (#13457) (Milos Djermanovic)
|
||||
* [`a96bc5e`](https://github.com/eslint/eslint/commit/a96bc5ec06f3a48bfe458bccd68d4d3b2a280ed9) Fix: arrow-body-style fixer for `in` wrap (fixes #11849) (#13228) (Anix)
|
||||
* [`748734f`](https://github.com/eslint/eslint/commit/748734fdd497fbf61f3a616ff4a09169135b9396) Upgrade: Updated puppeteer version to v4.0.0 (#13444) (odidev)
|
||||
* [`e951457`](https://github.com/eslint/eslint/commit/e951457b7aaa1b12b135588d36e3f4db4d7b8463) Docs: fix wording in configuring.md (#13469) (Piper)
|
||||
* [`0af1d28`](https://github.com/eslint/eslint/commit/0af1d2828d27885483737867653ba1659af72005) Update: add allowSeparatedGroups option to sort-imports (fixes #12951) (#13455) (Milos Djermanovic)
|
||||
* [`1050ee7`](https://github.com/eslint/eslint/commit/1050ee78a95da9484ff333dc1c74dac64c05da6f) Update: Improve report location for no-unneeded-ternary (refs #12334) (#13456) (Milos Djermanovic)
|
||||
* [`b77b420`](https://github.com/eslint/eslint/commit/b77b4202bd1d5d1306f6f645e88d7a41a51715db) Update: Improve report location for max-len (refs #12334) (#13458) (Milos Djermanovic)
|
||||
* [`095194c`](https://github.com/eslint/eslint/commit/095194c0fc0eb02aa69fde6b4280696e0e4de214) Fix: add end location to reports in object-curly-newline (refs #12334) (#13460) (Milos Djermanovic)
|
||||
* [`10251bb`](https://github.com/eslint/eslint/commit/10251bbaeba80ac15244f385fc42cf2f2a30e5d2) Fix: add end location to reports in keyword-spacing (refs #12334) (#13461) (Milos Djermanovic)
|
||||
* [`2ea7ee5`](https://github.com/eslint/eslint/commit/2ea7ee51a4e05ee76a6dae5954c3b6263b0970a3) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`b55fd3b`](https://github.com/eslint/eslint/commit/b55fd3b8c05a29a465a794a524b06c1a28cddf0c) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
|
||||
v7.4.0 - July 3, 2020
|
||||
|
||||
* [`f21bad2`](https://github.com/eslint/eslint/commit/f21bad2680406a2671b877f8dba47f4475d0cc64) Docs: fix description for `never` in multiline-ternary (fixes #13368) (#13452) (Milos Djermanovic)
|
||||
* [`ada2c89`](https://github.com/eslint/eslint/commit/ada2c891298382f82dfabf37cacd59a1057b2bb7) Fix: support typescript generics in arrow-parens (fixes #12570) (#13451) (Milos Djermanovic)
|
||||
* [`89ee01e`](https://github.com/eslint/eslint/commit/89ee01e083f1e02293bf8d1447f9b0fdb3cb9384) Fix: Revert config cloning (fixes #13447) (#13449) (薛定谔的猫)
|
||||
* [`0a463db`](https://github.com/eslint/eslint/commit/0a463dbf7cc5a77d442879c9117204d4d38db972) Docs: fix no-multiple-empty-lines examples (fixes #13432) (#13433) (Milos Djermanovic)
|
||||
* [`ff5317e`](https://github.com/eslint/eslint/commit/ff5317e93425f93cfdf808609551ee67b2032543) Update: Improve array-callback-return report message (#13395) (Philip (flip) Kromer)
|
||||
* [`3f51930`](https://github.com/eslint/eslint/commit/3f51930eea7cddc921a9ee3cb0328c7b649c0f83) Fix: false positive new with member in no-extra-parens (fixes #12740) (#13375) (YeonJuan)
|
||||
* [`825a5b9`](https://github.com/eslint/eslint/commit/825a5b98d3d84f6eb72b75f7d8519de763cc8898) Fix: Clarify documentation on implicit ignore behavior (fixes #12348) (#12600) (Scott Hardin)
|
||||
* [`c139156`](https://github.com/eslint/eslint/commit/c1391566a5f765f25716527de7b5cdee16c0ce36) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`0c17e9d`](https://github.com/eslint/eslint/commit/0c17e9d2ac307cc288eea6ed7971bd5a7d33321a) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`c680387`](https://github.com/eslint/eslint/commit/c680387ba61f6dccf0390d24a85d871fa83e9fea) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`bf3939b`](https://github.com/eslint/eslint/commit/bf3939bbd9a33d0eb96cebe6a53bf61c855f9ba6) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`7baf02e`](https://github.com/eslint/eslint/commit/7baf02e983af909800261263f125cca901a5bd0f) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`5c4c3fd`](https://github.com/eslint/eslint/commit/5c4c3fdfbda18a13223ad36f44283adbfee8c496) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`53912aa`](https://github.com/eslint/eslint/commit/53912aab1856327b399cca26cbb2ba81fd01bfa2) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`51e42ec`](https://github.com/eslint/eslint/commit/51e42eca3e87d8259815d736ffe81e604f184057) Update: Add option "ignoreGlobals" to camelcase rule (fixes #11716) (#12782) (David Gasperoni)
|
||||
* [`0655f66`](https://github.com/eslint/eslint/commit/0655f66525d167ca1288167b79a77087cfc8fcf6) Update: improve report location in arrow-body-style (refs #12334) (#13424) (YeonJuan)
|
||||
* [`d53d69a`](https://github.com/eslint/eslint/commit/d53d69af08cfe55f42e0a0ca725b1014dabccc21) Update: prefer-regex-literal detect regex literals (fixes #12840) (#12842) (Mathias Schreck)
|
||||
* [`004adae`](https://github.com/eslint/eslint/commit/004adae3f959414f56e44e5884f6221e9dcda142) Update: rename id-blacklist to id-denylist (fixes #13407) (#13408) (Kai Cataldo)
|
||||
|
||||
v7.3.1 - June 22, 2020
|
||||
|
||||
* [`de77c11`](https://github.com/eslint/eslint/commit/de77c11e7515f2097ff355ddc0d7b6db9c83c892) Fix: Replace Infinity with Number.MAX_SAFE_INTEGER (fixes #13427) (#13435) (Nicholas C. Zakas)
|
||||
|
||||
v7.3.0 - June 19, 2020
|
||||
|
||||
* [`638a6d6`](https://github.com/eslint/eslint/commit/638a6d6be18b4a37cfdc7223e1f5acd3718694be) Update: add missing `additionalProperties: false` to some rules' schema (#13198) (Milos Djermanovic)
|
||||
* [`949a5cd`](https://github.com/eslint/eslint/commit/949a5cd741c2e930cfb43d80a9b6b084f9d677c3) Update: fix operator-linebreak overrides schema (#13199) (Milos Djermanovic)
|
||||
* [`9e1414e`](https://github.com/eslint/eslint/commit/9e1414ee16b8caf582920f8fdf3b6ee1eb0b7cd5) New: Add no-promise-executor-return rule (fixes #12640) (#12648) (Milos Djermanovic)
|
||||
* [`09cc0a2`](https://github.com/eslint/eslint/commit/09cc0a2bb5bcf3bcb0766a3c989871f268518437) Update: max-lines reporting loc improvement (refs #12334) (#13318) (Anix)
|
||||
* [`ee2fc2e`](https://github.com/eslint/eslint/commit/ee2fc2e90d0f9dfcdba852b0609156bee5280b92) Update: object-property-newline end location (refs #12334) (#13399) (Anix)
|
||||
* [`d98152a`](https://github.com/eslint/eslint/commit/d98152a3d8c72e4f5ac4c6fa10a615b12090c8f7) Update: added empty error array check for false negative (#13200) (Anix)
|
||||
* [`7fb45cf`](https://github.com/eslint/eslint/commit/7fb45cf13e9908d489bd6d5fba3b7243c01508b9) Fix: clone config before validating (fixes #12592) (#13034) (Anix)
|
||||
* [`aed46f6`](https://github.com/eslint/eslint/commit/aed46f69d54da167d9838149954ceeb4b02be5fd) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`7686d7f`](https://github.com/eslint/eslint/commit/7686d7feaccc7b8fee927eda6602d641d8de1e5c) Update: semi-spacing should check do-while statements (#13358) (Milos Djermanovic)
|
||||
* [`cbd0d00`](https://github.com/eslint/eslint/commit/cbd0d00a1ec2824d7e025bbbc084855ed0bf08bb) Update: disallow multiple options in comma-dangle schema (fixes #13165) (#13166) (Milos Djermanovic)
|
||||
* [`b550330`](https://github.com/eslint/eslint/commit/b550330d739c73a7a8f887064e7c911d05a95f9a) New: Add no-unreachable-loop rule (fixes #12381) (#12660) (Milos Djermanovic)
|
||||
* [`13999d2`](https://github.com/eslint/eslint/commit/13999d292080f814fa4fb266e011d61c184197c4) Update: curly should check consequent `if` statements (#12947) (Milos Djermanovic)
|
||||
* [`c42e548`](https://github.com/eslint/eslint/commit/c42e54893b79b470ca7745bd2a626ffd069e017b) Chore: enable exceptRange option in the yoda rule (#12857) (Milos Djermanovic)
|
||||
* [`6cfbd03`](https://github.com/eslint/eslint/commit/6cfbd03b3f22edb4d1c9c61c64eea7c129da71aa) Update: Drop @typescript-eslint/eslint-recommended from `eslint --init` (#13340) (Minh Nguyen)
|
||||
* [`796f269`](https://github.com/eslint/eslint/commit/796f269e448fdcbf8a5a62edf1990bd857efd1af) Chore: update eslint-config-eslint's required node version (#13379) (薛定谔的猫)
|
||||
* [`9d0186e`](https://github.com/eslint/eslint/commit/9d0186e55bee769ea6aa08dc5a62682f58316412) Docs: Fix changelog versions (#13410) (Tony Brix)
|
||||
* [`1ee3c42`](https://github.com/eslint/eslint/commit/1ee3c42ceeee56b650bcc4206ed783b795f65643) Docs: On maxEOF with eol-last (fixes #12742) (#13374) (Arthur Dias)
|
||||
* [`2a21049`](https://github.com/eslint/eslint/commit/2a210499288ed14ec9a6fd72decabfb77504c197) Update: key-spacing loc changes for extra space (refs #12334) (#13362) (Anix)
|
||||
* [`7ce7988`](https://github.com/eslint/eslint/commit/7ce7988f411da64248a64a9d9d2b7884d5ba39e0) Chore: Replace the inquirer dependency with enquirer (#13254) (Selwyn)
|
||||
* [`0f1f5ed`](https://github.com/eslint/eslint/commit/0f1f5ed2a20b8fb575d4360316861cf4c2b9b7bc) Docs: Add security policy link to README (#13403) (Nicholas C. Zakas)
|
||||
* [`9e9ba89`](https://github.com/eslint/eslint/commit/9e9ba897c566601cfe90522099c635ea316b235f) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`ca59fb9`](https://github.com/eslint/eslint/commit/ca59fb95a395c0a02ed23768a70e086480ab1f6d) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
|
||||
v7.2.0 - June 5, 2020
|
||||
|
||||
* [`b735a48`](https://github.com/eslint/eslint/commit/b735a485e77bcc791e4c4c6b8716801d94e98b2c) Update: add enforceForFunctionPrototypeMethods option to no-extra-parens (#12895) (Milos Djermanovic)
|
||||
@ -17,6 +240,7 @@ v7.2.0 - June 5, 2020
|
||||
* [`ee30e5d`](https://github.com/eslint/eslint/commit/ee30e5d8bb1a4c82a2a3fbe1b9ee9f979b55c5c4) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
* [`c29bd9f`](https://github.com/eslint/eslint/commit/c29bd9f75582e5b1a403a8ffd0aafd1ffc8c58e1) Chore: Add breaking/core change link to issue templates (#13344) (Kai Cataldo)
|
||||
* [`d55490f`](https://github.com/eslint/eslint/commit/d55490fa73ff69416de375e4c1cd67b6edba531c) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
|
||||
v7.1.0 - May 22, 2020
|
||||
|
||||
* [`a93083a`](https://github.com/eslint/eslint/commit/a93083af89c6f9714dcdd4a7f27c8655a0b0dba6) Fix: astUtils.getNextLocation returns invalid location after CRLF (#13275) (Milos Djermanovic)
|
||||
@ -46,6 +270,7 @@ v7.1.0 - May 22, 2020
|
||||
* [`f44a6b4`](https://github.com/eslint/eslint/commit/f44a6b4fd92602af8e2c75d5852f796ec064aa8e) Chore: fix invalid syntax in require-await tests (#13277) (Milos Djermanovic)
|
||||
* [`2c778fb`](https://github.com/eslint/eslint/commit/2c778fb6e31b7943bb27a47a6e15dcbfd8336f39) Fix: remove custom plugins from replacedBy metadata (#13274) (Kai Cataldo)
|
||||
* [`0db3b1d`](https://github.com/eslint/eslint/commit/0db3b1d5cc5e4e1de21462679581b7a4d89ff36e) Sponsors: Sync README with website (ESLint Jenkins)
|
||||
|
||||
v7.0.0 - May 8, 2020
|
||||
|
||||
* [`b98d8bd`](https://github.com/eslint/eslint/commit/b98d8bda4630fe8278c5aa2b6650630770568fe5) Upgrade: eslint-release@2.0.0 (#13271) (Kai Cataldo)
|
||||
@ -256,6 +481,7 @@ v7.0.0 - May 8, 2020
|
||||
* [`39f5a45`](https://github.com/eslint/eslint/commit/39f5a453579b2ad732212edeb71f84ecb0991f97) Chore: add test cases for for-direction (#12698) (YeonJuan)
|
||||
* [`b340304`](https://github.com/eslint/eslint/commit/b3403045e535921df6d34785a4ce053e14ba27fd) Chore: Add extra test, improve docs (#12492) (Kevin Partington)
|
||||
* [`827259e`](https://github.com/eslint/eslint/commit/827259ea009f98a0fdf3f7ebf1bfb6cd661ce28d) Build: package.json update for eslint-config-eslint release (ESLint Jenkins)
|
||||
|
||||
v7.0.0-rc.0 - April 24, 2020
|
||||
|
||||
* [`0b1d65a`](https://github.com/eslint/eslint/commit/0b1d65a45aa5dfe08cd596c420490e81b546317e) Update: Improve report location for array-callback-return (refs #12334) (#13109) (Milos Djermanovic)
|
||||
|
@ -550,7 +550,7 @@ target.mocha = () => {
|
||||
errors++;
|
||||
}
|
||||
|
||||
lastReturn = exec(`${getBinFile("nyc")} check-coverage --statement 99 --branch 98 --function 99 --lines 99`);
|
||||
lastReturn = exec(`${getBinFile("nyc")} check-coverage --statement 98 --branch 97 --function 98 --lines 98`);
|
||||
if (lastReturn.code !== 0) {
|
||||
errors++;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
[](https://www.npmjs.com/package/eslint)
|
||||
[](https://www.npmjs.com/package/eslint)
|
||||
[](https://www.npmjs.com/package/eslint)
|
||||
[](https://github.com/eslint/eslint/actions)
|
||||
[](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Feslint%2Feslint?ref=badge_shield)
|
||||
@ -33,11 +33,12 @@ ESLint is a tool for identifying and reporting on patterns found in ECMAScript/J
|
||||
4. [Filing Issues](#filing-issues)
|
||||
5. [Frequently Asked Questions](#faq)
|
||||
6. [Releases](#releases)
|
||||
7. [Semantic Versioning Policy](#semantic-versioning-policy)
|
||||
8. [License](#license)
|
||||
9. [Team](#team)
|
||||
10. [Sponsors](#sponsors)
|
||||
11. [Technology Sponsors](#technology-sponsors)
|
||||
7. [Security Policy](#security-policy)
|
||||
8. [Semantic Versioning Policy](#semantic-versioning-policy)
|
||||
9. [License](#license)
|
||||
10. [Team](#team)
|
||||
11. [Sponsors](#sponsors)
|
||||
12. [Technology Sponsors](#technology-sponsors)
|
||||
|
||||
## <a name="installation-and-usage"></a>Installation and Usage
|
||||
|
||||
@ -121,7 +122,7 @@ Yes, ESLint natively supports parsing JSX syntax (this must be enabled in [confi
|
||||
|
||||
### What ECMAScript versions does ESLint support?
|
||||
|
||||
ESLint has full support for ECMAScript 3, 5 (default), 2015, 2016, 2017, 2018, and 2019. You can set your desired ECMAScript syntax (and other settings, like global variables or your target environments) through [configuration](https://eslint.org/docs/user-guide/configuring).
|
||||
ESLint has full support for ECMAScript 3, 5 (default), 2015, 2016, 2017, 2018, 2019, and 2020. You can set your desired ECMAScript syntax (and other settings, like global variables or your target environments) through [configuration](https://eslint.org/docs/user-guide/configuring).
|
||||
|
||||
### What about experimental features?
|
||||
|
||||
@ -139,32 +140,41 @@ Join our [Mailing List](https://groups.google.com/group/eslint) or [Chatroom](ht
|
||||
|
||||
We have scheduled releases every two weeks on Friday or Saturday. You can follow a [release issue](https://github.com/eslint/eslint/issues?q=is%3Aopen+is%3Aissue+label%3Arelease) for updates about the scheduling of any particular release.
|
||||
|
||||
## <a name="security-policy"></a>Security Policy
|
||||
|
||||
ESLint takes security seriously. We work hard to ensure that ESLint is safe for everyone and that security issues are addressed quickly and responsibly. Read the full [security policy](https://github.com/eslint/.github/blob/master/SECURITY.md).
|
||||
|
||||
## <a name="semantic-versioning-policy"></a>Semantic Versioning Policy
|
||||
|
||||
ESLint follows [semantic versioning](https://semver.org). However, due to the nature of ESLint as a code quality tool, it's not always clear when a minor or major version bump occurs. To help clarify this for everyone, we've defined the following semantic versioning policy for ESLint:
|
||||
|
||||
* Patch release (intended to not break your lint build)
|
||||
* A bug fix in a rule that results in ESLint reporting fewer errors.
|
||||
* A bug fix in a rule that results in ESLint reporting fewer linting errors.
|
||||
* A bug fix to the CLI or core (including formatters).
|
||||
* Improvements to documentation.
|
||||
* Non-user-facing changes such as refactoring code, adding, deleting, or modifying tests, and increasing test coverage.
|
||||
* Re-releasing after a failed release (i.e., publishing a release that doesn't work for anyone).
|
||||
* Minor release (might break your lint build)
|
||||
* A bug fix in a rule that results in ESLint reporting more errors.
|
||||
* A bug fix in a rule that results in ESLint reporting more linting errors.
|
||||
* A new rule is created.
|
||||
* A new option to an existing rule that does not result in ESLint reporting more errors by default.
|
||||
* A new option to an existing rule that does not result in ESLint reporting more linting errors by default.
|
||||
* An existing rule is deprecated.
|
||||
* A new CLI capability is created.
|
||||
* New capabilities to the public API are added (new classes, new methods, new arguments to existing methods, etc.).
|
||||
* A new formatter is created.
|
||||
* `eslint:recommended` is updated and will result in strictly fewer errors (e.g., rule removals).
|
||||
* `eslint:recommended` is updated and will result in strictly fewer linting errors (e.g., rule removals).
|
||||
* Major release (likely to break your lint build)
|
||||
* `eslint:recommended` is updated and may result in new errors (e.g., rule additions, most rule option updates).
|
||||
* A new option to an existing rule that results in ESLint reporting more errors by default.
|
||||
* `eslint:recommended` is updated and may result in new linting errors (e.g., rule additions, most rule option updates).
|
||||
* A new option to an existing rule that results in ESLint reporting more linting errors by default.
|
||||
* An existing formatter is removed.
|
||||
* Part of the public API is removed or changed in an incompatible way.
|
||||
* Part of the public API is removed or changed in an incompatible way. The public API includes:
|
||||
* Rule schemas
|
||||
* Configuration schema
|
||||
* Command-line options
|
||||
* Node.js API
|
||||
* Rule, formatter, parser, plugin APIs
|
||||
|
||||
According to our policy, any minor update may report more errors than the previous release (ex: from a bug fix). As such, we recommend using the tilde (`~`) in `package.json` e.g. `"eslint": "~3.1.0"` to guarantee the results of your builds.
|
||||
According to our policy, any minor update may report more linting errors than the previous release (ex: from a bug fix). As such, we recommend using the tilde (`~`) in `package.json` e.g. `"eslint": "~3.1.0"` to guarantee the results of your builds.
|
||||
|
||||
## <a name="license"></a>License
|
||||
|
||||
@ -201,6 +211,11 @@ Toru Nagashima
|
||||
<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">
|
||||
<img src="https://github.com/mdjermanovic.png?s=75" width="75" height="75"><br />
|
||||
Milos Djermanovic
|
||||
</a>
|
||||
</td></tr></tbody></table>
|
||||
|
||||
|
||||
@ -213,11 +228,6 @@ The people who review and implement new features.
|
||||
<img src="https://github.com/aladdin-add.png?s=75" width="75" height="75"><br />
|
||||
薛定谔的猫
|
||||
</a>
|
||||
</td><td align="center" valign="top" width="11%">
|
||||
<a href="https://github.com/mdjermanovic">
|
||||
<img src="https://github.com/mdjermanovic.png?s=75" width="75" height="75"><br />
|
||||
Milos Djermanovic
|
||||
</a>
|
||||
</td></tr></tbody></table>
|
||||
|
||||
|
||||
@ -233,6 +243,11 @@ The people who review and fix bugs and help triage issues.
|
||||
Pig Fang
|
||||
</a>
|
||||
</td><td align="center" valign="top" width="11%">
|
||||
<a href="https://github.com/anikethsaha">
|
||||
<img src="https://github.com/anikethsaha.png?s=75" width="75" height="75"><br />
|
||||
Anix
|
||||
</a>
|
||||
</td><td align="center" valign="top" width="11%">
|
||||
<a href="https://github.com/yeonjuan">
|
||||
<img src="https://github.com/yeonjuan.png?s=75" width="75" height="75"><br />
|
||||
YeonJuan
|
||||
@ -248,12 +263,15 @@ The following companies, organizations, and individuals support ESLint's ongoing
|
||||
|
||||
<!-- NOTE: This section is autogenerated. Do not manually edit.-->
|
||||
<!--sponsorsstart-->
|
||||
<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></p><h3>Silver 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://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://bruce.agency"><img src="https://images.opencollective.com/brucemade/0c70c59/logo.png" alt="Bruce" height="32"></a> <a href="https://edubirdie.com/"><img src="https://images.opencollective.com/edubirdie2/b1d51ab/logo.png" alt="EduBirdie" height="32"></a> <a href="https://www.casinotop.com/"><img src="https://images.opencollective.com/casinotop-com/10fd95b/logo.png" alt="CasinoTop.com" height="32"></a> <a href="https://www.casinotopp.net/"><img src="https://images.opencollective.com/casino-topp/1dd399a/logo.png" alt="Casino Topp" height="32"></a> <a href="https://writersperhour.com/write-my-essay"><img src="https://images.opencollective.com/writersperhour/5787d4b/logo.png" alt="Writers Per Hour" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://cooltechzone.com/netflix-vpn"><img src="https://images.opencollective.com/vpn-netflix/4850160/logo.png" alt="vpn netflix" height="32"></a> <a href="https://www.kasinot.fi"><img src="https://images.opencollective.com/kasinot-fi/e09aa2e/logo.png" alt="Kasinot.fi" height="32"></a> <a href="https://www.pelisivut.com"><img src="https://images.opencollective.com/pelisivut/04f08f2/logo.png" alt="Pelisivut" height="32"></a> <a href="https://www.nettikasinot.org"><img src="https://images.opencollective.com/nettikasinot-org/bbd887f/logo.png" alt="Nettikasinot.org" height="32"></a> <a href="https://www.bonus.com.de/freispiele"><img src="https://images.opencollective.com/bonusfinder-deutschland/646169e/logo.png" alt="BonusFinder Deutschland" height="32"></a> <a href="https://www.bugsnag.com/platforms?utm_source=Open Collective&utm_medium=Website&utm_content=open-source&utm_campaign=2019-community&utm_term="><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/0b37d14/logo.png" alt="Free Icons by Icons8" 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://tekhattan.com"><img src="https://images.opencollective.com/tekhattan/bc73c28/logo.png" alt="TekHattan" 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="http://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://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>
|
||||
<!--sponsorsend-->
|
||||
|
||||
## <a name="technology-sponsors"></a>Technology Sponsors
|
||||
|
||||
* Site search ([eslint.org](https://eslint.org)) is sponsored by [Algolia](https://www.algolia.com)
|
||||
* Hosting for ([eslint.org](https://eslint.org)) is sponsored by [Netlify](https://www.netlify.com)
|
||||
* Password management is sponsored by [1Password](https://www.1password.com)
|
||||
|
@ -1,3 +1,15 @@
|
||||
/*
|
||||
* STOP!!! DO NOT MODIFY.
|
||||
*
|
||||
* This file is part of the ongoing work to move the eslintrc-style config
|
||||
* system into the @eslint/eslintrc package. This file needs to remain
|
||||
* unchanged in order for this work to proceed.
|
||||
*
|
||||
* If you think you need to change this file, please contact @nzakas first.
|
||||
*
|
||||
* Thanks in advance for your cooperation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Defines a schema for configs.
|
||||
* @author Sylvan Mably
|
||||
|
@ -1,168 +0,0 @@
|
||||
/**
|
||||
* @fileoverview Defines environment settings and globals.
|
||||
* @author Elan Shanker
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const globals = require("globals");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get the object that has difference.
|
||||
* @param {Record<string,boolean>} current The newer object.
|
||||
* @param {Record<string,boolean>} prev The older object.
|
||||
* @returns {Record<string,boolean>} The difference object.
|
||||
*/
|
||||
function getDiff(current, prev) {
|
||||
const retv = {};
|
||||
|
||||
for (const [key, value] of Object.entries(current)) {
|
||||
if (!Object.hasOwnProperty.call(prev, key)) {
|
||||
retv[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return retv;
|
||||
}
|
||||
|
||||
const newGlobals2015 = getDiff(globals.es2015, globals.es5); // 19 variables such as Promise, Map, ...
|
||||
const newGlobals2017 = {
|
||||
Atomics: false,
|
||||
SharedArrayBuffer: false
|
||||
};
|
||||
const newGlobals2020 = {
|
||||
BigInt: false,
|
||||
BigInt64Array: false,
|
||||
BigUint64Array: false,
|
||||
globalThis: false
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {Map<string, import("../lib/shared/types").Environment>} */
|
||||
module.exports = new Map(Object.entries({
|
||||
|
||||
// Language
|
||||
builtin: {
|
||||
globals: globals.es5
|
||||
},
|
||||
es6: {
|
||||
globals: newGlobals2015,
|
||||
parserOptions: {
|
||||
ecmaVersion: 6
|
||||
}
|
||||
},
|
||||
es2015: {
|
||||
globals: newGlobals2015,
|
||||
parserOptions: {
|
||||
ecmaVersion: 6
|
||||
}
|
||||
},
|
||||
es2017: {
|
||||
globals: { ...newGlobals2015, ...newGlobals2017 },
|
||||
parserOptions: {
|
||||
ecmaVersion: 8
|
||||
}
|
||||
},
|
||||
es2020: {
|
||||
globals: { ...newGlobals2015, ...newGlobals2017, ...newGlobals2020 },
|
||||
parserOptions: {
|
||||
ecmaVersion: 11
|
||||
}
|
||||
},
|
||||
|
||||
// Platforms
|
||||
browser: {
|
||||
globals: globals.browser
|
||||
},
|
||||
node: {
|
||||
globals: globals.node,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
globalReturn: true
|
||||
}
|
||||
}
|
||||
},
|
||||
"shared-node-browser": {
|
||||
globals: globals["shared-node-browser"]
|
||||
},
|
||||
worker: {
|
||||
globals: globals.worker
|
||||
},
|
||||
serviceworker: {
|
||||
globals: globals.serviceworker
|
||||
},
|
||||
|
||||
// Frameworks
|
||||
commonjs: {
|
||||
globals: globals.commonjs,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
globalReturn: true
|
||||
}
|
||||
}
|
||||
},
|
||||
amd: {
|
||||
globals: globals.amd
|
||||
},
|
||||
mocha: {
|
||||
globals: globals.mocha
|
||||
},
|
||||
jasmine: {
|
||||
globals: globals.jasmine
|
||||
},
|
||||
jest: {
|
||||
globals: globals.jest
|
||||
},
|
||||
phantomjs: {
|
||||
globals: globals.phantomjs
|
||||
},
|
||||
jquery: {
|
||||
globals: globals.jquery
|
||||
},
|
||||
qunit: {
|
||||
globals: globals.qunit
|
||||
},
|
||||
prototypejs: {
|
||||
globals: globals.prototypejs
|
||||
},
|
||||
shelljs: {
|
||||
globals: globals.shelljs
|
||||
},
|
||||
meteor: {
|
||||
globals: globals.meteor
|
||||
},
|
||||
mongo: {
|
||||
globals: globals.mongo
|
||||
},
|
||||
protractor: {
|
||||
globals: globals.protractor
|
||||
},
|
||||
applescript: {
|
||||
globals: globals.applescript
|
||||
},
|
||||
nashorn: {
|
||||
globals: globals.nashorn
|
||||
},
|
||||
atomtest: {
|
||||
globals: globals.atomtest
|
||||
},
|
||||
embertest: {
|
||||
globals: globals.embertest
|
||||
},
|
||||
webextensions: {
|
||||
globals: globals.webextensions
|
||||
},
|
||||
greasemonkey: {
|
||||
globals: globals.greasemonkey
|
||||
}
|
||||
}));
|
@ -4,11 +4,11 @@ One of the great things about open source projects is that anyone can contribute
|
||||
|
||||
This guide is intended for anyone who wants to contribute to an ESLint project. Please read it carefully as it answers a lot of the questions many newcomers have when first working with our projects.
|
||||
|
||||
## Read the [Code of Conduct](https://js.foundation/community/code-of-conduct)
|
||||
## Read the [Code of Conduct](https://eslint.org/conduct)
|
||||
|
||||
ESLint welcomes contributions from everyone and adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct). We kindly request that you read over our code of conduct before contributing.
|
||||
ESLint welcomes contributions from everyone and adheres to the [OpenJS Foundation Code of Conduct](https://eslint.org/conduct). We kindly request that you read over our code of conduct before contributing.
|
||||
|
||||
## [Signing the CLA](https://js.foundation/CLA)
|
||||
## [Signing the CLA](https://openjsf.org/about/the-openjs-foundation-cla/)
|
||||
|
||||
In order to submit code or documentation to an ESLint project, you will need to electronically sign our [Contributor License Agreement](https://cla.js.foundation/eslint/eslint). The CLA is you giving us permission to use your contribution.
|
||||
|
||||
|
@ -6,7 +6,7 @@ ESLint has a very lightweight development environment that makes updating code f
|
||||
|
||||
Go to <https://nodejs.org/> to download and install the latest stable version for your operating system.
|
||||
|
||||
Most of the installers come with [npm](https://www.npmjs.com/) already installed, but if for some reason it doesn't work on your system, you can install it manually using the instructions on the site.
|
||||
Most of the installers already come with [npm](https://www.npmjs.com/) but if for some reason npm doesn't work on your system, you can install it manually using the instructions on the site.
|
||||
|
||||
## Step 2: Fork and checkout your own ESLint repository
|
||||
|
||||
@ -23,7 +23,7 @@ You must be connected to the Internet for this step to work. You'll see a lot of
|
||||
|
||||
## Step 3: Add the upstream source
|
||||
|
||||
The *upstream source* is the main ESLint repository that active development happens on. While you won't have push access to upstream, you will have pull access, allowing you to pull in the latest code whenever you want.
|
||||
The *upstream source* is the main ESLint repository where active development happens. While you won't have push access to upstream, you will have pull access, allowing you to pull in the latest code whenever you want.
|
||||
|
||||
To add the upstream source for ESLint, run the following in your repository:
|
||||
|
||||
|
@ -8,18 +8,18 @@ While ESLint is designed to be run on the command line, it's possible to use ESL
|
||||
|
||||
* [ESLint]
|
||||
* [constructor()][eslint-constructor]
|
||||
* [lintFiles()][eslint-lintFiles]
|
||||
* [lintText()][eslint-lintText]
|
||||
* [calculateConfigForFile()][eslint-calculateConfigForFile]
|
||||
* [isPathIgnored()][eslint-isPathIgnored]
|
||||
* [loadFormatter()][eslint-loadFormatter]
|
||||
* [lintFiles()][eslint-lintfiles]
|
||||
* [lintText()][eslint-linttext]
|
||||
* [calculateConfigForFile()][eslint-calculateconfigforfile]
|
||||
* [isPathIgnored()][eslint-ispathignored]
|
||||
* [loadFormatter()][eslint-loadformatter]
|
||||
* [static version][eslint-version]
|
||||
* [static outputFixes()][eslint-outputFixes]
|
||||
* [static getErrorResults()][eslint-getErrorResults]
|
||||
* [LintResult type](lintresult)
|
||||
* [LintMessage type](lintmessage)
|
||||
* [EditInfo type](editinfo)
|
||||
* [Formatter type](formatter)
|
||||
* [static outputFixes()][eslint-outputfixes]
|
||||
* [static getErrorResults()][eslint-geterrorresults]
|
||||
* [LintResult type][lintresult]
|
||||
* [LintMessage type][lintmessage]
|
||||
* [EditInfo type][editinfo]
|
||||
* [Formatter type][formatter]
|
||||
* [SourceCode](#sourcecode)
|
||||
* [splitLines()](#sourcecode-splitlines)
|
||||
* [Linter](#linter)
|
||||
@ -926,7 +926,7 @@ The top-level report object has a `results` array containing all linting results
|
||||
* `source` - The source code for the given file. This property is omitted if this file has no errors/warnings or if the `output` property is present.
|
||||
* `output` - The source code for the given file with as many fixes applied as possible, so you can use that to rewrite the files if necessary. This property is omitted if no fix is available.
|
||||
|
||||
The top-level report object also has `errorCount` and `warningCount` which give the exact number of errors and warnings respectively on all the files. Additionally, `usedDeprecatedRules` signals any deprecated rules used and their replacement (if available). Specifically, it is array of objects with properties like so:
|
||||
The top-level report object also has `errorCount` and `warningCount` which give the exact number of errors and warnings respectively on all the files. Additionally, `usedDeprecatedRules` signals any deprecated rules used and their replacement (if available). Specifically, it is an array of objects with properties like so:
|
||||
|
||||
* `ruleId` - The name of the rule (e.g. `indent-legacy`).
|
||||
* `replacedBy` - An array of rules that replace the deprecated rule (e.g. `["indent"]`).
|
||||
@ -1383,14 +1383,14 @@ ruleTester.run("my-rule", myRule, {
|
||||
[thirdparty-formatters]: https://www.npmjs.com/search?q=eslintformatter
|
||||
[eslint]: #eslint-class
|
||||
[eslint-constructor]: #-new-eslintoptions
|
||||
[eslint-lintfiles]: #-eslintlintFilespatterns
|
||||
[eslint-linttext]: #-eslintlintTextcode-options
|
||||
[eslint-calculateconfigforfile]: #-eslintcalculateConfigForFilefilePath
|
||||
[eslint-ispathignored]: #-eslintisPathIgnoredfilePath
|
||||
[eslint-loadformatter]: #-eslintloadFormatternameOrPath
|
||||
[eslint-lintfiles]: #-eslintlintfilespatterns
|
||||
[eslint-linttext]: #-eslintlinttextcode-options
|
||||
[eslint-calculateconfigforfile]: #-eslintcalculateconfigforfilefilepath
|
||||
[eslint-ispathignored]: #-eslintispathignoredfilepath
|
||||
[eslint-loadformatter]: #-eslintloadformatternameorpath
|
||||
[eslint-version]: #-eslintversion
|
||||
[eslint-outputfixes]: #-eslintoutputFixesresults
|
||||
[eslint-geterrorresults]: #-eslintgetErrorResultsresults
|
||||
[eslint-outputfixes]: #-eslintoutputfixesresults
|
||||
[eslint-geterrorresults]: #-eslintgeterrorresultsresults
|
||||
[lintresult]: #-lintresult-type
|
||||
[lintmessage]: #-lintmessage-type
|
||||
[editinfo]: #-editinfo-type
|
||||
|
@ -22,7 +22,7 @@ Once you have a local copy and have Node.JS and npm installed, you'll need to in
|
||||
|
||||
Now when you run `eslint`, it will be running your local copy and showing your changes.
|
||||
|
||||
**Note:** It's a good idea to re-rerun `npm install` whenever you pull from the main repository to ensure you have the latest development dependencies.
|
||||
**Note:** It's a good idea to re-run `npm install` whenever you pull from the main repository to ensure you have the latest development dependencies.
|
||||
|
||||
## Directory structure
|
||||
|
||||
|
@ -11,7 +11,7 @@ module.exports = function(results) {
|
||||
};
|
||||
```
|
||||
|
||||
To run ESLint with this formatter, you can use the `-f` (or `--formatter`) command line flag:
|
||||
To run ESLint with this formatter, you can use the `-f` (or `--format`) command line flag:
|
||||
|
||||
```bash
|
||||
eslint -f ./my-awesome-formatter.js src/
|
||||
@ -50,7 +50,7 @@ The [Using Rule metadata](#using-rule-metadata) example shows how to use the `da
|
||||
|
||||
## Packaging the Custom Formatter
|
||||
|
||||
Custom formatters can also be distributed through npm packages. To do so, create an npm package with a name in the format of `eslint-formatter-*`, where `*` is the name of your formatter (such as `eslint-formatter-awesome`). Projects should then install the package and can use the custom formatter with the `-f` (or `--formatter`) flag like this:
|
||||
Custom formatters can also be distributed through npm packages. To do so, create an npm package with a name in the format of `eslint-formatter-*`, where `*` is the name of your formatter (such as `eslint-formatter-awesome`). Projects should then install the package and can use the custom formatter with the `-f` (or `--format`) flag like this:
|
||||
|
||||
```bash
|
||||
eslint -f awesome src/
|
||||
|
@ -180,7 +180,7 @@ module.exports = {
|
||||
env: ["node"],
|
||||
rules: {
|
||||
"myPlugin/my-rule": "off",
|
||||
"eslint-plugin-myPlugin/another-rule": "off"
|
||||
"eslint-plugin-myPlugin/another-rule": "off",
|
||||
"eslint-plugin-myPlugin/yet-another-rule": "error"
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ The source file for a rule exports an object with the following properties.
|
||||
|
||||
* `fixable` (string) is either `"code"` or `"whitespace"` if the `--fix` option on the [command line](../user-guide/command-line-interface.md#fix) automatically fixes problems reported by the rule
|
||||
|
||||
**Important:** Without the `fixable` property, ESLint does not [apply fixes](#applying-fixes) even if the rule implements `fix` functions. Omit the `fixable` property if the rule is not fixable.
|
||||
**Important:** the `fixable` property is mandatory for fixable rules. If this property isn't specified, ESLint will throw an error whenever the rule attempts to produce a fix. Omit the `fixable` property if the rule is not fixable.
|
||||
|
||||
* `schema` (array) specifies the [options](#options-schemas) so ESLint can prevent invalid [rule configurations](../user-guide/configuring.md#configuring-rules)
|
||||
|
||||
@ -301,7 +301,7 @@ context.report({
|
||||
|
||||
Here, the `fix()` function is used to insert a semicolon after the node. Note that a fix is not immediately applied, and may not be applied at all if there are conflicts with other fixes. After applying fixes, ESLint will run all of the enabled rules again on the fixed code, potentially applying more fixes. This process will repeat up to 10 times, or until no more fixable problems are found. Afterwards, any remaining problems will be reported as usual.
|
||||
|
||||
**Important:** Unless the rule [exports](#rule-basics) the `meta.fixable` property, ESLint does not apply fixes even if the rule implements `fix` functions.
|
||||
**Important:** The `meta.fixable` property is mandatory for fixable rules. ESLint will throw an error if a rule that implements `fix` functions does not [export](#rule-basics) the `meta.fixable` property.
|
||||
|
||||
The `fixer` object has the following methods:
|
||||
|
||||
@ -375,13 +375,15 @@ context.report({
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
Note: Suggestions will be applied as a stand-alone change, without triggering multipass fixes. Each suggestion should focus on a singular change in the code and should not try to conform to user defined styles. For example, if a suggestion is adding a new statement into the codebase, it should not try to match correct indentation, or confirm to user preferences on presence/absence of semicolumns. All of those things can be corrected by multipass autofix when the user triggers it.
|
||||
Note: Suggestions will be applied as a stand-alone change, without triggering multipass fixes. Each suggestion should focus on a singular change in the code and should not try to conform to user defined styles. For example, if a suggestion is adding a new statement into the codebase, it should not try to match correct indentation, or confirm to user preferences on presence/absence of semicolons. All of those things can be corrected by multipass autofix when the user triggers it.
|
||||
|
||||
Best practices for suggestions:
|
||||
|
||||
1. Don't try to do too much and suggest large refactors that could introduce a lot of breaking changes.
|
||||
1. As noted above, don't try to conform to user-defined styles.
|
||||
|
||||
Suggestions are intended to provide fixes. ESLint will automatically remove the whole suggestion from the linting output if the suggestion's `fix` function returned `null` or an empty array/sequence.
|
||||
|
||||
#### Suggestion `messageId`s
|
||||
|
||||
Instead of using a `desc` key for suggestions a `messageId` can be used instead. This works the same way as `messageId`s for the overall error (see [messageIds](#messageIds)). Here is an example of how to use it in a rule:
|
||||
@ -733,5 +735,5 @@ The thing that makes ESLint different from other linters is the ability to defin
|
||||
Runtime rules are written in the same format as all other rules. Create your rule as you would any other and then follow these steps:
|
||||
|
||||
1. Place all of your runtime rules in the same directory (e.g., `eslint_rules`).
|
||||
2. Create a [configuration file](../user-guide/configuring.md) and specify your rule ID error level under the `rules` key. Your rule will not run unless it has a value of `1` or `2` in the configuration file.
|
||||
2. Create a [configuration file](../user-guide/configuring.md) and specify your rule ID error level under the `rules` key. Your rule will not run unless it has a value of `"warn"` or `"error"` in the configuration file.
|
||||
3. Run the [command line interface](../user-guide/command-line-interface.md) using the `--rulesdir` option to specify the location of your runtime rules.
|
||||
|
@ -1,16 +0,0 @@
|
||||
# npm two-factor authentication
|
||||
|
||||
The `eslint` npm account has two-factor authentication (2FA) enabled. The 2FA secret is distributed using a team on [Keybase](https://keybase.io). Anyone doing a release of a package from the Jenkins server needs to have access to the 2FA secret.
|
||||
|
||||
If you're on ESLint's TSC, you should perform the following steps to obtain the 2FA secret:
|
||||
|
||||
1. Download the [Keybase app](https://keybase.io/download) on a smartphone.
|
||||
1. Open the app and create an account.
|
||||
1. From the app, link your Keybase username with your GitHub username. (At the time of writing, the UI for this is to tap the face icon in the bottom-left of the app, then the profile picture in the top-right, then tap "Prove your GitHub" and follow the instructions.)
|
||||
1. Mention your Keybase username in the team chatroom, and wait for someone to add you to the Keybase team.
|
||||
1. Download an authenticator app like [Google Authenticator](https://support.google.com/accounts/answer/1066447) or [Authy](https://authy.com/), if you don't have one installed already.
|
||||
1. In the Keybase app, navigate to the Keybase filesystem (at the time of writing, the UI for this is to tap the hamburger icon in the bottom-right, then tap "Files") and then navigate to `/team/eslint/auth`.
|
||||
* If your authenticator app is downloaded on the same device as your Keybase app (this will usually be the case if you're using the Keybase mobile app), then open `npm_2fa_code.txt` and copy the contents to the clipboard. Open your authenticator app, and paste the contents as a new key (by selecting something like "Enter a provided key" or "Enter key manually").
|
||||
* If your authenticator app is downloaded on a *different* device from your Keybase app (e.g. if you're using a Keybase desktop app), then open `npm_2fa_code.png` and scan it as a QR code from your authenticator app.
|
||||
|
||||
You should now be able to generate 6-digit 2FA codes for the `eslint` npm account using your authenticator app.
|
@ -33,7 +33,7 @@ On the day of a scheduled release, the release team should follow these steps:
|
||||
* Small bugfixes written by a team member.
|
||||
1. Log into Jenkins and schedule a build for the "ESLint Release" job.
|
||||
1. Watch the console output of the build on Jenkins. At some point, the build will pause and a link will be produced with an input field for a six-digit 2FA code.
|
||||
1. Enter the current six-digit 2FA code from your authenticator app. (Also see: [npm-2fa](./npm-2fa))
|
||||
1. Enter the current six-digit 2FA code from your authenticator app.
|
||||
1. Continue the build and wait for it to finish.
|
||||
1. Update the release blog post with a "Highlights" section, including new rules and anything else that's important.
|
||||
1. Make a release announcement in the public chatroom.
|
||||
|
@ -16,6 +16,8 @@ This rule has an object option:
|
||||
* `"ignoreDestructuring": true` does not check destructured identifiers (but still checks any use of those identifiers later in the code)
|
||||
* `"ignoreImports": false` (default) enforces camelcase style for ES2015 imports
|
||||
* `"ignoreImports": true` does not check ES2015 imports (but still checks any use of the imports later in the code except function arguments)
|
||||
* `"ignoreGlobals": false` (default) enforces camelcase style for global variables
|
||||
* `"ignoreGlobals": true` does not enforce camelcase style for global variables
|
||||
* `allow` (`string[]`) list of properties to accept. Accept regex.
|
||||
|
||||
### properties: "always"
|
||||
@ -217,6 +219,28 @@ Examples of **correct** code for this rule with the `{ "ignoreImports": true }`
|
||||
import { snake_cased } from 'mod';
|
||||
```
|
||||
|
||||
### ignoreGlobals: false
|
||||
|
||||
Examples of **incorrect** code for this rule with the default `{ "ignoreGlobals": false }` option:
|
||||
|
||||
```js
|
||||
/*eslint camelcase: ["error", {ignoreGlobals: false}]*/
|
||||
/* global no_camelcased */
|
||||
|
||||
const foo = no_camelcased;
|
||||
```
|
||||
|
||||
### ignoreGlobals: true
|
||||
|
||||
Examples of **correct** code for this rule with the `{ "ignoreGlobals": true }` option:
|
||||
|
||||
```js
|
||||
/*eslint camelcase: ["error", {ignoreGlobals: true}]*/
|
||||
/* global no_camelcased */
|
||||
|
||||
const foo = no_camelcased;
|
||||
```
|
||||
|
||||
## allow
|
||||
|
||||
Examples of **correct** code for this rule with the `allow` option:
|
||||
|
@ -113,7 +113,7 @@ If your project will not be following a consistent comma-spacing pattern, turn t
|
||||
|
||||
## Further Reading
|
||||
|
||||
* [Javascript](http://javascript.crockford.com/code.html)
|
||||
* [JavaScript](http://javascript.crockford.com/code.html)
|
||||
* [Dojo Style Guide](https://dojotoolkit.org/reference-guide/1.9/developer/styleguide.html)
|
||||
|
||||
|
||||
|
@ -1,82 +1,3 @@
|
||||
# disallow specified identifiers (id-blacklist)
|
||||
|
||||
> "There are only two hard things in Computer Science: cache invalidation and naming things." — Phil Karlton
|
||||
|
||||
Bad names can lead to hard-to-decipher code. Generic names, such as `data`, don't infer much about the code and the values it receives. This rule allows you to configure a blacklist of bad identifier names, that you don't want to see in your code.
|
||||
|
||||
## Rule Details
|
||||
|
||||
This rule disallows specified identifiers in assignments and `function` definitions.
|
||||
|
||||
This rule will catch blacklisted identifiers that are:
|
||||
|
||||
- variable declarations
|
||||
- function declarations
|
||||
- object properties assigned to during object creation
|
||||
|
||||
It will not catch blacklisted identifiers that are:
|
||||
|
||||
- function calls (so you can still use functions you do not have control over)
|
||||
- object properties (so you can still use objects you do not have control over)
|
||||
|
||||
## Options
|
||||
|
||||
The rule takes one or more strings as options: the names of restricted identifiers.
|
||||
|
||||
For example, to restrict the use of common generic identifiers:
|
||||
|
||||
```json
|
||||
{
|
||||
"id-blacklist": ["error", "data", "err", "e", "cb", "callback"]
|
||||
}
|
||||
```
|
||||
|
||||
Examples of **incorrect** code for this rule with sample `"data", "callback"` restricted identifiers:
|
||||
|
||||
```js
|
||||
/*eslint id-blacklist: ["error", "data", "callback"] */
|
||||
|
||||
var data = {...};
|
||||
|
||||
function callback() {
|
||||
// ...
|
||||
}
|
||||
|
||||
element.callback = function() {
|
||||
// ...
|
||||
};
|
||||
|
||||
var itemSet = {
|
||||
data: [...]
|
||||
};
|
||||
```
|
||||
|
||||
Examples of **correct** code for this rule with sample `"data", "callback"` restricted identifiers:
|
||||
|
||||
```js
|
||||
/*eslint id-blacklist: ["error", "data", "callback"] */
|
||||
|
||||
var encodingOptions = {...};
|
||||
|
||||
function processFileResult() {
|
||||
// ...
|
||||
}
|
||||
|
||||
element.successHandler = function() {
|
||||
// ...
|
||||
};
|
||||
|
||||
var itemSet = {
|
||||
entities: [...]
|
||||
};
|
||||
|
||||
callback(); // all function calls are ignored
|
||||
|
||||
foo.callback(); // all function calls are ignored
|
||||
|
||||
foo.data; // all property names that are not assignments are ignored
|
||||
```
|
||||
|
||||
## When Not To Use It
|
||||
|
||||
You can turn this rule off if you are happy for identifiers to be named freely.
|
||||
This rule was **deprecated** in ESLint v7.5.0 and replaced by the [id-denylist](id-denylist.md) rule.
|
||||
|
82
eslint/docs/rules/id-denylist.md
Normal file
82
eslint/docs/rules/id-denylist.md
Normal file
@ -0,0 +1,82 @@
|
||||
# disallow specified identifiers (id-denylist)
|
||||
|
||||
> "There are only two hard things in Computer Science: cache invalidation and naming things." — Phil Karlton
|
||||
|
||||
Generic names can lead to hard-to-decipher code. This rule allows you to specify a deny list of disallowed identifier names to avoid this practice.
|
||||
|
||||
## Rule Details
|
||||
|
||||
This rule disallows specified identifiers in assignments and `function` definitions.
|
||||
|
||||
This rule will catch disallowed identifiers that are:
|
||||
|
||||
- variable declarations
|
||||
- function declarations
|
||||
- object properties assigned to during object creation
|
||||
|
||||
It will not catch disallowed identifiers that are:
|
||||
|
||||
- function calls (so you can still use functions you do not have control over)
|
||||
- object properties (so you can still use objects you do not have control over)
|
||||
|
||||
## Options
|
||||
|
||||
The rule takes one or more strings as options: the names of restricted identifiers.
|
||||
|
||||
For example, to restrict the use of common generic identifiers:
|
||||
|
||||
```json
|
||||
{
|
||||
"id-denylist": ["error", "data", "err", "e", "cb", "callback"]
|
||||
}
|
||||
```
|
||||
|
||||
Examples of **incorrect** code for this rule with sample `"data", "callback"` restricted identifiers:
|
||||
|
||||
```js
|
||||
/*eslint id-denylist: ["error", "data", "callback"] */
|
||||
|
||||
var data = {...};
|
||||
|
||||
function callback() {
|
||||
// ...
|
||||
}
|
||||
|
||||
element.callback = function() {
|
||||
// ...
|
||||
};
|
||||
|
||||
var itemSet = {
|
||||
data: [...]
|
||||
};
|
||||
```
|
||||
|
||||
Examples of **correct** code for this rule with sample `"data", "callback"` restricted identifiers:
|
||||
|
||||
```js
|
||||
/*eslint id-denylist: ["error", "data", "callback"] */
|
||||
|
||||
var encodingOptions = {...};
|
||||
|
||||
function processFileResult() {
|
||||
// ...
|
||||
}
|
||||
|
||||
element.successHandler = function() {
|
||||
// ...
|
||||
};
|
||||
|
||||
var itemSet = {
|
||||
entities: [...]
|
||||
};
|
||||
|
||||
callback(); // all function calls are ignored
|
||||
|
||||
foo.callback(); // all function calls are ignored
|
||||
|
||||
foo.data; // all property names that are not assignments are ignored
|
||||
```
|
||||
|
||||
## When Not To Use It
|
||||
|
||||
You can turn this rule off if you do not want to restrict the use of certain identifiers.
|
@ -82,6 +82,7 @@ This rule has an object option:
|
||||
* `"properties": always` (default) enforces identifier length convention for property names
|
||||
* `"properties": never` ignores identifier length convention for property names
|
||||
* `"exceptions"` allows an array of specified identifier names
|
||||
* `"exceptionPatterns"` array of strings representing regular expression patterns, allows identifiers that match any of the patterns.
|
||||
|
||||
### min
|
||||
|
||||
@ -217,6 +218,29 @@ const { x } = foo;
|
||||
const { a: x } = foo;
|
||||
```
|
||||
|
||||
### exceptionPatterns
|
||||
|
||||
Examples of additional **correct** code for this rule with the `{ "exceptionPatterns": ["E|S", "[x-z]"] }` option:
|
||||
|
||||
```js
|
||||
/*eslint id-length: ["error", { "exceptionPatterns": ["E|S", "[x-z]"] }]*/
|
||||
/*eslint-env es6*/
|
||||
|
||||
var E = 5;
|
||||
function S() { return 42; }
|
||||
obj.x = document.body;
|
||||
var foo = function (x) { /* do stuff */ };
|
||||
try {
|
||||
dangerousStuff();
|
||||
} catch (x) {
|
||||
// ignore as many do
|
||||
}
|
||||
(y) => {return y * y};
|
||||
var [E] = arr;
|
||||
const { y } = foo;
|
||||
const { a: z } = foo;
|
||||
```
|
||||
|
||||
## Related Rules
|
||||
|
||||
* [max-len](max-len.md)
|
||||
|
@ -437,7 +437,7 @@ const [
|
||||
|
||||
### ignorePattern
|
||||
|
||||
By default this rule ignores comments starting with the following words: `eslint`, `jshint`, `jslint`, `istanbul`, `global`, `exported`, `jscs`. An alternative regular expression can be provided.
|
||||
By default this rule ignores comments starting with the following words: `eslint`, `jshint`, `jslint`, `istanbul`, `global`, `exported`, `jscs`. To ignore more comments in addition to the defaults, set the `ignorePattern` option to a string pattern that will be passed to the [`RegExp` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/RegExp).
|
||||
|
||||
Examples of **correct** code for the `ignorePattern` option:
|
||||
|
||||
|
@ -84,7 +84,7 @@ class Foo{
|
||||
Examples of **correct** code for this rule with the object option:
|
||||
|
||||
```js
|
||||
/* eslint lines-between-class-members: ["error", "always", { exceptAfterSingleLine: true }]*/
|
||||
/* eslint lines-between-class-members: ["error", "always", { "exceptAfterSingleLine": true }]*/
|
||||
class Foo{
|
||||
bar(){} // single line class member
|
||||
baz(){
|
||||
|
@ -6,6 +6,7 @@ Some people consider large files a code smell. Large files tend to do a lot of t
|
||||
|
||||
This rule enforces a maximum number of lines per file, in order to aid in maintainability and reduce complexity.
|
||||
|
||||
Please note that most editors show an additional empty line at the end if the file ends with a line break. This rule does not count that extra line.
|
||||
|
||||
## Options
|
||||
|
||||
|
@ -27,7 +27,7 @@ This rule has a string option:
|
||||
|
||||
* `"always"` (default) enforces newlines between the operands of a ternary expression.
|
||||
* `"always-multiline"` enforces newlines between the operands of a ternary expression if the expression spans multiple lines.
|
||||
* `"never"` disallows newlines between the operands of a ternary expression (enforcing that the entire ternary expression is on one line).
|
||||
* `"never"` disallows newlines between the operands of a ternary expression.
|
||||
|
||||
### always
|
||||
|
||||
@ -134,6 +134,10 @@ Examples of **correct** code for this rule with the `"never"` option:
|
||||
foo > bar ? value1 : value2;
|
||||
|
||||
foo > bar ? (baz > qux ? value1 : value2) : value3;
|
||||
|
||||
foo > bar ? (
|
||||
baz > qux ? value1 : value2
|
||||
) : value3;
|
||||
```
|
||||
|
||||
## When Not To Use It
|
||||
|
@ -42,6 +42,8 @@ This rule disallows the use of `await` within loop bodies.
|
||||
Examples of **correct** code for this rule:
|
||||
|
||||
```js
|
||||
/*eslint no-await-in-loop: "error"*/
|
||||
|
||||
async function foo(things) {
|
||||
const results = [];
|
||||
for (const thing of things) {
|
||||
@ -56,6 +58,8 @@ async function foo(things) {
|
||||
Examples of **incorrect** code for this rule:
|
||||
|
||||
```js
|
||||
/*eslint no-await-in-loop: "error"*/
|
||||
|
||||
async function foo(things) {
|
||||
const results = [];
|
||||
for (const thing of things) {
|
||||
|
@ -22,6 +22,10 @@ foo = bar;
|
||||
function foo() {
|
||||
foo = bar;
|
||||
}
|
||||
|
||||
var a = function hello() {
|
||||
hello = 123;
|
||||
};
|
||||
```
|
||||
|
||||
Examples of **incorrect** code for this rule, unlike the corresponding rule in JSHint:
|
||||
|
@ -87,3 +87,25 @@ var quux = (
|
||||
</div>
|
||||
)
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
### ignorePattern
|
||||
|
||||
To make this rule ignore specific comments, set the `ignorePattern` option to a string pattern that will be passed to the [`RegExp` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/RegExp).
|
||||
|
||||
Examples of **correct** code for the `ignorePattern` option:
|
||||
|
||||
```js
|
||||
/*eslint no-inline-comments: ["error", { "ignorePattern": "webpackChunkName:\\s.+" }]*/
|
||||
|
||||
import(/* webpackChunkName: "my-chunk-name" */ './locale/en');
|
||||
```
|
||||
|
||||
Examples of **incorrect** code for the `ignorePattern` option:
|
||||
|
||||
```js
|
||||
/*eslint no-inline-comments: ["error", { "ignorePattern": "something" }] */
|
||||
|
||||
var foo = 4; // other thing
|
||||
```
|
||||
|
@ -16,6 +16,7 @@ const x = 5123000000000000000000000000001
|
||||
const x = 1230000000000000000000000.0
|
||||
const x = .1230000000000000000000000
|
||||
const x = 0X20000000000001
|
||||
const x = 0X2_000000000_0001;
|
||||
```
|
||||
|
||||
Examples of **correct** code for this rule:
|
||||
@ -29,4 +30,5 @@ const x = 123e34
|
||||
const x = 12300000000000000000000000
|
||||
const x = 0x1FFFFFFFFFFFFF
|
||||
const x = 9007199254740991
|
||||
const x = 9007_1992547409_91
|
||||
```
|
||||
|
@ -128,6 +128,27 @@ a = data[4294967295]; // above the max array index
|
||||
a = data[1e500]; // same as data["Infinity"]
|
||||
```
|
||||
|
||||
### ignoreDefaultValues
|
||||
|
||||
A boolean to specify if numbers used in default value assignments are considered okay. `false` by default.
|
||||
|
||||
Examples of **correct** code for the `{ "ignoreDefaultValues": true }` option:
|
||||
|
||||
```js
|
||||
/*eslint no-magic-numbers: ["error", { "ignoreDefaultValues": true }]*/
|
||||
|
||||
const { tax = 0.25 } = accountancy;
|
||||
|
||||
function mapParallel(concurrency = 3) { /***/ }
|
||||
```
|
||||
|
||||
```js
|
||||
/*eslint no-magic-numbers: ["error", { "ignoreDefaultValues": true }]*/
|
||||
|
||||
let head;
|
||||
[head = 100] = []
|
||||
```
|
||||
|
||||
### enforceConst
|
||||
|
||||
A boolean to specify if we should check for the const keyword in variable declaration of numbers. `false` by default.
|
||||
|
@ -10,9 +10,9 @@ This rule aims to reduce the scrolling required when reading through your code.
|
||||
|
||||
This rule has an object option:
|
||||
|
||||
* `"max"` (default: `2`) enforces a maximum number of consecutive empty lines.
|
||||
* `"maxEOF"` enforces a maximum number of consecutive empty lines at the end of files.
|
||||
* `"maxBOF"` enforces a maximum number of consecutive empty lines at the beginning of files.
|
||||
- `"max"` (default: `2`) enforces a maximum number of consecutive empty lines.
|
||||
- `"maxEOF"` enforces a maximum number of consecutive empty lines at the end of files.
|
||||
- `"maxBOF"` enforces a maximum number of consecutive empty lines at the beginning of files.
|
||||
|
||||
### max
|
||||
|
||||
@ -41,10 +41,10 @@ var bar = 3;
|
||||
|
||||
### maxEOF
|
||||
|
||||
Examples of **incorrect** code for this rule with the `{ max: 2, maxEOF: 1 }` options:
|
||||
Examples of **incorrect** code for this rule with the `{ max: 2, maxEOF: 0 }` options:
|
||||
|
||||
```js
|
||||
/*eslint no-multiple-empty-lines: ["error", { "max": 2, "maxEOF": 1 }]*/
|
||||
/*eslint no-multiple-empty-lines: ["error", { "max": 2, "maxEOF": 0 }]*/
|
||||
|
||||
var foo = 5;
|
||||
|
||||
@ -54,16 +54,42 @@ var bar = 3;
|
||||
|
||||
```
|
||||
|
||||
Examples of **correct** code for this rule with the `{ max: 2, maxEOF: 1 }` options:
|
||||
Examples of **correct** code for this rule with the `{ max: 2, maxEOF: 0 }` options:
|
||||
|
||||
```js
|
||||
/*eslint no-multiple-empty-lines: ["error", { "max": 2, "maxEOF": 1 }]*/
|
||||
/*eslint no-multiple-empty-lines: ["error", { "max": 2, "maxEOF": 0 }]*/
|
||||
|
||||
var foo = 5;
|
||||
|
||||
|
||||
var bar = 3;
|
||||
```
|
||||
|
||||
**Note**: Although this ensures zero empty lines at the EOF, most editors will still show one empty line at the end if the file ends with a line break, as illustrated below. There is no empty line at the end of a file after the last `\n`, although editors may show an additional line. A true additional line would be represented by `\n\n`.
|
||||
|
||||
**Incorrect**:
|
||||
|
||||
```
|
||||
1 /*eslint no-multiple-empty-lines: ["error", { "max": 2, "maxEOF": 0 }]*/⏎
|
||||
2 ⏎
|
||||
3 var foo = 5;⏎
|
||||
4 ⏎
|
||||
5 ⏎
|
||||
6 var bar = 3;⏎
|
||||
7 ⏎
|
||||
8
|
||||
```
|
||||
|
||||
**Correct**:
|
||||
|
||||
```
|
||||
1 /*eslint no-multiple-empty-lines: ["error", { "max": 2, "maxEOF": 0 }]*/⏎
|
||||
2 ⏎
|
||||
3 var foo = 5;⏎
|
||||
4 ⏎
|
||||
5 ⏎
|
||||
6 var bar = 3;⏎
|
||||
7
|
||||
```
|
||||
|
||||
### maxBOF
|
||||
|
96
eslint/docs/rules/no-promise-executor-return.md
Normal file
96
eslint/docs/rules/no-promise-executor-return.md
Normal file
@ -0,0 +1,96 @@
|
||||
# Disallow returning values from Promise executor functions (no-promise-executor-return)
|
||||
|
||||
The `new Promise` constructor accepts a single argument, called an *executor*.
|
||||
|
||||
```js
|
||||
const myPromise = new Promise(function executor(resolve, reject) {
|
||||
readFile('foo.txt', function(err, result) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
The executor function usually initiates some asynchronous operation. Once it is finished, the executor should call `resolve` with the result, or `reject` if an error occurred.
|
||||
|
||||
The return value of the executor is ignored. Returning a value from an executor function is a possible error because the returned value cannot be used and it doesn't affect the promise in any way.
|
||||
|
||||
## Rule Details
|
||||
|
||||
This rule disallows returning values from Promise executor functions.
|
||||
|
||||
Only `return` without a value is allowed, as it's a control flow statement.
|
||||
|
||||
Examples of **incorrect** code for this rule:
|
||||
|
||||
```js
|
||||
/*eslint no-promise-executor-return: "error"*/
|
||||
|
||||
new Promise((resolve, reject) => {
|
||||
if (someCondition) {
|
||||
return defaultResult;
|
||||
}
|
||||
getSomething((err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
new Promise((resolve, reject) => getSomething((err, data) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(data);
|
||||
}
|
||||
}));
|
||||
|
||||
new Promise(() => {
|
||||
return 1;
|
||||
});
|
||||
```
|
||||
|
||||
Examples of **correct** code for this rule:
|
||||
|
||||
```js
|
||||
/*eslint no-promise-executor-return: "error"*/
|
||||
|
||||
new Promise((resolve, reject) => {
|
||||
if (someCondition) {
|
||||
resolve(defaultResult);
|
||||
return;
|
||||
}
|
||||
getSomething((err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
new Promise((resolve, reject) => {
|
||||
getSomething((err, data) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Promise.resolve(1);
|
||||
```
|
||||
|
||||
## Further Reading
|
||||
|
||||
* [MDN Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
|
||||
|
||||
## Related Rules
|
||||
|
||||
* [no-async-promise-executor](no-async-promise-executor.md)
|
@ -11,6 +11,8 @@ This rule aims to prevent a likely common performance hazard due to a lack of un
|
||||
Examples of **incorrect** code for this rule:
|
||||
|
||||
```js
|
||||
/*eslint no-return-await: "error"*/
|
||||
|
||||
async function foo() {
|
||||
return await bar();
|
||||
}
|
||||
@ -19,6 +21,8 @@ async function foo() {
|
||||
Examples of **correct** code for this rule:
|
||||
|
||||
```js
|
||||
/*eslint no-return-await: "error"*/
|
||||
|
||||
async function foo() {
|
||||
return bar();
|
||||
}
|
||||
@ -28,11 +32,13 @@ async function foo() {
|
||||
return;
|
||||
}
|
||||
|
||||
// This is essentially the same as `return await bar();`, but the rule checks only `await` in `return` statements
|
||||
async function foo() {
|
||||
const x = await bar();
|
||||
return x;
|
||||
}
|
||||
|
||||
// In this example the `await` is necessary to be able to catch errors thrown from `bar()`
|
||||
async function foo() {
|
||||
try {
|
||||
return await bar();
|
||||
@ -40,8 +46,6 @@ async function foo() {
|
||||
}
|
||||
```
|
||||
|
||||
In the last example the `await` is necessary to be able to catch errors thrown from `bar()`.
|
||||
|
||||
## When Not To Use It
|
||||
|
||||
There are a few reasons you might want to turn this rule off:
|
||||
|
@ -10,6 +10,8 @@ Examples of **incorrect** code for this rule:
|
||||
/*eslint no-script-url: "error"*/
|
||||
|
||||
location.href = "javascript:void(0)";
|
||||
|
||||
location.href = `javascript:void(0)`;
|
||||
```
|
||||
|
||||
## Compatibility
|
||||
|
@ -33,17 +33,21 @@ var _ = require('underscore');
|
||||
var obj = _.contains(items, item);
|
||||
obj.__proto__ = {};
|
||||
var file = __filename;
|
||||
function foo(_bar) {};
|
||||
const foo = { onClick(_bar) {} };
|
||||
const foo = (_bar) => {};
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
This rule has an object option:
|
||||
|
||||
* `"allow"` allows specified identifiers to have dangling underscores
|
||||
* `"allowAfterThis": false` (default) disallows dangling underscores in members of the `this` object
|
||||
* `"allowAfterSuper": false` (default) disallows dangling underscores in members of the `super` object
|
||||
* `"allowAfterThisConstructor": false` (default) disallows dangling underscores in members of the `this.constructor` object
|
||||
* `"enforceInMethodNames": false` (default) allows dangling underscores in method names
|
||||
- `"allow"` allows specified identifiers to have dangling underscores
|
||||
- `"allowAfterThis": false` (default) disallows dangling underscores in members of the `this` object
|
||||
- `"allowAfterSuper": false` (default) disallows dangling underscores in members of the `super` object
|
||||
- `"allowAfterThisConstructor": false` (default) disallows dangling underscores in members of the `this.constructor` object
|
||||
- `"enforceInMethodNames": false` (default) allows dangling underscores in method names
|
||||
- `"allowFunctionParams": true` (default) allows dangling underscores in function parameter names
|
||||
|
||||
### allow
|
||||
|
||||
@ -113,6 +117,26 @@ const o = {
|
||||
};
|
||||
```
|
||||
|
||||
### allowFunctionParams
|
||||
|
||||
Examples of **incorrect** code for this rule with the `{ "allowFunctionParams": false }` option:
|
||||
|
||||
```js
|
||||
/*eslint no-underscore-dangle: ["error", { "allowFunctionParams": false }]*/
|
||||
|
||||
function foo (_bar) {}
|
||||
function foo (_bar = 0) {}
|
||||
function foo (..._bar) {}
|
||||
|
||||
const foo = function onClick (_bar) {}
|
||||
const foo = function onClick (_bar = 0) {}
|
||||
const foo = function onClick (..._bar) {}
|
||||
|
||||
const foo = (_bar) => {};
|
||||
const foo = (_bar = 0) => {};
|
||||
const foo = (..._bar) => {};
|
||||
```
|
||||
|
||||
## When Not To Use It
|
||||
|
||||
If you want to allow dangling underscores in identifiers, then you can safely turn this rule off.
|
||||
|
@ -29,6 +29,10 @@ If a reference is inside of a dynamic expression (e.g. `CallExpression`,
|
||||
Examples of **incorrect** code for this rule:
|
||||
|
||||
```js
|
||||
/*eslint no-unmodified-loop-condition: "error"*/
|
||||
|
||||
var node = something;
|
||||
|
||||
while (node) {
|
||||
doSomething(node);
|
||||
}
|
||||
@ -46,6 +50,8 @@ while (node !== root) {
|
||||
Examples of **correct** code for this rule:
|
||||
|
||||
```js
|
||||
/*eslint no-unmodified-loop-condition: "error"*/
|
||||
|
||||
while (node) {
|
||||
doSomething(node);
|
||||
node = node.parent;
|
||||
|
190
eslint/docs/rules/no-unreachable-loop.md
Normal file
190
eslint/docs/rules/no-unreachable-loop.md
Normal file
@ -0,0 +1,190 @@
|
||||
# Disallow loops with a body that allows only one iteration (no-unreachable-loop)
|
||||
|
||||
A loop that can never reach the second iteration is a possible error in the code.
|
||||
|
||||
```js
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i].name === myName) {
|
||||
doSomething(arr[i]);
|
||||
// break was supposed to be here
|
||||
}
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
In rare cases where only one iteration (or at most one iteration) is intended behavior, the code should be refactored to use `if` conditionals instead of `while`, `do-while` and `for` loops. It's considered a best practice to avoid using loop constructs for such cases.
|
||||
|
||||
## Rule Details
|
||||
|
||||
This rule aims to detect and disallow loops that can have at most one iteration, by performing static code path analysis on loop bodies.
|
||||
|
||||
In particular, this rule will disallow a loop with a body that exits the loop in all code paths. If all code paths in the loop's body will end with either a `break`, `return` or a `throw` statement, the second iteration of such loop is certainly unreachable, regardless of the loop's condition.
|
||||
|
||||
This rule checks `while`, `do-while`, `for`, `for-in` and `for-of` loops. You can optionally disable checks for each of these constructs.
|
||||
|
||||
Examples of **incorrect** code for this rule:
|
||||
|
||||
```js
|
||||
/*eslint no-unreachable-loop: "error"*/
|
||||
|
||||
while (foo) {
|
||||
doSomething(foo);
|
||||
foo = foo.parent;
|
||||
break;
|
||||
}
|
||||
|
||||
function verifyList(head) {
|
||||
let item = head;
|
||||
do {
|
||||
if (verify(item)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} while (item);
|
||||
}
|
||||
|
||||
function findSomething(arr) {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (isSomething(arr[i])) {
|
||||
return arr[i];
|
||||
} else {
|
||||
throw new Error("Doesn't exist.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (key in obj) {
|
||||
if (key.startsWith("_")) {
|
||||
break;
|
||||
}
|
||||
firstKey = key;
|
||||
firstValue = obj[key];
|
||||
break;
|
||||
}
|
||||
|
||||
for (foo of bar) {
|
||||
if (foo.id === id) {
|
||||
doSomething(foo);
|
||||
}
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
Examples of **correct** code for this rule:
|
||||
|
||||
```js
|
||||
/*eslint no-unreachable-loop: "error"*/
|
||||
|
||||
while (foo) {
|
||||
doSomething(foo);
|
||||
foo = foo.parent;
|
||||
}
|
||||
|
||||
function verifyList(head) {
|
||||
let item = head;
|
||||
do {
|
||||
if (verify(item)) {
|
||||
item = item.next;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} while (item);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function findSomething(arr) {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (isSomething(arr[i])) {
|
||||
return arr[i];
|
||||
}
|
||||
}
|
||||
throw new Error("Doesn't exist.");
|
||||
}
|
||||
|
||||
for (key in obj) {
|
||||
if (key.startsWith("_")) {
|
||||
continue;
|
||||
}
|
||||
firstKey = key;
|
||||
firstValue = obj[key];
|
||||
break;
|
||||
}
|
||||
|
||||
for (foo of bar) {
|
||||
if (foo.id === id) {
|
||||
doSomething(foo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Please note that this rule is not designed to check loop conditions, and will not warn in cases such as the following examples.
|
||||
|
||||
Examples of additional **correct** code for this rule:
|
||||
|
||||
```js
|
||||
/*eslint no-unreachable-loop: "error"*/
|
||||
|
||||
do {
|
||||
doSomething();
|
||||
} while (false)
|
||||
|
||||
for (let i = 0; i < 1; i++) {
|
||||
doSomething(i);
|
||||
}
|
||||
|
||||
for (const a of [1]) {
|
||||
doSomething(a);
|
||||
}
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
This rule has an object option, with one option:
|
||||
|
||||
* `"ignore"` - an optional array of loop types that will be ignored by this rule.
|
||||
|
||||
## ignore
|
||||
|
||||
You can specify up to 5 different elements in the `"ignore"` array:
|
||||
|
||||
* `"WhileStatement"` - to ignore all `while` loops.
|
||||
* `"DoWhileStatement"` - to ignore all `do-while` loops.
|
||||
* `"ForStatement"` - to ignore all `for` loops (does not apply to `for-in` and `for-of` loops).
|
||||
* `"ForInStatement"` - to ignore all `for-in` loops.
|
||||
* `"ForOfStatement"` - to ignore all `for-of` loops.
|
||||
|
||||
Examples of **correct** code for this rule with the `"ignore"` option:
|
||||
|
||||
```js
|
||||
/*eslint no-unreachable-loop: ["error", { "ignore": ["ForInStatement", "ForOfStatement"] }]*/
|
||||
|
||||
for (var key in obj) {
|
||||
hasEnumerableProperties = true;
|
||||
break;
|
||||
}
|
||||
|
||||
for (const a of b) break;
|
||||
```
|
||||
|
||||
## Known Limitations
|
||||
|
||||
Static code path analysis, in general, does not evaluate conditions. Due to this fact, this rule might miss reporting cases such as the following:
|
||||
|
||||
```js
|
||||
for (let i = 0; i < 10; i++) {
|
||||
doSomething(i);
|
||||
if (true) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Related Rules
|
||||
|
||||
* [no-unreachable](no-unreachable.md)
|
||||
* [no-constant-condition](no-constant-condition.md)
|
||||
* [no-unmodified-loop-condition](no-unmodified-loop-condition.md)
|
||||
* [for-direction](for-direction.md)
|
@ -335,19 +335,32 @@ let d = {
|
||||
foo: 1, bar: 2};
|
||||
let e = {foo: function() {
|
||||
dosomething();
|
||||
}};
|
||||
}
|
||||
};
|
||||
let f = {
|
||||
foo: function() {
|
||||
dosomething();}};
|
||||
|
||||
let {f
|
||||
let {g
|
||||
} = obj;
|
||||
let {
|
||||
g} = obj;
|
||||
let {h, i
|
||||
h} = obj;
|
||||
let {i, j
|
||||
} = obj;
|
||||
let {k, l
|
||||
} = obj;
|
||||
let {
|
||||
j, k} = obj;
|
||||
let {l = function() {
|
||||
m, n} = obj;
|
||||
let {
|
||||
o, p} = obj;
|
||||
let {q = function() {
|
||||
dosomething();
|
||||
}} = obj;
|
||||
}
|
||||
} = obj;
|
||||
let {
|
||||
r = function() {
|
||||
dosomething();
|
||||
}} = obj;
|
||||
```
|
||||
|
||||
Examples of **correct** code for this rule with the default `{ "consistent": true }` option:
|
||||
@ -356,27 +369,35 @@ Examples of **correct** code for this rule with the default `{ "consistent": tru
|
||||
/*eslint object-curly-newline: ["error", { "consistent": true }]*/
|
||||
/*eslint-env es6*/
|
||||
|
||||
let a = {};
|
||||
let b = {foo: 1};
|
||||
let c = {
|
||||
|
||||
let empty1 = {};
|
||||
let empty2 = {
|
||||
};
|
||||
let a = {foo: 1};
|
||||
let b = {
|
||||
foo: 1
|
||||
};
|
||||
let d = {
|
||||
let c = {
|
||||
foo: 1, bar: 2
|
||||
};
|
||||
let e = {
|
||||
let d = {
|
||||
foo: 1,
|
||||
bar: 2
|
||||
};
|
||||
let f = {foo: function() {dosomething();}};
|
||||
let g = {
|
||||
let e = {foo: function() {dosomething();}};
|
||||
let f = {
|
||||
foo: function() {
|
||||
dosomething();
|
||||
}
|
||||
};
|
||||
|
||||
let {} = obj;
|
||||
let {h} = obj;
|
||||
let {
|
||||
} = obj;
|
||||
let {g} = obj;
|
||||
let {
|
||||
h
|
||||
} = obj;
|
||||
let {i, j} = obj;
|
||||
let {
|
||||
k, l
|
||||
|
@ -10,6 +10,7 @@ JavaScript provides shorthand operators that combine variable assignment and som
|
||||
x *= y | x = x * y
|
||||
x /= y | x = x / y
|
||||
x %= y | x = x % y
|
||||
x **= y | x = x ** y
|
||||
x <<= y | x = x << y
|
||||
x >>= y | x = x >> y
|
||||
x >>>= y | x = x >>> y
|
||||
@ -22,6 +23,8 @@ JavaScript provides shorthand operators that combine variable assignment and som
|
||||
|
||||
This rule requires or disallows assignment operator shorthand where possible.
|
||||
|
||||
The rule applies to the operators listed in the above table. It does not report the logical assignment operators `&&=`, `||=`, and `??=` because their short-circuiting behavior is different from the other assignment operators.
|
||||
|
||||
## Options
|
||||
|
||||
This rule has a single string option:
|
||||
|
@ -22,7 +22,7 @@ This rule enforces a consistent linebreak style for operators.
|
||||
|
||||
## Options
|
||||
|
||||
This rule has one option, which can be a string option or an object option.
|
||||
This rule has two options, a string option and an object option.
|
||||
|
||||
String option:
|
||||
|
||||
|
@ -48,14 +48,14 @@ You can supply any number of configurations. If a statement pair matches multipl
|
||||
- `"block"` is lonely blocks.
|
||||
- `"block-like"` is block like statements. This matches statements that the last token is the closing brace of blocks; e.g. `{ }`, `if (a) { }`, and `while (a) { }`. Also matches immediately invoked function expression statements.
|
||||
- `"break"` is `break` statements.
|
||||
- `"case"` is `case` labels.
|
||||
- `"case"` is `case` clauses in `switch` statements.
|
||||
- `"cjs-export"` is `export` statements of CommonJS; e.g. `module.exports = 0`, `module.exports.foo = 1`, and `exports.foo = 2`. This is a special case of assignment.
|
||||
- `"cjs-import"` is `import` statements of CommonJS; e.g. `const foo = require("foo")`. This is a special case of variable declarations.
|
||||
- `"class"` is `class` declarations.
|
||||
- `"const"` is `const` variable declarations, both single-line and multiline.
|
||||
- `"continue"` is `continue` statements.
|
||||
- `"debugger"` is `debugger` statements.
|
||||
- `"default"` is `default` labels.
|
||||
- `"default"` is `default` clauses in `switch` statements.
|
||||
- `"directive"` is directive prologues. This matches directives; e.g. `"use strict"`.
|
||||
- `"do"` is `do-while` statements. This matches all statements that the first token is `do` keyword.
|
||||
- `"empty"` is empty statements.
|
||||
@ -212,6 +212,55 @@ Examples of **correct** code for the `[{ blankLine: "always", prev: "directive",
|
||||
foo();
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
This configuration would require blank lines between clauses in `switch` statements.
|
||||
|
||||
Examples of **incorrect** code for the `[{ blankLine: "always", prev: ["case", "default"], next: "*" }]` configuration:
|
||||
|
||||
```js
|
||||
/*eslint padding-line-between-statements: [
|
||||
"error",
|
||||
{ blankLine: "always", prev: ["case", "default"], next: "*" }
|
||||
]*/
|
||||
|
||||
switch (foo) {
|
||||
case 1:
|
||||
bar();
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
baz();
|
||||
break;
|
||||
default:
|
||||
quux();
|
||||
}
|
||||
```
|
||||
|
||||
Examples of **correct** code for the `[{ blankLine: "always", prev: ["case", "default"], next: "*" }]` configuration:
|
||||
|
||||
```js
|
||||
/*eslint padding-line-between-statements: [
|
||||
"error",
|
||||
{ blankLine: "always", prev: ["case", "default"], next: "*" }
|
||||
]*/
|
||||
|
||||
switch (foo) {
|
||||
case 1:
|
||||
bar();
|
||||
break;
|
||||
|
||||
case 2:
|
||||
|
||||
case 3:
|
||||
baz();
|
||||
break;
|
||||
|
||||
default:
|
||||
quux();
|
||||
}
|
||||
```
|
||||
|
||||
## Compatibility
|
||||
|
||||
- **JSCS:** [requirePaddingNewLineAfterVariableDeclaration]
|
||||
|
@ -21,6 +21,8 @@ The rule has a second object with a single key, `enforceForRenamedProperties`, w
|
||||
- Accessing an object property whose key is an integer will fall under the category `array` destructuring.
|
||||
- Accessing an array element through a computed index will fall under the category `object` destructuring.
|
||||
|
||||
The `--fix` option on the command line fixes only problems reported in variable declarations, and among them only those that fall under the category `object` destructuring. Furthermore, the name of the declared variable has to be the same as the name used for non-computed member access in the initializer. For example, `var foo = object.foo` can be automatically fixed by this rule. Problems that involve computed member access (e.g., `var foo = object[foo]`) or renamed properties (e.g., `var foo = object.bar`) are not automatically fixed.
|
||||
|
||||
Examples of **incorrect** code for this rule:
|
||||
|
||||
```javascript
|
||||
|
@ -9,6 +9,7 @@ Introduced in ES2018, object spread is a declarative alternative which may perfo
|
||||
Examples of **incorrect** code for this rule:
|
||||
|
||||
```js
|
||||
/*eslint prefer-object-spread: "error"*/
|
||||
|
||||
Object.assign({}, foo)
|
||||
|
||||
@ -31,6 +32,7 @@ Object.assign({ foo: bar });
|
||||
Examples of **correct** code for this rule:
|
||||
|
||||
```js
|
||||
/*eslint prefer-object-spread: "error"*/
|
||||
|
||||
Object.assign(...foo);
|
||||
|
||||
|
@ -88,6 +88,38 @@ RegExp(`${prefix}abc`);
|
||||
new RegExp(String.raw`^\d\. ${suffix}`);
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
This rule has an object option:
|
||||
|
||||
* `disallowRedundantWrapping` set to `true` additionally checks for unnecessarily wrapped regex literals (Default `false`).
|
||||
|
||||
### `disallowRedundantWrapping`
|
||||
|
||||
By default, this rule doesn’t check when a regex literal is unnecessarily wrapped in a `RegExp` constructor call. When the option `disallowRedundantWrapping` is set to `true`, the rule will also disallow such unnecessary patterns.
|
||||
|
||||
Examples of `incorrect` code for `{ "disallowRedundantWrapping": true }`
|
||||
|
||||
```js
|
||||
/*eslint prefer-regex-literals: ["error", {"disallowRedundantWrapping": true}]*/
|
||||
|
||||
new RegExp(/abc/);
|
||||
|
||||
new RegExp(/abc/, 'u');
|
||||
```
|
||||
|
||||
Examples of `correct` code for `{ "disallowRedundantWrapping": true }`
|
||||
|
||||
```js
|
||||
/*eslint prefer-regex-literals: ["error", {"disallowRedundantWrapping": true}]*/
|
||||
|
||||
/abc/;
|
||||
|
||||
/abc/u;
|
||||
|
||||
new RegExp(/abc/, flags);
|
||||
```
|
||||
|
||||
## Further Reading
|
||||
|
||||
* [MDN: Regular Expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions)
|
||||
|
@ -41,6 +41,7 @@ This rule accepts an object with its properties as
|
||||
* `all` = import all members provided by exported bindings.
|
||||
* `multiple` = import multiple members.
|
||||
* `single` = import single member.
|
||||
* `allowSeparatedGroups` (default: `false`)
|
||||
|
||||
Default option settings are:
|
||||
|
||||
@ -50,7 +51,8 @@ Default option settings are:
|
||||
"ignoreCase": false,
|
||||
"ignoreDeclarationSort": false,
|
||||
"ignoreMemberSort": false,
|
||||
"memberSyntaxSortOrder": ["none", "all", "multiple", "single"]
|
||||
"memberSyntaxSortOrder": ["none", "all", "multiple", "single"],
|
||||
"allowSeparatedGroups": false
|
||||
}]
|
||||
}
|
||||
```
|
||||
@ -226,6 +228,53 @@ import {a, b} from 'foo.js';
|
||||
|
||||
Default is `["none", "all", "multiple", "single"]`.
|
||||
|
||||
### `allowSeparatedGroups`
|
||||
|
||||
When `true` the rule checks the sorting of import declaration statements only for those that appear on consecutive lines.
|
||||
|
||||
In other words, a blank line or a comment line or line with any other statement after an import declaration statement will reset the sorting of import declaration statements.
|
||||
|
||||
Examples of **incorrect** code for this rule with the `{ "allowSeparatedGroups": true }` option:
|
||||
|
||||
```js
|
||||
/*eslint sort-imports: ["error", { "allowSeparatedGroups": true }]*/
|
||||
|
||||
import b from 'foo.js';
|
||||
import c from 'bar.js';
|
||||
import a from 'baz.js';
|
||||
```
|
||||
|
||||
Examples of **correct** code for this rule with the `{ "allowSeparatedGroups": true }` option:
|
||||
|
||||
```js
|
||||
/*eslint sort-imports: ["error", { "allowSeparatedGroups": true }]*/
|
||||
|
||||
import b from 'foo.js';
|
||||
import c from 'bar.js';
|
||||
|
||||
import a from 'baz.js';
|
||||
```
|
||||
|
||||
```js
|
||||
/*eslint sort-imports: ["error", { "allowSeparatedGroups": true }]*/
|
||||
|
||||
import b from 'foo.js';
|
||||
import c from 'bar.js';
|
||||
// comment
|
||||
import a from 'baz.js';
|
||||
```
|
||||
|
||||
```js
|
||||
/*eslint sort-imports: ["error", { "allowSeparatedGroups": true }]*/
|
||||
|
||||
import b from 'foo.js';
|
||||
import c from 'bar.js';
|
||||
quux();
|
||||
import a from 'baz.js';
|
||||
```
|
||||
|
||||
Default is `false`.
|
||||
|
||||
## When Not To Use It
|
||||
|
||||
This rule is a formatting preference and not following it won't negatively affect the quality of your code. If alphabetizing imports isn't a part of your coding standards, then you can leave this rule disabled.
|
||||
|
@ -6,6 +6,8 @@ Some style guides require or disallow spaces before or after unary operators. Th
|
||||
|
||||
This rule enforces consistency regarding the spaces after `words` unary operators and after/before `nonwords` unary operators.
|
||||
|
||||
For `words` operators, this rule only applies when a space is not syntactically required. For instance, `delete obj.foo` requires the space and will not be considered by this rule. The equivalent `delete(obj.foo)` has an optional space (`delete (obj.foo)`), therefore this rule will apply to it.
|
||||
|
||||
Examples of unary `words` operators:
|
||||
|
||||
```js
|
||||
@ -103,14 +105,17 @@ Examples of **correct** code for this rule with the `{"words": true, "nonwords":
|
||||
```js
|
||||
/*eslint space-unary-ops: "error"*/
|
||||
|
||||
// Word unary operator "delete" is followed by a whitespace.
|
||||
delete foo.bar;
|
||||
|
||||
// Word unary operator "new" is followed by a whitespace.
|
||||
new Foo;
|
||||
// Word unary operator "typeof" is followed by a whitespace.
|
||||
typeof !foo;
|
||||
|
||||
// Word unary operator "void" is followed by a whitespace.
|
||||
void 0;
|
||||
void {foo:0};
|
||||
|
||||
// Word unary operator "new" is followed by a whitespace.
|
||||
new [foo][0];
|
||||
|
||||
// Word unary operator "delete" is followed by a whitespace.
|
||||
delete (foo.bar);
|
||||
|
||||
// Unary operator "++" is not followed by whitespace.
|
||||
++foo;
|
||||
|
@ -37,6 +37,8 @@ typeof bar === typeof qux
|
||||
Examples of **incorrect** code with the `{ "requireStringLiterals": true }` option:
|
||||
|
||||
```js
|
||||
/*eslint valid-typeof: ["error", { "requireStringLiterals": true }]*/
|
||||
|
||||
typeof foo === undefined
|
||||
typeof bar == Object
|
||||
typeof baz === "strnig"
|
||||
@ -48,6 +50,8 @@ typeof foo == 5
|
||||
Examples of **correct** code with the `{ "requireStringLiterals": true }` option:
|
||||
|
||||
```js
|
||||
/*eslint valid-typeof: ["error", { "requireStringLiterals": true }]*/
|
||||
|
||||
typeof foo === "undefined"
|
||||
typeof bar == "object"
|
||||
typeof baz === "string"
|
||||
|
@ -13,6 +13,27 @@ There are several pieces of information that can be configured:
|
||||
|
||||
All of these options give you fine-grained control over how ESLint treats your code.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Specifying Parser Options](#specifying-parser-options)
|
||||
* [Specifying Parser](#specifying-parser)
|
||||
* [Specifying Processor](#specifying-processor)
|
||||
* [Specifying Environments](#specifying-environments)
|
||||
* [Specifying Globals](#specifying-globals)
|
||||
* [Configuring Plugins](#configuring-plugins)
|
||||
* [Configuring Rules](#configuring-rules)
|
||||
* [Disabling Rules with Inline Comments](#disabling-rules-with-inline-comments)
|
||||
* [Configuring Inline Comment Behaviors](#configuring-inline-comment-behaviors)
|
||||
* [Adding Shared Settings](#adding-shared-settings)
|
||||
* [Using Configuration Files](#using-configuration-files-1)
|
||||
* [Configuration File Formats](#configuration-file-formats)
|
||||
* [Configuration Cascading and Hierarchy](#configuration-cascading-and-hierarchy)
|
||||
* [Extending Configuration Files](#extending-configuration-files)
|
||||
* [Configuration Based on Glob Patterns](#configuration-based-on-glob-patterns)
|
||||
* [Comments in Configuration Files](#comments-in-configuration-files)
|
||||
* [Ignoring Files and Directories](#ignoring-files-and-directories)
|
||||
* [Personal Configuration File (deprecated)](#personal-configuration-file-deprecated)
|
||||
|
||||
## Specifying Parser Options
|
||||
|
||||
ESLint allows you to specify the JavaScript language options you want to support. By default, ESLint expects ECMAScript 5 syntax. You can override that setting to enable support for other ECMAScript versions as well as JSX by using parser options.
|
||||
@ -24,7 +45,7 @@ For ES6 syntax, use `{ "parserOptions": { "ecmaVersion": 6 } }`; for new ES6 glo
|
||||
{ "es6": true } }`. `{ "env": { "es6": true } }` enables ES6 syntax automatically, but `{ "parserOptions": { "ecmaVersion": 6 } }` does not enable ES6 globals automatically.
|
||||
Parser options are set in your `.eslintrc.*` file by using the `parserOptions` property. The available options are:
|
||||
|
||||
* `ecmaVersion` - set to 3, 5 (default), 6, 7, 8, 9, 10 or 11 to specify the version of ECMAScript syntax you want to use. You can also set to 2015 (same as 6), 2016 (same as 7), 2017 (same as 8), 2018 (same as 9), 2019 (same as 10) or 2020 (same as 11) to use the year-based naming.
|
||||
* `ecmaVersion` - set to 3, 5 (default), 6, 7, 8, 9, 10, 11, or 12 to specify the version of ECMAScript syntax you want to use. You can also set to 2015 (same as 6), 2016 (same as 7), 2017 (same as 8), 2018 (same as 9), 2019 (same as 10), 2020 (same as 11), or 2021 (same as 12) to use the year-based naming.
|
||||
* `sourceType` - set to `"script"` (default) or `"module"` if your code is in ECMAScript modules.
|
||||
* `ecmaFeatures` - an object indicating which additional language features you'd like to use:
|
||||
* `globalReturn` - allow `return` statements in the global scope
|
||||
@ -73,7 +94,7 @@ To indicate the npm module to use as your parser, specify it using the `parser`
|
||||
The following parsers are compatible with ESLint:
|
||||
|
||||
* [Esprima](https://www.npmjs.com/package/esprima)
|
||||
* [Babel-ESLint](https://www.npmjs.com/package/babel-eslint) - A wrapper around the [Babel](https://babeljs.io) parser that makes it compatible with ESLint.
|
||||
* [@babel/eslint-parser](https://www.npmjs.com/package/@babel/eslint-parser) - A wrapper around the [Babel](https://babeljs.io) parser that makes it compatible with ESLint.
|
||||
* [@typescript-eslint/parser](https://www.npmjs.com/package/@typescript-eslint/parser) - A parser that converts TypeScript into an ESTree-compatible form so it can be used in ESLint.
|
||||
|
||||
Note when using a custom parser, the `parserOptions` configuration property is still required for ESLint to work properly with features not in ECMAScript 5 by default. Parsers are all passed `parserOptions` and may or may not use them to determine which features to enable.
|
||||
@ -91,7 +112,7 @@ To specify processors in a configuration file, use the `processor` key with the
|
||||
}
|
||||
```
|
||||
|
||||
To specify processors for a specific kind of files, use the combination of the `overrides` key and the `processor` key. For example, the following uses the processor `a-plugin/markdown` for `*.md` files.
|
||||
To specify processors for specific kinds of files, use the combination of the `overrides` key and the `processor` key. For example, the following uses the processor `a-plugin/markdown` for `*.md` files.
|
||||
|
||||
```json
|
||||
{
|
||||
@ -138,6 +159,7 @@ An environment defines global variables that are predefined. The available envir
|
||||
* `es6` - enable all ECMAScript 6 features except for modules (this automatically sets the `ecmaVersion` parser option to 6).
|
||||
* `es2017` - adds all ECMAScript 2017 globals and automatically sets the `ecmaVersion` parser option to 8.
|
||||
* `es2020` - adds all ECMAScript 2020 globals and automatically sets the `ecmaVersion` parser option to 11.
|
||||
* `es2021` - adds all ECMAScript 2021 globals and automatically sets the `ecmaVersion` parser option to 12.
|
||||
* `worker` - web workers global variables.
|
||||
* `amd` - defines `require()` and `define()` as global variables as per the [amd](https://github.com/amdjs/amdjs-api/wiki/AMD) spec.
|
||||
* `mocha` - adds all of the Mocha testing global variables.
|
||||
@ -1070,18 +1092,36 @@ Of particular note is that like `.gitignore` files, all paths used as patterns f
|
||||
|
||||
Please see `.gitignore`'s specification for further examples of valid syntax.
|
||||
|
||||
In addition to any patterns in a `.eslintignore` file, ESLint ignores files in `/**/node_modules/*` by default. It can still be added using `!`.
|
||||
In addition to any patterns in the `.eslintignore` file, ESLint always follows a couple implicit ignore rules even if the `--no-ignore` flag is passed. The implicit rules are as follows:
|
||||
|
||||
For example, placing the following `.eslintignore` file in the current working directory will not ignore `node_modules/*` and ignore anything in the `build/` directory except `build/index.js`:
|
||||
* `node_modules/` is ignored.
|
||||
* Dotfiles (except for `.eslintrc.*`) as well as Dotfolders and their contents are ignored.
|
||||
|
||||
```text
|
||||
# node_modules/* is ignored by default, but can be added using !
|
||||
!node_modules/*
|
||||
There are also some exceptions to these rules:
|
||||
|
||||
# Ignore built files except build/index.js
|
||||
build/*
|
||||
!build/index.js
|
||||
```
|
||||
* If the path to lint is a glob pattern or directory path and contains a Dotfolder, all Dotfiles and Dotfolders will be linted. This includes sub-dotfiles and sub-dotfolders that are buried deeper in the directory structure.
|
||||
|
||||
For example, `eslint .config/` will lint all Dotfolders and Dotfiles in the `.config` directory, including immediate children as well as children that are deeper in the directory structure.
|
||||
|
||||
* If the path to lint is a specific file path and the `--no-ignore` flag has been passed, ESLint will lint the file regardless of the implicit ignore rules.
|
||||
|
||||
For example, `eslint .config/my-config-file.js --no-ignore` will cause `my-config-file.js` to be linted. It should be noted that the same command without the `--no-ignore` line will not lint the `my-config-file.js` file.
|
||||
|
||||
* Allowlist and denylist rules specified via `--ignore-pattern` or `.eslintignore` are prioritized above implicit ignore rules.
|
||||
|
||||
For example, in this scenario, `.build/test.js` is the desired file to allowlist. Because all Dotfolders and their children are ignored by default, `.build` must first be allowlisted so that eslint because aware of its children. Then, `.build/test.js` must be explicitly allowlisted, while the rest of the content is denylisted. This is done with the following `.eslintignore` file:
|
||||
|
||||
```text
|
||||
# Allowlist 'test.js' in the '.build' folder
|
||||
# But do not allow anything else in the '.build' folder to be linted
|
||||
!.build
|
||||
.build/*
|
||||
!.build/test.js
|
||||
```
|
||||
|
||||
The following `--ignore-pattern` is also equivalent:
|
||||
|
||||
eslint --ignore-pattern '!.build' --ignore-pattern '.build/*' --ignore-pattern '!.build/test.js' parent-folder/
|
||||
|
||||
### Using an Alternate File
|
||||
|
||||
@ -1127,13 +1167,28 @@ You'll see this warning:
|
||||
|
||||
```text
|
||||
foo.js
|
||||
0:0 warning File ignored because of your .eslintignore file. Use --no-ignore to override.
|
||||
0:0 warning File ignored because of a matching ignore pattern. Use "--no-ignore" to override.
|
||||
|
||||
✖ 1 problem (0 errors, 1 warning)
|
||||
```
|
||||
|
||||
This message occurs because ESLint is unsure if you wanted to actually lint the file or not. As the message indicates, you can use `--no-ignore` to omit using the ignore rules.
|
||||
|
||||
Consider another scenario where you may want to run ESLint on a specific Dotfile or Dotfolder, but have forgotten to specifically allow those files in your `.eslintignore` file. You would run something like this:
|
||||
|
||||
eslint .config/foo.js
|
||||
|
||||
You would see this warning:
|
||||
|
||||
```text
|
||||
.config/foo.js
|
||||
0:0 warning File ignored by default. Use a negated ignore pattern (like "--ignore-pattern '!<relative/path/to/filename>'") to override
|
||||
|
||||
✖ 1 problem (0 errors, 1 warning)
|
||||
```
|
||||
|
||||
This messages occurs because, normally, this file would be ignored by ESLint's implicit ignore rules (as mentioned above). A negated ignore rule in your `.eslintignore` file would override the implicit rule and reinclude this file for linting. Additionally, in this specific case, `--no-ignore` could be used to lint the file as well.
|
||||
|
||||
## Personal Configuration File (deprecated)
|
||||
|
||||
⚠️ **This feature has been deprecated**. This feature will be removed in the 8.0.0 release. If you want to continue to use personal configuration files, please use the [`--config` CLI option](https://eslint.org/docs/user-guide/command-line-interface#-c---config). For more information regarding this decision, please see [RFC 28](https://github.com/eslint/rfcs/pull/28) and [RFC 32](https://github.com/eslint/rfcs/pull/32).
|
||||
|
@ -20,16 +20,26 @@ npm install eslint --save-dev
|
||||
yarn add eslint --dev
|
||||
```
|
||||
|
||||
You should then set up a configuration file:
|
||||
You should then set up a configuration file, and the easiest way to do that is to use the `--init` flag:
|
||||
|
||||
```
|
||||
$ npx eslint --init
|
||||
|
||||
# or
|
||||
|
||||
$ yarn run eslint --init
|
||||
```
|
||||
|
||||
**Note:** `--init` assumes you have a `package.json` file already. If you don't, make sure to run `npm init` or `yarn init` beforehand.
|
||||
|
||||
After that, you can run ESLint on any file or directory like this:
|
||||
|
||||
```
|
||||
$ npx eslint yourfile.js
|
||||
|
||||
# or
|
||||
|
||||
$ yarn run eslint yourfile.js
|
||||
```
|
||||
|
||||
It is also possible to install ESLint globally rather than locally (using `npm install eslint --global`). However, this is not recommended, and any plugins or shareable configs that you use must be installed locally in either case.
|
||||
|
@ -30,7 +30,7 @@ The lists below are ordered roughly by the number of users each change is expect
|
||||
|
||||
---
|
||||
|
||||
## <a name="eslint-recommended-changes"/> `eslint:recommended` changes
|
||||
## <a name="eslint-recommended-changes"></a> `eslint:recommended` changes
|
||||
|
||||
Two new rules have been added to the [`eslint:recommended`](https://eslint.org/docs/user-guide/configuring#using-eslintrecommended) config:
|
||||
|
||||
@ -50,7 +50,7 @@ Two new rules have been added to the [`eslint:recommended`](https://eslint.org/d
|
||||
}
|
||||
```
|
||||
|
||||
## <a name="indent-rewrite"/> The `indent` rule is more strict
|
||||
## <a name="indent-rewrite"></a> The `indent` rule is more strict
|
||||
|
||||
Previously, the [`indent`](/docs/rules/indent) rule was fairly lenient about checking indentation; there were many code patterns where indentation was not validated by the rule. This caused confusion for users, because they were accidentally writing code with incorrect indentation, and they expected ESLint to catch the issues.
|
||||
|
||||
@ -69,25 +69,25 @@ To make the upgrade process easier, we've introduced the [`indent-legacy`](/docs
|
||||
}
|
||||
```
|
||||
|
||||
## <a name="config-validation"/> Unrecognized properties in config files now cause a fatal error
|
||||
## <a name="config-validation"></a> Unrecognized properties in config files now cause a fatal error
|
||||
|
||||
When creating a config, users sometimes make typos or misunderstand how the config is supposed to be structured. Previously, ESLint did not validate the properties of a config file, so a typo in a config could be very tedious to debug. Starting in 4.0.0, ESLint will raise an error if a property in a config file is unrecognized or has the wrong type.
|
||||
|
||||
**To address:** If you see a config validation error after upgrading, verify that your config doesn't contain any typos. If you are using an unrecognized property, you should be able to remove it from your config to restore the previous behavior.
|
||||
|
||||
## <a name="eslintignore-patterns"/> .eslintignore patterns are now resolved from the location of the file
|
||||
## <a name="eslintignore-patterns"></a> .eslintignore patterns are now resolved from the location of the file
|
||||
|
||||
Due to a bug, glob patterns in an `.eslintignore` file were previously resolved from the current working directory of the process, rather than the location of the `.eslintignore` file. Starting in 4.0, patterns in an `.eslintignore` file will be resolved from the `.eslintignore` file's location.
|
||||
|
||||
**To address:** If you use an `.eslintignore` file and you frequently run ESLint from somewhere other than the project root, it's possible that the patterns will be matched differently. You should update the patterns in the `.eslintignore` file to ensure they are relative to the file, not to the working directory.
|
||||
|
||||
## <a name="padded-blocks-defaults"/> The `padded-blocks` rule is more strict by default
|
||||
## <a name="padded-blocks-defaults"></a> The `padded-blocks` rule is more strict by default
|
||||
|
||||
By default, the [`padded-blocks`](/docs/rules/padded-blocks) rule will now enforce padding in class bodies and switch statements. Previously, the rule would ignore these cases unless the user opted into enforcing them.
|
||||
|
||||
**To address:** If this change results in more linting errors in your codebase, you should fix them or reconfigure the rule.
|
||||
|
||||
## <a name="space-before-function-paren-defaults"/> The `space-before-function-paren` rule is more strict by default
|
||||
## <a name="space-before-function-paren-defaults"></a> The `space-before-function-paren` rule is more strict by default
|
||||
|
||||
By default, the [`space-before-function-paren`](/docs/rules/space-before-function-paren) rule will now enforce spacing for async arrow functions. Previously, the rule would ignore these cases unless the user opted into enforcing them.
|
||||
|
||||
@ -105,7 +105,7 @@ By default, the [`space-before-function-paren`](/docs/rules/space-before-functio
|
||||
}
|
||||
```
|
||||
|
||||
## <a name="no-multi-spaces-eol-comments"/> The `no-multi-spaces` rule is more strict by default
|
||||
## <a name="no-multi-spaces-eol-comments"></a> The `no-multi-spaces` rule is more strict by default
|
||||
|
||||
By default, the [`no-multi-spaces`](/docs/rules/no-multi-spaces) rule will now disallow multiple spaces before comments at the end of a line. Previously, the rule did not check this case.
|
||||
|
||||
@ -119,7 +119,7 @@ By default, the [`no-multi-spaces`](/docs/rules/no-multi-spaces) rule will now d
|
||||
}
|
||||
```
|
||||
|
||||
## <a name="scoped-plugin-resolution"/> References to scoped plugins in config files are now required to include the scope
|
||||
## <a name="scoped-plugin-resolution"></a> References to scoped plugins in config files are now required to include the scope
|
||||
|
||||
In 3.x, there was a bug where references to scoped NPM packages as plugins in config files could omit the scope. For example, in 3.x the following config was legal:
|
||||
|
||||
@ -153,13 +153,13 @@ To avoid this ambiguity, in 4.0 references to scoped plugins must include the sc
|
||||
|
||||
---
|
||||
|
||||
## <a name="rule-tester-validation"/> `RuleTester` now validates properties of test cases
|
||||
## <a name="rule-tester-validation"></a> `RuleTester` now validates properties of test cases
|
||||
|
||||
Starting in 4.0, the `RuleTester` utility will validate properties of test case objects, and an error will be thrown if an unknown property is encountered. This change was added because we found that it was relatively common for developers to make typos in rule tests, often invalidating the assertions that the test cases were trying to make.
|
||||
|
||||
**To address:** If your tests for custom rules have extra properties, you should remove those properties.
|
||||
|
||||
## <a name="comment-attachment"/> AST Nodes no longer have comment properties
|
||||
## <a name="comment-attachment"></a> AST Nodes no longer have comment properties
|
||||
|
||||
Prior to 4.0, ESLint required parsers to implement comment attachment, a process where AST nodes would gain additional properties corresponding to their leading and trailing comments in the source file. This made it difficult for users to develop custom parsers, because they would have to replicate the confusing comment attachment semantics required by ESLint.
|
||||
|
||||
@ -177,7 +177,7 @@ Finally, please note that the following `SourceCode` methods have been deprecate
|
||||
* `getTokenOrCommentBefore()` - replaced by `getTokenBefore()` with the `{ includeComments: true }` option
|
||||
* `getTokenOrCommentAfter()` - replaced by `getTokenAfter()` with the `{ includeComments: true }` option
|
||||
|
||||
## <a name="event-comments"/> `LineComment` and `BlockComment` events will no longer be emitted during AST traversal
|
||||
## <a name="event-comments"></a> `LineComment` and `BlockComment` events will no longer be emitted during AST traversal
|
||||
|
||||
Starting in 4.0, `LineComment` and `BlockComments` events will not be emitted during AST traversal. There are two reasons for this:
|
||||
|
||||
@ -191,7 +191,7 @@ sourceCode.getAllComments().filter(comment => comment.type === "Line");
|
||||
sourceCode.getAllComments().filter(comment => comment.type === "Block");
|
||||
```
|
||||
|
||||
## <a name="shebangs"/> Shebangs are now returned from comment APIs
|
||||
## <a name="shebangs"></a> Shebangs are now returned from comment APIs
|
||||
|
||||
Prior to 4.0, shebang comments in a source file would not appear in the output of `sourceCode.getAllComments()` or `sourceCode.getComments()`, but they would appear in the output of `sourceCode.getTokenOrCommentBefore` as line comments. This inconsistency led to some confusion for rule developers.
|
||||
|
||||
@ -205,13 +205,13 @@ sourceCode.getAllComments().filter(comment => comment.type !== "Shebang");
|
||||
|
||||
---
|
||||
|
||||
## <a name="global-property"/> The `global` property in the `linter.verify()` API is no longer supported
|
||||
## <a name="global-property"></a> The `global` property in the `linter.verify()` API is no longer supported
|
||||
|
||||
Previously, the `linter.verify()` API accepted a `global` config option, which was a synonym for the documented `globals` property. The `global` option was never documented or officially supported, and did not work in config files. It has been removed in 4.0.
|
||||
|
||||
**To address:** If you were using the `global` property, please use the `globals` property instead, which does the same thing.
|
||||
|
||||
## <a name="report-locations"/> More report messages now have full location ranges
|
||||
## <a name="report-locations"></a> More report messages now have full location ranges
|
||||
|
||||
Starting in 3.1.0, rules have been able to specify the *end* location of a reported problem, in addition to the start location, by explicitly specifying an end location in the `report` call. This is useful for tools like editor integrations, which can use the range to precisely display where a reported problem occurs. Starting in 4.0, if a *node* is reported rather than a location, the end location of the range will automatically be inferred from the end location of the node. As a result, many more reported problems will have end locations.
|
||||
|
||||
@ -219,7 +219,7 @@ This is not expected to cause breakage. However, it will likely result in larger
|
||||
|
||||
**To address:** If you have an integration that deals with the ranges of reported problems, make sure you handle large report ranges in a user-friendly way.
|
||||
|
||||
## <a name="exposed-es2015-classes"/> Some exposed APIs are now ES2015 classes
|
||||
## <a name="exposed-es2015-classes"></a> Some exposed APIs are now ES2015 classes
|
||||
|
||||
The `CLIEngine`, `SourceCode`, and `RuleTester` modules from ESLint's Node.js API are now ES2015 classes. This will not break any documented behavior, but it does have some observable effects (for example, the methods on `CLIEngine.prototype` are now non-enumerable).
|
||||
|
||||
|
@ -171,7 +171,7 @@ Several rules have been enhanced and now report additional errors:
|
||||
- [func-names](https://eslint.org/docs/rules/func-names) rule now recognizes function declarations in default exports.
|
||||
- [no-extra-parens](https://eslint.org/docs/rules/no-extra-parens) rule now recognizes parentheses in assignment targets.
|
||||
- [no-dupe-class-members](https://eslint.org/docs/rules/no-dupe-class-members) rule now recognizes computed keys for static class members.
|
||||
- [no-magic-number](https://eslint.org/docs/rules/no-magic-number) rule now recognizes bigint literals.
|
||||
- [no-magic-numbers](https://eslint.org/docs/rules/no-magic-numbers) rule now recognizes bigint literals.
|
||||
- [radix](https://eslint.org/docs/rules/radix) rule now recognizes invalid numbers for the second parameter of `parseInt()`.
|
||||
- [use-isnan](https://eslint.org/docs/rules/use-isnan) rule now recognizes class members by default.
|
||||
- [yoda](https://eslint.org/docs/rules/yoda) rule now recognizes bigint literals.
|
||||
|
@ -1,6 +1,13 @@
|
||||
"use strict";
|
||||
const os = require("os");
|
||||
|
||||
process.env.CHROME_BIN = require("puppeteer").executablePath();
|
||||
if (os.arch() === "arm64") {
|
||||
|
||||
// For arm64 architecture, install chromium-browser using "apt-get install chromium-browser"
|
||||
process.env.CHROME_BIN = "/usr/bin/chromium-browser";
|
||||
} else {
|
||||
process.env.CHROME_BIN = require("puppeteer").executablePath();
|
||||
}
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
@ -1,490 +0,0 @@
|
||||
/**
|
||||
* @fileoverview `CascadingConfigArrayFactory` class.
|
||||
*
|
||||
* `CascadingConfigArrayFactory` class has a responsibility:
|
||||
*
|
||||
* 1. Handles cascading of config files.
|
||||
*
|
||||
* It provides two methods:
|
||||
*
|
||||
* - `getConfigArrayForFile(filePath)`
|
||||
* Get the corresponded configuration of a given file. This method doesn't
|
||||
* throw even if the given file didn't exist.
|
||||
* - `clearCache()`
|
||||
* Clear the internal cache. You have to call this method when
|
||||
* `additionalPluginPool` was updated if `baseConfig` or `cliConfig` depends
|
||||
* on the additional plugins. (`CLIEngine#addPlugin()` method calls this.)
|
||||
*
|
||||
* @author Toru Nagashima <https://github.com/mysticatea>
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const os = require("os");
|
||||
const path = require("path");
|
||||
const { validateConfigArray } = require("../shared/config-validator");
|
||||
const { emitDeprecationWarning } = require("../shared/deprecation-warnings");
|
||||
const { ConfigArrayFactory } = require("./config-array-factory");
|
||||
const { ConfigArray, ConfigDependency, IgnorePattern } = require("./config-array");
|
||||
const loadRules = require("./load-rules");
|
||||
const debug = require("debug")("eslint:cascading-config-array-factory");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Define types for VSCode IntelliSense.
|
||||
/** @typedef {import("../shared/types").ConfigData} ConfigData */
|
||||
/** @typedef {import("../shared/types").Parser} Parser */
|
||||
/** @typedef {import("../shared/types").Plugin} Plugin */
|
||||
/** @typedef {ReturnType<ConfigArrayFactory["create"]>} ConfigArray */
|
||||
|
||||
/**
|
||||
* @typedef {Object} CascadingConfigArrayFactoryOptions
|
||||
* @property {Map<string,Plugin>} [additionalPluginPool] The map for additional plugins.
|
||||
* @property {ConfigData} [baseConfig] The config by `baseConfig` option.
|
||||
* @property {ConfigData} [cliConfig] The config by CLI options (`--env`, `--global`, `--ignore-pattern`, `--parser`, `--parser-options`, `--plugin`, and `--rule`). CLI options overwrite the setting in config files.
|
||||
* @property {string} [cwd] The base directory to start lookup.
|
||||
* @property {string} [ignorePath] The path to the alternative file of `.eslintignore`.
|
||||
* @property {string[]} [rulePaths] The value of `--rulesdir` option.
|
||||
* @property {string} [specificConfigPath] The value of `--config` option.
|
||||
* @property {boolean} [useEslintrc] if `false` then it doesn't load config files.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} CascadingConfigArrayFactoryInternalSlots
|
||||
* @property {ConfigArray} baseConfigArray The config array of `baseConfig` option.
|
||||
* @property {ConfigData} baseConfigData The config data of `baseConfig` option. This is used to reset `baseConfigArray`.
|
||||
* @property {ConfigArray} cliConfigArray The config array of CLI options.
|
||||
* @property {ConfigData} cliConfigData The config data of CLI options. This is used to reset `cliConfigArray`.
|
||||
* @property {ConfigArrayFactory} configArrayFactory The factory for config arrays.
|
||||
* @property {Map<string, ConfigArray>} configCache The cache from directory paths to config arrays.
|
||||
* @property {string} cwd The base directory to start lookup.
|
||||
* @property {WeakMap<ConfigArray, ConfigArray>} finalizeCache The cache from config arrays to finalized config arrays.
|
||||
* @property {string} [ignorePath] The path to the alternative file of `.eslintignore`.
|
||||
* @property {string[]|null} rulePaths The value of `--rulesdir` option. This is used to reset `baseConfigArray`.
|
||||
* @property {string|null} specificConfigPath The value of `--config` option. This is used to reset `cliConfigArray`.
|
||||
* @property {boolean} useEslintrc if `false` then it doesn't load config files.
|
||||
*/
|
||||
|
||||
/** @type {WeakMap<CascadingConfigArrayFactory, CascadingConfigArrayFactoryInternalSlots>} */
|
||||
const internalSlotsMap = new WeakMap();
|
||||
|
||||
/**
|
||||
* Create the config array from `baseConfig` and `rulePaths`.
|
||||
* @param {CascadingConfigArrayFactoryInternalSlots} slots The slots.
|
||||
* @returns {ConfigArray} The config array of the base configs.
|
||||
*/
|
||||
function createBaseConfigArray({
|
||||
configArrayFactory,
|
||||
baseConfigData,
|
||||
rulePaths,
|
||||
cwd
|
||||
}) {
|
||||
const baseConfigArray = configArrayFactory.create(
|
||||
baseConfigData,
|
||||
{ name: "BaseConfig" }
|
||||
);
|
||||
|
||||
/*
|
||||
* Create the config array element for the default ignore patterns.
|
||||
* This element has `ignorePattern` property that ignores the default
|
||||
* patterns in the current working directory.
|
||||
*/
|
||||
baseConfigArray.unshift(configArrayFactory.create(
|
||||
{ ignorePatterns: IgnorePattern.DefaultPatterns },
|
||||
{ name: "DefaultIgnorePattern" }
|
||||
)[0]);
|
||||
|
||||
/*
|
||||
* Load rules `--rulesdir` option as a pseudo plugin.
|
||||
* Use a pseudo plugin to define rules of `--rulesdir`, so we can validate
|
||||
* the rule's options with only information in the config array.
|
||||
*/
|
||||
if (rulePaths && rulePaths.length > 0) {
|
||||
baseConfigArray.push({
|
||||
type: "config",
|
||||
name: "--rulesdir",
|
||||
filePath: "",
|
||||
plugins: {
|
||||
"": new ConfigDependency({
|
||||
definition: {
|
||||
rules: rulePaths.reduce(
|
||||
(map, rulesPath) => Object.assign(
|
||||
map,
|
||||
loadRules(rulesPath, cwd)
|
||||
),
|
||||
{}
|
||||
)
|
||||
},
|
||||
filePath: "",
|
||||
id: "",
|
||||
importerName: "--rulesdir",
|
||||
importerPath: ""
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return baseConfigArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the config array from CLI options.
|
||||
* @param {CascadingConfigArrayFactoryInternalSlots} slots The slots.
|
||||
* @returns {ConfigArray} The config array of the base configs.
|
||||
*/
|
||||
function createCLIConfigArray({
|
||||
cliConfigData,
|
||||
configArrayFactory,
|
||||
cwd,
|
||||
ignorePath,
|
||||
specificConfigPath
|
||||
}) {
|
||||
const cliConfigArray = configArrayFactory.create(
|
||||
cliConfigData,
|
||||
{ name: "CLIOptions" }
|
||||
);
|
||||
|
||||
cliConfigArray.unshift(
|
||||
...(ignorePath
|
||||
? configArrayFactory.loadESLintIgnore(ignorePath)
|
||||
: configArrayFactory.loadDefaultESLintIgnore())
|
||||
);
|
||||
|
||||
if (specificConfigPath) {
|
||||
cliConfigArray.unshift(
|
||||
...configArrayFactory.loadFile(
|
||||
specificConfigPath,
|
||||
{ name: "--config", basePath: cwd }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return cliConfigArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* The error type when there are files matched by a glob, but all of them have been ignored.
|
||||
*/
|
||||
class ConfigurationNotFoundError extends Error {
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-description
|
||||
/**
|
||||
* @param {string} directoryPath The directory path.
|
||||
*/
|
||||
constructor(directoryPath) {
|
||||
super(`No ESLint configuration found in ${directoryPath}.`);
|
||||
this.messageTemplate = "no-config-found";
|
||||
this.messageData = { directoryPath };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class provides the functionality that enumerates every file which is
|
||||
* matched by given glob patterns and that configuration.
|
||||
*/
|
||||
class CascadingConfigArrayFactory {
|
||||
|
||||
/**
|
||||
* Initialize this enumerator.
|
||||
* @param {CascadingConfigArrayFactoryOptions} options The options.
|
||||
*/
|
||||
constructor({
|
||||
additionalPluginPool = new Map(),
|
||||
baseConfig: baseConfigData = null,
|
||||
cliConfig: cliConfigData = null,
|
||||
cwd = process.cwd(),
|
||||
ignorePath,
|
||||
resolvePluginsRelativeTo,
|
||||
rulePaths = [],
|
||||
specificConfigPath = null,
|
||||
useEslintrc = true
|
||||
} = {}) {
|
||||
const configArrayFactory = new ConfigArrayFactory({
|
||||
additionalPluginPool,
|
||||
cwd,
|
||||
resolvePluginsRelativeTo
|
||||
});
|
||||
|
||||
internalSlotsMap.set(this, {
|
||||
baseConfigArray: createBaseConfigArray({
|
||||
baseConfigData,
|
||||
configArrayFactory,
|
||||
cwd,
|
||||
rulePaths
|
||||
}),
|
||||
baseConfigData,
|
||||
cliConfigArray: createCLIConfigArray({
|
||||
cliConfigData,
|
||||
configArrayFactory,
|
||||
cwd,
|
||||
ignorePath,
|
||||
specificConfigPath
|
||||
}),
|
||||
cliConfigData,
|
||||
configArrayFactory,
|
||||
configCache: new Map(),
|
||||
cwd,
|
||||
finalizeCache: new WeakMap(),
|
||||
ignorePath,
|
||||
rulePaths,
|
||||
specificConfigPath,
|
||||
useEslintrc
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The path to the current working directory.
|
||||
* This is used by tests.
|
||||
* @type {string}
|
||||
*/
|
||||
get cwd() {
|
||||
const { cwd } = internalSlotsMap.get(this);
|
||||
|
||||
return cwd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the config array of a given file.
|
||||
* If `filePath` was not given, it returns the config which contains only
|
||||
* `baseConfigData` and `cliConfigData`.
|
||||
* @param {string} [filePath] The file path to a file.
|
||||
* @param {Object} [options] The options.
|
||||
* @param {boolean} [options.ignoreNotFoundError] If `true` then it doesn't throw `ConfigurationNotFoundError`.
|
||||
* @returns {ConfigArray} The config array of the file.
|
||||
*/
|
||||
getConfigArrayForFile(filePath, { ignoreNotFoundError = false } = {}) {
|
||||
const {
|
||||
baseConfigArray,
|
||||
cliConfigArray,
|
||||
cwd
|
||||
} = internalSlotsMap.get(this);
|
||||
|
||||
if (!filePath) {
|
||||
return new ConfigArray(...baseConfigArray, ...cliConfigArray);
|
||||
}
|
||||
|
||||
const directoryPath = path.dirname(path.resolve(cwd, filePath));
|
||||
|
||||
debug(`Load config files for ${directoryPath}.`);
|
||||
|
||||
return this._finalizeConfigArray(
|
||||
this._loadConfigInAncestors(directoryPath),
|
||||
directoryPath,
|
||||
ignoreNotFoundError
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the config data to override all configs.
|
||||
* Require to call `clearCache()` method after this method is called.
|
||||
* @param {ConfigData} configData The config data to override all configs.
|
||||
* @returns {void}
|
||||
*/
|
||||
setOverrideConfig(configData) {
|
||||
const slots = internalSlotsMap.get(this);
|
||||
|
||||
slots.cliConfigData = configData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear config cache.
|
||||
* @returns {void}
|
||||
*/
|
||||
clearCache() {
|
||||
const slots = internalSlotsMap.get(this);
|
||||
|
||||
slots.baseConfigArray = createBaseConfigArray(slots);
|
||||
slots.cliConfigArray = createCLIConfigArray(slots);
|
||||
slots.configCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and normalize config files from the ancestor directories.
|
||||
* @param {string} directoryPath The path to a leaf directory.
|
||||
* @param {boolean} configsExistInSubdirs `true` if configurations exist in subdirectories.
|
||||
* @returns {ConfigArray} The loaded config.
|
||||
* @private
|
||||
*/
|
||||
_loadConfigInAncestors(directoryPath, configsExistInSubdirs = false) {
|
||||
const {
|
||||
baseConfigArray,
|
||||
configArrayFactory,
|
||||
configCache,
|
||||
cwd,
|
||||
useEslintrc
|
||||
} = internalSlotsMap.get(this);
|
||||
|
||||
if (!useEslintrc) {
|
||||
return baseConfigArray;
|
||||
}
|
||||
|
||||
let configArray = configCache.get(directoryPath);
|
||||
|
||||
// Hit cache.
|
||||
if (configArray) {
|
||||
debug(`Cache hit: ${directoryPath}.`);
|
||||
return configArray;
|
||||
}
|
||||
debug(`No cache found: ${directoryPath}.`);
|
||||
|
||||
const homePath = os.homedir();
|
||||
|
||||
// Consider this is root.
|
||||
if (directoryPath === homePath && cwd !== homePath) {
|
||||
debug("Stop traversing because of considered root.");
|
||||
if (configsExistInSubdirs) {
|
||||
const filePath = ConfigArrayFactory.getPathToConfigFileInDirectory(directoryPath);
|
||||
|
||||
if (filePath) {
|
||||
emitDeprecationWarning(
|
||||
filePath,
|
||||
"ESLINT_PERSONAL_CONFIG_SUPPRESS"
|
||||
);
|
||||
}
|
||||
}
|
||||
return this._cacheConfig(directoryPath, baseConfigArray);
|
||||
}
|
||||
|
||||
// Load the config on this directory.
|
||||
try {
|
||||
configArray = configArrayFactory.loadInDirectory(directoryPath);
|
||||
} catch (error) {
|
||||
/* istanbul ignore next */
|
||||
if (error.code === "EACCES") {
|
||||
debug("Stop traversing because of 'EACCES' error.");
|
||||
return this._cacheConfig(directoryPath, baseConfigArray);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (configArray.length > 0 && configArray.isRoot()) {
|
||||
debug("Stop traversing because of 'root:true'.");
|
||||
configArray.unshift(...baseConfigArray);
|
||||
return this._cacheConfig(directoryPath, configArray);
|
||||
}
|
||||
|
||||
// Load from the ancestors and merge it.
|
||||
const parentPath = path.dirname(directoryPath);
|
||||
const parentConfigArray = parentPath && parentPath !== directoryPath
|
||||
? this._loadConfigInAncestors(
|
||||
parentPath,
|
||||
configsExistInSubdirs || configArray.length > 0
|
||||
)
|
||||
: baseConfigArray;
|
||||
|
||||
if (configArray.length > 0) {
|
||||
configArray.unshift(...parentConfigArray);
|
||||
} else {
|
||||
configArray = parentConfigArray;
|
||||
}
|
||||
|
||||
// Cache and return.
|
||||
return this._cacheConfig(directoryPath, configArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Freeze and cache a given config.
|
||||
* @param {string} directoryPath The path to a directory as a cache key.
|
||||
* @param {ConfigArray} configArray The config array as a cache value.
|
||||
* @returns {ConfigArray} The `configArray` (frozen).
|
||||
*/
|
||||
_cacheConfig(directoryPath, configArray) {
|
||||
const { configCache } = internalSlotsMap.get(this);
|
||||
|
||||
Object.freeze(configArray);
|
||||
configCache.set(directoryPath, configArray);
|
||||
|
||||
return configArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize a given config array.
|
||||
* Concatenate `--config` and other CLI options.
|
||||
* @param {ConfigArray} configArray The parent config array.
|
||||
* @param {string} directoryPath The path to the leaf directory to find config files.
|
||||
* @param {boolean} ignoreNotFoundError If `true` then it doesn't throw `ConfigurationNotFoundError`.
|
||||
* @returns {ConfigArray} The loaded config.
|
||||
* @private
|
||||
*/
|
||||
_finalizeConfigArray(configArray, directoryPath, ignoreNotFoundError) {
|
||||
const {
|
||||
cliConfigArray,
|
||||
configArrayFactory,
|
||||
finalizeCache,
|
||||
useEslintrc
|
||||
} = internalSlotsMap.get(this);
|
||||
|
||||
let finalConfigArray = finalizeCache.get(configArray);
|
||||
|
||||
if (!finalConfigArray) {
|
||||
finalConfigArray = configArray;
|
||||
|
||||
// Load the personal config if there are no regular config files.
|
||||
if (
|
||||
useEslintrc &&
|
||||
configArray.every(c => !c.filePath) &&
|
||||
cliConfigArray.every(c => !c.filePath) // `--config` option can be a file.
|
||||
) {
|
||||
const homePath = os.homedir();
|
||||
|
||||
debug("Loading the config file of the home directory:", homePath);
|
||||
|
||||
const personalConfigArray = configArrayFactory.loadInDirectory(
|
||||
homePath,
|
||||
{ name: "PersonalConfig" }
|
||||
);
|
||||
|
||||
if (
|
||||
personalConfigArray.length > 0 &&
|
||||
!directoryPath.startsWith(homePath)
|
||||
) {
|
||||
const lastElement =
|
||||
personalConfigArray[personalConfigArray.length - 1];
|
||||
|
||||
emitDeprecationWarning(
|
||||
lastElement.filePath,
|
||||
"ESLINT_PERSONAL_CONFIG_LOAD"
|
||||
);
|
||||
}
|
||||
|
||||
finalConfigArray = finalConfigArray.concat(personalConfigArray);
|
||||
}
|
||||
|
||||
// Apply CLI options.
|
||||
if (cliConfigArray.length > 0) {
|
||||
finalConfigArray = finalConfigArray.concat(cliConfigArray);
|
||||
}
|
||||
|
||||
// Validate rule settings and environments.
|
||||
validateConfigArray(finalConfigArray);
|
||||
|
||||
// Cache it.
|
||||
Object.freeze(finalConfigArray);
|
||||
finalizeCache.set(configArray, finalConfigArray);
|
||||
|
||||
debug(
|
||||
"Configuration was determined: %o on %s",
|
||||
finalConfigArray,
|
||||
directoryPath
|
||||
);
|
||||
}
|
||||
|
||||
// At least one element (the default ignore patterns) exists.
|
||||
if (!ignoreNotFoundError && useEslintrc && finalConfigArray.length <= 1) {
|
||||
throw new ConfigurationNotFoundError(directoryPath);
|
||||
}
|
||||
|
||||
return finalConfigArray;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = { CascadingConfigArrayFactory };
|
@ -19,14 +19,29 @@ const fs = require("fs");
|
||||
const path = require("path");
|
||||
const defaultOptions = require("../../conf/default-cli-options");
|
||||
const pkg = require("../../package.json");
|
||||
const ConfigOps = require("../shared/config-ops");
|
||||
const naming = require("../shared/naming");
|
||||
const ModuleResolver = require("../shared/relative-module-resolver");
|
||||
|
||||
|
||||
const {
|
||||
Legacy: {
|
||||
ConfigOps,
|
||||
naming,
|
||||
CascadingConfigArrayFactory,
|
||||
IgnorePattern,
|
||||
getUsedExtractedConfigs
|
||||
}
|
||||
} = require("@eslint/eslintrc");
|
||||
|
||||
/*
|
||||
* For some reason, ModuleResolver must be included via filepath instead of by
|
||||
* API exports in order to work properly. That's why this is separated out onto
|
||||
* its own require() statement.
|
||||
*/
|
||||
const ModuleResolver = require("@eslint/eslintrc/lib/shared/relative-module-resolver");
|
||||
const { FileEnumerator } = require("./file-enumerator");
|
||||
|
||||
const { Linter } = require("../linter");
|
||||
const builtInRules = require("../rules");
|
||||
const { CascadingConfigArrayFactory } = require("./cascading-config-array-factory");
|
||||
const { IgnorePattern, getUsedExtractedConfigs } = require("./config-array");
|
||||
const { FileEnumerator } = require("./file-enumerator");
|
||||
const loadRules = require("./load-rules");
|
||||
const hash = require("./hash");
|
||||
const LintResultCache = require("./lint-result-cache");
|
||||
|
||||
@ -559,7 +574,11 @@ class CLIEngine {
|
||||
resolvePluginsRelativeTo: options.resolvePluginsRelativeTo,
|
||||
rulePaths: options.rulePaths,
|
||||
specificConfigPath: options.configFile,
|
||||
useEslintrc: options.useEslintrc
|
||||
useEslintrc: options.useEslintrc,
|
||||
builtInRules,
|
||||
loadRules,
|
||||
eslintRecommendedPath: path.resolve(__dirname, "../../conf/eslint-recommended.js"),
|
||||
eslintAllPath: path.resolve(__dirname, "../../conf/eslint-all.js")
|
||||
});
|
||||
const fileEnumerator = new FileEnumerator({
|
||||
configArrayFactory,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,524 +0,0 @@
|
||||
/**
|
||||
* @fileoverview `ConfigArray` class.
|
||||
*
|
||||
* `ConfigArray` class expresses the full of a configuration. It has the entry
|
||||
* config file, base config files that were extended, loaded parsers, and loaded
|
||||
* plugins.
|
||||
*
|
||||
* `ConfigArray` class provides three properties and two methods.
|
||||
*
|
||||
* - `pluginEnvironments`
|
||||
* - `pluginProcessors`
|
||||
* - `pluginRules`
|
||||
* The `Map` objects that contain the members of all plugins that this
|
||||
* config array contains. Those map objects don't have mutation methods.
|
||||
* Those keys are the member ID such as `pluginId/memberName`.
|
||||
* - `isRoot()`
|
||||
* If `true` then this configuration has `root:true` property.
|
||||
* - `extractConfig(filePath)`
|
||||
* Extract the final configuration for a given file. This means merging
|
||||
* every config array element which that `criteria` property matched. The
|
||||
* `filePath` argument must be an absolute path.
|
||||
*
|
||||
* `ConfigArrayFactory` provides the loading logic of config files.
|
||||
*
|
||||
* @author Toru Nagashima <https://github.com/mysticatea>
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const { ExtractedConfig } = require("./extracted-config");
|
||||
const { IgnorePattern } = require("./ignore-pattern");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Define types for VSCode IntelliSense.
|
||||
/** @typedef {import("../../shared/types").Environment} Environment */
|
||||
/** @typedef {import("../../shared/types").GlobalConf} GlobalConf */
|
||||
/** @typedef {import("../../shared/types").RuleConf} RuleConf */
|
||||
/** @typedef {import("../../shared/types").Rule} Rule */
|
||||
/** @typedef {import("../../shared/types").Plugin} Plugin */
|
||||
/** @typedef {import("../../shared/types").Processor} Processor */
|
||||
/** @typedef {import("./config-dependency").DependentParser} DependentParser */
|
||||
/** @typedef {import("./config-dependency").DependentPlugin} DependentPlugin */
|
||||
/** @typedef {import("./override-tester")["OverrideTester"]} OverrideTester */
|
||||
|
||||
/**
|
||||
* @typedef {Object} ConfigArrayElement
|
||||
* @property {string} name The name of this config element.
|
||||
* @property {string} filePath The path to the source file of this config element.
|
||||
* @property {InstanceType<OverrideTester>|null} criteria The tester for the `files` and `excludedFiles` of this config element.
|
||||
* @property {Record<string, boolean>|undefined} env The environment settings.
|
||||
* @property {Record<string, GlobalConf>|undefined} globals The global variable settings.
|
||||
* @property {IgnorePattern|undefined} ignorePattern The ignore patterns.
|
||||
* @property {boolean|undefined} noInlineConfig The flag that disables directive comments.
|
||||
* @property {DependentParser|undefined} parser The parser loader.
|
||||
* @property {Object|undefined} parserOptions The parser options.
|
||||
* @property {Record<string, DependentPlugin>|undefined} plugins The plugin loaders.
|
||||
* @property {string|undefined} processor The processor name to refer plugin's processor.
|
||||
* @property {boolean|undefined} reportUnusedDisableDirectives The flag to report unused `eslint-disable` comments.
|
||||
* @property {boolean|undefined} root The flag to express root.
|
||||
* @property {Record<string, RuleConf>|undefined} rules The rule settings
|
||||
* @property {Object|undefined} settings The shared settings.
|
||||
* @property {"config" | "ignore" | "implicit-processor"} type The element type.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ConfigArrayInternalSlots
|
||||
* @property {Map<string, ExtractedConfig>} cache The cache to extract configs.
|
||||
* @property {ReadonlyMap<string, Environment>|null} envMap The map from environment ID to environment definition.
|
||||
* @property {ReadonlyMap<string, Processor>|null} processorMap The map from processor ID to environment definition.
|
||||
* @property {ReadonlyMap<string, Rule>|null} ruleMap The map from rule ID to rule definition.
|
||||
*/
|
||||
|
||||
/** @type {WeakMap<ConfigArray, ConfigArrayInternalSlots>} */
|
||||
const internalSlotsMap = new class extends WeakMap {
|
||||
get(key) {
|
||||
let value = super.get(key);
|
||||
|
||||
if (!value) {
|
||||
value = {
|
||||
cache: new Map(),
|
||||
envMap: null,
|
||||
processorMap: null,
|
||||
ruleMap: null
|
||||
};
|
||||
super.set(key, value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}();
|
||||
|
||||
/**
|
||||
* Get the indices which are matched to a given file.
|
||||
* @param {ConfigArrayElement[]} elements The elements.
|
||||
* @param {string} filePath The path to a target file.
|
||||
* @returns {number[]} The indices.
|
||||
*/
|
||||
function getMatchedIndices(elements, filePath) {
|
||||
const indices = [];
|
||||
|
||||
for (let i = elements.length - 1; i >= 0; --i) {
|
||||
const element = elements[i];
|
||||
|
||||
if (!element.criteria || (filePath && element.criteria.test(filePath))) {
|
||||
indices.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a value is a non-null object.
|
||||
* @param {any} x The value to check.
|
||||
* @returns {boolean} `true` if the value is a non-null object.
|
||||
*/
|
||||
function isNonNullObject(x) {
|
||||
return typeof x === "object" && x !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two objects.
|
||||
*
|
||||
* Assign every property values of `y` to `x` if `x` doesn't have the property.
|
||||
* If `x`'s property value is an object, it does recursive.
|
||||
* @param {Object} target The destination to merge
|
||||
* @param {Object|undefined} source The source to merge.
|
||||
* @returns {void}
|
||||
*/
|
||||
function mergeWithoutOverwrite(target, source) {
|
||||
if (!isNonNullObject(source)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const key of Object.keys(source)) {
|
||||
if (key === "__proto__") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isNonNullObject(target[key])) {
|
||||
mergeWithoutOverwrite(target[key], source[key]);
|
||||
} else if (target[key] === void 0) {
|
||||
if (isNonNullObject(source[key])) {
|
||||
target[key] = Array.isArray(source[key]) ? [] : {};
|
||||
mergeWithoutOverwrite(target[key], source[key]);
|
||||
} else if (source[key] !== void 0) {
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The error for plugin conflicts.
|
||||
*/
|
||||
class PluginConflictError extends Error {
|
||||
|
||||
/**
|
||||
* Initialize this error object.
|
||||
* @param {string} pluginId The plugin ID.
|
||||
* @param {{filePath:string, importerName:string}[]} plugins The resolved plugins.
|
||||
*/
|
||||
constructor(pluginId, plugins) {
|
||||
super(`Plugin "${pluginId}" was conflicted between ${plugins.map(p => `"${p.importerName}"`).join(" and ")}.`);
|
||||
this.messageTemplate = "plugin-conflict";
|
||||
this.messageData = { pluginId, plugins };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge plugins.
|
||||
* `target`'s definition is prior to `source`'s.
|
||||
* @param {Record<string, DependentPlugin>} target The destination to merge
|
||||
* @param {Record<string, DependentPlugin>|undefined} source The source to merge.
|
||||
* @returns {void}
|
||||
*/
|
||||
function mergePlugins(target, source) {
|
||||
if (!isNonNullObject(source)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const key of Object.keys(source)) {
|
||||
if (key === "__proto__") {
|
||||
continue;
|
||||
}
|
||||
const targetValue = target[key];
|
||||
const sourceValue = source[key];
|
||||
|
||||
// Adopt the plugin which was found at first.
|
||||
if (targetValue === void 0) {
|
||||
if (sourceValue.error) {
|
||||
throw sourceValue.error;
|
||||
}
|
||||
target[key] = sourceValue;
|
||||
} else if (sourceValue.filePath !== targetValue.filePath) {
|
||||
throw new PluginConflictError(key, [
|
||||
{
|
||||
filePath: targetValue.filePath,
|
||||
importerName: targetValue.importerName
|
||||
},
|
||||
{
|
||||
filePath: sourceValue.filePath,
|
||||
importerName: sourceValue.importerName
|
||||
}
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge rule configs.
|
||||
* `target`'s definition is prior to `source`'s.
|
||||
* @param {Record<string, Array>} target The destination to merge
|
||||
* @param {Record<string, RuleConf>|undefined} source The source to merge.
|
||||
* @returns {void}
|
||||
*/
|
||||
function mergeRuleConfigs(target, source) {
|
||||
if (!isNonNullObject(source)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const key of Object.keys(source)) {
|
||||
if (key === "__proto__") {
|
||||
continue;
|
||||
}
|
||||
const targetDef = target[key];
|
||||
const sourceDef = source[key];
|
||||
|
||||
// Adopt the rule config which was found at first.
|
||||
if (targetDef === void 0) {
|
||||
if (Array.isArray(sourceDef)) {
|
||||
target[key] = [...sourceDef];
|
||||
} else {
|
||||
target[key] = [sourceDef];
|
||||
}
|
||||
|
||||
/*
|
||||
* If the first found rule config is severity only and the current rule
|
||||
* config has options, merge the severity and the options.
|
||||
*/
|
||||
} else if (
|
||||
targetDef.length === 1 &&
|
||||
Array.isArray(sourceDef) &&
|
||||
sourceDef.length >= 2
|
||||
) {
|
||||
targetDef.push(...sourceDef.slice(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the extracted config.
|
||||
* @param {ConfigArray} instance The config elements.
|
||||
* @param {number[]} indices The indices to use.
|
||||
* @returns {ExtractedConfig} The extracted config.
|
||||
*/
|
||||
function createConfig(instance, indices) {
|
||||
const config = new ExtractedConfig();
|
||||
const ignorePatterns = [];
|
||||
|
||||
// Merge elements.
|
||||
for (const index of indices) {
|
||||
const element = instance[index];
|
||||
|
||||
// Adopt the parser which was found at first.
|
||||
if (!config.parser && element.parser) {
|
||||
if (element.parser.error) {
|
||||
throw element.parser.error;
|
||||
}
|
||||
config.parser = element.parser;
|
||||
}
|
||||
|
||||
// Adopt the processor which was found at first.
|
||||
if (!config.processor && element.processor) {
|
||||
config.processor = element.processor;
|
||||
}
|
||||
|
||||
// Adopt the noInlineConfig which was found at first.
|
||||
if (config.noInlineConfig === void 0 && element.noInlineConfig !== void 0) {
|
||||
config.noInlineConfig = element.noInlineConfig;
|
||||
config.configNameOfNoInlineConfig = element.name;
|
||||
}
|
||||
|
||||
// Adopt the reportUnusedDisableDirectives which was found at first.
|
||||
if (config.reportUnusedDisableDirectives === void 0 && element.reportUnusedDisableDirectives !== void 0) {
|
||||
config.reportUnusedDisableDirectives = element.reportUnusedDisableDirectives;
|
||||
}
|
||||
|
||||
// Collect ignorePatterns
|
||||
if (element.ignorePattern) {
|
||||
ignorePatterns.push(element.ignorePattern);
|
||||
}
|
||||
|
||||
// Merge others.
|
||||
mergeWithoutOverwrite(config.env, element.env);
|
||||
mergeWithoutOverwrite(config.globals, element.globals);
|
||||
mergeWithoutOverwrite(config.parserOptions, element.parserOptions);
|
||||
mergeWithoutOverwrite(config.settings, element.settings);
|
||||
mergePlugins(config.plugins, element.plugins);
|
||||
mergeRuleConfigs(config.rules, element.rules);
|
||||
}
|
||||
|
||||
// Create the predicate function for ignore patterns.
|
||||
if (ignorePatterns.length > 0) {
|
||||
config.ignores = IgnorePattern.createIgnore(ignorePatterns.reverse());
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect definitions.
|
||||
* @template T, U
|
||||
* @param {string} pluginId The plugin ID for prefix.
|
||||
* @param {Record<string,T>} defs The definitions to collect.
|
||||
* @param {Map<string, U>} map The map to output.
|
||||
* @param {function(T): U} [normalize] The normalize function for each value.
|
||||
* @returns {void}
|
||||
*/
|
||||
function collect(pluginId, defs, map, normalize) {
|
||||
if (defs) {
|
||||
const prefix = pluginId && `${pluginId}/`;
|
||||
|
||||
for (const [key, value] of Object.entries(defs)) {
|
||||
map.set(
|
||||
`${prefix}${key}`,
|
||||
normalize ? normalize(value) : value
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a rule definition.
|
||||
* @param {Function|Rule} rule The rule definition to normalize.
|
||||
* @returns {Rule} The normalized rule definition.
|
||||
*/
|
||||
function normalizePluginRule(rule) {
|
||||
return typeof rule === "function" ? { create: rule } : rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the mutation methods from a given map.
|
||||
* @param {Map<any, any>} map The map object to delete.
|
||||
* @returns {void}
|
||||
*/
|
||||
function deleteMutationMethods(map) {
|
||||
Object.defineProperties(map, {
|
||||
clear: { configurable: true, value: void 0 },
|
||||
delete: { configurable: true, value: void 0 },
|
||||
set: { configurable: true, value: void 0 }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create `envMap`, `processorMap`, `ruleMap` with the plugins in the config array.
|
||||
* @param {ConfigArrayElement[]} elements The config elements.
|
||||
* @param {ConfigArrayInternalSlots} slots The internal slots.
|
||||
* @returns {void}
|
||||
*/
|
||||
function initPluginMemberMaps(elements, slots) {
|
||||
const processed = new Set();
|
||||
|
||||
slots.envMap = new Map();
|
||||
slots.processorMap = new Map();
|
||||
slots.ruleMap = new Map();
|
||||
|
||||
for (const element of elements) {
|
||||
if (!element.plugins) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const [pluginId, value] of Object.entries(element.plugins)) {
|
||||
const plugin = value.definition;
|
||||
|
||||
if (!plugin || processed.has(pluginId)) {
|
||||
continue;
|
||||
}
|
||||
processed.add(pluginId);
|
||||
|
||||
collect(pluginId, plugin.environments, slots.envMap);
|
||||
collect(pluginId, plugin.processors, slots.processorMap);
|
||||
collect(pluginId, plugin.rules, slots.ruleMap, normalizePluginRule);
|
||||
}
|
||||
}
|
||||
|
||||
deleteMutationMethods(slots.envMap);
|
||||
deleteMutationMethods(slots.processorMap);
|
||||
deleteMutationMethods(slots.ruleMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create `envMap`, `processorMap`, `ruleMap` with the plugins in the config array.
|
||||
* @param {ConfigArray} instance The config elements.
|
||||
* @returns {ConfigArrayInternalSlots} The extracted config.
|
||||
*/
|
||||
function ensurePluginMemberMaps(instance) {
|
||||
const slots = internalSlotsMap.get(instance);
|
||||
|
||||
if (!slots.ruleMap) {
|
||||
initPluginMemberMaps(instance, slots);
|
||||
}
|
||||
|
||||
return slots;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The Config Array.
|
||||
*
|
||||
* `ConfigArray` instance contains all settings, parsers, and plugins.
|
||||
* You need to call `ConfigArray#extractConfig(filePath)` method in order to
|
||||
* extract, merge and get only the config data which is related to an arbitrary
|
||||
* file.
|
||||
* @extends {Array<ConfigArrayElement>}
|
||||
*/
|
||||
class ConfigArray extends Array {
|
||||
|
||||
/**
|
||||
* Get the plugin environments.
|
||||
* The returned map cannot be mutated.
|
||||
* @type {ReadonlyMap<string, Environment>} The plugin environments.
|
||||
*/
|
||||
get pluginEnvironments() {
|
||||
return ensurePluginMemberMaps(this).envMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin processors.
|
||||
* The returned map cannot be mutated.
|
||||
* @type {ReadonlyMap<string, Processor>} The plugin processors.
|
||||
*/
|
||||
get pluginProcessors() {
|
||||
return ensurePluginMemberMaps(this).processorMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin rules.
|
||||
* The returned map cannot be mutated.
|
||||
* @returns {ReadonlyMap<string, Rule>} The plugin rules.
|
||||
*/
|
||||
get pluginRules() {
|
||||
return ensurePluginMemberMaps(this).ruleMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this config has `root` flag.
|
||||
* @returns {boolean} `true` if this config array is root.
|
||||
*/
|
||||
isRoot() {
|
||||
for (let i = this.length - 1; i >= 0; --i) {
|
||||
const root = this[i].root;
|
||||
|
||||
if (typeof root === "boolean") {
|
||||
return root;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the config data which is related to a given file.
|
||||
* @param {string} filePath The absolute path to the target file.
|
||||
* @returns {ExtractedConfig} The extracted config data.
|
||||
*/
|
||||
extractConfig(filePath) {
|
||||
const { cache } = internalSlotsMap.get(this);
|
||||
const indices = getMatchedIndices(this, filePath);
|
||||
const cacheKey = indices.join(",");
|
||||
|
||||
if (!cache.has(cacheKey)) {
|
||||
cache.set(cacheKey, createConfig(this, indices));
|
||||
}
|
||||
|
||||
return cache.get(cacheKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given path is an additional lint target.
|
||||
* @param {string} filePath The absolute path to the target file.
|
||||
* @returns {boolean} `true` if the file is an additional lint target.
|
||||
*/
|
||||
isAdditionalTargetPath(filePath) {
|
||||
for (const { criteria, type } of this) {
|
||||
if (
|
||||
type === "config" &&
|
||||
criteria &&
|
||||
!criteria.endsWithWildcard &&
|
||||
criteria.test(filePath)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const exportObject = {
|
||||
ConfigArray,
|
||||
|
||||
/**
|
||||
* Get the used extracted configs.
|
||||
* CLIEngine will use this method to collect used deprecated rules.
|
||||
* @param {ConfigArray} instance The config array object to get.
|
||||
* @returns {ExtractedConfig[]} The used extracted configs.
|
||||
* @private
|
||||
*/
|
||||
getUsedExtractedConfigs(instance) {
|
||||
const { cache } = internalSlotsMap.get(instance);
|
||||
|
||||
return Array.from(cache.values());
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = exportObject;
|
@ -1,116 +0,0 @@
|
||||
/**
|
||||
* @fileoverview `ConfigDependency` class.
|
||||
*
|
||||
* `ConfigDependency` class expresses a loaded parser or plugin.
|
||||
*
|
||||
* If the parser or plugin was loaded successfully, it has `definition` property
|
||||
* and `filePath` property. Otherwise, it has `error` property.
|
||||
*
|
||||
* When `JSON.stringify()` converted a `ConfigDependency` object to a JSON, it
|
||||
* omits `definition` property.
|
||||
*
|
||||
* `ConfigArrayFactory` creates `ConfigDependency` objects when it loads parsers
|
||||
* or plugins.
|
||||
*
|
||||
* @author Toru Nagashima <https://github.com/mysticatea>
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const util = require("util");
|
||||
|
||||
/**
|
||||
* The class is to store parsers or plugins.
|
||||
* This class hides the loaded object from `JSON.stringify()` and `console.log`.
|
||||
* @template T
|
||||
*/
|
||||
class ConfigDependency {
|
||||
|
||||
/**
|
||||
* Initialize this instance.
|
||||
* @param {Object} data The dependency data.
|
||||
* @param {T} [data.definition] The dependency if the loading succeeded.
|
||||
* @param {Error} [data.error] The error object if the loading failed.
|
||||
* @param {string} [data.filePath] The actual path to the dependency if the loading succeeded.
|
||||
* @param {string} data.id The ID of this dependency.
|
||||
* @param {string} data.importerName The name of the config file which loads this dependency.
|
||||
* @param {string} data.importerPath The path to the config file which loads this dependency.
|
||||
*/
|
||||
constructor({
|
||||
definition = null,
|
||||
error = null,
|
||||
filePath = null,
|
||||
id,
|
||||
importerName,
|
||||
importerPath
|
||||
}) {
|
||||
|
||||
/**
|
||||
* The loaded dependency if the loading succeeded.
|
||||
* @type {T|null}
|
||||
*/
|
||||
this.definition = definition;
|
||||
|
||||
/**
|
||||
* The error object if the loading failed.
|
||||
* @type {Error|null}
|
||||
*/
|
||||
this.error = error;
|
||||
|
||||
/**
|
||||
* The loaded dependency if the loading succeeded.
|
||||
* @type {string|null}
|
||||
*/
|
||||
this.filePath = filePath;
|
||||
|
||||
/**
|
||||
* The ID of this dependency.
|
||||
* @type {string}
|
||||
*/
|
||||
this.id = id;
|
||||
|
||||
/**
|
||||
* The name of the config file which loads this dependency.
|
||||
* @type {string}
|
||||
*/
|
||||
this.importerName = importerName;
|
||||
|
||||
/**
|
||||
* The path to the config file which loads this dependency.
|
||||
* @type {string}
|
||||
*/
|
||||
this.importerPath = importerPath;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-description
|
||||
/**
|
||||
* @returns {Object} a JSON compatible object.
|
||||
*/
|
||||
toJSON() {
|
||||
const obj = this[util.inspect.custom]();
|
||||
|
||||
// Display `error.message` (`Error#message` is unenumerable).
|
||||
if (obj.error instanceof Error) {
|
||||
obj.error = { ...obj.error, message: obj.error.message };
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-description
|
||||
/**
|
||||
* @returns {Object} an object to display by `console.log()`.
|
||||
*/
|
||||
[util.inspect.custom]() {
|
||||
const {
|
||||
definition: _ignore, // eslint-disable-line no-unused-vars
|
||||
...obj
|
||||
} = this;
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
/** @typedef {ConfigDependency<import("../../shared/types").Parser>} DependentParser */
|
||||
/** @typedef {ConfigDependency<import("../../shared/types").Plugin>} DependentPlugin */
|
||||
|
||||
module.exports = { ConfigDependency };
|
@ -1,146 +0,0 @@
|
||||
/**
|
||||
* @fileoverview `ExtractedConfig` class.
|
||||
*
|
||||
* `ExtractedConfig` class expresses a final configuration for a specific file.
|
||||
*
|
||||
* It provides one method.
|
||||
*
|
||||
* - `toCompatibleObjectAsConfigFileContent()`
|
||||
* Convert this configuration to the compatible object as the content of
|
||||
* config files. It converts the loaded parser and plugins to strings.
|
||||
* `CLIEngine#getConfigForFile(filePath)` method uses this method.
|
||||
*
|
||||
* `ConfigArray#extractConfig(filePath)` creates a `ExtractedConfig` instance.
|
||||
*
|
||||
* @author Toru Nagashima <https://github.com/mysticatea>
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const { IgnorePattern } = require("./ignore-pattern");
|
||||
|
||||
// For VSCode intellisense
|
||||
/** @typedef {import("../../shared/types").ConfigData} ConfigData */
|
||||
/** @typedef {import("../../shared/types").GlobalConf} GlobalConf */
|
||||
/** @typedef {import("../../shared/types").SeverityConf} SeverityConf */
|
||||
/** @typedef {import("./config-dependency").DependentParser} DependentParser */
|
||||
/** @typedef {import("./config-dependency").DependentPlugin} DependentPlugin */
|
||||
|
||||
/**
|
||||
* Check if `xs` starts with `ys`.
|
||||
* @template T
|
||||
* @param {T[]} xs The array to check.
|
||||
* @param {T[]} ys The array that may be the first part of `xs`.
|
||||
* @returns {boolean} `true` if `xs` starts with `ys`.
|
||||
*/
|
||||
function startsWith(xs, ys) {
|
||||
return xs.length >= ys.length && ys.every((y, i) => y === xs[i]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The class for extracted config data.
|
||||
*/
|
||||
class ExtractedConfig {
|
||||
constructor() {
|
||||
|
||||
/**
|
||||
* The config name what `noInlineConfig` setting came from.
|
||||
* @type {string}
|
||||
*/
|
||||
this.configNameOfNoInlineConfig = "";
|
||||
|
||||
/**
|
||||
* Environments.
|
||||
* @type {Record<string, boolean>}
|
||||
*/
|
||||
this.env = {};
|
||||
|
||||
/**
|
||||
* Global variables.
|
||||
* @type {Record<string, GlobalConf>}
|
||||
*/
|
||||
this.globals = {};
|
||||
|
||||
/**
|
||||
* The glob patterns that ignore to lint.
|
||||
* @type {(((filePath:string, dot?:boolean) => boolean) & { basePath:string; patterns:string[] }) | undefined}
|
||||
*/
|
||||
this.ignores = void 0;
|
||||
|
||||
/**
|
||||
* The flag that disables directive comments.
|
||||
* @type {boolean|undefined}
|
||||
*/
|
||||
this.noInlineConfig = void 0;
|
||||
|
||||
/**
|
||||
* Parser definition.
|
||||
* @type {DependentParser|null}
|
||||
*/
|
||||
this.parser = null;
|
||||
|
||||
/**
|
||||
* Options for the parser.
|
||||
* @type {Object}
|
||||
*/
|
||||
this.parserOptions = {};
|
||||
|
||||
/**
|
||||
* Plugin definitions.
|
||||
* @type {Record<string, DependentPlugin>}
|
||||
*/
|
||||
this.plugins = {};
|
||||
|
||||
/**
|
||||
* Processor ID.
|
||||
* @type {string|null}
|
||||
*/
|
||||
this.processor = null;
|
||||
|
||||
/**
|
||||
* The flag that reports unused `eslint-disable` directive comments.
|
||||
* @type {boolean|undefined}
|
||||
*/
|
||||
this.reportUnusedDisableDirectives = void 0;
|
||||
|
||||
/**
|
||||
* Rule settings.
|
||||
* @type {Record<string, [SeverityConf, ...any[]]>}
|
||||
*/
|
||||
this.rules = {};
|
||||
|
||||
/**
|
||||
* Shared settings.
|
||||
* @type {Object}
|
||||
*/
|
||||
this.settings = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert this config to the compatible object as a config file content.
|
||||
* @returns {ConfigData} The converted object.
|
||||
*/
|
||||
toCompatibleObjectAsConfigFileContent() {
|
||||
const {
|
||||
/* eslint-disable no-unused-vars */
|
||||
configNameOfNoInlineConfig: _ignore1,
|
||||
processor: _ignore2,
|
||||
/* eslint-enable no-unused-vars */
|
||||
ignores,
|
||||
...config
|
||||
} = this;
|
||||
|
||||
config.parser = config.parser && config.parser.filePath;
|
||||
config.plugins = Object.keys(config.plugins).filter(Boolean).reverse();
|
||||
config.ignorePatterns = ignores ? ignores.patterns : [];
|
||||
|
||||
// Strip the default patterns from `ignorePatterns`.
|
||||
if (startsWith(config.ignorePatterns, IgnorePattern.DefaultPatterns)) {
|
||||
config.ignorePatterns =
|
||||
config.ignorePatterns.slice(IgnorePattern.DefaultPatterns.length);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { ExtractedConfig };
|
@ -1,237 +0,0 @@
|
||||
/**
|
||||
* @fileoverview `IgnorePattern` class.
|
||||
*
|
||||
* `IgnorePattern` class has the set of glob patterns and the base path.
|
||||
*
|
||||
* It provides two static methods.
|
||||
*
|
||||
* - `IgnorePattern.createDefaultIgnore(cwd)`
|
||||
* Create the default predicate function.
|
||||
* - `IgnorePattern.createIgnore(ignorePatterns)`
|
||||
* Create the predicate function from multiple `IgnorePattern` objects.
|
||||
*
|
||||
* It provides two properties and a method.
|
||||
*
|
||||
* - `patterns`
|
||||
* The glob patterns that ignore to lint.
|
||||
* - `basePath`
|
||||
* The base path of the glob patterns. If absolute paths existed in the
|
||||
* glob patterns, those are handled as relative paths to the base path.
|
||||
* - `getPatternsRelativeTo(basePath)`
|
||||
* Get `patterns` as modified for a given base path. It modifies the
|
||||
* absolute paths in the patterns as prepending the difference of two base
|
||||
* paths.
|
||||
*
|
||||
* `ConfigArrayFactory` creates `IgnorePattern` objects when it processes
|
||||
* `ignorePatterns` properties.
|
||||
*
|
||||
* @author Toru Nagashima <https://github.com/mysticatea>
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const assert = require("assert");
|
||||
const path = require("path");
|
||||
const ignore = require("ignore");
|
||||
const debug = require("debug")("eslint:ignore-pattern");
|
||||
|
||||
/** @typedef {ReturnType<import("ignore").default>} Ignore */
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get the path to the common ancestor directory of given paths.
|
||||
* @param {string[]} sourcePaths The paths to calculate the common ancestor.
|
||||
* @returns {string} The path to the common ancestor directory.
|
||||
*/
|
||||
function getCommonAncestorPath(sourcePaths) {
|
||||
let result = sourcePaths[0];
|
||||
|
||||
for (let i = 1; i < sourcePaths.length; ++i) {
|
||||
const a = result;
|
||||
const b = sourcePaths[i];
|
||||
|
||||
// Set the shorter one (it's the common ancestor if one includes the other).
|
||||
result = a.length < b.length ? a : b;
|
||||
|
||||
// Set the common ancestor.
|
||||
for (let j = 0, lastSepPos = 0; j < a.length && j < b.length; ++j) {
|
||||
if (a[j] !== b[j]) {
|
||||
result = a.slice(0, lastSepPos);
|
||||
break;
|
||||
}
|
||||
if (a[j] === path.sep) {
|
||||
lastSepPos = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let resolvedResult = result || path.sep;
|
||||
|
||||
// if Windows common ancestor is root of drive must have trailing slash to be absolute.
|
||||
if (resolvedResult && resolvedResult.endsWith(":") && process.platform === "win32") {
|
||||
resolvedResult += path.sep;
|
||||
}
|
||||
return resolvedResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make relative path.
|
||||
* @param {string} from The source path to get relative path.
|
||||
* @param {string} to The destination path to get relative path.
|
||||
* @returns {string} The relative path.
|
||||
*/
|
||||
function relative(from, to) {
|
||||
const relPath = path.relative(from, to);
|
||||
|
||||
if (path.sep === "/") {
|
||||
return relPath;
|
||||
}
|
||||
return relPath.split(path.sep).join("/");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the trailing slash if existed.
|
||||
* @param {string} filePath The path to check.
|
||||
* @returns {string} The trailing slash if existed.
|
||||
*/
|
||||
function dirSuffix(filePath) {
|
||||
const isDir = (
|
||||
filePath.endsWith(path.sep) ||
|
||||
(process.platform === "win32" && filePath.endsWith("/"))
|
||||
);
|
||||
|
||||
return isDir ? "/" : "";
|
||||
}
|
||||
|
||||
const DefaultPatterns = Object.freeze(["/**/node_modules/*"]);
|
||||
const DotPatterns = Object.freeze([".*", "!.eslintrc.*", "!../"]);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class IgnorePattern {
|
||||
|
||||
/**
|
||||
* The default patterns.
|
||||
* @type {string[]}
|
||||
*/
|
||||
static get DefaultPatterns() {
|
||||
return DefaultPatterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the default predicate function.
|
||||
* @param {string} cwd The current working directory.
|
||||
* @returns {((filePath:string, dot:boolean) => boolean) & {basePath:string; patterns:string[]}}
|
||||
* The preficate function.
|
||||
* The first argument is an absolute path that is checked.
|
||||
* The second argument is the flag to not ignore dotfiles.
|
||||
* If the predicate function returned `true`, it means the path should be ignored.
|
||||
*/
|
||||
static createDefaultIgnore(cwd) {
|
||||
return this.createIgnore([new IgnorePattern(DefaultPatterns, cwd)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the predicate function from multiple `IgnorePattern` objects.
|
||||
* @param {IgnorePattern[]} ignorePatterns The list of ignore patterns.
|
||||
* @returns {((filePath:string, dot?:boolean) => boolean) & {basePath:string; patterns:string[]}}
|
||||
* The preficate function.
|
||||
* The first argument is an absolute path that is checked.
|
||||
* The second argument is the flag to not ignore dotfiles.
|
||||
* If the predicate function returned `true`, it means the path should be ignored.
|
||||
*/
|
||||
static createIgnore(ignorePatterns) {
|
||||
debug("Create with: %o", ignorePatterns);
|
||||
|
||||
const basePath = getCommonAncestorPath(ignorePatterns.map(p => p.basePath));
|
||||
const patterns = [].concat(
|
||||
...ignorePatterns.map(p => p.getPatternsRelativeTo(basePath))
|
||||
);
|
||||
const ig = ignore().add([...DotPatterns, ...patterns]);
|
||||
const dotIg = ignore().add(patterns);
|
||||
|
||||
debug(" processed: %o", { basePath, patterns });
|
||||
|
||||
return Object.assign(
|
||||
(filePath, dot = false) => {
|
||||
assert(path.isAbsolute(filePath), "'filePath' should be an absolute path.");
|
||||
const relPathRaw = relative(basePath, filePath);
|
||||
const relPath = relPathRaw && (relPathRaw + dirSuffix(filePath));
|
||||
const adoptedIg = dot ? dotIg : ig;
|
||||
const result = relPath !== "" && adoptedIg.ignores(relPath);
|
||||
|
||||
debug("Check", { filePath, dot, relativePath: relPath, result });
|
||||
return result;
|
||||
},
|
||||
{ basePath, patterns }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a new `IgnorePattern` instance.
|
||||
* @param {string[]} patterns The glob patterns that ignore to lint.
|
||||
* @param {string} basePath The base path of `patterns`.
|
||||
*/
|
||||
constructor(patterns, basePath) {
|
||||
assert(path.isAbsolute(basePath), "'basePath' should be an absolute path.");
|
||||
|
||||
/**
|
||||
* The glob patterns that ignore to lint.
|
||||
* @type {string[]}
|
||||
*/
|
||||
this.patterns = patterns;
|
||||
|
||||
/**
|
||||
* The base path of `patterns`.
|
||||
* @type {string}
|
||||
*/
|
||||
this.basePath = basePath;
|
||||
|
||||
/**
|
||||
* If `true` then patterns which don't start with `/` will match the paths to the outside of `basePath`. Defaults to `false`.
|
||||
*
|
||||
* It's set `true` for `.eslintignore`, `package.json`, and `--ignore-path` for backward compatibility.
|
||||
* It's `false` as-is for `ignorePatterns` property in config files.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.loose = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get `patterns` as modified for a given base path. It modifies the
|
||||
* absolute paths in the patterns as prepending the difference of two base
|
||||
* paths.
|
||||
* @param {string} newBasePath The base path.
|
||||
* @returns {string[]} Modifired patterns.
|
||||
*/
|
||||
getPatternsRelativeTo(newBasePath) {
|
||||
assert(path.isAbsolute(newBasePath), "'newBasePath' should be an absolute path.");
|
||||
const { basePath, loose, patterns } = this;
|
||||
|
||||
if (newBasePath === basePath) {
|
||||
return patterns;
|
||||
}
|
||||
const prefix = `/${relative(newBasePath, basePath)}`;
|
||||
|
||||
return patterns.map(pattern => {
|
||||
const negative = pattern.startsWith("!");
|
||||
const head = negative ? "!" : "";
|
||||
const body = negative ? pattern.slice(1) : pattern;
|
||||
|
||||
if (body.startsWith("/") || body.startsWith("../")) {
|
||||
return `${head}${prefix}${body}`;
|
||||
}
|
||||
return loose ? pattern : `${head}${prefix}/**/${body}`;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { IgnorePattern };
|
@ -1,20 +0,0 @@
|
||||
/**
|
||||
* @fileoverview `ConfigArray` class.
|
||||
* @author Toru Nagashima <https://github.com/mysticatea>
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const { ConfigArray, getUsedExtractedConfigs } = require("./config-array");
|
||||
const { ConfigDependency } = require("./config-dependency");
|
||||
const { ExtractedConfig } = require("./extracted-config");
|
||||
const { IgnorePattern } = require("./ignore-pattern");
|
||||
const { OverrideTester } = require("./override-tester");
|
||||
|
||||
module.exports = {
|
||||
ConfigArray,
|
||||
ConfigDependency,
|
||||
ExtractedConfig,
|
||||
IgnorePattern,
|
||||
OverrideTester,
|
||||
getUsedExtractedConfigs
|
||||
};
|
@ -1,223 +0,0 @@
|
||||
/**
|
||||
* @fileoverview `OverrideTester` class.
|
||||
*
|
||||
* `OverrideTester` class handles `files` property and `excludedFiles` property
|
||||
* of `overrides` config.
|
||||
*
|
||||
* It provides one method.
|
||||
*
|
||||
* - `test(filePath)`
|
||||
* Test if a file path matches the pair of `files` property and
|
||||
* `excludedFiles` property. The `filePath` argument must be an absolute
|
||||
* path.
|
||||
*
|
||||
* `ConfigArrayFactory` creates `OverrideTester` objects when it processes
|
||||
* `overrides` properties.
|
||||
*
|
||||
* @author Toru Nagashima <https://github.com/mysticatea>
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const assert = require("assert");
|
||||
const path = require("path");
|
||||
const util = require("util");
|
||||
const { Minimatch } = require("minimatch");
|
||||
const minimatchOpts = { dot: true, matchBase: true };
|
||||
|
||||
/**
|
||||
* @typedef {Object} Pattern
|
||||
* @property {InstanceType<Minimatch>[] | null} includes The positive matchers.
|
||||
* @property {InstanceType<Minimatch>[] | null} excludes The negative matchers.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Normalize a given pattern to an array.
|
||||
* @param {string|string[]|undefined} patterns A glob pattern or an array of glob patterns.
|
||||
* @returns {string[]|null} Normalized patterns.
|
||||
* @private
|
||||
*/
|
||||
function normalizePatterns(patterns) {
|
||||
if (Array.isArray(patterns)) {
|
||||
return patterns.filter(Boolean);
|
||||
}
|
||||
if (typeof patterns === "string" && patterns) {
|
||||
return [patterns];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the matchers of given patterns.
|
||||
* @param {string[]} patterns The patterns.
|
||||
* @returns {InstanceType<Minimatch>[] | null} The matchers.
|
||||
*/
|
||||
function toMatcher(patterns) {
|
||||
if (patterns.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return patterns.map(pattern => {
|
||||
if (/^\.[/\\]/u.test(pattern)) {
|
||||
return new Minimatch(
|
||||
pattern.slice(2),
|
||||
|
||||
// `./*.js` should not match with `subdir/foo.js`
|
||||
{ ...minimatchOpts, matchBase: false }
|
||||
);
|
||||
}
|
||||
return new Minimatch(pattern, minimatchOpts);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a given matcher to string.
|
||||
* @param {Pattern} matchers The matchers.
|
||||
* @returns {string} The string expression of the matcher.
|
||||
*/
|
||||
function patternToJson({ includes, excludes }) {
|
||||
return {
|
||||
includes: includes && includes.map(m => m.pattern),
|
||||
excludes: excludes && excludes.map(m => m.pattern)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The class to test given paths are matched by the patterns.
|
||||
*/
|
||||
class OverrideTester {
|
||||
|
||||
/**
|
||||
* Create a tester with given criteria.
|
||||
* If there are no criteria, returns `null`.
|
||||
* @param {string|string[]} files The glob patterns for included files.
|
||||
* @param {string|string[]} excludedFiles The glob patterns for excluded files.
|
||||
* @param {string} basePath The path to the base directory to test paths.
|
||||
* @returns {OverrideTester|null} The created instance or `null`.
|
||||
*/
|
||||
static create(files, excludedFiles, basePath) {
|
||||
const includePatterns = normalizePatterns(files);
|
||||
const excludePatterns = normalizePatterns(excludedFiles);
|
||||
let endsWithWildcard = false;
|
||||
|
||||
if (includePatterns.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Rejects absolute paths or relative paths to parents.
|
||||
for (const pattern of includePatterns) {
|
||||
if (path.isAbsolute(pattern) || pattern.includes("..")) {
|
||||
throw new Error(`Invalid override pattern (expected relative path not containing '..'): ${pattern}`);
|
||||
}
|
||||
if (pattern.endsWith("*")) {
|
||||
endsWithWildcard = true;
|
||||
}
|
||||
}
|
||||
for (const pattern of excludePatterns) {
|
||||
if (path.isAbsolute(pattern) || pattern.includes("..")) {
|
||||
throw new Error(`Invalid override pattern (expected relative path not containing '..'): ${pattern}`);
|
||||
}
|
||||
}
|
||||
|
||||
const includes = toMatcher(includePatterns);
|
||||
const excludes = toMatcher(excludePatterns);
|
||||
|
||||
return new OverrideTester(
|
||||
[{ includes, excludes }],
|
||||
basePath,
|
||||
endsWithWildcard
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine two testers by logical and.
|
||||
* If either of the testers was `null`, returns the other tester.
|
||||
* The `basePath` property of the two must be the same value.
|
||||
* @param {OverrideTester|null} a A tester.
|
||||
* @param {OverrideTester|null} b Another tester.
|
||||
* @returns {OverrideTester|null} Combined tester.
|
||||
*/
|
||||
static and(a, b) {
|
||||
if (!b) {
|
||||
return a && new OverrideTester(
|
||||
a.patterns,
|
||||
a.basePath,
|
||||
a.endsWithWildcard
|
||||
);
|
||||
}
|
||||
if (!a) {
|
||||
return new OverrideTester(
|
||||
b.patterns,
|
||||
b.basePath,
|
||||
b.endsWithWildcard
|
||||
);
|
||||
}
|
||||
|
||||
assert.strictEqual(a.basePath, b.basePath);
|
||||
return new OverrideTester(
|
||||
a.patterns.concat(b.patterns),
|
||||
a.basePath,
|
||||
a.endsWithWildcard || b.endsWithWildcard
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this instance.
|
||||
* @param {Pattern[]} patterns The matchers.
|
||||
* @param {string} basePath The base path.
|
||||
* @param {boolean} endsWithWildcard If `true` then a pattern ends with `*`.
|
||||
*/
|
||||
constructor(patterns, basePath, endsWithWildcard = false) {
|
||||
|
||||
/** @type {Pattern[]} */
|
||||
this.patterns = patterns;
|
||||
|
||||
/** @type {string} */
|
||||
this.basePath = basePath;
|
||||
|
||||
/** @type {boolean} */
|
||||
this.endsWithWildcard = endsWithWildcard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a given path is matched or not.
|
||||
* @param {string} filePath The absolute path to the target file.
|
||||
* @returns {boolean} `true` if the path was matched.
|
||||
*/
|
||||
test(filePath) {
|
||||
if (typeof filePath !== "string" || !path.isAbsolute(filePath)) {
|
||||
throw new Error(`'filePath' should be an absolute path, but got ${filePath}.`);
|
||||
}
|
||||
const relativePath = path.relative(this.basePath, filePath);
|
||||
|
||||
return this.patterns.every(({ includes, excludes }) => (
|
||||
(!includes || includes.some(m => m.match(relativePath))) &&
|
||||
(!excludes || !excludes.some(m => m.match(relativePath)))
|
||||
));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-description
|
||||
/**
|
||||
* @returns {Object} a JSON compatible object.
|
||||
*/
|
||||
toJSON() {
|
||||
if (this.patterns.length === 1) {
|
||||
return {
|
||||
...patternToJson(this.patterns[0]),
|
||||
basePath: this.basePath
|
||||
};
|
||||
}
|
||||
return {
|
||||
AND: this.patterns.map(patternToJson),
|
||||
basePath: this.basePath
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-description
|
||||
/**
|
||||
* @returns {Object} an object to display by `console.log()`.
|
||||
*/
|
||||
[util.inspect.custom]() {
|
||||
return this.toJSON();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { OverrideTester };
|
@ -40,8 +40,13 @@ const getGlobParent = require("glob-parent");
|
||||
const isGlob = require("is-glob");
|
||||
const { escapeRegExp } = require("lodash");
|
||||
const { Minimatch } = require("minimatch");
|
||||
const { IgnorePattern } = require("./config-array");
|
||||
const { CascadingConfigArrayFactory } = require("./cascading-config-array-factory");
|
||||
|
||||
const {
|
||||
Legacy: {
|
||||
IgnorePattern,
|
||||
CascadingConfigArrayFactory
|
||||
}
|
||||
} = require("@eslint/eslintrc");
|
||||
const debug = require("debug")("eslint:file-enumerator");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -208,7 +213,11 @@ class FileEnumerator {
|
||||
*/
|
||||
constructor({
|
||||
cwd = process.cwd(),
|
||||
configArrayFactory = new CascadingConfigArrayFactory({ cwd }),
|
||||
configArrayFactory = new CascadingConfigArrayFactory({
|
||||
cwd,
|
||||
eslintRecommendedPath: path.resolve(__dirname, "../../conf/eslint-recommended.js"),
|
||||
eslintAllPath: path.resolve(__dirname, "../../conf/eslint-all.js")
|
||||
}),
|
||||
extensions = null,
|
||||
globInputPaths = true,
|
||||
errorOnUnmatchedPattern = true,
|
||||
|
@ -42,8 +42,8 @@ module.exports = function(results) {
|
||||
|
||||
messages.forEach(message => {
|
||||
output += [
|
||||
`<error line="${xmlEscape(message.line)}"`,
|
||||
`column="${xmlEscape(message.column)}"`,
|
||||
`<error line="${xmlEscape(message.line || 0)}"`,
|
||||
`column="${xmlEscape(message.column || 0)}"`,
|
||||
`severity="${xmlEscape(getMessageType(message))}"`,
|
||||
`message="${xmlEscape(message.message)}${message.ruleId ? ` (${message.ruleId})` : ""}"`,
|
||||
`source="${message.ruleId ? xmlEscape(`eslint.rules.${message.ruleId}`) : ""}" />`
|
||||
|
@ -15,7 +15,13 @@ const fs = require("fs");
|
||||
const { promisify } = require("util");
|
||||
const { CLIEngine, getCLIEngineInternalSlots } = require("../cli-engine/cli-engine");
|
||||
const BuiltinRules = require("../rules");
|
||||
const { getRuleSeverity } = require("../shared/config-ops");
|
||||
const {
|
||||
Legacy: {
|
||||
ConfigOps: {
|
||||
getRuleSeverity
|
||||
}
|
||||
}
|
||||
} = require("@eslint/eslintrc");
|
||||
const { version } = require("../../package.json");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
const lodash = require("lodash"),
|
||||
recConfig = require("../../conf/eslint-recommended"),
|
||||
ConfigOps = require("../shared/config-ops"),
|
||||
ConfigOps = require("@eslint/eslintrc/lib/shared/config-ops"),
|
||||
{ Linter } = require("../linter"),
|
||||
configRule = require("./config-rule");
|
||||
|
||||
|
@ -12,14 +12,14 @@
|
||||
|
||||
const util = require("util"),
|
||||
path = require("path"),
|
||||
inquirer = require("inquirer"),
|
||||
enquirer = require("enquirer"),
|
||||
ProgressBar = require("progress"),
|
||||
semver = require("semver"),
|
||||
espree = require("espree"),
|
||||
recConfig = require("../../conf/eslint-recommended"),
|
||||
ConfigOps = require("../shared/config-ops"),
|
||||
ConfigOps = require("@eslint/eslintrc/lib/shared/config-ops"),
|
||||
log = require("../shared/logging"),
|
||||
naming = require("../shared/naming"),
|
||||
naming = require("@eslint/eslintrc/lib/shared/naming"),
|
||||
ModuleResolver = require("../shared/relative-module-resolver"),
|
||||
autoconfig = require("./autoconfig.js"),
|
||||
ConfigFile = require("./config-file"),
|
||||
@ -146,7 +146,7 @@ function getModulesList(config, installESLint) {
|
||||
*
|
||||
* Note: This clones the config object and returns a new config to avoid mutating
|
||||
* the original config parameter.
|
||||
* @param {Object} answers answers received from inquirer
|
||||
* @param {Object} answers answers received from enquirer
|
||||
* @param {Object} config config object
|
||||
* @returns {Object} config object with configured rules
|
||||
*/
|
||||
@ -253,7 +253,7 @@ function configureRules(answers, config) {
|
||||
|
||||
/**
|
||||
* process user's answers and create config object
|
||||
* @param {Object} answers answers received from inquirer
|
||||
* @param {Object} answers answers received from enquirer
|
||||
* @returns {Object} config object
|
||||
*/
|
||||
function processAnswers(answers) {
|
||||
@ -265,7 +265,7 @@ function processAnswers(answers) {
|
||||
};
|
||||
|
||||
config.parserOptions.ecmaVersion = espree.latestEcmaVersion;
|
||||
config.env.es2020 = true;
|
||||
config.env.es2021 = true;
|
||||
|
||||
// set the module type
|
||||
if (answers.moduleType === "esm") {
|
||||
@ -321,7 +321,6 @@ function processAnswers(answers) {
|
||||
}
|
||||
}
|
||||
if (answers.typescript && config.extends.includes("eslint:recommended")) {
|
||||
config.extends.push("plugin:@typescript-eslint/eslint-recommended");
|
||||
config.extends.push("plugin:@typescript-eslint/recommended");
|
||||
}
|
||||
|
||||
@ -409,7 +408,7 @@ function installModules(modules) {
|
||||
npmUtils.installSyncSaveDev(modules);
|
||||
}
|
||||
|
||||
/* istanbul ignore next: no need to test inquirer */
|
||||
/* istanbul ignore next: no need to test enquirer */
|
||||
/**
|
||||
* Ask user to install modules.
|
||||
* @param {string[]} modules Array of modules to be installed.
|
||||
@ -425,14 +424,19 @@ function askInstallModules(modules, packageJsonExists) {
|
||||
|
||||
log.info("The config that you've selected requires the following dependencies:\n");
|
||||
log.info(modules.join(" "));
|
||||
return inquirer.prompt([
|
||||
return enquirer.prompt([
|
||||
{
|
||||
type: "confirm",
|
||||
type: "toggle",
|
||||
name: "executeInstallation",
|
||||
message: "Would you like to install them now with npm?",
|
||||
default: true,
|
||||
when() {
|
||||
return modules.length && packageJsonExists;
|
||||
enabled: "Yes",
|
||||
disabled: "No",
|
||||
initial: 1,
|
||||
skip() {
|
||||
return !(modules.length && packageJsonExists);
|
||||
},
|
||||
result(input) {
|
||||
return this.skipped ? null : input;
|
||||
}
|
||||
}
|
||||
]).then(({ executeInstallation }) => {
|
||||
@ -442,114 +446,124 @@ function askInstallModules(modules, packageJsonExists) {
|
||||
});
|
||||
}
|
||||
|
||||
/* istanbul ignore next: no need to test inquirer */
|
||||
/* istanbul ignore next: no need to test enquirer */
|
||||
/**
|
||||
* Ask use a few questions on command prompt
|
||||
* @returns {Promise} The promise with the result of the prompt
|
||||
*/
|
||||
function promptUser() {
|
||||
|
||||
return inquirer.prompt([
|
||||
return enquirer.prompt([
|
||||
{
|
||||
type: "list",
|
||||
type: "select",
|
||||
name: "purpose",
|
||||
message: "How would you like to use ESLint?",
|
||||
default: "problems",
|
||||
|
||||
// The returned number matches the name value of nth in the choices array.
|
||||
initial: 1,
|
||||
choices: [
|
||||
{ name: "To check syntax only", value: "syntax" },
|
||||
{ name: "To check syntax and find problems", value: "problems" },
|
||||
{ name: "To check syntax, find problems, and enforce code style", value: "style" }
|
||||
{ message: "To check syntax only", name: "syntax" },
|
||||
{ message: "To check syntax and find problems", name: "problems" },
|
||||
{ message: "To check syntax, find problems, and enforce code style", name: "style" }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "list",
|
||||
type: "select",
|
||||
name: "moduleType",
|
||||
message: "What type of modules does your project use?",
|
||||
default: "esm",
|
||||
initial: 0,
|
||||
choices: [
|
||||
{ name: "JavaScript modules (import/export)", value: "esm" },
|
||||
{ name: "CommonJS (require/exports)", value: "commonjs" },
|
||||
{ name: "None of these", value: "none" }
|
||||
{ message: "JavaScript modules (import/export)", name: "esm" },
|
||||
{ message: "CommonJS (require/exports)", name: "commonjs" },
|
||||
{ message: "None of these", name: "none" }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "list",
|
||||
type: "select",
|
||||
name: "framework",
|
||||
message: "Which framework does your project use?",
|
||||
default: "react",
|
||||
initial: 0,
|
||||
choices: [
|
||||
{ name: "React", value: "react" },
|
||||
{ name: "Vue.js", value: "vue" },
|
||||
{ name: "None of these", value: "none" }
|
||||
{ message: "React", name: "react" },
|
||||
{ message: "Vue.js", name: "vue" },
|
||||
{ message: "None of these", name: "none" }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "confirm",
|
||||
type: "toggle",
|
||||
name: "typescript",
|
||||
message: "Does your project use TypeScript?",
|
||||
default: false
|
||||
enabled: "Yes",
|
||||
disabled: "No",
|
||||
initial: 0
|
||||
},
|
||||
{
|
||||
type: "checkbox",
|
||||
type: "multiselect",
|
||||
name: "env",
|
||||
message: "Where does your code run?",
|
||||
default: ["browser"],
|
||||
hint: "(Press <space> to select, <a> to toggle all, <i> to invert selection)",
|
||||
initial: 0,
|
||||
choices: [
|
||||
{ name: "Browser", value: "browser" },
|
||||
{ name: "Node", value: "node" }
|
||||
{ message: "Browser", name: "browser" },
|
||||
{ message: "Node", name: "node" }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "list",
|
||||
type: "select",
|
||||
name: "source",
|
||||
message: "How would you like to define a style for your project?",
|
||||
default: "guide",
|
||||
choices: [
|
||||
{ name: "Use a popular style guide", value: "guide" },
|
||||
{ name: "Answer questions about your style", value: "prompt" },
|
||||
{ name: "Inspect your JavaScript file(s)", value: "auto" }
|
||||
{ message: "Use a popular style guide", name: "guide" },
|
||||
{ message: "Answer questions about your style", name: "prompt" },
|
||||
{ message: "Inspect your JavaScript file(s)", name: "auto" }
|
||||
],
|
||||
when(answers) {
|
||||
return answers.purpose === "style";
|
||||
skip() {
|
||||
return this.state.answers.purpose !== "style";
|
||||
},
|
||||
result(input) {
|
||||
return this.skipped ? null : input;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "list",
|
||||
type: "select",
|
||||
name: "styleguide",
|
||||
message: "Which style guide do you want to follow?",
|
||||
choices: [
|
||||
{ name: "Airbnb: https://github.com/airbnb/javascript", value: "airbnb" },
|
||||
{ name: "Standard: https://github.com/standard/standard", value: "standard" },
|
||||
{ name: "Google: https://github.com/google/eslint-config-google", value: "google" }
|
||||
{ message: "Airbnb: https://github.com/airbnb/javascript", name: "airbnb" },
|
||||
{ message: "Standard: https://github.com/standard/standard", name: "standard" },
|
||||
{ message: "Google: https://github.com/google/eslint-config-google", name: "google" }
|
||||
],
|
||||
when(answers) {
|
||||
answers.packageJsonExists = npmUtils.checkPackageJson();
|
||||
return answers.source === "guide" && answers.packageJsonExists;
|
||||
skip() {
|
||||
this.state.answers.packageJsonExists = npmUtils.checkPackageJson();
|
||||
return !(this.state.answers.source === "guide" && this.state.answers.packageJsonExists);
|
||||
},
|
||||
result(input) {
|
||||
return this.skipped ? null : input;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "input",
|
||||
name: "patterns",
|
||||
message: "Which file(s), path(s), or glob(s) should be examined?",
|
||||
when(answers) {
|
||||
return (answers.source === "auto");
|
||||
skip() {
|
||||
return this.state.answers.source !== "auto";
|
||||
},
|
||||
validate(input) {
|
||||
if (input.trim().length === 0 && input.trim() !== ",") {
|
||||
if (!this.skipped && input.trim().length === 0 && input.trim() !== ",") {
|
||||
return "You must tell us what code to examine. Try again.";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "list",
|
||||
type: "select",
|
||||
name: "format",
|
||||
message: "What format do you want your config file to be in?",
|
||||
default: "JavaScript",
|
||||
initial: 0,
|
||||
choices: ["JavaScript", "YAML", "JSON"]
|
||||
},
|
||||
{
|
||||
type: "confirm",
|
||||
type: "toggle",
|
||||
name: "installESLint",
|
||||
message(answers) {
|
||||
const verb = semver.ltr(answers.localESLintVersion, answers.requiredESLintVersionRange)
|
||||
@ -558,9 +572,14 @@ function promptUser() {
|
||||
|
||||
return `The style guide "${answers.styleguide}" requires eslint@${answers.requiredESLintVersionRange}. You are currently using eslint@${answers.localESLintVersion}.\n Do you want to ${verb}?`;
|
||||
},
|
||||
default: true,
|
||||
when(answers) {
|
||||
return answers.source === "guide" && answers.packageJsonExists && hasESLintVersionConflict(answers);
|
||||
enabled: "Yes",
|
||||
disabled: "No",
|
||||
initial: 1,
|
||||
skip() {
|
||||
return !(this.state.answers.source === "guide" && this.state.answers.packageJsonExists && hasESLintVersionConflict(this.state.answers));
|
||||
},
|
||||
result(input) {
|
||||
return this.skipped ? null : input;
|
||||
}
|
||||
}
|
||||
]).then(earlyAnswers => {
|
||||
@ -613,33 +632,35 @@ function promptUser() {
|
||||
}
|
||||
|
||||
// continue with the style questions otherwise...
|
||||
return inquirer.prompt([
|
||||
return enquirer.prompt([
|
||||
{
|
||||
type: "list",
|
||||
type: "select",
|
||||
name: "indent",
|
||||
message: "What style of indentation do you use?",
|
||||
default: "tab",
|
||||
choices: [{ name: "Tabs", value: "tab" }, { name: "Spaces", value: 4 }]
|
||||
initial: 0,
|
||||
choices: [{ message: "Tabs", name: "tab" }, { message: "Spaces", name: 4 }]
|
||||
},
|
||||
{
|
||||
type: "list",
|
||||
type: "select",
|
||||
name: "quotes",
|
||||
message: "What quotes do you use for strings?",
|
||||
default: "double",
|
||||
choices: [{ name: "Double", value: "double" }, { name: "Single", value: "single" }]
|
||||
initial: 0,
|
||||
choices: [{ message: "Double", name: "double" }, { message: "Single", name: "single" }]
|
||||
},
|
||||
{
|
||||
type: "list",
|
||||
type: "select",
|
||||
name: "linebreak",
|
||||
message: "What line endings do you use?",
|
||||
default: "unix",
|
||||
choices: [{ name: "Unix", value: "unix" }, { name: "Windows", value: "windows" }]
|
||||
initial: 0,
|
||||
choices: [{ message: "Unix", name: "unix" }, { message: "Windows", name: "windows" }]
|
||||
},
|
||||
{
|
||||
type: "confirm",
|
||||
type: "toggle",
|
||||
name: "semi",
|
||||
message: "Do you require semicolons?",
|
||||
default: true
|
||||
enabled: "Yes",
|
||||
disabled: "No",
|
||||
initial: 1
|
||||
}
|
||||
]).then(answers => {
|
||||
const totalAnswers = Object.assign({}, earlyAnswers, answers);
|
||||
|
@ -39,6 +39,17 @@ function isHandledLogicalOperator(operator) {
|
||||
return operator === "&&" || operator === "||" || operator === "??";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given assignment operator is a logical assignment operator.
|
||||
* Logical assignments are taken into account for the code path analysis
|
||||
* because of their short-circuiting semantics.
|
||||
* @param {string} operator The operator found in the AssignmentExpression node
|
||||
* @returns {boolean} `true` if the operator is "&&=" or "||=" or "??="
|
||||
*/
|
||||
function isLogicalAssignmentOperator(operator) {
|
||||
return operator === "&&=" || operator === "||=" || operator === "??=";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the label if the parent node of a given node is a LabeledStatement.
|
||||
* @param {ASTNode} node A node to get.
|
||||
@ -71,6 +82,9 @@ function isForkingByTrueOrFalse(node) {
|
||||
case "LogicalExpression":
|
||||
return isHandledLogicalOperator(parent.operator);
|
||||
|
||||
case "AssignmentExpression":
|
||||
return isLogicalAssignmentOperator(parent.operator);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -244,6 +258,19 @@ function preprocess(analyzer, node) {
|
||||
const parent = node.parent;
|
||||
|
||||
switch (parent.type) {
|
||||
|
||||
// The `arguments.length == 0` case is in `postprocess` function.
|
||||
case "CallExpression":
|
||||
if (parent.optional === true && parent.arguments.length >= 1 && parent.arguments[0] === node) {
|
||||
state.makeOptionalRight();
|
||||
}
|
||||
break;
|
||||
case "MemberExpression":
|
||||
if (parent.optional === true && parent.property === node) {
|
||||
state.makeOptionalRight();
|
||||
}
|
||||
break;
|
||||
|
||||
case "LogicalExpression":
|
||||
if (
|
||||
parent.right === node &&
|
||||
@ -253,6 +280,15 @@ function preprocess(analyzer, node) {
|
||||
}
|
||||
break;
|
||||
|
||||
case "AssignmentExpression":
|
||||
if (
|
||||
parent.right === node &&
|
||||
isLogicalAssignmentOperator(parent.operator)
|
||||
) {
|
||||
state.makeLogicalRight();
|
||||
}
|
||||
break;
|
||||
|
||||
case "ConditionalExpression":
|
||||
case "IfStatement":
|
||||
|
||||
@ -377,6 +413,20 @@ function processCodePathToEnter(analyzer, node) {
|
||||
analyzer.emitter.emit("onCodePathStart", codePath, node);
|
||||
break;
|
||||
|
||||
case "ChainExpression":
|
||||
state.pushChainContext();
|
||||
break;
|
||||
case "CallExpression":
|
||||
if (node.optional === true) {
|
||||
state.makeOptionalNode();
|
||||
}
|
||||
break;
|
||||
case "MemberExpression":
|
||||
if (node.optional === true) {
|
||||
state.makeOptionalNode();
|
||||
}
|
||||
break;
|
||||
|
||||
case "LogicalExpression":
|
||||
if (isHandledLogicalOperator(node.operator)) {
|
||||
state.pushChoiceContext(
|
||||
@ -386,6 +436,15 @@ function processCodePathToEnter(analyzer, node) {
|
||||
}
|
||||
break;
|
||||
|
||||
case "AssignmentExpression":
|
||||
if (isLogicalAssignmentOperator(node.operator)) {
|
||||
state.pushChoiceContext(
|
||||
node.operator.slice(0, -1), // removes `=` from the end
|
||||
isForkingByTrueOrFalse(node)
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case "ConditionalExpression":
|
||||
case "IfStatement":
|
||||
state.pushChoiceContext("test", false);
|
||||
@ -449,6 +508,10 @@ function processCodePathToExit(analyzer, node) {
|
||||
let dontForward = false;
|
||||
|
||||
switch (node.type) {
|
||||
case "ChainExpression":
|
||||
state.popChainContext();
|
||||
break;
|
||||
|
||||
case "IfStatement":
|
||||
case "ConditionalExpression":
|
||||
state.popChoiceContext();
|
||||
@ -460,6 +523,12 @@ function processCodePathToExit(analyzer, node) {
|
||||
}
|
||||
break;
|
||||
|
||||
case "AssignmentExpression":
|
||||
if (isLogicalAssignmentOperator(node.operator)) {
|
||||
state.popChoiceContext();
|
||||
}
|
||||
break;
|
||||
|
||||
case "SwitchStatement":
|
||||
state.popSwitchContext();
|
||||
break;
|
||||
@ -583,6 +652,13 @@ function postprocess(analyzer, node) {
|
||||
break;
|
||||
}
|
||||
|
||||
// The `arguments.length >= 1` case is in `preprocess` function.
|
||||
case "CallExpression":
|
||||
if (node.optional === true && node.arguments.length === 0) {
|
||||
CodePath.getState(analyzer.codePath).makeOptionalRight();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -92,7 +92,6 @@ class CodePathSegment {
|
||||
/* istanbul ignore if */
|
||||
if (debug.enabled) {
|
||||
this.internal.nodes = [];
|
||||
this.internal.exitNodes = [];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,6 +234,7 @@ class CodePathState {
|
||||
this.tryContext = null;
|
||||
this.loopContext = null;
|
||||
this.breakContext = null;
|
||||
this.chainContext = null;
|
||||
|
||||
this.currentSegments = [];
|
||||
this.initialSegment = this.forkContext.head[0];
|
||||
@ -316,7 +317,7 @@ class CodePathState {
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates a context for ConditionalExpression, LogicalExpression,
|
||||
* Creates a context for ConditionalExpression, LogicalExpression, AssignmentExpression (logical assignments only),
|
||||
* IfStatement, WhileStatement, DoWhileStatement, or ForStatement.
|
||||
*
|
||||
* LogicalExpressions have cases that it goes different paths between the
|
||||
@ -338,7 +339,7 @@ class CodePathState {
|
||||
* a -> b -> foo();
|
||||
* a -> b -> bar();
|
||||
* @param {string} kind A kind string.
|
||||
* If the new context is LogicalExpression's, this is `"&&"` or `"||"`.
|
||||
* If the new context is LogicalExpression's or AssignmentExpression's, this is `"&&"` or `"||"` or `"??"`.
|
||||
* If it's IfStatement's or ConditionalExpression's, this is `"test"`.
|
||||
* Otherwise, this is `"loop"`.
|
||||
* @param {boolean} isForkingAsResult A flag that shows that goes different
|
||||
@ -555,6 +556,64 @@ class CodePathState {
|
||||
);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// ChainExpression
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Push a new `ChainExpression` context to the stack.
|
||||
* This method is called on entering to each `ChainExpression` node.
|
||||
* This context is used to count forking in the optional chain then merge them on the exiting from the `ChainExpression` node.
|
||||
* @returns {void}
|
||||
*/
|
||||
pushChainContext() {
|
||||
this.chainContext = {
|
||||
upper: this.chainContext,
|
||||
countChoiceContexts: 0
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop a `ChainExpression` context from the stack.
|
||||
* This method is called on exiting from each `ChainExpression` node.
|
||||
* This merges all forks of the last optional chaining.
|
||||
* @returns {void}
|
||||
*/
|
||||
popChainContext() {
|
||||
const context = this.chainContext;
|
||||
|
||||
this.chainContext = context.upper;
|
||||
|
||||
// pop all choice contexts of this.
|
||||
for (let i = context.countChoiceContexts; i > 0; --i) {
|
||||
this.popChoiceContext();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a choice context for optional access.
|
||||
* This method is called on entering to each `(Call|Member)Expression[optional=true]` node.
|
||||
* This creates a choice context as similar to `LogicalExpression[operator="??"]` node.
|
||||
* @returns {void}
|
||||
*/
|
||||
makeOptionalNode() {
|
||||
if (this.chainContext) {
|
||||
this.chainContext.countChoiceContexts += 1;
|
||||
this.pushChoiceContext("??", false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a fork.
|
||||
* This method is called on entering to the `arguments|property` property of each `(Call|Member)Expression` node.
|
||||
* @returns {void}
|
||||
*/
|
||||
makeOptionalRight() {
|
||||
if (this.chainContext) {
|
||||
this.makeLogicalRight();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// SwitchStatement
|
||||
//--------------------------------------------------------------------------
|
||||
|
@ -25,6 +25,22 @@ function getId(segment) { // eslint-disable-line jsdoc/require-jsdoc
|
||||
return segment.id + (segment.reachable ? "" : "!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string for the given node and operation.
|
||||
* @param {ASTNode} node The node to convert.
|
||||
* @param {"enter" | "exit" | undefined} label The operation label.
|
||||
* @returns {string} The string representation.
|
||||
*/
|
||||
function nodeToString(node, label) {
|
||||
const suffix = label ? `:${label}` : "";
|
||||
|
||||
switch (node.type) {
|
||||
case "Identifier": return `${node.type}${suffix} (${node.name})`;
|
||||
case "Literal": return `${node.type}${suffix} (${node.value})`;
|
||||
default: return `${node.type}${suffix}`;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
@ -56,9 +72,15 @@ module.exports = {
|
||||
const segInternal = state.currentSegments[i].internal;
|
||||
|
||||
if (leaving) {
|
||||
segInternal.exitNodes.push(node);
|
||||
const last = segInternal.nodes.length - 1;
|
||||
|
||||
if (last >= 0 && segInternal.nodes[last] === nodeToString(node, "enter")) {
|
||||
segInternal.nodes[last] = nodeToString(node, void 0);
|
||||
} else {
|
||||
segInternal.nodes.push(nodeToString(node, "exit"));
|
||||
}
|
||||
} else {
|
||||
segInternal.nodes.push(node);
|
||||
segInternal.nodes.push(nodeToString(node, "enter"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,23 +126,8 @@ module.exports = {
|
||||
text += "style=\"rounded,dashed,filled\",fillcolor=\"#FF9800\",label=\"<<unreachable>>\\n";
|
||||
}
|
||||
|
||||
if (segment.internal.nodes.length > 0 || segment.internal.exitNodes.length > 0) {
|
||||
text += [].concat(
|
||||
segment.internal.nodes.map(node => {
|
||||
switch (node.type) {
|
||||
case "Identifier": return `${node.type} (${node.name})`;
|
||||
case "Literal": return `${node.type} (${node.value})`;
|
||||
default: return node.type;
|
||||
}
|
||||
}),
|
||||
segment.internal.exitNodes.map(node => {
|
||||
switch (node.type) {
|
||||
case "Identifier": return `${node.type}:exit (${node.name})`;
|
||||
case "Literal": return `${node.type}:exit (${node.value})`;
|
||||
default: return `${node.type}:exit`;
|
||||
}
|
||||
})
|
||||
).join("\\n");
|
||||
if (segment.internal.nodes.length > 0) {
|
||||
text += segment.internal.nodes.join("\\n");
|
||||
} else {
|
||||
text += "????";
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const levn = require("levn"),
|
||||
ConfigOps = require("../shared/config-ops");
|
||||
ConfigOps = require("@eslint/eslintrc/lib/shared/config-ops");
|
||||
|
||||
const debug = require("debug")("eslint:config-comment-parser");
|
||||
|
||||
|
@ -16,11 +16,11 @@ const
|
||||
evk = require("eslint-visitor-keys"),
|
||||
espree = require("espree"),
|
||||
lodash = require("lodash"),
|
||||
BuiltInEnvironments = require("../../conf/environments"),
|
||||
BuiltInEnvironments = require("@eslint/eslintrc/conf/environments"),
|
||||
pkg = require("../../package.json"),
|
||||
astUtils = require("../shared/ast-utils"),
|
||||
ConfigOps = require("../shared/config-ops"),
|
||||
validator = require("../shared/config-validator"),
|
||||
ConfigOps = require("@eslint/eslintrc/lib/shared/config-ops"),
|
||||
ConfigValidator = require("@eslint/eslintrc/lib/shared/config-validator"),
|
||||
Traverser = require("../shared/traverser"),
|
||||
{ SourceCode } = require("../source-code"),
|
||||
CodePathAnalyzer = require("./code-path-analysis/code-path-analyzer"),
|
||||
@ -293,6 +293,9 @@ function getDirectiveComments(filename, ast, ruleMapper, warnInlineConfig) {
|
||||
const exportedVariables = {};
|
||||
const problems = [];
|
||||
const disableDirectives = [];
|
||||
const validator = new ConfigValidator({
|
||||
builtInRules: Rules
|
||||
});
|
||||
|
||||
ast.comments.filter(token => token.type !== "Shebang").forEach(comment => {
|
||||
const trimmedCommentText = stripDirectiveComment(comment.value);
|
||||
|
@ -196,15 +196,19 @@ function mapSuggestions(descriptor, sourceCode, messages) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return descriptor.suggest.map(suggestInfo => {
|
||||
const computedDesc = suggestInfo.desc || messages[suggestInfo.messageId];
|
||||
return descriptor.suggest
|
||||
.map(suggestInfo => {
|
||||
const computedDesc = suggestInfo.desc || messages[suggestInfo.messageId];
|
||||
|
||||
return {
|
||||
...suggestInfo,
|
||||
desc: interpolate(computedDesc, suggestInfo.data),
|
||||
fix: normalizeFixes(suggestInfo, sourceCode)
|
||||
};
|
||||
});
|
||||
return {
|
||||
...suggestInfo,
|
||||
desc: interpolate(computedDesc, suggestInfo.data),
|
||||
fix: normalizeFixes(suggestInfo, sourceCode)
|
||||
};
|
||||
})
|
||||
|
||||
// Remove suggestions that didn't provide a fix
|
||||
.filter(({ fix }) => fix);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -644,6 +644,10 @@ class RuleTester {
|
||||
assert.ok(item.errors || item.errors === 0,
|
||||
`Did not specify errors for an invalid test of ${ruleName}`);
|
||||
|
||||
if (Array.isArray(item.errors) && item.errors.length === 0) {
|
||||
assert.fail("Invalid cases must have at least one error");
|
||||
}
|
||||
|
||||
const ruleHasMetaMessages = hasOwnProperty(rule, "meta") && hasOwnProperty(rule.meta, "messages");
|
||||
const friendlyIDList = ruleHasMetaMessages ? `[${Object.keys(rule.meta.messages).map(key => `'${key}'`).join(", ")}]` : null;
|
||||
|
||||
@ -651,6 +655,11 @@ class RuleTester {
|
||||
const messages = result.messages;
|
||||
|
||||
if (typeof item.errors === "number") {
|
||||
|
||||
if (item.errors === 0) {
|
||||
assert.fail("Invalid cases must have 'error' value greater than 0");
|
||||
}
|
||||
|
||||
assert.strictEqual(messages.length, item.errors, util.format("Should have %d error%s but had %d: %s",
|
||||
item.errors, item.errors === 1 ? "" : "s", messages.length, util.inspect(messages)));
|
||||
} else {
|
||||
@ -852,6 +861,16 @@ class RuleTester {
|
||||
);
|
||||
}
|
||||
|
||||
// Rules that produce fixes must have `meta.fixable` property.
|
||||
if (result.output !== item.code) {
|
||||
assert.ok(
|
||||
hasOwnProperty(rule, "meta"),
|
||||
"Fixable rules should export a `meta.fixable` property."
|
||||
);
|
||||
|
||||
// Linter throws if a rule that produced a fix has `meta` but doesn't have `meta.fixable`.
|
||||
}
|
||||
|
||||
assertASTDidntChange(result.beforeAST, result.afterAST);
|
||||
}
|
||||
|
||||
|
@ -86,16 +86,6 @@ function isAccessorKind(node) {
|
||||
return node.kind === "get" || node.kind === "set";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a given node is an `Identifier` node which was named a given name.
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @param {string} name An expected name of the node.
|
||||
* @returns {boolean} `true` if the node is an `Identifier` node which was named as expected.
|
||||
*/
|
||||
function isIdentifier(node, name) {
|
||||
return node.type === "Identifier" && node.name === name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a given node is an argument of a specified method call.
|
||||
* @param {ASTNode} node A node to check.
|
||||
@ -109,10 +99,7 @@ function isArgumentOfMethodCall(node, index, object, property) {
|
||||
|
||||
return (
|
||||
parent.type === "CallExpression" &&
|
||||
parent.callee.type === "MemberExpression" &&
|
||||
parent.callee.computed === false &&
|
||||
isIdentifier(parent.callee.object, object) &&
|
||||
isIdentifier(parent.callee.property, property) &&
|
||||
astUtils.isSpecificMemberAccess(parent.callee, object, property) &&
|
||||
parent.arguments[index] === node
|
||||
);
|
||||
}
|
||||
|
@ -9,8 +9,6 @@
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const lodash = require("lodash");
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -30,17 +28,27 @@ function isReachable(segment) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a given node is a MemberExpression node which has the specified name's
|
||||
* Checks a given node is a member access which has the specified name's
|
||||
* property.
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @returns {boolean} `true` if the node is a MemberExpression node which has
|
||||
* the specified name's property
|
||||
* @returns {boolean} `true` if the node is a member access which has
|
||||
* the specified name's property. The node may be a `(Chain|Member)Expression` node.
|
||||
*/
|
||||
function isTargetMethod(node) {
|
||||
return (
|
||||
node.type === "MemberExpression" &&
|
||||
TARGET_METHODS.test(astUtils.getStaticPropertyName(node) || "")
|
||||
);
|
||||
return astUtils.isSpecificMemberAccess(node, null, TARGET_METHODS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a human-legible description of an array method
|
||||
* @param {string} arrayMethodName A method name to fully qualify
|
||||
* @returns {string} the method name prefixed with `Array.` if it is a class method,
|
||||
* or else `Array.prototype.` if it is an instance method.
|
||||
*/
|
||||
function fullMethodName(arrayMethodName) {
|
||||
if (["from", "of", "isArray"].includes(arrayMethodName)) {
|
||||
return "Array.".concat(arrayMethodName);
|
||||
}
|
||||
return "Array.prototype.".concat(arrayMethodName);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -65,6 +73,7 @@ function getArrayMethodName(node) {
|
||||
*/
|
||||
case "LogicalExpression":
|
||||
case "ConditionalExpression":
|
||||
case "ChainExpression":
|
||||
currentNode = parent;
|
||||
break;
|
||||
|
||||
@ -153,10 +162,10 @@ module.exports = {
|
||||
],
|
||||
|
||||
messages: {
|
||||
expectedAtEnd: "Expected to return a value at the end of {{name}}.",
|
||||
expectedInside: "Expected to return a value in {{name}}.",
|
||||
expectedReturnValue: "{{name}} expected a return value.",
|
||||
expectedNoReturnValue: "{{name}} did not expect a return value."
|
||||
expectedAtEnd: "{{arrayMethodName}}() expects a value to be returned at the end of {{name}}.",
|
||||
expectedInside: "{{arrayMethodName}}() expects a return value from {{name}}.",
|
||||
expectedReturnValue: "{{arrayMethodName}}() expects a return value from {{name}}.",
|
||||
expectedNoReturnValue: "{{arrayMethodName}}() expects no useless return value from {{name}}."
|
||||
}
|
||||
},
|
||||
|
||||
@ -202,14 +211,13 @@ module.exports = {
|
||||
}
|
||||
|
||||
if (messageId) {
|
||||
let name = astUtils.getFunctionNameWithKind(node);
|
||||
const name = astUtils.getFunctionNameWithKind(node);
|
||||
|
||||
name = messageId === "expectedNoReturnValue" ? lodash.upperFirst(name) : name;
|
||||
context.report({
|
||||
node,
|
||||
loc: astUtils.getFunctionHeadLoc(node, sourceCode),
|
||||
messageId,
|
||||
data: { name }
|
||||
data: { name, arrayMethodName: fullMethodName(funcInfo.arrayMethodName) }
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -273,7 +281,8 @@ module.exports = {
|
||||
node,
|
||||
messageId,
|
||||
data: {
|
||||
name: lodash.upperFirst(astUtils.getFunctionNameWithKind(funcInfo.node))
|
||||
name: astUtils.getFunctionNameWithKind(funcInfo.node),
|
||||
arrayMethodName: fullMethodName(funcInfo.arrayMethodName)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -75,6 +75,7 @@ module.exports = {
|
||||
const never = options[0] === "never";
|
||||
const requireReturnForObjectLiteral = options[1] && options[1].requireReturnForObjectLiteral;
|
||||
const sourceCode = context.getSourceCode();
|
||||
let funcInfo = null;
|
||||
|
||||
/**
|
||||
* Checks whether the given node has ASI problem or not.
|
||||
@ -99,6 +100,21 @@ module.exports = {
|
||||
return sourceCode.getTokenAfter(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the node is inside of a for loop's init
|
||||
* @param {ASTNode} node node is inside for loop
|
||||
* @returns {boolean} `true` if the node is inside of a for loop, else `false`
|
||||
*/
|
||||
function isInsideForLoopInitializer(node) {
|
||||
if (node && node.parent) {
|
||||
if (node.parent.type === "ForStatement" && node.parent.init === node) {
|
||||
return true;
|
||||
}
|
||||
return isInsideForLoopInitializer(node.parent);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a arrow function body needs braces
|
||||
* @param {ASTNode} node The arrow function node.
|
||||
@ -136,7 +152,7 @@ module.exports = {
|
||||
|
||||
context.report({
|
||||
node,
|
||||
loc: arrowBody.loc.start,
|
||||
loc: arrowBody.loc,
|
||||
messageId,
|
||||
fix(fixer) {
|
||||
const fixes = [];
|
||||
@ -178,11 +194,13 @@ module.exports = {
|
||||
* If the first token of the reutrn value is `{` or the return value is a sequence expression,
|
||||
* enclose the return value by parentheses to avoid syntax error.
|
||||
*/
|
||||
if (astUtils.isOpeningBraceToken(firstValueToken) || blockBody[0].argument.type === "SequenceExpression") {
|
||||
fixes.push(
|
||||
fixer.insertTextBefore(firstValueToken, "("),
|
||||
fixer.insertTextAfter(lastValueToken, ")")
|
||||
);
|
||||
if (astUtils.isOpeningBraceToken(firstValueToken) || blockBody[0].argument.type === "SequenceExpression" || (funcInfo.hasInOperator && isInsideForLoopInitializer(node))) {
|
||||
if (!astUtils.isParenthesised(sourceCode, blockBody[0].argument)) {
|
||||
fixes.push(
|
||||
fixer.insertTextBefore(firstValueToken, "("),
|
||||
fixer.insertTextAfter(lastValueToken, ")")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -201,7 +219,7 @@ module.exports = {
|
||||
if (always || (asNeeded && requireReturnForObjectLiteral && arrowBody.type === "ObjectExpression")) {
|
||||
context.report({
|
||||
node,
|
||||
loc: arrowBody.loc.start,
|
||||
loc: arrowBody.loc,
|
||||
messageId: "expectedBlock",
|
||||
fix(fixer) {
|
||||
const fixes = [];
|
||||
@ -245,7 +263,24 @@ module.exports = {
|
||||
}
|
||||
|
||||
return {
|
||||
"ArrowFunctionExpression:exit": validate
|
||||
"BinaryExpression[operator='in']"() {
|
||||
let info = funcInfo;
|
||||
|
||||
while (info) {
|
||||
info.hasInOperator = true;
|
||||
info = info.upper;
|
||||
}
|
||||
},
|
||||
ArrowFunctionExpression() {
|
||||
funcInfo = {
|
||||
upper: funcInfo,
|
||||
hasInOperator: false
|
||||
};
|
||||
},
|
||||
"ArrowFunctionExpression:exit"(node) {
|
||||
validate(node);
|
||||
funcInfo = funcInfo.upper;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -15,15 +15,12 @@ const astUtils = require("./utils/ast-utils");
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get location should be reported by AST node.
|
||||
* @param {ASTNode} node AST Node.
|
||||
* @returns {Location} Location information.
|
||||
* Determines if the given arrow function has block body.
|
||||
* @param {ASTNode} node `ArrowFunctionExpression` node.
|
||||
* @returns {boolean} `true` if the function has block body.
|
||||
*/
|
||||
function getLocation(node) {
|
||||
return {
|
||||
start: node.params[0].loc.start,
|
||||
end: node.params[node.params.length - 1].loc.end
|
||||
};
|
||||
function hasBlockBody(node) {
|
||||
return node.body.type === "BlockStatement";
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -75,126 +72,112 @@ module.exports = {
|
||||
const sourceCode = context.getSourceCode();
|
||||
|
||||
/**
|
||||
* Determines whether a arrow function argument end with `)`
|
||||
* @param {ASTNode} node The arrow function node.
|
||||
* @returns {void}
|
||||
* Finds opening paren of parameters for the given arrow function, if it exists.
|
||||
* It is assumed that the given arrow function has exactly one parameter.
|
||||
* @param {ASTNode} node `ArrowFunctionExpression` node.
|
||||
* @returns {Token|null} the opening paren, or `null` if the given arrow function doesn't have parens of parameters.
|
||||
*/
|
||||
function parens(node) {
|
||||
const isAsync = node.async;
|
||||
const firstTokenOfParam = sourceCode.getFirstToken(node, isAsync ? 1 : 0);
|
||||
|
||||
/**
|
||||
* Remove the parenthesis around a parameter
|
||||
* @param {Fixer} fixer Fixer
|
||||
* @returns {string} fixed parameter
|
||||
*/
|
||||
function fixParamsWithParenthesis(fixer) {
|
||||
const paramToken = sourceCode.getTokenAfter(firstTokenOfParam);
|
||||
|
||||
/*
|
||||
* ES8 allows Trailing commas in function parameter lists and calls
|
||||
* https://github.com/eslint/eslint/issues/8834
|
||||
*/
|
||||
const closingParenToken = sourceCode.getTokenAfter(paramToken, astUtils.isClosingParenToken);
|
||||
const asyncToken = isAsync ? sourceCode.getTokenBefore(firstTokenOfParam) : null;
|
||||
const shouldAddSpaceForAsync = asyncToken && (asyncToken.range[1] === firstTokenOfParam.range[0]);
|
||||
|
||||
return fixer.replaceTextRange([
|
||||
firstTokenOfParam.range[0],
|
||||
closingParenToken.range[1]
|
||||
], `${shouldAddSpaceForAsync ? " " : ""}${paramToken.value}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether there are comments inside the params or not.
|
||||
* @returns {boolean} `true` if there are comments inside of parens, else `false`
|
||||
*/
|
||||
function hasCommentsInParens() {
|
||||
if (astUtils.isOpeningParenToken(firstTokenOfParam)) {
|
||||
const closingParenToken = sourceCode.getTokenAfter(node.params[0], astUtils.isClosingParenToken);
|
||||
|
||||
return closingParenToken && sourceCode.commentsExistBetween(firstTokenOfParam, closingParenToken);
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
if (hasCommentsInParens()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// "as-needed", { "requireForBlockBody": true }: x => x
|
||||
if (
|
||||
requireForBlockBody &&
|
||||
node.params[0].type === "Identifier" &&
|
||||
!node.params[0].typeAnnotation &&
|
||||
node.body.type !== "BlockStatement" &&
|
||||
!node.returnType
|
||||
) {
|
||||
if (astUtils.isOpeningParenToken(firstTokenOfParam)) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "unexpectedParensInline",
|
||||
loc: getLocation(node),
|
||||
fix: fixParamsWithParenthesis
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
function findOpeningParenOfParams(node) {
|
||||
const tokenBeforeParams = sourceCode.getTokenBefore(node.params[0]);
|
||||
|
||||
if (
|
||||
requireForBlockBody &&
|
||||
node.body.type === "BlockStatement"
|
||||
tokenBeforeParams &&
|
||||
astUtils.isOpeningParenToken(tokenBeforeParams) &&
|
||||
node.range[0] <= tokenBeforeParams.range[0]
|
||||
) {
|
||||
if (!astUtils.isOpeningParenToken(firstTokenOfParam)) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "expectedParensBlock",
|
||||
loc: getLocation(node),
|
||||
fix(fixer) {
|
||||
return fixer.replaceText(firstTokenOfParam, `(${firstTokenOfParam.value})`);
|
||||
}
|
||||
});
|
||||
}
|
||||
return;
|
||||
return tokenBeforeParams;
|
||||
}
|
||||
|
||||
// "as-needed": x => x
|
||||
if (asNeeded &&
|
||||
node.params[0].type === "Identifier" &&
|
||||
!node.params[0].typeAnnotation &&
|
||||
!node.returnType
|
||||
) {
|
||||
if (astUtils.isOpeningParenToken(firstTokenOfParam)) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "unexpectedParens",
|
||||
loc: getLocation(node),
|
||||
fix: fixParamsWithParenthesis
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (firstTokenOfParam.type === "Identifier") {
|
||||
const after = sourceCode.getTokenAfter(firstTokenOfParam);
|
||||
/**
|
||||
* Finds closing paren of parameters for the given arrow function.
|
||||
* It is assumed that the given arrow function has parens of parameters and that it has exactly one parameter.
|
||||
* @param {ASTNode} node `ArrowFunctionExpression` node.
|
||||
* @returns {Token} the closing paren of parameters.
|
||||
*/
|
||||
function getClosingParenOfParams(node) {
|
||||
return sourceCode.getTokenAfter(node.params[0], astUtils.isClosingParenToken);
|
||||
}
|
||||
|
||||
// (x) => x
|
||||
if (after.value !== ")") {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "expectedParens",
|
||||
loc: getLocation(node),
|
||||
fix(fixer) {
|
||||
return fixer.replaceText(firstTokenOfParam, `(${firstTokenOfParam.value})`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Determines whether the given arrow function has comments inside parens of parameters.
|
||||
* It is assumed that the given arrow function has parens of parameters.
|
||||
* @param {ASTNode} node `ArrowFunctionExpression` node.
|
||||
* @param {Token} openingParen Opening paren of parameters.
|
||||
* @returns {boolean} `true` if the function has at least one comment inside of parens of parameters.
|
||||
*/
|
||||
function hasCommentsInParensOfParams(node, openingParen) {
|
||||
return sourceCode.commentsExistBetween(openingParen, getClosingParenOfParams(node));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given arrow function has unexpected tokens before opening paren of parameters,
|
||||
* in which case it will be assumed that the existing parens of parameters are necessary.
|
||||
* Only tokens within the range of the arrow function (tokens that are part of the arrow function) are taken into account.
|
||||
* Example: <T>(a) => b
|
||||
* @param {ASTNode} node `ArrowFunctionExpression` node.
|
||||
* @param {Token} openingParen Opening paren of parameters.
|
||||
* @returns {boolean} `true` if the function has at least one unexpected token.
|
||||
*/
|
||||
function hasUnexpectedTokensBeforeOpeningParen(node, openingParen) {
|
||||
const expectedCount = node.async ? 1 : 0;
|
||||
|
||||
return sourceCode.getFirstToken(node, { skip: expectedCount }) !== openingParen;
|
||||
}
|
||||
|
||||
return {
|
||||
"ArrowFunctionExpression[params.length=1]": parens
|
||||
"ArrowFunctionExpression[params.length=1]"(node) {
|
||||
const shouldHaveParens = !asNeeded || requireForBlockBody && hasBlockBody(node);
|
||||
const openingParen = findOpeningParenOfParams(node);
|
||||
const hasParens = openingParen !== null;
|
||||
const [param] = node.params;
|
||||
|
||||
if (shouldHaveParens && !hasParens) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: requireForBlockBody ? "expectedParensBlock" : "expectedParens",
|
||||
loc: param.loc,
|
||||
*fix(fixer) {
|
||||
yield fixer.insertTextBefore(param, "(");
|
||||
yield fixer.insertTextAfter(param, ")");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
!shouldHaveParens &&
|
||||
hasParens &&
|
||||
param.type === "Identifier" &&
|
||||
!param.typeAnnotation &&
|
||||
!node.returnType &&
|
||||
!hasCommentsInParensOfParams(node, openingParen) &&
|
||||
!hasUnexpectedTokensBeforeOpeningParen(node, openingParen)
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: requireForBlockBody ? "unexpectedParensInline" : "unexpectedParens",
|
||||
loc: param.loc,
|
||||
*fix(fixer) {
|
||||
const tokenBeforeOpeningParen = sourceCode.getTokenBefore(openingParen);
|
||||
const closingParen = getClosingParenOfParams(node);
|
||||
|
||||
if (
|
||||
tokenBeforeOpeningParen &&
|
||||
tokenBeforeOpeningParen.range[1] === openingParen.range[0] &&
|
||||
!astUtils.canTokensBeAdjacent(tokenBeforeOpeningParen, sourceCode.getFirstToken(param))
|
||||
) {
|
||||
yield fixer.insertTextBefore(openingParen, " ");
|
||||
}
|
||||
|
||||
// remove parens, whitespace inside parens, and possible trailing comma
|
||||
yield fixer.removeRange([openingParen.range[0], param.range[0]]);
|
||||
yield fixer.removeRange([param.range[1], closingParen.range[1]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -32,6 +32,10 @@ module.exports = {
|
||||
type: "boolean",
|
||||
default: false
|
||||
},
|
||||
ignoreGlobals: {
|
||||
type: "boolean",
|
||||
default: false
|
||||
},
|
||||
properties: {
|
||||
enum: ["always", "never"]
|
||||
},
|
||||
@ -61,8 +65,11 @@ module.exports = {
|
||||
let properties = options.properties || "";
|
||||
const ignoreDestructuring = options.ignoreDestructuring;
|
||||
const ignoreImports = options.ignoreImports;
|
||||
const ignoreGlobals = options.ignoreGlobals;
|
||||
const allow = options.allow || [];
|
||||
|
||||
let globalScope;
|
||||
|
||||
if (properties !== "always" && properties !== "never") {
|
||||
properties = "always";
|
||||
}
|
||||
@ -159,6 +166,37 @@ module.exports = {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given node represents a reference to a global variable that is not declared in the source code.
|
||||
* These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
|
||||
* @param {ASTNode} node `Identifier` node to check.
|
||||
* @returns {boolean} `true` if the node is a reference to a global variable.
|
||||
*/
|
||||
function isReferenceToGlobalVariable(node) {
|
||||
const variable = globalScope.set.get(node.name);
|
||||
|
||||
return variable && variable.defs.length === 0 &&
|
||||
variable.references.some(ref => ref.identifier === node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given node represents a reference to a property of an object in an object literal expression.
|
||||
* This allows to differentiate between a global variable that is allowed to be used as a reference, and the key
|
||||
* of the expressed object (which shouldn't be allowed).
|
||||
* @param {ASTNode} node `Identifier` node to check.
|
||||
* @returns {boolean} `true` if the node is a property name of an object literal expression
|
||||
*/
|
||||
function isPropertyNameInObjectLiteral(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
return (
|
||||
parent.type === "Property" &&
|
||||
parent.parent.type === "ObjectExpression" &&
|
||||
!parent.computed &&
|
||||
parent.key === node
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports an AST node as a rule violation.
|
||||
* @param {ASTNode} node The node to report.
|
||||
@ -174,6 +212,10 @@ module.exports = {
|
||||
|
||||
return {
|
||||
|
||||
Program() {
|
||||
globalScope = context.getScope();
|
||||
},
|
||||
|
||||
Identifier(node) {
|
||||
|
||||
/*
|
||||
@ -189,6 +231,11 @@ module.exports = {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if it's a global variable
|
||||
if (ignoreGlobals && isReferenceToGlobalVariable(node) && !isPropertyNameInObjectLiteral(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// MemberExpressions get special rules
|
||||
if (node.parent.type === "MemberExpression") {
|
||||
|
||||
|
@ -9,23 +9,12 @@
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const lodash = require("lodash");
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks whether or not a given node is an `Identifier` node which was named a given name.
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @param {string} name An expected name of the node.
|
||||
* @returns {boolean} `true` if the node is an `Identifier` node which was named as expected.
|
||||
*/
|
||||
function isIdentifier(node, name) {
|
||||
return node.type === "Identifier" && node.name === name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a given code path segment is unreachable.
|
||||
* @param {CodePathSegment} segment A CodePathSegment to check.
|
||||
@ -165,7 +154,7 @@ module.exports = {
|
||||
let hasReturnValue = Boolean(argument);
|
||||
|
||||
if (treatUndefinedAsUnspecified && hasReturnValue) {
|
||||
hasReturnValue = !isIdentifier(argument, "undefined") && argument.operator !== "void";
|
||||
hasReturnValue = !astUtils.isSpecificId(argument, "undefined") && argument.operator !== "void";
|
||||
}
|
||||
|
||||
if (!funcInfo.hasReturn) {
|
||||
|
@ -50,6 +50,7 @@ function isPossibleConstructor(node) {
|
||||
case "MemberExpression":
|
||||
case "CallExpression":
|
||||
case "NewExpression":
|
||||
case "ChainExpression":
|
||||
case "YieldExpression":
|
||||
case "TaggedTemplateExpression":
|
||||
case "MetaProperty":
|
||||
@ -59,9 +60,36 @@ function isPossibleConstructor(node) {
|
||||
return node.name !== "undefined";
|
||||
|
||||
case "AssignmentExpression":
|
||||
return isPossibleConstructor(node.right);
|
||||
if (["=", "&&="].includes(node.operator)) {
|
||||
return isPossibleConstructor(node.right);
|
||||
}
|
||||
|
||||
if (["||=", "??="].includes(node.operator)) {
|
||||
return (
|
||||
isPossibleConstructor(node.left) ||
|
||||
isPossibleConstructor(node.right)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* All other assignment operators are mathematical assignment operators (arithmetic or bitwise).
|
||||
* An assignment expression with a mathematical operator can either evaluate to a primitive value,
|
||||
* or throw, depending on the operands. Thus, it cannot evaluate to a constructor function.
|
||||
*/
|
||||
return false;
|
||||
|
||||
case "LogicalExpression":
|
||||
|
||||
/*
|
||||
* If the && operator short-circuits, the left side was falsy and therefore not a constructor, and if
|
||||
* it doesn't short-circuit, it takes the value from the right side, so the right side must always be a
|
||||
* possible constructor. A future improvement could verify that the left side could be truthy by
|
||||
* excluding falsy literals.
|
||||
*/
|
||||
if (node.operator === "&&") {
|
||||
return isPossibleConstructor(node.right);
|
||||
}
|
||||
|
||||
return (
|
||||
isPossibleConstructor(node.left) ||
|
||||
isPossibleConstructor(node.right)
|
||||
|
@ -457,11 +457,18 @@ module.exports = {
|
||||
|
||||
return {
|
||||
IfStatement(node) {
|
||||
if (node.parent.type !== "IfStatement") {
|
||||
const parent = node.parent;
|
||||
const isElseIf = parent.type === "IfStatement" && parent.alternate === node;
|
||||
|
||||
if (!isElseIf) {
|
||||
|
||||
// This is a top `if`, check the whole `if-else-if` chain
|
||||
prepareIfChecks(node).forEach(preparedCheck => {
|
||||
preparedCheck.check();
|
||||
});
|
||||
}
|
||||
|
||||
// Skip `else if`, it's already checked (when the top `if` was visited)
|
||||
},
|
||||
|
||||
WhileStatement(node) {
|
||||
|
@ -52,31 +52,37 @@ module.exports = {
|
||||
*/
|
||||
function checkDotLocation(node) {
|
||||
const property = node.property;
|
||||
const dot = sourceCode.getTokenBefore(property);
|
||||
|
||||
// `obj` expression can be parenthesized, but those paren tokens are not a part of the `obj` node.
|
||||
const tokenBeforeDot = sourceCode.getTokenBefore(dot);
|
||||
|
||||
const textBeforeDot = sourceCode.getText().slice(tokenBeforeDot.range[1], dot.range[0]);
|
||||
const textAfterDot = sourceCode.getText().slice(dot.range[1], property.range[0]);
|
||||
const dotToken = sourceCode.getTokenBefore(property);
|
||||
|
||||
if (onObject) {
|
||||
if (!astUtils.isTokenOnSameLine(tokenBeforeDot, dot)) {
|
||||
const neededTextAfterToken = astUtils.isDecimalIntegerNumericToken(tokenBeforeDot) ? " " : "";
|
||||
|
||||
// `obj` expression can be parenthesized, but those paren tokens are not a part of the `obj` node.
|
||||
const tokenBeforeDot = sourceCode.getTokenBefore(dotToken);
|
||||
|
||||
if (!astUtils.isTokenOnSameLine(tokenBeforeDot, dotToken)) {
|
||||
context.report({
|
||||
node,
|
||||
loc: dot.loc,
|
||||
loc: dotToken.loc,
|
||||
messageId: "expectedDotAfterObject",
|
||||
fix: fixer => fixer.replaceTextRange([tokenBeforeDot.range[1], property.range[0]], `${neededTextAfterToken}.${textBeforeDot}${textAfterDot}`)
|
||||
*fix(fixer) {
|
||||
if (dotToken.value.startsWith(".") && astUtils.isDecimalIntegerNumericToken(tokenBeforeDot)) {
|
||||
yield fixer.insertTextAfter(tokenBeforeDot, ` ${dotToken.value}`);
|
||||
} else {
|
||||
yield fixer.insertTextAfter(tokenBeforeDot, dotToken.value);
|
||||
}
|
||||
yield fixer.remove(dotToken);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (!astUtils.isTokenOnSameLine(dot, property)) {
|
||||
} else if (!astUtils.isTokenOnSameLine(dotToken, property)) {
|
||||
context.report({
|
||||
node,
|
||||
loc: dot.loc,
|
||||
loc: dotToken.loc,
|
||||
messageId: "expectedDotBeforeProperty",
|
||||
fix: fixer => fixer.replaceTextRange([tokenBeforeDot.range[1], property.range[0]], `${textBeforeDot}${textAfterDot}.`)
|
||||
*fix(fixer) {
|
||||
yield fixer.remove(dotToken);
|
||||
yield fixer.insertTextBefore(property, dotToken.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -87,28 +87,36 @@ module.exports = {
|
||||
data: {
|
||||
key: formattedValue
|
||||
},
|
||||
fix(fixer) {
|
||||
*fix(fixer) {
|
||||
const leftBracket = sourceCode.getTokenAfter(node.object, astUtils.isOpeningBracketToken);
|
||||
const rightBracket = sourceCode.getLastToken(node);
|
||||
const nextToken = sourceCode.getTokenAfter(node);
|
||||
|
||||
if (sourceCode.getFirstTokenBetween(leftBracket, rightBracket, { includeComments: true, filter: astUtils.isCommentToken })) {
|
||||
|
||||
// Don't perform any fixes if there are comments inside the brackets.
|
||||
return null;
|
||||
// Don't perform any fixes if there are comments inside the brackets.
|
||||
if (sourceCode.commentsExistBetween(leftBracket, rightBracket)) {
|
||||
return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
|
||||
}
|
||||
|
||||
const tokenAfterProperty = sourceCode.getTokenAfter(rightBracket);
|
||||
const needsSpaceAfterProperty = tokenAfterProperty &&
|
||||
rightBracket.range[1] === tokenAfterProperty.range[0] &&
|
||||
!astUtils.canTokensBeAdjacent(String(value), tokenAfterProperty);
|
||||
|
||||
const textBeforeDot = astUtils.isDecimalInteger(node.object) ? " " : "";
|
||||
const textAfterProperty = needsSpaceAfterProperty ? " " : "";
|
||||
|
||||
return fixer.replaceTextRange(
|
||||
// Replace the brackets by an identifier.
|
||||
if (!node.optional) {
|
||||
yield fixer.insertTextBefore(
|
||||
leftBracket,
|
||||
astUtils.isDecimalInteger(node.object) ? " ." : "."
|
||||
);
|
||||
}
|
||||
yield fixer.replaceTextRange(
|
||||
[leftBracket.range[0], rightBracket.range[1]],
|
||||
`${textBeforeDot}.${value}${textAfterProperty}`
|
||||
value
|
||||
);
|
||||
|
||||
// Insert a space after the property if it will be connected to the next token.
|
||||
if (
|
||||
nextToken &&
|
||||
rightBracket.range[1] === nextToken.range[0] &&
|
||||
!astUtils.canTokensBeAdjacent(String(value), nextToken)
|
||||
) {
|
||||
yield fixer.insertTextAfter(node, " ");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -141,29 +149,24 @@ module.exports = {
|
||||
data: {
|
||||
key: node.property.name
|
||||
},
|
||||
fix(fixer) {
|
||||
const dot = sourceCode.getTokenBefore(node.property);
|
||||
const textAfterDot = sourceCode.text.slice(dot.range[1], node.property.range[0]);
|
||||
*fix(fixer) {
|
||||
const dotToken = sourceCode.getTokenBefore(node.property);
|
||||
|
||||
if (textAfterDot.trim()) {
|
||||
|
||||
// Don't perform any fixes if there are comments between the dot and the property name.
|
||||
return null;
|
||||
// A statement that starts with `let[` is parsed as a destructuring variable declaration, not a MemberExpression.
|
||||
if (node.object.type === "Identifier" && node.object.name === "let" && !node.optional) {
|
||||
return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
|
||||
}
|
||||
|
||||
if (node.object.type === "Identifier" && node.object.name === "let") {
|
||||
|
||||
/*
|
||||
* A statement that starts with `let[` is parsed as a destructuring variable declaration, not
|
||||
* a MemberExpression.
|
||||
*/
|
||||
return null;
|
||||
// Don't perform any fixes if there are comments between the dot and the property name.
|
||||
if (sourceCode.commentsExistBetween(dotToken, node.property)) {
|
||||
return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
|
||||
}
|
||||
|
||||
return fixer.replaceTextRange(
|
||||
[dot.range[0], node.property.range[1]],
|
||||
`[${textAfterDot}"${node.property.name}"]`
|
||||
);
|
||||
// Replace the identifier to brackets.
|
||||
if (!node.optional) {
|
||||
yield fixer.remove(dotToken);
|
||||
}
|
||||
yield fixer.replaceText(node.property, `["${node.property.name}"]`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -126,15 +126,24 @@ module.exports = {
|
||||
messageId: "unexpectedWhitespace",
|
||||
fix(fixer) {
|
||||
|
||||
// Don't remove comments.
|
||||
if (sourceCode.commentsExistBetween(leftToken, rightToken)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If `?.` exsits, it doesn't hide no-undexpected-multiline errors
|
||||
if (node.optional) {
|
||||
return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], "?.");
|
||||
}
|
||||
|
||||
/*
|
||||
* Only autofix if there is no newline
|
||||
* https://github.com/eslint/eslint/issues/7787
|
||||
*/
|
||||
if (!hasNewline) {
|
||||
return fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
|
||||
if (hasNewline) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
return fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
|
||||
}
|
||||
});
|
||||
} else if (!never && !hasWhitespace) {
|
||||
@ -149,6 +158,9 @@ module.exports = {
|
||||
},
|
||||
messageId: "missing",
|
||||
fix(fixer) {
|
||||
if (node.optional) {
|
||||
return null; // Not sure if inserting a space to either before/after `?.` token.
|
||||
}
|
||||
return fixer.insertTextBefore(rightToken, " ");
|
||||
}
|
||||
});
|
||||
@ -161,7 +173,31 @@ module.exports = {
|
||||
},
|
||||
messageId: "unexpectedNewline",
|
||||
fix(fixer) {
|
||||
return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], " ");
|
||||
|
||||
/*
|
||||
* Only autofix if there is no newline
|
||||
* https://github.com/eslint/eslint/issues/7787
|
||||
* But if `?.` exsits, it doesn't hide no-undexpected-multiline errors
|
||||
*/
|
||||
if (!node.optional) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Don't remove comments.
|
||||
if (sourceCode.commentsExistBetween(leftToken, rightToken)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const range = [leftToken.range[1], rightToken.range[0]];
|
||||
const qdToken = sourceCode.getTokenAfter(leftToken);
|
||||
|
||||
if (qdToken.range[0] === leftToken.range[1]) {
|
||||
return fixer.replaceTextRange(range, "?. ");
|
||||
}
|
||||
if (qdToken.range[1] === rightToken.range[0]) {
|
||||
return fixer.replaceTextRange(range, " ?.");
|
||||
}
|
||||
return fixer.replaceTextRange(range, " ?. ");
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -172,7 +208,7 @@ module.exports = {
|
||||
const lastToken = sourceCode.getLastToken(node);
|
||||
const lastCalleeToken = sourceCode.getLastToken(node.callee);
|
||||
const parenToken = sourceCode.getFirstTokenBetween(lastCalleeToken, lastToken, astUtils.isOpeningParenToken);
|
||||
const prevToken = parenToken && sourceCode.getTokenBefore(parenToken);
|
||||
const prevToken = parenToken && sourceCode.getTokenBefore(parenToken, astUtils.isNotQuestionDotToken);
|
||||
|
||||
// Parens in NewExpression are optional
|
||||
if (!(parenToken && parenToken.range[1] < node.range[1])) {
|
||||
|
@ -117,10 +117,7 @@ module.exports = {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
return node.type === "CallExpression" &&
|
||||
node.callee.type === "MemberExpression" &&
|
||||
node.callee.object.name === objName &&
|
||||
node.callee.property.name === funcName;
|
||||
return node.type === "CallExpression" && astUtils.isSpecificMemberAccess(node.callee, objName, funcName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -218,7 +218,7 @@ module.exports = {
|
||||
}
|
||||
|
||||
case "ArrowFunctionExpression": {
|
||||
const firstToken = sourceCode.getFirstToken(node);
|
||||
const firstToken = sourceCode.getFirstToken(node, { skip: (node.async ? 1 : 0) });
|
||||
|
||||
if (!astUtils.isOpeningParenToken(firstToken)) {
|
||||
|
||||
|
@ -13,7 +13,8 @@ const ACCEPTABLE_PARENTS = [
|
||||
"CallExpression",
|
||||
"ConditionalExpression",
|
||||
"Program",
|
||||
"VariableDeclaration"
|
||||
"VariableDeclaration",
|
||||
"ChainExpression"
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @fileoverview Rule that warns when identifier names that are
|
||||
* blacklisted in the configuration are used.
|
||||
* specified in the configuration are used.
|
||||
* @author Keith Cirkel (http://keithcirkel.co.uk)
|
||||
*/
|
||||
|
||||
@ -111,6 +111,9 @@ function isShorthandPropertyDefinition(node) {
|
||||
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
replacedBy: ["id-denylist"],
|
||||
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
@ -128,25 +131,25 @@ module.exports = {
|
||||
uniqueItems: true
|
||||
},
|
||||
messages: {
|
||||
blacklisted: "Identifier '{{name}}' is blacklisted."
|
||||
restricted: "Identifier '{{name}}' is restricted."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
|
||||
const blacklist = new Set(context.options);
|
||||
const denyList = new Set(context.options);
|
||||
const reportedNodes = new Set();
|
||||
|
||||
let globalScope;
|
||||
|
||||
/**
|
||||
* Checks whether the given name is blacklisted.
|
||||
* Checks whether the given name is restricted.
|
||||
* @param {string} name The name to check.
|
||||
* @returns {boolean} `true` if the name is blacklisted.
|
||||
* @returns {boolean} `true` if the name is restricted.
|
||||
* @private
|
||||
*/
|
||||
function isBlacklisted(name) {
|
||||
return blacklist.has(name);
|
||||
function isRestricted(name) {
|
||||
return denyList.has(name);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -172,8 +175,8 @@ module.exports = {
|
||||
|
||||
/*
|
||||
* Member access has special rules for checking property names.
|
||||
* Read access to a property with a blacklisted name is allowed, because it can be on an object that user has no control over.
|
||||
* Write access isn't allowed, because it potentially creates a new property with a blacklisted name.
|
||||
* Read access to a property with a restricted name is allowed, because it can be on an object that user has no control over.
|
||||
* Write access isn't allowed, because it potentially creates a new property with a restricted name.
|
||||
*/
|
||||
if (
|
||||
parent.type === "MemberExpression" &&
|
||||
@ -205,7 +208,7 @@ module.exports = {
|
||||
if (!reportedNodes.has(node)) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "blacklisted",
|
||||
messageId: "restricted",
|
||||
data: {
|
||||
name: node.name
|
||||
}
|
||||
@ -221,7 +224,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
Identifier(node) {
|
||||
if (isBlacklisted(node.name) && shouldCheck(node)) {
|
||||
if (isRestricted(node.name) && shouldCheck(node)) {
|
||||
report(node);
|
||||
}
|
||||
}
|
||||
|
230
eslint/lib/rules/id-denylist.js
Normal file
230
eslint/lib/rules/id-denylist.js
Normal file
@ -0,0 +1,230 @@
|
||||
/**
|
||||
* @fileoverview Rule that warns when identifier names that are
|
||||
* specified in the configuration are used.
|
||||
* @author Keith Cirkel (http://keithcirkel.co.uk)
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks whether the given node represents assignment target in a normal assignment or destructuring.
|
||||
* @param {ASTNode} node The node to check.
|
||||
* @returns {boolean} `true` if the node is assignment target.
|
||||
*/
|
||||
function isAssignmentTarget(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
return (
|
||||
|
||||
// normal assignment
|
||||
(
|
||||
parent.type === "AssignmentExpression" &&
|
||||
parent.left === node
|
||||
) ||
|
||||
|
||||
// destructuring
|
||||
parent.type === "ArrayPattern" ||
|
||||
parent.type === "RestElement" ||
|
||||
(
|
||||
parent.type === "Property" &&
|
||||
parent.value === node &&
|
||||
parent.parent.type === "ObjectPattern"
|
||||
) ||
|
||||
(
|
||||
parent.type === "AssignmentPattern" &&
|
||||
parent.left === node
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given node represents an imported name that is renamed in the same import/export specifier.
|
||||
*
|
||||
* Examples:
|
||||
* import { a as b } from 'mod'; // node `a` is renamed import
|
||||
* export { a as b } from 'mod'; // node `a` is renamed import
|
||||
* @param {ASTNode} node `Identifier` node to check.
|
||||
* @returns {boolean} `true` if the node is a renamed import.
|
||||
*/
|
||||
function isRenamedImport(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
return (
|
||||
(
|
||||
parent.type === "ImportSpecifier" &&
|
||||
parent.imported !== parent.local &&
|
||||
parent.imported === node
|
||||
) ||
|
||||
(
|
||||
parent.type === "ExportSpecifier" &&
|
||||
parent.parent.source && // re-export
|
||||
parent.local !== parent.exported &&
|
||||
parent.local === node
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given node is a renamed identifier node in an ObjectPattern destructuring.
|
||||
*
|
||||
* Examples:
|
||||
* const { a : b } = foo; // node `a` is renamed node.
|
||||
* @param {ASTNode} node `Identifier` node to check.
|
||||
* @returns {boolean} `true` if the node is a renamed node in an ObjectPattern destructuring.
|
||||
*/
|
||||
function isRenamedInDestructuring(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
return (
|
||||
(
|
||||
!parent.computed &&
|
||||
parent.type === "Property" &&
|
||||
parent.parent.type === "ObjectPattern" &&
|
||||
parent.value !== node &&
|
||||
parent.key === node
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given node represents shorthand definition of a property in an object literal.
|
||||
* @param {ASTNode} node `Identifier` node to check.
|
||||
* @returns {boolean} `true` if the node is a shorthand property definition.
|
||||
*/
|
||||
function isShorthandPropertyDefinition(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
return (
|
||||
parent.type === "Property" &&
|
||||
parent.parent.type === "ObjectExpression" &&
|
||||
parent.shorthand
|
||||
);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "disallow specified identifiers",
|
||||
category: "Stylistic Issues",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/rules/id-denylist"
|
||||
},
|
||||
|
||||
schema: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string"
|
||||
},
|
||||
uniqueItems: true
|
||||
},
|
||||
messages: {
|
||||
restricted: "Identifier '{{name}}' is restricted."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
|
||||
const denyList = new Set(context.options);
|
||||
const reportedNodes = new Set();
|
||||
|
||||
let globalScope;
|
||||
|
||||
/**
|
||||
* Checks whether the given name is restricted.
|
||||
* @param {string} name The name to check.
|
||||
* @returns {boolean} `true` if the name is restricted.
|
||||
* @private
|
||||
*/
|
||||
function isRestricted(name) {
|
||||
return denyList.has(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given node represents a reference to a global variable that is not declared in the source code.
|
||||
* These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
|
||||
* @param {ASTNode} node `Identifier` node to check.
|
||||
* @returns {boolean} `true` if the node is a reference to a global variable.
|
||||
*/
|
||||
function isReferenceToGlobalVariable(node) {
|
||||
const variable = globalScope.set.get(node.name);
|
||||
|
||||
return variable && variable.defs.length === 0 &&
|
||||
variable.references.some(ref => ref.identifier === node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given node should be checked.
|
||||
* @param {ASTNode} node `Identifier` node.
|
||||
* @returns {boolean} `true` if the node should be checked.
|
||||
*/
|
||||
function shouldCheck(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
/*
|
||||
* Member access has special rules for checking property names.
|
||||
* Read access to a property with a restricted name is allowed, because it can be on an object that user has no control over.
|
||||
* Write access isn't allowed, because it potentially creates a new property with a restricted name.
|
||||
*/
|
||||
if (
|
||||
parent.type === "MemberExpression" &&
|
||||
parent.property === node &&
|
||||
!parent.computed
|
||||
) {
|
||||
return isAssignmentTarget(parent);
|
||||
}
|
||||
|
||||
return (
|
||||
parent.type !== "CallExpression" &&
|
||||
parent.type !== "NewExpression" &&
|
||||
!isRenamedImport(node) &&
|
||||
!isRenamedInDestructuring(node) &&
|
||||
!(
|
||||
isReferenceToGlobalVariable(node) &&
|
||||
!isShorthandPropertyDefinition(node)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports an AST node as a rule violation.
|
||||
* @param {ASTNode} node The node to report.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function report(node) {
|
||||
if (!reportedNodes.has(node)) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "restricted",
|
||||
data: {
|
||||
name: node.name
|
||||
}
|
||||
});
|
||||
reportedNodes.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Program() {
|
||||
globalScope = context.getScope();
|
||||
},
|
||||
|
||||
Identifier(node) {
|
||||
if (isRestricted(node.name) && shouldCheck(node)) {
|
||||
report(node);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
@ -39,6 +39,13 @@ module.exports = {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
exceptionPatterns: {
|
||||
type: "array",
|
||||
uniqueItems: true,
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
properties: {
|
||||
enum: ["always", "never"]
|
||||
}
|
||||
@ -57,14 +64,20 @@ module.exports = {
|
||||
const minLength = typeof options.min !== "undefined" ? options.min : 2;
|
||||
const maxLength = typeof options.max !== "undefined" ? options.max : Infinity;
|
||||
const properties = options.properties !== "never";
|
||||
const exceptions = (options.exceptions ? options.exceptions : [])
|
||||
.reduce((obj, item) => {
|
||||
obj[item] = true;
|
||||
|
||||
return obj;
|
||||
}, {});
|
||||
const exceptions = new Set(options.exceptions);
|
||||
const exceptionPatterns = (options.exceptionPatterns || []).map(pattern => new RegExp(pattern, "u"));
|
||||
const reportedNode = new Set();
|
||||
|
||||
/**
|
||||
* Checks if a string matches the provided exception patterns
|
||||
* @param {string} name The string to check.
|
||||
* @returns {boolean} if the string is a match
|
||||
* @private
|
||||
*/
|
||||
function matchesExceptionPattern(name) {
|
||||
return exceptionPatterns.some(pattern => pattern.test(name));
|
||||
}
|
||||
|
||||
const SUPPORTED_EXPRESSIONS = {
|
||||
MemberExpression: properties && function(parent) {
|
||||
return !parent.computed && (
|
||||
@ -112,7 +125,7 @@ module.exports = {
|
||||
const isShort = name.length < minLength;
|
||||
const isLong = name.length > maxLength;
|
||||
|
||||
if (!(isShort || isLong) || exceptions[name]) {
|
||||
if (!(isShort || isLong) || exceptions.has(name) || matchesExceptionPattern(name)) {
|
||||
return; // Nothing to report
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,8 @@ module.exports = {
|
||||
type: "boolean",
|
||||
default: false
|
||||
}
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
],
|
||||
messages: {
|
||||
|
@ -32,6 +32,7 @@ const KNOWN_NODES = new Set([
|
||||
"BreakStatement",
|
||||
"CallExpression",
|
||||
"CatchClause",
|
||||
"ChainExpression",
|
||||
"ClassBody",
|
||||
"ClassDeclaration",
|
||||
"ClassExpression",
|
||||
@ -934,6 +935,24 @@ module.exports = {
|
||||
parameterParens.add(openingParen);
|
||||
parameterParens.add(closingParen);
|
||||
|
||||
/*
|
||||
* If `?.` token exists, set desired offset for that.
|
||||
* This logic is copied from `MemberExpression`'s.
|
||||
*/
|
||||
if (node.optional) {
|
||||
const dotToken = sourceCode.getTokenAfter(node.callee, astUtils.isQuestionDotToken);
|
||||
const calleeParenCount = sourceCode.getTokensBetween(node.callee, dotToken, { filter: astUtils.isClosingParenToken }).length;
|
||||
const firstTokenOfCallee = calleeParenCount
|
||||
? sourceCode.getTokenBefore(node.callee, { skip: calleeParenCount - 1 })
|
||||
: sourceCode.getFirstToken(node.callee);
|
||||
const lastTokenOfCallee = sourceCode.getTokenBefore(dotToken);
|
||||
const offsetBase = lastTokenOfCallee.loc.end.line === openingParen.loc.start.line
|
||||
? lastTokenOfCallee
|
||||
: firstTokenOfCallee;
|
||||
|
||||
offsets.setDesiredOffset(dotToken, offsetBase, 1);
|
||||
}
|
||||
|
||||
const offsetAfterToken = node.callee.type === "TaggedTemplateExpression" ? sourceCode.getFirstToken(node.callee.quasi) : openingParen;
|
||||
const offsetToken = sourceCode.getTokenBefore(offsetAfterToken);
|
||||
|
||||
@ -1065,16 +1084,17 @@ module.exports = {
|
||||
},
|
||||
|
||||
ArrowFunctionExpression(node) {
|
||||
const firstToken = sourceCode.getFirstToken(node);
|
||||
const maybeOpeningParen = sourceCode.getFirstToken(node, { skip: node.async ? 1 : 0 });
|
||||
|
||||
if (astUtils.isOpeningParenToken(firstToken)) {
|
||||
const openingParen = firstToken;
|
||||
if (astUtils.isOpeningParenToken(maybeOpeningParen)) {
|
||||
const openingParen = maybeOpeningParen;
|
||||
const closingParen = sourceCode.getTokenBefore(node.body, astUtils.isClosingParenToken);
|
||||
|
||||
parameterParens.add(openingParen);
|
||||
parameterParens.add(closingParen);
|
||||
addElementListIndent(node.params, openingParen, closingParen, options.FunctionExpression.parameters);
|
||||
}
|
||||
|
||||
addBlocklessNodeIndent(node.body);
|
||||
},
|
||||
|
||||
|
@ -57,6 +57,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
|
||||
"guard-for-in": () => require("./guard-for-in"),
|
||||
"handle-callback-err": () => require("./handle-callback-err"),
|
||||
"id-blacklist": () => require("./id-blacklist"),
|
||||
"id-denylist": () => require("./id-denylist"),
|
||||
"id-length": () => require("./id-length"),
|
||||
"id-match": () => require("./id-match"),
|
||||
"implicit-arrow-linebreak": () => require("./implicit-arrow-linebreak"),
|
||||
@ -176,6 +177,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
|
||||
"no-plusplus": () => require("./no-plusplus"),
|
||||
"no-process-env": () => require("./no-process-env"),
|
||||
"no-process-exit": () => require("./no-process-exit"),
|
||||
"no-promise-executor-return": () => require("./no-promise-executor-return"),
|
||||
"no-proto": () => require("./no-proto"),
|
||||
"no-prototype-builtins": () => require("./no-prototype-builtins"),
|
||||
"no-redeclare": () => require("./no-redeclare"),
|
||||
@ -212,6 +214,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
|
||||
"no-unmodified-loop-condition": () => require("./no-unmodified-loop-condition"),
|
||||
"no-unneeded-ternary": () => require("./no-unneeded-ternary"),
|
||||
"no-unreachable": () => require("./no-unreachable"),
|
||||
"no-unreachable-loop": () => require("./no-unreachable-loop"),
|
||||
"no-unsafe-finally": () => require("./no-unsafe-finally"),
|
||||
"no-unsafe-negation": () => require("./no-unsafe-negation"),
|
||||
"no-unused-expressions": () => require("./no-unused-expressions"),
|
||||
|
@ -433,11 +433,15 @@ module.exports = {
|
||||
tokenBeforeColon = sourceCode.getTokenBefore(nextColon, { includeComments: true }),
|
||||
tokenAfterColon = sourceCode.getTokenAfter(nextColon, { includeComments: true }),
|
||||
isKeySide = side === "key",
|
||||
locStart = isKeySide ? tokenBeforeColon.loc.start : tokenAfterColon.loc.start,
|
||||
isExtra = diff > 0,
|
||||
diffAbs = Math.abs(diff),
|
||||
spaces = Array(diffAbs + 1).join(" ");
|
||||
|
||||
const locStart = isKeySide ? tokenBeforeColon.loc.end : nextColon.loc.start;
|
||||
const locEnd = isKeySide ? nextColon.loc.start : tokenAfterColon.loc.start;
|
||||
const missingLoc = isKeySide ? tokenBeforeColon.loc : tokenAfterColon.loc;
|
||||
const loc = isExtra ? { start: locStart, end: locEnd } : missingLoc;
|
||||
|
||||
if ((
|
||||
diff && mode === "strict" ||
|
||||
diff < 0 && mode === "minimum" ||
|
||||
@ -482,7 +486,7 @@ module.exports = {
|
||||
|
||||
context.report({
|
||||
node: property[side],
|
||||
loc: locStart,
|
||||
loc,
|
||||
messageId,
|
||||
data: {
|
||||
computed: property.computed ? "computed " : "",
|
||||
|
@ -126,7 +126,7 @@ module.exports = {
|
||||
!sourceCode.isSpaceBetweenTokens(prevToken, token)
|
||||
) {
|
||||
context.report({
|
||||
loc: token.loc.start,
|
||||
loc: token.loc,
|
||||
messageId: "expectedBefore",
|
||||
data: token,
|
||||
fix(fixer) {
|
||||
@ -178,7 +178,7 @@ module.exports = {
|
||||
!sourceCode.isSpaceBetweenTokens(token, nextToken)
|
||||
) {
|
||||
context.report({
|
||||
loc: token.loc.start,
|
||||
loc: token.loc,
|
||||
messageId: "expectedAfter",
|
||||
data: token,
|
||||
fix(fixer) {
|
||||
|
@ -383,11 +383,22 @@ module.exports = {
|
||||
return;
|
||||
}
|
||||
|
||||
const loc = {
|
||||
start: {
|
||||
line: lineNumber,
|
||||
column: 0
|
||||
},
|
||||
end: {
|
||||
line: lineNumber,
|
||||
column: textToMeasure.length
|
||||
}
|
||||
};
|
||||
|
||||
if (commentLengthApplies) {
|
||||
if (lineLength > maxCommentLength) {
|
||||
context.report({
|
||||
node,
|
||||
loc: { line: lineNumber, column: 0 },
|
||||
loc,
|
||||
messageId: "maxComment",
|
||||
data: {
|
||||
lineLength,
|
||||
@ -398,7 +409,7 @@ module.exports = {
|
||||
} else if (lineLength > maxLength) {
|
||||
context.report({
|
||||
node,
|
||||
loc: { line: lineNumber, column: 0 },
|
||||
loc,
|
||||
messageId: "max",
|
||||
data: {
|
||||
lineLength,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user