<Head>Test</Head>
',
id: 'headtesthead',
- children: []
+ children: [],
+ level: 3
}
- ]
+ ],
+ level: 2
},
{
value: '<div />
',
id: 'div-',
- children: []
+ children: [],
+ level: 2
},
{
value: '<div> Test </div>
',
id: 'div-test-div',
- children: []
+ children: [],
+ level: 2
},
{
value: '<div><i>Test</i></div>
',
id: 'divitestidiv',
- children: []
+ children: [],
+ level: 2
}
];
@@ -51,24 +56,29 @@ exports[`non text phrasing content 1`] = `
{
value: 'Importance',
id: 'importance',
- children: []
+ children: [],
+ level: 3
}
- ]
+ ],
+ level: 2
},
{
value: 'inline.code()
',
id: 'inlinecode',
- children: []
+ children: [],
+ level: 2
}
];
diff --git a/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/index.test.ts b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/index.test.ts
index e9df4502b208..dd786b95ad73 100644
--- a/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/index.test.ts
+++ b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/index.test.ts
@@ -12,7 +12,7 @@ import vfile from 'to-vfile';
import plugin from '../index';
import headings from '../../headings/index';
-const processFixture = async (name, options) => {
+const processFixture = async (name, options?) => {
const path = join(__dirname, 'fixtures', `${name}.md`);
const file = await vfile.read(path);
const result = await remark()
@@ -41,7 +41,8 @@ test('text content', async () => {
{
value: 'Endi',
id: 'endi',
- children: []
+ children: [],
+ level: 3
},
{
value: 'Endi',
@@ -50,14 +51,17 @@ test('text content', async () => {
{
value: 'Yangshun',
id: 'yangshun',
- children: []
+ children: [],
+ level: 3
}
- ]
+ ],
+ level: 2
},
{
value: 'I ♥ unicode.',
id: 'i--unicode',
- children: []
+ children: [],
+ level: 2
}
];
@@ -87,7 +91,8 @@ test('should export even with existing name', async () => {
{
value: 'Thanos',
id: 'thanos',
- children: []
+ children: [],
+ level: 2
},
{
value: 'Tony Stark',
@@ -96,9 +101,11 @@ test('should export even with existing name', async () => {
{
value: 'Avengers',
id: 'avengers',
- children: []
+ children: [],
+ level: 3
}
- ]
+ ],
+ level: 2
}
];
@@ -121,7 +128,8 @@ test('should export with custom name', async () => {
{
value: 'Endi',
id: 'endi',
- children: []
+ children: [],
+ level: 3
},
{
value: 'Endi',
@@ -130,14 +138,17 @@ test('should export with custom name', async () => {
{
value: 'Yangshun',
id: 'yangshun',
- children: []
+ children: [],
+ level: 3
}
- ]
+ ],
+ level: 2
},
{
value: 'I ♥ unicode.',
id: 'i--unicode',
- children: []
+ children: [],
+ level: 2
}
];
@@ -171,7 +182,8 @@ test('should insert below imports', async () => {
{
value: 'Title',
id: 'title',
- children: []
+ children: [],
+ level: 2
},
{
value: 'Test',
@@ -180,9 +192,11 @@ test('should insert below imports', async () => {
{
value: 'Again',
id: 'again',
- children: []
+ children: [],
+ level: 3
}
- ]
+ ],
+ level: 2
}
];
diff --git a/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/search.test.ts b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/search.test.ts
new file mode 100644
index 000000000000..c37ae54f9189
--- /dev/null
+++ b/packages/docusaurus-mdx-loader/src/remark/toc/__tests__/search.test.ts
@@ -0,0 +1,182 @@
+/**
+ * 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.
+ */
+
+import remark from 'remark';
+import mdx from 'remark-mdx';
+import search from '../search';
+import headings from '../../headings/index';
+
+const getHeadings = async (mdText: string) => {
+ const node = remark().parse(mdText);
+ const result = await remark().use(headings).use(mdx).run(node);
+ return search(result);
+};
+
+test('should process all heading levels', async () => {
+ const md = `
+# Alpha
+
+## Bravo
+
+### Charlie
+
+#### Delta
+
+##### Echo
+
+###### Foxtrot
+
+ `;
+
+ expect(await getHeadings(md)).toEqual([
+ {
+ children: [
+ {
+ children: [
+ {
+ children: [
+ {
+ children: [
+ {
+ children: [],
+ id: 'foxtrot',
+ level: 6,
+ value: 'Foxtrot',
+ },
+ ],
+ id: 'echo',
+ level: 5,
+ value: 'Echo',
+ },
+ ],
+ id: 'delta',
+ level: 4,
+ value: 'Delta',
+ },
+ ],
+ id: 'charlie',
+ level: 3,
+ value: 'Charlie',
+ },
+ ],
+ id: 'bravo',
+ level: 2,
+ value: 'Bravo',
+ },
+ ]);
+});
+
+test('should process real-world well-formatted md', async () => {
+ const md = `
+# title
+
+some text
+
+## section 1
+
+some text
+
+### subsection 1-1
+
+some text
+
+#### subsection 1-1-1
+
+some text
+
+#### subsection 1-1-2
+
+some text
+
+### subsection 1-2
+
+some text
+
+### subsection 1-3
+
+some text
+
+## section 2
+
+some text
+
+### subsection 2-1
+
+some text
+
+### subsection 2-1
+
+some text
+
+## section 3
+
+some text
+
+### subsection 3-1
+
+some text
+
+### subsection 3-2
+
+some text
+
+ `;
+
+ expect(await getHeadings(md)).toEqual([
+ {
+ children: [
+ {
+ children: [
+ {
+ children: [],
+ id: 'subsection-1-1-1',
+ level: 4,
+ value: 'subsection 1-1-1',
+ },
+ {
+ children: [],
+ id: 'subsection-1-1-2',
+ level: 4,
+ value: 'subsection 1-1-2',
+ },
+ ],
+ id: 'subsection-1-1',
+ level: 3,
+ value: 'subsection 1-1',
+ },
+ {children: [], id: 'subsection-1-2', level: 3, value: 'subsection 1-2'},
+ {children: [], id: 'subsection-1-3', level: 3, value: 'subsection 1-3'},
+ ],
+ id: 'section-1',
+ level: 2,
+ value: 'section 1',
+ },
+ {
+ children: [
+ {children: [], id: 'subsection-2-1', level: 3, value: 'subsection 2-1'},
+ {
+ children: [],
+ id: 'subsection-2-1-1',
+ level: 3,
+ value: 'subsection 2-1',
+ },
+ ],
+ id: 'section-2',
+ level: 2,
+ value: 'section 2',
+ },
+ {
+ children: [
+ {children: [], id: 'subsection-3-1', level: 3, value: 'subsection 3-1'},
+ {children: [], id: 'subsection-3-2', level: 3, value: 'subsection 3-2'},
+ ],
+ id: 'section-3',
+ level: 2,
+ value: 'section 3',
+ },
+ ]);
+});
diff --git a/packages/docusaurus-mdx-loader/src/remark/toc/search.ts b/packages/docusaurus-mdx-loader/src/remark/toc/search.ts
index 112a56c3fab2..fa0712024adf 100644
--- a/packages/docusaurus-mdx-loader/src/remark/toc/search.ts
+++ b/packages/docusaurus-mdx-loader/src/remark/toc/search.ts
@@ -8,40 +8,75 @@
import toString from 'mdast-util-to-string';
import visit, {Visitor} from 'unist-util-visit';
import {toValue} from '../utils';
-import type {TOCItem as TOC} from '@docusaurus/types';
+import type {TOCItem} from '@docusaurus/types';
import type {Node} from 'unist';
import type {Heading} from 'mdast';
-// Visit all headings. We `slug` all headings (to account for
-// duplicates), but only take h2 and h3 headings.
-export default function search(node: Node): TOC[] {
- const headings: TOC[] = [];
- let current = -1;
- let currentDepth = 0;
+// Intermediate interface for TOC algorithm
+interface SearchItem {
+ node: TOCItem;
+ level: number;
+ parentIndex: number;
+}
+
+/**
+ *
+ * Generate a TOC AST from the raw Markdown contents
+ */
+export default function search(node: Node): TOCItem[] {
+ const headings: SearchItem[] = [];
const visitor: Visitor