1
- import { describe , expect , it } from 'vitest'
1
+ /* eslint-disable style/no-tabs */
2
+ import { afterEach , describe , expect , it , vi } from 'vitest'
2
3
import { toHtml } from 'hast-util-to-html'
3
4
import { codeToHtml , getHighlighter } from '../src'
4
5
6
+ afterEach ( ( ) => {
7
+ vi . restoreAllMocks
8
+ } )
9
+
5
10
describe ( 'should' , ( ) => {
6
11
it ( 'works' , async ( ) => {
7
12
const shiki = await getHighlighter ( {
@@ -19,9 +24,13 @@ describe('should', () => {
19
24
} )
20
25
21
26
it ( 'hast transformer' , async ( ) => {
27
+ const warn = vi . spyOn ( console , 'warn' )
28
+ warn . mockImplementation ( ( ( ) => { } ) as any )
29
+
22
30
const code = await codeToHtml ( 'foo\bar' , {
23
31
lang : 'js' ,
24
32
theme : 'vitesse-light' ,
33
+ // Use deprecated `transforms` option on purpose to test
25
34
transforms : {
26
35
line ( node , line ) {
27
36
node . properties [ 'data-line' ] = line
@@ -37,6 +46,10 @@ describe('should', () => {
37
46
38
47
expect ( code )
39
48
. toMatchInlineSnapshot ( '"<pre class=\\"shiki vitesse-light\\" style=\\"background-color:#ffffff;color:#393a34\\" tabindex=\\"0\\"><code class=\\"language-js\\"><span class=\\"line\\" data-line=\\"1\\"><span style=\\"color:#B07D48\\" class=\\"token:1:0\\">foo</span><span style=\\"color:#393A34\\" class=\\"token:1:3\\"></span><span style=\\"color:#B07D48\\" class=\\"token:1:4\\">ar</span></span></code></pre>"' )
49
+
50
+ expect ( warn ) . toBeCalledTimes ( 1 )
51
+ expect ( warn . mock . calls [ 0 ] [ 0 ] )
52
+ . toMatchInlineSnapshot ( '"[shikiji] `transforms` option is deprecated, use `transformers` instead"' )
40
53
} )
41
54
} )
42
55
@@ -48,19 +61,21 @@ it('hasfocus support', async () => {
48
61
const code = await codeToHtml ( snippet , {
49
62
lang : 'php' ,
50
63
theme : 'vitesse-light' ,
51
- transforms : {
52
- code ( node ) {
53
- node . properties . class = 'language-php'
54
- } ,
55
- token ( node , line , col , parent ) {
56
- node . children . forEach ( ( child ) => {
57
- if ( child . type === 'text' && child . value . includes ( '[!code focus]' ) ) {
58
- parent . properties [ 'data-has-focus' ] = 'true'
59
- node . children . splice ( node . children . indexOf ( child ) , 1 )
60
- }
61
- } )
64
+ transformers : [
65
+ {
66
+ code ( node ) {
67
+ node . properties . class = 'language-php'
68
+ } ,
69
+ token ( node , line , col , parent ) {
70
+ node . children . forEach ( ( child ) => {
71
+ if ( child . type === 'text' && child . value . includes ( '[!code focus]' ) ) {
72
+ parent . properties [ 'data-has-focus' ] = 'true'
73
+ node . children . splice ( node . children . indexOf ( child ) , 1 )
74
+ }
75
+ } )
76
+ } ,
62
77
} ,
63
- } ,
78
+ ] ,
64
79
} )
65
80
66
81
expect ( code )
@@ -70,3 +85,59 @@ it('hasfocus support', async () => {
70
85
<span class=\\"line\\"><span style=\\"color:#999999\\">$</span><span style=\\"color:#B07D48\\">bar</span><span style=\\"color:#999999\\"> =</span><span style=\\"color:#B5695999\\"> \\"</span><span style=\\"color:#B56959\\">baz</span><span style=\\"color:#B5695999\\">\\"</span><span style=\\"color:#999999\\">;</span></span></code></pre>"
71
86
` )
72
87
} )
88
+
89
+ it ( 'render whitespace' , async ( ) => {
90
+ const snippet = [
91
+ ' space()' ,
92
+ '\t\ttab()' ,
93
+ ] . join ( '\n' )
94
+
95
+ const classMap : Record < string , string > = {
96
+ ' ' : 'space' ,
97
+ '\t' : 'tab' ,
98
+ }
99
+
100
+ const code = await codeToHtml ( snippet , {
101
+ lang : 'js' ,
102
+ theme : 'vitesse-light' ,
103
+ transformers : [
104
+ {
105
+ line ( node ) {
106
+ const first = node . children [ 0 ]
107
+ if ( ! first || first . type !== 'element' )
108
+ return
109
+ const textNode = first . children [ 0 ]
110
+ if ( ! textNode || textNode . type !== 'text' )
111
+ return
112
+ node . children = node . children . flatMap ( ( child ) => {
113
+ if ( child . type !== 'element' )
114
+ return child
115
+ const node = child . children [ 0 ]
116
+ if ( node . type !== 'text' || ! node . value )
117
+ return child
118
+ const parts = node . value . split ( / ( [ \t ] ) / ) . filter ( i => i . length )
119
+ if ( parts . length <= 1 )
120
+ return child
121
+
122
+ return parts . map ( ( part ) => {
123
+ const clone = {
124
+ ...child ,
125
+ properties : { ...child . properties } ,
126
+ }
127
+ clone . children = [ { type : 'text' , value : part } ]
128
+ if ( part in classMap )
129
+ clone . properties . class = [ clone . properties . class , classMap [ part ] ] . filter ( Boolean ) . join ( ' ' )
130
+ return clone
131
+ } )
132
+ } )
133
+ } ,
134
+ } ,
135
+ ] ,
136
+ } )
137
+
138
+ expect ( code )
139
+ . toMatchInlineSnapshot ( `
140
+ "<pre class=\\"shiki vitesse-light\\" style=\\"background-color:#ffffff;color:#393a34\\" tabindex=\\"0\\"><code><span class=\\"line\\"><span style=\\"color:#59873A\\" class=\\"space\\"> </span><span style=\\"color:#59873A\\" class=\\"space\\"> </span><span style=\\"color:#59873A\\">space</span><span style=\\"color:#999999\\">()</span></span>
141
+ <span class=\\"line\\"><span style=\\"color:#59873A\\" class=\\"tab\\"> </span><span style=\\"color:#59873A\\" class=\\"tab\\"> </span><span style=\\"color:#59873A\\">tab</span><span style=\\"color:#999999\\">()</span></span></code></pre>"
142
+ ` )
143
+ } )
0 commit comments