Skip to content

Commit a4d917b

Browse files
committed
feat: improve performance
1 parent 0684901 commit a4d917b

File tree

2 files changed

+86
-42
lines changed

2 files changed

+86
-42
lines changed

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

-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
.twoslash-popup-container {
5353
max-width: 50vw;
5454
transform: translateY(-100%);
55-
pointer-events: initial;
5655
}
5756
.twoslash-popup-code {
5857
white-space: break-spaces;

.dumi/theme/plugin.ts

+86-41
Original file line numberDiff line numberDiff line change
@@ -2,69 +2,114 @@ 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';
5+
import type { HighlighterCore, ShikiTransformerContextCommon } from 'shiki';
66
import type { Element, ElementContent } from 'hast';
77
import type { fromMarkdown } from 'mdast-util-from-markdown';
88
import type { gfmFromMarkdown } from 'mdast-util-gfm';
99
import type { defaultHandlers, toHast } from 'mdast-util-to-hast';
1010

11+
let highlighter: HighlighterCore | null = null;
12+
async function getHighlighterCore() {
13+
const [
14+
{ createHighlighterCore, createOnigurumaEngine },
15+
wasm,
16+
{ bundledLanguages },
17+
{ bundledThemes },
18+
] = await Promise.all([
19+
import('shiki'),
20+
import('shiki/dist/wasm.mjs'),
21+
import('shiki/dist/langs.mjs'),
22+
import('shiki/dist/themes.mjs'),
23+
]);
24+
highlighter =
25+
highlighter ||
26+
(await createHighlighterCore({
27+
themes: [bundledThemes['vitesse-light']],
28+
langs: [bundledLanguages['tsx'], bundledLanguages['js'], bundledLanguages['ts']],
29+
engine: createOnigurumaEngine(wasm),
30+
}));
31+
return highlighter;
32+
}
33+
34+
let creatingHighlighterCore = false;
35+
let listener: Function[] = [];
36+
async function getShiki() {
37+
return new Promise<HighlighterCore['codeToHtml']>((resolve) => {
38+
if (!creatingHighlighterCore) {
39+
creatingHighlighterCore = true;
40+
getHighlighterCore().then(({ codeToHtml }) => {
41+
listener.forEach((resolve) => resolve(codeToHtml));
42+
resolve(codeToHtml);
43+
creatingHighlighterCore = false;
44+
});
45+
} else {
46+
listener.push(resolve);
47+
}
48+
});
49+
}
50+
1151
class DTReactTech extends ReactTechStack {
1252
async generateMetadata(asset: ExampleBlockAsset) {
1353
// workaround for esm module
14-
const { transformerTwoslash } = await import('@shikijs/twoslash');
15-
const { codeToHtml } = await import('shiki');
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');
54+
const [
55+
{ transformerTwoslash },
56+
codeToHtml,
57+
{ fromMarkdown },
58+
{ gfmFromMarkdown },
59+
{ defaultHandlers, toHast },
60+
] = await Promise.all([
61+
import('@shikijs/twoslash'),
62+
getShiki(),
63+
import('mdast-util-from-markdown'),
64+
import('mdast-util-gfm'),
65+
import('mdast-util-to-hast'),
66+
]);
1967
const handler = {
2068
fromMarkdown,
2169
gfmFromMarkdown,
2270
defaultHandlers,
2371
toHast,
2472
};
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-
},
73+
Object.entries(asset.dependencies).map(([filename, dep]) => {
74+
if (dep.type === 'FILE') {
75+
const html = codeToHtml(dep.value, {
76+
lang: 'tsx',
77+
theme: 'vitesse-light',
78+
transformers: [
79+
transformerTwoslash({
80+
twoslashOptions: {
81+
compilerOptions: {
82+
jsx: ts.JsxEmit.React,
4083
},
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-
},
84+
handbookOptions: {
85+
noErrors: true,
5386
},
54-
}),
55-
],
56-
}).then((html) => {
57-
asset.dependencies[filename] = <any>{ ...dep, jsx: html };
58-
});
59-
}
60-
})
61-
);
87+
},
88+
rendererRich: {
89+
renderMarkdown: function (md) {
90+
return renderMarkdown.call(this, md, handler);
91+
},
92+
renderMarkdownInline: function (md, context) {
93+
return renderMarkdownInline.call(this, handler, md, context);
94+
},
95+
},
96+
}),
97+
],
98+
});
99+
asset.dependencies[filename] = <any>{ ...dep, jsx: html };
100+
}
101+
});
62102
return asset;
63103
}
64104
}
65105

66106
const AssetsPlugin = async (api: IApi) => {
67107
api.registerTechStack(() => new DTReactTech());
108+
109+
// TODO: 应该在 umi 退出的时候销毁,包括 catch 退出
110+
api.onDevCompileDone(() => {
111+
highlighter?.dispose();
112+
});
68113
};
69114

70115
export default AssetsPlugin;

0 commit comments

Comments
 (0)