mirror of
https://github.com/withastro/astro.git
synced 2024-12-23 21:53:55 -05:00
40ef43a59b
* deps: mdx github-slugger * feat: add getHeadings via rehype plugin * chore: stray console.log * test: getHeadings w/ & w/0 JSX expressions * docs: add generated exports * refactor: pass headings using vfile.data * deps: vfile * test: heading anchor IDs * docs: add collect-headings to default rehype plugins * chore: changeset * deps: estree-util-value-to-estree * refactor: inject getHeadings export the right way! * deps: switch to acorn * refactor: just use acorn * docs: `getHeadings` info structuring Co-authored-by: Chris Swithinbank <swithinbank@gmail.com> * docs: clarify `url` example Co-authored-by: Chris Swithinbank <swithinbank@gmail.com> * fix: move slugger inside plugin call * refactor: cleanup code reassignment * chore: lint * deps: mdast-util-mdx, test utils * refactor: add jsToTreeNode util * feat: expose utils for lib authors * test: rehype plugins w/ and w/o extends * test: fixture * refactor: remove utils from package exports Co-authored-by: Chris Swithinbank <swithinbank@gmail.com>
50 lines
1.3 KiB
TypeScript
50 lines
1.3 KiB
TypeScript
import Slugger from 'github-slugger';
|
|
import { visit } from 'unist-util-visit';
|
|
import { jsToTreeNode } from './utils.js';
|
|
|
|
export interface MarkdownHeading {
|
|
depth: number;
|
|
slug: string;
|
|
text: string;
|
|
}
|
|
|
|
export default function rehypeCollectHeadings() {
|
|
const slugger = new Slugger();
|
|
return function (tree: any) {
|
|
const headings: MarkdownHeading[] = [];
|
|
visit(tree, (node) => {
|
|
if (node.type !== 'element') return;
|
|
const { tagName } = node;
|
|
if (tagName[0] !== 'h') return;
|
|
const [_, level] = tagName.match(/h([0-6])/) ?? [];
|
|
if (!level) return;
|
|
const depth = Number.parseInt(level);
|
|
|
|
let text = '';
|
|
visit(node, (child, __, parent) => {
|
|
if (child.type === 'element' || parent == null) {
|
|
return;
|
|
}
|
|
if (child.type === 'raw' && child.value.match(/^\n?<.*>\n?$/)) {
|
|
return;
|
|
}
|
|
if (new Set(['text', 'raw', 'mdxTextExpression']).has(child.type)) {
|
|
text += child.value;
|
|
}
|
|
});
|
|
|
|
node.properties = node.properties || {};
|
|
if (typeof node.properties.id !== 'string') {
|
|
let slug = slugger.slug(text);
|
|
if (slug.endsWith('-')) {
|
|
slug = slug.slice(0, -1);
|
|
}
|
|
node.properties.id = slug;
|
|
}
|
|
headings.push({ depth, slug: node.properties.id, text });
|
|
});
|
|
tree.children.unshift(
|
|
jsToTreeNode(`export function getHeadings() { return ${JSON.stringify(headings)} }`)
|
|
);
|
|
};
|
|
}
|