Skip to content

Commit

Permalink
Add rule MD059/descriptive-link-text "Link text should be descriptive" (
Browse files Browse the repository at this point in the history
fixes #681).
  • Loading branch information
khiga8 authored and DavidAnson committed Feb 10, 2025
1 parent a4c553a commit 27d39b7
Show file tree
Hide file tree
Showing 42 changed files with 1,107 additions and 182 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ playground for learning and exploring.
- **[MD055](doc/md055.md)** *table-pipe-style* - Table pipe style
- **[MD056](doc/md056.md)** *table-column-count* - Table column count
- **[MD058](doc/md058.md)** *blanks-around-tables* - Tables should be surrounded by blank lines
- **[MD059](doc/md059.md)** *descriptive-link-text* - Link text should be descriptive

<!-- markdownlint-restore -->

Expand All @@ -165,7 +166,7 @@ To implement your own rules, refer to [CustomRules.md](doc/CustomRules.md).
Tags group related rules and can be used to enable/disable multiple
rules at once.

- **`accessibility`** - `MD045`
- **`accessibility`** - `MD045`, `MD059`
- **`atx`** - `MD018`, `MD019`
- **`atx_closed`** - `MD020`, `MD021`
- **`blank_lines`** - `MD012`, `MD022`, `MD031`, `MD032`, `MD047`
Expand All @@ -183,7 +184,7 @@ rules at once.
- **`language`** - `MD040`
- **`line_length`** - `MD013`
- **`links`** - `MD011`, `MD034`, `MD039`, `MD042`, `MD051`, `MD052`, `MD053`,
`MD054`
`MD054`, `MD059`
- **`ol`** - `MD029`, `MD030`, `MD032`
- **`spaces`** - `MD018`, `MD019`, `MD020`, `MD021`, `MD023`
- **`spelling`** - `MD044`
Expand Down
16 changes: 16 additions & 0 deletions doc-build/md059.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
This rule is triggered when a link is set with generic text like
"Click here", "here", or "learn more", giving it a generic accessible name.

Rationale: Screen reader users may navigate through a list of links
to quickly find content on a page. When the link name is something ambiguous
like "Learn more", there isn't sufficient context to help the user determine
whether to follow the link.

Link names should be descriptive and describe the purpose of the link, like:
`[Download the budget document]`, `[About markdownlint]`,`[View registration]`,
etc.

To override the default list and configure your own list of banned accessible
names, set `link_texts` in the config.

Note: This rule checks Markdown-style links and ignores HTML-style links.
29 changes: 29 additions & 0 deletions doc/Rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -2526,6 +2526,35 @@ Some text
Rationale: In addition to aesthetic reasons, some parsers will incorrectly parse
tables that don't have blank lines before and after them.

<a name="md059"></a>

## `MD059` - Link text should be descriptive

Tags: `accessibility`, `links`

Aliases: `descriptive-link-text`

Parameters:

- `link_texts`: List of restricted link texts (`string[]`, default `[]`)

This rule is triggered when a link is set with generic text like
"Click here", "here", or "learn more", giving it a generic accessible name.

Rationale: Screen reader users may navigate through a list of links
to quickly find content on a page. When the link name is something ambiguous
like "Learn more", there isn't sufficient context to help the user determine
whether to follow the link.

Link names should be descriptive and describe the purpose of the link, like:
`[Download the budget document]`, `[About markdownlint]`,`[View registration]`,
etc.

To override the default list and configure your own list of banned accessible
names, set `link_texts` in the config.

Note: This rule checks Markdown-style links and ignores HTML-style links.

<!-- markdownlint-configure-file {
"no-inline-html": {
"allowed_elements": [
Expand Down
26 changes: 26 additions & 0 deletions doc/md059.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# `MD059` - Link text should be descriptive

Tags: `accessibility`, `links`

Aliases: `descriptive-link-text`

Parameters:

- `link_texts`: List of restricted link texts (`string[]`, default `[]`)

This rule is triggered when a link is set with generic text like
"Click here", "here", or "learn more", giving it a generic accessible name.

Rationale: Screen reader users may navigate through a list of links
to quickly find content on a page. When the link name is something ambiguous
like "Learn more", there isn't sufficient context to help the user determine
whether to follow the link.

Link names should be descriptive and describe the purpose of the link, like:
`[Download the budget document]`, `[About markdownlint]`,`[View registration]`,
etc.

To override the default list and configure your own list of banned accessible
names, set `link_texts` in the config.

Note: This rule checks Markdown-style links and ignores HTML-style links.
26 changes: 24 additions & 2 deletions lib/configuration-strict.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,28 @@ export interface ConfigurationStrict {
* MD058/blanks-around-tables : Tables should be surrounded by blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md058.md
*/
"blanks-around-tables"?: boolean;
/**
* MD059/descriptive-link-text : Link text should be descriptive : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md059.md
*/
MD059?:
| boolean
| {
/**
* List of restricted link texts
*/
link_texts?: string[];
};
/**
* MD059/descriptive-link-text : Link text should be descriptive : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md059.md
*/
"descriptive-link-text"?:
| boolean
| {
/**
* List of restricted link texts
*/
link_texts?: string[];
};
/**
* headings : MD001, MD003, MD018, MD019, MD020, MD021, MD022, MD023, MD024, MD025, MD026, MD036, MD041, MD043
*/
Expand All @@ -1125,7 +1147,7 @@ export interface ConfigurationStrict {
*/
hard_tab?: boolean;
/**
* links : MD011, MD034, MD039, MD042, MD051, MD052, MD053, MD054
* links : MD011, MD034, MD039, MD042, MD051, MD052, MD053, MD054, MD059
*/
links?: boolean;
/**
Expand Down Expand Up @@ -1185,7 +1207,7 @@ export interface ConfigurationStrict {
*/
spelling?: boolean;
/**
* accessibility : MD045
* accessibility : MD045, MD059
*/
accessibility?: boolean;
/**
Expand Down
60 changes: 60 additions & 0 deletions lib/md059.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// @ts-check

import { addErrorContext } from "../helpers/helpers.cjs";
import { filterByTypesCached } from "./cache.mjs";

const defaultBannedText = [
"click here",
"here",
"learn more",
"link",
"more",
"read more"
];

/**
* Normalizes a string and removes extra whitespaces and punctuations.
*
* @param {string} text String to transform.
* @returns {string} Normalized string with no punctuations or extra whitespaces.
*/
function normalizeText(text) {
return text
.toLowerCase()
.replace(/\W+/g, " ")
.replace(/\s+/g, " ")
.trim();
}

/** @type {import("markdownlint").Rule} */
export default {
"names": [ "MD059", "descriptive-link-text" ],
"description": "Link text should be descriptive",
"tags": [ "links", "accessibility" ],
"parser": "micromark",
"function": function MD059(params, onError) {
const bannedNames = new Set(params.config.link_texts || defaultBannedText);
const labels = filterByTypesCached([ "label" ])
.filter((label) => label.parent?.type === "link");

for (const label of labels) {
const labelTexts = label.children.filter((child) => child.type === "labelText");
for (const labelText of labelTexts) {
const { text } = label;
if (bannedNames.has(normalizeText(text))) {
const range = labelText.startLine === labelText.endLine ?
[ labelText.startColumn, text.length ] :
undefined;
addErrorContext(
onError,
labelText.startLine,
text,
undefined,
undefined,
range
);
}
}
}
}
};
4 changes: 3 additions & 1 deletion lib/rules.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import md054 from "./md054.mjs";
import md055 from "./md055.mjs";
import md056 from "./md056.mjs";
import md058 from "./md058.mjs";
import md059 from "./md059.mjs";

const rules = [
md001,
Expand Down Expand Up @@ -108,7 +109,8 @@ const rules = [
md055,
md056,
// md057: See https://github.com/markdownlint/markdownlint
md058
md058,
md059
];
for (const rule of rules) {
const name = rule.names[0].toLowerCase();
Expand Down
8 changes: 7 additions & 1 deletion schema/.markdownlint.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -306,5 +306,11 @@
"MD056": true,

// MD058/blanks-around-tables : Tables should be surrounded by blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md058.md
"MD058": true
"MD058": true,

// MD059/descriptive-link-text : Link text should be descriptive : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md059.md
"MD059": {
// List of restricted link texts
"link_texts": []
}
}
5 changes: 5 additions & 0 deletions schema/.markdownlint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,8 @@ MD056: true

# MD058/blanks-around-tables : Tables should be surrounded by blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md058.md
MD058: true

# MD059/descriptive-link-text : Link text should be descriptive : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md059.md
MD059:
# List of restricted link texts
link_texts: []
12 changes: 12 additions & 0 deletions schema/build-config-schema.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,18 @@ for (const rule of rules) {
}
};
break;
case "MD059":
scheme.properties = {
"link_texts": {
"description": "List of restricted link texts",
"type": "array",
"items": {
"type": "string"
},
"default": []
}
};
break;
default:
custom = false;
break;
Expand Down
42 changes: 40 additions & 2 deletions schema/markdownlint-config-schema-strict.json
Original file line number Diff line number Diff line change
Expand Up @@ -1716,6 +1716,44 @@
"type": "boolean",
"default": true
},
"MD059": {
"description": "MD059/descriptive-link-text : Link text should be descriptive : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md059.md",
"type": [
"boolean",
"object"
],
"default": true,
"properties": {
"link_texts": {
"description": "List of restricted link texts",
"type": "array",
"items": {
"type": "string"
},
"default": []
}
},
"additionalProperties": false
},
"descriptive-link-text": {
"description": "MD059/descriptive-link-text : Link text should be descriptive : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md059.md",
"type": [
"boolean",
"object"
],
"default": true,
"properties": {
"link_texts": {
"description": "List of restricted link texts",
"type": "array",
"items": {
"type": "string"
},
"default": []
}
},
"additionalProperties": false
},
"headings": {
"description": "headings : MD001, MD003, MD018, MD019, MD020, MD021, MD022, MD023, MD024, MD025, MD026, MD036, MD041, MD043",
"type": "boolean",
Expand Down Expand Up @@ -1747,7 +1785,7 @@
"default": true
},
"links": {
"description": "links : MD011, MD034, MD039, MD042, MD051, MD052, MD053, MD054",
"description": "links : MD011, MD034, MD039, MD042, MD051, MD052, MD053, MD054, MD059",
"type": "boolean",
"default": true
},
Expand Down Expand Up @@ -1822,7 +1860,7 @@
"default": true
},
"accessibility": {
"description": "accessibility : MD045",
"description": "accessibility : MD045, MD059",
"type": "boolean",
"default": true
},
Expand Down
42 changes: 40 additions & 2 deletions schema/markdownlint-config-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1716,6 +1716,44 @@
"type": "boolean",
"default": true
},
"MD059": {
"description": "MD059/descriptive-link-text : Link text should be descriptive : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md059.md",
"type": [
"boolean",
"object"
],
"default": true,
"properties": {
"link_texts": {
"description": "List of restricted link texts",
"type": "array",
"items": {
"type": "string"
},
"default": []
}
},
"additionalProperties": false
},
"descriptive-link-text": {
"description": "MD059/descriptive-link-text : Link text should be descriptive : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md059.md",
"type": [
"boolean",
"object"
],
"default": true,
"properties": {
"link_texts": {
"description": "List of restricted link texts",
"type": "array",
"items": {
"type": "string"
},
"default": []
}
},
"additionalProperties": false
},
"headings": {
"description": "headings : MD001, MD003, MD018, MD019, MD020, MD021, MD022, MD023, MD024, MD025, MD026, MD036, MD041, MD043",
"type": "boolean",
Expand Down Expand Up @@ -1747,7 +1785,7 @@
"default": true
},
"links": {
"description": "links : MD011, MD034, MD039, MD042, MD051, MD052, MD053, MD054",
"description": "links : MD011, MD034, MD039, MD042, MD051, MD052, MD053, MD054, MD059",
"type": "boolean",
"default": true
},
Expand Down Expand Up @@ -1822,7 +1860,7 @@
"default": true
},
"accessibility": {
"description": "accessibility : MD045",
"description": "accessibility : MD045, MD059",
"type": "boolean",
"default": true
},
Expand Down
Loading

0 comments on commit 27d39b7

Please sign in to comment.