Skip to content

Commit

Permalink
Update new rule MD059/descriptive-link-text for project-level consist…
Browse files Browse the repository at this point in the history
…ency.
  • Loading branch information
DavidAnson committed Feb 14, 2025
1 parent 8950d7b commit a324ec5
Show file tree
Hide file tree
Showing 23 changed files with 439 additions and 356 deletions.
27 changes: 15 additions & 12 deletions doc-build/md059.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
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.
This rule is triggered when a link has generic text like `[click here](...)` or
`[link](...)`.

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 text should be descriptive and communicate the purpose of the link (e.g.,
`[Download the budget document](...)` or `[CommonMark Specification](...)`).
This is especially important for screen readers which sometimes present links
without context.

Link names should be descriptive and describe the purpose of the link, like:
`[Download the budget document]`, `[About markdownlint]`,`[View registration]`,
etc.
By default, this rule prohibits a small number of common English words/phrases.
To customize that list of words/phrases, set the `prohibited_texts` parameter to
an `Array` of `string`s.

To override the default list and configure your own list of banned accessible
names, set `link_texts` in the config.
Note: For languages other than English, use the `prohibited_texts` parameter to
customize the list for that language. It is *not* a goal for this rule to have
translations for every language.

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

More information: <https://webaim.org/techniques/hypertext/>
30 changes: 17 additions & 13 deletions doc/Rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -2548,24 +2548,28 @@ Aliases: `descriptive-link-text`

Parameters:

- `link_texts`: List of restricted link texts (`string[]`, default `[]`)
- `prohibited_texts`: Prohibited link texts (`string[]`, default `["click
here","here","link","more"]`)

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.
This rule is triggered when a link has generic text like `[click here](...)` or
`[link](...)`.

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 text should be descriptive and communicate the purpose of the link (e.g.,
`[Download the budget document](...)` or `[CommonMark Specification](...)`).
This is especially important for screen readers which sometimes present links
without context.

Link names should be descriptive and describe the purpose of the link, like:
`[Download the budget document]`, `[About markdownlint]`,`[View registration]`,
etc.
By default, this rule prohibits a small number of common English words/phrases.
To customize that list of words/phrases, set the `prohibited_texts` parameter to
an `Array` of `string`s.

To override the default list and configure your own list of banned accessible
names, set `link_texts` in the config.
Note: For languages other than English, use the `prohibited_texts` parameter to
customize the list for that language. It is *not* a goal for this rule to have
translations for every language.

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

More information: <https://webaim.org/techniques/hypertext/>

