1
1
import type { Element , Root , Text } from 'hast'
2
- import type { CodeToHastOptions , HtmlRendererOptions , ShikiContext , ShikijiTransformerContext , ThemedToken } from '../types'
2
+ import type {
3
+ CodeToHastOptions ,
4
+ HtmlRendererOptions ,
5
+ ShikiContext ,
6
+ ShikijiTransformerContext ,
7
+ ThemedToken ,
8
+ ThemedTokenWithVariants ,
9
+ TokenStyles ,
10
+ } from '../types'
11
+ import css from '../assets/langs/css'
3
12
import { codeToThemedTokens } from './tokenizer'
4
13
import { FontStyle } from './stackElementMetadata'
5
14
import { codeToTokensWithThemes } from './renderer-html-themes'
@@ -22,7 +31,9 @@ export function codeToHast(
22
31
} = options
23
32
24
33
const themes = Object . entries ( options . themes )
25
- . filter ( i => i [ 1 ] ) as [ string , string ] [ ]
34
+ . filter ( i => i [ 1 ] )
35
+ . map ( i => ( { color : i [ 0 ] , theme : i [ 1 ] ! } ) )
36
+ . sort ( ( a , b ) => a . color === defaultColor ? - 1 : b . color === defaultColor ? 1 : 0 )
26
37
27
38
if ( themes . length === 0 )
28
39
throw new Error ( '[shikiji] `themes` option must not be empty' )
@@ -32,55 +43,18 @@ export function codeToHast(
32
43
code ,
33
44
options ,
34
45
)
35
- . sort ( a => a [ 0 ] === defaultColor ? - 1 : 1 )
36
46
37
- if ( defaultColor && ! themeTokens . find ( t => t [ 0 ] === defaultColor ) )
47
+ if ( defaultColor && ! themes . find ( t => t . color === defaultColor ) )
38
48
throw new Error ( `[shikiji] \`themes\` option must contain the defaultColor key \`${ defaultColor } \`` )
39
49
40
- const themeRegs = themeTokens . map ( t => context . getTheme ( t [ 1 ] ) )
41
- const themeMap = themeTokens . map ( t => t [ 2 ] )
42
- tokens = [ ]
43
-
44
- for ( let i = 0 ; i < themeMap [ 0 ] . length ; i ++ ) {
45
- const lineMap = themeMap . map ( t => t [ i ] )
46
- const lineout : any [ ] = [ ]
47
- tokens . push ( lineout )
48
- for ( let j = 0 ; j < lineMap [ 0 ] . length ; j ++ ) {
49
- const tokenMap = lineMap . map ( t => t [ j ] )
50
- const tokenStyles = tokenMap . map ( t => getTokenStyles ( t ) )
51
-
52
- // Get all style keys, for themes that missing some style, we put `inherit` to override as needed
53
- const styleKeys = new Set ( tokenStyles . flatMap ( t => Object . keys ( t ) ) )
54
- const mergedStyles = tokenStyles . reduce ( ( acc , cur , idx ) => {
55
- for ( const key of styleKeys ) {
56
- const value = cur [ key ] || 'inherit'
57
-
58
- if ( idx === 0 && defaultColor ) {
59
- acc [ key ] = value
60
- }
61
- else {
62
- const varKey = cssVariablePrefix + themeTokens [ idx ] [ 0 ] + ( key === 'color' ? '' : `-${ key } ` )
63
- if ( acc [ key ] )
64
- acc [ key ] += `;${ varKey } :${ value } `
65
- else
66
- acc [ key ] = `${ varKey } :${ value } `
67
- }
68
- }
69
- return acc
70
- } , { } as Record < string , string > )
71
-
72
- lineout . push ( {
73
- ...tokenMap [ 0 ] ,
74
- color : '' ,
75
- htmlStyle : defaultColor
76
- ? stringifyTokenStyle ( mergedStyles )
77
- : Object . values ( mergedStyles ) . join ( ';' ) ,
78
- } )
79
- }
80
- }
50
+ const themeRegs = themes . map ( t => context . getTheme ( t . theme ) )
81
51
82
- fg = themeTokens . map ( ( t , idx ) => ( idx === 0 && defaultColor ? '' : `${ cssVariablePrefix + t [ 0 ] } :` ) + themeRegs [ idx ] . fg ) . join ( ';' )
83
- bg = themeTokens . map ( ( t , idx ) => ( idx === 0 && defaultColor ? '' : `${ cssVariablePrefix + t [ 0 ] } -bg:` ) + themeRegs [ idx ] . bg ) . join ( ';' )
52
+ const themesOrder = themes . map ( t => t . color )
53
+ tokens = themeTokens
54
+ . map ( line => line . map ( token => flattenToken ( token , themesOrder , cssVariablePrefix , defaultColor ) ) )
55
+
56
+ fg = themes . map ( ( t , idx ) => ( idx === 0 && defaultColor ? '' : `${ cssVariablePrefix + t . color } :` ) + themeRegs [ idx ] . fg ) . join ( ';' )
57
+ bg = themes . map ( ( t , idx ) => ( idx === 0 && defaultColor ? '' : `${ cssVariablePrefix + t . color } -bg:` ) + themeRegs [ idx ] . bg ) . join ( ';' )
84
58
themeName = `shiki-themes ${ themeRegs . map ( t => t . name ) . join ( ' ' ) } `
85
59
rootStyle = defaultColor ? undefined : [ fg , bg ] . join ( ';' )
86
60
}
@@ -108,6 +82,48 @@ export function codeToHast(
108
82
} )
109
83
}
110
84
85
+ /**
86
+ *
87
+ */
88
+ function flattenToken (
89
+ merged : ThemedTokenWithVariants ,
90
+ variantsOrder : string [ ] ,
91
+ cssVariablePrefix : string ,
92
+ defaultColor : string | boolean ,
93
+ ) {
94
+ const token : ThemedToken = {
95
+ content : merged . content ,
96
+ explanation : merged . explanation ,
97
+ }
98
+
99
+ const styles = variantsOrder . map ( t => getTokenStyleObject ( merged . variants [ t ] ) )
100
+
101
+ // Get all style keys, for themes that missing some style, we put `inherit` to override as needed
102
+ const styleKeys = new Set ( styles . flatMap ( t => Object . keys ( t ) ) )
103
+ const mergedStyles = styles . reduce ( ( acc , cur , idx ) => {
104
+ for ( const key of styleKeys ) {
105
+ const value = cur [ key ] || 'inherit'
106
+
107
+ if ( idx === 0 && defaultColor ) {
108
+ acc [ key ] = value
109
+ }
110
+ else {
111
+ const varKey = cssVariablePrefix + variantsOrder [ idx ] + ( key === 'color' ? '' : `-${ key } ` )
112
+ if ( acc [ key ] )
113
+ acc [ key ] += `;${ varKey } :${ value } `
114
+ else
115
+ acc [ key ] = `${ varKey } :${ value } `
116
+ }
117
+ }
118
+ return acc
119
+ } , { } as Record < string , string > )
120
+
121
+ token . htmlStyle = defaultColor
122
+ ? stringifyTokenStyle ( mergedStyles )
123
+ : Object . values ( mergedStyles ) . join ( ';' )
124
+ return token
125
+ }
126
+
111
127
export function tokensToHast (
112
128
tokens : ThemedToken [ ] [ ] ,
113
129
options : HtmlRendererOptions ,
@@ -190,7 +206,7 @@ export function tokensToHast(
190
206
children : [ { type : 'text' , value : token . content } ] ,
191
207
}
192
208
193
- const style = token . htmlStyle || stringifyTokenStyle ( getTokenStyles ( token ) )
209
+ const style = token . htmlStyle || stringifyTokenStyle ( getTokenStyleObject ( token ) )
194
210
if ( style )
195
211
tokenNode . properties . style = style
196
212
@@ -223,7 +239,7 @@ export function tokensToHast(
223
239
return result
224
240
}
225
241
226
- function getTokenStyles ( token : ThemedToken ) {
242
+ function getTokenStyleObject ( token : TokenStyles ) {
227
243
const styles : Record < string , string > = { }
228
244
if ( token . color )
229
245
styles . color = token . color
0 commit comments