Skip to content

Commit 5d0dd11

Browse files
committed
TypeScript: Support overriding renderMarkdown function
1 parent 9e57f2a commit 5d0dd11

File tree

6 files changed

+56
-77
lines changed

6 files changed

+56
-77
lines changed

.changeset/every-parks-stand.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'fumadocs-typescript': patch
3+
---
4+
5+
Support overriding `renderMarkdown` function

packages/typescript/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,8 @@
3737
"dependencies": {
3838
"fast-glob": "^3.3.3",
3939
"hast-util-to-jsx-runtime": "^2.3.5",
40-
"mdast-util-from-markdown": "^2.0.2",
41-
"mdast-util-gfm": "^3.1.0",
42-
"mdast-util-to-hast": "^13.2.0",
40+
"remark": "^15.0.1",
41+
"remark-rehype": "^11.1.1",
4342
"shiki": "^3.1.0",
4443
"ts-morph": "^25.0.1"
4544
},
@@ -51,6 +50,7 @@
5150
"@types/react": "19.0.10",
5251
"@types/react-dom": "19.0.4",
5352
"eslint-config-custom": "workspace:*",
53+
"fumadocs-core": "workspace:*",
5454
"fumadocs-ui": "workspace:*",
5555
"tsconfig": "workspace:*",
5656
"typescript": "^5.7.3"
Lines changed: 28 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,36 @@
1-
import { fromMarkdown } from 'mdast-util-from-markdown';
2-
import { gfmFromMarkdown } from 'mdast-util-gfm';
3-
import { toHast } from 'mdast-util-to-hast';
4-
import { getSingletonHighlighter, type LanguageRegistration } from 'shiki';
51
import type { Nodes } from 'hast';
6-
import { type Code, type RootContent } from 'mdast';
2+
import { remark } from 'remark';
3+
import {
4+
remarkGfm,
5+
rehypeCode,
6+
type RehypeCodeOptions,
7+
} from 'fumadocs-core/mdx-plugins';
8+
import remarkRehype from 'remark-rehype';
79

8-
export async function renderMarkdownToHast(md: string): Promise<Nodes> {
9-
const mdast = fromMarkdown(
10-
md.replace(/{@link (?<link>[^}]*)}/g, '$1'), // replace jsdoc links
11-
{ mdastExtensions: [gfmFromMarkdown()] },
12-
);
13-
14-
const highlighter = await getSingletonHighlighter({
15-
themes: ['vitesse-light', 'vitesse-dark'],
16-
});
10+
const processor = remark()
11+
.use(remarkGfm)
12+
.use(remarkRehype)
13+
.use(rehypeCode, {
14+
lazy: true,
1715

18-
async function preload(contents: RootContent[]): Promise<void> {
19-
await Promise.all(
20-
contents.map(async (c) => {
21-
if ('children' in c) await preload(c.children);
16+
themes: {
17+
light: 'github-light',
18+
dark: 'github-dark',
19+
},
20+
} satisfies RehypeCodeOptions)
21+
// @ts-expect-error -- safe
22+
.use(() => {
23+
return (tree, file: { data: Record<string, unknown> }) => {
24+
file.data.tree = tree;
2225

23-
if (c.type === 'code' && c.lang) {
24-
await highlighter.loadLanguage(
25-
c.lang as unknown as LanguageRegistration,
26-
);
27-
}
28-
}),
29-
);
30-
}
26+
return '';
27+
};
28+
});
3129

32-
await preload(mdast.children);
30+
export async function renderMarkdownToHast(md: string): Promise<Nodes> {
31+
md = md.replace(/{@link (?<link>[^}]*)}/g, '$1'); // replace jsdoc links
3332

34-
return toHast(mdast, {
35-
handlers: {
36-
// @ts-expect-error hast with mdx
37-
code(_, node: Code) {
38-
const lang = node.lang ?? 'plaintext';
33+
const out = await processor.process(md);
3934

40-
return highlighter.codeToHast(node.value, {
41-
lang,
42-
themes: {
43-
light: 'vitesse-light',
44-
dark: 'vitesse-dark',
45-
},
46-
defaultColor: false,
47-
transformers: [
48-
{
49-
name: 'rehype-code:pre-process',
50-
line(hast) {
51-
if (hast.children.length > 0) return;
52-
// Keep the empty lines when using grid layout
53-
hast.children.push({
54-
type: 'text',
55-
value: ' ',
56-
});
57-
},
58-
},
59-
],
60-
}).children;
61-
},
62-
},
63-
});
35+
return out.data.tree as Nodes;
6436
}

packages/typescript/src/ui/auto-type-table.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/// <reference types="react/experimental" />
2-
import fs from 'node:fs/promises';
2+
import * as fs from 'node:fs/promises';
33
import { TypeTable } from 'fumadocs-ui/components/type-table';
44
import { type Jsx, toJsxRuntime } from 'hast-util-to-jsx-runtime';
55
import * as runtime from 'react/jsx-runtime';
@@ -11,6 +11,7 @@ import {
1111
} from '@/generate/base';
1212
import 'server-only';
1313
import { getProject } from '@/get-project';
14+
import type { ReactNode } from 'react';
1415

1516
export interface AutoTypeTableProps {
1617
/**
@@ -46,6 +47,11 @@ export interface AutoTypeTableProps {
4647
*/
4748
type?: string;
4849

50+
/**
51+
* Override the function to render markdown into JSX nodes
52+
*/
53+
renderMarkdown?: typeof renderMarkdownDefault;
54+
4955
options?: GenerateDocumentationOptions;
5056
}
5157

@@ -72,6 +78,7 @@ export async function AutoTypeTable({
7278
path,
7379
name,
7480
type,
81+
renderMarkdown = renderMarkdownDefault,
7582
options = {},
7683
}: AutoTypeTableProps): Promise<React.ReactElement> {
7784
let typeName = name;
@@ -125,7 +132,7 @@ export async function AutoTypeTable({
125132
);
126133
}
127134

128-
async function renderMarkdown(md: string): Promise<React.ReactElement> {
135+
async function renderMarkdownDefault(md: string): Promise<ReactNode> {
129136
return toJsxRuntime(await renderMarkdownToHast(md), {
130137
Fragment: runtime.Fragment,
131138
jsx: runtime.jsx as Jsx,

packages/typescript/tsup.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { defineConfig } from 'tsup';
22

33
export default defineConfig({
4-
external: ['server-only', 'fumadocs-ui', 'react'],
4+
external: ['server-only', 'fumadocs-ui', 'fumadocs-core', 'react'],
55
dts: true,
66
target: 'es6',
77
format: 'esm',

pnpm-lock.yaml

Lines changed: 10 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)