1
1
import type { Element , Root } from 'hast' ;
2
2
import { toString } from 'hast-util-to-string' ;
3
- import { createHighlighter , type Highlighter , type BuiltinTheme , type BundledTheme } from 'shiki' ;
3
+ import { createHighlighter , type Highlighter } from 'shiki' ;
4
4
import type { Plugin } from 'unified' ;
5
5
import { visit } from 'unist-util-visit' ;
6
6
7
7
import {
8
+ type ShikiLang ,
9
+ type ShikiTheme ,
10
+ shikiColorReplacements ,
8
11
DEFAULT_LANG_ALIASES ,
9
12
SHIKI_THEMES ,
10
13
UNIQUE_LANGS ,
11
14
DEFAULT_LANG ,
12
- type ShikiLang ,
13
- type ShikiTheme ,
15
+ DEFAULT_DARK_THEME ,
16
+ DEFAULT_LIGHT_THEME ,
17
+ DEFAULT_THEMES ,
14
18
} from './shiki-constants.js' ;
15
-
16
- const shikiColorReplacements : Partial < Record < BundledTheme , string | Record < string , string > > > = {
17
- 'dark-plus' : {
18
- '#1e1e1e' : 'transparent' ,
19
- '#569cd6' : '#9cdcfe' ,
20
- '#c8c8c8' : '#f3f7f6' ,
21
- '#d4d4d4' : '#f3f7f6' ,
22
- } ,
23
- 'github-light-default' : {
24
- '#fff' : 'transparent' ,
25
- '#ffffff' : 'transparent' ,
26
- } ,
27
- } ;
19
+ import {
20
+ getLanguage ,
21
+ getLinesToHighlight ,
22
+ lineHighlightPattern ,
23
+ LINE_HIGHLIGHT_CLASS ,
24
+ } from './utils.js' ;
28
25
29
26
export type RehypeSyntaxHighlightingOptions = {
30
- theme ?: BuiltinTheme ;
31
- themes ?: Record < 'light' | 'dark' , BuiltinTheme > ;
27
+ theme ?: ShikiTheme ;
28
+ themes ?: Record < 'light' | 'dark' , ShikiTheme > ;
32
29
codeStyling ?: 'dark' | 'system' ;
33
30
} ;
34
31
35
- const lineHighlightPattern = / \{ ( .* ?) \} / ;
36
-
37
- function classNameOrEmptyArray ( element : Element ) : string [ ] {
38
- const className = element . properties . className ;
39
- if ( Array . isArray ( className ) && className . every ( ( el ) => typeof el === 'string' ) ) return className ;
40
- return [ ] ;
41
- }
42
-
43
32
let highlighterPromise : Promise < Highlighter > | null = null ;
44
33
45
34
async function getHighlighter ( ) : Promise < Highlighter > {
46
35
if ( ! highlighterPromise ) {
47
36
highlighterPromise = createHighlighter ( {
48
- themes : [ 'github-light-default' , 'dark-plus' ] ,
37
+ themes : DEFAULT_THEMES ,
49
38
langs : UNIQUE_LANGS ,
50
39
} ) ;
51
40
}
@@ -57,14 +46,18 @@ export const rehypeSyntaxHighlighting: Plugin<[RehypeSyntaxHighlightingOptions?]
57
46
) => {
58
47
return async ( tree ) => {
59
48
const highlighter = await getHighlighter ( ) ;
60
- if ( options . theme ) {
49
+ if ( options . theme && ! DEFAULT_THEMES . includes ( options . theme ) ) {
61
50
await highlighter . loadTheme ( options . theme ) ;
62
51
}
63
52
if ( options . themes ) {
64
- await Promise . all ( [
65
- highlighter . loadTheme ( options . themes . dark ) ,
66
- highlighter . loadTheme ( options . themes . light ) ,
67
- ] ) ;
53
+ const promises : Promise < void > [ ] = [ ] ;
54
+ if ( ! DEFAULT_THEMES . includes ( options . themes . dark ) ) {
55
+ promises . push ( highlighter . loadTheme ( options . themes . dark ) ) ;
56
+ }
57
+ if ( ! DEFAULT_THEMES . includes ( options . themes . light ) ) {
58
+ promises . push ( highlighter . loadTheme ( options . themes . light ) ) ;
59
+ }
60
+ await Promise . all ( promises ) ;
68
61
}
69
62
70
63
visit ( tree , 'element' , ( node , index , parent ) => {
@@ -106,8 +99,8 @@ export const rehypeSyntaxHighlighting: Plugin<[RehypeSyntaxHighlightingOptions?]
106
99
light :
107
100
options . themes ?. light ??
108
101
options . theme ??
109
- ( options . codeStyling === 'dark' ? 'dark-plus' : 'github-light-default' ) ,
110
- dark : options . themes ?. dark ?? options . theme ?? 'dark-plus' ,
102
+ ( options . codeStyling === 'dark' ? DEFAULT_DARK_THEME : DEFAULT_LIGHT_THEME ) ,
103
+ dark : options . themes ?. dark ?? options . theme ?? DEFAULT_DARK_THEME ,
111
104
} ,
112
105
colorReplacements : shikiColorReplacements ,
113
106
tabindex : false ,
@@ -134,9 +127,9 @@ export const rehypeSyntaxHighlighting: Plugin<[RehypeSyntaxHighlightingOptions?]
134
127
lineNumber ++ ;
135
128
if ( linesToHighlight . includes ( lineNumber ) ) {
136
129
if ( typeof span . properties . class === 'string' ) {
137
- span . properties . class += ' line-highlight' ;
130
+ span . properties . class += ' ' + LINE_HIGHLIGHT_CLASS ;
138
131
} else {
139
- span . properties . class = [ ...span . properties . class , 'line-highlight' ] ;
132
+ span . properties . class = [ ...span . properties . class , LINE_HIGHLIGHT_CLASS ] ;
140
133
}
141
134
}
142
135
} ) ;
@@ -165,50 +158,4 @@ export const rehypeSyntaxHighlighting: Plugin<[RehypeSyntaxHighlightingOptions?]
165
158
} ;
166
159
} ;
167
160
168
- function getLanguage ( node : Element , aliases : Record < string , ShikiLang > ) : ShikiLang | undefined {
169
- const className = classNameOrEmptyArray ( node ) ;
170
-
171
- for ( const classListItem of className ) {
172
- if ( classListItem . startsWith ( 'language-' ) ) {
173
- const lang = classListItem . slice ( 9 ) . toLowerCase ( ) ;
174
- if ( lang ) return aliases [ lang ] ;
175
- }
176
- }
177
-
178
- return undefined ;
179
- }
180
-
181
- function getLinesToHighlight ( node : Element , maxLines : number ) : number [ ] {
182
- const meta =
183
- typeof node . data ?. meta === 'string'
184
- ? node . data . meta
185
- : classNameOrEmptyArray ( node ) . reduce ( ( acc , item ) => acc + ' ' + item , '' ) ;
186
- if ( ! meta ) return [ ] ;
187
-
188
- const content = meta . match ( lineHighlightPattern ) ?. [ 1 ] ?. trim ( ) ;
189
- if ( ! content ) return [ ] ;
190
-
191
- const lineNumbers = new Set < number > ( ) ;
192
-
193
- content . split ( ',' ) . forEach ( ( part ) => {
194
- const [ start , end ] = part . split ( '-' ) . map ( ( num ) => {
195
- const trimmed = num . trim ( ) ;
196
- if ( ! / ^ \d + $ / . test ( trimmed ) ) return undefined ;
197
- const parsed = parseInt ( trimmed , 10 ) ;
198
- return parsed > maxLines ? maxLines : parsed ;
199
- } ) ;
200
-
201
- if ( ! start ) return ;
202
- const endLine = end ?? start ;
203
-
204
- if ( endLine < start ) return ;
205
- const max = Math . min ( endLine , maxLines ) ;
206
- for ( let i = start ; i <= max ; i ++ ) {
207
- lineNumbers . add ( i ) ;
208
- }
209
- } ) ;
210
-
211
- return Array . from ( lineNumbers ) . sort ( ( a , b ) => a - b ) ;
212
- }
213
-
214
161
export { UNIQUE_LANGS , DEFAULT_LANG_ALIASES , SHIKI_THEMES , ShikiLang , ShikiTheme } ;
0 commit comments