Core: Support exportAs in remarkStructure.

This commit is contained in:
Fuma Nama
2025-11-01 17:33:29 +08:00
parent d1e43f490b
commit f97cd1ea2b
7 changed files with 76 additions and 20 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-core': patch
---
Support `exportAs` in `remarkStructure`.
+1
View File
@@ -112,6 +112,7 @@
"@orama/orama": "^3.1.16",
"@shikijs/rehype": "^3.14.0",
"@shikijs/transformers": "^3.14.0",
"estree-util-value-to-estree": "^3.5.0",
"github-slugger": "^2.0.0",
"hast-util-to-estree": "^3.1.3",
"hast-util-to-jsx-runtime": "^2.3.6",
@@ -0,0 +1,46 @@
import type { RootContent } from 'mdast';
import { valueToEstree } from 'estree-util-value-to-estree';
export function flattenNode(node: RootContent): string {
if ('children' in node)
return node.children.map((child) => flattenNode(child)).join('');
if ('value' in node) return node.value;
return '';
}
export function toMdxExport(name: string, value: unknown): RootContent {
return {
type: 'mdxjsEsm',
value: '',
data: {
estree: {
type: 'Program',
sourceType: 'module',
body: [
{
type: 'ExportNamedDeclaration',
attributes: [],
specifiers: [],
source: null,
declaration: {
type: 'VariableDeclaration',
kind: 'let',
declarations: [
{
type: 'VariableDeclarator',
id: {
type: 'Identifier',
name,
},
init: valueToEstree(value),
},
],
},
},
],
},
},
};
}
@@ -1,7 +1,7 @@
import { type Transformer } from 'unified';
import { visit } from 'unist-util-visit';
import { type Root, type RootContent } from 'mdast';
import { flattenNode } from '@/mdx-plugins/remark-utils';
import { flattenNode } from '@/mdx-plugins/mdast-utils';
export interface RemarkAdmonitionOptions {
tag?: string;
@@ -3,7 +3,7 @@ import type { Heading, Root } from 'mdast';
import type { Transformer } from 'unified';
import { visit } from 'unist-util-visit';
import type { TOCItemType } from '@/toc';
import { flattenNode } from '@/mdx-plugins/remark-utils';
import { flattenNode } from '@/mdx-plugins/mdast-utils';
const slugger = new Slugger();
@@ -4,7 +4,7 @@ import { remark } from 'remark';
import remarkGfm from 'remark-gfm';
import type { PluggableList, Transformer } from 'unified';
import { visit } from 'unist-util-visit';
import { flattenNode } from './remark-utils';
import { flattenNode, toMdxExport } from './mdast-utils';
import type {
MdxJsxAttribute,
MdxJsxExpressionAttribute,
@@ -49,14 +49,17 @@ export interface StructureOptions {
node: MdxJsxFlowElement,
attribute: MdxJsxAttribute | MdxJsxExpressionAttribute,
) => boolean);
/**
* export as `structuredData` or specified variable name.
*/
exportAs?: string | boolean;
}
declare module 'mdast' {
interface Data {
/**
* Get content of unserializable element
*
* Needed for `remarkStructure` to generate search index
* [Fumadocs] Get content of unserializable element, `remarkStructure` uses it to generate search index.
*/
_string?: string[];
}
@@ -64,6 +67,9 @@ declare module 'mdast' {
declare module 'vfile' {
interface DataMap {
/**
* [Fumadocs] injected by `remarkStructure`
*/
structuredData: StructuredData;
}
}
@@ -84,6 +90,7 @@ export function remarkStructure({
return ['TypeTable', 'Callout'].includes(node.name);
},
exportAs = false,
}: StructureOptions = {}): Transformer<Root, Root> {
const slugger = new Slugger();
@@ -98,7 +105,7 @@ export function remarkStructure({
types = (node) => arr.includes(node.type);
}
return (node, file) => {
return (tree, file) => {
slugger.reset();
const data: StructuredData = { contents: [], headings: [] };
let lastHeading: string | undefined;
@@ -117,9 +124,8 @@ export function remarkStructure({
}
}
visit(node, (element) => {
if (element.type === 'root') return;
if (!types(element)) return;
visit(tree, (element) => {
if (element.type === 'root' || !types(element)) return;
if (element.type === 'heading') {
element.data ||= {};
@@ -187,6 +193,14 @@ export function remarkStructure({
});
file.data.structuredData = data;
if (exportAs) {
tree.children.unshift(
toMdxExport(
typeof exportAs === 'string' ? exportAs : 'structuredData',
data,
),
);
}
};
}
@@ -1,10 +0,0 @@
import type { RootContent } from 'mdast';
export function flattenNode(node: RootContent): string {
if ('children' in node)
return node.children.map((child) => flattenNode(child)).join('');
if ('value' in node) return node.value;
return '';
}