1import { parse } from 'acorn';
2import { visit } from 'unist-util-visit';
3
4/**
5 * @typedef {import('@types/mdast').Root} Root - https://github.com/syntax-tree/mdast#root
6 * @typedef {import('@types/mdast').Heading} Heading - https://github.com/syntax-tree/mdast#heading
7 */
8
9/**
10 * Find all headings within a MDX document, and export them as JS array.
11 * This uses the MDAST's `heading` and tries to guess the children's content.
12 * When the node has an ID, generated by `remark-slug`, the ID is added to the exported object.
13 *
14 * @param {object} options
15 * @param {string} [options.exportName="headings"]
16 */
17export default function remarkExportHeadings(options = {}) {
18  const { exportName = 'headings' } = options;
19
20  /** @param {Root} tree */
21  return tree => {
22    const headings = [];
23
24    /** @param {Heading} node */
25    const visitor = node => {
26      if (node.children.length > 0) {
27        headings.push({
28          id: node.data?.id,
29          depth: node.depth,
30          type: node.children.find(node => node.type !== 'text')?.type || 'text',
31          title: node.children.map(child => child.value).join(' '),
32        });
33      }
34    };
35
36    visit(tree, 'heading', visitor);
37
38    tree.children.push({
39      type: 'mdxjsEsm',
40      data: {
41        estree: parse(`export const ${exportName} = ${JSON.stringify(headings)};`, {
42          sourceType: 'module',
43          ecmaVersion: 2022,
44        }),
45      },
46    });
47  };
48}
49