Skip to content

Commit 0684901

Browse files
committed
feat: support transform md
1 parent 0275493 commit 0684901

File tree

4 files changed

+418
-27
lines changed

4 files changed

+418
-27
lines changed

.dumi/theme/builtins/sourceCode/index.less

+21-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
@import "~@shikijs/twoslash/style-rich.css";
1+
@import '~@shikijs/twoslash/style-rich.css';
22

33
.source-code {
4-
&-container {
5-
position: relative;
6-
max-height: 400px;
7-
overflow: auto;
8-
}
94
&-content {
105
padding: 12px 24px;
116
> pre {
@@ -27,17 +22,37 @@
2722
}
2823

2924
// override style-rich.css
25+
:root {
26+
--twoslash-docs-font: 'Helvetica Neue', Helvetica, Arial, sans-serif;
27+
}
3028
.twoslash {
3129
.twoslash-hover {
3230
position: static;
3331
}
3432
.twoslash-popup-docs {
3533
max-height: 200px;
3634
overflow: auto;
35+
font-size: 0.9em;
36+
white-space: initial;
37+
38+
p {
39+
margin-bottom: 0.2em;
40+
}
41+
code {
42+
color: #c7254e;
43+
background-color: #f9f2f4;
44+
padding: 2px 4px;
45+
font-size: 90%;
46+
border-radius: 4px;
47+
}
48+
.twoslash-popup-docs-tag-name {
49+
font-style: italic;
50+
}
3751
}
3852
.twoslash-popup-container {
3953
max-width: 50vw;
4054
transform: translateY(-100%);
55+
pointer-events: initial;
4156
}
4257
.twoslash-popup-code {
4358
white-space: break-spaces;

.dumi/theme/plugin.ts

+104-20
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,63 @@ import { IApi } from 'dumi';
22
import ReactTechStack from 'dumi/dist/techStacks/react';
33
import type { ExampleBlockAsset } from 'dumi-assets-types';
44
import ts from 'typescript';
5+
import { ShikiTransformerContextCommon } from 'shiki';
6+
import type { Element, ElementContent } from 'hast';
7+
import type { fromMarkdown } from 'mdast-util-from-markdown';
8+
import type { gfmFromMarkdown } from 'mdast-util-gfm';
9+
import type { defaultHandlers, toHast } from 'mdast-util-to-hast';
510

611
class DTReactTech extends ReactTechStack {
712
async generateMetadata(asset: ExampleBlockAsset) {
813
// workaround for esm module
914
const { transformerTwoslash } = await import('@shikijs/twoslash');
1015
const { codeToHtml } = await import('shiki');
11-
for (const [filename, dep] of Object.entries(asset.dependencies)) {
12-
if (dep.type === 'FILE' && asset.id === 'blockheader-demo-basic') {
13-
const html = await codeToHtml(dep.value, {
14-
lang: 'tsx',
15-
theme: 'vitesse-light',
16-
transformers: [
17-
transformerTwoslash({
18-
twoslashOptions: {
19-
compilerOptions: {
20-
jsx: ts.JsxEmit.React,
16+
const { fromMarkdown } = await import('mdast-util-from-markdown');
17+
const { gfmFromMarkdown } = await import('mdast-util-gfm');
18+
const { defaultHandlers, toHast } = await import('mdast-util-to-hast');
19+
const handler = {
20+
fromMarkdown,
21+
gfmFromMarkdown,
22+
defaultHandlers,
23+
toHast,
24+
};
25+
await Promise.all(
26+
Object.entries(asset.dependencies).map(([filename, dep]) => {
27+
if (dep.type === 'FILE') {
28+
return codeToHtml(dep.value, {
29+
lang: 'tsx',
30+
theme: 'vitesse-light',
31+
transformers: [
32+
transformerTwoslash({
33+
twoslashOptions: {
34+
compilerOptions: {
35+
jsx: ts.JsxEmit.React,
36+
},
37+
handbookOptions: {
38+
noErrors: true,
39+
},
2140
},
22-
handbookOptions: {
23-
noErrors: true,
41+
rendererRich: {
42+
renderMarkdown: function (md) {
43+
return renderMarkdown.call(this, md, handler);
44+
},
45+
renderMarkdownInline: function (md, context) {
46+
return renderMarkdownInline.call(
47+
this,
48+
handler,
49+
md,
50+
context
51+
);
52+
},
2453
},
25-
},
26-
}),
27-
],
28-
});
29-
30-
asset.dependencies[filename] = <any>{ ...dep, jsx: html };
31-
}
32-
}
54+
}),
55+
],
56+
}).then((html) => {
57+
asset.dependencies[filename] = <any>{ ...dep, jsx: html };
58+
});
59+
}
60+
})
61+
);
3362
return asset;
3463
}
3564
}
@@ -39,3 +68,58 @@ const AssetsPlugin = async (api: IApi) => {
3968
};
4069

4170
export default AssetsPlugin;
71+
72+
type Handler = {
73+
fromMarkdown: typeof fromMarkdown;
74+
gfmFromMarkdown: typeof gfmFromMarkdown;
75+
toHast: typeof toHast;
76+
defaultHandlers: typeof defaultHandlers;
77+
};
78+
/**
79+
* refer:https://github.com/shikijs/shiki/blob/main/packages/vitepress-twoslash/src/renderer-floating-vue.ts#L130-L131
80+
*/
81+
function renderMarkdown(this: ShikiTransformerContextCommon, md: string, handler: Handler) {
82+
const { fromMarkdown, gfmFromMarkdown, toHast, defaultHandlers } = handler;
83+
const mdast = fromMarkdown(
84+
md.replace(/\{@link ([^}]*)\}/g, '$1'), // replace jsdoc links
85+
{ mdastExtensions: [gfmFromMarkdown()] }
86+
);
87+
88+
return (
89+
toHast(mdast, {
90+
handlers: {
91+
code: (state, node) => {
92+
const lang = node.lang || '';
93+
if (lang) {
94+
return <Element>{
95+
type: 'element',
96+
tagName: 'code',
97+
properties: {},
98+
children: this.codeToHast(node.value, {
99+
...this.options,
100+
transformers: [],
101+
lang,
102+
structure: node.value.trim().includes('\n') ? 'classic' : 'inline',
103+
}).children,
104+
};
105+
}
106+
return defaultHandlers.code(state, node);
107+
},
108+
},
109+
}) as Element
110+
).children;
111+
}
112+
113+
function renderMarkdownInline(
114+
this: ShikiTransformerContextCommon,
115+
handler: Handler,
116+
md: string,
117+
context?: string
118+
): ElementContent[] {
119+
if (context === 'tag:param') md = md.replace(/^([\w$-]+)/, '`$1` ');
120+
121+
const children = renderMarkdown.call(this, md, handler);
122+
if (children.length === 1 && children[0].type === 'element' && children[0].tagName === 'p')
123+
return children[0].children;
124+
return children;
125+
}

package.json

+5
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
"@testing-library/react": "^13.4.0",
8282
"@testing-library/react-hooks": "^8.0.1",
8383
"@testing-library/user-event": "^14.4.3",
84+
"@types/hast": "^3.0.4",
8485
"@types/jest": "^29.2.3",
8586
"@types/lodash-es": "^4.17.12",
8687
"@types/react": "^18.0.0",
@@ -94,11 +95,15 @@
9495
"eslint": "^8.23.0",
9596
"father": "~4.1.0",
9697
"gh-pages": "^4.0.0",
98+
"hast": "^1.0.0",
9799
"husky": "^8.0.1",
98100
"jest": "^29.3.1",
99101
"jest-environment-jsdom": "^29.3.1",
100102
"ko-lint-config": "2.2.21",
101103
"lint-staged": "^13.0.3",
104+
"mdast-util-from-markdown": "^2.0.2",
105+
"mdast-util-gfm": "^3.1.0",
106+
"mdast-util-to-hast": "^13.2.0",
102107
"prettier": "^2.7.1",
103108
"rc-motion": "2.6.2",
104109
"rc-tabs": "^12.10.0",

0 commit comments

Comments
 (0)