@mintlify/common
Version:
Commonly shared code within Mintlify
72 lines (71 loc) • 3.27 kB
JavaScript
import { slugifyWithCounter } from '@sindresorhus/slugify';
import { createMdxJsxAttribute, getTOCTitle, getUnicodeId } from '../../lib/remark-utils.js';
export const remarkExtractTableOfContents = (mdxExtracts) => {
// slugifyWithCounter adds a counter (eg. slug, slug-2, slug-3) to the end of the slug if the header
// already exists. No counter is added for the first occurence.
const slugify = slugifyWithCounter();
return (tree) => {
const contents = [];
let hasTopLayer = false;
for (let nodeIndex = 0; nodeIndex < tree.children.length; nodeIndex++) {
let node = tree.children[nodeIndex];
if ((node.type === 'heading' && [1, 2, 3, 4].includes(node.depth)) ||
(node.type === 'mdxJsxFlowElement' && ['h1', 'h2', 'h3', 'h4'].includes(node.name)) ||
(node.name === 'Update' &&
node.attributes.some((attr) => node.type === 'mdxJsxFlowElement' && attr.name === 'label'))) {
let level;
if (node.name === 'Update') {
level = 1;
node.depth = 1;
}
else {
level = node.depth ? node.depth : node.name.split('')[1];
}
let title = getTOCTitle(node);
const encodedTitle = getUnicodeId(title);
let slug;
// if encoded title is already percent-encoded, return it as is
// slugify doesn't support percent-encoded characters, like Chinese, Korean, etc.
if (/%[0-9A-F]{2}/.test(encodedTitle)) {
slug = slugify(encodedTitle, {
decamelize: false,
preserveCharacters: ['%'],
lowercase: false,
});
}
else {
slug = slugify(encodedTitle, { decamelize: false });
}
let mdxJsxAttributes;
if (node.name === 'Update') {
mdxJsxAttributes = [...node.attributes, createMdxJsxAttribute('id', slug)];
}
else {
mdxJsxAttributes = [
createMdxJsxAttribute('level', level),
createMdxJsxAttribute('id', slug),
];
}
node.attributes = mdxJsxAttributes;
node.type = 'mdxJsxFlowElement';
node.name = node.name === 'Update' ? 'Update' : 'Heading';
const depth = node.depth;
if (level <= 2) {
hasTopLayer = true;
contents.push({ title, slug, depth, children: [] });
}
else {
// Account if there is no first layer
let arrToPushInto = contents;
if (hasTopLayer) {
arrToPushInto = contents[contents.length - 1].children;
}
arrToPushInto.push({ title, slug, depth, children: [] });
}
}
}
if (mdxExtracts) {
mdxExtracts.tableOfContents = contents;
}
};
};