Repository: eslint/eslint
Pull request #15867 (2022-05-11 13:20:29)

feat: add Unicode flag suggestion in no-misleading-character-class
<!--
    Thank you for contributing!

    ESLint adheres to the [JS Foundation Code of Conduct](https://eslint.org/conduct).
-->

#### Prerequisites checklist

- [x] I have read the [contributing guidelines](https://github.com/eslint/eslint/blob/HEAD/CONTRIBUTING.md).

#### What is the purpose of this pull request? (put an "X" next to an item)

<!--
    The following template is intentionally not a markdown checkbox list for the reasons
    explained in https://github.com/eslint/eslint/pull/12848#issuecomment-580302888
-->

[ ] Documentation update
[ ] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/rule-proposal.md))
[x] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/rule-change-proposal.md))
[ ] Add autofix to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ ] Other, please explain:

Continued from https://github.com/eslint/eslint/pull/15279.

Adds Unicode flag suggestion in the `no-misleading-character-class` rule.

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (https://eslint.org/docs/developer-guide/contributing/pull-requests)
    - Include tests for this change
    - Update documentation for this change (if appropriate)
-->

<!--
    The following is required for all pull requests:
-->

#### What changes did you make? (Give an overview)

Addressed code reviews from https://github.com/eslint/eslint/pull/15279.

#### Is there anything you'd like reviewers to focus on?

<!-- markdownlint-disable-file MD004 -->


netlify[bot]: ### <span aria-hidden="true">👷</span> Deploy Preview for *docs-eslint* processing.


|  Name | Link |
|---------------------------------|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | f1871da43ac02ddba82062b1a25bb5e19eece928 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/docs-eslint/deploys/627bb81f2d3f45000994f84b |

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 90f97f2ef..1bbaacfca 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -10,8 +10,8 @@ jobs:
     name: Verify Files
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v2
-    - uses: actions/setup-node@v2
+    - uses: actions/checkout@v3
+    - uses: actions/setup-node@v3
       with:
         node-version: '16.x'
     - name: Install Packages
@@ -36,8 +36,8 @@ jobs:
           node: "16.x"
     runs-on: ${{ matrix.os }}
     steps:
-    - uses: actions/checkout@v2
-    - uses: actions/setup-node@v2
+    - uses: actions/checkout@v3
+    - uses: actions/setup-node@v3
       with:
         node-version: ${{ matrix.node }}
     - name: Install Packages
@@ -51,8 +51,8 @@ jobs:
     name: Browser Test
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v2
-    - uses: actions/setup-node@v2
+    - uses: actions/checkout@v3
+    - uses: actions/setup-node@v3
       with:
         node-version: '16'
     - name: Install Packages
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 39aa47c11..e2995e66d 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -39,11 +39,11 @@ jobs:
 
     steps:
     - name: Checkout repository
-      uses: actions/checkout@v2
+      uses: actions/checkout@v3
 
     # Initializes the CodeQL tools for scanning.
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@v1
+      uses: github/codeql-action/init@v2
       with:
         languages: ${{ matrix.language }}
         # If you wish to specify custom queries, you can do so here or in a config file.
@@ -54,7 +54,7 @@ jobs:
     # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
     # If this step fails, then you should remove it and run the build manually (see below)
     - name: Autobuild
-      uses: github/codeql-action/autobuild@v1
+      uses: github/codeql-action/autobuild@v2
 
     # ℹ️ Command-line programs to run using the OS shell.
     # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -68,4 +68,4 @@ jobs:
     #   make release
 
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@v1
+      uses: github/codeql-action/analyze@v2
diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml
new file mode 100644
index 000000000..d1c79835f
--- /dev/null
+++ b/.github/workflows/docs-deploy.yml
@@ -0,0 +1,25 @@
+name: 'Docs Deploy'
+
+on:
+  release:
+    types: ['published']
+
+jobs:
+  deploy:
+    name: 'Deploy'
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v3
+      - uses: actions/setup-node@v3
+        with:
+          node-version: '16.x'
+      - working-directory: docs
+        run: |
+          npm install
+          npm run build
+          npx netlify deploy --dir=_site --message='Docs deploy ${{ github.event.release.tag_name }}' 
+        env:
+          NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
+          NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
+          NETLIFY_DEPLOY_TO_PROD: true
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index 457402294..8680ebdad 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -18,7 +18,7 @@ jobs:
       pull-requests: write
 
     steps:
-    - uses: actions/stale@v3
+    - uses: actions/stale@v5
       with:
         repo-token: ${{ secrets.GITHUB_TOKEN }}
         days-before-stale: 60
diff --git a/Makefile.js b/Makefile.js
index e0a195414..de786148c 100644
--- a/Makefile.js
+++ b/Makefile.js
@@ -614,7 +614,7 @@ target.gensite = function(prereleaseVersion) {
         if (test("-f", fullPath)) {
             rm("-rf", fullPath);
 
-            if (filePath.indexOf(".md") >= 0 && test("-f", htmlFullPath)) {
+            if (filePath.includes(".md") && test("-f", htmlFullPath)) {
                 rm("-rf", htmlFullPath);
             }
         }
@@ -673,7 +673,7 @@ target.gensite = function(prereleaseVersion) {
             process.stdout.write(`> Updating files (Steps 4-9): ${i}/${length} - ${filePath + " ".repeat(30)}\r`);
 
             // 5. Prepend page title and layout variables at the top of rules
-            if (path.dirname(filename).indexOf("rules") >= 0) {
+            if (path.dirname(filename).includes("rules")) {
 
                 // Find out if the rule requires a special docs portion (e.g. if it is recommended and/or fixable)
                 const rule = rules.get(ruleName);
@@ -696,7 +696,7 @@ target.gensite = function(prereleaseVersion) {
             }
 
             // 8. Append first version of ESLint rule was added at.
-            if (filename.indexOf("rules/") !== -1) {
+            if (filename.includes("rules/")) {
                 if (!versions.added[baseName]) {
                     versions.added[baseName] = getFirstVersionOfFile(sourcePath);
                 }
@@ -775,7 +775,7 @@ target.checkRuleFiles = function() {
         const docText = cat(docFilename);
         const docMarkdown = marked.lexer(docText, { gfm: true, silent: false });
         const ruleCode = cat(filename);
-        const knownHeaders = ["Rule Details", "Options", "Environments", "Examples", "Known Limitations", "When Not To Use It", "Related Rules", "Compatibility", "Further Reading"];
+        const knownHeaders = ["Rule Details", "Options", "Environments", "Examples", "Known Limitations", "When Not To Use It", "Compatibility"];
 
         /**
          * Check if basename is present in rule-types.json file.
diff --git a/docs/.eleventy.js b/docs/.eleventy.js
index 56ce2de99..7b4c4f1a0 100644
--- a/docs/.eleventy.js
+++ b/docs/.eleventy.js
@@ -25,6 +25,15 @@ const {
 module.exports = function(eleventyConfig) {
     let now = new Date();
 
+    /*
+     * The site is loaded from /docs on eslint.org and so we need to adjust
+     * the path prefix so URLs are evaluated correctly.
+     *
+     * The path prefix is turned off for deploy previews so we can properly
+     * see changes before deployed.
+    */
+    const pathPrefix = process.env.CONTEXT === "deploy-preview" ? "" : "/docs";
+
     /*****************************************************************************************
      *  Filters
      * ***************************************************************************************/
@@ -126,8 +135,6 @@ module.exports = function(eleventyConfig) {
     eleventyConfig.addNunjucksAsyncShortcode("link", async function(link) {
         const { body: html, url } = await got(link);
         const metadata = await metascraper({ html, url });
-
-        const encodedURL = encodeURIComponent(link);
         const the_url = (new URL(link)); // same as url
         const domain = the_url.hostname;
 
@@ -182,7 +189,7 @@ module.exports = function(eleventyConfig) {
 
         rules.forEach(function(rule) {
             let list_item = `<li class="related-rules__list__item">
-                <a href="/rules/${rule}">
+                <a href="${pathPrefix}/rules/${rule}">
                     <span>${rule}</span>
                     <svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false">
                         <path d="M5 12H19M19 12L12 5M19 12L12 19" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
@@ -345,6 +352,8 @@ module.exports = function(eleventyConfig) {
     return {
         passthroughFileCopy: true,
 
+        pathPrefix,
+
         markdownTemplateEngine: 'njk',
         dataTemplateEngine: 'njk',
         htmlTemplateEngine: 'njk',
diff --git a/docs/package.json b/docs/package.json
index da9319743..f09a9c187 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -30,6 +30,7 @@
         "imagemin-cli": "^7.0.0",
         "markdown-it": "^12.2.0",
         "markdown-it-anchor": "^8.1.2",
+        "netlify-cli": "^10.3.1",
         "node-sass": "^6.0.1",
         "npm-run-all": "^4.1.5",
         "rimraf": "^3.0.2",
diff --git a/docs/src/_includes/components/docs-toc.html b/docs/src/_includes/components/docs-toc.html
index 94e9a6672..20befc02d 100644
--- a/docs/src/_includes/components/docs-toc.html
+++ b/docs/src/_includes/components/docs-toc.html
@@ -1,8 +1,8 @@
 <nav class="docs-toc c-toc" aria-labelledby="js-toc-label">
-    {%- if content | toc | safe -%}
+    {%- if all_content | toc | safe -%}
     <h2 class="c-toc__label" id="js-toc-label">Table of Contents</h2>
     <div class="c-toc__panel" id="js-toc-panel">
-        {{ content | toc | safe }}
+        {{ all_content | toc | safe }}
     </div>
     {%- endif -%}
 </nav>
diff --git a/docs/src/_includes/components/rule.macro.html b/docs/src/_includes/components/rule.macro.html
index b459cd645..aa84e3448 100644
--- a/docs/src/_includes/components/rule.macro.html
+++ b/docs/src/_includes/components/rule.macro.html
@@ -7,7 +7,7 @@
             <span class="rule__status">deprecated</span>
         </p>
         {%- if params.replacedBy|length -%}
-        <p class="rule__description">Replaced by <a href="/rules/{{ params.replacedBy }}"><code>{{ params.replacedBy }}</code></a></p>
+        <p class="rule__description">Replaced by <a href="{{ '/rules' | url }}/{{ params.replacedBy }}"><code>{{ params.replacedBy }}</code></a></p>
         {%- else -%}<p class="rule__description">{{ params.description }}</p>
         {%- endif -%}
         {%- elseif params.removed == true -%}
@@ -16,11 +16,11 @@
             <span class="rule__status">removed</span>
         </p>
         {%- if params.replacedBy -%}
-        <p class="rule__description">Replaced by <a href="/rules/{{ params.replacedBy }}"><code>{{ params.replacedBy }}</code></a></p>
+        <p class="rule__description">Replaced by <a href="{{ '/rules' | url }}/{{ params.replacedBy }}"><code>{{ params.replacedBy }}</code></a></p>
         {%- else -%}<p class="rule__description">{{ params.description }}</p>
         {%- endif -%}
         {%- else -%}
-        <a href="/rules/{{ params.name }}" class="rule__name">{{ params.name }}</a>
+        <a href="{{ '/rules' | url }}/{{ params.name }}" class="rule__name">{{ params.name }}</a>
         <p class="rule__description">{{ params.description }}</p>
         {%- endif -%}
     </div>
diff --git a/docs/src/_includes/layouts/doc.html b/docs/src/_includes/layouts/doc.html
index b3f301f76..b8fbd3b98 100644
--- a/docs/src/_includes/layouts/doc.html
+++ b/docs/src/_includes/layouts/doc.html
@@ -13,12 +13,39 @@ layout: base.njk
             {% include 'components/docs-index.html' %}
     </div>
 
+    {# Add in related rules and further reading sections to content so TOC is accurate #}
+    {% set all_content = content %}
+    {% if related_rules %}
+        {% set related_rules_content %}
+            <h2 id="related-rules">Related Rules</h2>
+            {% related_rules related_rules %}
+        {% endset %}
+
+        {% set all_content = [all_content, related_rules_content] | join %}
+    {% endif %}
+
+    {% if further_reading %}
+        {% set further_reading_content %}
+            <h2 id="further-reading">Further Reading</h2>
+            {# async shortcodes don't work here so need to manually insert later #}
+        {% endset %}
+
+        {% set all_content = [all_content, further_reading_content] | join %}
+    {% endif %}
+
     <div class="docs-content">
         <main id="main" tabindex="-1" class="docs-main">
             <h1>{{ title }}</h1>
             {% include 'components/docs-toc.html' %}
 
-            {{ content | safe }}
+            {{ all_content | safe }}
+
+            {# now insert the async-fetched link data if necessary #}
+            {% if further_reading %}
+                {% for url in further_reading %}
+                    {% link url %}
+                {% endfor %}            
+            {% endif %}
 
             <div class="docs-edit-link">
                 <a href="{{ edit_link }}" class="c-btn c-btn--secondary">Edit this page</a>
diff --git a/docs/src/rules/accessor-pairs.md b/docs/src/rules/accessor-pairs.md
index b5d5ece3d..06bc33fe6 100644
--- a/docs/src/rules/accessor-pairs.md
+++ b/docs/src/rules/accessor-pairs.md
@@ -3,6 +3,13 @@ title: accessor-pairs
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/accessor-pairs.md
 rule_type: suggestion
+related_rules:
+- no-dupe-keys
+- no-dupe-class-members
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects
 ---
 
 Enforces getter/setter pairs in objects and classes.
@@ -287,14 +294,3 @@ See [no-dupe-class-members](no-dupe-class-members) if you also want to disallow
 ## When Not To Use It
 
 You can turn this rule off if you are not concerned with the simultaneous presence of setters and getters on objects.
-
-## Related Rules
-
-* [no-dupe-keys](no-dupe-keys)
-* [no-dupe-class-members](no-dupe-class-members)
-
-## Further Reading
-
-* [Object Setters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set)
-* [Object Getters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get)
-* [Working with Objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects)
diff --git a/docs/src/rules/array-bracket-newline.md b/docs/src/rules/array-bracket-newline.md
index 54be15698..a363f02e4 100644
--- a/docs/src/rules/array-bracket-newline.md
+++ b/docs/src/rules/array-bracket-newline.md
@@ -3,6 +3,8 @@ title: array-bracket-newline
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/array-bracket-newline.md
 rule_type: layout
+related_rules:
+- array-bracket-spacing
 ---
 
 <!--FIXABLE-->
@@ -280,10 +282,6 @@ var e = [
 
 If you don't want to enforce line breaks after opening and before closing array brackets, don't enable this rule.
 
-## Related Rules
-
-* [array-bracket-spacing](array-bracket-spacing)
-
 ## Compatibility
 
 * **JSCS:** [validateNewlineAfterArrayElements](https://jscs-dev.github.io/rule/validateNewlineAfterArrayElements)
diff --git a/docs/src/rules/array-bracket-spacing.md b/docs/src/rules/array-bracket-spacing.md
index 825557f07..1caa2a6c1 100644
--- a/docs/src/rules/array-bracket-spacing.md
+++ b/docs/src/rules/array-bracket-spacing.md
@@ -3,6 +3,10 @@ title: array-bracket-spacing
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/array-bracket-spacing.md
 rule_type: layout
+related_rules:
+- space-in-parens
+- object-curly-spacing
+- computed-property-spacing
 ---
 
 <!--FIXABLE-->
@@ -224,9 +228,3 @@ var arr = [[ 1, 2 ], 2, [ 3, 4 ]];
 ## When Not To Use It
 
 You can turn this rule off if you are not concerned with the consistency of spacing between array brackets.
-
-## Related Rules
-
-* [space-in-parens](space-in-parens)
-* [object-curly-spacing](object-curly-spacing)
-* [computed-property-spacing](computed-property-spacing)
diff --git a/docs/src/rules/array-element-newline.md b/docs/src/rules/array-element-newline.md
index 66db00248..936f8fa95 100644
--- a/docs/src/rules/array-element-newline.md
+++ b/docs/src/rules/array-element-newline.md
@@ -3,6 +3,15 @@ title: array-element-newline
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/array-element-newline.md
 rule_type: layout
+related_rules:
+- array-bracket-spacing
+- array-bracket-newline
+- object-property-newline
+- object-curly-spacing
+- object-curly-newline
+- max-statements-per-line
+- block-spacing
+- brace-style
 ---
 
 <!--FIXABLE-->
@@ -383,17 +392,6 @@ var [i = function foo() {
 
 If you don't want to enforce linebreaks between array elements, don't enable this rule.
 
-## Related Rules
-
-* [array-bracket-spacing](array-bracket-spacing)
-* [array-bracket-newline](array-bracket-newline)
-* [object-property-newline](object-property-newline)
-* [object-curly-spacing](object-curly-spacing)
-* [object-curly-newline](object-curly-newline)
-* [max-statements-per-line](max-statements-per-line)
-* [block-spacing](block-spacing)
-* [brace-style](brace-style)
-
 ## Compatibility
 
 * **JSCS:** [validateNewlineAfterArrayElements](https://jscs-dev.github.io/rule/validateNewlineAfterArrayElements)
diff --git a/docs/src/rules/arrow-parens.md b/docs/src/rules/arrow-parens.md
index 856bcb8f8..522034aac 100644
--- a/docs/src/rules/arrow-parens.md
+++ b/docs/src/rules/arrow-parens.md
@@ -3,6 +3,8 @@ title: arrow-parens
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/arrow-parens.md
 rule_type: layout
+further_reading:
+- https://github.com/airbnb/javascript#arrows--one-arg-parens
 ---
 
 <!--FIXABLE-->
@@ -230,8 +232,3 @@ a((foo) => { if (true) {} });
 ([a, b]) => a;
 ({a, b}) => a;
 ```
-
-## Further Reading
-
-* The `"as-needed", { "requireForBlockBody": true }` rule is directly inspired by the Airbnb
- [JS Style Guide](https://github.com/airbnb/javascript#arrows--one-arg-parens).
diff --git a/docs/src/rules/block-scoped-var.md b/docs/src/rules/block-scoped-var.md
index e23310547..a9131e604 100644
--- a/docs/src/rules/block-scoped-var.md
+++ b/docs/src/rules/block-scoped-var.md
@@ -3,6 +3,9 @@ title: block-scoped-var
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/block-scoped-var.md
 rule_type: suggestion
+further_reading:
+- https://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var#var_hoisting
 ---
 
 Enforces treating `var` as block scoped.
@@ -111,8 +114,3 @@ class C {
     }
 }
 ```
-
-## Further Reading
-
-* [JavaScript Scoping and Hoisting](http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html)
-* [var Hoisting](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var#var_hoisting)
diff --git a/docs/src/rules/block-spacing.md b/docs/src/rules/block-spacing.md
index e07b7652d..76ca2e612 100644
--- a/docs/src/rules/block-spacing.md
+++ b/docs/src/rules/block-spacing.md
@@ -3,6 +3,9 @@ title: block-spacing
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/block-spacing.md
 rule_type: layout
+related_rules:
+- space-before-blocks
+- brace-style
 ---
 
 <!--FIXABLE-->
@@ -82,8 +85,3 @@ class C {
 ## When Not To Use It
 
 If you don't want to be notified about spacing style inside of blocks, you can safely disable this rule.
-
-## Related Rules
-
-* [space-before-blocks](space-before-blocks)
-* [brace-style](brace-style)
diff --git a/docs/src/rules/brace-style.md b/docs/src/rules/brace-style.md
index 6b69184fd..477d866d9 100644
--- a/docs/src/rules/brace-style.md
+++ b/docs/src/rules/brace-style.md
@@ -3,6 +3,11 @@ title: brace-style
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/brace-style.md
 rule_type: layout
+related_rules:
+- block-spacing
+- space-before-blocks
+further_reading:
+- https://en.wikipedia.org/wiki/Indent_style
 ---
 
 <!--FIXABLE-->
@@ -392,12 +397,3 @@ class D { static { foo(); } }
 ## When Not To Use It
 
 If you don't want to enforce a particular brace style, don't enable this rule.
-
-## Related Rules
-
-* [block-spacing](block-spacing)
-* [space-before-blocks](space-before-blocks)
-
-## Further Reading
-
-* [Indent style](https://en.wikipedia.org/wiki/Indent_style)
diff --git a/docs/src/rules/callback-return.md b/docs/src/rules/callback-return.md
index 7acf5dc9c..87a927ec9 100644
--- a/docs/src/rules/callback-return.md
+++ b/docs/src/rules/callback-return.md
@@ -3,6 +3,11 @@ title: callback-return
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/callback-return.md
 rule_type: suggestion
+related_rules:
+- handle-callback-err
+further_reading:
+- https://github.com/maxogden/art-of-node#callbacks
+- https://web.archive.org/web/20171224042620/https://docs.nodejitsu.com/articles/errors/what-are-the-error-conventions/
 ---
 
 Enforces return after callback.
@@ -171,12 +176,3 @@ function foo(err, callback) {
 There are some cases where you might want to call a callback function more than once. In those cases this rule
  may lead to incorrect behavior. In those cases you may want to reserve a special name for those callbacks and
  not include that in the list of callbacks that trigger warnings.
-
-## Related Rules
-
-* [handle-callback-err](handle-callback-err)
-
-## Further Reading
-
-* [The Art Of Node: Callbacks](https://github.com/maxogden/art-of-node#callbacks)
-* [Nodejitsu: What are the error conventions?](https://docs.nodejitsu.com/articles/errors/what-are-the-error-conventions/)
diff --git a/docs/src/rules/class-methods-use-this.md b/docs/src/rules/class-methods-use-this.md
index 350e0f7c3..b02cf487e 100644
--- a/docs/src/rules/class-methods-use-this.md
+++ b/docs/src/rules/class-methods-use-this.md
@@ -3,6 +3,9 @@ title: class-methods-use-this
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/class-methods-use-this.md
 rule_type: suggestion
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static
 ---
 
 Enforces that class methods utilize `this`.
@@ -173,8 +176,3 @@ class A {
     foo = () => {}
 }
 ```
-
-## Further Reading
-
-* [Classes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes)
-* [Static Methods](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static)
diff --git a/docs/src/rules/comma-spacing.md b/docs/src/rules/comma-spacing.md
index 2463c9bb9..55eb9ccdc 100644
--- a/docs/src/rules/comma-spacing.md
+++ b/docs/src/rules/comma-spacing.md
@@ -3,6 +3,18 @@ title: comma-spacing
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/comma-spacing.md
 rule_type: layout
+related_rules:
+- array-bracket-spacing
+- comma-style
+- space-in-brackets
+- space-in-parens
+- space-infix-ops
+- space-after-keywords
+- space-unary-ops
+- space-return-throw-case
+further_reading:
+- https://www.crockford.com/code.html
+- https://dojotoolkit.org/reference-guide/1.9/developer/styleguide.html
 ---
 
 <!--FIXABLE-->
@@ -118,19 +130,3 @@ var arr = [,2 ,3]
 ## When Not To Use It
 
 If your project will not be following a consistent comma-spacing pattern, turn this rule off.
-
-## Related Rules
-
-* [array-bracket-spacing](array-bracket-spacing)
-* [comma-style](comma-style)
-* [space-in-brackets](space-in-brackets) (deprecated)
-* [space-in-parens](space-in-parens)
-* [space-infix-ops](space-infix-ops)
-* [space-after-keywords](space-after-keywords)
-* [space-unary-ops](space-unary-ops)
-* [space-return-throw-case](space-return-throw-case)
-
-## Further Reading
-
-* [JavaScript](http://javascript.crockford.com/code.html)
-* [Dojo Style Guide](https://dojotoolkit.org/reference-guide/1.9/developer/styleguide.html)
diff --git a/docs/src/rules/comma-style.md b/docs/src/rules/comma-style.md
index 4ce1979c9..e72c59b79 100644
--- a/docs/src/rules/comma-style.md
+++ b/docs/src/rules/comma-style.md
@@ -3,6 +3,10 @@ title: comma-style
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/comma-style.md
 rule_type: layout
+related_rules:
+- operator-linebreak
+further_reading:
+- https://gist.github.com/isaacs/357981
 ---
 
 <!--FIXABLE-->
@@ -165,14 +169,3 @@ var o = {fst:1,
 ## When Not To Use It
 
 This rule can safely be turned off if your project does not care about enforcing a consistent comma style.
-
-## Related Rules
-
-* [operator-linebreak](operator-linebreak)
-
-## Further Reading
-
-For more information on the Comma First style:
-
-* [A better coding convention for lists and object literals in JavaScript by isaacs](https://gist.github.com/isaacs/357981)
-* [npm coding style guideline](https://docs.npmjs.com/misc/coding-style)
diff --git a/docs/src/rules/complexity.md b/docs/src/rules/complexity.md
index 16a2e0565..e58e75415 100644
--- a/docs/src/rules/complexity.md
+++ b/docs/src/rules/complexity.md
@@ -3,6 +3,20 @@ title: complexity
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/complexity.md
 rule_type: suggestion
+related_rules:
+- max-depth
+- max-len
+- max-lines
+- max-lines-per-function
+- max-nested-callbacks
+- max-params
+- max-statements
+further_reading:
+- https://en.wikipedia.org/wiki/Cyclomatic_complexity
+- https://ariya.io/2012/12/complexity-analysis-of-javascript-code
+- https://craftsmanshipforsoftware.com/2015/05/25/complexity-for-javascript/
+- https://web.archive.org/web/20160808115119/http://jscomplexity.org/complexity
+- https://github.com/eslint/eslint/issues/4808#issuecomment-167795140
 ---
 
 Enforces a maximum cyclomatic complexity.
@@ -130,21 +144,3 @@ is equivalent to
 ## When Not To Use It
 
 If you can't determine an appropriate complexity limit for your code, then it's best to disable this rule.
-
-## Related Rules
-
-* [max-depth](max-depth)
-* [max-len](max-len)
-* [max-lines](max-lines)
-* [max-lines-per-function](max-lines-per-function)
-* [max-nested-callbacks](max-nested-callbacks)
-* [max-params](max-params)
-* [max-statements](max-statements)
-
-## Further Reading
-
-* [Cyclomatic Complexity](https://en.wikipedia.org/wiki/Cyclomatic_complexity)
-* [Complexity Analysis of JavaScript Code](https://ariya.io/2012/12/complexity-analysis-of-javascript-code)
-* [More about Complexity in JavaScript](https://craftsmanshipforsoftware.com/2015/05/25/complexity-for-javascript/)
-* [About Complexity](https://web.archive.org/web/20160808115119/http://jscomplexity.org/complexity)
-* [Discussion about Complexity in ESLint and more links](https://github.com/eslint/eslint/issues/4808#issuecomment-167795140)
diff --git a/docs/src/rules/computed-property-spacing.md b/docs/src/rules/computed-property-spacing.md
index f9f97f499..e0546c6db 100644
--- a/docs/src/rules/computed-property-spacing.md
+++ b/docs/src/rules/computed-property-spacing.md
@@ -3,6 +3,10 @@ title: computed-property-spacing
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/computed-property-spacing.md
 rule_type: layout
+related_rules:
+- array-bracket-spacing
+- comma-spacing
+- space-in-parens
 ---
 
 <!--FIXABLE-->
@@ -180,9 +184,3 @@ const Bar = class {
 ## When Not To Use It
 
 You can turn this rule off if you are not concerned with the consistency of computed properties.
-
-## Related Rules
-
-* [array-bracket-spacing](array-bracket-spacing)
-* [comma-spacing](comma-spacing)
-* [space-in-parens](space-in-parens)
diff --git a/docs/src/rules/default-case-last.md b/docs/src/rules/default-case-last.md
index a75b1ebbb..279d11280 100644
--- a/docs/src/rules/default-case-last.md
+++ b/docs/src/rules/default-case-last.md
@@ -3,6 +3,10 @@ title: default-case-last
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/default-case-last.md
 rule_type: suggestion
+related_rules:
+- default-case
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch
 ---
 
 Enforces default clauses in switch statements to be last.
@@ -122,11 +126,3 @@ if (foo !== 0) {
 }
 doSomethingAnyway();
 ```
-
-## Related Rules
-
-* [default-case](default-case)
-
-## Further Reading
-
-* [MDN switch statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch)
diff --git a/docs/src/rules/default-case.md b/docs/src/rules/default-case.md
index 9e389ca2f..f43bc21b0 100644
--- a/docs/src/rules/default-case.md
+++ b/docs/src/rules/default-case.md
@@ -3,6 +3,8 @@ title: default-case
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/default-case.md
 rule_type: suggestion
+related_rules:
+- no-fallthrough
 ---
 
 Requires a `default` case in switch statements.
@@ -126,7 +128,3 @@ switch(a) {
 ## When Not To Use It
 
 If you don't want to enforce a `default` case for `switch` statements, you can safely disable this rule.
-
-## Related Rules
-
-* [no-fallthrough](no-fallthrough)
diff --git a/docs/src/rules/dot-location.md b/docs/src/rules/dot-location.md
index 2193e6745..73b0481ed 100644
--- a/docs/src/rules/dot-location.md
+++ b/docs/src/rules/dot-location.md
@@ -3,6 +3,9 @@ title: dot-location
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/dot-location.md
 rule_type: layout
+related_rules:
+- newline-after-var
+- dot-notation
 ---
 
 <!--FIXABLE-->
@@ -87,8 +90,3 @@ var bar = object.property;
 ## When Not To Use It
 
 You can turn this rule off if you are not concerned with the consistency of newlines before or after dots in member expressions.
-
-## Related Rules
-
-* [newline-after-var](newline-after-var)
-* [dot-notation](dot-notation)
diff --git a/docs/src/rules/func-call-spacing.md b/docs/src/rules/func-call-spacing.md
index 775655aa9..0f5032b32 100644
--- a/docs/src/rules/func-call-spacing.md
+++ b/docs/src/rules/func-call-spacing.md
@@ -3,6 +3,8 @@ title: func-call-spacing
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/func-call-spacing.md
 rule_type: layout
+related_rules:
+- no-spaced-func
 ---
 
 <!--FIXABLE-->
@@ -104,10 +106,6 @@ fn
 
 This rule can safely be turned off if your project does not care about enforcing a consistent style for spacing within function calls.
 
-## Related Rules
-
-* [no-spaced-func](no-spaced-func) (deprecated)
-
 ## Compatibility
 
 * **JSCS**: [disallowSpacesInCallExpression](https://jscs-dev.github.io/rule/disallowSpacesInCallExpression)
diff --git a/docs/src/rules/func-names.md b/docs/src/rules/func-names.md
index 4c2e87cae..b62686006 100644
--- a/docs/src/rules/func-names.md
+++ b/docs/src/rules/func-names.md
@@ -3,6 +3,9 @@ title: func-names
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/func-names.md
 rule_type: suggestion
+further_reading:
+- https://web.archive.org/web/20201112040809/http://markdaggett.com/blog/2013/02/15/functions-explained/
+- https://2ality.com/2015/09/function-names-es6.html
 ---
 
 Requires or disallows named `function` expressions.
@@ -217,8 +220,3 @@ var foo = bar(function *baz() {});
 
 * **JSCS**: [requireAnonymousFunctions](https://jscs-dev.github.io/rule/requireAnonymousFunctions)
 * **JSCS**: [disallowAnonymousFunctions](https://jscs-dev.github.io/rule/disallowAnonymousFunctions)
-
-## Further Reading
-
-* [Functions Explained](http://markdaggett.com/blog/2013/02/15/functions-explained/)
-* [Function Names in ES6](http://2ality.com/2015/09/function-names-es6.html)
diff --git a/docs/src/rules/func-style.md b/docs/src/rules/func-style.md
index f2babc4c9..769a7a1f8 100644
--- a/docs/src/rules/func-style.md
+++ b/docs/src/rules/func-style.md
@@ -3,6 +3,8 @@ title: func-style
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/func-style.md
 rule_type: suggestion
+further_reading:
+- https://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html
 ---
 
 Enforces the consistent use of either `function` declarations or expressions.
@@ -132,7 +134,3 @@ var foo = () => {};
 ## When Not To Use It
 
 If you want to allow developers to each decide how they want to write functions on their own, then you can disable this rule.
-
-## Further Reading
-
-* [JavaScript Scoping and Hoisting](http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html)
diff --git a/docs/src/rules/function-call-argument-newline.md b/docs/src/rules/function-call-argument-newline.md
index 834eebc12..53c049915 100644
--- a/docs/src/rules/function-call-argument-newline.md
+++ b/docs/src/rules/function-call-argument-newline.md
@@ -3,6 +3,11 @@ title: function-call-argument-newline
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/function-call-argument-newline.md
 rule_type: layout
+related_rules:
+- function-paren-newline
+- func-call-spacing
+- object-property-newline
+- array-element-newline
 ---
 
 <!--FIXABLE-->
@@ -199,10 +204,3 @@ baz(
 ## When Not To Use It
 
 If you don't want to enforce line breaks between arguments, don't enable this rule.
-
-## Related Rules
-
-* [function-paren-newline](function-paren-newline)
-* [func-call-spacing](func-call-spacing)
-* [object-property-newline](object-property-newline)
-* [array-element-newline](array-element-newline)
diff --git a/docs/src/rules/generator-star-spacing.md b/docs/src/rules/generator-star-spacing.md
index f30cb587d..0833382a6 100644
--- a/docs/src/rules/generator-star-spacing.md
+++ b/docs/src/rules/generator-star-spacing.md
@@ -3,6 +3,8 @@ title: generator-star-spacing
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/generator-star-spacing.md
 rule_type: layout
+further_reading:
+- https://leanpub.com/understandinges6/read/#leanpub-auto-generators
 ---
 
 <!--FIXABLE-->
@@ -210,7 +212,3 @@ class Class { static * method() {} }
 ## When Not To Use It
 
 If your project will not be using generators or you are not concerned with spacing consistency, you do not need this rule.
-
-## Further Reading
-
-* [Understanding ES6: Generators](https://leanpub.com/understandinges6/read/#leanpub-auto-generators)
diff --git a/docs/src/rules/generator-star.md b/docs/src/rules/generator-star.md
index 0cddfddd4..f944081a2 100644
--- a/docs/src/rules/generator-star.md
+++ b/docs/src/rules/generator-star.md
@@ -2,6 +2,8 @@
 title: generator-star
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/generator-star.md
+further_reading:
+- https://leanpub.com/understandinges6/read/#leanpub-auto-generators
 ---
 
 Enforces consistent spacing around the asterisk in generator functions.
@@ -126,7 +128,3 @@ The shortened object literal syntax for generators is not affected by this rule.
 ## When Not To Use It
 
 If your project will not be using generators you do not need this rule.
-
-## Further Reading
-
-* [Understanding ES6: Generators](https://leanpub.com/understandinges6/read/#leanpub-auto-generators)
diff --git a/docs/src/rules/getter-return.md b/docs/src/rules/getter-return.md
index 55fc49cda..4b3770d84 100644
--- a/docs/src/rules/getter-return.md
+++ b/docs/src/rules/getter-return.md
@@ -3,6 +3,9 @@ title: getter-return
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/getter-return.md
 rule_type: problem
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get
+- https://leanpub.com/understandinges6/read/#leanpub-auto-accessor-properties
 ---
 
 <!--RECOMMENDED-->
@@ -99,8 +102,3 @@ p = {
 ## When Not To Use It
 
 If your project will not be using ES5 property getters you do not need this rule.
-
-## Further Reading
-
-* [MDN: Functions getter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get)
-* [Understanding ES6: Accessor Properties](https://leanpub.com/understandinges6/read/#leanpub-auto-accessor-properties)
diff --git a/docs/src/rules/grouped-accessor-pairs.md b/docs/src/rules/grouped-accessor-pairs.md
index 92dc21a2f..45fd15f86 100644
--- a/docs/src/rules/grouped-accessor-pairs.md
+++ b/docs/src/rules/grouped-accessor-pairs.md
@@ -3,6 +3,14 @@ title: grouped-accessor-pairs
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/grouped-accessor-pairs.md
 rule_type: suggestion
+related_rules:
+- accessor-pairs
+- no-dupe-keys
+- no-dupe-class-members
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
 ---
 
 Requires grouped accessor pairs in object literals and classes.
@@ -319,15 +327,3 @@ Also, this rule does not report any warnings for properties that have duplicate
 See [no-dupe-keys](no-dupe-keys) if you also want to disallow duplicate keys in object literals.
 
 See [no-dupe-class-members](no-dupe-class-members) if you also want to disallow duplicate names in class definitions.
-
-## Related Rules
-
-* [accessor-pairs](accessor-pairs)
-* [no-dupe-keys](no-dupe-keys)
-* [no-dupe-class-members](no-dupe-class-members)
-
-## Further Reading
-
-* [Object Setters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set)
-* [Object Getters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get)
-* [Classes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes)
diff --git a/docs/src/rules/guard-for-in.md b/docs/src/rules/guard-for-in.md
index 1a39d5221..dec9df179 100644
--- a/docs/src/rules/guard-for-in.md
+++ b/docs/src/rules/guard-for-in.md
@@ -3,6 +3,11 @@ title: guard-for-in
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/guard-for-in.md
 rule_type: suggestion
+related_rules:
+- no-prototype-builtins
+further_reading:
+- https://javascriptweblog.wordpress.com/2011/01/04/exploring-javascript-for-in-loops/
+- https://2ality.com/2012/01/objects-as-maps.html
 ---
 
 Requires `for in` loops to include an `if` statement.
@@ -48,12 +53,3 @@ for (key in foo) {
     }
 }
 ```
-
-## Related Rules
-
-* [no-prototype-builtins](no-prototype-builtins)
-
-## Further Reading
-
-* [Exploring JavaScript for-in loops](https://javascriptweblog.wordpress.com/2011/01/04/exploring-javascript-for-in-loops/)
-* [The pitfalls of using objects as maps in JavaScript](http://2ality.com/2012/01/objects-as-maps.html)
diff --git a/docs/src/rules/handle-callback-err.md b/docs/src/rules/handle-callback-err.md
index 64db6d074..fb2b7c72b 100644
--- a/docs/src/rules/handle-callback-err.md
+++ b/docs/src/rules/handle-callback-err.md
@@ -3,6 +3,9 @@ title: handle-callback-err
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/handle-callback-err.md
 rule_type: suggestion
+further_reading:
+- https://github.com/maxogden/art-of-node#callbacks
+- https://web.archive.org/web/20171224042620/https://docs.nodejitsu.com/articles/errors/what-are-the-error-conventions/
 ---
 
 Enforces callback error handling.
@@ -83,8 +86,3 @@ If the configured name of the error variable begins with a `^` it is considered
 
 There are cases where it may be safe for your application to ignore errors, however only ignore errors if you are
 confident that some other form of monitoring will help you catch the problem.
-
-## Further Reading
-
-* [The Art Of Node: Callbacks](https://github.com/maxogden/art-of-node#callbacks)
-* [Nodejitsu: What are the error conventions?](https://docs.nodejitsu.com/articles/errors/what-are-the-error-conventions/)
diff --git a/docs/src/rules/id-length.md b/docs/src/rules/id-length.md
index ba2051a58..318b2be65 100644
--- a/docs/src/rules/id-length.md
+++ b/docs/src/rules/id-length.md
@@ -3,6 +3,11 @@ title: id-length
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/id-length.md
 rule_type: suggestion
+related_rules:
+- max-len
+- new-cap
+- func-names
+- camelcase
 ---
 
 Enforces minimum and maximum identifier lengths.
@@ -253,10 +258,3 @@ var [E] = arr;
 const { y } = foo;
 const { a: z } = foo;
 ```
-
-## Related Rules
-
-* [max-len](max-len)
-* [new-cap](new-cap)
-* [func-names](func-names)
-* [camelcase](camelcase)
diff --git a/docs/src/rules/implicit-arrow-linebreak.md b/docs/src/rules/implicit-arrow-linebreak.md
index 876f96965..1a0941bd1 100644
--- a/docs/src/rules/implicit-arrow-linebreak.md
+++ b/docs/src/rules/implicit-arrow-linebreak.md
@@ -3,6 +3,8 @@ title: implicit-arrow-linebreak
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/implicit-arrow-linebreak.md
 rule_type: layout
+related_rules:
+- brace-style
 ---
 
 <!--FIXABLE-->
@@ -103,7 +105,3 @@ Examples of **correct** code for this rule with the `"below"` option:
 If you're not concerned about consistent locations of implicitly returned arrow function expressions, you should not turn on this rule.
 
 You can also disable this rule if you are using the `"always"` option for the [`arrow-body-style`](arrow-body-style), since this will disable the use of implicit returns in arrow functions.
-
-## Related Rules
-
-* [`brace-style`](brace-style) which enforces this behavior for arrow functions with block bodies.
diff --git a/docs/src/rules/jsx-quotes.md b/docs/src/rules/jsx-quotes.md
index 91cc4501e..14d0aa533 100644
--- a/docs/src/rules/jsx-quotes.md
+++ b/docs/src/rules/jsx-quotes.md
@@ -3,6 +3,8 @@ title: jsx-quotes
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/jsx-quotes.md
 rule_type: layout
+related_rules:
+- quotes
 ---
 
 <!--FIXABLE-->
@@ -76,7 +78,3 @@ Examples of **correct** code for this rule with the `"prefer-single"` option:
 ## When Not To Use It
 
 You can turn this rule off if you don’t use JSX or if you aren’t concerned with a consistent usage of quotes within JSX attributes.
-
-## Related Rules
-
-* [quotes](quotes)
diff --git a/docs/src/rules/lines-around-comment.md b/docs/src/rules/lines-around-comment.md
index 2cb707e18..100cd2daf 100644
--- a/docs/src/rules/lines-around-comment.md
+++ b/docs/src/rules/lines-around-comment.md
@@ -3,6 +3,9 @@ title: lines-around-comment
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/lines-around-comment.md
 rule_type: layout
+related_rules:
+- space-before-blocks
+- spaced-comment
 ---
 
 <!--FIXABLE-->
@@ -577,8 +580,3 @@ foo();
 ## When Not To Use It
 
 Many people enjoy a terser code style and don't mind comments bumping up against code. If you fall into that category this rule is not for you.
-
-## Related Rules
-
-* [space-before-blocks](space-before-blocks)
-* [spaced-comment](spaced-comment)
diff --git a/docs/src/rules/lines-around-directive.md b/docs/src/rules/lines-around-directive.md
index 4f8b7fc1d..6c31f4368 100644
--- a/docs/src/rules/lines-around-directive.md
+++ b/docs/src/rules/lines-around-directive.md
@@ -3,6 +3,9 @@ title: lines-around-directive
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/lines-around-directive.md
 rule_type: layout
+related_rules:
+- lines-around-comment
+- padded-blocks
 ---
 
 <!--FIXABLE-->
@@ -317,11 +320,6 @@ function foo() {
 
 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.
 
-## Related Rules
-
-* [lines-around-comment](lines-around-comment)
-* [padded-blocks](padded-blocks)
-
 ## Compatibility
 
 * **JSCS**: [requirePaddingNewLinesAfterUseStrict](https://jscs-dev.github.io/rule/requirePaddingNewLinesAfterUseStrict)
diff --git a/docs/src/rules/lines-between-class-members.md b/docs/src/rules/lines-between-class-members.md
index 05a2c90ce..0a798eb6a 100644
--- a/docs/src/rules/lines-between-class-members.md
+++ b/docs/src/rules/lines-between-class-members.md
@@ -3,6 +3,9 @@ title: lines-between-class-members
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/lines-between-class-members.md
 rule_type: layout
+related_rules:
+- padded-blocks
+- padding-line-between-statements
 ---
 
 <!--FIXABLE-->
@@ -129,11 +132,6 @@ class Foo{
 
 If you don't want to enforce empty lines between class members, you can disable this rule.
 
-## Related Rules
-
-* [padded-blocks](padded-blocks)
-* [padding-line-between-statements](padding-line-between-statements)
-
 ## Compatibility
 
 * [requirePaddingNewLinesAfterBlocks](https://jscs-dev.github.io/rule/requirePaddingNewLinesAfterBlocks)
diff --git a/docs/src/rules/max-depth.md b/docs/src/rules/max-depth.md
index fd2539b3f..0263e343a 100644
--- a/docs/src/rules/max-depth.md
+++ b/docs/src/rules/max-depth.md
@@ -3,6 +3,14 @@ title: max-depth
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/max-depth.md
 rule_type: suggestion
+related_rules:
+- complexity
+- max-len
+- max-lines
+- max-lines-per-function
+- max-nested-callbacks
+- max-params
+- max-statements
 ---
 
 Enforces a maximum depth that blocks can be nested.
@@ -100,13 +108,3 @@ function foo() {
     }
 }
 ```
-
-## Related Rules
-
-* [complexity](complexity)
-* [max-len](max-len)
-* [max-lines](max-lines)
-* [max-lines-per-function](max-lines-per-function)
-* [max-nested-callbacks](max-nested-callbacks)
-* [max-params](max-params)
-* [max-statements](max-statements)
diff --git a/docs/src/rules/max-len.md b/docs/src/rules/max-len.md
index 5b2ab80b1..a52cefbba 100644
--- a/docs/src/rules/max-len.md
+++ b/docs/src/rules/max-len.md
@@ -3,6 +3,12 @@ title: max-len
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/max-len.md
 rule_type: layout
+related_rules:
+- complexity
+- max-depth
+- max-nested-callbacks
+- max-params
+- max-statements
 ---
 
 Enforces a maximum line length.
@@ -158,11 +164,3 @@ Examples of **correct** code for this rule with the `ignorePattern` option:
 
 var dep = require('really/really/really/really/really/really/really/really/long/module');
 ```
-
-## Related Rules
-
-* [complexity](complexity)
-* [max-depth](max-depth)
-* [max-nested-callbacks](max-nested-callbacks)
-* [max-params](max-params)
-* [max-statements](max-statements)
diff --git a/docs/src/rules/max-lines-per-function.md b/docs/src/rules/max-lines-per-function.md
index f04ef8026..daf6f389d 100644
--- a/docs/src/rules/max-lines-per-function.md
+++ b/docs/src/rules/max-lines-per-function.md
@@ -3,6 +3,14 @@ title: max-lines-per-function
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/max-lines-per-function.md
 rule_type: suggestion
+related_rules:
+- complexity
+- max-depth
+- max-lines
+- max-nested-callbacks
+- max-params
+- max-statements
+- max-statements-per-line
 ---
 
 Enforces a maximum function length.
@@ -194,13 +202,3 @@ Examples of **correct** code for this rule with the `{ "IIFEs": true }` option:
 ## When Not To Use It
 
 You can turn this rule off if you are not concerned with the number of lines in your functions.
-
-## Related Rules
-
-* [complexity](complexity)
-* [max-depth](max-depth)
-* [max-lines](max-lines)
-* [max-nested-callbacks](max-nested-callbacks)
-* [max-params](max-params)
-* [max-statements](max-statements)
-* [max-statements-per-line](max-statements-per-line)
diff --git a/docs/src/rules/max-lines.md b/docs/src/rules/max-lines.md
index 31d232a6d..2ce5e5813 100644
--- a/docs/src/rules/max-lines.md
+++ b/docs/src/rules/max-lines.md
@@ -3,6 +3,15 @@ title: max-lines
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/max-lines.md
 rule_type: suggestion
+related_rules:
+- complexity
+- max-depth
+- max-lines-per-function
+- max-nested-callbacks
+- max-params
+- max-statements
+further_reading:
+- https://web.archive.org/web/20160725154648/http://www.mind2b.com/component/content/article/24-software-module-size-and-file-size
 ---
 
 Enforces a maximum file length.
@@ -116,19 +125,6 @@ var a,
 
 You can turn this rule off if you are not concerned with the number of lines in your files.
 
-## Related Rules
-
-* [complexity](complexity)
-* [max-depth](max-depth)
-* [max-lines-per-function](max-lines-per-function)
-* [max-nested-callbacks](max-nested-callbacks)
-* [max-params](max-params)
-* [max-statements](max-statements)
-
 ## Compatibility
 
 * **JSCS**: [maximumNumberOfLines](https://jscs-dev.github.io/rule/maximumNumberOfLines)
-
-## Further Reading
-
-* [Software Module size and file size](https://web.archive.org/web/20160725154648/http://www.mind2b.com/component/content/article/24-software-module-size-and-file-size)
diff --git a/docs/src/rules/max-nested-callbacks.md b/docs/src/rules/max-nested-callbacks.md
index 61a2ca750..a541eb7a5 100644
--- a/docs/src/rules/max-nested-callbacks.md
+++ b/docs/src/rules/max-nested-callbacks.md
@@ -3,6 +3,18 @@ title: max-nested-callbacks
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/max-nested-callbacks.md
 rule_type: suggestion
+related_rules:
+- complexity
+- max-depth
+- max-len
+- max-lines
+- max-lines-per-function
+- max-params
+- max-statements
+further_reading:
+- http://book.mixu.net/node/ch7.html
+- https://web.archive.org/web/20220104141150/https://howtonode.org/control-flow
+- https://web.archive.org/web/20220127215850/https://howtonode.org/control-flow-part-ii
 ---
 
 Enforces a maximum depth that callbacks can be nested.
@@ -74,19 +86,3 @@ function handleFoo4() {
     foo5();
 }
 ```
-
-## Related Rules
-
-* [complexity](complexity)
-* [max-depth](max-depth)
-* [max-len](max-len)
-* [max-lines](max-lines)
-* [max-lines-per-function](max-lines-per-function)
-* [max-params](max-params)
-* [max-statements](max-statements)
-
-## Further Reading
-
-* [Control flow in Node.js](http://book.mixu.net/node/ch7.html)
-* [Control Flow in Node](https://howtonode.org/control-flow)
-* [Control Flow in Node Part II](https://howtonode.org/control-flow-part-ii)
diff --git a/docs/src/rules/max-params.md b/docs/src/rules/max-params.md
index ece959881..ad45082ec 100644
--- a/docs/src/rules/max-params.md
+++ b/docs/src/rules/max-params.md
@@ -3,6 +3,14 @@ title: max-params
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/max-params.md
 rule_type: suggestion
+related_rules:
+- complexity
+- max-depth
+- max-len
+- max-lines
+- max-lines-per-function
+- max-nested-callbacks
+- max-statements
 ---
 
 Enforces a maximum number of parameters in function definitions.
@@ -58,13 +66,3 @@ let foo = (bar, baz, qux) => {
     doSomething();
 };
 ```
-
-## Related Rules
-
-* [complexity](complexity)
-* [max-depth](max-depth)
-* [max-len](max-len)
-* [max-lines](max-lines)
-* [max-lines-per-function](max-lines-per-function)
-* [max-nested-callbacks](max-nested-callbacks)
-* [max-statements](max-statements)
diff --git a/docs/src/rules/max-statements-per-line.md b/docs/src/rules/max-statements-per-line.md
index d2a172450..8d34246ea 100644
--- a/docs/src/rules/max-statements-per-line.md
+++ b/docs/src/rules/max-statements-per-line.md
@@ -3,6 +3,14 @@ title: max-statements-per-line
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/max-statements-per-line.md
 rule_type: layout
+related_rules:
+- max-depth
+- max-len
+- max-lines
+- max-lines-per-function
+- max-nested-callbacks
+- max-params
+- max-statements
 ---
 
 Enforces a maximum number of statements allowed per line.
@@ -82,13 +90,3 @@ var foo = function foo() { bar = 1; };
 ## When Not To Use It
 
 You can turn this rule off if you are not concerned with the number of statements on each line.
-
-## Related Rules
-
-* [max-depth](max-depth)
-* [max-len](max-len)
-* [max-lines](max-lines)
-* [max-lines-per-function](max-lines-per-function)
-* [max-nested-callbacks](max-nested-callbacks)
-* [max-params](max-params)
-* [max-statements](max-statements)
diff --git a/docs/src/rules/max-statements.md b/docs/src/rules/max-statements.md
index 9c1dfe0ff..2d18021f3 100644
--- a/docs/src/rules/max-statements.md
+++ b/docs/src/rules/max-statements.md
@@ -3,6 +3,14 @@ title: max-statements
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/max-statements.md
 rule_type: suggestion
+related_rules:
+- complexity
+- max-depth
+- max-len
+- max-lines
+- max-lines-per-function
+- max-nested-callbacks
+- max-params
 ---
 
 Enforces a maximum number of statements allowed in function blocks.
@@ -164,13 +172,3 @@ function foo() {
   var foo11 = 11;
 }
 ```
-
-## Related Rules
-
-* [complexity](complexity)
-* [max-depth](max-depth)
-* [max-len](max-len)
-* [max-lines](max-lines)
-* [max-lines-per-function](max-lines-per-function)
-* [max-nested-callbacks](max-nested-callbacks)
-* [max-params](max-params)
diff --git a/docs/src/rules/multiline-ternary.md b/docs/src/rules/multiline-ternary.md
index d4c5e1e6d..f92d6715f 100644
--- a/docs/src/rules/multiline-ternary.md
+++ b/docs/src/rules/multiline-ternary.md
@@ -3,6 +3,8 @@ title: multiline-ternary
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/multiline-ternary.md
 rule_type: layout
+related_rules:
+- operator-linebreak
 ---
 
 <!--FIXABLE-->
@@ -153,10 +155,6 @@ foo > bar ? (
 
 You can safely disable this rule if you do not have any strict conventions about whether the operands of a ternary expression should be separated by newlines.
 
-## Related Rules
-
-* [operator-linebreak](operator-linebreak)
-
 ## Compatibility
 
 * **JSCS**: [requireMultiLineTernary](https://jscs-dev.github.io/rule/requireMultiLineTernary)
diff --git a/docs/src/rules/newline-before-return.md b/docs/src/rules/newline-before-return.md
index 718ceb253..261b8619f 100644
--- a/docs/src/rules/newline-before-return.md
+++ b/docs/src/rules/newline-before-return.md
@@ -3,6 +3,8 @@ title: newline-before-return
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/newline-before-return.md
 rule_type: layout
+related_rules:
+- newline-after-var
 ---
 
 <!--FIXABLE-->
@@ -119,7 +121,3 @@ function foo() {
 ## When Not To Use It
 
 You can safely disable this rule if you do not have any strict conventions about whitespace before `return` statements.
-
-## Related Rules
-
-* [newline-after-var](newline-after-var)
diff --git a/docs/src/rules/no-alert.md b/docs/src/rules/no-alert.md
index 48167bd1f..414cce09d 100644
--- a/docs/src/rules/no-alert.md
+++ b/docs/src/rules/no-alert.md
@@ -3,6 +3,9 @@ title: no-alert
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-alert.md
 rule_type: suggestion
+related_rules:
+- no-console
+- no-debugger
 ---
 
 Disallows the use of `alert`, `confirm`, and `prompt`.
@@ -45,8 +48,3 @@ function foo() {
     alert();
 }
 ```
-
-## Related Rules
-
-* [no-console](no-console)
-* [no-debugger](no-debugger)
diff --git a/docs/src/rules/no-array-constructor.md b/docs/src/rules/no-array-constructor.md
index 746704b55..da49901c4 100644
--- a/docs/src/rules/no-array-constructor.md
+++ b/docs/src/rules/no-array-constructor.md
@@ -3,6 +3,9 @@ title: no-array-constructor
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-array-constructor.md
 rule_type: suggestion
+related_rules:
+- no-new-object
+- no-new-wrappers
 ---
 
 Disallows `Array` constructors.
@@ -43,8 +46,3 @@ new Array(someOtherArray.length)
 
 This rule enforces a nearly universal stylistic concern. That being said, this
 rule may be disabled if the constructor style is preferred.
-
-## Related Rules
-
-* [no-new-object](no-new-object)
-* [no-new-wrappers](no-new-wrappers)
diff --git a/docs/src/rules/no-arrow-condition.md b/docs/src/rules/no-arrow-condition.md
index be451c877..ad15b4698 100644
--- a/docs/src/rules/no-arrow-condition.md
+++ b/docs/src/rules/no-arrow-condition.md
@@ -3,6 +3,10 @@ title: no-arrow-condition
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-arrow-condition.md
 
+related_rules:
+- arrow-parens
+- no-confusing-arrow
+- no-constant-condition
 ---
 
 Disallows arrow functions where test conditions are expected.
@@ -47,9 +51,3 @@ a => 1 ? 2 : 3
 var x = a => 1 ? 2 : 3
 var x = (a) => 1 ? 2 : 3
 ```
-
-## Related Rules
-
-* [arrow-parens](arrow-parens)
-* [no-confusing-arrow](no-confusing-arrow)
-* [no-constant-condition](no-constant-condition)
diff --git a/docs/src/rules/no-buffer-constructor.md b/docs/src/rules/no-buffer-constructor.md
index 64e7c1e17..93dde3018 100644
--- a/docs/src/rules/no-buffer-constructor.md
+++ b/docs/src/rules/no-buffer-constructor.md
@@ -3,6 +3,10 @@ title: no-buffer-constructor
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-buffer-constructor.md
 rule_type: problem
+further_reading:
+- https://nodejs.org/api/buffer.html
+- https://github.com/ChALkeR/notes/blob/master/Lets-fix-Buffer-API.md
+- https://github.com/nodejs/node/issues/4660
 ---
 
 Disallows use of the `Buffer()` constructor.
@@ -42,9 +46,3 @@ Buffer.from(res.body.values);
 ## When Not To Use It
 
 If you don't use Node.js, or you still need to support versions of Node.js that lack methods like `Buffer.from`, then you should not enable this rule.
-
-## Further Reading
-
-* [Buffer API documentation](https://nodejs.org/api/buffer.html)
-* [Let's fix Node.js Buffer API](https://github.com/ChALkeR/notes/blob/master/Lets-fix-Buffer-API.md)
-* [Buffer(number) is unsafe](https://github.com/nodejs/node/issues/4660)
diff --git a/docs/src/rules/no-case-declarations.md b/docs/src/rules/no-case-declarations.md
index 4e0717e8d..feeb25499 100644
--- a/docs/src/rules/no-case-declarations.md
+++ b/docs/src/rules/no-case-declarations.md
@@ -3,6 +3,8 @@ title: no-case-declarations
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-case-declarations.md
 rule_type: suggestion
+related_rules:
+- no-fallthrough
 ---
 
 <!--RECOMMENDED-->
@@ -78,7 +80,3 @@ switch (foo) {
 ## When Not To Use It
 
 If you depend on fall through behavior and want access to bindings introduced in the case block.
-
-## Related Rules
-
-* [no-fallthrough](no-fallthrough)
diff --git a/docs/src/rules/no-cond-assign.md b/docs/src/rules/no-cond-assign.md
index 318a9e8bf..a1e61504b 100644
--- a/docs/src/rules/no-cond-assign.md
+++ b/docs/src/rules/no-cond-assign.md
@@ -3,6 +3,8 @@ title: no-cond-assign
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-cond-assign.md
 rule_type: problem
+related_rules:
+- no-extra-parens
 ---
 
 <!--RECOMMENDED-->
@@ -130,7 +132,3 @@ if (x === 0) {
     var b = 1;
 }
 ```
-
-## Related Rules
-
-* [no-extra-parens](no-extra-parens)
diff --git a/docs/src/rules/no-confusing-arrow.md b/docs/src/rules/no-confusing-arrow.md
index d5a3a0e40..094497135 100644
--- a/docs/src/rules/no-confusing-arrow.md
+++ b/docs/src/rules/no-confusing-arrow.md
@@ -3,6 +3,9 @@ title: no-confusing-arrow
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-confusing-arrow.md
 rule_type: suggestion
+related_rules:
+- no-constant-condition
+- arrow-parens
 ---
 
 <!--FIXABLE-->
@@ -95,8 +98,3 @@ Examples of **correct** code for this rule with the `{"onlyOneSimpleParam": true
 ([a]) => 1 ? 2 : 3;
 (...a) => 1 ? 2 : 3;
 ```
-
-## Related Rules
-
-* [no-constant-condition](no-constant-condition)
-* [arrow-parens](arrow-parens)
diff --git a/docs/src/rules/no-console.md b/docs/src/rules/no-console.md
index 6bf0637aa..1b258a3d8 100644
--- a/docs/src/rules/no-console.md
+++ b/docs/src/rules/no-console.md
@@ -3,6 +3,9 @@ title: no-console
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-console.md
 rule_type: suggestion
+related_rules:
+- no-alert
+- no-debugger
 ---
 
 Disallows the use of `console`.
@@ -97,8 +100,3 @@ However, you might not want to manually add `eslint-disable-next-line` or `eslin
     }
 }
 ```
-
-## Related Rules
-
-* [no-alert](no-alert)
-* [no-debugger](no-debugger)
diff --git a/docs/src/rules/no-constant-binary-expression.md b/docs/src/rules/no-constant-binary-expression.md
index 7efd4fcf4..5f3c42aa4 100644
--- a/docs/src/rules/no-constant-binary-expression.md
+++ b/docs/src/rules/no-constant-binary-expression.md
@@ -3,6 +3,8 @@ title: no-constant-binary-expression
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-constant-binary-expression.md
 rule_type: problem
+related_rules:
+- no-constant-condition
 ---
 
 Disallows expressions where the operation doesn't affect the value.
@@ -69,7 +71,3 @@ const objIsEmpty = Object.keys(someObj).length === 0;
 
 const arrIsEmpty = someArr.length === 0;
 ```
-
-## Related Rules
-
-* [no-constant-condition](no-constant-condition)
diff --git a/docs/src/rules/no-constant-condition.md b/docs/src/rules/no-constant-condition.md
index 14ad92d04..a2b4e025a 100644
--- a/docs/src/rules/no-constant-condition.md
+++ b/docs/src/rules/no-constant-condition.md
@@ -3,6 +3,8 @@ title: no-constant-condition
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-constant-condition.md
 rule_type: problem
+related_rules:
+- no-constant-binary-expression
 ---
 
 <!--RECOMMENDED-->
@@ -132,7 +134,3 @@ do {
     }
 } while (true)
 ```
-
-## Related Rules
-
-* [no-constant-binary-expression](no-constant-binary-expression)
diff --git a/docs/src/rules/no-control-regex.md b/docs/src/rules/no-control-regex.md
index 542e8f391..d7bdff87f 100644
--- a/docs/src/rules/no-control-regex.md
+++ b/docs/src/rules/no-control-regex.md
@@ -3,6 +3,9 @@ title: no-control-regex
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-control-regex.md
 rule_type: problem
+related_rules:
+- no-div-regex
+- no-regex-spaces
 ---
 
 <!--RECOMMENDED-->
@@ -19,6 +22,7 @@ The following elements of regular expression patterns are considered possible er
 
 * Hexadecimal character escapes from `\x00` to `\x1F`.
 * Unicode character escapes from `\u0000` to `\u001F`.
+* Unicode code point escapes from `\u{0}` to `\u{1F}`.
 * Unescaped raw characters from U+0000 to U+001F.
 
 Control escapes such as `\t` and `\n` are allowed by this rule.
@@ -32,8 +36,9 @@ var pattern1 = /\x00/;
 var pattern2 = /\x0C/;
 var pattern3 = /\x1F/;
 var pattern4 = /\u000C/;
-var pattern5 = new RegExp("\x0C"); // raw U+000C character in the pattern
-var pattern6 = new RegExp("\\x0C"); // \x0C pattern
+var pattern5 = /\u{C}/u;
+var pattern6 = new RegExp("\x0C"); // raw U+000C character in the pattern
+var pattern7 = new RegExp("\\x0C"); // \x0C pattern
 ```
 
 Examples of **correct** code for this rule:
@@ -43,11 +48,12 @@ Examples of **correct** code for this rule:
 
 var pattern1 = /\x20/;
 var pattern2 = /\u0020/;
-var pattern3 = /\t/;
-var pattern4 = /\n/;
-var pattern5 = new RegExp("\x20");
-var pattern6 = new RegExp("\\t");
-var pattern7 = new RegExp("\\n");
+var pattern3 = /\u{20}/u;
+var pattern4 = /\t/;
+var pattern5 = /\n/;
+var pattern6 = new RegExp("\x20");
+var pattern7 = new RegExp("\\t");
+var pattern8 = new RegExp("\\n");
 ```
 
 ## Known Limitations
@@ -67,8 +73,3 @@ There is no difference in behavior between `new RegExp("\t")` and `new RegExp("\
 ## When Not To Use It
 
 If you need to use control character pattern matching, then you should turn this rule off.
-
-## Related Rules
-
-* [no-div-regex](no-div-regex)
-* [no-regex-spaces](no-regex-spaces)
diff --git a/docs/src/rules/no-debugger.md b/docs/src/rules/no-debugger.md
index 99188c801..67588f209 100644
--- a/docs/src/rules/no-debugger.md
+++ b/docs/src/rules/no-debugger.md
@@ -3,6 +3,11 @@ title: no-debugger
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-debugger.md
 rule_type: problem
+related_rules:
+- no-alert
+- no-console
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger
 ---
 
 <!--RECOMMENDED-->
@@ -39,12 +44,3 @@ function isTruthy(x) {
 ## When Not To Use It
 
 If your code is still very much in development and don't want to worry about stripping `debugger` statements, then turn this rule off. You'll generally want to turn it back on when testing code prior to deployment.
-
-## Related Rules
-
-* [no-alert](no-alert)
-* [no-console](no-console)
-
-## Further Reading
-
-* [Debugger](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger)
diff --git a/docs/src/rules/no-div-regex.md b/docs/src/rules/no-div-regex.md
index 26022d058..60a1440a2 100644
--- a/docs/src/rules/no-div-regex.md
+++ b/docs/src/rules/no-div-regex.md
@@ -3,6 +3,9 @@ title: no-div-regex
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-div-regex.md
 rule_type: suggestion
+related_rules:
+- no-control-regex
+- no-regex-spaces
 ---
 
 <!--FIXABLE-->
@@ -34,8 +37,3 @@ Examples of **correct** code for this rule:
 
 function bar() { return /[=]foo/; }
 ```
-
-## Related Rules
-
-* [no-control-regex](no-control-regex)
-* [no-regex-spaces](no-regex-spaces)
diff --git a/docs/src/rules/no-dupe-else-if.md b/docs/src/rules/no-dupe-else-if.md
index 73c98f9d2..38a9c9691 100644
--- a/docs/src/rules/no-dupe-else-if.md
+++ b/docs/src/rules/no-dupe-else-if.md
@@ -3,6 +3,9 @@ title: no-dupe-else-if
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-dupe-else-if.md
 rule_type: problem
+related_rules:
+- no-duplicate-case
+- no-lonely-if
 ---
 
 <!--RECOMMENDED-->
@@ -180,8 +183,3 @@ if (a) {
 ## When Not To Use It
 
 In rare cases where you really need identical test conditions in the same chain, which necessarily means that the expressions in the chain are causing and relying on side effects, you will have to turn this rule off.
-
-## Related Rules
-
-* [no-duplicate-case](no-duplicate-case)
-* [no-lonely-if](no-lonely-if)
diff --git a/docs/src/rules/no-empty-function.md b/docs/src/rules/no-empty-function.md
index bb9763361..e80566c18 100644
--- a/docs/src/rules/no-empty-function.md
+++ b/docs/src/rules/no-empty-function.md
@@ -3,6 +3,8 @@ title: no-empty-function
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-empty-function.md
 rule_type: suggestion
+related_rules:
+- no-empty
 ---
 
 Disallows empty functions.
@@ -348,7 +350,3 @@ class A {
 ## When Not To Use It
 
 If you don't want to be notified about empty functions, then it's safe to disable this rule.
-
-## Related Rules
-
-* [no-empty](./no-empty)
diff --git a/docs/src/rules/no-empty-label.md b/docs/src/rules/no-empty-label.md
index f7a3fadeb..72ef07f75 100644
--- a/docs/src/rules/no-empty-label.md
+++ b/docs/src/rules/no-empty-label.md
@@ -3,6 +3,10 @@ title: no-empty-label
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-empty-label.md
 
+related_rules:
+- no-labels
+- no-label-var
+- no-unused-labels
 ---
 
 Disallows labels for anything other than loops and switches.
@@ -38,9 +42,3 @@ for (var i=10; i; i--) {
 ## When Not To Use It
 
 If you don't want to be notified about usage of labels, then it's safe to disable this rule.
-
-## Related Rules
-
-* [no-labels](./no-labels)
-* [no-label-var](./no-label-var)
-* [no-unused-labels](./no-unused-labels)
diff --git a/docs/src/rules/no-empty.md b/docs/src/rules/no-empty.md
index cf3ab39d1..4b1d428a1 100644
--- a/docs/src/rules/no-empty.md
+++ b/docs/src/rules/no-empty.md
@@ -3,6 +3,8 @@ title: no-empty
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-empty.md
 rule_type: suggestion
+related_rules:
+- no-empty-function
 ---
 
 <!--RECOMMENDED-->
@@ -92,7 +94,3 @@ finally {
 ## When Not To Use It
 
 If you intentionally use empty block statements then you can disable this rule.
-
-## Related Rules
-
-* [no-empty-function](./no-empty-function)
diff --git a/docs/src/rules/no-eval.md b/docs/src/rules/no-eval.md
index 35cbc7ee1..f559ebe66 100644
--- a/docs/src/rules/no-eval.md
+++ b/docs/src/rules/no-eval.md
@@ -3,6 +3,11 @@ title: no-eval
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-eval.md
 rule_type: suggestion
+related_rules:
+- no-implied-eval
+further_reading:
+- https://ericlippert.com/2003/11/01/eval-is-evil-part-one/
+- https://javascriptweblog.wordpress.com/2010/04/19/how-evil-is-eval/
 ---
 
 Disallows eval().
@@ -151,12 +156,3 @@ global.eval("var a = 0");
   var foo = window;
   foo.eval("var a = 0");
   ```
-
-## Related Rules
-
-* [no-implied-eval](no-implied-eval)
-
-## Further Reading
-
-* [Eval is Evil, Part One](https://ericlippert.com/2003/11/01/eval-is-evil-part-one/)
-* [How evil is eval](https://javascriptweblog.wordpress.com/2010/04/19/how-evil-is-eval/)
diff --git a/docs/src/rules/no-ex-assign.md b/docs/src/rules/no-ex-assign.md
index 233ec2390..a4e186626 100644
--- a/docs/src/rules/no-ex-assign.md
+++ b/docs/src/rules/no-ex-assign.md
@@ -3,6 +3,8 @@ title: no-ex-assign
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-ex-assign.md
 rule_type: problem
+further_reading:
+- https://bocoup.com/blog/the-catch-with-try-catch
 ---
 
 <!--RECOMMENDED-->
@@ -39,7 +41,3 @@ try {
     var foo = 10;
 }
 ```
-
-## Further Reading
-
-* [The "catch" with try...catch](https://bocoup.com/blog/the-catch-with-try-catch) by Ben Alman explains how the exception identifier can leak into the outer scope in IE 6-8
diff --git a/docs/src/rules/no-extend-native.md b/docs/src/rules/no-extend-native.md
index d0be66af5..eef3c34a0 100644
--- a/docs/src/rules/no-extend-native.md
+++ b/docs/src/rules/no-extend-native.md
@@ -3,6 +3,8 @@ title: no-extend-native
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-extend-native.md
 rule_type: suggestion
+related_rules:
+- no-global-assign
 ---
 
 Disallows extending of native objects.
@@ -76,7 +78,3 @@ window.Function.prototype.bind = 'tight';
 ## When Not To Use It
 
 You may want to disable this rule when working with polyfills that try to patch older versions of JavaScript with the latest spec, such as those that might `Function.prototype.bind` or `Array.prototype.forEach` in a future-friendly way.
-
-## Related Rules
-
-* [no-global-assign](no-global-assign)
diff --git a/docs/src/rules/no-extra-bind.md b/docs/src/rules/no-extra-bind.md
index 5a7d28f32..eebe43a4f 100644
--- a/docs/src/rules/no-extra-bind.md
+++ b/docs/src/rules/no-extra-bind.md
@@ -3,6 +3,9 @@ title: no-extra-bind
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-extra-bind.md
 rule_type: suggestion
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
+- https://www.smashingmagazine.com/2014/01/understanding-javascript-function-prototype-bind/
 ---
 
 <!--FIXABLE-->
@@ -88,8 +91,3 @@ var x = function (a) {
 ## When Not To Use It
 
 If you are not concerned about unnecessary calls to `bind()`, you can safely disable this rule.
-
-## Further Reading
-
-* [Function.prototype.bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)
-* [Understanding JavaScript's Function.prototype.bind](https://www.smashingmagazine.com/2014/01/understanding-javascript-function-prototype-bind/)
diff --git a/docs/src/rules/no-extra-label.md b/docs/src/rules/no-extra-label.md
index 82b98c1dc..c5dda3f8d 100644
--- a/docs/src/rules/no-extra-label.md
+++ b/docs/src/rules/no-extra-label.md
@@ -3,6 +3,10 @@ title: no-extra-label
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-extra-label.md
 rule_type: suggestion
+related_rules:
+- no-labels
+- no-label-var
+- no-unused-labels
 ---
 
 <!--FIXABLE-->
@@ -83,9 +87,3 @@ C: switch (a) {
 ## When Not To Use It
 
 If you don't want to be notified about usage of labels, then it's safe to disable this rule.
-
-## Related Rules
-
-* [no-labels](./no-labels)
-* [no-label-var](./no-label-var)
-* [no-unused-labels](./no-unused-labels)
diff --git a/docs/src/rules/no-extra-parens.md b/docs/src/rules/no-extra-parens.md
index 858734e43..d5afd39a3 100644
--- a/docs/src/rules/no-extra-parens.md
+++ b/docs/src/rules/no-extra-parens.md
@@ -3,6 +3,12 @@ title: no-extra-parens
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-extra-parens.md
 rule_type: layout
+related_rules:
+- arrow-parens
+- no-cond-assign
+- no-return-assign
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
 ---
 
 <!--FIXABLE-->
@@ -297,13 +303,3 @@ a = (b * c);
 
 typeof (a);
 ```
-
-## Related Rules
-
-* [arrow-parens](arrow-parens)
-* [no-cond-assign](no-cond-assign)
-* [no-return-assign](no-return-assign)
-
-## Further Reading
-
-* [MDN: Operator Precedence](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence)
diff --git a/docs/src/rules/no-extra-semi.md b/docs/src/rules/no-extra-semi.md
index 6aea002eb..ae7b65e36 100644
--- a/docs/src/rules/no-extra-semi.md
+++ b/docs/src/rules/no-extra-semi.md
@@ -3,6 +3,9 @@ title: no-extra-semi
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-extra-semi.md
 rule_type: suggestion
+related_rules:
+- semi
+- semi-spacing
 ---
 
 <!--RECOMMENDED-->
@@ -72,8 +75,3 @@ class C {
 ## When Not To Use It
 
 If you intentionally use extra semicolons then you can disable this rule.
-
-## Related Rules
-
-* [semi](semi)
-* [semi-spacing](semi-spacing)
diff --git a/docs/src/rules/no-extra-strict.md b/docs/src/rules/no-extra-strict.md
index f2627ebaf..28dba6d8a 100644
--- a/docs/src/rules/no-extra-strict.md
+++ b/docs/src/rules/no-extra-strict.md
@@ -3,6 +3,8 @@ title: no-extra-strict
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-extra-strict.md
 
+further_reading:
+- https://es5.github.io/#C
 ---
 
 Disallows strict mode directives when already in strict mode.
@@ -51,7 +53,3 @@ Examples of **correct** code for this rule:
     var foo = true;
 }());
 ```
-
-## Further Reading
-
-* [The ECMAScript 5 Annotated Specification - Strict Mode](https://es5.github.io/#C)
diff --git a/docs/src/rules/no-fallthrough.md b/docs/src/rules/no-fallthrough.md
index bb7c61872..d7a3e8031 100644
--- a/docs/src/rules/no-fallthrough.md
+++ b/docs/src/rules/no-fallthrough.md
@@ -3,6 +3,8 @@ title: no-fallthrough
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-fallthrough.md
 rule_type: problem
+related_rules:
+- default-case
 ---
 
 <!--RECOMMENDED-->
@@ -194,7 +196,3 @@ switch(foo) {
 ## When Not To Use It
 
 If you don't want to enforce that each `case` statement should end with a `throw`, `return`, `break`, or comment, then you can safely turn this rule off.
-
-## Related Rules
-
-* [default-case](default-case)
diff --git a/docs/src/rules/no-global-assign.md b/docs/src/rules/no-global-assign.md
index b5be6d41e..4e10466d2 100644
--- a/docs/src/rules/no-global-assign.md
+++ b/docs/src/rules/no-global-assign.md
@@ -3,6 +3,10 @@ title: no-global-assign
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-global-assign.md
 rule_type: suggestion
+related_rules:
+- no-extend-native
+- no-redeclare
+- no-shadow
 ---
 
 <!--RECOMMENDED-->
@@ -90,9 +94,3 @@ This rule accepts an `exceptions` option, which can be used to specify a list of
 ## When Not To Use It
 
 If you are trying to override one of the native objects.
-
-## Related Rules
-
-* [no-extend-native](no-extend-native)
-* [no-redeclare](no-redeclare)
-* [no-shadow](no-shadow)
diff --git a/docs/src/rules/no-implicit-globals.md b/docs/src/rules/no-implicit-globals.md
index c8a0a542f..8c075ab62 100644
--- a/docs/src/rules/no-implicit-globals.md
+++ b/docs/src/rules/no-implicit-globals.md
@@ -3,6 +3,13 @@ title: no-implicit-globals
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-implicit-globals.md
 rule_type: suggestion
+related_rules:
+- no-undef
+- no-global-assign
+further_reading:
+- https://benalman.com/news/2010/11/immediately-invoked-function-expression/
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Undeclared_var
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Temporal_dead_zone
 ---
 
 Disallows declarations in the global scope.
@@ -220,14 +227,3 @@ In the case of a CommonJS module, if your code is in strict mode or you don't wa
 and you also don't want this rule to warn you about the read-only globals, you can disable this rule.
 
 In the case of an ES module, if you don't want this rule to warn you about the read-only globals you can disable this rule.
-
-## Related Rules
-
-* [no-undef](no-undef)
-* [no-global-assign](no-global-assign)
-
-## Further Reading
-
-* [Immediately-Invoked Function Expression (IIFE)](http://benalman.com/news/2010/11/immediately-invoked-function-expression/)
-* [ReferenceError: assignment to undeclared variable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Undeclared_var)
-* [Temporal Dead Zone](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Temporal_dead_zone)
diff --git a/docs/src/rules/no-implied-eval.md b/docs/src/rules/no-implied-eval.md
index 48af96090..66596a9e5 100644
--- a/docs/src/rules/no-implied-eval.md
+++ b/docs/src/rules/no-implied-eval.md
@@ -3,6 +3,8 @@ title: no-implied-eval
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-implied-eval.md
 rule_type: suggestion
+related_rules:
+- no-eval
 ---
 
 Disallows the use of `eval()`-like methods.
@@ -63,7 +65,3 @@ setInterval(function() {
 ## When Not To Use It
 
 If you want to allow `setTimeout()` and `setInterval()` with string arguments, then you can safely disable this rule.
-
-## Related Rules
-
-* [no-eval](no-eval)
diff --git a/docs/src/rules/no-invalid-regexp.md b/docs/src/rules/no-invalid-regexp.md
index 724cf2118..5c70b81aa 100644
--- a/docs/src/rules/no-invalid-regexp.md
+++ b/docs/src/rules/no-invalid-regexp.md
@@ -3,6 +3,8 @@ title: no-invalid-regexp
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-invalid-regexp.md
 rule_type: problem
+further_reading:
+- https://es5.github.io/#x7.8.5
 ---
 
 <!--RECOMMENDED-->
@@ -60,7 +62,3 @@ new RegExp('.', 'a')
 
 new RegExp('.', 'az')
 ```
-
-## Further Reading
-
-* [Annotated ES5 §7.8.5 - Regular Expression Literals](https://es5.github.io/#x7.8.5)
diff --git a/docs/src/rules/no-irregular-whitespace.md b/docs/src/rules/no-irregular-whitespace.md
index b99f1faf0..f470a8938 100644
--- a/docs/src/rules/no-irregular-whitespace.md
+++ b/docs/src/rules/no-irregular-whitespace.md
@@ -3,6 +3,9 @@ title: no-irregular-whitespace
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-irregular-whitespace.md
 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
 ---
 
 <!--RECOMMENDED-->
@@ -174,8 +177,3 @@ function thing() {
 ## 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.
-
-## Further Reading
-
-* [ECMA whitespace](https://es5.github.io/#x7.2)
-* [JSON whitespace issues](http://timelessrepo.com/json-isnt-a-javascript-subset)
diff --git a/docs/src/rules/no-iterator.md b/docs/src/rules/no-iterator.md
index 39116ee88..87aab8ce4 100644
--- a/docs/src/rules/no-iterator.md
+++ b/docs/src/rules/no-iterator.md
@@ -3,6 +3,10 @@ title: no-iterator
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-iterator.md
 rule_type: suggestion
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators
+- https://kangax.github.io/es5-compat-table/es6/#Iterators
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Deprecated_and_obsolete_features#Object_methods
 ---
 
 Disallows the use of the `__iterator__` property.
@@ -43,9 +47,3 @@ Examples of **correct** code for this rule:
 
 var __iterator__ = foo; // Not using the `__iterator__` property.
 ```
-
-## Further Reading
-
-* [MDN - Iterators and Generators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators)
-* [ECMAScript 6 compatibility table - Iterators](https://kangax.github.io/es5-compat-table/es6/#Iterators)
-* [Deprecated and Obsolete Features](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Deprecated_and_obsolete_features#Object_methods)
diff --git a/docs/src/rules/no-label-var.md b/docs/src/rules/no-label-var.md
index 60f433d94..3f98e2de3 100644
--- a/docs/src/rules/no-label-var.md
+++ b/docs/src/rules/no-label-var.md
@@ -3,6 +3,10 @@ title: no-label-var
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-label-var.md
 rule_type: suggestion
+related_rules:
+- no-extra-label
+- no-labels
+- no-unused-labels
 ---
 
 Disallows labels that are variable names.
@@ -47,9 +51,3 @@ q:
 ## When Not To Use It
 
 If you don't want to be notified about usage of labels, then it's safe to disable this rule.
-
-## Related Rules
-
-* [no-extra-label](./no-extra-label)
-* [no-labels](./no-labels)
-* [no-unused-labels](./no-unused-labels)
diff --git a/docs/src/rules/no-labels.md b/docs/src/rules/no-labels.md
index fdf9231c5..116543953 100644
--- a/docs/src/rules/no-labels.md
+++ b/docs/src/rules/no-labels.md
@@ -3,6 +3,10 @@ title: no-labels
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-labels.md
 rule_type: suggestion
+related_rules:
+- no-extra-label
+- no-label-var
+- no-unused-labels
 ---
 
 Disallows labeled statements.
@@ -122,9 +126,3 @@ label:
 ## When Not To Use It
 
 If you need to use labeled statements everywhere, then you can safely disable this rule.
-
-## Related Rules
-
-* [no-extra-label](./no-extra-label)
-* [no-label-var](./no-label-var)
-* [no-unused-labels](./no-unused-labels)
diff --git a/docs/src/rules/no-misleading-character-class.md b/docs/src/rules/no-misleading-character-class.md
index 005127abf..9d1908201 100644
--- a/docs/src/rules/no-misleading-character-class.md
+++ b/docs/src/rules/no-misleading-character-class.md
@@ -7,6 +7,8 @@ rule_type: problem
 
 <!--RECOMMENDED-->
 
+<!--SUGGESTIONS-->
+
 Disallows characters which are made with multiple code points in character class syntax.
 
 Unicode includes the characters which are made with multiple code points.
diff --git a/docs/src/rules/no-mixed-operators.md b/docs/src/rules/no-mixed-operators.md
index 48f8849a7..d47e47781 100644
--- a/docs/src/rules/no-mixed-operators.md
+++ b/docs/src/rules/no-mixed-operators.md
@@ -3,6 +3,8 @@ title: no-mixed-operators
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-mixed-operators.md
 rule_type: suggestion
+related_rules:
+- no-extra-parens
 ---
 
 Disallows mixes of different operators.
@@ -12,8 +14,6 @@ This rule warns when different operators are used consecutively without parenthe
 
 ```js
 var foo = a && b || c || d;    /*BAD: Unexpected mix of '&&' and '||'.*/
-var foo = a && b ? c : d;      /*BAD: Unexpected mix of '&&' and '?:'.*/
-var foo = (a && b) ? c : d;    /*GOOD*/
 var foo = (a && b) || c || d;  /*GOOD*/
 var foo = a && (b || c || d);  /*GOOD*/
 ```
@@ -32,17 +32,6 @@ will generate
 1:18  Unexpected mix of '&&' and '||'. (no-mixed-operators)
 ```
 
-```js
-var foo = a && b ? c : d;
-```
-
-will generate
-
-```shell
-1:13  Unexpected mix of '&&' and '?:'. (no-mixed-operators)
-1:18  Unexpected mix of '&&' and '?:'. (no-mixed-operators)
-```
-
 ## Rule Details
 
 This rule checks `BinaryExpression`, `LogicalExpression` and `ConditionalExpression`.
@@ -194,7 +183,3 @@ var foo = (a + b) - c;
 ## When Not To Use It
 
 If you don't want to be notified about mixed operators, then it's safe to disable this rule.
-
-## Related Rules
-
-* [no-extra-parens](no-extra-parens)
diff --git a/docs/src/rules/no-mixed-spaces-and-tabs.md b/docs/src/rules/no-mixed-spaces-and-tabs.md
index d53fc786f..2e7dc0cfc 100644
--- a/docs/src/rules/no-mixed-spaces-and-tabs.md
+++ b/docs/src/rules/no-mixed-spaces-and-tabs.md
@@ -3,6 +3,8 @@ title: no-mixed-spaces-and-tabs
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-mixed-spaces-and-tabs.md
 rule_type: layout
+further_reading:
+- https://www.emacswiki.org/emacs/SmartTabs
 ---
 
 <!--RECOMMENDED-->
@@ -67,7 +69,3 @@ function main() {
         y = 7;
 }
 ```
-
-## Further Reading
-
-* [Smart Tabs](https://www.emacswiki.org/emacs/SmartTabs)
diff --git a/docs/src/rules/no-multi-assign.md b/docs/src/rules/no-multi-assign.md
index 8ebc10b38..34909f92c 100644
--- a/docs/src/rules/no-multi-assign.md
+++ b/docs/src/rules/no-multi-assign.md
@@ -3,6 +3,8 @@ title: no-multi-assign
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-multi-assign.md
 rule_type: suggestion
+related_rules:
+- max-statements-per-line
 ---
 
 Disallows use of chained assignment expressions.
@@ -100,7 +102,3 @@ class Foo {
     a = b = 10;
 }
 ```
-
-## Related Rules
-
-* [max-statements-per-line](max-statements-per-line)
diff --git a/docs/src/rules/no-multi-spaces.md b/docs/src/rules/no-multi-spaces.md
index 2e93c4571..373ef1ea4 100644
--- a/docs/src/rules/no-multi-spaces.md
+++ b/docs/src/rules/no-multi-spaces.md
@@ -3,6 +3,14 @@ title: no-multi-spaces
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-multi-spaces.md
 rule_type: layout
+related_rules:
+- key-spacing
+- space-infix-ops
+- space-in-brackets
+- space-in-parens
+- space-after-keywords
+- space-unary-ops
+- space-return-throw-case
 ---
 
 <!--FIXABLE-->
@@ -168,13 +176,3 @@ import someOtherMod from 'some-other-mod';
 ## When Not To Use It
 
 If you don't want to check and disallow multiple spaces, then you should turn this rule off.
-
-## Related Rules
-
-* [key-spacing](key-spacing)
-* [space-infix-ops](space-infix-ops)
-* [space-in-brackets](space-in-brackets) (deprecated)
-* [space-in-parens](space-in-parens)
-* [space-after-keywords](space-after-keywords)
-* [space-unary-ops](space-unary-ops)
-* [space-return-throw-case](space-return-throw-case)
diff --git a/docs/src/rules/no-native-reassign.md b/docs/src/rules/no-native-reassign.md
index f66a09ca4..ce6b557b1 100644
--- a/docs/src/rules/no-native-reassign.md
+++ b/docs/src/rules/no-native-reassign.md
@@ -3,6 +3,10 @@ title: no-native-reassign
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-native-reassign.md
 rule_type: suggestion
+related_rules:
+- no-extend-native
+- no-redeclare
+- no-shadow
 ---
 
 Disallows reassignment of native objects.
@@ -90,9 +94,3 @@ This rule accepts an `exceptions` option, which can be used to specify a list of
 ## When Not To Use It
 
 If you are trying to override one of the native objects.
-
-## Related Rules
-
-* [no-extend-native](no-extend-native)
-* [no-redeclare](no-redeclare)
-* [no-shadow](no-shadow)
diff --git a/docs/src/rules/no-nested-ternary.md b/docs/src/rules/no-nested-ternary.md
index a9194ff30..c1906cb16 100644
--- a/docs/src/rules/no-nested-ternary.md
+++ b/docs/src/rules/no-nested-ternary.md
@@ -3,6 +3,9 @@ title: no-nested-ternary
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-nested-ternary.md
 rule_type: suggestion
+related_rules:
+- no-ternary
+- no-unneeded-ternary
 ---
 
 Disallows nested ternary expressions.
@@ -44,8 +47,3 @@ if (foo) {
   thing = foobar;
 }
 ```
-
-## Related Rules
-
-* [no-ternary](no-ternary)
-* [no-unneeded-ternary](no-unneeded-ternary)
diff --git a/docs/src/rules/no-new-object.md b/docs/src/rules/no-new-object.md
index 96533e5f0..9655b13bb 100644
--- a/docs/src/rules/no-new-object.md
+++ b/docs/src/rules/no-new-object.md
@@ -3,6 +3,9 @@ title: no-new-object
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-new-object.md
 rule_type: suggestion
+related_rules:
+- no-array-constructor
+- no-new-wrappers
 ---
 
 Disallows `new` operators with the `Object` object.
@@ -53,8 +56,3 @@ 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.
-
-## Related Rules
-
-* [no-array-constructor](no-array-constructor)
-* [no-new-wrappers](no-new-wrappers)
diff --git a/docs/src/rules/no-new-symbol.md b/docs/src/rules/no-new-symbol.md
index 06e965d5d..461cdc8f0 100644
--- a/docs/src/rules/no-new-symbol.md
+++ b/docs/src/rules/no-new-symbol.md
@@ -3,6 +3,8 @@ title: no-new-symbol
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-new-symbol.md
 rule_type: problem
+further_reading:
+- https://www.ecma-international.org/ecma-262/6.0/#sec-symbol-objects
 ---
 
 <!--RECOMMENDED-->
@@ -50,7 +52,3 @@ function bar(Symbol) {
 ## When Not To Use It
 
 This rule should not be used in ES3/5 environments.
-
-## Further Reading
-
-* [Symbol Objects specification](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 91f2d1821..174d27d9c 100644
--- a/docs/src/rules/no-new-wrappers.md
+++ b/docs/src/rules/no-new-wrappers.md
@@ -3,6 +3,11 @@ title: no-new-wrappers
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-new-wrappers.md
 rule_type: suggestion
+related_rules:
+- no-array-constructor
+- no-new-object
+further_reading:
+- https://www.inkling.com/read/javascript-definitive-guide-david-flanagan-6th/chapter-3/wrapper-objects
 ---
 
 Disallows `new` operators with the `String`, `Number`, and `Boolean` objects.
@@ -74,12 +79,3 @@ var object = new MyString();
 ## When Not To Use It
 
 If you want to allow the use of primitive wrapper objects, then you can safely disable this rule.
-
-## Related Rules
-
-* [no-array-constructor](no-array-constructor)
-* [no-new-object](no-new-object)
-
-## Further Reading
-
-* [Wrapper objects](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 22b08a254..9fb530a7f 100644
--- a/docs/src/rules/no-nonoctal-decimal-escape.md
+++ b/docs/src/rules/no-nonoctal-decimal-escape.md
@@ -3,6 +3,10 @@ title: no-nonoctal-decimal-escape
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-nonoctal-decimal-escape.md
 rule_type: suggestion
+related_rules:
+- no-octal-escape
+further_reading:
+- https://tc39.es/ecma262/#prod-annexB-NonOctalDecimalEscapeSequence
 ---
 
 <!--RECOMMENDED-->
@@ -63,11 +67,3 @@ var baz = "Don't use \\8 and \\9 escapes.";
 
 var quux = "\0\u0038";
 ```
-
-## Related Rules
-
-* [no-octal-escape](no-octal-escape)
-
-## Further Reading
-
-* [NonOctalDecimalEscapeSequence](https://tc39.es/ecma262/#prod-annexB-NonOctalDecimalEscapeSequence) in ECMAScript specification
diff --git a/docs/src/rules/no-obj-calls.md b/docs/src/rules/no-obj-calls.md
index f8a394103..cd5bb376a 100644
--- a/docs/src/rules/no-obj-calls.md
+++ b/docs/src/rules/no-obj-calls.md
@@ -3,6 +3,8 @@ title: no-obj-calls
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-obj-calls.md
 rule_type: problem
+further_reading:
+- https://es5.github.io/#x15.8
 ---
 
 <!--RECOMMENDED-->
@@ -68,7 +70,3 @@ var value = Reflect.get({ x: 1, y: 2 }, "x");
 
 var first = Atomics.load(foo, 0);
 ```
-
-## Further Reading
-
-* [The Math Object](https://es5.github.io/#x15.8)
diff --git a/docs/src/rules/no-param-reassign.md b/docs/src/rules/no-param-reassign.md
index 08200d838..8ee3eb573 100644
--- a/docs/src/rules/no-param-reassign.md
+++ b/docs/src/rules/no-param-reassign.md
@@ -3,6 +3,8 @@ title: no-param-reassign
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-param-reassign.md
 rule_type: suggestion
+further_reading:
+- https://spin.atomicobject.com/2011/04/10/javascript-don-t-reassign-your-function-arguments/
 ---
 
 Disallows reassignment of function parameters.
@@ -160,7 +162,3 @@ function foo(barBaz) {
 ## When Not To Use It
 
 If you want to allow assignment to function parameters, then you can safely disable this rule.
-
-## Further Reading
-
-* [JavaScript: Don’t Reassign Your Function Arguments](https://spin.atomicobject.com/2011/04/10/javascript-don-t-reassign-your-function-arguments/)
diff --git a/docs/src/rules/no-process-env.md b/docs/src/rules/no-process-env.md
index 9bd325cf2..f9223d46d 100644
--- a/docs/src/rules/no-process-env.md
+++ b/docs/src/rules/no-process-env.md
@@ -3,6 +3,9 @@ title: no-process-env
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-process-env.md
 rule_type: suggestion
+further_reading:
+- https://stackoverflow.com/questions/5869216/how-to-store-node-js-deployment-settings-configuration-files
+- https://blog.benhall.me.uk/2012/02/storing-application-config-data-in/
 ---
 
 Disallows the use of `process.env`.
@@ -40,8 +43,3 @@ if(config.env === "development") {
 ## When Not To Use It
 
 If you prefer to use `process.env` throughout your project to retrieve values from environment variables, then you can safely disable this rule.
-
-## Further Reading
-
-* [How to store Node.js deployment settings/configuration files? - Stack Overflow](https://stackoverflow.com/questions/5869216/how-to-store-node-js-deployment-settings-configuration-files)
-* [Storing Node.js application config data - Ben Hall's blog](https://blog.benhall.me.uk/2012/02/storing-application-config-data-in/)
diff --git a/docs/src/rules/no-promise-executor-return.md b/docs/src/rules/no-promise-executor-return.md
index d57988512..1a694603d 100644
--- a/docs/src/rules/no-promise-executor-return.md
+++ b/docs/src/rules/no-promise-executor-return.md
@@ -3,6 +3,10 @@ title: no-promise-executor-return
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-promise-executor-return.md
 rule_type: problem
+related_rules:
+- no-async-promise-executor
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
 ---
 
 Disallows returning values from Promise executor functions.
@@ -93,11 +97,3 @@ new Promise((resolve, reject) => {
 
 Promise.resolve(1);
 ```
-
-## Related Rules
-
-* [no-async-promise-executor](no-async-promise-executor)
-
-## Further Reading
-
-* [MDN Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
diff --git a/docs/src/rules/no-proto.md b/docs/src/rules/no-proto.md
index 77063d311..07ee39412 100644
--- a/docs/src/rules/no-proto.md
+++ b/docs/src/rules/no-proto.md
@@ -3,6 +3,8 @@ title: no-proto
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-proto.md
 rule_type: suggestion
+further_reading:
+- https://johnresig.com/blog/objectgetprototypeof/
 ---
 
 Disallows the use of the `__proto__` property.
@@ -43,7 +45,3 @@ var c = { __proto__: a };
 
 You might want to turn this rule off if you need to support legacy browsers which implement the
 `__proto__` property but not `Object.getPrototypeOf` or `Object.setPrototypeOf`.
-
-## Further Reading
-
-* [Object.getPrototypeOf](http://ejohn.org/blog/objectgetprototypeof/)
diff --git a/docs/src/rules/no-redeclare.md b/docs/src/rules/no-redeclare.md
index e0e2e7563..6a5de9120 100644
--- a/docs/src/rules/no-redeclare.md
+++ b/docs/src/rules/no-redeclare.md
@@ -3,6 +3,8 @@ title: no-redeclare
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-redeclare.md
 rule_type: suggestion
+related_rules:
+- no-shadow
 ---
 
 <!--RECOMMENDED-->
@@ -87,7 +89,3 @@ var top = 0;
 The `browser` environment has many built-in global variables (for example, `top`). Some of built-in global variables cannot be redeclared.
 
 Note that when using the `node` or `commonjs` environments (or `ecmaFeatures.globalReturn`, if using the default parser), the top scope of a program is not actually the global scope, but rather a "module" scope. When this is the case, declaring a variable named after a builtin global is not a redeclaration, but rather a shadowing of the global variable. In that case, the [`no-shadow`](no-shadow) rule with the `"builtinGlobals"` option should be used.
-
-## Related Rules
-
-* [no-shadow](no-shadow)
diff --git a/docs/src/rules/no-regex-spaces.md b/docs/src/rules/no-regex-spaces.md
index 2edf2e586..8bb213303 100644
--- a/docs/src/rules/no-regex-spaces.md
+++ b/docs/src/rules/no-regex-spaces.md
@@ -3,6 +3,9 @@ title: no-regex-spaces
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-regex-spaces.md
 rule_type: suggestion
+related_rules:
+- no-div-regex
+- no-control-regex
 ---
 
 <!--RECOMMENDED-->
@@ -50,8 +53,3 @@ var re = new RegExp("foo {3}bar");
 ## When Not To Use It
 
 If you want to allow multiple spaces in a regular expression, then you can safely turn this rule off.
-
-## Related Rules
-
-* [no-div-regex](no-div-regex)
-* [no-control-regex](no-control-regex)
diff --git a/docs/src/rules/no-reserved-keys.md b/docs/src/rules/no-reserved-keys.md
index 5644d0042..8608eeb60 100644
--- a/docs/src/rules/no-reserved-keys.md
+++ b/docs/src/rules/no-reserved-keys.md
@@ -3,6 +3,8 @@ title: no-reserved-keys
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-reserved-keys.md
 
+further_reading:
+- https://kangax.github.io/compat-table/es5/#Reserved_words_as_property_names
 ---
 
 Disallows unquoted reserved words as property names in object literals.
@@ -54,7 +56,3 @@ var values = {
 ## When Not To Use It
 
 If your code is only going to be executed in an ECMAScript 5 or higher environment, then you can safely leave this rule off.
-
-## Further Reading
-
-* [Reserved words as property names](https://kangax.github.io/compat-table/es5/#Reserved_words_as_property_names)
diff --git a/docs/src/rules/no-restricted-globals.md b/docs/src/rules/no-restricted-globals.md
index 5164b9f15..4e9de8a0e 100644
--- a/docs/src/rules/no-restricted-globals.md
+++ b/docs/src/rules/no-restricted-globals.md
@@ -3,6 +3,9 @@ title: no-restricted-globals
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-restricted-globals.md
 rule_type: suggestion
+related_rules:
+- no-restricted-properties
+- no-restricted-syntax
 ---
 
 Disallows specific global variables.
@@ -90,8 +93,3 @@ function onClick() {
     console.log(event);    // Unexpected global variable 'event'. Use local parameter instead.
 }
 ```
-
-## Related Rules
-
-* [no-restricted-properties](no-restricted-properties)
-* [no-restricted-syntax](no-restricted-syntax)
diff --git a/docs/src/rules/no-restricted-properties.md b/docs/src/rules/no-restricted-properties.md
index a2781c9d7..fbb925b5d 100644
--- a/docs/src/rules/no-restricted-properties.md
+++ b/docs/src/rules/no-restricted-properties.md
@@ -3,6 +3,9 @@ title: no-restricted-properties
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-restricted-properties.md
 rule_type: suggestion
+related_rules:
+- no-restricted-globals
+- no-restricted-syntax
 ---
 
 Disallows certain object properties.
@@ -124,8 +127,3 @@ require('foo');
 ## When Not To Use It
 
 If you don't have any object/property combinations to restrict, you should not use this rule.
-
-## Related Rules
-
-* [no-restricted-globals](no-restricted-globals)
-* [no-restricted-syntax](no-restricted-syntax)
diff --git a/docs/src/rules/no-restricted-syntax.md b/docs/src/rules/no-restricted-syntax.md
index b94d47c30..e0a71e17b 100644
--- a/docs/src/rules/no-restricted-syntax.md
+++ b/docs/src/rules/no-restricted-syntax.md
@@ -3,6 +3,11 @@ title: no-restricted-syntax
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-restricted-syntax.md
 rule_type: suggestion
+related_rules:
+- no-alert
+- no-console
+- no-debugger
+- no-restricted-properties
 ---
 
 Disallows specified syntax.
@@ -82,10 +87,3 @@ foo instanceof bar;
 ## When Not To Use It
 
 If you don't want to restrict your code from using any JavaScript features or syntax, you should not use this rule.
-
-## Related Rules
-
-* [no-alert](no-alert)
-* [no-console](no-console)
-* [no-debugger](no-debugger)
-* [no-restricted-properties](no-restricted-properties)
diff --git a/docs/src/rules/no-return-await.md b/docs/src/rules/no-return-await.md
index 2ec7bbe02..a6a411f24 100644
--- a/docs/src/rules/no-return-await.md
+++ b/docs/src/rules/no-return-await.md
@@ -3,6 +3,9 @@ title: no-return-await
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-return-await.md
 rule_type: suggestion
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
+- https://jakearchibald.com/2017/await-vs-return-vs-return-await/
 ---
 
 Disallows unnecessary `return await`.
@@ -60,9 +63,3 @@ There are a few reasons you might want to turn this rule off:
 * If you want to use `await` to denote a value that is a thenable
 * If you do not want the performance benefit of avoiding `return await`
 * If you want the functions to show up in stack traces (useful for debugging purposes)
-
-## Further Reading
-
-[`async function` on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)
-
-[`await vs return vs return await` by Jake Archibald](https://jakearchibald.com/2017/await-vs-return-vs-return-await/)
diff --git a/docs/src/rules/no-script-url.md b/docs/src/rules/no-script-url.md
index 582233cc0..80ce5f7a6 100644
--- a/docs/src/rules/no-script-url.md
+++ b/docs/src/rules/no-script-url.md
@@ -3,6 +3,8 @@ title: no-script-url
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-script-url.md
 rule_type: suggestion
+further_reading:
+- https://stackoverflow.com/questions/13497971/what-is-the-matter-with-script-targeted-urls
 ---
 
 Disallows `javascript:` URLs.
@@ -24,7 +26,3 @@ location.href = `javascript:void(0)`;
 ## Compatibility
 
 * **JSHint**: This rule corresponds to `scripturl` rule of JSHint.
-
-## Further Reading
-
-* [What is the matter with script-targeted URLs?](https://stackoverflow.com/questions/13497971/what-is-the-matter-with-script-targeted-urls)
diff --git a/docs/src/rules/no-setter-return.md b/docs/src/rules/no-setter-return.md
index 527411134..b2dd68ed4 100644
--- a/docs/src/rules/no-setter-return.md
+++ b/docs/src/rules/no-setter-return.md
@@ -3,6 +3,10 @@ title: no-setter-return
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-setter-return.md
 rule_type: problem
+related_rules:
+- getter-return
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set
 ---
 
 <!--RECOMMENDED-->
@@ -100,11 +104,3 @@ Object.defineProperty(foo, "bar", {
     }
 });
 ```
-
-## Related Rules
-
-* [getter-return](getter-return)
-
-## Further Reading
-
-* [MDN setter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set)
diff --git a/docs/src/rules/no-shadow-restricted-names.md b/docs/src/rules/no-shadow-restricted-names.md
index e1c13853a..95a8553ad 100644
--- a/docs/src/rules/no-shadow-restricted-names.md
+++ b/docs/src/rules/no-shadow-restricted-names.md
@@ -3,6 +3,11 @@ title: no-shadow-restricted-names
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-shadow-restricted-names.md
 rule_type: suggestion
+related_rules:
+- no-shadow
+further_reading:
+- https://es5.github.io/#x15.1.1
+- https://es5.github.io/#C
 ---
 
 <!--RECOMMENDED-->
@@ -45,12 +50,3 @@ function f(a, b){}
 // Exception: `undefined` may be shadowed if the variable is never assigned a value.
 var undefined;
 ```
-
-## Related Rules
-
-* [no-shadow](no-shadow)
-
-## Further Reading
-
-* [Annotated ES5 - §15.1.1](https://es5.github.io/#x15.1.1)
-* [Annotated ES5 - Annex C](https://es5.github.io/#C)
diff --git a/docs/src/rules/no-shadow.md b/docs/src/rules/no-shadow.md
index de033113b..fee157c54 100644
--- a/docs/src/rules/no-shadow.md
+++ b/docs/src/rules/no-shadow.md
@@ -3,6 +3,10 @@ title: no-shadow
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-shadow.md
 rule_type: suggestion
+related_rules:
+- no-shadow-restricted-names
+further_reading:
+- https://en.wikipedia.org/wiki/Variable_shadowing
 ---
 
 Disallows variable declarations from shadowing variables declared in the outer scope.
@@ -198,11 +202,3 @@ var y = (y => y)()
 ```
 
 The rationale for callback functions is the assumption that they will be called during the initialization, so that at the time when the shadowing variable will be used, the shadowed variable has not yet been initialized.
-
-## Related Rules
-
-* [no-shadow-restricted-names](no-shadow-restricted-names)
-
-## Further Reading
-
-* [Variable Shadowing](https://en.wikipedia.org/wiki/Variable_shadowing)
diff --git a/docs/src/rules/no-space-before-semi.md b/docs/src/rules/no-space-before-semi.md
index 60c2116fd..627003d3c 100644
--- a/docs/src/rules/no-space-before-semi.md
+++ b/docs/src/rules/no-space-before-semi.md
@@ -3,6 +3,9 @@ title: no-space-before-semi
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-space-before-semi.md
 
+related_rules:
+- semi
+- no-extra-semi
 ---
 
 Disallows spaces before semicolons.
@@ -43,8 +46,3 @@ Examples of **correct** code for this rule:
 
 var foo = "bar";
 ```
-
-## Related Rules
-
-* [semi](semi)
-* [no-extra-semi](no-extra-semi)
diff --git a/docs/src/rules/no-sparse-arrays.md b/docs/src/rules/no-sparse-arrays.md
index 4340619cb..df061c50a 100644
--- a/docs/src/rules/no-sparse-arrays.md
+++ b/docs/src/rules/no-sparse-arrays.md
@@ -3,6 +3,8 @@ title: no-sparse-arrays
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-sparse-arrays.md
 rule_type: problem
+further_reading:
+- https://www.nczonline.net/blog/2007/09/09/inconsistent-array-literals/
 ---
 
 <!--RECOMMENDED-->
@@ -53,7 +55,3 @@ var colors = [ "red", "blue", ];
 ## When Not To Use It
 
 If you want to use sparse arrays, then it is safe to disable this rule.
-
-## Further Reading
-
-* [Inconsistent array literals](https://www.nczonline.net/blog/2007/09/09/inconsistent-array-literals/)
diff --git a/docs/src/rules/no-ternary.md b/docs/src/rules/no-ternary.md
index 280862070..2f9eb38b3 100644
--- a/docs/src/rules/no-ternary.md
+++ b/docs/src/rules/no-ternary.md
@@ -3,6 +3,9 @@ title: no-ternary
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-ternary.md
 rule_type: suggestion
+related_rules:
+- no-nested-ternary
+- no-unneeded-ternary
 ---
 
 Disallows ternary operators.
@@ -50,8 +53,3 @@ function quux() {
     }
 }
 ```
-
-## Related Rules
-
-* [no-nested-ternary](no-nested-ternary)
-* [no-unneeded-ternary](no-unneeded-ternary)
diff --git a/docs/src/rules/no-undef-init.md b/docs/src/rules/no-undef-init.md
index 830d219e8..84908ace7 100644
--- a/docs/src/rules/no-undef-init.md
+++ b/docs/src/rules/no-undef-init.md
@@ -3,6 +3,9 @@ title: no-undef-init
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-undef-init.md
 rule_type: suggestion
+related_rules:
+- no-undefined
+- no-void
 ---
 
 <!--FIXABLE-->
@@ -129,8 +132,3 @@ for (i = 0; i < 10; i++) {
     x = i;
 }
 ```
-
-## Related Rules
-
-* [no-undefined](no-undefined)
-* [no-void](no-void)
diff --git a/docs/src/rules/no-undef.md b/docs/src/rules/no-undef.md
index f3f996779..6f153b94d 100644
--- a/docs/src/rules/no-undef.md
+++ b/docs/src/rules/no-undef.md
@@ -3,6 +3,9 @@ title: no-undef
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-undef.md
 rule_type: problem
+related_rules:
+- no-global-assign
+- no-redeclare
 ---
 
 <!--RECOMMENDED-->
@@ -110,11 +113,6 @@ module.exports = function() {
 
 If explicit declaration of global variables is not to your taste.
 
-## Related Rules
-
-* [no-global-assign](no-global-assign)
-* [no-redeclare](no-redeclare)
-
 ## Compatibility
 
 This rule provides compatibility with treatment of global variables in [JSHint](http://jshint.com/) and [JSLint](http://www.jslint.com).
diff --git a/docs/src/rules/no-undefined.md b/docs/src/rules/no-undefined.md
index 54da12de5..9398312a0 100644
--- a/docs/src/rules/no-undefined.md
+++ b/docs/src/rules/no-undefined.md
@@ -3,6 +3,15 @@ title: no-undefined
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-undefined.md
 rule_type: suggestion
+related_rules:
+- no-undef-init
+- no-void
+- no-shadow-restricted-names
+- no-global-assign
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined
+- https://javascriptweblog.wordpress.com/2010/08/16/understanding-undefined-and-preventing-referenceerrors/
+- https://es5.github.io/#x15.1.1.3
 ---
 
 Disallows the use of `undefined` as an identifier.
@@ -70,16 +79,3 @@ global.undefined = "foo";
 ## When Not To Use It
 
 If you want to allow the use of `undefined` in your code, then you can safely turn this rule off.
-
-## Related Rules
-
-* [no-undef-init](no-undef-init)
-* [no-void](no-void)
-* [no-shadow-restricted-names](no-shadow-restricted-names)
-* [no-global-assign](no-global-assign)
-
-## Further Reading
-
-* [undefined - JavaScript \| MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined)
-* [Understanding JavaScript’s ‘undefined’ \| JavaScript, JavaScript...](https://javascriptweblog.wordpress.com/2010/08/16/understanding-undefined-and-preventing-referenceerrors/)
-* [ECMA262 edition 5.1 &sect;15.1.1.3: undefined](https://es5.github.io/#x15.1.1.3)
diff --git a/docs/src/rules/no-unexpected-multiline.md b/docs/src/rules/no-unexpected-multiline.md
index 18a0b828a..cae4d7450 100644
--- a/docs/src/rules/no-unexpected-multiline.md
+++ b/docs/src/rules/no-unexpected-multiline.md
@@ -3,6 +3,10 @@ title: no-unexpected-multiline
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-unexpected-multiline.md
 rule_type: problem
+related_rules:
+- func-call-spacing
+- semi
+- space-unary-ops
 ---
 
 <!--RECOMMENDED-->
@@ -75,9 +79,3 @@ tag `hello`
 You can turn this rule off if you are confident that you will not accidentally introduce code like this.
 
 Note that the patterns considered problems are **not** flagged by the [semi](semi) rule.
-
-## Related Rules
-
-* [func-call-spacing](func-call-spacing)
-* [semi](semi)
-* [space-unary-ops](space-unary-ops)
diff --git a/docs/src/rules/no-unneeded-ternary.md b/docs/src/rules/no-unneeded-ternary.md
index e1550e9f2..a9a8b540f 100644
--- a/docs/src/rules/no-unneeded-ternary.md
+++ b/docs/src/rules/no-unneeded-ternary.md
@@ -3,6 +3,9 @@ title: no-unneeded-ternary
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-unneeded-ternary.md
 rule_type: suggestion
+related_rules:
+- no-ternary
+- no-nested-ternary
 ---
 
 <!--FIXABLE-->
@@ -93,8 +96,3 @@ Note that `defaultAssignment: false` still allows expressions of the form `x ? e
 ## When Not To Use It
 
 You can turn this rule off if you are not concerned with unnecessary complexity in conditional expressions.
-
-## Related Rules
-
-* [no-ternary](no-ternary)
-* [no-nested-ternary](no-nested-ternary)
diff --git a/docs/src/rules/no-unreachable-loop.md b/docs/src/rules/no-unreachable-loop.md
index c777d4807..8cdd64ee3 100644
--- a/docs/src/rules/no-unreachable-loop.md
+++ b/docs/src/rules/no-unreachable-loop.md
@@ -3,6 +3,11 @@ title: no-unreachable-loop
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-unreachable-loop.md
 rule_type: problem
+related_rules:
+- no-unreachable
+- no-constant-condition
+- no-unmodified-loop-condition
+- for-direction
 ---
 
 Disallows loops with a body that allows only one iteration.
@@ -188,10 +193,3 @@ for (let i = 0; i < 10; i++) {
     }
 }
 ```
-
-## Related Rules
-
-* [no-unreachable](no-unreachable)
-* [no-constant-condition](no-constant-condition)
-* [no-unmodified-loop-condition](no-unmodified-loop-condition)
-* [for-direction](for-direction)
diff --git a/docs/src/rules/no-unused-labels.md b/docs/src/rules/no-unused-labels.md
index ecfeb3c36..6bdc389b4 100644
--- a/docs/src/rules/no-unused-labels.md
+++ b/docs/src/rules/no-unused-labels.md
@@ -3,6 +3,10 @@ title: no-unused-labels
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-unused-labels.md
 rule_type: suggestion
+related_rules:
+- no-extra-label
+- no-labels
+- no-label-var
 ---
 
 <!--RECOMMENDED-->
@@ -71,9 +75,3 @@ for (let i = 0; i < 10; ++i) {
 ## When Not To Use It
 
 If you don't want to be notified about unused labels, then it's safe to disable this rule.
-
-## Related Rules
-
-* [no-extra-label](./no-extra-label)
-* [no-labels](./no-labels)
-* [no-label-var](./no-label-var)
diff --git a/docs/src/rules/no-useless-backreference.md b/docs/src/rules/no-useless-backreference.md
index 4d31b3b31..615287b12 100644
--- a/docs/src/rules/no-useless-backreference.md
+++ b/docs/src/rules/no-useless-backreference.md
@@ -3,6 +3,12 @@ title: no-useless-backreference
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-useless-backreference.md
 rule_type: problem
+related_rules:
+- no-control-regex
+- no-empty-character-class
+- no-invalid-regexp
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
 ---
 
 <!--RECOMMENDED-->
@@ -127,13 +133,3 @@ Examples of additional **correct** code for this rule:
 /^\1$/.test("\x01"); // true. Since the group 1 doesn't exist, \1 is treated as an octal escape sequence.
 /^(a)\1\2$/.test("aa\x02"); // true. In this case, \1 is a backreference, \2 is an octal escape sequence.
 ```
-
-## Related Rules
-
-* [no-control-regex](no-control-regex)
-* [no-empty-character-class](no-empty-character-class)
-* [no-invalid-regexp](no-invalid-regexp)
-
-## Further Reading
-
-* [MDN: Regular Expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions)
diff --git a/docs/src/rules/no-useless-call.md b/docs/src/rules/no-useless-call.md
index 36765cecb..053c2744e 100644
--- a/docs/src/rules/no-useless-call.md
+++ b/docs/src/rules/no-useless-call.md
@@ -3,6 +3,8 @@ title: no-useless-call
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-useless-call.md
 rule_type: suggestion
+related_rules:
+- prefer-spread
 ---
 
 Disallows unnecessary `.call()` and `.apply()`.
@@ -74,7 +76,3 @@ a[++i].foo.call(a[i], 1, 2, 3);
 ## When Not To Use It
 
 If you don't want to be notified about unnecessary `.call()` and `.apply()`, you can safely disable this rule.
-
-## Related Rules
-
-* [prefer-spread](prefer-spread)
diff --git a/docs/src/rules/no-useless-rename.md b/docs/src/rules/no-useless-rename.md
index 58f127c96..ddaafdd0c 100644
--- a/docs/src/rules/no-useless-rename.md
+++ b/docs/src/rules/no-useless-rename.md
@@ -3,6 +3,8 @@ title: no-useless-rename
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-useless-rename.md
 rule_type: suggestion
+related_rules:
+- object-shorthand
 ---
 
 <!--FIXABLE-->
@@ -130,10 +132,6 @@ function foo({ bar: bar }) {}
 
 You can safely disable this rule if you do not care about redundantly renaming import, export, and destructuring assignments.
 
-## Related Rules
-
-* [`object-shorthand`](object-shorthand) which can enforce this behavior for properties in object literals.
-
 ## Compatibility
 
 * **JSCS**: [disallowIdenticalDestructuringNames](https://jscs-dev.github.io/rule/disallowIdenticalDestructuringNames)
diff --git a/docs/src/rules/no-void.md b/docs/src/rules/no-void.md
index 1ff470926..d37beda42 100644
--- a/docs/src/rules/no-void.md
+++ b/docs/src/rules/no-void.md
@@ -3,6 +3,12 @@ title: no-void
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-void.md
 rule_type: suggestion
+related_rules:
+- no-undef-init
+- no-undefined
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void
+- https://oreilly.com/javascript/excerpts/javascript-good-parts/bad-parts.html
 ---
 
 Disallows use of the void operator.
@@ -102,13 +108,3 @@ void someFunction();
 ## When Not To Use It
 
 If you intentionally use the `void` operator then you can disable this rule.
-
-## Related Rules
-
-* [no-undef-init](no-undef-init)
-* [no-undefined](no-undefined)
-
-## Further Reading
-
-* [Mozilla Developer Network](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void)
-* [Bad Parts: Appendix B - JavaScript: The Good Parts by Douglas Crockford](https://oreilly.com/javascript/excerpts/javascript-good-parts/bad-parts.html)
diff --git a/docs/src/rules/no-with.md b/docs/src/rules/no-with.md
index b7fa4d7cb..4e479dee0 100644
--- a/docs/src/rules/no-with.md
+++ b/docs/src/rules/no-with.md
@@ -3,6 +3,8 @@ title: no-with
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/no-with.md
 rule_type: suggestion
+further_reading:
+- https://web.archive.org/web/20200717110117/https://yuiblog.com/blog/2006/04/11/with-statement-considered-harmful/
 ---
 
 <!--RECOMMENDED-->
@@ -39,7 +41,3 @@ const r = ({x, y}) => Math.sqrt(x * x + y * y);
 ## When Not To Use It
 
 If you intentionally use `with` statements then you can disable this rule.
-
-## Further Reading
-
-* [with Statement Considered Harmful](https://yuiblog.com/blog/2006/04/11/with-statement-considered-harmful/)
diff --git a/docs/src/rules/nonblock-statement-body-position.md b/docs/src/rules/nonblock-statement-body-position.md
index 7ae31b5c4..05b2a40ce 100644
--- a/docs/src/rules/nonblock-statement-body-position.md
+++ b/docs/src/rules/nonblock-statement-body-position.md
@@ -3,6 +3,8 @@ title: nonblock-statement-body-position
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/nonblock-statement-body-position.md
 rule_type: layout
+further_reading:
+- https://jscs-dev.github.io/rule/requireNewlineBeforeSingleStatementsInIf
 ---
 
 <!--FIXABLE-->
@@ -161,7 +163,3 @@ while (foo)
 ## When Not To Use It
 
 If you're not concerned about consistent locations of single-line statements, you should not turn on this rule. You can also disable this rule if you're using the `"all"` option for the [`curly`](/docs/rules/curly) rule, because this will disallow single-line statements entirely.
-
-## Further Reading
-
-* JSCS: [requireNewlineBeforeSingleStatementsInIf](https://jscs-dev.github.io/rule/requireNewlineBeforeSingleStatementsInIf)
diff --git a/docs/src/rules/object-curly-newline.md b/docs/src/rules/object-curly-newline.md
index 156a39a54..eadbc7768 100644
--- a/docs/src/rules/object-curly-newline.md
+++ b/docs/src/rules/object-curly-newline.md
@@ -3,6 +3,11 @@ title: object-curly-newline
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/object-curly-newline.md
 rule_type: layout
+related_rules:
+- comma-spacing
+- key-spacing
+- object-curly-spacing
+- object-property-newline
 ---
 
 <!--FIXABLE-->
@@ -543,13 +548,6 @@ export { foo as f, bar } from 'foo-bar';
 
 If you don't want to enforce consistent line breaks after opening and before closing braces, then it's safe to disable this rule.
 
-## Related Rules
-
-* [comma-spacing](comma-spacing)
-* [key-spacing](key-spacing)
-* [object-curly-spacing](object-curly-spacing)
-* [object-property-newline](object-property-newline)
-
 ## Compatibility
 
 * **JSCS**: [requirePaddingNewLinesInObjects](https://jscs-dev.github.io/rule/requirePaddingNewLinesInObjects)
diff --git a/docs/src/rules/object-curly-spacing.md b/docs/src/rules/object-curly-spacing.md
index bf2dceeb5..42f296c2f 100644
--- a/docs/src/rules/object-curly-spacing.md
+++ b/docs/src/rules/object-curly-spacing.md
@@ -3,6 +3,11 @@ title: object-curly-spacing
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/object-curly-spacing.md
 rule_type: layout
+related_rules:
+- array-bracket-spacing
+- comma-spacing
+- computed-property-spacing
+- space-in-parens
 ---
 
 <!--FIXABLE-->
@@ -156,10 +161,3 @@ var obj = { "foo": { "baz": 1, "bar": 2 }};
 ## When Not To Use It
 
 You can turn this rule off if you are not concerned with the consistency of spacing between curly braces.
-
-## Related Rules
-
-* [array-bracket-spacing](array-bracket-spacing)
-* [comma-spacing](comma-spacing)
-* [computed-property-spacing](computed-property-spacing)
-* [space-in-parens](space-in-parens)
diff --git a/docs/src/rules/object-property-newline.md b/docs/src/rules/object-property-newline.md
index 43bc8bd8e..5e0985907 100644
--- a/docs/src/rules/object-property-newline.md
+++ b/docs/src/rules/object-property-newline.md
@@ -3,6 +3,11 @@ title: object-property-newline
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/object-property-newline.md
 rule_type: layout
+related_rules:
+- brace-style
+- comma-dangle
+- key-spacing
+- object-curly-spacing
 ---
 
 <!--FIXABLE-->
@@ -267,13 +272,6 @@ const obj3 = {
 
 You can turn this rule off if you want to decide, case-by-case, whether to place property specifications on separate lines.
 
-## Related Rules
-
-* [brace-style](brace-style)
-* [comma-dangle](comma-dangle)
-* [key-spacing](key-spacing)
-* [object-curly-spacing](object-curly-spacing)
-
 ## Compatibility
 
 * **JSCS**: This rule provides partial compatibility with [requireObjectKeysOnNewLine](https://jscs-dev.github.io/rule/requireObjectKeysOnNewLine).
diff --git a/docs/src/rules/object-shorthand.md b/docs/src/rules/object-shorthand.md
index 24e3dbda9..7ff7c2df6 100644
--- a/docs/src/rules/object-shorthand.md
+++ b/docs/src/rules/object-shorthand.md
@@ -3,6 +3,10 @@ title: object-shorthand
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/object-shorthand.md
 rule_type: suggestion
+related_rules:
+- no-useless-rename
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer
 ---
 
 <!--FIXABLE-->
@@ -250,11 +254,3 @@ var foo = {
 
 Anyone not yet in an ES6 environment would not want to apply this rule. Others may find the terseness of the shorthand
 syntax harder to read and may not want to encourage it with this rule.
-
-## Related Rules
-
-* [`no-useless-rename`](no-useless-rename) which disallows renaming import, export, and destructured assignments to the same name.
-
-## Further Reading
-
-[Object initializer - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer)
diff --git a/docs/src/rules/one-var-declaration-per-line.md b/docs/src/rules/one-var-declaration-per-line.md
index bb6e0d991..0a04ec25f 100644
--- a/docs/src/rules/one-var-declaration-per-line.md
+++ b/docs/src/rules/one-var-declaration-per-line.md
@@ -3,6 +3,8 @@ title: one-var-declaration-per-line
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/one-var-declaration-per-line.md
 rule_type: suggestion
+related_rules:
+- one-var
 ---
 
 <!--FIXABLE-->
@@ -92,7 +94,3 @@ var a,
 let a,
     b = 0;
 ```
-
-## Related Rules
-
-* [one-var](one-var)
diff --git a/docs/src/rules/operator-linebreak.md b/docs/src/rules/operator-linebreak.md
index 085bc3521..34efbc6fc 100644
--- a/docs/src/rules/operator-linebreak.md
+++ b/docs/src/rules/operator-linebreak.md
@@ -3,6 +3,8 @@ title: operator-linebreak
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/operator-linebreak.md
 rule_type: layout
+related_rules:
+- comma-style
 ---
 
 <!--FIXABLE-->
@@ -339,7 +341,3 @@ answer = everything
 ## When Not To Use It
 
 If your project will not be using a common operator line break style, turn this rule off.
-
-## Related Rules
-
-* [comma-style](comma-style)
diff --git a/docs/src/rules/padded-blocks.md b/docs/src/rules/padded-blocks.md
index 731cf49cf..7667c0a02 100644
--- a/docs/src/rules/padded-blocks.md
+++ b/docs/src/rules/padded-blocks.md
@@ -3,6 +3,9 @@ title: padded-blocks
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/padded-blocks.md
 rule_type: layout
+related_rules:
+- lines-between-class-members
+- padding-line-between-statements
 ---
 
 <!--FIXABLE-->
@@ -482,8 +485,3 @@ if (a) {
 ## When Not To Use It
 
 You can turn this rule off if you are not concerned with the consistency of padding within blocks.
-
-## Related Rules
-
-* [lines-between-class-members](lines-between-class-members)
-* [padding-line-between-statements](padding-line-between-statements)
diff --git a/docs/src/rules/prefer-arrow-callback.md b/docs/src/rules/prefer-arrow-callback.md
index e4222dc09..0d0dbd947 100644
--- a/docs/src/rules/prefer-arrow-callback.md
+++ b/docs/src/rules/prefer-arrow-callback.md
@@ -3,6 +3,8 @@ title: prefer-arrow-callback
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/prefer-arrow-callback.md
 rule_type: suggestion
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
 ---
 
 <!--FIXABLE-->
@@ -103,7 +105,3 @@ someArray.map(function(item) { return this.doSomething(item); }, someObject);
 * In environments that have not yet adopted ES6 language features (ES3/5).
 
 * In ES6+ environments that allow the use of function expressions when describing callbacks or function arguments.
-
-## Further Reading
-
-* [More on ES6 arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions)
diff --git a/docs/src/rules/prefer-const.md b/docs/src/rules/prefer-const.md
index 7d652b35a..7bf20de69 100644
--- a/docs/src/rules/prefer-const.md
+++ b/docs/src/rules/prefer-const.md
@@ -3,6 +3,9 @@ title: prefer-const
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/prefer-const.md
 rule_type: suggestion
+related_rules:
+- no-var
+- no-use-before-define
 ---
 
 <!--FIXABLE-->
@@ -222,8 +225,3 @@ function initialize() {
 ## When Not To Use It
 
 If you don't want to be notified about variables that are never reassigned after initial assignment, you can safely disable this rule.
-
-## Related Rules
-
-* [no-var](no-var)
-* [no-use-before-define](no-use-before-define)
diff --git a/docs/src/rules/prefer-destructuring.md b/docs/src/rules/prefer-destructuring.md
index e32be78b4..e49efb2b0 100644
--- a/docs/src/rules/prefer-destructuring.md
+++ b/docs/src/rules/prefer-destructuring.md
@@ -3,6 +3,9 @@ title: prefer-destructuring
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/prefer-destructuring.md
 rule_type: suggestion
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
+- https://2ality.com/2015/01/es6-destructuring.html
 ---
 
 <!--FIXABLE-->
@@ -187,10 +190,3 @@ var $ = require('jquery');
 var foo = $('body')[0];
 var [bar] = $('body'); // fails with a TypeError
 ```
-
-## Further Reading
-
-If you want to learn more about destructuring, check out the links below:
-
-* [Destructuring Assignment (MDN)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)
-* [Destructuring and parameter handling in ECMAScript 6 (2ality blog)](http://2ality.com/2015/01/es6-destructuring.html)
diff --git a/docs/src/rules/prefer-exponentiation-operator.md b/docs/src/rules/prefer-exponentiation-operator.md
index 72b24a574..08759f14f 100644
--- a/docs/src/rules/prefer-exponentiation-operator.md
+++ b/docs/src/rules/prefer-exponentiation-operator.md
@@ -3,6 +3,9 @@ title: prefer-exponentiation-operator
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/prefer-exponentiation-operator.md
 rule_type: suggestion
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Exponentiation
+- https://bugs.chromium.org/p/v8/issues/detail?id=5848
 ---
 
 <!--FIXABLE-->
@@ -48,8 +51,3 @@ let quux = (-1) ** n;
 ## When Not To Use It
 
 This rule should not be used unless ES2016 is supported in your codebase.
-
-## Further Reading
-
-* [MDN Arithmetic Operators - Exponentiation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Exponentiation)
-* [Issue 5848: Exponentiation operator ** has different results for numbers and variables from 50 upwards](https://bugs.chromium.org/p/v8/issues/detail?id=5848)
diff --git a/docs/src/rules/prefer-named-capture-group.md b/docs/src/rules/prefer-named-capture-group.md
index a947a05c2..59f963894 100644
--- a/docs/src/rules/prefer-named-capture-group.md
+++ b/docs/src/rules/prefer-named-capture-group.md
@@ -3,6 +3,8 @@ title: prefer-named-capture-group
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/prefer-named-capture-group.md
 rule_type: suggestion
+related_rules:
+- no-invalid-regexp
 ---
 
 Suggest using named capture group in regular expression.
@@ -50,7 +52,3 @@ foo.exec('bar').groups.id; // Retrieve the group result.
 ## When Not To Use It
 
 If you are targeting ECMAScript 2017 and/or older environments, you should not use this rule, because this ECMAScript feature is only supported in ECMAScript 2018 and/or newer environments.
-
-## Related Rules
-
-* [no-invalid-regexp](./no-invalid-regexp)
diff --git a/docs/src/rules/prefer-object-has-own.md b/docs/src/rules/prefer-object-has-own.md
index 91fdc8e95..3608bc6b3 100644
--- a/docs/src/rules/prefer-object-has-own.md
+++ b/docs/src/rules/prefer-object-has-own.md
@@ -3,6 +3,8 @@ title: prefer-object-has-own
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/prefer-object-has-own.md
 rule_type: suggestion
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn
 ---
 
 <!--FIXABLE-->
@@ -56,7 +58,3 @@ const hasProperty = Object.hasOwn(object, property);
 ## When Not To Use It
 
 This rule should not be used unless ES2022 is supported in your codebase.
-
-## Further Reading
-
-* [Object.hasOwn()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn)
diff --git a/docs/src/rules/prefer-promise-reject-errors.md b/docs/src/rules/prefer-promise-reject-errors.md
index 5c5c56699..6c83c525d 100644
--- a/docs/src/rules/prefer-promise-reject-errors.md
+++ b/docs/src/rules/prefer-promise-reject-errors.md
@@ -3,6 +3,10 @@ title: prefer-promise-reject-errors
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/prefer-promise-reject-errors.md
 rule_type: suggestion
+related_rules:
+- no-throw-literal
+further_reading:
+- http://bluebirdjs.com/docs/warning-explanations.html#warning-a-promise-was-rejected-with-a-non-error
 ---
 
 Requires using Error objects as Promise rejection reasons.
@@ -78,11 +82,3 @@ To avoid conflicts between rules, this rule does not report non-error values use
 ## When Not To Use It
 
 If you're using custom non-error values as Promise rejection reasons, you can turn off this rule.
-
-## Related Rules
-
-* [`no-throw-literal`](no-throw-literal)
-
-## Further Reading
-
-* [Warning: a promise was rejected with a non-error](http://bluebirdjs.com/docs/warning-explanations.html#warning-a-promise-was-rejected-with-a-non-error)
diff --git a/docs/src/rules/prefer-reflect.md b/docs/src/rules/prefer-reflect.md
index 2c25ddd8e..59063fd50 100644
--- a/docs/src/rules/prefer-reflect.md
+++ b/docs/src/rules/prefer-reflect.md
@@ -3,6 +3,10 @@ title: prefer-reflect
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/prefer-reflect.md
 rule_type: suggestion
+related_rules:
+- no-useless-call
+- prefer-spread
+- no-delete-var
 ---
 
 Suggest using Reflect methods where applicable.
@@ -338,9 +342,3 @@ Reflect.deleteProperty(foo, 'bar');
 This rule should not be used in ES3/5 environments.
 
 In ES2015 (ES6) or later, if you don't want to be notified about places where Reflect could be used, you can safely disable this rule.
-
-## Related Rules
-
-* [no-useless-call](no-useless-call)
-* [prefer-spread](prefer-spread)
-* [no-delete-var](no-delete-var)
diff --git a/docs/src/rules/prefer-regex-literals.md b/docs/src/rules/prefer-regex-literals.md
index 7ca6b3275..c88210952 100644
--- a/docs/src/rules/prefer-regex-literals.md
+++ b/docs/src/rules/prefer-regex-literals.md
@@ -3,6 +3,9 @@ title: prefer-regex-literals
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/prefer-regex-literals.md
 rule_type: suggestion
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
 ---
 
 <!--SUGGESTIONS-->
@@ -128,8 +131,3 @@ Examples of `correct` code for `{ "disallowRedundantWrapping": true }`
 
 new RegExp(/abc/, flags);
 ```
-
-## Further Reading
-
-* [MDN: Regular Expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions)
-* [MDN: RegExp Constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
diff --git a/docs/src/rules/prefer-rest-params.md b/docs/src/rules/prefer-rest-params.md
index 5eb69ba96..3583f8371 100644
--- a/docs/src/rules/prefer-rest-params.md
+++ b/docs/src/rules/prefer-rest-params.md
@@ -3,6 +3,8 @@ title: prefer-rest-params
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/prefer-rest-params.md
 rule_type: suggestion
+related_rules:
+- prefer-spread
 ---
 
 Suggests using rest parameters instead of `arguments`.
@@ -66,7 +68,3 @@ function 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 `arguments` variables, then it's safe to disable this rule.
-
-## Related Rules
-
-* [prefer-spread](prefer-spread)
diff --git a/docs/src/rules/prefer-spread.md b/docs/src/rules/prefer-spread.md
index 08c0eae73..f3f1fedce 100644
--- a/docs/src/rules/prefer-spread.md
+++ b/docs/src/rules/prefer-spread.md
@@ -3,6 +3,8 @@ title: prefer-spread
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/prefer-spread.md
 rule_type: suggestion
+related_rules:
+- no-useless-call
 ---
 
 Suggests using spread syntax instead of `.apply()`.
@@ -79,7 +81,3 @@ a[++i].foo.apply(a[i], args);
 This rule should not be used in ES3/5 environments.
 
 In ES2015 (ES6) or later, if you don't want to be notified about `Function.prototype.apply()` callings, you can safely disable this rule.
-
-## Related Rules
-
-* [no-useless-call](no-useless-call)
diff --git a/docs/src/rules/prefer-template.md b/docs/src/rules/prefer-template.md
index f2045ee26..c47f81f1b 100644
--- a/docs/src/rules/prefer-template.md
+++ b/docs/src/rules/prefer-template.md
@@ -3,6 +3,9 @@ title: prefer-template
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/prefer-template.md
 rule_type: suggestion
+related_rules:
+- no-useless-concat
+- quotes
 ---
 
 <!--FIXABLE-->
@@ -55,8 +58,3 @@ var str = "Hello, " + "World!";
 This rule should not be used in ES3/5 environments.
 
 In ES2015 (ES6) or later, if you don't want to be notified about string concatenation, you can safely disable this rule.
-
-## Related Rules
-
-* [no-useless-concat](no-useless-concat)
-* [quotes](quotes)
diff --git a/docs/src/rules/quote-props.md b/docs/src/rules/quote-props.md
index 6761bc0a3..796278f98 100644
--- a/docs/src/rules/quote-props.md
+++ b/docs/src/rules/quote-props.md
@@ -3,6 +3,9 @@ title: quote-props
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/quote-props.md
 rule_type: suggestion
+further_reading:
+- https://kangax.github.io/compat-table/es5/#Reserved_words_as_property_names
+- https://mathiasbynens.be/notes/javascript-properties
 ---
 
 <!--FIXABLE-->
@@ -270,8 +273,3 @@ var x = {
 ## When Not To Use It
 
 If you don't care if property names are consistently wrapped in quotes or not, and you don't target legacy ES3 environments, turn this rule off.
-
-## Further Reading
-
-* [Reserved words as property names](https://kangax.github.io/compat-table/es5/#Reserved_words_as_property_names)
-* [Unquoted property names / object keys in JavaScript](https://mathiasbynens.be/notes/javascript-properties)
diff --git a/docs/src/rules/radix.md b/docs/src/rules/radix.md
index b1adcee93..09a67df4c 100644
--- a/docs/src/rules/radix.md
+++ b/docs/src/rules/radix.md
@@ -3,6 +3,8 @@ title: radix
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/radix.md
 rule_type: suggestion
+further_reading:
+- https://davidwalsh.name/parseint-radix
 ---
 
 <!--SUGGESTIONS-->
@@ -97,7 +99,3 @@ var num = parseFloat(someValue);
 ## When Not To Use It
 
 If you don't want to enforce either presence or omission of the `10` radix value you can turn this rule off.
-
-## Further Reading
-
-* [parseInt and radix](https://davidwalsh.name/parseint-radix)
diff --git a/docs/src/rules/require-await.md b/docs/src/rules/require-await.md
index 0d9362d23..94226b129 100644
--- a/docs/src/rules/require-await.md
+++ b/docs/src/rules/require-await.md
@@ -3,6 +3,8 @@ title: require-await
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/require-await.md
 rule_type: suggestion
+related_rules:
+- require-yield
 ---
 
 Disallows async functions which have no `await` expression.
@@ -87,7 +89,3 @@ fail().catch(error => {
 In this case, the `fail()` function throws an error that is intended to be caught by the `catch()` handler assigned later. Converting the `fail()` function into a synchronous function would require the call to `fail()` to be refactored to use a `try-catch` statement instead of a promise.
 
 If you are throwing an error inside of an asynchronous function for this purpose, then you may want to disable this rule.
-
-## Related Rules
-
-* [require-yield](require-yield)
diff --git a/docs/src/rules/require-jsdoc.md b/docs/src/rules/require-jsdoc.md
index 522566978..8bd899e79 100644
--- a/docs/src/rules/require-jsdoc.md
+++ b/docs/src/rules/require-jsdoc.md
@@ -3,6 +3,8 @@ title: require-jsdoc
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/require-jsdoc.md
 rule_type: suggestion
+related_rules:
+- valid-jsdoc
 ---
 
 Requires JSDoc comments.
@@ -190,7 +192,3 @@ setTimeout(() => {}, 10); // since it's an anonymous arrow function
 ## When Not To Use It
 
 If you do not require JSDoc for your functions, then you can leave this rule off.
-
-## Related Rules
-
-* [valid-jsdoc](valid-jsdoc)
diff --git a/docs/src/rules/require-yield.md b/docs/src/rules/require-yield.md
index 291f438df..08d183c1b 100644
--- a/docs/src/rules/require-yield.md
+++ b/docs/src/rules/require-yield.md
@@ -3,6 +3,8 @@ title: require-yield
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/require-yield.md
 rule_type: suggestion
+related_rules:
+- require-await
 ---
 
 <!--RECOMMENDED-->
@@ -48,7 +50,3 @@ function* foo() { }
 ## When Not To Use It
 
 If you don't want to notify generator functions that have no `yield` expression, then it's safe to disable this rule.
-
-## Related Rules
-
-* [require-await](require-await)
diff --git a/docs/src/rules/rest-spread-spacing.md b/docs/src/rules/rest-spread-spacing.md
index 48c1a7a99..1a9e71f8f 100644
--- a/docs/src/rules/rest-spread-spacing.md
+++ b/docs/src/rules/rest-spread-spacing.md
@@ -3,6 +3,8 @@ title: rest-spread-spacing
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/rest-spread-spacing.md
 rule_type: layout
+further_reading:
+- https://github.com/tc39/proposal-object-rest-spread
 ---
 
 <!--FIXABLE-->
@@ -143,7 +145,3 @@ let n = { x, y, ... z };
 ## When Not To Use It
 
 You can safely disable this rule if you do not care about enforcing consistent spacing between spread operators and their expressions.
-
-## Further Reading
-
-* [Object Rest/Spread Properties for ECMAScript](https://github.com/tc39/proposal-object-rest-spread)
diff --git a/docs/src/rules/semi-spacing.md b/docs/src/rules/semi-spacing.md
index b2d465d4a..8b60e16bf 100644
--- a/docs/src/rules/semi-spacing.md
+++ b/docs/src/rules/semi-spacing.md
@@ -3,6 +3,12 @@ title: semi-spacing
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/semi-spacing.md
 rule_type: layout
+related_rules:
+- semi
+- no-extra-semi
+- comma-spacing
+- block-spacing
+- space-in-parens
 ---
 
 <!--FIXABLE-->
@@ -108,11 +114,3 @@ for (i = 0 ;i < 10 ;i++) {}
 ## When Not To Use It
 
 You can turn this rule off if you are not concerned with the consistency of spacing before or after semicolons.
-
-## Related Rules
-
-* [semi](semi)
-* [no-extra-semi](no-extra-semi)
-* [comma-spacing](comma-spacing)
-* [block-spacing](block-spacing)
-* [space-in-parens](space-in-parens)
diff --git a/docs/src/rules/semi-style.md b/docs/src/rules/semi-style.md
index 1c44bd9ff..0cf379c3b 100644
--- a/docs/src/rules/semi-style.md
+++ b/docs/src/rules/semi-style.md
@@ -3,6 +3,10 @@ title: semi-style
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/semi-style.md
 rule_type: layout
+related_rules:
+- no-extra-semi
+- semi
+- semi-spacing
 ---
 
 <!--FIXABLE-->
@@ -125,9 +129,3 @@ class C {
 ## When Not To Use It
 
 If you don't want to notify the location of semicolons, then it's safe to disable this rule.
-
-## Related Rules
-
-* [no-extra-semi](./no-extra-semi)
-* [semi](./semi)
-* [semi-spacing](./semi-spacing)
diff --git a/docs/src/rules/semi.md b/docs/src/rules/semi.md
index 013b9675a..101479357 100644
--- a/docs/src/rules/semi.md
+++ b/docs/src/rules/semi.md
@@ -3,6 +3,13 @@ title: semi
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/semi.md
 rule_type: layout
+related_rules:
+- no-extra-semi
+- no-unexpected-multiline
+- semi-spacing
+further_reading:
+- https://blog.izs.me/2010/12/an-open-letter-to-javascript-leaders-regarding/
+- https://web.archive.org/web/20200420230322/http://inimino.org/~inimino/blog/javascript_semicolons
 ---
 
 <!--FIXABLE-->
@@ -218,14 +225,3 @@ import a from "a"
 ## When Not To Use It
 
 If you do not want to enforce semicolon usage (or omission) in any particular way, then you can turn this rule off.
-
-## Related Rules
-
-* [no-extra-semi](no-extra-semi)
-* [no-unexpected-multiline](no-unexpected-multiline)
-* [semi-spacing](semi-spacing)
-
-## Further Reading
-
-* [An Open Letter to JavaScript Leaders Regarding Semicolons](http://blog.izs.me/post/2353458699/an-open-letter-to-javascript-leaders-regarding)
-* [JavaScript Semicolon Insertion](http://inimino.org/~inimino/blog/javascript_semicolons)
diff --git a/docs/src/rules/sort-imports.md b/docs/src/rules/sort-imports.md
index 40f7686c8..9f8459fda 100644
--- a/docs/src/rules/sort-imports.md
+++ b/docs/src/rules/sort-imports.md
@@ -3,6 +3,9 @@ title: sort-imports
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/sort-imports.md
 rule_type: suggestion
+related_rules:
+- sort-keys
+- sort-vars
 ---
 
 <!--FIXABLE-->
@@ -292,8 +295,3 @@ Default is `false`.
 ## When Not To Use It
 
 This rule is a formatting preference and not following it won't negatively affect the quality of your code. If alphabetizing imports isn't a part of your coding standards, then you can leave this rule disabled.
-
-## Related Rules
-
-* [sort-keys](sort-keys)
-* [sort-vars](sort-vars)
diff --git a/docs/src/rules/sort-keys.md b/docs/src/rules/sort-keys.md
index f01771336..1a803b19c 100644
--- a/docs/src/rules/sort-keys.md
+++ b/docs/src/rules/sort-keys.md
@@ -3,6 +3,9 @@ title: sort-keys
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/sort-keys.md
 rule_type: suggestion
+related_rules:
+- sort-imports
+- sort-vars
 ---
 
 Requires object keys to be sorted.
@@ -225,11 +228,6 @@ let obj = {
 
 If you don't want to notify about properties' order, then it's safe to disable this rule.
 
-## Related Rules
-
-* [sort-imports](sort-imports)
-* [sort-vars](sort-vars)
-
 ## Compatibility
 
 * **JSCS:** [validateOrderInObjectKeys](https://jscs-dev.github.io/rule/validateOrderInObjectKeys)
diff --git a/docs/src/rules/sort-vars.md b/docs/src/rules/sort-vars.md
index 2cd8f478d..a0282a65f 100644
--- a/docs/src/rules/sort-vars.md
+++ b/docs/src/rules/sort-vars.md
@@ -3,6 +3,9 @@ title: sort-vars
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/sort-vars.md
 rule_type: suggestion
+related_rules:
+- sort-keys
+- sort-imports
 ---
 
 <!--FIXABLE-->
@@ -80,8 +83,3 @@ var a, B, c;
 ## When Not To Use It
 
 This rule is a formatting preference and not following it won't negatively affect the quality of your code. If you alphabetizing variables isn't a part of your coding standards, then you can leave this rule off.
-
-## Related Rules
-
-* [sort-keys](sort-keys)
-* [sort-imports](sort-imports)
diff --git a/docs/src/rules/space-before-blocks.md b/docs/src/rules/space-before-blocks.md
index 755d05762..0af8e7d0b 100644
--- a/docs/src/rules/space-before-blocks.md
+++ b/docs/src/rules/space-before-blocks.md
@@ -3,6 +3,12 @@ title: space-before-blocks
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/space-before-blocks.md
 rule_type: layout
+related_rules:
+- keyword-spacing
+- arrow-spacing
+- switch-colon-spacing
+- block-spacing
+- brace-style
 ---
 
 <!--FIXABLE-->
@@ -218,11 +224,3 @@ class Foo {
 ## When Not To Use It
 
 You can turn this rule off if you are not concerned with the consistency of spacing before blocks.
-
-## Related Rules
-
-* [keyword-spacing](keyword-spacing)
-* [arrow-spacing](arrow-spacing)
-* [switch-colon-spacing](switch-colon-spacing)
-* [block-spacing](block-spacing)
-* [brace-style](brace-style)
diff --git a/docs/src/rules/space-before-function-paren.md b/docs/src/rules/space-before-function-paren.md
index b16fc9548..ad0a20b57 100644
--- a/docs/src/rules/space-before-function-paren.md
+++ b/docs/src/rules/space-before-function-paren.md
@@ -3,6 +3,9 @@ title: space-before-function-paren
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/space-before-function-paren.md
 rule_type: layout
+related_rules:
+- space-after-keywords
+- space-return-throw-case
 ---
 
 <!--FIXABLE-->
@@ -370,8 +373,3 @@ var foo = {
 ## When Not To Use It
 
 You can turn this rule off if you are not concerned with the consistency of spacing before function parenthesis.
-
-## Related Rules
-
-* [space-after-keywords](space-after-keywords)
-* [space-return-throw-case](space-return-throw-case)
diff --git a/docs/src/rules/space-before-function-parentheses.md b/docs/src/rules/space-before-function-parentheses.md
index 376bd1cfe..2bc0e3b74 100644
--- a/docs/src/rules/space-before-function-parentheses.md
+++ b/docs/src/rules/space-before-function-parentheses.md
@@ -3,6 +3,9 @@ title: space-before-function-parentheses
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/space-before-function-parentheses.md
 
+related_rules:
+- space-after-keywords
+- space-return-throw-case
 ---
 
 Enforces consistent spacing before opening parenthesis in function definitions.
@@ -260,8 +263,3 @@ var foo = {
 ## When Not To Use It
 
 You can turn this rule off if you are not concerned with the consistency of spacing before function parenthesis.
-
-## Related Rules
-
-* [space-after-keywords](space-after-keywords)
-* [space-return-throw-case](space-return-throw-case)
diff --git a/docs/src/rules/space-before-keywords.md b/docs/src/rules/space-before-keywords.md
index 1d9384e03..361a26268 100644
--- a/docs/src/rules/space-before-keywords.md
+++ b/docs/src/rules/space-before-keywords.md
@@ -3,6 +3,12 @@ title: space-before-keywords
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/space-before-keywords.md
 
+related_rules:
+- space-after-keywords
+- block-spacing
+- space-return-throw-case
+- space-unary-ops
+- space-infix-ops
 ---
 
 Enforces consistent spacing before keywords.
@@ -111,11 +117,3 @@ try{}catch(e) {}
 ## When Not To Use It
 
 If you do not wish to enforce consistency on keyword spacing.
-
-## Related Rules
-
-* [space-after-keywords](space-after-keywords)
-* [block-spacing](block-spacing)
-* [space-return-throw-case](space-return-throw-case)
-* [space-unary-ops](space-unary-ops)
-* [space-infix-ops](space-infix-ops)
diff --git a/docs/src/rules/space-in-brackets.md b/docs/src/rules/space-in-brackets.md
index d7de26eba..4deafc6dd 100644
--- a/docs/src/rules/space-in-brackets.md
+++ b/docs/src/rules/space-in-brackets.md
@@ -3,6 +3,11 @@ title: space-in-brackets
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/space-in-brackets.md
 
+related_rules:
+- array-bracket-spacing
+- object-curly-spacing
+- space-in-parens
+- computed-property-spacing
 ---
 
 Enforces consistent spacing inside braces of object literals and brackets of array literals.
@@ -305,10 +310,3 @@ var foo = obj[0, 1];
 ## When Not To Use It
 
 You can turn this rule off if you are not concerned with the consistency of spacing between brackets.
-
-## Related Rules
-
-* [array-bracket-spacing](array-bracket-spacing)
-* [object-curly-spacing](object-curly-spacing)
-* [space-in-parens](space-in-parens)
-* [computed-property-spacing](computed-property-spacing)
diff --git a/docs/src/rules/space-in-parens.md b/docs/src/rules/space-in-parens.md
index 7d6f4bc3a..0625410cc 100644
--- a/docs/src/rules/space-in-parens.md
+++ b/docs/src/rules/space-in-parens.md
@@ -3,6 +3,10 @@ title: space-in-parens
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/space-in-parens.md
 rule_type: layout
+related_rules:
+- array-bracket-spacing
+- object-curly-spacing
+- computed-property-spacing
 ---
 
 <!--FIXABLE-->
@@ -293,9 +297,3 @@ foo({bar: 'baz'}, [1, 2]);
 ## When Not To Use It
 
 You can turn this rule off if you are not concerned with the consistency of spacing between parentheses.
-
-## Related Rules
-
-* [array-bracket-spacing](array-bracket-spacing)
-* [object-curly-spacing](object-curly-spacing)
-* [computed-property-spacing](computed-property-spacing)
diff --git a/docs/src/rules/spaced-comment.md b/docs/src/rules/spaced-comment.md
index c99077374..6d9c63982 100644
--- a/docs/src/rules/spaced-comment.md
+++ b/docs/src/rules/spaced-comment.md
@@ -3,6 +3,8 @@ title: spaced-comment
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/spaced-comment.md
 rule_type: suggestion
+related_rules:
+- spaced-line-comment
 ---
 
 <!--FIXABLE-->
@@ -289,7 +291,3 @@ subsequent lines are ignored
 
 /*global ABC*/
 ```
-
-## Related Rules
-
-* [spaced-line-comment](spaced-line-comment)
diff --git a/docs/src/rules/spaced-line-comment.md b/docs/src/rules/spaced-line-comment.md
index e032d05df..ea5247bd6 100644
--- a/docs/src/rules/spaced-line-comment.md
+++ b/docs/src/rules/spaced-line-comment.md
@@ -3,6 +3,8 @@ title: spaced-line-comment
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/spaced-line-comment.md
 
+related_rules:
+- spaced-comment
 ---
 
 Enforces consistent spacing after `//` in line comments.
@@ -73,7 +75,3 @@ var foo = 5;
 // Comment block
 //-+-+-+-+-+-+-+
 ```
-
-## Related Rules
-
-* [spaced-comment](spaced-comment)
diff --git a/docs/src/rules/symbol-description.md b/docs/src/rules/symbol-description.md
index 88eabce8f..d33284a46 100644
--- a/docs/src/rules/symbol-description.md
+++ b/docs/src/rules/symbol-description.md
@@ -3,6 +3,8 @@ title: symbol-description
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/symbol-description.md
 rule_type: suggestion
+further_reading:
+- https://www.ecma-international.org/ecma-262/6.0/#sec-symbol-description
 ---
 
 Requires symbol descriptions.
@@ -58,7 +60,3 @@ var bar = Symbol(someString);
 
 This rule should not be used in ES3/5 environments.
 In addition, this rule can be safely turned off if you don't want to enforce presence of `description` when creating Symbols.
-
-## Further Reading
-
-* [Symbol Objects specification: Symbol description](https://www.ecma-international.org/ecma-262/6.0/#sec-symbol-description)
diff --git a/docs/src/rules/template-tag-spacing.md b/docs/src/rules/template-tag-spacing.md
index e6d6f1896..a2ec2d6d6 100644
--- a/docs/src/rules/template-tag-spacing.md
+++ b/docs/src/rules/template-tag-spacing.md
@@ -3,6 +3,9 @@ title: template-tag-spacing
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/template-tag-spacing.md
 rule_type: layout
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_template_literals
+- https://exploringjs.com/es6/ch_template-literals.html#_examples-of-using-tagged-template-literals
 ---
 
 <!--FIXABLE-->
@@ -76,10 +79,3 @@ func `Hello world`;
 ## When Not To Use It
 
 If you don't want to be notified about usage of spacing between tag functions and their template literals, then it's safe to disable this rule.
-
-## Further Reading
-
-If you want to learn more about tagged template literals, check out the links below:
-
-* [Template literals (MDN)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_template_literals)
-* [Examples of using tagged template literals (Exploring ES6)](http://exploringjs.com/es6/ch_template-literals.html#_examples-of-using-tagged-template-literals)
diff --git a/docs/src/rules/valid-jsdoc.md b/docs/src/rules/valid-jsdoc.md
index 84eb32b0e..c7bed2ef8 100644
--- a/docs/src/rules/valid-jsdoc.md
+++ b/docs/src/rules/valid-jsdoc.md
@@ -3,6 +3,10 @@ title: valid-jsdoc
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/valid-jsdoc.md
 rule_type: suggestion
+related_rules:
+- require-jsdoc
+further_reading:
+- https://jsdoc.app
 ---
 
 <!--FIXABLE-->
@@ -392,11 +396,3 @@ function add(num1, num2) {
 ## When Not To Use It
 
 If you aren't using JSDoc, then you can safely turn this rule off.
-
-## Related Rules
-
-* [require-jsdoc](require-jsdoc)
-
-## Further Reading
-
-* [JSDoc](http://usejsdoc.org)
diff --git a/docs/src/rules/valid-typeof.md b/docs/src/rules/valid-typeof.md
index a37def6f4..b43884fea 100644
--- a/docs/src/rules/valid-typeof.md
+++ b/docs/src/rules/valid-typeof.md
@@ -3,6 +3,8 @@ title: valid-typeof
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/valid-typeof.md
 rule_type: problem
+further_reading:
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof
 ---
 
 <!--RECOMMENDED-->
@@ -72,7 +74,3 @@ typeof bar === typeof qux
 ## When Not To Use It
 
 You may want to turn this rule off if you will be using the `typeof` operator on host objects.
-
-## Further Reading
-
-* [MDN: `typeof` documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof)
diff --git a/docs/src/rules/vars-on-top.md b/docs/src/rules/vars-on-top.md
index 858689e65..39a25c4a4 100644
--- a/docs/src/rules/vars-on-top.md
+++ b/docs/src/rules/vars-on-top.md
@@ -3,6 +3,11 @@ title: vars-on-top
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/vars-on-top.md
 rule_type: suggestion
+further_reading:
+- https://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html
+- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var#var_hoisting
+- https://danhough.com/blog/single-var-pattern-rant/
+- https://benalman.com/news/2012/05/multiple-var-statements-javascript/
 ---
 
 Requires variable declarations to be at the top of their scope.
@@ -128,10 +133,3 @@ function doSomething() {
     var second
 }
 ```
-
-## Further Reading
-
-* [JavaScript Scoping and Hoisting](http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html)
-* [var Hoisting](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var#var_hoisting)
-* [A criticism of the Single Var Pattern in JavaScript, and a simple alternative](http://danielhough.co.uk/blog/single-var-pattern-rant/)
-* [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/)
diff --git a/docs/src/rules/yield-star-spacing.md b/docs/src/rules/yield-star-spacing.md
index dabe6b2a9..f97e00b5d 100644
--- a/docs/src/rules/yield-star-spacing.md
+++ b/docs/src/rules/yield-star-spacing.md
@@ -3,6 +3,8 @@ title: yield-star-spacing
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/yield-star-spacing.md
 rule_type: layout
+further_reading:
+- https://leanpub.com/understandinges6/read/#leanpub-auto-generators
 ---
 
 <!--FIXABLE-->
@@ -97,7 +99,3 @@ function*generator() {
 ## When Not To Use It
 
 If your project will not be using generators or you are not concerned with spacing consistency, you do not need this rule.
-
-## Further Reading
-
-* [Understanding ES6: Generators](https://leanpub.com/understandinges6/read/#leanpub-auto-generators)
diff --git a/docs/src/rules/yoda.md b/docs/src/rules/yoda.md
index 65efff7ec..64ad9f4fb 100644
--- a/docs/src/rules/yoda.md
+++ b/docs/src/rules/yoda.md
@@ -3,6 +3,9 @@ title: yoda
 layout: doc
 edit_link: https://github.com/eslint/eslint/edit/main/docs/src/rules/yoda.md
 rule_type: suggestion
+further_reading:
+- https://en.wikipedia.org/wiki/Yoda_conditions
+- http://thomas.tuerke.net/on/design/?with=1249091668#msg1146181680
 ---
 
 <!--FIXABLE-->
@@ -189,8 +192,3 @@ if (-1 < str.indexOf(substr)) {
     // ...
 }
 ```
-
-## Further Reading
-
-* [Yoda Conditions](https://en.wikipedia.org/wiki/Yoda_conditions)
-* [Yoda Notation and Safe Switching](http://thomas.tuerke.net/on/design/?with=1249091668#msg1146181680)
diff --git a/docs/src/user-guide/configuring/configuration-files.md b/docs/src/user-guide/configuring/configuration-files.md
index 72845aa2f..24a92bb14 100644
--- a/docs/src/user-guide/configuring/configuration-files.md
+++ b/docs/src/user-guide/configuring/configuration-files.md
@@ -156,7 +156,7 @@ your-project
   └── test.js
 ```
 
-If there is an `.eslintrc` and a `package.json` file found in the same directory, `.eslintrc` will take priority and `package.json` file will not be used.
+If there is a `.eslintrc` and a `package.json` file found in the same directory, `.eslintrc` will take priority and `package.json` file will not be used.
 
 By default, ESLint will look for configuration files in all parent folders up to the root directory. This can be useful if you want all of your projects to follow a certain convention, but can sometimes lead to unexpected results. To limit ESLint to a specific project, place `"root": true` inside the `.eslintrc.*` file or `eslintConfig` field of the `package.json` file or in the `.eslintrc.*` file at your project's root level. ESLint will stop looking in parent folders once it finds a configuration with `"root": true`.
 
diff --git a/docs/src/user-guide/configuring/index.md b/docs/src/user-guide/configuring/index.md
index ef0d5e874..76c9ed30a 100644
--- a/docs/src/user-guide/configuring/index.md
+++ b/docs/src/user-guide/configuring/index.md
@@ -13,7 +13,7 @@ eleventyNavigation:
 ESLint is designed to be flexible and configurable for your use case. You can turn off every rule and run only with basic syntax validation or mix and match the bundled rules and your custom rules to fit the needs of your project. There are two primary ways to configure ESLint:
 
 1. **Configuration Comments** - use JavaScript comments to embed configuration information directly into a file.
-1. **Configuration Files** - use a JavaScript, JSON, or YAML file to specify configuration information for an entire directory and all of its subdirectories. This can be in the form of an [`.eslintrc.*`](./configuration-files#configuration-file-formats) file or an `eslintConfig` field in a [`package.json`](https://docs.npmjs.com/files/package.json) file, both of which ESLint will look for and read automatically, or you can specify a configuration file on the [command line](https://eslint.org/docs/user-guide/command-line-interface).
+1. **Configuration Files** - use a JavaScript, JSON, or YAML file to specify configuration information for an entire directory and all of its subdirectories. This can be in the form of a [`.eslintrc.*`](./configuration-files#configuration-file-formats) file or an `eslintConfig` field in a [`package.json`](https://docs.npmjs.com/files/package.json) file, both of which ESLint will look for and read automatically, or you can specify a configuration file on the [command line](https://eslint.org/docs/user-guide/command-line-interface).
 
 Here are some of the options that you can configure in ESLint:
 
diff --git a/lib/cli-engine/cli-engine.js b/lib/cli-engine/cli-engine.js
index f7aa9e224..fdc661988 100644
--- a/lib/cli-engine/cli-engine.js
+++ b/lib/cli-engine/cli-engine.js
@@ -366,9 +366,7 @@ function *iterateRuleDeprecationWarnings(usedConfigArrays) {
 
     // Flatten used configs.
     /** @type {ExtractedConfig[]} */
-    const configs = [].concat(
-        ...usedConfigArrays.map(getUsedExtractedConfigs)
-    );
+    const configs = usedConfigArrays.flatMap(getUsedExtractedConfigs);
 
     // Traverse rule configs.
     for (const config of configs) {
@@ -1023,7 +1021,7 @@ class CLIEngine {
             let formatterPath;
 
             // if there's a slash, then it's a file (TODO: this check seems dubious for scoped npm packages)
-            if (!namespace && normalizedFormatName.indexOf("/") > -1) {
+            if (!namespace && normalizedFormatName.includes("/")) {
                 formatterPath = path.resolve(cwd, normalizedFormatName);
             } else {
                 try {
diff --git a/lib/cli-engine/lint-result-cache.js b/lib/cli-engine/lint-result-cache.js
index 3304d429d..e36eb74ba 100644
--- a/lib/cli-engine/lint-result-cache.js
+++ b/lib/cli-engine/lint-result-cache.js
@@ -36,7 +36,7 @@ const invalidCacheStrategyErrorMessage = `Cache strategy must be one of: ${valid
  */
 function isValidCacheStrategy(cacheStrategy) {
     return (
-        validCacheStrategies.indexOf(cacheStrategy) !== -1
+        validCacheStrategies.includes(cacheStrategy)
     );
 }
 
diff --git a/lib/linter/code-path-analysis/code-path-segment.js b/lib/linter/code-path-analysis/code-path-segment.js
index fad559a2a..eeedd2752 100644
--- a/lib/linter/code-path-analysis/code-path-segment.js
+++ b/lib/linter/code-path-analysis/code-path-segment.js
@@ -100,7 +100,7 @@ class CodePathSegment {
      * @returns {boolean} `true` if the segment is coming from the end of a loop.
      */
     isLoopedPrevSegment(segment) {
-        return this.internal.loopedPrevSegments.indexOf(segment) !== -1;
+        return this.internal.loopedPrevSegments.includes(segment);
     }
 
     /**
diff --git a/lib/linter/code-path-analysis/code-path-state.js b/lib/linter/code-path-analysis/code-path-state.js
index 118f70a17..426da8d49 100644
--- a/lib/linter/code-path-analysis/code-path-state.js
+++ b/lib/linter/code-path-analysis/code-path-state.js
@@ -33,7 +33,7 @@ function addToReturnedOrThrown(dest, others, all, segments) {
         const segment = segments[i];
 
         dest.push(segment);
-        if (others.indexOf(segment) === -1) {
+        if (!others.includes(segment)) {
             all.push(segment);
         }
     }
diff --git a/lib/linter/code-path-analysis/code-path.js b/lib/linter/code-path-analysis/code-path.js
index 0e6662770..a028ca694 100644
--- a/lib/linter/code-path-analysis/code-path.js
+++ b/lib/linter/code-path-analysis/code-path.js
@@ -212,7 +212,7 @@ class CodePath {
                 }
 
                 // Reset the flag of skipping if all branches have been skipped.
-                if (skippedSegment && segment.prevSegments.indexOf(skippedSegment) !== -1) {
+                if (skippedSegment && segment.prevSegments.includes(skippedSegment)) {
                     skippedSegment = null;
                 }
                 visited[segment.id] = true;
diff --git a/lib/rules/accessor-pairs.js b/lib/rules/accessor-pairs.js
index 2fcf15352..33887affe 100644
--- a/lib/rules/accessor-pairs.js
+++ b/lib/rules/accessor-pairs.js
@@ -299,12 +299,12 @@ module.exports = {
          * @private
          */
         function checkPropertyDescriptor(node) {
-            const namesToCheck = node.properties
+            const namesToCheck = new Set(node.properties
                 .filter(p => p.type === "Property" && p.kind === "init" && !p.computed)
-                .map(({ key }) => key.name);
+                .map(({ key }) => key.name));
 
-            const hasGetter = namesToCheck.includes("get");
-            const hasSetter = namesToCheck.includes("set");
+            const hasGetter = namesToCheck.has("get");
+            const hasSetter = namesToCheck.has("set");
 
             if (checkSetWithoutGet && hasSetter && !hasGetter) {
                 report(node, "missingGetter");
diff --git a/lib/rules/callback-return.js b/lib/rules/callback-return.js
index 532faee45..34b74631f 100644
--- a/lib/rules/callback-return.js
+++ b/lib/rules/callback-return.js
@@ -53,7 +53,7 @@ module.exports = {
             if (!node.parent) {
                 return null;
             }
-            if (types.indexOf(node.parent.type) === -1) {
+            if (!types.includes(node.parent.type)) {
                 return findClosestParentOfType(node.parent, types);
             }
             return node.parent;
@@ -87,7 +87,7 @@ module.exports = {
          * @returns {boolean} Whether or not this function matches our callback name.
          */
         function isCallback(node) {
-            return containsOnlyIdentifiers(node.callee) && callbacks.indexOf(sourceCode.getText(node.callee)) > -1;
+            return containsOnlyIdentifiers(node.callee) && callbacks.includes(sourceCode.getText(node.callee));
         }
 
         /**
diff --git a/lib/rules/capitalized-comments.js b/lib/rules/capitalized-comments.js
index 6ea87a70a..dffe95650 100644
--- a/lib/rules/capitalized-comments.js
+++ b/lib/rules/capitalized-comments.js
@@ -185,7 +185,7 @@ module.exports = {
 
             return Boolean(
                 previousTokenOrComment &&
-                ["Block", "Line"].indexOf(previousTokenOrComment.type) !== -1
+                ["Block", "Line"].includes(previousTokenOrComment.type)
             );
         }
 
diff --git a/lib/rules/consistent-this.js b/lib/rules/consistent-this.js
index a613f992d..2aa079146 100644
--- a/lib/rules/consistent-this.js
+++ b/lib/rules/consistent-this.js
@@ -65,7 +65,7 @@ module.exports = {
         function checkAssignment(node, name, value) {
             const isThis = value.type === "ThisExpression";
 
-            if (aliases.indexOf(name) !== -1) {
+            if (aliases.includes(name)) {
                 if (!isThis || node.operator && node.operator !== "=") {
                     reportBadAssignment(node, name);
                 }
diff --git a/lib/rules/dot-notation.js b/lib/rules/dot-notation.js
index 6f6b5166f..90ba6d83b 100644
--- a/lib/rules/dot-notation.js
+++ b/lib/rules/dot-notation.js
@@ -76,7 +76,7 @@ module.exports = {
         function checkComputedProperty(node, value) {
             if (
                 validIdentifier.test(value) &&
-                (allowKeywords || keywords.indexOf(String(value)) === -1) &&
+                (allowKeywords || !keywords.includes(String(value))) &&
                 !(allowPattern && allowPattern.test(value))
             ) {
                 const formattedValue = node.property.type === "Literal" ? JSON.stringify(value) : `\`${value}\``;
@@ -142,7 +142,7 @@ module.exports = {
                     !allowKeywords &&
                     !node.computed &&
                     node.property.type === "Identifier" &&
-                    keywords.indexOf(String(node.property.name)) !== -1
+                    keywords.includes(String(node.property.name))
                 ) {
                     context.report({
                         node: node.property,
diff --git a/lib/rules/global-require.js b/lib/rules/global-require.js
index f00ca9bf6..71a2bf1ac 100644
--- a/lib/rules/global-require.js
+++ b/lib/rules/global-require.js
@@ -6,7 +6,7 @@
 
 "use strict";
 
-const ACCEPTABLE_PARENTS = [
+const ACCEPTABLE_PARENTS = new Set([
     "AssignmentExpression",
     "VariableDeclarator",
     "MemberExpression",
@@ -16,7 +16,7 @@ const ACCEPTABLE_PARENTS = [
     "Program",
     "VariableDeclaration",
     "ChainExpression"
-];
+]);
 
 /**
  * Finds the eslint-scope reference in the given scope.
@@ -75,7 +75,7 @@ module.exports = {
                 const currentScope = context.getScope();
 
                 if (node.callee.name === "require" && !isShadowed(currentScope, node.callee)) {
-                    const isGoodRequire = context.getAncestors().every(parent => ACCEPTABLE_PARENTS.indexOf(parent.type) > -1);
+                    const isGoodRequire = context.getAncestors().every(parent => ACCEPTABLE_PARENTS.has(parent.type));
 
                     if (!isGoodRequire) {
                         context.report({ node, messageId: "unexpected" });
diff --git a/lib/rules/indent-legacy.js b/lib/rules/indent-legacy.js
index bd61f6f84..c5be4a5d1 100644
--- a/lib/rules/indent-legacy.js
+++ b/lib/rules/indent-legacy.js
@@ -753,7 +753,7 @@ module.exports = {
                             if (typeof options.CallExpression.arguments === "number") {
                                 nodeIndent += options.CallExpression.arguments * indentSize;
                             } else if (options.CallExpression.arguments === "first") {
-                                if (parent.arguments.indexOf(node) !== -1) {
+                                if (parent.arguments.includes(node)) {
                                     nodeIndent = parent.arguments[0].loc.start.column;
                                 }
                             } else {
@@ -840,7 +840,7 @@ module.exports = {
                 "IfStatement", "WhileStatement", "ForStatement", "ForInStatement", "ForOfStatement", "DoWhileStatement", "ClassDeclaration", "TryStatement"
             ];
 
-            if (node.parent && statementsWithProperties.indexOf(node.parent.type) !== -1 && isNodeBodyBlock(node)) {
+            if (node.parent && statementsWithProperties.includes(node.parent.type) && isNodeBodyBlock(node)) {
                 indent = getNodeIndent(node.parent).goodChar;
             } else if (node.parent && node.parent.type === "CatchClause") {
                 indent = getNodeIndent(node.parent.parent).goodChar;
diff --git a/lib/rules/indent.js b/lib/rules/indent.js
index 6152bc561..9c534cd58 100644
--- a/lib/rules/indent.js
+++ b/lib/rules/indent.js
@@ -796,7 +796,7 @@ module.exports = {
             let statement = node.parent && node.parent.parent;
 
             while (
-                statement.type === "UnaryExpression" && ["!", "~", "+", "-"].indexOf(statement.operator) > -1 ||
+                statement.type === "UnaryExpression" && ["!", "~", "+", "-"].includes(statement.operator) ||
                 statement.type === "AssignmentExpression" ||
                 statement.type === "LogicalExpression" ||
                 statement.type === "SequenceExpression" ||
diff --git a/lib/rules/jsx-quotes.js b/lib/rules/jsx-quotes.js
index a0cdfaa62..f63dfd608 100644
--- a/lib/rules/jsx-quotes.js
+++ b/lib/rules/jsx-quotes.js
@@ -70,7 +70,7 @@ module.exports = {
          * @public
          */
         function usesExpectedQuotes(node) {
-            return node.value.indexOf(setting.quote) !== -1 || astUtils.isSurroundedBy(node.raw, setting.quote);
+            return node.value.includes(setting.quote) || astUtils.isSurroundedBy(node.raw, setting.quote);
         }
 
         return {
diff --git a/lib/rules/lines-around-comment.js b/lib/rules/lines-around-comment.js
index 74df09b01..6b1cd5f84 100644
--- a/lib/rules/lines-around-comment.js
+++ b/lib/rules/lines-around-comment.js
@@ -141,7 +141,7 @@ module.exports = {
             comments = sourceCode.getAllComments(),
             commentLines = getCommentLineNums(comments),
             emptyLines = getEmptyLineNums(lines),
-            commentAndEmptyLines = commentLines.concat(emptyLines);
+            commentAndEmptyLines = new Set(commentLines.concat(emptyLines));
 
         /**
          * Returns whether or not comments are on lines starting with or ending with code
@@ -393,7 +393,7 @@ module.exports = {
             const nextTokenOrComment = sourceCode.getTokenAfter(token, { includeComments: true });
 
             // check for newline before
-            if (!exceptionStartAllowed && before && !commentAndEmptyLines.includes(prevLineNum) &&
+            if (!exceptionStartAllowed && before && !commentAndEmptyLines.has(prevLineNum) &&
                     !(astUtils.isCommentToken(previousTokenOrComment) && astUtils.isTokenOnSameLine(previousTokenOrComment, token))) {
                 const lineStart = token.range[0] - token.loc.start.column;
                 const range = [lineStart, lineStart];
@@ -408,7 +408,7 @@ module.exports = {
             }
 
             // check for newline after
-            if (!exceptionEndAllowed && after && !commentAndEmptyLines.includes(nextLineNum) &&
+            if (!exceptionEndAllowed && after && !commentAndEmptyLines.has(nextLineNum) &&
                     !(astUtils.isCommentToken(nextTokenOrComment) && astUtils.isTokenOnSameLine(token, nextTokenOrComment))) {
                 context.report({
                     node: token,
diff --git a/lib/rules/max-lines.js b/lib/rules/max-lines.js
index 31fc657a6..772f02aff 100644
--- a/lib/rules/max-lines.js
+++ b/lib/rules/max-lines.js
@@ -159,10 +159,10 @@ module.exports = {
                 if (skipComments) {
                     const comments = sourceCode.getAllComments();
 
-                    const commentLines = comments.flatMap(getLinesWithoutCode);
+                    const commentLines = new Set(comments.flatMap(getLinesWithoutCode));
 
                     lines = lines.filter(
-                        l => !commentLines.includes(l.lineNumber)
+                        l => !commentLines.has(l.lineNumber)
                     );
                 }
 
diff --git a/lib/rules/newline-before-return.js b/lib/rules/newline-before-return.js
index f4aa5166f..d07c23658 100644
--- a/lib/rules/newline-before-return.js
+++ b/lib/rules/newline-before-return.js
@@ -47,7 +47,7 @@ module.exports = {
         function isPrecededByTokens(node, testTokens) {
             const tokenBefore = sourceCode.getTokenBefore(node);
 
-            return testTokens.some(token => tokenBefore.value === token);
+            return testTokens.includes(tokenBefore.value);
         }
 
         /**
diff --git a/lib/rules/no-bitwise.js b/lib/rules/no-bitwise.js
index 1de1619bf..43a1e764e 100644
--- a/lib/rules/no-bitwise.js
+++ b/lib/rules/no-bitwise.js
@@ -76,7 +76,7 @@ module.exports = {
          * @returns {boolean} Whether or not the node has a bitwise operator.
          */
         function hasBitwiseOperator(node) {
-            return BITWISE_OPERATORS.indexOf(node.operator) !== -1;
+            return BITWISE_OPERATORS.includes(node.operator);
         }
 
         /**
@@ -85,7 +85,7 @@ module.exports = {
          * @returns {boolean} Whether or not the node has a bitwise operator.
          */
         function allowedOperator(node) {
-            return allowed.indexOf(node.operator) !== -1;
+            return allowed.includes(node.operator);
         }
 
         /**
diff --git a/lib/rules/no-console.js b/lib/rules/no-console.js
index c0802e23d..464d5647c 100644
--- a/lib/rules/no-console.js
+++ b/lib/rules/no-console.js
@@ -72,7 +72,7 @@ module.exports = {
         function isAllowed(node) {
             const propertyName = astUtils.getStaticPropertyName(node);
 
-            return propertyName && allowed.indexOf(propertyName) !== -1;
+            return propertyName && allowed.includes(propertyName);
         }
 
         /**
diff --git a/lib/rules/no-control-regex.js b/lib/rules/no-control-regex.js
index 04f3449fb..00278f5c1 100644
--- a/lib/rules/no-control-regex.js
+++ b/lib/rules/no-control-regex.js
@@ -30,10 +30,12 @@ const collector = new (class {
         }
     }
 
-    collectControlChars(regexpStr) {
+    collectControlChars(regexpStr, flags) {
+        const uFlag = typeof flags === "string" && flags.includes("u");
+
         try {
             this._source = regexpStr;
-            this._validator.validatePattern(regexpStr); // Call onCharacter hook
+            this._validator.validatePattern(regexpStr, void 0, void 0, uFlag); // Call onCharacter hook
         } catch {
 
             // Ignore syntax errors in RegExp.
@@ -68,13 +70,15 @@ module.exports = {
 
         /**
          * Get the regex expression
-         * @param {ASTNode} node node to evaluate
-         * @returns {RegExp|null} Regex if found else null
+         * @param {ASTNode} node `Literal` node to evaluate
+         * @returns {{ pattern: string, flags: string | null } | null} Regex if found (the given node is either a regex literal
+         * or a string literal that is the pattern argument of a RegExp constructor call). Otherwise `null`. If flags cannot be determined,
+         * the `flags` property will be `null`.
          * @private
          */
-        function getRegExpPattern(node) {
+        function getRegExp(node) {
             if (node.regex) {
-                return node.regex.pattern;
+                return node.regex;
             }
             if (typeof node.value === "string" &&
                 (node.parent.type === "NewExpression" || node.parent.type === "CallExpression") &&
@@ -82,7 +86,15 @@ module.exports = {
                 node.parent.callee.name === "RegExp" &&
                 node.parent.arguments[0] === node
             ) {
-                return node.value;
+                const pattern = node.value;
+                const flags =
+                    node.parent.arguments.length > 1 &&
+                    node.parent.arguments[1].type === "Literal" &&
+                    typeof node.parent.arguments[1].value === "string"
+                        ? node.parent.arguments[1].value
+                        : null;
+
+                return { pattern, flags };
             }
 
             return null;
@@ -90,10 +102,11 @@ module.exports = {
 
         return {
             Literal(node) {
-                const pattern = getRegExpPattern(node);
+                const regExp = getRegExp(node);
 
-                if (pattern) {
-                    const controlCharacters = collector.collectControlChars(pattern);
+                if (regExp) {
+                    const { pattern, flags } = regExp;
+                    const controlCharacters = collector.collectControlChars(pattern, flags);
 
                     if (controlCharacters.length > 0) {
                         context.report({
diff --git a/lib/rules/no-empty-function.js b/lib/rules/no-empty-function.js
index 6e73d63b9..e23b69e53 100644
--- a/lib/rules/no-empty-function.js
+++ b/lib/rules/no-empty-function.js
@@ -144,7 +144,7 @@ module.exports = {
                 filter: astUtils.isCommentToken
             });
 
-            if (allowed.indexOf(kind) === -1 &&
+            if (!allowed.includes(kind) &&
                 node.body.type === "BlockStatement" &&
                 node.body.body.length === 0 &&
                 innerComments.length === 0
diff --git a/lib/rules/no-extra-boolean-cast.js b/lib/rules/no-extra-boolean-cast.js
index f45ac616e..ddb1d8330 100644
--- a/lib/rules/no-extra-boolean-cast.js
+++ b/lib/rules/no-extra-boolean-cast.js
@@ -51,13 +51,13 @@ module.exports = {
         const sourceCode = context.getSourceCode();
 
         // Node types which have a test which will coerce values to booleans.
-        const BOOLEAN_NODE_TYPES = [
+        const BOOLEAN_NODE_TYPES = new Set([
             "IfStatement",
             "DoWhileStatement",
             "WhileStatement",
             "ConditionalExpression",
             "ForStatement"
-        ];
+        ]);
 
         /**
          * Check if a node is a Boolean function or constructor.
@@ -95,7 +95,7 @@ module.exports = {
                 (isBooleanFunctionOrConstructorCall(node.parent) &&
                 node === node.parent.arguments[0]) ||
 
-                (BOOLEAN_NODE_TYPES.indexOf(node.parent.type) !== -1 &&
+                (BOOLEAN_NODE_TYPES.has(node.parent.type) &&
                     node === node.parent.test) ||
 
                 // !<bool>
diff --git a/lib/rules/no-extra-semi.js b/lib/rules/no-extra-semi.js
index 89d9fce57..625310eea 100644
--- a/lib/rules/no-extra-semi.js
+++ b/lib/rules/no-extra-semi.js
@@ -98,7 +98,7 @@ module.exports = {
                         "WithStatement"
                     ];
 
-                if (allowedParentTypes.indexOf(parent.type) === -1) {
+                if (!allowedParentTypes.includes(parent.type)) {
                     report(node);
                 }
             },
diff --git a/lib/rules/no-global-assign.js b/lib/rules/no-global-assign.js
index 340c6c798..1225baec5 100644
--- a/lib/rules/no-global-assign.js
+++ b/lib/rules/no-global-assign.js
@@ -78,7 +78,7 @@ module.exports = {
          * @returns {void}
          */
         function checkVariable(variable) {
-            if (variable.writeable === false && exceptions.indexOf(variable.name) === -1) {
+            if (variable.writeable === false && !exceptions.includes(variable.name)) {
                 variable.references.forEach(checkReference);
             }
         }
diff --git a/lib/rules/no-implicit-coercion.js b/lib/rules/no-implicit-coercion.js
index f646093f5..4b21e3d94 100644
--- a/lib/rules/no-implicit-coercion.js
+++ b/lib/rules/no-implicit-coercion.js
@@ -257,7 +257,7 @@ module.exports = {
                 let operatorAllowed;
 
                 // !!foo
-                operatorAllowed = options.allow.indexOf("!!") >= 0;
+                operatorAllowed = options.allow.includes("!!");
                 if (!operatorAllowed && options.boolean && isDoubleLogicalNegating(node)) {
                     const recommendation = `Boolean(${sourceCode.getText(node.argument.argument)})`;
 
@@ -265,7 +265,7 @@ module.exports = {
                 }
 
                 // ~foo.indexOf(bar)
-                operatorAllowed = options.allow.indexOf("~") >= 0;
+                operatorAllowed = options.allow.includes("~");
                 if (!operatorAllowed && options.boolean && isBinaryNegatingOfIndexOf(node)) {
 
                     // `foo?.indexOf(bar) !== -1` will be true (== found) if the `foo` is nullish. So use `>= 0` in that case.
@@ -276,7 +276,7 @@ module.exports = {
                 }
 
                 // +foo
-                operatorAllowed = options.allow.indexOf("+") >= 0;
+                operatorAllowed = options.allow.includes("+");
                 if (!operatorAllowed && options.number && node.operator === "+" && !isNumeric(node.argument)) {
                     const recommendation = `Number(${sourceCode.getText(node.argument)})`;
 
@@ -289,7 +289,7 @@ module.exports = {
                 let operatorAllowed;
 
                 // 1 * foo
-                operatorAllowed = options.allow.indexOf("*") >= 0;
+                operatorAllowed = options.allow.includes("*");
                 const nonNumericOperand = !operatorAllowed && options.number && isMultiplyByOne(node) && getNonNumericOperand(node);
 
                 if (nonNumericOperand) {
@@ -299,7 +299,7 @@ module.exports = {
                 }
 
                 // "" + foo
-                operatorAllowed = options.allow.indexOf("+") >= 0;
+                operatorAllowed = options.allow.includes("+");
                 if (!operatorAllowed && options.string && isConcatWithEmptyString(node)) {
                     const recommendation = `String(${sourceCode.getText(getNonEmptyOperand(node))})`;
 
@@ -310,7 +310,7 @@ module.exports = {
             AssignmentExpression(node) {
 
                 // foo += ""
-                const operatorAllowed = options.allow.indexOf("+") >= 0;
+                const operatorAllowed = options.allow.includes("+");
 
                 if (!operatorAllowed && options.string && isAppendEmptyString(node)) {
                     const code = sourceCode.getText(getNonEmptyOperand(node));
diff --git a/lib/rules/no-magic-numbers.js b/lib/rules/no-magic-numbers.js
index dfcd593ec..6b4cf77c3 100644
--- a/lib/rules/no-magic-numbers.js
+++ b/lib/rules/no-magic-numbers.js
@@ -80,7 +80,7 @@ module.exports = {
         const config = context.options[0] || {},
             detectObjects = !!config.detectObjects,
             enforceConst = !!config.enforceConst,
-            ignore = (config.ignore || []).map(normalizeIgnoreValue),
+            ignore = new Set((config.ignore || []).map(normalizeIgnoreValue)),
             ignoreArrayIndexes = !!config.ignoreArrayIndexes,
             ignoreDefaultValues = !!config.ignoreDefaultValues;
 
@@ -92,7 +92,7 @@ module.exports = {
          * @returns {boolean} true if the value is ignored
          */
         function isIgnoredValue(value) {
-            return ignore.indexOf(value) !== -1;
+            return ignore.has(value);
         }
 
         /**
@@ -209,7 +209,7 @@ module.exports = {
                         });
                     }
                 } else if (
-                    okTypes.indexOf(parent.type) === -1 ||
+                    !okTypes.includes(parent.type) ||
                     (parent.type === "AssignmentExpression" && parent.left.type === "Identifier")
                 ) {
                     context.report({
diff --git a/lib/rules/no-misleading-character-class.js b/lib/rules/no-misleading-character-class.js
index 94b28784a..d3d9d382a 100644
--- a/lib/rules/no-misleading-character-class.js
+++ b/lib/rules/no-misleading-character-class.js
@@ -4,13 +4,16 @@
 "use strict";
 
 const { CALL, CONSTRUCT, ReferenceTracker, getStringIfConstant } = require("eslint-utils");
-const { RegExpParser, visitRegExpAST } = require("regexpp");
+const { RegExpValidator, RegExpParser, visitRegExpAST } = require("regexpp");
 const { isCombiningCharacter, isEmojiModifier, isRegionalIndicatorSymbol, isSurrogatePair } = require("./utils/unicode");
+const astUtils = require("./utils/ast-utils.js");
 
 //------------------------------------------------------------------------------
 // Helpers
 //------------------------------------------------------------------------------
 
+const REGEXPP_LATEST_ECMA_VERSION = 2022;
+
 /**
  * Iterate character sequences of a given nodes.
  *
@@ -109,6 +112,8 @@ module.exports = {
             url: "https://eslint.org/docs/rules/no-misleading-character-class"
         },
 
+        hasSuggestions: true,
+
         schema: [],
 
         messages: {
@@ -116,10 +121,12 @@ module.exports = {
             combiningClass: "Unexpected combined character in character class.",
             emojiModifier: "Unexpected modified Emoji in character class.",
             regionalIndicatorSymbol: "Unexpected national flag in character class.",
-            zwj: "Unexpected joined character sequence in character class."
+            zwj: "Unexpected joined character sequence in character class.",
+            suggestUnicodeFlag: "Add unicode 'u' flag to regex."
         }
     },
     create(context) {
+        const sourceCode = context.getSourceCode();
         const parser = new RegExpParser();
 
         /**
@@ -127,17 +134,10 @@ module.exports = {
          * @param {Node} node The node to report.
          * @param {string} pattern The regular expression pattern to verify.
          * @param {string} flags The flags of the regular expression.
+         * @param {Function} unicodeFixer Fixer for missing "u" flag.
          * @returns {void}
          */
-        function verify(node, pattern, flags) {
-            const has = {
-                surrogatePairWithoutUFlag: false,
-                combiningClass: false,
-                variationSelector: false,
-                emojiModifier: false,
-                regionalIndicatorSymbol: false,
-                zwj: false
-            };
+        function verify(node, pattern, flags, unicodeFixer) {
             let patternNode;
 
             try {
@@ -153,26 +153,75 @@ module.exports = {
                 return;
             }
 
+            const foundKinds = new Set();
+
             visitRegExpAST(patternNode, {
                 onCharacterClassEnter(ccNode) {
                     for (const chars of iterateCharacterSequence(ccNode.elements)) {
                         for (const kind of kinds) {
-                            has[kind] = has[kind] || hasCharacterSequence[kind](chars);
+                            if (hasCharacterSequence[kind](chars)) {
+                                foundKinds.add(kind);
+                            }
                         }
                     }
                 }
             });
 
-            for (const kind of kinds) {
-                if (has[kind]) {
-                    context.report({ node, messageId: kind });
+            for (const kind of foundKinds) {
+                let suggest;
+
+                if (kind === "surrogatePairWithoutUFlag") {
+                    suggest = [{
+                        messageId: "suggestUnicodeFlag",
+                        fix: unicodeFixer
+                    }];
                 }
+
+                context.report({
+                    node,
+                    messageId: kind,
+                    suggest
+                });
             }
         }
 
+        /**
+         * Checks if the given regular expression pattern would be valid with the `u` flag.
+         * @param {string} pattern The regular expression pattern to verify.
+         * @returns {boolean} `true` if the pattern would be valid with the `u` flag.
+         * `false` if the pattern would be invalid with the `u` flag or the configured
+         * ecmaVersion doesn't support the `u` flag.
+         */
+        function isValidWithUnicodeFlag(pattern) {
+            const { ecmaVersion } = context.parserOptions;
+
+            // ecmaVersion is unknown or it doesn't support the 'u' flag
+            if (typeof ecmaVersion !== "number" || ecmaVersion <= 5) {
+                return false;
+            }
+
+            const validator = new RegExpValidator({
+                ecmaVersion: Math.min(ecmaVersion + 2009, REGEXPP_LATEST_ECMA_VERSION)
+            });
+
+            try {
+                validator.validatePattern(pattern, void 0, void 0, /* uFlag = */ true);
+            } catch {
+                return false;
+            }
+
+            return true;
+        }
+
         return {
             "Literal[regex]"(node) {
-                verify(node, node.regex.pattern, node.regex.flags);
+                verify(node, node.regex.pattern, node.regex.flags, fixer => {
+                    if (!isValidWithUnicodeFlag(node.regex.pattern)) {
+                        return null;
+                    }
+
+                    return fixer.insertTextAfter(node, "u");
+                });
             },
             "Program"() {
                 const scope = context.getScope();
@@ -191,7 +240,31 @@ module.exports = {
                     const flags = getStringIfConstant(flagsNode, scope);
 
                     if (typeof pattern === "string") {
-                        verify(node, pattern, flags || "");
+                        verify(node, pattern, flags || "", fixer => {
+
+                            if (!isValidWithUnicodeFlag(pattern)) {
+                                return null;
+                            }
+
+                            if (node.arguments.length === 1) {
+                                const penultimateToken = sourceCode.getLastToken(node, { skip: 1 }); // skip closing parenthesis
+
+                                return fixer.insertTextAfter(
+                                    penultimateToken,
+                                    astUtils.isCommaToken(penultimateToken)
+                                        ? ' "u",'
+                                        : ', "u"'
+                                );
+                            }
+
+                            if ((flagsNode.type === "Literal" && typeof flagsNode.value === "string") || flagsNode.type === "TemplateLiteral") {
+                                const range = [flagsNode.range[0], flagsNode.range[1] - 1];
+
+                                return fixer.insertTextAfterRange(range, "u");
+                            }
+
+                            return null;
+                        });
                     }
                 }
             }
diff --git a/lib/rules/no-mixed-operators.js b/lib/rules/no-mixed-operators.js
index c75d01147..0cace7ea9 100644
--- a/lib/rules/no-mixed-operators.js
+++ b/lib/rules/no-mixed-operators.js
@@ -64,7 +64,7 @@ function normalizeOptions(options = {}) {
  * @returns {boolean} `true` if such group existed.
  */
 function includesBothInAGroup(groups, left, right) {
-    return groups.some(group => group.indexOf(left) !== -1 && group.indexOf(right) !== -1);
+    return groups.some(group => group.includes(left) && group.includes(right));
 }
 
 /**
diff --git a/lib/rules/no-mixed-requires.js b/lib/rules/no-mixed-requires.js
index 5e05cf7a7..97064243e 100644
--- a/lib/rules/no-mixed-requires.js
+++ b/lib/rules/no-mixed-requires.js
@@ -160,7 +160,7 @@ module.exports = {
                 return REQ_COMPUTED;
             }
 
-            if (BUILTIN_MODULES.indexOf(arg.value) !== -1) {
+            if (BUILTIN_MODULES.includes(arg.value)) {
 
                 // "var fs = require('fs');"
                 return REQ_CORE;
diff --git a/lib/rules/no-multi-spaces.js b/lib/rules/no-multi-spaces.js
index e37aae0a1..6fac7e3ca 100644
--- a/lib/rules/no-multi-spaces.js
+++ b/lib/rules/no-multi-spaces.js
@@ -56,7 +56,7 @@ module.exports = {
         const options = context.options[0] || {};
         const ignoreEOLComments = options.ignoreEOLComments;
         const exceptions = Object.assign({ Property: true }, options.exceptions);
-        const hasExceptions = Object.keys(exceptions).filter(key => exceptions[key]).length > 0;
+        const hasExceptions = Object.keys(exceptions).some(key => exceptions[key]);
 
         /**
          * Formats value of given comment token for error message by truncating its length.
diff --git a/lib/rules/no-native-reassign.js b/lib/rules/no-native-reassign.js
index 1814a5b3c..5f396e404 100644
--- a/lib/rules/no-native-reassign.js
+++ b/lib/rules/no-native-reassign.js
@@ -81,7 +81,7 @@ module.exports = {
          * @returns {void}
          */
         function checkVariable(variable) {
-            if (variable.writeable === false && exceptions.indexOf(variable.name) === -1) {
+            if (variable.writeable === false && !exceptions.includes(variable.name)) {
                 variable.references.forEach(checkReference);
             }
         }
diff --git a/lib/rules/no-new-wrappers.js b/lib/rules/no-new-wrappers.js
index 4667d581f..1fe06c194 100644
--- a/lib/rules/no-new-wrappers.js
+++ b/lib/rules/no-new-wrappers.js
@@ -34,7 +34,7 @@ module.exports = {
             NewExpression(node) {
                 const wrapperObjects = ["String", "Number", "Boolean"];
 
-                if (wrapperObjects.indexOf(node.callee.name) > -1) {
+                if (wrapperObjects.includes(node.callee.name)) {
                     context.report({
                         node,
                         messageId: "noConstructor",
diff --git a/lib/rules/no-prototype-builtins.js b/lib/rules/no-prototype-builtins.js
index ae736981e..dc12669c8 100644
--- a/lib/rules/no-prototype-builtins.js
+++ b/lib/rules/no-prototype-builtins.js
@@ -33,11 +33,11 @@ module.exports = {
     },
 
     create(context) {
-        const DISALLOWED_PROPS = [
+        const DISALLOWED_PROPS = new Set([
             "hasOwnProperty",
             "isPrototypeOf",
             "propertyIsEnumerable"
-        ];
+        ]);
 
         /**
          * Reports if a disallowed property is used in a CallExpression
@@ -54,7 +54,7 @@ module.exports = {
 
             const propName = astUtils.getStaticPropertyName(callee);
 
-            if (propName !== null && DISALLOWED_PROPS.indexOf(propName) > -1) {
+            if (propName !== null && DISALLOWED_PROPS.has(propName)) {
                 context.report({
                     messageId: "prototypeBuildIn",
                     loc: callee.property.loc,
diff --git a/lib/rules/no-shadow.js b/lib/rules/no-shadow.js
index 43d7d738e..b4ef334ef 100644
--- a/lib/rules/no-shadow.js
+++ b/lib/rules/no-shadow.js
@@ -15,8 +15,8 @@ const astUtils = require("./utils/ast-utils");
 // Helpers
 //------------------------------------------------------------------------------
 
-const FUNC_EXPR_NODE_TYPES = ["ArrowFunctionExpression", "FunctionExpression"];
-const CALL_EXPR_NODE_TYPE = ["CallExpression"];
+const FUNC_EXPR_NODE_TYPES = new Set(["ArrowFunctionExpression", "FunctionExpression"]);
+const CALL_EXPR_NODE_TYPE = new Set(["CallExpression"]);
 const FOR_IN_OF_TYPE = /^For(?:In|Of)Statement$/u;
 const SENTINEL_TYPE = /^(?:(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|CatchClause|ImportDeclaration|ExportNamedDeclaration)$/u;
 
@@ -123,7 +123,7 @@ module.exports = {
             const { variableScope } = variable.scope;
 
 
-            if (!(FUNC_EXPR_NODE_TYPES.includes(variableScope.block.type) && getOuterScope(variableScope) === shadowedVariable.scope)) {
+            if (!(FUNC_EXPR_NODE_TYPES.has(variableScope.block.type) && getOuterScope(variableScope) === shadowedVariable.scope)) {
                 return false;
             }
 
@@ -132,7 +132,7 @@ module.exports = {
 
             const callExpression = findSelfOrAncestor(
                 parent,
-                node => CALL_EXPR_NODE_TYPE.includes(node.type)
+                node => CALL_EXPR_NODE_TYPE.has(node.type)
             );
 
             if (!callExpression) {
@@ -173,7 +173,7 @@ module.exports = {
          * @returns {boolean} Whether or not the variable name is allowed.
          */
         function isAllowed(variable) {
-            return options.allow.indexOf(variable.name) !== -1;
+            return options.allow.includes(variable.name);
         }
 
         /**
diff --git a/lib/rules/no-sparse-arrays.js b/lib/rules/no-sparse-arrays.js
index c95bc203c..ff5c2cf99 100644
--- a/lib/rules/no-sparse-arrays.js
+++ b/lib/rules/no-sparse-arrays.js
@@ -37,7 +37,7 @@ module.exports = {
 
             ArrayExpression(node) {
 
-                const emptySpot = node.elements.indexOf(null) > -1;
+                const emptySpot = node.elements.includes(null);
 
                 if (emptySpot) {
                     context.report({ node, messageId: "unexpectedSparseArray" });
diff --git a/lib/rules/no-underscore-dangle.js b/lib/rules/no-underscore-dangle.js
index 55fb9b8cf..a3a9f6500 100644
--- a/lib/rules/no-underscore-dangle.js
+++ b/lib/rules/no-underscore-dangle.js
@@ -86,7 +86,7 @@ module.exports = {
          * @private
          */
         function isAllowed(identifier) {
-            return ALLOWED_VARIABLES.some(ident => ident === identifier);
+            return ALLOWED_VARIABLES.includes(identifier);
         }
 
         /**
diff --git a/lib/rules/no-unused-expressions.js b/lib/rules/no-unused-expressions.js
index 68a7ebd5e..e90099d62 100644
--- a/lib/rules/no-unused-expressions.js
+++ b/lib/rules/no-unused-expressions.js
@@ -124,7 +124,7 @@ module.exports = {
              */
             return (parent.type === "Program" || parent.type === "BlockStatement" &&
                     (/Function/u.test(grandparent.type))) &&
-                    directives(parent).indexOf(node) >= 0;
+                    directives(parent).includes(node);
         }
 
         /**
diff --git a/lib/rules/no-unused-vars.js b/lib/rules/no-unused-vars.js
index 54e33705f..11b1f3722 100644
--- a/lib/rules/no-unused-vars.js
+++ b/lib/rules/no-unused-vars.js
@@ -259,7 +259,7 @@ module.exports = {
             let scope = ref.from;
 
             while (scope) {
-                if (nodes.indexOf(scope.block) >= 0) {
+                if (nodes.includes(scope.block)) {
                     return true;
                 }
 
diff --git a/lib/rules/operator-assignment.js b/lib/rules/operator-assignment.js
index 50cdff9f9..8b9fb5bd7 100644
--- a/lib/rules/operator-assignment.js
+++ b/lib/rules/operator-assignment.js
@@ -22,7 +22,7 @@ const astUtils = require("./utils/ast-utils");
  *     shorthand form.
  */
 function isCommutativeOperatorWithShorthand(operator) {
-    return ["*", "&", "^", "|"].indexOf(operator) >= 0;
+    return ["*", "&", "^", "|"].includes(operator);
 }
 
 /**
@@ -33,7 +33,7 @@ function isCommutativeOperatorWithShorthand(operator) {
  *     a shorthand form.
  */
 function isNonCommutativeOperatorWithShorthand(operator) {
-    return ["+", "-", "/", "%", "<<", ">>", ">>>", "**"].indexOf(operator) >= 0;
+    return ["+", "-", "/", "%", "<<", ">>", ">>>", "**"].includes(operator);
 }
 
 //------------------------------------------------------------------------------
diff --git a/lib/rules/prefer-const.js b/lib/rules/prefer-const.js
index 190860019..cf07d6ce7 100644
--- a/lib/rules/prefer-const.js
+++ b/lib/rules/prefer-const.js
@@ -60,7 +60,7 @@ function canBecomeVariableDeclaration(identifier) {
  */
 function isOuterVariableInDestructing(name, initScope) {
 
-    if (initScope.through.find(ref => ref.resolved && ref.resolved.name === name)) {
+    if (initScope.through.some(ref => ref.resolved && ref.resolved.name === name)) {
         return true;
     }
 
diff --git a/lib/rules/prefer-reflect.js b/lib/rules/prefer-reflect.js
index 1367a1848..377268900 100644
--- a/lib/rules/prefer-reflect.js
+++ b/lib/rules/prefer-reflect.js
@@ -106,7 +106,7 @@ module.exports = {
                 const methodName = (node.callee.property || {}).name;
                 const isReflectCall = (node.callee.object || {}).name === "Reflect";
                 const hasReflectSubstitute = Object.prototype.hasOwnProperty.call(reflectSubstitutes, methodName);
-                const userConfiguredException = exceptions.indexOf(methodName) !== -1;
+                const userConfiguredException = exceptions.includes(methodName);
 
                 if (hasReflectSubstitute && !isReflectCall && !userConfiguredException) {
                     report(node, existingNames[methodName], reflectSubstitutes[methodName]);
@@ -115,7 +115,7 @@ module.exports = {
             UnaryExpression(node) {
                 const isDeleteOperator = node.operator === "delete";
                 const targetsIdentifier = node.argument.type === "Identifier";
-                const userConfiguredException = exceptions.indexOf("delete") !== -1;
+                const userConfiguredException = exceptions.includes("delete");
 
                 if (isDeleteOperator && !targetsIdentifier && !userConfiguredException) {
                     report(node, "the delete keyword", "Reflect.deleteProperty");
diff --git a/lib/rules/prefer-regex-literals.js b/lib/rules/prefer-regex-literals.js
index 158f84b94..aa7258997 100644
--- a/lib/rules/prefer-regex-literals.js
+++ b/lib/rules/prefer-regex-literals.js
@@ -47,7 +47,7 @@ function isStaticTemplateLiteral(node) {
     return node.type === "TemplateLiteral" && node.expressions.length === 0;
 }
 
-const validPrecedingTokens = [
+const validPrecedingTokens = new Set([
     "(",
     ";",
     "[",
@@ -110,7 +110,7 @@ const validPrecedingTokens = [
     "debugger",
     "case",
     "throw"
-];
+]);
 
 
 //------------------------------------------------------------------------------
@@ -334,7 +334,7 @@ module.exports = {
 
                         const tokenBefore = sourceCode.getTokenBefore(node);
 
-                        if (tokenBefore && !validPrecedingTokens.includes(tokenBefore.value)) {
+                        if (tokenBefore && !validPrecedingTokens.has(tokenBefore.value)) {
                             noFix = true;
                         }
 
diff --git a/lib/rules/quote-props.js b/lib/rules/quote-props.js
index e57ad6eea..0b66d761c 100644
--- a/lib/rules/quote-props.js
+++ b/lib/rules/quote-props.js
@@ -95,7 +95,7 @@ module.exports = {
          * @returns {boolean} `true` if it is an ES3 token.
          */
         function isKeyword(tokenStr) {
-            return keywords.indexOf(tokenStr) >= 0;
+            return keywords.includes(tokenStr);
         }
 
         /**
@@ -108,7 +108,7 @@ module.exports = {
          */
         function areQuotesRedundant(rawKey, tokens, skipNumberLiterals) {
             return tokens.length === 1 && tokens[0].start === 0 && tokens[0].end === rawKey.length &&
-                (["Identifier", "Keyword", "Null", "Boolean"].indexOf(tokens[0].type) >= 0 ||
+                (["Identifier", "Keyword", "Null", "Boolean"].includes(tokens[0].type) ||
                 (tokens[0].type === "Numeric" && !skipNumberLiterals && String(+tokens[0].value) === tokens[0].value));
         }
 
diff --git a/lib/rules/quotes.js b/lib/rules/quotes.js
index 41bff3c4e..f1d30a044 100644
--- a/lib/rules/quotes.js
+++ b/lib/rules/quotes.js
@@ -283,7 +283,7 @@ module.exports = {
                         astUtils.isSurroundedBy(rawVal, settings.quote);
 
                     if (!isValid && avoidEscape) {
-                        isValid = astUtils.isSurroundedBy(rawVal, settings.alternateQuote) && rawVal.indexOf(settings.quote) >= 0;
+                        isValid = astUtils.isSurroundedBy(rawVal, settings.alternateQuote) && rawVal.includes(settings.quote);
                     }
 
                     if (!isValid) {
diff --git a/lib/rules/spaced-comment.js b/lib/rules/spaced-comment.js
index 674c13cf3..d858fc47c 100644
--- a/lib/rules/spaced-comment.js
+++ b/lib/rules/spaced-comment.js
@@ -39,7 +39,7 @@ function escapeAndRepeat(s) {
 function parseMarkersOption(markers) {
 
     // `*` is a marker for JSDoc comments.
-    if (markers.indexOf("*") === -1) {
+    if (!markers.includes("*")) {
         return markers.concat("*");
     }
 
diff --git a/lib/rules/valid-jsdoc.js b/lib/rules/valid-jsdoc.js
index c118e033d..8662bf0ea 100644
--- a/lib/rules/valid-jsdoc.js
+++ b/lib/rules/valid-jsdoc.js
@@ -405,7 +405,7 @@ module.exports = {
                             loc: getAbsoluteRange(jsdocNode, param),
                             data: { name: param.name }
                         });
-                    } else if (param.name.indexOf(".") === -1) {
+                    } else if (!param.name.includes(".")) {
                         paramTagsByName[param.name] = param;
                     }
                 });
diff --git a/lib/rules/valid-typeof.js b/lib/rules/valid-typeof.js
index cb85cd9cb..2286d8926 100644
--- a/lib/rules/valid-typeof.js
+++ b/lib/rules/valid-typeof.js
@@ -42,8 +42,8 @@ module.exports = {
 
     create(context) {
 
-        const VALID_TYPES = ["symbol", "undefined", "object", "boolean", "number", "string", "function", "bigint"],
-            OPERATORS = ["==", "===", "!=", "!=="];
+        const VALID_TYPES = new Set(["symbol", "undefined", "object", "boolean", "number", "string", "function", "bigint"]),
+            OPERATORS = new Set(["==", "===", "!=", "!=="]);
 
         const requireStringLiterals = context.options[0] && context.options[0].requireStringLiterals;
 
@@ -85,13 +85,13 @@ module.exports = {
                 if (isTypeofExpression(node)) {
                     const parent = context.getAncestors().pop();
 
-                    if (parent.type === "BinaryExpression" && OPERATORS.indexOf(parent.operator) !== -1) {
+                    if (parent.type === "BinaryExpression" && OPERATORS.has(parent.operator)) {
                         const sibling = parent.left === node ? parent.right : parent.left;
 
                         if (sibling.type === "Literal" || sibling.type === "TemplateLiteral" && !sibling.expressions.length) {
                             const value = sibling.type === "Literal" ? sibling.value : sibling.quasis[0].value.cooked;
 
-                            if (VALID_TYPES.indexOf(value) === -1) {
+                            if (!VALID_TYPES.has(value)) {
                                 context.report({ node: sibling, messageId: "invalidValue" });
                             }
                         } else if (sibling.type === "Identifier" && sibling.name === "undefined" && isReferenceToGlobalVariable(sibling)) {
diff --git a/lib/rules/yoda.js b/lib/rules/yoda.js
index f491f756d..5b64287a1 100644
--- a/lib/rules/yoda.js
+++ b/lib/rules/yoda.js
@@ -39,7 +39,7 @@ function isEqualityOperator(operator) {
  * @returns {boolean} Whether the operator is used in range tests.
  */
 function isRangeTestOperator(operator) {
-    return ["<", "<="].indexOf(operator) >= 0;
+    return ["<", "<="].includes(operator);
 }
 
 /**
diff --git a/package.json b/package.json
index ed5ec404f..380ebf659 100644
--- a/package.json
+++ b/package.json
@@ -65,7 +65,7 @@
     "file-entry-cache": "^6.0.1",
     "functional-red-black-tree": "^1.0.1",
     "glob-parent": "^6.0.1",
-    "globals": "^13.6.0",
+    "globals": "^13.15.0",
     "ignore": "^5.2.0",
     "import-fresh": "^3.0.0",
     "imurmurhash": "^0.1.4",
@@ -96,10 +96,11 @@
     "eslint": "file:.",
     "eslint-config-eslint": "file:packages/eslint-config-eslint",
     "eslint-plugin-eslint-comments": "^3.2.0",
-    "eslint-plugin-eslint-plugin": "^4.0.1",
+    "eslint-plugin-eslint-plugin": "^4.2.0",
     "eslint-plugin-internal-rules": "file:tools/internal-rules",
     "eslint-plugin-jsdoc": "^37.0.0",
     "eslint-plugin-node": "^11.1.0",
+    "eslint-plugin-unicorn": "^42.0.0",
     "eslint-release": "^3.2.0",
     "eslump": "^3.0.0",
     "esprima": "^4.0.1",
diff --git a/packages/eslint-config-eslint/default.yml b/packages/eslint-config-eslint/default.yml
index 1fcbebe75..3985ae298 100644
--- a/packages/eslint-config-eslint/default.yml
+++ b/packages/eslint-config-eslint/default.yml
@@ -4,6 +4,8 @@ extends:
     - "plugin:node/recommended"
     - "plugin:jsdoc/recommended"
     - "plugin:eslint-comments/recommended"
+plugins:
+    - unicorn
 settings:
     jsdoc:
         tagNamePreference:
@@ -279,6 +281,19 @@ rules:
     template-curly-spacing: ["error", "never"]
     template-tag-spacing: "error"
     unicode-bom: "error"
+
+    # Selectively-enable unicorn rules since many of its recommended rules are too aggressive/stylistic for us.
+    unicorn/prefer-array-find: "error"
+    unicorn/prefer-array-flat-map: "error"
+    unicorn/prefer-array-flat: "error"
+    unicorn/prefer-array-index-of: "error"
+    unicorn/prefer-array-some: "error"
+    unicorn/prefer-includes: "error"
+    unicorn/prefer-set-has: "error"
+    unicorn/prefer-string-slice: "error"
+    unicorn/prefer-string-starts-ends-with: "error"
+    unicorn/prefer-string-trim-start-end: "error"
+
     wrap-iife: "error"
     yield-star-spacing: "error"
     yoda: ["error", "never", { exceptRange: true }]
diff --git a/packages/eslint-config-eslint/package.json b/packages/eslint-config-eslint/package.json
index faa9f4a71..e64f65a7a 100644
--- a/packages/eslint-config-eslint/package.json
+++ b/packages/eslint-config-eslint/package.json
@@ -22,7 +22,8 @@
   "peerDependencies": {
     "eslint-plugin-eslint-comments": ">=3.2.0",
     "eslint-plugin-jsdoc": ">=36.0.6",
-    "eslint-plugin-node": ">=11.1.0"
+    "eslint-plugin-node": ">=11.1.0",
+    "eslint-plugin-unicorn": ">=42.0.0"
   },
   "keywords": [
     "eslintconfig",
diff --git a/tools/code-sample-minimizer.js b/tools/code-sample-minimizer.js
index c68f45820..e1eb4995c 100644
--- a/tools/code-sample-minimizer.js
+++ b/tools/code-sample-minimizer.js
@@ -133,10 +133,8 @@ function reduceBadExampleSize({
      * @returns {ASTNode} A descendent of `node` which is also bad
      */
     function extractRelevantChild(node) {
-        const childNodes = [].concat(
-            ...visitorKeys[node.type]
-                .map(key => (Array.isArray(node[key]) ? node[key] : [node[key]]))
-        );
+        const childNodes = visitorKeys[node.type]
+            .flatMap(key => (Array.isArray(node[key]) ? node[key] : [node[key]]));
 
         for (const childNode of childNodes) {
             if (!childNode) {


Test patch:
diff --git a/tests/lib/cli.js b/tests/lib/cli.js
index 59ff0eaf7..d1725c05b 100644
--- a/tests/lib/cli.js
+++ b/tests/lib/cli.js
@@ -552,9 +552,9 @@ describe("cli", () => {
             const call = log.info.getCall(0);
 
             assert.isTrue(log.info.calledOnce);
-            assert.isTrue(call.args[0].indexOf("String!") > -1);
-            assert.isTrue(call.args[0].indexOf("Literal!") > -1);
-            assert.isTrue(call.args[0].indexOf("2 problems") > -1);
+            assert.isTrue(call.args[0].includes("String!"));
+            assert.isTrue(call.args[0].includes("Literal!"));
+            assert.isTrue(call.args[0].includes("2 problems"));
             assert.isTrue(log.info.neverCalledWith(""));
             assert.strictEqual(exit, 1);
         });
diff --git a/tests/lib/linter/node-event-generator.js b/tests/lib/linter/node-event-generator.js
index 44f8e512d..4004f7a28 100644
--- a/tests/lib/linter/node-event-generator.js
+++ b/tests/lib/linter/node-event-generator.js
@@ -126,7 +126,7 @@ describe("NodeEventGenerator", () => {
             it(possibleQueries.join("; "), () => {
                 const ast = espree.parse(sourceText, ESPREE_CONFIG);
                 const emissions = getEmissions(ast, possibleQueries)
-                    .filter(emission => possibleQueries.indexOf(emission[0]) !== -1);
+                    .filter(emission => possibleQueries.includes(emission[0]));
 
                 assert.deepStrictEqual(emissions, expectedEmissions(ast));
             });
@@ -388,7 +388,7 @@ describe("NodeEventGenerator", () => {
         function assertEmissions(ast, visitorKeys, possibleQueries, expectedEmissions) {
             it(possibleQueries.join("; "), () => {
                 const emissions = getEmissions(ast, visitorKeys, possibleQueries)
-                    .filter(emission => possibleQueries.indexOf(emission[0]) !== -1);
+                    .filter(emission => possibleQueries.includes(emission[0]));
 
                 assert.deepStrictEqual(emissions, expectedEmissions(ast));
             });
diff --git a/tests/lib/rule-tester/rule-tester.js b/tests/lib/rule-tester/rule-tester.js
index ae5bee314..ba12bdc38 100644
--- a/tests/lib/rule-tester/rule-tester.js
+++ b/tests/lib/rule-tester/rule-tester.js
@@ -1220,8 +1220,8 @@ describe("RuleTester", () => {
                     },
                     {
                         code: "",
-                        errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: "6" } }],
-                        parserOptions: {}
+                        parserOptions: {},
+                        errors: [{ messageId: "ecmaVersionMessage", data: { type: "number", ecmaVersion: "6" } }]
                     }
                 ]
             });
@@ -1236,8 +1236,8 @@ describe("RuleTester", () => {
                 },
                 {
                     code: "",
-                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "string", ecmaVersion: "latest" } }],
-                    parserOptions: { ecmaVersion: "latest" }
+                    parserOptions: { ecmaVersion: "latest" },
+                    errors: [{ messageId: "ecmaVersionMessage", data: { type: "string", ecmaVersion: "latest" } }]
                 }
             ]
         });
diff --git a/tests/lib/rules/no-control-regex.js b/tests/lib/rules/no-control-regex.js
index 059e50dc4..14abfbce4 100644
--- a/tests/lib/rules/no-control-regex.js
+++ b/tests/lib/rules/no-control-regex.js
@@ -26,7 +26,14 @@ ruleTester.run("no-control-regex", rule, {
         "var regex = RegExp('x1f')",
         "new RegExp('[')",
         "RegExp('[')",
-        "new (function foo(){})('\\x1f')"
+        "new (function foo(){})('\\x1f')",
+        { code: String.raw`/\u{20}/u`, parserOptions: { ecmaVersion: 2015 } },
+        String.raw`/\u{1F}/`,
+        String.raw`/\u{1F}/g`,
+        String.raw`new RegExp("\\u{20}", "u")`,
+        String.raw`new RegExp("\\u{1F}")`,
+        String.raw`new RegExp("\\u{1F}", "g")`,
+        String.raw`new RegExp("\\u{1F}", flags)` // when flags are unknown, this rule assumes there's no `u` flag
     ],
     invalid: [
         { code: String.raw`var regex = /\x1f/`, errors: [{ messageId: "unexpected", data: { controlChars: "\\x1f" }, type: "Literal" }] },
@@ -46,6 +53,38 @@ ruleTester.run("no-control-regex", rule, {
             code: String.raw`var regex = /(?<\u{1d49c}>.)\x1f/`,
             parserOptions: { ecmaVersion: 2020 },
             errors: [{ messageId: "unexpected", data: { controlChars: "\\x1f" }, type: "Literal" }]
+        },
+        {
+            code: String.raw`new RegExp("\\u001F", flags)`,
+            errors: [{ messageId: "unexpected", data: { controlChars: "\\x1f" }, type: "Literal" }]
+        },
+        {
+            code: String.raw`/\u{1111}*\x1F/u`,
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{ messageId: "unexpected", data: { controlChars: "\\x1f" }, type: "Literal" }]
+        },
+        {
+            code: String.raw`new RegExp("\\u{1111}*\\x1F", "u")`,
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{ messageId: "unexpected", data: { controlChars: "\\x1f" }, type: "Literal" }]
+        },
+        {
+            code: String.raw`/\u{1F}/u`,
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{ messageId: "unexpected", data: { controlChars: "\\x1f" }, type: "Literal" }]
+        },
+        {
+            code: String.raw`/\u{1F}/gui`,
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{ messageId: "unexpected", data: { controlChars: "\\x1f" }, type: "Literal" }]
+        },
+        {
+            code: String.raw`new RegExp("\\u{1F}", "u")`,
+            errors: [{ messageId: "unexpected", data: { controlChars: "\\x1f" }, type: "Literal" }]
+        },
+        {
+            code: String.raw`new RegExp("\\u{1F}", "gui")`,
+            errors: [{ messageId: "unexpected", data: { controlChars: "\\x1f" }, type: "Literal" }]
         }
     ]
 });
diff --git a/tests/lib/rules/no-invalid-this.js b/tests/lib/rules/no-invalid-this.js
index b106a706c..dd72be499 100644
--- a/tests/lib/rules/no-invalid-this.js
+++ b/tests/lib/rules/no-invalid-this.js
@@ -86,8 +86,7 @@ function extractPatterns(patterns, type) {
         return thisPattern;
     }));
 
-    // Flatten.
-    return [].concat(...patternsList);
+    return patternsList.flat();
 }
 
 //------------------------------------------------------------------------------
diff --git a/tests/lib/rules/no-misleading-character-class.js b/tests/lib/rules/no-misleading-character-class.js
index a02e5e13e..0aaf34e59 100644
--- a/tests/lib/rules/no-misleading-character-class.js
+++ b/tests/lib/rules/no-misleading-character-class.js
@@ -76,223 +76,549 @@ ruleTester.run("no-misleading-character-class", rule, {
         // RegExp Literals.
         {
             code: "var r = /[👍]/",
-            errors: [{ messageId: "surrogatePairWithoutUFlag" }]
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👍]/u" }]
+            }]
         },
         {
             code: "var r = /[\\uD83D\\uDC4D]/",
-            errors: [{ messageId: "surrogatePairWithoutUFlag" }]
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[\\uD83D\\uDC4D]/u" }]
+            }]
+        },
+        {
+            code: "var r = /[👍]/",
+            parserOptions: { ecmaVersion: 3 },
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: null // ecmaVersion doesn't support the 'u' flag
+            }]
+        },
+        {
+            code: "var r = /[👍]/",
+            parserOptions: { ecmaVersion: 5 },
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: null // ecmaVersion doesn't support the 'u' flag
+            }]
+        },
+        {
+            code: "var r = /[👍]\\a/",
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: null // pattern would be invalid with the 'u' flag
+            }]
+        },
+        {
+            code: "var r = /(?<=[👍])/",
+            parserOptions: { ecmaVersion: 9 },
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /(?<=[👍])/u" }]
+            }]
+        },
+        {
+            code: "var r = /(?<=[👍])/",
+            parserOptions: { ecmaVersion: 2018 },
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /(?<=[👍])/u" }]
+            }]
         },
         {
             code: "var r = /[Á]/",
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: "var r = /[Á]/u",
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: "var r = /[\\u0041\\u0301]/",
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: "var r = /[\\u0041\\u0301]/u",
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: "var r = /[\\u{41}\\u{301}]/u",
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: "var r = /[❇️]/",
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: "var r = /[❇️]/u",
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: "var r = /[\\u2747\\uFE0F]/",
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: "var r = /[\\u2747\\uFE0F]/u",
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: "var r = /[\\u{2747}\\u{FE0F}]/u",
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: "var r = /[👶🏻]/",
-            errors: [{ messageId: "surrogatePairWithoutUFlag" }]
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👶🏻]/u" }]
+            }]
         },
         {
             code: "var r = /[👶🏻]/u",
-            errors: [{ messageId: "emojiModifier" }]
+            errors: [{
+                messageId: "emojiModifier",
+                suggestions: null
+            }]
         },
         {
             code: "var r = /[\\uD83D\\uDC76\\uD83C\\uDFFB]/u",
-            errors: [{ messageId: "emojiModifier" }]
+            errors: [{
+                messageId: "emojiModifier",
+                suggestions: null
+            }]
         },
         {
             code: "var r = /[\\u{1F476}\\u{1F3FB}]/u",
-            errors: [{ messageId: "emojiModifier" }]
+            errors: [{
+                messageId: "emojiModifier",
+                suggestions: null
+            }]
         },
         {
             code: "var r = /[🇯🇵]/",
-            errors: [{ messageId: "surrogatePairWithoutUFlag" }]
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/u" }]
+            }]
+        },
+        {
+            code: "var r = /[🇯🇵]/i",
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[🇯🇵]/iu" }]
+            }]
         },
         {
             code: "var r = /[🇯🇵]/u",
-            errors: [{ messageId: "regionalIndicatorSymbol" }]
+            errors: [{
+                messageId: "regionalIndicatorSymbol",
+                suggestions: null
+            }]
         },
         {
             code: "var r = /[\\uD83C\\uDDEF\\uD83C\\uDDF5]/u",
-            errors: [{ messageId: "regionalIndicatorSymbol" }]
+            errors: [{
+                messageId: "regionalIndicatorSymbol",
+                suggestions: null
+            }]
         },
         {
             code: "var r = /[\\u{1F1EF}\\u{1F1F5}]/u",
-            errors: [{ messageId: "regionalIndicatorSymbol" }]
+            errors: [{
+                messageId: "regionalIndicatorSymbol",
+                suggestions: null
+            }]
         },
         {
             code: "var r = /[👨‍👩‍👦]/",
             errors: [
-                { messageId: "surrogatePairWithoutUFlag" },
-                { messageId: "zwj" }
+                {
+                    messageId: "surrogatePairWithoutUFlag",
+                    suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = /[👨‍👩‍👦]/u" }]
+                },
+                {
+                    messageId: "zwj",
+                    suggestions: null
+                }
             ]
         },
         {
             code: "var r = /[👨‍👩‍👦]/u",
-            errors: [{ messageId: "zwj" }]
+            errors: [{
+                messageId: "zwj",
+                suggestions: null
+            }]
         },
         {
             code: "var r = /[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]/u",
-            errors: [{ messageId: "zwj" }]
+            errors: [{
+                messageId: "zwj",
+                suggestions: null
+            }]
         },
         {
             code: "var r = /[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]/u",
-            errors: [{ messageId: "zwj" }]
+            errors: [{
+                messageId: "zwj",
+                suggestions: null
+            }]
         },
 
         // RegExp constructors.
         {
             code: String.raw`var r = new RegExp("[👍]", "")`,
-            errors: [{ messageId: "surrogatePairWithoutUFlag" }]
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👍]", "u")` }]
+            }]
+        },
+        {
+            code: "var r = new RegExp('[👍]', ``)",
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[👍]', `u`)" }]
+            }]
+        },
+        {
+            code: String.raw`var r = new RegExp("[👍]", flags)`,
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: null
+            }]
+        },
+        {
+            code: String.raw`const flags = ""; var r = new RegExp("[👍]", flags)`,
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "")`,
-            errors: [{ messageId: "surrogatePairWithoutUFlag" }]
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[\\uD83D\\uDC4D]", "u")` }]
+            }]
+        },
+        {
+            code: String.raw`var r = new RegExp("[👍]", "")`,
+            parserOptions: { ecmaVersion: 3 },
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: null // ecmaVersion doesn't support the 'u' flag
+            }]
+        },
+        {
+            code: String.raw`var r = new RegExp("[👍]", "")`,
+            parserOptions: { ecmaVersion: 5 },
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: null // ecmaVersion doesn't support the 'u' flag
+            }]
+        },
+        {
+            code: String.raw`var r = new RegExp("[👍]\\a", "")`,
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: null // pattern would be invalid with the 'u' flag
+            }]
+        },
+        {
+            code: String.raw`var r = new RegExp("/(?<=[👍])", "")`,
+            parserOptions: { ecmaVersion: 9 },
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("/(?<=[👍])", "u")` }]
+            }]
+        },
+        {
+            code: String.raw`var r = new RegExp("/(?<=[👍])", "")`,
+            parserOptions: { ecmaVersion: 2018 },
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("/(?<=[👍])", "u")` }]
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[Á]", "")`,
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[Á]", "u")`,
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "")`,
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[\\u0041\\u0301]", "u")`,
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[\\u{41}\\u{301}]", "u")`,
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[❇️]", "")`,
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[❇️]", "u")`,
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "")`,
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[\\u2747\\uFE0F]", "u")`,
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[\\u{2747}\\u{FE0F}]", "u")`,
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[👶🏻]", "")`,
-            errors: [{ messageId: "surrogatePairWithoutUFlag" }]
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👶🏻]", "u")` }]
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[👶🏻]", "u")`,
-            errors: [{ messageId: "emojiModifier" }]
+            errors: [{
+                messageId: "emojiModifier",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[\\uD83D\\uDC76\\uD83C\\uDFFB]", "u")`,
-            errors: [{ messageId: "emojiModifier" }]
+            errors: [{
+                messageId: "emojiModifier",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[\\u{1F476}\\u{1F3FB}]", "u")`,
-            errors: [{ messageId: "emojiModifier" }]
+            errors: [{
+                messageId: "emojiModifier",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[🇯🇵]", "")`,
-            errors: [{ messageId: "surrogatePairWithoutUFlag" }]
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u")` }]
+            }]
+        },
+        {
+            code: String.raw`var r = new RegExp("[🇯🇵]", "i")`,
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "iu")` }]
+            }]
+        },
+        {
+            code: "var r = new RegExp('[🇯🇵]', `i`)",
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[🇯🇵]', `iu`)" }]
+            }]
+        },
+        {
+            code: "var r = new RegExp('[🇯🇵]', `${foo}`)",
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: "var r = new RegExp('[🇯🇵]', `${foo}u`)" }]
+            }]
+        },
+        {
+            code: String.raw`var r = new RegExp("[🇯🇵]")`,
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u")` }]
+            }]
+        },
+        {
+            code: String.raw`var r = new RegExp("[🇯🇵]",)`,
+            parserOptions: { ecmaVersion: 2017 },
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[🇯🇵]", "u",)` }]
+            }]
+        },
+        {
+            code: String.raw`var r = new RegExp(("[🇯🇵]"))`,
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp(("[🇯🇵]"), "u")` }]
+            }]
+        },
+        {
+            code: String.raw`var r = new RegExp((("[🇯🇵]")))`,
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp((("[🇯🇵]")), "u")` }]
+            }]
+        },
+        {
+            code: String.raw`var r = new RegExp(("[🇯🇵]"),)`,
+            parserOptions: { ecmaVersion: 2017 },
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp(("[🇯🇵]"), "u",)` }]
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[🇯🇵]", "u")`,
-            errors: [{ messageId: "regionalIndicatorSymbol" }]
+            errors: [{
+                messageId: "regionalIndicatorSymbol",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[\\uD83C\\uDDEF\\uD83C\\uDDF5]", "u")`,
-            errors: [{ messageId: "regionalIndicatorSymbol" }]
+            errors: [{
+                messageId: "regionalIndicatorSymbol",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[\\u{1F1EF}\\u{1F1F5}]", "u")`,
-            errors: [{ messageId: "regionalIndicatorSymbol" }]
+            errors: [{
+                messageId: "regionalIndicatorSymbol",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[👨‍👩‍👦]", "")`,
             errors: [
-                { messageId: "surrogatePairWithoutUFlag" },
-                { messageId: "zwj" }
+                {
+                    messageId: "surrogatePairWithoutUFlag",
+                    suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new RegExp("[👨‍👩‍👦]", "u")` }]
+                },
+                {
+                    messageId: "zwj",
+                    suggestions: null
+                }
             ]
         },
         {
             code: String.raw`var r = new RegExp("[👨‍👩‍👦]", "u")`,
-            errors: [{ messageId: "zwj" }]
+            errors: [{
+                messageId: "zwj",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[\\uD83D\\uDC68\\u200D\\uD83D\\uDC69\\u200D\\uD83D\\uDC66]", "u")`,
-            errors: [{ messageId: "zwj" }]
+            errors: [{
+                messageId: "zwj",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`,
-            errors: [{ messageId: "zwj" }]
+            errors: [{
+                messageId: "zwj",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new globalThis.RegExp("[❇️]", "")`,
             env: { es2020: true },
-            errors: [{ messageId: "combiningClass" }]
+            errors: [{
+                messageId: "combiningClass",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new globalThis.RegExp("[👶🏻]", "u")`,
             env: { es2020: true },
-            errors: [{ messageId: "emojiModifier" }]
+            errors: [{
+                messageId: "emojiModifier",
+                suggestions: null
+            }]
         },
         {
             code: String.raw`var r = new globalThis.RegExp("[🇯🇵]", "")`,
             env: { es2020: true },
-            errors: [{ messageId: "surrogatePairWithoutUFlag" }]
+            errors: [{
+                messageId: "surrogatePairWithoutUFlag",
+                suggestions: [{ messageId: "suggestUnicodeFlag", output: String.raw`var r = new globalThis.RegExp("[🇯🇵]", "u")` }]
+            }]
         },
         {
             code: String.raw`var r = new globalThis.RegExp("[\\u{1F468}\\u{200D}\\u{1F469}\\u{200D}\\u{1F466}]", "u")`,
             env: { es2020: true },
-            errors: [{ messageId: "zwj" }]
+            errors: [{
+                messageId: "zwj",
+                suggestions: null
+            }]
         }
     ]
 });