- Some problems reported by this rule are manually fixable by editor suggestions
+ Some problems reported by this rule are manually fixable by editor suggestions
{% include "partials/docs-footer.html" %}
+
diff --git a/docs/src/assets/js/main.js b/docs/src/assets/js/main.js
index bf3268266f4..80168a136c9 100644
--- a/docs/src/assets/js/main.js
+++ b/docs/src/assets/js/main.js
@@ -192,24 +192,6 @@
}
})();
-// add "Open in Playground" button to code blocks
-// (function() {
-// let blocks = document.querySelectorAll('pre[class*="language-"]');
-// if (blocks) {
-// blocks.forEach(function(block) {
-// let button = document.createElement("a");
-// button.classList.add('c-btn--playground');
-// button.classList.add('c-btn');
-// button.classList.add('c-btn--secondary');
-// button.setAttribute("href", "#");
-// button.innerText = "Open in Playground";
-// block.appendChild(button);
-// });
-// }
-// })();
-
-
-
// add utilities
var util = {
keyCodes: {
diff --git a/docs/src/assets/scss/docs.scss b/docs/src/assets/scss/docs.scss
index ee40123891d..a59742475da 100644
--- a/docs/src/assets/scss/docs.scss
+++ b/docs/src/assets/scss/docs.scss
@@ -112,6 +112,10 @@ div.incorrect {
offset-inline-end: -22px;
offset-block-start: -22px;
}
+
+ pre.line-numbers-mode {
+ padding-bottom: 4.5rem;
+ }
}
div.correct {
@@ -142,13 +146,12 @@ pre[class*="language-"] {
.c-btn.c-btn--playground {
position: absolute;
font-size: var(--step--1);
- bottom: 0.5rem;
- right: 0.5rem;
- offset-block-end: 0.5rem;
- offset-inline-end: 0.5rem;
+ bottom: 1rem;
+ right: 1rem;
+ z-index: 1;
- @media all and (max-width: 768px) {
- display: none;
+ @media all and (min-width: 768px) {
+ bottom: 1.5rem;
}
}
@@ -163,8 +166,9 @@ pre[class*="language-"] {
height: 50px;
display: none;
position: fixed;
- right: 50px;
+ right: 19.8vw;
bottom: 35px;
+ z-index: 1;
font-size: 1.5rem;
border-radius: 50%;
color: var(--body-background-color);
@@ -173,7 +177,35 @@ pre[class*="language-"] {
align-items: center;
background-color: var(--link-color);
- @media (max-width: 800px) {
+ @media (max-width: 1299px) {
+ right: 18.99vw;
+ }
+
+ @media (max-width: 1100px) {
+ right: 19.4vw;
+ }
+
+ @media (max-width: 1060px) {
+ right: 19.9vw;
+ }
+
+ @media (max-width: 1024px) {
+ right: 22vw;
+ }
+
+ @media (max-width: 860px) {
+ right: 22.2vw;
+ }
+
+ @media (max-width: 850px) {
+ right: 22.6vw;
+ }
+
+ @media (max-width: 820px) {
+ right: 23.4vw;
+ }
+
+ @media (max-width: 799px) {
right: 35px;
}
diff --git a/docs/src/contribute/core-rules.md b/docs/src/contribute/core-rules.md
index 5610c000e84..93b7647f52b 100644
--- a/docs/src/contribute/core-rules.md
+++ b/docs/src/contribute/core-rules.md
@@ -4,7 +4,7 @@ eleventyNavigation:
key: contribute core rule
parent: contribute to eslint
title: Contribute to Core Rules
- order: 10
+ order: 11
---
The ESLint core rules are the rules included in the ESLint package.
diff --git a/docs/src/contribute/governance.md b/docs/src/contribute/governance.md
index 563840a01dc..3f761fb2a51 100644
--- a/docs/src/contribute/governance.md
+++ b/docs/src/contribute/governance.md
@@ -4,7 +4,7 @@ eleventyNavigation:
key: governance
parent: contribute to eslint
title: Governance
- order: 11
+ order: 12
---
diff --git a/docs/src/contribute/index.md b/docs/src/contribute/index.md
index 3a20390db3d..3b0a6d8f5c5 100644
--- a/docs/src/contribute/index.md
+++ b/docs/src/contribute/index.md
@@ -3,7 +3,7 @@ title: Contribute to ESLint
eleventyNavigation:
key: contribute to eslint
title: Contribute to ESLint
- order: 3
+ order: 4
---
One of the great things about open source projects is that anyone can contribute in any number of meaningful ways. ESLint couldn't exist without the help of the many contributors it's had since the project began, and we want you to feel like you can contribute and make a difference as well.
diff --git a/docs/src/contribute/package-json-conventions.md b/docs/src/contribute/package-json-conventions.md
index 99afe8b2222..a030dc3f3dc 100644
--- a/docs/src/contribute/package-json-conventions.md
+++ b/docs/src/contribute/package-json-conventions.md
@@ -1,6 +1,11 @@
---
title: Package.json Conventions
edit_link: https://github.com/eslint/eslint/edit/main/docs/src/contribute/package-json-conventions.md
+eleventyNavigation:
+ key: package.json conventions
+ parent: contribute to eslint
+ title: Package.json Conventions
+ order: 8
---
The following applies to the "scripts" section of `package.json` files.
@@ -14,7 +19,7 @@ Here is a summary of the proposal in ABNF.
```abnf
name = life-cycle / main target? option* ":watch"?
life-cycle = "prepare" / "preinstall" / "install" / "postinstall" / "prepublish" / "preprepare" / "prepare" / "postprepare" / "prepack" / "postpack" / "prepublishOnly"
-main = "build" / "lint" ":fix"? / "release" / "start" / "test"
+main = "build" / "lint" ":fix"? / "release" / "start" / "test" / "fetch"
target = ":" word ("-" word)* / extension ("+" extension)*
option = ":" word ("-" word)*
word = ALPHA +
@@ -35,6 +40,12 @@ Scripts that generate a set of files from source code and / or data MUST have na
If a package contains any `build:*` scripts, there MAY be a script named `build`. If so, SHOULD produce the same output as running each of the `build` scripts individually. It MUST produce a subset of the output from running those scripts.
+### Fetch
+
+Scripts that generate a set of files from external data or resources MUST have names that begin with `fetch`.
+
+If a package contains any `fetch:*` scripts, there MAY be a script named `fetch`. If so, it SHOULD produce the same output as running each of the `fetch` scripts individually. It MUST produce a subset of the output from running those scripts.
+
### Release
Scripts that have public side effects (publishing the web site, committing to Git, etc.) MUST begin with `release`.
diff --git a/docs/src/contribute/pull-requests.md b/docs/src/contribute/pull-requests.md
index 8854ee2fbb2..44f2378d8dc 100644
--- a/docs/src/contribute/pull-requests.md
+++ b/docs/src/contribute/pull-requests.md
@@ -4,7 +4,7 @@ eleventyNavigation:
key: submit pull request
parent: contribute to eslint
title: Submit a Pull Request
- order: 9
+ order: 10
---
If you want to contribute to an ESLint repo, please use a GitHub pull request. This is the fastest way for us to evaluate your code and to merge it into the code base. Please don't file an issue with snippets of code. Doing so means that we need to manually merge the changes in and update any appropriate tests. That decreases the likelihood that your code is going to get included in a timely manner. Please use pull requests.
diff --git a/docs/src/contribute/report-security-vulnerability.md b/docs/src/contribute/report-security-vulnerability.md
index f68319fd34e..69f31a9a73b 100644
--- a/docs/src/contribute/report-security-vulnerability.md
+++ b/docs/src/contribute/report-security-vulnerability.md
@@ -4,7 +4,7 @@ eleventyNavigation:
key: report security vulnerability
parent: contribute to eslint
title: Report a Security Vulnerability
- order: 11
+ order: 13
---
To report a security vulnerability in ESLint, please use our [create an advisory form](https://github.com/eslint/eslint/security/advisories/new) on GitHub.
diff --git a/docs/src/contribute/work-on-issue.md b/docs/src/contribute/work-on-issue.md
index 6205a37fd7e..d258aa4eb15 100644
--- a/docs/src/contribute/work-on-issue.md
+++ b/docs/src/contribute/work-on-issue.md
@@ -4,17 +4,17 @@ eleventyNavigation:
key: work on issues
parent: contribute to eslint
title: Work on Issues
- order: 8
+ order: 9
---
Our public [issues tracker](https://github.com/eslint/eslint/issues) lists all of the things we plan on doing as well as suggestions from the community. Before starting to work on an issue, be sure you read through the rest of this page.
## Issue Labels
-We use labels to indicate the status of issues. The most complete documentation on the labels is found in the [Maintain ESLint documentation](../maintain/manage-issues#when-an-issue-is-opened), but most contributors should find the information on this page sufficient. The most important questions that labels can help you, as a contributor, answer are:
+We use labels to indicate the status of issues. The most complete documentation on the labels is found in the [Maintain ESLint documentation](../maintain/manage-issues#when-an-issue-or-pull-request-is-opened), but most contributors should find the information on this page sufficient. The most important questions that labels can help you, as a contributor, answer are:
1. Is this issue available for me to work on? If you have little or no experience contributing to ESLint, the [`good first issue`](https://github.com/eslint/eslint/labels/good%20first%20issue) label marks appropriate issues. Otherwise, the [`help wanted`](https://github.com/eslint/eslint/labels/help%20wanted) label is an invitation to work on the issue. If you have more experience, you can try working on other issues labeled [`accepted`](https://github.com/eslint/eslint/labels/accepted). Conversely, issues not yet ready to work on are labeled `triage`, `evaluating`, and/or `needs bikeshedding`, and issues that cannot currently be worked on because of something else, such as a bug in a dependency, are labeled `blocked`.
-1. What is this issue about? Labels describing the nature of issues include `bug`, `enhancement`, `feature`, `question`, `rule`, `documentation`, `core`, `build`, `cli`, `infrastructure`, `breaking`, and `chore`. These are documented in [Maintain ESLint](../maintain/manage-issues#types-of-issues).
+1. What is this issue about? Labels describing the nature of issues include `bug`, `enhancement`, `feature`, `question`, `rule`, `documentation`, `core`, `build`, `cli`, `infrastructure`, `breaking`, and `chore`. These are documented in [Maintain ESLint](../maintain/manage-issues#types-of-issues-and-pull-requests).
1. What is the priority of this issue? Because we have a lot of issues, we prioritize certain issues above others. The following is the list of priorities, from highest to lowest:
1. **Bugs** - problems with the project are actively affecting users. We want to get these resolved as quickly as possible.
diff --git a/docs/src/extend/code-path-analysis.md b/docs/src/extend/code-path-analysis.md
index 7344f8647ad..87911957490 100644
--- a/docs/src/extend/code-path-analysis.md
+++ b/docs/src/extend/code-path-analysis.md
@@ -37,7 +37,7 @@ This has references of both the initial segment and the final segments of a code
* `finalSegments` (`CodePathSegment[]`) - The final segments which includes both returned and thrown.
* `returnedSegments` (`CodePathSegment[]`) - The final segments which includes only returned.
* `thrownSegments` (`CodePathSegment[]`) - The final segments which includes only thrown.
-* `currentSegments` (`CodePathSegment[]`) - Segments of the current position.
+* `currentSegments` (`CodePathSegment[]`) - **Deprecated.** Segments of the current traversal position.
* `upper` (`CodePath|null`) - The code path of the upper function/global scope.
* `childCodePaths` (`CodePath[]`) - Code paths of functions this code path contains.
@@ -56,77 +56,110 @@ Difference from doubly linked list is what there are forking and merging (the ne
## Events
-There are five events related to code paths, and you can define event handlers in rules.
+There are seven events related to code paths, and you can define event handlers by adding them alongside node visitors in the object exported from the `create()` method of your rule.
```js
-module.exports = function(context) {
- return {
- /**
- * This is called at the start of analyzing a code path.
- * In this time, the code path object has only the initial segment.
- *
- * @param {CodePath} codePath - The new code path.
- * @param {ASTNode} node - The current node.
- * @returns {void}
- */
- "onCodePathStart": function(codePath, node) {
- // do something with codePath
- },
-
- /**
- * This is called at the end of analyzing a code path.
- * In this time, the code path object is complete.
- *
- * @param {CodePath} codePath - The completed code path.
- * @param {ASTNode} node - The current node.
- * @returns {void}
- */
- "onCodePathEnd": function(codePath, node) {
- // do something with codePath
- },
-
- /**
- * This is called when a code path segment was created.
- * It meant the code path is forked or merged.
- * In this time, the segment has the previous segments and has been
- * judged reachable or not.
- *
- * @param {CodePathSegment} segment - The new code path segment.
- * @param {ASTNode} node - The current node.
- * @returns {void}
- */
- "onCodePathSegmentStart": function(segment, node) {
- // do something with segment
- },
-
- /**
- * This is called when a code path segment was left.
- * In this time, the segment does not have the next segments yet.
- *
- * @param {CodePathSegment} segment - The left code path segment.
- * @param {ASTNode} node - The current node.
- * @returns {void}
- */
- "onCodePathSegmentEnd": function(segment, node) {
- // do something with segment
- },
-
- /**
- * This is called when a code path segment was looped.
- * Usually segments have each previous segments when created,
- * but when looped, a segment is added as a new previous segment into a
- * existing segment.
- *
- * @param {CodePathSegment} fromSegment - A code path segment of source.
- * @param {CodePathSegment} toSegment - A code path segment of destination.
- * @param {ASTNode} node - The current node.
- * @returns {void}
- */
- "onCodePathSegmentLoop": function(fromSegment, toSegment, node) {
- // do something with segment
- }
- };
-};
+module.exports = {
+ meta: {
+ // ...
+ },
+ create(context) {
+
+ return {
+ /**
+ * This is called at the start of analyzing a code path.
+ * In this time, the code path object has only the initial segment.
+ *
+ * @param {CodePath} codePath - The new code path.
+ * @param {ASTNode} node - The current node.
+ * @returns {void}
+ */
+ onCodePathStart(codePath, node) {
+ // do something with codePath
+ },
+
+ /**
+ * This is called at the end of analyzing a code path.
+ * In this time, the code path object is complete.
+ *
+ * @param {CodePath} codePath - The completed code path.
+ * @param {ASTNode} node - The current node.
+ * @returns {void}
+ */
+ onCodePathEnd(codePath, node) {
+ // do something with codePath
+ },
+
+ /**
+ * This is called when a reachable code path segment was created.
+ * It meant the code path is forked or merged.
+ * In this time, the segment has the previous segments and has been
+ * judged reachable or not.
+ *
+ * @param {CodePathSegment} segment - The new code path segment.
+ * @param {ASTNode} node - The current node.
+ * @returns {void}
+ */
+ onCodePathSegmentStart(segment, node) {
+ // do something with segment
+ },
+
+ /**
+ * This is called when a reachable code path segment was left.
+ * In this time, the segment does not have the next segments yet.
+ *
+ * @param {CodePathSegment} segment - The left code path segment.
+ * @param {ASTNode} node - The current node.
+ * @returns {void}
+ */
+ onCodePathSegmentEnd(segment, node) {
+ // do something with segment
+ },
+
+ /**
+ * This is called when an unreachable code path segment was created.
+ * It meant the code path is forked or merged.
+ * In this time, the segment has the previous segments and has been
+ * judged reachable or not.
+ *
+ * @param {CodePathSegment} segment - The new code path segment.
+ * @param {ASTNode} node - The current node.
+ * @returns {void}
+ */
+ onUnreachableCodePathSegmentStart(segment, node) {
+ // do something with segment
+ },
+
+ /**
+ * This is called when an unreachable code path segment was left.
+ * In this time, the segment does not have the next segments yet.
+ *
+ * @param {CodePathSegment} segment - The left code path segment.
+ * @param {ASTNode} node - The current node.
+ * @returns {void}
+ */
+ onUnreachableCodePathSegmentEnd(segment, node) {
+ // do something with segment
+ },
+
+ /**
+ * This is called when a code path segment was looped.
+ * Usually segments have each previous segments when created,
+ * but when looped, a segment is added as a new previous segment into a
+ * existing segment.
+ *
+ * @param {CodePathSegment} fromSegment - A code path segment of source.
+ * @param {CodePathSegment} toSegment - A code path segment of destination.
+ * @param {ASTNode} node - The current node.
+ * @returns {void}
+ */
+ onCodePathSegmentLoop(fromSegment, toSegment, node) {
+ // do something with segment
+ }
+ };
+
+ }
+}
```
### About `onCodePathSegmentLoop`
@@ -212,35 +245,134 @@ bar();
## Usage Examples
-### To check whether or not this is reachable
+### Track current segment position
+
+To track the current code path segment position, you can define a rule like this:
```js
-function isReachable(segment) {
- return segment.reachable;
-}
+module.exports = {
+ meta: {
+ // ...
+ },
+ create(context) {
+
+ // tracks the code path we are currently in
+ let currentCodePath;
+
+ // tracks the segments we've traversed in the current code path
+ let currentSegments;
+
+ // tracks all current segments for all open paths
+ const allCurrentSegments = [];
+
+ return {
+
+ onCodePathStart(codePath) {
+ currentCodePath = codePath;
+ allCurrentSegments.push(currentSegments);
+ currentSegments = new Set();
+ },
+
+ onCodePathEnd(codePath) {
+ currentCodePath = codePath.upper;
+ currentSegments = allCurrentSegments.pop();
+ },
+
+ onCodePathSegmentStart(segment) {
+ currentSegments.add(segment);
+ },
+
+ onCodePathSegmentEnd(segment) {
+ currentSegments.delete(segment);
+ },
-module.exports = function(context) {
- var codePathStack = [];
-
- return {
- // Stores CodePath objects.
- "onCodePathStart": function(codePath) {
- codePathStack.push(codePath);
- },
- "onCodePathEnd": function(codePath) {
- codePathStack.pop();
- },
-
- // Checks reachable or not.
- "ExpressionStatement": function(node) {
- var codePath = codePathStack[codePathStack.length - 1];
-
- // Checks the current code path segments.
- if (!codePath.currentSegments.some(isReachable)) {
- context.report({message: "Unreachable!", node: node});
+ onUnreachableCodePathSegmentStart(segment) {
+ currentSegments.add(segment);
+ },
+
+ onUnreachableCodePathSegmentEnd(segment) {
+ currentSegments.delete(segment);
}
+ };
+
+ }
+};
+```
+
+In this example, the `currentCodePath` variable is used to access the code path that is currently being traversed and the `currentSegments` variable tracks the segments in that code path that have been traversed to that point. Note that `currentSegments` both starts and ends as an empty set, constantly being updated as the traversal progresses.
+
+Tracking the current segment position is helpful for analyzing the code path that led to a particular node, as in the next example.
+
+### Find an unreachable node
+
+To find an unreachable node, track the current segment position and then use a node visitor to check if any of the segments are reachable. For example, the following looks for any `ExpressionStatement` that is unreachable.
+
+```js
+function areAnySegmentsReachable(segments) {
+ for (const segment of segments) {
+ if (segment.reachable) {
+ return true;
}
- };
+ }
+
+ return false;
+}
+
+module.exports = {
+ meta: {
+ // ...
+ },
+ create(context) {
+
+ // tracks the code path we are currently in
+ let currentCodePath;
+
+ // tracks the segments we've traversed in the current code path
+ let currentSegments;
+
+ // tracks all current segments for all open paths
+ const allCurrentSegments = [];
+
+ return {
+
+ onCodePathStart(codePath) {
+ currentCodePath = codePath;
+ allCurrentSegments.push(currentSegments);
+ currentSegments = new Set();
+ },
+
+ onCodePathEnd(codePath) {
+ currentCodePath = codePath.upper;
+ currentSegments = allCurrentSegments.pop();
+ },
+
+ onCodePathSegmentStart(segment) {
+ currentSegments.add(segment);
+ },
+
+ onCodePathSegmentEnd(segment) {
+ currentSegments.delete(segment);
+ },
+
+ onUnreachableCodePathSegmentStart(segment) {
+ currentSegments.add(segment);
+ },
+
+ onUnreachableCodePathSegmentEnd(segment) {
+ currentSegments.delete(segment);
+ },
+
+ ExpressionStatement(node) {
+
+ // check all the code path segments that led to this node
+ if (!areAnySegmentsReachable(currentSegments)) {
+ context.report({ message: "Unreachable!", node });
+ }
+ }
+
+ };
+
+ }
};
```
@@ -249,9 +381,9 @@ See Also:
[no-fallthrough](https://github.com/eslint/eslint/blob/HEAD/lib/rules/no-fallthrough.js),
[consistent-return](https://github.com/eslint/eslint/blob/HEAD/lib/rules/consistent-return.js)
-### To check state of a code path
+### Check if a function is called in every path
-This example is checking whether or not the parameter `cb` is called in every path.
+This example checks whether or not the parameter `cb` is called in every path.
Instances of `CodePath` and `CodePathSegment` are shared to every rule.
So a rule must not modify those instances.
Please use a map of information instead.
@@ -271,75 +403,101 @@ function isCbCalled(info) {
return info.cbCalled;
}
-module.exports = function(context) {
- var funcInfoStack = [];
- var segmentInfoMap = Object.create(null);
-
- return {
- // Checks `cb`.
- "onCodePathStart": function(codePath, node) {
- funcInfoStack.push({
- codePath: codePath,
- hasCb: hasCb(node, context)
- });
- },
- "onCodePathEnd": function(codePath, node) {
- funcInfoStack.pop();
-
- // Checks `cb` was called in every paths.
- var cbCalled = codePath.finalSegments.every(function(segment) {
- var info = segmentInfoMap[segment.id];
- return info.cbCalled;
- });
-
- if (!cbCalled) {
- context.report({
- message: "`cb` should be called in every path.",
- node: node
+module.exports = {
+ meta: {
+ // ...
+ },
+ create(context) {
+
+ let funcInfo;
+ const funcInfoStack = [];
+ const segmentInfoMap = Object.create(null);
+
+ return {
+ // Checks `cb`.
+ onCodePathStart(codePath, node) {
+ funcInfoStack.push(funcInfo);
+
+ funcInfo = {
+ codePath: codePath,
+ hasCb: hasCb(node, context),
+ currentSegments: new Set()
+ };
+ },
+
+ onCodePathEnd(codePath, node) {
+ funcInfo = funcInfoStack.pop();
+
+ // Checks `cb` was called in every paths.
+ const cbCalled = codePath.finalSegments.every(function(segment) {
+ const info = segmentInfoMap[segment.id];
+ return info.cbCalled;
});
- }
- },
-
- // Manages state of code paths.
- "onCodePathSegmentStart": function(segment) {
- var funcInfo = funcInfoStack[funcInfoStack.length - 1];
- // Ignores if `cb` doesn't exist.
- if (!funcInfo.hasCb) {
- return;
+ if (!cbCalled) {
+ context.report({
+ message: "`cb` should be called in every path.",
+ node: node
+ });
+ }
+ },
+
+ // Manages state of code paths and tracks traversed segments
+ onCodePathSegmentStart(segment) {
+
+ funcInfo.currentSegments.add(segment);
+
+ // Ignores if `cb` doesn't exist.
+ if (!funcInfo.hasCb) {
+ return;
+ }
+
+ // Initialize state of this path.
+ const info = segmentInfoMap[segment.id] = {
+ cbCalled: false
+ };
+
+ // If there are the previous paths, merges state.
+ // Checks `cb` was called in every previous path.
+ if (segment.prevSegments.length > 0) {
+ info.cbCalled = segment.prevSegments.every(isCbCalled);
+ }
+ },
+
+ // Tracks unreachable segment traversal
+ onUnreachableCodePathSegmentStart(segment) {
+ funcInfo.currentSegments.add(segment);
+ },
+
+ // Tracks reachable segment traversal
+ onCodePathSegmentEnd(segment) {
+ funcInfo.currentSegments.delete(segment);
+ },
+
+ // Tracks unreachable segment traversal
+ onUnreachableCodePathSegmentEnd(segment) {
+ funcInfo.currentSegments.delete(segment);
+ },
+
+ // Checks reachable or not.
+ CallExpression(node) {
+
+ // Ignores if `cb` doesn't exist.
+ if (!funcInfo.hasCb) {
+ return;
+ }
+
+ // Sets marks that `cb` was called.
+ const callee = node.callee;
+ if (callee.type === "Identifier" && callee.name === "cb") {
+ funcInfo.currentSegments.forEach(segment => {
+ const info = segmentInfoMap[segment.id];
+ info.cbCalled = true;
+ });
+ }
}
-
- // Initialize state of this path.
- var info = segmentInfoMap[segment.id] = {
- cbCalled: false
- };
-
- // If there are the previous paths, merges state.
- // Checks `cb` was called in every previous path.
- if (segment.prevSegments.length > 0) {
- info.cbCalled = segment.prevSegments.every(isCbCalled);
- }
- },
-
- // Checks reachable or not.
- "CallExpression": function(node) {
- var funcInfo = funcInfoStack[funcInfoStack.length - 1];
-
- // Ignores if `cb` doesn't exist.
- if (!funcInfo.hasCb) {
- return;
- }
-
- // Sets marks that `cb` was called.
- var callee = node.callee;
- if (callee.type === "Identifier" && callee.name === "cb") {
- funcInfo.codePath.currentSegments.forEach(function(segment) {
- var info = segmentInfoMap[segment.id];
- info.cbCalled = true;
- });
- }
- }
- };
+ };
+ }
};
```
diff --git a/docs/src/extend/custom-parsers.md b/docs/src/extend/custom-parsers.md
index 388f54b726b..55ed726f046 100644
--- a/docs/src/extend/custom-parsers.md
+++ b/docs/src/extend/custom-parsers.md
@@ -12,6 +12,8 @@ ESLint custom parsers let you extend ESLint to support linting new non-standard
## Creating a Custom Parser
+### Methods in Custom Parsers
+
A custom parser is a JavaScript object with either a `parse` or `parseForESLint` method. The `parse` method only returns the AST, whereas `parseForESLint` also returns additional values that let the parser customize the behavior of ESLint even more.
Both methods should take in the source code as the first argument, and an optional configuration object as the second argument, which is provided as [`parserOptions`](../use/configure/language-options#specifying-parser-options) in a configuration file.
@@ -33,21 +35,37 @@ function parse(code, options) {
module.exports = { parse };
```
-## `parse` Return Object
+### `parse` Return Object
The `parse` method should simply return the [AST](#ast-specification) object.
-## `parseForESLint` Return Object
+### `parseForESLint` Return Object
The `parseForESLint` method should return an object that contains the required property `ast` and optional properties `services`, `scopeManager`, and `visitorKeys`.
* `ast` should contain the [AST](#ast-specification) object.
-* `services` can contain any parser-dependent services (such as type checkers for nodes). The value of the `services` property is available to rules as `context.parserServices`. Default is an empty object.
+* `services` can contain any parser-dependent services (such as type checkers for nodes). The value of the `services` property is available to rules as `context.sourceCode.parserServices`. Default is an empty object.
* `scopeManager` can be a [ScopeManager](./scope-manager-interface) object. Custom parsers can use customized scope analysis for experimental/enhancement syntaxes. The default is the `ScopeManager` object which is created by [eslint-scope](https://github.com/eslint/eslint-scope).
* Support for `scopeManager` was added in ESLint v4.14.0. ESLint versions that support `scopeManager` will provide an `eslintScopeManager: true` property in `parserOptions`, which can be used for feature detection.
* `visitorKeys` can be an object to customize AST traversal. The keys of the object are the type of AST nodes. Each value is an array of the property names which should be traversed. The default is [KEYS of `eslint-visitor-keys`](https://github.com/eslint/eslint-visitor-keys#evkkeys).
* Support for `visitorKeys` was added in ESLint v4.14.0. ESLint versions that support `visitorKeys` will provide an `eslintVisitorKeys: true` property in `parserOptions`, which can be used for feature detection.
+### Meta Data in Custom Parsers
+
+For easier debugging and more effective caching of custom parsers, it's recommended to provide a name and version in a `meta` object at the root of your custom parsers, like this:
+
+```js
+// preferred location of name and version
+module.exports = {
+ meta: {
+ name: "eslint-parser-custom",
+ version: "1.2.3"
+ }
+};
+```
+
+The `meta.name` property should match the npm package name for your custom parser and the `meta.version` property should match the npm package version for your custom parser. The easiest way to accomplish this is by reading this information from your `package.json`.
+
## AST Specification
The AST that custom parsers should create is based on [ESTree](https://github.com/estree/estree). The AST requires some additional properties about detail information of the source code.
@@ -120,7 +138,7 @@ To learn more about using ESLint parsers in your project, refer to [Configure a
For a complex example of a custom parser, refer to the [`@typescript-eslint/parser`](https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/parser) source code.
-A simple custom parser that provides a `context.parserServices.foo()` method to rules.
+A simple custom parser that provides a `context.sourceCode.parserServices.foo()` method to rules.
```javascript
// awesome-custom-parser.js
diff --git a/docs/src/extend/custom-processors.md b/docs/src/extend/custom-processors.md
index d112d724688..d5a5261d976 100644
--- a/docs/src/extend/custom-processors.md
+++ b/docs/src/extend/custom-processors.md
@@ -4,7 +4,7 @@ eleventyNavigation:
key: custom processors
parent: create plugins
title: Custom Processors
- order: 2
+ order: 3
---
@@ -18,6 +18,10 @@ In order to create a custom processor, the object exported from your module has
module.exports = {
processors: {
"processor-name": {
+ meta: {
+ name: "eslint-processor-name",
+ version: "1.2.3"
+ },
// takes text of the file and filename
preprocess: function(text, filename) {
// here, you can strip out any non-JS content
@@ -121,6 +125,8 @@ By default, ESLint does not perform autofixes when a custom processor is used, e
You can have both rules and custom processors in a single plugin. You can also have multiple processors in one plugin. To support multiple extensions, add each one to the `processors` element and point them to the same object.
+**The `meta` object** helps ESLint cache the processor and provide more friendly debug message. The `meta.name` property should match the processor name and the `meta.version` property should match the npm package version for your processors. The easiest way to accomplish this is by reading this information from your `package.json`.
+
## Specifying Processor in Config Files
To use a processor, add its ID to a `processor` section in the config file. Processor ID is a concatenated string of plugin name and processor name with a slash as a separator. This can also be added to a `overrides` section of the config, to specify which processors should handle which files.
@@ -139,6 +145,10 @@ See [Specify a Processor](../use/configure/plugins#specify-a-processor) in the P
## File Extension-named Processor
+::: warning
+This feature is deprecated and only works in eslintrc-style configuration files. Flat config files do not automatically apply processors; you need to explicitly set the `processor` property.
+:::
+
If a custom processor name starts with `.`, ESLint handles the processor as a **file extension-named processor**. ESLint applies the processor to files with that filename extension automatically. Users don't need to specify the file extension-named processors in their config files.
For example:
diff --git a/docs/src/extend/custom-rule-tutorial.md b/docs/src/extend/custom-rule-tutorial.md
new file mode 100644
index 00000000000..8426961acae
--- /dev/null
+++ b/docs/src/extend/custom-rule-tutorial.md
@@ -0,0 +1,478 @@
+---
+title: Custom Rule Tutorial
+eleventyNavigation:
+ key: custom rule tutorial
+ parent: create plugins
+ title: Custom Rule Tutorial
+ order: 1
+---
+This tutorial covers how to create a custom rule for ESLint and distribute it with a plugin.
+
+You can create custom rules to validate if your code meets a certain expectation, and determine what to do if it does not meet that expectation. Plugins package custom rules and other configuration, allowing you to easily share and reuse them in different projects.
+
+To learn more about custom rules and plugins refer to the following documentation:
+
+* [Custom Rules](custom-rules)
+* [Plugins](plugins)
+
+## Why Create a Custom Rule?
+
+Create a custom rule if the ESLint [built-in rules](../rules/) and community-published custom rules do not meet your needs. You might create a custom rule to enforce a best practice for your company or project, prevent a particular bug from recurring, or ensure compliance with a style guide.
+
+Before creating a custom rule that isn't specific to your company or project, it's worth searching the web to see if someone has published a plugin with a custom rule that solves your use case. It's quite possible the rule may already exist.
+
+## Prerequisites
+
+Before you begin, make sure you have the following installed in your development environment:
+
+* [Node.js](https://nodejs.org/en/download/)
+* [npm](https://www.npmjs.com/)
+
+This tutorial also assumes that you have a basic understanding of ESLint and ESLint rules.
+
+## The Custom Rule
+
+The custom rule in this tutorial requires that all `const` variables named `foo` are assigned the string literal `"bar"`. The rule is defined in the file `enforce-foo-bar.js`. The rule also suggests replacing any other value assigned to `const foo` with `"bar"`.
+
+For example, say you had the following `foo.js` file:
+
+```javascript
+// foo.js
+
+const foo = "baz123";
+```
+
+Running ESLint with the rule would flag `"baz123"` as an incorrect value for variable `foo`. If ESLint is running in autofix mode, then ESLint would fix the file to contain the following:
+
+```javascript
+// foo.js
+
+const foo = "bar";
+```
+
+## Step 1: Set up Your Project
+
+First, create a new project for your custom rule. Create a new directory, initiate a new npm project in it, and create a new file for the custom rule:
+
+```shell
+mkdir eslint-custom-rule-example # create directory
+cd eslint-custom-rule-example # enter the directory
+npm init -y # init new npm project
+touch enforce-foo-bar.js # create file enforce-foo-bar.js
+```
+
+## Step 2: Stub Out the Rule File
+
+In the `enforce-foo-bar.js` file, add some scaffolding for the `enforce-foo-bar` custom rule. Also, add a `meta` object with some basic information about the rule.
+
+```javascript
+// enforce-foo-bar.js
+
+module.exports = {
+ meta: {
+ // TODO: add metadata
+ },
+ create(context) {
+ return {
+ // TODO: add callback function(s)
+ };
+ }
+};
+```
+
+## Step 3: Add Rule Metadata
+
+Before writing the rule, add some metadata to the rule object. ESLint uses this information when running the rule.
+
+Start by exporting an object with a `meta` property containing the rule's metadata, such as the rule type, documentation, and fixability. In this case, the rule type is "problem," the description is "Enforce that a variable named `foo` can only be assigned a value of 'bar'.", and the rule is fixable by modifying the code.
+
+```javascript
+// enforce-foo-bar.js
+
+module.exports = {
+ meta: {
+ type: "problem",
+ docs: {
+ description: "Enforce that a variable named `foo` can only be assigned a value of 'bar'.",
+ },
+ fixable: "code",
+ schema: []
+ },
+ create(context) {
+ return {
+ // TODO: add callback function(s)
+ };
+ }
+};
+```
+
+To learn more about rule metadata, refer to [Rule Structure](custom-rules#rule-structure).
+
+## Step 4: Add Rule Visitor Methods
+
+Define the rule's `create` function, which accepts a `context` object and returns an object with a property for each syntax node type you want to handle. In this case, you want to handle `VariableDeclarator` nodes.
+You can choose any [ESTree node type](https://github.com/estree/estree) or [selector](selectors).
+
+Inside the `VariableDeclarator` visitor method, check if the node represents a `const` variable declaration, if its name is `foo`, and if it's not assigned to the string `"bar"`. You do this by evaluating the `node` passed to the `VariableDeclaration` method.
+
+If the `const foo` declaration is assigned a value of `"bar"`, then the rule does nothing. If `const foo` **is not** assigned a value of `"bar"`, then `context.report()` reports an error to ESLint. The error report includes information about the error and how to fix it.
+
+```javascript
+// enforce-foo-bar.js
+{% raw %}
+module.exports = {
+ meta: {
+ type: "problem",
+ docs: {
+ description: "Enforce that a variable named `foo` can only be assigned a value of 'bar'."
+ },
+ fixable: "code",
+ schema: []
+ },
+ create(context) {
+ return {
+
+ // Performs action in the function on every variable declarator
+ VariableDeclarator(node) {
+
+ // Check if a `const` variable declaration
+ if (node.parent.kind === "const") {
+
+ // Check if variable name is `foo`
+ if (node.id.type === "Identifier" && node.id.name === "foo") {
+
+ // Check if value of variable is "bar"
+ if (node.init && node.init.type === "Literal" && node.init.value !== "bar") {
+
+ /*
+ * Report error to ESLint. Error message uses
+ * a message placeholder to include the incorrect value
+ * in the error message.
+ * Also includes a `fix(fixer)` function that replaces
+ * any values assigned to `const foo` with "bar".
+ */
+ context.report({
+ node,
+ message: 'Value other than "bar" assigned to `const foo`. Unexpected value: {{ notBar }}.',
+ data: {
+ notBar: node.init.value
+ },
+ fix(fixer) {
+ return fixer.replaceText(node.init, '"bar"');
+ }
+ });
+ }
+ }
+ }
+ }
+ };
+ }
+};
+{% endraw %}
+```
+
+## Step 5: Set up Testing
+
+With the rule written, you can test it to make sure it's working as expected.
+
+ESLint provides the built-in [`RuleTester`](../integrate/nodejs-api#ruletester) class to test rules. You do not need to use third-party testing libraries to test ESLint rules, but `RuleTester` works seamlessly with tools like Mocha and Jest.
+
+Next, create the file for the tests, `enforce-foo-bar.test.js`:
+
+```shell
+touch enforce-foo-bar.test.js
+```
+
+You will use the `eslint` package in the test file. Install it as a development dependency:
+
+```shell
+npm install eslint --save-dev
+```
+
+And add a test script to your `package.json` file to run the tests:
+
+```javascript
+// package.json
+{
+ // ...other configuration
+ "scripts": {
+ "test": "node enforce-foo-bar.test.js"
+ },
+ // ...other configuration
+}
+```
+
+## Step 6: Write the Test
+
+To write the test using `RuleTester`, import the class and your custom rule into the `enforce-foo-bar.test.js` file.
+
+The `RuleTester#run()` method tests the rule against valid and invalid test cases. If the rule fails to pass any of the test scenarios, this method throws an error.
+`RuleTester` requires that at least one valid and one invalid test scenario be present.
+
+```javascript
+// enforce-foo-bar.test.js
+const {RuleTester} = require("eslint");
+const fooBarRule = require("./enforce-foo-bar");
+
+const ruleTester = new RuleTester({
+ // Must use at least ecmaVersion 2015 because
+ // that's when `const` variables were introduced.
+ parserOptions: { ecmaVersion: 2015 }
+});
+
+// Throws error if the tests in ruleTester.run() do not pass
+ruleTester.run(
+ "enforce-foo-bar", // rule name
+ fooBarRule, // rule code
+ { // checks
+ // 'valid' checks cases that should pass
+ valid: [{
+ code: "const foo = 'bar';",
+ }],
+ // 'invalid' checks cases that should not pass
+ invalid: [{
+ code: "const foo = 'baz';",
+ output: 'const foo = "bar";',
+ errors: 1,
+ }],
+ }
+);
+
+console.log("All tests passed!");
+```
+
+Run the test with the following command:
+
+```shell
+npm test
+```
+
+If the test passes, you should see the following in your console:
+
+```shell
+All tests passed!
+```
+
+## Step 7: Bundle the Custom Rule in a Plugin
+
+Now that you've written the custom rule and validated that it works, you can include it in a plugin. Using a plugin, you can share the rule in an npm package to use in other projects.
+
+Create the file for the plugin:
+
+```shell
+touch eslint-plugin-example.js
+```
+
+And now write the plugin code. Plugins are just exported JavaScript objects. To include a rule in a plugin, include it in the plugin's `rules` object, which contains key-value pairs of rule names and their source code.
+
+To learn more about creating plugins, refer to [Create Plugins](plugins).
+
+```javascript
+// eslint-plugin-example.js
+
+const fooBarRule = require("./enforce-foo-bar");
+const plugin = { rules: { "enforce-foo-bar": fooBarRule } };
+module.exports = plugin;
+```
+
+## Step 8: Use the Plugin Locally
+
+You can use a locally defined plugin to execute the custom rule in your project. To use a local plugin, specify the path to the plugin in the `plugins` property of your ESLint configuration file.
+
+You might want to use a locally defined plugin in one of the following scenarios:
+
+* You want to test the plugin before publishing it to npm.
+* You want to use a plugin, but do not want to publish it to npm.
+
+Before you can add the plugin to the project, create an ESLint configuration for your project using a [flat configuration file](../use/configure/configuration-files-new), `eslint.config.js`:
+
+```shell
+touch eslint.config.js
+```
+
+Then, add the following code to `eslint.config.js`:
+
+```javascript
+// eslint.config.js
+"use strict";
+
+// Import the ESLint plugin locally
+const eslintPluginExample = require("./eslint-plugin-example");
+
+module.exports = [
+ {
+ files: ["**/*.js"],
+ languageOptions: {
+ sourceType: "commonjs",
+ ecmaVersion: "latest",
+ },
+ // Using the eslint-plugin-example plugin defined locally
+ plugins: {"example": eslintPluginExample},
+ rules: {
+ "example/enforce-foo-bar": "error",
+ },
+ }
+]
+```
+
+Before you can test the rule, you must create a file to test the rule on.
+
+Create a file `example.js`:
+
+```shell
+touch example.js
+```
+
+Add the following code to `example.js`:
+
+```javascript
+// example.js
+
+function correctFooBar() {
+ const foo = "bar";
+}
+
+function incorrectFoo(){
+ const foo = "baz"; // Problem!
+}
+```
+
+Now you're ready to test the custom rule with the locally defined plugin.
+
+Run ESLint on `example.js`:
+
+```shell
+npx eslint example.js
+```
+
+This produces the following output in the terminal:
+
+```text
+//eslint-custom-rule-example/example.js
+ 8:11 error Value other than "bar" assigned to `const foo`. Unexpected value: baz example/enforce-foo-bar
+
+✖ 1 problem (1 error, 0 warnings)
+ 1 error and 0 warnings potentially fixable with the `--fix` option.
+```
+
+## Step 9: Publish the Plugin
+
+To publish a plugin containing a rule to npm, you need to configure the `package.json`. Add the following in the corresponding fields:
+
+1. `"name"`: A unique name for the package. No other package on npm can have the same name.
+1. `"main"`: The relative path to the plugin file. Following this example, the path is `"eslint-plugin-example.js"`.
+1. `"description"`: A description of the package that's viewable on npm.
+1. `"peerDependencies"`: Add `"eslint": ">=8.0.0"` as a peer dependency. Any version greater than or equal to that is necessary to use the plugin. Declaring `eslint` as a peer dependency requires that users add the package to the project separately from the plugin.
+1. `"keywords"`: Include the standard keywords `["eslint", "eslintplugin", "eslint-plugin"]` to make the package easy to find. You can add any other keywords that might be relevant to your plugin as well.
+
+A complete annotated example of what a plugin's `package.json` file should look like:
+
+```javascript
+// package.json
+{
+ // Name npm package.
+ // Add your own package name. eslint-plugin-example is taken!
+ "name": "eslint-plugin-example",
+ "version": "1.0.0",
+ "description": "ESLint plugin for enforce-foo-bar rule.",
+ "main": "eslint-plugin-example.js", // plugin entry point
+ "scripts": {
+ "test": "node enforce-foo-bar.test.js"
+ },
+ // Add eslint>=8.0.0 as a peer dependency.
+ "peerDependencies": {
+ "eslint": ">=8.0.0"
+ },
+ // Add these standard keywords to make plugin easy to find!
+ "keywords": [
+ "eslint",
+ "eslintplugin",
+ "eslint-plugin"
+ ],
+ "author": "",
+ "license": "ISC",
+ "devDependencies": {
+ "eslint": "^8.36.0"
+ }
+}
+```
+
+To publish the package, run `npm publish` and follow the CLI prompts.
+
+You should see the package live on npm!
+
+## Step 10: Use the Published Custom Rule
+
+Next, you can use the published plugin.
+
+Run the following command in your project to download the package:
+
+```shell
+npm install --save-dev eslint-plugin-example # Add your package name here
+```
+
+Update the `eslint.config.js` to use the packaged version of the plugin:
+
+```javascript
+// eslint.config.js
+"use strict";
+
+// Import the plugin downloaded from npm
+const eslintPluginExample = require("eslint-plugin-example");
+
+// ... rest of configuration
+```
+
+Now you're ready to test the custom rule.
+
+Run ESLint on the `example.js` file you created in step 8, now with the downloaded plugin:
+
+```shell
+npx eslint example.js
+```
+
+This produces the following output in the terminal:
+
+```text
+//eslint-custom-rule-example/example.js
+ 8:11 error Value other than "bar" assigned to `const foo`. Unexpected value: baz example/enforce-foo-bar
+
+✖ 1 problem (1 error, 0 warnings)
+ 1 error and 0 warnings potentially fixable with the `--fix` option.
+```
+
+As you can see in the above message, you can actually fix the issue with the `--fix` flag, correcting the variable assignment to be `"bar"`.
+
+Run ESLint again with the `--fix` flag:
+
+```shell
+npx eslint example.js --fix
+```
+
+There is no error output in the terminal when you run this, but you can see the fix applied in `example.js`. You should see the following:
+
+```javascript
+// example.js
+
+// ... rest of file
+
+function incorrectFoo(){
+ const foo = "bar"; // Fixed!
+}
+```
+
+## Summary
+
+In this tutorial, you've made a custom rule that requires all `const` variables named `foo` to be assigned the string `"bar"` and suggests replacing any other value assigned to `const foo` with `"bar"`. You've also added the rule to a plugin, and published the plugin on npm.
+
+Through doing this, you've learned the following practices which you can apply to create other custom rules and plugins:
+
+1. Creating a custom ESLint rule
+1. Testing the custom rule
+1. Bundling the rule in a plugin
+1. Publishing the plugin
+1. Using the rule from the plugin
+
+## View the Tutorial Code
+
+You can view the annotated source code for the tutorial [here](https://github.com/eslint/eslint/tree/main/docs/_examples/custom-rule-tutorial-code).
diff --git a/docs/src/extend/custom-rules.md b/docs/src/extend/custom-rules.md
index 35b04fdbf8e..35df20541b6 100644
--- a/docs/src/extend/custom-rules.md
+++ b/docs/src/extend/custom-rules.md
@@ -4,7 +4,7 @@ eleventyNavigation:
key: custom rules
parent: create plugins
title: Custom Rules
- order: 1
+ order: 2
---
@@ -131,7 +131,7 @@ The `context` object has the following properties:
* `sourceCode`: (`object`) A `SourceCode` object that you can use to work with the source that was passed to ESLint (see [Accessing the Source Code](#accessing-the-source-code)).
* `settings`: (`object`) The [shared settings](../use/configure/configuration-files#adding-shared-settings) from the configuration.
* `parserPath`: (`string`) The name of the `parser` from the configuration.
-* `parserServices`: (`object`) Contains parser-provided services for rules. The default parser does not provide any services. However, if a rule is intended to be used with a custom parser, it could use `parserServices` to access anything provided by that parser. (For example, a TypeScript parser could provide the ability to get the computed type of a given node.)
+* `parserServices`: (**Deprecated:** Use `SourceCode#parserServices` instead.) Contains parser-provided services for rules. The default parser does not provide any services. However, if a rule is intended to be used with a custom parser, it could use `parserServices` to access anything provided by that parser. (For example, a TypeScript parser could provide the ability to get the computed type of a given node.)
* `parserOptions`: The parser options configured for this run (more details [here](../use/configure/language-options#specifying-parser-options)).
Additionally, the `context` object has the following methods:
@@ -575,6 +575,7 @@ There are also some properties you can access:
* `ast`: (`object`) `Program` node of the AST for the code being linted.
* `scopeManager`: [ScopeManager](./scope-manager-interface#scopemanager-interface) object of the code.
* `visitorKeys`: (`object`) Visitor keys to traverse this AST.
+* `parserServices`: (`object`) Contains parser-provided services for rules. The default parser does not provide any services. However, if a rule is intended to be used with a custom parser, it could use `parserServices` to access anything provided by that parser. (For example, a TypeScript parser could provide the ability to get the computed type of a given node.)
* `lines`: (`array`) Array of lines, split according to the specification's definition of line breaks.
You should use a `SourceCode` object whenever you need to get more information about the code being linted.
@@ -612,39 +613,168 @@ You can also access comments through many of `sourceCode`'s methods using the `i
### Options Schemas
-Rules may export a `schema` property, which is a [JSON Schema](https://json-schema.org/) format description of a rule's options which will be used by ESLint to validate configuration options and prevent invalid or unexpected inputs before they are passed to the rule in `context.options`.
+Rules may specify a `schema` property, which is a [JSON Schema](https://json-schema.org/) format description of a rule's options which will be used by ESLint to validate configuration options and prevent invalid or unexpected inputs before they are passed to the rule in `context.options`.
+
+Note: Prior to ESLint v9.0.0, rules without a schema are passed their options directly from the config without any validation. In ESLint v9.0.0 and later, rules without schemas will throw errors when options are passed. See the [Require schemas and object-style rules](https://github.com/eslint/rfcs/blob/main/designs/2021-schema-object-rules/README.md) RFC for further details.
+
+When validating a rule's config, there are five steps:
-There are two formats for a rule's exported `schema`:
+1. If the rule config is not an array, then the value is wrapped into an array (e.g. `"off"` becomes `["off"]`); if the rule config is an array then it is used directly.
+2. ESLint validates the first element of the rule config array as a severity (`"off"`, `"warn"`, `"error"`, `0`, `1`, `2`)
+3. If the severity is `off` or `0`, then the rule is disabled and validation stops, ignoring any other elements of the rule config array.
+4. If the rule is enabled, then any elements of the array after the severity are copied into the `context.options` array (e.g. a config of `["warn", "never", { someOption: 5 }]` results in `context.options = ["never", { someOption: 5 }]`)
+5. The rule's schema validation is run on the `context.options` array.
-1. A full JSON Schema object describing all possible options the rule accepts.
-2. An array of JSON Schema objects for each optional positional argument.
+Note: this means that the rule schema cannot validate the severity. The rule schema only validates the array elements _after_ the severity in a rule config. There is no way for a rule to know what severity it is configured at.
-In both cases, these should exclude the [severity](../use/configure/rules#rule-severities), as ESLint automatically validates this first.
+There are two formats for a rule's `schema`:
+
+* An array of JSON Schema objects
+ * Each element will be checked against the same position in the `context.options` array.
+ * If the `context.options` array has fewer elements than there are schemas, then the unmatched schemas are ignored
+ * If the `context.options` array has more elements than there are schemas, then the validation fails
+ * There are two important consequences to using this format:
+ * It is _always valid_ for a user to provide no options to your rule (beyond severity)
+ * If you specify an empty array, then it is _always an error_ for a user to provide any options to your rule (beyond severity)
+* A full JSON Schema object that will validate the `context.options` array
+ * The schema should assume an array of options to validate even if your rule only accepts one option.
+ * The schema can be arbitrarily complex, so you can validate completely different sets of potential options via `oneOf`, `anyOf` etc.
+ * The supported version of JSON Schemas is [Draft-04](http://json-schema.org/draft-04/schema), so some newer features such as `if` or `$data` are unavailable.
+ * At present, it is explicitly planned to not update schema support beyond this level due to ecosystem compatibility concerns. See [this comment](https://github.com/eslint/eslint/issues/13888#issuecomment-872591875) for further context.
For example, the `yoda` rule accepts a primary mode argument of `"always"` or `"never"`, as well as an extra options object with an optional property `exceptRange`:
```js
+// Valid configuration:
+// "yoda": "warn"
+// "yoda": ["error"]
+// "yoda": ["error", "always"]
// "yoda": ["error", "never", { "exceptRange": true }]
+// Invalid configuration:
+// "yoda": ["warn", "never", { "exceptRange": true }, 5]
+// "yoda": ["error", { "exceptRange": true }, "never"]
module.exports = {
meta: {
schema: [
{
- "enum": ["always", "never"]
+ enum: ["always", "never"]
},
{
- "type": "object",
- "properties": {
- "exceptRange": {
- "type": "boolean"
- }
+ type: "object",
+ properties: {
+ exceptRange: { type: "boolean" }
},
- "additionalProperties": false
+ additionalProperties: false
}
]
- },
+ }
};
```
+And here is the equivalent object-based schema:
+
+```js
+// Valid configuration:
+// "yoda": "warn"
+// "yoda": ["error"]
+// "yoda": ["error", "always"]
+// "yoda": ["error", "never", { "exceptRange": true }]
+// Invalid configuration:
+// "yoda": ["warn", "never", { "exceptRange": true }, 5]
+// "yoda": ["error", { "exceptRange": true }, "never"]
+module.exports = {
+ meta: {
+ schema: {
+ type: "array",
+ minItems: 0,
+ maxItems: 2,
+ items: [
+ {
+ enum: ["always", "never"]
+ },
+ {
+ type: "object",
+ properties: {
+ exceptRange: { type: "boolean" }
+ },
+ additionalProperties: false
+ }
+ ]
+ }
+ }
+};
+```
+
+Object schemas can be more precise and restrictive in what is permitted. For example, the below schema always requires the first option to be specified (a number between 0 and 10), but the second option is optional, and can either be an object with some options explicitly set, or `"off"` or `"strict"`.
+
+```js
+// Valid configuration:
+// "someRule": ["error", 6]
+// "someRule": ["error", 5, "strict"]
+// "someRule": ["warn", 10, { someNonOptionalProperty: true }]
+// Invalid configuration:
+// "someRule": "warn"
+// "someRule": ["error"]
+// "someRule": ["warn", 15]
+// "someRule": ["warn", 7, { }]
+// "someRule": ["error", 3, "on"]
+// "someRule": ["warn", 7, { someOtherProperty: 5 }]
+// "someRule": ["warn", 7, { someNonOptionalProperty: false, someOtherProperty: 5 }]
+module.exports = {
+ meta: {
+ schema: {
+ type: "array",
+ minItems: 1, // Can't specify only severity!
+ maxItems: 2,
+ items: [
+ {
+ type: "number",
+ minimum: 0,
+ maximum: 10
+ },
+ {
+ anyOf: [
+ {
+ type: "object",
+ properties: {
+ someNonOptionalProperty: { type: "boolean" }
+ },
+ required: ["someNonOptionalProperty"],
+ additionalProperties: false
+ },
+ {
+ enum: ["off", "strict"]
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
+```
+
+Remember, rule options are always an array, so be careful not to specify a schema for a non-array type at the top level. If your schema does not specify an array at the top-level, users can _never_ enable your rule, as their configuration will always be invalid when the rule is enabled.
+
+Here's an example schema that will always fail validation:
+
+```js
+// Possibly trying to validate ["error", { someOptionalProperty: true }]
+// but when the rule is enabled, config will always fail validation because the options are an array which doesn't match "object"
+module.exports = {
+ meta: {
+ schema: {
+ type: "object",
+ properties: {
+ someOptionalProperty: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ }
+}
+```
+
**Note:** If your rule schema uses JSON schema [`$ref`](https://json-schema.org/understanding-json-schema/structuring.html#ref) properties, you must use the full JSON Schema object rather than the array of positional property schemas. This is because ESLint transforms the array shorthand into a single schema without updating references that makes them incorrect (they are ignored).
To learn more about JSON Schema, we recommend looking at some examples on the [JSON Schema website](https://json-schema.org/learn/), or reading the free [Understanding JSON Schema](https://json-schema.org/understanding-json-schema/) ebook.
diff --git a/docs/src/extend/index.md b/docs/src/extend/index.md
index 9f623a6119d..815a42b457b 100644
--- a/docs/src/extend/index.md
+++ b/docs/src/extend/index.md
@@ -25,6 +25,10 @@ This page summarizes the various ways that you can extend ESLint and how these e
You've developed custom rules for ESLint and you want to share them with the community. You can publish an ESLint plugin on npm.
+## [Custom Rule Tutorial](custom-rule-tutorial)
+
+A tutorial that walks you through creating a custom rule for ESLint.
+
## [Custom Rules](custom-rules)
This section explains how to create custom rules to use with ESLint.
@@ -44,7 +48,3 @@ This section explains how you can use a custom processor to have ESLint process
## [Share Configurations](shareable-configs)
This section explains how you can bundle and share ESLint configuration in a JavaScript package.
-
-## [Node.js API Reference](../integrate/nodejs-api)
-
-If you're interested in writing a tool that uses ESLint, then you can use the Node.js API to get programmatic access to functionality.
diff --git a/docs/src/extend/plugin-migration-flat-config.md b/docs/src/extend/plugin-migration-flat-config.md
new file mode 100644
index 00000000000..6c277596fc4
--- /dev/null
+++ b/docs/src/extend/plugin-migration-flat-config.md
@@ -0,0 +1,293 @@
+---
+title: Plugin Migration to Flat Config
+eleventyNavigation:
+ key: plugin flat config
+ parent: create plugins
+ title: Migration to Flat Config
+ order: 4
+
+---
+
+Beginning in ESLint v9.0.0, the default configuration system will be the new flat config system. In order for your plugins to work with flat config files, you'll need to make some changes to your existing plugins.
+
+## Recommended Plugin Structure
+
+To make it easier to work with your plugin in the flat config system, it's recommended that you switch your existing plugin entrypoint to look like this:
+
+```js
+const plugin = {
+ meta: {},
+ configs: {},
+ rules: {},
+ processors: {}
+};
+
+// for ESM
+export default plugin;
+
+// OR for CommonJS
+module.exports = plugin;
+```
+
+This structure allows the most flexibility when making other changes discussed on this page.
+
+## Adding Plugin Meta Information
+
+With the old eslintrc configuration system, ESLint could pull information about the plugin from the package name, but with flat config, ESLint no longer has access to the name of the plugin package. To replace that missing information, you should add a `meta` key that contains at least a `name` key, and ideally, a `version` key, such as:
+
+```js
+const plugin = {
+ meta: {
+ name: "eslint-plugin-example",
+ version: "1.0.0"
+ },
+ configs: {},
+ rules: {},
+ processors: {}
+};
+
+// for ESM
+export default plugin;
+
+// OR for CommonJS
+module.exports = plugin;
+```
+
+If your plugin is published as an npm package, the `name` and `version` should be the same as in your `package.json` file; otherwise, you can assign any value you'd like.
+
+Without this meta information, your plugin will not be usable with the `--cache` and `--print-config` command line options.
+
+## Migrating Rules for Flat Config
+
+No changes are necessary for the `rules` key in your plugin. Everything works the same as with the old eslintrc configuration system.
+
+## Migrating Processors for Flat Config
+
+No changes are necessary for the `processors` key in your plugin as long as you aren't using file extension-named processors. If you have any [file extension-named processors](custom-processors#file-extension-named-processor), you must update the name to a valid identifier (numbers and letters). File extension-named processors were automatically applied in the old configuration system but are not automatically applied when using flat config. Here is an example of a file extension-named processor:
+
+```js
+const plugin = {
+ configs: {},
+ rules: {},
+ processors: {
+
+ // no longer supported
+ ".md": {
+ preprocess() {},
+ postprocess() {}
+ }
+ }
+};
+
+// for ESM
+export default plugin;
+
+// OR for CommonJS
+module.exports = plugin;
+```
+
+The name `".md"` is no longer valid for a processor, so it must be replaced with a valid identifier such as `markdown`:
+
+```js
+const plugin = {
+ configs: {},
+ rules: {},
+ processors: {
+
+ // works in both old and new config systems
+ "markdown": {
+ preprocess() {},
+ postprocess() {}
+ }
+ }
+};
+
+// for ESM
+export default plugin;
+
+// OR for CommonJS
+module.exports = plugin;
+```
+
+In order to use this renamed processor, you'll also need to manually specify it inside of a config, such as:
+
+```js
+import example from "eslint-plugin-example";
+
+export default [
+ {
+ plugins: {
+ example
+ },
+ processor: "example/markdown"
+ }
+];
+```
+
+You should update your plugin's documentation to advise your users if you have renamed a file extension-named processor.
+
+## Migrating Configs for Flat Config
+
+If your plugin is exporting configs that refer back to your plugin, then you'll need to update your configs to flat config format. As part of the migration, you'll need to reference your plugin directly in the `plugins` key. For example, here is an exported config in the old configuration system format for a plugin named `eslint-plugin-example`:
+
+```js
+// plugin name: eslint-plugin-example
+module.exports = {
+ configs: {
+
+ // the config referenced by example/recommended
+ recommended: {
+ plugins: ["example"],
+ rules: {
+ "example/rule1": "error",
+ "example/rule2": "error"
+ }
+ }
+ },
+ rules: {
+ rule1: {},
+ rule2: {};
+ }
+};
+```
+
+To migrate to flat config format, you'll need to move the configs to after the definition of the `plugin` variable in the recommended plugin structure, like this:
+
+```js
+const plugin = {
+ configs: {},
+ rules: {},
+ processors: {}
+};
+
+// assign configs here so we can reference `plugin`
+Object.assign(plugin.configs, {
+ recommended: {
+ plugins: {
+ example: plugin
+ },
+ rules: {
+ "example/rule1": "error",
+ "example/rule2": "error"
+ }
+ }
+})
+
+// for ESM
+export default plugin;
+
+// OR for CommonJS
+module.exports = plugin;
+```
+
+Your users can then use this exported config like this:
+
+```js
+import example from "eslint-plugin-example";
+
+export default [
+
+ // use recommended config
+ example.configs.recommended,
+
+ // and provide your own overrides
+ {
+ rules: {
+ "example/rule1": "warn"
+ }
+ }
+];
+```
+
+You should update our documentation so your plugin users know how to reference the exported configs.
+
+## Migrating Environments for Flat Config
+
+Environments are no longer supported in flat config, and so we recommend transitioning your environments into exported configs. For example, suppose you export a `mocha` environment like this:
+
+```js
+// plugin name: eslint-plugin-example
+module.exports = {
+ environments: {
+ mocha: {
+ globals: {
+ it: true,
+ xit: true,
+ describe: true,
+ xdescribe: true
+ }
+ }
+ },
+ rules: {
+ rule1: {},
+ rule2: {};
+ }
+};
+```
+
+To migrate this environment into a config, you need to add a new key in the `plugin.configs` object that has a flat config object containing the same information, like this:
+
+```js
+const plugin = {
+ configs: {},
+ rules: {},
+ processors: {}
+};
+
+// assign configs here so we can reference `plugin`
+Object.assign(plugin.configs, {
+ mocha: {
+ languageOptions: {
+ globals: {
+ it: "writeable",
+ xit: "writeable",
+ describe: "writeable",
+ xdescribe: "writeable"
+ }
+ }
+ }
+})
+
+// for ESM
+export default plugin;
+
+// OR for CommonJS
+module.exports = plugin;
+```
+
+Your users can then use this exported config like this:
+
+```js
+import example from "eslint-plugin-example";
+
+export default [
+
+ // use the mocha globals
+ example.configs.mocha,
+
+ // and provide your own overrides
+ {
+ languageOptions: {
+ globals: {
+ it: "readonly"
+ }
+ }
+ }
+];
+```
+
+You should update your documentation so your plugin users know how to reference the exported configs.
+
+## Backwards Compatibility
+
+If your plugin needs to work with both the old and new configuration systems, then you'll need to:
+
+1. **Export a CommonJS entrypoint.** The old configuration system cannot load plugins that are published only in ESM format. If your source code is in ESM, then you'll need to use a bundler that can generate a CommonJS version and use the [`exports`](https://nodejs.org/api/packages.html#package-entry-points) key in your `package.json` file to ensure the CommonJS version can be found by Node.js.
+1. **Keep the `environments` key.** If your plugin exports custom environments, you should keep those as they are and also export the equivalent flat configs as described above. The `environments` key is ignored when ESLint is running in flat config mode.
+1. **Export both eslintrc and flat configs.** The `configs` key is only validated when a config is used, so you can provide both formats of configs in the `configs` key. We recommend that you append older format configs with `-legacy` to make it clear that these configs will not be supported in the future. For example, if your primary config is called `recommended` and is in flat config format, then you can also have a config named `recommended-legacy` that is the eslintrc config format.
+
+## Further Reading
+
+* [Overview of the flat config file format blog post](https://eslint.org/blog/2022/08/new-config-system-part-2/)
+* [API usage of new configuration system blog post](https://eslint.org/blog/2022/08/new-config-system-part-3/)
+* [Background to new configuration system blog post](https://eslint.org/blog/2022/08/new-config-system-part-1/)
diff --git a/docs/src/extend/plugins.md b/docs/src/extend/plugins.md
index 70bc3ee7c74..5374a0f5094 100644
--- a/docs/src/extend/plugins.md
+++ b/docs/src/extend/plugins.md
@@ -18,34 +18,6 @@ Each plugin is an npm module with a name in the format of `eslint-plugin- acc + result.errorCount + result.warningCount, 0);
+
+ if (problems > 0) {
+ console.log("Linting errors found!");
+ console.log(results);
+ } else {
+ console.log("No linting errors found.");
+ }
+ return results;
+}
+```
+
+## Step 5: Put It All Together
+
+Put the above functions together in a new function called `lintFiles`. This function will be the main entry point for your integration:
+
+```javascript
+// example-eslint-integration.js
+
+// Put previous functions all together
+async function lintFiles(filePaths) {
+
+ // The ESLint configuration. Alternatively, you could load the configuration
+ // from a .eslintrc file or just use the default config.
+ const overrideConfig = {
+ env: {
+ es6: true,
+ node: true,
+ },
+ parserOptions: {
+ ecmaVersion: 2018,
+ },
+ rules: {
+ "no-console": "error",
+ "no-unused-vars": "warn",
+ },
+ };
+
+ const eslint = createESLintInstance(overrideConfig);
+ const results = await lintAndFix(eslint, filePaths);
+ return outputLintingResults(results);
+}
+
+// Export integration
+module.exports = { lintFiles }
+```
+
+Here's the complete code example for `example-eslint-integration.js`:
+
+```javascript
+const { ESLint } = require("eslint");
+
+// Create an instance of ESLint with the configuration passed to the function
+function createESLintInstance(overrideConfig){
+ return new ESLint({ useEslintrc: false, overrideConfig: overrideConfig, fix: true });
+}
+
+// Lint the specified files and return the results
+async function lintAndFix(eslint, filePaths) {
+ const results = await eslint.lintFiles(filePaths);
+
+ // Apply automatic fixes and output fixed code
+ await ESLint.outputFixes(results);
+
+ return results;
+}
+
+// Log results to console if there are any problems
+function outputLintingResults(results) {
+ // Identify the number of problems found
+ const problems = results.reduce((acc, result) => acc + result.errorCount + result.warningCount, 0);
+
+ if (problems > 0) {
+ console.log("Linting errors found!");
+ console.log(results);
+ } else {
+ console.log("No linting errors found.");
+ }
+ return results;
+}
+
+// Put previous functions all together
+async function lintFiles(filePaths) {
+
+ // The ESLint configuration. Alternatively, you could load the configuration
+ // from a .eslintrc file or just use the default config.
+ const overrideConfig = {
+ env: {
+ es6: true,
+ node: true,
+ },
+ parserOptions: {
+ ecmaVersion: 2018,
+ },
+ rules: {
+ "no-console": "error",
+ "no-unused-vars": "warn",
+ },
+ };
+
+ const eslint = createESLintInstance(overrideConfig);
+ const results = await lintAndFix(eslint, filePaths);
+ return outputLintingResults(results);
+}
+
+// Export integration
+module.exports = { lintFiles }
+```
+
+## Conclusion
+
+In this tutorial, we have covered the essentials of using the `ESLint` class to lint files and retrieve results in your projects. This knowledge can be applied to create custom integrations, such as code editor plugins, to provide real-time feedback on code quality.
+
+## View the Tutorial Code
+
+You can view the annotated source code for the tutorial [here](https://github.com/eslint/eslint/tree/main/docs/_examples/integration-tutorial-code).
diff --git a/docs/src/integrate/nodejs-api.md b/docs/src/integrate/nodejs-api.md
index 853abdc57b8..744f98295f4 100644
--- a/docs/src/integrate/nodejs-api.md
+++ b/docs/src/integrate/nodejs-api.md
@@ -2,9 +2,9 @@
title: Node.js API Reference
eleventyNavigation:
key: node.js api
- parent: extend eslint
+ parent: integrate eslint
title: Node.js API Reference
- order: 6
+ order: 2
---
While ESLint is designed to be run on the command line, it's possible to use ESLint programmatically through the Node.js API. The purpose of the Node.js API is to allow plugin and tool authors to use the ESLint functionality directly, without going through the command line interface.
@@ -152,7 +152,7 @@ The `ESLint` constructor takes an `options` object. If you omit the `options` ob
* `options.plugins` (`Record | null`)
Default is `null`. The plugin implementations that ESLint uses for the `plugins` setting of your configuration. This is a map-like object. Those keys are plugin IDs and each value is implementation.
* `options.reportUnusedDisableDirectives` (`"error" | "warn" | "off" | null`)
- Default is `null`. The severity to report unused eslint-disable directives. If this option is a severity, it overrides the `reportUnusedDisableDirectives` setting in your configurations.
+ Default is `null`. The severity to report unused eslint-disable and eslint-enable directives. If this option is a severity, it overrides the `reportUnusedDisableDirectives` setting in your configurations.
* `options.resolvePluginsRelativeTo` (`string` | `null`)
Default is `null`. The path to a directory where plugins should be resolved from. If `null` is present, ESLint loads plugins from the location of the configuration file that contains the plugin setting. If a path is present, ESLint loads all plugins from there.
* `options.rulePaths` (`string[]`)
@@ -537,7 +537,7 @@ The most important method on `Linter` is `verify()`, which initiates linting of
* `filterCodeBlock` - (optional) A function that decides which code blocks the linter should adopt. The function receives two arguments. The first argument is the virtual filename of a code block. The second argument is the text of the code block. If the function returned `true` then the linter adopts the code block. If the function was omitted, the linter adopts only `*.js` code blocks. If you provided a `filterCodeBlock` function, it overrides this default behavior, so the linter doesn't adopt `*.js` code blocks automatically.
* `disableFixes` - (optional) when set to `true`, the linter doesn't make either the `fix` or `suggestions` property of the lint result.
* `allowInlineConfig` - (optional) set to `false` to disable inline comments from changing ESLint rules.
- * `reportUnusedDisableDirectives` - (optional) when set to `true`, adds reported errors for unused `eslint-disable` directives when no problems would be reported in the disabled area anyway.
+ * `reportUnusedDisableDirectives` - (optional) when set to `true`, adds reported errors for unused `eslint-disable` and `eslint-enable` directives when no problems would be reported in the disabled area anyway.
If the third argument is a string, it is interpreted as the `filename`.
diff --git a/docs/src/maintain/index.md b/docs/src/maintain/index.md
index 29c3ab89f30..1b083836f3f 100644
--- a/docs/src/maintain/index.md
+++ b/docs/src/maintain/index.md
@@ -3,7 +3,7 @@ title: Maintain ESLint
eleventyNavigation:
key: maintain eslint
title: Maintain ESLint
- order: 4
+ order: 5
---
diff --git a/docs/src/maintain/manage-issues.md b/docs/src/maintain/manage-issues.md
index 803451b4c63..9e243c12e72 100644
--- a/docs/src/maintain/manage-issues.md
+++ b/docs/src/maintain/manage-issues.md
@@ -1,14 +1,14 @@
---
-title: Manage Issues
+title: Manage Issues and Pull Requests
eleventyNavigation:
key: manage issues
parent: maintain eslint
- title: Manage Issues
+ title: Manage Issues and Pull Requests
order: 2
---
-New issues are filed frequently, and how we respond to those issues directly affects the success of the project. Being part of the project team means helping to triage and address issues as they come in so the project can continue to run smoothly.
+New issues and pull requests are filed frequently, and how we respond to those issues directly affects the success of the project. Being part of the project team means helping to triage and address issues as they come in so the project can continue to run smoothly.
## Things to Keep in Mind
@@ -17,44 +17,45 @@ New issues are filed frequently, and how we respond to those issues directly aff
1. **Not all requests are equal.** It's unlikely we'll be able to accommodate every request, so don't be afraid to say that something doesn't fit into the scope of the project or isn't practical. It's better to give such feedback if that's the case.
1. **Close when appropriate.** Don't be afraid to close issues that you don't think will be done, or when it's become clear from the conversation that there's no further work to do. Issues can always be reopened if they are closed incorrectly, so feel free to close issues when appropriate. Just be sure to leave a comment explaining why the issue is being closed (if not closed by a commit).
-## Types of Issues
+## Types of Issues and Pull Requests
-There are four primary issue categories:
+There are five primary categories:
1. **Bug**: Something isn't working the way it's expected to work.
1. **Enhancement**: A change to something that already exists. For instance, adding a new option to an existing rule or fixing a bug in a rule where fixing it will result in the rule reporting more problems (in this case, use both "Bug" and "Enhancement").
1. **Feature**: Adding something that doesn't already exist. For example, adding a new rule, new formatter, or new command line flag.
+1. **Documentation**: Adding, updating, or removing project documentation.
1. **Question**: An inquiry about how something works that won't result in a code change. We prefer if people use GitHub Discussions or Discord for questions, but sometimes they'll open an issue.
-The first goal when evaluating an issue is to determine which category the issue falls into.
+The first goal when evaluating an issue or pull request is to determine which category the issue falls into.
## Triaging Process
-All of ESLint's issues, across all GitHub repositories, are managed on our [Triage Project](https://github.com/orgs/eslint/projects/3). Please use the Triage project instead of the issues list when reviewing issues to determine what to work on. The Triage project has several columns:
+All of ESLint's issues and pull requests, across all GitHub repositories, are managed on our [Triage Project](https://github.com/orgs/eslint/projects/3). Please use the Triage project instead of the issues list when reviewing issues to determine what to work on. The Triage project has several columns:
-* **Needs Triage**: Issues that have not yet been reviewed by anyone
-* **Triaging**: Issues that someone has reviewed but has not been able to fully triage yet
-* **Ready for Dev Team**: Issues that have been triaged and have all the information necessary for the dev team to take a look
-* **Evaluating**: The dev team is evaluating these issues to determine whether to move forward or not
+* **Needs Triage**: Issues and pull requests that have not yet been reviewed by anyone
+* **Triaging**: Issues and pull requests that someone has reviewed but has not been able to fully triage yet
+* **Ready for Dev Team**: Issues and pull requests that have been triaged and have all the information necessary for the dev team to take a look
+* **Evaluating**: The dev team is evaluating these issues and pull requests to determine whether to move forward or not
* **Feedback Needed**: A team member is requesting more input from the rest of the team before proceeding
* **Waiting for RFC**: The next step in the process is for an RFC to be written
* **RFC Opened**: An RFC is opened to address these issues
* **Blocked**: The issue can't move forward due to some dependency
* **Ready to Implement**: These issues have all the details necessary to start implementation
-* **Implementing**: There is an open pull request for each of these issues
+* **Implementing**: There is an open pull request for each of these issues or this is a pull request that has been approved
* **Completed**: The issue has been closed (either via pull request merge or by the team manually closing the issue)
We make every attempt to automate movement between as many columns as we can, but sometimes moving issues needs to be done manually.
-### When an Issue is Opened
+### When an Issue or Pull Request is Opened
-When an issue is opened, it is automatically added to the "Needs Triage" column in the Triage project. These issues need to be evaluated to determine next steps. Anyone on the support team or dev team can follow these steps to properly triage issues.
+When an issue or pull request is opened, it is automatically added to the "Needs Triage" column in the Triage project. These issues and pull requests need to be evaluated to determine the next steps. Anyone on the support team or dev team can follow these steps to properly triage issues.
-**Note:** If an issue is in the "Triaging" column, that means someone is already triaging it, and you should let them finish. There's no need to comment on issues in the "Triaging" column unless someone asks for help.
+**Note:** If an issue or pull request is in the "Triaging" column, that means someone is already triaging it, and you should let them finish. There's no need to comment on issues or pull requests in the "Triaging" column unless someone asks for help.
-The steps for triaging an issue are:
+The steps for triaging an issue or pull request are:
-1. Move the issue from "Needs Triage" to "Triaging" in the Triage project.
+1. Move the issue or pull request from "Needs Triage" to "Triaging" in the Triage project.
1. Check: Has all the information in the issue template been provided?
* **No:** If information is missing from the issue template, or you can't tell what is being requested, please ask the author to provide the missing information:
* Add the "needs info" label to the issue so we know that this issue is stalled due to lack of information.
@@ -62,8 +63,8 @@ The steps for triaging an issue are:
* If the issue author hasn't provided the necessary information after 7 days, please close the issue. The bot will add a comment stating that the issue was closed because there was information missing.
* **Yes:**
* If the issue is actually a question (rather than something the dev team needs to change), please [convert it to a discussion](https://docs.github.com/en/free-pro-team@latest/discussions/managing-discussions-for-your-community/moderating-discussions#converting-an-issue-to-a-discussion). You can continue the conversation as a discussion.
- * If the issue is reporting a bug, try to reproduce the issue following the instructions in the issue. If you can reproduce the bug, please add the "repro:yes" label. (The bot will automatically remove the "repro:needed" label.) If you can't reproduce the bug, ask the author for more information about their environment or to clarify reproduction steps.
- * If the issue is reporting something that works as intended, please add the "works as intended" label and close the issue.
+ * If the issue is reporting a bug, or if a pull request is fixing a bug, try to reproduce the issue following the instructions in the issue. If you can reproduce the bug, please add the "repro:yes" label. (The bot will automatically remove the "repro:needed" label.) If you can't reproduce the bug, ask the author for more information about their environment or to clarify reproduction steps.
+ * If the issue or pull request is reporting something that works as intended, please add the "works as intended" label and close the issue.
* Please add labels describing the part of ESLint affected:
* **3rd party plugin**: Related to third-party functionality (plugins, parsers, rules, etc.)
* **build**: Related to commands run during a build (testing, linting, release scripts, etc.)
@@ -72,17 +73,18 @@ The steps for triaging an issue are:
* **documentation**: Related to content on eslint.org
* **infrastructure**: Related to resources needed for builds or deployment (VMs, CI tools, bots, etc.)
* **rule**: Related to core rules
- * Please assign an initial priority based on the importance of the issue. If you're not sure, use your best judgment. We can always change the priority later.
+ * Please assign an initial priority based on the importance of the issue or pull request. If you're not sure, use your best judgment. We can always change the priority later.
* **P1**: Urgent and important, we need to address this immediately.
* **P2**: Important but not urgent. Should be handled by a TSC member or reviewer.
* **P3**: Nice to have but not important. Can be handled by any team member.
* **P4**: A good idea that we'd like to have but may take a while for the team to get to it.
* **P5**: A good idea that the core team can't commit to. Will likely need to be done by an outside contributor.
- * Please assign an initial impact assessement (make your best guess):
+ * Please assign an initial impact assessment (make your best guess):
* **Low**: Doesn't affect many users.
* **Medium**: Affects most users or has a noticeable effect on user experience.
* **High**: Affects a lot of users, is a breaking change, or otherwise will be very noticeable to users.
- * If you can't properly triage the issue, move the issue back to the "Needs Triage" column in the Triage project so someone else can triage it.
+ * If you can't properly triage the issue or pull request, move the issue back to the "Needs Triage" column in the Triage project so someone else can triage it.
+ * If a pull request references an already accepted issue, move it to the "Implementing" column in the Triage project.
* If you have triaged the issue, move the issue to the "Ready for Dev Team" column in the Triage project.
## Evaluation Process
@@ -102,7 +104,7 @@ When an issue has been moved to the "Ready for Dev Team" column, any dev team me
**Note:** "Good first issue" issues are intended to help new contributors feel welcome and empowered to make a contribution to ESLint. To ensure that new contributors are given a chance to work on these issues, issues labeled "good first issue" must be open for 30 days *from the day the issue was labeled* before a team member is permitted to work on them.
-## Accepting Issues
+## Accepting Issues and Pull Requests
Issues may be labeled as "accepted" when the issue is:
diff --git a/docs/src/pages/index.md b/docs/src/pages/index.md
index c8f5f62dcaa..f1a112a0010 100644
--- a/docs/src/pages/index.md
+++ b/docs/src/pages/index.md
@@ -12,7 +12,11 @@ as well as guides for migrating from earlier versions of ESLint.
## [Extend ESLint](extend/)
-Intended for people who wish to extend ESLint. Contains information about creating custom rules, configurations, plugins, and formatters; and information about our Node.js API.
+Intended for people who wish to extend ESLint. Contains information about creating custom rules, configurations, plugins, and formatters.
+
+## [Integrate ESLint](integrate/)
+
+Intended for people who wish to create integrations with ESLint. Contains information about creating integrations and using the Node.js API.
## [Contribute to ESLint](contribute/)
diff --git a/docs/src/pages/rules.md b/docs/src/pages/rules.md
index 996860e2c86..9d801cf5126 100644
--- a/docs/src/pages/rules.md
+++ b/docs/src/pages/rules.md
@@ -20,14 +20,17 @@ Rules in ESLint are grouped by type to help you understand their purpose. Each r
hasSuggestions: true
}) }}
-{%- for type in rules.types -%}
+{%- for type, content in rules.types -%}
-
{{ type.displayName }}
+
{{ rules_categories[type].displayName }}
-{{ type.description | safe }}
+{{ rules_categories[type].description | safe }}
- {%- for the_rule in type.rules -%}
- {%- if type.displayName == 'deprecated' -%}{%- set deprecated_value = true -%}{%- endif -%}
+ {%- for the_rule in content -%}
+
+ {%- if rules_categories[type].displayName == 'deprecated' -%}
+ {%- set deprecated_value = true -%}
+ {%- endif -%}
{%- set name_value = the_rule.name -%}
{%- set description_value = the_rule.description -%}
@@ -50,11 +53,11 @@ Rules in ESLint are grouped by type to help you understand their purpose. Each r
{%- if rules.deprecated -%}
-
{{ rules.deprecated.name }}
+
{{ rules_categories.deprecated.displayName }}
-{{ rules.deprecated.description | safe }}
+{{ rules_categories.deprecated.description | safe }}
-{%- for the_rule in rules.deprecated.rules -%}
+{%- for the_rule in rules.deprecated -%}
{%- set name_value = the_rule.name -%}
{%- set isReplacedBy = the_rule.replacedBy -%}
@@ -68,11 +71,11 @@ Rules in ESLint are grouped by type to help you understand their purpose. Each r
{%- if rules.removed -%}
-
{{ rules.removed.name }}
+
{{ rules_categories.removed.displayName }}
-{{ rules.removed.description | safe }}
+{{ rules_categories.removed.description | safe }}
-{%- for the_rule in rules.removed.rules -%}
+{%- for the_rule in rules.removed -%}
{%- set name_value = the_rule.removed -%}
{%- set isReplacedBy = the_rule.replacedBy -%}
diff --git a/docs/src/rules/array-bracket-newline.md b/docs/src/rules/array-bracket-newline.md
index 4ad7e27b560..2ccb13dddf9 100644
--- a/docs/src/rules/array-bracket-newline.md
+++ b/docs/src/rules/array-bracket-newline.md
@@ -5,7 +5,7 @@ related_rules:
- array-bracket-spacing
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
A number of style guides require or disallow line breaks inside of array brackets.
diff --git a/docs/src/rules/array-bracket-spacing.md b/docs/src/rules/array-bracket-spacing.md
index 5b6dd8a7cac..0fdc8310747 100644
--- a/docs/src/rules/array-bracket-spacing.md
+++ b/docs/src/rules/array-bracket-spacing.md
@@ -7,7 +7,7 @@ related_rules:
- computed-property-spacing
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
A number of style guides require or disallow spaces between array brackets and other tokens. This rule
applies to both array literals and destructuring assignments (ECMAScript 6).
diff --git a/docs/src/rules/array-callback-return.md b/docs/src/rules/array-callback-return.md
index 965d18537af..625cb14ced3 100644
--- a/docs/src/rules/array-callback-return.md
+++ b/docs/src/rules/array-callback-return.md
@@ -92,10 +92,13 @@ var bar = foo.map(node => node.getAttribute("id"));
## Options
-This rule accepts a configuration object with two options:
+This rule accepts a configuration object with three options:
* `"allowImplicit": false` (default) When set to `true`, allows callbacks of methods that require a return value to implicitly return `undefined` with a `return` statement containing no expression.
* `"checkForEach": false` (default) When set to `true`, rule will also report `forEach` callbacks that return a value.
+* `"allowVoid": false` (default) When set to `true`, allows `void` in `forEach` callbacks, so rule will not report the return value with a `void` operator.
+
+**Note:** `{ "allowVoid": true }` works only if `checkForEach` option is set to `true`.
### allowImplicit
@@ -122,7 +125,7 @@ Examples of **incorrect** code for the `{ "checkForEach": true }` option:
/*eslint array-callback-return: ["error", { checkForEach: true }]*/
myArray.forEach(function(item) {
- return handleItem(item)
+ return handleItem(item);
});
myArray.forEach(function(item) {
@@ -132,11 +135,24 @@ myArray.forEach(function(item) {
handleItem(item);
});
+myArray.forEach(function(item) {
+ if (item < 0) {
+ return void x;
+ }
+ handleItem(item);
+});
+
myArray.forEach(item => handleItem(item));
+myArray.forEach(item => void handleItem(item));
+
myArray.forEach(item => {
return handleItem(item);
});
+
+myArray.forEach(item => {
+ return void handleItem(item);
+});
```
:::
@@ -171,6 +187,31 @@ myArray.forEach(item => {
:::
+### allowVoid
+
+Examples of **correct** code for the `{ "allowVoid": true }` option:
+
+:::correct
+
+```js
+/*eslint array-callback-return: ["error", { checkForEach: true, allowVoid: true }]*/
+
+myArray.forEach(item => void handleItem(item));
+
+myArray.forEach(item => {
+ return void handleItem(item);
+});
+
+myArray.forEach(item => {
+ if (item < 0) {
+ return void x;
+ }
+ handleItem(item);
+});
+```
+
+:::
+
## Known Limitations
This rule checks callback functions of methods with the given names, *even if* the object which has the method is *not* an array.
diff --git a/docs/src/rules/array-element-newline.md b/docs/src/rules/array-element-newline.md
index 2b34bd53014..0a8bc39b9bc 100644
--- a/docs/src/rules/array-element-newline.md
+++ b/docs/src/rules/array-element-newline.md
@@ -12,7 +12,7 @@ related_rules:
- brace-style
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
A number of style guides require or disallow line breaks between array elements.
diff --git a/docs/src/rules/arrow-body-style.md b/docs/src/rules/arrow-body-style.md
index 1975af13eed..1dc6fcc36e8 100644
--- a/docs/src/rules/arrow-body-style.md
+++ b/docs/src/rules/arrow-body-style.md
@@ -32,6 +32,7 @@ Examples of **incorrect** code for this rule with the `"always"` option:
```js
/*eslint arrow-body-style: ["error", "always"]*/
/*eslint-env es6*/
+
let foo = () => 0;
```
@@ -42,10 +43,13 @@ Examples of **correct** code for this rule with the `"always"` option:
:::correct
```js
+/*eslint arrow-body-style: ["error", "always"]*/
+/*eslint-env es6*/
+
let foo = () => {
return 0;
};
-let foo = (retv, name) => {
+let bar = (retv, name) => {
retv[name] = true;
return retv;
};
@@ -66,7 +70,7 @@ Examples of **incorrect** code for this rule with the default `"as-needed"` opti
let foo = () => {
return 0;
};
-let foo = () => {
+let bar = () => {
return {
bar: {
foo: 1,
@@ -86,24 +90,24 @@ Examples of **correct** code for this rule with the default `"as-needed"` option
/*eslint arrow-body-style: ["error", "as-needed"]*/
/*eslint-env es6*/
-let foo = () => 0;
-let foo = (retv, name) => {
+let foo1 = () => 0;
+let foo2 = (retv, name) => {
retv[name] = true;
return retv;
};
-let foo = () => ({
+let foo3 = () => ({
bar: {
foo: 1,
bar: 2,
}
});
-let foo = () => { bar(); };
-let foo = () => {};
-let foo = () => { /* do nothing */ };
-let foo = () => {
+let foo4 = () => { bar(); };
+let foo5 = () => {};
+let foo6 = () => { /* do nothing */ };
+let foo7 = () => {
// do nothing.
};
-let foo = () => ({ bar: 0 });
+let foo8 = () => ({ bar: 0 });
```
:::
@@ -120,7 +124,7 @@ Examples of **incorrect** code for this rule with the `{ "requireReturnForObject
/*eslint arrow-body-style: ["error", "as-needed", { "requireReturnForObjectLiteral": true }]*/
/*eslint-env es6*/
let foo = () => ({});
-let foo = () => ({ bar: 0 });
+let bar = () => ({ bar: 0 });
```
:::
@@ -134,7 +138,7 @@ Examples of **correct** code for this rule with the `{ "requireReturnForObjectLi
/*eslint-env es6*/
let foo = () => {};
-let foo = () => { return { bar: 0 }; };
+let bar = () => { return { bar: 0 }; };
```
:::
@@ -152,7 +156,7 @@ Examples of **incorrect** code for this rule with the `"never"` option:
let foo = () => {
return 0;
};
-let foo = (retv, name) => {
+let bar = (retv, name) => {
retv[name] = true;
return retv;
};
@@ -169,7 +173,7 @@ Examples of **correct** code for this rule with the `"never"` option:
/*eslint-env es6*/
let foo = () => 0;
-let foo = () => ({ foo: 0 });
+let bar = () => ({ foo: 0 });
```
:::
diff --git a/docs/src/rules/arrow-parens.md b/docs/src/rules/arrow-parens.md
index 8b956c35664..4bfa819f87b 100644
--- a/docs/src/rules/arrow-parens.md
+++ b/docs/src/rules/arrow-parens.md
@@ -5,7 +5,7 @@ further_reading:
- https://github.com/airbnb/javascript#arrows--one-arg-parens
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Arrow functions can omit parentheses when they have exactly one parameter. In all other cases the parameter(s) must
be wrapped in parentheses. This rule enforces the consistent use of parentheses in arrow functions.
diff --git a/docs/src/rules/arrow-spacing.md b/docs/src/rules/arrow-spacing.md
index fa5a42410a4..3975e9f82cd 100644
--- a/docs/src/rules/arrow-spacing.md
+++ b/docs/src/rules/arrow-spacing.md
@@ -3,7 +3,7 @@ title: arrow-spacing
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
This rule normalize style of spacing before/after an arrow function's arrow(`=>`).
diff --git a/docs/src/rules/block-spacing.md b/docs/src/rules/block-spacing.md
index 415fc7db0bd..831916b55e7 100644
--- a/docs/src/rules/block-spacing.md
+++ b/docs/src/rules/block-spacing.md
@@ -6,7 +6,7 @@ related_rules:
- brace-style
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
## Rule Details
diff --git a/docs/src/rules/brace-style.md b/docs/src/rules/brace-style.md
index 456ccf2c2e1..8fb982328a6 100644
--- a/docs/src/rules/brace-style.md
+++ b/docs/src/rules/brace-style.md
@@ -8,7 +8,7 @@ further_reading:
- https://en.wikipedia.org/wiki/Indent_style
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Brace style is closely related to [indent style](https://en.wikipedia.org/wiki/Indent_style) in programming and describes the placement of braces relative to their control statement and body. There are probably a dozen, if not more, brace styles in the world.
diff --git a/docs/src/rules/camelcase.md b/docs/src/rules/camelcase.md
index 46757fd53b0..536fba9439b 100644
--- a/docs/src/rules/camelcase.md
+++ b/docs/src/rules/camelcase.md
@@ -49,11 +49,11 @@ function foo({ no_camelcased }) {
// ...
};
-function foo({ isCamelcased: no_camelcased }) {
+function bar({ isCamelcased: no_camelcased }) {
// ...
}
-function foo({ no_camelcased = 'default value' }) {
+function baz({ no_camelcased = 'default value' }) {
// ...
};
@@ -63,7 +63,7 @@ var obj = {
var { category_id = 1 } = query;
-var { foo: no_camelcased } = bar;
+var { foo: snake_cased } = bar;
var { foo: bar_baz = 1 } = quz;
```
@@ -83,8 +83,8 @@ var myFavoriteColor = "#112C85";
var _myFavoriteColor = "#112C85";
var myFavoriteColor_ = "#112C85";
var MY_FAVORITE_COLOR = "#112C85";
-var foo = bar.baz_boom;
-var foo = { qux: bar.baz_boom };
+var foo1 = bar.baz_boom;
+var foo2 = { qux: bar.baz_boom };
obj.do_something();
do_something();
@@ -96,11 +96,11 @@ function foo({ isCamelCased }) {
// ...
};
-function foo({ isCamelCased: isAlsoCamelCased }) {
+function bar({ isCamelCased: isAlsoCamelCased }) {
// ...
}
-function foo({ isCamelCased = 'default value' }) {
+function baz({ isCamelCased = 'default value' }) {
// ...
};
@@ -143,9 +143,9 @@ Examples of **incorrect** code for this rule with the default `{ "ignoreDestruct
var { category_id } = query;
-var { category_id = 1 } = query;
+var { category_name = 1 } = query;
-var { category_id: category_id } = query;
+var { category_id: category_title } = query;
var { category_id: category_alias } = query;
@@ -328,7 +328,7 @@ function UNSAFE_componentWillMount() {
// ...
}
-function UNSAFE_componentWillMount() {
+function UNSAFE_componentWillReceiveProps() {
// ...
}
```
diff --git a/docs/src/rules/class-methods-use-this.md b/docs/src/rules/class-methods-use-this.md
index 2269fac8eff..5a12f9af3c9 100644
--- a/docs/src/rules/class-methods-use-this.md
+++ b/docs/src/rules/class-methods-use-this.md
@@ -86,13 +86,13 @@ class A {
}
}
-class A {
+class B {
constructor() {
// OK. constructor is exempt
}
}
-class A {
+class C {
static foo() {
// OK. static methods aren't expected to use this.
}
diff --git a/docs/src/rules/comma-dangle.md b/docs/src/rules/comma-dangle.md
index 1370874ed50..17685a08f3d 100644
--- a/docs/src/rules/comma-dangle.md
+++ b/docs/src/rules/comma-dangle.md
@@ -3,7 +3,7 @@ title: comma-dangle
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Trailing commas in object literals are valid according to the ECMAScript 5 (and ECMAScript 3!) spec. However, IE8 (when not in IE8 document mode) and below will throw an error when it encounters trailing commas in JavaScript.
diff --git a/docs/src/rules/comma-spacing.md b/docs/src/rules/comma-spacing.md
index b6937856321..e4d430997be 100644
--- a/docs/src/rules/comma-spacing.md
+++ b/docs/src/rules/comma-spacing.md
@@ -16,7 +16,7 @@ further_reading:
- https://dojotoolkit.org/reference-guide/1.9/developer/styleguide.html
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Spacing around commas improves readability of a list of items. Although most of the style guidelines for languages prescribe adding a space after a comma and not before it, it is subjective to the preferences of a project.
@@ -60,7 +60,7 @@ var arr = [1 , 2];
var obj = {"foo": "bar" ,"baz": "qur"};
foo(a ,b);
new Foo(a ,b);
-function foo(a ,b){}
+function baz(a ,b){}
a ,b
```
@@ -80,7 +80,7 @@ var arr = [1,, 3]
var obj = {"foo": "bar", "baz": "qur"};
foo(a, b);
new Foo(a, b);
-function foo(a, b){}
+function qur(a, b){}
a, b
```
@@ -131,7 +131,7 @@ var foo = 1, bar = 2;
var arr = [1 , 2];
var obj = {"foo": "bar", "baz": "qur"};
new Foo(a,b);
-function foo(a,b){}
+function baz(a,b){}
a, b
```
@@ -151,7 +151,7 @@ var arr = [1 ,,3]
var obj = {"foo": "bar" ,"baz": "qur"};
foo(a ,b);
new Foo(a ,b);
-function foo(a ,b){}
+function qur(a ,b){}
a ,b
```
diff --git a/docs/src/rules/comma-style.md b/docs/src/rules/comma-style.md
index 99d14b27f4f..5859710ee89 100644
--- a/docs/src/rules/comma-style.md
+++ b/docs/src/rules/comma-style.md
@@ -7,7 +7,7 @@ further_reading:
- https://gist.github.com/isaacs/357981
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
The Comma Style rule enforces styles for comma-separated lists. There are two comma styles primarily used in JavaScript:
@@ -69,7 +69,7 @@ var foo = 1
var foo = ["apples"
, "oranges"];
-function bar() {
+function baz() {
return {
"a": 1
,"b:": 2
@@ -94,7 +94,7 @@ var foo = 1,
var foo = ["apples",
"oranges"];
-function bar() {
+function baz() {
return {
"a": 1,
"b:": 2
@@ -119,7 +119,7 @@ var foo = 1,
var foo = ["apples",
"oranges"];
-function bar() {
+function baz() {
return {
"a": 1,
"b:": 2
@@ -144,7 +144,7 @@ var foo = 1
var foo = ["apples"
,"oranges"];
-function bar() {
+function baz() {
return {
"a": 1
,"b:": 2
diff --git a/docs/src/rules/computed-property-spacing.md b/docs/src/rules/computed-property-spacing.md
index 065270d6caf..424e97cce63 100644
--- a/docs/src/rules/computed-property-spacing.md
+++ b/docs/src/rules/computed-property-spacing.md
@@ -7,7 +7,7 @@ related_rules:
- space-in-parens
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
While formatting preferences are very personal, a number of style guides require
or disallow spaces between computed properties in the following situations:
diff --git a/docs/src/rules/consistent-return.md b/docs/src/rules/consistent-return.md
index 2d5f4e3f2ca..797c9b08ca0 100644
--- a/docs/src/rules/consistent-return.md
+++ b/docs/src/rules/consistent-return.md
@@ -48,7 +48,7 @@ function doSomething(condition) {
}
}
-function doSomething(condition) {
+function doSomethingElse(condition) {
if (condition) {
return true;
}
diff --git a/docs/src/rules/constructor-super.md b/docs/src/rules/constructor-super.md
index 93019eb2a79..c6f008f13d6 100644
--- a/docs/src/rules/constructor-super.md
+++ b/docs/src/rules/constructor-super.md
@@ -1,6 +1,7 @@
---
title: constructor-super
rule_type: problem
+handled_by_typescript: true
---
Constructors of derived classes must call `super()`.
@@ -13,6 +14,16 @@ This rule checks whether or not there is a valid `super()` call.
This rule is aimed to flag invalid/missing `super()` calls.
+This is a syntax error because there is no `extends` clause in the class:
+
+```js
+class A {
+ constructor() {
+ super();
+ }
+}
+```
+
Examples of **incorrect** code for this rule:
:::incorrect
@@ -21,24 +32,18 @@ Examples of **incorrect** code for this rule:
/*eslint constructor-super: "error"*/
/*eslint-env es6*/
-class A {
- constructor() {
- super(); // This is a SyntaxError.
- }
-}
-
class A extends B {
constructor() { } // Would throw a ReferenceError.
}
// Classes which inherits from a non constructor are always problems.
-class A extends null {
+class C extends null {
constructor() {
super(); // Would throw a TypeError.
}
}
-class A extends null {
+class D extends null {
constructor() { } // Would throw a ReferenceError.
}
```
@@ -57,7 +62,7 @@ class A {
constructor() { }
}
-class A extends B {
+class B extends C {
constructor() {
super();
}
diff --git a/docs/src/rules/default-param-last.md b/docs/src/rules/default-param-last.md
index 7f64f7c4464..63672a9103e 100644
--- a/docs/src/rules/default-param-last.md
+++ b/docs/src/rules/default-param-last.md
@@ -28,7 +28,7 @@ Examples of **incorrect** code for this rule:
function f(a = 0, b) {}
-function f(a, b = 0, c) {}
+function g(a, b = 0, c) {}
```
:::
diff --git a/docs/src/rules/dot-location.md b/docs/src/rules/dot-location.md
index 47ee575be97..8abb47708f4 100644
--- a/docs/src/rules/dot-location.md
+++ b/docs/src/rules/dot-location.md
@@ -6,7 +6,7 @@ related_rules:
- dot-notation
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
JavaScript allows you to place newlines before or after a dot in a member expression.
diff --git a/docs/src/rules/eol-last.md b/docs/src/rules/eol-last.md
index 4c9d837c5c3..c4c0dc991f0 100644
--- a/docs/src/rules/eol-last.md
+++ b/docs/src/rules/eol-last.md
@@ -3,7 +3,7 @@ title: eol-last
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Trailing newlines in non-empty files are a common UNIX idiom. Benefits of
trailing newlines include the ability to concatenate or append to files as well
@@ -42,7 +42,8 @@ Examples of **correct** code for this rule:
function doSomething() {
var foo = 2;
-}\n
+}
+
```
:::
diff --git a/docs/src/rules/for-direction.md b/docs/src/rules/for-direction.md
index 832c87cde3d..45759eb48f9 100644
--- a/docs/src/rules/for-direction.md
+++ b/docs/src/rules/for-direction.md
@@ -3,11 +3,11 @@ title: for-direction
rule_type: problem
---
-
+A `for` loop with a stop condition that can never be reached, such as one with a counter that moves in the wrong direction, will run infinitely. While there are occasions when an infinite loop is intended, the convention is to construct such loops as `while` loops. More typically, an infinite `for` loop is a bug.
## Rule Details
-A `for` loop with a stop condition that can never be reached, such as one with a counter that moves in the wrong direction, will run infinitely. While there are occasions when an infinite loop is intended, the convention is to construct such loops as `while` loops. More typically, an infinite for loop is a bug.
+This rule forbids `for` loops where the counter variable changes in such a way that the stop condition will never be met. For example, if the counter variable is increasing (i.e. `i++`) and the stop condition tests that the counter is greater than zero (`i >= 0`) then the loop will never exit.
Examples of **incorrect** code for this rule:
@@ -23,6 +23,10 @@ for (var i = 10; i >= 0; i++) {
for (var i = 0; i > 10; i++) {
}
+
+const n = -2;
+for (let i = 0; i < 10; i += n) {
+}
```
:::
@@ -35,6 +39,12 @@ Examples of **correct** code for this rule:
/*eslint for-direction: "error"*/
for (var i = 0; i < 10; i++) {
}
+
+for (let i = 10; i >= 0; i += this.step) { // direction unknown
+}
+
+for (let i = MIN; i <= MAX; i -= 0) { // not increasing or decreasing
+}
```
:::
diff --git a/docs/src/rules/func-call-spacing.md b/docs/src/rules/func-call-spacing.md
index 082c86d8140..2821630fdbf 100644
--- a/docs/src/rules/func-call-spacing.md
+++ b/docs/src/rules/func-call-spacing.md
@@ -5,7 +5,7 @@ related_rules:
- no-spaced-func
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
When calling a function, developers may insert optional whitespace between the function's name and the parentheses that invoke it. The following pairs of function calls are equivalent:
diff --git a/docs/src/rules/function-call-argument-newline.md b/docs/src/rules/function-call-argument-newline.md
index be44b2ed7e9..73adf30c59f 100644
--- a/docs/src/rules/function-call-argument-newline.md
+++ b/docs/src/rules/function-call-argument-newline.md
@@ -8,7 +8,7 @@ related_rules:
- array-element-newline
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
A number of style guides require or disallow line breaks between arguments of a function call.
diff --git a/docs/src/rules/function-paren-newline.md b/docs/src/rules/function-paren-newline.md
index 4ba5a922c8f..116737118e9 100644
--- a/docs/src/rules/function-paren-newline.md
+++ b/docs/src/rules/function-paren-newline.md
@@ -3,7 +3,7 @@ title: function-paren-newline
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Many style guides require or disallow newlines inside of function parentheses.
@@ -49,9 +49,9 @@ Examples of **incorrect** code for this rule with the `"always"` option:
function foo(bar, baz) {}
-var foo = function(bar, baz) {};
+var qux = function(bar, baz) {};
-var foo = (bar, baz) => {};
+var qux = (bar, baz) => {};
foo(bar, baz);
```
@@ -70,11 +70,11 @@ function foo(
baz
) {}
-var foo = function(
+var qux = function(
bar, baz
) {};
-var foo = (
+var qux = (
bar,
baz
) => {};
@@ -99,11 +99,11 @@ function foo(
baz
) {}
-var foo = function(
+var qux = function(
bar, baz
) {};
-var foo = (
+var qux = (
bar,
baz
) => {};
@@ -125,12 +125,12 @@ Examples of **correct** code for this rule with the `"never"` option:
function foo(bar, baz) {}
-function foo(bar,
+function qux(bar,
baz) {}
-var foo = function(bar, baz) {};
+var foobar = function(bar, baz) {};
-var foo = (bar, baz) => {};
+var foobar = (bar, baz) => {};
foo(bar, baz);
@@ -151,11 +151,11 @@ function foo(bar,
baz
) {}
-var foo = function(
+var qux = function(
bar, baz
) {};
-var foo = (
+var qux = (
bar,
baz) => {};
@@ -180,12 +180,12 @@ Examples of **correct** code for this rule with the default `"multiline"` option
function foo(bar, baz) {}
-var foo = function(
+var foobar = function(
bar,
baz
) {};
-var foo = (bar, baz) => {};
+var foobar = (bar, baz) => {};
foo(bar, baz, qux);
@@ -213,11 +213,11 @@ function foo(bar,
baz
) {}
-var foo = function(bar,
+var qux = function(bar,
baz
) {};
-var foo = (
+var qux = (
bar,
baz) => {};
@@ -243,9 +243,9 @@ Examples of **correct** code for this rule with the `"consistent"` option:
function foo(bar,
baz) {}
-var foo = function(bar, baz) {};
+var qux = function(bar, baz) {};
-var foo = (
+var qux = (
bar,
baz
) => {};
@@ -274,11 +274,11 @@ function foo(bar,
baz
) {}
-var foo = function(bar,
+var foobar = function(bar,
baz
) {};
-var foo = (
+var foobar = (
bar,
baz) => {};
@@ -306,9 +306,9 @@ function foo(
baz
) {}
-var foo = function(bar, baz) {};
+var qux = function(bar, baz) {};
-var foo = (
+var qux = (
bar
) => {};
@@ -333,17 +333,21 @@ function foo(
baz
) {}
-function foo(bar, baz, qux) {}
+function foobar(bar, baz, qux) {}
-var foo = function(
+var barbaz = function(
bar, baz
) {};
-var foo = (bar,
- baz) => {};
+var barbaz = (
+ bar,
+ baz
+) => {};
-foo(bar,
- baz);
+foo(
+ bar,
+ baz
+);
```
:::
@@ -357,13 +361,13 @@ Examples of **correct** code for this rule with the `{ "minItems": 3 }` option:
function foo(bar, baz) {}
-var foo = function(
+var foobar = function(
bar,
baz,
qux
) {};
-var foo = (
+var foobar = (
bar, baz, qux
) => {};
diff --git a/docs/src/rules/generator-star-spacing.md b/docs/src/rules/generator-star-spacing.md
index 3ef58d86da6..a8b3c6c9b17 100644
--- a/docs/src/rules/generator-star-spacing.md
+++ b/docs/src/rules/generator-star-spacing.md
@@ -5,7 +5,7 @@ further_reading:
- https://leanpub.com/understandinges6/read/#leanpub-auto-generators
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Generators are a new type of function in ECMAScript 6 that can return multiple values over time.
These special functions are indicated by placing an `*` after the `function` keyword.
diff --git a/docs/src/rules/getter-return.md b/docs/src/rules/getter-return.md
index 0c8937d14cc..9d316303d02 100644
--- a/docs/src/rules/getter-return.md
+++ b/docs/src/rules/getter-return.md
@@ -1,6 +1,7 @@
---
title: getter-return
rule_type: problem
+handled_by_typescript: true
further_reading:
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get
- https://leanpub.com/understandinges6/read/#leanpub-auto-accessor-properties
diff --git a/docs/src/rules/global-require.md b/docs/src/rules/global-require.md
index e960468b380..8460acb89ad 100644
--- a/docs/src/rules/global-require.md
+++ b/docs/src/rules/global-require.md
@@ -32,7 +32,7 @@ This rule requires all calls to `require()` to be at the top level of the module
Examples of **incorrect** code for this rule:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint global-require: "error"*/
@@ -76,7 +76,7 @@ try {
Examples of **correct** code for this rule:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint global-require: "error"*/
diff --git a/docs/src/rules/id-denylist.md b/docs/src/rules/id-denylist.md
index 850f5923e83..82859ec8b43 100644
--- a/docs/src/rules/id-denylist.md
+++ b/docs/src/rules/id-denylist.md
@@ -46,7 +46,7 @@ Examples of **incorrect** code for this rule with sample `"data", "callback"` re
```js
/*eslint id-denylist: ["error", "data", "callback"] */
-var data = {...};
+var data = { ...values };
function callback() {
// ...
@@ -57,23 +57,23 @@ element.callback = function() {
};
var itemSet = {
- data: [...]
+ data: [...values]
};
class Foo {
data = [];
}
-class Foo {
+class Bar {
#data = [];
}
-class Foo {
- callback( {);
+class Baz {
+ callback() {}
}
-class Foo {
- #callback( {);
+class Qux {
+ #callback() {}
}
```
@@ -86,7 +86,7 @@ Examples of **correct** code for this rule with sample `"data", "callback"` rest
```js
/*eslint id-denylist: ["error", "data", "callback"] */
-var encodingOptions = {...};
+var encodingOptions = {...values};
function processFileResult() {
// ...
@@ -97,7 +97,7 @@ element.successHandler = function() {
};
var itemSet = {
- entities: [...]
+ entities: [...values]
};
callback(); // all function calls are ignored
@@ -110,16 +110,16 @@ class Foo {
items = [];
}
-class Foo {
+class Bar {
#items = [];
}
-class Foo {
- method( {);
+class Baz {
+ method() {}
}
-class Foo {
- #method( {);
+class Qux {
+ #method() {}
}
```
diff --git a/docs/src/rules/id-length.md b/docs/src/rules/id-length.md
index adf5e897817..ea5d2fa9d1a 100644
--- a/docs/src/rules/id-length.md
+++ b/docs/src/rules/id-length.md
@@ -41,16 +41,16 @@ try {
}
var myObj = { a: 1 };
(a) => { a * a };
-class x { }
+class y { }
class Foo { x() {} }
-class Foo { #x() {} }
-class Foo { x = 1 }
-class Foo { #x = 1 }
-function foo(...x) { }
-function foo([x]) { }
+class Bar { #x() {} }
+class Baz { x = 1 }
+class Qux { #x = 1 }
+function bar(...x) { }
+function baz([x]) { }
var [x] = arr;
var { prop: [x]} = {};
-function foo({x}) { }
+function qux({x}) { }
var { x } = {};
var { prop: a} = {};
({ prop: obj.x } = {});
@@ -78,19 +78,19 @@ try {
}
var myObj = { apple: 1 };
(num) => { num * num };
-function foo(num = 0) { }
+function bar(num = 0) { }
class MyClass { }
class Foo { method() {} }
-class Foo { #method() {} }
-class Foo { field = 1 }
-class Foo { #field = 1 }
-function foo(...args) { }
-function foo([longName]) { }
+class Bar { #method() {} }
+class Baz { field = 1 }
+class Qux { #field = 1 }
+function baz(...args) { }
+function qux([longName]) { }
var { prop } = {};
var { prop: [longName] } = {};
var [longName] = arr;
-function foo({ prop }) { }
-function foo({ a: prop }) { }
+function foobar({ prop }) { }
+function foobaz({ a: prop }) { }
var { prop } = {};
var { a: prop } = {};
({ prop: obj.longName } = {});
@@ -129,9 +129,9 @@ try {
}
var myObj = { a: 1 };
(val) => { val * val };
-class x { }
+class y { }
class Foo { x() {} }
-function foo(...x) { }
+function bar(...x) { }
var { x } = {};
var { prop: a} = {};
var [x] = arr;
@@ -151,7 +151,7 @@ Examples of **correct** code for this rule with the `{ "min": 4 }` option:
var value = 5;
function func() { return 42; }
-obj.element = document.body;
+object.element = document.body;
var foobar = function (event) { /* do stuff */ };
try {
dangerousStuff();
@@ -160,15 +160,15 @@ try {
}
var myObj = { apple: 1 };
(value) => { value * value };
-function foobar(value = 0) { }
+function foobaz(value = 0) { }
class MyClass { }
class Foobar { method() {} }
-function foobar(...args) { }
+function barbaz(...args) { }
var { prop } = {};
var [longName] = foo;
var { a: [prop] } = {};
var { a: longName } = {};
-({ prop: obj.name } = {});
+({ prop: object.name } = {});
var data = { "x": 1 }; // excused because of quotes
data["y"] = 3; // excused because of calculated property access
```
@@ -242,16 +242,16 @@ var myObj = { a: 1 };
### exceptions
-Examples of additional **correct** code for this rule with the `{ "exceptions": ["x"] }` option:
+Examples of additional **correct** code for this rule with the `{ "exceptions": ["x", "y", "z", "ζ"] }` option:
::: correct
```js
-/*eslint id-length: ["error", { "exceptions": ["x"] }]*/
+/*eslint id-length: ["error", { "exceptions": ["x", "y", "z", "ζ"] }]*/
/*eslint-env es6*/
var x = 5;
-function x() { return 42; }
+function y() { return 42; }
obj.x = document.body;
var foo = function (x) { /* do stuff */ };
try {
@@ -261,8 +261,8 @@ try {
}
(x) => { return x * x; };
var [x] = arr;
-const { x } = foo;
-const { a: x } = foo;
+const { z } = foo;
+const { a: ζ } = foo;
```
:::
diff --git a/docs/src/rules/id-match.md b/docs/src/rules/id-match.md
index 08d5ff3ba9c..59954fc2a48 100644
--- a/docs/src/rules/id-match.md
+++ b/docs/src/rules/id-match.md
@@ -42,17 +42,13 @@ function do_something() {
// ...
}
-obj.do_something = function() {
- // ...
-};
-
class My_Class {}
class myClass {
do_something() {}
}
-class myClass {
+class anotherClass {
#do_something() {}
}
```
@@ -76,11 +72,11 @@ var obj = {
class myClass {}
-class myClass {
+class anotherClass {
doSomething() {}
}
-class myClass {
+class oneMoreClass {
#doSomething() {}
}
```
@@ -110,6 +106,10 @@ Examples of **incorrect** code for this rule with the `"^[a-z]+([A-Z][a-z]+)*$",
var obj = {
my_pref: 1
};
+
+obj.do_something = function() {
+ // ...
+};
```
:::
@@ -121,13 +121,13 @@ Examples of **incorrect** code for this rule with the `"^[a-z]+([A-Z][a-z]+)*$",
::: incorrect
```js
-/*eslint id-match: ["error", "^[a-z]+([A-Z][a-z]+)*$", { "properties": true }]*/
+/*eslint id-match: ["error", "^[a-z]+([A-Z][a-z]+)*$", { "classFields": true }]*/
class myClass {
my_pref = 1;
}
-class myClass {
+class anotherClass {
#my_pref = 1;
}
```
@@ -143,7 +143,7 @@ Examples of **correct** code for this rule with the `"^[a-z]+([A-Z][a-z]+)*$", {
```js
/*eslint id-match: [2, "^[a-z]+([A-Z][a-z]+)*$", { "onlyDeclarations": true }]*/
-do_something(__dirname);
+foo = __dirname;
```
:::
diff --git a/docs/src/rules/implicit-arrow-linebreak.md b/docs/src/rules/implicit-arrow-linebreak.md
index 09aaba18e03..25f5bc97d76 100644
--- a/docs/src/rules/implicit-arrow-linebreak.md
+++ b/docs/src/rules/implicit-arrow-linebreak.md
@@ -5,7 +5,7 @@ related_rules:
- brace-style
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
An arrow function body can contain an implicit return as an expression instead of a block body. It can be useful to enforce a consistent location for the implicitly returned expression.
diff --git a/docs/src/rules/indent-legacy.md b/docs/src/rules/indent-legacy.md
index 97372ebc8ff..a8097b22932 100644
--- a/docs/src/rules/indent-legacy.md
+++ b/docs/src/rules/indent-legacy.md
@@ -39,7 +39,7 @@ For example, for 2-space indentation:
```json
{
- "indent": ["error", 2]
+ "indent-legacy": ["error", 2]
}
```
@@ -47,7 +47,7 @@ Or for tabbed indentation:
```json
{
- "indent": ["error", "tab"]
+ "indent-legacy": ["error", "tab"]
}
```
@@ -56,7 +56,7 @@ Examples of **incorrect** code for this rule with the default options:
::: incorrect
```js
-/*eslint indent: "error"*/
+/*eslint indent-legacy: "error"*/
if (a) {
b=c;
@@ -73,7 +73,7 @@ Examples of **correct** code for this rule with the default options:
::: correct
```js
-/*eslint indent: "error"*/
+/*eslint indent-legacy: "error"*/
if (a) {
b=c;
@@ -126,7 +126,7 @@ Examples of **incorrect** code for this rule with the `"tab"` option:
::: incorrect
```js
-/*eslint indent: ["error", "tab"]*/
+/*eslint indent-legacy: ["error", "tab"]*/
if (a) {
b=c;
@@ -140,20 +140,23 @@ function foo(d) {
Examples of **correct** code for this rule with the `"tab"` option:
+
+
::: correct
```js
-/*eslint indent: ["error", "tab"]*/
+/*eslint indent-legacy: ["error", "tab"]*/
if (a) {
-/*tab*/b=c;
-/*tab*/function foo(d) {
-/*tab*//*tab*/e=f;
-/*tab*/}
+ b=c;
+ function foo(d) {
+ e=f;
+ }
}
```
:::
+
### SwitchCase
@@ -162,7 +165,7 @@ Examples of **incorrect** code for this rule with the `2, { "SwitchCase": 1 }` o
::: incorrect
```js
-/*eslint indent: ["error", 2, { "SwitchCase": 1 }]*/
+/*eslint indent-legacy: ["error", 2, { "SwitchCase": 1 }]*/
switch(a){
case "a":
@@ -179,7 +182,7 @@ Examples of **correct** code for this rule with the `2, { "SwitchCase": 1 }` opt
::: correct
```js
-/*eslint indent: ["error", 2, { "SwitchCase": 1 }]*/
+/*eslint indent-legacy: ["error", 2, { "SwitchCase": 1 }]*/
switch(a){
case "a":
@@ -198,18 +201,18 @@ Examples of **incorrect** code for this rule with the `2, { "VariableDeclarator"
::: incorrect
```js
-/*eslint indent: ["error", 2, { "VariableDeclarator": 1 }]*/
+/*eslint indent-legacy: ["error", 2, { "VariableDeclarator": 1 }]*/
/*eslint-env es6*/
var a,
b,
c;
-let a,
- b,
- c;
-const a = 1,
- b = 2,
- c = 3;
+let d,
+ e,
+ f;
+const g = 1,
+ h = 2,
+ i = 3;
```
:::
@@ -219,18 +222,18 @@ Examples of **correct** code for this rule with the `2, { "VariableDeclarator":
::: correct
```js
-/*eslint indent: ["error", 2, { "VariableDeclarator": 1 }]*/
+/*eslint indent-legacy: ["error", 2, { "VariableDeclarator": 1 }]*/
/*eslint-env es6*/
var a,
b,
c;
-let a,
- b,
- c;
-const a = 1,
- b = 2,
- c = 3;
+let d,
+ e,
+ f;
+const g = 1,
+ h = 2,
+ i = 3;
```
:::
@@ -240,18 +243,18 @@ Examples of **correct** code for this rule with the `2, { "VariableDeclarator":
::: correct
```js
-/*eslint indent: ["error", 2, { "VariableDeclarator": 2 }]*/
+/*eslint indent-legacy: ["error", 2, { "VariableDeclarator": 2 }]*/
/*eslint-env es6*/
var a,
b,
c;
-let a,
- b,
- c;
-const a = 1,
- b = 2,
- c = 3;
+let d,
+ e,
+ f;
+const g = 1,
+ h = 2,
+ i = 3;
```
:::
@@ -261,18 +264,18 @@ Examples of **correct** code for this rule with the `2, { "VariableDeclarator":
::: correct
```js
-/*eslint indent: ["error", 2, { "VariableDeclarator": { "var": 2, "let": 2, "const": 3 } }]*/
+/*eslint indent-legacy: ["error", 2, { "VariableDeclarator": { "var": 2, "let": 2, "const": 3 } }]*/
/*eslint-env es6*/
var a,
b,
c;
-let a,
- b,
- c;
-const a = 1,
- b = 2,
- c = 3;
+let e,
+ f,
+ g;
+const h = 1,
+ i = 2,
+ j = 3;
```
:::
@@ -284,7 +287,7 @@ Examples of **incorrect** code for this rule with the options `2, { "outerIIFEBo
::: incorrect
```js
-/*eslint indent: ["error", 2, { "outerIIFEBody": 0 }]*/
+/*eslint indent-legacy: ["error", 2, { "outerIIFEBody": 0 }]*/
(function() {
@@ -306,7 +309,7 @@ Examples of **correct** code for this rule with the options `2, {"outerIIFEBody"
::: correct
```js
-/*eslint indent: ["error", 2, { "outerIIFEBody": 0 }]*/
+/*eslint indent-legacy: ["error", 2, { "outerIIFEBody": 0 }]*/
(function() {
@@ -330,7 +333,7 @@ Examples of **incorrect** code for this rule with the `2, { "MemberExpression":
::: incorrect
```js
-/*eslint indent: ["error", 2, { "MemberExpression": 1 }]*/
+/*eslint indent-legacy: ["error", 2, { "MemberExpression": 1 }]*/
foo
.bar
@@ -344,7 +347,7 @@ Examples of **correct** code for this rule with the `2, { "MemberExpression": 1
::: correct
```js
-/*eslint indent: ["error", 2, { "MemberExpression": 1 }]*/
+/*eslint indent-legacy: ["error", 2, { "MemberExpression": 1 }]*/
foo
.bar
@@ -364,7 +367,7 @@ Examples of **incorrect** code for this rule with the `2, { "FunctionDeclaration
::: incorrect
```js
-/*eslint indent: ["error", 2, { "FunctionDeclaration": {"body": 1, "parameters": 2} }]*/
+/*eslint indent-legacy: ["error", 2, { "FunctionDeclaration": {"body": 1, "parameters": 2} }]*/
function foo(bar,
baz,
@@ -380,7 +383,7 @@ Examples of **correct** code for this rule with the `2, { "FunctionDeclaration":
::: correct
```js
-/*eslint indent: ["error", 2, { "FunctionDeclaration": {"body": 1, "parameters": 2} }]*/
+/*eslint indent-legacy: ["error", 2, { "FunctionDeclaration": {"body": 1, "parameters": 2} }]*/
function foo(bar,
baz,
@@ -396,7 +399,7 @@ Examples of **incorrect** code for this rule with the `2, { "FunctionDeclaration
::: incorrect
```js
-/*eslint indent: ["error", 2, {"FunctionDeclaration": {"parameters": "first"}}]*/
+/*eslint indent-legacy: ["error", 2, {"FunctionDeclaration": {"parameters": "first"}}]*/
function foo(bar, baz,
qux, boop) {
@@ -411,7 +414,7 @@ Examples of **correct** code for this rule with the `2, { "FunctionDeclaration":
::: correct
```js
-/*eslint indent: ["error", 2, {"FunctionDeclaration": {"parameters": "first"}}]*/
+/*eslint indent-legacy: ["error", 2, {"FunctionDeclaration": {"parameters": "first"}}]*/
function foo(bar, baz,
qux, boop) {
@@ -428,7 +431,7 @@ Examples of **incorrect** code for this rule with the `2, { "FunctionExpression"
::: incorrect
```js
-/*eslint indent: ["error", 2, { "FunctionExpression": {"body": 1, "parameters": 2} }]*/
+/*eslint indent-legacy: ["error", 2, { "FunctionExpression": {"body": 1, "parameters": 2} }]*/
var foo = function(bar,
baz,
@@ -444,7 +447,7 @@ Examples of **correct** code for this rule with the `2, { "FunctionExpression":
::: correct
```js
-/*eslint indent: ["error", 2, { "FunctionExpression": {"body": 1, "parameters": 2} }]*/
+/*eslint indent-legacy: ["error", 2, { "FunctionExpression": {"body": 1, "parameters": 2} }]*/
var foo = function(bar,
baz,
@@ -460,7 +463,7 @@ Examples of **incorrect** code for this rule with the `2, { "FunctionExpression"
::: incorrect
```js
-/*eslint indent: ["error", 2, {"FunctionExpression": {"parameters": "first"}}]*/
+/*eslint indent-legacy: ["error", 2, {"FunctionExpression": {"parameters": "first"}}]*/
var foo = function(bar, baz,
qux, boop) {
@@ -475,7 +478,7 @@ Examples of **correct** code for this rule with the `2, { "FunctionExpression":
::: correct
```js
-/*eslint indent: ["error", 2, {"FunctionExpression": {"parameters": "first"}}]*/
+/*eslint indent-legacy: ["error", 2, {"FunctionExpression": {"parameters": "first"}}]*/
var foo = function(bar, baz,
qux, boop) {
@@ -492,7 +495,7 @@ Examples of **incorrect** code for this rule with the `2, { "CallExpression": {"
::: incorrect
```js
-/*eslint indent: ["error", 2, { "CallExpression": {"arguments": 1} }]*/
+/*eslint indent-legacy: ["error", 2, { "CallExpression": {"arguments": 1} }]*/
foo(bar,
baz,
@@ -507,7 +510,7 @@ Examples of **correct** code for this rule with the `2, { "CallExpression": {"ar
::: correct
```js
-/*eslint indent: ["error", 2, { "CallExpression": {"arguments": 1} }]*/
+/*eslint indent-legacy: ["error", 2, { "CallExpression": {"arguments": 1} }]*/
foo(bar,
baz,
@@ -522,7 +525,7 @@ Examples of **incorrect** code for this rule with the `2, { "CallExpression": {"
::: incorrect
```js
-/*eslint indent: ["error", 2, {"CallExpression": {"arguments": "first"}}]*/
+/*eslint indent-legacy: ["error", 2, {"CallExpression": {"arguments": "first"}}]*/
foo(bar, baz,
baz, boop, beep);
@@ -535,7 +538,7 @@ Examples of **correct** code for this rule with the `2, { "CallExpression": {"ar
::: correct
```js
-/*eslint indent: ["error", 2, {"CallExpression": {"arguments": "first"}}]*/
+/*eslint indent-legacy: ["error", 2, {"CallExpression": {"arguments": "first"}}]*/
foo(bar, baz,
baz, boop, beep);
@@ -550,7 +553,7 @@ Examples of **incorrect** code for this rule with the `2, { "ArrayExpression": 1
::: incorrect
```js
-/*eslint indent: ["error", 2, { "ArrayExpression": 1 }]*/
+/*eslint indent-legacy: ["error", 2, { "ArrayExpression": 1 }]*/
var foo = [
bar,
@@ -566,7 +569,7 @@ Examples of **correct** code for this rule with the `2, { "ArrayExpression": 1 }
::: correct
```js
-/*eslint indent: ["error", 2, { "ArrayExpression": 1 }]*/
+/*eslint indent-legacy: ["error", 2, { "ArrayExpression": 1 }]*/
var foo = [
bar,
@@ -582,7 +585,7 @@ Examples of **incorrect** code for this rule with the `2, { "ArrayExpression": "
::: incorrect
```js
-/*eslint indent: ["error", 2, {"ArrayExpression": "first"}]*/
+/*eslint indent-legacy: ["error", 2, {"ArrayExpression": "first"}]*/
var foo = [bar,
baz,
@@ -597,7 +600,7 @@ Examples of **correct** code for this rule with the `2, { "ArrayExpression": "fi
::: correct
```js
-/*eslint indent: ["error", 2, {"ArrayExpression": "first"}]*/
+/*eslint indent-legacy: ["error", 2, {"ArrayExpression": "first"}]*/
var foo = [bar,
baz,
@@ -614,7 +617,7 @@ Examples of **incorrect** code for this rule with the `2, { "ObjectExpression":
::: incorrect
```js
-/*eslint indent: ["error", 2, { "ObjectExpression": 1 }]*/
+/*eslint indent-legacy: ["error", 2, { "ObjectExpression": 1 }]*/
var foo = {
bar: 1,
@@ -630,7 +633,7 @@ Examples of **correct** code for this rule with the `2, { "ObjectExpression": 1
::: correct
```js
-/*eslint indent: ["error", 2, { "ObjectExpression": 1 }]*/
+/*eslint indent-legacy: ["error", 2, { "ObjectExpression": 1 }]*/
var foo = {
bar: 1,
@@ -646,7 +649,7 @@ Examples of **incorrect** code for this rule with the `2, { "ObjectExpression":
::: incorrect
```js
-/*eslint indent: ["error", 2, {"ObjectExpression": "first"}]*/
+/*eslint indent-legacy: ["error", 2, {"ObjectExpression": "first"}]*/
var foo = { bar: 1,
baz: 2 };
@@ -659,7 +662,7 @@ Examples of **correct** code for this rule with the `2, { "ObjectExpression": "f
::: correct
```js
-/*eslint indent: ["error", 2, {"ObjectExpression": "first"}]*/
+/*eslint indent-legacy: ["error", 2, {"ObjectExpression": "first"}]*/
var foo = { bar: 1,
baz: 2 };
diff --git a/docs/src/rules/indent.md b/docs/src/rules/indent.md
index 7f81c0fd57b..6cd25b73aec 100644
--- a/docs/src/rules/indent.md
+++ b/docs/src/rules/indent.md
@@ -3,7 +3,7 @@ title: indent
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
There are several common guidelines which require specific indentation of nested blocks and statements, like:
@@ -141,20 +141,23 @@ function foo(d) {
Examples of **correct** code for this rule with the `"tab"` option:
+
+
::: correct
```js
/*eslint indent: ["error", "tab"]*/
if (a) {
-/*tab*/b=c;
-/*tab*/function foo(d) {
-/*tab*//*tab*/e=f;
-/*tab*/}
+ b=c;
+ function foo(d) {
+ e=f;
+ }
}
```
:::
+
### ignoredNodes
@@ -192,7 +195,7 @@ Examples of **correct** code for this rule with the `4, { "ignoredNodes": ["Call
foo();
bar();
-})
+})();
```
:::
@@ -248,12 +251,12 @@ Examples of **incorrect** code for this rule with the `2, { "VariableDeclarator"
var a,
b,
c;
-let a,
- b,
- c;
-const a = 1,
- b = 2,
- c = 3;
+let d,
+ e,
+ f;
+const g = 1,
+ h = 2,
+ i = 3;
```
:::
@@ -269,12 +272,12 @@ Examples of **correct** code for this rule with the `2, { "VariableDeclarator":
var a,
b,
c;
-let a,
- b,
- c;
-const a = 1,
- b = 2,
- c = 3;
+let d,
+ e,
+ f;
+const g = 1,
+ h = 2,
+ i = 3;
```
:::
@@ -290,12 +293,12 @@ Examples of **correct** code for this rule with the `2, { "VariableDeclarator":
var a,
b,
c;
-let a,
- b,
- c;
-const a = 1,
- b = 2,
- c = 3;
+let d,
+ e,
+ f;
+const g = 1,
+ h = 2,
+ i = 3;
```
:::
@@ -311,12 +314,12 @@ Examples of **incorrect** code for this rule with the `2, { "VariableDeclarator"
var a,
b,
c;
-let a,
- b,
- c;
-const a = 1,
- b = 2,
- c = 3;
+let d,
+ e,
+ f;
+const g = 1,
+ h = 2,
+ i = 3;
```
:::
@@ -332,12 +335,12 @@ Examples of **correct** code for this rule with the `2, { "VariableDeclarator":
var a,
b,
c;
-let a,
- b,
- c;
-const a = 1,
- b = 2,
- c = 3;
+let d,
+ e,
+ f;
+const g = 1,
+ h = 2,
+ i = 3;
```
:::
@@ -353,12 +356,12 @@ Examples of **correct** code for this rule with the `2, { "VariableDeclarator":
var a,
b,
c;
-let a,
- b,
- c;
-const a = 1,
- b = 2,
- c = 3;
+let d,
+ e,
+ f;
+const g = 1,
+ h = 2,
+ i = 3;
```
:::
@@ -403,7 +406,7 @@ function foo(x) {
})();
if (y) {
- console.log('foo');
+ console.log('foo');
}
```
@@ -858,6 +861,14 @@ import { foo,
bar,
baz,
} from 'qux';
+```
+
+:::
+
+::: correct
+
+```js
+/*eslint indent: ["error", 4, { "ImportDeclaration": 1 }]*/
import {
foo,
diff --git a/docs/src/rules/jsx-quotes.md b/docs/src/rules/jsx-quotes.md
index 4f0d78ba78a..b26450a9bc4 100644
--- a/docs/src/rules/jsx-quotes.md
+++ b/docs/src/rules/jsx-quotes.md
@@ -5,21 +5,21 @@ related_rules:
- quotes
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
JSX attribute values can contain string literals, which are delimited with single or double quotes.
-```xml
-
-
+```jsx
+;
+;
```
Unlike string literals in JavaScript, string literals within JSX attributes can’t contain escaped quotes.
If you want to have e.g. a double quote within a JSX attribute value, you have to use single quotes as string delimiter.
-```xml
-
-
+```jsx
+;
+;
```
## Rule Details
@@ -37,25 +37,25 @@ This rule has a string option:
Examples of **incorrect** code for this rule with the default `"prefer-double"` option:
-:::incorrect
+:::incorrect { "ecmaFeatures": { "jsx": true } }
-```xml
+```jsx
/*eslint jsx-quotes: ["error", "prefer-double"]*/
-
+;
```
:::
Examples of **correct** code for this rule with the default `"prefer-double"` option:
-:::correct
+:::correct { "ecmaFeatures": { "jsx": true } }
-```xml
+```jsx
/*eslint jsx-quotes: ["error", "prefer-double"]*/
-
-
+;
+;
```
:::
@@ -64,25 +64,25 @@ Examples of **correct** code for this rule with the default `"prefer-double"` op
Examples of **incorrect** code for this rule with the `"prefer-single"` option:
-:::incorrect
+:::incorrect { "ecmaFeatures": { "jsx": true } }
-```xml
+```jsx
/*eslint jsx-quotes: ["error", "prefer-single"]*/
-
+;
```
:::
Examples of **correct** code for this rule with the `"prefer-single"` option:
-:::correct
+:::correct { "ecmaFeatures": { "jsx": true } }
-```xml
+```jsx
/*eslint jsx-quotes: ["error", "prefer-single"]*/
-
-
+;
+;
```
:::
diff --git a/docs/src/rules/key-spacing.md b/docs/src/rules/key-spacing.md
index d6e500053cf..234b0de1b21 100644
--- a/docs/src/rules/key-spacing.md
+++ b/docs/src/rules/key-spacing.md
@@ -3,7 +3,7 @@ title: key-spacing
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
This rule enforces spacing around the colon in object literal properties. It can verify each property individually, or it can ensure horizontal alignment of adjacent properties in an object literal.
diff --git a/docs/src/rules/keyword-spacing.md b/docs/src/rules/keyword-spacing.md
index a77e3ddc420..7510378dea0 100644
--- a/docs/src/rules/keyword-spacing.md
+++ b/docs/src/rules/keyword-spacing.md
@@ -3,7 +3,7 @@ title: keyword-spacing
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Keywords are syntax elements of JavaScript, such as `try` and `if`.
These keywords have special meaning to the language and so often appear in a different color in code editors.
@@ -58,9 +58,9 @@ if (foo) {
Examples of **correct** code for this rule with the default `{ "before": true }` option:
-::: correct
+::: correct { "ecmaFeatures": { "jsx": true } }
-```js
+```jsx
/*eslint keyword-spacing: ["error", { "before": true }]*/
/*eslint-env es6*/
@@ -77,30 +77,30 @@ let a = [this];
let b = [function() {}];
// Avoid conflict with `arrow-spacing`
-let a = ()=> this.foo;
+let c = ()=> this.foo;
// Avoid conflict with `block-spacing`
{function foo() {}}
// Avoid conflict with `comma-spacing`
-let a = [100,this.foo, this.bar];
+let d = [100,this.foo, this.bar];
// Avoid conflict with `computed-property-spacing`
obj[this.foo] = 0;
// Avoid conflict with `generator-star-spacing`
-function *foo() {}
+function *bar() {}
// Avoid conflict with `key-spacing`
-let obj = {
+let obj1 = {
foo:function() {}
};
// Avoid conflict with `object-curly-spacing`
-let obj = {foo: this};
+let obj2 = {foo: this};
// Avoid conflict with `semi-spacing`
-let a = this;function foo() {}
+let e = this;function foo() {}
// Avoid conflict with `space-in-parens`
(function () {})();
@@ -110,7 +110,7 @@ if ("foo"in {foo: 0}) {}
if (10+this.foo<= this.bar) {}
// Avoid conflict with `jsx-curly-spacing`
-let a =
+let f =
```
:::
@@ -173,9 +173,9 @@ if(foo) {
Examples of **correct** code for this rule with the default `{ "after": true }` option:
-::: correct
+::: correct { "ecmaFeatures": { "jsx": true } }
-```js
+```jsx
/*eslint keyword-spacing: ["error", { "after": true }]*/
if (foo) {
@@ -190,10 +190,10 @@ if (foo) {
let a = [this];
// Avoid conflict with `arrow-spacing`
-let a = ()=> this.foo;
+let b = ()=> this.foo;
// Avoid conflict with `comma-spacing`
-let a = [100, this.foo, this.bar];
+let c = [100, this.foo, this.bar];
// Avoid conflict with `computed-property-spacing`
obj[this.foo] = 0;
@@ -202,42 +202,42 @@ obj[this.foo] = 0;
function* foo() {}
// Avoid conflict with `key-spacing`
-let obj = {
+let obj1 = {
foo:function() {}
};
// Avoid conflict with `func-call-spacing`
-class A {
+class A extends B {
constructor() {
super();
}
}
// Avoid conflict with `object-curly-spacing`
-let obj = {foo: this};
+let obj2 = {foo: this};
// Avoid conflict with `semi-spacing`
-let a = this;function foo() {}
+let d = this;function bar() {}
// Avoid conflict with `space-before-function-paren`
-function() {}
+(function() {})();
// Avoid conflict with `space-infix-ops`
if ("foo"in{foo: 0}) {}
if (10+this.foo<= this.bar) {}
// Avoid conflict with `space-unary-ops`
-function* foo(a) {
+function* baz(a) {
return yield+a;
}
// Avoid conflict with `yield-star-spacing`
-function* foo(a) {
+function* qux(a) {
return yield* a;
}
// Avoid conflict with `jsx-curly-spacing`
-let a =
+let e =
```
:::
diff --git a/docs/src/rules/linebreak-style.md b/docs/src/rules/linebreak-style.md
index 71158115f34..c4a764c9deb 100644
--- a/docs/src/rules/linebreak-style.md
+++ b/docs/src/rules/linebreak-style.md
@@ -3,7 +3,7 @@ title: linebreak-style
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
When developing with a lot of people all having different editors, VCS applications and operating systems it may occur that
different line endings are written by either of the mentioned (might especially happen when using the windows and mac versions of SourceTree together).
diff --git a/docs/src/rules/lines-around-comment.md b/docs/src/rules/lines-around-comment.md
index 924ba66cf1a..56ea775ef44 100644
--- a/docs/src/rules/lines-around-comment.md
+++ b/docs/src/rules/lines-around-comment.md
@@ -6,7 +6,7 @@ related_rules:
- spaced-comment
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Many style guides require empty lines before or after comments. The primary goal
of these rules is to make the comments easier to read and improve readability of the code.
@@ -663,7 +663,7 @@ Examples of **correct** code for the `ignorePattern` option:
/*eslint lines-around-comment: ["error"]*/
foo();
-/* eslint mentioned in this comment */,
+/* eslint mentioned in this comment */
bar();
/*eslint lines-around-comment: ["error", { "ignorePattern": "pragma" }] */
diff --git a/docs/src/rules/lines-around-directive.md b/docs/src/rules/lines-around-directive.md
index fb71775c7f1..7c18b517446 100644
--- a/docs/src/rules/lines-around-directive.md
+++ b/docs/src/rules/lines-around-directive.md
@@ -60,19 +60,13 @@ This is the default option.
Examples of **incorrect** code for this rule with the `"always"` option:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/* eslint lines-around-directive: ["error", "always"] */
-/* Top of file */
-"use strict";
-var foo;
-
-/* Top of file */
// comment
"use strict";
-"use asm";
var foo;
function foo() {
@@ -90,23 +84,29 @@ function foo() {
:::
-Examples of **correct** code for this rule with the `"always"` option:
-
-::: correct
+::: incorrect { "sourceType": "script" }
```js
/* eslint lines-around-directive: ["error", "always"] */
-/* Top of file */
+// comment
"use strict";
-
+"use asm";
var foo;
+```
+
+:::
+
+Examples of **correct** code for this rule with the `"always"` option:
+
+::: correct { "sourceType": "script" }
+
+```js
+/* eslint lines-around-directive: ["error", "always"] */
-/* Top of file */
// comment
"use strict";
-"use asm";
var foo;
@@ -128,26 +128,33 @@ function foo() {
:::
-### never
-
-Examples of **incorrect** code for this rule with the `"never"` option:
-
-::: incorrect
+::: correct { "sourceType": "script" }
```js
-/* eslint lines-around-directive: ["error", "never"] */
+/* eslint lines-around-directive: ["error", "always"] */
-/* Top of file */
+// comment
"use strict";
+"use asm";
var foo;
+```
+
+:::
+
+### never
+
+Examples of **incorrect** code for this rule with the `"never"` option:
+
+::: incorrect { "sourceType": "script" }
+
+```js
+/* eslint lines-around-directive: ["error", "never"] */
-/* Top of file */
// comment
"use strict";
-"use asm";
var foo;
@@ -169,21 +176,30 @@ function foo() {
:::
-Examples of **correct** code for this rule with the `"never"` option:
-
-::: correct
+::: incorrect { "sourceType": "script" }
```js
/* eslint lines-around-directive: ["error", "never"] */
-/* Top of file */
+// comment
+
"use strict";
+"use asm";
+
var foo;
+```
+
+:::
+
+Examples of **correct** code for this rule with the `"never"` option:
+
+::: correct { "sourceType": "script" }
+
+```js
+/* eslint lines-around-directive: ["error", "never"] */
-/* Top of file */
// comment
"use strict";
-"use asm";
var foo;
function foo() {
@@ -201,25 +217,31 @@ function foo() {
:::
+::: correct { "sourceType": "script" }
+
+```js
+/* eslint lines-around-directive: ["error", "never"] */
+
+// comment
+"use strict";
+"use asm";
+var foo;
+```
+
+:::
+
### before & after
Examples of **incorrect** code for this rule with the `{ "before": "never", "after": "always" }` option:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/* eslint lines-around-directive: ["error", { "before": "never", "after": "always" }] */
-/* Top of file */
-
-"use strict";
-var foo;
-
-/* Top of file */
// comment
"use strict";
-"use asm";
var foo;
function foo() {
@@ -238,22 +260,29 @@ function foo() {
:::
-Examples of **correct** code for this rule with the `{ "before": "never", "after": "always" }` option:
-
-::: correct
+::: incorrect { "sourceType": "script" }
```js
/* eslint lines-around-directive: ["error", { "before": "never", "after": "always" }] */
-/* Top of file */
-"use strict";
+// comment
+"use strict";
+"use asm";
var foo;
+```
+
+:::
+
+Examples of **correct** code for this rule with the `{ "before": "never", "after": "always" }` option:
+
+::: correct { "sourceType": "script" }
+
+```js
+/* eslint lines-around-directive: ["error", { "before": "never", "after": "always" }] */
-/* Top of file */
// comment
"use strict";
-"use asm";
var foo;
@@ -274,22 +303,29 @@ function foo() {
:::
-Examples of **incorrect** code for this rule with the `{ "before": "always", "after": "never" }` option:
-
-::: incorrect
+::: correct { "sourceType": "script" }
```js
-/* eslint lines-around-directive: ["error", { "before": "always", "after": "never" }] */
+/* eslint lines-around-directive: ["error", { "before": "never", "after": "always" }] */
-/* Top of file */
+// comment
"use strict";
+"use asm";
var foo;
+```
+
+:::
+
+Examples of **incorrect** code for this rule with the `{ "before": "always", "after": "never" }` option:
+
+::: incorrect { "sourceType": "script" }
+
+```js
+/* eslint lines-around-directive: ["error", { "before": "always", "after": "never" }] */
-/* Top of file */
// comment
"use strict";
-"use asm";
var foo;
@@ -310,22 +346,30 @@ function foo() {
:::
-Examples of **correct** code for this rule with the `{ "before": "always", "after": "never" }` option:
-
-::: correct
+::: incorrect { "sourceType": "script" }
```js
/* eslint lines-around-directive: ["error", { "before": "always", "after": "never" }] */
-/* Top of file */
+// comment
"use strict";
+"use asm";
+
var foo;
+```
+
+:::
+
+Examples of **correct** code for this rule with the `{ "before": "always", "after": "never" }` option:
+
+::: correct { "sourceType": "script" }
+
+```js
+/* eslint lines-around-directive: ["error", { "before": "always", "after": "never" }] */
-/* Top of file */
// comment
"use strict";
-"use asm";
var foo;
function foo() {
@@ -344,6 +388,20 @@ function foo() {
:::
+::: correct { "sourceType": "script" }
+
+```js
+/* eslint lines-around-directive: ["error", { "before": "always", "after": "never" }] */
+
+// comment
+
+"use strict";
+"use asm";
+var foo;
+```
+
+:::
+
## When Not To Use It
You can safely disable this rule if you do not have any strict conventions about whether or not directive prologues should have blank newlines before or after them.
diff --git a/docs/src/rules/lines-between-class-members.md b/docs/src/rules/lines-between-class-members.md
index 8daf2a2f78d..9c9d7e0cbad 100644
--- a/docs/src/rules/lines-between-class-members.md
+++ b/docs/src/rules/lines-between-class-members.md
@@ -6,7 +6,7 @@ related_rules:
- padding-line-between-statements
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
This rule improves readability by enforcing lines between class members. It will not check empty lines before the first member and after the last member, since that is already taken care of by padded-blocks.
@@ -69,14 +69,19 @@ class MyClass {
### Options
-This rule has a string option and an object option.
+This rule has two options, first option can be string or object, second option is object.
-String option:
+First option can be string `"always"` or `"never"` or an object with a property named `enforce`:
* `"always"`(default) require an empty line after class members
* `"never"` disallows an empty line after class members
+* `Object`: An object with a property named `enforce`. The enforce property should be an array of objects, each specifying the configuration for enforcing empty lines between specific pairs of class members.
+ * **enforce**: You can supply any number of configurations. If a member pair matches multiple configurations, the last matched configuration will be used. If a member pair does not match any configurations, it will be ignored. Each object should have the following properties:
+ * **blankLine**: Can be set to either `"always"` or `"never"`, indicating whether a blank line should be required or disallowed between the specified members.
+ * **prev**: Specifies the type of the preceding class member. It can be `"method"` for class methods, `"field"` for class fields, or `"*"` for any class member.
+ * **next**: Specifies the type of the following class member. It follows the same options as `prev`.
-Object option:
+Second option is an object with a property named `exceptAfterSingleLine`:
* `"exceptAfterSingleLine": false`(default) **do not** skip checking empty lines after single-line class members
* `"exceptAfterSingleLine": true` skip checking empty lines after single-line class members
@@ -92,9 +97,15 @@ class Foo{
bar(){}
baz(){}
}
+```
+
+:::
+
+::: incorrect
+```js
/* eslint lines-between-class-members: ["error", "never"]*/
-class Foo{
+class Bar{
x;
bar(){}
@@ -118,9 +129,15 @@ class Foo{
baz(){}
}
+```
+:::
+
+::: correct
+
+```js
/* eslint lines-between-class-members: ["error", "never"]*/
-class Foo{
+class Bar{
x;
bar(){}
baz(){}
@@ -129,6 +146,146 @@ class Foo{
:::
+Examples of **incorrect** code for this rule with the array of configurations option:
+
+::: incorrect
+
+```js
+// disallows blank lines between methods
+/*eslint lines-between-class-members: [
+ "error",
+ {
+ enforce: [
+ { blankLine: "never", prev: "method", next: "method" }
+ ]
+ },
+]*/
+
+class MyClass {
+ constructor(height, width) {
+ this.height = height;
+ this.width = width;
+ }
+
+ fieldA = 'Field A';
+ #fieldB = 'Field B';
+
+ method1() {}
+
+ get area() {
+ return this.method1();
+ }
+
+ method2() {}
+}
+```
+
+:::
+
+::: incorrect
+
+```js
+// requires blank lines around fields, disallows blank lines between methods
+/*eslint lines-between-class-members: [
+ "error",
+ {
+ enforce: [
+ { blankLine: "always", prev: "*", next: "field" },
+ { blankLine: "always", prev: "field", next: "*" },
+ { blankLine: "never", prev: "method", next: "method" }
+ ]
+ },
+]*/
+
+class MyClass {
+ constructor(height, width) {
+ this.height = height;
+ this.width = width;
+ }
+ fieldA = 'Field A';
+ #fieldB = 'Field B';
+ method1() {}
+
+ get area() {
+ return this.method1();
+ }
+
+ method2() {}
+}
+```
+
+:::
+
+Examples of **correct** code for this rule with the array of configurations option:
+
+::: correct
+
+```js
+// disallows blank lines between methods
+/*eslint lines-between-class-members: [
+ "error",
+ {
+ enforce: [
+ { blankLine: "never", prev: "method", next: "method" }
+ ]
+ },
+]*/
+
+class MyClass {
+ constructor(height, width) {
+ this.height = height;
+ this.width = width;
+ }
+
+ fieldA = 'Field A';
+
+ #fieldB = 'Field B';
+
+ method1() {}
+ get area() {
+ return this.method1();
+ }
+ method2() {}
+}
+```
+
+:::
+
+::: correct
+
+```js
+// requires blank lines around fields, disallows blank lines between methods
+/*eslint lines-between-class-members: [
+ "error",
+ {
+ enforce: [
+ { blankLine: "always", prev: "*", next: "field" },
+ { blankLine: "always", prev: "field", next: "*" },
+ { blankLine: "never", prev: "method", next: "method" }
+ ]
+ },
+]*/
+
+class MyClass {
+ constructor(height, width) {
+ this.height = height;
+ this.width = width;
+ }
+
+ fieldA = 'Field A';
+
+ #fieldB = 'Field B';
+
+ method1() {}
+ get area() {
+ return this.method1();
+ }
+ method2() {}
+}
+```
+
+:::
+
Examples of **correct** code for this rule with the object option:
::: correct
@@ -148,6 +305,40 @@ class Foo{
:::
+::: correct
+
+```js
+/*eslint lines-between-class-members: [
+ "error",
+ {
+ enforce: [
+ { blankLine: "always", prev: "*", next: "method" },
+ { blankLine: "always", prev: "method", next: "*" },
+ { blankLine: "always", prev: "field", next: "field" }
+ ]
+ },
+ { exceptAfterSingleLine: true }
+]*/
+
+class MyClass {
+ constructor(height, width) {
+ this.height = height;
+ this.width = width;
+ }
+
+ fieldA = 'Field A';
+ #fieldB = 'Field B';
+ method1() {}
+ get area() {
+ return this.method1();
+ }
+
+ method2() {}
+}
+```
+
+:::
+
## When Not To Use It
If you don't want to enforce empty lines between class members, you can disable this rule.
diff --git a/docs/src/rules/logical-assignment-operators.md b/docs/src/rules/logical-assignment-operators.md
index 3f9350031f3..1b59cfa70bb 100644
--- a/docs/src/rules/logical-assignment-operators.md
+++ b/docs/src/rules/logical-assignment-operators.md
@@ -10,7 +10,7 @@ For example `a = a || b` can be shortened to `a ||= b`.
## Rule Details
-This rule requires or disallows logical assignment operator shorthand.
+This rule requires or disallows logical assignment operator shorthand.
### Options
@@ -27,6 +27,9 @@ Object option (only available if string option is set to `"always"`):
#### always
+This option checks for expressions that can be shortened using logical assignment operator. For example, `a = a || b` can be shortened to `a ||= b`.
+Expressions with associativity such as `a = a || b || c` are reported as being able to be shortened to `a ||= b || c` unless the evaluation order is explicitly defined using parentheses, such as `a = (a || b) || c`.
+
Examples of **incorrect** code for this rule with the default `"always"` option:
::: incorrect
@@ -40,6 +43,9 @@ a = a ?? b
a || (a = b)
a && (a = b)
a ?? (a = b)
+a = a || b || c
+a = a && b && c
+a = a ?? b ?? c
```
:::
@@ -58,6 +64,8 @@ a = b || c
a || (b = c)
if (a) a = b
+
+a = (a || b) || c
```
:::
@@ -96,10 +104,10 @@ a = a ?? b
This option checks for additional patterns with if statements which could be expressed with the logical assignment operator.
-::: incorrect
-
Examples of **incorrect** code for this rule with the `["always", { enforceForIfStatements: true }]` option:
+::: incorrect
+
```js
/*eslint logical-assignment-operators: ["error", "always", { enforceForIfStatements: true }]*/
diff --git a/docs/src/rules/max-len.md b/docs/src/rules/max-len.md
index be883a8ca0e..7f7a0d006c0 100644
--- a/docs/src/rules/max-len.md
+++ b/docs/src/rules/max-len.md
@@ -9,6 +9,7 @@ related_rules:
- max-statements
---
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Very long lines of code in any language can be difficult to read. In order to aid in readability and maintainability many coders have developed a convention to limit lines of code to X number of characters (traditionally 80 characters).
@@ -22,7 +23,7 @@ This rule enforces a maximum line length to increase code readability and mainta
## Options
-This rule has a number or object option:
+This rule can have up to two numbers as positional arguments (for `code` and `tabWidth` options), followed by an object option (provided positional arguments have priority):
* `"code"` (default `80`) enforces a maximum line length
* `"tabWidth"` (default `4`) specifies the character width for tab characters
@@ -69,30 +70,36 @@ var foo = {
Examples of **incorrect** code for this rule with the default `{ "tabWidth": 4 }` option:
+
+
::: incorrect
```js
/*eslint max-len: ["error", { "code": 80, "tabWidth": 4 }]*/
-\t \t var foo = { "bar": "This is a bar.", "baz": { "qux": "This is a qux" } };
+ var foo = { "bar": "This is a bar.", "baz": { "qux": "This is a qux" } };
```
:::
+
Examples of **correct** code for this rule with the default `{ "tabWidth": 4 }` option:
+
+
::: correct
```js
/*eslint max-len: ["error", { "code": 80, "tabWidth": 4 }]*/
-\t \t var foo = {
-\t \t \t \t "bar": "This is a bar.",
-\t \t \t \t "baz": { "qux": "This is a qux" }
-\t \t };
+ var foo = {
+ "bar": "This is a bar.",
+ "baz": { "qux": "This is a qux" }
+ };
```
:::
+
### comments
@@ -203,7 +210,8 @@ Examples of **correct** code for this rule with the `ignorePattern` option:
::: correct
```js
-/*eslint max-len: ["error", { "ignorePattern": "^\\s*var\\s.+=\\s*require\\s*\\(" }]*/
+/*eslint max-len:
+["error", { "ignorePattern": "^\\s*var\\s.+=\\s*require\\s*\\(" }]*/
var dep = require('really/really/really/really/really/really/really/really/long/module');
```
diff --git a/docs/src/rules/max-params.md b/docs/src/rules/max-params.md
index 681723fb811..b0a55e9009f 100644
--- a/docs/src/rules/max-params.md
+++ b/docs/src/rules/max-params.md
@@ -42,11 +42,11 @@ Examples of **incorrect** code for this rule with the default `{ "max": 3 }` opt
/*eslint max-params: ["error", 3]*/
/*eslint-env es6*/
-function foo (bar, baz, qux, qxx) {
+function foo1 (bar, baz, qux, qxx) {
doSomething();
}
-let foo = (bar, baz, qux, qxx) => {
+let foo2 = (bar, baz, qux, qxx) => {
doSomething();
};
```
@@ -61,11 +61,11 @@ Examples of **correct** code for this rule with the default `{ "max": 3 }` optio
/*eslint max-params: ["error", 3]*/
/*eslint-env es6*/
-function foo (bar, baz, qux) {
+function foo1 (bar, baz, qux) {
doSomething();
}
-let foo = (bar, baz, qux) => {
+let foo2 = (bar, baz, qux) => {
doSomething();
};
```
diff --git a/docs/src/rules/max-statements-per-line.md b/docs/src/rules/max-statements-per-line.md
index 238ade787e8..1a579add6fb 100644
--- a/docs/src/rules/max-statements-per-line.md
+++ b/docs/src/rules/max-statements-per-line.md
@@ -11,6 +11,7 @@ related_rules:
- max-statements
---
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
A line of code containing too many statements can be difficult to read. Code is generally read from the top down, especially when scanning, so limiting the number of statements allowed on a single line can be very beneficial for readability and maintainability.
@@ -40,7 +41,7 @@ if (condition) { bar = 1; }
for (var i = 0; i < length; ++i) { bar = 1; }
switch (discriminant) { default: break; }
function foo() { bar = 1; }
-var foo = function foo() { bar = 1; };
+var qux = function qux() { bar = 1; };
(function foo() { bar = 1; })();
```
@@ -58,7 +59,7 @@ if (condition) bar = 1;
for (var i = 0; i < length; ++i);
switch (discriminant) { default: }
function foo() { }
-var foo = function foo() { };
+var qux = function qux() { };
(function foo() { })();
```
@@ -76,7 +77,7 @@ if (condition) { bar = 1; } else { baz = 2; }
for (var i = 0; i < length; ++i) { bar = 1; baz = 2; }
switch (discriminant) { case 'test': break; default: break; }
function foo() { bar = 1; baz = 2; }
-var foo = function foo() { bar = 1; };
+var qux = function qux() { bar = 1; baz = 2; };
(function foo() { bar = 1; baz = 2; })();
```
@@ -94,7 +95,7 @@ if (condition) bar = 1; if (condition) baz = 2;
for (var i = 0; i < length; ++i) { bar = 1; }
switch (discriminant) { default: break; }
function foo() { bar = 1; }
-var foo = function foo() { bar = 1; };
+var qux = function qux() { bar = 1; };
(function foo() { var bar = 1; })();
```
diff --git a/docs/src/rules/max-statements.md b/docs/src/rules/max-statements.md
index d41f7f2de4d..5bdc7e8c2f4 100644
--- a/docs/src/rules/max-statements.md
+++ b/docs/src/rules/max-statements.md
@@ -63,7 +63,7 @@ function foo() {
var foo11 = 11; // Too many.
}
-let foo = () => {
+let bar = () => {
var foo1 = 1;
var foo2 = 2;
var foo3 = 3;
@@ -99,17 +99,18 @@ function foo() {
var foo7 = 7;
var foo8 = 8;
var foo9 = 9;
- var foo10 = 10;
- return function () {
+ return function () { // 10
// The number of statements in the inner function does not count toward the
// statement maximum.
+ var bar;
+ var baz;
return 42;
};
}
-let foo = () => {
+let bar = () => {
var foo1 = 1;
var foo2 = 2;
var foo3 = 3;
@@ -119,12 +120,13 @@ let foo = () => {
var foo7 = 7;
var foo8 = 8;
var foo9 = 9;
- var foo10 = 10;
- return function () {
+ return function () { // 10
// The number of statements in the inner function does not count toward the
// statement maximum.
+ var bar;
+ var baz;
return 42;
};
}
diff --git a/docs/src/rules/multiline-ternary.md b/docs/src/rules/multiline-ternary.md
index 7e4688d637a..12ed8e176b5 100644
--- a/docs/src/rules/multiline-ternary.md
+++ b/docs/src/rules/multiline-ternary.md
@@ -5,7 +5,7 @@ related_rules:
- operator-linebreak
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
JavaScript allows operands of ternary expressions to be separated by newlines, which can improve the readability of your program.
@@ -18,9 +18,14 @@ var foo = bar > baz ? value1 : value2;
The above can be rewritten as the following to improve readability and more clearly delineate the operands:
```js
+
var foo = bar > baz ?
value1 :
value2;
+
+var foo = bar > baz
+ ? value1
+ : value2;
```
## Rule Details
@@ -74,6 +79,12 @@ foo > bar ?
value1 :
value2) :
value3;
+
+foo > bar
+ ? (baz > qux
+ ? value1
+ : value2)
+ : value3;
```
:::
@@ -126,6 +137,12 @@ foo > bar &&
bar > baz ?
value1 :
value2;
+
+foo > bar
+ ? baz > qux
+ ? value1
+ : value2
+ : value3;
```
:::
diff --git a/docs/src/rules/new-parens.md b/docs/src/rules/new-parens.md
index 08292e8a978..0f652c2eb34 100644
--- a/docs/src/rules/new-parens.md
+++ b/docs/src/rules/new-parens.md
@@ -3,7 +3,7 @@ title: new-parens
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
JavaScript allows the omission of parentheses when invoking a function via the `new` keyword and the constructor has no arguments. However, some coders believe that omitting the parentheses is inconsistent with the rest of the language and thus makes code less clear.
diff --git a/docs/src/rules/newline-after-var.md b/docs/src/rules/newline-after-var.md
index db11e25471c..9d6dc216ece 100644
--- a/docs/src/rules/newline-after-var.md
+++ b/docs/src/rules/newline-after-var.md
@@ -52,9 +52,9 @@ var greet = "hello,",
name = "world";
console.log(greet, name);
-let greet = "hello,",
- name = "world";
-console.log(greet, name);
+let hello = "hello,",
+ world = "world";
+console.log(hello, world);
var greet = "hello,";
const NAME = "world";
@@ -81,10 +81,10 @@ var greet = "hello,",
console.log(greet, name);
-let greet = "hello,",
- name = "world";
+let hello = "hello,",
+ world = "world";
-console.log(greet, name);
+console.log(hello, world);
var greet = "hello,";
const NAME = "world";
@@ -115,10 +115,10 @@ var greet = "hello,",
console.log(greet, name);
-let greet = "hello,",
- name = "world";
+let hello = "hello,",
+ world = "world";
-console.log(greet, name);
+console.log(hello, world);
var greet = "hello,";
const NAME = "world";
@@ -146,9 +146,9 @@ var greet = "hello,",
name = "world";
console.log(greet, name);
-let greet = "hello,",
- name = "world";
-console.log(greet, name);
+let hello = "hello,",
+ world = "world";
+console.log(hello, world);
var greet = "hello,";
const NAME = "world";
diff --git a/docs/src/rules/newline-before-return.md b/docs/src/rules/newline-before-return.md
index 41fb7726c7b..1980f5bd346 100644
--- a/docs/src/rules/newline-before-return.md
+++ b/docs/src/rules/newline-before-return.md
@@ -49,14 +49,14 @@ Examples of **incorrect** code for this rule:
```js
/*eslint newline-before-return: "error"*/
-function foo(bar) {
+function foo1(bar) {
if (!bar) {
return;
}
return bar;
}
-function foo(bar) {
+function foo2(bar) {
if (!bar) {
return;
}
@@ -75,30 +75,30 @@ Examples of **correct** code for this rule:
```js
/*eslint newline-before-return: "error"*/
-function foo() {
+function foo1() {
return;
}
-function foo() {
+function foo2() {
return;
}
-function foo(bar) {
+function foo3(bar) {
if (!bar) return;
}
-function foo(bar) {
+function foo4(bar) {
if (!bar) { return };
}
-function foo(bar) {
+function foo5(bar) {
if (!bar) {
return;
}
}
-function foo(bar) {
+function foo6(bar) {
if (!bar) {
return;
}
@@ -106,14 +106,14 @@ function foo(bar) {
return bar;
}
-function foo(bar) {
+function foo7(bar) {
if (!bar) {
return;
}
}
-function foo() {
+function foo8() {
// comment
return;
diff --git a/docs/src/rules/newline-per-chained-call.md b/docs/src/rules/newline-per-chained-call.md
index e2482cdbc20..34eddb31338 100644
--- a/docs/src/rules/newline-per-chained-call.md
+++ b/docs/src/rules/newline-per-chained-call.md
@@ -3,7 +3,7 @@ title: newline-per-chained-call
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Chained method calls on a single line without line breaks are harder to read, so some developers place a newline character after each method call in the chain to make it more readable and easy to maintain.
diff --git a/docs/src/rules/no-array-constructor.md b/docs/src/rules/no-array-constructor.md
index 7a22df0e68a..aa7c079baa2 100644
--- a/docs/src/rules/no-array-constructor.md
+++ b/docs/src/rules/no-array-constructor.md
@@ -2,8 +2,8 @@
title: no-array-constructor
rule_type: suggestion
related_rules:
-- no-new-object
- no-new-wrappers
+- no-object-constructor
---
diff --git a/docs/src/rules/no-catch-shadow.md b/docs/src/rules/no-catch-shadow.md
index 23328e42fef..c875f9fe4a9 100644
--- a/docs/src/rules/no-catch-shadow.md
+++ b/docs/src/rules/no-catch-shadow.md
@@ -39,13 +39,13 @@ try {
}
-function err() {
+function error() {
// ...
};
try {
throw "problem";
-} catch (err) {
+} catch (error) {
}
```
@@ -67,7 +67,7 @@ try {
}
-function err() {
+function error() {
// ...
};
diff --git a/docs/src/rules/no-cond-assign.md b/docs/src/rules/no-cond-assign.md
index b72c2d09c67..7cb02d7c32f 100644
--- a/docs/src/rules/no-cond-assign.md
+++ b/docs/src/rules/no-cond-assign.md
@@ -45,8 +45,7 @@ if (x = 0) {
}
// Practical example that is similar to an error
-function setHeight(someNode) {
- "use strict";
+var setHeight = function (someNode) {
do {
someNode.height = "100px";
} while (someNode = someNode.parentNode);
@@ -69,16 +68,14 @@ if (x === 0) {
}
// Practical example that wraps the assignment in parentheses
-function setHeight(someNode) {
- "use strict";
+var setHeight = function (someNode) {
do {
someNode.height = "100px";
} while ((someNode = someNode.parentNode));
}
// Practical example that wraps the assignment and tests for 'null'
-function setHeight(someNode) {
- "use strict";
+var setHeight = function (someNode) {
do {
someNode.height = "100px";
} while ((someNode = someNode.parentNode) !== null);
@@ -103,24 +100,21 @@ if (x = 0) {
}
// Practical example that is similar to an error
-function setHeight(someNode) {
- "use strict";
+var setHeight = function (someNode) {
do {
someNode.height = "100px";
} while (someNode = someNode.parentNode);
}
// Practical example that wraps the assignment in parentheses
-function setHeight(someNode) {
- "use strict";
+var setHeight = function (someNode) {
do {
someNode.height = "100px";
} while ((someNode = someNode.parentNode));
}
// Practical example that wraps the assignment and tests for 'null'
-function setHeight(someNode) {
- "use strict";
+var setHeight = function (someNode) {
do {
someNode.height = "100px";
} while ((someNode = someNode.parentNode) !== null);
diff --git a/docs/src/rules/no-confusing-arrow.md b/docs/src/rules/no-confusing-arrow.md
index 546e30321a8..b51140aa75f 100644
--- a/docs/src/rules/no-confusing-arrow.md
+++ b/docs/src/rules/no-confusing-arrow.md
@@ -6,7 +6,7 @@ related_rules:
- arrow-parens
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Arrow functions (`=>`) are similar in syntax to some comparison operators (`>`, `<`, `<=`, and `>=`). This rule warns against using the arrow function syntax in places where it could be confused with a comparison operator.
diff --git a/docs/src/rules/no-const-assign.md b/docs/src/rules/no-const-assign.md
index f9f0ed172bb..ca62132a757 100644
--- a/docs/src/rules/no-const-assign.md
+++ b/docs/src/rules/no-const-assign.md
@@ -1,6 +1,7 @@
---
title: no-const-assign
rule_type: problem
+handled_by_typescript: true
---
diff --git a/docs/src/rules/no-continue.md b/docs/src/rules/no-continue.md
index af5cd8f98f1..fd0df533dec 100644
--- a/docs/src/rules/no-continue.md
+++ b/docs/src/rules/no-continue.md
@@ -15,7 +15,7 @@ for(i = 0; i < 10; i++) {
continue;
}
- a += i;
+ sum += i;
}
```
@@ -38,7 +38,7 @@ for(i = 0; i < 10; i++) {
continue;
}
- a += i;
+ sum += i;
}
```
@@ -57,7 +57,7 @@ labeledLoop: for(i = 0; i < 10; i++) {
continue labeledLoop;
}
- a += i;
+ sum += i;
}
```
@@ -75,7 +75,7 @@ var sum = 0,
for(i = 0; i < 10; i++) {
if(i < 5) {
- a += i;
+ sum += i;
}
}
```
diff --git a/docs/src/rules/no-delete-var.md b/docs/src/rules/no-delete-var.md
index 65a5aa1614d..f66aec5879f 100644
--- a/docs/src/rules/no-delete-var.md
+++ b/docs/src/rules/no-delete-var.md
@@ -15,7 +15,7 @@ If ESLint parses code in strict mode, the parser (instead of this rule) reports
Examples of **incorrect** code for this rule:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint no-delete-var: "error"*/
diff --git a/docs/src/rules/no-dupe-args.md b/docs/src/rules/no-dupe-args.md
index 79f791c4666..748d52c2e21 100644
--- a/docs/src/rules/no-dupe-args.md
+++ b/docs/src/rules/no-dupe-args.md
@@ -1,6 +1,7 @@
---
title: no-dupe-args
rule_type: problem
+handled_by_typescript: true
---
@@ -15,7 +16,7 @@ If ESLint parses code in strict mode, the parser (instead of this rule) reports
Examples of **incorrect** code for this rule:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint no-dupe-args: "error"*/
@@ -33,7 +34,7 @@ var bar = function (a, b, a) {
Examples of **correct** code for this rule:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint no-dupe-args: "error"*/
diff --git a/docs/src/rules/no-dupe-class-members.md b/docs/src/rules/no-dupe-class-members.md
index aaea4ce324e..6565c9f15ee 100644
--- a/docs/src/rules/no-dupe-class-members.md
+++ b/docs/src/rules/no-dupe-class-members.md
@@ -1,6 +1,7 @@
---
title: no-dupe-class-members
rule_type: problem
+handled_by_typescript: true
---
@@ -33,27 +34,27 @@ Examples of **incorrect** code for this rule:
```js
/*eslint no-dupe-class-members: "error"*/
-class Foo {
+class A {
bar() { }
bar() { }
}
-class Foo {
+class B {
bar() { }
get bar() { }
}
-class Foo {
+class C {
bar;
bar;
}
-class Foo {
+class D {
bar;
bar() { }
}
-class Foo {
+class E {
static bar() { }
static bar() { }
}
@@ -68,27 +69,27 @@ Examples of **correct** code for this rule:
```js
/*eslint no-dupe-class-members: "error"*/
-class Foo {
+class A {
bar() { }
qux() { }
}
-class Foo {
+class B {
get bar() { }
set bar(value) { }
}
-class Foo {
+class C {
bar;
qux;
}
-class Foo {
+class D {
bar;
qux() { }
}
-class Foo {
+class E {
static bar() { }
bar() { }
}
@@ -101,5 +102,3 @@ class Foo {
This rule should not be used in ES3/5 environments.
In ES2015 (ES6) or later, if you don't want to be notified about duplicate names in class members, you can safely disable this rule.
-
-It's also safe to disable this rule when using TypeScript because TypeScript's compiler already checks for duplicate function implementations.
diff --git a/docs/src/rules/no-dupe-keys.md b/docs/src/rules/no-dupe-keys.md
index 75fc9491fb6..1527bf8f1ec 100644
--- a/docs/src/rules/no-dupe-keys.md
+++ b/docs/src/rules/no-dupe-keys.md
@@ -1,6 +1,7 @@
---
title: no-dupe-keys
rule_type: problem
+handled_by_typescript: true
---
diff --git a/docs/src/rules/no-else-return.md b/docs/src/rules/no-else-return.md
index 917c4455390..79b4fe5fb01 100644
--- a/docs/src/rules/no-else-return.md
+++ b/docs/src/rules/no-else-return.md
@@ -37,7 +37,7 @@ Examples of **incorrect** code for this rule:
```js
/*eslint no-else-return: "error"*/
-function foo() {
+function foo1() {
if (x) {
return y;
} else {
@@ -45,7 +45,7 @@ function foo() {
}
}
-function foo() {
+function foo2() {
if (x) {
return y;
} else if (z) {
@@ -55,7 +55,7 @@ function foo() {
}
}
-function foo() {
+function foo3() {
if (x) {
return y;
} else {
@@ -65,7 +65,7 @@ function foo() {
return t;
}
-function foo() {
+function foo4() {
if (error) {
return 'It failed';
} else {
@@ -76,7 +76,7 @@ function foo() {
}
// Two warnings for nested occurrences
-function foo() {
+function foo5() {
if (x) {
if (y) {
return y;
@@ -98,7 +98,7 @@ Examples of **correct** code for this rule:
```js
/*eslint no-else-return: "error"*/
-function foo() {
+function foo1() {
if (x) {
return y;
}
@@ -106,7 +106,7 @@ function foo() {
return z;
}
-function foo() {
+function foo2() {
if (x) {
return y;
} else if (z) {
@@ -116,7 +116,7 @@ function foo() {
}
}
-function foo() {
+function foo3() {
if (x) {
if (z) {
return y;
@@ -126,7 +126,7 @@ function foo() {
}
}
-function foo() {
+function foo4() {
if (error) {
return 'It failed';
} else if (loading) {
diff --git a/docs/src/rules/no-empty-character-class.md b/docs/src/rules/no-empty-character-class.md
index 984f1e18330..2ab0679b394 100644
--- a/docs/src/rules/no-empty-character-class.md
+++ b/docs/src/rules/no-empty-character-class.md
@@ -24,6 +24,23 @@ Examples of **incorrect** code for this rule:
/^abc[]/.test("abcdefg"); // false
"abcdefg".match(/^abc[]/); // null
+
+/^abc[[]]/v.test("abcdefg"); // false
+"abcdefg".match(/^abc[[]]/v); // null
+
+/^abc[[]--[x]]/v.test("abcdefg"); // false
+"abcdefg".match(/^abc[[]--[x]]/v); // null
+
+/^abc[[d]&&[]]/v.test("abcdefg"); // false
+"abcdefg".match(/^abc[[d]&&[]]/v); // null
+
+const regex = /^abc[d[]]/v;
+regex.test("abcdefg"); // true, the nested `[]` has no effect
+"abcdefg".match(regex); // ["abcd"]
+regex.test("abcefg"); // false, the nested `[]` has no effect
+"abcefg".match(regex); // null
+regex.test("abc"); // false, the nested `[]` has no effect
+"abc".match(regex); // null
```
:::
@@ -40,6 +57,9 @@ Examples of **correct** code for this rule:
/^abc[a-z]/.test("abcdefg"); // true
"abcdefg".match(/^abc[a-z]/); // ["abcd"]
+
+/^abc[^]/.test("abcdefg"); // true
+"abcdefg".match(/^abc[^]/); // ["abcd"]
```
:::
diff --git a/docs/src/rules/no-empty-function.md b/docs/src/rules/no-empty-function.md
index a9be4bb05d5..68aa2ad887b 100644
--- a/docs/src/rules/no-empty-function.md
+++ b/docs/src/rules/no-empty-function.md
@@ -38,13 +38,13 @@ Examples of **incorrect** code for this rule:
function foo() {}
-var foo = function() {};
+var bar = function() {};
-var foo = () => {};
+var bar = () => {};
-function* foo() {}
+function* baz() {}
-var foo = function*() {};
+var bar = function*() {};
var obj = {
foo: function() {},
@@ -95,19 +95,19 @@ function foo() {
// do nothing.
}
-var foo = function() {
+var baz = function() {
// any clear comments.
};
-var foo = () => {
+var baz = () => {
bar();
};
-function* foo() {
+function* foobar() {
// do nothing.
}
-var foo = function*() {
+var baz = function*() {
// do nothing.
};
@@ -205,7 +205,7 @@ Examples of **correct** code for the `{ "allow": ["functions"] }` option:
function foo() {}
-var foo = function() {};
+var bar = function() {};
var obj = {
foo: function() {}
@@ -241,7 +241,7 @@ Examples of **correct** code for the `{ "allow": ["generatorFunctions"] }` optio
function* foo() {}
-var foo = function*() {};
+var bar = function*() {};
var obj = {
foo: function*() {}
diff --git a/docs/src/rules/no-empty-pattern.md b/docs/src/rules/no-empty-pattern.md
index d510f592d55..92000add185 100644
--- a/docs/src/rules/no-empty-pattern.md
+++ b/docs/src/rules/no-empty-pattern.md
@@ -44,9 +44,9 @@ var [] = foo;
var {a: {}} = foo;
var {a: []} = foo;
function foo({}) {}
-function foo([]) {}
-function foo({a: {}}) {}
-function foo({a: []}) {}
+function bar([]) {}
+function baz({a: {}}) {}
+function qux({a: []}) {}
```
:::
@@ -61,7 +61,51 @@ Examples of **correct** code for this rule:
var {a = {}} = foo;
var {a = []} = foo;
function foo({a = {}}) {}
-function foo({a = []}) {}
+function bar({a = []}) {}
+```
+
+:::
+
+## Options
+
+This rule has an object option for exceptions:
+
+### allowObjectPatternsAsParameters
+
+Set to `false` by default. Setting this option to `true` allows empty object patterns as function parameters.
+
+**Note:** This rule doesn't allow empty array patterns as function parameters.
+
+Examples of **incorrect** code for this rule with the `{"allowObjectPatternsAsParameters": true}` option:
+
+::: incorrect
+
+```js
+/*eslint no-empty-pattern: ["error", { "allowObjectPatternsAsParameters": true }]*/
+
+function foo({a: {}}) {}
+var bar = function({a: {}}) {};
+var bar = ({a: {}}) => {};
+var bar = ({} = bar) => {};
+var bar = ({} = { bar: 1 }) => {};
+
+function baz([]) {}
+```
+
+:::
+
+Examples of **correct** code for this rule with the `{"allowObjectPatternsAsParameters": true}` option:
+
+::: correct
+
+```js
+/*eslint no-empty-pattern: ["error", { "allowObjectPatternsAsParameters": true }]*/
+
+function foo({}) {}
+var bar = function({}) {};
+var bar = ({}) => {};
+
+function baz({} = {}) {}
```
:::
diff --git a/docs/src/rules/no-empty-static-block.md b/docs/src/rules/no-empty-static-block.md
index 283a4e21b77..1825e5c7832 100644
--- a/docs/src/rules/no-empty-static-block.md
+++ b/docs/src/rules/no-empty-static-block.md
@@ -42,7 +42,7 @@ class Foo {
}
}
-class Foo {
+class Bar {
static {
// comment
}
diff --git a/docs/src/rules/no-extra-boolean-cast.md b/docs/src/rules/no-extra-boolean-cast.md
index e45509890f5..2408d617174 100644
--- a/docs/src/rules/no-extra-boolean-cast.md
+++ b/docs/src/rules/no-extra-boolean-cast.md
@@ -75,7 +75,7 @@ Examples of **correct** code for this rule:
var foo = !!bar;
var foo = Boolean(bar);
-function foo() {
+function qux() {
return !!bar;
}
diff --git a/docs/src/rules/no-extra-parens.md b/docs/src/rules/no-extra-parens.md
index 9b1b8df0725..ab5655b2b0a 100644
--- a/docs/src/rules/no-extra-parens.md
+++ b/docs/src/rules/no-extra-parens.md
@@ -9,7 +9,7 @@ further_reading:
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
This rule restricts the use of parentheses to only where they are necessary.
@@ -21,6 +21,25 @@ This rule always ignores extra parentheses around the following:
* immediately-invoked function expressions (also known as IIFEs) such as `var x = (function () {})();` and `var x = (function () {}());` to avoid conflicts with the [wrap-iife](wrap-iife) rule
* arrow function arguments to avoid conflicts with the [arrow-parens](arrow-parens) rule
+Problems reported by this rule can be fixed automatically, except when removing the parentheses would create a new directive, because that could change the semantics of the code.
+For example, the following script prints `object` to the console, but if the parentheses around `"use strict"` were removed, it would print `undefined` instead.
+
+```js
+
+
+("use strict");
+
+function test() {
+ console.log(typeof this);
+}
+
+test();
+```
+
+In this case, the rule will not try to remove the parentheses around `"use strict"` but will still report them as a problem.
+
## Options
This rule has a string option:
@@ -33,6 +52,7 @@ This rule has an object option for exceptions to the `"all"` option:
* `"conditionalAssign": false` allows extra parentheses around assignments in conditional test expressions
* `"returnAssign": false` allows extra parentheses around assignments in `return` statements
* `"nestedBinaryExpressions": false` allows extra parentheses in nested binary expressions
+* `"ternaryOperandBinaryExpressions": false` allows extra parentheses around binary expressions that are operands of ternary `?:`
* `"ignoreJSX": "none|all|multi-line|single-line"` allows extra parentheses around no/all/multi-line/single-line JSX components. Defaults to `none`.
* `"enforceForArrowConditionals": false` allows extra parentheses around ternary expressions which are the body of an arrow function
* `"enforceForSequenceExpressions": false` allows extra parentheses around sequence expressions
@@ -63,8 +83,6 @@ typeof (a);
(Object.prototype.toString.call());
-(function(){} ? a() : b());
-
class A {
[(x)] = 1;
}
@@ -139,11 +157,11 @@ Examples of **correct** code for this rule with the `"all"` and `{ "returnAssign
```js
/* eslint no-extra-parens: ["error", "all", { "returnAssign": false }] */
-function a(b) {
+function a1(b) {
return (b = 1);
}
-function a(b) {
+function a2(b) {
return b ? (c = d) : (c = e);
}
@@ -170,16 +188,36 @@ x = (a * b) / c;
:::
-### ignoreJSX
+### ternaryOperandBinaryExpressions
-Examples of **correct** code for this rule with the `all` and `{ "ignoreJSX": "all" }` options:
+Examples of **correct** code for this rule with the `"all"` and `{ "ternaryOperandBinaryExpressions": false }` options:
::: correct
```js
+/* eslint no-extra-parens: ["error", "all", { "ternaryOperandBinaryExpressions": false }] */
+
+(a && b) ? foo : bar;
+
+(a - b > a) ? foo : bar;
+
+foo ? (bar || baz) : qux;
+
+foo ? bar : (baz || qux);
+```
+
+:::
+
+### ignoreJSX
+
+Examples of **correct** code for this rule with the `all` and `{ "ignoreJSX": "all" }` options:
+
+::: correct { "ecmaFeatures": { "jsx": true } }
+
+```jsx
/* eslint no-extra-parens: ["error", "all", { ignoreJSX: "all" }] */
-const Component = ()
-const Component = (
+const ThisComponent = ()
+const ThatComponent = (
@@ -190,28 +228,28 @@ const Component = (
Examples of **incorrect** code for this rule with the `all` and `{ "ignoreJSX": "multi-line" }` options:
-::: incorrect
+::: incorrect { "ecmaFeatures": { "jsx": true } }
-```js
+```jsx
/* eslint no-extra-parens: ["error", "all", { ignoreJSX: "multi-line" }] */
-const Component = ()
-const Component = (
)
```
:::
diff --git a/docs/src/rules/no-extra-semi.md b/docs/src/rules/no-extra-semi.md
index 9755c5167fd..1e9ee0a611f 100644
--- a/docs/src/rules/no-extra-semi.md
+++ b/docs/src/rules/no-extra-semi.md
@@ -6,9 +6,7 @@ related_rules:
- semi-spacing
---
-
-
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Typing mistakes and misunderstandings about where semicolons are required can lead to semicolons that are unnecessary. While not technically an error, extra semicolons can cause confusion when reading code.
@@ -16,6 +14,8 @@ Typing mistakes and misunderstandings about where semicolons are required can le
This rule disallows unnecessary semicolons.
+Problems reported by this rule can be fixed automatically, except when removing a semicolon would cause a following statement to become a directive such as `"use strict"`.
+
Examples of **incorrect** code for this rule:
::: incorrect
diff --git a/docs/src/rules/no-floating-decimal.md b/docs/src/rules/no-floating-decimal.md
index 30a43317d55..8ba7a799150 100644
--- a/docs/src/rules/no-floating-decimal.md
+++ b/docs/src/rules/no-floating-decimal.md
@@ -3,7 +3,7 @@ title: no-floating-decimal
rule_type: suggestion
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Float values in JavaScript contain a decimal point, and there is no requirement that the decimal point be preceded or followed by a number. For example, the following are all valid JavaScript numbers:
diff --git a/docs/src/rules/no-func-assign.md b/docs/src/rules/no-func-assign.md
index ffbcb46c6b8..6346e2b8064 100644
--- a/docs/src/rules/no-func-assign.md
+++ b/docs/src/rules/no-func-assign.md
@@ -1,6 +1,7 @@
---
title: no-func-assign
rule_type: problem
+handled_by_typescript: true
---
@@ -26,8 +27,8 @@ Examples of **incorrect** code for this rule:
function foo() {}
foo = bar;
-function foo() {
- foo = bar;
+function baz() {
+ baz = bar;
}
var a = function hello() {
@@ -60,12 +61,12 @@ Examples of **correct** code for this rule:
var foo = function () {}
foo = bar;
-function foo(foo) { // `foo` is shadowed.
- foo = bar;
+function baz(baz) { // `baz` is shadowed.
+ baz = bar;
}
-function foo() {
- var foo = bar; // `foo` is shadowed.
+function qux() {
+ var qux = bar; // `qux` is shadowed.
}
```
diff --git a/docs/src/rules/no-implicit-globals.md b/docs/src/rules/no-implicit-globals.md
index 1e5c5fad723..6a479a0a765 100644
--- a/docs/src/rules/no-implicit-globals.md
+++ b/docs/src/rules/no-implicit-globals.md
@@ -44,7 +44,7 @@ This rule disallows `var` and `function` declarations at the top-level script sc
Examples of **incorrect** code for this rule:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint no-implicit-globals: "error"*/
@@ -58,7 +58,7 @@ function bar() {}
Examples of **correct** code for this rule:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint no-implicit-globals: "error"*/
@@ -79,7 +79,7 @@ window.bar = function() {};
Examples of **correct** code for this rule with `"parserOptions": { "sourceType": "module" }` in the ESLint configuration:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint no-implicit-globals: "error"*/
@@ -100,7 +100,7 @@ This does not apply to ES modules since the module code is implicitly in `strict
Examples of **incorrect** code for this rule:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint no-implicit-globals: "error"*/
@@ -127,7 +127,7 @@ or in a `/*global */` comment.
Examples of **incorrect** code for this rule:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint no-implicit-globals: "error"*/
@@ -155,7 +155,7 @@ If the variable is intended to be local to the script, wrap the code with a bloc
Examples of **correct** code for this rule with `"lexicalBindings"` option set to `false` (default):
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint no-implicit-globals: ["error", {"lexicalBindings": false}]*/
@@ -171,7 +171,7 @@ class Bar {}
Examples of **incorrect** code for this rule with `"lexicalBindings"` option set to `true`:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint no-implicit-globals: ["error", {"lexicalBindings": true}]*/
@@ -187,7 +187,7 @@ class Bar {}
Examples of **correct** code for this rule with `"lexicalBindings"` option set to `true`:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint no-implicit-globals: ["error", {"lexicalBindings": true}]*/
@@ -221,7 +221,7 @@ Even the `typeof` check is not safe from TDZ reference exceptions.
Examples of **incorrect** code for this rule with `"lexicalBindings"` option set to `true`:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint no-implicit-globals: ["error", {"lexicalBindings": true}]*/
@@ -239,7 +239,7 @@ const MyGlobalFunction = (function() {
Examples of **correct** code for this rule with `"lexicalBindings"` option set to `true`:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint no-implicit-globals: ["error", {"lexicalBindings": true}]*/
@@ -261,7 +261,7 @@ You can use `/* exported variableName */` block comments in the same way as in [
Examples of **correct** code for `/* exported variableName */` operation:
-::: correct
+::: correct { "sourceType": "script" }
```js
/* exported global_var */
diff --git a/docs/src/rules/no-import-assign.md b/docs/src/rules/no-import-assign.md
index ca4b912de81..b0a7432bb80 100644
--- a/docs/src/rules/no-import-assign.md
+++ b/docs/src/rules/no-import-assign.md
@@ -1,6 +1,8 @@
---
title: no-import-assign
rule_type: problem
+handled_by_typescript: true
+extra_typescript_info: Note that the compiler will not catch the `Object.assign()` case. Thus, if you use `Object.assign()` in your codebase, this rule will still provide some value.
---
diff --git a/docs/src/rules/no-inline-comments.md b/docs/src/rules/no-inline-comments.md
index 687464e91c7..d6a32db4e47 100644
--- a/docs/src/rules/no-inline-comments.md
+++ b/docs/src/rules/no-inline-comments.md
@@ -54,9 +54,9 @@ Comments inside the curly braces in JSX are allowed to be on the same line as th
Examples of **incorrect** code for this rule:
-::: incorrect
+::: incorrect { "ecmaFeatures": { "jsx": true } }
-```js
+```jsx
/*eslint no-inline-comments: "error"*/
var foo =
{ /* On the same line with other code */ }
Some heading
;
@@ -74,9 +74,9 @@ var bar = (
Examples of **correct** code for this rule:
-::: correct
+::: correct { "ecmaFeatures": { "jsx": true } }
-```js
+```jsx
/*eslint no-inline-comments: "error"*/
var foo = (
diff --git a/docs/src/rules/no-inner-declarations.md b/docs/src/rules/no-inner-declarations.md
index 8828fabe7d2..a81a45a61fa 100644
--- a/docs/src/rules/no-inner-declarations.md
+++ b/docs/src/rules/no-inner-declarations.md
@@ -74,7 +74,7 @@ This rule has a string option:
Examples of **incorrect** code for this rule with the default `"functions"` option:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint no-inner-declarations: "error"*/
@@ -104,7 +104,7 @@ class C {
Examples of **correct** code for this rule with the default `"functions"` option:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint no-inner-declarations: "error"*/
@@ -139,7 +139,7 @@ if (foo) var a;
Examples of **incorrect** code for this rule with the `"both"` option:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint no-inner-declarations: ["error", "both"]*/
@@ -171,7 +171,7 @@ class C {
Examples of **correct** code for this rule with the `"both"` option:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint no-inner-declarations: ["error", "both"]*/
diff --git a/docs/src/rules/no-invalid-this.md b/docs/src/rules/no-invalid-this.md
index f3aa6ed763e..454fdbd7185 100644
--- a/docs/src/rules/no-invalid-this.md
+++ b/docs/src/rules/no-invalid-this.md
@@ -1,6 +1,8 @@
---
title: no-invalid-this
rule_type: suggestion
+handled_by_typescript: true
+extra_typescript_info: Note that, technically, TypeScript will only catch this if you have the `strict` or `noImplicitThis` flags enabled. These are enabled in most TypeScript projects, since they are considered to be best practice.
---
@@ -47,7 +49,7 @@ With `"parserOptions": { "sourceType": "module" }` in the ESLint configuration,
Examples of **incorrect** code for this rule in strict mode:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint no-invalid-this: "error"*/
@@ -95,7 +97,7 @@ foo.forEach(function() {
Examples of **correct** code for this rule in strict mode:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint no-invalid-this: "error"*/
@@ -112,7 +114,7 @@ function Foo() {
baz(() => this);
}
-class Foo {
+class Bar {
constructor() {
// OK, this is in a constructor.
this.a = 0;
@@ -180,7 +182,7 @@ Foo.prototype.foo = function foo() {
this.a = 0;
};
-class Foo {
+class Baz {
// OK, this is in a class field initializer.
a = this.b;
@@ -241,7 +243,7 @@ Set `"capIsConstructor"` to `false` if you want those functions to be treated as
Examples of **incorrect** code for this rule with `"capIsConstructor"` option set to `false`:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint no-invalid-this: ["error", { "capIsConstructor": false }]*/
@@ -269,7 +271,7 @@ Baz = function() {
Examples of **correct** code for this rule with `"capIsConstructor"` option set to `false`:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint no-invalid-this: ["error", { "capIsConstructor": false }]*/
diff --git a/docs/src/rules/no-irregular-whitespace.md b/docs/src/rules/no-irregular-whitespace.md
index 258733b0827..4118defe631 100644
--- a/docs/src/rules/no-irregular-whitespace.md
+++ b/docs/src/rules/no-irregular-whitespace.md
@@ -4,6 +4,7 @@ rule_type: problem
further_reading:
- https://es5.github.io/#x7.2
- https://web.archive.org/web/20200414142829/http://timelessrepo.com/json-isnt-a-javascript-subset
+- https://codepoints.net/U+1680
---
@@ -16,11 +17,17 @@ A simple fix for this problem could be to rewrite the offending line from scratc
Known issues these spaces cause:
+* Ogham Space Mark
+ * Is a valid token separator, but is rendered as a visible glyph in most typefaces, which may be misleading in source code.
+* Mongolian Vowel Separator
+ * Is no longer considered a space separator since Unicode 6.3. It will result in a syntax error in current parsers when used in place of a regular token separator.
+* Line Separator and Paragraph Separator
+ * These have always been valid whitespace characters and line terminators, but were considered illegal in string literals prior to ECMAScript 2019.
* Zero Width Space
- * Is NOT considered a separator for tokens and is often parsed as an `Unexpected token ILLEGAL`
- * Is NOT shown in modern browsers making code repository software expected to resolve the visualization
-* Line Separator
- * Is NOT a valid character within JSON which would cause parse errors
+ * Is NOT considered a separator for tokens and is often parsed as an `Unexpected token ILLEGAL`.
+ * Is NOT shown in modern browsers making code repository software expected to resolve the visualization.
+
+In JSON, none of the characters listed as irregular whitespace by this rule may appear outside of a string.
## Rule Details
@@ -63,6 +70,7 @@ This rule has an object option for exceptions:
* `"skipComments": true` allows any whitespace characters in comments
* `"skipRegExps": true` allows any whitespace characters in regular expression literals
* `"skipTemplates": true` allows any whitespace characters in template literals
+* `"skipJSXText": true` allows any whitespace characters in JSX text
### skipStrings
@@ -73,31 +81,31 @@ Examples of **incorrect** code for this rule with the default `{ "skipStrings":
```js
/*eslint no-irregular-whitespace: "error"*/
-function thing() /**/{
+var thing = function() /**/{
return 'test';
}
-function thing( /**/){
+var thing = function( /**/){
return 'test';
}
-function thing /**/(){
+var thing = function /**/(){
return 'test';
}
-function thing/**/(){
+var thing = function /**/(){
return 'test';
}
-function thing() {
+var thing = function() {
return 'test'; /**/
}
-function thing() {
+var thing = function() {
return 'test'; /**/
}
-function thing() {
+var thing = function() {
// Description : some descriptive text
}
@@ -105,12 +113,12 @@ function thing() {
Description : some descriptive text
*/
-function thing() {
+var thing = function() {
return / regexp/;
}
/*eslint-env es6*/
-function thing() {
+var thing = function() {
return `template string`;
}
```
@@ -124,15 +132,15 @@ Examples of **correct** code for this rule with the default `{ "skipStrings": tr
```js
/*eslint no-irregular-whitespace: "error"*/
-function thing() {
+var thing = function() {
return ' thing';
}
-function thing() {
+var thing = function() {
return 'thing';
}
-function thing() {
+var thing = function() {
return 'th ing';
}
```
@@ -192,6 +200,23 @@ function thing() {
:::
+### skipJSXText
+
+Examples of additional **correct** code for this rule with the `{ "skipJSXText": true }` option:
+
+::: correct { "ecmaFeatures": { "jsx": true } }
+
+```jsx
+/*eslint no-irregular-whitespace: ["error", { "skipJSXText": true }]*/
+/*eslint-env es6*/
+
+function Thing() {
+ return
text in JSX
; // before `JSX`
+}
+```
+
+:::
+
## When Not To Use It
If you decide that you wish to use whitespace other than tabs and spaces outside of strings in your application.
diff --git a/docs/src/rules/no-loss-of-precision.md b/docs/src/rules/no-loss-of-precision.md
index 1bec49266dc..6674b15da6b 100644
--- a/docs/src/rules/no-loss-of-precision.md
+++ b/docs/src/rules/no-loss-of-precision.md
@@ -18,12 +18,12 @@ Examples of **incorrect** code for this rule:
```js
/*eslint no-loss-of-precision: "error"*/
-const x = 9007199254740993
-const x = 5123000000000000000000000000001
-const x = 1230000000000000000000000.0
-const x = .1230000000000000000000000
-const x = 0X20000000000001
-const x = 0X2_000000000_0001;
+const a = 9007199254740993
+const b = 5123000000000000000000000000001
+const c = 1230000000000000000000000.0
+const d = .1230000000000000000000000
+const e = 0X20000000000001
+const f = 0X2_000000000_0001;
```
:::
@@ -35,13 +35,13 @@ Examples of **correct** code for this rule:
```js
/*eslint no-loss-of-precision: "error"*/
-const x = 12345
-const x = 123.456
-const x = 123e34
-const x = 12300000000000000000000000
-const x = 0x1FFFFFFFFFFFFF
-const x = 9007199254740991
-const x = 9007_1992547409_91
+const a = 12345
+const b = 123.456
+const c = 123e34
+const d = 12300000000000000000000000
+const e = 0x1FFFFFFFFFFFFF
+const f = 9007199254740991
+const g = 9007_1992547409_91
```
:::
diff --git a/docs/src/rules/no-misleading-character-class.md b/docs/src/rules/no-misleading-character-class.md
index a0d9e6d0693..792760f2641 100644
--- a/docs/src/rules/no-misleading-character-class.md
+++ b/docs/src/rules/no-misleading-character-class.md
@@ -17,36 +17,36 @@ This rule reports the regular expressions which include multiple code point char
The combining characters are characters which belong to one of `Mc`, `Me`, and `Mn` [Unicode general categories](http://www.unicode.org/L2/L1999/UnicodeData.html#General%20Category).
```js
-/^[Á]$/u.test("Á") //→ false
-/^[❇️]$/u.test("❇️") //→ false
+/^[Á]$/u.test("Á"); //→ false
+/^[❇️]$/u.test("❇️"); //→ false
```
**A character with Emoji modifiers:**
```js
-/^[👶🏻]$/u.test("👶🏻") //→ false
-/^[👶🏽]$/u.test("👶🏽") //→ false
+/^[👶🏻]$/u.test("👶🏻"); //→ false
+/^[👶🏽]$/u.test("👶🏽"); //→ false
```
**A pair of regional indicator symbols:**
```js
-/^[🇯🇵]$/u.test("🇯🇵") //→ false
+/^[🇯🇵]$/u.test("🇯🇵"); //→ false
```
**Characters that ZWJ joins:**
```js
-/^[👨👩👦]$/u.test("👨👩👦") //→ false
+/^[👨👩👦]$/u.test("👨👩👦"); //→ false
```
**A surrogate pair without Unicode flag:**
```js
-/^[👍]$/.test("👍") //→ false
+/^[👍]$/.test("👍"); //→ false
// Surrogate pair is OK if with u flag.
-/^[👍]$/u.test("👍") //→ true
+/^[👍]$/u.test("👍"); //→ true
```
## Rule Details
@@ -60,12 +60,12 @@ Examples of **incorrect** code for this rule:
```js
/*eslint no-misleading-character-class: error */
-/^[Á]$/u
-/^[❇️]$/u
-/^[👶🏻]$/u
-/^[🇯🇵]$/u
-/^[👨👩👦]$/u
-/^[👍]$/
+/^[Á]$/u;
+/^[❇️]$/u;
+/^[👶🏻]$/u;
+/^[🇯🇵]$/u;
+/^[👨👩👦]$/u;
+/^[👍]$/;
```
:::
@@ -77,8 +77,9 @@ Examples of **correct** code for this rule:
```js
/*eslint no-misleading-character-class: error */
-/^[abc]$/
-/^[👍]$/u
+/^[abc]$/;
+/^[👍]$/u;
+/^[\q{👶🏻}]$/v;
```
:::
diff --git a/docs/src/rules/no-mixed-operators.md b/docs/src/rules/no-mixed-operators.md
index 07ab40f7fe5..d8bb908540b 100644
--- a/docs/src/rules/no-mixed-operators.md
+++ b/docs/src/rules/no-mixed-operators.md
@@ -5,6 +5,7 @@ related_rules:
- no-extra-parens
---
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Enclosing complex expressions by parentheses clarifies the developer's intention, which makes the code more readable.
This rule warns when different operators are used consecutively without parentheses in an expression.
diff --git a/docs/src/rules/no-mixed-spaces-and-tabs.md b/docs/src/rules/no-mixed-spaces-and-tabs.md
index 29f82e2191f..b13223cf585 100644
--- a/docs/src/rules/no-mixed-spaces-and-tabs.md
+++ b/docs/src/rules/no-mixed-spaces-and-tabs.md
@@ -5,7 +5,7 @@ further_reading:
- https://www.emacswiki.org/emacs/SmartTabs
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Most code conventions require either tabs or spaces be used for indentation. As such, it's usually an error if a single line of code is indented with both tabs and spaces.
@@ -15,42 +15,42 @@ This rule disallows mixed spaces and tabs for indentation.
Examples of **incorrect** code for this rule:
+
+
::: incorrect
```js
/*eslint no-mixed-spaces-and-tabs: "error"*/
function add(x, y) {
-// --->..return x + y;
-
- return x + y;
+ return x + y;
}
function main() {
-// --->var x = 5,
-// --->....y = 7;
-
- var x = 5,
- y = 7;
+ var x = 5,
+ y = 7;
}
```
:::
+
Examples of **correct** code for this rule:
+
+
::: correct
```js
/*eslint no-mixed-spaces-and-tabs: "error"*/
function add(x, y) {
-// --->return x + y;
- return x + y;
+ return x + y;
}
```
:::
+
## Options
@@ -62,18 +62,18 @@ This rule has a string option.
Examples of **correct** code for this rule with the `"smart-tabs"` option:
+
+
::: correct
```js
/*eslint no-mixed-spaces-and-tabs: ["error", "smart-tabs"]*/
function main() {
-// --->var x = 5,
-// --->....y = 7;
-
- var x = 5,
- y = 7;
+ var x = 5,
+ y = 7;
}
```
:::
+
diff --git a/docs/src/rules/no-multi-assign.md b/docs/src/rules/no-multi-assign.md
index 7cc4581d152..f4f4c2fce5a 100644
--- a/docs/src/rules/no-multi-assign.md
+++ b/docs/src/rules/no-multi-assign.md
@@ -31,9 +31,9 @@ var a = b = c = 5;
const foo = bar = "baz";
-let a =
- b =
- c;
+let d =
+ e =
+ f;
class Foo {
a = b = 10;
@@ -58,8 +58,8 @@ var c = 5;
const foo = "baz";
const bar = "baz";
-let a = c;
-let b = c;
+let d = c;
+let e = c;
class Foo {
a = 10;
diff --git a/docs/src/rules/no-multi-spaces.md b/docs/src/rules/no-multi-spaces.md
index b23a97964fe..ca7e873ab0b 100644
--- a/docs/src/rules/no-multi-spaces.md
+++ b/docs/src/rules/no-multi-spaces.md
@@ -11,7 +11,7 @@ related_rules:
- space-return-throw-case
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Multiple spaces in a row that are not used for indentation are typically mistakes. For example:
diff --git a/docs/src/rules/no-multiple-empty-lines.md b/docs/src/rules/no-multiple-empty-lines.md
index 5457c54c1f0..f6ccb3050e5 100644
--- a/docs/src/rules/no-multiple-empty-lines.md
+++ b/docs/src/rules/no-multiple-empty-lines.md
@@ -3,7 +3,7 @@ title: no-multiple-empty-lines
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Some developers prefer to have multiple blank lines removed, while others feel that it helps improve readability. Whitespace is useful for separating logical sections of code, but excess whitespace takes up more of the screen.
@@ -59,13 +59,13 @@ Examples of **incorrect** code for this rule with the `{ max: 2, maxEOF: 0 }` op
::: incorrect
```js
-/*eslint no-multiple-empty-lines: ["error", { "max": 2, "maxEOF": 0 }]*/
-
-var foo = 5;
-
-
-var bar = 3;
-
+/*eslint no-multiple-empty-lines: ["error", { "max": 2, "maxEOF": 0 }]*/⏎
+⏎
+var foo = 5;⏎
+⏎
+⏎
+var bar = 3;⏎
+⏎
```
@@ -75,36 +75,19 @@ Examples of **correct** code for this rule with the `{ max: 2, maxEOF: 0 }` opti
::: correct
-```js
-/*eslint no-multiple-empty-lines: ["error", { "max": 2, "maxEOF": 0 }]*/
-
-var foo = 5;
-
-
-var bar = 3;
-```
-
-:::
-
-**Note**: Although this ensures zero empty lines at the EOF, most editors will still show one empty line at the end if the file ends with a line break, as illustrated below. There is no empty line at the end of a file after the last `\n`, although editors may show an additional line. A true additional line would be represented by `\n\n`.
-
-**Incorrect**:
-
-::: incorrect
-
```js
/*eslint no-multiple-empty-lines: ["error", { "max": 2, "maxEOF": 0 }]*/⏎
⏎
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`.
+
**Correct**:
::: correct
@@ -128,6 +111,8 @@ Examples of **incorrect** code for this rule with the `{ max: 2, maxBOF: 1 }` op
::: incorrect
```js
+
+
/*eslint no-multiple-empty-lines: ["error", { "max": 2, "maxBOF": 1 }]*/
@@ -149,6 +134,20 @@ Examples of **correct** code for this rule with the `{ max: 2, maxBOF: 1 }` opti
var foo = 5;
+var bar = 3;
+```
+
+:::
+
+::: correct
+
+```js
+
+/*eslint no-multiple-empty-lines: ["error", { "max": 2, "maxBOF": 1}]*/
+
+var foo = 5;
+
+
var bar = 3;
```
diff --git a/docs/src/rules/no-new-object.md b/docs/src/rules/no-new-object.md
index 41b02f87406..bdd57741198 100644
--- a/docs/src/rules/no-new-object.md
+++ b/docs/src/rules/no-new-object.md
@@ -6,6 +6,7 @@ related_rules:
- no-new-wrappers
---
+This rule was **deprecated** in ESLint v8.50.0 and replaced by the [no-object-constructor](no-object-constructor) rule. The new rule flags more situations where object literal syntax can be used, and it does not report a problem when the `Object` constructor is invoked with an argument.
The `Object` constructor is used to create new generic objects in JavaScript, such as:
@@ -25,7 +26,7 @@ While there are no performance differences between the two approaches, the byte
## Rule Details
-This rule disallows `Object` constructors.
+This rule disallows calling the `Object` constructor with `new`.
Examples of **incorrect** code for this rule:
@@ -37,6 +38,8 @@ Examples of **incorrect** code for this rule:
var myObject = new Object();
new Object();
+
+var foo = new Object("foo");
```
:::
@@ -54,10 +57,12 @@ var myObject = {};
var Object = function Object() {};
new Object();
+
+var foo = Object("foo");
```
:::
## When Not To Use It
-If you wish to allow the use of the `Object` constructor, you can safely turn this rule off.
+If you wish to allow the use of the `Object` constructor with `new`, you can safely turn this rule off.
diff --git a/docs/src/rules/no-new-symbol.md b/docs/src/rules/no-new-symbol.md
index 44c34a4eef0..d557811f30c 100644
--- a/docs/src/rules/no-new-symbol.md
+++ b/docs/src/rules/no-new-symbol.md
@@ -1,6 +1,7 @@
---
title: no-new-symbol
rule_type: problem
+handled_by_typescript: true
further_reading:
- https://www.ecma-international.org/ecma-262/6.0/#sec-symbol-objects
---
diff --git a/docs/src/rules/no-new-wrappers.md b/docs/src/rules/no-new-wrappers.md
index da0a860ca15..fe11e3af7ee 100644
--- a/docs/src/rules/no-new-wrappers.md
+++ b/docs/src/rules/no-new-wrappers.md
@@ -3,7 +3,7 @@ title: no-new-wrappers
rule_type: suggestion
related_rules:
- no-array-constructor
-- no-new-object
+- no-object-constructor
further_reading:
- https://www.inkling.com/read/javascript-definitive-guide-david-flanagan-6th/chapter-3/wrapper-objects
---
diff --git a/docs/src/rules/no-nonoctal-decimal-escape.md b/docs/src/rules/no-nonoctal-decimal-escape.md
index 6ca59fdf79a..43d0282635a 100644
--- a/docs/src/rules/no-nonoctal-decimal-escape.md
+++ b/docs/src/rules/no-nonoctal-decimal-escape.md
@@ -30,7 +30,7 @@ This rule disallows `\8` and `\9` escape sequences in string literals.
Examples of **incorrect** code for this rule:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint no-nonoctal-decimal-escape: "error"*/
@@ -52,7 +52,7 @@ var quux = "\0\8";
Examples of **correct** code for this rule:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint no-nonoctal-decimal-escape: "error"*/
diff --git a/docs/src/rules/no-obj-calls.md b/docs/src/rules/no-obj-calls.md
index 8d72e4ce277..2fde92e5cbf 100644
--- a/docs/src/rules/no-obj-calls.md
+++ b/docs/src/rules/no-obj-calls.md
@@ -1,6 +1,7 @@
---
title: no-obj-calls
rule_type: problem
+handled_by_typescript: true
further_reading:
- https://es5.github.io/#x15.8
---
diff --git a/docs/src/rules/no-object-constructor.md b/docs/src/rules/no-object-constructor.md
new file mode 100644
index 00000000000..e2f7015cdab
--- /dev/null
+++ b/docs/src/rules/no-object-constructor.md
@@ -0,0 +1,50 @@
+---
+title: no-object-constructor
+rule_type: suggestion
+related_rules:
+- no-array-constructor
+- no-new-wrappers
+---
+
+Use of the `Object` constructor to construct a new empty object is generally discouraged in favor of object literal notation because of conciseness and because the `Object` global may be redefined.
+The exception is when the `Object` constructor is used to intentionally wrap a specified value which is passed as an argument.
+
+## Rule Details
+
+This rule disallows calling the `Object` constructor without an argument.
+
+Examples of **incorrect** code for this rule:
+
+:::incorrect
+
+```js
+/*eslint no-object-constructor: "error"*/
+
+Object();
+
+new Object();
+```
+
+:::
+
+Examples of **correct** code for this rule:
+
+:::correct
+
+```js
+/*eslint no-object-constructor: "error"*/
+
+Object("foo");
+
+const obj = { a: 1, b: 2 };
+
+const isObject = value => value === Object(value);
+
+const createObject = Object => new Object();
+```
+
+:::
+
+## When Not To Use It
+
+If you wish to allow the use of the `Object` constructor, you can safely turn this rule off.
diff --git a/docs/src/rules/no-octal-escape.md b/docs/src/rules/no-octal-escape.md
index ae8fecdce32..755bc56bc84 100644
--- a/docs/src/rules/no-octal-escape.md
+++ b/docs/src/rules/no-octal-escape.md
@@ -18,7 +18,7 @@ If ESLint parses code in strict mode, the parser (instead of this rule) reports
Examples of **incorrect** code for this rule:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint no-octal-escape: "error"*/
@@ -30,7 +30,7 @@ var foo = "Copyright \251";
Examples of **correct** code for this rule:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint no-octal-escape: "error"*/
diff --git a/docs/src/rules/no-octal.md b/docs/src/rules/no-octal.md
index 6a8fa7709a4..c40d9a977b2 100644
--- a/docs/src/rules/no-octal.md
+++ b/docs/src/rules/no-octal.md
@@ -21,7 +21,7 @@ If ESLint parses code in strict mode, the parser (instead of this rule) reports
Examples of **incorrect** code for this rule:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint no-octal: "error"*/
@@ -34,7 +34,7 @@ var result = 5 + 07;
Examples of **correct** code for this rule:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint no-octal: "error"*/
diff --git a/docs/src/rules/no-param-reassign.md b/docs/src/rules/no-param-reassign.md
index 2bddf7b9921..a3cc558cf45 100644
--- a/docs/src/rules/no-param-reassign.md
+++ b/docs/src/rules/no-param-reassign.md
@@ -6,7 +6,7 @@ further_reading:
---
-Assignment to variables declared as function parameters can be misleading and lead to confusing behavior, as modifying function parameters will also mutate the `arguments` object. Often, assignment to function parameters is unintended and indicative of a mistake or programmer error.
+Assignment to variables declared as function parameters can be misleading and lead to confusing behavior, as modifying function parameters will also mutate the `arguments` object when not in strict mode (see [When Not To Use It](#when-not-to-use-it) below). Often, assignment to function parameters is unintended and indicative of a mistake or programmer error.
This rule can be also configured to fail when function parameters are modified. Side effects on parameters can cause counter-intuitive execution flow and make errors difficult to track down.
@@ -21,19 +21,19 @@ Examples of **incorrect** code for this rule:
```js
/*eslint no-param-reassign: "error"*/
-function foo(bar) {
+var foo = function(bar) {
bar = 13;
}
-function foo(bar) {
+var foo = function(bar) {
bar++;
}
-function foo(bar) {
+var foo = function(bar) {
for (bar in baz) {}
}
-function foo(bar) {
+var foo = function(bar) {
for (bar of baz) {}
}
```
@@ -47,7 +47,7 @@ Examples of **correct** code for this rule:
```js
/*eslint no-param-reassign: "error"*/
-function foo(bar) {
+var foo = function(bar) {
var baz = bar;
}
```
@@ -67,23 +67,23 @@ Examples of **correct** code for the default `{ "props": false }` option:
```js
/*eslint no-param-reassign: ["error", { "props": false }]*/
-function foo(bar) {
+var foo = function(bar) {
bar.prop = "value";
}
-function foo(bar) {
+var foo = function(bar) {
delete bar.aaa;
}
-function foo(bar) {
+var foo = function(bar) {
bar.aaa++;
}
-function foo(bar) {
+var foo = function(bar) {
for (bar.aaa in baz) {}
}
-function foo(bar) {
+var foo = function(bar) {
for (bar.aaa of baz) {}
}
```
@@ -97,23 +97,23 @@ Examples of **incorrect** code for the `{ "props": true }` option:
```js
/*eslint no-param-reassign: ["error", { "props": true }]*/
-function foo(bar) {
+var foo = function(bar) {
bar.prop = "value";
}
-function foo(bar) {
+var foo = function(bar) {
delete bar.aaa;
}
-function foo(bar) {
+var foo = function(bar) {
bar.aaa++;
}
-function foo(bar) {
+var foo = function(bar) {
for (bar.aaa in baz) {}
}
-function foo(bar) {
+var foo = function(bar) {
for (bar.aaa of baz) {}
}
```
@@ -127,23 +127,23 @@ Examples of **correct** code for the `{ "props": true }` option with `"ignorePro
```js
/*eslint no-param-reassign: ["error", { "props": true, "ignorePropertyModificationsFor": ["bar"] }]*/
-function foo(bar) {
+var foo = function(bar) {
bar.prop = "value";
}
-function foo(bar) {
+var foo = function(bar) {
delete bar.aaa;
}
-function foo(bar) {
+var foo = function(bar) {
bar.aaa++;
}
-function foo(bar) {
+var foo = function(bar) {
for (bar.aaa in baz) {}
}
-function foo(bar) {
+var foo = function(bar) {
for (bar.aaa of baz) {}
}
```
@@ -157,23 +157,23 @@ Examples of **correct** code for the `{ "props": true }` option with `"ignorePro
```js
/*eslint no-param-reassign: ["error", { "props": true, "ignorePropertyModificationsForRegex": ["^bar"] }]*/
-function foo(barVar) {
+var foo = function(barVar) {
barVar.prop = "value";
}
-function foo(barrito) {
+var foo = function(barrito) {
delete barrito.aaa;
}
-function foo(bar_) {
+var foo = function(bar_) {
bar_.aaa++;
}
-function foo(barBaz) {
+var foo = function(barBaz) {
for (barBaz.aaa in baz) {}
}
-function foo(barBaz) {
+var foo = function(barBaz) {
for (barBaz.aaa of baz) {}
}
```
@@ -183,3 +183,5 @@ function foo(barBaz) {
## When Not To Use It
If you want to allow assignment to function parameters, then you can safely disable this rule.
+
+Strict mode code doesn't sync indices of the arguments object with each parameter binding. Therefore, this rule is not necessary to protect against arguments object mutation in ESM modules or other strict mode functions.
diff --git a/docs/src/rules/no-plusplus.md b/docs/src/rules/no-plusplus.md
index ba787f1968f..4f564b51cb9 100644
--- a/docs/src/rules/no-plusplus.md
+++ b/docs/src/rules/no-plusplus.md
@@ -43,7 +43,7 @@ var bar = 42;
bar--;
for (i = 0; i < l; i++) {
- return;
+ doSomething(i);
}
```
@@ -63,7 +63,7 @@ var bar = 42;
bar -= 1;
for (i = 0; i < l; i += 1) {
- return;
+ doSomething(i);
}
```
diff --git a/docs/src/rules/no-promise-executor-return.md b/docs/src/rules/no-promise-executor-return.md
index 28891624822..f163d9ebf0b 100644
--- a/docs/src/rules/no-promise-executor-return.md
+++ b/docs/src/rules/no-promise-executor-return.md
@@ -38,6 +38,7 @@ Examples of **incorrect** code for this rule:
```js
/*eslint no-promise-executor-return: "error"*/
+/*eslint-env es6*/
new Promise((resolve, reject) => {
if (someCondition) {
@@ -63,6 +64,8 @@ new Promise((resolve, reject) => getSomething((err, data) => {
new Promise(() => {
return 1;
});
+
+new Promise(r => r(1));
```
:::
@@ -73,7 +76,9 @@ Examples of **correct** code for this rule:
```js
/*eslint no-promise-executor-return: "error"*/
+/*eslint-env es6*/
+// Turn return inline into two lines
new Promise((resolve, reject) => {
if (someCondition) {
resolve(defaultResult);
@@ -88,6 +93,7 @@ new Promise((resolve, reject) => {
});
});
+// Add curly braces
new Promise((resolve, reject) => {
getSomething((err, data) => {
if (err) {
@@ -98,7 +104,51 @@ new Promise((resolve, reject) => {
});
});
+new Promise(r => { r(1) });
+// or just use Promise.resolve
Promise.resolve(1);
```
:::
+
+## Options
+
+This rule takes one option, an object, with the following properties:
+
+* `allowVoid`: If set to `true` (`false` by default), this rule will allow returning void values.
+
+### allowVoid
+
+Examples of **correct** code for this rule with the `{ "allowVoid": true }` option:
+
+::: correct
+
+```js
+/*eslint no-promise-executor-return: ["error", { allowVoid: true }]*/
+/*eslint-env es6*/
+
+new Promise((resolve, reject) => {
+ if (someCondition) {
+ return void resolve(defaultResult);
+ }
+ getSomething((err, result) => {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(result);
+ }
+ });
+});
+
+new Promise((resolve, reject) => void getSomething((err, data) => {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(data);
+ }
+}));
+
+new Promise(r => void r(1));
+```
+
+:::
diff --git a/docs/src/rules/no-redeclare.md b/docs/src/rules/no-redeclare.md
index e66f0570fc6..009ba889fcc 100644
--- a/docs/src/rules/no-redeclare.md
+++ b/docs/src/rules/no-redeclare.md
@@ -1,6 +1,8 @@
---
title: no-redeclare
rule_type: suggestion
+handled_by_typescript: true
+extra_typescript_info: Note that while TypeScript will catch `let` redeclares and `const` redeclares, it will not catch `var` redeclares. Thus, if you use the legacy `var` keyword in your TypeScript codebase, this rule will still provide some value.
related_rules:
- no-shadow
---
diff --git a/docs/src/rules/no-restricted-exports.md b/docs/src/rules/no-restricted-exports.md
index 2f83643f9cb..44672021a10 100644
--- a/docs/src/rules/no-restricted-exports.md
+++ b/docs/src/rules/no-restricted-exports.md
@@ -144,7 +144,25 @@ Examples of **incorrect** code for the `"restrictDefaultExports": { "direct": tr
/*eslint no-restricted-exports: ["error", { "restrictDefaultExports": { "direct": true } }]*/
export default foo;
+```
+
+:::
+
+::: incorrect
+
+```js
+/*eslint no-restricted-exports: ["error", { "restrictDefaultExports": { "direct": true } }]*/
+
export default 42;
+```
+
+:::
+
+::: incorrect
+
+```js
+/*eslint no-restricted-exports: ["error", { "restrictDefaultExports": { "direct": true } }]*/
+
export default function foo() {}
```
@@ -176,6 +194,15 @@ Examples of **incorrect** code for the `"restrictDefaultExports": { "defaultFrom
/*eslint no-restricted-exports: ["error", { "restrictDefaultExports": { "defaultFrom": true } }]*/
export { default } from 'foo';
+```
+
+:::
+
+::: incorrect
+
+```js
+/*eslint no-restricted-exports: ["error", { "restrictDefaultExports": { "defaultFrom": true } }]*/
+
export { default as default } from 'foo';
```
diff --git a/docs/src/rules/no-restricted-imports.md b/docs/src/rules/no-restricted-imports.md
index ece0ceb5490..72d97baf3e7 100644
--- a/docs/src/rules/no-restricted-imports.md
+++ b/docs/src/rules/no-restricted-imports.md
@@ -130,7 +130,7 @@ To restrict the use of all Node.js core imports (via a = b
const bar = (a, b, c) => (a = b, c == b)
-function doSomething() {
+function doSomethingMore() {
return foo = bar && foo > 0;
}
```
@@ -69,11 +69,11 @@ function doSomething() {
return foo == bar + 2;
}
-function doSomething() {
+function doSomethingElse() {
return foo === bar + 2;
}
-function doSomething() {
+function doSomethingMore() {
return (foo = bar + 2);
}
@@ -81,7 +81,7 @@ const foo = (a, b) => (a = b)
const bar = (a, b, c) => ((a = b), c == b)
-function doSomething() {
+function doAnotherThing() {
return (foo = bar) && foo > 0;
}
```
@@ -104,11 +104,11 @@ function doSomething() {
return foo = bar + 2;
}
-function doSomething() {
+function doSomethingElse() {
return foo += 2;
}
-function doSomething() {
+function doSomethingMore() {
return (foo = bar + 2);
}
```
@@ -126,7 +126,7 @@ function doSomething() {
return foo == bar + 2;
}
-function doSomething() {
+function doSomethingElse() {
return foo === bar + 2;
}
```
diff --git a/docs/src/rules/no-return-await.md b/docs/src/rules/no-return-await.md
index 8f42100cfff..2ee5a8b3659 100644
--- a/docs/src/rules/no-return-await.md
+++ b/docs/src/rules/no-return-await.md
@@ -2,10 +2,12 @@
title: no-return-await
rule_type: suggestion
further_reading:
+- https://v8.dev/blog/fast-async
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
- https://jakearchibald.com/2017/await-vs-return-vs-return-await/
---
+This rule was **deprecated** in ESLint v8.46.0 with no replacement. The original intent of this rule no longer applies due to the fact JavaScript now handles native `Promises` differently. It can now be slower to remove `await` rather than keeping it. More technical information can be found in [this V8 blog entry](https://v8.dev/blog/fast-async).
Using `return await` inside an `async function` keeps the current function in the call stack until the Promise that is being awaited has resolved, at the cost of an extra microtask before resolving the outer Promise. `return await` can also be used in a try/catch statement to catch errors from another function that returns a Promise.
@@ -36,23 +38,23 @@ Examples of **correct** code for this rule:
```js
/*eslint no-return-await: "error"*/
-async function foo() {
+async function foo1() {
return bar();
}
-async function foo() {
+async function foo2() {
await bar();
return;
}
// This is essentially the same as `return await bar();`, but the rule checks only `await` in `return` statements
-async function foo() {
+async function foo3() {
const x = await bar();
return x;
}
// In this example the `await` is necessary to be able to catch errors thrown from `bar()`
-async function foo() {
+async function foo4() {
try {
return await bar();
} catch (error) {}
diff --git a/docs/src/rules/no-sequences.md b/docs/src/rules/no-sequences.md
index cb2b99c50e1..cbacb46ce26 100644
--- a/docs/src/rules/no-sequences.md
+++ b/docs/src/rules/no-sequences.md
@@ -25,7 +25,7 @@ This rule forbids the use of the comma operator, with the following exceptions:
Examples of **incorrect** code for this rule:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint no-sequences: "error"*/
@@ -51,7 +51,7 @@ with (doSomething(), val) {}
Examples of **correct** code for this rule:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint no-sequences: "error"*/
@@ -87,9 +87,9 @@ Examples of **incorrect** code for arrow functions:
/*eslint no-sequences: "error"*/
const foo = (val) => (console.log('bar'), val);
-const foo = () => ((bar = 123), 10);
+const baz = () => ((bar = 123), 10);
-const foo = () => { return (bar = 123), 10 }
+const qux = () => { return (bar = 123), 10 }
```
:::
@@ -102,9 +102,9 @@ Examples of **correct** code for arrow functions:
/*eslint no-sequences: "error"*/
const foo = (val) => ((console.log('bar'), val));
-const foo = () => (((bar = 123), 10));
+const baz = () => (((bar = 123), 10));
-const foo = () => { return ((bar = 123), 10) }
+const qux = () => { return ((bar = 123), 10) }
```
:::
@@ -119,7 +119,7 @@ This rule takes one option, an object, with the following properties:
Examples of **incorrect** code for this rule with the `{ "allowInParentheses": false }` option:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint no-sequences: ["error", { "allowInParentheses": false }]*/
diff --git a/docs/src/rules/no-setter-return.md b/docs/src/rules/no-setter-return.md
index ceb4558c15a..50353c754da 100644
--- a/docs/src/rules/no-setter-return.md
+++ b/docs/src/rules/no-setter-return.md
@@ -1,6 +1,7 @@
---
title: no-setter-return
rule_type: problem
+handled_by_typescript: true
related_rules:
- getter-return
further_reading:
diff --git a/docs/src/rules/no-shadow-restricted-names.md b/docs/src/rules/no-shadow-restricted-names.md
index 308772edaf3..7a5e1b0403d 100644
--- a/docs/src/rules/no-shadow-restricted-names.md
+++ b/docs/src/rules/no-shadow-restricted-names.md
@@ -22,7 +22,7 @@ Then any code used within the same scope would not get the global `undefined`, b
Examples of **incorrect** code for this rule:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint no-shadow-restricted-names: "error"*/
@@ -40,7 +40,7 @@ try {} catch(eval){}
Examples of **correct** code for this rule:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint no-shadow-restricted-names: "error"*/
diff --git a/docs/src/rules/no-shadow.md b/docs/src/rules/no-shadow.md
index e4a081f0a30..9b9a27ee7d3 100644
--- a/docs/src/rules/no-shadow.md
+++ b/docs/src/rules/no-shadow.md
@@ -36,14 +36,14 @@ function b() {
var a = 10;
}
-var b = function () {
+var c = function () {
var a = 10;
}
-function b(a) {
+function d(a) {
a = 10;
}
-b(a);
+d(a);
if (true) {
let a = 5;
diff --git a/docs/src/rules/no-tabs.md b/docs/src/rules/no-tabs.md
index 316db6203f3..fe999fa3c78 100644
--- a/docs/src/rules/no-tabs.md
+++ b/docs/src/rules/no-tabs.md
@@ -3,6 +3,7 @@ title: no-tabs
rule_type: layout
---
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Some style guides don't allow the use of tab characters at all, including within comments.
@@ -12,26 +13,33 @@ This rule looks for tabs anywhere inside a file: code, comments or anything else
Examples of **incorrect** code for this rule:
+
+
::: incorrect
```js
-var a \t= 2;
+/* eslint no-tabs: "error" */
+
+var a = 2;
/**
-* \t\t it's a test function
+* it's a test function
*/
function test(){}
-var x = 1; // \t test
+var x = 1; // test
```
:::
+
Examples of **correct** code for this rule:
::: correct
```js
+/* eslint no-tabs: "error" */
+
var a = 2;
/**
@@ -54,19 +62,22 @@ This rule has an optional object option with the following properties:
Examples of **correct** code for this rule with the `allowIndentationTabs: true` option:
+
+
::: correct
```js
/* eslint no-tabs: ["error", { allowIndentationTabs: true }] */
function test() {
-\tdoSomething();
+ doSomething();
}
-\t// comment with leading indentation tab
+ // comment with leading indentation tab
```
:::
+
## When Not To Use It
diff --git a/docs/src/rules/no-this-before-super.md b/docs/src/rules/no-this-before-super.md
index f1425b5ed35..a06b5735553 100644
--- a/docs/src/rules/no-this-before-super.md
+++ b/docs/src/rules/no-this-before-super.md
@@ -1,6 +1,7 @@
---
title: no-this-before-super
rule_type: problem
+handled_by_typescript: true
---
@@ -23,28 +24,28 @@ Examples of **incorrect** code for this rule:
/*eslint no-this-before-super: "error"*/
/*eslint-env es6*/
-class A extends B {
+class A1 extends B {
constructor() {
this.a = 0;
super();
}
}
-class A extends B {
+class A2 extends B {
constructor() {
this.foo();
super();
}
}
-class A extends B {
+class A3 extends B {
constructor() {
super.foo();
super();
}
}
-class A extends B {
+class A4 extends B {
constructor() {
super(this.foo());
}
@@ -61,20 +62,20 @@ Examples of **correct** code for this rule:
/*eslint no-this-before-super: "error"*/
/*eslint-env es6*/
-class A {
+class A1 {
constructor() {
this.a = 0; // OK, this class doesn't have an `extends` clause.
}
}
-class A extends B {
+class A2 extends B {
constructor() {
super();
this.a = 0; // OK, this is after `super()`.
}
}
-class A extends B {
+class A3 extends B {
foo() {
this.a = 0; // OK. this is not in a constructor.
}
diff --git a/docs/src/rules/no-throw-literal.md b/docs/src/rules/no-throw-literal.md
index 621bb11a09c..1ab939ff3a0 100644
--- a/docs/src/rules/no-throw-literal.md
+++ b/docs/src/rules/no-throw-literal.md
@@ -84,10 +84,10 @@ throw foo("error");
throw new String("error");
-var foo = {
+var baz = {
bar: "error"
};
-throw foo.bar;
+throw baz.bar;
```
:::
diff --git a/docs/src/rules/no-trailing-spaces.md b/docs/src/rules/no-trailing-spaces.md
index fba4f7df9a2..71c50e41f44 100644
--- a/docs/src/rules/no-trailing-spaces.md
+++ b/docs/src/rules/no-trailing-spaces.md
@@ -3,7 +3,7 @@ title: no-trailing-spaces
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Sometimes in the course of editing files, you can end up with extra whitespace at the end of lines. These whitespace differences can be picked up by source control systems and flagged as diffs, causing frustration for developers. While this extra whitespace causes no functional issues, many code conventions require that trailing spaces be removed before check-in.
@@ -18,9 +18,9 @@ Examples of **incorrect** code for this rule:
```js
/*eslint no-trailing-spaces: "error"*/
-var foo = 0;//•••••
-var baz = 5;//••
-//•••••
+var foo = 0;/* trailing whitespace */
+var baz = 5;/* trailing whitespace */
+/* trailing whitespace */
```
:::
@@ -58,7 +58,8 @@ Examples of **correct** code for this rule with the `{ "skipBlankLines": true }`
var foo = 0;
var baz = 5;
-//•••••
+// ↓ a line with whitespace only ↓
+
```
:::
@@ -72,12 +73,12 @@ Examples of **correct** code for this rule with the `{ "ignoreComments": true }`
```js
/*eslint no-trailing-spaces: ["error", { "ignoreComments": true }]*/
-//foo•
-//•••••
+// ↓ these comments have trailing whitespace →
+//
/**
- *•baz
- *••
- *•bar
+ * baz
+ *
+ * bar
*/
```
diff --git a/docs/src/rules/no-undef.md b/docs/src/rules/no-undef.md
index 0bc7a28279f..72ea516f966 100644
--- a/docs/src/rules/no-undef.md
+++ b/docs/src/rules/no-undef.md
@@ -1,6 +1,7 @@
---
title: no-undef
rule_type: problem
+handled_by_typescript: true
related_rules:
- no-global-assign
- no-redeclare
diff --git a/docs/src/rules/no-undefined.md b/docs/src/rules/no-undefined.md
index 9b0be204b2f..10096533a64 100644
--- a/docs/src/rules/no-undefined.md
+++ b/docs/src/rules/no-undefined.md
@@ -54,7 +54,7 @@ if (foo === undefined) {
// ...
}
-function foo(undefined) {
+function baz(undefined) {
// ...
}
diff --git a/docs/src/rules/no-underscore-dangle.md b/docs/src/rules/no-underscore-dangle.md
index d35aefeb72d..19157dfb55f 100644
--- a/docs/src/rules/no-underscore-dangle.md
+++ b/docs/src/rules/no-underscore-dangle.md
@@ -3,16 +3,17 @@ title: no-underscore-dangle
rule_type: suggestion
---
-
As far as naming conventions for identifiers go, dangling underscores may be the most polarizing in JavaScript. Dangling underscores are underscores at either the beginning or end of an identifier, such as:
```js
var _foo;
```
-There is actually a long history of using dangling underscores to indicate "private" members of objects in JavaScript (though JavaScript doesn't have truly private members, this convention served as a warning). This began with SpiderMonkey adding nonstandard methods such as `__defineGetter__()`. The intent with the underscores was to make it obvious that this method was special in some way. Since that time, using a single underscore prefix has become popular as a way to indicate "private" members of objects.
+There is a long history of marking "private" members with dangling underscores in JavaScript, beginning with SpiderMonkey adding nonstandard methods such as `__defineGetter__()`. Since that time, using a single underscore prefix has become the most popular convention for indicating a member is not part of the public interface of an object.
+
+It is recommended to use the formal [private class features](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields) introduced in ECMAScript 2022 for encapsulating private data and methods rather than relying on naming conventions.
-Whether or not you choose to allow dangling underscores in identifiers is purely a convention and has no effect on performance, readability, or complexity. It's purely a preference.
+Allowing dangling underscores in identifiers is purely a convention and has no effect on performance, readability, or complexity. They do not have the same encapsulation benefits as private class features, even with this rule enabled.
## Rule Details
@@ -44,8 +45,8 @@ var obj = _.contains(items, item);
obj.__proto__ = {};
var file = __filename;
function foo(_bar) {};
-const foo = { onClick(_bar) {} };
-const foo = (_bar) => {};
+const bar = { onClick(_bar) {} };
+const baz = (_bar) => {};
```
:::
@@ -103,8 +104,12 @@ Examples of **correct** code for this rule with the `{ "allowAfterSuper": true }
```js
/*eslint no-underscore-dangle: ["error", { "allowAfterSuper": true }]*/
-var a = super.foo_;
-super._bar();
+class Foo extends Bar {
+ doSomething() {
+ var a = super.foo_;
+ super._bar();
+ }
+}
```
:::
@@ -137,16 +142,16 @@ class Foo {
_bar() {}
}
-class Foo {
+class Bar {
bar_() {}
}
-const o = {
+const o1 = {
_bar() {}
};
-const o = {
- bar_() = {}
+const o2 = {
+ bar_() {}
};
```
@@ -165,19 +170,19 @@ class Foo {
_bar;
}
-class Foo {
+class Bar {
_bar = () => {};
}
-class Foo {
+class Baz {
bar_;
}
-class Foo {
+class Qux {
#_bar;
}
-class Foo {
+class FooBar {
#bar_;
}
```
@@ -194,7 +199,7 @@ Examples of **incorrect** code for this rule with the `{ "allowInArrayDestructur
/*eslint no-underscore-dangle: ["error", { "allowInArrayDestructuring": false }]*/
const [_foo, _bar] = list;
-const [foo_, ..._bar] = list;
+const [foo_, ..._qux] = list;
const [foo, [bar, _baz]] = list;
```
@@ -210,7 +215,7 @@ Examples of **incorrect** code for this rule with the `{ "allowInObjectDestructu
/*eslint no-underscore-dangle: ["error", { "allowInObjectDestructuring": false }]*/
const { foo, bar: _bar } = collection;
-const { foo, bar, _baz } = collection;
+const { qux, xyz, _baz } = collection;
```
:::
@@ -223,7 +228,7 @@ Examples of **correct** code for this rule with the `{ "allowInObjectDestructuri
/*eslint no-underscore-dangle: ["error", { "allowInObjectDestructuring": false }]*/
const { foo, bar, _baz: { a, b } } = collection;
-const { foo, bar, _baz: baz } = collection;
+const { qux, xyz, _baz: baz } = collection;
```
:::
@@ -237,17 +242,17 @@ Examples of **incorrect** code for this rule with the `{ "allowFunctionParams":
```js
/*eslint no-underscore-dangle: ["error", { "allowFunctionParams": false }]*/
-function foo (_bar) {}
-function foo (_bar = 0) {}
-function foo (..._bar) {}
+function foo1 (_bar) {}
+function foo2 (_bar = 0) {}
+function foo3 (..._bar) {}
-const foo = function onClick (_bar) {}
-const foo = function onClick (_bar = 0) {}
-const foo = function onClick (..._bar) {}
+const foo4 = function onClick (_bar) {}
+const foo5 = function onClick (_bar = 0) {}
+const foo6 = function onClick (..._bar) {}
-const foo = (_bar) => {};
-const foo = (_bar = 0) => {};
-const foo = (..._bar) => {};
+const foo7 = (_bar) => {};
+const foo8 = (_bar = 0) => {};
+const foo9 = (..._bar) => {};
```
:::
diff --git a/docs/src/rules/no-unexpected-multiline.md b/docs/src/rules/no-unexpected-multiline.md
index 158a38642a4..b8f3582fb39 100644
--- a/docs/src/rules/no-unexpected-multiline.md
+++ b/docs/src/rules/no-unexpected-multiline.md
@@ -40,11 +40,11 @@ var hello = 'world'
let x = function() {}
`hello`
-let x = function() {}
-x
+let y = function() {}
+y
`hello`
-let x = foo
+let z = foo
/regex/g.test(bar)
```
diff --git a/docs/src/rules/no-unreachable.md b/docs/src/rules/no-unreachable.md
index 4f762084b41..15f77f8173e 100644
--- a/docs/src/rules/no-unreachable.md
+++ b/docs/src/rules/no-unreachable.md
@@ -1,6 +1,7 @@
---
title: no-unreachable
rule_type: problem
+handled_by_typescript: true
---
diff --git a/docs/src/rules/no-unsafe-negation.md b/docs/src/rules/no-unsafe-negation.md
index 522e4ab4d1a..f3b49522316 100644
--- a/docs/src/rules/no-unsafe-negation.md
+++ b/docs/src/rules/no-unsafe-negation.md
@@ -1,6 +1,7 @@
---
title: no-unsafe-negation
rule_type: problem
+handled_by_typescript: true
---
diff --git a/docs/src/rules/no-unsafe-optional-chaining.md b/docs/src/rules/no-unsafe-optional-chaining.md
index e4cc6f83166..57ad6de8780 100644
--- a/docs/src/rules/no-unsafe-optional-chaining.md
+++ b/docs/src/rules/no-unsafe-optional-chaining.md
@@ -32,7 +32,7 @@ This rule aims to detect some cases where the use of optional chaining doesn't p
Examples of **incorrect** code for this rule:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint no-unsafe-optional-chaining: "error"*/
diff --git a/docs/src/rules/no-unused-expressions.md b/docs/src/rules/no-unused-expressions.md
index c67a2a61706..614ea5b11b7 100644
--- a/docs/src/rules/no-unused-expressions.md
+++ b/docs/src/rules/no-unused-expressions.md
@@ -251,7 +251,7 @@ JSX is most-commonly used in the React ecosystem, where it is compiled to `React
Examples of **incorrect** code for the `{ "enforceForJSX": true }` option:
-::: incorrect
+::: incorrect { "ecmaFeatures": { "jsx": true } }
```jsx
/*eslint no-unused-expressions: ["error", { "enforceForJSX": true }]*/
@@ -265,7 +265,7 @@ Examples of **incorrect** code for the `{ "enforceForJSX": true }` option:
Examples of **correct** code for the `{ "enforceForJSX": true }` option:
-::: correct
+::: correct { "ecmaFeatures": { "jsx": true } }
```jsx
/*eslint no-unused-expressions: ["error", { "enforceForJSX": true }]*/
diff --git a/docs/src/rules/no-unused-labels.md b/docs/src/rules/no-unused-labels.md
index 971c5672203..fcbe2e179ab 100644
--- a/docs/src/rules/no-unused-labels.md
+++ b/docs/src/rules/no-unused-labels.md
@@ -30,6 +30,8 @@ Such labels take up space in the code and can lead to confusion by readers.
This rule is aimed at eliminating unused labels.
+Problems reported by this rule can be fixed automatically, except when there are any comments between the label and the following statement, or when removing a label would cause the following statement to become a directive such as `"use strict"`.
+
Examples of **incorrect** code for this rule:
::: incorrect
diff --git a/docs/src/rules/no-unused-private-class-members.md b/docs/src/rules/no-unused-private-class-members.md
index c92a8827be1..ec6951480b5 100644
--- a/docs/src/rules/no-unused-private-class-members.md
+++ b/docs/src/rules/no-unused-private-class-members.md
@@ -20,29 +20,29 @@ Examples of **incorrect** code for this rule:
```js
/*eslint no-unused-private-class-members: "error"*/
-class Foo {
+class A {
#unusedMember = 5;
}
-class Foo {
+class B {
#usedOnlyInWrite = 5;
method() {
this.#usedOnlyInWrite = 42;
}
}
-class Foo {
+class C {
#usedOnlyToUpdateItself = 5;
method() {
this.#usedOnlyToUpdateItself++;
}
}
-class Foo {
+class D {
#unusedMethod() {}
}
-class Foo {
+class E {
get #unusedAccessor() {}
set #unusedAccessor(value) {}
}
@@ -57,14 +57,14 @@ Examples of **correct** code for this rule:
```js
/*eslint no-unused-private-class-members: "error"*/
-class Foo {
+class A {
#usedMember = 42;
method() {
return this.#usedMember;
}
}
-class Foo {
+class B {
#usedMethod() {
return 42;
}
@@ -73,7 +73,7 @@ class Foo {
}
}
-class Foo {
+class C {
get #usedAccessor() {}
set #usedAccessor(value) {}
diff --git a/docs/src/rules/no-useless-constructor.md b/docs/src/rules/no-useless-constructor.md
index cfdae434d72..bf2c4c32984 100644
--- a/docs/src/rules/no-useless-constructor.md
+++ b/docs/src/rules/no-useless-constructor.md
@@ -56,19 +56,19 @@ Examples of **correct** code for this rule:
class A { }
-class A {
+class B {
constructor () {
doSomething();
}
}
-class B extends A {
+class C extends A {
constructor() {
super('foo');
}
}
-class B extends A {
+class D extends A {
constructor() {
super();
doSomething();
diff --git a/docs/src/rules/no-useless-escape.md b/docs/src/rules/no-useless-escape.md
index cd28fda75a8..ff173c229c2 100644
--- a/docs/src/rules/no-useless-escape.md
+++ b/docs/src/rules/no-useless-escape.md
@@ -43,7 +43,7 @@ Examples of **incorrect** code for this rule:
Examples of **correct** code for this rule:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint no-useless-escape: "error"*/
diff --git a/docs/src/rules/no-useless-rename.md b/docs/src/rules/no-useless-rename.md
index d65541c2ad0..6e79e3e7dc0 100644
--- a/docs/src/rules/no-useless-rename.md
+++ b/docs/src/rules/no-useless-rename.md
@@ -60,14 +60,14 @@ Examples of **incorrect** code for this rule by default:
```js
/*eslint no-useless-rename: "error"*/
-import { foo as foo } from "bar";
-import { "foo" as foo } from "bar";
-export { foo as foo };
-export { foo as "foo" };
-export { foo as foo } from "bar";
-export { "foo" as "foo" } from "bar";
-let { foo: foo } = bar;
-let { 'foo': foo } = bar;
+import { foo1 as foo1 } from "bar";
+import { "foo2" as foo2 } from "bar";
+export { foo1 as foo1 };
+export { foo2 as "foo2" };
+export { foo3 as foo3 } from "bar";
+export { "foo4" as "foo4" } from "bar";
+let { foo3: foo3 } = bar;
+let { 'foo4': foo4 } = bar;
function foo({ bar: bar }) {}
({ foo: foo }) => {}
```
@@ -81,23 +81,23 @@ Examples of **correct** code for this rule by default:
```js
/*eslint no-useless-rename: "error"*/
-import * as foo from "foo";
-import { foo } from "bar";
-import { foo as bar } from "baz";
-import { "foo" as bar } from "baz";
+import * as foo1 from "foo";
+import { foo2 } from "bar";
+import { foo as bar1 } from "baz";
+import { "foo" as bar2 } from "baz";
export { foo };
-export { foo as bar };
-export { foo as "bar" };
-export { foo as bar } from "foo";
-export { "foo" as "bar" } from "foo";
+export { foo as bar1 };
+export { foo as "bar2" };
+export { foo as bar3 } from "foo";
+export { "foo" as "bar4" } from "foo";
let { foo } = bar;
let { foo: bar } = baz;
-let { [foo]: foo } = bar;
+let { [qux]: qux } = bar;
-function foo({ bar }) {}
-function foo({ bar: baz }) {}
+function foo3({ bar }) {}
+function foo4({ bar: baz }) {}
({ foo }) => {}
({ foo: bar }) => {}
@@ -124,8 +124,9 @@ Examples of **correct** code for this rule with `{ ignoreExport: true }`:
```js
/*eslint no-useless-rename: ["error", { ignoreExport: true }]*/
+const foo = 1;
export { foo as foo };
-export { foo as foo } from "bar";
+export { bar as bar } from "bar";
```
:::
@@ -138,7 +139,7 @@ Examples of **correct** code for this rule with `{ ignoreDestructuring: true }`:
/*eslint no-useless-rename: ["error", { ignoreDestructuring: true }]*/
let { foo: foo } = bar;
-function foo({ bar: bar }) {}
+function baz({ bar: bar }) {}
({ foo: foo }) => {}
```
diff --git a/docs/src/rules/no-useless-return.md b/docs/src/rules/no-useless-return.md
index 961f0aef5ca..de7045347c2 100644
--- a/docs/src/rules/no-useless-return.md
+++ b/docs/src/rules/no-useless-return.md
@@ -18,14 +18,14 @@ Examples of **incorrect** code for this rule:
```js
/* eslint no-useless-return: "error" */
-function foo() { return; }
+var foo = function() { return; }
-function foo() {
+var foo = function() {
doSomething();
return;
}
-function foo() {
+var foo = function() {
if (condition) {
bar();
return;
@@ -34,7 +34,7 @@ function foo() {
}
}
-function foo() {
+var foo = function() {
switch (bar) {
case 1:
doSomething();
@@ -55,13 +55,13 @@ Examples of **correct** code for this rule:
```js
/* eslint no-useless-return: "error" */
-function foo() { return 5; }
+var foo = function() { return 5; }
-function foo() {
+var foo = function() {
return doSomething();
}
-function foo() {
+var foo = function() {
if (condition) {
bar();
return;
@@ -71,7 +71,7 @@ function foo() {
qux();
}
-function foo() {
+var foo = function() {
switch (bar) {
case 1:
doSomething();
@@ -81,7 +81,7 @@ function foo() {
}
}
-function foo() {
+var foo = function() {
for (const foo of bar) {
return;
}
diff --git a/docs/src/rules/no-whitespace-before-property.md b/docs/src/rules/no-whitespace-before-property.md
index db79ca2a897..a00864d491c 100644
--- a/docs/src/rules/no-whitespace-before-property.md
+++ b/docs/src/rules/no-whitespace-before-property.md
@@ -3,7 +3,7 @@ title: no-whitespace-before-property
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
JavaScript allows whitespace between objects and their properties. However, inconsistent spacing can make code harder to read and can lead to errors.
diff --git a/docs/src/rules/no-with.md b/docs/src/rules/no-with.md
index 4fc3f75841d..c4e880f812d 100644
--- a/docs/src/rules/no-with.md
+++ b/docs/src/rules/no-with.md
@@ -17,7 +17,7 @@ If ESLint parses code in strict mode, the parser (instead of this rule) reports
Examples of **incorrect** code for this rule:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint no-with: "error"*/
@@ -31,7 +31,7 @@ with (point) {
Examples of **correct** code for this rule:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint no-with: "error"*/
diff --git a/docs/src/rules/nonblock-statement-body-position.md b/docs/src/rules/nonblock-statement-body-position.md
index 78d332026e7..1a2aae9122f 100644
--- a/docs/src/rules/nonblock-statement-body-position.md
+++ b/docs/src/rules/nonblock-statement-body-position.md
@@ -5,7 +5,7 @@ further_reading:
- https://jscs-dev.github.io/rule/requireNewlineBeforeSingleStatementsInIf
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
When writing `if`, `else`, `while`, `do-while`, and `for` statements, the body can be a single statement instead of a block. It can be useful to enforce a consistent location for these single statements.
diff --git a/docs/src/rules/object-curly-newline.md b/docs/src/rules/object-curly-newline.md
index a9b8875a592..d33dd6433e8 100644
--- a/docs/src/rules/object-curly-newline.md
+++ b/docs/src/rules/object-curly-newline.md
@@ -8,7 +8,7 @@ related_rules:
- object-property-newline
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
A number of style guides require or disallow line breaks inside of object braces and other tokens.
@@ -554,9 +554,9 @@ Examples of **incorrect** code for this rule with the `{ "ImportDeclaration": "a
/*eslint-env es6*/
import {foo, bar} from 'foo-bar';
-import {foo as f, bar} from 'foo-bar';
-import {foo,
- bar} from 'foo-bar';
+import {foo as f, baz} from 'foo-bar';
+import {qux,
+ foobar} from 'foo-bar';
export {
foo,
@@ -564,7 +564,7 @@ export {
};
export {
foo as f,
- bar
+ baz
} from 'foo-bar';
```
@@ -583,15 +583,15 @@ import {
bar
} from 'foo-bar';
import {
- foo, bar
+ baz, qux
} from 'foo-bar';
import {
foo as f,
- bar
+ foobar
} from 'foo-bar';
export { foo, bar } from 'foo-bar';
-export { foo as f, bar } from 'foo-bar';
+export { foo as f, baz } from 'foo-bar';
```
:::
diff --git a/docs/src/rules/object-curly-spacing.md b/docs/src/rules/object-curly-spacing.md
index 4027c9ab755..0484c436a4b 100644
--- a/docs/src/rules/object-curly-spacing.md
+++ b/docs/src/rules/object-curly-spacing.md
@@ -8,7 +8,7 @@ related_rules:
- space-in-parens
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
While formatting preferences are very personal, a number of style guides require
or disallow spaces between curly braces in the following situations:
diff --git a/docs/src/rules/object-property-newline.md b/docs/src/rules/object-property-newline.md
index 50214bdb7c0..e2bb161ea58 100644
--- a/docs/src/rules/object-property-newline.md
+++ b/docs/src/rules/object-property-newline.md
@@ -8,7 +8,7 @@ related_rules:
- object-curly-spacing
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
This rule permits you to restrict the locations of property specifications in object literals. You may prohibit any part of any property specification from appearing on the same line as any part of any other property specification. You may make this prohibition absolute, or, by invoking an object option, you may allow an exception, permitting an object literal to have all parts of all of its property specifications on a single line.
diff --git a/docs/src/rules/one-var-declaration-per-line.md b/docs/src/rules/one-var-declaration-per-line.md
index e9b159fc992..742f52352fb 100644
--- a/docs/src/rules/one-var-declaration-per-line.md
+++ b/docs/src/rules/one-var-declaration-per-line.md
@@ -5,7 +5,7 @@ related_rules:
- one-var
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Some developers declare multiple var statements on the same line:
@@ -46,8 +46,8 @@ Examples of **incorrect** code for this rule with the default `"initializations"
var a, b, c = 0;
-let a,
- b = 0, c;
+let d,
+ e = 0, f;
```
:::
@@ -62,11 +62,11 @@ Examples of **correct** code for this rule with the default `"initializations"`
var a, b;
-let a,
- b;
+let c,
+ d;
-let a,
- b = 0;
+let e,
+ f = 0;
```
:::
@@ -83,9 +83,9 @@ Examples of **incorrect** code for this rule with the `"always"` option:
var a, b;
-let a, b = 0;
+let c, d = 0;
-const a = 0, b = 0;
+const e = 0, f = 0;
```
:::
@@ -101,8 +101,8 @@ Examples of **correct** code for this rule with the `"always"` option:
var a,
b;
-let a,
- b = 0;
+let c,
+ d = 0;
```
:::
diff --git a/docs/src/rules/one-var.md b/docs/src/rules/one-var.md
index d9a50ed8671..cb3bc09b955 100644
--- a/docs/src/rules/one-var.md
+++ b/docs/src/rules/one-var.md
@@ -74,21 +74,21 @@ Examples of **incorrect** code for this rule with the default `"always"` option:
```js
/*eslint one-var: ["error", "always"]*/
-function foo() {
+function foo1() {
var bar;
var baz;
let qux;
let norf;
}
-function foo(){
+function foo2(){
const bar = false;
const baz = true;
let qux;
let norf;
}
-function foo() {
+function foo3() {
var bar;
if (baz) {
@@ -125,21 +125,21 @@ Examples of **correct** code for this rule with the default `"always"` option:
```js
/*eslint one-var: ["error", "always"]*/
-function foo() {
+function foo1() {
var bar,
baz;
let qux,
norf;
}
-function foo(){
+function foo2(){
const bar = true,
baz = false;
let qux,
norf;
}
-function foo() {
+function foo3() {
var bar,
qux;
@@ -148,7 +148,7 @@ function foo() {
}
}
-function foo(){
+function foo4(){
let bar;
if (baz) {
@@ -192,14 +192,14 @@ Examples of **incorrect** code for this rule with the `"never"` option:
```js
/*eslint one-var: ["error", "never"]*/
-function foo() {
+function foo1() {
var bar,
baz;
- const bar = true,
- baz = false;
+ const qux = true,
+ foobar = false;
}
-function foo() {
+function foo2() {
var bar,
qux;
@@ -208,7 +208,7 @@ function foo() {
}
}
-function foo(){
+function foo3(){
let bar = true,
baz = false;
}
@@ -230,12 +230,12 @@ Examples of **correct** code for this rule with the `"never"` option:
```js
/*eslint one-var: ["error", "never"]*/
-function foo() {
+function foo1() {
var bar;
var baz;
}
-function foo() {
+function foo2() {
var bar;
if (baz) {
@@ -243,7 +243,7 @@ function foo() {
}
}
-function foo() {
+function foo3() {
let bar;
if (baz) {
@@ -277,12 +277,12 @@ Examples of **incorrect** code for this rule with the `"consecutive"` option:
```js
/*eslint one-var: ["error", "consecutive"]*/
-function foo() {
+function foo1() {
var bar;
var baz;
}
-function foo(){
+function foo2(){
var bar = 1;
var baz = 2;
@@ -311,12 +311,12 @@ Examples of **correct** code for this rule with the `"consecutive"` option:
```js
/*eslint one-var: ["error", "consecutive"]*/
-function foo() {
+function foo1() {
var bar,
baz;
}
-function foo(){
+function foo2(){
var bar = 1,
baz = 2;
@@ -349,14 +349,14 @@ Examples of **incorrect** code for this rule with the `{ var: "always", let: "ne
/*eslint one-var: ["error", { var: "always", let: "never", const: "never" }]*/
/*eslint-env es6*/
-function foo() {
+function foo1() {
var bar;
var baz;
let qux,
norf;
}
-function foo() {
+function foo2() {
const bar = 1,
baz = 2;
let qux,
@@ -374,14 +374,14 @@ Examples of **correct** code for this rule with the `{ var: "always", let: "neve
/*eslint one-var: ["error", { var: "always", let: "never", const: "never" }]*/
/*eslint-env es6*/
-function foo() {
+function foo1() {
var bar,
baz;
let qux;
let norf;
}
-function foo() {
+function foo2() {
const bar = 1;
const baz = 2;
let qux;
@@ -416,12 +416,16 @@ Examples of **correct** code for this rule with the `{ var: "never" }` option:
/*eslint-env es6*/
function foo() {
- var bar,
- baz;
- const bar = 1; // `const` and `let` declarations are ignored if they are not specified
- const baz = 2;
+ var bar;
+ var baz;
+
+ // `const` and `let` declarations are ignored if they are not specified
+ const foobar = 1;
+ const foobaz = 2;
+ const barfoo = 1, bazfoo = 2;
let qux;
let norf;
+ let fooqux, foonorf;
}
```
@@ -472,7 +476,7 @@ Examples of **incorrect** code for this rule with the `{ var: "never", let: "con
/*eslint one-var: ["error", { var: "never", let: "consecutive", const: "consecutive" }]*/
/*eslint-env es6*/
-function foo() {
+function foo1() {
let a,
b;
let c;
@@ -481,7 +485,7 @@ function foo() {
e;
}
-function foo() {
+function foo2() {
const a = 1,
b = 2;
const c = 3;
@@ -501,7 +505,7 @@ Examples of **correct** code for this rule with the `{ var: "never", let: "conse
/*eslint one-var: ["error", { var: "never", let: "consecutive", const: "consecutive" }]*/
/*eslint-env es6*/
-function foo() {
+function foo1() {
let a,
b;
@@ -511,7 +515,7 @@ function foo() {
let f;
}
-function foo() {
+function foo2() {
const a = 1,
b = 2;
diff --git a/docs/src/rules/operator-linebreak.md b/docs/src/rules/operator-linebreak.md
index f3cab6e4f03..0724797f3ee 100644
--- a/docs/src/rules/operator-linebreak.md
+++ b/docs/src/rules/operator-linebreak.md
@@ -5,7 +5,7 @@ related_rules:
- comma-style
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
When a statement is too long to fit on a single line, line breaks are generally inserted next to the operators separating expressions. The first style coming to mind would be to place the operator at the end of the line, following the English punctuation rules.
diff --git a/docs/src/rules/padded-blocks.md b/docs/src/rules/padded-blocks.md
index fd57f2a2905..7d1a32042af 100644
--- a/docs/src/rules/padded-blocks.md
+++ b/docs/src/rules/padded-blocks.md
@@ -6,7 +6,7 @@ related_rules:
- padding-line-between-statements
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Some style guides require block statements to start and end with blank lines. The goal is
to improve readability by visually separating the block content and the surrounding code.
diff --git a/docs/src/rules/padding-line-between-statements.md b/docs/src/rules/padding-line-between-statements.md
index 1b16881c3ee..af57ed48990 100644
--- a/docs/src/rules/padding-line-between-statements.md
+++ b/docs/src/rules/padding-line-between-statements.md
@@ -3,7 +3,7 @@ title: padding-line-between-statements
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
This rule requires or disallows blank lines between the given 2 kinds of statements.
Properly blank lines help developers to understand the code.
@@ -120,13 +120,13 @@ Examples of **correct** code for the `[{ blankLine: "always", prev: "*", next: "
{ blankLine: "always", prev: "*", next: "return" }
]*/
-function foo() {
+function foo1() {
bar();
return;
}
-function foo() {
+function foo2() {
return;
}
```
@@ -148,17 +148,17 @@ Examples of **incorrect** code for the `[{ blankLine: "always", prev: ["const",
{ blankLine: "any", prev: ["const", "let", "var"], next: ["const", "let", "var"]}
]*/
-function foo() {
+function foo1() {
var a = 0;
bar();
}
-function foo() {
+function foo2() {
let a = 0;
bar();
}
-function foo() {
+function foo3() {
const a = 0;
bar();
}
@@ -184,21 +184,21 @@ Examples of **correct** code for the `[{ blankLine: "always", prev: ["const", "l
{ blankLine: "any", prev: ["const", "let", "var"], next: ["const", "let", "var"]}
]*/
-function foo() {
+function foo1() {
var a = 0;
var b = 0;
bar();
}
-function foo() {
+function foo2() {
let a = 0;
const b = 0;
bar();
}
-function foo() {
+function foo3() {
const a = 0;
const b = 0;
diff --git a/docs/src/rules/prefer-arrow-callback.md b/docs/src/rules/prefer-arrow-callback.md
index 0d724938447..d3e9000561c 100644
--- a/docs/src/rules/prefer-arrow-callback.md
+++ b/docs/src/rules/prefer-arrow-callback.md
@@ -23,6 +23,8 @@ This rule locates function expressions used as callbacks or function arguments.
The following examples **will** be flagged:
+::: incorrect
+
```js
/* eslint prefer-arrow-callback: "error" */
@@ -33,10 +35,14 @@ foo(function() { return this.a; }.bind(this)); // ERROR
// prefer: foo(() => this.a)
```
+:::
+
Instances where an arrow function would not produce identical results will be ignored.
The following examples **will not** be flagged:
+::: correct
+
```js
/* eslint prefer-arrow-callback: "error" */
/* eslint-env es6 */
@@ -57,6 +63,8 @@ foo(function() { return this.a; }); // OK
foo(function bar(n) { return n && n + bar(n - 1); }); // OK
```
+:::
+
## Options
Access further control over this rule's behavior via an options object.
@@ -71,12 +79,16 @@ Changing this value to `true` will reverse this option's behavior by allowing us
`{ "allowNamedFunctions": true }` **will not** flag the following example:
+::: correct
+
```js
/* eslint prefer-arrow-callback: [ "error", { "allowNamedFunctions": true } ] */
foo(function bar() {});
```
+:::
+
### allowUnboundThis
By default `{ "allowUnboundThis": true }`, this `boolean` option allows function expressions containing `this` to be used as callbacks, as long as the function in question has not been explicitly bound.
@@ -85,6 +97,8 @@ When set to `false` this option prohibits the use of function expressions as cal
`{ "allowUnboundThis": false }` **will** flag the following examples:
+::: incorrect
+
```js
/* eslint prefer-arrow-callback: [ "error", { "allowUnboundThis": false } ] */
/* eslint-env es6 */
@@ -96,6 +110,8 @@ foo(function() { (() => this); });
someArray.map(function(item) { return this.doSomething(item); }, someObject);
```
+:::
+
## When Not To Use It
* In environments that have not yet adopted ES6 language features (ES3/5).
diff --git a/docs/src/rules/prefer-const.md b/docs/src/rules/prefer-const.md
index 19801eb952a..41b144fe466 100644
--- a/docs/src/rules/prefer-const.md
+++ b/docs/src/rules/prefer-const.md
@@ -27,9 +27,9 @@ Examples of **incorrect** code for this rule:
let a = 3;
console.log(a);
-let a;
-a = 0;
-console.log(a);
+let b;
+b = 0;
+console.log(b);
class C {
static {
@@ -63,35 +63,35 @@ Examples of **correct** code for this rule:
const a = 0;
// it's never initialized.
-let a;
-console.log(a);
+let b;
+console.log(b);
// it's reassigned after initialized.
-let a;
-a = 0;
-a = 1;
-console.log(a);
+let c;
+c = 0;
+c = 1;
+console.log(c);
// it's initialized in a different block from the declaration.
-let a;
+let d;
if (true) {
- a = 0;
+ d = 0;
}
-console.log(a);
+console.log(d);
// it's initialized in a different scope.
-let a;
+let e;
class C {
#x;
static {
- a = obj => obj.#x;
+ e = obj => obj.#x;
}
}
// it's initialized at a place that we cannot write a variable declaration.
-let a;
-if (true) a = 0;
-console.log(a);
+let f;
+if (true) f = 0;
+console.log(f);
// `i` gets a new binding each iteration
for (const i in [1, 2, 3]) {
@@ -112,14 +112,14 @@ for (let i = 0, end = 10; i < end; ++i) {
let predicate;
[object.type, predicate] = foo();
-// `a` is only assigned once but cannot be separately declared as `const`
-let a;
-const b = {};
-({ a, c: b.c } = func());
+// `g` is only assigned once but cannot be separately declared as `const`
+let g;
+const h = {};
+({ g, c: h.c } = func());
// suggest to use `no-var` rule.
-var b = 3;
-console.log(b);
+var i = 3;
+console.log(i);
```
:::
@@ -170,9 +170,9 @@ const {a: a0, b} = obj;
const a = a0 + 1;
// all variables are reassigned.
-let {a, b} = obj;
-a = a + 1;
-b = b + 1;
+let {c, d} = obj;
+c = c + 1;
+d = d + 1;
```
:::
diff --git a/docs/src/rules/prefer-destructuring.md b/docs/src/rules/prefer-destructuring.md
index c98abdc0c90..34b30c8de32 100644
--- a/docs/src/rules/prefer-destructuring.md
+++ b/docs/src/rules/prefer-destructuring.md
@@ -36,8 +36,11 @@ Examples of **incorrect** code for this rule:
::: incorrect
```javascript
+/* eslint prefer-destructuring: "error" */
+
// With `array` enabled
var foo = array[0];
+bar.baz = array[0];
// With `object` enabled
var foo = object.foo;
@@ -51,17 +54,21 @@ Examples of **correct** code for this rule:
::: correct
```javascript
+/* eslint prefer-destructuring: "error" */
+
// With `array` enabled
var [ foo ] = array;
var foo = array[someIndex];
+[bar.baz] = array;
+
// With `object` enabled
var { foo } = object;
var foo = object.bar;
-let foo;
-({ foo } = object);
+let bar;
+({ bar } = object);
```
:::
@@ -71,6 +78,7 @@ Examples of **incorrect** code when `enforceForRenamedProperties` is enabled:
::: incorrect
```javascript
+/* eslint "prefer-destructuring": ["error", { "object": true }, { "enforceForRenamedProperties": true }] */
var foo = object.bar;
```
@@ -81,6 +89,7 @@ Examples of **correct** code when `enforceForRenamedProperties` is enabled:
::: correct
```javascript
+/* eslint "prefer-destructuring": ["error", { "object": true }, { "enforceForRenamedProperties": true }] */
var { bar: foo } = object;
```
@@ -91,6 +100,7 @@ Examples of additional **correct** code when `enforceForRenamedProperties` is en
::: correct
```javascript
+/* eslint "prefer-destructuring": ["error", { "object": true }, { "enforceForRenamedProperties": true }] */
class C {
#x;
foo() {
diff --git a/docs/src/rules/prefer-reflect.md b/docs/src/rules/prefer-reflect.md
index e2a632eb6c6..d6e3d87330a 100644
--- a/docs/src/rules/prefer-reflect.md
+++ b/docs/src/rules/prefer-reflect.md
@@ -380,7 +380,7 @@ delete foo.bar; // deleting object property
Examples of **correct** code for this rule when used without exceptions:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint prefer-reflect: "error"*/
@@ -395,7 +395,7 @@ Note: For a rule preventing deletion of variables, see [no-delete-var instead](n
Examples of **correct** code for this rule with the `{ "exceptions": ["delete"] }` option:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint prefer-reflect: ["error", { "exceptions": ["delete"] }]*/
diff --git a/docs/src/rules/prefer-rest-params.md b/docs/src/rules/prefer-rest-params.md
index cf234260290..4406b5468dc 100644
--- a/docs/src/rules/prefer-rest-params.md
+++ b/docs/src/rules/prefer-rest-params.md
@@ -19,7 +19,7 @@ This rule is aimed to flag usage of `arguments` variables.
Examples of **incorrect** code for this rule:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint prefer-rest-params: "error"*/
@@ -43,7 +43,7 @@ function foo(action) {
Examples of **correct** code for this rule:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint prefer-rest-params: "error"*/
diff --git a/docs/src/rules/quote-props.md b/docs/src/rules/quote-props.md
index b994b82507e..2e204dd0cb6 100644
--- a/docs/src/rules/quote-props.md
+++ b/docs/src/rules/quote-props.md
@@ -6,7 +6,7 @@ further_reading:
- https://mathiasbynens.be/notes/javascript-properties
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Object literal property names can be defined in two ways: using literals or using strings. For example, these two objects are equivalent:
diff --git a/docs/src/rules/quotes.md b/docs/src/rules/quotes.md
index 38d21b428f9..ea935d610a1 100644
--- a/docs/src/rules/quotes.md
+++ b/docs/src/rules/quotes.md
@@ -3,7 +3,7 @@ title: quotes
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
JavaScript allows you to define strings in one of three ways: double quotes, single quotes, and backticks (as of ECMAScript 6). For example:
@@ -15,7 +15,7 @@ var single = 'single';
var backtick = `backtick`; // ES6 only
```
-Each of these lines creates a string and, in some cases, can be used interchangeably. The choice of how to define strings in a codebase is a stylistic one outside of template literals (which allow embedded of expressions to be interpreted).
+Each of these lines creates a string and, in some cases, can be used interchangeably. The choice of how to define strings in a codebase is a stylistic one outside of template literals (which allow embedded expressions to be interpreted).
Many codebases require strings to be defined in a consistent manner.
@@ -23,6 +23,8 @@ Many codebases require strings to be defined in a consistent manner.
This rule enforces the consistent use of either backticks, double, or single quotes.
+This rule is aware of directive prologues such as `"use strict"` and will not flag or autofix them if doing so will change how the directive prologue is interpreted.
+
## Options
This rule has two options, a string option and an object option.
@@ -125,7 +127,9 @@ Examples of **correct** code for this rule with the `"backtick"` option:
/*eslint quotes: ["error", "backtick"]*/
/*eslint-env es6*/
+"use strict"; // directives must use single or double quotes
var backtick = `backtick`;
+var obj = { 'prop-name': `value` }; // backticks not allowed for property names
```
:::
diff --git a/docs/src/rules/require-await.md b/docs/src/rules/require-await.md
index a17eb972dca..e0371dfb6ad 100644
--- a/docs/src/rules/require-await.md
+++ b/docs/src/rules/require-await.md
@@ -63,7 +63,7 @@ bar(async () => {
await doSomething();
});
-function foo() {
+function baz() {
doSomething();
}
diff --git a/docs/src/rules/require-jsdoc.md b/docs/src/rules/require-jsdoc.md
index 20516fb0188..82ea4b43222 100644
--- a/docs/src/rules/require-jsdoc.md
+++ b/docs/src/rules/require-jsdoc.md
@@ -77,7 +77,7 @@ function foo() {
return 10;
}
-var foo = () => {
+var bar = () => {
return 10;
};
@@ -87,11 +87,11 @@ class Foo {
}
}
-var foo = function() {
+var bar = function() {
return 10;
};
-var foo = {
+var bar = {
bar: function() {
return 10;
},
@@ -131,21 +131,21 @@ function foo() {
* @params {int} test - some number
* @returns {int} sum of test and 10
*/
-var foo = (test) => {
+var bar = (test) => {
return test + 10;
}
/**
* It returns 10
*/
-var foo = () => {
+var bar = () => {
return 10;
}
/**
* It returns 10
*/
-var foo = function() {
+var bar = function() {
return 10;
}
@@ -169,11 +169,11 @@ class Foo {
/**
* It returns 10
*/
-var foo = function() {
+var bar = function() {
return 10;
};
-var foo = {
+var bar = {
/**
* It returns 10
*/
diff --git a/docs/src/rules/require-unicode-regexp.md b/docs/src/rules/require-unicode-regexp.md
index f3277066a5b..768f34a71d3 100644
--- a/docs/src/rules/require-unicode-regexp.md
+++ b/docs/src/rules/require-unicode-regexp.md
@@ -1,6 +1,9 @@
---
title: require-unicode-regexp
rule_type: suggestion
+further_reading:
+- https://github.com/tc39/proposal-regexp-v-flag
+- https://v8.dev/features/regexp-v-flag
---
@@ -21,11 +24,39 @@ RegExp `u` flag has two effects:
The `u` flag disables the recovering logic Annex B defined. As a result, you can find errors early. This is similar to [the strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode).
-Therefore, the `u` flag lets us work better with regular expressions.
+The RegExp `v` flag, introduced in ECMAScript 2024, is a superset of the `u` flag, and offers two more features:
+
+1. **Unicode properties of strings**
+
+ With the Unicode property escape, you can use properties of strings.
+
+ ```js
+ const re = /^\p{RGI_Emoji}$/v;
+
+ // Match an emoji that consists of just 1 code point:
+ re.test('⚽'); // '\u26BD'
+ // → true ✅
+
+ // Match an emoji that consists of multiple code points:
+ re.test('👨🏾⚕️'); // '\u{1F468}\u{1F3FE}\u200D\u2695\uFE0F'
+ // → true ✅
+ ```
+
+2. **Set notation**
+
+ It allows for set operations between character classes.
+
+ ```js
+ const re = /[\p{White_Space}&&\p{ASCII}]/v;
+ re.test('\n'); // → true
+ re.test('\u2028'); // → false
+ ```
+
+Therefore, the `u` and `v` flags let us work better with regular expressions.
## Rule Details
-This rule aims to enforce the use of `u` flag on regular expressions.
+This rule aims to enforce the use of `u` or `v` flag on regular expressions.
Examples of **incorrect** code for this rule:
@@ -54,8 +85,13 @@ const b = /bbb/giu
const c = new RegExp("ccc", "u")
const d = new RegExp("ddd", "giu")
+const e = /aaa/v
+const f = /bbb/giv
+const g = new RegExp("ccc", "v")
+const h = new RegExp("ddd", "giv")
+
// This rule ignores RegExp calls if the flags could not be evaluated to a static value.
-function f(flags) {
+function i(flags) {
return new RegExp("eee", flags)
}
```
@@ -64,4 +100,4 @@ function f(flags) {
## When Not To Use It
-If you don't want to notify regular expressions with no `u` flag, then it's safe to disable this rule.
+If you don't want to warn on regular expressions without either a `u` or a `v` flag, then it's safe to disable this rule.
diff --git a/docs/src/rules/require-yield.md b/docs/src/rules/require-yield.md
index e4f975faa3e..eec9d0cfc94 100644
--- a/docs/src/rules/require-yield.md
+++ b/docs/src/rules/require-yield.md
@@ -41,12 +41,12 @@ function* foo() {
return 10;
}
-function foo() {
+function bar() {
return 10;
}
// This rule does not warn on empty generator functions.
-function* foo() { }
+function* baz() { }
```
:::
diff --git a/docs/src/rules/rest-spread-spacing.md b/docs/src/rules/rest-spread-spacing.md
index b27b5378d23..8a52856cad9 100644
--- a/docs/src/rules/rest-spread-spacing.md
+++ b/docs/src/rules/rest-spread-spacing.md
@@ -5,7 +5,7 @@ further_reading:
- https://github.com/tc39/proposal-object-rest-spread
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
ES2015 introduced the rest and spread operators, which expand an iterable structure into its individual parts. Some examples of their usage are as follows:
@@ -85,8 +85,8 @@ Examples of **incorrect** code for this rule with `"never"`:
```js
/*eslint rest-spread-spacing: ["error", "never"]*/
-fn(... args)
-[... arr, 4, 5, 6]
+fn(... args);
+[... arr, 4, 5, 6];
let [a, b, ... arr] = [1, 2, 3, 4, 5];
function fn(... args) { console.log(args); }
let { x, y, ... z } = { x: 1, y: 2, a: 3, b: 4 };
@@ -102,8 +102,8 @@ Examples of **correct** code for this rule with `"never"`:
```js
/*eslint rest-spread-spacing: ["error", "never"]*/
-fn(...args)
-[...arr, 4, 5, 6]
+fn(...args);
+[...arr, 4, 5, 6];
let [a, b, ...arr] = [1, 2, 3, 4, 5];
function fn(...args) { console.log(args); }
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
@@ -127,8 +127,8 @@ Examples of **incorrect** code for this rule with `"always"`:
```js
/*eslint rest-spread-spacing:["error", "always"]*/
-fn(...args)
-[...arr, 4, 5, 6]
+fn(...args);
+[...arr, 4, 5, 6];
let [a, b, ...arr] = [1, 2, 3, 4, 5];
function fn(...args) { console.log(args); }
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
@@ -144,8 +144,8 @@ Examples of **correct** code for this rule with `"always"`:
```js
/*eslint rest-spread-spacing: ["error", "always"]*/
-fn(... args)
-[... arr, 4, 5, 6]
+fn(... args);
+[... arr, 4, 5, 6];
let [a, b, ... arr] = [1, 2, 3, 4, 5];
function fn(... args) { console.log(args); }
let { x, y, ... z } = { x: 1, y: 2, a: 3, b: 4 };
diff --git a/docs/src/rules/semi-spacing.md b/docs/src/rules/semi-spacing.md
index 4ed43ac570e..5a27040f9b5 100644
--- a/docs/src/rules/semi-spacing.md
+++ b/docs/src/rules/semi-spacing.md
@@ -9,7 +9,7 @@ related_rules:
- space-in-parens
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
JavaScript allows you to place unnecessary spaces before or after a semicolon.
diff --git a/docs/src/rules/semi-style.md b/docs/src/rules/semi-style.md
index 66db3af0f75..2979c188564 100644
--- a/docs/src/rules/semi-style.md
+++ b/docs/src/rules/semi-style.md
@@ -7,7 +7,7 @@ related_rules:
- semi-spacing
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Generally, semicolons are at the end of lines. However, in semicolon-less style, semicolons are at the beginning of lines. This rule enforces that semicolons are at the configured location.
diff --git a/docs/src/rules/semi.md b/docs/src/rules/semi.md
index 6fd05501cd1..e118b4518f8 100644
--- a/docs/src/rules/semi.md
+++ b/docs/src/rules/semi.md
@@ -10,7 +10,7 @@ further_reading:
- https://web.archive.org/web/20200420230322/http://inimino.org/~inimino/blog/javascript_semicolons
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
JavaScript doesn't require semicolons at the end of each statement. In many cases, the JavaScript engine can determine that a semicolon should be in a certain spot and will automatically add it. This feature is known as **automatic semicolon insertion (ASI)** and is considered one of the more controversial features of JavaScript. For example, the following lines are both valid:
diff --git a/docs/src/rules/sort-imports.md b/docs/src/rules/sort-imports.md
index d48ead6f66e..762e4acfa43 100644
--- a/docs/src/rules/sort-imports.md
+++ b/docs/src/rules/sort-imports.md
@@ -82,19 +82,37 @@ import {alpha, beta} from 'alpha.js';
import {delta, gamma} from 'delta.js';
import a from 'baz.js';
import {b} from 'qux.js';
+```
+
+:::
+::: correct
+
+```js
/*eslint sort-imports: "error"*/
import a from 'foo.js';
import b from 'bar.js';
import c from 'baz.js';
+```
+:::
+
+::: correct
+
+```js
/*eslint sort-imports: "error"*/
import 'foo.js'
import * as bar from 'bar.js';
import {a, b} from 'baz.js';
import c from 'qux.js';
import {d} from 'quux.js';
+```
+:::
+
+::: correct
+
+```js
/*eslint sort-imports: "error"*/
import {a, b, c} from 'foo.js'
```
@@ -109,27 +127,63 @@ Examples of **incorrect** code for this rule when using default options:
/*eslint sort-imports: "error"*/
import b from 'foo.js';
import a from 'bar.js';
+```
+
+:::
+::: incorrect
+
+```js
/*eslint sort-imports: "error"*/
import a from 'foo.js';
import A from 'bar.js';
+```
+
+:::
+::: incorrect
+
+```js
/*eslint sort-imports: "error"*/
-import {b, c} from 'foo.js';
+import {c, d} from 'foo.js';
import {a, b} from 'bar.js';
+```
+
+:::
+
+::: incorrect
+```js
/*eslint sort-imports: "error"*/
import a from 'foo.js';
import {b, c} from 'bar.js';
+```
+
+:::
+
+::: incorrect
+```js
/*eslint sort-imports: "error"*/
import {a} from 'foo.js';
import {b, c} from 'bar.js';
+```
+
+:::
+::: incorrect
+
+```js
/*eslint sort-imports: "error"*/
import a from 'foo.js';
import * as b from 'bar.js';
+```
+
+:::
+::: incorrect
+
+```js
/*eslint sort-imports: "error"*/
import {b, a, c} from 'foo.js'
```
diff --git a/docs/src/rules/sort-keys.md b/docs/src/rules/sort-keys.md
index 0fd00aeff7f..14cdc12f8f3 100644
--- a/docs/src/rules/sort-keys.md
+++ b/docs/src/rules/sort-keys.md
@@ -21,20 +21,20 @@ Examples of **incorrect** code for this rule:
/*eslint sort-keys: "error"*/
/*eslint-env es6*/
-let obj = {a: 1, c: 3, b: 2};
-let obj = {a: 1, "c": 3, b: 2};
+let obj1 = {a: 1, c: 3, b: 2};
+let obj2 = {a: 1, "c": 3, b: 2};
// Case-sensitive by default.
-let obj = {a: 1, b: 2, C: 3};
+let obj3 = {a: 1, b: 2, C: 3};
// Non-natural order by default.
-let obj = {1: a, 2: c, 10: b};
+let obj4 = {1: a, 2: c, 10: b};
// This rule checks computed properties which have a simple name as well.
// Simple names are names which are expressed by an Identifier node or a Literal node.
const S = Symbol("s")
-let obj = {a: 1, ["c"]: 3, b: 2};
-let obj = {a: 1, [S]: 3, b: 2};
+let obj5 = {a: 1, ["c"]: 3, b: 2};
+let obj6 = {a: 1, [S]: 3, b: 2};
```
:::
@@ -47,27 +47,27 @@ Examples of **correct** code for this rule:
/*eslint sort-keys: "error"*/
/*eslint-env es6*/
-let obj = {a: 1, b: 2, c: 3};
-let obj = {a: 1, "b": 2, c: 3};
+let obj1 = {a: 1, b: 2, c: 3};
+let obj2 = {a: 1, "b": 2, c: 3};
// Case-sensitive by default.
-let obj = {C: 3, a: 1, b: 2};
+let obj3 = {C: 3, a: 1, b: 2};
// Non-natural order by default.
-let obj = {1: a, 10: b, 2: c};
+let obj4 = {1: a, 10: b, 2: c};
// This rule checks computed properties which have a simple name as well.
-let obj = {a: 1, ["b"]: 2, c: 3};
-let obj = {a: 1, [b]: 2, c: 3};
+let obj5 = {a: 1, ["b"]: 2, c: 3};
+let obj6 = {a: 1, [b]: 2, c: 3};
// This rule ignores computed properties which have a non-simple name.
-let obj = {a: 1, [c + d]: 3, b: 2};
-let obj = {a: 1, ["c" + "d"]: 3, b: 2};
-let obj = {a: 1, [`${c}`]: 3, b: 2};
-let obj = {a: 1, [tag`c`]: 3, b: 2};
+let obj7 = {a: 1, [c + d]: 3, b: 2};
+let obj8 = {a: 1, ["c" + "d"]: 3, b: 2};
+let obj9 = {a: 1, [`${c}`]: 3, b: 2};
+let obj10 = {a: 1, [tag`c`]: 3, b: 2};
// This rule does not report unsorted properties that are separated by a spread property.
-let obj = {b: 1, ...c, a: 2};
+let obj11 = {b: 1, ...c, a: 2};
```
:::
@@ -118,14 +118,14 @@ Examples of **incorrect** code for the `"desc"` option:
/*eslint sort-keys: ["error", "desc"]*/
/*eslint-env es6*/
-let obj = {b: 2, c: 3, a: 1};
-let obj = {"b": 2, c: 3, a: 1};
+let obj1 = {b: 2, c: 3, a: 1};
+let obj2 = {"b": 2, c: 3, a: 1};
// Case-sensitive by default.
-let obj = {C: 1, b: 3, a: 2};
+let obj3 = {C: 1, b: 3, a: 2};
// Non-natural order by default.
-let obj = {10: b, 2: c, 1: a};
+let obj4 = {10: b, 2: c, 1: a};
```
:::
@@ -138,14 +138,14 @@ Examples of **correct** code for the `"desc"` option:
/*eslint sort-keys: ["error", "desc"]*/
/*eslint-env es6*/
-let obj = {c: 3, b: 2, a: 1};
-let obj = {c: 3, "b": 2, a: 1};
+let obj1 = {c: 3, b: 2, a: 1};
+let obj2 = {c: 3, "b": 2, a: 1};
// Case-sensitive by default.
-let obj = {b: 3, a: 2, C: 1};
+let obj3 = {b: 3, a: 2, C: 1};
// Non-natural order by default.
-let obj = {2: c, 10: b, 1: a};
+let obj4 = {2: c, 10: b, 1: a};
```
:::
@@ -160,8 +160,8 @@ Examples of **incorrect** code for the `{caseSensitive: false}` option:
/*eslint sort-keys: ["error", "asc", {caseSensitive: false}]*/
/*eslint-env es6*/
-let obj = {a: 1, c: 3, C: 4, b: 2};
-let obj = {a: 1, C: 3, c: 4, b: 2};
+let obj1 = {a: 1, c: 3, C: 4, b: 2};
+let obj2 = {a: 1, C: 3, c: 4, b: 2};
```
:::
@@ -174,8 +174,8 @@ Examples of **correct** code for the `{caseSensitive: false}` option:
/*eslint sort-keys: ["error", "asc", {caseSensitive: false}]*/
/*eslint-env es6*/
-let obj = {a: 1, b: 2, c: 3, C: 4};
-let obj = {a: 1, b: 2, C: 3, c: 4};
+let obj1 = {a: 1, b: 2, c: 3, C: 4};
+let obj2 = {a: 1, b: 2, C: 3, c: 4};
```
:::
@@ -219,7 +219,7 @@ Examples of **incorrect** code for the `{minKeys: 4}` option:
/*eslint-env es6*/
// 4 keys
-let obj = {
+let obj1 = {
b: 2,
a: 1, // not sorted correctly (should be 1st key)
c: 3,
@@ -227,7 +227,7 @@ let obj = {
};
// 5 keys
-let obj = {
+let obj2 = {
2: 'a',
1: 'b', // not sorted correctly (should be 1st key)
3: 'c',
@@ -247,14 +247,14 @@ Examples of **correct** code for the `{minKeys: 4}` option:
/*eslint-env es6*/
// 3 keys
-let obj = {
+let obj1 = {
b: 2,
a: 1,
c: 3,
};
// 2 keys
-let obj = {
+let obj2 = {
2: 'b',
1: 'a',
};
@@ -318,7 +318,7 @@ Examples of **correct** code for the `{allowLineSeparatedGroups: true}` option:
/*eslint sort-keys: ["error", "asc", {allowLineSeparatedGroups: true}]*/
/*eslint-env es6*/
-let obj = {
+let obj1 = {
e: 1,
f: 2,
g: 3,
@@ -328,7 +328,7 @@ let obj = {
c: 6
}
-let obj = {
+let obj2 = {
b: 1,
// comment
@@ -336,7 +336,7 @@ let obj = {
c: 5,
}
-let obj = {
+let obj3 = {
c: 1,
d: 2,
@@ -346,7 +346,7 @@ let obj = {
e: 3,
}
-let obj = {
+let obj4 = {
c: 1,
d: 2,
// comment
@@ -358,14 +358,14 @@ let obj = {
e: 4
}
-let obj = {
+let obj5 = {
b,
[foo + bar]: 1,
a
}
-let obj = {
+let obj6 = {
b: 1
// comment before comma
@@ -373,7 +373,7 @@ let obj = {
a: 2
};
-var obj = {
+var obj7 = {
b: 1,
a: 2,
diff --git a/docs/src/rules/space-before-blocks.md b/docs/src/rules/space-before-blocks.md
index a7bcad2c70a..42f02f99ebf 100644
--- a/docs/src/rules/space-before-blocks.md
+++ b/docs/src/rules/space-before-blocks.md
@@ -9,7 +9,7 @@ related_rules:
- brace-style
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Consistency is an important part of any style guide.
While it is a personal preference where to put the opening brace of blocks,
diff --git a/docs/src/rules/space-before-function-paren.md b/docs/src/rules/space-before-function-paren.md
index 1948d903cf0..8b439dac5f1 100644
--- a/docs/src/rules/space-before-function-paren.md
+++ b/docs/src/rules/space-before-function-paren.md
@@ -5,7 +5,7 @@ related_rules:
- keyword-spacing
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
When formatting a function, whitespace is allowed between the function name or `function` keyword and the opening paren. Named functions also require a space between the `function` keyword and the function name, but anonymous functions require no whitespace. For example:
@@ -85,13 +85,13 @@ class Foo {
}
}
-var foo = {
+var baz = {
bar() {
// ...
}
};
-var foo = async() => 1
+var baz = async() => 1
```
:::
@@ -122,13 +122,13 @@ class Foo {
}
}
-var foo = {
+var baz = {
bar () {
// ...
}
};
-var foo = async () => 1
+var baz = async () => 1
```
:::
@@ -161,13 +161,13 @@ class Foo {
}
}
-var foo = {
+var baz = {
bar () {
// ...
}
};
-var foo = async () => 1
+var baz = async () => 1
```
:::
@@ -198,13 +198,13 @@ class Foo {
}
}
-var foo = {
+var baz = {
bar() {
// ...
}
};
-var foo = async() => 1
+var baz = async() => 1
```
:::
@@ -233,13 +233,13 @@ class Foo {
}
}
-var foo = {
+var baz = {
bar () {
// ...
}
};
-var foo = async(a) => await a
+var baz = async(a) => await a
```
:::
@@ -266,13 +266,13 @@ class Foo {
}
}
-var foo = {
+var baz = {
bar() {
// ...
}
};
-var foo = async (a) => await a
+var baz = async (a) => await a
```
:::
@@ -301,7 +301,7 @@ class Foo {
}
}
-var foo = {
+var baz = {
bar() {
// ...
}
@@ -332,7 +332,7 @@ class Foo {
}
}
-var foo = {
+var baz = {
bar () {
// ...
}
@@ -361,7 +361,7 @@ class Foo {
}
}
-var foo = {
+var baz = {
bar() {
// ...
}
@@ -396,7 +396,7 @@ class Foo {
}
}
-var foo = {
+var baz = {
bar () {
// ...
}
diff --git a/docs/src/rules/space-before-function-parentheses.md b/docs/src/rules/space-before-function-parentheses.md
index a5c0eceb01f..0b6b9b60d61 100644
--- a/docs/src/rules/space-before-function-parentheses.md
+++ b/docs/src/rules/space-before-function-parentheses.md
@@ -59,7 +59,7 @@ class Foo {
}
}
-var foo = {
+var baz = {
bar() {
// ...
}
@@ -93,7 +93,7 @@ class Foo {
}
}
-var foo = {
+var baz = {
bar () {
// ...
}
@@ -127,7 +127,7 @@ class Foo {
}
}
-var foo = {
+var baz = {
bar () {
// ...
}
@@ -161,7 +161,7 @@ class Foo {
}
}
-var foo = {
+var baz = {
bar() {
// ...
}
@@ -191,7 +191,7 @@ class Foo {
}
}
-var foo = {
+var baz = {
bar () {
// ...
}
@@ -221,7 +221,7 @@ class Foo {
}
}
-var foo = {
+var baz = {
bar() {
// ...
}
@@ -251,7 +251,7 @@ class Foo {
}
}
-var foo = {
+var baz = {
bar() {
// ...
}
@@ -281,7 +281,7 @@ class Foo {
}
}
-var foo = {
+var baz = {
bar () {
// ...
}
diff --git a/docs/src/rules/space-before-keywords.md b/docs/src/rules/space-before-keywords.md
index 9c2f7358109..5eaa76db768 100644
--- a/docs/src/rules/space-before-keywords.md
+++ b/docs/src/rules/space-before-keywords.md
@@ -55,7 +55,7 @@ if (foo) {
const foo = 'bar';let baz = 'qux';
-var foo =function bar () {}
+var qux =function bar () {}
function bar() {
if (foo) {return; }
@@ -66,7 +66,7 @@ function bar() {
Examples of **correct** code for this rule with the default `"always"` option:
-::: correct
+::: correct { "ecmaFeatures": { "jsx": true } }
```js
/*eslint space-before-keywords: ["error", "always"]*/
@@ -76,7 +76,7 @@ if (foo) {
// ...
} else {}
-(function() {})()
+(function() {})();
diff --git a/docs/src/rules/space-in-parens.md b/docs/src/rules/space-in-parens.md
index 1d6ca52bdf1..9f92cdc524a 100644
--- a/docs/src/rules/space-in-parens.md
+++ b/docs/src/rules/space-in-parens.md
@@ -7,7 +7,7 @@ related_rules:
- computed-property-spacing
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Some style guides require or disallow spaces inside of parentheses:
diff --git a/docs/src/rules/space-infix-ops.md b/docs/src/rules/space-infix-ops.md
index 984e3b37f41..8a8c0c3dff7 100644
--- a/docs/src/rules/space-infix-ops.md
+++ b/docs/src/rules/space-infix-ops.md
@@ -3,7 +3,7 @@ title: space-infix-ops
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
While formatting preferences are very personal, a number of style guides require spaces around operators, such as:
@@ -57,7 +57,7 @@ a?b:c
const a={b:1};
-var {a=0}=bar;
+var {b=0}=bar;
function foo(a=0) { }
```
@@ -80,7 +80,7 @@ a ? b : c
const a = {b:1};
-var {a = 0} = bar;
+var {b = 0} = bar;
function foo(a = 0) { }
```
diff --git a/docs/src/rules/space-unary-ops.md b/docs/src/rules/space-unary-ops.md
index a33735b5565..4e7f79bfce9 100644
--- a/docs/src/rules/space-unary-ops.md
+++ b/docs/src/rules/space-unary-ops.md
@@ -3,7 +3,7 @@ title: space-unary-ops
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Some style guides require or disallow spaces before or after unary operators. This is mainly a stylistic issue, however, some JavaScript expressions can be written without spacing which makes it harder to read and maintain.
diff --git a/docs/src/rules/spaced-comment.md b/docs/src/rules/spaced-comment.md
index f15e16c72e8..3094c3cebed 100644
--- a/docs/src/rules/spaced-comment.md
+++ b/docs/src/rules/spaced-comment.md
@@ -5,7 +5,7 @@ related_rules:
- spaced-line-comment
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Some style guides require or disallow a whitespace immediately after the initial `//` or `/*` of a comment.
Whitespace after the `//` or `/*` makes it easier to read text in comments.
diff --git a/docs/src/rules/strict.md b/docs/src/rules/strict.md
index c559b00710d..731ddba2c20 100644
--- a/docs/src/rules/strict.md
+++ b/docs/src/rules/strict.md
@@ -82,7 +82,7 @@ Otherwise the `"safe"` option corresponds to the `"function"` option. Note that
Examples of **incorrect** code for this rule with the `"global"` option:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint strict: ["error", "global"]*/
@@ -93,7 +93,7 @@ function foo() {
:::
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint strict: ["error", "global"]*/
@@ -105,7 +105,7 @@ function foo() {
:::
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint strict: ["error", "global"]*/
@@ -121,7 +121,7 @@ function foo() {
Examples of **correct** code for this rule with the `"global"` option:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint strict: ["error", "global"]*/
@@ -140,7 +140,7 @@ This option ensures that all function bodies are strict mode code, while global
Examples of **incorrect** code for this rule with the `"function"` option:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint strict: ["error", "function"]*/
@@ -153,7 +153,7 @@ function foo() {
:::
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint strict: ["error", "function"]*/
@@ -170,7 +170,7 @@ function foo() {
:::
-::: incorrect
+::: incorrect { "ecmaVersion": 6, "sourceType": "script" }
```js
/*eslint strict: ["error", "function"]*/
@@ -192,7 +192,7 @@ function foo(a = 1) {
Examples of **correct** code for this rule with the `"function"` option:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint strict: ["error", "function"]*/
@@ -225,7 +225,7 @@ var foo = (function() {
Examples of **incorrect** code for this rule with the `"never"` option:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint strict: ["error", "never"]*/
@@ -238,7 +238,7 @@ function foo() {
:::
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
/*eslint strict: ["error", "never"]*/
@@ -252,7 +252,7 @@ function foo() {
Examples of **correct** code for this rule with the `"never"` option:
-::: correct
+::: correct { "sourceType": "script" }
```js
/*eslint strict: ["error", "never"]*/
@@ -271,7 +271,7 @@ This option ensures that all functions are executed in strict mode. A strict mod
Examples of **incorrect** code for this rule with the earlier default option which has been removed:
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
// "strict": "error"
@@ -282,7 +282,7 @@ function foo() {
:::
-::: incorrect
+::: incorrect { "sourceType": "script" }
```js
// "strict": "error"
@@ -298,7 +298,7 @@ function foo() {
Examples of **correct** code for this rule with the earlier default option which has been removed:
-::: correct
+::: correct { "sourceType": "script" }
```js
// "strict": "error"
@@ -311,7 +311,7 @@ function foo() {
:::
-::: correct
+::: correct { "sourceType": "script" }
```js
// "strict": "error"
@@ -323,7 +323,7 @@ function foo() {
:::
-::: correct
+::: correct { "sourceType": "script" }
```js
// "strict": "error"
diff --git a/docs/src/rules/switch-colon-spacing.md b/docs/src/rules/switch-colon-spacing.md
index 63df48f23e1..7672c6275b7 100644
--- a/docs/src/rules/switch-colon-spacing.md
+++ b/docs/src/rules/switch-colon-spacing.md
@@ -3,7 +3,7 @@ title: switch-colon-spacing
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
Spacing around colons improves readability of `case`/`default` clauses.
diff --git a/docs/src/rules/template-curly-spacing.md b/docs/src/rules/template-curly-spacing.md
index b96a30c870d..7a095cc38d8 100644
--- a/docs/src/rules/template-curly-spacing.md
+++ b/docs/src/rules/template-curly-spacing.md
@@ -3,7 +3,7 @@ title: template-curly-spacing
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
We can embed expressions in template strings with using a pair of `${` and `}`.
diff --git a/docs/src/rules/template-tag-spacing.md b/docs/src/rules/template-tag-spacing.md
index da0cfa8884b..2adf4c76205 100644
--- a/docs/src/rules/template-tag-spacing.md
+++ b/docs/src/rules/template-tag-spacing.md
@@ -6,7 +6,7 @@ further_reading:
- https://exploringjs.com/es6/ch_template-literals.html#_examples-of-using-tagged-template-literals
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
With ES6, it's possible to create functions called [tagged template literals](#further-reading) where the function parameters consist of a template literal's strings and expressions.
diff --git a/docs/src/rules/unicode-bom.md b/docs/src/rules/unicode-bom.md
index 1bae51ce64e..6fcde6c3304 100644
--- a/docs/src/rules/unicode-bom.md
+++ b/docs/src/rules/unicode-bom.md
@@ -31,9 +31,10 @@ Example of **correct** code for this rule with the `"always"` option:
::: correct
```js
+// U+FEFF at the beginning
+
/*eslint unicode-bom: ["error", "always"]*/
-U+FEFF
var abc;
```
@@ -70,9 +71,10 @@ Example of **incorrect** code for this rule with the `"never"` option:
::: incorrect
```js
+// U+FEFF at the beginning
+
/*eslint unicode-bom: ["error", "never"]*/
-U+FEFF
var abc;
```
diff --git a/docs/src/rules/vars-on-top.md b/docs/src/rules/vars-on-top.md
index 20d1440aee4..5fdb2f16048 100644
--- a/docs/src/rules/vars-on-top.md
+++ b/docs/src/rules/vars-on-top.md
@@ -34,7 +34,7 @@ function doSomething() {
}
// Variable declaration in for initializer:
-function doSomething() {
+function doSomethingElse() {
for (var i=0; i<10; i++) {}
}
```
@@ -95,7 +95,7 @@ function doSomething() {
}
}
-function doSomething() {
+function doSomethingElse() {
var i;
for (i=0; i<10; i++) {}
}
diff --git a/docs/src/rules/wrap-iife.md b/docs/src/rules/wrap-iife.md
index af8ec85c785..dd0048a8bb7 100644
--- a/docs/src/rules/wrap-iife.md
+++ b/docs/src/rules/wrap-iife.md
@@ -3,7 +3,7 @@ title: wrap-iife
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
You can immediately invoke function expressions, but not function declarations. A common technique to create an immediately-invoked function expression (IIFE) is to wrap a function declaration in parentheses. The opening parentheses causes the contained function to be parsed as an expression, rather than a declaration.
diff --git a/docs/src/rules/wrap-regex.md b/docs/src/rules/wrap-regex.md
index d4cff7df988..0a5daf0e1a0 100644
--- a/docs/src/rules/wrap-regex.md
+++ b/docs/src/rules/wrap-regex.md
@@ -3,7 +3,7 @@ title: wrap-regex
rule_type: layout
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
When a regular expression is used in certain situations, it can end up looking like a division operator. For example:
diff --git a/docs/src/rules/yield-star-spacing.md b/docs/src/rules/yield-star-spacing.md
index c5e47380926..aeeb0c73b2d 100644
--- a/docs/src/rules/yield-star-spacing.md
+++ b/docs/src/rules/yield-star-spacing.md
@@ -5,7 +5,7 @@ further_reading:
- https://leanpub.com/understandinges6/read/#leanpub-auto-generators
---
-
+This rule was **deprecated** in ESLint v8.53.0. Please use the corresponding rule in [`@stylistic/eslint-plugin-js`](https://eslint.style/packages/js).
## Rule Details
diff --git a/docs/src/use/command-line-interface.md b/docs/src/use/command-line-interface.md
index 59f1d2a392f..9253e9c23f3 100644
--- a/docs/src/use/command-line-interface.md
+++ b/docs/src/use/command-line-interface.md
@@ -97,7 +97,7 @@ Output:
Inline configuration comments:
--no-inline-config Prevent comments from changing config or rules
- --report-unused-disable-directives Adds reported errors for unused eslint-disable directives
+ --report-unused-disable-directives Adds reported errors for unused eslint-disable and eslint-enable directives
--report-unused-disable-directives-severity String Choose what severity level that eslint-disable directives should be reported as
Caching:
@@ -111,6 +111,7 @@ Miscellaneous:
--env-info Output execution environment information - default: false
--no-error-on-unmatched-pattern Prevent errors when pattern is unmatched
--exit-on-fatal-error Exit with exit code 2 in case of fatal error - default: false
+ --no-warn-ignored Suppress warnings when the file list includes ignored files. *Flat Config Mode Only*
--debug Output debugging information
-h, --help Show help
-v, --version Output the version number
@@ -121,7 +122,7 @@ Miscellaneous:
#### `--no-eslintrc`
-Disables use of configuration from `.eslintrc.*` and `package.json` files.
+**eslintrc Mode Only.** Disables use of configuration from `.eslintrc.*` and `package.json` files. For flat config mode, use `--no-config-lookup` instead.
* **Argument Type**: No argument.
@@ -150,7 +151,7 @@ If `.eslintrc.*` and/or `package.json` files are also used for configuration (i.
#### `--env`
-This option enables specific environments.
+**eslintrc Mode Only.** This option enables specific environments.
* **Argument Type**: String. One of the available environments.
* **Multiple Arguments**: Yes
@@ -166,7 +167,7 @@ npx eslint --env browser --env node file.js
#### `--ext`
-This option allows you to specify which file extensions ESLint uses when searching for target files in the directories you specify.
+**eslintrc Mode Only.** This option allows you to specify which file extensions ESLint uses when searching for target files in the directories you specify.
* **Argument Type**: String. File extension.
* **Multiple Arguments**: Yes
@@ -232,7 +233,7 @@ echo '3 ** 4' | npx eslint --stdin --parser-options ecmaVersion:7 # succeeds, ya
#### `--resolve-plugins-relative-to`
-Changes the directory where plugins are resolved from.
+**eslintrc Mode Only.** Changes the directory where plugins are resolved from.
* **Argument Type**: String. Path to directory.
* **Multiple Arguments**: No
@@ -374,7 +375,7 @@ npx eslint --fix --fix-type suggestion,layout .
#### `--ignore-path`
-This option allows you to specify the file to use as your `.eslintignore`.
+**eslintrc Mode Only.** This option allows you to specify the file to use as your `.eslintignore`.
* **Argument Type**: String. Path to file.
* **Multiple Arguments**: No
@@ -577,7 +578,7 @@ This option causes ESLint to report disable directive comments like `// eslint-d
* **Argument Type**: No argument.
-This can be useful to prevent future errors from unexpectedly being suppressed, by cleaning up old `eslint-disable` comments which are no longer applicable.
+This can be useful to prevent future errors from unexpectedly being suppressed, by cleaning up old `eslint-disable` and `eslint-enable` comments which are no longer applicable.
::: warning
When using this option, it is possible that new errors start being reported whenever ESLint or custom rules are upgraded.
@@ -714,6 +715,18 @@ This option causes ESLint to exit with exit code 2 if one or more fatal parsing
npx eslint --exit-on-fatal-error file.js
```
+#### `--no-warn-ignored`
+
+**Flat Config Mode Only.** This option suppresses both `File ignored by default` and `File ignored because of a matching ignore pattern` warnings when an ignored filename is passed explicitly. It is useful when paired with `--max-warnings 0` as it will prevent exit code 1 due to the aforementioned warning.
+
+* **Argument Type**: No argument.
+
+##### `--no-warn-ignored` example
+
+```shell
+npx eslint --no-warn-ignored --max-warnings 0 ignored-file.js
+```
+
#### `--debug`
This option outputs debugging information to the console. Add this flag to an ESLint command line invocation in order to get extra debugging information while the command runs.
diff --git a/docs/src/use/configure/configuration-files-new.md b/docs/src/use/configure/configuration-files-new.md
index 7693048c28b..96907fcf05f 100644
--- a/docs/src/use/configure/configuration-files-new.md
+++ b/docs/src/use/configure/configuration-files-new.md
@@ -26,11 +26,42 @@ export default [
"prefer-const": "error"
}
}
-]
+];
```
In this example, the configuration array contains just one configuration object. The configuration object enables two rules: `semi` and `prefer-const`. These rules are applied to all of the files ESLint processes using this config file.
+If your project does not specify `"type":"module"` in its `package.json` file, then `eslint.config.js` must be in CommonJS format, such as:
+
+```js
+module.exports = [
+ {
+ rules: {
+ semi: "error",
+ "prefer-const": "error"
+ }
+ }
+];
+```
+
+The configuration file can also export a promise that resolves to the configuration array. This can be useful for using ESM dependencies in CommonJS configuration files, as in this example:
+
+```js
+module.exports = (async () => {
+
+ const someDependency = await import("some-esm-dependency");
+
+ return [
+ // ... use `someDependency` here
+ ];
+
+})();
+```
+
+::: warning
+ESLint only automatically looks for a config file named `eslint.config.js` and does not look for `eslint.config.cjs` or `eslint.config.mjs`. If you'd like to specify a different config filename than the default, use the `--config` command line option.
+:::
+
## Configuration Objects
Each configuration object contains all of the information ESLint needs to execute on a set of files. Each configuration object is made up of these properties:
@@ -45,7 +76,7 @@ Each configuration object contains all of the information ESLint needs to execut
* `parserOptions` - An object specifying additional options that are passed directly to the `parse()` or `parseForESLint()` method on the parser. The available options are parser-dependent.
* `linterOptions` - An object containing settings related to the linting process.
* `noInlineConfig` - A Boolean value indicating if inline configuration is allowed.
- * `reportUnusedDisableDirectives` - A severity string indicating if and how unused disable directives should be tracked and reported. For legacy compatibility, `true` is equivalent to `"warn"` and `false` is equivalent to `"off"`. (default: `"off"`)
+ * `reportUnusedDisableDirectives` - A severity string indicating if and how unused disable and enable directives should be tracked and reported. For legacy compatibility, `true` is equivalent to `"warn"` and `false` is equivalent to `"off"`. (default: `"off"`)
* `processor` - Either an object containing `preprocess()` and `postprocess()` methods or a string indicating the name of a processor inside of a plugin (i.e., `"pluginName/processorName"`).
* `plugins` - An object containing a name-value mapping of plugin names to plugin objects. When `files` is specified, these plugins are only available to the matching files.
* `rules` - An object containing the configured rules. When `files` or `ignores` are specified, these rule configurations are only available to the matching files.
@@ -116,7 +147,9 @@ export default [
Here, the configuration object excludes files ending with `.config.js` except for `eslint.config.js`. That file still has `semi` applied.
-If `ignores` is used without `files` and any other setting, then the configuration object applies to all files except the ones specified in `ignores`, for example:
+Non-global `ignores` patterns can only match file names. A pattern like `"dir-to-exclude/"` will not ignore anything. To ignore everything in a particular directory, a pattern like `"dir-to-exclude/**"` should be used instead.
+
+If `ignores` is used without `files` and there are other keys (such as `rules`), then the configuration object applies to all files except the ones specified in `ignores`, for example:
```js
export default [
@@ -159,6 +192,9 @@ export default [
];
```
+Note that only global `ignores` patterns can match directories.
+`ignores` patterns that are specific to a configuration will only match file names.
+
#### Cascading configuration objects
When more than one configuration object matches a given filename, the configuration objects are merged with later objects overriding previous objects when there is a conflict. For example:
@@ -208,7 +244,7 @@ export default [
#### Reporting unused disable directives
-Disable directives such as `/*eslint-disable*/` and `/*eslint-disable-next-line*/` are used to disable ESLint rules around certain portions of code. As code changes, it's possible for these directives to no longer be needed because the code has changed in such a way that the rule is no longer triggered. You can enable reporting of these unused disable directives by setting the `reportUnusedDisableDirectives` option to a severity string, as in this example:
+Disable and enable directives such as `/*eslint-disable*/` and `/*eslint-disable-next-line*/` are used to disable ESLint rules around certain portions of code. As code changes, it's possible for these directives to no longer be needed because the code has changed in such a way that the rule is no longer triggered. You can enable reporting of these unused disable directives by setting the `reportUnusedDisableDirectives` option to a severity string, as in this example:
```js
export default [
@@ -350,7 +386,6 @@ Apart from the ECMAScript standard built-in globals, which are automatically ena
```js
import globals from "globals";
-
export default [
{
languageOptions: {
@@ -442,7 +477,7 @@ import jsdoc from "eslint-plugin-jsdoc";
export default [
// configuration included in plugin
- jsdoc.configs.recommended,
+ jsdoc.configs["flat/recommended"],
// other configuration objects...
{
files: ["**/*.js"],
@@ -638,7 +673,7 @@ When ESLint is run on the command line, it first checks the current working dire
You can prevent this search for `eslint.config.js` by setting the `ESLINT_USE_FLAT_CONFIG` environment variable to `true` and using the `-c` or `--config` option on the command line to specify an alternate configuration file, such as:
```shell
-ESLINT_USE_FLAT_CONFIG=true npx eslint -c some-other-file.js **/*.js
+ESLINT_USE_FLAT_CONFIG=true npx eslint --config some-other-file.js **/*.js
```
In this case, ESLint does not search for `eslint.config.js` and instead uses `some-other-file.js`.
diff --git a/docs/src/use/configure/ignore.md b/docs/src/use/configure/ignore.md
index ffc23428ee0..16f1bfbcdc9 100644
--- a/docs/src/use/configure/ignore.md
+++ b/docs/src/use/configure/ignore.md
@@ -149,7 +149,7 @@ You'll see this warning:
```text
foo.js
- 0:0 warning File ignored because of a matching ignore pattern. Use "--no-ignore" to override.
+ 0:0 warning File ignored because of a matching ignore pattern. Use "--no-ignore" to disable file ignore settings or use "--no-warn-ignored" to suppress this warning.
✖ 1 problem (0 errors, 1 warning)
```
diff --git a/docs/src/use/configure/language-options.md b/docs/src/use/configure/language-options.md
index 4f8fa148bce..4bcd63e3c98 100644
--- a/docs/src/use/configure/language-options.md
+++ b/docs/src/use/configure/language-options.md
@@ -26,6 +26,8 @@ An environment provides predefined global variables. The available environments
* `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.
* `es2022` - adds all ECMAScript 2022 globals and automatically sets the `ecmaVersion` parser option to 13.
+* `es2023` - adds all ECMAScript 2023 globals and automatically sets the `ecmaVersion` parser option to 14.
+* `es2024` - adds all ECMAScript 2024 globals and automatically sets the `ecmaVersion` parser option to 15.
* `worker` - web workers global variables.
* `amd` - defines `require()` and `define()` as global variables as per the [amd](https://github.com/amdjs/amdjs-api/blob/master/AMD.md) spec.
* `mocha` - adds all of the Mocha testing global variables.
@@ -189,11 +191,11 @@ ESLint allows you to specify the JavaScript language options you want to support
Please note that supporting JSX syntax is not the same as supporting React. React applies specific semantics to JSX syntax that ESLint doesn't recognize. We recommend using [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) if you are using React.
-By the same token, supporting ES6 syntax is not the same as supporting new ES6 globals (e.g., new types such as `Set`). For ES6 syntax, use `{ "parserOptions": { "ecmaVersion": 6 } }`; for new ES6 global variables, use `{ "env": { "es6": true } }`. Setting `{ "env": { "es6": true } }` enables ES6 syntax automatically, but `{ "parserOptions": { "ecmaVersion": 6 } }` does not enable ES6 globals automatically.
+By the same token, supporting ES6 syntax is not the same as supporting new ES6 globals (e.g., new types such as `Set`). For ES6 syntax, use `{ "parserOptions": { "ecmaVersion": 6 } }`; for new ES6 global variables, use `{ "env": { "es6": true } }`. Setting `{ "env": { "es6": true } }` enables ES6 syntax automatically, but `{ "parserOptions": { "ecmaVersion": 6 } }` does not enable ES6 globals automatically. In summary, to support only ES6 syntax, use `{ "parserOptions": { "ecmaVersion": 6 } }`, and to support both ES6 syntax and new ES6 global variables, such as `Set` and others, use `{ "env": { "es6": true } }`.
Parser options are set in your `.eslintrc.*` file with the `parserOptions` property. The available options are:
-* `ecmaVersion` - set to 3, 5 (default), 6, 7, 8, 9, 10, 11, 12, 13, or 14 to specify the version of ECMAScript syntax you want to use. You can also set it 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), 2021 (same as 12), 2022 (same as 13), or 2023 (same as 14) to use the year-based naming. You can also set `"latest"` to use the most recently supported version.
+* `ecmaVersion` - set to 3, 5 (default), 6, 7, 8, 9, 10, 11, 12, 13, 14, or 15 to specify the version of ECMAScript syntax you want to use. You can also set it 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), 2021 (same as 12), 2022 (same as 13), 2023 (same as 14), or 2024 (same as 15) to use the year-based naming. You can also set `"latest"` to use the most recently supported version.
* `sourceType` - set to `"script"` (default) or `"module"` if your code is in ECMAScript modules.
* `allowReserved` - allow the use of reserved words as identifiers (if `ecmaVersion` is 3).
* `ecmaFeatures` - an object indicating which additional language features you'd like to use:
diff --git a/docs/src/use/configure/migration-guide.md b/docs/src/use/configure/migration-guide.md
new file mode 100644
index 00000000000..d89a8347dfc
--- /dev/null
+++ b/docs/src/use/configure/migration-guide.md
@@ -0,0 +1,614 @@
+---
+title: Configuration Migration Guide
+eleventyNavigation:
+ key: migration guide
+ parent: configure
+ title: Configuration Migration Guide
+ order: 8
+---
+
+This guide provides an overview of how you can migrate your ESLint configuration file from the eslintrc format (typically configured in `.eslintrc.js` or `.eslintrc.json` files) to the new flat config format (typically configured in an `eslint.config.js` file).
+
+To learn more about the flat config format, refer to [this blog post](https://eslint.org/blog/2022/08/new-config-system-part-2/).
+
+For reference information on these configuration formats, refer to the following documentation:
+
+* [eslintrc configuration files](configuration-files)
+* [flat configuration files](configuration-files-new)
+
+## Start Using Flat Config Files
+
+Starting with ESLint v9.0.0, the flat config file format will be the default configuration file format. Once ESLint v9.0.0 is released, you can start using the flat config file format without any additional configuration.
+
+To use flat config with ESLint v8, place a `eslint.config.js` file in the root of your project **or** set the `ESLINT_USE_FLAT_CONFIG` environment variable to `true`.
+
+## Things That Haven’t Changed between Configuration File Formats
+
+While the configuration file format has changed from eslintrc to flat config, the following has stayed the same:
+
+* Syntax for configuring rules
+* Syntax for configuring processors
+* The CLI, except for the flag changes noted in [CLI Flag Changes](#cli-flag-changes).
+* Global variables are configured the same way, but on a different property (see [Configuring Language Options](#configuring-language-options)).
+
+## Key Differences between Configuration Formats
+
+A few of the most notable differences between the eslintrc and flat config formats are the following:
+
+### Importing Plugins and Custom Parsers
+
+Eslintrc files use string-based import system inside the `plugins` property to load plugins and inside the `extends` property to load external configurations.
+
+Flat config files represent plugins and parsers as JavaScript objects. This means you can use CommonJS `require()` or ES module `import` statements to load plugins and custom parsers from external files.
+
+For example, this eslintrc config file loads `eslint-plugin-jsdoc` and configures rules from that plugin:
+
+```javascript
+// .eslintrc.js
+
+module.exports = {
+ // ...other config
+ plugins: ["jsdoc"],
+ rules: {
+ "jsdoc/require-description": "error",
+ "jsdoc/check-values": "error"
+ }
+ // ...other config
+};
+```
+
+In flat config, you would do the same thing like this:
+
+```javascript
+// eslint.config.js
+
+import jsdoc from "eslint-plugin-jsdoc";
+
+export default [
+ {
+ files: ["**/*.js"],
+ plugins: {
+ jsdoc: jsdoc
+ },
+ rules: {
+ "jsdoc/require-description": "error",
+ "jsdoc/check-values": "error"
+ }
+ }
+];
+```
+
+### Custom Parsers
+
+In eslintrc files, importing a custom parser is similar to importing a plugin: you use a string to specify the name of the parser.
+
+In flat config files, import a custom parser as a module, then assign it to the `languageOptions.parser` property of a configuration object.
+
+For example, this eslintrc config file uses the `@babel/eslint-parser` parser:
+
+```javascript
+// .eslintrc.js
+
+module.exports = {
+ // ...other config
+ parser: "@babel/eslint-parser",
+ // ...other config
+};
+```
+
+In flat config, you would do the same thing like this:
+
+```javascript
+// eslint.config.js
+
+import babelParser from "@babel/eslint-parser";
+
+export default [
+ {
+ // ...other config
+ languageOptions: {
+ parser: babelParser
+ }
+ // ...other config
+ }
+];
+```
+
+### Processors
+
+In eslintrc files, processors had to be defined in a plugin, and then referenced by name in the configuration. Processors beginning with a dot indicated a [file extension-named processor](../../extend/custom-processors#file-extension-named-processor) which ESLint would automatically configure for that file extension.
+
+In flat config files, processors can still be referenced from plugins by their name, but they can now also be inserted directly into the configuration. Processors will _never_ be automatically configured, and must be explicitly set in the configuration.
+
+As an example with a custom plugin with processors:
+
+```javascript
+// node_modules/eslint-plugin-someplugin/index.js
+module.exports = {
+ processors: {
+ ".md": {
+ preprocess() {},
+ postprocess() {}
+ },
+ "someProcessor": {
+ preprocess() {},
+ postprocess() {}
+ }
+ }
+};
+```
+
+In eslintrc, you would configure as follows:
+
+```javascript
+// .eslintrc.js
+module.exports = {
+ plugins: ["someplugin"],
+ processor: "someplugin/someProcessor"
+};
+```
+
+ESLint would also automatically add the equivalent of the following:
+
+```javascript
+{
+ overrides: [{
+ files: ["**/*.md"],
+ processor: "someplugin/.md"
+ }]
+}
+```
+
+In flat config, the following are all valid ways to express the same:
+
+```javascript
+// eslint.config.js
+import somePlugin from "eslint-plugin-someplugin";
+
+export default [
+ {
+ plugins: { somePlugin },
+ processor: "somePlugin/someProcessor"
+ },
+ {
+ plugins: { somePlugin },
+ // We can embed the processor object in the config directly
+ processor: somePlugin.processors.someProcessor
+ },
+ {
+ // We don't need the plugin to be present in the config to use the processor directly
+ processor: somePlugin.processors.someProcessor
+ }
+];
+```
+
+Note that because the `.md` processor is _not_ automatically added by flat config, you also need to specify an extra configuration element:
+
+```javascript
+{
+ files: ["**/*.md"],
+ processor: somePlugin.processors[".md"]
+}
+```
+
+### Glob-Based Configs
+
+By default, eslintrc files lint all files (except those covered by `.eslintignore`) in the directory in which they’re placed and its child directories. If you want to have different configurations for different file glob patterns, you can specify them in the `overrides` property.
+
+By default, flat config files support different glob pattern-based configs in exported array. You can include the glob pattern in a config object's `files` property. If you don't specify a `files` property, the config defaults to the glob pattern `"**/*.{js,mjs,cjs}"`. Basically, all configuration in the flat config file is like the eslintrc `overrides` property.
+
+#### eslintrc Examples
+
+For example, this eslintrc file applies to all files in the directory where it is placed and its child directories:
+
+```javascript
+// .eslintrc.js
+
+module.exports = {
+ // ...other config
+ rules: {
+ semi: ["warn", "always"]
+ }
+};
+```
+
+This eslintrc file supports multiple configs with overrides:
+
+```javascript
+// .eslintrc.js
+
+module.exports = {
+ // ...other config
+ overrides: [
+ {
+ files: ["src/**/*"],
+ rules: {
+ semi: ["warn", "always"]
+ }
+ },
+ {
+ files:["test/**/*"],
+ rules: {
+ "no-console": "off"
+ }
+ }
+ ]
+};
+```
+
+For flat config, here is a configuration with the default glob pattern:
+
+```javascript
+// eslint.config.js
+
+import js from "@eslint/js";
+
+export default [
+ js.configs.recommended, // Recommended config applied to all files
+ // Override the recommended config
+ {
+ rules: {
+ indent: ["error", 2],
+ "no-unused-vars": "warn"
+ }
+ // ...other configuration
+ }
+];
+```
+
+A flag config example configuration supporting multiple configs for different glob patterns:
+
+```javascript
+// eslint.config.js
+
+import js from "@eslint/js";
+
+export default [
+ js.configs.recommended, // Recommended config applied to all files
+ // File-pattern specific overrides
+ {
+ files: ["src/**/*", "test/**/*"],
+ rules: {
+ semi: ["warn", "always"]
+ }
+ },
+ {
+ files:["test/**/*"],
+ rules: {
+ "no-console": "off"
+ }
+ }
+ // ...other configurations
+];
+```
+
+### Configuring Language Options
+
+In eslintrc files, you configure various language options across the `env`, `globals` and `parserOptions` properties. Groups of global variables for specific runtimes (e.g. `document` and `window` for browser JavaScript; `process` and `require` for Node.js) are configured with the `env` property.
+
+In flat config files, the `globals`, and `parserOptions` are consolidated under the `languageOptions` key; the `env` property doesn't exist. Groups of global variables for specific runtimes are imported from the [globals](https://www.npmjs.com/package/globals) npm package and included in the `globals` property. You can use the spread operator (`...`) to import multiple globals at once.
+
+For example, here's an eslintrc file with language options:
+
+```javascript
+// .eslintrc.js
+
+module.exports = {
+ env: {
+ browser: true,
+ },
+ globals: {
+ myCustomGlobal: "readonly",
+ },
+ parserOptions: {
+ ecmaVersion: 2022,
+ sourceType: "module"
+ }
+ // ...other config
+}
+```
+
+Here's the same configuration in flat config:
+
+```javascript
+// eslint.config.js
+
+import globals from "globals";
+
+export default [
+ {
+ languageOptions: {
+ globals: {
+ ...globals.browser,
+ myCustomGlobal: "readonly"
+ },
+ parserOptions: {
+ ecmaVersion: 2022,
+ sourceType: "module"
+ }
+ }
+ // ...other config
+ }
+];
+```
+
+### `eslint-env` Configuration Comments
+
+In the eslintrc config system it was possible to use `eslint-env` configuration comments to define globals for a file.
+These comments are no longer recognized when linting with flat config: in a future version of ESLint, `eslint-env` comments will be reported as errors.
+For this reason, when migrating from eslintrc to flat config, `eslint-env` configuration comments should be removed from all files.
+They can be either replaced with equivalent but more verbose `global` configuration comments, or dropped in favor of `globals` definitions in the config file.
+
+For example, when using eslintrc, a file to be linted could look like this:
+
+```javascript
+// tests/my-file.js
+
+/* eslint-env mocha */
+
+describe("unit tests", () => {
+ it("should pass", () => {
+ // ...
+ });
+});
+```
+
+In the above example, `describe` and `it` would be recognized as global identifiers because of the `/* eslint-env mocha */` comment.
+
+The same effect can be achieved with flat config with a `global` configuration comment, e.g.:
+
+```javascript
+// tests/my-file.js
+
+/* global describe, it -- Globals defined by Mocha */
+
+describe("unit tests", () => {
+ it("should pass", () => {
+ // ...
+ });
+});
+```
+
+Another option is to remove the comment from the file being linted and define the globals in the configuration, for example:
+
+```javascript
+// eslint.config.js
+
+import globals from "globals";
+
+export default [
+ // ...other config
+ {
+ files: [
+ "tests/**"
+ ],
+ languageOptions: {
+ globals: {
+ ...globals.mocha
+ }
+ }
+ }
+];
+```
+
+### Predefined and Shareable Configs
+
+In eslintrc files, use the `extends` property to use predefined and shareable configs. ESLint comes with two predefined configs that you can access as strings:
+
+* `"eslint:recommended"`: the rules recommended by ESLint
+* `"eslint:all"`: all rules shipped with ESLint
+
+You can also use the `extends` property to extend a shareable config. Shareable configs can either be paths to local config files or npm package names.
+
+In flat config files, predefined configs are imported from separate modules into flat config files. The `recommended` and `all` rules configs are located in the [`@eslint/js`](https://www.npmjs.com/package/@eslint/js) package. You must import this package to use these configs:
+
+```shell
+npm install @eslint/js --save-dev
+```
+
+You can add each of these configs to the exported array or expose specific rules from them. You must import the modules for local config files and npm package configs with flat config.
+
+For example, here's an eslintrc file using the built-in `eslint:recommended` config:
+
+```javascript
+// .eslintrc.js
+
+module.exports = {
+ // ...other config
+ extends: "eslint:recommended",
+ rules: {
+ semi: ["warn", "always"]
+ },
+ // ...other config
+}
+```
+
+This eslintrc file uses built-in config, local custom config, and shareable config from an npm package:
+
+```javascript
+// .eslintrc.js
+
+module.exports = {
+ // ...other config
+ extends: ["eslint:recommended", "./custom-config.js", "eslint-config-my-config"],
+ rules: {
+ semi: ["warn", "always"]
+ },
+ // ...other config
+}
+```
+
+To use the same configs in flat config, you would do the following:
+
+```javascript
+// eslint.config.js
+
+import js from "@eslint/js";
+import customConfig from "./custom-config.js";
+import myConfig from "eslint-config-my-config";
+
+export default [
+ js.configs.recommended,
+ customConfig,
+ myConfig,
+ {
+ rules: {
+ semi: ["warn", "always"]
+ },
+ // ...other config
+ }
+];
+```
+
+Note that because you are just importing JavaScript modules, you can mutate the config objects before ESLint uses them. For example, you might want to have a certain config object only apply to your test files:
+
+```javascript
+// eslint.config.js
+
+import js from "@eslint/js";
+import customTestConfig from "./custom-test-config.js";
+
+export default [
+ js.configs.recommended,
+ {
+ ...customTestConfig,
+ files: ["**/*.test.js"],
+ },
+];
+```
+
+#### Using eslintrc Configs in Flat Config
+
+You may find that there's a shareable config you rely on that hasn't yet been updated to flat config format. In that case, you can use the `FlatCompat` utility to translate the eslintrc format into flat config format. First, install the `@eslint/eslintrc` package:
+
+```shell
+npm install @eslint/eslintrc --save-dev
+```
+
+Then, import `FlatCompat` and create a new instance to convert an existing eslintrc config. For example, if the npm package `eslint-config-my-config` is in eslintrc format, you can write this:
+
+```js
+import { FlatCompat } from "@eslint/eslintrc";
+import path from "path";
+import { fileURLToPath } from "url";
+
+// mimic CommonJS variables -- not needed if using CommonJS
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+const compat = new FlatCompat({
+ baseDirectory: __dirname
+});
+
+export default [
+
+ // mimic ESLintRC-style extends
+ ...compat.extends("eslint-config-my-config"),
+];
+```
+
+This example uses the `FlatCompat#extends()` method to insert the `eslint-config-my-config` into the flat config array.
+
+For more information about the `FlatCompat` class, please see the [package README](https://github.com/eslint/eslintrc#usage).
+
+### Ignoring Files
+
+With eslintrc, you can make ESLint ignore files by creating a separate `.eslintignore` file in the root of your project. The `.eslintignore` file uses the same glob pattern syntax as `.gitignore` files. Alternatively, you can use an `ignorePatterns` property in your eslintrc file.
+
+To ignore files with flat config, you can use the `ignores` property in a config object. The `ignores` property accepts an array of glob patterns. Flat config does not support loading ignore patterns from `.eslintignore` files, so you'll need to migrate those patterns directly into flat config.
+
+For example, here's a `.eslintignore` example you can use with an eslintrc config:
+
+```shell
+# .eslintignore
+temp.js
+config/*
+# ...other ignored files
+```
+
+`ignorePatterns` example:
+
+```javascript
+// .eslintrc.js
+module.exports = {
+ // ...other config
+ ignorePatterns: ["temp.js", "config/*"],
+};
+```
+
+Here are the same files ignore patterns in flat config:
+
+```javascript
+export default [
+ // ...other config
+ {
+ ignores: ["**/temp.js", "config/*"]
+ }
+];
+```
+
+Also, with flat config, dotfiles (e.g. `.dotfile.js`) are no longer ignored by default. If you want to ignore dotfiles, add files ignore pattern `"**/.*"`.
+
+### Linter Options
+
+ESlintrc files let you configure the linter itself with the `noInlineConfig` and `reportUnusedDisableDirectives` properties.
+
+The flat config system introduces a new top-level property `linterOptions` that you can use to configure the linter. In the `linterOptions` object, you can include `noInlineConfig` and `reportUnusedDisableDirectives`.
+
+For example, here's an eslintrc file with linter options enabled:
+
+```javascript
+// .eslintrc.js
+
+module.exports = {
+ // ...other config
+ noInlineConfig: true,
+ reportUnusedDisableDirectives: true
+}
+```
+
+Here's the same options in flat config:
+
+```javascript
+// eslint.config.js
+
+export default [
+ {
+ // ...other config
+ linterOptions: {
+ noInlineConfig: true,
+ reportUnusedDisableDirectives: true
+ }
+ }
+];
+```
+
+### CLI Flag Changes
+
+The following CLI flags are no longer supported with the flat config file format:
+
+* `--rulesdir`
+* `--ext`
+* `--resolve-plugins-relative-to`
+
+The flag `--no-eslintrc` has been replaced with `--no-config-lookup`.
+
+### Additional Changes
+
+The following changes have been made from the eslintrc to the flat config file format:
+
+* The `root` option no longer exists. (Flat config files act as if `root: true` is set.)
+* The `files` option cannot be a single string anymore, it must be an array.
+* The `sourceType` option now supports the new value `"commonjs"` (`.eslintrc` supports it too, but it was never documented).
+
+## TypeScript Types for Flat Config Files
+
+You can see the TypeScript types for the flat config file format in the DefinitelyTyped project. The interface for the objects in the config’s array is called the `FlatConfig`.
+
+You can view the type definitions in the [DefinitelyTyped repository on Github](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/eslint/index.d.ts).
+
+## Further Reading
+
+* [Overview of the flat config file format blog post](https://eslint.org/blog/2022/08/new-config-system-part-2/)
+* [API usage of new configuration system blog post](https://eslint.org/blog/2022/08/new-config-system-part-3/)
+* [Background to new configuration system blog post](https://eslint.org/blog/2022/08/new-config-system-part-1/)
diff --git a/docs/src/use/configure/rules.md b/docs/src/use/configure/rules.md
index 2be606fb2af..dffe4232052 100644
--- a/docs/src/use/configure/rules.md
+++ b/docs/src/use/configure/rules.md
@@ -144,6 +144,17 @@ You can also use this format with configuration comments, such as:
### Using configuration comments
+* **Use with Caution.** Disabling ESLint rules inline should be restricted and used only in situations with a clear and
+ valid reason for doing so. Disabling rules inline should not be the default solution to resolve linting errors.
+* **Document the Reason.** Provide a comment explaining the reason for disabling a particular rule after the `--` section of the comment. This
+ documentation should clarify why the rule is being disabled and why it is necessary in that specific situation.
+* **Temporary Solutions.** If a disable comment is added as a temporary measure to address a pressing issue, create a follow-up task to address the underlying problem adequately. This ensures that the
+ disable comment is revisited and resolved at a later stage.
+* **Code Reviews and Pair Programming.** Encourage team members to review each other's code regularly. Code reviews can help
+ identify the reasons behind disable comments and ensure that they are used appropriately.
+* **Configurations.** Whenever possible, prefer using ESLint configuration files over disable comments. Configuration
+ files allow for consistent and project-wide rule handling.
+
To disable rule warnings in a part of a file, use block comments in the following format:
```js
diff --git a/docs/src/use/core-concepts.md b/docs/src/use/core-concepts.md
index b9dfbbbfa1c..b60ad3b0b0e 100644
--- a/docs/src/use/core-concepts.md
+++ b/docs/src/use/core-concepts.md
@@ -23,6 +23,23 @@ ESLint contains hundreds of built-in rules that you can use. You can also create
For more information, refer to [Rules](../rules/).
+### Rule Fixes
+
+Rules may optionally provide fixes for violations that they find. Fixes safely correct the violation without changing application logic.
+
+Fixes may be applied automatically with the [`--fix` command line option](https://eslint.org/docs/latest/use/command-line-interface#--fix) and via editor extensions.
+
+Rules that may provide fixes are marked with 🔧 in [Rules](../rules/).
+
+### Rule Suggestions
+
+Rules may optionally provide suggestions in addition to or instead of providing fixes. Suggestions differ from fixes in two ways:
+
+1. Suggestions may change application logic and so cannot be automatically applied.
+1. Suggestions cannot be applied through the ESLint CLI and are only available through editor integrations.
+
+Rules that may provide suggestions are marked with 💡 in [Rules](../rules/).
+
## Configuration Files
An ESLint configuration file is a place where you put the configuration for ESLint in your project. You can include built-in rules, how you want them enforced, plugins with custom rules, shareable configurations, which files you want rules to apply to, and more.
diff --git a/docs/src/use/formatters/html-formatter-example.html b/docs/src/use/formatters/html-formatter-example.html
index 1bc28e19389..03dc7dcefef 100644
--- a/docs/src/use/formatters/html-formatter-example.html
+++ b/docs/src/use/formatters/html-formatter-example.html
@@ -118,7 +118,7 @@
ESLint Report
- 9 problems (5 errors, 4 warnings) - Generated on Fri May 19 2023 16:52:13 GMT-0400 (Eastern Daylight Time)
+ 9 problems (5 errors, 4 warnings) - Generated on Fri Nov 03 2023 19:23:39 GMT-0400 (Eastern Daylight Time)
diff --git a/docs/src/use/formatters/index.md b/docs/src/use/formatters/index.md
index ec6ce537ed7..6d853e16785 100644
--- a/docs/src/use/formatters/index.md
+++ b/docs/src/use/formatters/index.md
@@ -71,7 +71,7 @@ Outputs results to the [Checkstyle](https://checkstyle.sourceforge.io/) format.
Example output:
-```text
+```xml
```
@@ -109,7 +109,7 @@ Outputs results to format compatible with the [JSLint Jenkins plugin](https://pl
Example output:
-```text
+```xml
```
@@ -119,10 +119,601 @@ Outputs JSON-serialized results. The `json-with-metadata` provides the same lint
Alternatively, you can use the [ESLint Node.js API](../../integrate/nodejs-api) to programmatically use ESLint.
-Example output:
+Example output (formatted for easier reading):
-```text
-{"results":[{"filePath":"/var/lib/jenkins/workspace/Releases/eslint Release/eslint/fullOfProblems.js","messages":[{"ruleId":"no-unused-vars","severity":2,"message":"'addOne' is defined but never used.","line":1,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":16},{"ruleId":"use-isnan","severity":2,"message":"Use the isNaN function to compare with NaN.","line":2,"column":9,"nodeType":"BinaryExpression","messageId":"comparisonWithNaN","endLine":2,"endColumn":17},{"ruleId":"space-unary-ops","severity":2,"message":"Unexpected space before unary operator '++'.","line":3,"column":16,"nodeType":"UpdateExpression","messageId":"unexpectedBefore","endLine":3,"endColumn":20,"fix":{"range":[57,58],"text":""}},{"ruleId":"semi","severity":1,"message":"Missing semicolon.","line":3,"column":20,"nodeType":"ReturnStatement","messageId":"missingSemi","endLine":4,"endColumn":1,"fix":{"range":[60,60],"text":";"}},{"ruleId":"no-else-return","severity":1,"message":"Unnecessary 'else' after 'return'.","line":4,"column":12,"nodeType":"BlockStatement","messageId":"unexpected","endLine":6,"endColumn":6,"fix":{"range":[0,94],"text":"function addOne(i) {\n if (i != NaN) {\n return i ++\n } \n return\n \n}"}},{"ruleId":"indent","severity":1,"message":"Expected indentation of 8 spaces but found 6.","line":5,"column":1,"nodeType":"Keyword","messageId":"wrongIndentation","endLine":5,"endColumn":7,"fix":{"range":[74,80],"text":" "}},{"ruleId":"consistent-return","severity":2,"message":"Function 'addOne' expected a return value.","line":5,"column":7,"nodeType":"ReturnStatement","messageId":"missingReturnValue","endLine":5,"endColumn":13},{"ruleId":"semi","severity":1,"message":"Missing semicolon.","line":5,"column":13,"nodeType":"ReturnStatement","messageId":"missingSemi","endLine":6,"endColumn":1,"fix":{"range":[86,86],"text":";"}},{"ruleId":"no-extra-semi","severity":2,"message":"Unnecessary semicolon.","line":7,"column":2,"nodeType":"EmptyStatement","messageId":"unexpected","endLine":7,"endColumn":3,"fix":{"range":[93,95],"text":"}"}}],"suppressedMessages":[],"errorCount":5,"fatalErrorCount":0,"warningCount":4,"fixableErrorCount":2,"fixableWarningCount":4,"source":"function addOne(i) {\n if (i != NaN) {\n return i ++\n } else {\n return\n }\n};"}],"metadata":{"rulesMeta":{"no-else-return":{"type":"suggestion","docs":{"description":"Disallow `else` blocks after `return` statements in `if` statements","recommended":false,"url":"https://eslint.org/docs/latest/rules/no-else-return"},"schema":[{"type":"object","properties":{"allowElseIf":{"type":"boolean","default":true}},"additionalProperties":false}],"fixable":"code","messages":{"unexpected":"Unnecessary 'else' after 'return'."}},"indent":{"type":"layout","docs":{"description":"Enforce consistent indentation","recommended":false,"url":"https://eslint.org/docs/latest/rules/indent"},"fixable":"whitespace","schema":[{"oneOf":[{"enum":["tab"]},{"type":"integer","minimum":0}]},{"type":"object","properties":{"SwitchCase":{"type":"integer","minimum":0,"default":0},"VariableDeclarator":{"oneOf":[{"oneOf":[{"type":"integer","minimum":0},{"enum":["first","off"]}]},{"type":"object","properties":{"var":{"oneOf":[{"type":"integer","minimum":0},{"enum":["first","off"]}]},"let":{"oneOf":[{"type":"integer","minimum":0},{"enum":["first","off"]}]},"const":{"oneOf":[{"type":"integer","minimum":0},{"enum":["first","off"]}]}},"additionalProperties":false}]},"outerIIFEBody":{"oneOf":[{"type":"integer","minimum":0},{"enum":["off"]}]},"MemberExpression":{"oneOf":[{"type":"integer","minimum":0},{"enum":["off"]}]},"FunctionDeclaration":{"type":"object","properties":{"parameters":{"oneOf":[{"type":"integer","minimum":0},{"enum":["first","off"]}]},"body":{"type":"integer","minimum":0}},"additionalProperties":false},"FunctionExpression":{"type":"object","properties":{"parameters":{"oneOf":[{"type":"integer","minimum":0},{"enum":["first","off"]}]},"body":{"type":"integer","minimum":0}},"additionalProperties":false},"StaticBlock":{"type":"object","properties":{"body":{"type":"integer","minimum":0}},"additionalProperties":false},"CallExpression":{"type":"object","properties":{"arguments":{"oneOf":[{"type":"integer","minimum":0},{"enum":["first","off"]}]}},"additionalProperties":false},"ArrayExpression":{"oneOf":[{"type":"integer","minimum":0},{"enum":["first","off"]}]},"ObjectExpression":{"oneOf":[{"type":"integer","minimum":0},{"enum":["first","off"]}]},"ImportDeclaration":{"oneOf":[{"type":"integer","minimum":0},{"enum":["first","off"]}]},"flatTernaryExpressions":{"type":"boolean","default":false},"offsetTernaryExpressions":{"type":"boolean","default":false},"ignoredNodes":{"type":"array","items":{"type":"string","not":{"pattern":":exit$"}}},"ignoreComments":{"type":"boolean","default":false}},"additionalProperties":false}],"messages":{"wrongIndentation":"Expected indentation of {{expected}} but found {{actual}}."}},"space-unary-ops":{"type":"layout","docs":{"description":"Enforce consistent spacing before or after unary operators","recommended":false,"url":"https://eslint.org/docs/latest/rules/space-unary-ops"},"fixable":"whitespace","schema":[{"type":"object","properties":{"words":{"type":"boolean","default":true},"nonwords":{"type":"boolean","default":false},"overrides":{"type":"object","additionalProperties":{"type":"boolean"}}},"additionalProperties":false}],"messages":{"unexpectedBefore":"Unexpected space before unary operator '{{operator}}'.","unexpectedAfter":"Unexpected space after unary operator '{{operator}}'.","unexpectedAfterWord":"Unexpected space after unary word operator '{{word}}'.","wordOperator":"Unary word operator '{{word}}' must be followed by whitespace.","operator":"Unary operator '{{operator}}' must be followed by whitespace.","beforeUnaryExpressions":"Space is required before unary expressions '{{token}}'."}},"semi":{"type":"layout","docs":{"description":"Require or disallow semicolons instead of ASI","recommended":false,"url":"https://eslint.org/docs/latest/rules/semi"},"fixable":"code","schema":{"anyOf":[{"type":"array","items":[{"enum":["never"]},{"type":"object","properties":{"beforeStatementContinuationChars":{"enum":["always","any","never"]}},"additionalProperties":false}],"minItems":0,"maxItems":2},{"type":"array","items":[{"enum":["always"]},{"type":"object","properties":{"omitLastInOneLineBlock":{"type":"boolean"},"omitLastInOneLineClassBody":{"type":"boolean"}},"additionalProperties":false}],"minItems":0,"maxItems":2}]},"messages":{"missingSemi":"Missing semicolon.","extraSemi":"Extra semicolon."}},"consistent-return":{"type":"suggestion","docs":{"description":"Require `return` statements to either always or never specify values","recommended":false,"url":"https://eslint.org/docs/latest/rules/consistent-return"},"schema":[{"type":"object","properties":{"treatUndefinedAsUnspecified":{"type":"boolean","default":false}},"additionalProperties":false}],"messages":{"missingReturn":"Expected to return a value at the end of {{name}}.","missingReturnValue":"{{name}} expected a return value.","unexpectedReturnValue":"{{name}} expected no return value."}}}}}
+```json
+{
+ "results": [
+ {
+ "filePath": "/var/lib/jenkins/workspace/Releases/eslint Release/eslint/fullOfProblems.js",
+ "messages": [
+ {
+ "ruleId": "no-unused-vars",
+ "severity": 2,
+ "message": "'addOne' is defined but never used.",
+ "line": 1,
+ "column": 10,
+ "nodeType": "Identifier",
+ "messageId": "unusedVar",
+ "endLine": 1,
+ "endColumn": 16
+ },
+ {
+ "ruleId": "use-isnan",
+ "severity": 2,
+ "message": "Use the isNaN function to compare with NaN.",
+ "line": 2,
+ "column": 9,
+ "nodeType": "BinaryExpression",
+ "messageId": "comparisonWithNaN",
+ "endLine": 2,
+ "endColumn": 17
+ },
+ {
+ "ruleId": "space-unary-ops",
+ "severity": 2,
+ "message": "Unexpected space before unary operator '++'.",
+ "line": 3,
+ "column": 16,
+ "nodeType": "UpdateExpression",
+ "messageId": "unexpectedBefore",
+ "endLine": 3,
+ "endColumn": 20,
+ "fix": {
+ "range": [
+ 57,
+ 58
+ ],
+ "text": ""
+ }
+ },
+ {
+ "ruleId": "semi",
+ "severity": 1,
+ "message": "Missing semicolon.",
+ "line": 3,
+ "column": 20,
+ "nodeType": "ReturnStatement",
+ "messageId": "missingSemi",
+ "endLine": 4,
+ "endColumn": 1,
+ "fix": {
+ "range": [
+ 60,
+ 60
+ ],
+ "text": ";"
+ }
+ },
+ {
+ "ruleId": "no-else-return",
+ "severity": 1,
+ "message": "Unnecessary 'else' after 'return'.",
+ "line": 4,
+ "column": 12,
+ "nodeType": "BlockStatement",
+ "messageId": "unexpected",
+ "endLine": 6,
+ "endColumn": 6,
+ "fix": {
+ "range": [
+ 0,
+ 94
+ ],
+ "text": "function addOne(i) {\n if (i != NaN) {\n return i ++\n } \n return\n \n}"
+ }
+ },
+ {
+ "ruleId": "indent",
+ "severity": 1,
+ "message": "Expected indentation of 8 spaces but found 6.",
+ "line": 5,
+ "column": 1,
+ "nodeType": "Keyword",
+ "messageId": "wrongIndentation",
+ "endLine": 5,
+ "endColumn": 7,
+ "fix": {
+ "range": [
+ 74,
+ 80
+ ],
+ "text": " "
+ }
+ },
+ {
+ "ruleId": "consistent-return",
+ "severity": 2,
+ "message": "Function 'addOne' expected a return value.",
+ "line": 5,
+ "column": 7,
+ "nodeType": "ReturnStatement",
+ "messageId": "missingReturnValue",
+ "endLine": 5,
+ "endColumn": 13
+ },
+ {
+ "ruleId": "semi",
+ "severity": 1,
+ "message": "Missing semicolon.",
+ "line": 5,
+ "column": 13,
+ "nodeType": "ReturnStatement",
+ "messageId": "missingSemi",
+ "endLine": 6,
+ "endColumn": 1,
+ "fix": {
+ "range": [
+ 86,
+ 86
+ ],
+ "text": ";"
+ }
+ },
+ {
+ "ruleId": "no-extra-semi",
+ "severity": 2,
+ "message": "Unnecessary semicolon.",
+ "line": 7,
+ "column": 2,
+ "nodeType": "EmptyStatement",
+ "messageId": "unexpected",
+ "endLine": 7,
+ "endColumn": 3,
+ "fix": {
+ "range": [
+ 93,
+ 95
+ ],
+ "text": "}"
+ }
+ }
+ ],
+ "suppressedMessages": [],
+ "errorCount": 5,
+ "fatalErrorCount": 0,
+ "warningCount": 4,
+ "fixableErrorCount": 2,
+ "fixableWarningCount": 4,
+ "source": "function addOne(i) {\n if (i != NaN) {\n return i ++\n } else {\n return\n }\n};"
+ }
+ ],
+ "metadata": {
+ "rulesMeta": {
+ "no-else-return": {
+ "type": "suggestion",
+ "docs": {
+ "description": "Disallow `else` blocks after `return` statements in `if` statements",
+ "recommended": false,
+ "url": "https://eslint.org/docs/latest/rules/no-else-return"
+ },
+ "schema": [
+ {
+ "type": "object",
+ "properties": {
+ "allowElseIf": {
+ "type": "boolean",
+ "default": true
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "fixable": "code",
+ "messages": {
+ "unexpected": "Unnecessary 'else' after 'return'."
+ }
+ },
+ "indent": {
+ "deprecated": true,
+ "replacedBy": [],
+ "type": "layout",
+ "docs": {
+ "description": "Enforce consistent indentation",
+ "recommended": false,
+ "url": "https://eslint.org/docs/latest/rules/indent"
+ },
+ "fixable": "whitespace",
+ "schema": [
+ {
+ "oneOf": [
+ {
+ "enum": [
+ "tab"
+ ]
+ },
+ {
+ "type": "integer",
+ "minimum": 0
+ }
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "SwitchCase": {
+ "type": "integer",
+ "minimum": 0,
+ "default": 0
+ },
+ "VariableDeclarator": {
+ "oneOf": [
+ {
+ "oneOf": [
+ {
+ "type": "integer",
+ "minimum": 0
+ },
+ {
+ "enum": [
+ "first",
+ "off"
+ ]
+ }
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "var": {
+ "oneOf": [
+ {
+ "type": "integer",
+ "minimum": 0
+ },
+ {
+ "enum": [
+ "first",
+ "off"
+ ]
+ }
+ ]
+ },
+ "let": {
+ "oneOf": [
+ {
+ "type": "integer",
+ "minimum": 0
+ },
+ {
+ "enum": [
+ "first",
+ "off"
+ ]
+ }
+ ]
+ },
+ "const": {
+ "oneOf": [
+ {
+ "type": "integer",
+ "minimum": 0
+ },
+ {
+ "enum": [
+ "first",
+ "off"
+ ]
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ]
+ },
+ "outerIIFEBody": {
+ "oneOf": [
+ {
+ "type": "integer",
+ "minimum": 0
+ },
+ {
+ "enum": [
+ "off"
+ ]
+ }
+ ]
+ },
+ "MemberExpression": {
+ "oneOf": [
+ {
+ "type": "integer",
+ "minimum": 0
+ },
+ {
+ "enum": [
+ "off"
+ ]
+ }
+ ]
+ },
+ "FunctionDeclaration": {
+ "type": "object",
+ "properties": {
+ "parameters": {
+ "oneOf": [
+ {
+ "type": "integer",
+ "minimum": 0
+ },
+ {
+ "enum": [
+ "first",
+ "off"
+ ]
+ }
+ ]
+ },
+ "body": {
+ "type": "integer",
+ "minimum": 0
+ }
+ },
+ "additionalProperties": false
+ },
+ "FunctionExpression": {
+ "type": "object",
+ "properties": {
+ "parameters": {
+ "oneOf": [
+ {
+ "type": "integer",
+ "minimum": 0
+ },
+ {
+ "enum": [
+ "first",
+ "off"
+ ]
+ }
+ ]
+ },
+ "body": {
+ "type": "integer",
+ "minimum": 0
+ }
+ },
+ "additionalProperties": false
+ },
+ "StaticBlock": {
+ "type": "object",
+ "properties": {
+ "body": {
+ "type": "integer",
+ "minimum": 0
+ }
+ },
+ "additionalProperties": false
+ },
+ "CallExpression": {
+ "type": "object",
+ "properties": {
+ "arguments": {
+ "oneOf": [
+ {
+ "type": "integer",
+ "minimum": 0
+ },
+ {
+ "enum": [
+ "first",
+ "off"
+ ]
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "ArrayExpression": {
+ "oneOf": [
+ {
+ "type": "integer",
+ "minimum": 0
+ },
+ {
+ "enum": [
+ "first",
+ "off"
+ ]
+ }
+ ]
+ },
+ "ObjectExpression": {
+ "oneOf": [
+ {
+ "type": "integer",
+ "minimum": 0
+ },
+ {
+ "enum": [
+ "first",
+ "off"
+ ]
+ }
+ ]
+ },
+ "ImportDeclaration": {
+ "oneOf": [
+ {
+ "type": "integer",
+ "minimum": 0
+ },
+ {
+ "enum": [
+ "first",
+ "off"
+ ]
+ }
+ ]
+ },
+ "flatTernaryExpressions": {
+ "type": "boolean",
+ "default": false
+ },
+ "offsetTernaryExpressions": {
+ "type": "boolean",
+ "default": false
+ },
+ "ignoredNodes": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "not": {
+ "pattern": ":exit$"
+ }
+ }
+ },
+ "ignoreComments": {
+ "type": "boolean",
+ "default": false
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "messages": {
+ "wrongIndentation": "Expected indentation of {{expected}} but found {{actual}}."
+ }
+ },
+ "space-unary-ops": {
+ "deprecated": true,
+ "replacedBy": [],
+ "type": "layout",
+ "docs": {
+ "description": "Enforce consistent spacing before or after unary operators",
+ "recommended": false,
+ "url": "https://eslint.org/docs/latest/rules/space-unary-ops"
+ },
+ "fixable": "whitespace",
+ "schema": [
+ {
+ "type": "object",
+ "properties": {
+ "words": {
+ "type": "boolean",
+ "default": true
+ },
+ "nonwords": {
+ "type": "boolean",
+ "default": false
+ },
+ "overrides": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "boolean"
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "messages": {
+ "unexpectedBefore": "Unexpected space before unary operator '{{operator}}'.",
+ "unexpectedAfter": "Unexpected space after unary operator '{{operator}}'.",
+ "unexpectedAfterWord": "Unexpected space after unary word operator '{{word}}'.",
+ "wordOperator": "Unary word operator '{{word}}' must be followed by whitespace.",
+ "operator": "Unary operator '{{operator}}' must be followed by whitespace.",
+ "beforeUnaryExpressions": "Space is required before unary expressions '{{token}}'."
+ }
+ },
+ "semi": {
+ "deprecated": true,
+ "replacedBy": [],
+ "type": "layout",
+ "docs": {
+ "description": "Require or disallow semicolons instead of ASI",
+ "recommended": false,
+ "url": "https://eslint.org/docs/latest/rules/semi"
+ },
+ "fixable": "code",
+ "schema": {
+ "anyOf": [
+ {
+ "type": "array",
+ "items": [
+ {
+ "enum": [
+ "never"
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "beforeStatementContinuationChars": {
+ "enum": [
+ "always",
+ "any",
+ "never"
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "minItems": 0,
+ "maxItems": 2
+ },
+ {
+ "type": "array",
+ "items": [
+ {
+ "enum": [
+ "always"
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "omitLastInOneLineBlock": {
+ "type": "boolean"
+ },
+ "omitLastInOneLineClassBody": {
+ "type": "boolean"
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "minItems": 0,
+ "maxItems": 2
+ }
+ ]
+ },
+ "messages": {
+ "missingSemi": "Missing semicolon.",
+ "extraSemi": "Extra semicolon."
+ }
+ },
+ "consistent-return": {
+ "type": "suggestion",
+ "docs": {
+ "description": "Require `return` statements to either always or never specify values",
+ "recommended": false,
+ "url": "https://eslint.org/docs/latest/rules/consistent-return"
+ },
+ "schema": [
+ {
+ "type": "object",
+ "properties": {
+ "treatUndefinedAsUnspecified": {
+ "type": "boolean",
+ "default": false
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "messages": {
+ "missingReturn": "Expected to return a value at the end of {{name}}.",
+ "missingReturnValue": "{{name}} expected a return value.",
+ "unexpectedReturnValue": "{{name}} expected no return value."
+ }
+ }
+ }
+ }
+}
```
### json
@@ -131,10 +722,164 @@ Outputs JSON-serialized results. The `json` formatter is useful when you want to
Alternatively, you can use the [ESLint Node.js API](../../integrate/nodejs-api) to programmatically use ESLint.
-Example output:
+Example output (formatted for easier reading):
-```text
-[{"filePath":"/var/lib/jenkins/workspace/Releases/eslint Release/eslint/fullOfProblems.js","messages":[{"ruleId":"no-unused-vars","severity":2,"message":"'addOne' is defined but never used.","line":1,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":16},{"ruleId":"use-isnan","severity":2,"message":"Use the isNaN function to compare with NaN.","line":2,"column":9,"nodeType":"BinaryExpression","messageId":"comparisonWithNaN","endLine":2,"endColumn":17},{"ruleId":"space-unary-ops","severity":2,"message":"Unexpected space before unary operator '++'.","line":3,"column":16,"nodeType":"UpdateExpression","messageId":"unexpectedBefore","endLine":3,"endColumn":20,"fix":{"range":[57,58],"text":""}},{"ruleId":"semi","severity":1,"message":"Missing semicolon.","line":3,"column":20,"nodeType":"ReturnStatement","messageId":"missingSemi","endLine":4,"endColumn":1,"fix":{"range":[60,60],"text":";"}},{"ruleId":"no-else-return","severity":1,"message":"Unnecessary 'else' after 'return'.","line":4,"column":12,"nodeType":"BlockStatement","messageId":"unexpected","endLine":6,"endColumn":6,"fix":{"range":[0,94],"text":"function addOne(i) {\n if (i != NaN) {\n return i ++\n } \n return\n \n}"}},{"ruleId":"indent","severity":1,"message":"Expected indentation of 8 spaces but found 6.","line":5,"column":1,"nodeType":"Keyword","messageId":"wrongIndentation","endLine":5,"endColumn":7,"fix":{"range":[74,80],"text":" "}},{"ruleId":"consistent-return","severity":2,"message":"Function 'addOne' expected a return value.","line":5,"column":7,"nodeType":"ReturnStatement","messageId":"missingReturnValue","endLine":5,"endColumn":13},{"ruleId":"semi","severity":1,"message":"Missing semicolon.","line":5,"column":13,"nodeType":"ReturnStatement","messageId":"missingSemi","endLine":6,"endColumn":1,"fix":{"range":[86,86],"text":";"}},{"ruleId":"no-extra-semi","severity":2,"message":"Unnecessary semicolon.","line":7,"column":2,"nodeType":"EmptyStatement","messageId":"unexpected","endLine":7,"endColumn":3,"fix":{"range":[93,95],"text":"}"}}],"suppressedMessages":[],"errorCount":5,"fatalErrorCount":0,"warningCount":4,"fixableErrorCount":2,"fixableWarningCount":4,"source":"function addOne(i) {\n if (i != NaN) {\n return i ++\n } else {\n return\n }\n};"}]
+```json
+[
+ {
+ "filePath": "/var/lib/jenkins/workspace/Releases/eslint Release/eslint/fullOfProblems.js",
+ "messages": [
+ {
+ "ruleId": "no-unused-vars",
+ "severity": 2,
+ "message": "'addOne' is defined but never used.",
+ "line": 1,
+ "column": 10,
+ "nodeType": "Identifier",
+ "messageId": "unusedVar",
+ "endLine": 1,
+ "endColumn": 16
+ },
+ {
+ "ruleId": "use-isnan",
+ "severity": 2,
+ "message": "Use the isNaN function to compare with NaN.",
+ "line": 2,
+ "column": 9,
+ "nodeType": "BinaryExpression",
+ "messageId": "comparisonWithNaN",
+ "endLine": 2,
+ "endColumn": 17
+ },
+ {
+ "ruleId": "space-unary-ops",
+ "severity": 2,
+ "message": "Unexpected space before unary operator '++'.",
+ "line": 3,
+ "column": 16,
+ "nodeType": "UpdateExpression",
+ "messageId": "unexpectedBefore",
+ "endLine": 3,
+ "endColumn": 20,
+ "fix": {
+ "range": [
+ 57,
+ 58
+ ],
+ "text": ""
+ }
+ },
+ {
+ "ruleId": "semi",
+ "severity": 1,
+ "message": "Missing semicolon.",
+ "line": 3,
+ "column": 20,
+ "nodeType": "ReturnStatement",
+ "messageId": "missingSemi",
+ "endLine": 4,
+ "endColumn": 1,
+ "fix": {
+ "range": [
+ 60,
+ 60
+ ],
+ "text": ";"
+ }
+ },
+ {
+ "ruleId": "no-else-return",
+ "severity": 1,
+ "message": "Unnecessary 'else' after 'return'.",
+ "line": 4,
+ "column": 12,
+ "nodeType": "BlockStatement",
+ "messageId": "unexpected",
+ "endLine": 6,
+ "endColumn": 6,
+ "fix": {
+ "range": [
+ 0,
+ 94
+ ],
+ "text": "function addOne(i) {\n if (i != NaN) {\n return i ++\n } \n return\n \n}"
+ }
+ },
+ {
+ "ruleId": "indent",
+ "severity": 1,
+ "message": "Expected indentation of 8 spaces but found 6.",
+ "line": 5,
+ "column": 1,
+ "nodeType": "Keyword",
+ "messageId": "wrongIndentation",
+ "endLine": 5,
+ "endColumn": 7,
+ "fix": {
+ "range": [
+ 74,
+ 80
+ ],
+ "text": " "
+ }
+ },
+ {
+ "ruleId": "consistent-return",
+ "severity": 2,
+ "message": "Function 'addOne' expected a return value.",
+ "line": 5,
+ "column": 7,
+ "nodeType": "ReturnStatement",
+ "messageId": "missingReturnValue",
+ "endLine": 5,
+ "endColumn": 13
+ },
+ {
+ "ruleId": "semi",
+ "severity": 1,
+ "message": "Missing semicolon.",
+ "line": 5,
+ "column": 13,
+ "nodeType": "ReturnStatement",
+ "messageId": "missingSemi",
+ "endLine": 6,
+ "endColumn": 1,
+ "fix": {
+ "range": [
+ 86,
+ 86
+ ],
+ "text": ";"
+ }
+ },
+ {
+ "ruleId": "no-extra-semi",
+ "severity": 2,
+ "message": "Unnecessary semicolon.",
+ "line": 7,
+ "column": 2,
+ "nodeType": "EmptyStatement",
+ "messageId": "unexpected",
+ "endLine": 7,
+ "endColumn": 3,
+ "fix": {
+ "range": [
+ 93,
+ 95
+ ],
+ "text": "}"
+ }
+ }
+ ],
+ "suppressedMessages": [],
+ "errorCount": 5,
+ "fatalErrorCount": 0,
+ "warningCount": 4,
+ "fixableErrorCount": 2,
+ "fixableWarningCount": 4,
+ "source": "function addOne(i) {\n if (i != NaN) {\n return i ++\n } else {\n return\n }\n};"
+ }
+]
```
### junit
@@ -143,7 +888,7 @@ Outputs results to format compatible with the [JUnit Jenkins plugin](https://plu
Example output:
-```text
+```xml
diff --git a/eslint.config.js b/eslint.config.js
index 5c488a57a15..40ebe8c08f7 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -27,20 +27,16 @@
const path = require("path");
const internalPlugin = require("eslint-plugin-internal-rules");
-const eslintPlugin = require("eslint-plugin-eslint-plugin");
-const { FlatCompat } = require("@eslint/eslintrc");
-const js = require("./packages/js");
+const eslintPluginRulesRecommendedConfig = require("eslint-plugin-eslint-plugin/configs/rules-recommended");
+const eslintPluginTestsRecommendedConfig = require("eslint-plugin-eslint-plugin/configs/tests-recommended");
const globals = require("globals");
+const merge = require("lodash.merge");
+const eslintConfigESLintCJS = require("eslint-config-eslint/cjs");
//-----------------------------------------------------------------------------
// Helpers
//-----------------------------------------------------------------------------
-const compat = new FlatCompat({
- baseDirectory: __dirname,
- recommendedConfig: js.configs.recommended
-});
-
const INTERNAL_FILES = {
CLI_ENGINE_PATTERN: "lib/cli-engine/**/*",
LINTER_PATTERN: "lib/linter/**/*",
@@ -79,7 +75,7 @@ function createInternalFilesPatterns(pattern = null) {
}
module.exports = [
- ...compat.extends("eslint"),
+ ...eslintConfigESLintCJS,
{
ignores: [
"build/**",
@@ -99,22 +95,11 @@ module.exports = [
},
{
plugins: {
- "internal-rules": internalPlugin,
- "eslint-plugin": eslintPlugin
+ "internal-rules": internalPlugin
},
languageOptions: {
ecmaVersion: "latest"
},
-
- /*
- * it fixes eslint-plugin-jsdoc's reports: "Invalid JSDoc tag name "template" jsdoc/check-tag-names"
- * refs: https://github.com/gajus/eslint-plugin-jsdoc#check-tag-names
- */
- settings: {
- jsdoc: {
- mode: "typescript"
- }
- },
rules: {
"internal-rules/multiline-comment-style": "error"
}
@@ -129,33 +114,31 @@ module.exports = [
{
files: ["lib/rules/*", "tools/internal-rules/*"],
ignores: ["**/index.js"],
- rules: {
- ...eslintPlugin.configs["rules-recommended"].rules,
- "eslint-plugin/no-missing-message-ids": "error",
- "eslint-plugin/no-unused-message-ids": "error",
- "eslint-plugin/prefer-message-ids": "error",
- "eslint-plugin/prefer-placeholders": "error",
- "eslint-plugin/prefer-replace-text": "error",
- "eslint-plugin/report-message-format": ["error", "[^a-z].*\\.$"],
- "eslint-plugin/require-meta-docs-description": ["error", { pattern: "^(Enforce|Require|Disallow) .+[^. ]$" }],
- "internal-rules/no-invalid-meta": "error"
- }
+ ...merge({}, eslintPluginRulesRecommendedConfig, {
+ rules: {
+ "eslint-plugin/prefer-placeholders": "error",
+ "eslint-plugin/prefer-replace-text": "error",
+ "eslint-plugin/report-message-format": ["error", "[^a-z].*\\.$"],
+ "eslint-plugin/require-meta-docs-description": ["error", { pattern: "^(Enforce|Require|Disallow) .+[^. ]$" }],
+ "internal-rules/no-invalid-meta": "error"
+ }
+ })
},
{
files: ["lib/rules/*"],
- ignores: ["index.js"],
+ ignores: ["**/index.js"],
rules: {
"eslint-plugin/require-meta-docs-url": ["error", { pattern: "https://eslint.org/docs/latest/rules/{{name}}" }]
}
},
{
files: ["tests/lib/rules/*", "tests/tools/internal-rules/*"],
- rules: {
- ...eslintPlugin.configs["tests-recommended"].rules,
- "eslint-plugin/prefer-output-null": "error",
- "eslint-plugin/test-case-property-ordering": "error",
- "eslint-plugin/test-case-shorthand-strings": "error"
- }
+ ...merge({}, eslintPluginTestsRecommendedConfig, {
+ rules: {
+ "eslint-plugin/test-case-property-ordering": "error",
+ "eslint-plugin/test-case-shorthand-strings": "error"
+ }
+ })
},
{
files: ["tests/**/*.js"],
@@ -243,7 +226,6 @@ module.exports = [
files: [INTERNAL_FILES.RULE_TESTER_PATTERN],
rules: {
"n/no-restricted-require": ["error", [
- ...createInternalFilesPatterns(INTERNAL_FILES.RULE_TESTER_PATTERN),
resolveAbsolutePath("lib/cli-engine/index.js")
]]
}
diff --git a/karma.conf.js b/karma.conf.js
deleted file mode 100644
index 606d13f88f6..00000000000
--- a/karma.conf.js
+++ /dev/null
@@ -1,125 +0,0 @@
-"use strict";
-const os = require("os");
-const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
-
-if (os.platform === "linux" && os.arch() === "arm64") {
-
- // For arm64 architecture, install chromium-browser using "apt-get install chromium-browser"
- process.env.CHROME_BIN = "/usr/bin/chromium-browser";
-} else {
- process.env.CHROME_BIN = require("puppeteer").executablePath();
-}
-
-module.exports = function(config) {
- config.set({
-
- // base path that will be used to resolve all patterns (eg. files, exclude)
- basePath: "",
-
- // next three sections allow console.log to work
- client: {
- captureConsole: true
- },
-
- browserConsoleLogOptions: {
- terminal: true,
- level: "log"
- },
-
- /*
- * frameworks to use
- * available frameworks: https://npmjs.org/browse/keyword/karma-adapter
- */
- frameworks: ["mocha", "webpack"],
-
-
- // list of files / patterns to load in the browser
- files: [
- "tests/lib/linter/linter.js"
- ],
-
-
- // list of files to exclude
- exclude: [
- ],
-
-
- /*
- * preprocess matching files before serving them to the browser
- * available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
- */
- preprocessors: {
- "tests/lib/linter/linter.js": ["webpack"]
- },
- webpack: {
- mode: "none",
- plugins: [
- new NodePolyfillPlugin()
- ],
- resolve: {
- alias: {
- "../../../lib/linter$": "../../../build/eslint.js"
- }
- },
- stats: "errors-only"
- },
- webpackMiddleware: {
- logLevel: "error"
- },
-
-
- /*
- * test results reporter to use
- * possible values: "dots", "progress"
- * available reporters: https://npmjs.org/browse/keyword/karma-reporter
- */
- reporters: ["mocha"],
-
- mochaReporter: {
- output: "minimal"
- },
-
- // web server port
- port: 9876,
-
-
- // enable / disable colors in the output (reporters and logs)
- colors: true,
-
-
- /*
- * level of logging
- * possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
- */
- logLevel: config.LOG_INFO,
-
-
- // enable / disable watching file and executing tests whenever any file changes
- autoWatch: false,
-
-
- /*
- * start these browsers
- * available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
- */
- browsers: ["HeadlessChrome"],
- customLaunchers: {
- HeadlessChrome: {
- base: "ChromeHeadless",
- flags: ["--no-sandbox"]
- }
- },
-
- /*
- * Continuous Integration mode
- * if true, Karma captures browsers, runs the tests and exits
- */
- singleRun: true,
-
- /*
- * Concurrency level
- * how many browser should be started simultaneous
- */
- concurrency: Infinity
- });
-};
diff --git a/lib/cli-engine/cli-engine.js b/lib/cli-engine/cli-engine.js
index 2814bd81541..aafb3deb8be 100644
--- a/lib/cli-engine/cli-engine.js
+++ b/lib/cli-engine/cli-engine.js
@@ -158,7 +158,17 @@ function validateFixTypes(fixTypes) {
* @private
*/
function calculateStatsPerFile(messages) {
- return messages.reduce((stat, message) => {
+ const stat = {
+ errorCount: 0,
+ fatalErrorCount: 0,
+ warningCount: 0,
+ fixableErrorCount: 0,
+ fixableWarningCount: 0
+ };
+
+ for (let i = 0; i < messages.length; i++) {
+ const message = messages[i];
+
if (message.fatal || message.severity === 2) {
stat.errorCount++;
if (message.fatal) {
@@ -173,14 +183,8 @@ function calculateStatsPerFile(messages) {
stat.fixableWarningCount++;
}
}
- return stat;
- }, {
- errorCount: 0,
- fatalErrorCount: 0,
- warningCount: 0,
- fixableErrorCount: 0,
- fixableWarningCount: 0
- });
+ }
+ return stat;
}
/**
@@ -190,20 +194,25 @@ function calculateStatsPerFile(messages) {
* @private
*/
function calculateStatsPerRun(results) {
- return results.reduce((stat, result) => {
- stat.errorCount += result.errorCount;
- stat.fatalErrorCount += result.fatalErrorCount;
- stat.warningCount += result.warningCount;
- stat.fixableErrorCount += result.fixableErrorCount;
- stat.fixableWarningCount += result.fixableWarningCount;
- return stat;
- }, {
+ const stat = {
errorCount: 0,
fatalErrorCount: 0,
warningCount: 0,
fixableErrorCount: 0,
fixableWarningCount: 0
- });
+ };
+
+ for (let i = 0; i < results.length; i++) {
+ const result = results[i];
+
+ stat.errorCount += result.errorCount;
+ stat.fatalErrorCount += result.fatalErrorCount;
+ stat.warningCount += result.warningCount;
+ stat.fixableErrorCount += result.fixableErrorCount;
+ stat.fixableWarningCount += result.fixableWarningCount;
+ }
+
+ return stat;
}
/**
diff --git a/lib/cli.js b/lib/cli.js
index 01287da6417..a0f387c50de 100644
--- a/lib/cli.js
+++ b/lib/cli.js
@@ -92,7 +92,8 @@ async function translateOptions({
reportUnusedDisableDirectivesSeverity,
resolvePluginsRelativeTo,
rule,
- rulesdir
+ rulesdir,
+ warnIgnored
}, configType) {
let overrideConfig, overrideConfigFile;
@@ -183,6 +184,7 @@ async function translateOptions({
if (configType === "flat") {
options.ignorePatterns = ignorePattern;
+ options.warnIgnored = warnIgnored;
} else {
options.resolvePluginsRelativeTo = resolvePluginsRelativeTo;
options.rulePaths = rulesdir;
@@ -317,7 +319,14 @@ const cli = {
options = CLIOptions.parse(args);
} catch (error) {
debug("Error parsing CLI options:", error.message);
- log.error(error.message);
+
+ let errorMessage = error.message;
+
+ if (usingFlatConfig) {
+ errorMessage += "\nYou're using eslint.config.js, some command line flags are no longer available. Please see https://eslint.org/docs/latest/use/command-line-interface for details.";
+ }
+
+ log.error(errorMessage);
return 2;
}
@@ -391,7 +400,9 @@ const cli = {
if (useStdin) {
results = await engine.lintText(text, {
filePath: options.stdinFilename,
- warnIgnored: true
+
+ // flatConfig respects CLI flag and constructor warnIgnored, eslintrc forces true for backwards compatibility
+ warnIgnored: usingFlatConfig ? void 0 : true
});
} else {
results = await engine.lintFiles(files);
diff --git a/lib/config/flat-config-schema.js b/lib/config/flat-config-schema.js
index 5700668a37b..cab5e131a4d 100644
--- a/lib/config/flat-config-schema.js
+++ b/lib/config/flat-config-schema.js
@@ -5,6 +5,16 @@
"use strict";
+//-----------------------------------------------------------------------------
+// Requirements
+//-----------------------------------------------------------------------------
+
+/*
+ * Note: This can be removed in ESLint v9 because structuredClone is available globally
+ * starting in Node.js v17.
+ */
+const structuredClone = require("@ungap/structured-clone").default;
+
//-----------------------------------------------------------------------------
// Type Definitions
//-----------------------------------------------------------------------------
@@ -119,7 +129,7 @@ function normalizeRuleOptions(ruleOptions) {
: [ruleOptions];
finalOptions[0] = ruleSeverities.get(finalOptions[0]);
- return finalOptions;
+ return structuredClone(finalOptions);
}
//-----------------------------------------------------------------------------
@@ -179,9 +189,7 @@ class InvalidRuleSeverityError extends Error {
* @throws {InvalidRuleSeverityError} If the value isn't a valid rule severity.
*/
function assertIsRuleSeverity(ruleId, value) {
- const severity = typeof value === "string"
- ? ruleSeverities.get(value.toLowerCase())
- : ruleSeverities.get(value);
+ const severity = ruleSeverities.get(value);
if (typeof severity === "undefined") {
throw new InvalidRuleSeverityError(ruleId, value);
@@ -212,6 +220,38 @@ function assertIsObject(value) {
}
}
+/**
+ * The error type when there's an eslintrc-style options in a flat config.
+ */
+class IncompatibleKeyError extends Error {
+
+ /**
+ * @param {string} key The invalid key.
+ */
+ constructor(key) {
+ super("This appears to be in eslintrc format rather than flat config format.");
+ this.messageTemplate = "eslintrc-incompat";
+ this.messageData = { key };
+ }
+}
+
+/**
+ * The error type when there's an eslintrc-style plugins array found.
+ */
+class IncompatiblePluginsError extends Error {
+
+ /**
+ * Creates a new instance.
+ * @param {Array} plugins The plugins array.
+ */
+ constructor(plugins) {
+ super("This appears to be in eslintrc format (array of strings) rather than flat config format (object).");
+ this.messageTemplate = "eslintrc-plugins";
+ this.messageData = { plugins };
+ }
+}
+
+
//-----------------------------------------------------------------------------
// Low-Level Schemas
//-----------------------------------------------------------------------------
@@ -313,6 +353,11 @@ const pluginsSchema = {
throw new TypeError("Expected an object.");
}
+ // make sure it's not an array, which would mean eslintrc-style is used
+ if (Array.isArray(value)) {
+ throw new IncompatiblePluginsError(value);
+ }
+
// second check the keys to make sure they are objects
for (const key of Object.keys(value)) {
@@ -353,48 +398,57 @@ const rulesSchema = {
...second
};
- for (const ruleId of Object.keys(result)) {
- // avoid hairy edge case
- if (ruleId === "__proto__") {
-
- /* eslint-disable-next-line no-proto -- Though deprecated, may still be present */
- delete result.__proto__;
- continue;
- }
-
- result[ruleId] = normalizeRuleOptions(result[ruleId]);
-
- /*
- * If either rule config is missing, then the correct
- * config is already present and we just need to normalize
- * the severity.
- */
- if (!(ruleId in first) || !(ruleId in second)) {
- continue;
- }
-
- const firstRuleOptions = normalizeRuleOptions(first[ruleId]);
- const secondRuleOptions = normalizeRuleOptions(second[ruleId]);
+ for (const ruleId of Object.keys(result)) {
- /*
- * If the second rule config only has a severity (length of 1),
- * then use that severity and keep the rest of the options from
- * the first rule config.
- */
- if (secondRuleOptions.length === 1) {
- result[ruleId] = [secondRuleOptions[0], ...firstRuleOptions.slice(1)];
- continue;
+ try {
+
+ // avoid hairy edge case
+ if (ruleId === "__proto__") {
+
+ /* eslint-disable-next-line no-proto -- Though deprecated, may still be present */
+ delete result.__proto__;
+ continue;
+ }
+
+ result[ruleId] = normalizeRuleOptions(result[ruleId]);
+
+ /*
+ * If either rule config is missing, then the correct
+ * config is already present and we just need to normalize
+ * the severity.
+ */
+ if (!(ruleId in first) || !(ruleId in second)) {
+ continue;
+ }
+
+ const firstRuleOptions = normalizeRuleOptions(first[ruleId]);
+ const secondRuleOptions = normalizeRuleOptions(second[ruleId]);
+
+ /*
+ * If the second rule config only has a severity (length of 1),
+ * then use that severity and keep the rest of the options from
+ * the first rule config.
+ */
+ if (secondRuleOptions.length === 1) {
+ result[ruleId] = [secondRuleOptions[0], ...firstRuleOptions.slice(1)];
+ continue;
+ }
+
+ /*
+ * In any other situation, then the second rule config takes
+ * precedence. That means the value at `result[ruleId]` is
+ * already correct and no further work is necessary.
+ */
+ } catch (ex) {
+ throw new Error(`Key "${ruleId}": ${ex.message}`, { cause: ex });
}
- /*
- * In any other situation, then the second rule config takes
- * precedence. That means the value at `result[ruleId]` is
- * already correct and no further work is necessary.
- */
}
return result;
+
+
},
validate(value) {
@@ -448,11 +502,44 @@ const sourceTypeSchema = {
}
};
+/**
+ * Creates a schema that always throws an error. Useful for warning
+ * about eslintrc-style keys.
+ * @param {string} key The eslintrc key to create a schema for.
+ * @returns {ObjectPropertySchema} The schema.
+ */
+function createEslintrcErrorSchema(key) {
+ return {
+ merge: "replace",
+ validate() {
+ throw new IncompatibleKeyError(key);
+ }
+ };
+}
+
+const eslintrcKeys = [
+ "env",
+ "extends",
+ "globals",
+ "ignorePatterns",
+ "noInlineConfig",
+ "overrides",
+ "parser",
+ "parserOptions",
+ "reportUnusedDisableDirectives",
+ "root"
+];
+
//-----------------------------------------------------------------------------
// Full schema
//-----------------------------------------------------------------------------
-exports.flatConfigSchema = {
+const flatConfigSchema = {
+
+ // eslintrc-style keys that should always error
+ ...Object.fromEntries(eslintrcKeys.map(key => [key, createEslintrcErrorSchema(key)])),
+
+ // flat config keys
settings: deepObjectAssignSchema,
linterOptions: {
schema: {
@@ -473,3 +560,13 @@ exports.flatConfigSchema = {
plugins: pluginsSchema,
rules: rulesSchema
};
+
+//-----------------------------------------------------------------------------
+// Exports
+//-----------------------------------------------------------------------------
+
+module.exports = {
+ flatConfigSchema,
+ assertIsRuleSeverity,
+ assertIsRuleOptions
+};
diff --git a/lib/config/rule-validator.js b/lib/config/rule-validator.js
index 0b5858fb30f..eee5b40bd07 100644
--- a/lib/config/rule-validator.js
+++ b/lib/config/rule-validator.js
@@ -9,7 +9,8 @@
// Requirements
//-----------------------------------------------------------------------------
-const ajv = require("../shared/ajv")();
+const ajvImport = require("../shared/ajv");
+const ajv = ajvImport();
const {
parseRuleId,
getRuleFromConfig,
diff --git a/lib/eslint/eslint-helpers.js b/lib/eslint/eslint-helpers.js
index c81c82dc944..2ad1619ff0c 100644
--- a/lib/eslint/eslint-helpers.js
+++ b/lib/eslint/eslint-helpers.js
@@ -591,12 +591,12 @@ function isErrorMessage(message) {
*/
function createIgnoreResult(filePath, baseDir) {
let message;
- const isInNodeModules = baseDir && path.relative(baseDir, filePath).startsWith("node_modules");
+ const isInNodeModules = baseDir && path.dirname(path.relative(baseDir, filePath)).split(path.sep).includes("node_modules");
if (isInNodeModules) {
- message = "File ignored by default because it is located under the node_modules directory. Use ignore pattern \"!**/node_modules/\" to override.";
+ message = "File ignored by default because it is located under the node_modules directory. Use ignore pattern \"!**/node_modules/\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning.";
} else {
- message = "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to override.";
+ message = "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning.";
}
return {
@@ -676,6 +676,7 @@ function processOptions({
overrideConfigFile = null,
plugins = {},
reportUnusedDisableDirectives = null, // ← should be null by default because if it's a string then it overrides the 'reportUnusedDisableDirectives' setting in config files. And we cannot use `overrideConfig.reportUnusedDisableDirectives` instead because we cannot configure the `error` severity with that. TODO: should anything change based on this comment?
+ warnIgnored = true,
...unknownOptions
}) {
const errors = [];
@@ -781,6 +782,9 @@ function processOptions({
) {
errors.push("'reportUnusedDisableDirectives' must be any of \"error\", \"warn\", \"off\", and null.");
}
+ if (typeof warnIgnored !== "boolean") {
+ errors.push("'warnIgnored' must be a boolean.");
+ }
if (errors.length > 0) {
throw new ESLintInvalidOptionsError(errors);
}
@@ -795,14 +799,15 @@ function processOptions({
// when overrideConfigFile is true that means don't do config file lookup
configFile: overrideConfigFile === true ? false : overrideConfigFile,
overrideConfig,
- cwd,
+ cwd: path.normalize(cwd),
errorOnUnmatchedPattern,
fix,
fixTypes,
globInputPaths,
ignore,
ignorePatterns,
- reportUnusedDisableDirectives
+ reportUnusedDisableDirectives,
+ warnIgnored
};
}
diff --git a/lib/eslint/eslint.js b/lib/eslint/eslint.js
index 056e40f1461..3717b9fe447 100644
--- a/lib/eslint/eslint.js
+++ b/lib/eslint/eslint.js
@@ -289,7 +289,7 @@ function processOptions({
cacheLocation,
cacheStrategy,
configFile: overrideConfigFile,
- cwd,
+ cwd: path.normalize(cwd),
errorOnUnmatchedPattern,
extensions,
fix,
diff --git a/lib/eslint/flat-eslint.js b/lib/eslint/flat-eslint.js
index dc8bf0cea50..8f86b9a5962 100644
--- a/lib/eslint/flat-eslint.js
+++ b/lib/eslint/flat-eslint.js
@@ -84,6 +84,7 @@ const LintResultCache = require("../cli-engine/lint-result-cache");
* when a string.
* @property {Record} [plugins] An array of plugin implementations.
* @property {"error" | "warn" | "off"} [reportUnusedDisableDirectives] the severity to report unused eslint-disable directives.
+ * @property {boolean} warnIgnored Show warnings when the file list includes ignored files
*/
//------------------------------------------------------------------------------
@@ -103,7 +104,17 @@ const importedConfigFileModificationTime = new Map();
* @private
*/
function calculateStatsPerFile(messages) {
- return messages.reduce((stat, message) => {
+ const stat = {
+ errorCount: 0,
+ fatalErrorCount: 0,
+ warningCount: 0,
+ fixableErrorCount: 0,
+ fixableWarningCount: 0
+ };
+
+ for (let i = 0; i < messages.length; i++) {
+ const message = messages[i];
+
if (message.fatal || message.severity === 2) {
stat.errorCount++;
if (message.fatal) {
@@ -118,37 +129,8 @@ function calculateStatsPerFile(messages) {
stat.fixableWarningCount++;
}
}
- return stat;
- }, {
- errorCount: 0,
- fatalErrorCount: 0,
- warningCount: 0,
- fixableErrorCount: 0,
- fixableWarningCount: 0
- });
-}
-
-/**
- * It will calculate the error and warning count for collection of results from all files
- * @param {LintResult[]} results Collection of messages from all the files
- * @returns {Object} Contains the stats
- * @private
- */
-function calculateStatsPerRun(results) {
- return results.reduce((stat, result) => {
- stat.errorCount += result.errorCount;
- stat.fatalErrorCount += result.fatalErrorCount;
- stat.warningCount += result.warningCount;
- stat.fixableErrorCount += result.fixableErrorCount;
- stat.fixableWarningCount += result.fixableWarningCount;
- return stat;
- }, {
- errorCount: 0,
- fatalErrorCount: 0,
- warningCount: 0,
- fixableErrorCount: 0,
- fixableWarningCount: 0
- });
+ }
+ return stat;
}
/**
@@ -550,43 +532,6 @@ function shouldMessageBeFixed(message, config, fixTypes) {
return Boolean(rule && rule.meta && fixTypes.has(rule.meta.type));
}
-/**
- * Collect used deprecated rules.
- * @param {Array} configs The configs to evaluate.
- * @returns {IterableIterator} Used deprecated rules.
- */
-function *iterateRuleDeprecationWarnings(configs) {
- const processedRuleIds = new Set();
-
- for (const config of configs) {
- for (const [ruleId, ruleConfig] of Object.entries(config.rules)) {
-
- // Skip if it was processed.
- if (processedRuleIds.has(ruleId)) {
- continue;
- }
- processedRuleIds.add(ruleId);
-
- // Skip if it's not used.
- if (!getRuleSeverity(ruleConfig)) {
- continue;
- }
- const rule = getRuleFromConfig(ruleId, config);
-
- // Skip if it's not deprecated.
- if (!(rule && rule.meta && rule.meta.deprecated)) {
- continue;
- }
-
- // This rule was used and deprecated.
- yield {
- ruleId,
- replacedBy: rule.meta.replacedBy || []
- };
- }
- }
-}
-
/**
* Creates an error to be thrown when an array of results passed to `getRulesMetaForResults` was not created by the current engine.
* @returns {TypeError} An error object.
@@ -632,7 +577,6 @@ class FlatESLint {
cacheFilePath,
lintResultCache,
defaultConfigs,
- defaultIgnores: () => false,
configs: null
});
@@ -771,12 +715,10 @@ class FlatESLint {
}
const rule = getRuleFromConfig(ruleId, config);
- // ensure the rule exists
- if (!rule) {
- throw new TypeError(`Could not find the rule "${ruleId}".`);
+ // ignore unknown rules
+ if (rule) {
+ resultRules.set(ruleId, rule);
}
-
- resultRules.set(ruleId, rule);
}
}
@@ -808,10 +750,10 @@ class FlatESLint {
fixTypes,
reportUnusedDisableDirectives,
globInputPaths,
- errorOnUnmatchedPattern
+ errorOnUnmatchedPattern,
+ warnIgnored
} = eslintOptions;
const startTime = Date.now();
- const usedConfigs = [];
const fixTypesSet = fixTypes ? new Set(fixTypes) : null;
// Delete cache file; should this be done here?
@@ -855,7 +797,11 @@ class FlatESLint {
* pattern, then notify the user.
*/
if (ignored) {
- return createIgnoreResult(filePath, cwd);
+ if (warnIgnored) {
+ return createIgnoreResult(filePath, cwd);
+ }
+
+ return void 0;
}
const config = configs.getConfig(filePath);
@@ -869,15 +815,6 @@ class FlatESLint {
return void 0;
}
- /*
- * Store used configs for:
- * - this method uses to collect used deprecated rules.
- * - `--fix-type` option uses to get the loaded rule's meta data.
- */
- if (!usedConfigs.includes(config)) {
- usedConfigs.push(config);
- }
-
// Skip if there is cached result.
if (lintResultCache) {
const cachedResult =
@@ -946,22 +883,10 @@ class FlatESLint {
lintResultCache.reconcile();
}
- let usedDeprecatedRules;
const finalResults = results.filter(result => !!result);
return processLintReport(this, {
- results: finalResults,
- ...calculateStatsPerRun(finalResults),
-
- // Initialize it lazily because CLI and `ESLint` API don't use it.
- get usedDeprecatedRules() {
- if (!usedDeprecatedRules) {
- usedDeprecatedRules = Array.from(
- iterateRuleDeprecationWarnings(usedConfigs)
- );
- }
- return usedDeprecatedRules;
- }
+ results: finalResults
});
}
@@ -989,7 +914,7 @@ class FlatESLint {
const {
filePath,
- warnIgnored = false,
+ warnIgnored,
...unknownOptions
} = options || {};
@@ -1003,7 +928,7 @@ class FlatESLint {
throw new Error("'options.filePath' must be a non-empty string or undefined");
}
- if (typeof warnIgnored !== "boolean") {
+ if (typeof warnIgnored !== "boolean" && typeof warnIgnored !== "undefined") {
throw new Error("'options.warnIgnored' must be a boolean or undefined");
}
@@ -1018,23 +943,22 @@ class FlatESLint {
allowInlineConfig,
cwd,
fix,
- reportUnusedDisableDirectives
+ reportUnusedDisableDirectives,
+ warnIgnored: constructorWarnIgnored
} = eslintOptions;
const results = [];
const startTime = Date.now();
const resolvedFilename = path.resolve(cwd, filePath || "__placeholder__.js");
- let config;
// Clear the last used config arrays.
if (resolvedFilename && await this.isPathIgnored(resolvedFilename)) {
- if (warnIgnored) {
+ const shouldWarnIgnored = typeof warnIgnored === "boolean" ? warnIgnored : constructorWarnIgnored;
+
+ if (shouldWarnIgnored) {
results.push(createIgnoreResult(resolvedFilename, cwd));
}
} else {
- // TODO: Needed?
- config = configs.getConfig(resolvedFilename);
-
// Do lint.
results.push(verifyText({
text: code,
@@ -1049,21 +973,9 @@ class FlatESLint {
}
debug(`Linting complete in: ${Date.now() - startTime}ms`);
- let usedDeprecatedRules;
return processLintReport(this, {
- results,
- ...calculateStatsPerRun(results),
-
- // Initialize it lazily because CLI and `ESLint` API don't use it.
- get usedDeprecatedRules() {
- if (!usedDeprecatedRules) {
- usedDeprecatedRules = Array.from(
- iterateRuleDeprecationWarnings(config)
- );
- }
- return usedDeprecatedRules;
- }
+ results
});
}
diff --git a/lib/linter/apply-disable-directives.js b/lib/linter/apply-disable-directives.js
index 13ced990ff4..c5e3c9ddc1c 100644
--- a/lib/linter/apply-disable-directives.js
+++ b/lib/linter/apply-disable-directives.js
@@ -30,7 +30,7 @@ function compareLocations(itemA, itemB) {
/**
* Groups a set of directives into sub-arrays by their parent comment.
- * @param {Directive[]} directives Unused directives to be removed.
+ * @param {Iterable} directives Unused directives to be removed.
* @returns {Directive[][]} Directives grouped by their parent comment.
*/
function groupByParentComment(directives) {
@@ -87,7 +87,7 @@ function createIndividualDirectivesRemoval(directives, commentToken) {
return directives.map(directive => {
const { ruleId } = directive;
- const regex = new RegExp(String.raw`(?:^|\s*,\s*)${escapeRegExp(ruleId)}(?:\s*,\s*|$)`, "u");
+ const regex = new RegExp(String.raw`(?:^|\s*,\s*)(?['"]?)${escapeRegExp(ruleId)}\k(?:\s*,\s*|$)`, "u");
const match = regex.exec(listText);
const matchedText = match[0];
const matchStartOffset = listStartOffset + match.index;
@@ -177,10 +177,10 @@ function createCommentRemoval(directives, commentToken) {
/**
* Parses details from directives to create output Problems.
- * @param {Directive[]} allDirectives Unused directives to be removed.
+ * @param {Iterable} allDirectives Unused directives to be removed.
* @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
*/
-function processUnusedDisableDirectives(allDirectives) {
+function processUnusedDirectives(allDirectives) {
const directiveGroups = groupByParentComment(allDirectives);
return directiveGroups.flatMap(
@@ -199,6 +199,95 @@ function processUnusedDisableDirectives(allDirectives) {
);
}
+/**
+ * Collect eslint-enable comments that are removing suppressions by eslint-disable comments.
+ * @param {Directive[]} directives The directives to check.
+ * @returns {Set} The used eslint-enable comments
+ */
+function collectUsedEnableDirectives(directives) {
+
+ /**
+ * A Map of `eslint-enable` keyed by ruleIds that may be marked as used.
+ * If `eslint-enable` does not have a ruleId, the key will be `null`.
+ * @type {Map}
+ */
+ const enabledRules = new Map();
+
+ /**
+ * A Set of `eslint-enable` marked as used.
+ * It is also the return value of `collectUsedEnableDirectives` function.
+ * @type {Set}
+ */
+ const usedEnableDirectives = new Set();
+
+ /*
+ * Checks the directives backwards to see if the encountered `eslint-enable` is used by the previous `eslint-disable`,
+ * and if so, stores the `eslint-enable` in `usedEnableDirectives`.
+ */
+ for (let index = directives.length - 1; index >= 0; index--) {
+ const directive = directives[index];
+
+ if (directive.type === "disable") {
+ if (enabledRules.size === 0) {
+ continue;
+ }
+ if (directive.ruleId === null) {
+
+ // If encounter `eslint-disable` without ruleId,
+ // mark all `eslint-enable` currently held in enabledRules as used.
+ // e.g.
+ // /* eslint-disable */ <- current directive
+ // /* eslint-enable rule-id1 */ <- used
+ // /* eslint-enable rule-id2 */ <- used
+ // /* eslint-enable */ <- used
+ for (const enableDirective of enabledRules.values()) {
+ usedEnableDirectives.add(enableDirective);
+ }
+ enabledRules.clear();
+ } else {
+ const enableDirective = enabledRules.get(directive.ruleId);
+
+ if (enableDirective) {
+
+ // If encounter `eslint-disable` with ruleId, and there is an `eslint-enable` with the same ruleId in enabledRules,
+ // mark `eslint-enable` with ruleId as used.
+ // e.g.
+ // /* eslint-disable rule-id */ <- current directive
+ // /* eslint-enable rule-id */ <- used
+ usedEnableDirectives.add(enableDirective);
+ } else {
+ const enabledDirectiveWithoutRuleId = enabledRules.get(null);
+
+ if (enabledDirectiveWithoutRuleId) {
+
+ // If encounter `eslint-disable` with ruleId, and there is no `eslint-enable` with the same ruleId in enabledRules,
+ // mark `eslint-enable` without ruleId as used.
+ // e.g.
+ // /* eslint-disable rule-id */ <- current directive
+ // /* eslint-enable */ <- used
+ usedEnableDirectives.add(enabledDirectiveWithoutRuleId);
+ }
+ }
+ }
+ } else if (directive.type === "enable") {
+ if (directive.ruleId === null) {
+
+ // If encounter `eslint-enable` without ruleId, the `eslint-enable` that follows it are unused.
+ // So clear enabledRules.
+ // e.g.
+ // /* eslint-enable */ <- current directive
+ // /* eslint-enable rule-id *// <- unused
+ // /* eslint-enable */ <- unused
+ enabledRules.clear();
+ enabledRules.set(null, directive);
+ } else {
+ enabledRules.set(directive.ruleId, directive);
+ }
+ }
+ }
+ return usedEnableDirectives;
+}
+
/**
* This is the same as the exported function, except that it
* doesn't handle disable-line and disable-next-line directives, and it always reports unused
@@ -206,7 +295,7 @@ function processUnusedDisableDirectives(allDirectives) {
* @param {Object} options options for applying directives. This is the same as the options
* for the exported function, except that `reportUnusedDisableDirectives` is not supported
* (this function always reports unused disable directives).
- * @returns {{problems: LintMessage[], unusedDisableDirectives: LintMessage[]}} An object with a list
+ * @returns {{problems: LintMessage[], unusedDirectives: LintMessage[]}} An object with a list
* of problems (including suppressed ones) and unused eslint-disable directives
*/
function applyDirectives(options) {
@@ -258,17 +347,42 @@ function applyDirectives(options) {
const unusedDisableDirectivesToReport = options.directives
.filter(directive => directive.type === "disable" && !usedDisableDirectives.has(directive));
- const processed = processUnusedDisableDirectives(unusedDisableDirectivesToReport);
- const unusedDisableDirectives = processed
+ const unusedEnableDirectivesToReport = new Set(
+ options.directives.filter(directive => directive.unprocessedDirective.type === "enable")
+ );
+
+ /*
+ * If directives has the eslint-enable directive,
+ * check whether the eslint-enable comment is used.
+ */
+ if (unusedEnableDirectivesToReport.size > 0) {
+ for (const directive of collectUsedEnableDirectives(options.directives)) {
+ unusedEnableDirectivesToReport.delete(directive);
+ }
+ }
+
+ const processed = processUnusedDirectives(unusedDisableDirectivesToReport)
+ .concat(processUnusedDirectives(unusedEnableDirectivesToReport));
+
+ const unusedDirectives = processed
.map(({ description, fix, unprocessedDirective }) => {
const { parentComment, type, line, column } = unprocessedDirective;
+ let message;
+
+ if (type === "enable") {
+ message = description
+ ? `Unused eslint-enable directive (no matching eslint-disable directives were found for ${description}).`
+ : "Unused eslint-enable directive (no matching eslint-disable directives were found).";
+ } else {
+ message = description
+ ? `Unused eslint-disable directive (no problems were reported from ${description}).`
+ : "Unused eslint-disable directive (no problems were reported).";
+ }
return {
ruleId: null,
- message: description
- ? `Unused eslint-disable directive (no problems were reported from ${description}).`
- : "Unused eslint-disable directive (no problems were reported).",
+ message,
line: type === "disable-next-line" ? parentComment.commentToken.loc.start.line : line,
column: type === "disable-next-line" ? parentComment.commentToken.loc.start.column + 1 : column,
severity: options.reportUnusedDisableDirectives === "warn" ? 1 : 2,
@@ -277,7 +391,7 @@ function applyDirectives(options) {
};
});
- return { problems, unusedDisableDirectives };
+ return { problems, unusedDirectives };
}
/**
@@ -344,8 +458,8 @@ module.exports = ({ directives, disableFixes, problems, reportUnusedDisableDirec
return reportUnusedDisableDirectives !== "off"
? lineDirectivesResult.problems
- .concat(blockDirectivesResult.unusedDisableDirectives)
- .concat(lineDirectivesResult.unusedDisableDirectives)
+ .concat(blockDirectivesResult.unusedDirectives)
+ .concat(lineDirectivesResult.unusedDirectives)
.sort(compareLocations)
: lineDirectivesResult.problems;
};
diff --git a/lib/linter/code-path-analysis/code-path-analyzer.js b/lib/linter/code-path-analysis/code-path-analyzer.js
index 2dcc2734884..b60e55c16de 100644
--- a/lib/linter/code-path-analysis/code-path-analyzer.js
+++ b/lib/linter/code-path-analysis/code-path-analyzer.js
@@ -192,15 +192,18 @@ function forwardCurrentToHead(analyzer, node) {
headSegment = headSegments[i];
if (currentSegment !== headSegment && currentSegment) {
- debug.dump(`onCodePathSegmentEnd ${currentSegment.id}`);
- if (currentSegment.reachable) {
- analyzer.emitter.emit(
- "onCodePathSegmentEnd",
- currentSegment,
- node
- );
- }
+ const eventName = currentSegment.reachable
+ ? "onCodePathSegmentEnd"
+ : "onUnreachableCodePathSegmentEnd";
+
+ debug.dump(`${eventName} ${currentSegment.id}`);
+
+ analyzer.emitter.emit(
+ eventName,
+ currentSegment,
+ node
+ );
}
}
@@ -213,16 +216,19 @@ function forwardCurrentToHead(analyzer, node) {
headSegment = headSegments[i];
if (currentSegment !== headSegment && headSegment) {
- debug.dump(`onCodePathSegmentStart ${headSegment.id}`);
+
+ const eventName = headSegment.reachable
+ ? "onCodePathSegmentStart"
+ : "onUnreachableCodePathSegmentStart";
+
+ debug.dump(`${eventName} ${headSegment.id}`);
CodePathSegment.markUsed(headSegment);
- if (headSegment.reachable) {
- analyzer.emitter.emit(
- "onCodePathSegmentStart",
- headSegment,
- node
- );
- }
+ analyzer.emitter.emit(
+ eventName,
+ headSegment,
+ node
+ );
}
}
@@ -241,15 +247,17 @@ function leaveFromCurrentSegment(analyzer, node) {
for (let i = 0; i < currentSegments.length; ++i) {
const currentSegment = currentSegments[i];
+ const eventName = currentSegment.reachable
+ ? "onCodePathSegmentEnd"
+ : "onUnreachableCodePathSegmentEnd";
- debug.dump(`onCodePathSegmentEnd ${currentSegment.id}`);
- if (currentSegment.reachable) {
- analyzer.emitter.emit(
- "onCodePathSegmentEnd",
- currentSegment,
- node
- );
- }
+ debug.dump(`${eventName} ${currentSegment.id}`);
+
+ analyzer.emitter.emit(
+ eventName,
+ currentSegment,
+ node
+ );
}
state.currentSegments = [];
diff --git a/lib/linter/code-path-analysis/code-path-segment.js b/lib/linter/code-path-analysis/code-path-segment.js
index fd2726a9937..3b8dbb41be6 100644
--- a/lib/linter/code-path-analysis/code-path-segment.js
+++ b/lib/linter/code-path-analysis/code-path-segment.js
@@ -1,5 +1,5 @@
/**
- * @fileoverview A class of the code path segment.
+ * @fileoverview The CodePathSegment class.
* @author Toru Nagashima
*/
@@ -30,10 +30,22 @@ function isReachable(segment) {
/**
* A code path segment.
+ *
+ * Each segment is arranged in a series of linked lists (implemented by arrays)
+ * that keep track of the previous and next segments in a code path. In this way,
+ * you can navigate between all segments in any code path so long as you have a
+ * reference to any segment in that code path.
+ *
+ * When first created, the segment is in a detached state, meaning that it knows the
+ * segments that came before it but those segments don't know that this new segment
+ * follows it. Only when `CodePathSegment#markUsed()` is called on a segment does it
+ * officially become part of the code path by updating the previous segments to know
+ * that this new segment follows.
*/
class CodePathSegment {
/**
+ * Creates a new instance.
* @param {string} id An identifier.
* @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
* This array includes unreachable segments.
@@ -49,27 +61,25 @@ class CodePathSegment {
this.id = id;
/**
- * An array of the next segments.
+ * An array of the next reachable segments.
* @type {CodePathSegment[]}
*/
this.nextSegments = [];
/**
- * An array of the previous segments.
+ * An array of the previous reachable segments.
* @type {CodePathSegment[]}
*/
this.prevSegments = allPrevSegments.filter(isReachable);
/**
- * An array of the next segments.
- * This array includes unreachable segments.
+ * An array of all next segments including reachable and unreachable.
* @type {CodePathSegment[]}
*/
this.allNextSegments = [];
/**
- * An array of the previous segments.
- * This array includes unreachable segments.
+ * An array of all previous segments including reachable and unreachable.
* @type {CodePathSegment[]}
*/
this.allPrevSegments = allPrevSegments;
@@ -83,7 +93,11 @@ class CodePathSegment {
// Internal data.
Object.defineProperty(this, "internal", {
value: {
+
+ // determines if the segment has been attached to the code path
used: false,
+
+ // array of previous segments coming from the end of a loop
loopedPrevSegments: []
}
});
@@ -113,9 +127,10 @@ class CodePathSegment {
}
/**
- * Creates a segment that follows given segments.
+ * Creates a new segment and appends it after the given segments.
* @param {string} id An identifier.
- * @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
+ * @param {CodePathSegment[]} allPrevSegments An array of the previous segments
+ * to append to.
* @returns {CodePathSegment} The created segment.
*/
static newNext(id, allPrevSegments) {
@@ -127,7 +142,7 @@ class CodePathSegment {
}
/**
- * Creates an unreachable segment that follows given segments.
+ * Creates an unreachable segment and appends it after the given segments.
* @param {string} id An identifier.
* @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
* @returns {CodePathSegment} The created segment.
@@ -137,7 +152,7 @@ class CodePathSegment {
/*
* In `if (a) return a; foo();` case, the unreachable segment preceded by
- * the return statement is not used but must not be remove.
+ * the return statement is not used but must not be removed.
*/
CodePathSegment.markUsed(segment);
@@ -157,7 +172,7 @@ class CodePathSegment {
}
/**
- * Makes a given segment being used.
+ * Marks a given segment as used.
*
* And this function registers the segment into the previous segments as a next.
* @param {CodePathSegment} segment A segment to mark.
@@ -172,6 +187,13 @@ class CodePathSegment {
let i;
if (segment.reachable) {
+
+ /*
+ * If the segment is reachable, then it's officially part of the
+ * code path. This loops through all previous segments to update
+ * their list of next segments. Because the segment is reachable,
+ * it's added to both `nextSegments` and `allNextSegments`.
+ */
for (i = 0; i < segment.allPrevSegments.length; ++i) {
const prevSegment = segment.allPrevSegments[i];
@@ -179,6 +201,13 @@ class CodePathSegment {
prevSegment.nextSegments.push(segment);
}
} else {
+
+ /*
+ * If the segment is not reachable, then it's not officially part of the
+ * code path. This loops through all previous segments to update
+ * their list of next segments. Because the segment is not reachable,
+ * it's added only to `allNextSegments`.
+ */
for (i = 0; i < segment.allPrevSegments.length; ++i) {
segment.allPrevSegments[i].allNextSegments.push(segment);
}
@@ -196,19 +225,20 @@ class CodePathSegment {
}
/**
- * Replaces unused segments with the previous segments of each unused segment.
- * @param {CodePathSegment[]} segments An array of segments to replace.
- * @returns {CodePathSegment[]} The replaced array.
+ * Creates a new array based on an array of segments. If any segment in the
+ * array is unused, then it is replaced by all of its previous segments.
+ * All used segments are returned as-is without replacement.
+ * @param {CodePathSegment[]} segments The array of segments to flatten.
+ * @returns {CodePathSegment[]} The flattened array.
*/
static flattenUnusedSegments(segments) {
- const done = Object.create(null);
- const retv = [];
+ const done = new Set();
for (let i = 0; i < segments.length; ++i) {
const segment = segments[i];
// Ignores duplicated.
- if (done[segment.id]) {
+ if (done.has(segment)) {
continue;
}
@@ -217,18 +247,16 @@ class CodePathSegment {
for (let j = 0; j < segment.allPrevSegments.length; ++j) {
const prevSegment = segment.allPrevSegments[j];
- if (!done[prevSegment.id]) {
- done[prevSegment.id] = true;
- retv.push(prevSegment);
+ if (!done.has(prevSegment)) {
+ done.add(prevSegment);
}
}
} else {
- done[segment.id] = true;
- retv.push(segment);
+ done.add(segment);
}
}
- return retv;
+ return [...done];
}
}
diff --git a/lib/linter/code-path-analysis/code-path-state.js b/lib/linter/code-path-analysis/code-path-state.js
index d187297d32b..2b0dc2bfca0 100644
--- a/lib/linter/code-path-analysis/code-path-state.js
+++ b/lib/linter/code-path-analysis/code-path-state.js
@@ -12,13 +12,622 @@
const CodePathSegment = require("./code-path-segment"),
ForkContext = require("./fork-context");
+//-----------------------------------------------------------------------------
+// Contexts
+//-----------------------------------------------------------------------------
+
+/**
+ * Represents the context in which a `break` statement can be used.
+ *
+ * A `break` statement without a label is only valid in a few places in
+ * JavaScript: any type of loop or a `switch` statement. Otherwise, `break`
+ * without a label causes a syntax error. For these contexts, `breakable` is
+ * set to `true` to indicate that a `break` without a label is valid.
+ *
+ * However, a `break` statement with a label is also valid inside of a labeled
+ * statement. For example, this is valid:
+ *
+ * a : {
+ * break a;
+ * }
+ *
+ * The `breakable` property is set false for labeled statements to indicate
+ * that `break` without a label is invalid.
+ */
+class BreakContext {
+
+ /**
+ * Creates a new instance.
+ * @param {BreakContext} upperContext The previous `BreakContext`.
+ * @param {boolean} breakable Indicates if we are inside a statement where
+ * `break` without a label will exit the statement.
+ * @param {string|null} label The label for the statement.
+ * @param {ForkContext} forkContext The current fork context.
+ */
+ constructor(upperContext, breakable, label, forkContext) {
+
+ /**
+ * The previous `BreakContext`
+ * @type {BreakContext}
+ */
+ this.upper = upperContext;
+
+ /**
+ * Indicates if we are inside a statement where `break` without a label
+ * will exit the statement.
+ * @type {boolean}
+ */
+ this.breakable = breakable;
+
+ /**
+ * The label associated with the statement.
+ * @type {string|null}
+ */
+ this.label = label;
+
+ /**
+ * The fork context for the `break`.
+ * @type {ForkContext}
+ */
+ this.brokenForkContext = ForkContext.newEmpty(forkContext);
+ }
+}
+
+/**
+ * Represents the context for `ChainExpression` nodes.
+ */
+class ChainContext {
+
+ /**
+ * Creates a new instance.
+ * @param {ChainContext} upperContext The previous `ChainContext`.
+ */
+ constructor(upperContext) {
+
+ /**
+ * The previous `ChainContext`
+ * @type {ChainContext}
+ */
+ this.upper = upperContext;
+
+ /**
+ * The number of choice contexts inside of the `ChainContext`.
+ * @type {number}
+ */
+ this.choiceContextCount = 0;
+
+ }
+}
+
+/**
+ * Represents a choice in the code path.
+ *
+ * Choices are created by logical operators such as `&&`, loops, conditionals,
+ * and `if` statements. This is the point at which the code path has a choice of
+ * which direction to go.
+ *
+ * The result of a choice might be in the left (test) expression of another choice,
+ * and in that case, may create a new fork. For example, `a || b` is a choice
+ * but does not create a new fork because the result of the expression is
+ * not used as the test expression in another expression. In this case,
+ * `isForkingAsResult` is false. In the expression `a || b || c`, the `a || b`
+ * expression appears as the test expression for `|| c`, so the
+ * result of `a || b` creates a fork because execution may or may not
+ * continue to `|| c`. `isForkingAsResult` for `a || b` in this case is true
+ * while `isForkingAsResult` for `|| c` is false. (`isForkingAsResult` is always
+ * false for `if` statements, conditional expressions, and loops.)
+ *
+ * All of the choices except one (`??`) operate on a true/false fork, meaning if
+ * true go one way and if false go the other (tracked by `trueForkContext` and
+ * `falseForkContext`). The `??` operator doesn't operate on true/false because
+ * the left expression is evaluated to be nullish or not, so only if nullish do
+ * we fork to the right expression (tracked by `nullishForkContext`).
+ */
+class ChoiceContext {
+
+ /**
+ * Creates a new instance.
+ * @param {ChoiceContext} upperContext The previous `ChoiceContext`.
+ * @param {string} kind The kind of choice. If it's a logical or assignment expression, this
+ * is `"&&"` or `"||"` or `"??"`; if it's an `if` statement or
+ * conditional expression, this is `"test"`; otherwise, this is `"loop"`.
+ * @param {boolean} isForkingAsResult Indicates if the result of the choice
+ * creates a fork.
+ * @param {ForkContext} forkContext The containing `ForkContext`.
+ */
+ constructor(upperContext, kind, isForkingAsResult, forkContext) {
+
+ /**
+ * The previous `ChoiceContext`
+ * @type {ChoiceContext}
+ */
+ this.upper = upperContext;
+
+ /**
+ * The kind of choice. If it's a logical or assignment expression, this
+ * is `"&&"` or `"||"` or `"??"`; if it's an `if` statement or
+ * conditional expression, this is `"test"`; otherwise, this is `"loop"`.
+ * @type {string}
+ */
+ this.kind = kind;
+
+ /**
+ * Indicates if the result of the choice forks the code path.
+ * @type {boolean}
+ */
+ this.isForkingAsResult = isForkingAsResult;
+
+ /**
+ * The fork context for the `true` path of the choice.
+ * @type {ForkContext}
+ */
+ this.trueForkContext = ForkContext.newEmpty(forkContext);
+
+ /**
+ * The fork context for the `false` path of the choice.
+ * @type {ForkContext}
+ */
+ this.falseForkContext = ForkContext.newEmpty(forkContext);
+
+ /**
+ * The fork context for when the choice result is `null` or `undefined`.
+ * @type {ForkContext}
+ */
+ this.nullishForkContext = ForkContext.newEmpty(forkContext);
+
+ /**
+ * Indicates if any of `trueForkContext`, `falseForkContext`, or
+ * `nullishForkContext` have been updated with segments from a child context.
+ * @type {boolean}
+ */
+ this.processed = false;
+ }
+
+}
+
+/**
+ * Base class for all loop contexts.
+ */
+class LoopContextBase {
+
+ /**
+ * Creates a new instance.
+ * @param {LoopContext|null} upperContext The previous `LoopContext`.
+ * @param {string} type The AST node's `type` for the loop.
+ * @param {string|null} label The label for the loop from an enclosing `LabeledStatement`.
+ * @param {BreakContext} breakContext The context for breaking the loop.
+ */
+ constructor(upperContext, type, label, breakContext) {
+
+ /**
+ * The previous `LoopContext`.
+ * @type {LoopContext}
+ */
+ this.upper = upperContext;
+
+ /**
+ * The AST node's `type` for the loop.
+ * @type {string}
+ */
+ this.type = type;
+
+ /**
+ * The label for the loop from an enclosing `LabeledStatement`.
+ * @type {string|null}
+ */
+ this.label = label;
+
+ /**
+ * The fork context for when `break` is encountered.
+ * @type {ForkContext}
+ */
+ this.brokenForkContext = breakContext.brokenForkContext;
+ }
+}
+
+/**
+ * Represents the context for a `while` loop.
+ */
+class WhileLoopContext extends LoopContextBase {
+
+ /**
+ * Creates a new instance.
+ * @param {LoopContext|null} upperContext The previous `LoopContext`.
+ * @param {string|null} label The label for the loop from an enclosing `LabeledStatement`.
+ * @param {BreakContext} breakContext The context for breaking the loop.
+ */
+ constructor(upperContext, label, breakContext) {
+ super(upperContext, "WhileStatement", label, breakContext);
+
+ /**
+ * The hardcoded literal boolean test condition for
+ * the loop. Used to catch infinite or skipped loops.
+ * @type {boolean|undefined}
+ */
+ this.test = void 0;
+
+ /**
+ * The segments representing the test condition where `continue` will
+ * jump to. The test condition will typically have just one segment but
+ * it's possible for there to be more than one.
+ * @type {Array|null}
+ */
+ this.continueDestSegments = null;
+ }
+}
+
+/**
+ * Represents the context for a `do-while` loop.
+ */
+class DoWhileLoopContext extends LoopContextBase {
+
+ /**
+ * Creates a new instance.
+ * @param {LoopContext|null} upperContext The previous `LoopContext`.
+ * @param {string|null} label The label for the loop from an enclosing `LabeledStatement`.
+ * @param {BreakContext} breakContext The context for breaking the loop.
+ * @param {ForkContext} forkContext The enclosing fork context.
+ */
+ constructor(upperContext, label, breakContext, forkContext) {
+ super(upperContext, "DoWhileStatement", label, breakContext);
+
+ /**
+ * The hardcoded literal boolean test condition for
+ * the loop. Used to catch infinite or skipped loops.
+ * @type {boolean|undefined}
+ */
+ this.test = void 0;
+
+ /**
+ * The segments at the start of the loop body. This is the only loop
+ * where the test comes at the end, so the first iteration always
+ * happens and we need a reference to the first statements.
+ * @type {Array|null}
+ */
+ this.entrySegments = null;
+
+ /**
+ * The fork context to follow when a `continue` is found.
+ * @type {ForkContext}
+ */
+ this.continueForkContext = ForkContext.newEmpty(forkContext);
+ }
+}
+
+/**
+ * Represents the context for a `for` loop.
+ */
+class ForLoopContext extends LoopContextBase {
+
+ /**
+ * Creates a new instance.
+ * @param {LoopContext|null} upperContext The previous `LoopContext`.
+ * @param {string|null} label The label for the loop from an enclosing `LabeledStatement`.
+ * @param {BreakContext} breakContext The context for breaking the loop.
+ */
+ constructor(upperContext, label, breakContext) {
+ super(upperContext, "ForStatement", label, breakContext);
+
+ /**
+ * The hardcoded literal boolean test condition for
+ * the loop. Used to catch infinite or skipped loops.
+ * @type {boolean|undefined}
+ */
+ this.test = void 0;
+
+ /**
+ * The end of the init expression. This may change during the lifetime
+ * of the instance as we traverse the loop because some loops don't have
+ * an init expression.
+ * @type {Array|null}
+ */
+ this.endOfInitSegments = null;
+
+ /**
+ * The start of the test expression. This may change during the lifetime
+ * of the instance as we traverse the loop because some loops don't have
+ * a test expression.
+ * @type {Array|null}
+ */
+ this.testSegments = null;
+
+ /**
+ * The end of the test expression. This may change during the lifetime
+ * of the instance as we traverse the loop because some loops don't have
+ * a test expression.
+ * @type {Array|null}
+ */
+ this.endOfTestSegments = null;
+
+ /**
+ * The start of the update expression. This may change during the lifetime
+ * of the instance as we traverse the loop because some loops don't have
+ * an update expression.
+ * @type {Array|null}
+ */
+ this.updateSegments = null;
+
+ /**
+ * The end of the update expresion. This may change during the lifetime
+ * of the instance as we traverse the loop because some loops don't have
+ * an update expression.
+ * @type {Array|null}
+ */
+ this.endOfUpdateSegments = null;
+
+ /**
+ * The segments representing the test condition where `continue` will
+ * jump to. The test condition will typically have just one segment but
+ * it's possible for there to be more than one. This may change during the
+ * lifetime of the instance as we traverse the loop because some loops
+ * don't have an update expression. When there is an update expression, this
+ * will end up pointing to that expression; otherwise it will end up pointing
+ * to the test expression.
+ * @type {Array|null}
+ */
+ this.continueDestSegments = null;
+ }
+}
+
+/**
+ * Represents the context for a `for-in` loop.
+ *
+ * Terminology:
+ * - "left" means the part of the loop to the left of the `in` keyword. For
+ * example, in `for (var x in y)`, the left is `var x`.
+ * - "right" means the part of the loop to the right of the `in` keyword. For
+ * example, in `for (var x in y)`, the right is `y`.
+ */
+class ForInLoopContext extends LoopContextBase {
+
+ /**
+ * Creates a new instance.
+ * @param {LoopContext|null} upperContext The previous `LoopContext`.
+ * @param {string|null} label The label for the loop from an enclosing `LabeledStatement`.
+ * @param {BreakContext} breakContext The context for breaking the loop.
+ */
+ constructor(upperContext, label, breakContext) {
+ super(upperContext, "ForInStatement", label, breakContext);
+
+ /**
+ * The segments that came immediately before the start of the loop.
+ * This allows you to traverse backwards out of the loop into the
+ * surrounding code. This is necessary to evaluate the right expression
+ * correctly, as it must be evaluated in the same way as the left
+ * expression, but the pointer to these segments would otherwise be
+ * lost if not stored on the instance. Once the right expression has
+ * been evaluated, this property is no longer used.
+ * @type {Array|null}
+ */
+ this.prevSegments = null;
+
+ /**
+ * Segments representing the start of everything to the left of the
+ * `in` keyword. This can be used to move forward towards
+ * `endOfLeftSegments`. `leftSegments` and `endOfLeftSegments` are
+ * effectively the head and tail of a doubly-linked list.
+ * @type {Array|null}
+ */
+ this.leftSegments = null;
+
+ /**
+ * Segments representing the end of everything to the left of the
+ * `in` keyword. This can be used to move backward towards `leftSegments`.
+ * `leftSegments` and `endOfLeftSegments` are effectively the head
+ * and tail of a doubly-linked list.
+ * @type {Array|null}
+ */
+ this.endOfLeftSegments = null;
+
+ /**
+ * The segments representing the left expression where `continue` will
+ * jump to. In `for-in` loops, `continue` must always re-execute the
+ * left expression each time through the loop. This contains the same
+ * segments as `leftSegments`, but is duplicated here so each loop
+ * context has the same property pointing to where `continue` should
+ * end up.
+ * @type {Array|null}
+ */
+ this.continueDestSegments = null;
+ }
+}
+
+/**
+ * Represents the context for a `for-of` loop.
+ */
+class ForOfLoopContext extends LoopContextBase {
+
+ /**
+ * Creates a new instance.
+ * @param {LoopContext|null} upperContext The previous `LoopContext`.
+ * @param {string|null} label The label for the loop from an enclosing `LabeledStatement`.
+ * @param {BreakContext} breakContext The context for breaking the loop.
+ */
+ constructor(upperContext, label, breakContext) {
+ super(upperContext, "ForOfStatement", label, breakContext);
+
+ /**
+ * The segments that came immediately before the start of the loop.
+ * This allows you to traverse backwards out of the loop into the
+ * surrounding code. This is necessary to evaluate the right expression
+ * correctly, as it must be evaluated in the same way as the left
+ * expression, but the pointer to these segments would otherwise be
+ * lost if not stored on the instance. Once the right expression has
+ * been evaluated, this property is no longer used.
+ * @type {Array|null}
+ */
+ this.prevSegments = null;
+
+ /**
+ * Segments representing the start of everything to the left of the
+ * `of` keyword. This can be used to move forward towards
+ * `endOfLeftSegments`. `leftSegments` and `endOfLeftSegments` are
+ * effectively the head and tail of a doubly-linked list.
+ * @type {Array|null}
+ */
+ this.leftSegments = null;
+
+ /**
+ * Segments representing the end of everything to the left of the
+ * `of` keyword. This can be used to move backward towards `leftSegments`.
+ * `leftSegments` and `endOfLeftSegments` are effectively the head
+ * and tail of a doubly-linked list.
+ * @type {Array|null}
+ */
+ this.endOfLeftSegments = null;
+
+ /**
+ * The segments representing the left expression where `continue` will
+ * jump to. In `for-in` loops, `continue` must always re-execute the
+ * left expression each time through the loop. This contains the same
+ * segments as `leftSegments`, but is duplicated here so each loop
+ * context has the same property pointing to where `continue` should
+ * end up.
+ * @type {Array|null}
+ */
+ this.continueDestSegments = null;
+ }
+}
+
+/**
+ * Represents the context for any loop.
+ * @typedef {WhileLoopContext|DoWhileLoopContext|ForLoopContext|ForInLoopContext|ForOfLoopContext} LoopContext
+ */
+
+/**
+ * Represents the context for a `switch` statement.
+ */
+class SwitchContext {
+
+ /**
+ * Creates a new instance.
+ * @param {SwitchContext} upperContext The previous context.
+ * @param {boolean} hasCase Indicates if there is at least one `case` statement.
+ * `default` doesn't count.
+ */
+ constructor(upperContext, hasCase) {
+
+ /**
+ * The previous context.
+ * @type {SwitchContext}
+ */
+ this.upper = upperContext;
+
+ /**
+ * Indicates if there is at least one `case` statement. `default` doesn't count.
+ * @type {boolean}
+ */
+ this.hasCase = hasCase;
+
+ /**
+ * The `default` keyword.
+ * @type {Array|null}
+ */
+ this.defaultSegments = null;
+
+ /**
+ * The default case body starting segments.
+ * @type {Array|null}
+ */
+ this.defaultBodySegments = null;
+
+ /**
+ * Indicates if a `default` case and is empty exists.
+ * @type {boolean}
+ */
+ this.foundEmptyDefault = false;
+
+ /**
+ * Indicates that a `default` exists and is the last case.
+ * @type {boolean}
+ */
+ this.lastIsDefault = false;
+
+ /**
+ * The number of fork contexts created. This is equivalent to the
+ * number of `case` statements plus a `default` statement (if present).
+ * @type {number}
+ */
+ this.forkCount = 0;
+ }
+}
+
+/**
+ * Represents the context for a `try` statement.
+ */
+class TryContext {
+
+ /**
+ * Creates a new instance.
+ * @param {TryContext} upperContext The previous context.
+ * @param {boolean} hasFinalizer Indicates if the `try` statement has a
+ * `finally` block.
+ * @param {ForkContext} forkContext The enclosing fork context.
+ */
+ constructor(upperContext, hasFinalizer, forkContext) {
+
+ /**
+ * The previous context.
+ * @type {TryContext}
+ */
+ this.upper = upperContext;
+
+ /**
+ * Indicates if the `try` statement has a `finally` block.
+ * @type {boolean}
+ */
+ this.hasFinalizer = hasFinalizer;
+
+ /**
+ * Tracks the traversal position inside of the `try` statement. This is
+ * used to help determine the context necessary to create paths because
+ * a `try` statement may or may not have `catch` or `finally` blocks,
+ * and code paths behave differently in those blocks.
+ * @type {"try"|"catch"|"finally"}
+ */
+ this.position = "try";
+
+ /**
+ * If the `try` statement has a `finally` block, this affects how a
+ * `return` statement behaves in the `try` block. Without `finally`,
+ * `return` behaves as usual and doesn't require a fork; with `finally`,
+ * `return` forks into the `finally` block, so we need a fork context
+ * to track it.
+ * @type {ForkContext|null}
+ */
+ this.returnedForkContext = hasFinalizer
+ ? ForkContext.newEmpty(forkContext)
+ : null;
+
+ /**
+ * When a `throw` occurs inside of a `try` block, the code path forks
+ * into the `catch` or `finally` blocks, and this fork context tracks
+ * that path.
+ * @type {ForkContext}
+ */
+ this.thrownForkContext = ForkContext.newEmpty(forkContext);
+
+ /**
+ * Indicates if the last segment in the `try` block is reachable.
+ * @type {boolean}
+ */
+ this.lastOfTryIsReachable = false;
+
+ /**
+ * Indicates if the last segment in the `catch` block is reachable.
+ * @type {boolean}
+ */
+ this.lastOfCatchIsReachable = false;
+ }
+}
+
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/**
* Adds given segments into the `dest` array.
- * If the `others` array does not includes the given segments, adds to the `all`
+ * If the `others` array does not include the given segments, adds to the `all`
* array as well.
*
* This adds only reachable and used segments.
@@ -40,9 +649,9 @@ function addToReturnedOrThrown(dest, others, all, segments) {
}
/**
- * Gets a loop-context for a `continue` statement.
- * @param {CodePathState} state A state to get.
- * @param {string} label The label of a `continue` statement.
+ * Gets a loop context for a `continue` statement based on a given label.
+ * @param {CodePathState} state The state to search within.
+ * @param {string|null} label The label of a `continue` statement.
* @returns {LoopContext} A loop-context for a `continue` statement.
*/
function getContinueContext(state, label) {
@@ -65,9 +674,9 @@ function getContinueContext(state, label) {
/**
* Gets a context for a `break` statement.
- * @param {CodePathState} state A state to get.
- * @param {string} label The label of a `break` statement.
- * @returns {LoopContext|SwitchContext} A context for a `break` statement.
+ * @param {CodePathState} state The state to search within.
+ * @param {string|null} label The label of a `break` statement.
+ * @returns {BreakContext} A context for a `break` statement.
*/
function getBreakContext(state, label) {
let context = state.breakContext;
@@ -84,8 +693,10 @@ function getBreakContext(state, label) {
}
/**
- * Gets a context for a `return` statement.
- * @param {CodePathState} state A state to get.
+ * Gets a context for a `return` statement. There is just one special case:
+ * if there is a `try` statement with a `finally` block, because that alters
+ * how `return` behaves; otherwise, this just passes through the given state.
+ * @param {CodePathState} state The state to search within
* @returns {TryContext|CodePathState} A context for a `return` statement.
*/
function getReturnContext(state) {
@@ -102,8 +713,11 @@ function getReturnContext(state) {
}
/**
- * Gets a context for a `throw` statement.
- * @param {CodePathState} state A state to get.
+ * Gets a context for a `throw` statement. There is just one special case:
+ * if there is a `try` statement with a `finally` block and we are inside of
+ * a `catch` because that changes how `throw` behaves; otherwise, this just
+ * passes through the given state.
+ * @param {CodePathState} state The state to search within.
* @returns {TryContext|CodePathState} A context for a `throw` statement.
*/
function getThrowContext(state) {
@@ -122,13 +736,13 @@ function getThrowContext(state) {
}
/**
- * Removes a given element from a given array.
- * @param {any[]} xs An array to remove the specific element.
- * @param {any} x An element to be removed.
+ * Removes a given value from a given array.
+ * @param {any[]} elements An array to remove the specific element.
+ * @param {any} value The value to be removed.
* @returns {void}
*/
-function remove(xs, x) {
- xs.splice(xs.indexOf(x), 1);
+function removeFromArray(elements, value) {
+ elements.splice(elements.indexOf(value), 1);
}
/**
@@ -141,48 +755,77 @@ function remove(xs, x) {
* @param {CodePathSegment[]} nextSegments Backward segments to disconnect.
* @returns {void}
*/
-function removeConnection(prevSegments, nextSegments) {
+function disconnectSegments(prevSegments, nextSegments) {
for (let i = 0; i < prevSegments.length; ++i) {
const prevSegment = prevSegments[i];
const nextSegment = nextSegments[i];
- remove(prevSegment.nextSegments, nextSegment);
- remove(prevSegment.allNextSegments, nextSegment);
- remove(nextSegment.prevSegments, prevSegment);
- remove(nextSegment.allPrevSegments, prevSegment);
+ removeFromArray(prevSegment.nextSegments, nextSegment);
+ removeFromArray(prevSegment.allNextSegments, nextSegment);
+ removeFromArray(nextSegment.prevSegments, prevSegment);
+ removeFromArray(nextSegment.allPrevSegments, prevSegment);
}
}
/**
- * Creates looping path.
- * @param {CodePathState} state The instance.
+ * Creates looping path between two arrays of segments, ensuring that there are
+ * paths going between matching segments in the arrays.
+ * @param {CodePathState} state The state to operate on.
* @param {CodePathSegment[]} unflattenedFromSegments Segments which are source.
* @param {CodePathSegment[]} unflattenedToSegments Segments which are destination.
* @returns {void}
*/
function makeLooped(state, unflattenedFromSegments, unflattenedToSegments) {
+
const fromSegments = CodePathSegment.flattenUnusedSegments(unflattenedFromSegments);
const toSegments = CodePathSegment.flattenUnusedSegments(unflattenedToSegments);
-
const end = Math.min(fromSegments.length, toSegments.length);
+ /*
+ * This loop effectively updates a doubly-linked list between two collections
+ * of segments making sure that segments in the same array indices are
+ * combined to create a path.
+ */
for (let i = 0; i < end; ++i) {
+
+ // get the segments in matching array indices
const fromSegment = fromSegments[i];
const toSegment = toSegments[i];
+ /*
+ * If the destination segment is reachable, then create a path from the
+ * source segment to the destination segment.
+ */
if (toSegment.reachable) {
fromSegment.nextSegments.push(toSegment);
}
+
+ /*
+ * If the source segment is reachable, then create a path from the
+ * destination segment back to the source segment.
+ */
if (fromSegment.reachable) {
toSegment.prevSegments.push(fromSegment);
}
+
+ /*
+ * Also update the arrays that don't care if the segments are reachable
+ * or not. This should always happen regardless of anything else.
+ */
fromSegment.allNextSegments.push(toSegment);
toSegment.allPrevSegments.push(fromSegment);
+ /*
+ * If the destination segment has at least two previous segments in its
+ * path then that means there was one previous segment before this iteration
+ * of the loop was executed. So, we need to mark the source segment as
+ * looped.
+ */
if (toSegment.allPrevSegments.length >= 2) {
CodePathSegment.markPrevSegmentAsLooped(toSegment, fromSegment);
}
+ // let the code path analyzer know that there's been a loop created
state.notifyLooped(fromSegment, toSegment);
}
}
@@ -198,15 +841,27 @@ function makeLooped(state, unflattenedFromSegments, unflattenedToSegments) {
* @returns {void}
*/
function finalizeTestSegmentsOfFor(context, choiceContext, head) {
+
+ /*
+ * If this choice context doesn't already contain paths from a
+ * child context, then add the current head to each potential path.
+ */
if (!choiceContext.processed) {
choiceContext.trueForkContext.add(head);
choiceContext.falseForkContext.add(head);
- choiceContext.qqForkContext.add(head);
+ choiceContext.nullishForkContext.add(head);
}
+ /*
+ * If the test condition isn't a hardcoded truthy value, then `break`
+ * must follow the same path as if the test condition is false. To represent
+ * that, we append the path for when the loop test is false (represented by
+ * `falseForkContext`) to the `brokenForkContext`.
+ */
if (context.test !== true) {
context.brokenForkContext.addAll(choiceContext.falseForkContext);
}
+
context.endOfTestSegments = choiceContext.trueForkContext.makeNext(0, -1);
}
@@ -220,35 +875,124 @@ function finalizeTestSegmentsOfFor(context, choiceContext, head) {
class CodePathState {
/**
+ * Creates a new instance.
* @param {IdGenerator} idGenerator An id generator to generate id for code
* path segments.
* @param {Function} onLooped A callback function to notify looping.
*/
constructor(idGenerator, onLooped) {
+
+ /**
+ * The ID generator to use when creating new segments.
+ * @type {IdGenerator}
+ */
this.idGenerator = idGenerator;
+
+ /**
+ * A callback function to call when there is a loop.
+ * @type {Function}
+ */
this.notifyLooped = onLooped;
+
+ /**
+ * The root fork context for this state.
+ * @type {ForkContext}
+ */
this.forkContext = ForkContext.newRoot(idGenerator);
+
+ /**
+ * Context for logical expressions, conditional expressions, `if` statements,
+ * and loops.
+ * @type {ChoiceContext}
+ */
this.choiceContext = null;
+
+ /**
+ * Context for `switch` statements.
+ * @type {SwitchContext}
+ */
this.switchContext = null;
+
+ /**
+ * Context for `try` statements.
+ * @type {TryContext}
+ */
this.tryContext = null;
+
+ /**
+ * Context for loop statements.
+ * @type {LoopContext}
+ */
this.loopContext = null;
+
+ /**
+ * Context for `break` statements.
+ * @type {BreakContext}
+ */
this.breakContext = null;
+
+ /**
+ * Context for `ChainExpression` nodes.
+ * @type {ChainContext}
+ */
this.chainContext = null;
+ /**
+ * An array that tracks the current segments in the state. The array
+ * starts empty and segments are added with each `onCodePathSegmentStart`
+ * event and removed with each `onCodePathSegmentEnd` event. Effectively,
+ * this is tracking the code path segment traversal as the state is
+ * modified.
+ * @type {Array}
+ */
this.currentSegments = [];
+
+ /**
+ * Tracks the starting segment for this path. This value never changes.
+ * @type {CodePathSegment}
+ */
this.initialSegment = this.forkContext.head[0];
- // returnedSegments and thrownSegments push elements into finalSegments also.
- const final = this.finalSegments = [];
- const returned = this.returnedForkContext = [];
- const thrown = this.thrownForkContext = [];
+ /**
+ * The final segments of the code path which are either `return` or `throw`.
+ * This is a union of the segments in `returnedForkContext` and `thrownForkContext`.
+ * @type {Array}
+ */
+ this.finalSegments = [];
+
+ /**
+ * The final segments of the code path which are `return`. These
+ * segments are also contained in `finalSegments`.
+ * @type {Array}
+ */
+ this.returnedForkContext = [];
+
+ /**
+ * The final segments of the code path which are `throw`. These
+ * segments are also contained in `finalSegments`.
+ * @type {Array}
+ */
+ this.thrownForkContext = [];
+
+ /*
+ * We add an `add` method so that these look more like fork contexts and
+ * can be used interchangeably when a fork context is needed to add more
+ * segments to a path.
+ *
+ * Ultimately, we want anything added to `returned` or `thrown` to also
+ * be added to `final`. We only add reachable and used segments to these
+ * arrays.
+ */
+ const final = this.finalSegments;
+ const returned = this.returnedForkContext;
+ const thrown = this.thrownForkContext;
returned.add = addToReturnedOrThrown.bind(null, returned, thrown, final);
thrown.add = addToReturnedOrThrown.bind(null, thrown, returned, final);
}
/**
- * The head segments.
+ * A passthrough property exposing the current pointer as part of the API.
* @type {CodePathSegment[]}
*/
get headSegments() {
@@ -341,77 +1085,72 @@ class CodePathState {
* If the new context is LogicalExpression's or AssignmentExpression's, this is `"&&"` or `"||"` or `"??"`.
* If it's IfStatement's or ConditionalExpression's, this is `"test"`.
* Otherwise, this is `"loop"`.
- * @param {boolean} isForkingAsResult A flag that shows that goes different
- * paths between `true` and `false`.
+ * @param {boolean} isForkingAsResult Indicates if the result of the choice
+ * creates a fork.
* @returns {void}
*/
pushChoiceContext(kind, isForkingAsResult) {
- this.choiceContext = {
- upper: this.choiceContext,
- kind,
- isForkingAsResult,
- trueForkContext: ForkContext.newEmpty(this.forkContext),
- falseForkContext: ForkContext.newEmpty(this.forkContext),
- qqForkContext: ForkContext.newEmpty(this.forkContext),
- processed: false
- };
+ this.choiceContext = new ChoiceContext(this.choiceContext, kind, isForkingAsResult, this.forkContext);
}
/**
* Pops the last choice context and finalizes it.
+ * This is called upon leaving a node that represents a choice.
* @throws {Error} (Unreachable.)
* @returns {ChoiceContext} The popped context.
*/
popChoiceContext() {
- const context = this.choiceContext;
-
- this.choiceContext = context.upper;
-
+ const poppedChoiceContext = this.choiceContext;
const forkContext = this.forkContext;
- const headSegments = forkContext.head;
+ const head = forkContext.head;
+
+ this.choiceContext = poppedChoiceContext.upper;
- switch (context.kind) {
+ switch (poppedChoiceContext.kind) {
case "&&":
case "||":
case "??":
/*
- * If any result were not transferred from child contexts,
- * this sets the head segments to both cases.
- * The head segments are the path of the right-hand operand.
+ * The `head` are the path of the right-hand operand.
+ * If we haven't previously added segments from child contexts,
+ * then we add these segments to all possible forks.
*/
- if (!context.processed) {
- context.trueForkContext.add(headSegments);
- context.falseForkContext.add(headSegments);
- context.qqForkContext.add(headSegments);
+ if (!poppedChoiceContext.processed) {
+ poppedChoiceContext.trueForkContext.add(head);
+ poppedChoiceContext.falseForkContext.add(head);
+ poppedChoiceContext.nullishForkContext.add(head);
}
/*
- * Transfers results to upper context if this context is in
- * test chunk.
+ * If this context is the left (test) expression for another choice
+ * context, such as `a || b` in the expression `a || b || c`,
+ * then we take the segments for this context and move them up
+ * to the parent context.
*/
- if (context.isForkingAsResult) {
+ if (poppedChoiceContext.isForkingAsResult) {
const parentContext = this.choiceContext;
- parentContext.trueForkContext.addAll(context.trueForkContext);
- parentContext.falseForkContext.addAll(context.falseForkContext);
- parentContext.qqForkContext.addAll(context.qqForkContext);
+ parentContext.trueForkContext.addAll(poppedChoiceContext.trueForkContext);
+ parentContext.falseForkContext.addAll(poppedChoiceContext.falseForkContext);
+ parentContext.nullishForkContext.addAll(poppedChoiceContext.nullishForkContext);
parentContext.processed = true;
- return context;
+ // Exit early so we don't collapse all paths into one.
+ return poppedChoiceContext;
}
break;
case "test":
- if (!context.processed) {
+ if (!poppedChoiceContext.processed) {
/*
* The head segments are the path of the `if` block here.
* Updates the `true` path with the end of the `if` block.
*/
- context.trueForkContext.clear();
- context.trueForkContext.add(headSegments);
+ poppedChoiceContext.trueForkContext.clear();
+ poppedChoiceContext.trueForkContext.add(head);
} else {
/*
@@ -419,8 +1158,8 @@ class CodePathState {
* Updates the `false` path with the end of the `else`
* block.
*/
- context.falseForkContext.clear();
- context.falseForkContext.add(headSegments);
+ poppedChoiceContext.falseForkContext.clear();
+ poppedChoiceContext.falseForkContext.add(head);
}
break;
@@ -428,82 +1167,129 @@ class CodePathState {
case "loop":
/*
- * Loops are addressed in popLoopContext().
- * This is called from popLoopContext().
+ * Loops are addressed in `popLoopContext()` so just return
+ * the context without modification.
*/
- return context;
+ return poppedChoiceContext;
/* c8 ignore next */
default:
throw new Error("unreachable");
}
- // Merges all paths.
- const prevForkContext = context.trueForkContext;
+ /*
+ * Merge the true path with the false path to create a single path.
+ */
+ const combinedForkContext = poppedChoiceContext.trueForkContext;
- prevForkContext.addAll(context.falseForkContext);
- forkContext.replaceHead(prevForkContext.makeNext(0, -1));
+ combinedForkContext.addAll(poppedChoiceContext.falseForkContext);
+ forkContext.replaceHead(combinedForkContext.makeNext(0, -1));
- return context;
+ return poppedChoiceContext;
}
/**
- * Makes a code path segment of the right-hand operand of a logical
+ * Creates a code path segment to represent right-hand operand of a logical
* expression.
+ * This is called in the preprocessing phase when entering a node.
* @throws {Error} (Unreachable.)
* @returns {void}
*/
makeLogicalRight() {
- const context = this.choiceContext;
+ const currentChoiceContext = this.choiceContext;
const forkContext = this.forkContext;
- if (context.processed) {
+ if (currentChoiceContext.processed) {
/*
- * This got segments already from the child choice context.
- * Creates the next path from own true/false fork context.
+ * This context was already assigned segments from a child
+ * choice context. In this case, we are concerned only about
+ * the path that does not short-circuit and so ends up on the
+ * right-hand operand of the logical expression.
*/
let prevForkContext;
- switch (context.kind) {
+ switch (currentChoiceContext.kind) {
case "&&": // if true then go to the right-hand side.
- prevForkContext = context.trueForkContext;
+ prevForkContext = currentChoiceContext.trueForkContext;
break;
case "||": // if false then go to the right-hand side.
- prevForkContext = context.falseForkContext;
+ prevForkContext = currentChoiceContext.falseForkContext;
break;
- case "??": // Both true/false can short-circuit, so needs the third path to go to the right-hand side. That's qqForkContext.
- prevForkContext = context.qqForkContext;
+ case "??": // Both true/false can short-circuit, so needs the third path to go to the right-hand side. That's nullishForkContext.
+ prevForkContext = currentChoiceContext.nullishForkContext;
break;
default:
throw new Error("unreachable");
}
+ /*
+ * Create the segment for the right-hand operand of the logical expression
+ * and adjust the fork context pointer to point there. The right-hand segment
+ * is added at the end of all segments in `prevForkContext`.
+ */
forkContext.replaceHead(prevForkContext.makeNext(0, -1));
+
+ /*
+ * We no longer need this list of segments.
+ *
+ * Reset `processed` because we've removed the segments from the child
+ * choice context. This allows `popChoiceContext()` to continue adding
+ * segments later.
+ */
prevForkContext.clear();
- context.processed = false;
+ currentChoiceContext.processed = false;
+
} else {
/*
- * This did not get segments from the child choice context.
- * So addresses the head segments.
- * The head segments are the path of the left-hand operand.
+ * This choice context was not assigned segments from a child
+ * choice context, which means that it's a terminal logical
+ * expression.
+ *
+ * `head` is the segments for the left-hand operand of the
+ * logical expression.
+ *
+ * Each of the fork contexts below are empty at this point. We choose
+ * the path(s) that will short-circuit and add the segment for the
+ * left-hand operand to it. Ultimately, this will be the only segment
+ * in that path due to the short-circuting, so we are just seeding
+ * these paths to start.
*/
- switch (context.kind) {
- case "&&": // the false path can short-circuit.
- context.falseForkContext.add(forkContext.head);
+ switch (currentChoiceContext.kind) {
+ case "&&":
+
+ /*
+ * In most contexts, when a && expression evaluates to false,
+ * it short circuits, so we need to account for that by setting
+ * the `falseForkContext` to the left operand.
+ *
+ * When a && expression is the left-hand operand for a ??
+ * expression, such as `(a && b) ?? c`, a nullish value will
+ * also short-circuit in a different way than a false value,
+ * so we also set the `nullishForkContext` to the left operand.
+ * This path is only used with a ?? expression and is thrown
+ * away for any other type of logical expression, so it's safe
+ * to always add.
+ */
+ currentChoiceContext.falseForkContext.add(forkContext.head);
+ currentChoiceContext.nullishForkContext.add(forkContext.head);
break;
case "||": // the true path can short-circuit.
- context.trueForkContext.add(forkContext.head);
+ currentChoiceContext.trueForkContext.add(forkContext.head);
break;
case "??": // both can short-circuit.
- context.trueForkContext.add(forkContext.head);
- context.falseForkContext.add(forkContext.head);
+ currentChoiceContext.trueForkContext.add(forkContext.head);
+ currentChoiceContext.falseForkContext.add(forkContext.head);
break;
default:
throw new Error("unreachable");
}
+ /*
+ * Create the segment for the right-hand operand of the logical expression
+ * and adjust the fork context pointer to point there.
+ */
forkContext.replaceHead(forkContext.makeNext(-1, -1));
}
}
@@ -524,7 +1310,7 @@ class CodePathState {
if (!context.processed) {
context.trueForkContext.add(forkContext.head);
context.falseForkContext.add(forkContext.head);
- context.qqForkContext.add(forkContext.head);
+ context.nullishForkContext.add(forkContext.head);
}
context.processed = false;
@@ -562,22 +1348,20 @@ class CodePathState {
//--------------------------------------------------------------------------
/**
- * 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.
+ * Pushes a new `ChainExpression` context to the stack. This method is
+ * called when entering a `ChainExpression` node. A chain 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
- };
+ this.chainContext = new ChainContext(this.chainContext);
}
/**
- * 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.
+ * 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() {
@@ -586,7 +1370,7 @@ class CodePathState {
this.chainContext = context.upper;
// pop all choice contexts of this.
- for (let i = context.countChoiceContexts; i > 0; --i) {
+ for (let i = context.choiceContextCount; i > 0; --i) {
this.popChoiceContext();
}
}
@@ -599,7 +1383,7 @@ class CodePathState {
*/
makeOptionalNode() {
if (this.chainContext) {
- this.chainContext.countChoiceContexts += 1;
+ this.chainContext.choiceContextCount += 1;
this.pushChoiceContext("??", false);
}
}
@@ -627,16 +1411,7 @@ class CodePathState {
* @returns {void}
*/
pushSwitchContext(hasCase, label) {
- this.switchContext = {
- upper: this.switchContext,
- hasCase,
- defaultSegments: null,
- defaultBodySegments: null,
- foundDefault: false,
- lastIsDefault: false,
- countForks: 0
- };
-
+ this.switchContext = new SwitchContext(this.switchContext, hasCase);
this.pushBreakContext(true, label);
}
@@ -657,7 +1432,7 @@ class CodePathState {
const forkContext = this.forkContext;
const brokenForkContext = this.popBreakContext().brokenForkContext;
- if (context.countForks === 0) {
+ if (context.forkCount === 0) {
/*
* When there is only one `default` chunk and there is one or more
@@ -684,47 +1459,54 @@ class CodePathState {
brokenForkContext.add(lastSegments);
/*
- * A path which is failed in all case test should be connected to path
- * of `default` chunk.
+ * Any value that doesn't match a `case` test should flow to the default
+ * case. That happens normally when the default case is last in the `switch`,
+ * but if it's not, we need to rewire some of the paths to be correct.
*/
if (!context.lastIsDefault) {
if (context.defaultBodySegments) {
/*
- * Remove a link from `default` label to its chunk.
- * It's false route.
+ * There is a non-empty default case, so remove the path from the `default`
+ * label to its body for an accurate representation.
+ */
+ disconnectSegments(context.defaultSegments, context.defaultBodySegments);
+
+ /*
+ * Connect the path from the last non-default case to the body of the
+ * default case.
*/
- removeConnection(context.defaultSegments, context.defaultBodySegments);
makeLooped(this, lastCaseSegments, context.defaultBodySegments);
+
} else {
/*
- * It handles the last case body as broken if `default` chunk
- * does not exist.
+ * There is no default case, so we treat this as if the last case
+ * had a `break` in it.
*/
brokenForkContext.add(lastCaseSegments);
}
}
- // Pops the segment context stack until the entry segment.
- for (let i = 0; i < context.countForks; ++i) {
+ // Traverse up to the original fork context for the `switch` statement
+ for (let i = 0; i < context.forkCount; ++i) {
this.forkContext = this.forkContext.upper;
}
/*
- * Creates a path from all brokenForkContext paths.
- * This is a path after switch statement.
+ * Creates a path from all `brokenForkContext` paths.
+ * This is a path after `switch` statement.
*/
this.forkContext.replaceHead(brokenForkContext.makeNext(0, -1));
}
/**
* Makes a code path segment for a `SwitchCase` node.
- * @param {boolean} isEmpty `true` if the body is empty.
- * @param {boolean} isDefault `true` if the body is the default case.
+ * @param {boolean} isCaseBodyEmpty `true` if the body is empty.
+ * @param {boolean} isDefaultCase `true` if the body is the default case.
* @returns {void}
*/
- makeSwitchCaseBody(isEmpty, isDefault) {
+ makeSwitchCaseBody(isCaseBodyEmpty, isDefaultCase) {
const context = this.switchContext;
if (!context.hasCase) {
@@ -734,7 +1516,7 @@ class CodePathState {
/*
* Merge forks.
* The parent fork context has two segments.
- * Those are from the current case and the body of the previous case.
+ * Those are from the current `case` and the body of the previous case.
*/
const parentForkContext = this.forkContext;
const forkContext = this.pushForkContext();
@@ -742,26 +1524,53 @@ class CodePathState {
forkContext.add(parentForkContext.makeNext(0, -1));
/*
- * Save `default` chunk info.
- * If the `default` label is not at the last, we must make a path from
- * the last `case` to the `default` chunk.
+ * Add information about the default case.
+ *
+ * The purpose of this is to identify the starting segments for the
+ * default case to make sure there is a path there.
*/
- if (isDefault) {
+ if (isDefaultCase) {
+
+ /*
+ * This is the default case in the `switch`.
+ *
+ * We first save the current pointer as `defaultSegments` to point
+ * to the `default` keyword.
+ */
context.defaultSegments = parentForkContext.head;
- if (isEmpty) {
- context.foundDefault = true;
+
+ /*
+ * If the body of the case is empty then we just set
+ * `foundEmptyDefault` to true; otherwise, we save a reference
+ * to the current pointer as `defaultBodySegments`.
+ */
+ if (isCaseBodyEmpty) {
+ context.foundEmptyDefault = true;
} else {
context.defaultBodySegments = forkContext.head;
}
+
} else {
- if (!isEmpty && context.foundDefault) {
- context.foundDefault = false;
+
+ /*
+ * This is not the default case in the `switch`.
+ *
+ * If it's not empty and there is already an empty default case found,
+ * that means the default case actually comes before this case,
+ * and that it will fall through to this case. So, we can now
+ * ignore the previous default case (reset `foundEmptyDefault` to false)
+ * and set `defaultBodySegments` to the current segments because this is
+ * effectively the new default case.
+ */
+ if (!isCaseBodyEmpty && context.foundEmptyDefault) {
+ context.foundEmptyDefault = false;
context.defaultBodySegments = forkContext.head;
}
}
- context.lastIsDefault = isDefault;
- context.countForks += 1;
+ // keep track if the default case ends up last
+ context.lastIsDefault = isDefaultCase;
+ context.forkCount += 1;
}
//--------------------------------------------------------------------------
@@ -775,19 +1584,7 @@ class CodePathState {
* @returns {void}
*/
pushTryContext(hasFinalizer) {
- this.tryContext = {
- upper: this.tryContext,
- position: "try",
- hasFinalizer,
-
- returnedForkContext: hasFinalizer
- ? ForkContext.newEmpty(this.forkContext)
- : null,
-
- thrownForkContext: ForkContext.newEmpty(this.forkContext),
- lastOfTryIsReachable: false,
- lastOfCatchIsReachable: false
- };
+ this.tryContext = new TryContext(this.tryContext, hasFinalizer, this.forkContext);
}
/**
@@ -799,25 +1596,35 @@ class CodePathState {
this.tryContext = context.upper;
+ /*
+ * If we're inside the `catch` block, that means there is no `finally`,
+ * so we can process the `try` and `catch` blocks the simple way and
+ * merge their two paths.
+ */
if (context.position === "catch") {
-
- // Merges two paths from the `try` block and `catch` block merely.
this.popForkContext();
return;
}
/*
- * The following process is executed only when there is the `finally`
+ * The following process is executed only when there is a `finally`
* block.
*/
- const returned = context.returnedForkContext;
- const thrown = context.thrownForkContext;
+ const originalReturnedForkContext = context.returnedForkContext;
+ const originalThrownForkContext = context.thrownForkContext;
- if (returned.empty && thrown.empty) {
+ // no `return` or `throw` in `try` or `catch` so there's nothing left to do
+ if (originalReturnedForkContext.empty && originalThrownForkContext.empty) {
return;
}
+ /*
+ * The following process is executed only when there is a `finally`
+ * block and there was a `return` or `throw` in the `try` or `catch`
+ * blocks.
+ */
+
// Separate head to normal paths and leaving paths.
const headSegments = this.forkContext.head;
@@ -826,10 +1633,10 @@ class CodePathState {
const leavingSegments = headSegments.slice(headSegments.length / 2 | 0);
// Forwards the leaving path to upper contexts.
- if (!returned.empty) {
+ if (!originalReturnedForkContext.empty) {
getReturnContext(this).returnedForkContext.add(leavingSegments);
}
- if (!thrown.empty) {
+ if (!originalThrownForkContext.empty) {
getThrowContext(this).thrownForkContext.add(leavingSegments);
}
@@ -852,16 +1659,20 @@ class CodePathState {
makeCatchBlock() {
const context = this.tryContext;
const forkContext = this.forkContext;
- const thrown = context.thrownForkContext;
+ const originalThrownForkContext = context.thrownForkContext;
- // Update state.
+ /*
+ * We are now in a catch block so we need to update the context
+ * with that information. This includes creating a new fork
+ * context in case we encounter any `throw` statements here.
+ */
context.position = "catch";
context.thrownForkContext = ForkContext.newEmpty(forkContext);
context.lastOfTryIsReachable = forkContext.reachable;
- // Merge thrown paths.
- thrown.add(forkContext.head);
- const thrownSegments = thrown.makeNext(0, -1);
+ // Merge the thrown paths from the `try` and `catch` blocks
+ originalThrownForkContext.add(forkContext.head);
+ const thrownSegments = originalThrownForkContext.makeNext(0, -1);
// Fork to a bypass and the merged thrown path.
this.pushForkContext();
@@ -880,8 +1691,8 @@ class CodePathState {
makeFinallyBlock() {
const context = this.tryContext;
let forkContext = this.forkContext;
- const returned = context.returnedForkContext;
- const thrown = context.thrownForkContext;
+ const originalReturnedForkContext = context.returnedForkContext;
+ const originalThrownForContext = context.thrownForkContext;
const headOfLeavingSegments = forkContext.head;
// Update state.
@@ -895,9 +1706,15 @@ class CodePathState {
} else {
context.lastOfTryIsReachable = forkContext.reachable;
}
+
+
context.position = "finally";
- if (returned.empty && thrown.empty) {
+ /*
+ * If there was no `return` or `throw` in either the `try` or `catch`
+ * blocks, then there's no further code paths to create for `finally`.
+ */
+ if (originalReturnedForkContext.empty && originalThrownForContext.empty) {
// This path does not leave.
return;
@@ -905,18 +1722,18 @@ class CodePathState {
/*
* Create a parallel segment from merging returned and thrown.
- * This segment will leave at the end of this finally block.
+ * This segment will leave at the end of this `finally` block.
*/
const segments = forkContext.makeNext(-1, -1);
for (let i = 0; i < forkContext.count; ++i) {
const prevSegsOfLeavingSegment = [headOfLeavingSegments[i]];
- for (let j = 0; j < returned.segmentsList.length; ++j) {
- prevSegsOfLeavingSegment.push(returned.segmentsList[j][i]);
+ for (let j = 0; j < originalReturnedForkContext.segmentsList.length; ++j) {
+ prevSegsOfLeavingSegment.push(originalReturnedForkContext.segmentsList[j][i]);
}
- for (let j = 0; j < thrown.segmentsList.length; ++j) {
- prevSegsOfLeavingSegment.push(thrown.segmentsList[j][i]);
+ for (let j = 0; j < originalThrownForContext.segmentsList.length; ++j) {
+ prevSegsOfLeavingSegment.push(originalThrownForContext.segmentsList[j][i]);
}
segments.push(
@@ -971,63 +1788,32 @@ class CodePathState {
*/
pushLoopContext(type, label) {
const forkContext = this.forkContext;
+
+ // All loops need a path to account for `break` statements
const breakContext = this.pushBreakContext(true, label);
switch (type) {
case "WhileStatement":
this.pushChoiceContext("loop", false);
- this.loopContext = {
- upper: this.loopContext,
- type,
- label,
- test: void 0,
- continueDestSegments: null,
- brokenForkContext: breakContext.brokenForkContext
- };
+ this.loopContext = new WhileLoopContext(this.loopContext, label, breakContext);
break;
case "DoWhileStatement":
this.pushChoiceContext("loop", false);
- this.loopContext = {
- upper: this.loopContext,
- type,
- label,
- test: void 0,
- entrySegments: null,
- continueForkContext: ForkContext.newEmpty(forkContext),
- brokenForkContext: breakContext.brokenForkContext
- };
+ this.loopContext = new DoWhileLoopContext(this.loopContext, label, breakContext, forkContext);
break;
case "ForStatement":
this.pushChoiceContext("loop", false);
- this.loopContext = {
- upper: this.loopContext,
- type,
- label,
- test: void 0,
- endOfInitSegments: null,
- testSegments: null,
- endOfTestSegments: null,
- updateSegments: null,
- endOfUpdateSegments: null,
- continueDestSegments: null,
- brokenForkContext: breakContext.brokenForkContext
- };
+ this.loopContext = new ForLoopContext(this.loopContext, label, breakContext);
break;
case "ForInStatement":
+ this.loopContext = new ForInLoopContext(this.loopContext, label, breakContext);
+ break;
+
case "ForOfStatement":
- this.loopContext = {
- upper: this.loopContext,
- type,
- label,
- prevSegments: null,
- leftSegments: null,
- endOfLeftSegments: null,
- continueDestSegments: null,
- brokenForkContext: breakContext.brokenForkContext
- };
+ this.loopContext = new ForOfLoopContext(this.loopContext, label, breakContext);
break;
/* c8 ignore next */
@@ -1054,6 +1840,11 @@ class CodePathState {
case "WhileStatement":
case "ForStatement":
this.popChoiceContext();
+
+ /*
+ * Creates the path from the end of the loop body up to the
+ * location where `continue` would jump to.
+ */
makeLooped(
this,
forkContext.head,
@@ -1068,11 +1859,21 @@ class CodePathState {
choiceContext.trueForkContext.add(forkContext.head);
choiceContext.falseForkContext.add(forkContext.head);
}
+
+ /*
+ * If this isn't a hardcoded `true` condition, then `break`
+ * should continue down the path as if the condition evaluated
+ * to false.
+ */
if (context.test !== true) {
brokenForkContext.addAll(choiceContext.falseForkContext);
}
- // `true` paths go to looping.
+ /*
+ * When the condition is true, the loop continues back to the top,
+ * so create a path from each possible true condition back to the
+ * top of the loop.
+ */
const segmentsList = choiceContext.trueForkContext.segmentsList;
for (let i = 0; i < segmentsList.length; ++i) {
@@ -1088,6 +1889,11 @@ class CodePathState {
case "ForInStatement":
case "ForOfStatement":
brokenForkContext.add(forkContext.head);
+
+ /*
+ * Creates the path from the end of the loop body up to the
+ * left expression (left of `in` or `of`) of the loop.
+ */
makeLooped(
this,
forkContext.head,
@@ -1100,7 +1906,14 @@ class CodePathState {
throw new Error("unreachable");
}
- // Go next.
+ /*
+ * If there wasn't a `break` statement in the loop, then we're at
+ * the end of the loop's path, so we make an unreachable segment
+ * to mark that.
+ *
+ * If there was a `break` statement, then we continue on into the
+ * `brokenForkContext`.
+ */
if (brokenForkContext.empty) {
forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
} else {
@@ -1138,7 +1951,11 @@ class CodePathState {
choiceContext.falseForkContext.add(forkContext.head);
}
- // Update state.
+ /*
+ * If this isn't a hardcoded `true` condition, then `break`
+ * should continue down the path as if the condition evaluated
+ * to false.
+ */
if (context.test !== true) {
context.brokenForkContext.addAll(choiceContext.falseForkContext);
}
@@ -1170,7 +1987,11 @@ class CodePathState {
context.test = test;
- // Creates paths of `continue` statements.
+ /*
+ * If there is a `continue` statement in the loop then `continueForkContext`
+ * won't be empty. We wire up the path from `continue` to the loop
+ * test condition and then continue the traversal in the root fork context.
+ */
if (!context.continueForkContext.empty) {
context.continueForkContext.add(forkContext.head);
const testSegments = context.continueForkContext.makeNext(0, -1);
@@ -1190,7 +2011,14 @@ class CodePathState {
const endOfInitSegments = forkContext.head;
const testSegments = forkContext.makeNext(-1, -1);
- // Update state.
+ /*
+ * Update the state.
+ *
+ * The `continueDestSegments` are set to `testSegments` because we
+ * don't yet know if there is an update expression in this loop. So,
+ * from what we already know at this point, a `continue` statement
+ * will jump back to the test expression.
+ */
context.test = test;
context.endOfInitSegments = endOfInitSegments;
context.continueDestSegments = context.testSegments = testSegments;
@@ -1217,7 +2045,14 @@ class CodePathState {
context.endOfInitSegments = forkContext.head;
}
- // Update state.
+ /*
+ * Update the state.
+ *
+ * The `continueDestSegments` are now set to `updateSegments` because we
+ * know there is an update expression in this loop. So, a `continue` statement
+ * in the loop will jump to the update expression first, and then to any
+ * test expression the loop might have.
+ */
const updateSegments = forkContext.makeDisconnected(-1, -1);
context.continueDestSegments = context.updateSegments = updateSegments;
@@ -1233,11 +2068,30 @@ class CodePathState {
const choiceContext = this.choiceContext;
const forkContext = this.forkContext;
- // Update state.
+ /*
+ * Determine what to do based on which part of the `for` loop are present.
+ * 1. If there is an update expression, then `updateSegments` is not null and
+ * we need to assign `endOfUpdateSegments`, and if there is a test
+ * expression, we then need to create the looped path to get back to
+ * the test condition.
+ * 2. If there is no update expression but there is a test expression,
+ * then we only need to update the test segment information.
+ * 3. If there is no update expression and no test expression, then we
+ * just save `endOfInitSegments`.
+ */
if (context.updateSegments) {
context.endOfUpdateSegments = forkContext.head;
- // `update` -> `test`
+ /*
+ * In a `for` loop that has both an update expression and a test
+ * condition, execution flows from the test expression into the
+ * loop body, to the update expression, and then back to the test
+ * expression to determine if the loop should continue.
+ *
+ * To account for that, we need to make a path from the end of the
+ * update expression to the start of the test expression. This is
+ * effectively what creates the loop in the code path.
+ */
if (context.testSegments) {
makeLooped(
this,
@@ -1257,12 +2111,18 @@ class CodePathState {
let bodySegments = context.endOfTestSegments;
+ /*
+ * If there is a test condition, then there `endOfTestSegments` is also
+ * the start of the loop body. If there isn't a test condition then
+ * `bodySegments` will be null and we need to look elsewhere to find
+ * the start of the body.
+ *
+ * The body starts at the end of the init expression and ends at the end
+ * of the update expression, so we use those locations to determine the
+ * body segments.
+ */
if (!bodySegments) {
- /*
- * If there is not the `test` part, the `body` path comes from the
- * `init` part and the `update` part.
- */
const prevForkContext = ForkContext.newEmpty(forkContext);
prevForkContext.add(context.endOfInitSegments);
@@ -1272,7 +2132,16 @@ class CodePathState {
bodySegments = prevForkContext.makeNext(0, -1);
}
+
+ /*
+ * If there was no test condition and no update expression, then
+ * `continueDestSegments` will be null. In that case, a
+ * `continue` should skip directly to the body of the loop.
+ * Otherwise, we want to keep the current `continueDestSegments`.
+ */
context.continueDestSegments = context.continueDestSegments || bodySegments;
+
+ // move pointer to the body
forkContext.replaceHead(bodySegments);
}
@@ -1336,19 +2205,15 @@ class CodePathState {
//--------------------------------------------------------------------------
/**
- * Creates new context for BreakStatement.
- * @param {boolean} breakable The flag to indicate it can break by
- * an unlabeled BreakStatement.
- * @param {string|null} label The label of this context.
- * @returns {Object} The new context.
+ * Creates new context in which a `break` statement can be used. This occurs inside of a loop,
+ * labeled statement, or switch statement.
+ * @param {boolean} breakable Indicates if we are inside a statement where
+ * `break` without a label will exit the statement.
+ * @param {string|null} label The label associated with the statement.
+ * @returns {BreakContext} The new context.
*/
pushBreakContext(breakable, label) {
- this.breakContext = {
- upper: this.breakContext,
- breakable,
- label,
- brokenForkContext: ForkContext.newEmpty(this.forkContext)
- };
+ this.breakContext = new BreakContext(this.breakContext, breakable, label, this.forkContext);
return this.breakContext;
}
@@ -1380,7 +2245,7 @@ class CodePathState {
*
* It registers the head segment to a context of `break`.
* It makes new unreachable segment, then it set the head with the segment.
- * @param {string} label A label of the break statement.
+ * @param {string|null} label A label of the break statement.
* @returns {void}
*/
makeBreak(label) {
@@ -1406,7 +2271,7 @@ class CodePathState {
*
* It makes a looping path.
* It makes new unreachable segment, then it set the head with the segment.
- * @param {string} label A label of the continue statement.
+ * @param {string|null} label A label of the continue statement.
* @returns {void}
*/
makeContinue(label) {
@@ -1422,7 +2287,7 @@ class CodePathState {
if (context.continueDestSegments) {
makeLooped(this, forkContext.head, context.continueDestSegments);
- // If the context is a for-in/of loop, this effects a break also.
+ // If the context is a for-in/of loop, this affects a break also.
if (context.type === "ForInStatement" ||
context.type === "ForOfStatement"
) {
diff --git a/lib/linter/code-path-analysis/code-path.js b/lib/linter/code-path-analysis/code-path.js
index a028ca69481..3bf570d754b 100644
--- a/lib/linter/code-path-analysis/code-path.js
+++ b/lib/linter/code-path-analysis/code-path.js
@@ -80,7 +80,9 @@ class CodePath {
}
/**
- * The initial code path segment.
+ * The initial code path segment. This is the segment that is at the head
+ * of the code path.
+ * This is a passthrough to the underlying `CodePathState`.
* @type {CodePathSegment}
*/
get initialSegment() {
@@ -88,8 +90,10 @@ class CodePath {
}
/**
- * Final code path segments.
- * This array is a mix of `returnedSegments` and `thrownSegments`.
+ * Final code path segments. These are the terminal (tail) segments in the
+ * code path, which is the combination of `returnedSegments` and `thrownSegments`.
+ * All segments in this array are reachable.
+ * This is a passthrough to the underlying `CodePathState`.
* @type {CodePathSegment[]}
*/
get finalSegments() {
@@ -97,9 +101,14 @@ class CodePath {
}
/**
- * Final code path segments which is with `return` statements.
- * This array contains the last path segment if it's reachable.
- * Since the reachable last path returns `undefined`.
+ * Final code path segments that represent normal completion of the code path.
+ * For functions, this means both explicit `return` statements and implicit returns,
+ * such as the last reachable segment in a function that does not have an
+ * explicit `return` as this implicitly returns `undefined`. For scripts,
+ * modules, class field initializers, and class static blocks, this means
+ * all lines of code have been executed.
+ * These segments are also present in `finalSegments`.
+ * This is a passthrough to the underlying `CodePathState`.
* @type {CodePathSegment[]}
*/
get returnedSegments() {
@@ -107,7 +116,9 @@ class CodePath {
}
/**
- * Final code path segments which is with `throw` statements.
+ * Final code path segments that represent `throw` statements.
+ * This is a passthrough to the underlying `CodePathState`.
+ * These segments are also present in `finalSegments`.
* @type {CodePathSegment[]}
*/
get thrownSegments() {
@@ -115,8 +126,14 @@ class CodePath {
}
/**
- * Current code path segments.
+ * Tracks the traversal of the code path through each segment. This array
+ * starts empty and segments are added or removed as the code path is
+ * traversed. This array always ends up empty at the end of a code path
+ * traversal. The `CodePathState` uses this to track its progress through
+ * the code path.
+ * This is a passthrough to the underlying `CodePathState`.
* @type {CodePathSegment[]}
+ * @deprecated
*/
get currentSegments() {
return this.internal.currentSegments;
@@ -125,46 +142,70 @@ class CodePath {
/**
* Traverses all segments in this code path.
*
- * codePath.traverseSegments(function(segment, controller) {
+ * codePath.traverseSegments((segment, controller) => {
* // do something.
* });
*
* This method enumerates segments in order from the head.
*
- * The `controller` object has two methods.
+ * The `controller` argument has two methods:
*
- * - `controller.skip()` - Skip the following segments in this branch.
- * - `controller.break()` - Skip all following segments.
- * @param {Object} [options] Omittable.
- * @param {CodePathSegment} [options.first] The first segment to traverse.
- * @param {CodePathSegment} [options.last] The last segment to traverse.
+ * - `skip()` - skips the following segments in this branch
+ * - `break()` - skips all following segments in the traversal
+ *
+ * A note on the parameters: the `options` argument is optional. This means
+ * the first argument might be an options object or the callback function.
+ * @param {Object} [optionsOrCallback] Optional first and last segments to traverse.
+ * @param {CodePathSegment} [optionsOrCallback.first] The first segment to traverse.
+ * @param {CodePathSegment} [optionsOrCallback.last] The last segment to traverse.
* @param {Function} callback A callback function.
* @returns {void}
*/
- traverseSegments(options, callback) {
+ traverseSegments(optionsOrCallback, callback) {
+
+ // normalize the arguments into a callback and options
let resolvedOptions;
let resolvedCallback;
- if (typeof options === "function") {
- resolvedCallback = options;
+ if (typeof optionsOrCallback === "function") {
+ resolvedCallback = optionsOrCallback;
resolvedOptions = {};
} else {
- resolvedOptions = options || {};
+ resolvedOptions = optionsOrCallback || {};
resolvedCallback = callback;
}
+ // determine where to start traversing from based on the options
const startSegment = resolvedOptions.first || this.internal.initialSegment;
const lastSegment = resolvedOptions.last;
- let item = null;
+ // set up initial location information
+ let record = null;
let index = 0;
let end = 0;
let segment = null;
- const visited = Object.create(null);
+
+ // segments that have already been visited during traversal
+ const visited = new Set();
+
+ // tracks the traversal steps
const stack = [[startSegment, 0]];
+
+ // tracks the last skipped segment during traversal
let skippedSegment = null;
+
+ // indicates if we exited early from the traversal
let broken = false;
+
+ /**
+ * Maintains traversal state.
+ */
const controller = {
+
+ /**
+ * Skip the following segments in this branch.
+ * @returns {void}
+ */
skip() {
if (stack.length <= 1) {
broken = true;
@@ -172,32 +213,52 @@ class CodePath {
skippedSegment = stack[stack.length - 2][0];
}
},
+
+ /**
+ * Stop traversal completely - do not traverse to any
+ * other segments.
+ * @returns {void}
+ */
break() {
broken = true;
}
};
/**
- * Checks a given previous segment has been visited.
+ * Checks if a given previous segment has been visited.
* @param {CodePathSegment} prevSegment A previous segment to check.
* @returns {boolean} `true` if the segment has been visited.
*/
function isVisited(prevSegment) {
return (
- visited[prevSegment.id] ||
+ visited.has(prevSegment) ||
segment.isLoopedPrevSegment(prevSegment)
);
}
+ // the traversal
while (stack.length > 0) {
- item = stack[stack.length - 1];
- segment = item[0];
- index = item[1];
+
+ /*
+ * This isn't a pure stack. We use the top record all the time
+ * but don't always pop it off. The record is popped only if
+ * one of the following is true:
+ *
+ * 1) We have already visited the segment.
+ * 2) We have not visited *all* of the previous segments.
+ * 3) We have traversed past the available next segments.
+ *
+ * Otherwise, we just read the value and sometimes modify the
+ * record as we traverse.
+ */
+ record = stack[stack.length - 1];
+ segment = record[0];
+ index = record[1];
if (index === 0) {
// Skip if this segment has been visited already.
- if (visited[segment.id]) {
+ if (visited.has(segment)) {
stack.pop();
continue;
}
@@ -211,18 +272,29 @@ class CodePath {
continue;
}
- // Reset the flag of skipping if all branches have been skipped.
+ // Reset the skipping flag if all branches have been skipped.
if (skippedSegment && segment.prevSegments.includes(skippedSegment)) {
skippedSegment = null;
}
- visited[segment.id] = true;
+ visited.add(segment);
- // Call the callback when the first time.
+ /*
+ * If the most recent segment hasn't been skipped, then we call
+ * the callback, passing in the segment and the controller.
+ */
if (!skippedSegment) {
resolvedCallback.call(this, segment, controller);
+
+ // exit if we're at the last segment
if (segment === lastSegment) {
controller.skip();
}
+
+ /*
+ * If the previous statement was executed, or if the callback
+ * called a method on the controller, we might need to exit the
+ * loop, so check for that and break accordingly.
+ */
if (broken) {
break;
}
@@ -232,12 +304,35 @@ class CodePath {
// Update the stack.
end = segment.nextSegments.length - 1;
if (index < end) {
- item[1] += 1;
+
+ /*
+ * If we haven't yet visited all of the next segments, update
+ * the current top record on the stack to the next index to visit
+ * and then push a record for the current segment on top.
+ *
+ * Setting the current top record's index lets us know how many
+ * times we've been here and ensures that the segment won't be
+ * reprocessed (because we only process segments with an index
+ * of 0).
+ */
+ record[1] += 1;
stack.push([segment.nextSegments[index], 0]);
} else if (index === end) {
- item[0] = segment.nextSegments[index];
- item[1] = 0;
+
+ /*
+ * If we are at the last next segment, then reset the top record
+ * in the stack to next segment and set its index to 0 so it will
+ * be processed next.
+ */
+ record[0] = segment.nextSegments[index];
+ record[1] = 0;
} else {
+
+ /*
+ * If index > end, that means we have no more segments that need
+ * processing. So, we pop that record off of the stack in order to
+ * continue traversing at the next level up.
+ */
stack.pop();
}
}
diff --git a/lib/linter/code-path-analysis/debug-helpers.js b/lib/linter/code-path-analysis/debug-helpers.js
index e06b6cde5f1..c0e01a8248b 100644
--- a/lib/linter/code-path-analysis/debug-helpers.js
+++ b/lib/linter/code-path-analysis/debug-helpers.js
@@ -109,7 +109,7 @@ module.exports = {
text += "final[label=\"\",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];\n";
}
if (codePath.thrownSegments.length > 0) {
- text += "thrown[label=\"✘\",shape=circle,width=0.3,height=0.3,fixedsize];\n";
+ text += "thrown[label=\"✘\",shape=circle,width=0.3,height=0.3,fixedsize=true];\n";
}
const traceMap = Object.create(null);
diff --git a/lib/linter/code-path-analysis/fork-context.js b/lib/linter/code-path-analysis/fork-context.js
index 04c59b5e417..33140272f53 100644
--- a/lib/linter/code-path-analysis/fork-context.js
+++ b/lib/linter/code-path-analysis/fork-context.js
@@ -21,8 +21,8 @@ const assert = require("assert"),
//------------------------------------------------------------------------------
/**
- * Gets whether or not a given segment is reachable.
- * @param {CodePathSegment} segment A segment to get.
+ * Determines whether or not a given segment is reachable.
+ * @param {CodePathSegment} segment The segment to check.
* @returns {boolean} `true` if the segment is reachable.
*/
function isReachable(segment) {
@@ -30,32 +30,64 @@ function isReachable(segment) {
}
/**
- * Creates new segments from the specific range of `context.segmentsList`.
+ * Creates a new segment for each fork in the given context and appends it
+ * to the end of the specified range of segments. Ultimately, this ends up calling
+ * `new CodePathSegment()` for each of the forks using the `create` argument
+ * as a wrapper around special behavior.
+ *
+ * The `startIndex` and `endIndex` arguments specify a range of segments in
+ * `context` that should become `allPrevSegments` for the newly created
+ * `CodePathSegment` objects.
*
* When `context.segmentsList` is `[[a, b], [c, d], [e, f]]`, `begin` is `0`, and
- * `end` is `-1`, this creates `[g, h]`. This `g` is from `a`, `c`, and `e`.
- * This `h` is from `b`, `d`, and `f`.
- * @param {ForkContext} context An instance.
- * @param {number} begin The first index of the previous segments.
- * @param {number} end The last index of the previous segments.
- * @param {Function} create A factory function of new segments.
- * @returns {CodePathSegment[]} New segments.
+ * `end` is `-1`, this creates two new segments, `[g, h]`. This `g` is appended to
+ * the end of the path from `a`, `c`, and `e`. This `h` is appended to the end of
+ * `b`, `d`, and `f`.
+ * @param {ForkContext} context An instance from which the previous segments
+ * will be obtained.
+ * @param {number} startIndex The index of the first segment in the context
+ * that should be specified as previous segments for the newly created segments.
+ * @param {number} endIndex The index of the last segment in the context
+ * that should be specified as previous segments for the newly created segments.
+ * @param {Function} create A function that creates new `CodePathSegment`
+ * instances in a particular way. See the `CodePathSegment.new*` methods.
+ * @returns {Array} An array of the newly-created segments.
*/
-function makeSegments(context, begin, end, create) {
+function createSegments(context, startIndex, endIndex, create) {
+
+ /** @type {Array>} */
const list = context.segmentsList;
- const normalizedBegin = begin >= 0 ? begin : list.length + begin;
- const normalizedEnd = end >= 0 ? end : list.length + end;
+ /*
+ * Both `startIndex` and `endIndex` work the same way: if the number is zero
+ * or more, then the number is used as-is. If the number is negative,
+ * then that number is added to the length of the segments list to
+ * determine the index to use. That means -1 for either argument
+ * is the last element, -2 is the second to last, and so on.
+ *
+ * So if `startIndex` is 0, `endIndex` is -1, and `list.length` is 3, the
+ * effective `startIndex` is 0 and the effective `endIndex` is 2, so this function
+ * will include items at indices 0, 1, and 2.
+ *
+ * Therefore, if `startIndex` is -1 and `endIndex` is -1, that means we'll only
+ * be using the last segment in `list`.
+ */
+ const normalizedBegin = startIndex >= 0 ? startIndex : list.length + startIndex;
+ const normalizedEnd = endIndex >= 0 ? endIndex : list.length + endIndex;
+ /** @type {Array} */
const segments = [];
for (let i = 0; i < context.count; ++i) {
+
+ // this is passed into `new CodePathSegment` to add to code path.
const allPrevSegments = [];
for (let j = normalizedBegin; j <= normalizedEnd; ++j) {
allPrevSegments.push(list[j][i]);
}
+ // note: `create` is just a wrapper that augments `new CodePathSegment`.
segments.push(create(context.idGenerator.next(), allPrevSegments));
}
@@ -63,28 +95,57 @@ function makeSegments(context, begin, end, create) {
}
/**
- * `segments` becomes doubly in a `finally` block. Then if a code path exits by a
- * control statement (such as `break`, `continue`) from the `finally` block, the
- * destination's segments may be half of the source segments. In that case, this
- * merges segments.
- * @param {ForkContext} context An instance.
- * @param {CodePathSegment[]} segments Segments to merge.
- * @returns {CodePathSegment[]} The merged segments.
+ * Inside of a `finally` block we end up with two parallel paths. If the code path
+ * exits by a control statement (such as `break` or `continue`) from the `finally`
+ * block, then we need to merge the remaining parallel paths back into one.
+ * @param {ForkContext} context The fork context to work on.
+ * @param {Array} segments Segments to merge.
+ * @returns {Array} The merged segments.
*/
function mergeExtraSegments(context, segments) {
let currentSegments = segments;
+ /*
+ * We need to ensure that the array returned from this function contains no more
+ * than the number of segments that the context allows. `context.count` indicates
+ * how many items should be in the returned array to ensure that the new segment
+ * entries will line up with the already existing segment entries.
+ */
while (currentSegments.length > context.count) {
const merged = [];
- for (let i = 0, length = currentSegments.length / 2 | 0; i < length; ++i) {
+ /*
+ * Because `context.count` is a factor of 2 inside of a `finally` block,
+ * we can divide the segment count by 2 to merge the paths together.
+ * This loops through each segment in the list and creates a new `CodePathSegment`
+ * that has the segment and the segment two slots away as previous segments.
+ *
+ * If `currentSegments` is [a,b,c,d], this will create new segments e and f, such
+ * that:
+ *
+ * When `i` is 0:
+ * a->e
+ * c->e
+ *
+ * When `i` is 1:
+ * b->f
+ * d->f
+ */
+ for (let i = 0, length = Math.floor(currentSegments.length / 2); i < length; ++i) {
merged.push(CodePathSegment.newNext(
context.idGenerator.next(),
[currentSegments[i], currentSegments[i + length]]
));
}
+
+ /*
+ * Go through the loop condition one more time to see if we have the
+ * number of segments for the context. If not, we'll keep merging paths
+ * of the merged segments until we get there.
+ */
currentSegments = merged;
}
+
return currentSegments;
}
@@ -93,25 +154,55 @@ function mergeExtraSegments(context, segments) {
//------------------------------------------------------------------------------
/**
- * A class to manage forking.
+ * Manages the forking of code paths.
*/
class ForkContext {
/**
+ * Creates a new instance.
* @param {IdGenerator} idGenerator An identifier generator for segments.
- * @param {ForkContext|null} upper An upper fork context.
- * @param {number} count A number of parallel segments.
+ * @param {ForkContext|null} upper The preceding fork context.
+ * @param {number} count The number of parallel segments in each element
+ * of `segmentsList`.
*/
constructor(idGenerator, upper, count) {
+
+ /**
+ * The ID generator that will generate segment IDs for any new
+ * segments that are created.
+ * @type {IdGenerator}
+ */
this.idGenerator = idGenerator;
+
+ /**
+ * The preceding fork context.
+ * @type {ForkContext|null}
+ */
this.upper = upper;
+
+ /**
+ * The number of elements in each element of `segmentsList`. In most
+ * cases, this is 1 but can be 2 when there is a `finally` present,
+ * which forks the code path outside of normal flow. In the case of nested
+ * `finally` blocks, this can be a multiple of 2.
+ * @type {number}
+ */
this.count = count;
+
+ /**
+ * The segments within this context. Each element in this array has
+ * `count` elements that represent one step in each fork. For example,
+ * when `segmentsList` is `[[a, b], [c, d], [e, f]]`, there is one path
+ * a->c->e and one path b->d->f, and `count` is 2 because each element
+ * is an array with two elements.
+ * @type {Array>}
+ */
this.segmentsList = [];
}
/**
- * The head segments.
- * @type {CodePathSegment[]}
+ * The segments that begin this fork context.
+ * @type {Array}
*/
get head() {
const list = this.segmentsList;
@@ -120,7 +211,7 @@ class ForkContext {
}
/**
- * A flag which shows empty.
+ * Indicates if the context contains no segments.
* @type {boolean}
*/
get empty() {
@@ -128,7 +219,7 @@ class ForkContext {
}
/**
- * A flag which shows reachable.
+ * Indicates if there are any segments that are reachable.
* @type {boolean}
*/
get reachable() {
@@ -138,75 +229,82 @@ class ForkContext {
}
/**
- * Creates new segments from this context.
- * @param {number} begin The first index of previous segments.
- * @param {number} end The last index of previous segments.
- * @returns {CodePathSegment[]} New segments.
+ * Creates new segments in this context and appends them to the end of the
+ * already existing `CodePathSegment`s specified by `startIndex` and
+ * `endIndex`.
+ * @param {number} startIndex The index of the first segment in the context
+ * that should be specified as previous segments for the newly created segments.
+ * @param {number} endIndex The index of the last segment in the context
+ * that should be specified as previous segments for the newly created segments.
+ * @returns {Array} An array of the newly created segments.
*/
- makeNext(begin, end) {
- return makeSegments(this, begin, end, CodePathSegment.newNext);
+ makeNext(startIndex, endIndex) {
+ return createSegments(this, startIndex, endIndex, CodePathSegment.newNext);
}
/**
- * Creates new segments from this context.
- * The new segments is always unreachable.
- * @param {number} begin The first index of previous segments.
- * @param {number} end The last index of previous segments.
- * @returns {CodePathSegment[]} New segments.
+ * Creates new unreachable segments in this context and appends them to the end of the
+ * already existing `CodePathSegment`s specified by `startIndex` and
+ * `endIndex`.
+ * @param {number} startIndex The index of the first segment in the context
+ * that should be specified as previous segments for the newly created segments.
+ * @param {number} endIndex The index of the last segment in the context
+ * that should be specified as previous segments for the newly created segments.
+ * @returns {Array} An array of the newly created segments.
*/
- makeUnreachable(begin, end) {
- return makeSegments(this, begin, end, CodePathSegment.newUnreachable);
+ makeUnreachable(startIndex, endIndex) {
+ return createSegments(this, startIndex, endIndex, CodePathSegment.newUnreachable);
}
/**
- * Creates new segments from this context.
- * The new segments don't have connections for previous segments.
- * But these inherit the reachable flag from this context.
- * @param {number} begin The first index of previous segments.
- * @param {number} end The last index of previous segments.
- * @returns {CodePathSegment[]} New segments.
+ * Creates new segments in this context and does not append them to the end
+ * of the already existing `CodePathSegment`s specified by `startIndex` and
+ * `endIndex`. The `startIndex` and `endIndex` are only used to determine if
+ * the new segments should be reachable. If any of the segments in this range
+ * are reachable then the new segments are also reachable; otherwise, the new
+ * segments are unreachable.
+ * @param {number} startIndex The index of the first segment in the context
+ * that should be considered for reachability.
+ * @param {number} endIndex The index of the last segment in the context
+ * that should be considered for reachability.
+ * @returns {Array} An array of the newly created segments.
*/
- makeDisconnected(begin, end) {
- return makeSegments(this, begin, end, CodePathSegment.newDisconnected);
+ makeDisconnected(startIndex, endIndex) {
+ return createSegments(this, startIndex, endIndex, CodePathSegment.newDisconnected);
}
/**
- * Adds segments into this context.
- * The added segments become the head.
- * @param {CodePathSegment[]} segments Segments to add.
+ * Adds segments to the head of this context.
+ * @param {Array} segments The segments to add.
* @returns {void}
*/
add(segments) {
assert(segments.length >= this.count, `${segments.length} >= ${this.count}`);
-
this.segmentsList.push(mergeExtraSegments(this, segments));
}
/**
- * Replaces the head segments with given segments.
+ * Replaces the head segments with the given segments.
* The current head segments are removed.
- * @param {CodePathSegment[]} segments Segments to add.
+ * @param {Array} replacementHeadSegments The new head segments.
* @returns {void}
*/
- replaceHead(segments) {
- assert(segments.length >= this.count, `${segments.length} >= ${this.count}`);
-
- this.segmentsList.splice(-1, 1, mergeExtraSegments(this, segments));
+ replaceHead(replacementHeadSegments) {
+ assert(
+ replacementHeadSegments.length >= this.count,
+ `${replacementHeadSegments.length} >= ${this.count}`
+ );
+ this.segmentsList.splice(-1, 1, mergeExtraSegments(this, replacementHeadSegments));
}
/**
* Adds all segments of a given fork context into this context.
- * @param {ForkContext} context A fork context to add.
+ * @param {ForkContext} otherForkContext The fork context to add from.
* @returns {void}
*/
- addAll(context) {
- assert(context.count === this.count);
-
- const source = context.segmentsList;
-
- for (let i = 0; i < source.length; ++i) {
- this.segmentsList.push(source[i]);
- }
+ addAll(otherForkContext) {
+ assert(otherForkContext.count === this.count);
+ this.segmentsList.push(...otherForkContext.segmentsList);
}
/**
@@ -218,7 +316,8 @@ class ForkContext {
}
/**
- * Creates the root fork context.
+ * Creates a new root context, meaning that there are no parent
+ * fork contexts.
* @param {IdGenerator} idGenerator An identifier generator for segments.
* @returns {ForkContext} New fork context.
*/
@@ -233,14 +332,16 @@ class ForkContext {
/**
* Creates an empty fork context preceded by a given context.
* @param {ForkContext} parentContext The parent fork context.
- * @param {boolean} forkLeavingPath A flag which shows inside of `finally` block.
+ * @param {boolean} shouldForkLeavingPath Indicates that we are inside of
+ * a `finally` block and should therefore fork the path that leaves
+ * `finally`.
* @returns {ForkContext} New fork context.
*/
- static newEmpty(parentContext, forkLeavingPath) {
+ static newEmpty(parentContext, shouldForkLeavingPath) {
return new ForkContext(
parentContext.idGenerator,
parentContext,
- (forkLeavingPath ? 2 : 1) * parentContext.count
+ (shouldForkLeavingPath ? 2 : 1) * parentContext.count
);
}
}
diff --git a/lib/linter/config-comment-parser.js b/lib/linter/config-comment-parser.js
index 9aab3c44458..cde261204f5 100644
--- a/lib/linter/config-comment-parser.js
+++ b/lib/linter/config-comment-parser.js
@@ -139,7 +139,7 @@ module.exports = class ConfigCommentParser {
const items = {};
string.split(",").forEach(name => {
- const trimmedName = name.trim();
+ const trimmedName = name.trim().replace(/^(?['"]?)(?.*)\k$/us, "$");
if (trimmedName) {
items[trimmedName] = true;
diff --git a/lib/linter/linter.js b/lib/linter/linter.js
index e5d11e938b0..a99677e3118 100644
--- a/lib/linter/linter.js
+++ b/lib/linter/linter.js
@@ -42,7 +42,8 @@ const
ruleReplacements = require("../../conf/replacements.json");
const { getRuleFromConfig } = require("../config/flat-config-helpers");
const { FlatConfigArray } = require("../config/flat-config-array");
-
+const { RuleValidator } = require("../config/rule-validator");
+const { assertIsRuleOptions, assertIsRuleSeverity } = require("../config/flat-config-schema");
const debug = require("debug")("eslint:linter");
const MAX_AUTOFIX_PASSES = 10;
const DEFAULT_PARSER_NAME = "espree";
@@ -50,7 +51,6 @@ const DEFAULT_ECMA_VERSION = 5;
const commentParser = new ConfigCommentParser();
const DEFAULT_ERROR_LOC = { start: { line: 1, column: 0 }, end: { line: 1, column: 1 } };
const parserSymbol = Symbol.for("eslint.RuleTester.parser");
-const globals = require("../../conf/globals");
//------------------------------------------------------------------------------
// Typedefs
@@ -145,29 +145,6 @@ function isEspree(parser) {
return !!(parser === espree || parser[parserSymbol] === espree);
}
-/**
- * Retrieves globals for the given ecmaVersion.
- * @param {number} ecmaVersion The version to retrieve globals for.
- * @returns {Object} The globals for the given ecmaVersion.
- */
-function getGlobalsForEcmaVersion(ecmaVersion) {
-
- switch (ecmaVersion) {
- case 3:
- return globals.es3;
-
- case 5:
- return globals.es5;
-
- default:
- if (ecmaVersion < 2015) {
- return globals[`es${ecmaVersion + 2009}`];
- }
-
- return globals[`es${ecmaVersion}`];
- }
-}
-
/**
* Ensures that variables representing built-in properties of the Global Object,
* and any globals declared by special block comments, are present in the global
@@ -361,13 +338,13 @@ function extractDirectiveComment(value) {
* Parses comments in file to extract file-specific config of rules, globals
* and environments and merges them with global config; also code blocks
* where reporting is disabled or enabled and merges them with reporting config.
- * @param {ASTNode} ast The top node of the AST.
+ * @param {SourceCode} sourceCode The SourceCode object to get comments from.
* @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
* @param {string|null} warnInlineConfig If a string then it should warn directive comments as disabled. The string value is the config name what the setting came from.
* @returns {{configuredRules: Object, enabledGlobals: {value:string,comment:Token}[], exportedVariables: Object, problems: LintMessage[], disableDirectives: DisableDirective[]}}
* A collection of the directive comments that were found, along with any problems that occurred when parsing
*/
-function getDirectiveComments(ast, ruleMapper, warnInlineConfig) {
+function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig) {
const configuredRules = {};
const enabledGlobals = Object.create(null);
const exportedVariables = {};
@@ -377,7 +354,7 @@ function getDirectiveComments(ast, ruleMapper, warnInlineConfig) {
builtInRules: Rules
});
- ast.comments.filter(token => token.type !== "Shebang").forEach(comment => {
+ sourceCode.getInlineConfigNodes().filter(token => token.type !== "Shebang").forEach(comment => {
const { directivePart, justificationPart } = extractDirectiveComment(comment.value);
const match = directivesPattern.exec(directivePart);
@@ -511,6 +488,69 @@ function getDirectiveComments(ast, ruleMapper, warnInlineConfig) {
};
}
+/**
+ * Parses comments in file to extract disable directives.
+ * @param {SourceCode} sourceCode The SourceCode object to get comments from.
+ * @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
+ * @returns {{problems: LintMessage[], disableDirectives: DisableDirective[]}}
+ * A collection of the directive comments that were found, along with any problems that occurred when parsing
+ */
+function getDirectiveCommentsForFlatConfig(sourceCode, ruleMapper) {
+ const problems = [];
+ const disableDirectives = [];
+
+ sourceCode.getInlineConfigNodes().filter(token => token.type !== "Shebang").forEach(comment => {
+ const { directivePart, justificationPart } = extractDirectiveComment(comment.value);
+
+ const match = directivesPattern.exec(directivePart);
+
+ if (!match) {
+ return;
+ }
+ const directiveText = match[1];
+ const lineCommentSupported = /^eslint-disable-(next-)?line$/u.test(directiveText);
+
+ if (comment.type === "Line" && !lineCommentSupported) {
+ return;
+ }
+
+ if (directiveText === "eslint-disable-line" && comment.loc.start.line !== comment.loc.end.line) {
+ const message = `${directiveText} comment should not span multiple lines.`;
+
+ problems.push(createLintingProblem({
+ ruleId: null,
+ message,
+ loc: comment.loc
+ }));
+ return;
+ }
+
+ const directiveValue = directivePart.slice(match.index + directiveText.length);
+
+ switch (directiveText) {
+ case "eslint-disable":
+ case "eslint-enable":
+ case "eslint-disable-next-line":
+ case "eslint-disable-line": {
+ const directiveType = directiveText.slice("eslint-".length);
+ const options = { commentToken: comment, type: directiveType, value: directiveValue, justification: justificationPart, ruleMapper };
+ const { directives, directiveProblems } = createDisableDirectives(options);
+
+ disableDirectives.push(...directives);
+ problems.push(...directiveProblems);
+ break;
+ }
+
+ // no default
+ }
+ });
+
+ return {
+ problems,
+ disableDirectives
+ };
+}
+
/**
* Normalize ECMAScript version from the initial config
* @param {Parser} parser The parser which uses this options.
@@ -908,6 +948,7 @@ const DEPRECATED_SOURCECODE_PASSTHROUGHS = {
getTokensBetween: "getTokensBetween"
};
+
const BASE_TRAVERSAL_CONTEXT = Object.freeze(
Object.keys(DEPRECATED_SOURCECODE_PASSTHROUGHS).reduce(
(contextInfo, methodName) =>
@@ -1322,7 +1363,7 @@ class Linter {
const sourceCode = slots.lastSourceCode;
const commentDirectives = options.allowInlineConfig
- ? getDirectiveComments(sourceCode.ast, ruleId => getRule(slots, ruleId), options.warnInlineConfig)
+ ? getDirectiveComments(sourceCode, ruleId => getRule(slots, ruleId), options.warnInlineConfig)
: { configuredRules: {}, enabledGlobals: {}, exportedVariables: {}, problems: [], disableDirectives: [] };
// augment global scope with declared global variables
@@ -1333,7 +1374,6 @@ class Linter {
);
const configuredRules = Object.assign({}, config.rules, commentDirectives.configuredRules);
-
let lintingProblems;
try {
@@ -1549,19 +1589,6 @@ class Linter {
languageOptions.ecmaVersion
);
- /*
- * add configured globals and language globals
- *
- * using Object.assign instead of object spread for performance reasons
- * https://github.com/eslint/eslint/issues/16302
- */
- const configuredGlobals = Object.assign(
- {},
- getGlobalsForEcmaVersion(languageOptions.ecmaVersion),
- languageOptions.sourceType === "commonjs" ? globals.commonjs : void 0,
- languageOptions.globals
- );
-
// double check that there is a parser to avoid mysterious error messages
if (!languageOptions.parser) {
throw new TypeError(`No parser specified for ${options.filename}`);
@@ -1617,25 +1644,113 @@ class Linter {
}
const sourceCode = slots.lastSourceCode;
- const commentDirectives = options.allowInlineConfig
- ? getDirectiveComments(
- sourceCode.ast,
- ruleId => getRuleFromConfig(ruleId, config),
- options.warnInlineConfig
- )
- : { configuredRules: {}, enabledGlobals: {}, exportedVariables: {}, problems: [], disableDirectives: [] };
- // augment global scope with declared global variables
- addDeclaredGlobals(
- sourceCode.scopeManager.scopes[0],
- configuredGlobals,
- { exportedVariables: commentDirectives.exportedVariables, enabledGlobals: commentDirectives.enabledGlobals }
- );
+ /*
+ * Make adjustments based on the language options. For JavaScript,
+ * this is primarily about adding variables into the global scope
+ * to account for ecmaVersion and configured globals.
+ */
+ sourceCode.applyLanguageOptions(languageOptions);
- const configuredRules = Object.assign({}, config.rules, commentDirectives.configuredRules);
+ const mergedInlineConfig = {
+ rules: {}
+ };
+ const inlineConfigProblems = [];
+ /*
+ * Inline config can be either enabled or disabled. If disabled, it's possible
+ * to detect the inline config and emit a warning (though this is not required).
+ * So we first check to see if inline config is allowed at all, and if so, we
+ * need to check if it's a warning or not.
+ */
+ if (options.allowInlineConfig) {
+
+ // if inline config should warn then add the warnings
+ if (options.warnInlineConfig) {
+ sourceCode.getInlineConfigNodes().forEach(node => {
+ inlineConfigProblems.push(createLintingProblem({
+ ruleId: null,
+ message: `'${sourceCode.text.slice(node.range[0], node.range[1])}' has no effect because you have 'noInlineConfig' setting in ${options.warnInlineConfig}.`,
+ loc: node.loc,
+ severity: 1
+ }));
+
+ });
+ } else {
+ const inlineConfigResult = sourceCode.applyInlineConfig();
+
+ inlineConfigProblems.push(
+ ...inlineConfigResult.problems
+ .map(createLintingProblem)
+ .map(problem => {
+ problem.fatal = true;
+ return problem;
+ })
+ );
+
+ // next we need to verify information about the specified rules
+ const ruleValidator = new RuleValidator();
+
+ for (const { config: inlineConfig, node } of inlineConfigResult.configs) {
+
+ Object.keys(inlineConfig.rules).forEach(ruleId => {
+ const rule = getRuleFromConfig(ruleId, config);
+ const ruleValue = inlineConfig.rules[ruleId];
+
+ if (!rule) {
+ inlineConfigProblems.push(createLintingProblem({ ruleId, loc: node.loc }));
+ return;
+ }
+
+ try {
+
+ const ruleOptions = Array.isArray(ruleValue) ? ruleValue : [ruleValue];
+
+ assertIsRuleOptions(ruleId, ruleValue);
+ assertIsRuleSeverity(ruleId, ruleOptions[0]);
+
+ ruleValidator.validate({
+ plugins: config.plugins,
+ rules: {
+ [ruleId]: ruleOptions
+ }
+ });
+ mergedInlineConfig.rules[ruleId] = ruleValue;
+ } catch (err) {
+
+ let baseMessage = err.message.slice(
+ err.message.startsWith("Key \"rules\":")
+ ? err.message.indexOf(":", 12) + 1
+ : err.message.indexOf(":") + 1
+ ).trim();
+
+ if (err.messageTemplate) {
+ baseMessage += ` You passed "${ruleValue}".`;
+ }
+
+ inlineConfigProblems.push(createLintingProblem({
+ ruleId,
+ message: `Inline configuration for rule "${ruleId}" is invalid:\n\t${baseMessage}\n`,
+ loc: node.loc
+ }));
+ }
+ });
+ }
+ }
+ }
+
+ const commentDirectives = options.allowInlineConfig && !options.warnInlineConfig
+ ? getDirectiveCommentsForFlatConfig(
+ sourceCode,
+ ruleId => getRuleFromConfig(ruleId, config)
+ )
+ : { problems: [], disableDirectives: [] };
+
+ const configuredRules = Object.assign({}, config.rules, mergedInlineConfig.rules);
let lintingProblems;
+ sourceCode.finalize();
+
try {
lintingProblems = runRules(
sourceCode,
@@ -1676,6 +1791,7 @@ class Linter {
disableFixes: options.disableFixes,
problems: lintingProblems
.concat(commentDirectives.problems)
+ .concat(inlineConfigProblems)
.sort((problemA, problemB) => problemA.line - problemB.line || problemA.column - problemB.column),
reportUnusedDisableDirectives: options.reportUnusedDisableDirectives
});
diff --git a/lib/linter/report-translator.js b/lib/linter/report-translator.js
index 7d2705206cd..41a43eadc3b 100644
--- a/lib/linter/report-translator.js
+++ b/lib/linter/report-translator.js
@@ -100,6 +100,22 @@ function normalizeReportLoc(descriptor) {
return descriptor.node.loc;
}
+/**
+ * Clones the given fix object.
+ * @param {Fix|null} fix The fix to clone.
+ * @returns {Fix|null} Deep cloned fix object or `null` if `null` or `undefined` was passed in.
+ */
+function cloneFix(fix) {
+ if (!fix) {
+ return null;
+ }
+
+ return {
+ range: [fix.range[0], fix.range[1]],
+ text: fix.text
+ };
+}
+
/**
* Check that a fix has a valid range.
* @param {Fix|null} fix The fix to validate.
@@ -137,7 +153,7 @@ function mergeFixes(fixes, sourceCode) {
return null;
}
if (fixes.length === 1) {
- return fixes[0];
+ return cloneFix(fixes[0]);
}
fixes.sort(compareFixesByRange);
@@ -183,7 +199,7 @@ function normalizeFixes(descriptor, sourceCode) {
}
assertValidFix(fix);
- return fix;
+ return cloneFix(fix);
}
/**
diff --git a/lib/options.js b/lib/options.js
index aaaec286485..c1994df9550 100644
--- a/lib/options.js
+++ b/lib/options.js
@@ -47,7 +47,7 @@ const optionator = require("optionator");
* @property {Object} [parserOptions] Specify parser options
* @property {string[]} [plugin] Specify plugins
* @property {string} [printConfig] Print the configuration for the given file
- * @property {boolean|string|undefined} reportUnusedDisableDirectives Adds reported errors for unused eslint-disable directives
+ * @property {boolean | string | undefined} reportUnusedDisableDirectives Adds reported errors for unused eslint-disable and eslint-enable directives
* @property {string} [resolvePluginsRelativeTo] A folder where plugins should be resolved from, CWD by default
* @property {Object} [rule] Specify rules
* @property {string[]} [rulesdir] Load additional rules from this directory. Deprecated: Use rules from plugins
@@ -55,6 +55,7 @@ const optionator = require("optionator");
* @property {string} [stdinFilename] Specify filename to process STDIN as
* @property {boolean} quiet Report errors only
* @property {boolean} [version] Output the version number
+ * @property {boolean} warnIgnored Show warnings when the file list includes ignored files
* @property {string[]} _ Positional filenames or patterns
*/
@@ -139,6 +140,17 @@ module.exports = function(usingFlatConfig) {
};
}
+ let warnIgnoredFlag;
+
+ if (usingFlatConfig) {
+ warnIgnoredFlag = {
+ option: "warn-ignored",
+ type: "Boolean",
+ default: "true",
+ description: "Suppress warnings when the file list includes ignored files"
+ };
+ }
+
return optionator({
prepend: "eslint [options] file.js [file.js] [dir]",
defaults: {
@@ -292,7 +304,7 @@ module.exports = function(usingFlatConfig) {
option: "report-unused-disable-directives",
type: "Boolean",
default: void 0,
- description: "Adds reported errors for unused eslint-disable directives"
+ description: "Adds reported errors for unused eslint-disable and eslint-enable directives"
},
{
option: "report-unused-disable-directives-severity",
@@ -356,6 +368,7 @@ module.exports = function(usingFlatConfig) {
default: "false",
description: "Exit with exit code 2 in case of fatal error"
},
+ warnIgnoredFlag,
{
option: "debug",
type: "Boolean",
diff --git a/lib/rule-tester/flat-rule-tester.js b/lib/rule-tester/flat-rule-tester.js
index 97055d104f4..51cb73b5f80 100644
--- a/lib/rule-tester/flat-rule-tester.js
+++ b/lib/rule-tester/flat-rule-tester.js
@@ -16,7 +16,9 @@ const
equal = require("fast-deep-equal"),
Traverser = require("../shared/traverser"),
{ getRuleOptionsSchema } = require("../config/flat-config-helpers"),
- { Linter, SourceCodeFixer, interpolate } = require("../linter");
+ { Linter, SourceCodeFixer, interpolate } = require("../linter"),
+ CodePath = require("../linter/code-path-analysis/code-path");
+
const { FlatConfigArray } = require("../config/flat-config-array");
const { defaultConfig } = require("../config/default-config");
@@ -32,8 +34,9 @@ const { ConfigArraySymbol } = require("@humanwhocodes/config-array");
/** @typedef {import("../shared/types").Parser} Parser */
/** @typedef {import("../shared/types").LanguageOptions} LanguageOptions */
+/** @typedef {import("../shared/types").Rule} Rule */
+
-/* eslint-disable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
/**
* A test case that is expected to pass lint.
* @typedef {Object} ValidTestCase
@@ -72,7 +75,6 @@ const { ConfigArraySymbol } = require("@humanwhocodes/config-array");
* @property {number} [endLine] The 1-based line number of the reported end location.
* @property {number} [endColumn] The 1-based column number of the reported end location.
*/
-/* eslint-enable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
//------------------------------------------------------------------------------
// Private Members
@@ -131,6 +133,15 @@ const suggestionObjectParameters = new Set([
]);
const friendlySuggestionObjectParameterList = `[${[...suggestionObjectParameters].map(key => `'${key}'`).join(", ")}]`;
+const forbiddenMethods = [
+ "applyInlineConfig",
+ "applyLanguageOptions",
+ "finalize"
+];
+
+/** @type {Map} */
+const forbiddenMethodCalls = new Map(forbiddenMethods.map(methodName => ([methodName, new WeakSet()])));
+
const hasOwnProperty = Function.call.bind(Object.hasOwnProperty);
/**
@@ -274,6 +285,49 @@ function getCommentsDeprecation() {
);
}
+/**
+ * Emit a deprecation warning if rule uses CodePath#currentSegments.
+ * @param {string} ruleName Name of the rule.
+ * @returns {void}
+ */
+function emitCodePathCurrentSegmentsWarning(ruleName) {
+ if (!emitCodePathCurrentSegmentsWarning[`warned-${ruleName}`]) {
+ emitCodePathCurrentSegmentsWarning[`warned-${ruleName}`] = true;
+ process.emitWarning(
+ `"${ruleName}" rule uses CodePath#currentSegments and will stop working in ESLint v9. Please read the documentation for how to update your code: https://eslint.org/docs/latest/extend/code-path-analysis#usage-examples`,
+ "DeprecationWarning"
+ );
+ }
+}
+
+/**
+ * Function to replace forbidden `SourceCode` methods. Allows just one call per method.
+ * @param {string} methodName The name of the method to forbid.
+ * @param {Function} prototype The prototype with the original method to call.
+ * @returns {Function} The function that throws the error.
+ */
+function throwForbiddenMethodError(methodName, prototype) {
+
+ const original = prototype[methodName];
+
+ return function(...args) {
+
+ const called = forbiddenMethodCalls.get(methodName);
+
+ /* eslint-disable no-invalid-this -- needed to operate as a method. */
+ if (!called.has(this)) {
+ called.add(this);
+
+ return original.apply(this, args);
+ }
+ /* eslint-enable no-invalid-this -- not needed past this point */
+
+ throw new Error(
+ `\`SourceCode#${methodName}()\` cannot be called inside a rule.`
+ );
+ };
+}
+
//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------
@@ -447,7 +501,7 @@ class FlatRuleTester {
/**
* Adds a new rule test to execute.
* @param {string} ruleName The name of the rule to run.
- * @param {Function} rule The rule to test.
+ * @param {Function | Rule} rule The rule to test.
* @param {{
* valid: (ValidTestCase | string)[],
* invalid: InvalidTestCase[]
@@ -481,6 +535,7 @@ class FlatRuleTester {
}
const baseConfig = [
+ { files: ["**"] }, // Make sure the default config matches for all files
{
plugins: {
@@ -662,10 +717,6 @@ class FlatRuleTester {
}
}
- // Verify the code.
- const { getComments } = SourceCode.prototype;
- let messages;
-
// check for validation errors
try {
configs.normalizeSync();
@@ -675,13 +726,34 @@ class FlatRuleTester {
throw error;
}
+ // Verify the code.
+ const { getComments, applyLanguageOptions, applyInlineConfig, finalize } = SourceCode.prototype;
+ const originalCurrentSegments = Object.getOwnPropertyDescriptor(CodePath.prototype, "currentSegments");
+ let messages;
+
try {
SourceCode.prototype.getComments = getCommentsDeprecation;
+ Object.defineProperty(CodePath.prototype, "currentSegments", {
+ get() {
+ emitCodePathCurrentSegmentsWarning(ruleName);
+ return originalCurrentSegments.get.call(this);
+ }
+ });
+
+ forbiddenMethods.forEach(methodName => {
+ SourceCode.prototype[methodName] = throwForbiddenMethodError(methodName, SourceCode.prototype);
+ });
+
messages = linter.verify(code, configs, filename);
} finally {
SourceCode.prototype.getComments = getComments;
+ Object.defineProperty(CodePath.prototype, "currentSegments", originalCurrentSegments);
+ SourceCode.prototype.applyInlineConfig = applyInlineConfig;
+ SourceCode.prototype.applyLanguageOptions = applyLanguageOptions;
+ SourceCode.prototype.finalize = finalize;
}
+
const fatalErrorMessage = messages.find(m => m.fatal);
assert(!fatalErrorMessage, `A fatal parsing error occurred: ${fatalErrorMessage && fatalErrorMessage.message}`);
@@ -1012,29 +1084,35 @@ class FlatRuleTester {
/*
* This creates a mocha test suite and pipes all supplied info through
* one of the templates above.
+ * The test suites for valid/invalid are created conditionally as
+ * test runners (eg. vitest) fail for empty test suites.
*/
this.constructor.describe(ruleName, () => {
- this.constructor.describe("valid", () => {
- test.valid.forEach(valid => {
- this.constructor[valid.only ? "itOnly" : "it"](
- sanitize(typeof valid === "object" ? valid.name || valid.code : valid),
- () => {
- testValidTemplate(valid);
- }
- );
+ if (test.valid.length > 0) {
+ this.constructor.describe("valid", () => {
+ test.valid.forEach(valid => {
+ this.constructor[valid.only ? "itOnly" : "it"](
+ sanitize(typeof valid === "object" ? valid.name || valid.code : valid),
+ () => {
+ testValidTemplate(valid);
+ }
+ );
+ });
});
- });
+ }
- this.constructor.describe("invalid", () => {
- test.invalid.forEach(invalid => {
- this.constructor[invalid.only ? "itOnly" : "it"](
- sanitize(invalid.name || invalid.code),
- () => {
- testInvalidTemplate(invalid);
- }
- );
+ if (test.invalid.length > 0) {
+ this.constructor.describe("invalid", () => {
+ test.invalid.forEach(invalid => {
+ this.constructor[invalid.only ? "itOnly" : "it"](
+ sanitize(invalid.name || invalid.code),
+ () => {
+ testInvalidTemplate(invalid);
+ }
+ );
+ });
});
- });
+ }
});
}
}
diff --git a/lib/rule-tester/rule-tester.js b/lib/rule-tester/rule-tester.js
index 8518299d0b0..3bc80ab1837 100644
--- a/lib/rule-tester/rule-tester.js
+++ b/lib/rule-tester/rule-tester.js
@@ -48,7 +48,8 @@ const
equal = require("fast-deep-equal"),
Traverser = require("../../lib/shared/traverser"),
{ getRuleOptionsSchema, validate } = require("../shared/config-validator"),
- { Linter, SourceCodeFixer, interpolate } = require("../linter");
+ { Linter, SourceCodeFixer, interpolate } = require("../linter"),
+ CodePath = require("../linter/code-path-analysis/code-path");
const ajv = require("../shared/ajv")({ strictDefaults: true });
@@ -62,8 +63,9 @@ const { SourceCode } = require("../source-code");
//------------------------------------------------------------------------------
/** @typedef {import("../shared/types").Parser} Parser */
+/** @typedef {import("../shared/types").Rule} Rule */
+
-/* eslint-disable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
/**
* A test case that is expected to pass lint.
* @typedef {Object} ValidTestCase
@@ -108,7 +110,6 @@ const { SourceCode } = require("../source-code");
* @property {number} [endLine] The 1-based line number of the reported end location.
* @property {number} [endColumn] The 1-based column number of the reported end location.
*/
-/* eslint-enable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
//------------------------------------------------------------------------------
// Private Members
@@ -162,8 +163,43 @@ const suggestionObjectParameters = new Set([
]);
const friendlySuggestionObjectParameterList = `[${[...suggestionObjectParameters].map(key => `'${key}'`).join(", ")}]`;
+const forbiddenMethods = [
+ "applyInlineConfig",
+ "applyLanguageOptions",
+ "finalize"
+];
+
const hasOwnProperty = Function.call.bind(Object.hasOwnProperty);
+const DEPRECATED_SOURCECODE_PASSTHROUGHS = {
+ getSource: "getText",
+ getSourceLines: "getLines",
+ getAllComments: "getAllComments",
+ getNodeByRangeIndex: "getNodeByRangeIndex",
+
+ // getComments: "getComments", -- already handled by a separate error
+ getCommentsBefore: "getCommentsBefore",
+ getCommentsAfter: "getCommentsAfter",
+ getCommentsInside: "getCommentsInside",
+ getJSDocComment: "getJSDocComment",
+ getFirstToken: "getFirstToken",
+ getFirstTokens: "getFirstTokens",
+ getLastToken: "getLastToken",
+ getLastTokens: "getLastTokens",
+ getTokenAfter: "getTokenAfter",
+ getTokenBefore: "getTokenBefore",
+ getTokenByRangeStart: "getTokenByRangeStart",
+ getTokens: "getTokens",
+ getTokensAfter: "getTokensAfter",
+ getTokensBefore: "getTokensBefore",
+ getTokensBetween: "getTokensBetween",
+
+ getScope: "getScope",
+ getAncestors: "getAncestors",
+ getDeclaredVariables: "getDeclaredVariables",
+ markVariableAsUsed: "markVariableAsUsed"
+};
+
/**
* Clones a given value deeply.
* Note: This ignores `parent` property.
@@ -305,6 +341,19 @@ function getCommentsDeprecation() {
);
}
+/**
+ * Function to replace forbidden `SourceCode` methods.
+ * @param {string} methodName The name of the method to forbid.
+ * @returns {Function} The function that throws the error.
+ */
+function throwForbiddenMethodError(methodName) {
+ return () => {
+ throw new Error(
+ `\`SourceCode#${methodName}()\` cannot be called inside a rule.`
+ );
+ };
+}
+
/**
* Emit a deprecation warning if function-style format is being used.
* @param {string} ruleName Name of the rule.
@@ -335,6 +384,53 @@ function emitMissingSchemaWarning(ruleName) {
}
}
+/**
+ * Emit a deprecation warning if a rule uses a deprecated `context` method.
+ * @param {string} ruleName Name of the rule.
+ * @param {string} methodName The name of the method on `context` that was used.
+ * @returns {void}
+ */
+function emitDeprecatedContextMethodWarning(ruleName, methodName) {
+ if (!emitDeprecatedContextMethodWarning[`warned-${ruleName}-${methodName}`]) {
+ emitDeprecatedContextMethodWarning[`warned-${ruleName}-${methodName}`] = true;
+ process.emitWarning(
+ `"${ruleName}" rule is using \`context.${methodName}()\`, which is deprecated and will be removed in ESLint v9. Please use \`sourceCode.${DEPRECATED_SOURCECODE_PASSTHROUGHS[methodName]}()\` instead.`,
+ "DeprecationWarning"
+ );
+ }
+}
+
+/**
+ * Emit a deprecation warning if rule uses CodePath#currentSegments.
+ * @param {string} ruleName Name of the rule.
+ * @returns {void}
+ */
+function emitCodePathCurrentSegmentsWarning(ruleName) {
+ if (!emitCodePathCurrentSegmentsWarning[`warned-${ruleName}`]) {
+ emitCodePathCurrentSegmentsWarning[`warned-${ruleName}`] = true;
+ process.emitWarning(
+ `"${ruleName}" rule uses CodePath#currentSegments and will stop working in ESLint v9. Please read the documentation for how to update your code: https://eslint.org/docs/latest/extend/code-path-analysis#usage-examples`,
+ "DeprecationWarning"
+ );
+ }
+}
+
+/**
+ * Emit a deprecation warning if `context.parserServices` is used.
+ * @param {string} ruleName Name of the rule.
+ * @returns {void}
+ */
+function emitParserServicesWarning(ruleName) {
+ if (!emitParserServicesWarning[`warned-${ruleName}`]) {
+ emitParserServicesWarning[`warned-${ruleName}`] = true;
+ process.emitWarning(
+ `"${ruleName}" rule is using \`context.parserServices\`, which is deprecated and will be removed in ESLint v9. Please use \`sourceCode.parserServices\` instead.`,
+ "DeprecationWarning"
+ );
+ }
+}
+
+
//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------
@@ -509,17 +605,20 @@ class RuleTester {
/**
* Define a rule for one particular run of tests.
* @param {string} name The name of the rule to define.
- * @param {Function} rule The rule definition.
+ * @param {Function | Rule} rule The rule definition.
* @returns {void}
*/
defineRule(name, rule) {
+ if (typeof rule === "function") {
+ emitLegacyRuleAPIWarning(name);
+ }
this.rules[name] = rule;
}
/**
* Adds a new rule test to execute.
* @param {string} ruleName The name of the rule to run.
- * @param {Function} rule The rule to test.
+ * @param {Function | Rule} rule The rule to test.
* @param {{
* valid: (ValidTestCase | string)[],
* invalid: InvalidTestCase[]
@@ -563,7 +662,38 @@ class RuleTester {
freezeDeeply(context.settings);
freezeDeeply(context.parserOptions);
- return (typeof rule === "function" ? rule : rule.create)(context);
+ // wrap all deprecated methods
+ const newContext = Object.create(
+ context,
+ Object.fromEntries(Object.keys(DEPRECATED_SOURCECODE_PASSTHROUGHS).map(methodName => [
+ methodName,
+ {
+ value(...args) {
+
+ // emit deprecation warning
+ emitDeprecatedContextMethodWarning(ruleName, methodName);
+
+ // call the original method
+ return context[methodName].call(this, ...args);
+ },
+ enumerable: true
+ }
+ ]))
+ );
+
+ // emit warning about context.parserServices
+ const parserServices = context.parserServices;
+
+ Object.defineProperty(newContext, "parserServices", {
+ get() {
+ emitParserServicesWarning(ruleName);
+ return parserServices;
+ }
+ });
+
+ Object.freeze(newContext);
+
+ return (typeof rule === "function" ? rule : rule.create)(newContext);
}
}));
@@ -682,14 +812,30 @@ class RuleTester {
validate(config, "rule-tester", id => (id === ruleName ? rule : null));
// Verify the code.
- const { getComments } = SourceCode.prototype;
+ const { getComments, applyLanguageOptions, applyInlineConfig, finalize } = SourceCode.prototype;
+ const originalCurrentSegments = Object.getOwnPropertyDescriptor(CodePath.prototype, "currentSegments");
let messages;
try {
SourceCode.prototype.getComments = getCommentsDeprecation;
+ Object.defineProperty(CodePath.prototype, "currentSegments", {
+ get() {
+ emitCodePathCurrentSegmentsWarning(ruleName);
+ return originalCurrentSegments.get.call(this);
+ }
+ });
+
+ forbiddenMethods.forEach(methodName => {
+ SourceCode.prototype[methodName] = throwForbiddenMethodError(methodName);
+ });
+
messages = linter.verify(code, config, filename);
} finally {
SourceCode.prototype.getComments = getComments;
+ Object.defineProperty(CodePath.prototype, "currentSegments", originalCurrentSegments);
+ SourceCode.prototype.applyInlineConfig = applyInlineConfig;
+ SourceCode.prototype.applyLanguageOptions = applyLanguageOptions;
+ SourceCode.prototype.finalize = finalize;
}
const fatalErrorMessage = messages.find(m => m.fatal);
@@ -1022,29 +1168,35 @@ class RuleTester {
/*
* This creates a mocha test suite and pipes all supplied info through
* one of the templates above.
+ * The test suites for valid/invalid are created conditionally as
+ * test runners (eg. vitest) fail for empty test suites.
*/
this.constructor.describe(ruleName, () => {
- this.constructor.describe("valid", () => {
- test.valid.forEach(valid => {
- this.constructor[valid.only ? "itOnly" : "it"](
- sanitize(typeof valid === "object" ? valid.name || valid.code : valid),
- () => {
- testValidTemplate(valid);
- }
- );
+ if (test.valid.length > 0) {
+ this.constructor.describe("valid", () => {
+ test.valid.forEach(valid => {
+ this.constructor[valid.only ? "itOnly" : "it"](
+ sanitize(typeof valid === "object" ? valid.name || valid.code : valid),
+ () => {
+ testValidTemplate(valid);
+ }
+ );
+ });
});
- });
+ }
- this.constructor.describe("invalid", () => {
- test.invalid.forEach(invalid => {
- this.constructor[invalid.only ? "itOnly" : "it"](
- sanitize(invalid.name || invalid.code),
- () => {
- testInvalidTemplate(invalid);
- }
- );
+ if (test.invalid.length > 0) {
+ this.constructor.describe("invalid", () => {
+ test.invalid.forEach(invalid => {
+ this.constructor[invalid.only ? "itOnly" : "it"](
+ sanitize(invalid.name || invalid.code),
+ () => {
+ testInvalidTemplate(invalid);
+ }
+ );
+ });
});
- });
+ }
});
}
}
diff --git a/lib/rules/accessor-pairs.js b/lib/rules/accessor-pairs.js
index 03b51e461c0..f97032895df 100644
--- a/lib/rules/accessor-pairs.js
+++ b/lib/rules/accessor-pairs.js
@@ -223,43 +223,6 @@ module.exports = {
}
}
- /**
- * Creates a new `AccessorData` object for the given getter or setter node.
- * @param {ASTNode} node A getter or setter node.
- * @returns {AccessorData} New `AccessorData` object that contains the given node.
- * @private
- */
- function createAccessorData(node) {
- const name = astUtils.getStaticPropertyName(node);
- const key = (name !== null) ? name : sourceCode.getTokens(node.key);
-
- return {
- key,
- getters: node.kind === "get" ? [node] : [],
- setters: node.kind === "set" ? [node] : []
- };
- }
-
- /**
- * Merges the given `AccessorData` object into the given accessors list.
- * @param {AccessorData[]} accessors The list to merge into.
- * @param {AccessorData} accessorData The object to merge.
- * @returns {AccessorData[]} The same instance with the merged object.
- * @private
- */
- function mergeAccessorData(accessors, accessorData) {
- const equalKeyElement = accessors.find(a => areEqualKeys(a.key, accessorData.key));
-
- if (equalKeyElement) {
- equalKeyElement.getters.push(...accessorData.getters);
- equalKeyElement.setters.push(...accessorData.setters);
- } else {
- accessors.push(accessorData);
- }
-
- return accessors;
- }
-
/**
* Checks accessor pairs in the given list of nodes.
* @param {ASTNode[]} nodes The list to check.
@@ -267,10 +230,39 @@ module.exports = {
* @private
*/
function checkList(nodes) {
- const accessors = nodes
- .filter(isAccessorKind)
- .map(createAccessorData)
- .reduce(mergeAccessorData, []);
+ const accessors = [];
+ let found = false;
+
+ for (let i = 0; i < nodes.length; i++) {
+ const node = nodes[i];
+
+ if (isAccessorKind(node)) {
+
+ // Creates a new `AccessorData` object for the given getter or setter node.
+ const name = astUtils.getStaticPropertyName(node);
+ const key = (name !== null) ? name : sourceCode.getTokens(node.key);
+
+ // Merges the given `AccessorData` object into the given accessors list.
+ for (let j = 0; j < accessors.length; j++) {
+ const accessor = accessors[j];
+
+ if (areEqualKeys(accessor.key, key)) {
+ accessor.getters.push(...node.kind === "get" ? [node] : []);
+ accessor.setters.push(...node.kind === "set" ? [node] : []);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ accessors.push({
+ key,
+ getters: node.kind === "get" ? [node] : [],
+ setters: node.kind === "set" ? [node] : []
+ });
+ }
+ found = false;
+ }
+ }
for (const { getters, setters } of accessors) {
if (checkSetWithoutGet && setters.length && !getters.length) {
diff --git a/lib/rules/array-bracket-newline.js b/lib/rules/array-bracket-newline.js
index c3676bf4dfa..12ef5b612d6 100644
--- a/lib/rules/array-bracket-newline.js
+++ b/lib/rules/array-bracket-newline.js
@@ -1,6 +1,7 @@
/**
* @fileoverview Rule to enforce linebreaks after open and before close array brackets
* @author Jan Peer Stöcklmair
+ * @deprecated in ESLint v8.53.0
*/
"use strict";
@@ -14,6 +15,8 @@ const astUtils = require("./utils/ast-utils");
/** @type {import('../shared/types').Rule} */
module.exports = {
meta: {
+ deprecated: true,
+ replacedBy: [],
type: "layout",
docs: {
diff --git a/lib/rules/array-bracket-spacing.js b/lib/rules/array-bracket-spacing.js
index e3a46d82214..9dd3ffd902c 100644
--- a/lib/rules/array-bracket-spacing.js
+++ b/lib/rules/array-bracket-spacing.js
@@ -1,6 +1,7 @@
/**
* @fileoverview Disallows or enforces spaces inside of array brackets.
* @author Jamund Ferguson
+ * @deprecated in ESLint v8.53.0
*/
"use strict";
@@ -13,6 +14,8 @@ const astUtils = require("./utils/ast-utils");
/** @type {import('../shared/types').Rule} */
module.exports = {
meta: {
+ deprecated: true,
+ replacedBy: [],
type: "layout",
docs: {
diff --git a/lib/rules/array-callback-return.js b/lib/rules/array-callback-return.js
index 05cd4ede966..6d8f258fa14 100644
--- a/lib/rules/array-callback-return.js
+++ b/lib/rules/array-callback-return.js
@@ -18,15 +18,6 @@ const astUtils = require("./utils/ast-utils");
const TARGET_NODE_TYPE = /^(?:Arrow)?FunctionExpression$/u;
const TARGET_METHODS = /^(?:every|filter|find(?:Last)?(?:Index)?|flatMap|forEach|map|reduce(?:Right)?|some|sort|toSorted)$/u;
-/**
- * Checks a given code path segment is reachable.
- * @param {CodePathSegment} segment A segment to check.
- * @returns {boolean} `true` if the segment is reachable.
- */
-function isReachable(segment) {
- return segment.reachable;
-}
-
/**
* Checks a given node is a member access which has the specified name's
* property.
@@ -38,6 +29,22 @@ function isTargetMethod(node) {
return astUtils.isSpecificMemberAccess(node, null, TARGET_METHODS);
}
+/**
+ * Checks all segments in a set and returns true if any are reachable.
+ * @param {Set} segments The segments to check.
+ * @returns {boolean} True if any segment is reachable; false otherwise.
+ */
+function isAnySegmentReachable(segments) {
+
+ for (const segment of segments) {
+ if (segment.reachable) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
/**
* Returns a human-legible description of an array method
* @param {string} arrayMethodName A method name to fully qualify
@@ -129,6 +136,76 @@ function getArrayMethodName(node) {
return null;
}
+/**
+ * Checks if the given node is a void expression.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} - `true` if the node is a void expression
+ */
+function isExpressionVoid(node) {
+ return node.type === "UnaryExpression" && node.operator === "void";
+}
+
+/**
+ * Fixes the linting error by prepending "void " to the given node
+ * @param {Object} sourceCode context given by context.sourceCode
+ * @param {ASTNode} node The node to fix.
+ * @param {Object} fixer The fixer object provided by ESLint.
+ * @returns {Array