Update MD025/single-title and MD051/link-fragments to ignore headings that exactly match Docfx tab syntax (fixes #1099) (fixes #1101).

This commit is contained in:
David Anson 2025-05-01 22:09:36 -07:00
parent a4b7ffa8ce
commit 6adfaf68b5
6 changed files with 115 additions and 4 deletions

View file

@ -270,6 +270,26 @@ function getParentOfType(token, types) {
return current;
}
const docfxTabSyntaxRe = /^#tab\//;
/**
* Returns whether the specified Micromark token looks like a Docfx tab.
*
* @param {Token | null} heading Micromark token.
* @returns {boolean} True iff the token looks like a Docfx tab.
*/
function isDocfxTab(heading) {
// See https://dotnet.github.io/docfx/docs/markdown.html?tabs=linux%2Cdotnet#tabs
if (heading?.type === "atxHeading") {
const headingTexts = getDescendantsByType(heading, [ "atxHeadingText" ]);
if ((headingTexts.length === 1) && (headingTexts[0].children.length === 1) && (headingTexts[0].children[0].type === "link")) {
const resourceDestinationStrings = filterByTypes(headingTexts[0].children[0].children, [ "resourceDestinationString" ]);
return (resourceDestinationStrings.length === 1) && docfxTabSyntaxRe.test(resourceDestinationStrings[0].text);
}
}
return false;
}
/**
* Set containing token types that do not contain content.
*
@ -301,6 +321,7 @@ module.exports = {
getHtmlTagInfo,
getParentOfType,
inHtmlFlow,
isDocfxTab,
isHtmlFlowComment,
nonContentTokens
};

View file

@ -1,7 +1,7 @@
// @ts-check
import { addErrorContext, frontMatterHasTitle } from "../helpers/helpers.cjs";
import { getHeadingLevel, getHeadingText, isHtmlFlowComment, nonContentTokens } from "../helpers/micromark-helpers.cjs";
import { getHeadingLevel, getHeadingText, isDocfxTab, isHtmlFlowComment, nonContentTokens } from "../helpers/micromark-helpers.cjs";
import { filterByTypesCached } from "./cache.mjs";
/** @type {import("markdownlint").Rule} */
@ -14,7 +14,7 @@ export default {
const level = Number(params.config.level || 1);
const { tokens } = params.parsers.micromark;
const matchingHeadings = filterByTypesCached([ "atxHeading", "setextHeading" ])
.filter((heading) => level === getHeadingLevel(heading));
.filter((heading) => (level === getHeadingLevel(heading)) && !isDocfxTab(heading));
if (matchingHeadings.length > 0) {
const foundFrontMatterTitle =
frontMatterHasTitle(

View file

@ -1,7 +1,7 @@
// @ts-check
import { addError, getHtmlAttributeRe } from "../helpers/helpers.cjs";
import { filterByPredicate, filterByTypes, getHtmlTagInfo } from "../helpers/micromark-helpers.cjs";
import { filterByPredicate, filterByTypes, getHtmlTagInfo, isDocfxTab } from "../helpers/micromark-helpers.cjs";
import { filterByTypesCached } from "./cache.mjs";
// Regular expression for identifying HTML anchor names
@ -110,7 +110,10 @@ export default {
[ "definition", "definitionDestinationString" ]
];
for (const [ parentType, definitionType ] of parentChilds) {
const links = filterByTypesCached([ parentType ]);
const links = filterByTypesCached([ parentType ])
.filter(
(link) => !((link.parent?.type === "atxHeadingText") && isDocfxTab(link.parent.parent))
);
for (const link of links) {
const definitions = filterByTypes(link.children, [ definitionType ]);
for (const definition of definitions) {

39
test/docfx-tab-syntax.md Normal file
View file

@ -0,0 +1,39 @@
# Docfx Tab Syntax
Examples copied from: <https://dotnet.github.io/docfx/docs/markdown.html?tabs=linux%2Cdotnet#tabs>
---
# [Tab Display Name](#tab/tab-id)
---
# [Linux](#tab/linux)
Content for Linux...
# [Windows](#tab/windows)
Content for Windows...
---
# [.NET](#tab/dotnet/linux)
.NET content for Linux...
# [.NET](#tab/dotnet/windows)
.NET content for Windows...
# [TypeScript](#tab/typescript/linux)
TypeScript content for Linux...
# [TypeScript](#tab/typescript/windows)
TypeScript content for Windows...
# [REST API](#tab/rest)
REST API content, independent of platform...

View file

@ -11348,6 +11348,54 @@ Generated by [AVA](https://avajs.dev).
`,
}
## docfx-tab-syntax.md
> Snapshot 1
{
errors: [],
fixed: `# Docfx Tab Syntax␊
Examples copied from: <https://dotnet.github.io/docfx/docs/markdown.html?tabs=linux%2Cdotnet#tabs>
---␊
# [Tab Display Name](#tab/tab-id)␊
---␊
# [Linux](#tab/linux)␊
Content for Linux...␊
# [Windows](#tab/windows)␊
Content for Windows...␊
---␊
# [.NET](#tab/dotnet/linux)␊
.NET content for Linux...␊
# [.NET](#tab/dotnet/windows)␊
.NET content for Windows...␊
# [TypeScript](#tab/typescript/linux)␊
TypeScript content for Linux...␊
# [TypeScript](#tab/typescript/windows)␊
TypeScript content for Windows...␊
# [REST API](#tab/rest)␊
REST API content, independent of platform...␊
`,
}
## emoji-headings.md
> Snapshot 1