diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4b630a88f14..5f8b968a550 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -33,6 +33,12 @@ jobs:
run: npm run lint:scss
- name: Lint Docs JS Files
run: node Makefile lintDocsJS
+ - name: Build Docs Website
+ working-directory: docs
+ run: npm run build
+ - name: Validate internal links
+ working-directory: docs
+ run: npm run lint:links
test_on_node:
name: Test
diff --git a/conf/rule-type-list.json b/conf/rule-type-list.json
index 517f8219166..d5823acc898 100644
--- a/conf/rule-type-list.json
+++ b/conf/rule-type-list.json
@@ -6,12 +6,12 @@
],
"deprecated": {
"name": "Deprecated",
- "description": "These rules have been deprecated in accordance with the deprecation policy, and replaced by newer rules:",
+ "description": "These rules have been deprecated in accordance with the deprecation policy, and replaced by newer rules:",
"rules": []
},
"removed": {
"name": "Removed",
- "description": "These rules from older versions of ESLint (before the deprecation policy existed) have been replaced by newer rules:",
+ "description": "These rules from older versions of ESLint (before the deprecation policy existed) have been replaced by newer rules:",
"rules": [
{ "removed": "generator-star", "replacedBy": ["generator-star-spacing"] },
{ "removed": "global-strict", "replacedBy": ["strict"] },
diff --git a/docs/package.json b/docs/package.json
index 6dfed6dfb25..785689e424c 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -19,6 +19,7 @@
"start": "npm-run-all build:sass build:postcss --parallel watch:*",
"build": "npm-run-all build:sass build:postcss build:eleventy images",
"lint:scss": "stylelint \"**/*.{scss,html}\"",
+ "lint:links": "cross-env NODE_OPTIONS=--max-old-space-size=4096 node tools/validate-links.js",
"lint:fix:scss": "npm run lint:scss -- --fix"
},
"devDependencies": {
@@ -27,15 +28,18 @@
"@11ty/eleventy-navigation": "^0.3.2",
"@11ty/eleventy-plugin-rss": "^1.1.1",
"@11ty/eleventy-plugin-syntaxhighlight": "^3.1.2",
+ "@munter/tap-render": "^0.2.0",
"@types/markdown-it": "^12.2.3",
"algoliasearch": "^4.12.1",
"autoprefixer": "^10.4.13",
+ "cross-env": "^7.0.3",
"cssnano": "^5.1.14",
"dom-parser": "^0.1.6",
"eleventy-plugin-nesting-toc": "^1.3.0",
"eleventy-plugin-page-assets": "^0.3.0",
"eleventy-plugin-reading-time": "^0.0.1",
"github-slugger": "^1.5.0",
+ "hyperlink": "^5.0.4",
"imagemin": "^8.0.1",
"imagemin-cli": "^7.0.0",
"js-yaml": "^3.14.1",
@@ -53,7 +57,8 @@
"stylelint": "^14.13.0",
"stylelint-config-html": "^1.1.0",
"stylelint-config-standard": "^29.0.0",
- "stylelint-config-standard-scss": "^5.0.0"
+ "stylelint-config-standard-scss": "^5.0.0",
+ "tap-spot": "^1.1.2"
},
"engines": {
"node": ">=14.0.0"
diff --git a/docs/src/_data/rules.json b/docs/src/_data/rules.json
index 5b9d2e21ff3..27280e44935 100644
--- a/docs/src/_data/rules.json
+++ b/docs/src/_data/rules.json
@@ -1893,7 +1893,7 @@
],
"deprecated": {
"name": "Deprecated",
- "description": "These rules have been deprecated in accordance with the deprecation policy, and replaced by newer rules:",
+ "description": "These rules have been deprecated in accordance with the deprecation policy, and replaced by newer rules:",
"rules": [
{
"name": "callback-return",
@@ -2009,7 +2009,7 @@
},
"removed": {
"name": "Removed",
- "description": "These rules from older versions of ESLint (before the deprecation policy existed) have been replaced by newer rules:",
+ "description": "These rules from older versions of ESLint (before the deprecation policy existed) have been replaced by newer rules:",
"rules": [
{
"removed": "generator-star",
diff --git a/docs/src/extend/custom-rules-deprecated.md b/docs/src/extend/custom-rules-deprecated.md
index 0742233bbab..49faa4f13c7 100644
--- a/docs/src/extend/custom-rules-deprecated.md
+++ b/docs/src/extend/custom-rules-deprecated.md
@@ -37,7 +37,7 @@ module.exports.schema = []; // no options
## Rule Basics
-`schema` (array) specifies the [options](#options-schemas) so ESLint can prevent invalid [rule configurations](../use/configure/rules#configuring-rules)
+`schema` (array) specifies the [options](#options-schemas) so ESLint can prevent invalid [rule configurations](../use/configure/rules)
`create` (function) returns an object with methods that ESLint calls to "visit" nodes while traversing the abstract syntax tree (AST as defined by [ESTree](https://github.com/estree/estree)) of JavaScript code:
diff --git a/docs/src/extend/custom-rules.md b/docs/src/extend/custom-rules.md
index 9c57dd1a122..0118757c6a4 100644
--- a/docs/src/extend/custom-rules.md
+++ b/docs/src/extend/custom-rules.md
@@ -61,7 +61,7 @@ The source file for a rule exports an object with the following properties.
**Important:** the `hasSuggestions` property is mandatory for rules that provide suggestions. If this property isn't set to `true`, ESLint will throw an error whenever the rule attempts to produce a suggestion. Omit the `hasSuggestions` property if the rule does not provide suggestions.
-* `schema` (array) specifies the [options](#options-schemas) so ESLint can prevent invalid [rule configurations](../use/configure/rules#configuring-rules)
+* `schema` (array) specifies the [options](#options-schemas) so ESLint can prevent invalid [rule configurations](../use/configure/rules)
* `deprecated` (boolean) indicates whether the rule has been deprecated. You may omit the `deprecated` property if the rule has not been deprecated.
@@ -110,7 +110,7 @@ The `context` object contains additional functionality that is helpful for rules
* `parserOptions` - the parser options configured for this run (more details [here](../use/configure/language-options#specifying-parser-options)).
* `id` - the rule ID.
-* `options` - an array of the [configured options](../use/configure/rules#configuring-rules) for this rule. This array does not include the rule severity. For more information, see [here](#contextoptions).
+* `options` - an array of the [configured options](../use/configure/rules) for this rule. This array does not include the rule severity. For more information, see [here](#contextoptions).
* `settings` - the [shared settings](../use/configure/configuration-files#adding-shared-settings) from configuration.
* `parserPath` - the name of the `parser` from configuration.
* `parserServices` - an object containing parser-provided services for rules. The default parser does not provide any services. However, if a rule is intended to be used with a custom parser, it could use `parserServices` to access anything provided by that parser. (For example, a TypeScript parser could provide the ability to get the computed type of a given node.)
diff --git a/docs/src/use/configure/index.md b/docs/src/use/configure/index.md
index 44f84aa4291..e228383c944 100644
--- a/docs/src/use/configure/index.md
+++ b/docs/src/use/configure/index.md
@@ -42,7 +42,7 @@ All of these options give you fine-grained control over how ESLint treats your c
[**Configure Rules**](rules)
-* [Configuring Rules](./rules#configuring-rules)
+* [Configuring Rules](./rules)
* [Disabling Rules](./rules#disabling-rules)
[**Configure Plugins**](plugins)
diff --git a/docs/tools/validate-links.js b/docs/tools/validate-links.js
new file mode 100644
index 00000000000..5c1ca43578d
--- /dev/null
+++ b/docs/tools/validate-links.js
@@ -0,0 +1,49 @@
+const path = require("path");
+const TapRender = require("@munter/tap-render");
+const spot = require("tap-spot");
+const hyperlink = require("hyperlink");
+
+const tapRenderInstance = new TapRender();
+
+tapRenderInstance.pipe(spot()).pipe(process.stdout);
+
+const skipPatterns = [
+ "https://",
+ "fragment-redirect",
+ "migrating-to",
+ "/blog",
+ "/play",
+ "/team",
+ "/donate",
+ "/docs/latest",
+ `src="null"`,
+];
+
+const skipFilter = (report) =>
+ Object.values(report).some((value) =>
+ skipPatterns.some((pattern) => String(value).includes(pattern))
+ );
+
+(async () => {
+ try {
+ await hyperlink(
+ {
+ inputUrls: ["../_site/index.html"],
+ root: path.resolve(__dirname, "../_site"),
+ canonicalRoot: "https://eslint.org/docs/head/",
+ recursive: true,
+ internalOnly: true,
+ pretty: true,
+ concurrency: 25,
+ skipFilter,
+ },
+ tapRenderInstance
+ );
+ } catch (err) {
+ console.log(err.stack);
+ process.exit(1);
+ }
+ const results = tapRenderInstance.close();
+
+ process.exit(results.fail ? 1 : 0);
+})();