@@ -2,69 +2,114 @@ import { IApi } from 'dumi';
2
2
import ReactTechStack from 'dumi/dist/techStacks/react' ;
3
3
import type { ExampleBlockAsset } from 'dumi-assets-types' ;
4
4
import ts from 'typescript' ;
5
- import { ShikiTransformerContextCommon } from 'shiki' ;
5
+ import type { HighlighterCore , ShikiTransformerContextCommon } from 'shiki' ;
6
6
import type { Element , ElementContent } from 'hast' ;
7
7
import type { fromMarkdown } from 'mdast-util-from-markdown' ;
8
8
import type { gfmFromMarkdown } from 'mdast-util-gfm' ;
9
9
import type { defaultHandlers , toHast } from 'mdast-util-to-hast' ;
10
10
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
+
11
51
class DTReactTech extends ReactTechStack {
12
52
async generateMetadata ( asset : ExampleBlockAsset ) {
13
53
// 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
+ ] ) ;
19
67
const handler = {
20
68
fromMarkdown,
21
69
gfmFromMarkdown,
22
70
defaultHandlers,
23
71
toHast,
24
72
} ;
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 ,
40
83
} ,
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 ,
53
86
} ,
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
+ } ) ;
62
102
return asset ;
63
103
}
64
104
}
65
105
66
106
const AssetsPlugin = async ( api : IApi ) => {
67
107
api . registerTechStack ( ( ) => new DTReactTech ( ) ) ;
108
+
109
+ // TODO: 应该在 umi 退出的时候销毁,包括 catch 退出
110
+ api . onDevCompileDone ( ( ) => {
111
+ highlighter ?. dispose ( ) ;
112
+ } ) ;
68
113
} ;
69
114
70
115
export default AssetsPlugin ;
0 commit comments