Skip to content

Commit 0a05680

Browse files
authored
feat(plugin-docs-cli): Extract TOC headings at build time (#2744)
1 parent 71eeba2 commit 0a05680

3 files changed

Lines changed: 38 additions & 6 deletions

File tree

packages/plugin-docs-cli/src/scanner.test.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,26 @@ describe('scanDocsFolder', () => {
6262
expect(pages[2].file).toBe('advanced.md');
6363
});
6464

65-
it('should not include headings in page objects', async () => {
65+
it('should extract h2/h3 headings into page objects', async () => {
6666
const result = await scanDocsFolder(testDocsPath);
6767

68-
const pages = result.manifest.pages;
69-
for (const page of pages) {
70-
expect(page).not.toHaveProperty('headings');
71-
}
68+
const configPage = result.manifest.pages.find((p: Page) => p.slug === 'config');
69+
const settingsPage = configPage?.children?.find((p: Page) => p.slug === 'config/settings');
70+
71+
expect(settingsPage?.headings).toEqual([
72+
{ level: 2, text: 'General Settings', id: 'general-settings' },
73+
{ level: 3, text: 'Display Name', id: 'display-name' },
74+
{ level: 2, text: 'Advanced Settings', id: 'advanced-settings' },
75+
]);
76+
});
77+
78+
it('should omit headings on pages without h2/h3 content', async () => {
79+
const result = await scanDocsFolder(testDocsPath);
80+
81+
// home.md has only an h1, no h2/h3 — field should be omitted
82+
const homePage = result.manifest.pages.find((p: Page) => p.slug === 'home');
83+
expect(homePage).toBeDefined();
84+
expect(homePage).not.toHaveProperty('headings');
7285
});
7386

7487
it('should throw error when no valid markdown files found', async () => {

packages/plugin-docs-cli/src/scanner.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@ import { join, relative, parse, sep } from 'node:path';
33
import GithubSlugger from 'github-slugger';
44
import matter from 'gray-matter';
55
import createDebug from 'debug';
6-
import type { Manifest, Page, MarkdownFiles, Frontmatter } from '@grafana/plugin-docs-parser';
6+
import {
7+
parseMarkdown,
8+
type Manifest,
9+
type Page,
10+
type MarkdownFiles,
11+
type Frontmatter,
12+
} from '@grafana/plugin-docs-parser';
713

814
const debug = createDebug('plugin-docs-cli:scanner');
915

@@ -227,6 +233,13 @@ function treeToPages(node: TreeNode): Page[] {
227233
file: child.file.relativePath,
228234
};
229235

236+
// extract h2/h3 headings via the canonical parser pipeline so heading
237+
// IDs in the manifest match the IDs rendered at serve time
238+
const { headings } = parseMarkdown(child.file.content);
239+
if (headings.length > 0) {
240+
page.headings = headings;
241+
}
242+
230243
// if this file has children (folder with same name), add them
231244
if (child.children.size > 0) {
232245
page.children = treeToPages(child);

packages/plugin-docs-parser/src/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ export interface Page {
2323
*/
2424
file: string;
2525

26+
/**
27+
* Optional headings (h2, h3) extracted from the page's markdown body.
28+
* Present only on pages backed by a real file; omitted on category nodes.
29+
*/
30+
headings?: Heading[];
31+
2632
/**
2733
* Optional nested child pages.
2834
*/

0 commit comments

Comments
 (0)