feat: html anchor support

This commit is contained in:
Igor Tsiglyar
2024-01-11 13:42:34 +00:00
committed by Théo LUDWIG
parent 9d2cc818d5
commit 24a0788d32
11 changed files with 123 additions and 11 deletions

View File

@ -7,6 +7,7 @@ const {
filterTokens,
convertHeadingToHTMLFragment,
getMarkdownHeadings,
getMarkdownAnchorHTMLFragments,
} = require("./utils.js")
/** @typedef {import('markdownlint').Rule} MarkdownLintRule */
@ -63,6 +64,8 @@ const customRule = {
if (type === "link_open" && url.hash !== "") {
const fileContent = fs.readFileSync(url, { encoding: "utf8" })
const headings = getMarkdownHeadings(fileContent)
const anchorHTMLFragments =
getMarkdownAnchorHTMLFragments(fileContent)
/** @type {Map<string, number>} */
const fragments = new Map()
@ -77,6 +80,8 @@ const customRule = {
return fragment
})
headingsHTMLFragments.push(...anchorHTMLFragments)
if (!headingsHTMLFragments.includes(url.hash)) {
onError({
lineNumber,

View File

@ -1,4 +1,6 @@
const MarkdownIt = require("markdown-it")
// @ts-ignore
const { getHtmlAttributeRe } = require("markdownlint-rule-helpers")
/** @typedef {import('markdownlint').RuleParams} MarkdownLintRuleParams */
/** @typedef {import('markdownlint').MarkdownItToken} MarkdownItToken */
@ -94,8 +96,55 @@ const getMarkdownHeadings = (content) => {
return headings
}
const anchorNameRe = getHtmlAttributeRe("name")
const anchorIdRe = getHtmlAttributeRe("id")
/**
* Gets the anchor HTML fragments from a Markdown string.
* @param {string} content
* @returns {string[]}
*/
const getMarkdownAnchorHTMLFragments = (content) => {
const markdownIt = new MarkdownIt({ html: true })
const tokens = markdownIt.parse(content, {})
/** @type {string[]} */
const result = []
for (const token of tokens) {
if (token.type === "inline") {
if (!token.children) {
continue
}
for (const child of token.children) {
if (child.type === "html_inline") {
const anchorMatch =
anchorIdRe.exec(token.content) || anchorNameRe.exec(token.content)
if (!anchorMatch || anchorMatch.length === 0) {
continue
}
const anchorIdOrName = anchorMatch[1]
if (anchorMatch[1] === undefined) {
continue
}
const anchorHTMLFragment = "#" + anchorIdOrName
if (!result.includes(anchorHTMLFragment)) {
result.push(anchorHTMLFragment)
}
}
}
}
}
return result
}
module.exports = {
filterTokens,
convertHeadingToHTMLFragment,
getMarkdownHeadings,
getMarkdownAnchorHTMLFragments,
}