<!-- markdownlint-configure-file {
"no-inline-html": {
Expand Down
30 changes: 17 additions & 13 deletions doc/md059.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,25 @@ Aliases: `descriptive-link-text`

Parameters:

- `link_texts`: List of restricted link texts (`string[]`, default `[]`)
- `prohibited_texts`: Prohibited link texts (`string[]`, default `["click
here","here","link","more"]`)

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.
This rule is triggered when a link has generic text like `[click here](...)` or
`[link](...)`.

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 text should be descriptive and communicate the purpose of the link (e.g.,
`[Download the budget document](...)` or `[CommonMark Specification](...)`).
This is especially important for screen readers which sometimes present links
without context.

Link names should be descriptive and describe the purpose of the link, like:
`[Download the budget document]`, `[About markdownlint]`,`[View registration]`,
etc.
By default, this rule prohibits a small number of common English words/phrases.
To customize that list of words/phrases, set the `prohibited_texts` parameter to
an `Array` of `string`s.

To override the default list and configure your own list of banned accessible
names, set `link_texts` in the config.
Note: For languages other than English, use the `prohibited_texts` parameter to
customize the list for that language. It is *not* a goal for this rule to have
translations for every language.

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

More information: <https://webaim.org/techniques/hypertext/>
2 changes: 1 addition & 1 deletion helpers/helpers.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ module.exports.addErrorDetailIf = addErrorDetailIf;
*/
function addErrorContext(
onError, lineNumber, context, start, end, range, fixInfo) {
context = ellipsify(context, start, end);
context = ellipsify(context.replace(newLineRe, "\n"), start, end);
addError(onError, lineNumber, undefined, context, range, fixInfo);
}
module.exports.addErrorContext = addErrorContext;
Expand Down
8 changes: 4 additions & 4 deletions lib/configuration-strict.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1107,9 +1107,9 @@ export interface ConfigurationStrict {
| boolean
| {
/**
* List of restricted link texts
* Prohibited link texts
*/
link_texts?: string[];
prohibited_texts?: string[];
};
/**
* MD059/descriptive-link-text : Link text should be descriptive : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md059.md
Expand All @@ -1118,9 +1118,9 @@ export interface ConfigurationStrict {
| boolean
| {
/**
* List of restricted link texts
* Prohibited link texts
*/
link_texts?: string[];
prohibited_texts?: string[];
};
/**
* headings : MD001, MD003, MD018, MD019, MD020, MD021, MD022, MD023, MD024, MD025, MD026, MD036, MD041, MD043
Expand Down
4 changes: 2 additions & 2 deletions lib/md038.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @ts-check

import { addErrorContext, newLineRe } from "../helpers/helpers.cjs";
import { addErrorContext } from "../helpers/helpers.cjs";
import { getDescendantsByType } from "../helpers/micromark-helpers.cjs";
import { filterByTypesCached } from "./cache.mjs";

Expand Down Expand Up @@ -32,7 +32,7 @@ export default {
const endSpaces = endCount > 0;
// Check if safe to remove 1-space padding
const removePadding = startSpaces && endSpaces && startPadding && endPadding && !startBacktick && !endBacktick;
const context = codeText.text.replace(newLineRe, "\n");
const context = codeText.text;
// If extra space at start, report violation
if (startSpaces) {
const startColumn = (removePadding ? startPadding : startData).startColumn;
Expand Down
75 changes: 43 additions & 32 deletions lib/md059.mjs
Original file line number Diff line number Diff line change
@@ -1,58 +1,69 @@
// @ts-check

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

const defaultBannedText = [
/** @typedef {import("markdownlint").MicromarkTokenType} MicromarkTokenType */
/** @type {Set<MicromarkTokenType>} */
const allowedChildrenTypes = new Set([
"codeText",
"htmlText"
]);
const defaultProhibitedTexts = [
"click here",
"here",
"learn more",
"link",
"more",
"read more"
"more"
];

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

/** @type {import("markdownlint").Rule} */
export default {
"names": [ "MD059", "descriptive-link-text" ],
"description": "Link text should be descriptive",
"tags": [ "links", "accessibility" ],
"tags": [ "accessibility", "links" ],
"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
);
const prohibitedTexts = new Set(
(params.config.prohibited_texts || defaultProhibitedTexts).map(normalize)
);
if (prohibitedTexts.size > 0) {
const links = filterByTypesCached([ "link" ]);
for (const link of links) {
const labelTexts = getDescendantsByType(link, [ "label", "labelText" ]);
for (const labelText of labelTexts) {
const { children, endColumn, endLine, parent, startColumn, startLine, text } = labelText;
if (
!children.some((child) => allowedChildrenTypes.has(child.type)) &&
prohibitedTexts.has(normalize(text))
) {
const range = (startLine === endLine) ?
[ startColumn, endColumn - startColumn ] :
undefined;
addErrorContext(
onError,
startLine,
// @ts-ignore
parent.text,
undefined,
undefined,
range
);
}
}
}
}
Expand Down
9 changes: 7 additions & 2 deletions schema/.markdownlint.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,12 @@

// 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": []
// Prohibited link texts
"prohibited_texts": [
"click here",
"here",
"link",
"more"
]
}
}
8 changes: 6 additions & 2 deletions schema/.markdownlint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -278,5 +278,9 @@ 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: []
# Prohibited link texts
prohibited_texts:
- "click here"
- "here"
- "link"
- "more"
11 changes: 8 additions & 3 deletions schema/build-config-schema.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -558,13 +558,18 @@ for (const rule of rules) {
break;
case "MD059":
scheme.properties = {
"link_texts": {
"description": "List of restricted link texts",
"prohibited_texts": {
"description": "Prohibited link texts",
"type": "array",
"items": {
"type": "string"
},
"default": []
"default": [
"click here",
"here",
"link",
"more"
]
}
};
break;
Expand Down
22 changes: 16 additions & 6 deletions schema/markdownlint-config-schema-strict.json
Original file line number Diff line number Diff line change
Expand Up @@ -1724,13 +1724,18 @@
],
"default": true,
"properties": {
"link_texts": {
"description": "List of restricted link texts",
"prohibited_texts": {
"description": "Prohibited link texts",
"type": "array",
"items": {
"type": "string"
},
"default": []
"default": [
"click here",
"here",
"link",
"more"
]
}
},
"additionalProperties": false
Expand All @@ -1743,13 +1748,18 @@
],
"default": true,
"properties": {
"link_texts": {
"description": "List of restricted link texts",
"prohibited_texts": {
"description": "Prohibited link texts",
"type": "array",
"items": {
"type": "string"
},
"default": []
"default": [
"click here",
"here",
"link",
"more"
]
}
},
"additionalProperties": false
Expand Down
Loading

0 comments on commit a324ec5

Please sign in to comment.