import 7.12.1 upstream release

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
Thomas Lamprecht 2020-10-22 13:01:02 +02:00
parent a4a2572412
commit 6f03646270
361 changed files with 18151 additions and 12845 deletions

View File

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

View File

@ -95,12 +95,8 @@ module.exports = {
files: ["lib/rules/*", "tools/internal-rules/*"], files: ["lib/rules/*", "tools/internal-rules/*"],
excludedFiles: ["index.js"], excludedFiles: ["index.js"],
rules: { rules: {
"internal-rules/no-invalid-meta": "error" "internal-rules/no-invalid-meta": "error",
"internal-rules/consistent-meta-messages": "error"
/*
* TODO: enable it when all the rules using meta.messages
* "internal-rules/consistent-meta-messages": "error"
*/
} }
}, },
{ {

17
eslint/.github/CODEOWNERS.md vendored Normal file
View 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

View File

@ -22,7 +22,7 @@
* **Node Version:** * **Node Version:**
* **npm 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:** **Please show your full configuration:**

View File

@ -32,7 +32,7 @@ assignees: ''
* **Node Version:** * **Node Version:**
* **npm 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:** **Please show your full configuration:**

View File

@ -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 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) * [`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) * [`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) * [`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) * [`d55490f`](https://github.com/eslint/eslint/commit/d55490fa73ff69416de375e4c1cd67b6edba531c) Sponsors: Sync README with website (ESLint Jenkins)
v7.1.0 - May 22, 2020 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) * [`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) * [`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) * [`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) * [`0db3b1d`](https://github.com/eslint/eslint/commit/0db3b1d5cc5e4e1de21462679581b7a4d89ff36e) Sponsors: Sync README with website (ESLint Jenkins)
v7.0.0 - May 8, 2020 v7.0.0 - May 8, 2020
* [`b98d8bd`](https://github.com/eslint/eslint/commit/b98d8bda4630fe8278c5aa2b6650630770568fe5) Upgrade: eslint-release@2.0.0 (#13271) (Kai Cataldo) * [`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) * [`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) * [`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) * [`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 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) * [`0b1d65a`](https://github.com/eslint/eslint/commit/0b1d65a45aa5dfe08cd596c420490e81b546317e) Update: Improve report location for array-callback-return (refs #12334) (#13109) (Milos Djermanovic)

View File

@ -550,7 +550,7 @@ target.mocha = () => {
errors++; 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) { if (lastReturn.code !== 0) {
errors++; errors++;
} }

View File

@ -1,4 +1,4 @@
[![NPM version](https://img.shields.io/npm/v/eslint.svg)](https://www.npmjs.com/package/eslint) [![npm version](https://img.shields.io/npm/v/eslint.svg)](https://www.npmjs.com/package/eslint)
[![Downloads](https://img.shields.io/npm/dm/eslint.svg)](https://www.npmjs.com/package/eslint) [![Downloads](https://img.shields.io/npm/dm/eslint.svg)](https://www.npmjs.com/package/eslint)
[![Build Status](https://github.com/eslint/eslint/workflows/CI/badge.svg)](https://github.com/eslint/eslint/actions) [![Build Status](https://github.com/eslint/eslint/workflows/CI/badge.svg)](https://github.com/eslint/eslint/actions)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Feslint%2Feslint.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Feslint%2Feslint?ref=badge_shield) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Feslint%2Feslint.svg?type=shield)](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) 4. [Filing Issues](#filing-issues)
5. [Frequently Asked Questions](#faq) 5. [Frequently Asked Questions](#faq)
6. [Releases](#releases) 6. [Releases](#releases)
7. [Semantic Versioning Policy](#semantic-versioning-policy) 7. [Security Policy](#security-policy)
8. [License](#license) 8. [Semantic Versioning Policy](#semantic-versioning-policy)
9. [Team](#team) 9. [License](#license)
10. [Sponsors](#sponsors) 10. [Team](#team)
11. [Technology Sponsors](#technology-sponsors) 11. [Sponsors](#sponsors)
12. [Technology Sponsors](#technology-sponsors)
## <a name="installation-and-usage"></a>Installation and Usage ## <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? ### 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? ### 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. 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 ## <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: 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) * 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). * A bug fix to the CLI or core (including formatters).
* Improvements to documentation. * Improvements to documentation.
* Non-user-facing changes such as refactoring code, adding, deleting, or modifying tests, and increasing test coverage. * 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). * Re-releasing after a failed release (i.e., publishing a release that doesn't work for anyone).
* Minor release (might break your lint build) * 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 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. * An existing rule is deprecated.
* A new CLI capability is created. * A new CLI capability is created.
* New capabilities to the public API are added (new classes, new methods, new arguments to existing methods, etc.). * New capabilities to the public API are added (new classes, new methods, new arguments to existing methods, etc.).
* A new formatter is created. * 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) * 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). * `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 errors by default. * A new option to an existing rule that results in ESLint reporting more linting errors by default.
* An existing formatter is removed. * 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 ## <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 /> <img src="https://github.com/kaicataldo.png?s=75" width="75" height="75"><br />
Kai Cataldo Kai Cataldo
</a> </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> </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 /> <img src="https://github.com/aladdin-add.png?s=75" width="75" height="75"><br />
薛定谔的猫 薛定谔的猫
</a> </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> </td></tr></tbody></table>
@ -233,6 +243,11 @@ The people who review and fix bugs and help triage issues.
Pig Fang Pig Fang
</a> </a>
</td><td align="center" valign="top" width="11%"> </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"> <a href="https://github.com/yeonjuan">
<img src="https://github.com/yeonjuan.png?s=75" width="75" height="75"><br /> <img src="https://github.com/yeonjuan.png?s=75" width="75" height="75"><br />
YeonJuan YeonJuan
@ -248,12 +263,15 @@ The following companies, organizations, and individuals support ESLint's ongoing
<!-- NOTE: This section is autogenerated. Do not manually edit.--> <!-- NOTE: This section is autogenerated. Do not manually edit.-->
<!--sponsorsstart--> <!--sponsorsstart-->
<h3>Gold Sponsors</h3> <h3>Platinum 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> <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://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--> <!--sponsorsend-->
## <a name="technology-sponsors"></a>Technology Sponsors ## <a name="technology-sponsors"></a>Technology Sponsors
* Site search ([eslint.org](https://eslint.org)) is sponsored by [Algolia](https://www.algolia.com) * 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)

View File

@ -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. * @fileoverview Defines a schema for configs.
* @author Sylvan Mably * @author Sylvan Mably

View File

@ -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
}
}));

View File

@ -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. 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. 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.

View File

@ -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. 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 ## 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 ## 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: To add the upstream source for ESLint, run the following in your repository:

View File

@ -8,18 +8,18 @@ While ESLint is designed to be run on the command line, it's possible to use ESL
* [ESLint] * [ESLint]
* [constructor()][eslint-constructor] * [constructor()][eslint-constructor]
* [lintFiles()][eslint-lintFiles] * [lintFiles()][eslint-lintfiles]
* [lintText()][eslint-lintText] * [lintText()][eslint-linttext]
* [calculateConfigForFile()][eslint-calculateConfigForFile] * [calculateConfigForFile()][eslint-calculateconfigforfile]
* [isPathIgnored()][eslint-isPathIgnored] * [isPathIgnored()][eslint-ispathignored]
* [loadFormatter()][eslint-loadFormatter] * [loadFormatter()][eslint-loadformatter]
* [static version][eslint-version] * [static version][eslint-version]
* [static outputFixes()][eslint-outputFixes] * [static outputFixes()][eslint-outputfixes]
* [static getErrorResults()][eslint-getErrorResults] * [static getErrorResults()][eslint-geterrorresults]
* [LintResult type](lintresult) * [LintResult type][lintresult]
* [LintMessage type](lintmessage) * [LintMessage type][lintmessage]
* [EditInfo type](editinfo) * [EditInfo type][editinfo]
* [Formatter type](formatter) * [Formatter type][formatter]
* [SourceCode](#sourcecode) * [SourceCode](#sourcecode)
* [splitLines()](#sourcecode-splitlines) * [splitLines()](#sourcecode-splitlines)
* [Linter](#linter) * [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. * `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. * `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`). * `ruleId` - The name of the rule (e.g. `indent-legacy`).
* `replacedBy` - An array of rules that replace the deprecated rule (e.g. `["indent"]`). * `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 [thirdparty-formatters]: https://www.npmjs.com/search?q=eslintformatter
[eslint]: #eslint-class [eslint]: #eslint-class
[eslint-constructor]: #-new-eslintoptions [eslint-constructor]: #-new-eslintoptions
[eslint-lintfiles]: #-eslintlintFilespatterns [eslint-lintfiles]: #-eslintlintfilespatterns
[eslint-linttext]: #-eslintlintTextcode-options [eslint-linttext]: #-eslintlinttextcode-options
[eslint-calculateconfigforfile]: #-eslintcalculateConfigForFilefilePath [eslint-calculateconfigforfile]: #-eslintcalculateconfigforfilefilepath
[eslint-ispathignored]: #-eslintisPathIgnoredfilePath [eslint-ispathignored]: #-eslintispathignoredfilepath
[eslint-loadformatter]: #-eslintloadFormatternameOrPath [eslint-loadformatter]: #-eslintloadformatternameorpath
[eslint-version]: #-eslintversion [eslint-version]: #-eslintversion
[eslint-outputfixes]: #-eslintoutputFixesresults [eslint-outputfixes]: #-eslintoutputfixesresults
[eslint-geterrorresults]: #-eslintgetErrorResultsresults [eslint-geterrorresults]: #-eslintgeterrorresultsresults
[lintresult]: #-lintresult-type [lintresult]: #-lintresult-type
[lintmessage]: #-lintmessage-type [lintmessage]: #-lintmessage-type
[editinfo]: #-editinfo-type [editinfo]: #-editinfo-type

View File

@ -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. 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 ## Directory structure

View File

@ -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 ```bash
eslint -f ./my-awesome-formatter.js src/ 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 ## 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 ```bash
eslint -f awesome src/ eslint -f awesome src/

View File

@ -180,7 +180,7 @@ module.exports = {
env: ["node"], env: ["node"],
rules: { rules: {
"myPlugin/my-rule": "off", "myPlugin/my-rule": "off",
"eslint-plugin-myPlugin/another-rule": "off" "eslint-plugin-myPlugin/another-rule": "off",
"eslint-plugin-myPlugin/yet-another-rule": "error" "eslint-plugin-myPlugin/yet-another-rule": "error"
} }
} }

View File

@ -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 * `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) * `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. 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: The `fixer` object has the following methods:
@ -375,13 +375,15 @@ context.report({
{% endraw %} {% 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: 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. 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. 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 #### 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: 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: 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`). 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. 3. Run the [command line interface](../user-guide/command-line-interface.md) using the `--rulesdir` option to specify the location of your runtime rules.

View File

@ -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.

View File

@ -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. * Small bugfixes written by a team member.
1. Log into Jenkins and schedule a build for the "ESLint Release" job. 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. 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. 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. 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. 1. Make a release announcement in the public chatroom.

View File

@ -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) * `"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": 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) * `"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. * `allow` (`string[]`) list of properties to accept. Accept regex.
### properties: "always" ### properties: "always"
@ -217,6 +219,28 @@ Examples of **correct** code for this rule with the `{ "ignoreImports": true }`
import { snake_cased } from 'mod'; 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 ## allow
Examples of **correct** code for this rule with the `allow` option: Examples of **correct** code for this rule with the `allow` option:

View File

@ -113,7 +113,7 @@ If your project will not be following a consistent comma-spacing pattern, turn t
## Further Reading ## 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) * [Dojo Style Guide](https://dojotoolkit.org/reference-guide/1.9/developer/styleguide.html)

View File

@ -1,82 +1,3 @@
# disallow specified identifiers (id-blacklist) # disallow specified identifiers (id-blacklist)
> "There are only two hard things in Computer Science: cache invalidation and naming things." — Phil Karlton This rule was **deprecated** in ESLint v7.5.0 and replaced by the [id-denylist](id-denylist.md) rule.
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.

View 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.

View File

@ -82,6 +82,7 @@ This rule has an object option:
* `"properties": always` (default) enforces identifier length convention for property names * `"properties": always` (default) enforces identifier length convention for property names
* `"properties": never` ignores identifier length convention for property names * `"properties": never` ignores identifier length convention for property names
* `"exceptions"` allows an array of specified identifier 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 ### min
@ -217,6 +218,29 @@ const { x } = foo;
const { a: 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 ## Related Rules
* [max-len](max-len.md) * [max-len](max-len.md)

View File

@ -437,7 +437,7 @@ const [
### ignorePattern ### 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: Examples of **correct** code for the `ignorePattern` option:

View File

@ -84,7 +84,7 @@ class Foo{
Examples of **correct** code for this rule with the object option: Examples of **correct** code for this rule with the object option:
```js ```js
/* eslint lines-between-class-members: ["error", "always", { exceptAfterSingleLine: true }]*/ /* eslint lines-between-class-members: ["error", "always", { "exceptAfterSingleLine": true }]*/
class Foo{ class Foo{
bar(){} // single line class member bar(){} // single line class member
baz(){ baz(){

View File

@ -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. 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 ## Options

View File

@ -27,7 +27,7 @@ This rule has a string option:
* `"always"` (default) enforces newlines between the operands of a ternary expression. * `"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. * `"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 ### always
@ -134,6 +134,10 @@ Examples of **correct** code for this rule with the `"never"` option:
foo > bar ? value1 : value2; foo > bar ? value1 : value2;
foo > bar ? (baz > qux ? value1 : value2) : value3; foo > bar ? (baz > qux ? value1 : value2) : value3;
foo > bar ? (
baz > qux ? value1 : value2
) : value3;
``` ```
## When Not To Use It ## When Not To Use It

View File

@ -42,6 +42,8 @@ This rule disallows the use of `await` within loop bodies.
Examples of **correct** code for this rule: Examples of **correct** code for this rule:
```js ```js
/*eslint no-await-in-loop: "error"*/
async function foo(things) { async function foo(things) {
const results = []; const results = [];
for (const thing of things) { for (const thing of things) {
@ -56,6 +58,8 @@ async function foo(things) {
Examples of **incorrect** code for this rule: Examples of **incorrect** code for this rule:
```js ```js
/*eslint no-await-in-loop: "error"*/
async function foo(things) { async function foo(things) {
const results = []; const results = [];
for (const thing of things) { for (const thing of things) {

View File

@ -22,6 +22,10 @@ foo = bar;
function foo() { function foo() {
foo = bar; foo = bar;
} }
var a = function hello() {
hello = 123;
};
``` ```
Examples of **incorrect** code for this rule, unlike the corresponding rule in JSHint: Examples of **incorrect** code for this rule, unlike the corresponding rule in JSHint:

View File

@ -87,3 +87,25 @@ var quux = (
</div> </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
```

View File

@ -16,6 +16,7 @@ const x = 5123000000000000000000000000001
const x = 1230000000000000000000000.0 const x = 1230000000000000000000000.0
const x = .1230000000000000000000000 const x = .1230000000000000000000000
const x = 0X20000000000001 const x = 0X20000000000001
const x = 0X2_000000000_0001;
``` ```
Examples of **correct** code for this rule: Examples of **correct** code for this rule:
@ -29,4 +30,5 @@ const x = 123e34
const x = 12300000000000000000000000 const x = 12300000000000000000000000
const x = 0x1FFFFFFFFFFFFF const x = 0x1FFFFFFFFFFFFF
const x = 9007199254740991 const x = 9007199254740991
const x = 9007_1992547409_91
``` ```

View File

@ -128,6 +128,27 @@ a = data[4294967295]; // above the max array index
a = data[1e500]; // same as data["Infinity"] 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 ### enforceConst
A boolean to specify if we should check for the const keyword in variable declaration of numbers. `false` by default. A boolean to specify if we should check for the const keyword in variable declaration of numbers. `false` by default.

View File

@ -10,9 +10,9 @@ This rule aims to reduce the scrolling required when reading through your code.
This rule has an object option: This rule has an object option:
* `"max"` (default: `2`) enforces a maximum number of consecutive empty lines. - `"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. - `"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. - `"maxBOF"` enforces a maximum number of consecutive empty lines at the beginning of files.
### max ### max
@ -41,10 +41,10 @@ var bar = 3;
### maxEOF ### 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 ```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 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 ```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 foo = 5;
var bar = 3; 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 ### maxBOF

View 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)

View File

@ -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: Examples of **incorrect** code for this rule:
```js ```js
/*eslint no-return-await: "error"*/
async function foo() { async function foo() {
return await bar(); return await bar();
} }
@ -19,6 +21,8 @@ async function foo() {
Examples of **correct** code for this rule: Examples of **correct** code for this rule:
```js ```js
/*eslint no-return-await: "error"*/
async function foo() { async function foo() {
return bar(); return bar();
} }
@ -28,11 +32,13 @@ async function foo() {
return; return;
} }
// This is essentially the same as `return await bar();`, but the rule checks only `await` in `return` statements
async function foo() { async function foo() {
const x = await bar(); const x = await bar();
return x; return x;
} }
// In this example the `await` is necessary to be able to catch errors thrown from `bar()`
async function foo() { async function foo() {
try { try {
return await bar(); 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 ## When Not To Use It
There are a few reasons you might want to turn this rule off: There are a few reasons you might want to turn this rule off:

View File

@ -10,6 +10,8 @@ Examples of **incorrect** code for this rule:
/*eslint no-script-url: "error"*/ /*eslint no-script-url: "error"*/
location.href = "javascript:void(0)"; location.href = "javascript:void(0)";
location.href = `javascript:void(0)`;
``` ```
## Compatibility ## Compatibility

View File

@ -33,17 +33,21 @@ var _ = require('underscore');
var obj = _.contains(items, item); var obj = _.contains(items, item);
obj.__proto__ = {}; obj.__proto__ = {};
var file = __filename; var file = __filename;
function foo(_bar) {};
const foo = { onClick(_bar) {} };
const foo = (_bar) => {};
``` ```
## Options ## Options
This rule has an object option: This rule has an object option:
* `"allow"` allows specified identifiers to have dangling underscores - `"allow"` allows specified identifiers to have dangling underscores
* `"allowAfterThis": false` (default) disallows dangling underscores in members of the `this` object - `"allowAfterThis": false` (default) disallows dangling underscores in members of the `this` object
* `"allowAfterSuper": false` (default) disallows dangling underscores in members of the `super` 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 - `"allowAfterThisConstructor": false` (default) disallows dangling underscores in members of the `this.constructor` object
* `"enforceInMethodNames": false` (default) allows dangling underscores in method names - `"enforceInMethodNames": false` (default) allows dangling underscores in method names
- `"allowFunctionParams": true` (default) allows dangling underscores in function parameter names
### allow ### 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 ## When Not To Use It
If you want to allow dangling underscores in identifiers, then you can safely turn this rule off. If you want to allow dangling underscores in identifiers, then you can safely turn this rule off.

View File

@ -29,6 +29,10 @@ If a reference is inside of a dynamic expression (e.g. `CallExpression`,
Examples of **incorrect** code for this rule: Examples of **incorrect** code for this rule:
```js ```js
/*eslint no-unmodified-loop-condition: "error"*/
var node = something;
while (node) { while (node) {
doSomething(node); doSomething(node);
} }
@ -46,6 +50,8 @@ while (node !== root) {
Examples of **correct** code for this rule: Examples of **correct** code for this rule:
```js ```js
/*eslint no-unmodified-loop-condition: "error"*/
while (node) { while (node) {
doSomething(node); doSomething(node);
node = node.parent; node = node.parent;

View 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)

View File

@ -335,19 +335,32 @@ let d = {
foo: 1, bar: 2}; foo: 1, bar: 2};
let e = {foo: function() { let e = {foo: function() {
dosomething(); dosomething();
}}; }
};
let f = {
foo: function() {
dosomething();}};
let {f let {g
} = obj; } = obj;
let { let {
g} = obj; h} = obj;
let {h, i let {i, j
} = obj;
let {k, l
} = obj; } = obj;
let { let {
j, k} = obj; m, n} = obj;
let {l = function() { let {
o, p} = obj;
let {q = function() {
dosomething(); dosomething();
}} = obj; }
} = obj;
let {
r = function() {
dosomething();
}} = obj;
``` ```
Examples of **correct** code for this rule with the default `{ "consistent": true }` option: 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 object-curly-newline: ["error", { "consistent": true }]*/
/*eslint-env es6*/ /*eslint-env es6*/
let a = {};
let b = {foo: 1}; let empty1 = {};
let c = { let empty2 = {
};
let a = {foo: 1};
let b = {
foo: 1 foo: 1
}; };
let d = { let c = {
foo: 1, bar: 2 foo: 1, bar: 2
}; };
let e = { let d = {
foo: 1, foo: 1,
bar: 2 bar: 2
}; };
let f = {foo: function() {dosomething();}}; let e = {foo: function() {dosomething();}};
let g = { let f = {
foo: function() { foo: function() {
dosomething(); dosomething();
} }
}; };
let {} = obj; let {} = obj;
let {h} = obj; let {
} = obj;
let {g} = obj;
let {
h
} = obj;
let {i, j} = obj; let {i, j} = obj;
let { let {
k, l k, l

View File

@ -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
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. 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 ## Options
This rule has a single string option: This rule has a single string option:

View File

@ -22,7 +22,7 @@ This rule enforces a consistent linebreak style for operators.
## Options ## 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: String option:

View File

@ -48,14 +48,14 @@ You can supply any number of configurations. If a statement pair matches multipl
- `"block"` is lonely blocks. - `"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. - `"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. - `"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-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. - `"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. - `"class"` is `class` declarations.
- `"const"` is `const` variable declarations, both single-line and multiline. - `"const"` is `const` variable declarations, both single-line and multiline.
- `"continue"` is `continue` statements. - `"continue"` is `continue` statements.
- `"debugger"` is `debugger` 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"`. - `"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. - `"do"` is `do-while` statements. This matches all statements that the first token is `do` keyword.
- `"empty"` is empty statements. - `"empty"` is empty statements.
@ -212,6 +212,55 @@ Examples of **correct** code for the `[{ blankLine: "always", prev: "directive",
foo(); 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 ## Compatibility
- **JSCS:** [requirePaddingNewLineAfterVariableDeclaration] - **JSCS:** [requirePaddingNewLineAfterVariableDeclaration]

View File

@ -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 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. - 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: Examples of **incorrect** code for this rule:
```javascript ```javascript

View File

@ -9,6 +9,7 @@ Introduced in ES2018, object spread is a declarative alternative which may perfo
Examples of **incorrect** code for this rule: Examples of **incorrect** code for this rule:
```js ```js
/*eslint prefer-object-spread: "error"*/
Object.assign({}, foo) Object.assign({}, foo)
@ -31,6 +32,7 @@ Object.assign({ foo: bar });
Examples of **correct** code for this rule: Examples of **correct** code for this rule:
```js ```js
/*eslint prefer-object-spread: "error"*/
Object.assign(...foo); Object.assign(...foo);

View File

@ -88,6 +88,38 @@ RegExp(`${prefix}abc`);
new RegExp(String.raw`^\d\. ${suffix}`); 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 doesnt 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 ## Further Reading
* [MDN: Regular Expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) * [MDN: Regular Expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions)

View File

@ -41,6 +41,7 @@ This rule accepts an object with its properties as
* `all` = import all members provided by exported bindings. * `all` = import all members provided by exported bindings.
* `multiple` = import multiple members. * `multiple` = import multiple members.
* `single` = import single member. * `single` = import single member.
* `allowSeparatedGroups` (default: `false`)
Default option settings are: Default option settings are:
@ -50,7 +51,8 @@ Default option settings are:
"ignoreCase": false, "ignoreCase": false,
"ignoreDeclarationSort": false, "ignoreDeclarationSort": false,
"ignoreMemberSort": 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"]`. 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 ## 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. 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.

View File

@ -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. 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: Examples of unary `words` operators:
```js ```js
@ -103,14 +105,17 @@ Examples of **correct** code for this rule with the `{"words": true, "nonwords":
```js ```js
/*eslint space-unary-ops: "error"*/ /*eslint space-unary-ops: "error"*/
// Word unary operator "delete" is followed by a whitespace. // Word unary operator "typeof" is followed by a whitespace.
delete foo.bar; typeof !foo;
// Word unary operator "new" is followed by a whitespace.
new Foo;
// Word unary operator "void" is followed by a whitespace. // 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. // Unary operator "++" is not followed by whitespace.
++foo; ++foo;

View File

@ -37,6 +37,8 @@ typeof bar === typeof qux
Examples of **incorrect** code with the `{ "requireStringLiterals": true }` option: Examples of **incorrect** code with the `{ "requireStringLiterals": true }` option:
```js ```js
/*eslint valid-typeof: ["error", { "requireStringLiterals": true }]*/
typeof foo === undefined typeof foo === undefined
typeof bar == Object typeof bar == Object
typeof baz === "strnig" typeof baz === "strnig"
@ -48,6 +50,8 @@ typeof foo == 5
Examples of **correct** code with the `{ "requireStringLiterals": true }` option: Examples of **correct** code with the `{ "requireStringLiterals": true }` option:
```js ```js
/*eslint valid-typeof: ["error", { "requireStringLiterals": true }]*/
typeof foo === "undefined" typeof foo === "undefined"
typeof bar == "object" typeof bar == "object"
typeof baz === "string" typeof baz === "string"

View File

@ -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. 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 ## 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. 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. { "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: 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. * `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: * `ecmaFeatures` - an object indicating which additional language features you'd like to use:
* `globalReturn` - allow `return` statements in the global scope * `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: The following parsers are compatible with ESLint:
* [Esprima](https://www.npmjs.com/package/esprima) * [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. * [@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. 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 ```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). * `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. * `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. * `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. * `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. * `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. * `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. 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 There are also some exceptions to these rules:
# node_modules/* is ignored by default, but can be added using !
!node_modules/*
# Ignore built files except 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.
build/*
!build/index.js 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 ### Using an Alternate File
@ -1127,13 +1167,28 @@ You'll see this warning:
```text ```text
foo.js 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) ✖ 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. 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) ## 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). ⚠️ **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).

View File

@ -20,16 +20,26 @@ npm install eslint --save-dev
yarn add eslint --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 $ 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: After that, you can run ESLint on any file or directory like this:
``` ```
$ npx eslint yourfile.js $ 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. 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.

View File

@ -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: 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. 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. 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. **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. 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. **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. 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. **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. 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. 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: 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. 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. **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. 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 * `getTokenOrCommentBefore()` - replaced by `getTokenBefore()` with the `{ includeComments: true }` option
* `getTokenOrCommentAfter()` - replaced by `getTokenAfter()` 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: 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"); 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. 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. 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. **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. 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. **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). 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).

View File

@ -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. - [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-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-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()`. - [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. - [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. - [yoda](https://eslint.org/docs/rules/yoda) rule now recognizes bigint literals.

View File

@ -1,6 +1,13 @@
"use strict"; "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) { module.exports = function(config) {
config.set({ config.set({

View File

@ -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 };

View File

@ -19,14 +19,29 @@ const fs = require("fs");
const path = require("path"); const path = require("path");
const defaultOptions = require("../../conf/default-cli-options"); const defaultOptions = require("../../conf/default-cli-options");
const pkg = require("../../package.json"); 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 { Linter } = require("../linter");
const builtInRules = require("../rules"); const builtInRules = require("../rules");
const { CascadingConfigArrayFactory } = require("./cascading-config-array-factory"); const loadRules = require("./load-rules");
const { IgnorePattern, getUsedExtractedConfigs } = require("./config-array");
const { FileEnumerator } = require("./file-enumerator");
const hash = require("./hash"); const hash = require("./hash");
const LintResultCache = require("./lint-result-cache"); const LintResultCache = require("./lint-result-cache");
@ -559,7 +574,11 @@ class CLIEngine {
resolvePluginsRelativeTo: options.resolvePluginsRelativeTo, resolvePluginsRelativeTo: options.resolvePluginsRelativeTo,
rulePaths: options.rulePaths, rulePaths: options.rulePaths,
specificConfigPath: options.configFile, 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({ const fileEnumerator = new FileEnumerator({
configArrayFactory, configArrayFactory,

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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 };

View File

@ -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 };

View File

@ -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 };

View File

@ -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
};

View File

@ -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 };

View File

@ -40,8 +40,13 @@ const getGlobParent = require("glob-parent");
const isGlob = require("is-glob"); const isGlob = require("is-glob");
const { escapeRegExp } = require("lodash"); const { escapeRegExp } = require("lodash");
const { Minimatch } = require("minimatch"); 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"); const debug = require("debug")("eslint:file-enumerator");
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -208,7 +213,11 @@ class FileEnumerator {
*/ */
constructor({ constructor({
cwd = process.cwd(), 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, extensions = null,
globInputPaths = true, globInputPaths = true,
errorOnUnmatchedPattern = true, errorOnUnmatchedPattern = true,

View File

@ -42,8 +42,8 @@ module.exports = function(results) {
messages.forEach(message => { messages.forEach(message => {
output += [ output += [
`<error line="${xmlEscape(message.line)}"`, `<error line="${xmlEscape(message.line || 0)}"`,
`column="${xmlEscape(message.column)}"`, `column="${xmlEscape(message.column || 0)}"`,
`severity="${xmlEscape(getMessageType(message))}"`, `severity="${xmlEscape(getMessageType(message))}"`,
`message="${xmlEscape(message.message)}${message.ruleId ? ` (${message.ruleId})` : ""}"`, `message="${xmlEscape(message.message)}${message.ruleId ? ` (${message.ruleId})` : ""}"`,
`source="${message.ruleId ? xmlEscape(`eslint.rules.${message.ruleId}`) : ""}" />` `source="${message.ruleId ? xmlEscape(`eslint.rules.${message.ruleId}`) : ""}" />`

View File

@ -15,7 +15,13 @@ const fs = require("fs");
const { promisify } = require("util"); const { promisify } = require("util");
const { CLIEngine, getCLIEngineInternalSlots } = require("../cli-engine/cli-engine"); const { CLIEngine, getCLIEngineInternalSlots } = require("../cli-engine/cli-engine");
const BuiltinRules = require("../rules"); const BuiltinRules = require("../rules");
const { getRuleSeverity } = require("../shared/config-ops"); const {
Legacy: {
ConfigOps: {
getRuleSeverity
}
}
} = require("@eslint/eslintrc");
const { version } = require("../../package.json"); const { version } = require("../../package.json");
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@ -11,7 +11,7 @@
const lodash = require("lodash"), const lodash = require("lodash"),
recConfig = require("../../conf/eslint-recommended"), recConfig = require("../../conf/eslint-recommended"),
ConfigOps = require("../shared/config-ops"), ConfigOps = require("@eslint/eslintrc/lib/shared/config-ops"),
{ Linter } = require("../linter"), { Linter } = require("../linter"),
configRule = require("./config-rule"); configRule = require("./config-rule");

View File

@ -12,14 +12,14 @@
const util = require("util"), const util = require("util"),
path = require("path"), path = require("path"),
inquirer = require("inquirer"), enquirer = require("enquirer"),
ProgressBar = require("progress"), ProgressBar = require("progress"),
semver = require("semver"), semver = require("semver"),
espree = require("espree"), espree = require("espree"),
recConfig = require("../../conf/eslint-recommended"), recConfig = require("../../conf/eslint-recommended"),
ConfigOps = require("../shared/config-ops"), ConfigOps = require("@eslint/eslintrc/lib/shared/config-ops"),
log = require("../shared/logging"), log = require("../shared/logging"),
naming = require("../shared/naming"), naming = require("@eslint/eslintrc/lib/shared/naming"),
ModuleResolver = require("../shared/relative-module-resolver"), ModuleResolver = require("../shared/relative-module-resolver"),
autoconfig = require("./autoconfig.js"), autoconfig = require("./autoconfig.js"),
ConfigFile = require("./config-file"), 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 * Note: This clones the config object and returns a new config to avoid mutating
* the original config parameter. * the original config parameter.
* @param {Object} answers answers received from inquirer * @param {Object} answers answers received from enquirer
* @param {Object} config config object * @param {Object} config config object
* @returns {Object} config object with configured rules * @returns {Object} config object with configured rules
*/ */
@ -253,7 +253,7 @@ function configureRules(answers, config) {
/** /**
* process user's answers and create config object * 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 * @returns {Object} config object
*/ */
function processAnswers(answers) { function processAnswers(answers) {
@ -265,7 +265,7 @@ function processAnswers(answers) {
}; };
config.parserOptions.ecmaVersion = espree.latestEcmaVersion; config.parserOptions.ecmaVersion = espree.latestEcmaVersion;
config.env.es2020 = true; config.env.es2021 = true;
// set the module type // set the module type
if (answers.moduleType === "esm") { if (answers.moduleType === "esm") {
@ -321,7 +321,6 @@ function processAnswers(answers) {
} }
} }
if (answers.typescript && config.extends.includes("eslint:recommended")) { if (answers.typescript && config.extends.includes("eslint:recommended")) {
config.extends.push("plugin:@typescript-eslint/eslint-recommended");
config.extends.push("plugin:@typescript-eslint/recommended"); config.extends.push("plugin:@typescript-eslint/recommended");
} }
@ -409,7 +408,7 @@ function installModules(modules) {
npmUtils.installSyncSaveDev(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. * Ask user to install modules.
* @param {string[]} modules Array of modules to be installed. * @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("The config that you've selected requires the following dependencies:\n");
log.info(modules.join(" ")); log.info(modules.join(" "));
return inquirer.prompt([ return enquirer.prompt([
{ {
type: "confirm", type: "toggle",
name: "executeInstallation", name: "executeInstallation",
message: "Would you like to install them now with npm?", message: "Would you like to install them now with npm?",
default: true, enabled: "Yes",
when() { disabled: "No",
return modules.length && packageJsonExists; initial: 1,
skip() {
return !(modules.length && packageJsonExists);
},
result(input) {
return this.skipped ? null : input;
} }
} }
]).then(({ executeInstallation }) => { ]).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 * Ask use a few questions on command prompt
* @returns {Promise} The promise with the result of the prompt * @returns {Promise} The promise with the result of the prompt
*/ */
function promptUser() { function promptUser() {
return inquirer.prompt([ return enquirer.prompt([
{ {
type: "list", type: "select",
name: "purpose", name: "purpose",
message: "How would you like to use ESLint?", 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: [ choices: [
{ name: "To check syntax only", value: "syntax" }, { message: "To check syntax only", name: "syntax" },
{ name: "To check syntax and find problems", value: "problems" }, { message: "To check syntax and find problems", name: "problems" },
{ name: "To check syntax, find problems, and enforce code style", value: "style" } { message: "To check syntax, find problems, and enforce code style", name: "style" }
] ]
}, },
{ {
type: "list", type: "select",
name: "moduleType", name: "moduleType",
message: "What type of modules does your project use?", message: "What type of modules does your project use?",
default: "esm", initial: 0,
choices: [ choices: [
{ name: "JavaScript modules (import/export)", value: "esm" }, { message: "JavaScript modules (import/export)", name: "esm" },
{ name: "CommonJS (require/exports)", value: "commonjs" }, { message: "CommonJS (require/exports)", name: "commonjs" },
{ name: "None of these", value: "none" } { message: "None of these", name: "none" }
] ]
}, },
{ {
type: "list", type: "select",
name: "framework", name: "framework",
message: "Which framework does your project use?", message: "Which framework does your project use?",
default: "react", initial: 0,
choices: [ choices: [
{ name: "React", value: "react" }, { message: "React", name: "react" },
{ name: "Vue.js", value: "vue" }, { message: "Vue.js", name: "vue" },
{ name: "None of these", value: "none" } { message: "None of these", name: "none" }
] ]
}, },
{ {
type: "confirm", type: "toggle",
name: "typescript", name: "typescript",
message: "Does your project use TypeScript?", message: "Does your project use TypeScript?",
default: false enabled: "Yes",
disabled: "No",
initial: 0
}, },
{ {
type: "checkbox", type: "multiselect",
name: "env", name: "env",
message: "Where does your code run?", message: "Where does your code run?",
default: ["browser"], hint: "(Press <space> to select, <a> to toggle all, <i> to invert selection)",
initial: 0,
choices: [ choices: [
{ name: "Browser", value: "browser" }, { message: "Browser", name: "browser" },
{ name: "Node", value: "node" } { message: "Node", name: "node" }
] ]
}, },
{ {
type: "list", type: "select",
name: "source", name: "source",
message: "How would you like to define a style for your project?", message: "How would you like to define a style for your project?",
default: "guide",
choices: [ choices: [
{ name: "Use a popular style guide", value: "guide" }, { message: "Use a popular style guide", name: "guide" },
{ name: "Answer questions about your style", value: "prompt" }, { message: "Answer questions about your style", name: "prompt" },
{ name: "Inspect your JavaScript file(s)", value: "auto" } { message: "Inspect your JavaScript file(s)", name: "auto" }
], ],
when(answers) { skip() {
return answers.purpose === "style"; return this.state.answers.purpose !== "style";
},
result(input) {
return this.skipped ? null : input;
} }
}, },
{ {
type: "list", type: "select",
name: "styleguide", name: "styleguide",
message: "Which style guide do you want to follow?", message: "Which style guide do you want to follow?",
choices: [ choices: [
{ name: "Airbnb: https://github.com/airbnb/javascript", value: "airbnb" }, { message: "Airbnb: https://github.com/airbnb/javascript", name: "airbnb" },
{ name: "Standard: https://github.com/standard/standard", value: "standard" }, { message: "Standard: https://github.com/standard/standard", name: "standard" },
{ name: "Google: https://github.com/google/eslint-config-google", value: "google" } { message: "Google: https://github.com/google/eslint-config-google", name: "google" }
], ],
when(answers) { skip() {
answers.packageJsonExists = npmUtils.checkPackageJson(); this.state.answers.packageJsonExists = npmUtils.checkPackageJson();
return answers.source === "guide" && answers.packageJsonExists; return !(this.state.answers.source === "guide" && this.state.answers.packageJsonExists);
},
result(input) {
return this.skipped ? null : input;
} }
}, },
{ {
type: "input", type: "input",
name: "patterns", name: "patterns",
message: "Which file(s), path(s), or glob(s) should be examined?", message: "Which file(s), path(s), or glob(s) should be examined?",
when(answers) { skip() {
return (answers.source === "auto"); return this.state.answers.source !== "auto";
}, },
validate(input) { 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 "You must tell us what code to examine. Try again.";
} }
return true; return true;
} }
}, },
{ {
type: "list", type: "select",
name: "format", name: "format",
message: "What format do you want your config file to be in?", message: "What format do you want your config file to be in?",
default: "JavaScript", initial: 0,
choices: ["JavaScript", "YAML", "JSON"] choices: ["JavaScript", "YAML", "JSON"]
}, },
{ {
type: "confirm", type: "toggle",
name: "installESLint", name: "installESLint",
message(answers) { message(answers) {
const verb = semver.ltr(answers.localESLintVersion, answers.requiredESLintVersionRange) 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}?`; 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, enabled: "Yes",
when(answers) { disabled: "No",
return answers.source === "guide" && answers.packageJsonExists && hasESLintVersionConflict(answers); 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 => { ]).then(earlyAnswers => {
@ -613,33 +632,35 @@ function promptUser() {
} }
// continue with the style questions otherwise... // continue with the style questions otherwise...
return inquirer.prompt([ return enquirer.prompt([
{ {
type: "list", type: "select",
name: "indent", name: "indent",
message: "What style of indentation do you use?", message: "What style of indentation do you use?",
default: "tab", initial: 0,
choices: [{ name: "Tabs", value: "tab" }, { name: "Spaces", value: 4 }] choices: [{ message: "Tabs", name: "tab" }, { message: "Spaces", name: 4 }]
}, },
{ {
type: "list", type: "select",
name: "quotes", name: "quotes",
message: "What quotes do you use for strings?", message: "What quotes do you use for strings?",
default: "double", initial: 0,
choices: [{ name: "Double", value: "double" }, { name: "Single", value: "single" }] choices: [{ message: "Double", name: "double" }, { message: "Single", name: "single" }]
}, },
{ {
type: "list", type: "select",
name: "linebreak", name: "linebreak",
message: "What line endings do you use?", message: "What line endings do you use?",
default: "unix", initial: 0,
choices: [{ name: "Unix", value: "unix" }, { name: "Windows", value: "windows" }] choices: [{ message: "Unix", name: "unix" }, { message: "Windows", name: "windows" }]
}, },
{ {
type: "confirm", type: "toggle",
name: "semi", name: "semi",
message: "Do you require semicolons?", message: "Do you require semicolons?",
default: true enabled: "Yes",
disabled: "No",
initial: 1
} }
]).then(answers => { ]).then(answers => {
const totalAnswers = Object.assign({}, earlyAnswers, answers); const totalAnswers = Object.assign({}, earlyAnswers, answers);

View File

@ -39,6 +39,17 @@ function isHandledLogicalOperator(operator) {
return operator === "&&" || operator === "||" || 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. * Gets the label if the parent node of a given node is a LabeledStatement.
* @param {ASTNode} node A node to get. * @param {ASTNode} node A node to get.
@ -71,6 +82,9 @@ function isForkingByTrueOrFalse(node) {
case "LogicalExpression": case "LogicalExpression":
return isHandledLogicalOperator(parent.operator); return isHandledLogicalOperator(parent.operator);
case "AssignmentExpression":
return isLogicalAssignmentOperator(parent.operator);
default: default:
return false; return false;
} }
@ -244,6 +258,19 @@ function preprocess(analyzer, node) {
const parent = node.parent; const parent = node.parent;
switch (parent.type) { 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": case "LogicalExpression":
if ( if (
parent.right === node && parent.right === node &&
@ -253,6 +280,15 @@ function preprocess(analyzer, node) {
} }
break; break;
case "AssignmentExpression":
if (
parent.right === node &&
isLogicalAssignmentOperator(parent.operator)
) {
state.makeLogicalRight();
}
break;
case "ConditionalExpression": case "ConditionalExpression":
case "IfStatement": case "IfStatement":
@ -377,6 +413,20 @@ function processCodePathToEnter(analyzer, node) {
analyzer.emitter.emit("onCodePathStart", codePath, node); analyzer.emitter.emit("onCodePathStart", codePath, node);
break; 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": case "LogicalExpression":
if (isHandledLogicalOperator(node.operator)) { if (isHandledLogicalOperator(node.operator)) {
state.pushChoiceContext( state.pushChoiceContext(
@ -386,6 +436,15 @@ function processCodePathToEnter(analyzer, node) {
} }
break; break;
case "AssignmentExpression":
if (isLogicalAssignmentOperator(node.operator)) {
state.pushChoiceContext(
node.operator.slice(0, -1), // removes `=` from the end
isForkingByTrueOrFalse(node)
);
}
break;
case "ConditionalExpression": case "ConditionalExpression":
case "IfStatement": case "IfStatement":
state.pushChoiceContext("test", false); state.pushChoiceContext("test", false);
@ -449,6 +508,10 @@ function processCodePathToExit(analyzer, node) {
let dontForward = false; let dontForward = false;
switch (node.type) { switch (node.type) {
case "ChainExpression":
state.popChainContext();
break;
case "IfStatement": case "IfStatement":
case "ConditionalExpression": case "ConditionalExpression":
state.popChoiceContext(); state.popChoiceContext();
@ -460,6 +523,12 @@ function processCodePathToExit(analyzer, node) {
} }
break; break;
case "AssignmentExpression":
if (isLogicalAssignmentOperator(node.operator)) {
state.popChoiceContext();
}
break;
case "SwitchStatement": case "SwitchStatement":
state.popSwitchContext(); state.popSwitchContext();
break; break;
@ -583,6 +652,13 @@ function postprocess(analyzer, node) {
break; 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: default:
break; break;
} }

View File

@ -92,7 +92,6 @@ class CodePathSegment {
/* istanbul ignore if */ /* istanbul ignore if */
if (debug.enabled) { if (debug.enabled) {
this.internal.nodes = []; this.internal.nodes = [];
this.internal.exitNodes = [];
} }
} }

View File

@ -234,6 +234,7 @@ class CodePathState {
this.tryContext = null; this.tryContext = null;
this.loopContext = null; this.loopContext = null;
this.breakContext = null; this.breakContext = null;
this.chainContext = null;
this.currentSegments = []; this.currentSegments = [];
this.initialSegment = this.forkContext.head[0]; 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. * IfStatement, WhileStatement, DoWhileStatement, or ForStatement.
* *
* LogicalExpressions have cases that it goes different paths between the * LogicalExpressions have cases that it goes different paths between the
@ -338,7 +339,7 @@ class CodePathState {
* a -> b -> foo(); * a -> b -> foo();
* a -> b -> bar(); * a -> b -> bar();
* @param {string} kind A kind string. * @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"`. * If it's IfStatement's or ConditionalExpression's, this is `"test"`.
* Otherwise, this is `"loop"`. * Otherwise, this is `"loop"`.
* @param {boolean} isForkingAsResult A flag that shows that goes different * @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 // SwitchStatement
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------

View File

@ -25,6 +25,22 @@ function getId(segment) { // eslint-disable-line jsdoc/require-jsdoc
return segment.id + (segment.reachable ? "" : "!"); 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 // Public Interface
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -56,9 +72,15 @@ module.exports = {
const segInternal = state.currentSegments[i].internal; const segInternal = state.currentSegments[i].internal;
if (leaving) { 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 { } 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"; text += "style=\"rounded,dashed,filled\",fillcolor=\"#FF9800\",label=\"<<unreachable>>\\n";
} }
if (segment.internal.nodes.length > 0 || segment.internal.exitNodes.length > 0) { if (segment.internal.nodes.length > 0) {
text += [].concat( text += segment.internal.nodes.join("\\n");
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");
} else { } else {
text += "????"; text += "????";
} }

View File

@ -11,7 +11,7 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
const levn = require("levn"), 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"); const debug = require("debug")("eslint:config-comment-parser");

View File

@ -16,11 +16,11 @@ const
evk = require("eslint-visitor-keys"), evk = require("eslint-visitor-keys"),
espree = require("espree"), espree = require("espree"),
lodash = require("lodash"), lodash = require("lodash"),
BuiltInEnvironments = require("../../conf/environments"), BuiltInEnvironments = require("@eslint/eslintrc/conf/environments"),
pkg = require("../../package.json"), pkg = require("../../package.json"),
astUtils = require("../shared/ast-utils"), astUtils = require("../shared/ast-utils"),
ConfigOps = require("../shared/config-ops"), ConfigOps = require("@eslint/eslintrc/lib/shared/config-ops"),
validator = require("../shared/config-validator"), ConfigValidator = require("@eslint/eslintrc/lib/shared/config-validator"),
Traverser = require("../shared/traverser"), Traverser = require("../shared/traverser"),
{ SourceCode } = require("../source-code"), { SourceCode } = require("../source-code"),
CodePathAnalyzer = require("./code-path-analysis/code-path-analyzer"), CodePathAnalyzer = require("./code-path-analysis/code-path-analyzer"),
@ -293,6 +293,9 @@ function getDirectiveComments(filename, ast, ruleMapper, warnInlineConfig) {
const exportedVariables = {}; const exportedVariables = {};
const problems = []; const problems = [];
const disableDirectives = []; const disableDirectives = [];
const validator = new ConfigValidator({
builtInRules: Rules
});
ast.comments.filter(token => token.type !== "Shebang").forEach(comment => { ast.comments.filter(token => token.type !== "Shebang").forEach(comment => {
const trimmedCommentText = stripDirectiveComment(comment.value); const trimmedCommentText = stripDirectiveComment(comment.value);

View File

@ -196,15 +196,19 @@ function mapSuggestions(descriptor, sourceCode, messages) {
return []; return [];
} }
return descriptor.suggest.map(suggestInfo => { return descriptor.suggest
const computedDesc = suggestInfo.desc || messages[suggestInfo.messageId]; .map(suggestInfo => {
const computedDesc = suggestInfo.desc || messages[suggestInfo.messageId];
return { return {
...suggestInfo, ...suggestInfo,
desc: interpolate(computedDesc, suggestInfo.data), desc: interpolate(computedDesc, suggestInfo.data),
fix: normalizeFixes(suggestInfo, sourceCode) fix: normalizeFixes(suggestInfo, sourceCode)
}; };
}); })
// Remove suggestions that didn't provide a fix
.filter(({ fix }) => fix);
} }
/** /**

View File

@ -644,6 +644,10 @@ class RuleTester {
assert.ok(item.errors || item.errors === 0, assert.ok(item.errors || item.errors === 0,
`Did not specify errors for an invalid test of ${ruleName}`); `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 ruleHasMetaMessages = hasOwnProperty(rule, "meta") && hasOwnProperty(rule.meta, "messages");
const friendlyIDList = ruleHasMetaMessages ? `[${Object.keys(rule.meta.messages).map(key => `'${key}'`).join(", ")}]` : null; const friendlyIDList = ruleHasMetaMessages ? `[${Object.keys(rule.meta.messages).map(key => `'${key}'`).join(", ")}]` : null;
@ -651,6 +655,11 @@ class RuleTester {
const messages = result.messages; const messages = result.messages;
if (typeof item.errors === "number") { 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", 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))); item.errors, item.errors === 1 ? "" : "s", messages.length, util.inspect(messages)));
} else { } 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); assertASTDidntChange(result.beforeAST, result.afterAST);
} }

View File

@ -86,16 +86,6 @@ function isAccessorKind(node) {
return node.kind === "get" || node.kind === "set"; 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. * Checks whether or not a given node is an argument of a specified method call.
* @param {ASTNode} node A node to check. * @param {ASTNode} node A node to check.
@ -109,10 +99,7 @@ function isArgumentOfMethodCall(node, index, object, property) {
return ( return (
parent.type === "CallExpression" && parent.type === "CallExpression" &&
parent.callee.type === "MemberExpression" && astUtils.isSpecificMemberAccess(parent.callee, object, property) &&
parent.callee.computed === false &&
isIdentifier(parent.callee.object, object) &&
isIdentifier(parent.callee.property, property) &&
parent.arguments[index] === node parent.arguments[index] === node
); );
} }

View File

@ -9,8 +9,6 @@
// Requirements // Requirements
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
const lodash = require("lodash");
const astUtils = require("./utils/ast-utils"); 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. * property.
* @param {ASTNode} node A node to check. * @param {ASTNode} node A node to check.
* @returns {boolean} `true` if the node is a MemberExpression node which has * @returns {boolean} `true` if the node is a member access which has
* the specified name's property * the specified name's property. The node may be a `(Chain|Member)Expression` node.
*/ */
function isTargetMethod(node) { function isTargetMethod(node) {
return ( return astUtils.isSpecificMemberAccess(node, null, TARGET_METHODS);
node.type === "MemberExpression" && }
TARGET_METHODS.test(astUtils.getStaticPropertyName(node) || "")
); /**
* 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 "LogicalExpression":
case "ConditionalExpression": case "ConditionalExpression":
case "ChainExpression":
currentNode = parent; currentNode = parent;
break; break;
@ -153,10 +162,10 @@ module.exports = {
], ],
messages: { messages: {
expectedAtEnd: "Expected to return a value at the end of {{name}}.", expectedAtEnd: "{{arrayMethodName}}() expects a value to be returned at the end of {{name}}.",
expectedInside: "Expected to return a value in {{name}}.", expectedInside: "{{arrayMethodName}}() expects a return value from {{name}}.",
expectedReturnValue: "{{name}} expected a return value.", expectedReturnValue: "{{arrayMethodName}}() expects a return value from {{name}}.",
expectedNoReturnValue: "{{name}} did not expect a return value." expectedNoReturnValue: "{{arrayMethodName}}() expects no useless return value from {{name}}."
} }
}, },
@ -202,14 +211,13 @@ module.exports = {
} }
if (messageId) { if (messageId) {
let name = astUtils.getFunctionNameWithKind(node); const name = astUtils.getFunctionNameWithKind(node);
name = messageId === "expectedNoReturnValue" ? lodash.upperFirst(name) : name;
context.report({ context.report({
node, node,
loc: astUtils.getFunctionHeadLoc(node, sourceCode), loc: astUtils.getFunctionHeadLoc(node, sourceCode),
messageId, messageId,
data: { name } data: { name, arrayMethodName: fullMethodName(funcInfo.arrayMethodName) }
}); });
} }
} }
@ -273,7 +281,8 @@ module.exports = {
node, node,
messageId, messageId,
data: { data: {
name: lodash.upperFirst(astUtils.getFunctionNameWithKind(funcInfo.node)) name: astUtils.getFunctionNameWithKind(funcInfo.node),
arrayMethodName: fullMethodName(funcInfo.arrayMethodName)
} }
}); });
} }

View File

@ -75,6 +75,7 @@ module.exports = {
const never = options[0] === "never"; const never = options[0] === "never";
const requireReturnForObjectLiteral = options[1] && options[1].requireReturnForObjectLiteral; const requireReturnForObjectLiteral = options[1] && options[1].requireReturnForObjectLiteral;
const sourceCode = context.getSourceCode(); const sourceCode = context.getSourceCode();
let funcInfo = null;
/** /**
* Checks whether the given node has ASI problem or not. * Checks whether the given node has ASI problem or not.
@ -99,6 +100,21 @@ module.exports = {
return sourceCode.getTokenAfter(node); 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 * Determines whether a arrow function body needs braces
* @param {ASTNode} node The arrow function node. * @param {ASTNode} node The arrow function node.
@ -136,7 +152,7 @@ module.exports = {
context.report({ context.report({
node, node,
loc: arrowBody.loc.start, loc: arrowBody.loc,
messageId, messageId,
fix(fixer) { fix(fixer) {
const fixes = []; 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, * 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. * enclose the return value by parentheses to avoid syntax error.
*/ */
if (astUtils.isOpeningBraceToken(firstValueToken) || blockBody[0].argument.type === "SequenceExpression") { if (astUtils.isOpeningBraceToken(firstValueToken) || blockBody[0].argument.type === "SequenceExpression" || (funcInfo.hasInOperator && isInsideForLoopInitializer(node))) {
fixes.push( if (!astUtils.isParenthesised(sourceCode, blockBody[0].argument)) {
fixer.insertTextBefore(firstValueToken, "("), fixes.push(
fixer.insertTextAfter(lastValueToken, ")") fixer.insertTextBefore(firstValueToken, "("),
); fixer.insertTextAfter(lastValueToken, ")")
);
}
} }
/* /*
@ -201,7 +219,7 @@ module.exports = {
if (always || (asNeeded && requireReturnForObjectLiteral && arrowBody.type === "ObjectExpression")) { if (always || (asNeeded && requireReturnForObjectLiteral && arrowBody.type === "ObjectExpression")) {
context.report({ context.report({
node, node,
loc: arrowBody.loc.start, loc: arrowBody.loc,
messageId: "expectedBlock", messageId: "expectedBlock",
fix(fixer) { fix(fixer) {
const fixes = []; const fixes = [];
@ -245,7 +263,24 @@ module.exports = {
} }
return { 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;
}
}; };
} }
}; };

View File

@ -15,15 +15,12 @@ const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** /**
* Get location should be reported by AST node. * Determines if the given arrow function has block body.
* @param {ASTNode} node AST Node. * @param {ASTNode} node `ArrowFunctionExpression` node.
* @returns {Location} Location information. * @returns {boolean} `true` if the function has block body.
*/ */
function getLocation(node) { function hasBlockBody(node) {
return { return node.body.type === "BlockStatement";
start: node.params[0].loc.start,
end: node.params[node.params.length - 1].loc.end
};
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -75,126 +72,112 @@ module.exports = {
const sourceCode = context.getSourceCode(); const sourceCode = context.getSourceCode();
/** /**
* Determines whether a arrow function argument end with `)` * Finds opening paren of parameters for the given arrow function, if it exists.
* @param {ASTNode} node The arrow function node. * It is assumed that the given arrow function has exactly one parameter.
* @returns {void} * @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) { function findOpeningParenOfParams(node) {
const isAsync = node.async; const tokenBeforeParams = sourceCode.getTokenBefore(node.params[0]);
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;
}
if ( if (
requireForBlockBody && tokenBeforeParams &&
node.body.type === "BlockStatement" astUtils.isOpeningParenToken(tokenBeforeParams) &&
node.range[0] <= tokenBeforeParams.range[0]
) { ) {
if (!astUtils.isOpeningParenToken(firstTokenOfParam)) { return tokenBeforeParams;
context.report({
node,
messageId: "expectedParensBlock",
loc: getLocation(node),
fix(fixer) {
return fixer.replaceText(firstTokenOfParam, `(${firstTokenOfParam.value})`);
}
});
}
return;
} }
// "as-needed": x => x return null;
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;
}
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 !== ")") { * Determines whether the given arrow function has comments inside parens of parameters.
context.report({ * It is assumed that the given arrow function has parens of parameters.
node, * @param {ASTNode} node `ArrowFunctionExpression` node.
messageId: "expectedParens", * @param {Token} openingParen Opening paren of parameters.
loc: getLocation(node), * @returns {boolean} `true` if the function has at least one comment inside of parens of parameters.
fix(fixer) { */
return fixer.replaceText(firstTokenOfParam, `(${firstTokenOfParam.value})`); 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 { 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]]);
}
});
}
}
}; };
} }
}; };

View File

@ -32,6 +32,10 @@ module.exports = {
type: "boolean", type: "boolean",
default: false default: false
}, },
ignoreGlobals: {
type: "boolean",
default: false
},
properties: { properties: {
enum: ["always", "never"] enum: ["always", "never"]
}, },
@ -61,8 +65,11 @@ module.exports = {
let properties = options.properties || ""; let properties = options.properties || "";
const ignoreDestructuring = options.ignoreDestructuring; const ignoreDestructuring = options.ignoreDestructuring;
const ignoreImports = options.ignoreImports; const ignoreImports = options.ignoreImports;
const ignoreGlobals = options.ignoreGlobals;
const allow = options.allow || []; const allow = options.allow || [];
let globalScope;
if (properties !== "always" && properties !== "never") { if (properties !== "always" && properties !== "never") {
properties = "always"; properties = "always";
} }
@ -159,6 +166,37 @@ module.exports = {
return false; 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. * Reports an AST node as a rule violation.
* @param {ASTNode} node The node to report. * @param {ASTNode} node The node to report.
@ -174,6 +212,10 @@ module.exports = {
return { return {
Program() {
globalScope = context.getScope();
},
Identifier(node) { Identifier(node) {
/* /*
@ -189,6 +231,11 @@ module.exports = {
return; return;
} }
// Check if it's a global variable
if (ignoreGlobals && isReferenceToGlobalVariable(node) && !isPropertyNameInObjectLiteral(node)) {
return;
}
// MemberExpressions get special rules // MemberExpressions get special rules
if (node.parent.type === "MemberExpression") { if (node.parent.type === "MemberExpression") {

View File

@ -9,23 +9,12 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
const lodash = require("lodash"); const lodash = require("lodash");
const astUtils = require("./utils/ast-utils"); const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Helpers // 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. * Checks whether or not a given code path segment is unreachable.
* @param {CodePathSegment} segment A CodePathSegment to check. * @param {CodePathSegment} segment A CodePathSegment to check.
@ -165,7 +154,7 @@ module.exports = {
let hasReturnValue = Boolean(argument); let hasReturnValue = Boolean(argument);
if (treatUndefinedAsUnspecified && hasReturnValue) { if (treatUndefinedAsUnspecified && hasReturnValue) {
hasReturnValue = !isIdentifier(argument, "undefined") && argument.operator !== "void"; hasReturnValue = !astUtils.isSpecificId(argument, "undefined") && argument.operator !== "void";
} }
if (!funcInfo.hasReturn) { if (!funcInfo.hasReturn) {

View File

@ -50,6 +50,7 @@ function isPossibleConstructor(node) {
case "MemberExpression": case "MemberExpression":
case "CallExpression": case "CallExpression":
case "NewExpression": case "NewExpression":
case "ChainExpression":
case "YieldExpression": case "YieldExpression":
case "TaggedTemplateExpression": case "TaggedTemplateExpression":
case "MetaProperty": case "MetaProperty":
@ -59,9 +60,36 @@ function isPossibleConstructor(node) {
return node.name !== "undefined"; return node.name !== "undefined";
case "AssignmentExpression": 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": 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 ( return (
isPossibleConstructor(node.left) || isPossibleConstructor(node.left) ||
isPossibleConstructor(node.right) isPossibleConstructor(node.right)

View File

@ -457,11 +457,18 @@ module.exports = {
return { return {
IfStatement(node) { 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 => { prepareIfChecks(node).forEach(preparedCheck => {
preparedCheck.check(); preparedCheck.check();
}); });
} }
// Skip `else if`, it's already checked (when the top `if` was visited)
}, },
WhileStatement(node) { WhileStatement(node) {

View File

@ -52,31 +52,37 @@ module.exports = {
*/ */
function checkDotLocation(node) { function checkDotLocation(node) {
const property = node.property; const property = node.property;
const dot = sourceCode.getTokenBefore(property); const dotToken = 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]);
if (onObject) { 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({ context.report({
node, node,
loc: dot.loc, loc: dotToken.loc,
messageId: "expectedDotAfterObject", 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({ context.report({
node, node,
loc: dot.loc, loc: dotToken.loc,
messageId: "expectedDotBeforeProperty", 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);
}
}); });
} }
} }

View File

@ -87,28 +87,36 @@ module.exports = {
data: { data: {
key: formattedValue key: formattedValue
}, },
fix(fixer) { *fix(fixer) {
const leftBracket = sourceCode.getTokenAfter(node.object, astUtils.isOpeningBracketToken); const leftBracket = sourceCode.getTokenAfter(node.object, astUtils.isOpeningBracketToken);
const rightBracket = sourceCode.getLastToken(node); 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.
if (sourceCode.commentsExistBetween(leftBracket, rightBracket)) {
// Don't perform any fixes if there are comments inside the brackets. return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
return null;
} }
const tokenAfterProperty = sourceCode.getTokenAfter(rightBracket); // Replace the brackets by an identifier.
const needsSpaceAfterProperty = tokenAfterProperty && if (!node.optional) {
rightBracket.range[1] === tokenAfterProperty.range[0] && yield fixer.insertTextBefore(
!astUtils.canTokensBeAdjacent(String(value), tokenAfterProperty); leftBracket,
astUtils.isDecimalInteger(node.object) ? " ." : "."
const textBeforeDot = astUtils.isDecimalInteger(node.object) ? " " : ""; );
const textAfterProperty = needsSpaceAfterProperty ? " " : ""; }
yield fixer.replaceTextRange(
return fixer.replaceTextRange(
[leftBracket.range[0], rightBracket.range[1]], [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: { data: {
key: node.property.name key: node.property.name
}, },
fix(fixer) { *fix(fixer) {
const dot = sourceCode.getTokenBefore(node.property); const dotToken = sourceCode.getTokenBefore(node.property);
const textAfterDot = sourceCode.text.slice(dot.range[1], node.property.range[0]);
if (textAfterDot.trim()) { // 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) {
// Don't perform any fixes if there are comments between the dot and the property name. return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
return null;
} }
if (node.object.type === "Identifier" && node.object.name === "let") { // 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
* A statement that starts with `let[` is parsed as a destructuring variable declaration, not
* a MemberExpression.
*/
return null;
} }
return fixer.replaceTextRange( // Replace the identifier to brackets.
[dot.range[0], node.property.range[1]], if (!node.optional) {
`[${textAfterDot}"${node.property.name}"]` yield fixer.remove(dotToken);
); }
yield fixer.replaceText(node.property, `["${node.property.name}"]`);
} }
}); });
} }

View File

@ -126,15 +126,24 @@ module.exports = {
messageId: "unexpectedWhitespace", messageId: "unexpectedWhitespace",
fix(fixer) { 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 * Only autofix if there is no newline
* https://github.com/eslint/eslint/issues/7787 * https://github.com/eslint/eslint/issues/7787
*/ */
if (!hasNewline) { if (hasNewline) {
return fixer.removeRange([leftToken.range[1], rightToken.range[0]]); return null;
} }
return fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
return null;
} }
}); });
} else if (!never && !hasWhitespace) { } else if (!never && !hasWhitespace) {
@ -149,6 +158,9 @@ module.exports = {
}, },
messageId: "missing", messageId: "missing",
fix(fixer) { fix(fixer) {
if (node.optional) {
return null; // Not sure if inserting a space to either before/after `?.` token.
}
return fixer.insertTextBefore(rightToken, " "); return fixer.insertTextBefore(rightToken, " ");
} }
}); });
@ -161,7 +173,31 @@ module.exports = {
}, },
messageId: "unexpectedNewline", messageId: "unexpectedNewline",
fix(fixer) { 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 lastToken = sourceCode.getLastToken(node);
const lastCalleeToken = sourceCode.getLastToken(node.callee); const lastCalleeToken = sourceCode.getLastToken(node.callee);
const parenToken = sourceCode.getFirstTokenBetween(lastCalleeToken, lastToken, astUtils.isOpeningParenToken); 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 // Parens in NewExpression are optional
if (!(parenToken && parenToken.range[1] < node.range[1])) { if (!(parenToken && parenToken.range[1] < node.range[1])) {

View File

@ -117,10 +117,7 @@ module.exports = {
if (!node) { if (!node) {
return false; return false;
} }
return node.type === "CallExpression" && return node.type === "CallExpression" && astUtils.isSpecificMemberAccess(node.callee, objName, funcName);
node.callee.type === "MemberExpression" &&
node.callee.object.name === objName &&
node.callee.property.name === funcName;
} }
/** /**

View File

@ -218,7 +218,7 @@ module.exports = {
} }
case "ArrowFunctionExpression": { case "ArrowFunctionExpression": {
const firstToken = sourceCode.getFirstToken(node); const firstToken = sourceCode.getFirstToken(node, { skip: (node.async ? 1 : 0) });
if (!astUtils.isOpeningParenToken(firstToken)) { if (!astUtils.isOpeningParenToken(firstToken)) {

View File

@ -13,7 +13,8 @@ const ACCEPTABLE_PARENTS = [
"CallExpression", "CallExpression",
"ConditionalExpression", "ConditionalExpression",
"Program", "Program",
"VariableDeclaration" "VariableDeclaration",
"ChainExpression"
]; ];
/** /**

View File

@ -1,6 +1,6 @@
/** /**
* @fileoverview Rule that warns when identifier names that are * @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) * @author Keith Cirkel (http://keithcirkel.co.uk)
*/ */
@ -111,6 +111,9 @@ function isShorthandPropertyDefinition(node) {
module.exports = { module.exports = {
meta: { meta: {
deprecated: true,
replacedBy: ["id-denylist"],
type: "suggestion", type: "suggestion",
docs: { docs: {
@ -128,25 +131,25 @@ module.exports = {
uniqueItems: true uniqueItems: true
}, },
messages: { messages: {
blacklisted: "Identifier '{{name}}' is blacklisted." restricted: "Identifier '{{name}}' is restricted."
} }
}, },
create(context) { create(context) {
const blacklist = new Set(context.options); const denyList = new Set(context.options);
const reportedNodes = new Set(); const reportedNodes = new Set();
let globalScope; let globalScope;
/** /**
* Checks whether the given name is blacklisted. * Checks whether the given name is restricted.
* @param {string} name The name to check. * @param {string} name The name to check.
* @returns {boolean} `true` if the name is blacklisted. * @returns {boolean} `true` if the name is restricted.
* @private * @private
*/ */
function isBlacklisted(name) { function isRestricted(name) {
return blacklist.has(name); return denyList.has(name);
} }
/** /**
@ -172,8 +175,8 @@ module.exports = {
/* /*
* Member access has special rules for checking property names. * 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. * 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 blacklisted name. * Write access isn't allowed, because it potentially creates a new property with a restricted name.
*/ */
if ( if (
parent.type === "MemberExpression" && parent.type === "MemberExpression" &&
@ -205,7 +208,7 @@ module.exports = {
if (!reportedNodes.has(node)) { if (!reportedNodes.has(node)) {
context.report({ context.report({
node, node,
messageId: "blacklisted", messageId: "restricted",
data: { data: {
name: node.name name: node.name
} }
@ -221,7 +224,7 @@ module.exports = {
}, },
Identifier(node) { Identifier(node) {
if (isBlacklisted(node.name) && shouldCheck(node)) { if (isRestricted(node.name) && shouldCheck(node)) {
report(node); report(node);
} }
} }

View 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);
}
}
};
}
};

View File

@ -39,6 +39,13 @@ module.exports = {
type: "string" type: "string"
} }
}, },
exceptionPatterns: {
type: "array",
uniqueItems: true,
items: {
type: "string"
}
},
properties: { properties: {
enum: ["always", "never"] enum: ["always", "never"]
} }
@ -57,14 +64,20 @@ module.exports = {
const minLength = typeof options.min !== "undefined" ? options.min : 2; const minLength = typeof options.min !== "undefined" ? options.min : 2;
const maxLength = typeof options.max !== "undefined" ? options.max : Infinity; const maxLength = typeof options.max !== "undefined" ? options.max : Infinity;
const properties = options.properties !== "never"; const properties = options.properties !== "never";
const exceptions = (options.exceptions ? options.exceptions : []) const exceptions = new Set(options.exceptions);
.reduce((obj, item) => { const exceptionPatterns = (options.exceptionPatterns || []).map(pattern => new RegExp(pattern, "u"));
obj[item] = true;
return obj;
}, {});
const reportedNode = new Set(); 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 = { const SUPPORTED_EXPRESSIONS = {
MemberExpression: properties && function(parent) { MemberExpression: properties && function(parent) {
return !parent.computed && ( return !parent.computed && (
@ -112,7 +125,7 @@ module.exports = {
const isShort = name.length < minLength; const isShort = name.length < minLength;
const isLong = name.length > maxLength; const isLong = name.length > maxLength;
if (!(isShort || isLong) || exceptions[name]) { if (!(isShort || isLong) || exceptions.has(name) || matchesExceptionPattern(name)) {
return; // Nothing to report return; // Nothing to report
} }

View File

@ -39,7 +39,8 @@ module.exports = {
type: "boolean", type: "boolean",
default: false default: false
} }
} },
additionalProperties: false
} }
], ],
messages: { messages: {

View File

@ -32,6 +32,7 @@ const KNOWN_NODES = new Set([
"BreakStatement", "BreakStatement",
"CallExpression", "CallExpression",
"CatchClause", "CatchClause",
"ChainExpression",
"ClassBody", "ClassBody",
"ClassDeclaration", "ClassDeclaration",
"ClassExpression", "ClassExpression",
@ -934,6 +935,24 @@ module.exports = {
parameterParens.add(openingParen); parameterParens.add(openingParen);
parameterParens.add(closingParen); 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 offsetAfterToken = node.callee.type === "TaggedTemplateExpression" ? sourceCode.getFirstToken(node.callee.quasi) : openingParen;
const offsetToken = sourceCode.getTokenBefore(offsetAfterToken); const offsetToken = sourceCode.getTokenBefore(offsetAfterToken);
@ -1065,16 +1084,17 @@ module.exports = {
}, },
ArrowFunctionExpression(node) { ArrowFunctionExpression(node) {
const firstToken = sourceCode.getFirstToken(node); const maybeOpeningParen = sourceCode.getFirstToken(node, { skip: node.async ? 1 : 0 });
if (astUtils.isOpeningParenToken(firstToken)) { if (astUtils.isOpeningParenToken(maybeOpeningParen)) {
const openingParen = firstToken; const openingParen = maybeOpeningParen;
const closingParen = sourceCode.getTokenBefore(node.body, astUtils.isClosingParenToken); const closingParen = sourceCode.getTokenBefore(node.body, astUtils.isClosingParenToken);
parameterParens.add(openingParen); parameterParens.add(openingParen);
parameterParens.add(closingParen); parameterParens.add(closingParen);
addElementListIndent(node.params, openingParen, closingParen, options.FunctionExpression.parameters); addElementListIndent(node.params, openingParen, closingParen, options.FunctionExpression.parameters);
} }
addBlocklessNodeIndent(node.body); addBlocklessNodeIndent(node.body);
}, },

View File

@ -57,6 +57,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
"guard-for-in": () => require("./guard-for-in"), "guard-for-in": () => require("./guard-for-in"),
"handle-callback-err": () => require("./handle-callback-err"), "handle-callback-err": () => require("./handle-callback-err"),
"id-blacklist": () => require("./id-blacklist"), "id-blacklist": () => require("./id-blacklist"),
"id-denylist": () => require("./id-denylist"),
"id-length": () => require("./id-length"), "id-length": () => require("./id-length"),
"id-match": () => require("./id-match"), "id-match": () => require("./id-match"),
"implicit-arrow-linebreak": () => require("./implicit-arrow-linebreak"), "implicit-arrow-linebreak": () => require("./implicit-arrow-linebreak"),
@ -176,6 +177,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
"no-plusplus": () => require("./no-plusplus"), "no-plusplus": () => require("./no-plusplus"),
"no-process-env": () => require("./no-process-env"), "no-process-env": () => require("./no-process-env"),
"no-process-exit": () => require("./no-process-exit"), "no-process-exit": () => require("./no-process-exit"),
"no-promise-executor-return": () => require("./no-promise-executor-return"),
"no-proto": () => require("./no-proto"), "no-proto": () => require("./no-proto"),
"no-prototype-builtins": () => require("./no-prototype-builtins"), "no-prototype-builtins": () => require("./no-prototype-builtins"),
"no-redeclare": () => require("./no-redeclare"), "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-unmodified-loop-condition": () => require("./no-unmodified-loop-condition"),
"no-unneeded-ternary": () => require("./no-unneeded-ternary"), "no-unneeded-ternary": () => require("./no-unneeded-ternary"),
"no-unreachable": () => require("./no-unreachable"), "no-unreachable": () => require("./no-unreachable"),
"no-unreachable-loop": () => require("./no-unreachable-loop"),
"no-unsafe-finally": () => require("./no-unsafe-finally"), "no-unsafe-finally": () => require("./no-unsafe-finally"),
"no-unsafe-negation": () => require("./no-unsafe-negation"), "no-unsafe-negation": () => require("./no-unsafe-negation"),
"no-unused-expressions": () => require("./no-unused-expressions"), "no-unused-expressions": () => require("./no-unused-expressions"),

View File

@ -433,11 +433,15 @@ module.exports = {
tokenBeforeColon = sourceCode.getTokenBefore(nextColon, { includeComments: true }), tokenBeforeColon = sourceCode.getTokenBefore(nextColon, { includeComments: true }),
tokenAfterColon = sourceCode.getTokenAfter(nextColon, { includeComments: true }), tokenAfterColon = sourceCode.getTokenAfter(nextColon, { includeComments: true }),
isKeySide = side === "key", isKeySide = side === "key",
locStart = isKeySide ? tokenBeforeColon.loc.start : tokenAfterColon.loc.start,
isExtra = diff > 0, isExtra = diff > 0,
diffAbs = Math.abs(diff), diffAbs = Math.abs(diff),
spaces = Array(diffAbs + 1).join(" "); 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 (( if ((
diff && mode === "strict" || diff && mode === "strict" ||
diff < 0 && mode === "minimum" || diff < 0 && mode === "minimum" ||
@ -482,7 +486,7 @@ module.exports = {
context.report({ context.report({
node: property[side], node: property[side],
loc: locStart, loc,
messageId, messageId,
data: { data: {
computed: property.computed ? "computed " : "", computed: property.computed ? "computed " : "",

View File

@ -126,7 +126,7 @@ module.exports = {
!sourceCode.isSpaceBetweenTokens(prevToken, token) !sourceCode.isSpaceBetweenTokens(prevToken, token)
) { ) {
context.report({ context.report({
loc: token.loc.start, loc: token.loc,
messageId: "expectedBefore", messageId: "expectedBefore",
data: token, data: token,
fix(fixer) { fix(fixer) {
@ -178,7 +178,7 @@ module.exports = {
!sourceCode.isSpaceBetweenTokens(token, nextToken) !sourceCode.isSpaceBetweenTokens(token, nextToken)
) { ) {
context.report({ context.report({
loc: token.loc.start, loc: token.loc,
messageId: "expectedAfter", messageId: "expectedAfter",
data: token, data: token,
fix(fixer) { fix(fixer) {

View File

@ -383,11 +383,22 @@ module.exports = {
return; return;
} }
const loc = {
start: {
line: lineNumber,
column: 0
},
end: {
line: lineNumber,
column: textToMeasure.length
}
};
if (commentLengthApplies) { if (commentLengthApplies) {
if (lineLength > maxCommentLength) { if (lineLength > maxCommentLength) {
context.report({ context.report({
node, node,
loc: { line: lineNumber, column: 0 }, loc,
messageId: "maxComment", messageId: "maxComment",
data: { data: {
lineLength, lineLength,
@ -398,7 +409,7 @@ module.exports = {
} else if (lineLength > maxLength) { } else if (lineLength > maxLength) {
context.report({ context.report({
node, node,
loc: { line: lineNumber, column: 0 }, loc,
messageId: "max", messageId: "max",
data: { data: {
lineLength, lineLength,

Some files were not shown because too many files have changed in this diff Show More