mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-29 17:07:08 +02:00
73 lines
2.4 KiB
TypeScript
73 lines
2.4 KiB
TypeScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
/* Based on remark-slug (https://github.com/remarkjs/remark-slug) and gatsby-remark-autolink-headers (https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-remark-autolink-headers) */
|
|
|
|
import {parseMarkdownHeadingId, createSlugger} from '@docusaurus/utils';
|
|
import visit from 'unist-util-visit';
|
|
import toString from 'mdast-util-to-string';
|
|
import type {Transformer} from 'unified';
|
|
import type {Parent} from 'unist';
|
|
import type {Heading, Text} from 'mdast';
|
|
|
|
export default function plugin(): Transformer {
|
|
return (root) => {
|
|
const slugs = createSlugger();
|
|
visit(root, 'heading', (headingNode: Heading) => {
|
|
const data = headingNode.data || (headingNode.data = {});
|
|
const properties = (data.hProperties || (data.hProperties = {})) as {
|
|
id: string;
|
|
};
|
|
let {id} = properties;
|
|
|
|
if (id) {
|
|
id = slugs.slug(id, {maintainCase: true});
|
|
} else {
|
|
const headingTextNodes = headingNode.children.filter(
|
|
({type}) => !['html', 'jsx'].includes(type),
|
|
);
|
|
const heading = toString(
|
|
headingTextNodes.length > 0
|
|
? ({children: headingTextNodes} as Parent)
|
|
: headingNode,
|
|
);
|
|
|
|
// Support explicit heading IDs
|
|
const parsedHeading = parseMarkdownHeadingId(heading);
|
|
|
|
id = parsedHeading.id || slugs.slug(heading);
|
|
|
|
if (parsedHeading.id) {
|
|
// When there's an id, it is always in the last child node
|
|
// Sometimes heading is in multiple "parts" (** syntax creates a child
|
|
// node):
|
|
// ## part1 *part2* part3 {#id}
|
|
const lastNode = headingNode.children[
|
|
headingNode.children.length - 1
|
|
] as Text;
|
|
|
|
if (headingNode.children.length > 1) {
|
|
const lastNodeText = parseMarkdownHeadingId(lastNode.value).text;
|
|
// When last part contains test+id, remove the id
|
|
if (lastNodeText) {
|
|
lastNode.value = lastNodeText;
|
|
}
|
|
// When last part contains only the id: completely remove that node
|
|
else {
|
|
headingNode.children.pop();
|
|
}
|
|
} else {
|
|
lastNode.value = parsedHeading.text;
|
|
}
|
|
}
|
|
}
|
|
|
|
data.id = id;
|
|
properties.id = id;
|
|
});
|
|
};
|
|
}
|