Update new rule MD059/descriptive-link-text for project-level consistency.

This commit is contained in:
David Anson 2025-02-13 22:07:27 -08:00
parent b8374ec5d2
commit 571c2353ea
23 changed files with 466 additions and 383 deletions

View file

@ -1,28 +1,33 @@
// @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();
}
@ -30,29 +35,35 @@ function normalizeText(text) {
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
);
}
}
}
}