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

@ -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
@ -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

View file

@ -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";
@ -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;

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
);
}
}
}
}